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

#define S2N_DEFAULT_TEST_CERT_CHAIN_LENGTH 3
#define S2N_CERT_DER_SIZE 2048

#define S2N_RSA_2048_SHA256_INTERMEDIATE_CA_KEY "../pems/rsa_2048_sha256_intermediate_ca_key.pem"
#define S2N_RSA_2048_SHA256_INTERMEDIATE_CERT_CUSTOM_OID "../pems/rsa_2048_sha256_intermediate_cert_custom_oid.pem"

#define ext_value_MAX_LEN UINT16_MAX
#define OFFSET_INSUFFICIENT_MEM_SIZE 3

struct host_verify_data {
    bool callback_invoked;
    bool 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;
}

static S2N_RESULT s2n_compare_cert_chain(struct s2n_connection *conn, struct s2n_cert_chain_and_key *test_peer_chain)
{
    RESULT_ENSURE_REF(conn);
    RESULT_ENSURE_REF(test_peer_chain);
    uint32_t cert_chain_length = 0;
    RESULT_GUARD_POSIX(s2n_cert_chain_get_length(test_peer_chain, &cert_chain_length));
    DEFER_CLEANUP(STACK_OF(X509) *cert_chain_validated = X509_STORE_CTX_get1_chain(conn->x509_validator.store_ctx),
                  s2n_openssl_x509_stack_pop_free);
    RESULT_ENSURE_REF(cert_chain_validated);
    RESULT_ENSURE_EQ(cert_chain_length, sk_X509_num(cert_chain_validated));
    struct s2n_cert *cur_cert = NULL;

    for (size_t cert_idx = 0; cert_idx < cert_chain_length; cert_idx++) {
        X509 *cert = sk_X509_value(cert_chain_validated, cert_idx);
        RESULT_ENSURE_REF(cert);
        DEFER_CLEANUP(uint8_t *cert_data_from_validator = NULL, s2n_crypto_free);
        int cert_size_from_validator = i2d_X509(cert, &cert_data_from_validator);
        RESULT_ENSURE_REF(cert_data_from_validator);
        RESULT_ENSURE_GT(cert_size_from_validator, 0);

        RESULT_GUARD_POSIX(s2n_cert_chain_get_cert(test_peer_chain, &cur_cert, cert_idx));
        RESULT_ENSURE_REF(cur_cert);
        RESULT_ENSURE_EQ(cert_size_from_validator, cur_cert->raw.size);
        RESULT_ENSURE_EQ(memcmp(cert_data_from_validator, cur_cert->raw.data, cur_cert->raw.size), 0);
    }

    return S2N_RESULT_OK;
}

static S2N_RESULT s2n_compare_utf8_strings(struct s2n_blob *input_der, const char *expected_utf8_str, uint32_t utf8_len_in, uint32_t expected_utf8_len)
{
    RESULT_ENSURE_REF(input_der);
    RESULT_ENSURE_REF(expected_utf8_str);
    RESULT_ENSURE_GT(expected_utf8_len, 0);

    DEFER_CLEANUP(struct s2n_blob utf8_str = { 0 }, s2n_free);
    RESULT_GUARD_POSIX(s2n_alloc(&utf8_str, utf8_len_in));

    RESULT_GUARD_POSIX(s2n_cert_get_utf8_string_from_extension_data(input_der->data, input_der->size, utf8_str.data, &utf8_str.size));

    RESULT_ENSURE_EQ(utf8_str.size, expected_utf8_len);
    RESULT_ENSURE_EQ(memcmp(utf8_str.data, expected_utf8_str, utf8_str.size), 0);

    RESULT_GUARD_POSIX(s2n_free(&utf8_str));
    return S2N_RESULT_OK;
}

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

    const uint8_t ocsp_data[] = "ocsp data";

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

    /* Test s2n_cert_chain_get_length */ 
    {
        uint32_t length = 0;

        /* Safety checks */
        {
            EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_get_length(NULL, &length), S2N_ERR_NULL);
            EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_get_length(chain_and_key, NULL), S2N_ERR_NULL);
        }

        /* Test success case */
        EXPECT_SUCCESS(s2n_cert_chain_get_length(chain_and_key, &length));
        EXPECT_EQUAL(length, S2N_DEFAULT_TEST_CERT_CHAIN_LENGTH);

    }

    /* Test s2n_cert_chain_get_cert */
    {
        struct s2n_cert *out_cert = NULL;
        uint32_t cert_idx = 0;

        /* Safety checks */
        {
            EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_get_cert(NULL, &out_cert, cert_idx), S2N_ERR_NULL);
            EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_get_cert(chain_and_key, NULL, cert_idx), S2N_ERR_NULL);
        }

        struct s2n_cert *cur_cert = chain_and_key->cert_chain->head;

        /* Test error case for invalid cert_idx, the valid range of cert_idx is 0 to cert_chain_length - 1 */  
        cert_idx = S2N_DEFAULT_TEST_CERT_CHAIN_LENGTH;
        EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_get_cert(chain_and_key, &out_cert, cert_idx), S2N_ERR_NO_CERT_FOUND);

        /* Test success case */
        for (size_t i = 0; i < S2N_DEFAULT_TEST_CERT_CHAIN_LENGTH; i++)
        {
            EXPECT_SUCCESS(s2n_cert_chain_get_cert(chain_and_key, &out_cert, i));
            EXPECT_NOT_NULL(cur_cert);
            EXPECT_EQUAL(out_cert, cur_cert);
            cur_cert = cur_cert->next;
        }

    }

    /* Test s2n_cert_get_der */ 
    {
        struct s2n_cert *cert = chain_and_key->cert_chain->head;
        const uint8_t *out_cert_der = NULL;
        uint32_t cert_len = 0;

        /* Safety checks */
        {
            EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_der(NULL, &out_cert_der, &cert_len), S2N_ERR_NULL);
            EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_der(cert, NULL, &cert_len), S2N_ERR_NULL);
            EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_der(cert, &out_cert_der, NULL), S2N_ERR_NULL);
        }

        EXPECT_SUCCESS(s2n_cert_get_der(cert, &out_cert_der, &cert_len));
        EXPECT_EQUAL(cert_len, cert->raw.size); 
        EXPECT_BYTEARRAY_EQUAL(out_cert_der, cert->raw.data, cert_len);
    }

    /* Test s2n_connection_get_peer_cert_chain */
    {
        /* 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));

        /* Create nonblocking pipes */
        struct s2n_test_io_pair io_pair = { 0 };
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));

        struct s2n_cert_chain_and_key *s2n_chain_and_key = NULL;
        EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&s2n_chain_and_key, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN,
                                                       S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY));

        struct s2n_config *config_skip_x509_verification = s2n_config_new();
        EXPECT_NOT_NULL(config_skip_x509_verification);
        /* Skip x509 verification */
        EXPECT_SUCCESS(s2n_config_disable_x509_verification(config_skip_x509_verification));
        EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(config_skip_x509_verification, 0));
        EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_skip_x509_verification, "test_all"));
        EXPECT_SUCCESS(s2n_config_set_client_auth_type(config_skip_x509_verification, S2N_CERT_AUTH_OPTIONAL));
        struct host_verify_data verify_data = {.allow = 1, .callback_invoked = 0};
        EXPECT_SUCCESS(s2n_config_set_verify_host_callback(config_skip_x509_verification, verify_host_fn, &verify_data));
        EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config_skip_x509_verification, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, NULL));
        EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_skip_x509_verification, s2n_chain_and_key));

        struct s2n_config *config_with_x509_verification = s2n_config_new();
        EXPECT_NOT_NULL(config_with_x509_verification);
        EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_x509_verification, "test_all"));
        EXPECT_SUCCESS(s2n_config_set_client_auth_type(config_with_x509_verification, S2N_CERT_AUTH_REQUIRED));
        EXPECT_SUCCESS(s2n_config_set_verify_host_callback(config_with_x509_verification, verify_host_fn, &verify_data));
        EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config_with_x509_verification, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, NULL));
        EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_x509_verification, s2n_chain_and_key));
        EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(config_with_x509_verification, false));

        /* Test s2n_connection_get_peer_cert_chain failure cases with error codes */
        {            
            EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_skip_x509_verification));
            EXPECT_EQUAL(client_conn->x509_validator.skip_cert_validation, 1);

            EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_x509_verification));
            EXPECT_EQUAL(server_conn->x509_validator.skip_cert_validation, 0);

            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_EQUAL(server_conn->x509_validator.state, VALIDATED);
            EXPECT_NOT_EQUAL(client_conn->x509_validator.state, VALIDATED);

            struct s2n_cert_chain_and_key *test_peer_chain = s2n_cert_chain_and_key_new();
            EXPECT_NOT_NULL(test_peer_chain);

            /* Safety checks */
            EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_peer_cert_chain(NULL, chain_and_key), S2N_ERR_NULL);
            EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_peer_cert_chain(server_conn, NULL), S2N_ERR_NULL);

            /* Input certificate chain is not empty */
            EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_peer_cert_chain(server_conn, chain_and_key),
                                      S2N_ERR_INVALID_ARGUMENT);

            /* x509 verification is skipped on client side */
            EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_peer_cert_chain(client_conn, test_peer_chain),
                                      S2N_ERR_CERT_NOT_VALIDATED);

            EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn));

            /* Clean-up */
            EXPECT_SUCCESS(s2n_cert_chain_and_key_free(test_peer_chain));
            EXPECT_SUCCESS(s2n_connection_wipe(client_conn));
            EXPECT_SUCCESS(s2n_connection_wipe(server_conn));
        }

        /* Test s2n_connection_get_peer_cert_chain success on the server side */
        {
            EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_skip_x509_verification));
            EXPECT_EQUAL(client_conn->x509_validator.skip_cert_validation, 1);  
    
            EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_x509_verification));
            EXPECT_EQUAL(server_conn->x509_validator.skip_cert_validation, 0);  

            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_EQUAL(server_conn->x509_validator.state, VALIDATED);

            struct s2n_cert_chain_and_key *test_peer_chain = s2n_cert_chain_and_key_new();
            EXPECT_NOT_NULL(test_peer_chain);

            EXPECT_SUCCESS(s2n_connection_get_peer_cert_chain(server_conn, test_peer_chain));

            EXPECT_OK(s2n_compare_cert_chain(server_conn, test_peer_chain));

            EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn));

            /* Clean-up */
            EXPECT_SUCCESS(s2n_cert_chain_and_key_free(test_peer_chain));
            EXPECT_SUCCESS(s2n_connection_wipe(client_conn));
            EXPECT_SUCCESS(s2n_connection_wipe(server_conn));
        }

        /* Test s2n_connection_get_peer_cert_chain success on the client side */
        {
            EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_skip_x509_verification));
            EXPECT_EQUAL(server_conn->x509_validator.skip_cert_validation, 1);  
    
            EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_x509_verification));
            EXPECT_EQUAL(client_conn->x509_validator.skip_cert_validation, 0);  

            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_EQUAL(client_conn->x509_validator.state, VALIDATED);

            struct s2n_cert_chain_and_key *test_peer_chain = s2n_cert_chain_and_key_new();
            EXPECT_NOT_NULL(test_peer_chain);

            EXPECT_SUCCESS(s2n_connection_get_peer_cert_chain(client_conn, test_peer_chain));

            EXPECT_OK(s2n_compare_cert_chain(client_conn, test_peer_chain));

            EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn));

            /* Clean-up */
            EXPECT_SUCCESS(s2n_cert_chain_and_key_free(test_peer_chain));
            EXPECT_SUCCESS(s2n_connection_wipe(client_conn));
            EXPECT_SUCCESS(s2n_connection_wipe(server_conn));
        }

        /* Test s2n_connection_get_peer_cert_chain with OCSP */
        if (s2n_x509_ocsp_stapling_supported()) {
            EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(s2n_chain_and_key,
                    ocsp_data, s2n_array_len(ocsp_data)));
            EXPECT_SUCCESS(s2n_config_set_status_request_type(config_with_x509_verification, S2N_STATUS_REQUEST_OCSP));

            EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_skip_x509_verification));
            EXPECT_EQUAL(server_conn->x509_validator.skip_cert_validation, 1);

            EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_x509_verification));
            EXPECT_EQUAL(client_conn->x509_validator.skip_cert_validation, 0);

            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_EQUAL(client_conn->x509_validator.state, OCSP_VALIDATED);

            struct s2n_cert_chain_and_key *test_peer_chain = s2n_cert_chain_and_key_new();
            EXPECT_NOT_NULL(test_peer_chain);

            EXPECT_SUCCESS(s2n_connection_get_peer_cert_chain(client_conn, test_peer_chain));

            EXPECT_OK(s2n_compare_cert_chain(client_conn, test_peer_chain));

            EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn));

            /* Clean-up */
            EXPECT_SUCCESS(s2n_cert_chain_and_key_free(test_peer_chain));
            EXPECT_SUCCESS(s2n_connection_wipe(client_conn));
            EXPECT_SUCCESS(s2n_connection_wipe(server_conn));
        }

        /* Clean-up */
        EXPECT_SUCCESS(s2n_config_free(config_skip_x509_verification));
        EXPECT_SUCCESS(s2n_config_free(config_with_x509_verification));
        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(s2n_chain_and_key));
    }

    /* Test X509 Extension helper functions */
    {
        struct s2n_blob ext_value = { 0 };
        struct s2n_blob utf8_str = { 0 };
        bool critical = false;
        size_t i = 0;

        struct s2n_cert_chain_and_key *custom_cert_chain = NULL;
        EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&custom_cert_chain,
                                                       S2N_RSA_2048_SHA256_INTERMEDIATE_CERT_CUSTOM_OID,
                                                       S2N_RSA_2048_SHA256_INTERMEDIATE_CA_KEY));
        struct s2n_cert *cert = custom_cert_chain->cert_chain->head;
        EXPECT_NOT_NULL(cert);

        S2N_BLOB_FROM_HEX(subject_key_id_blob, "04 14 F9 19 58 9D 9E 97 89 9C 27 67 5B 62 19 \
                                                2A 1E 27 D6 4E 1E F6");
        S2N_BLOB_FROM_HEX(authority_key_id_blob, "30 16 80 14 56 9E 26 B6 09 4C 2E AC C8 4E 51 \
                                                E1 AD 7F E7 92 84 28 D4 3E");
        S2N_BLOB_FROM_HEX(basic_constraints_blob, "30 06 01 01 FF 02 01 00");
        S2N_BLOB_FROM_HEX(key_usage_blob, "03 02 01 86");
        S2N_BLOB_FROM_HEX(custom_oid_1_blob, "0C 41 6B 65 79 69 64 3A 33 \
                                              36 3A 36 31 3A 33 46 3A 31 42 3A 30 32 3A 43 37 \
                                              3A 31 32 3A 32 42 3A 35 33 3A 30 41 3A 32 32 3A \
                                              42 41 3A 35 38 3A 42 36 3A 41 38 3A 38 30 3A 31 \
                                              39 3A 45 45 3A 35 31 3A 38 35");
        S2N_BLOB_FROM_HEX(custom_oid_2_blob, "0C 18 49 50 20 41 64 64 72 65 73 73 3A 31 32 2E \
                                              33 34 35 2E 36 37 2E 38 39 30");
        S2N_BLOB_FROM_HEX(custom_oid_3_blob, "0C 28 44 4E 53 3A 31 32 2E 33 34 35 2E 36 37 2E \
                                              38 39 30 2E 61 75 74 6F 2E 70 64 78 2E 65 63 32 \
                                              2E 73 75 62 73 74 72 61 74 65");

        struct {
            const char *oid;
            uint32_t ext_value_len;
            const char *expected_utf8;
            uint32_t utf8_len;
            struct s2n_blob expected_der;
            struct s2n_blob returned_der;
            bool critical;
        } test_cases[] = {
            {
                .oid = "X509v3 Subject Key Identifier",
                .expected_der = subject_key_id_blob,
                .critical = false
            },
            {
                .oid = "X509v3 Authority Key Identifier",
                .expected_der = authority_key_id_blob,
                .critical = false
            },
            {
                .oid = "X509v3 Basic Constraints",
                .expected_der = basic_constraints_blob,
                .critical = true
            },
            {
                .oid = "X509v3 Key Usage",
                .expected_der = key_usage_blob,
                .critical = true
            },
            {
                .oid = "1.2.3.4.5.6.7890.1.2.100.1",
                .expected_utf8 = "keyid:36:61:3F:1B:02:C7:12:2B:53:0A:22:BA:58:B6:A8:80:19:EE:51:85",
                .expected_der = custom_oid_1_blob,
                .critical = false
            },
            {
                .oid = "1.2.3.4.5.6.7890.1.2.100.2",
                .expected_utf8 = "IP Address:12.345.67.890",
                .expected_der = custom_oid_2_blob,
                .critical = false 
            },
            {   .oid = "1.2.3.4.5.6.7890.1.2.100.3",
                .expected_utf8 = "DNS:12.345.67.890.auto.pdx.ec2.substrate",
                .expected_der = custom_oid_3_blob,
                .critical = false 
            },
            {   .oid = "1.2.3.4.5.6.7890.1.2.100",
                .critical = false
            },
        };

        /* Test s2n_cert_get_x509_extension_value_length */ 
        {
            /* Safety checks */ 
            {
                const uint8_t oid[] = "Example X509 extension OID";
                uint32_t ext_value_len = 0;

                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value_length(NULL, oid, &ext_value_len), S2N_ERR_NULL);
                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value_length(cert, NULL, &ext_value_len), S2N_ERR_NULL);
                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value_length(cert, oid, NULL), S2N_ERR_NULL);
            }

            /* Test success cases */
            for (i = 0; i < s2n_array_len(test_cases) - 1; i++) {
                EXPECT_SUCCESS(s2n_cert_get_x509_extension_value_length(cert, (const uint8_t *)test_cases[i].oid, &test_cases[i].ext_value_len));
                EXPECT_EQUAL(test_cases[i].ext_value_len, test_cases[i].expected_der.size);
            }

            /* Test failure case for invalid X509 extension OID */
            {
                size_t invalid_test_case = s2n_array_len(test_cases) - 1;
                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value_length(cert, (const uint8_t *)test_cases[invalid_test_case].oid,
                                                     &test_cases[i].ext_value_len), S2N_ERR_X509_EXTENSION_VALUE_NOT_FOUND);
            }

        }

        /* Test s2n_cert_get_x509_extension_value */
        {
            /* Safety checks */ 
            {
                const uint8_t oid[] = "Example X509 extension OID";
                EXPECT_SUCCESS(s2n_alloc(&ext_value, ext_value_MAX_LEN));

                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value(NULL, oid,
                                                    ext_value.data, &ext_value.size, &critical), S2N_ERR_NULL);
                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value(cert, NULL,
                                                    ext_value.data, &ext_value.size, &critical), S2N_ERR_NULL);
                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value(cert, oid, 
                                                    NULL, &ext_value.size, &critical), S2N_ERR_NULL);
                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value(cert, oid,
                                                    ext_value.data, NULL, &critical), S2N_ERR_NULL);
                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value(cert, oid,
                                                    ext_value.data, &ext_value.size, NULL), S2N_ERR_NULL);

                EXPECT_SUCCESS(s2n_free(&ext_value));
            }

            /* Test success cases for s2n_cert_get_x509_extension_value */
            for (i = 0; i < s2n_array_len(test_cases) - 1; i++) {
                EXPECT_SUCCESS(s2n_alloc(&test_cases[i].returned_der, test_cases[i].ext_value_len));
                EXPECT_SUCCESS(s2n_cert_get_x509_extension_value(cert, (const uint8_t *)test_cases[i].oid,
                                                                 test_cases[i].returned_der.data,
                                                                 &test_cases[i].returned_der.size, &critical));
                EXPECT_BYTEARRAY_EQUAL(test_cases[i].returned_der.data, test_cases[i].expected_der.data, test_cases[i].expected_der.size);
                EXPECT_EQUAL(critical, test_cases[i].critical);
            }

            /* Test failure case for insufficient amount of memory allocated */
            {
                size_t insuf_test_case = 0;
                EXPECT_SUCCESS(s2n_alloc(&ext_value, test_cases[insuf_test_case].returned_der.size - OFFSET_INSUFFICIENT_MEM_SIZE));
                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value(cert, (const uint8_t *)test_cases[insuf_test_case].oid,
                                                    ext_value.data, &ext_value.size, &critical), S2N_ERR_INSUFFICIENT_MEM_SIZE);
                EXPECT_SUCCESS(s2n_free(&ext_value));
            }

            /* Test failure case for invalid X509 extension OID */
            {
                size_t invalid_test_case = s2n_array_len(test_cases) - 1;
                EXPECT_SUCCESS(s2n_alloc(&test_cases[invalid_test_case].returned_der, ext_value_MAX_LEN));
                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value(cert, (const uint8_t *)test_cases[invalid_test_case].oid,
                                                    test_cases[invalid_test_case].returned_der.data,
                                                    &test_cases[invalid_test_case].returned_der.size, &critical), 
                                                    S2N_ERR_X509_EXTENSION_VALUE_NOT_FOUND);
            }
        }

        /* Test s2n_cert_get_utf8_string_from_extension_data_length */
        {
            /* Safety checks */ 
            {
                const uint8_t der_ext_value[] = "DER encoded X509 extension value";
                size_t der_ext_value_len = strlen((const char *)der_ext_value);
                uint32_t utf8_len = 0;

                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data_length(NULL, der_ext_value_len, &utf8_len), S2N_ERR_NULL);
                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data_length(der_ext_value, 0, &utf8_len), S2N_ERR_SAFETY);
                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data_length(der_ext_value, der_ext_value_len, 0), S2N_ERR_NULL);
            }

            /* Test success and failure cases */
            for (i = 0; i < s2n_array_len(test_cases) - 1; i++) {
                if (i > 3) {
                    EXPECT_SUCCESS(s2n_cert_get_utf8_string_from_extension_data_length(test_cases[i].returned_der.data,
                                                                                  test_cases[i].returned_der.size,
                                                                                  &test_cases[i].utf8_len));
                    EXPECT_EQUAL(test_cases[i].utf8_len, strlen((const char *)test_cases[i].expected_utf8));
                } else {
                    EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data_length(
                                                  test_cases[i].returned_der.data, 
                                                  test_cases[i].returned_der.size,
                                                  &test_cases[i].utf8_len), S2N_ERR_INVALID_X509_EXTENSION_TYPE);
                }
            }

            /* Test failure case for insufficient amount of memory allocated */
            {
                size_t insuf_test_case = 4;
                EXPECT_SUCCESS(s2n_alloc(&utf8_str, test_cases[insuf_test_case].returned_der.size - OFFSET_INSUFFICIENT_MEM_SIZE));
                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data(
                                                                        test_cases[insuf_test_case].returned_der.data,
                                                                        test_cases[insuf_test_case].returned_der.size,
                                                                        utf8_str.data, &utf8_str.size), 
                                                                        S2N_ERR_INSUFFICIENT_MEM_SIZE);
                EXPECT_SUCCESS(s2n_free(&utf8_str));
            }

        }

        /* Test s2n_cert_get_utf8_string_from_extension_data */
        {
            /* Safety checks */ 
            {
                const uint8_t der_ext_value[] = "DER encoded X509 extension value";
                size_t der_ext_value_len = strlen((const char *)der_ext_value);
                EXPECT_SUCCESS(s2n_alloc(&utf8_str, ext_value_MAX_LEN));

                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data(NULL, der_ext_value_len, utf8_str.data,
                                                                        &utf8_str.size), S2N_ERR_NULL);
                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data(der_ext_value, 0, utf8_str.data,
                                                                        &utf8_str.size), S2N_ERR_SAFETY);
                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data(der_ext_value, der_ext_value_len, NULL,
                                                                        &utf8_str.size), S2N_ERR_NULL);
                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data(der_ext_value, der_ext_value_len,
                                                                        utf8_str.data, NULL), S2N_ERR_NULL);

                EXPECT_SUCCESS(s2n_free(&utf8_str));
            }

            /* Test success and failure cases for s2n_cert_get_utf8_string_from_extension_data */
            for (i = 0; i < s2n_array_len(test_cases) - 1; i++) {
                if (i > 3) {
                    EXPECT_OK(s2n_compare_utf8_strings(&test_cases[i].returned_der, test_cases[i].expected_utf8,
                                                       test_cases[i].utf8_len, strlen((const char *)test_cases[i].expected_utf8)));
                } else {
                    EXPECT_ERROR_WITH_ERRNO(s2n_compare_utf8_strings(
                        &test_cases[i].returned_der, (const char *)test_cases[i].expected_der.data, ext_value_MAX_LEN,
                        test_cases[i].expected_der.size), S2N_ERR_INVALID_X509_EXTENSION_TYPE);
                }
            }

            /* Test failure case for insufficient amount of memory allocated */
            {
                size_t insuf_test_case = 4;
                EXPECT_SUCCESS(s2n_alloc(&utf8_str, test_cases[insuf_test_case].returned_der.size - OFFSET_INSUFFICIENT_MEM_SIZE));
                EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data(
                                                                        test_cases[insuf_test_case].returned_der.data,
                                                                        test_cases[insuf_test_case].returned_der.size,
                                                                        utf8_str.data, &utf8_str.size), 
                                                                        S2N_ERR_INSUFFICIENT_MEM_SIZE);
                EXPECT_SUCCESS(s2n_free(&utf8_str));
            }
        }

        /* Cleanup */
        for (i = 0; i < s2n_array_len(test_cases); i++) {
            EXPECT_SUCCESS(s2n_free(&test_cases[i].returned_der));
        }
        EXPECT_SUCCESS(s2n_cert_chain_and_key_free(custom_cert_chain));
    }

    EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key));
    END_TEST();
}
