/* Copyright Statement:
 *
 * This software/firmware and related documentation ("AutoChips Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to AutoChips Inc. and/or its licensors. Without
 * the prior written permission of AutoChips inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of AutoChips Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * AutoChips Inc. (C) 2024. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("AUTOCHIPS SOFTWARE")
 * RECEIVED FROM AUTOCHIPS AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. AUTOCHIPS EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES AUTOCHIPS PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE AUTOCHIPS SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN AUTOCHIPS
 * SOFTWARE. AUTOCHIPS SHALL ALSO NOT BE RESPONSIBLE FOR ANY AUTOCHIPS SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND AUTOCHIPS'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE AUTOCHIPS SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT AUTOCHIPS'S OPTION, TO REVISE OR REPLACE THE
 * AUTOCHIPS SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO AUTOCHIPS FOR SUCH AUTOCHIPS SOFTWARE AT ISSUE.
 */
/******************************************************************************
 * @version: V3.1.0  AC7840x  Demo
 ******************************************************************************/

#include "cse_functions.h"
#include "cse_keys.h"
#include "device_status.h"

status_t KDF(const uint8_t *AuthKey, const uint8_t *Constant, uint8_t *K_out);

/*
* @brief Connect the parameter authkey with constant and compress the result.
*
* @param[in] AuthKey:authorizing key
* @param[in] Constant:constant value
* @param[in] K_out:output buffer
* @return status_t
*/
static status_t KDF(const uint8_t *AuthKey, const uint8_t *Constant, uint8_t *K_out)
{
    status_t Res        = STATUS_SUCCESS;
    uint8_t i           = 0;
    uint8_t Concat[32u] = {0u};

    for (i = 0u; i < 16u; i++)
    {
        Concat[i]      = AuthKey[i];
        Concat[i + 16] = Constant[i];
    }
    Res = CSE_DRV_MPCompress(Concat, 2u, K_out, 2u);

    return Res;
}

/*
* @brief Calculate M1 To M5
*
* @param[in] authorizing_key:authorizing key
* ......
* @return status_t
*/
status_t CalculateM1ToM5(uint8_t *authorizing_key, uint8_t *new_key, uint8_t auth_key_id, uint8_t key_id, uint32_t counter, uint8_t attribute_flags,
                         uint8_t *M1_out, uint8_t *M2_out, uint8_t *M3_out, uint8_t *M4_out, uint8_t *M5_out)
{
    status_t Res            = STATUS_SUCCESS;
    uint8_t Sreg            = 0u;
    uint8_t i               = 0u;
    uint8_t UID[16u]        = {0};
    uint8_t UID_MAC[16u]    = {0u};
    uint8_t M4_star[16u]    = {0u};
    uint8_t M4_tmp[16u]     = {0u};
    uint8_t K_out[16u]      = {0u};
    uint8_t Iv[16u]         = {0u};
    uint8_t PLAIN_TEXT[32u] = {0u};
    uint8_t M1M2[48u]       = {0u};
    uint8_t Challenge[16u]  = {0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, 15u};

    Res = CSE_DRV_GetID(Challenge, UID, &Sreg, UID_MAC); //All UID Key is 0x0 for calculate MASTE_ECU_KEY

    //For keys in the other bank, don't consider the KBS bit in M1 & M4 calculation. Follow traditional SHE specs
    //****Calculate M1****
    #ifdef AC784X_UCOS_CSE_REVERSE
    /* UID only byte0 ~ byte14 valid */
    for (i = 0u; i < 15u; i++)
    {
        M1_out[i] = UID[i];
    }
    M1_out[15] = ((key_id & 0x0Fu) << 4u) | (auth_key_id & 0x0Fu);
    #else
    /* UID only byte1 ~ byte15 valid */
    for (i = 1u; i < 16u; i++)
    {
        M1_out[i] = UID[i];
    }
    M1_out[0] = ((key_id & 0x0Fu) << 4u) | (auth_key_id & 0x0Fu);
    #endif

    //****Calculate M2****
    //First, calculate K1 and load K1 into RAM
    Res = KDF(authorizing_key, KEY_UPDATE_ENC_C, K_out);

    Res = CSE_DRV_LoadPlainKey(K_out);

    //0bit~93bit:fill 0(94bit)
    for (i = 0u; i < 11u; i++)
    {
        #ifdef AC784X_UCOS_CSE_REVERSE
        PLAIN_TEXT[5U + i] = 0u;
        #else
        PLAIN_TEXT[i] = 0u;
        #endif
    }
    //Second, concatenate the input data
    //94bit~99bit:6bit FID  100bit~127bit: 28bit CID
    #ifdef AC784X_UCOS_CSE_REVERSE
    PLAIN_TEXT[4U] = (uint8_t)((uint8_t)attribute_flags & 0x0FU) << 6U;
    PLAIN_TEXT[3U] = (uint8_t)(((counter & 0x0000000FU) << 4U) | (((uint8_t)attribute_flags & 0x3CU) >> 2U));
    PLAIN_TEXT[2U] = (uint8_t)((counter & 0x00000FF0U) >> 4U);
    PLAIN_TEXT[1U] = (uint8_t)((counter & 0x000FF000U) >> 12U);
    PLAIN_TEXT[0U] = (uint8_t)((counter & 0x0FF00000U) >> 20U);
    #else
    PLAIN_TEXT[11u] = (uint8_t)(attribute_flags & 0x0Fu) << 6u;
    PLAIN_TEXT[12u] = (uint8_t)(((counter & 0x0000000Fu) << 4u) | ((attribute_flags & 0x3Cu) >> 2u));
    PLAIN_TEXT[13u] = (uint8_t)((counter &  0x00000FF0u) >> 4u);
    PLAIN_TEXT[14u] = (uint8_t)((counter &  0x000FF000u) >> 12u);
    PLAIN_TEXT[15u] = (uint8_t)((counter &  0x0FF00000u) >> 20u);
    #endif

    //128bit~255bit key id
    for (i = 0u; i < 16u; i++)
    {
        PLAIN_TEXT[16u + i] = new_key[i];
    }

    Res = CSE_DRV_EncryptCBC(CSE_RAM_KEY, PLAIN_TEXT, 32u, Iv, M2_out, 2u);

    //****Calculate M3****
    //First, calculate K2 load it as RAM_KEY
    Res = KDF(authorizing_key, KEY_UPDATE_MAC_C, K_out);
    Res = CSE_DRV_LoadPlainKey(K_out);
    //concatenate M1 and M2
    for (i = 0u;  i < 16u; i++)
    {
        M1M2[i] = M1_out[i];
    }
    for (i = 16u; i < 48u; i++)
    {
        M1M2[i] = M2_out[i - 16u];
    }
    Res = CSE_DRV_GenerateMAC(CSE_RAM_KEY, M1M2, 384u, M3_out, 2u);

    //First, calculate K3 load it as RAM_KEY
    Res = KDF(new_key, KEY_UPDATE_ENC_C, K_out);
    Res = CSE_DRV_LoadPlainKey(K_out);

    //calculate K4*
    #ifdef AC784X_UCOS_CSE_REVERSE
    M4_tmp[3U] = (uint8_t)(((counter & 0x0000000FU) << 4U) | 0x08U);
    M4_tmp[2U] = (uint8_t)((counter & 0x00000FF0U) >> 4U);
    M4_tmp[1U] = (uint8_t)((counter & 0x000FF000U) >> 12U);
    M4_tmp[0U] = (uint8_t)((counter & 0x0FF00000U) >> 20U);
    #else
    M4_tmp[12u] = (uint8_t)(((counter & 0x0000000Fu) << 4u) | 0x08u);
    M4_tmp[13u] = (uint8_t)((counter &  0x00000FF0u) >> 4u);
    M4_tmp[14u] = (uint8_t)((counter &  0x000FF000u) >> 12u);
    M4_tmp[15u] = (uint8_t)((counter &  0x0FF00000u) >> 20u);
    #endif

    Res = CSE_DRV_EncryptCBC(CSE_RAM_KEY, M4_tmp, 16u, Iv, M4_star, 2u);
    //0bit ~ 119bit UID
    #ifdef AC784X_UCOS_CSE_REVERSE
    /* UID only byte0 ~ byte14 valid */
    for (i = 0u; i < 15u; i++)
    {
        M4_out[i] = UID[i];
    }
    //120bit ~ 1237bit KeyID(4bits)|AuthID(4bits)
    M4_out[15u] = (key_id << 4u) | (auth_key_id & 0x0Fu);
    #else
    /* UID only byte1 ~ byte15 valid */
    for (i = 1u; i < 16u; i++)
    {
        M4_out[i] = UID[i];
    }
    //120bit ~ 1237bit KeyID(4bits)|AuthID(4bits)
    M4_out[0u] = (key_id << 4u) | (auth_key_id & 0x0Fu);
    #endif

    //128bit ~ 255bit M4*
    for (i = 0u; i < 16u; i++)
    {
        M4_out[16u + i] = M4_star[i];
    }

    //****Calculate M5****
    Res = KDF(new_key, KEY_UPDATE_MAC_C, K_out);
    Res = CSE_DRV_LoadPlainKey(K_out);
    Res = CSE_DRV_GenerateMAC(CSE_RAM_KEY, M4_out, 256u, M5_out, 2u);

    return Res;
}

/*
* @brief CalculateDbgAuth
*
* @param[in] MasterEcuKey:Master ECU key buffer
* @param[in] Challenge:Challenge buffer
* @param[in] DbgAuthOut:DbgAuthOut buffer
* @return status_t
*/
status_t CalculateDbgAuth(uint8_t* MasterEcuKey, uint8_t * Challenge, uint8_t *DbgAuthOut)
{
    status_t Res                = STATUS_SUCCESS;
    uint8_t Sreg                = 0u;
    uint8_t i                   = 0u;
    uint8_t K_out[16u]          = {0u};
    uint8_t UID[16u]            = {0u};//UID just have 15 bytes
    uint8_t UID_MAC[16u]        = {0u};
    uint8_t UidChallenge[16u]   = {0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, 15u};
    uint8_t DATA[32u]           = {0u};

    //step1 Calculate the Authorization
    Res = KDF(MasterEcuKey, DEBUG_KEY_C, K_out);
    Res = CSE_DRV_LoadPlainKey(K_out);
    Res = CSE_DRV_GetID(UidChallenge, UID, &Sreg, UID_MAC);
    for (i = 0u; i < 16u; i++)
    {
        DATA[i] = Challenge[i];
    }
    #ifdef AC784X_UCOS_CSE_REVERSE
    for (i = 16; i < 31u; i++)
    {
        DATA[i] = UID[i - 16u];
    }
    #else
    for (i = 16; i < 31u; i++)
    {
        DATA[i] = UID[i - 15u];
    }
    #endif
    /* byte0 ~ byte30 Generate CMAC*/
    Res = CSE_DRV_GenerateMAC(CSE_RAM_KEY, DATA, 248u, DbgAuthOut, 2u);

    return Res;
}
