/**
 * \file
 * \brief  Cryptoauthlib Testing: Common Resources & Functions
 *
 * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries.
 *
 * \page License
 *
 * Subject to your compliance with these terms, you may use Microchip software
 * and any derivatives exclusively with Microchip products. It is your
 * responsibility to comply with third party license terms applicable to your
 * use of third party software (including open source software) that may
 * accompany Microchip software.
 *
 * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
 * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
 * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
 * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT,
 * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE
 * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF
 * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE
 * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL
 * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED
 * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR
 * THIS SOFTWARE.
 */
#include "atca_test.h"
#include "atca_execution.h"
#include "app/tng/tng_atca.h"

extern tng_type_t g_tng_test_type;

// gCfg must point to one of the cfg_ structures for any unit test to work.  this allows
// the command console to switch device types at runtime.
ATCAIfaceCfg g_iface_config = {
    .iface_type            = ATCA_UNKNOWN_IFACE,
    .devtype               = ATCA_DEV_UNKNOWN,
    {
        .atcai2c           = {
            .slave_address = 0xC0,
            .bus           = 2,
            .baud          = 400000,
        },
    },
    .wake_delay            = 1500,
    .rx_retries            = 20
};

ATCAIfaceCfg *gCfg = &g_iface_config;

const uint8_t g_slot4_key[] = {
    0x37, 0x80, 0xe6, 0x3d, 0x49, 0x68, 0xad, 0xe5,
    0xd8, 0x22, 0xc0, 0x13, 0xfc, 0xc3, 0x23, 0x84,
    0x5d, 0x1b, 0x56, 0x9f, 0xe7, 0x05, 0xb6, 0x00,
    0x06, 0xfe, 0xec, 0x14, 0x5a, 0x0d, 0xb1, 0xe3
};

uint8_t test_ecc608_configdata[ATCA_ECC_CONFIG_SIZE] = {
    0x01, 0x23, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x04, 0x05, 0x06, 0x07, 0xEE, 0x01, 0x01, 0x00,  //15
    0xC0, 0x00, 0xA1, 0x00, 0xAF, 0x2F, 0xC4, 0x44, 0x87, 0x20, 0xC4, 0xF4, 0x8F, 0x0F, 0x0F, 0x0F,  //31, 5
    0x9F, 0x8F, 0x83, 0x64, 0xC4, 0x44, 0xC4, 0x64, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,  //47
    0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,  //63
    0x00, 0x00, 0x00, 0x00, 0xFF, 0x84, 0x03, 0xBC, 0x09, 0x69, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00,  //79
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x0E, 0x40, 0x00, 0x00, 0x00, 0x00,  //95
    0x33, 0x00, 0x1C, 0x00, 0x13, 0x00, 0x1C, 0x00, 0x3C, 0x00, 0x3E, 0x00, 0x1C, 0x00, 0x33, 0x00,  //111
    0x1C, 0x00, 0x1C, 0x00, 0x38, 0x10, 0x30, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x32, 0x00, 0x30, 0x00   //127
};

const uint8_t test_ecc_configdata[ATCA_ECC_CONFIG_SIZE] = {
    0x01, 0x23, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x04, 0x05, 0x06, 0x07, 0xEE, 0x00, 0x01, 0x00, //15
    0xC0, 0x00, 0x55, 0x00, 0x8F, 0x2F, 0xC4, 0x44, 0x87, 0x20, 0xC4, 0xF4, 0x8F, 0x0F, 0x8F, 0x8F, //31, 5
    0x9F, 0x8F, 0x83, 0x64, 0xC4, 0x44, 0xC4, 0x64, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, //47
    0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, //63
    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //79
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //95
    0x33, 0x00, 0x1C, 0x00, 0x13, 0x00, 0x1C, 0x00, 0x3C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x33, 0x00, //111
    0x1C, 0x00, 0x1C, 0x00, 0x3C, 0x00, 0x30, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x32, 0x00, 0x30, 0x00  //127
};

const uint8_t sha204_default_config[ATCA_SHA_CONFIG_SIZE] = {
    // block 0
    // Not Written: First 16 bytes are not written
    0x01, 0x23, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0xEE, 0x55, 0x00, 0x00,
    // I2C, CheckMacConfig, OtpMode, SelectorMode
    0xC8, 0x00, 0x55, 0x00,
    // SlotConfig
    0x8F, 0x80, 0x80, 0xA1,
    0x82, 0xE0, 0xC4, 0xF4,
    0x84, 0x00, 0xA0, 0x85,
    // block 1
    0x86, 0x40, 0x87, 0x07,
    0x0F, 0x00, 0xC4, 0x64,
    0x8A, 0x7A, 0x0B, 0x8B,
    0x0C, 0x4C, 0xDD, 0x4D,
    0xC2, 0x42, 0xAF, 0x8F,
    // Use Flags
    0xFF, 0x00, 0xFF, 0x00,
    0xFF, 0x00, 0xFF, 0x00,
    0xFF, 0x00, 0xFF, 0x00,
    // block 2
    0xFF, 0x00, 0xFF, 0x00,
    // Last Key Use
    0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF,
    // Not Written: UserExtra, Selector, LockData, LockConfig (word offset = 5)
    0x00, 0x00, 0x55, 0x55,
};

// These keys are chosen specifically to test the CMAC subkey generation code.
// When the keys are used to encrypt an all-zero block we need all bit
// combinations of the uppermost 2 bits (0b00, 0b01, 0b10, 0b11)
// 2B7E151628AED2A6ABF7158809CF4F3C AESEnc(0)=7DF76B0C1AB899B33E42F047B91B546F 7D=0b01 111101
// 6BE163D42B623E70D164FA145DB1D463 AESEnc(0)=EEA8C3FD920AC8D3D3A424E473C56B4A EE=0b11 101110
// 7058710B58E1E665D3D2F5B465176403 AESEnc(0)=38AE4CF5CAB844CF6D1463044C8749AE 38=0b00 111000
// 114443FA8E9614845EC7296CD13BC9DC AESEnc(0)=863496604DDD579049A63908D49853D5 86=0b10 000110
// The first key is one commonly used by NIST for some AES test vectors
const uint8_t g_aes_keys[4][16] = {
    { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C },
    { 0x6B, 0xE1, 0x63, 0xD4, 0x2B, 0x62, 0x3E, 0x70, 0xD1, 0x64, 0xFA, 0x14, 0x5D, 0xB1, 0xD4, 0x63 },
    { 0x70, 0x58, 0x71, 0x0B, 0x58, 0xE1, 0xE6, 0x65, 0xD3, 0xD2, 0xF5, 0xB4, 0x65, 0x17, 0x64, 0x03 },
    { 0x11, 0x44, 0x43, 0xFA, 0x8E, 0x96, 0x14, 0x84, 0x5E, 0xC7, 0x29, 0x6C, 0xD1, 0x3B, 0xC9, 0xDC }
};

// Input plaintext for testing.
// This input test data is commonly used by NIST for some AES test vectors
const uint8_t g_plaintext[64] = {
    0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
    0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
    0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
    0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
};

t_test_case_info* basic_tests[] =
{
    startup_basic_test_info,
    info_basic_test_info,
    aes_basic_test_info,
    aes_cbc_basic_test_info,
    aes_cmac_basic_test_info,
    aes_ctr_basic_test_info,
    aes_gcm_basic_test_info,
    verify_basic_test_info,
    derivekey_basic_test_info,
    sha_basic_test_info,
    hmac_basic_test_info,
    sign_basic_test_info,
    mac_basic_test_info,
    ecdh_basic_test_info,
    write_basic_test_info,
    read_basic_test_info,
    genkey_basic_test_info,
    privwrite_basic_test_info,
    lock_basic_test_info,
    kdf_basic_test_info,
    sboot_basic_test_info,
    selftest_basic_test_info,
    gendig_basic_test_info,
    random_basic_test_info,
    nonce_basic_test_info,
    pause_basic_test_info,
    updateextra_basic_test_info,
    counter_basic_test_info,
    (t_test_case_info*)NULL, /* Array Termination element*/
};

t_test_case_info* unit_tests[] =
{
    startup_unit_test_info,
    info_unit_test_info,
    aes_unit_test_info,
    verify_unit_test_info,
    derivekey_unit_test_info,
    sha_unit_test_info,
    hmac_unit_test_info,
    sign_unit_test_info,
    mac_unit_test_info,
    ecdh_unit_test_info,
    write_unit_test_info,
    read_unit_test_info,
    genkey_unit_test_info,
    privwrite_unit_test_info,
    lock_unit_test_info,
    kdf_unit_test_info,
    sboot_unit_test_info,
    selftest_unit_test_info,
    gendig_unit_test_info,
    random_unit_test_info,
    nonce_unit_test_info,
    pause_unit_test_info,
    updateextra_unit_test_info,
    counter_unit_test_info,
    (t_test_case_info*)NULL, /* Array Termination element*/
};

t_test_case_info* otpzero_tests[] =
{
    otpzero_basic_test_info,
    (t_test_case_info*)NULL, /* Array Termination element*/
};

t_test_case_info* helper_tests[] =
{
    helper_basic_test_info,
    jwt_unit_test_info,
    (t_test_case_info*)NULL, /* Array Termination element*/
};

t_test_case_info* tng_tests[] =
{
    tng_atca_unit_test_info,
#ifndef DO_NOT_TEST_CERT
    tng_atcacert_client_unit_test_info,
#endif
    (t_test_case_info*)NULL, /* Array Termination element*/
};


void RunAllTests(t_test_case_info** tests_list)
{
    t_test_case_info* sp_current_test;
    uint8_t support_device_mask;

    /*Loop through all the commands test info*/
    while (*tests_list != NULL)
    {
        /*Get current command test info*/
        sp_current_test = *tests_list;

        /*Loop through till last test in the test info*/
        while (sp_current_test->fp_test != NULL)
        {
            /*Get current device mask*/
            support_device_mask = DEVICE_MASK(gCfg->devtype);

            /*check if current test mask contains current device mask*/
            if ((sp_current_test->support_device_mask & support_device_mask) == support_device_mask)
            {
                /*Execute current test case*/
                sp_current_test->fp_test();
            }

            /*Move to next test*/
            sp_current_test++;
        }

        /*Move to next command*/
        tests_list++;
    }
}

void RunAllBasicTests(void)
{
    RunAllTests(basic_tests);
};

void RunAllFeatureTests(void)
{
    RunAllTests(unit_tests);
}

void RunBasicOtpZero(void)
{
    RunAllTests(otpzero_tests);
}

void RunAllHelperTests(void)
{
    RunAllTests(helper_tests);
}

void RunTNGTLSTests(void)
{
    g_tng_test_type = TNGTYPE_TLS;
    RunAllTests(tng_tests);
}

void RunTNGLORATests(void)
{
    g_tng_test_type = TNGTYPE_LORA;
    RunAllTests(tng_tests);
}


static bool atcau_is_locked(uint8_t zone)
{
    ATCA_STATUS status = ATCA_GEN_FAIL;
    ATCAPacket packet;
    ATCACommand ca_cmd = _gDevice->mCommands;

    // build an read command
    packet.param1 = 0x00;
    packet.param2 = 0x15;
    status = atRead(ca_cmd, &packet);
    TEST_ASSERT_EQUAL(ATCA_SUCCESS, status);

    status = atca_execute_command(&packet, _gDevice);
    TEST_ASSERT_EQUAL(ATCA_SUCCESS, status);

    switch (zone)
    {
    case LOCK_ZONE_DATA:
        return packet.data[ATCA_RSP_DATA_IDX + 2] == 0;
        break;
    case LOCK_ZONE_CONFIG:
        return packet.data[ATCA_RSP_DATA_IDX + 3] == 0;
        break;
    default:
        TEST_FAIL_MESSAGE("Invalid lock zone");
        break;
    }

    return false;
}

#ifdef ATCA_NO_HEAP
extern struct atca_device g_atcab_device;
extern struct atca_command g_atcab_command;
extern struct atca_iface g_atcab_iface;
#endif

/**
 * \brief Initialize the interface and check it was successful
 */
void test_assert_interface_init(void)
{
#ifdef ATCA_NO_HEAP
    ATCA_STATUS status;
#endif

    // If the device is still connected - disconnect it
    if (_gDevice)
    {
#ifdef ATCA_NO_HEAP
        status = releaseATCADevice(_gDevice);
        _gDevice = NULL;
#else
        deleteATCADevice(&_gDevice);
        TEST_ASSERT_NULL(_gDevice);
#endif
    }

    // Get the device
#ifdef ATCA_NO_HEAP
    g_atcab_device.mCommands = &g_atcab_command;
    g_atcab_device.mIface    = &g_atcab_iface;
    status = initATCADevice(gCfg, &g_atcab_device);
    TEST_ASSERT_EQUAL(ATCA_SUCCESS, status);
    _gDevice = &g_atcab_device;
#else
    _gDevice = newATCADevice(gCfg);
    TEST_ASSERT_NOT_NULL(_gDevice);
#endif

    if (_gDevice->mCommands->dt == ATECC608A)
    {
        // Set the clock divider, which should be the same value as the test config
        _gDevice->mCommands->clock_divider = test_ecc608_configdata[ATCA_CHIPMODE_OFFSET] & ATCA_CHIPMODE_CLOCK_DIV_MASK;
    }
}

/**
 * \brief Clean up the allocated interface
 */
void test_assert_interface_deinit(void)
{
    ATCA_STATUS status;

    TEST_ASSERT((_gDevice != NULL) && (_gDevice->mIface != NULL));

    status = atwake(_gDevice->mIface);
    TEST_ASSERT_EQUAL(ATCA_SUCCESS, status);

    status = atsleep(_gDevice->mIface);
    TEST_ASSERT_EQUAL(ATCA_SUCCESS, status);

#ifdef ATCA_NO_HEAP
    status = releaseATCADevice(_gDevice);
    TEST_ASSERT_EQUAL(ATCA_SUCCESS, status);
    _gDevice = NULL;
#else
    deleteATCADevice(&_gDevice);
    TEST_ASSERT_NULL(_gDevice);
#endif
}

void test_assert_config_is_unlocked(void)
{
    bool is_locked = false;
    ATCA_STATUS status = atcab_is_locked(LOCK_ZONE_CONFIG, &is_locked);

    TEST_ASSERT_EQUAL(ATCA_SUCCESS, status);

    if (is_locked)
    {
        TEST_IGNORE_MESSAGE("Config zone must be unlocked for this test.");
    }
}

void test_assert_config_is_locked(void)
{
    bool is_locked = false;
    ATCA_STATUS status = atcab_is_locked(LOCK_ZONE_CONFIG, &is_locked);

    TEST_ASSERT_EQUAL(ATCA_SUCCESS, status);

    if (!is_locked)
    {
        TEST_IGNORE_MESSAGE("Config zone must be locked for this test.");
    }
}

void test_assert_data_is_unlocked(void)
{
    bool is_locked = false;
    ATCA_STATUS status = atcab_is_locked(LOCK_ZONE_DATA, &is_locked);

    TEST_ASSERT_EQUAL(ATCA_SUCCESS, status);

    if (is_locked)
    {
        TEST_IGNORE_MESSAGE("Data zone must be unlocked for this test.");
    }
}

void test_assert_data_is_locked(void)
{
    bool is_locked = false;
    ATCA_STATUS status = atcab_is_locked(LOCK_ZONE_DATA, &is_locked);

    TEST_ASSERT_EQUAL(ATCA_SUCCESS, status);

    if (!is_locked)
    {
        TEST_IGNORE_MESSAGE("Data zone must be locked for this test.");
    }
}


//The Function checks the AES_ENABLE byte in configuration zone , if it is not set, it skips the test
void check_config_aes_enable(void)
{
    ATCA_STATUS status;
    uint8_t aes_enable;

    // Byte 13 of the config zone contains the AES enable bit
    status = atcab_read_bytes_zone(ATCA_ZONE_CONFIG, 0, 13, &aes_enable, 1);
    TEST_ASSERT_EQUAL(ATCA_SUCCESS, status);

    if ((aes_enable & AES_CONFIG_ENABLE_BIT_MASK) == 0)
    {
        TEST_IGNORE_MESSAGE("Ignoring the test, AES is not enabled in config zone");
    }
}

void unit_test_assert_config_is_locked(void)
{
    if (!atcau_is_locked(LOCK_ZONE_CONFIG))
    {
        TEST_IGNORE_MESSAGE("Config zone must be locked for this test.");
    }
}
void unit_test_assert_config_is_unlocked(void)
{
    if (atcau_is_locked(LOCK_ZONE_CONFIG))
    {
        TEST_IGNORE_MESSAGE("Config zone must be unlocked for this test.");
    }
}

void unit_test_assert_data_is_locked(void)
{
    if (!atcau_is_locked(LOCK_ZONE_DATA))
    {
        TEST_IGNORE_MESSAGE("Data zone must be locked for this test.");
    }
}

void unit_test_assert_data_is_unlocked(void)
{
    if (atcau_is_locked(LOCK_ZONE_DATA))
    {
        TEST_IGNORE_MESSAGE("Data zone must be unlocked for this test.");
    }
}


