/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES 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 "s2n_test.h"
#include "testlib/s2n_sslv2_client_hello.h"
#include "testlib/s2n_testlib.h"
#include "tls/s2n_tls.h"

/* SSLv2 == 0x0200 == 512 */
#define S2N_JA3_SSLV2_VAL 512

/* clang-format off */
#define S2N_TEST_CLIENT_HELLO_AFTER_VERSION \
    /* random */ \
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
    /* session id */ \
    0x00
/* clang-format on */

#define S2N_TEST_CLIENT_HELLO_AFTER_CIPHERS \
    /* legacy compression methods */        \
    0x01, 0x00

/* This macro currently assumes that the message size is only one byte (<=255). */
#define S2N_INIT_CLIENT_HELLO(name, ...)                 \
    uint8_t _##name##_message[] = { __VA_ARGS__ };       \
    EXPECT_TRUE(sizeof(_##name##_message) <= UINT8_MAX); \
    uint8_t name[] = {                                   \
        TLS_CLIENT_HELLO,                                \
        0x00, 0x00, sizeof(_##name##_message),           \
        __VA_ARGS__                                      \
    }

typedef enum {
    S2N_CH_FROM_IO = 0,
    S2N_CH_FROM_RAW,
} s2n_ch_source;

static S2N_RESULT s2n_validate_ja3_str_list(struct s2n_stuffer *input)
{
    uint32_t skipped = 0;
    size_t count = 0;
    DEFER_CLEANUP(struct s2n_stuffer list = { 0 }, s2n_stuffer_free);
    RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&list, 0));
    RESULT_GUARD_POSIX(s2n_stuffer_read_token(input, &list, ','));
    while (s2n_stuffer_data_available(&list)) {
        RESULT_GUARD_POSIX(s2n_stuffer_skip_to_char(&list, '-'));
        RESULT_GUARD_POSIX(s2n_stuffer_skip_expected_char(&list, '-',
                0, 1, &skipped));
        count++;
    }
    RESULT_ENSURE_GT(count, 1);
    return S2N_RESULT_OK;
}

static S2N_RESULT s2n_validate_ja3_str(uint8_t *ja3, uint32_t ja3_size,
        const char *expected_version)
{
    struct s2n_blob input_blob = { 0 };
    struct s2n_stuffer input = { 0 };
    RESULT_GUARD_POSIX(s2n_blob_init(&input_blob, ja3, ja3_size));
    RESULT_GUARD_POSIX(s2n_stuffer_init(&input, &input_blob));
    RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&input, ja3_size));

    /* Expect the provided version */
    RESULT_GUARD_POSIX(s2n_stuffer_read_expected_str(&input, expected_version));
    RESULT_GUARD_POSIX(s2n_stuffer_read_expected_str(&input, ","));

    /* Expect at least one entry in the cipher list */
    RESULT_GUARD(s2n_validate_ja3_str_list(&input));

    /* Expect at least one entry in the extensions list */
    RESULT_GUARD(s2n_validate_ja3_str_list(&input));

    /* Expect at least one entry in the curves list */
    RESULT_GUARD(s2n_validate_ja3_str_list(&input));

    /* Expect only one point format: 0 / uncompressed */
    RESULT_GUARD_POSIX(s2n_stuffer_read_expected_str(&input, "0"));

    RESULT_ENSURE_EQ(s2n_stuffer_data_available(&input), 0);
    return S2N_RESULT_OK;
}

static S2N_RESULT s2n_client_hello_from_source(struct s2n_client_hello **client_hello,
        struct s2n_connection *server, uint8_t *input, uint32_t input_size, s2n_ch_source source)
{
    switch (source) {
        case S2N_CH_FROM_IO:
            RESULT_GUARD_POSIX(s2n_connection_wipe(server));
            RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&server->handshake.io,
                    input + TLS_HANDSHAKE_HEADER_LENGTH, input_size - TLS_HANDSHAKE_HEADER_LENGTH));
            RESULT_GUARD_POSIX(s2n_client_hello_recv(server));
            *client_hello = s2n_connection_get_client_hello(server);
            RESULT_ENSURE_REF(*client_hello);
            break;
        case S2N_CH_FROM_RAW:
            *client_hello = s2n_client_hello_parse_message(input, input_size);
            RESULT_GUARD_PTR(*client_hello);
            break;
    }
    return S2N_RESULT_OK;
}

int main(int argc, char **argv)
{
    BEGIN_TEST();

    uint8_t output_mem[500] = { 0 };
    s2n_ch_source sources[] = {
        S2N_CH_FROM_IO,
        S2N_CH_FROM_RAW,
    };

    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_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_cipher_preferences(config, "test_all"));

    uint8_t empty_md5_hash[MD5_DIGEST_LENGTH] = { 0 };
    DEFER_CLEANUP(struct s2n_hash_state md5_hash = { 0 }, s2n_hash_free);
    EXPECT_SUCCESS(s2n_hash_new(&md5_hash));
    if (s2n_is_in_fips_mode()) {
        EXPECT_SUCCESS(s2n_hash_allow_md5_for_fips(&md5_hash));
    }
    EXPECT_SUCCESS(s2n_hash_init(&md5_hash, S2N_HASH_MD5));
    EXPECT_SUCCESS(s2n_hash_digest(&md5_hash, empty_md5_hash, MD5_DIGEST_LENGTH));

    /* Test: safety / input validation */
    {
        DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER),
                s2n_connection_ptr_free);
        struct s2n_client_hello *client_hello = &server->client_hello;
        uint32_t output_size = 0, str_size = 0;

        /* Valid client hello required */
        EXPECT_FAILURE_WITH_ERRNO(
                s2n_client_hello_get_fingerprint_string(NULL, S2N_FINGERPRINT_JA3,
                        sizeof(output_mem), output_mem, &output_size),
                S2N_ERR_INVALID_ARGUMENT);
        EXPECT_FAILURE_WITH_ERRNO(
                s2n_client_hello_get_fingerprint_hash(NULL, S2N_FINGERPRINT_JA3,
                        sizeof(output_mem), output_mem, &output_size, &str_size),
                S2N_ERR_INVALID_ARGUMENT);

        /* Valid output buffer required */
        EXPECT_FAILURE_WITH_ERRNO(
                s2n_client_hello_get_fingerprint_string(client_hello, S2N_FINGERPRINT_JA3,
                        sizeof(output_mem), NULL, &output_size),
                S2N_ERR_INVALID_ARGUMENT);
        EXPECT_FAILURE_WITH_ERRNO(
                s2n_client_hello_get_fingerprint_hash(client_hello, S2N_FINGERPRINT_JA3,
                        sizeof(output_mem), NULL, &output_size, &str_size),
                S2N_ERR_INVALID_ARGUMENT);
        EXPECT_FAILURE_WITH_ERRNO(
                s2n_client_hello_get_fingerprint_string(client_hello, S2N_FINGERPRINT_JA3,
                        0, NULL, &output_size),
                S2N_ERR_INSUFFICIENT_MEM_SIZE);
        EXPECT_FAILURE_WITH_ERRNO(
                s2n_client_hello_get_fingerprint_hash(client_hello, S2N_FINGERPRINT_JA3,
                        0, NULL, &output_size, &str_size),
                S2N_ERR_INSUFFICIENT_MEM_SIZE);

        /* Valid size ptr required */
        EXPECT_FAILURE_WITH_ERRNO(
                s2n_client_hello_get_fingerprint_string(client_hello, S2N_FINGERPRINT_JA3,
                        sizeof(output_mem), output_mem, NULL),
                S2N_ERR_INVALID_ARGUMENT);
        EXPECT_FAILURE_WITH_ERRNO(
                s2n_client_hello_get_fingerprint_hash(client_hello, S2N_FINGERPRINT_JA3,
                        sizeof(output_mem), output_mem, NULL, &str_size),
                S2N_ERR_INVALID_ARGUMENT);
        EXPECT_FAILURE_WITH_ERRNO(
                s2n_client_hello_get_fingerprint_hash(client_hello, S2N_FINGERPRINT_JA3,
                        sizeof(output_mem), output_mem, &output_size, NULL),
                S2N_ERR_INVALID_ARGUMENT);

        /* Only JA3 currently supported */
        EXPECT_FAILURE_WITH_ERRNO(
                s2n_client_hello_get_fingerprint_string(client_hello, S2N_FINGERPRINT_JA3 + 1,
                        sizeof(output_mem), output_mem, &output_size),
                S2N_ERR_INVALID_ARGUMENT);
        EXPECT_FAILURE_WITH_ERRNO(
                s2n_client_hello_get_fingerprint_hash(client_hello, S2N_FINGERPRINT_JA3 + 1,
                        sizeof(output_mem), output_mem, &output_size, &str_size),
                S2N_ERR_INVALID_ARGUMENT);
    };

    /* Test: ja3 string */
    {
        /* Test: basic case */
        {
            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(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_FALSE(client_hello->sslv2);

            uint32_t output_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello,
                    S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size));

            /* Expect valid ja3.
             * TLS1.2 == 0x0303 == 771 */
            EXPECT_OK(s2n_validate_ja3_str(output_mem, output_size, "771"));
        };

        /* Test: SSLv2 not supported */
        {
            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, "test_all"));

            /* SSLv2 ClientHellos are used by clients as a backwards-compatible attempt
             * to communicate with servers that don't support later versions.
             * s2n-tls DOES support later versions, so requires that SSLv2 ClientHellos
             * advertise a higher version.
             *
             * This version negotiation is done when processing the record,
             * not when processing the actual ClientHello message.
             * So we need to set the versions manually.
             */
            server->client_hello_version = S2N_SSLv2;
            server->client_protocol_version = S2N_TLS12;

            uint8_t sslv2_client_hello[] = {
                SSLv2_CLIENT_HELLO_PREFIX,
                SSLv2_CLIENT_HELLO_CIPHER_SUITES,
                SSLv2_CLIENT_HELLO_CHALLENGE,
            };
            EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server->handshake.io,
                    sslv2_client_hello, sizeof(sslv2_client_hello)));
            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_TRUE(client_hello->sslv2);

            uint32_t output_size = 0;
            EXPECT_FAILURE_WITH_ERRNO(
                    s2n_client_hello_get_fingerprint_string(client_hello,
                            S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size),
                    S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED);
            EXPECT_EQUAL(output_size, 0);
        };

        /* Test: single entry lists */
        for (size_t i = 0; i < s2n_array_len(sources); i++) {
            s2n_ch_source source = sources[i];

            S2N_INIT_CLIENT_HELLO(client_hello_bytes,
                    /* protocol version */
                    0x03, 0x02,
                    S2N_TEST_CLIENT_HELLO_AFTER_VERSION,
                    /* cipher suites size */
                    0x00, 0x02,
                    /* cipher suites */
                    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                    S2N_TEST_CLIENT_HELLO_AFTER_CIPHERS,
                    /* extensions size */
                    0x00, 0x08,
                    /* extension: supported groups */
                    0x00, TLS_EXTENSION_SUPPORTED_GROUPS, 0x00, 0x04,
                    0x00, 0x02, 0x00, TLS_EC_CURVE_SECP_256_R1);
            const uint8_t expected_ja3[] = "770,49199,10,23,";
            size_t expected_ja3_size = strlen((const char *) expected_ja3);

            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, "test_all"));

            DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL,
                    s2n_client_hello_free);
            EXPECT_OK(s2n_client_hello_from_source(&client_hello, server,
                    client_hello_bytes, sizeof(client_hello_bytes), source));

            uint32_t output_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello,
                    S2N_FINGERPRINT_JA3, expected_ja3_size, output_mem, &output_size));
            EXPECT_EQUAL(output_size, expected_ja3_size);
            EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size);
        };

        /* Test: missing fields
         *
         * We have to provide a protocol version and one cipher suite
         * in order for the ClientHello to be considered valid, but all
         * other fields can be empty.
         */
        for (size_t i = 0; i < s2n_array_len(sources); i++) {
            s2n_ch_source source = sources[i];

            S2N_INIT_CLIENT_HELLO(client_hello_bytes,
                    /* protocol version */
                    0x03, 0x01,
                    S2N_TEST_CLIENT_HELLO_AFTER_VERSION,
                    /* cipher suites size */
                    0x00, 0x02,
                    /* cipher suites */
                    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                    S2N_TEST_CLIENT_HELLO_AFTER_CIPHERS,
                    /* extensions size */
                    0x00, 0x00);
            const uint8_t expected_ja3[] = "769,49199,,,";
            size_t expected_ja3_size = strlen((const char *) expected_ja3);

            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, "test_all"));

            DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL,
                    s2n_client_hello_free);
            EXPECT_OK(s2n_client_hello_from_source(&client_hello, server,
                    client_hello_bytes, sizeof(client_hello_bytes), source));

            uint32_t output_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello,
                    S2N_FINGERPRINT_JA3, expected_ja3_size, output_mem, &output_size));
            EXPECT_EQUAL(output_size, expected_ja3_size);
            EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size);
        };

        /* Test: fails if insufficient memory */
        {
            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(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);

            uint32_t output_size = 0;
            for (size_t i = 0; i < 10; i++) {
                EXPECT_FAILURE_WITH_ERRNO(
                        s2n_client_hello_get_fingerprint_string(client_hello,
                                S2N_FINGERPRINT_JA3, i, output_mem, &output_size),
                        S2N_ERR_INSUFFICIENT_MEM_SIZE);
                EXPECT_EQUAL(output_size, 0);
            }
        };

        /* Test: grease values ignored */
        for (size_t i = 0; i < s2n_array_len(sources); i++) {
            s2n_ch_source source = sources[i];

            const uint8_t grease_value = 0x0A;
            S2N_INIT_CLIENT_HELLO(client_hello_bytes,
                    /* protocol version */
                    0x03, 0x02,
                    S2N_TEST_CLIENT_HELLO_AFTER_VERSION,
                    /* cipher suites size */
                    0x00, 0x04,
                    /* cipher suites */
                    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                    grease_value, grease_value,
                    S2N_TEST_CLIENT_HELLO_AFTER_CIPHERS,
                    /* extensions size */
                    0x00, 0x0E,
                    /* extension: grease */
                    grease_value, grease_value, 0x00, 0x00,
                    /* extension: supported groups */
                    0x00, TLS_EXTENSION_SUPPORTED_GROUPS, 0x00, 0x06,
                    0x00, 0x04, 0x00, TLS_EC_CURVE_SECP_256_R1, grease_value, grease_value);
            const uint8_t expected_ja3[] = "770,49199,10,23,";
            size_t expected_ja3_size = strlen((const char *) expected_ja3);

            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, "test_all"));

            DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL,
                    s2n_client_hello_free);
            EXPECT_OK(s2n_client_hello_from_source(&client_hello, server,
                    client_hello_bytes, sizeof(client_hello_bytes), source));

            uint32_t output_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello,
                    S2N_FINGERPRINT_JA3, expected_ja3_size, output_mem, &output_size));
            EXPECT_EQUAL(output_size, expected_ja3_size);
            EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size);
        };
    };

    /* Test: ja3 hash */
    {
        /* Test: basic case */
        {
            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(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);

            /* Assert reasonable result */
            uint32_t output_size = 0, str_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_hash(client_hello,
                    S2N_FINGERPRINT_JA3, MD5_DIGEST_LENGTH, output_mem, &output_size, &str_size));
            EXPECT_TRUE(str_size > MD5_DIGEST_LENGTH);
            EXPECT_EQUAL(output_size, MD5_DIGEST_LENGTH);
            EXPECT_BYTEARRAY_NOT_EQUAL(empty_md5_hash, output_mem, output_size);

            /* Assert same result when given same inputs again */
            uint8_t output_mem_2[MD5_DIGEST_LENGTH] = { 0 };
            uint32_t output_size_2 = 0, str_size_2 = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_hash(client_hello,
                    S2N_FINGERPRINT_JA3, MD5_DIGEST_LENGTH, output_mem_2,
                    &output_size_2, &str_size_2));
            EXPECT_EQUAL(str_size, str_size_2);
            EXPECT_EQUAL(output_size, output_size_2);
            EXPECT_BYTEARRAY_EQUAL(output_mem, output_mem_2, output_size_2);

            /* Assert length for full ja3 string is correct */
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello,
                    S2N_FINGERPRINT_JA3, str_size, output_mem, &output_size));
            EXPECT_FAILURE_WITH_ERRNO(
                    s2n_client_hello_get_fingerprint_string(client_hello,
                            S2N_FINGERPRINT_JA3, str_size - 1, output_mem, &output_size),
                    S2N_ERR_INSUFFICIENT_MEM_SIZE);
        };

        /* Test: fails if insufficient memory */
        {
            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(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);

            uint32_t output_size = 0, str_size = 0;
            for (size_t i = 0; i < MD5_DIGEST_LENGTH; i++) {
                EXPECT_FAILURE_WITH_ERRNO(
                        s2n_client_hello_get_fingerprint_hash(client_hello,
                                S2N_FINGERPRINT_JA3, i, output_mem, &output_size, &str_size),
                        S2N_ERR_INSUFFICIENT_MEM_SIZE);
            }
            EXPECT_EQUAL(output_size, 0);
        };
    };

    /* Test: Known values
     *
     * No definitive source exists for JA3 test vectors.
     * We sample some test values used by other implementations.
     */
    for (size_t i = 0; i < s2n_array_len(sources); i++) {
        s2n_ch_source source = sources[i];

        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));

        /* Known value from Java implementation:
         * https://github.com/lafaspot/ja3_4java/blob/d605ea2b51c1024eb9056568aac68c2d26011c4f/src/test/resources/openssl-ssl3.bin
         */
        {
            uint8_t raw_client_hello[135] = {
                0x01, 0x00, 0x00, 0x83,
                0x03, 0x00, 0x54, 0x3D, 0xD2, 0xA9, 0xB2, 0xD7, 0x59, 0xF7, 0xC4,
                0xCF, 0x64, 0x30, 0xEB, 0xCC, 0xF7, 0x36, 0x58, 0x9B, 0x78, 0xB8,
                0x9D, 0xB5, 0x0D, 0x59, 0xAF, 0x82, 0xA6, 0xC0, 0xAC, 0xFB, 0xA0,
                0xB1, 0x00, 0x00, 0x5C, 0xC0, 0x14, 0xC0, 0x0A, 0x00, 0x39, 0x00,
                0x38, 0x00, 0x88, 0x00, 0x87, 0xC0, 0x0F, 0xC0, 0x05, 0x00, 0x35,
                0x00, 0x84, 0xC0, 0x12, 0xC0, 0x08, 0x00, 0x16, 0x00, 0x13, 0xC0,
                0x0D, 0xC0, 0x03, 0x00, 0x0A, 0xC0, 0x13, 0xC0, 0x09, 0x00, 0x33,
                0x00, 0x32, 0x00, 0x9A, 0x00, 0x99, 0x00, 0x45, 0x00, 0x44, 0xC0,
                0x0E, 0xC0, 0x04, 0x00, 0x2F, 0x00, 0x96, 0x00, 0x41, 0x00, 0x07,
                0xC0, 0x11, 0xC0, 0x07, 0xC0, 0x0C, 0xC0, 0x02, 0x00, 0x05, 0x00,
                0x04, 0x00, 0x15, 0x00, 0x12, 0x00, 0x09, 0x00, 0x14, 0x00, 0x11,
                0x00, 0x08, 0x00, 0x06, 0x00, 0x03, 0x00, 0xFF, 0x01, 0x00
            };
            const char expected_ja3[] = "768,49172-49162-57-56-136-135-49167-"
                                        "49157-53-132-49170-49160-22-19-49165-49155-"
                                        "10-49171-49161-51-50-154-153-69-68-49166-"
                                        "49156-47-150-65-7-49169-49159-49164-49154-"
                                        "5-4-21-18-9-20-17-8-6-3-255,,,";

            DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL,
                    s2n_client_hello_free);
            EXPECT_OK(s2n_client_hello_from_source(&client_hello, server,
                    raw_client_hello, sizeof(raw_client_hello), source));

            uint32_t output_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello,
                    S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size));
            EXPECT_EQUAL(strlen(expected_ja3), output_size);
            EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size);
        };

        /* Known value from Java implementation:
         * https://github.com/lafaspot/ja3_4java/blob/d605ea2b51c1024eb9056568aac68c2d26011c4f/src/test/resources/openssl-ssl3.bin
         */
        {
            uint8_t raw_client_hello[164] = {
                0x01, 0x00, 0x00, 0xA0,
                0x03, 0x01, 0x54, 0x3D, 0xD2, 0xDD, 0x48, 0xF5, 0x17, 0xCA, 0x9A,
                0x93, 0xB1, 0xE5, 0x99, 0xF0, 0x19, 0xFD, 0xEC, 0xE7, 0x04, 0xA2,
                0x3E, 0x86, 0xC1, 0xDC, 0xAC, 0x58, 0x84, 0x27, 0xAB, 0xBA, 0xDD,
                0xF2, 0x00, 0x00, 0x5C, 0xC0, 0x14, 0xC0, 0x0A, 0x00, 0x39, 0x00,
                0x38, 0x00, 0x88, 0x00, 0x87, 0xC0, 0x0F, 0xC0, 0x05, 0x00, 0x35,
                0x00, 0x84, 0xC0, 0x12, 0xC0, 0x08, 0x00, 0x16, 0x00, 0x13, 0xC0,
                0x0D, 0xC0, 0x03, 0x00, 0x0A, 0xC0, 0x13, 0xC0, 0x09, 0x00, 0x33,
                0x00, 0x32, 0x00, 0x9A, 0x00, 0x99, 0x00, 0x45, 0x00, 0x44, 0xC0,
                0x0E, 0xC0, 0x04, 0x00, 0x2F, 0x00, 0x96, 0x00, 0x41, 0x00, 0x07,
                0xC0, 0x11, 0xC0, 0x07, 0xC0, 0x0C, 0xC0, 0x02, 0x00, 0x05, 0x00,
                0x04, 0x00, 0x15, 0x00, 0x12, 0x00, 0x09, 0x00, 0x14, 0x00, 0x11,
                0x00, 0x08, 0x00, 0x06, 0x00, 0x03, 0x00, 0xFF, 0x01, 0x00, 0x00,
                0x1B, 0x00, 0x0B, 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0A,
                0x00, 0x06, 0x00, 0x04, 0x00, 0x18, 0x00, 0x17, 0x00, 0x23, 0x00,
                0x00, 0x00, 0x0F, 0x00, 0x01, 0x01
            };
            const char expected_ja3[] = "769,49172-49162-57-56-136-135-49167-"
                                        "49157-53-132-49170-49160-22-19-49165-49155-"
                                        "10-49171-49161-51-50-154-153-69-68-49166-"
                                        "49156-47-150-65-7-49169-49159-49164-49154-"
                                        "5-4-21-18-9-20-17-8-6-3-255,11-10-35-15,"
                                        "24-23,0-1-2";

            DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL,
                    s2n_client_hello_free);
            EXPECT_OK(s2n_client_hello_from_source(&client_hello, server,
                    raw_client_hello, sizeof(raw_client_hello), source));

            uint32_t output_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello,
                    S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size));
            EXPECT_EQUAL(strlen(expected_ja3), output_size);
            EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size);
        };

        /* Known value from Java implementation:
         * https://github.com/lafaspot/ja3_4java/blob/d605ea2b51c1024eb9056568aac68c2d26011c4f/src/test/resources/openssl-tls1_1.bin
         */
        {
            uint8_t raw_client_hello[164] = {
                0x01, 0x00, 0x00, 0xA0,
                0x03, 0x02, 0x54, 0x3D, 0xD2, 0xED, 0x90, 0x7E, 0x47, 0xD0, 0x08,
                0x6F, 0x34, 0xBE, 0xE2, 0xC5, 0x2D, 0xD6, 0xCC, 0xD8, 0xDE, 0x63,
                0xBA, 0x93, 0x87, 0xF5, 0xE8, 0x10, 0xB0, 0x9D, 0x9D, 0x49, 0xB3,
                0x80, 0x00, 0x00, 0x5C, 0xC0, 0x14, 0xC0, 0x0A, 0x00, 0x39, 0x00,
                0x38, 0x00, 0x88, 0x00, 0x87, 0xC0, 0x0F, 0xC0, 0x05, 0x00, 0x35,
                0x00, 0x84, 0xC0, 0x12, 0xC0, 0x08, 0x00, 0x16, 0x00, 0x13, 0xC0,
                0x0D, 0xC0, 0x03, 0x00, 0x0A, 0xC0, 0x13, 0xC0, 0x09, 0x00, 0x33,
                0x00, 0x32, 0x00, 0x9A, 0x00, 0x99, 0x00, 0x45, 0x00, 0x44, 0xC0,
                0x0E, 0xC0, 0x04, 0x00, 0x2F, 0x00, 0x96, 0x00, 0x41, 0x00, 0x07,
                0xC0, 0x11, 0xC0, 0x07, 0xC0, 0x0C, 0xC0, 0x02, 0x00, 0x05, 0x00,
                0x04, 0x00, 0x15, 0x00, 0x12, 0x00, 0x09, 0x00, 0x14, 0x00, 0x11,
                0x00, 0x08, 0x00, 0x06, 0x00, 0x03, 0x00, 0xFF, 0x01, 0x00, 0x00,
                0x1B, 0x00, 0x0B, 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0A,
                0x00, 0x06, 0x00, 0x04, 0x00, 0x18, 0x00, 0x17, 0x00, 0x23, 0x00,
                0x00, 0x00, 0x0F, 0x00, 0x01, 0x01
            };
            const char expected_ja3[] = "770,49172-49162-57-56-136-135-49167-"
                                        "49157-53-132-49170-49160-22-19-49165-49155-"
                                        "10-49171-49161-51-50-154-153-69-68-49166-"
                                        "49156-47-150-65-7-49169-49159-49164-49154-"
                                        "5-4-21-18-9-20-17-8-6-3-255,11-10-35-15,"
                                        "24-23,0-1-2";

            DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL,
                    s2n_client_hello_free);
            EXPECT_OK(s2n_client_hello_from_source(&client_hello, server,
                    raw_client_hello, sizeof(raw_client_hello), source));

            uint32_t output_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello,
                    S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size));
            EXPECT_EQUAL(strlen(expected_ja3), output_size);
            EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size);
        };

        /* Known value from Java implementation:
         * https://github.com/lafaspot/ja3_4java/blob/d605ea2b51c1024eb9056568aac68c2d26011c4f/src/test/resources/openssl-tls1_2.bin
         */
        {
            uint8_t raw_client_hello[258] = {
                0x01, 0x00, 0x00, 0xFE,
                0x03, 0x03, 0x54, 0x3D, 0xD3, 0x28, 0x32, 0x83, 0x69, 0x2D, 0x85,
                0xF9, 0x41, 0x6B, 0x5C, 0xCC, 0x65, 0xD2, 0xAA, 0xFC, 0xA4, 0x5C,
                0x65, 0x30, 0xB3, 0xC6, 0xEA, 0xFB, 0xF6, 0xD3, 0x71, 0xB6, 0xA0,
                0x15, 0x00, 0x00, 0x94, 0xC0, 0x30, 0xC0, 0x2C, 0xC0, 0x28, 0xC0,
                0x24, 0xC0, 0x14, 0xC0, 0x0A, 0x00, 0xA3, 0x00, 0x9F, 0x00, 0x6B,
                0x00, 0x6A, 0x00, 0x39, 0x00, 0x38, 0x00, 0x88, 0x00, 0x87, 0xC0,
                0x32, 0xC0, 0x2E, 0xC0, 0x2A, 0xC0, 0x26, 0xC0, 0x0F, 0xC0, 0x05,
                0x00, 0x9D, 0x00, 0x3D, 0x00, 0x35, 0x00, 0x84, 0xC0, 0x12, 0xC0,
                0x08, 0x00, 0x16, 0x00, 0x13, 0xC0, 0x0D, 0xC0, 0x03, 0x00, 0x0A,
                0xC0, 0x2F, 0xC0, 0x2B, 0xC0, 0x27, 0xC0, 0x23, 0xC0, 0x13, 0xC0,
                0x09, 0x00, 0xA2, 0x00, 0x9E, 0x00, 0x67, 0x00, 0x40, 0x00, 0x33,
                0x00, 0x32, 0x00, 0x9A, 0x00, 0x99, 0x00, 0x45, 0x00, 0x44, 0xC0,
                0x31, 0xC0, 0x2D, 0xC0, 0x29, 0xC0, 0x25, 0xC0, 0x0E, 0xC0, 0x04,
                0x00, 0x9C, 0x00, 0x3C, 0x00, 0x2F, 0x00, 0x96, 0x00, 0x41, 0x00,
                0x07, 0xC0, 0x11, 0xC0, 0x07, 0xC0, 0x0C, 0xC0, 0x02, 0x00, 0x05,
                0x00, 0x04, 0x00, 0x15, 0x00, 0x12, 0x00, 0x09, 0x00, 0x14, 0x00,
                0x11, 0x00, 0x08, 0x00, 0x06, 0x00, 0x03, 0x00, 0xFF, 0x01, 0x00,
                0x00, 0x41, 0x00, 0x0B, 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00,
                0x0A, 0x00, 0x06, 0x00, 0x04, 0x00, 0x18, 0x00, 0x17, 0x00, 0x23,
                0x00, 0x00, 0x00, 0x0D, 0x00, 0x22, 0x00, 0x20, 0x06, 0x01, 0x06,
                0x02, 0x06, 0x03, 0x05, 0x01, 0x05, 0x02, 0x05, 0x03, 0x04, 0x01,
                0x04, 0x02, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x02,
                0x01, 0x02, 0x02, 0x02, 0x03, 0x01, 0x01, 0x00, 0x0F, 0x00, 0x01,
                0x01
            };
            const char expected_ja3[] = "771,49200-49196-49192-49188-49172-49162-"
                                        "163-159-107-106-57-56-136-135-49202-49198-"
                                        "49194-49190-49167-49157-157-61-53-132-"
                                        "49170-49160-22-19-49165-49155-10-49199-"
                                        "49195-49191-49187-49171-49161-162-158-103-"
                                        "64-51-50-154-153-69-68-49201-49197-49193-"
                                        "49189-49166-49156-156-60-47-150-65-7-"
                                        "49169-49159-49164-49154-5-4-21-18-9-20-"
                                        "17-8-6-3-255,11-10-35-13-15,24-23,0-1-2";

            DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL,
                    s2n_client_hello_free);
            EXPECT_OK(s2n_client_hello_from_source(&client_hello, server,
                    raw_client_hello, sizeof(raw_client_hello), source));

            uint32_t output_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello,
                    S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size));
            EXPECT_EQUAL(strlen(expected_ja3), output_size);
            EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size);
        };

        /* Known values from Rust implementation:
         * https://github.com/jabedude/ja3-rs/blob/4f2629b86ce3496b4614296f754954806c9c849c/tests/chrome-grease-single.pcap
         */
        {
            uint8_t raw_client_hello[512] = {
                0x01, 0x00, 0x01, 0xFC,
                0x03, 0x03, 0x86, 0xad, 0xa4, 0xcc, 0x19, 0xe7, 0x14, 0x54, 0x54,
                0xfd, 0xe7, 0x37, 0x33, 0xdf, 0x66, 0xcb, 0xf6, 0xef, 0x3e, 0xc0,
                0xa1, 0x54, 0xc6, 0xdd, 0x14, 0x5e, 0xc0, 0x83, 0xac, 0xb9, 0xb4,
                0xe7, 0x20, 0x1c, 0x64, 0xae, 0xa7, 0xa2, 0xc3, 0xe1, 0x8c, 0xd1,
                0x25, 0x02, 0x4d, 0xf7, 0x86, 0x4a, 0xc7, 0x19, 0xd0, 0xc4, 0xbd,
                0xfb, 0x40, 0xc2, 0xef, 0x7f, 0x6d, 0xd3, 0x9a, 0xa7, 0x53, 0xdf,
                0xdd, 0x00, 0x22, 0x1a, 0x1a, 0x13, 0x01, 0x13, 0x02, 0x13, 0x03,
                0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xcc, 0xa9, 0xcc,
                0xa8, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f,
                0x00, 0x35, 0x00, 0x0a, 0x01, 0x00, 0x01, 0x91, 0x0a, 0x0a, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x1e, 0x00, 0x00, 0x1b, 0x67,
                0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x64, 0x73, 0x2e, 0x67, 0x2e,
                0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x63, 0x6c, 0x69, 0x63, 0x6b,
                0x2e, 0x6e, 0x65, 0x74, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00,
                0x01, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x9a, 0x9a, 0x00,
                0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00,
                0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x0c, 0x02,
                0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31,
                0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d,
                0x00, 0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05,
                0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01,
                0x00, 0x12, 0x00, 0x00, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x29, 0x9a,
                0x9a, 0x00, 0x01, 0x00, 0x00, 0x1d, 0x00, 0x20, 0x59, 0x08, 0x6f,
                0x41, 0x9a, 0xa5, 0xaa, 0x1d, 0x81, 0xe3, 0x47, 0xf0, 0x25, 0x5f,
                0x92, 0x07, 0xfc, 0x4b, 0x13, 0x74, 0x51, 0x46, 0x98, 0x08, 0x74,
                0x3b, 0xde, 0x57, 0x86, 0xe8, 0x2c, 0x74, 0x00, 0x2d, 0x00, 0x02,
                0x01, 0x01, 0x00, 0x2b, 0x00, 0x0b, 0x0a, 0xfa, 0xfa, 0x03, 0x04,
                0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00, 0x1b, 0x00, 0x03, 0x02,
                0x00, 0x02, 0xba, 0xba, 0x00, 0x01, 0x00, 0x00, 0x15, 0x00, 0xbd,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00
            };
            const char expected_ja3[] = "771,4865-4866-4867-49195-49199-49196-"
                                        "49200-52393-52392-49171-49172-156-157-47-"
                                        "53-10,0-23-65281-10-11-35-16-5-13-18-51-"
                                        "45-43-27-21,29-23-24,0";
            S2N_BLOB_FROM_HEX(expected_hash, "66918128f1b9b03303d77c6f2eefd128");

            DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL,
                    s2n_client_hello_free);
            EXPECT_OK(s2n_client_hello_from_source(&client_hello, server,
                    raw_client_hello, sizeof(raw_client_hello), source));

            uint32_t output_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello,
                    S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size));
            EXPECT_EQUAL(strlen(expected_ja3), output_size);
            EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size);

            uint32_t str_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_hash(client_hello,
                    S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size,
                    &str_size));
            EXPECT_EQUAL(strlen(expected_ja3), str_size);
            EXPECT_EQUAL(expected_hash.size, output_size);
            EXPECT_BYTEARRAY_EQUAL(expected_hash.data, output_mem, output_size);
        };

        /* Known values from Rust implementation:
         * https://github.com/jabedude/ja3-rs/blob/4f2629b86ce3496b4614296f754954806c9c849c/tests/curl-ipv6.pcap
         */
        {
            uint8_t raw_client_hello[512] = {
                0x01, 0x00, 0x01, 0xFC,
                0x03, 0x03, 0x40, 0xc7, 0x8a, 0xef, 0x5c, 0x7f, 0xed, 0x98, 0x4a,
                0x19, 0x8a, 0x03, 0x0b, 0xc0, 0x2d, 0xc0, 0xd6, 0x8f, 0x0b, 0x14,
                0x7d, 0x23, 0x3d, 0x90, 0xb4, 0x2b, 0x4b, 0x28, 0x2c, 0x44, 0x0c,
                0x4d, 0x20, 0xf4, 0x73, 0x04, 0xed, 0x17, 0x42, 0xd6, 0xb5, 0x08,
                0x2e, 0x73, 0x78, 0x71, 0x25, 0x52, 0x5c, 0xea, 0xe7, 0xd7, 0xe5,
                0x7c, 0xfa, 0x27, 0xfe, 0xa3, 0x52, 0x63, 0x7a, 0x27, 0xc3, 0x5d,
                0x58, 0x00, 0x3e, 0x13, 0x02, 0x13, 0x03, 0x13, 0x01, 0xc0, 0x2c,
                0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0xaa, 0xc0,
                0x2b, 0xc0, 0x2f, 0x00, 0x9e, 0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b,
                0xc0, 0x23, 0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a, 0xc0, 0x14, 0x00,
                0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33, 0x00, 0x9d, 0x00, 0x9c,
                0x00, 0x3d, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2f, 0x00, 0xff, 0x01,
                0x00, 0x01, 0x75, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00,
                0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
                0x6d, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a,
                0x00, 0x0c, 0x00, 0x0a, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x1e, 0x00,
                0x19, 0x00, 0x18, 0x33, 0x74, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e,
                0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f,
                0x31, 0x2e, 0x31, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00,
                0x00, 0x0d, 0x00, 0x30, 0x00, 0x2e, 0x04, 0x03, 0x05, 0x03, 0x06,
                0x03, 0x08, 0x07, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b,
                0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06,
                0x01, 0x03, 0x03, 0x02, 0x03, 0x03, 0x01, 0x02, 0x01, 0x03, 0x02,
                0x02, 0x02, 0x04, 0x02, 0x05, 0x02, 0x06, 0x02, 0x00, 0x2b, 0x00,
                0x09, 0x08, 0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00,
                0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24,
                0x00, 0x1d, 0x00, 0x20, 0x29, 0x90, 0xc2, 0xec, 0x21, 0x68, 0x2c,
                0x5a, 0x7a, 0x5a, 0x46, 0x49, 0x59, 0x42, 0x54, 0x66, 0x02, 0x92,
                0x0c, 0x08, 0x16, 0x59, 0xf6, 0xcc, 0x75, 0xb8, 0x16, 0x53, 0x20,
                0x46, 0x79, 0x23, 0x00, 0x15, 0x00, 0xb6, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00
            };
            const char expected_ja3[] = "771,4866-4867-4865-49196-49200-159-52393-"
                                        "52392-52394-49195-49199-158-49188-49192-"
                                        "107-49187-49191-103-49162-49172-57-49161-"
                                        "49171-51-157-156-61-60-53-47-255,0-11-"
                                        "10-13172-16-22-23-13-43-45-51-21,29-23-"
                                        "30-25-24,0-1-2";
            S2N_BLOB_FROM_HEX(expected_hash, "456523fc94726331a4d5a2e1d40b2cd7");

            DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL,
                    s2n_client_hello_free);
            EXPECT_OK(s2n_client_hello_from_source(&client_hello, server,
                    raw_client_hello, sizeof(raw_client_hello), source));

            uint32_t output_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello,
                    S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size));
            EXPECT_EQUAL(strlen(expected_ja3), output_size);
            EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size);

            uint32_t str_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_hash(client_hello,
                    S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size,
                    &str_size));
            EXPECT_EQUAL(strlen(expected_ja3), str_size);
            EXPECT_EQUAL(expected_hash.size, output_size);
            EXPECT_BYTEARRAY_EQUAL(expected_hash.data, output_mem, output_size);
        };

        /* Known values from Rust implementation:
         * https://github.com/jabedude/ja3-rs/blob/4f2629b86ce3496b4614296f754954806c9c849c/tests/test.pcap
         */
        {
            uint8_t raw_client_hello[240] = {
                0x01, 0x00, 0x00, 0xEC,
                0x03, 0x03, 0x90, 0xe8, 0xcc, 0xee, 0xe5, 0x70, 0xa2, 0xa1, 0x2f,
                0x6b, 0x69, 0xd2, 0x66, 0x96, 0x0f, 0xcf, 0x20, 0xd5, 0x32, 0x6e,
                0xc4, 0xb2, 0x8c, 0xc7, 0xbd, 0x0a, 0x06, 0xc2, 0xa5, 0x14, 0xfc,
                0x34, 0x20, 0xaf, 0x72, 0xbf, 0x39, 0x99, 0xfb, 0x20, 0x70, 0xc3,
                0x10, 0x83, 0x0c, 0xee, 0xfb, 0xfa, 0x72, 0xcc, 0x5d, 0xa8, 0x99,
                0xb4, 0xc5, 0x53, 0xd6, 0x3d, 0xa0, 0x53, 0x7a, 0x5c, 0xbc, 0xf5,
                0x0b, 0x00, 0x1e, 0xc0, 0x2b, 0xc0, 0x2f, 0xcc, 0xa9, 0xcc, 0xa8,
                0xc0, 0x2c, 0xc0, 0x30, 0xc0, 0x0a, 0xc0, 0x09, 0xc0, 0x13, 0xc0,
                0x14, 0x00, 0x33, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a,
                0x01, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x23, 0x00, 0x21, 0x00,
                0x00, 0x1e, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x2e,
                0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x6d,
                0x6f, 0x7a, 0x69, 0x6c, 0x6c, 0x61, 0x2e, 0x6f, 0x72, 0x67, 0x00,
                0x17, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0a, 0x00,
                0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19,
                0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
                0x10, 0x00, 0x0e, 0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74,
                0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x00, 0x05, 0x00, 0x05, 0x01,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x16, 0x04,
                0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06,
                0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03, 0x02, 0x01, 0x00,
                0x1c, 0x00, 0x02, 0x40, 0x00
            };
            const char expected_ja3[] = "771,49195-49199-52393-52392-49196-49200-"
                                        "49162-49161-49171-49172-51-57-47-53-10,0-"
                                        "23-65281-10-11-35-16-5-13-28,29-23-24-25,0";
            S2N_BLOB_FROM_HEX(expected_hash, "839bbe3ed07fed922ded5aaf714d6842");

            DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL,
                    s2n_client_hello_free);
            EXPECT_OK(s2n_client_hello_from_source(&client_hello, server,
                    raw_client_hello, sizeof(raw_client_hello), source));

            uint32_t output_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello,
                    S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size));
            EXPECT_EQUAL(strlen(expected_ja3), output_size);
            EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size);

            uint32_t str_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_hash(client_hello,
                    S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size,
                    &str_size));
            EXPECT_EQUAL(strlen(expected_ja3), str_size);
            EXPECT_EQUAL(expected_hash.size, output_size);
            EXPECT_BYTEARRAY_EQUAL(expected_hash.data, output_mem, output_size);
        };

        /* Known values from Rust implementation:
         * https://github.com/jabedude/ja3-rs/blob/4f2629b86ce3496b4614296f754954806c9c849c/tests/ncat-port-4450.pcap
         */
        {
            uint8_t raw_client_hello[512] = {
                0x01, 0x00, 0x01, 0xFC,
                0x03, 0x03, 0xf4, 0x0f, 0xfd, 0xee, 0xc7, 0x27, 0xc2, 0x1e, 0x32,
                0x70, 0x5f, 0x85, 0x25, 0xa6, 0xbb, 0x6c, 0xca, 0x4b, 0x6c, 0xbe,
                0x01, 0x66, 0x32, 0x66, 0x76, 0x4b, 0x67, 0x74, 0x3b, 0x91, 0xbd,
                0xb2, 0x20, 0x83, 0xd4, 0x9e, 0x77, 0xaf, 0xc1, 0x5a, 0x63, 0x35,
                0xba, 0x2f, 0xe9, 0x76, 0xbe, 0x9a, 0x42, 0x6b, 0x2e, 0xb5, 0x58,
                0x23, 0x84, 0x2a, 0x99, 0x2b, 0x37, 0x88, 0xd1, 0xf7, 0x9d, 0xd6,
                0x20, 0x00, 0x9c, 0x13, 0x02, 0x13, 0x03, 0x13, 0x01, 0xc0, 0x2c,
                0xc0, 0x30, 0x00, 0xa3, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc,
                0xaa, 0xc0, 0xaf, 0xc0, 0xad, 0xc0, 0xa3, 0xc0, 0x9f, 0xc0, 0x5d,
                0xc0, 0x61, 0xc0, 0x57, 0xc0, 0x53, 0xc0, 0x24, 0xc0, 0x28, 0x00,
                0x6b, 0x00, 0x6a, 0xc0, 0x73, 0xc0, 0x77, 0x00, 0xc4, 0x00, 0xc3,
                0xc0, 0x0a, 0xc0, 0x14, 0x00, 0x39, 0x00, 0x38, 0x00, 0x88, 0x00,
                0x87, 0x00, 0x9d, 0xc0, 0xa1, 0xc0, 0x9d, 0xc0, 0x51, 0x00, 0x3d,
                0x00, 0xc0, 0x00, 0x35, 0x00, 0x84, 0xc0, 0x2b, 0xc0, 0x2f, 0x00,
                0xa2, 0x00, 0x9e, 0xc0, 0xae, 0xc0, 0xac, 0xc0, 0xa2, 0xc0, 0x9e,
                0xc0, 0x5c, 0xc0, 0x60, 0xc0, 0x56, 0xc0, 0x52, 0xc0, 0x23, 0xc0,
                0x27, 0x00, 0x67, 0x00, 0x40, 0xc0, 0x72, 0xc0, 0x76, 0x00, 0xbe,
                0x00, 0xbd, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33, 0x00, 0x32, 0x00,
                0x9a, 0x00, 0x99, 0x00, 0x45, 0x00, 0x44, 0x00, 0x9c, 0xc0, 0xa0,
                0xc0, 0x9c, 0xc0, 0x50, 0x00, 0x3c, 0x00, 0xba, 0x00, 0x2f, 0x00,
                0x96, 0x00, 0x41, 0x00, 0xff, 0x01, 0x00, 0x01, 0x17, 0x00, 0x00,
                0x00, 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x31, 0x32, 0x37, 0x2e,
                0x30, 0x2e, 0x30, 0x2e, 0x31, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00,
                0x01, 0x02, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x0a, 0x00, 0x1d, 0x00,
                0x17, 0x00, 0x1e, 0x00, 0x19, 0x00, 0x18, 0x00, 0x23, 0x00, 0x00,
                0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x0d, 0x00,
                0x30, 0x00, 0x2e, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x07,
                0x08, 0x08, 0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b, 0x08, 0x04, 0x08,
                0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x03, 0x03,
                0x02, 0x03, 0x03, 0x01, 0x02, 0x01, 0x03, 0x02, 0x02, 0x02, 0x04,
                0x02, 0x05, 0x02, 0x06, 0x02, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x03,
                0x04, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00, 0x2d, 0x00, 0x02,
                0x01, 0x01, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00,
                0x20, 0x29, 0x61, 0x96, 0xc4, 0x0c, 0x16, 0x7c, 0xde, 0x20, 0x01,
                0x86, 0x32, 0xdf, 0x84, 0x2f, 0x67, 0x2f, 0x3f, 0x64, 0x17, 0xc0,
                0x2e, 0xa2, 0xb2, 0x9e, 0xfc, 0xa8, 0xb0, 0xc5, 0x71, 0x6e, 0x7d,
                0x00, 0x15, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00
            };
            const char expected_ja3[] = "771,4866-4867-4865-49196-49200-163-159-"
                                        "52393-52392-52394-49327-49325-49315-"
                                        "49311-49245-49249-49239-49235-49188-49192-"
                                        "107-106-49267-49271-196-195-49162-49172-"
                                        "57-56-136-135-157-49313-49309-49233-61-"
                                        "192-53-132-49195-49199-162-158-49326-"
                                        "49324-49314-49310-49244-49248-49238-49234-"
                                        "49187-49191-103-64-49266-49270-190-189-"
                                        "49161-49171-51-50-154-153-69-68-156-49312-"
                                        "49308-49232-60-186-47-150-65-255,0-11-"
                                        "10-35-22-23-13-43-45-51-21,29-23-30-25-"
                                        "24,0-1-2";
            S2N_BLOB_FROM_HEX(expected_hash, "10a6b69a81bac09072a536ce9d35dd43");

            DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL,
                    s2n_client_hello_free);
            EXPECT_OK(s2n_client_hello_from_source(&client_hello, server,
                    raw_client_hello, sizeof(raw_client_hello), source));

            uint32_t output_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello,
                    S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size));
            EXPECT_EQUAL(strlen(expected_ja3), output_size);
            EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size);

            uint32_t str_size = 0;
            EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_hash(client_hello,
                    S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size,
                    &str_size));
            EXPECT_EQUAL(strlen(expected_ja3), str_size);
            EXPECT_EQUAL(expected_hash.size, output_size);
            EXPECT_BYTEARRAY_EQUAL(expected_hash.data, output_mem, output_size);
        };
    };

    END_TEST();
}
