diff options
author | robot-contrib <robot-contrib@yandex-team.com> | 2023-03-02 10:05:41 +0300 |
---|---|---|
committer | robot-contrib <robot-contrib@yandex-team.com> | 2023-03-02 10:05:41 +0300 |
commit | c8b3fb9dab5150585defd576d83647a12a84b990 (patch) | |
tree | 918b7d8f1a373e70555914fd15aa4e0a8cc09940 | |
parent | 093b04166b8440db82cf0590a4beb6c5e8cd5668 (diff) | |
download | ydb-c8b3fb9dab5150585defd576d83647a12a84b990.tar.gz |
Update contrib/restricted/aws/s2n to 1.3.37
-rw-r--r-- | contrib/restricted/aws/s2n/CMakeLists.darwin.txt | 1 | ||||
-rw-r--r-- | contrib/restricted/aws/s2n/CMakeLists.linux-aarch64.txt | 1 | ||||
-rw-r--r-- | contrib/restricted/aws/s2n/CMakeLists.linux.txt | 1 | ||||
-rw-r--r-- | contrib/restricted/aws/s2n/README.md | 2 | ||||
-rw-r--r-- | contrib/restricted/aws/s2n/api/s2n.h | 21 | ||||
-rw-r--r-- | contrib/restricted/aws/s2n/api/unstable/fingerprint.h | 76 | ||||
-rw-r--r-- | contrib/restricted/aws/s2n/stuffer/s2n_stuffer.c | 8 | ||||
-rw-r--r-- | contrib/restricted/aws/s2n/stuffer/s2n_stuffer.h | 1 | ||||
-rw-r--r-- | contrib/restricted/aws/s2n/tls/s2n_client_hello.c | 18 | ||||
-rw-r--r-- | contrib/restricted/aws/s2n/tls/s2n_client_hello.h | 10 | ||||
-rw-r--r-- | contrib/restricted/aws/s2n/tls/s2n_fingerprint.c | 322 | ||||
-rw-r--r-- | contrib/restricted/aws/s2n/tls/s2n_record_read.c | 30 | ||||
-rw-r--r-- | contrib/restricted/aws/s2n/utils/s2n_init.c | 6 | ||||
-rw-r--r-- | contrib/restricted/aws/s2n/utils/s2n_random.c | 21 |
14 files changed, 507 insertions, 11 deletions
diff --git a/contrib/restricted/aws/s2n/CMakeLists.darwin.txt b/contrib/restricted/aws/s2n/CMakeLists.darwin.txt index 09035da4f2..976996467f 100644 --- a/contrib/restricted/aws/s2n/CMakeLists.darwin.txt +++ b/contrib/restricted/aws/s2n/CMakeLists.darwin.txt @@ -147,6 +147,7 @@ target_sources(restricted-aws-s2n PRIVATE ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_ecc_preferences.c ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_encrypted_extensions.c ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_establish_session.c + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_fingerprint.c ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_handshake.c ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_handshake_hashes.c ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_handshake_io.c diff --git a/contrib/restricted/aws/s2n/CMakeLists.linux-aarch64.txt b/contrib/restricted/aws/s2n/CMakeLists.linux-aarch64.txt index 84dcb48aad..c7372a04a0 100644 --- a/contrib/restricted/aws/s2n/CMakeLists.linux-aarch64.txt +++ b/contrib/restricted/aws/s2n/CMakeLists.linux-aarch64.txt @@ -142,6 +142,7 @@ target_sources(restricted-aws-s2n PRIVATE ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_ecc_preferences.c ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_encrypted_extensions.c ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_establish_session.c + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_fingerprint.c ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_handshake.c ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_handshake_hashes.c ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_handshake_io.c diff --git a/contrib/restricted/aws/s2n/CMakeLists.linux.txt b/contrib/restricted/aws/s2n/CMakeLists.linux.txt index aa77c97b45..e4b895391c 100644 --- a/contrib/restricted/aws/s2n/CMakeLists.linux.txt +++ b/contrib/restricted/aws/s2n/CMakeLists.linux.txt @@ -149,6 +149,7 @@ target_sources(restricted-aws-s2n PRIVATE ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_ecc_preferences.c ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_encrypted_extensions.c ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_establish_session.c + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_fingerprint.c ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_handshake.c ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_handshake_hashes.c ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/s2n/tls/s2n_handshake_io.c diff --git a/contrib/restricted/aws/s2n/README.md b/contrib/restricted/aws/s2n/README.md index 1271226f3c..a2574fdc31 100644 --- a/contrib/restricted/aws/s2n/README.md +++ b/contrib/restricted/aws/s2n/README.md @@ -146,7 +146,7 @@ s2n-tls avoids implementing rarely used options and extensions, as well as featu The security of TLS and its associated encryption algorithms depends upon secure random number generation. s2n-tls provides every thread with two separate random number generators. One for "public" randomly generated data that may appear in the clear, and one for "private" data that should remain secret. This approach lessens the risk of potential predictability weaknesses in random number generation algorithms from leaking information across contexts. ##### Modularized encryption -s2n-tls has been structured so that different encryption libraries may be used. Today s2n-tls supports OpenSSL (versions 1.0.2, 1.1.1 and 3.0.x), LibreSSL, BoringSSL, and the Apple Common Crypto framework to perform the underlying cryptographic operations. +s2n-tls has been structured so that different encryption libraries may be used. Today s2n-tls supports OpenSSL (versions 1.0.2, 1.1.1 and 3.0.x), LibreSSL, BoringSSL, AWS-LC, and the Apple Common Crypto framework to perform the underlying cryptographic operations. ##### Timing blinding s2n-tls includes structured support for blinding time-based side-channels that may leak sensitive data. For example, if s2n-tls fails to parse a TLS record or handshake message, s2n-tls will add a randomized delay of between 10 and 30 seconds, granular to nanoseconds, before responding. This raises the complexity of real-world timing side-channel attacks by a factor of at least tens of trillions. diff --git a/contrib/restricted/aws/s2n/api/s2n.h b/contrib/restricted/aws/s2n/api/s2n.h index 558d371a03..9993a61062 100644 --- a/contrib/restricted/aws/s2n/api/s2n.h +++ b/contrib/restricted/aws/s2n/api/s2n.h @@ -1325,12 +1325,16 @@ S2N_API extern ssize_t s2n_client_hello_get_raw_message_length(struct s2n_client /** * 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. For SSLv2 ClientHello messages, the raw message contains only - * the cipher_specs, session_id and members portions of the hello message - * (see [RFC5246](https://tools.ietf.org/html/rfc5246#appendix-E.2)). To access other - * members, you may use s2n_connection_get_client_hello_version(), - * s2n_connection_get_client_protocol_version() and s2n_connection_get_session_id_length() - * accessors functions. + * zero-ed out. + * + * 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 @@ -1351,6 +1355,11 @@ S2N_API extern ssize_t s2n_client_hello_get_cipher_suites_length(struct s2n_clie /** * 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 diff --git a/contrib/restricted/aws/s2n/api/unstable/fingerprint.h b/contrib/restricted/aws/s2n/api/unstable/fingerprint.h new file mode 100644 index 0000000000..e8163b86e3 --- /dev/null +++ b/contrib/restricted/aws/s2n/api/unstable/fingerprint.h @@ -0,0 +1,76 @@ +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT 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 <s2n.h> + +/** + * @file fingerprint.h + * + * The following APIs enable applications to calculate fingerprints to + * identify ClientHellos. + * + * The fingerprinting APIs are currently considered unstable. They will be finalized + * and marked as stable after an initial customer integration and feedback. + */ + +typedef enum { + /* + * The current standard open source fingerprinting method. + * See https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967. + */ + S2N_FINGERPRINT_JA3, +} s2n_fingerprint_type; + +/** + * Calculates a fingerprint hash for a given ClientHello. + * + * Currently the only type supported is S2N_FINGERPRINT_JA3, which uses MD5 and + * requires at least 16 bytes of memory. + * + * @param ch The ClientHello to fingerprint. + * @param type The algorithm to use for the fingerprint. Currently only JA3 is supported. + * @param max_hash_size The maximum size of data that may be written to `hash`. + * If too small for the requested hash, an S2N_ERR_T_USAGE error will occur. + * @param hash The location that the requested hash will be written to. + * @param hash_size The actual size of the data written to `hash`. + * @param str_size The actual size of the full string associated with this hash. + * This size can be used to ensure that sufficient memory is provided for the + * output of `s2n_client_hello_get_fingerprint_string`. + * @returns S2N_SUCCESS on success, S2N_FAILURE on failure. + */ +int s2n_client_hello_get_fingerprint_hash(struct s2n_client_hello *ch, + s2n_fingerprint_type type, uint32_t max_hash_size, + uint8_t *hash, uint32_t *hash_size, uint32_t *str_size); + +/** + * Calculates a full, variable-length fingerprint string for a given ClientHello. + * + * Because the length of the string is variable and unknown until the string is + * calculated, `s2n_client_hello_get_fingerprint_hash` can be called first to + * determine `max_size` and ensure `output` is sufficiently large. + * + * @param ch The ClientHello to fingerprint. + * @param type The algorithm to use for the fingerprint. Currently only JA3 is supported. + * @param max_size The maximum size of data that may be written to `output`. + * If too small for the requested string, an S2N_ERR_T_USAGE error will occur. + * @param output The location that the requested string will be written to. + * @param output_size The actual size of the data written to `output`. + * @returns S2N_SUCCESS on success, S2N_FAILURE on failure. + */ +int s2n_client_hello_get_fingerprint_string(struct s2n_client_hello *ch, + s2n_fingerprint_type type, uint32_t max_size, + uint8_t *output, uint32_t *output_size); diff --git a/contrib/restricted/aws/s2n/stuffer/s2n_stuffer.c b/contrib/restricted/aws/s2n/stuffer/s2n_stuffer.c index ea46e0056f..92a47a3db4 100644 --- a/contrib/restricted/aws/s2n/stuffer/s2n_stuffer.c +++ b/contrib/restricted/aws/s2n/stuffer/s2n_stuffer.c @@ -76,6 +76,14 @@ int s2n_stuffer_init(struct s2n_stuffer *stuffer, struct s2n_blob *in) 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); diff --git a/contrib/restricted/aws/s2n/stuffer/s2n_stuffer.h b/contrib/restricted/aws/s2n/stuffer/s2n_stuffer.h index d283cb25f5..56088e2608 100644 --- a/contrib/restricted/aws/s2n/stuffer/s2n_stuffer.h +++ b/contrib/restricted/aws/s2n/stuffer/s2n_stuffer.h @@ -65,6 +65,7 @@ S2N_RESULT s2n_stuffer_validate(const struct s2n_stuffer *stuffer); /* Initialize and destroying stuffers */ int s2n_stuffer_init(struct s2n_stuffer *stuffer, struct s2n_blob *in); +int s2n_stuffer_init_written(struct s2n_stuffer *stuffer, struct s2n_blob *in); int s2n_stuffer_alloc(struct s2n_stuffer *stuffer, const uint32_t size); int s2n_stuffer_growable_alloc(struct s2n_stuffer *stuffer, const uint32_t size); int s2n_stuffer_free(struct s2n_stuffer *stuffer); diff --git a/contrib/restricted/aws/s2n/tls/s2n_client_hello.c b/contrib/restricted/aws/s2n/tls/s2n_client_hello.c index c4b67f85eb..f185292ea0 100644 --- a/contrib/restricted/aws/s2n/tls/s2n_client_hello.c +++ b/contrib/restricted/aws/s2n/tls/s2n_client_hello.c @@ -652,10 +652,24 @@ int s2n_client_hello_send(struct s2n_connection *conn) return S2N_SUCCESS; } -/* See http://www-archive.mozilla.org/projects/security/pki/nss/ssl/draft02.html 2.5 */ +/* + * 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_version will be SSLv2, but conn->client_protocol_version + * will likely be higher. + * + * 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_recv(struct s2n_connection *conn) { struct s2n_client_hello *client_hello = &conn->client_hello; + client_hello->sslv2 = true; + 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)); @@ -713,7 +727,7 @@ int s2n_sslv2_client_hello_recv(struct s2n_connection *conn) return 0; } -static int s2n_client_hello_get_parsed_extension(s2n_tls_extension_type extension_type, +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); diff --git a/contrib/restricted/aws/s2n/tls/s2n_client_hello.h b/contrib/restricted/aws/s2n/tls/s2n_client_hello.h index cef7f8175d..d7eaa749b8 100644 --- a/contrib/restricted/aws/s2n/tls/s2n_client_hello.h +++ b/contrib/restricted/aws/s2n/tls/s2n_client_hello.h @@ -43,6 +43,14 @@ struct s2n_client_hello { * issues a hello retry. */ unsigned int parsed : 1; + + /* + * SSLv2 ClientHellos have a different format. + * Cipher suites are each three bytes instead of two. + * And due to how s2n-tls parses the record, + * the raw_message will not contain the protocol version. + */ + unsigned int sslv2 : 1; }; int s2n_client_hello_free(struct s2n_client_hello *client_hello); @@ -55,5 +63,7 @@ ssize_t s2n_client_hello_get_raw_message(struct s2n_client_hello *ch, uint8_t *o ssize_t s2n_client_hello_get_cipher_suites_length(struct s2n_client_hello *ch); ssize_t s2n_client_hello_get_cipher_suites(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length); +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); ssize_t s2n_client_hello_get_extensions_length(struct s2n_client_hello *ch); ssize_t s2n_client_hello_get_extensions(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length); diff --git a/contrib/restricted/aws/s2n/tls/s2n_fingerprint.c b/contrib/restricted/aws/s2n/tls/s2n_fingerprint.c new file mode 100644 index 0000000000..db21d6ca6f --- /dev/null +++ b/contrib/restricted/aws/s2n/tls/s2n_fingerprint.c @@ -0,0 +1,322 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES 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/unstable/fingerprint.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_hash.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/s2n_client_hello.h" +#include "tls/s2n_crypto_constants.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_result.h" +#include "utils/s2n_safety.h" + +#define S2N_JA3_FIELD_DIV ',' +#define S2N_JA3_LIST_DIV '-' + +/* UINT16_MAX == 65535 */ +#define S2N_UINT16_STR_MAX_SIZE 5 + +/* See https://datatracker.ietf.org/doc/html/rfc8701 + * for an explanation of GREASE and lists of the GREASE values. + */ +static S2N_RESULT s2n_assert_grease_value(uint16_t val) +{ + uint8_t byte1 = val >> 8; + uint8_t byte2 = val & 0x00FF; + /* Both bytes of the GREASE values are identical */ + RESULT_ENSURE_EQ(byte1, byte2); + /* The GREASE value bytes all follow the format 0x[0-F]A. + * So 0x0A, 0x1A, 0x2A etc, up to 0xFA. */ + RESULT_ENSURE_EQ((byte1 | 0xF0), 0xFA); + return S2N_RESULT_OK; +} + +static bool s2n_is_grease_value(uint16_t val) +{ + return s2n_result_is_ok(s2n_assert_grease_value(val)); +} + +static S2N_RESULT s2n_fingerprint_hash_flush(struct s2n_hash_state *hash, struct s2n_stuffer *in) +{ + if (hash == NULL) { + /* If the buffer is full and needs to be flushed, but no hash was provided, + * then we have insufficient memory to complete the fingerprint. + * + * The application will need to provide a larger buffer. + */ + RESULT_BAIL(S2N_ERR_INSUFFICIENT_MEM_SIZE); + } + + uint32_t hash_data_len = s2n_stuffer_data_available(in); + uint8_t *hash_data = s2n_stuffer_raw_read(in, hash_data_len); + RESULT_ENSURE_REF(hash_data); + RESULT_GUARD_POSIX(s2n_hash_update(hash, hash_data, hash_data_len)); + RESULT_GUARD_POSIX(s2n_stuffer_wipe(in)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_fingerprint_write_char(struct s2n_stuffer *stuffer, + char c, struct s2n_hash_state *hash) +{ + if (s2n_stuffer_space_remaining(stuffer) < 1) { + RESULT_GUARD(s2n_fingerprint_hash_flush(hash, stuffer)); + } + RESULT_GUARD_POSIX(s2n_stuffer_write_char(stuffer, c)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_fingerprint_write_entry(struct s2n_stuffer *stuffer, + bool *is_list, uint16_t value, struct s2n_hash_state *hash) +{ + /* If we have already written at least one value for this field, + * then we are writing a list and need to prepend a list divider before + * writing the next value. + */ + RESULT_ENSURE_REF(is_list); + if (*is_list) { + RESULT_GUARD(s2n_fingerprint_write_char(stuffer, S2N_JA3_LIST_DIV, hash)); + } + *is_list = true; + + /* snprintf always appends a '\0' to the output, + * but that extra '\0' is not included in the return value */ + uint8_t entry[S2N_UINT16_STR_MAX_SIZE + 1] = { 0 }; + int written = snprintf((char *) entry, sizeof(entry), "%u", value); + RESULT_ENSURE_GT(written, 0); + RESULT_ENSURE_LTE(written, S2N_UINT16_STR_MAX_SIZE); + + if (s2n_stuffer_space_remaining(stuffer) < written) { + RESULT_GUARD(s2n_fingerprint_hash_flush(hash, stuffer)); + } + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(stuffer, entry, written)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_fingerprint_write_version(struct s2n_client_hello *ch, + struct s2n_stuffer *output, struct s2n_hash_state *hash) +{ + RESULT_ENSURE_REF(ch); + bool is_list = false; + uint16_t version = 0; + struct s2n_stuffer message = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&message, &ch->raw_message)); + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&message, &version)); + RESULT_GUARD(s2n_fingerprint_write_entry(output, &is_list, version, hash)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_fingerprint_write_ciphers(struct s2n_client_hello *ch, + struct s2n_stuffer *output, struct s2n_hash_state *hash) +{ + RESULT_ENSURE_REF(ch); + + bool cipher_found = false; + struct s2n_stuffer ciphers = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&ciphers, &ch->cipher_suites)); + while (s2n_stuffer_data_available(&ciphers)) { + uint16_t cipher = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&ciphers, &cipher)); + if (s2n_is_grease_value(cipher)) { + continue; + } + RESULT_GUARD(s2n_fingerprint_write_entry(output, &cipher_found, cipher, hash)); + } + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_fingerprint_write_extensions(struct s2n_client_hello *ch, + struct s2n_stuffer *output, struct s2n_hash_state *hash) +{ + RESULT_ENSURE_REF(ch); + + /* We have to use the raw extensions instead of the parsed extensions + * because s2n-tls both intentionally ignores any unknown extensions + * and reorders the extensions when parsing the list. + */ + struct s2n_stuffer extensions = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&extensions, &ch->extensions.raw)); + + bool extension_found = false; + while (s2n_stuffer_data_available(&extensions)) { + uint16_t extension = 0, extension_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&extensions, &extension)); + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&extensions, &extension_size)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_read(&extensions, extension_size)); + if (s2n_is_grease_value(extension)) { + continue; + } + RESULT_GUARD(s2n_fingerprint_write_entry(output, &extension_found, extension, hash)); + } + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_fingerprint_write_elliptic_curves(struct s2n_client_hello *ch, + struct s2n_stuffer *output, struct s2n_hash_state *hash) +{ + RESULT_ENSURE_REF(ch); + + s2n_parsed_extension *elliptic_curves_extension = NULL; + int result = s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SUPPORTED_GROUPS, + &ch->extensions, &elliptic_curves_extension); + if (result != S2N_SUCCESS) { + return S2N_RESULT_OK; + } + + struct s2n_stuffer elliptic_curves = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&elliptic_curves, + &elliptic_curves_extension->extension)); + + uint16_t count = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&elliptic_curves, &count)); + + bool curve_found = false; + while (s2n_stuffer_data_available(&elliptic_curves)) { + uint16_t curve = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&elliptic_curves, &curve)); + if (s2n_is_grease_value(curve)) { + continue; + } + RESULT_GUARD(s2n_fingerprint_write_entry(output, &curve_found, curve, hash)); + } + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_fingerprint_write_point_formats(struct s2n_client_hello *ch, + struct s2n_stuffer *output, struct s2n_hash_state *hash) +{ + RESULT_ENSURE_REF(ch); + + s2n_parsed_extension *point_formats_extension = NULL; + int result = s2n_client_hello_get_parsed_extension(S2N_EXTENSION_EC_POINT_FORMATS, + &ch->extensions, &point_formats_extension); + if (result != S2N_SUCCESS) { + return S2N_RESULT_OK; + } + + struct s2n_stuffer point_formats = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&point_formats, + &point_formats_extension->extension)); + + uint8_t count = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(&point_formats, &count)); + + bool format_found = false; + while (s2n_stuffer_data_available(&point_formats)) { + uint8_t format = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(&point_formats, &format)); + RESULT_GUARD(s2n_fingerprint_write_entry(output, &format_found, format, hash)); + } + return S2N_RESULT_OK; +} + +/* JA3 involves concatenating a set of fields from the ClientHello: + * SSLVersion,Cipher,SSLExtension,EllipticCurve,EllipticCurvePointFormat + * For example: + * "769,47-53-5-10-49161-49162-49171-49172-50-56-19-4,0-10-11,23-24-25,0" + * See https://github.com/salesforce/ja3 + */ +static S2N_RESULT s2n_fingerprint_ja3(struct s2n_client_hello *ch, + struct s2n_stuffer *output, uint32_t *output_size, struct s2n_hash_state *hash) +{ + RESULT_ENSURE_REF(ch); + RESULT_ENSURE(!ch->sslv2, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + RESULT_GUARD(s2n_fingerprint_write_version(ch, output, hash)); + RESULT_GUARD(s2n_fingerprint_write_char(output, S2N_JA3_FIELD_DIV, hash)); + RESULT_GUARD(s2n_fingerprint_write_ciphers(ch, output, hash)); + RESULT_GUARD(s2n_fingerprint_write_char(output, S2N_JA3_FIELD_DIV, hash)); + RESULT_GUARD(s2n_fingerprint_write_extensions(ch, output, hash)); + RESULT_GUARD(s2n_fingerprint_write_char(output, S2N_JA3_FIELD_DIV, hash)); + RESULT_GUARD(s2n_fingerprint_write_elliptic_curves(ch, output, hash)); + RESULT_GUARD(s2n_fingerprint_write_char(output, S2N_JA3_FIELD_DIV, hash)); + RESULT_GUARD(s2n_fingerprint_write_point_formats(ch, output, hash)); + + return S2N_RESULT_OK; +} + +int s2n_client_hello_get_fingerprint_hash(struct s2n_client_hello *ch, s2n_fingerprint_type type, + uint32_t max_hash_size, uint8_t *hash, uint32_t *hash_size, uint32_t *str_size) +{ + POSIX_ENSURE(type == S2N_FINGERPRINT_JA3, S2N_ERR_INVALID_ARGUMENT); + POSIX_ENSURE(max_hash_size >= MD5_DIGEST_LENGTH, S2N_ERR_INSUFFICIENT_MEM_SIZE); + POSIX_ENSURE_REF(hash); + POSIX_ENSURE_REF(hash_size); + POSIX_ENSURE_REF(str_size); + *hash_size = 0; + *str_size = 0; + + /* The maximum size of the JA3 string is variable and could theoretically + * be extremely large. However, we don't need enough memory to hold the full + * string when calculating a hash. We can calculate and add the JA3 string + * to the hash in chunks, similarly to how the TLS transcript hash is + * calculated by adding handshake messages to the hash as they become + * available. After a chunk is added to the hash, the string buffer can be + * wiped and reused for the next chunk. + * + * The size of this buffer was chosen fairly arbitrarily. + */ + uint8_t string_mem[50] = { 0 }; + struct s2n_blob string_blob = { 0 }; + struct s2n_stuffer string_stuffer = { 0 }; + POSIX_GUARD(s2n_blob_init(&string_blob, string_mem, sizeof(string_mem))); + POSIX_GUARD(s2n_stuffer_init(&string_stuffer, &string_blob)); + + /* JA3 uses an MD5 hash. + * The hash doesn't have to be cryptographically secure, + * so the weakness of MD5 shouldn't be a problem. + */ + DEFER_CLEANUP(struct s2n_hash_state md5_hash = { 0 }, s2n_hash_free); + POSIX_GUARD(s2n_hash_new(&md5_hash)); + if (s2n_is_in_fips_mode()) { + /* This hash is unrelated to TLS and does not affect FIPS */ + POSIX_GUARD(s2n_hash_allow_md5_for_fips(&md5_hash)); + } + POSIX_GUARD(s2n_hash_init(&md5_hash, S2N_HASH_MD5)); + + POSIX_GUARD_RESULT(s2n_fingerprint_ja3(ch, &string_stuffer, hash_size, &md5_hash)); + POSIX_GUARD_RESULT(s2n_fingerprint_hash_flush(&md5_hash, &string_stuffer)); + + uint64_t in_hash = 0; + POSIX_GUARD(s2n_hash_get_currently_in_hash_total(&md5_hash, &in_hash)); + POSIX_ENSURE_LTE(in_hash, UINT32_MAX); + *str_size = in_hash; + + POSIX_GUARD(s2n_hash_digest(&md5_hash, hash, MD5_DIGEST_LENGTH)); + *hash_size = MD5_DIGEST_LENGTH; + return S2N_SUCCESS; +} + +int s2n_client_hello_get_fingerprint_string(struct s2n_client_hello *ch, s2n_fingerprint_type type, + uint32_t max_size, uint8_t *output, uint32_t *output_size) +{ + POSIX_ENSURE(type == S2N_FINGERPRINT_JA3, S2N_ERR_INVALID_ARGUMENT); + POSIX_ENSURE(max_size > 0, S2N_ERR_INSUFFICIENT_MEM_SIZE); + POSIX_ENSURE_REF(output); + POSIX_ENSURE_REF(output_size); + *output_size = 0; + + struct s2n_blob output_blob = { 0 }; + struct s2n_stuffer output_stuffer = { 0 }; + POSIX_GUARD(s2n_blob_init(&output_blob, output, max_size)); + POSIX_GUARD(s2n_stuffer_init(&output_stuffer, &output_blob)); + + POSIX_GUARD_RESULT(s2n_fingerprint_ja3(ch, &output_stuffer, output_size, NULL)); + *output_size = s2n_stuffer_data_available(&output_stuffer); + + return S2N_SUCCESS; +} diff --git a/contrib/restricted/aws/s2n/tls/s2n_record_read.c b/contrib/restricted/aws/s2n/tls/s2n_record_read.c index ef5811847f..552b6b2aae 100644 --- a/contrib/restricted/aws/s2n/tls/s2n_record_read.c +++ b/contrib/restricted/aws/s2n/tls/s2n_record_read.c @@ -40,13 +40,39 @@ int s2n_sslv2_record_header_parse( POSIX_GUARD(s2n_stuffer_read_uint16(in, fragment_length)); - /* Adjust to account for the 3 bytes of payload data we consumed in the header */ + /* The SSLv2 header is only a 2 byte record length (technically 3 bytes if + * padding is included, but s2n-tls assumes no padding). + * See https://www.ietf.org/archive/id/draft-hickman-netscape-ssl-00.txt. + * + * So by reading 5 bytes for a standard header we have also read the first + * 3 bytes of the record payload. s2n-tls only supports SSLv2 ClientHellos, + * so we assume that those 3 bytes are the first two fields of the + * SSLv2 ClientHello. + */ + + /* Because we already read 3 bytes of the record payload while trying to + * read a standard header, we need to adjust the length so that we only + * try to read the remainder of the record payload. + */ POSIX_ENSURE_GTE(*fragment_length, 3); *fragment_length -= 3; + /* + * 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(in, record_type)); - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + /* + * 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_recv. + */ + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; POSIX_GUARD(s2n_stuffer_read_bytes(in, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); *client_protocol_version = (protocol_version[0] * 10) + protocol_version[1]; diff --git a/contrib/restricted/aws/s2n/utils/s2n_init.c b/contrib/restricted/aws/s2n/utils/s2n_init.c index 0fc2849143..8f1b66e0f9 100644 --- a/contrib/restricted/aws/s2n/utils/s2n_init.c +++ b/contrib/restricted/aws/s2n/utils/s2n_init.c @@ -42,6 +42,12 @@ int s2n_disable_atexit(void) 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 diff --git a/contrib/restricted/aws/s2n/utils/s2n_random.c b/contrib/restricted/aws/s2n/utils/s2n_random.c index 97f8197074..fa6ec242f8 100644 --- a/contrib/restricted/aws/s2n/utils/s2n_random.c +++ b/contrib/restricted/aws/s2n/utils/s2n_random.c @@ -62,6 +62,11 @@ struct s2n_rand_state { bool drbgs_initialized; }; +/* Key which will control per-thread freeing of drbg memory */ +static pthread_key_t s2n_per_thread_rand_state_key; +/* Needed to ensure key is initialized only once */ +static pthread_once_t s2n_per_thread_rand_state_key_once = PTHREAD_ONCE_INIT; + static __thread struct s2n_rand_state s2n_per_thread_rand_state = { .cached_fork_generation_number = 0, .public_drbg = { 0 }, @@ -130,6 +135,18 @@ S2N_RESULT s2n_get_mix_entropy(struct s2n_blob *blob) return S2N_RESULT_OK; } +static void s2n_drbg_destructor(void *_unused_argument) +{ + (void) _unused_argument; + + s2n_result_ignore(s2n_rand_cleanup_thread()); +} + +static void s2n_drbg_make_rand_state_key(void) +{ + (void) pthread_key_create(&s2n_per_thread_rand_state_key, s2n_drbg_destructor); +} + static S2N_RESULT s2n_init_drbgs(void) { uint8_t s2n_public_drbg[] = "s2n public drbg"; @@ -139,9 +156,13 @@ static S2N_RESULT s2n_init_drbgs(void) struct s2n_blob private = { 0 }; RESULT_GUARD_POSIX(s2n_blob_init(&private, s2n_private_drbg, sizeof(s2n_private_drbg))); + RESULT_ENSURE(pthread_once(&s2n_per_thread_rand_state_key_once, s2n_drbg_make_rand_state_key) == 0, S2N_ERR_DRBG); + RESULT_GUARD(s2n_drbg_instantiate(&s2n_per_thread_rand_state.public_drbg, &public, S2N_AES_128_CTR_NO_DF_PR)); RESULT_GUARD(s2n_drbg_instantiate(&s2n_per_thread_rand_state.private_drbg, &private, S2N_AES_256_CTR_NO_DF_PR)); + RESULT_ENSURE(pthread_setspecific(s2n_per_thread_rand_state_key, &s2n_per_thread_rand_state) == 0, S2N_ERR_DRBG); + s2n_per_thread_rand_state.drbgs_initialized = true; return S2N_RESULT_OK; |