/* Copyright (c) 2010 - 2020, Nordic Semiconductor ASA
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <string.h>
#include <unity.h>

#include "ccm_soft.h"
#include "nrf_mesh_assert.h"

#define TEST_VECTORS 8

typedef struct
{
    uint32_t mic_len;
    uint32_t payload_len;
    uint32_t additional_data_len;
    uint8_t * p_unencrypted;
    uint8_t * p_encrypted;
    uint8_t * p_additional_data;
    uint8_t * p_key;
    uint8_t * p_nonce;
    uint8_t * p_mic;
} test_vector_t;

#define BUILD_TEST_VECTOR(p_VECTOR) do {                                  \
        TEST_ASSERT_EQUAL_MESSAGE(sizeof(unencrypted), sizeof(encrypted), \
                "Length of enc and unenc are different");                 \
        (p_VECTOR)->mic_len       = sizeof(mic);                          \
        (p_VECTOR)->payload_len   = sizeof(unencrypted);                  \
        (p_VECTOR)->additional_data_len   = sizeof(additional_data);      \
        (p_VECTOR)->p_unencrypted = unencrypted;                          \
        (p_VECTOR)->p_encrypted   = encrypted;                            \
        (p_VECTOR)->p_additional_data   = additional_data;                \
        (p_VECTOR)->p_key         = p_key;                                \
        (p_VECTOR)->p_nonce       = nonce;                                \
        (p_VECTOR)->p_mic         = mic;                                  \
    } while (0)

static void get_test_vector(uint32_t i, test_vector_t * p_vector)
{
    /* Based off test vectors in RFC 3610 */
    static uint8_t key[] = {0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF};
    switch (i)
    {
        case 0: /* RFC Vector #1 */
        {
            static uint8_t unencrypted[] = {0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E};
            static uint8_t encrypted[] = {0x58, 0x8C, 0x97, 0x9A, 0x61, 0xC6, 0x63, 0xD2, 0xF0, 0x66, 0xD0, 0xC2, 0xC0, 0xF9, 0x89, 0x80, 0x6D, 0x5F, 0x6B, 0x61, 0xDA, 0xC3, 0x84};
            static uint8_t additional_data[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
            static uint8_t nonce[] = {0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5};
            static uint8_t mic[] = {0x17, 0xE8, 0xD1, 0x2C, 0xFD, 0xF9, 0x26, 0xE0};
            static uint8_t * p_key = key;
            BUILD_TEST_VECTOR(p_vector);
            break;
        }
        case 1: /* RFC Vector #2 */
        {
            static uint8_t unencrypted[] = {0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F};
            static uint8_t encrypted[] = {0x72, 0xC9, 0x1A, 0x36, 0xE1, 0x35, 0xF8, 0xCF, 0x29, 0x1C, 0xA8, 0x94, 0x08, 0x5C, 0x87, 0xE3, 0xCC, 0x15, 0xC4, 0x39, 0xC9, 0xE4, 0x3A, 0x3B};
            static uint8_t additional_data[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
            static uint8_t nonce[] = {0x00, 0x00, 0x00, 0x04, 0x03, 0x02, 0x01, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5};
            static uint8_t mic[] = {0xA0, 0x91, 0xD5, 0x6E, 0x10, 0x40, 0x09, 0x16};
            static uint8_t * p_key = key;
            BUILD_TEST_VECTOR(p_vector);
            break;
        }
        case 2: /* RFC Vector #3 */
        {
            static uint8_t unencrypted[] = {0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20};
            static uint8_t encrypted[] = {0x51, 0xB1, 0xE5, 0xF4, 0x4A, 0x19, 0x7D, 0x1D, 0xA4, 0x6B, 0x0F, 0x8E, 0x2D, 0x28, 0x2A, 0xE8, 0x71, 0xE8, 0x38, 0xBB, 0x64, 0xDA, 0x85, 0x96, 0x57};
            static uint8_t additional_data[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
            static uint8_t nonce[] = {0x00, 0x00, 0x00, 0x05, 0x04, 0x03, 0x02, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5};
            static uint8_t mic[] = {0x4A, 0xDA, 0xA7, 0x6F, 0xBD, 0x9F, 0xB0, 0xC5};
            static uint8_t * p_key = key;
            BUILD_TEST_VECTOR(p_vector);
            break;
        }
        case 3: /* RFC Vector #4 */
        {
            static uint8_t unencrypted[] = {0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E};
            static uint8_t encrypted[] = {0xA2, 0x8C, 0x68, 0x65, 0x93, 0x9A, 0x9A, 0x79, 0xFA, 0xAA, 0x5C, 0x4C, 0x2A, 0x9D, 0x4A, 0x91, 0xCD, 0xAC, 0x8C};
            static uint8_t additional_data[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B};
            static uint8_t nonce[] = {0x00, 0x00, 0x00, 0x06, 0x05, 0x04, 0x03, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5};
            static uint8_t mic[] = {0x96, 0xC8, 0x61, 0xB9, 0xC9, 0xE6, 0x1E, 0xF1};
            static uint8_t * p_key = key;
            BUILD_TEST_VECTOR(p_vector);
            break;
        }
        case 4: /* RFC Vector #5 */
        {
            static uint8_t unencrypted[] = {0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F};
            static uint8_t encrypted[] = {0xDC, 0xF1, 0xFB, 0x7B, 0x5D, 0x9E, 0x23, 0xFB, 0x9D, 0x4E, 0x13, 0x12, 0x53, 0x65, 0x8A, 0xD8, 0x6E, 0xBD, 0xCA, 0x3E};
            static uint8_t additional_data[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B};
            static uint8_t nonce[] = {0x00, 0x00, 0x00, 0x07, 0x06, 0x05, 0x04, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5};
            static uint8_t mic[] = {0x51, 0xE8, 0x3F, 0x07, 0x7D, 0x9C, 0x2D, 0x93};
            static uint8_t * p_key = key;
            BUILD_TEST_VECTOR(p_vector);
            break;
        }
        case 5: /* RFC Vector #6 */
        {
            static uint8_t unencrypted[] = {0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20};
            static uint8_t encrypted[] = {0x6F, 0xC1, 0xB0, 0x11, 0xF0, 0x06, 0x56, 0x8B,  0x51, 0x71, 0xA4, 0x2D,  0x95, 0x3D, 0x46, 0x9B,  0x25, 0x70, 0xA4, 0xBD, 0x87};
            static uint8_t additional_data[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B};
            static uint8_t nonce[] = {0x00, 0x00, 0x00, 0x08, 0x07, 0x06, 0x05, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5};
            static uint8_t mic[] = {0x40, 0x5A, 0x04,  0x43, 0xAC, 0x91, 0xCB,  0x94};
            static uint8_t * p_key = key;
            BUILD_TEST_VECTOR(p_vector);
            break;
        }
        case 6: /* RFC Vector #7 */
        {
            static uint8_t unencrypted[] = {0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E};
            static uint8_t encrypted[] = {0x01, 0x35, 0xD1, 0xB2,  0xC9, 0x5F, 0x41, 0xD5, 0xD1, 0xD4, 0xFE, 0xC1,  0x85, 0xD1, 0x66, 0xB8,  0x09, 0x4E, 0x99, 0x9D,  0xFE, 0xD9, 0x6C};
            static uint8_t additional_data[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
            static uint8_t nonce[] = {0x00, 0x00, 0x00, 0x09, 0x08, 0x07, 0x06, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5};
            static uint8_t mic[] = {0x04, 0x8C, 0x56, 0x60, 0x2C,  0x97, 0xAC, 0xBB, 0x74,  0x90};
            static uint8_t * p_key = key;
            BUILD_TEST_VECTOR(p_vector);
            break;
        }
        case 7: /* RFC Vector #24 */
        {
            static uint8_t unencrypted[] = {0xAB, 0xF2, 0x1C, 0x0B, 0x02, 0xFE, 0xB8, 0x8F,  0x85, 0x6D, 0xF4, 0xA3,  0x73, 0x81, 0xBC, 0xE3,  0xCC, 0x12, 0x85, 0x17, 0xD4};
            static uint8_t encrypted[] = {0xF3, 0x29, 0x05, 0xB8, 0x8A, 0x64, 0x1B, 0x04,  0xB9, 0xC9, 0xFF, 0xB5,  0x8C, 0xC3, 0x90, 0x90,  0x0F, 0x3D, 0xA1, 0x2A, 0xB1};
            static uint8_t additional_data[] = {0x6E, 0x37, 0xA6, 0xEF,  0x54, 0x6D, 0x95, 0x5D,  0x34, 0xAB, 0x60, 0x59};
            static uint8_t nonce[] = {0x00, 0x8D, 0x49, 0x3B,  0x30, 0xAE, 0x8B, 0x3C,  0x96, 0x96, 0x76, 0x6C, 0xFA};
            static uint8_t mic[] = {0x6D, 0xCE, 0x9E,  0x82, 0xEF, 0xA1, 0x6D,  0xA6, 0x20, 0x59};
            static uint8_t p_key[] = {0xD7, 0x82, 0x8D, 0x13,  0xB2, 0xB0, 0xBD, 0xC3,  0x25, 0xA7, 0x62, 0x36,  0xDF, 0x93, 0xCC, 0x6B};
            BUILD_TEST_VECTOR(p_vector);
            break;
        }
        default:
            TEST_FAIL();
    }
}

void setUp(void)
{
}

void tearDown(void)
{
}


void test_ccm_soft_encrypt(void)
{
    test_vector_t vector;
    for (uint32_t i = 0; i < TEST_VECTORS; ++i)
    {
        char fail_string[64];
        sprintf(fail_string, "Failed test vector #%d", i);
        get_test_vector(i, &vector);
        uint8_t output[vector.payload_len];
        uint8_t mic[vector.mic_len];

        ccm_soft_data_t enc_data =
        {
            .p_key   = vector.p_key,
            .p_nonce = vector.p_nonce,
            .p_m     = vector.p_unencrypted,
            .p_a     = vector.p_additional_data,
            .m_len   = vector.payload_len,
            .a_len   = vector.additional_data_len,
            .mic_len = vector.mic_len,
            .p_mic   = mic,
            .p_out   = output
        };

        ccm_soft_encrypt(&enc_data);
        TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(vector.p_encrypted, output, vector.payload_len, fail_string);
        TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(vector.p_mic, mic, vector.mic_len, fail_string);
    }
}

void test_ccm_soft_decrypt(void)
{
    test_vector_t vector;
    for (uint32_t i = 0; i < TEST_VECTORS; ++i)
    {
        char fail_string[64];
        sprintf(fail_string, "Failed test vector #%d", i);
        get_test_vector(i, &vector);
        uint8_t output[vector.payload_len];

        ccm_soft_data_t enc_data =
        {
            .p_key   = vector.p_key,
            .p_nonce = vector.p_nonce,
            .p_m     = vector.p_encrypted,
            .p_a     = vector.p_additional_data,
            .m_len   = vector.payload_len,
            .a_len   = vector.additional_data_len,
            .mic_len = vector.mic_len,
            .p_mic   = vector.p_mic,
            .p_out   = output
        };

        bool mic_passed = false;
        ccm_soft_decrypt(&enc_data, &mic_passed);
        TEST_ASSERT_TRUE_MESSAGE(mic_passed, fail_string);
        TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(vector.p_unencrypted, output, vector.payload_len, fail_string);
    }
}


void test_ccm_soft_data_authenticate(void)
{
    const uint8_t key[16] = {0x16, 0xC7, 0x2D, 0xAB, 0x61, 0x57, 0x68, 0xC5, 0xA0, 0x23, 0x93, 0xB4, 0x41, 0x81, 0x8C, 0x61};
    const uint8_t data[] = {0x0C, 0xFF, 0xDA, 0x03, 0xB6, 0x24, 0x00, 0x00, 0x0A};
    uint8_t mic_in[4] = {0xA2, 0x93, 0x97, 0x30};
    const uint8_t nonce[13] = {0xFE, 0x19, 0x00, 0x00, 0x15, 0xE2, 0xB6, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00};

    ccm_soft_data_t enc_data =
    {
        .p_key   = key,
        .p_nonce = nonce,
        .p_a     = data,
        .p_m     = NULL,
        .a_len   = sizeof(data),
        .m_len   = 0,
        .mic_len = 4,
        .p_mic   = mic_in,
        .p_out   = NULL
    };
    bool authenticated = false;
    ccm_soft_decrypt(&enc_data, &authenticated);
    TEST_ASSERT(authenticated);
}
