/**
 *  Copyright Notice:
 *  Copyright 2021-2022 DMTF. All rights reserved.
 *  License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md
 **/

#include "spdm_unit_test.h"
#include "internal/libspdm_responder_lib.h"

#if LIBSPDM_ENABLE_CAPABILITY_CERT_CAP

/* #define TEST_LIBSPDM_DEBUG*/
#ifdef TEST_LIBSPDM_DEBUG
#define TEST_LIBSPDM_DEBUG_PRINT(format, ...) printf(format, ## __VA_ARGS__)
#else
#define TEST_LIBSPDM_DEBUG_PRINT(...)
#endif

spdm_get_certificate_request_t m_libspdm_get_certificate_request1 = {
    { SPDM_MESSAGE_VERSION_10, SPDM_GET_CERTIFICATE, 0, 0 },
    0,
    LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN
};
size_t m_libspdm_get_certificate_request1_size = sizeof(m_libspdm_get_certificate_request1);

spdm_get_certificate_request_t m_libspdm_get_certificate_request3 = {
    { SPDM_MESSAGE_VERSION_10, SPDM_GET_CERTIFICATE, 0, 0 },
    0,
    0
};
size_t m_libspdm_get_certificate_request3_size = sizeof(m_libspdm_get_certificate_request3);

spdm_get_certificate_request_t m_libspdm_get_certificate_request4 = {
    { SPDM_MESSAGE_VERSION_10, SPDM_GET_CERTIFICATE, 9, 0 },
    0,
    LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN
};
size_t m_libspdm_get_certificate_request4_size = sizeof(m_libspdm_get_certificate_request4);

spdm_get_certificate_request_t m_libspdm_get_certificate_request5 = {
    { SPDM_MESSAGE_VERSION_13, SPDM_GET_CERTIFICATE, 0, 0 },
    0,
    LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN
};
size_t m_libspdm_get_certificate_request5_size = sizeof(m_libspdm_get_certificate_request5);
/**
 * Test 1: request the first LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN bytes of the certificate chain
 * Expected Behavior: generate a correctly formed Certficate message, including its portion_length and remainder_length fields
 **/
void libspdm_test_responder_certificate_case1(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_certificate_response_t *spdm_response;
    void *data;
    size_t data_size;

    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 0x1;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_10 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;
    spdm_context->connection_info.connection_state =
        LIBSPDM_CONNECTION_STATE_AFTER_DIGESTS;
    spdm_context->local_context.capability.flags |=
        SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo =
        m_libspdm_use_hash_algo;
    libspdm_read_responder_public_certificate_chain(m_libspdm_use_hash_algo,
                                                    m_libspdm_use_asym_algo, &data,
                                                    &data_size, NULL, NULL);
    spdm_context->local_context.local_cert_chain_provision[0] = data;
    spdm_context->local_context.local_cert_chain_provision_size[0] =
        data_size;

#if LIBSPDM_RECORD_TRANSCRIPT_DATA_SUPPORT
    spdm_context->transcript.message_m.buffer_size =
        spdm_context->transcript.message_m.max_buffer_size;
#endif

    response_size = sizeof(response);
    status = libspdm_get_response_certificate(
        spdm_context, m_libspdm_get_certificate_request1_size,
        &m_libspdm_get_certificate_request1, &response_size, response);
    assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);
    assert_int_equal(response_size, sizeof(spdm_certificate_response_t) +
                     LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN);
    spdm_response = (void *)response;
    assert_int_equal(spdm_response->header.request_response_code,
                     SPDM_CERTIFICATE);
    assert_int_equal(spdm_response->header.param1, 0);
    assert_int_equal(spdm_response->portion_length,
                     LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN);
    assert_int_equal(spdm_response->remainder_length,
                     data_size - LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN);
#if LIBSPDM_RECORD_TRANSCRIPT_DATA_SUPPORT
    assert_int_equal(spdm_context->transcript.message_m.buffer_size,
                     0);
#endif
    free(data);
}

/**
 * Test 2:
 * Expected Behavior:
 **/
void libspdm_test_responder_certificate_case2(void **state)
{
}

/**
 * Test 3: Force response_state = LIBSPDM_RESPONSE_STATE_BUSY when asked GET_CERTIFICATE
 * Expected Behavior: generate an ERROR_RESPONSE with code SPDM_ERROR_CODE_BUSY
 **/
void libspdm_test_responder_certificate_case3(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_certificate_response_t *spdm_response;
    void *data;
    size_t data_size;

    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 0x3;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_10 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;
    spdm_context->response_state = LIBSPDM_RESPONSE_STATE_BUSY;
    spdm_context->connection_info.connection_state =
        LIBSPDM_CONNECTION_STATE_AFTER_DIGESTS;
    spdm_context->local_context.capability.flags |=
        SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo =
        m_libspdm_use_hash_algo;
    libspdm_read_responder_public_certificate_chain(m_libspdm_use_hash_algo,
                                                    m_libspdm_use_asym_algo, &data,
                                                    &data_size, NULL, NULL);
    spdm_context->local_context.local_cert_chain_provision[0] = data;
    spdm_context->local_context.local_cert_chain_provision_size[0] =
        data_size;

    response_size = sizeof(response);
    status = libspdm_get_response_certificate(
        spdm_context, m_libspdm_get_certificate_request1_size,
        &m_libspdm_get_certificate_request1, &response_size, response);
    assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);
    assert_int_equal(response_size, sizeof(spdm_error_response_t));
    spdm_response = (void *)response;
    assert_int_equal(spdm_response->header.request_response_code,
                     SPDM_ERROR);
    assert_int_equal(spdm_response->header.param1, SPDM_ERROR_CODE_BUSY);
    assert_int_equal(spdm_response->header.param2, 0);
    assert_int_equal(spdm_context->response_state,
                     LIBSPDM_RESPONSE_STATE_BUSY);
    free(data);
}

/**
 * Test 4: Force response_state = LIBSPDM_RESPONSE_STATE_NEED_RESYNC when asked GET_CERTIFICATE
 * Expected Behavior: generate an ERROR_RESPONSE with code SPDM_ERROR_CODE_REQUEST_RESYNCH
 **/
void libspdm_test_responder_certificate_case4(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_certificate_response_t *spdm_response;
    void *data;
    size_t data_size;

    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 0x4;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_10 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;
    spdm_context->response_state = LIBSPDM_RESPONSE_STATE_NEED_RESYNC;
    spdm_context->connection_info.connection_state =
        LIBSPDM_CONNECTION_STATE_AFTER_DIGESTS;
    spdm_context->local_context.capability.flags |=
        SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo =
        m_libspdm_use_hash_algo;
    libspdm_read_responder_public_certificate_chain(m_libspdm_use_hash_algo,
                                                    m_libspdm_use_asym_algo, &data,
                                                    &data_size, NULL, NULL);
    spdm_context->local_context.local_cert_chain_provision[0] = data;
    spdm_context->local_context.local_cert_chain_provision_size[0] =
        data_size;

    response_size = sizeof(response);
    status = libspdm_get_response_certificate(
        spdm_context, m_libspdm_get_certificate_request1_size,
        &m_libspdm_get_certificate_request1, &response_size, response);
    assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);
    assert_int_equal(response_size, sizeof(spdm_error_response_t));
    spdm_response = (void *)response;
    assert_int_equal(spdm_response->header.request_response_code,
                     SPDM_ERROR);
    assert_int_equal(spdm_response->header.param1,
                     SPDM_ERROR_CODE_REQUEST_RESYNCH);
    assert_int_equal(spdm_response->header.param2, 0);
    assert_int_equal(spdm_context->response_state,
                     LIBSPDM_RESPONSE_STATE_NEED_RESYNC);
    free(data);
}

#if LIBSPDM_RESPOND_IF_READY_SUPPORT
/**
 * Test 5: Force response_state = LIBSPDM_RESPONSE_STATE_NOT_READY when asked GET_CERTIFICATE
 * Expected Behavior: generate an ERROR_RESPONSE with code SPDM_ERROR_CODE_RESPONSE_NOT_READY and correct error_data
 **/
void libspdm_test_responder_certificate_case5(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_certificate_response_t *spdm_response;
    void *data;
    size_t data_size;
    spdm_error_data_response_not_ready_t *error_data;

    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 0x5;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_10 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;
    spdm_context->response_state = LIBSPDM_RESPONSE_STATE_NOT_READY;
    spdm_context->connection_info.connection_state =
        LIBSPDM_CONNECTION_STATE_AFTER_DIGESTS;
    spdm_context->local_context.capability.flags |=
        SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo = m_libspdm_use_hash_algo;
    libspdm_read_responder_public_certificate_chain(m_libspdm_use_hash_algo,
                                                    m_libspdm_use_asym_algo, &data,
                                                    &data_size, NULL, NULL);
    spdm_context->local_context.local_cert_chain_provision[0] = data;
    spdm_context->local_context.local_cert_chain_provision_size[0] = data_size;

    response_size = sizeof(response);
    status = libspdm_get_response_certificate(
        spdm_context, m_libspdm_get_certificate_request1_size,
        &m_libspdm_get_certificate_request1, &response_size, response);
    assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);
    assert_int_equal(response_size,
                     sizeof(spdm_error_response_t) +
                     sizeof(spdm_error_data_response_not_ready_t));
    spdm_response = (void *)response;
    error_data = (spdm_error_data_response_not_ready_t
                  *)(&spdm_response->portion_length);
    assert_int_equal(spdm_response->header.request_response_code,
                     SPDM_ERROR);
    assert_int_equal(spdm_response->header.param1,
                     SPDM_ERROR_CODE_RESPONSE_NOT_READY);
    assert_int_equal(spdm_response->header.param2, 0);
    assert_int_equal(spdm_context->response_state,
                     LIBSPDM_RESPONSE_STATE_NOT_READY);
    assert_int_equal(error_data->request_code, SPDM_GET_CERTIFICATE);
    free(data);
}
#endif /* LIBSPDM_RESPOND_IF_READY_SUPPORT */

/**
 * Test 6: simulate wrong connection_state when asked GET_CERTIFICATE (missing SPDM_GET_DIGESTS_RECEIVE_FLAG and SPDM_GET_CAPABILITIES_RECEIVE_FLAG)
 * Expected Behavior: generate an ERROR_RESPONSE with code SPDM_ERROR_CODE_UNEXPECTED_REQUEST
 **/
void libspdm_test_responder_certificate_case6(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_certificate_response_t *spdm_response;
    void *data;
    size_t data_size;

    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 0x6;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_10 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;
    spdm_context->response_state = LIBSPDM_RESPONSE_STATE_NORMAL;
    spdm_context->connection_info.connection_state =
        LIBSPDM_CONNECTION_STATE_NOT_STARTED;
    spdm_context->local_context.capability.flags |=
        SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo =
        m_libspdm_use_hash_algo;
    libspdm_read_responder_public_certificate_chain(m_libspdm_use_hash_algo,
                                                    m_libspdm_use_asym_algo, &data,
                                                    &data_size, NULL, NULL);
    spdm_context->local_context.local_cert_chain_provision[0] = data;
    spdm_context->local_context.local_cert_chain_provision_size[0] =
        data_size;


    response_size = sizeof(response);
    status = libspdm_get_response_certificate(
        spdm_context, m_libspdm_get_certificate_request1_size,
        &m_libspdm_get_certificate_request1, &response_size, response);
    assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);
    assert_int_equal(response_size, sizeof(spdm_error_response_t));
    spdm_response = (void *)response;
    assert_int_equal(spdm_response->header.request_response_code,
                     SPDM_ERROR);
    assert_int_equal(spdm_response->header.param1,
                     SPDM_ERROR_CODE_UNEXPECTED_REQUEST);
    assert_int_equal(spdm_response->header.param2, 0);
    free(data);
}

/**
 * Test 7: request length at the boundary of maximum integer values, while keeping offset 0
 * Expected Behavior: generate correctly formed Certficate messages, including its portion_length and remainder_length fields
 **/
void libspdm_test_responder_certificate_case7(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_certificate_response_t *spdm_response;
    void *data;
    size_t data_size;

    /* Testing Lengths at the boundary of maximum integer values*/
    uint16_t test_lengths[] = {
        1,        0x7F,     (uint16_t)(0x7F + 1),
        0xFF,  0x7FFF,     (uint16_t)(0x7FFF + 1),
        0xFFFF, (uint16_t)(-1)
    };
    uint16_t expected_chunk_size;

    /* Setting up the spdm_context and loading a sample certificate chain*/
    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 0x7;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_10 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;
    spdm_context->connection_info.connection_state =
        LIBSPDM_CONNECTION_STATE_AFTER_DIGESTS;
    spdm_context->local_context.capability.flags |=
        SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo =
        m_libspdm_use_hash_algo;
    libspdm_read_responder_public_certificate_chain(m_libspdm_use_hash_algo,
                                                    m_libspdm_use_asym_algo, &data,
                                                    &data_size, NULL, NULL);
    spdm_context->local_context.local_cert_chain_provision[0] = data;
    spdm_context->local_context.local_cert_chain_provision_size[0] =
        data_size;

    /* This tests considers only offset = 0, other tests vary offset value*/
    m_libspdm_get_certificate_request3.offset = 0;

    for (int i = 0; i < sizeof(test_lengths) / sizeof(test_lengths[0]); i++) {
        TEST_LIBSPDM_DEBUG_PRINT("i:%d test_lengths[i]:%u\n", i, test_lengths[i]);
        m_libspdm_get_certificate_request3.length = test_lengths[i];
        /* Expected received length is limited by LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN (implementation specific?)*/
        expected_chunk_size = LIBSPDM_MIN(m_libspdm_get_certificate_request3.length,
                                          LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN);

        /* reseting an internal buffer to avoid overflow and prevent tests to succeed*/
        libspdm_reset_message_b(spdm_context);
        response_size = sizeof(response);
        status = libspdm_get_response_certificate(
            spdm_context, m_libspdm_get_certificate_request3_size,
            &m_libspdm_get_certificate_request3, &response_size,
            response);
        assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);
        assert_int_equal(response_size,
                         sizeof(spdm_certificate_response_t) +
                         expected_chunk_size);
        spdm_response = (void *)response;
        assert_int_equal(spdm_response->header.request_response_code,
                         SPDM_CERTIFICATE);
        assert_int_equal(spdm_response->header.param1, 0);
        assert_int_equal(spdm_response->portion_length,
                         expected_chunk_size);
        assert_int_equal(spdm_response->remainder_length,
                         data_size - expected_chunk_size);
    }
    free(data);
}

/**
 * Test 8: request offset at the boundary of maximum integer values, while keeping length 0
 * Expected Behavior: generate correctly formed Certficate messages, including its portion_length and remainder_length fields
 **/
void libspdm_test_responder_certificate_case8(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_certificate_response_t *spdm_response;
    spdm_error_response_t *spdm_responseError;
    void *data;
    size_t data_size;

    /* Testing offsets at the boundary of maximum integer values and at the boundary of certificate length (first three positions)*/
    uint16_t test_offsets[] = { (uint16_t)(-1),
                                0,
                                +1,
                                0,
                                0x7F,
                                (uint16_t)(0x7F + 1),
                                0xFF,
                                0x7FFF,
                                (uint16_t)(0x7FFF + 1),
                                0xFFFF,
                                (uint16_t)(-1) };

    /* Setting up the spdm_context and loading a sample certificate chain*/
    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 0x8;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_10 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;
    spdm_context->connection_info.connection_state =
        LIBSPDM_CONNECTION_STATE_AFTER_DIGESTS;
    spdm_context->local_context.capability.flags |=
        SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo =
        m_libspdm_use_hash_algo;
    libspdm_read_responder_public_certificate_chain(m_libspdm_use_hash_algo,
                                                    m_libspdm_use_asym_algo, &data,
                                                    &data_size, NULL, NULL);
    spdm_context->local_context.local_cert_chain_provision[0] = data;
    spdm_context->local_context.local_cert_chain_provision_size[0] =
        data_size;

    /* This tests considers only length = 1, other tests vary length value*/
    m_libspdm_get_certificate_request3.length = 1;
    /* Setting up offset values at the boundary of certificate length*/
    test_offsets[0] = (uint16_t)(test_offsets[0] + data_size);
    test_offsets[1] = (uint16_t)(test_offsets[1] + data_size);
    test_offsets[2] = (uint16_t)(test_offsets[2] + data_size);

    for (int i = 0; i < sizeof(test_offsets) / sizeof(test_offsets[0]); i++) {
        TEST_LIBSPDM_DEBUG_PRINT("i:%d test_offsets[i]:%u\n", i, test_offsets[i]);
        m_libspdm_get_certificate_request3.offset = test_offsets[i];

        /* reseting an internal buffer to avoid overflow and prevent tests to succeed*/
        libspdm_reset_message_b(spdm_context);
        response_size = sizeof(response);
        status = libspdm_get_response_certificate(
            spdm_context, m_libspdm_get_certificate_request3_size,
            &m_libspdm_get_certificate_request3, &response_size,
            response);
        assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);

        if (m_libspdm_get_certificate_request3.offset >= data_size) {
            /* A too long of an offset should return an error*/
            spdm_responseError = (void *)response;
            assert_int_equal(
                spdm_responseError->header.request_response_code,
                SPDM_ERROR);
            assert_int_equal(spdm_responseError->header.param1,
                             SPDM_ERROR_CODE_INVALID_REQUEST);
        } else {
            /* Otherwise it should work properly, considering length = 1*/
            assert_int_equal(response_size,
                             sizeof(spdm_certificate_response_t) + 1);
            spdm_response = (void *)response;
            assert_int_equal(
                spdm_response->header.request_response_code,
                SPDM_CERTIFICATE);
            assert_int_equal(spdm_response->header.param1, 0);
            assert_int_equal(spdm_response->portion_length, 1);
            assert_int_equal(
                spdm_response->remainder_length + 1,
                (uint16_t)(
                    data_size -
                    m_libspdm_get_certificate_request3.offset));
        }
    }
    free(data);
}

/**
 * Test 9: request offset and length at the boundary of maximum integer values
 * Expected Behavior: generate correctly formed Certficate messages, including its portion_length and remainder_length fields
 **/
void libspdm_test_responder_certificate_case9(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_certificate_response_t *spdm_response;
    spdm_error_response_t *spdm_responseError;
    void *data;
    size_t data_size;

    /* Testing offsets and length combinations
     * Check at the boundary of maximum integer values and at the boundary of certificate length*/
    uint16_t test_sizes[] = {
        (uint16_t)(-1),
        0,
        +1, /* reserved for sizes around the certificate chain size*/
        (uint16_t)(-1),
        +1,
        (uint16_t)(0x7F - 1),
        0x7F,
        (uint16_t)(0x7F + 1),
        (uint16_t)(0xFF - 1),
        0xFF,
        (uint16_t)(0x7FFF - 1),
        0x7FFF,
        (uint16_t)(0x7FFF + 1),
        (uint16_t)(0xFFFF - 1),
        0xFFFF
    };
    uint16_t expected_chunk_size;
    uint16_t expected_remainder;

    /* Setting up the spdm_context and loading a sample certificate chain*/
    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 0x9;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_10 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;
    spdm_context->connection_info.connection_state =
        LIBSPDM_CONNECTION_STATE_AFTER_DIGESTS;
    spdm_context->local_context.capability.flags |=
        SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo =
        m_libspdm_use_hash_algo;
    libspdm_read_responder_public_certificate_chain(m_libspdm_use_hash_algo,
                                                    m_libspdm_use_asym_algo, &data,
                                                    &data_size, NULL, NULL);
    spdm_context->local_context.local_cert_chain_provision[0] = data;
    spdm_context->local_context.local_cert_chain_provision_size[0] =
        data_size;

    /* Setting up offset values at the boundary of certificate length*/
    test_sizes[0] += (uint16_t)(test_sizes[0] + data_size);
    test_sizes[1] += (uint16_t)(test_sizes[1] + data_size);
    test_sizes[2] += (uint16_t)(test_sizes[2] + data_size);

    for (int i = 0; i < sizeof(test_sizes) / sizeof(test_sizes[0]); i++) {
        TEST_LIBSPDM_DEBUG_PRINT("i:%d test_sizes[i]=length:%u\n", i,
                                 test_sizes[i]);
        m_libspdm_get_certificate_request3.length = test_sizes[i];
        for (int j = 0; j < sizeof(test_sizes) / sizeof(test_sizes[0]);
             j++) {
            TEST_LIBSPDM_DEBUG_PRINT("\tj:%d test_sizes[j]=offset:%u\n", j,
                                     test_sizes[j]);
            m_libspdm_get_certificate_request3.offset = test_sizes[j];

            /* reseting an internal buffer to avoid overflow and prevent tests to succeed*/
            libspdm_reset_message_b(spdm_context);
            response_size = sizeof(response);
            status = libspdm_get_response_certificate(
                spdm_context,
                m_libspdm_get_certificate_request3_size,
                &m_libspdm_get_certificate_request3,
                &response_size, response);
            assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);

            if (m_libspdm_get_certificate_request3.offset >=
                data_size) {
                /* A too long of an offset should return an error*/
                spdm_responseError = (void *)response;
                assert_int_equal(spdm_responseError->header
                                 .request_response_code,
                                 SPDM_ERROR);
                assert_int_equal(
                    spdm_responseError->header.param1,
                    SPDM_ERROR_CODE_INVALID_REQUEST);
            } else {
                /* Otherwise it should work properly*/

                /* Expected received length is limited by LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN and by the remaining length*/
                expected_chunk_size = (uint16_t)(LIBSPDM_MIN(
                                                     m_libspdm_get_certificate_request3.length,
                                                     data_size -
                                                     m_libspdm_get_certificate_request3
                                                     .offset));
                expected_chunk_size =
                    LIBSPDM_MIN(expected_chunk_size,
                                LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN);
                /* Expected certificate length left*/
                expected_remainder = (uint16_t)(
                    data_size -
                    m_libspdm_get_certificate_request3.offset -
                    expected_chunk_size);

                assert_int_equal(
                    response_size,
                    sizeof(spdm_certificate_response_t) +
                    expected_chunk_size);
                spdm_response = (void *)response;
                assert_int_equal(spdm_response->header
                                 .request_response_code,
                                 SPDM_CERTIFICATE);
                assert_int_equal(spdm_response->header.param1,
                                 0);
                assert_int_equal(spdm_response->portion_length,
                                 expected_chunk_size);
                assert_int_equal(
                    spdm_response->remainder_length,
                    expected_remainder);
            }
        }
    }
    free(data);
}

/**
 * Test 10: request LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN bytes of long certificate chains, with the largest valid offset
 * Expected Behavior: generate correctly formed Certficate messages, including its portion_length and remainder_length fields
 **/
void libspdm_test_responder_certificate_case10(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_certificate_response_t *spdm_response;
    spdm_error_response_t *spdm_responseError;
    void *data;
    size_t data_size;

    uint16_t test_cases[] = { LIBSPDM_TEST_CERT_MAXINT16, LIBSPDM_TEST_CERT_MAXUINT16 };

    size_t expected_chunk_size;
    size_t expected_remainder;

    /* Setting up the spdm_context and loading a sample certificate chain*/
    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 0xA;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_10 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;
    spdm_context->connection_info.connection_state =
        LIBSPDM_CONNECTION_STATE_AFTER_DIGESTS;
    spdm_context->local_context.capability.flags |=
        SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo =
        m_libspdm_use_hash_algo;

    m_libspdm_get_certificate_request3.length = LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN;

    for (int i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) {
        libspdm_read_responder_public_certificate_chain_by_size(
            /*MAXUINT16_CERT signature_algo is SHA256RSA */
            m_libspdm_use_hash_algo, SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_RSASSA_2048,
            test_cases[i], &data, &data_size, NULL, NULL);

        spdm_context->local_context.local_cert_chain_provision[0] =
            data;
        spdm_context->local_context.local_cert_chain_provision_size[0] =
            data_size;

        m_libspdm_get_certificate_request3.offset =
            (uint16_t)(LIBSPDM_MIN(data_size - 1, 0xFFFF));
        TEST_LIBSPDM_DEBUG_PRINT("data_size: %u\n", data_size);
        TEST_LIBSPDM_DEBUG_PRINT("m_libspdm_get_certificate_request3.offset: %u\n",
                                 m_libspdm_get_certificate_request3.offset);
        TEST_LIBSPDM_DEBUG_PRINT("m_libspdm_get_certificate_request3.length: %u\n",
                                 m_libspdm_get_certificate_request3.length);
        TEST_LIBSPDM_DEBUG_PRINT(
            "offset + length: %u\n",
            m_libspdm_get_certificate_request3.offset +
            m_libspdm_get_certificate_request3.length);

        /* reseting an internal buffer to avoid overflow and prevent tests to succeed*/
        libspdm_reset_message_b(spdm_context);
        response_size = sizeof(response);
        status = libspdm_get_response_certificate(
            spdm_context, m_libspdm_get_certificate_request3_size,
            &m_libspdm_get_certificate_request3, &response_size,
            response);
        assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);

        /* Expected received length is limited by LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN and by the remaining length*/
        expected_chunk_size = (uint16_t)(LIBSPDM_MIN(
                                             m_libspdm_get_certificate_request3.length,
                                             data_size -
                                             m_libspdm_get_certificate_request3.offset));
        expected_chunk_size =
            LIBSPDM_MIN(expected_chunk_size, LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN);
        /* Expected certificate length left*/
        expected_remainder = (uint16_t)(
            data_size - m_libspdm_get_certificate_request3.offset -
            expected_chunk_size);

        TEST_LIBSPDM_DEBUG_PRINT("expected_chunk_size %u\n", expected_chunk_size);
        TEST_LIBSPDM_DEBUG_PRINT("expected_remainder %u\n", expected_remainder);

        if (expected_remainder > 0xFFFF ||
            expected_chunk_size > 0xFFFF) {
            spdm_responseError = (void *)response;
            assert_int_equal(
                spdm_responseError->header.request_response_code,
                SPDM_ERROR);
            assert_int_equal(spdm_responseError->header.param1,
                             SPDM_ERROR_CODE_INVALID_REQUEST);
        } else {
            assert_int_equal(response_size,
                             sizeof(spdm_certificate_response_t) +
                             expected_chunk_size);
            spdm_response = (void *)response;
            assert_int_equal(
                spdm_response->header.request_response_code,
                SPDM_CERTIFICATE);
            assert_int_equal(spdm_response->header.param1, 0);
            assert_int_equal(spdm_response->portion_length,
                             expected_chunk_size);
            assert_int_equal(spdm_response->remainder_length,
                             expected_remainder);
        }

        TEST_LIBSPDM_DEBUG_PRINT("\n");

        spdm_context->local_context.local_cert_chain_provision[0] =
            NULL;
        spdm_context->local_context.local_cert_chain_provision_size[0] =
            0;
        free(data);
    }
}

/**
 * Test 11: request LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN bytes of a short certificate chain (fits in 1 message)
 * Expected Behavior: generate correctly formed Certficate messages, including its portion_length and remainder_length fields
 **/
void libspdm_test_responder_certificate_case11(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_certificate_response_t *spdm_response;
    spdm_error_response_t *spdm_responseError;
    void *data;
    size_t data_size;

    uint16_t test_cases[] = { LIBSPDM_TEST_CERT_SMALL };

    size_t expected_chunk_size;
    size_t expected_remainder;

    /* This case requires a short certificate chain (fits in 1 message) for testing,
     * so skip when m_libspdm_use_asym_algo is other than ECC_P256 */
    if (m_libspdm_use_asym_algo !=
        SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_ECDSA_ECC_NIST_P256) {
        return;
    }

    /* Setting up the spdm_context and loading a sample certificate chain*/
    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 0xB;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_10 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;
    spdm_context->connection_info.connection_state =
        LIBSPDM_CONNECTION_STATE_AFTER_DIGESTS;
    spdm_context->local_context.capability.flags |=
        SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo =
        m_libspdm_use_hash_algo;

    m_libspdm_get_certificate_request3.length = LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN;
    m_libspdm_get_certificate_request3.offset = 0;

    for (int i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) {
        libspdm_read_responder_public_certificate_chain_by_size(
            m_libspdm_use_hash_algo, m_libspdm_use_asym_algo, test_cases[i], &data,
            &data_size, NULL, NULL);
        spdm_context->local_context.local_cert_chain_provision[0] =
            data;
        spdm_context->local_context.local_cert_chain_provision_size[0] =
            data_size;

        TEST_LIBSPDM_DEBUG_PRINT("data_size: %u\n", data_size);
        TEST_LIBSPDM_DEBUG_PRINT("m_libspdm_get_certificate_request3.offset: %u\n",
                                 m_libspdm_get_certificate_request3.offset);
        TEST_LIBSPDM_DEBUG_PRINT("m_libspdm_get_certificate_request3.length: %u\n",
                                 m_libspdm_get_certificate_request3.length);
        TEST_LIBSPDM_DEBUG_PRINT(
            "offset + length: %u\n",
            m_libspdm_get_certificate_request3.offset +
            m_libspdm_get_certificate_request3.length);

        /* reseting an internal buffer to avoid overflow and prevent tests to succeed*/
        libspdm_reset_message_b(spdm_context);
        response_size = sizeof(response);
        status = libspdm_get_response_certificate(
            spdm_context, m_libspdm_get_certificate_request3_size,
            &m_libspdm_get_certificate_request3, &response_size,
            response);
        assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);

        /* Expected received length is limited by LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN and by the remaining length*/
        expected_chunk_size =
            LIBSPDM_MIN(m_libspdm_get_certificate_request3.length,
                        data_size - m_libspdm_get_certificate_request3.offset);
        expected_chunk_size =
            LIBSPDM_MIN(expected_chunk_size, LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN);
        /* Expected certificate length left*/
        expected_remainder = data_size -
                             m_libspdm_get_certificate_request3.offset -
                             expected_chunk_size;

        TEST_LIBSPDM_DEBUG_PRINT("expected_chunk_size %u\n", expected_chunk_size);
        TEST_LIBSPDM_DEBUG_PRINT("expected_remainder %u\n", expected_remainder);

        if (expected_remainder > 0xFFFF ||
            expected_chunk_size > 0xFFFF) {
            spdm_responseError = (void *)response;
            assert_int_equal(
                spdm_responseError->header.request_response_code,
                SPDM_ERROR);
            assert_int_equal(spdm_responseError->header.param1,
                             SPDM_ERROR_CODE_INVALID_REQUEST);
        } else {
            assert_int_equal(response_size,
                             sizeof(spdm_certificate_response_t) +
                             expected_chunk_size);
            spdm_response = (void *)response;
            assert_int_equal(
                spdm_response->header.request_response_code,
                SPDM_CERTIFICATE);
            assert_int_equal(spdm_response->header.param1, 0);
            assert_int_equal(spdm_response->portion_length,
                             expected_chunk_size);
            assert_int_equal(spdm_response->remainder_length,
                             expected_remainder);
        }

        TEST_LIBSPDM_DEBUG_PRINT("\n");

        free(data);
    }
}

/**
 * Test 12: request a whole certificate chain byte by byte
 * Expected Behavior: generate correctly formed Certficate messages, including its portion_length and remainder_length fields
 **/
void libspdm_test_responder_certificate_case12(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_certificate_response_t *spdm_response;
    void *data;
    size_t data_size;
    uint16_t expected_chunk_size;
#if LIBSPDM_RECORD_TRANSCRIPT_DATA_SUPPORT
    size_t count;
#endif
    /* Setting up the spdm_context and loading a sample certificate chain*/
    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 0xC;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_10 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;
    spdm_context->connection_info.connection_state =
        LIBSPDM_CONNECTION_STATE_AFTER_DIGESTS;
    spdm_context->local_context.capability.flags |=
        SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo =
        m_libspdm_use_hash_algo;
    libspdm_read_responder_public_certificate_chain(m_libspdm_use_hash_algo,
                                                    m_libspdm_use_asym_algo, &data,
                                                    &data_size, NULL, NULL);
    spdm_context->local_context.local_cert_chain_provision[0] = data;
    spdm_context->local_context.local_cert_chain_provision_size[0] =
        data_size;

    /* This tests considers only length = 1*/
    m_libspdm_get_certificate_request3.length = 1;
    expected_chunk_size = 1;


    /* reseting an internal buffer to avoid overflow and prevent tests to succeed*/
    libspdm_reset_message_b(spdm_context);

    spdm_response = NULL;
    for (size_t offset = 0; offset < data_size; offset++) {
        TEST_LIBSPDM_DEBUG_PRINT("offset:%u \n", offset);
        m_libspdm_get_certificate_request3.offset = (uint16_t)offset;

        response_size = sizeof(response);
        status = libspdm_get_response_certificate(
            spdm_context, m_libspdm_get_certificate_request3_size,
            &m_libspdm_get_certificate_request3, &response_size,
            response);
        assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);
        spdm_response = (void *)response;
        /* It may fail because the spdm does not support too many messages.
         * assert_int_equal (spdm_response->header.request_response_code, SPDM_CERTIFICATE);*/
        if (spdm_response->header.request_response_code ==
            SPDM_CERTIFICATE) {
            assert_int_equal(
                spdm_response->header.request_response_code,
                SPDM_CERTIFICATE);
            assert_int_equal(response_size,
                             sizeof(spdm_certificate_response_t) +
                             expected_chunk_size);
            assert_int_equal(spdm_response->header.param1, 0);
            assert_int_equal(spdm_response->portion_length,
                             expected_chunk_size);
            assert_int_equal(spdm_response->remainder_length,
                             data_size - offset -
                             expected_chunk_size);
            assert_int_equal(
                ((uint8_t *)data)[offset],
                (response +
                 sizeof(spdm_certificate_response_t))[0]);
        } else {
            assert_int_equal(
                spdm_response->header.request_response_code,
                SPDM_ERROR);
            break;
        }
    }
    if (spdm_response != NULL) {
        if (spdm_response->header.request_response_code ==
            SPDM_CERTIFICATE) {
#if LIBSPDM_RECORD_TRANSCRIPT_DATA_SUPPORT
            count = (data_size + m_libspdm_get_certificate_request3.length - 1) /
                    m_libspdm_get_certificate_request3.length;
            assert_int_equal(
                spdm_context->transcript.message_b.buffer_size,
                sizeof(spdm_get_certificate_request_t) * count +
                sizeof(spdm_certificate_response_t) *
                count +
                data_size);
#endif
        }
    }
    free(data);
}

/**
 * Test 13: receiving a correct GET_CERTIFICATE from the requester. Buffer B
 * already has arbitrary data.
 * Expected behavior: the responder accepts the request and produces a valid
 * CERTIFICATE response message, and buffer B receives the exchanged
 * GET_CERTIFICATE and CERTIFICATE messages.
 **/
void libspdm_test_responder_certificate_case13(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_certificate_response_t *spdm_response;
    void *data;
    size_t data_size;
#if LIBSPDM_RECORD_TRANSCRIPT_DATA_SUPPORT
    size_t arbitrary_size;
#endif

    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 0xD;
    spdm_context->connection_info.connection_state =
        LIBSPDM_CONNECTION_STATE_AFTER_DIGESTS;
    spdm_context->local_context.capability.flags |=
        SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo = m_libspdm_use_hash_algo;
    libspdm_read_responder_public_certificate_chain(m_libspdm_use_hash_algo,
                                                    m_libspdm_use_asym_algo, &data,
                                                    &data_size, NULL, NULL);
    spdm_context->local_context.local_cert_chain_provision[0] = data;
    spdm_context->local_context.local_cert_chain_provision_size[0] = data_size;

#if LIBSPDM_RECORD_TRANSCRIPT_DATA_SUPPORT
    /*filling buffer B with arbitrary data*/
    arbitrary_size = 8;
    libspdm_set_mem(spdm_context->transcript.message_b.buffer, arbitrary_size, (uint8_t) 0xEE);
    spdm_context->transcript.message_b.buffer_size = arbitrary_size;
#endif

    response_size = sizeof(response);
    status = libspdm_get_response_certificate(
        spdm_context, m_libspdm_get_certificate_request1_size,
        &m_libspdm_get_certificate_request1, &response_size, response);
    assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);
    assert_int_equal(response_size, sizeof(spdm_certificate_response_t) +
                     LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN);
    spdm_response = (void *)response;
    assert_int_equal(spdm_response->header.request_response_code, SPDM_CERTIFICATE);
    assert_int_equal(spdm_response->header.param1, 0);
    assert_int_equal(spdm_response->portion_length, LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN);
    assert_int_equal(spdm_response->remainder_length,
                     data_size - LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN);

#if LIBSPDM_RECORD_TRANSCRIPT_DATA_SUPPORT
    assert_int_equal(spdm_context->transcript.message_b.buffer_size,
                     arbitrary_size + m_libspdm_get_certificate_request1_size + response_size);
    assert_memory_equal(spdm_context->transcript.message_b.buffer + arbitrary_size,
                        &m_libspdm_get_certificate_request1,
                        m_libspdm_get_certificate_request1_size);
    assert_memory_equal(spdm_context->transcript.message_b.buffer + arbitrary_size +
                        m_libspdm_get_certificate_request1_size,
                        response, response_size);
#endif

    free(data);
}

/**
 * Test 14: request the first LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN bytes of the certificate chain in a session
 * Expected Behavior: generate a correctly formed Certficate message, including its portion_length and remainder_length fields
 **/
void libspdm_test_responder_certificate_case14(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_certificate_response_t *spdm_response;
    void *data;
    size_t data_size;
    libspdm_session_info_t *session_info;
    uint32_t session_id;

    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 0xE;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_10 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;
    spdm_context->connection_info.connection_state =
        LIBSPDM_CONNECTION_STATE_AFTER_DIGESTS;
    spdm_context->local_context.capability.flags |=
        SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo =
        m_libspdm_use_hash_algo;

    session_id = 0xFFFFFFFF;
    spdm_context->latest_session_id = session_id;
    spdm_context->last_spdm_request_session_id_valid = true;
    spdm_context->last_spdm_request_session_id = session_id;
    session_info = &spdm_context->session_info[0];
    libspdm_session_info_init(spdm_context, session_info, session_id, true);
    libspdm_secured_message_set_session_state(
        session_info->secured_message_context,
        LIBSPDM_SESSION_STATE_ESTABLISHED);

    libspdm_read_responder_public_certificate_chain(m_libspdm_use_hash_algo,
                                                    m_libspdm_use_asym_algo, &data,
                                                    &data_size, NULL, NULL);
    spdm_context->local_context.local_cert_chain_provision[0] = data;
    spdm_context->local_context.local_cert_chain_provision_size[0] =
        data_size;

#if LIBSPDM_RECORD_TRANSCRIPT_DATA_SUPPORT
    session_info->session_transcript.message_m.buffer_size =
        session_info->session_transcript.message_m.max_buffer_size;
#endif

    response_size = sizeof(response);
    status = libspdm_get_response_certificate(
        spdm_context, m_libspdm_get_certificate_request1_size,
        &m_libspdm_get_certificate_request1, &response_size, response);
    assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);
    assert_int_equal(response_size, sizeof(spdm_certificate_response_t) +
                     LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN);
    spdm_response = (void *)response;
    assert_int_equal(spdm_response->header.request_response_code,
                     SPDM_CERTIFICATE);
    assert_int_equal(spdm_response->header.param1, 0);
    assert_int_equal(spdm_response->portion_length,
                     LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN);
    assert_int_equal(spdm_response->remainder_length,
                     data_size - LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN);
#if LIBSPDM_RECORD_TRANSCRIPT_DATA_SUPPORT
    assert_int_equal(session_info->session_transcript.message_m.buffer_size,
                     0);
#endif
    free(data);
}

/**
 * Test 15: Produce a CERTIFICATE response that is meant to be chunked.
 *          LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN is ignored when chunking is enabled.
 **/
void libspdm_test_responder_certificate_case15(void **state)
{
    libspdm_return_t status;
    bool ret;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    uint8_t request[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_get_certificate_request_t *spdm_cert_request;
    spdm_certificate_response_t *spdm_cert_response;
    void *data;
    size_t data_size;

    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 15;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_12 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;

    spdm_context->connection_info.connection_state = LIBSPDM_CONNECTION_STATE_NEGOTIATED;
    spdm_context->connection_info.algorithm.base_hash_algo = m_libspdm_use_hash_algo;
    spdm_context->connection_info.algorithm.base_asym_algo = m_libspdm_use_asym_algo;
    spdm_context->connection_info.capability.flags = 0;
    spdm_context->connection_info.capability.flags |= SPDM_GET_CAPABILITIES_REQUEST_FLAGS_CHUNK_CAP;
    spdm_context->connection_info.capability.data_transfer_size =
        SPDM_MIN_DATA_TRANSFER_SIZE_VERSION_12;

    spdm_context->local_context.algorithm.base_hash_algo = m_libspdm_use_hash_algo;
    spdm_context->local_context.algorithm.base_asym_algo = m_libspdm_use_asym_algo;
    spdm_context->local_context.capability.data_transfer_size =
        SPDM_MIN_DATA_TRANSFER_SIZE_VERSION_12;
    spdm_context->local_context.capability.flags |= SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CHUNK_CAP |
                                                    SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;

    ret = libspdm_read_responder_public_certificate_chain(
        m_libspdm_use_hash_algo, m_libspdm_use_asym_algo, &data,
        &data_size, NULL, NULL);
    assert_true(ret);

    spdm_context->local_context.local_cert_chain_provision[0] = data;
    spdm_context->local_context.local_cert_chain_provision_size[0] = data_size;

    spdm_cert_request = (spdm_get_certificate_request_t *) request;

    spdm_cert_request->header.spdm_version = SPDM_MESSAGE_VERSION_12;
    spdm_cert_request->header.request_response_code = SPDM_GET_CERTIFICATE;
    spdm_cert_request->header.param1 = 0;
    spdm_cert_request->header.param2 = 0;
    spdm_cert_request->offset = 0;
    spdm_cert_request->length = 0xffff;

    response_size = sizeof(response);
    libspdm_zero_mem(response, response_size);

    status = libspdm_get_response_certificate(
        spdm_context,
        sizeof(spdm_get_certificate_request_t), &request,
        &response_size, response);
    assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);

    spdm_cert_response = (spdm_certificate_response_t *) response;

    assert_int_equal(spdm_cert_response->header.spdm_version, SPDM_MESSAGE_VERSION_12);
    assert_int_equal(spdm_cert_response->header.request_response_code, SPDM_CERTIFICATE);
    assert_int_equal(spdm_cert_response->header.param1, 0);
    assert_int_equal(spdm_cert_response->header.param2, 0);
    assert_int_equal(spdm_cert_response->portion_length, data_size);
    assert_int_equal(spdm_cert_response->remainder_length, 0);

    free(data);
}

/**
 * Test 16: request a 0 byte certificate chain
 * Expected Behavior: portion length should be 0 and remainder length should be the size of the
 *                    certificate chain.
 **/
void libspdm_test_responder_certificate_case16(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_certificate_response_t *spdm_response;
    void *data;
    size_t data_size;

    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 16;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_10 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;
    spdm_context->connection_info.connection_state = LIBSPDM_CONNECTION_STATE_AFTER_DIGESTS;
    spdm_context->local_context.capability.flags |= SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo = m_libspdm_use_hash_algo;
    libspdm_read_responder_public_certificate_chain(m_libspdm_use_hash_algo,
                                                    m_libspdm_use_asym_algo, &data,
                                                    &data_size, NULL, NULL);
    spdm_context->local_context.local_cert_chain_provision[0] = data;
    spdm_context->local_context.local_cert_chain_provision_size[0] = data_size;

    m_libspdm_get_certificate_request3.offset = 0;
    m_libspdm_get_certificate_request3.length = 0;

    response_size = sizeof(response);
    status = libspdm_get_response_certificate(
        spdm_context, m_libspdm_get_certificate_request3_size,
        &m_libspdm_get_certificate_request3, &response_size, response);
    assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);
    assert_int_equal(response_size, sizeof(spdm_certificate_response_t));
    spdm_response = (void *)response;
    assert_int_equal(spdm_response->header.request_response_code, SPDM_CERTIFICATE);
    assert_int_equal(spdm_response->header.param1, 0);
    assert_int_equal(spdm_response->portion_length, 0);
    assert_int_equal(spdm_response->remainder_length, data_size);

    free(data);
}

/**
 * Test 17: SlotID in GET_CERTIFICATE request message is 9, but it should be between 0 and 7 inclusive.
 * Expected Behavior: generate an ERROR_RESPONSE with code SPDM_ERROR_CODE_INVALID_REQUEST.
 **/
void libspdm_test_responder_certificate_case17(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_certificate_response_t *spdm_response;
    void *data;
    size_t data_size;

    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 17;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_10 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;
    spdm_context->connection_info.connection_state =
        LIBSPDM_CONNECTION_STATE_AFTER_DIGESTS;
    spdm_context->local_context.capability.flags |=
        SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo =
        m_libspdm_use_hash_algo;
    libspdm_read_responder_public_certificate_chain(m_libspdm_use_hash_algo,
                                                    m_libspdm_use_asym_algo, &data,
                                                    &data_size, NULL, NULL);
    spdm_context->local_context.local_cert_chain_provision[0] = data;
    spdm_context->local_context.local_cert_chain_provision_size[0] =
        data_size;

    response_size = sizeof(response);
    status = libspdm_get_response_certificate(
        spdm_context, m_libspdm_get_certificate_request4_size,
        &m_libspdm_get_certificate_request4, &response_size, response);
    assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);
    assert_int_equal(response_size, sizeof(spdm_error_response_t));
    spdm_response = (void *)response;
    assert_int_equal(spdm_response->header.request_response_code,
                     SPDM_ERROR);
    assert_int_equal(spdm_response->header.param1,
                     SPDM_ERROR_CODE_INVALID_REQUEST);
    assert_int_equal(spdm_response->header.param2, 0);
    free(data);
}

/**
 * Test 18: check request attributes and response attributes , SlotSizeRequested=1b the Offset and Length fields in the
 * GET_CERTIFICATE request shall be ignored by the Responde
 * Expected Behavior: generate a correctly formed Certficate message, including its portion_length and remainder_length fields
 **/
void libspdm_test_responder_certificate_case18(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_certificate_response_t *spdm_response;
    void *data;
    size_t data_size;

    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 18;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_13 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;
    spdm_context->connection_info.connection_state =
        LIBSPDM_CONNECTION_STATE_AFTER_DIGESTS;
    spdm_context->local_context.capability.flags |=
        SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo =
        m_libspdm_use_hash_algo;
    libspdm_read_responder_public_certificate_chain(m_libspdm_use_hash_algo,
                                                    m_libspdm_use_asym_algo, &data,
                                                    &data_size, NULL, NULL);
    spdm_context->local_context.local_cert_chain_provision[0] = data;
    spdm_context->local_context.local_cert_chain_provision_size[0] =
        data_size;

    /* When SlotSizeRequested=1b , the Offset and Length fields in the GET_CERTIFICATE request shall be ignored by the Responder */
    m_libspdm_get_certificate_request5.header.param2 =
        SPDM_GET_CERTIFICATE_REQUEST_ATTRIBUTES_SLOT_SIZE_REQUESTED;
    m_libspdm_get_certificate_request5.length = LIBSPDM_MAX_CERT_CHAIN_BLOCK_LEN;
    m_libspdm_get_certificate_request5.offset = 0xFF;

    response_size = sizeof(response);
    status = libspdm_get_response_certificate(
        spdm_context, m_libspdm_get_certificate_request5_size,
        &m_libspdm_get_certificate_request5, &response_size, response);
    assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);
    assert_int_equal(response_size, sizeof(spdm_certificate_response_t));
    spdm_response = (void *)response;
    assert_int_equal(spdm_response->header.request_response_code,
                     SPDM_CERTIFICATE);
    assert_int_equal(spdm_response->header.param1, 0);
    assert_int_equal(spdm_response->portion_length,0);
    assert_int_equal(spdm_response->remainder_length, data_size);
    free(data);
}

/**
 * Test 19: Attempt to retrieve a certificate chain from a slot that needs to be reset.
 * Expected Behavior: Responder responds with ResetRequired.
 **/
void libspdm_test_responder_certificate_case19(void **state)
{
    libspdm_return_t status;
    libspdm_test_context_t *spdm_test_context;
    libspdm_context_t *spdm_context;
    size_t response_size;
    uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE];
    spdm_error_response_t *spdm_response;
    void *data;
    size_t data_size;
    const uint8_t slot_id = 5;

    spdm_test_context = *state;
    spdm_context = spdm_test_context->spdm_context;
    spdm_test_context->case_id = 19;
    spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_13 <<
                                            SPDM_VERSION_NUMBER_SHIFT_BIT;
    spdm_context->connection_info.connection_state = LIBSPDM_CONNECTION_STATE_AFTER_DIGESTS;
    spdm_context->local_context.capability.flags |= SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP;
    spdm_context->connection_info.algorithm.base_hash_algo = m_libspdm_use_hash_algo;
    libspdm_read_responder_public_certificate_chain(m_libspdm_use_hash_algo,
                                                    m_libspdm_use_asym_algo, &data,
                                                    &data_size, NULL, NULL);
    spdm_context->local_context.local_cert_chain_provision[0] = data;
    spdm_context->local_context.local_cert_chain_provision_size[0] = data_size;

    /* Responder needs to be reset before certificate can be retrieved from specified SlotID. */
    m_libspdm_get_certificate_request5.header.param1 = slot_id;
    spdm_context->local_context.cert_slot_reset_mask = 1 << slot_id;

    response_size = sizeof(response);
    status = libspdm_get_response_certificate(
        spdm_context, m_libspdm_get_certificate_request5_size,
        &m_libspdm_get_certificate_request5, &response_size, response);

    assert_int_equal(status, LIBSPDM_STATUS_SUCCESS);
    assert_int_equal(response_size, sizeof(spdm_error_response_t));
    spdm_response = (void *)response;
    assert_int_equal(spdm_response->header.request_response_code, SPDM_ERROR);
    assert_int_equal(spdm_response->header.param1, SPDM_ERROR_CODE_RESET_REQUIRED);
    assert_int_equal(spdm_response->header.param2, 0);

    free(data);
}

int libspdm_responder_certificate_test_main(void)
{
    const struct CMUnitTest spdm_responder_certificate_tests[] = {
        /* Success Case*/
        cmocka_unit_test(libspdm_test_responder_certificate_case1),
        /* Can be populated with new test.*/
        cmocka_unit_test(libspdm_test_responder_certificate_case2),
        /* response_state: LIBSPDM_RESPONSE_STATE_BUSY*/
        cmocka_unit_test(libspdm_test_responder_certificate_case3),
        /* response_state: LIBSPDM_RESPONSE_STATE_NEED_RESYNC*/
        cmocka_unit_test(libspdm_test_responder_certificate_case4),
        #if LIBSPDM_RESPOND_IF_READY_SUPPORT
        /* response_state: LIBSPDM_RESPONSE_STATE_NOT_READY*/
        cmocka_unit_test(libspdm_test_responder_certificate_case5),
        #endif /* LIBSPDM_RESPOND_IF_READY_SUPPORT */
        /* connection_state Check*/
        cmocka_unit_test(libspdm_test_responder_certificate_case6),
        /* Tests varying length*/
        cmocka_unit_test(libspdm_test_responder_certificate_case7),
        /* Tests varying offset*/
        cmocka_unit_test(libspdm_test_responder_certificate_case8),
        /* Tests varying length and offset*/
        cmocka_unit_test(libspdm_test_responder_certificate_case9),
        /* Tests large certificate chains*/
        cmocka_unit_test(libspdm_test_responder_certificate_case10),
        /* Certificate fits in one single message*/
        cmocka_unit_test(libspdm_test_responder_certificate_case11),
        /* Requests byte by byte*/
        cmocka_unit_test(libspdm_test_responder_certificate_case12),
        /* Buffer verification*/
        cmocka_unit_test(libspdm_test_responder_certificate_case13),
        /* Success Case in a session*/
        cmocka_unit_test(libspdm_test_responder_certificate_case14),
        /* Produce a CERTIFICATE response that is meant to be chunked. */
        cmocka_unit_test(libspdm_test_responder_certificate_case15),
        cmocka_unit_test(libspdm_test_responder_certificate_case16),
        /* Bad SlotID in request message */
        cmocka_unit_test(libspdm_test_responder_certificate_case17),
        /* check request attributes and response attributes*/
        cmocka_unit_test(libspdm_test_responder_certificate_case18),
        cmocka_unit_test(libspdm_test_responder_certificate_case19),
    };

    libspdm_test_context_t test_context = {
        LIBSPDM_TEST_CONTEXT_VERSION,
        false,
    };

    libspdm_setup_test_context(&test_context);

    return cmocka_run_group_tests(spdm_responder_certificate_tests,
                                  libspdm_unit_test_group_setup,
                                  libspdm_unit_test_group_teardown);
}

#endif /* LIBSPDM_ENABLE_CAPABILITY_CERT_CAP*/
