Newer
Older
mbed-os / targets / TARGET_NUVOTON / TARGET_M460 / device / StdDriver / src / m460_keystore.c
@Jay Sridharan Jay Sridharan on 31 Dec 2022 20 KB Merge upstream changes into mbed-ce (#117)
/**************************************************************************//**
 * @file     keystore.c
 * @version  V3.00
 * @brief    Key store driver source file
 *
 * @copyright SPDX-License-Identifier: Apache-2.0
 * @copyright Copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
 *****************************************************************************/
#include "NuMicro.h"
/** @addtogroup Standard_Driver Standard Driver
  @{
*/

/** @addtogroup KS_Driver Key Store Driver
  @{
*/

int32_t g_KS_i32ErrCode = 0;       /*!< KS global error code */

/** @addtogroup KS_EXPORTED_FUNCTIONS Key Store Exported Functions
  @{
*/

/**
  * @brief      Initial key store
  * @return     None
  * @details    This function is used to initial the key store.
  *             It is necessary to be called before using other APIs of Key Store.
  * @note       This function sets g_KS_i32ErrCode to KS_TIMEOUT_ERR if waiting Key Store time-out.
  */
void KS_Open(void)
{
    uint32_t u32TimeOutCount;

    CLK->AHBCLK0 |= CLK_AHBCLK0_KSCKEN_Msk;

    g_KS_i32ErrCode = 0;

    if((KS->STS & KS_STS_INITDONE_Msk) == 0)
    {
        /* Waiting for busy */
        u32TimeOutCount = KS_TIMEOUT;
        while(KS->STS & KS_STS_BUSY_Msk)
        {
            if(--u32TimeOutCount == 0)
            {
                g_KS_i32ErrCode = KS_TIMEOUT_ERR;
                break;
            }
        }

        /* Start Key Store Initial */
        KS->CTL = KS_CTL_INIT_Msk | KS_CTL_START_Msk;

        /* Waiting for initilization */
        u32TimeOutCount = KS_TIMEOUT;
        while((KS->STS & KS_STS_INITDONE_Msk) == 0)
        {
            if(--u32TimeOutCount == 0)
            {
                g_KS_i32ErrCode = KS_TIMEOUT_ERR;
                break;
            }
        }
    }

    /* Waiting busy to make sure KS is ready. */
    u32TimeOutCount = KS_TIMEOUT;
    while(KS->STS & KS_STS_BUSY_Msk)
    {
        if(--u32TimeOutCount == 0)
        {
            g_KS_i32ErrCode = KS_TIMEOUT_ERR;
            break;
        }
    }
}


/**
  * @brief      Read key from key store
  * @param[in]  eType       The memory type. It could be:
                            \ref KS_SRAM
                            \ref KS_FLASH
                            \ref KS_OTP
  * @param[in]  i32KeyIdx   The key index to read
  * @param[out] au32Key     The buffer to store the key
  * @param[in]  u32WordCnt  The word (32-bit) count of the key buffer size
  * @retval     0           Successful
  * @retval     -1          Fail
  * @details    This function is used to read the key.
  */

int32_t KS_Read(KS_MEM_Type eType, int32_t i32KeyIdx, uint32_t au32Key[], uint32_t u32WordCnt)
{
    int32_t i32Cnt;
    uint32_t u32Cont;
    int32_t offset, i, cnt;
    uint32_t u32TimeOutCount;

    /* Just return when key store is in busy */
    if(KS->STS & KS_STS_BUSY_Msk)
        return -1;

    /* Specify the key address */
    KS->METADATA = ((uint32_t)eType << KS_METADATA_DST_Pos) | KS_TOMETAKEY(i32KeyIdx);

    /* Clear error flag */
    KS->STS = KS_STS_EIF_Msk;
    offset = 0;
    u32Cont = 0;
    i32Cnt = (int32_t)u32WordCnt;
    do
    {
        /* Clear Status */
        KS->STS = KS_STS_EIF_Msk | KS_STS_IF_Msk;

        /* Trigger to read the key */
        KS->CTL = u32Cont | KS_OP_READ | KS_CTL_START_Msk;
        /* Waiting for key store processing */
        u32TimeOutCount = KS_TIMEOUT;
        while(KS->STS & KS_STS_BUSY_Msk)
        {
            if(--u32TimeOutCount == 0)
                return -1;
        }

        /* Read the key to key buffer */
        cnt = i32Cnt;
        if(cnt > 8)
            cnt = 8;
        for(i = 0; i < cnt; i++)
        {
            au32Key[offset + i] = KS->KEY[i];
            //printf("R[%d]:0x%08x\n", i, au32Key[offset+i]);
        }

        u32Cont = KS_CTL_CONT_Msk;
        i32Cnt -= 8;
        offset += 8;
    }
    while(i32Cnt > 0);

    /* Check error flag */
    if(KS->STS & KS_STS_EIF_Msk)
        return -1;


    return 0;
}

/**
  * @brief      Get the word count of the specified Metadata key length
  * @param[in]  u32Meta     The metadata define of the key length. It could be
                                \ref KS_META_128
                                \ref KS_META_163
                                \ref KS_META_192
                                \ref KS_META_224
                                \ref KS_META_233
                                \ref KS_META_255
                                \ref KS_META_256
                                \ref KS_META_283
                                \ref KS_META_384
                                \ref KS_META_409
                                \ref KS_META_512
                                \ref KS_META_521
                                \ref KS_META_571
                                \ref KS_META_1024
                                \ref KS_META_2048
                                \ref KS_META_4096
  * @return     The word (32-bit) count of the key
  * @details    This function is used to get word counts of the specified metadata key length.
  *             It could be used to know how may words needs to allocate for the key.
  */

uint32_t KS_GetKeyWordCnt(uint32_t u32Meta)
{
    const uint16_t au8CntTbl[21] = { 4, 6, 6, 7, 8, 8, 8, 9, 12, 13, 16, 17, 18, 0, 0, 0, 32, 48, 64, 96, 128 };
    return au8CntTbl[((u32Meta & KS_METADATA_SIZE_Msk) >> KS_METADATA_SIZE_Pos)];
}

/**
  * @brief      Write key to key store
* @param[in]    eType       The memory type. It could be:
                            \ref KS_SRAM
                            \ref KS_FLASH
  * @param[in]  u32Meta     The metadata of the key. It could be the combine of
                                \ref KS_META_AES
                                \ref KS_META_HMAC
                                \ref KS_META_RSA_EXP
                                \ref KS_META_RSA_MID
                                \ref KS_META_ECC
                                \ref KS_META_CPU
                                \ref KS_META_128
                                \ref KS_META_163
                                \ref KS_META_192
                                \ref KS_META_224
                                \ref KS_META_233
                                \ref KS_META_255
                                \ref KS_META_256
                                \ref KS_META_283
                                \ref KS_META_384
                                \ref KS_META_409
                                \ref KS_META_512
                                \ref KS_META_521
                                \ref KS_META_571
                                \ref KS_META_1024
                                \ref KS_META_2048
                                \ref KS_META_4096
                                \ref KS_META_BOOT
                                \ref KS_META_READABLE
                                \ref KS_META_PRIV
                                \ref KS_META_NONPRIV
                                \ref KS_META_SECURE
                                \ref KS_META_NONSECUR

  * @param[out] au32Key     The buffer to store the key
  * @param[in]  u32WordCnt  The word (32-bit) count of the key buffer size
  * @return     Index of the key. Failed when index < 0.
  * @details    This function is used to write a key to key store.
  */

int32_t KS_Write(KS_MEM_Type eType, uint32_t u32Meta, uint32_t au32Key[])
{
    int32_t i32Cnt;
    uint32_t u32Cont;
    int32_t i, cnt;
    volatile int32_t offset;
    uint32_t u32TimeOutCount;

    /* Just return when key store is in busy */
    if(KS->STS & KS_STS_BUSY_Msk)
        return -1;

    /* Specify the key address */
    KS->METADATA = (eType << KS_METADATA_DST_Pos) | u32Meta;

    /* Get size index */
    i32Cnt = (int32_t)KS_GetKeyWordCnt(u32Meta);

    /* Invalid key length */
    if(i32Cnt == 0)
        return -1;

    /* OTP only support maximum 256 bits */
    if((eType == KS_OTP) && (i32Cnt > 8))
        return -1;

    /* Clear error flag */
    KS->STS = KS_STS_EIF_Msk;
    offset = 0;
    u32Cont = 0;
    do
    {
        /* Prepare the key to write */
        cnt = i32Cnt;
        if(cnt > 8)
            cnt = 8;
        for(i = 0; i < cnt; i++)
        {
            KS->KEY[i] = au32Key[offset + i];
        }
//        for(;i < 8;i++)
//        {
//            KS->KEY[i] = 0;
//        }

        /* Clear Status */
        KS->STS = KS_STS_EIF_Msk | KS_STS_IF_Msk;

        /* Write the key */
        KS->CTL = u32Cont | KS_OP_WRITE | KS_CTL_START_Msk;

        u32Cont = KS_CTL_CONT_Msk;
        i32Cnt -= 8;
        offset += 8;

        /* Waiting for key store processing */
        u32TimeOutCount = KS_TIMEOUT;
        while(KS->STS & KS_STS_BUSY_Msk)
        {
            if(--u32TimeOutCount == 0)
                return -1;
        }

    }
    while(i32Cnt > 0);

    /* Check error flag */
    if(KS->STS & KS_STS_EIF_Msk)
    {
        //printf("KS_Write. EIF!\n");
        return -1;
    }

    return KS_TOKEYIDX(KS->METADATA);
}

/**
  * @brief      Erase a key from key store SRAM
  * @param[in]  i32KeyIdx   The key index to read
  * @retval     0           Successful
  * @retval     -1          Fail
  * @details    This function is used to erase a key from SRAM of key store.
   */
int32_t KS_EraseKey(int32_t i32KeyIdx)
{
    uint32_t u32TimeOutCount = KS_TIMEOUT;

    /* Just return when key store is in busy */
    if(KS->STS & KS_STS_BUSY_Msk)
        return -1;

    /* Clear error flag */
    KS->STS = KS_STS_EIF_Msk;

    /* Specify the key address */
    KS->METADATA = (KS_SRAM << KS_METADATA_DST_Pos) | KS_TOMETAKEY(i32KeyIdx);

    /* Clear Status */
    KS->STS = KS_STS_EIF_Msk | KS_STS_IF_Msk;

    /* Erase the key */
    KS->CTL = KS_OP_ERASE | KS_CTL_START_Msk;

    /* Waiting for processing */
    while(KS->STS & KS_STS_BUSY_Msk)
    {
        if(--u32TimeOutCount == 0)
            return -1;
    }

    /* Check error flag */
    if(KS->STS & KS_STS_EIF_Msk)
        return -1;

    return 0;

}


/**
  * @brief      Erase a key from key store OTP
  * @param[in]  i32KeyIdx   The key index to erase
  * @retval     0           Successful
  * @retval     -1          Fail
  * @details    This function is used to erase a key from key store OTP.
   */
int32_t KS_EraseOTPKey(int32_t i32KeyIdx)
{
    uint32_t u32TimeOutCount = KS_TIMEOUT; /* 1 second time-out */

    /* Just return when key store is in busy */
    if(KS->STS & KS_STS_BUSY_Msk)
        return -1;

    /* Clear error flag */
    KS->STS = KS_STS_EIF_Msk;

    /* Specify the key address */
    KS->METADATA = ((uint32_t)KS_OTP << KS_METADATA_DST_Pos) | KS_TOMETAKEY(i32KeyIdx);

    /* Clear Status */
    KS->STS = KS_STS_EIF_Msk | KS_STS_IF_Msk;

    /* Erase the key */
    KS->CTL = KS_OP_ERASE | KS_CTL_START_Msk;

    /* Waiting for processing */
    while(KS->STS & KS_STS_BUSY_Msk)
    {
        if(--u32TimeOutCount == 0)
            return -1;
    }

    /* Check error flag */
    if(KS->STS & KS_STS_EIF_Msk)
        return -1;

    return 0;

}



/**
  * @brief      Lock the OTP key
  * @param[in]  i32KeyIdx   The key index to lock
  * @retval     0           Successful
  * @retval     -1          Fail
  * @details    This function is used to lock a key of KS OTP.
   */
int32_t KS_LockOTPKey(int32_t i32KeyIdx)
{
    uint32_t u32TimeOutCount = KS_TIMEOUT;

    /* Just return when key store is in busy */
    if(KS->STS & KS_STS_BUSY_Msk)
        return -1;

    /* Clear error flag */
    KS->STS = KS_STS_EIF_Msk;

    /* Specify the key address */
    KS->METADATA = ((uint32_t)KS_OTP << KS_METADATA_DST_Pos) | KS_TOMETAKEY(i32KeyIdx);

    /* Clear Status */
    KS->STS = KS_STS_EIF_Msk | KS_STS_IF_Msk;

    /* Erase the key */
    KS->CTL = KS_OP_LOCK | KS_CTL_START_Msk;

    /* Waiting for processing */
    while(KS->STS & KS_STS_BUSY_Msk)
    {
        if(--u32TimeOutCount == 0)
            return -1;
    }

    /* Check error flag */
    if(KS->STS & KS_STS_EIF_Msk)
        return -1;

    return 0;

}

/**
  * @brief      Erase all keys from key store
  * @param[in]  eType       The memory type. It could be:
                            \ref KS_SRAM
                            \ref KS_FLASH
                            \ref KS_OTP
  * @param[in]  i32KeyIdx   The key index to read
  * @retval     0           Successful
  * @retval     -1          Fail
  * @details    This function is used to erase all keys in SRAM or Flash of key store.
  */
int32_t KS_EraseAll(KS_MEM_Type eType)
{
    uint32_t u32TimeOutCount = KS_TIMEOUT;

    /* Just return when key store is in busy */
    if(KS->STS & KS_STS_BUSY_Msk)
        return -1;

    /* Clear error flag */
    KS->STS = KS_STS_EIF_Msk;

    /* Specify the key address */
    KS->METADATA = (eType << KS_METADATA_DST_Pos);

    /* Clear Status */
    KS->STS = KS_STS_EIF_Msk | KS_STS_IF_Msk;

    /* Erase the key */
    KS->CTL = KS_OP_ERASE_ALL | KS_CTL_START_Msk;

    /* Waiting for processing */
    while(KS->STS & KS_STS_BUSY_Msk)
    {
        if(--u32TimeOutCount == 0)
            return -1;
    }

    /* Check error flag */
    if(KS->STS & KS_STS_EIF_Msk)
        return -1;

    return 0;

}



/**
  * @brief      Revoke a key in key store
  * @param[in]  eType       The memory type. It could be:
                            \ref KS_SRAM
                            \ref KS_FLASH
                            \ref KS_OTP
  * @param[in]  i32KeyIdx   The key index to read
  * @retval     0           Successful
  * @retval     -1          Fail
  * @details    This function is used to revoke a key in key store.
  */
int32_t KS_RevokeKey(KS_MEM_Type eType, int32_t i32KeyIdx)
{
    uint32_t u32TimeOutCount = KS_TIMEOUT;

    /* Just return when key store is in busy */
    if(KS->STS & KS_STS_BUSY_Msk)
        return -1;

    /* Clear error flag */
    KS->STS = KS_STS_EIF_Msk;

    /* Specify the key address */
    KS->METADATA = (eType << KS_METADATA_DST_Pos) | KS_TOMETAKEY(i32KeyIdx);

    /* Clear Status */
    KS->STS = KS_STS_EIF_Msk | KS_STS_IF_Msk;

    /* Erase the key */
    KS->CTL = KS_OP_REVOKE | KS_CTL_START_Msk;

    /* Waiting for processing */
    while(KS->STS & KS_STS_BUSY_Msk)
    {
        if(--u32TimeOutCount == 0)
            return -1;
    }

    /* Check error flag */
    if(KS->STS & KS_STS_EIF_Msk)
        return -1;

    return 0;

}


/**
  * @brief      Get remain size of specified Key Store memory
  * @param[in]  eType       The memory type. It could be:
                            \ref KS_SRAM
                            \ref KS_FLASH
  * @retval     remain size of specified Key Store memory
  * @details    This function is used to get remain size of Key Store.
  */
uint32_t KS_GetRemainSize(KS_MEM_Type mem)
{
    uint32_t u32Reg;
    uint32_t u32SramRemain, u32FlashRemain;

    u32Reg = KS->REMAIN;
    //printf("KS Remain 0x%08x\n", u32Reg);
    //printf("SRAM remain %lu bytes, Flash remain %lu bytes\n",(u32Reg&KS_REMAIN_RRMNG_Msk) >> KS_REMAIN_RRMNG_Pos, (u32Reg&KS_REMAIN_FRMNG_Msk) >> KS_REMAIN_FRMNG_Pos);
    u32SramRemain = (u32Reg & KS_REMAIN_RRMNG_Msk) >> KS_REMAIN_RRMNG_Pos;
    u32FlashRemain = (u32Reg & KS_REMAIN_FRMNG_Msk) >> KS_REMAIN_FRMNG_Pos;

    if(mem == KS_SRAM)
        return u32SramRemain;
    else
        return u32FlashRemain;
}



/**
  * @brief      Get remain key count of specified Key Store memory
  * @param[in]  eType       The memory type. It could be:
                            \ref KS_SRAM
                            \ref KS_FLASH
  * @retval     Remain key count in the specified key store memory
  * @details    This function is used to get remain key count in specified key store memory.
  */
uint32_t KS_GetRemainKeyCount(KS_MEM_Type mem)
{
    uint32_t u32Reg;
    uint32_t u32SramRemain, u32FlashRemain;

    u32Reg = KS->REMKCNT;
    u32SramRemain = (u32Reg & KS_REMKCNT_RRMKCNT_Msk) >> KS_REMKCNT_RRMKCNT_Pos;
    u32FlashRemain = (u32Reg & KS_REMKCNT_FRMKCNT_Msk) >> KS_REMKCNT_FRMKCNT_Pos;

    if(mem == KS_SRAM)
        return u32SramRemain;
    else
        return u32FlashRemain;
}



/**
  * @brief      Write OTP key to key store
  * @param[in]  i32KeyIdx   The OTP key index to store the key. It could be 0~7.
                            OTP key index 0 is default for ROTPK.
  * @param[in]  u32Meta     The metadata of the key. It could be the combine of
                                \ref KS_META_AES
                                \ref KS_META_HMAC
                                \ref KS_META_RSA_EXP
                                \ref KS_META_RSA_MID
                                \ref KS_META_ECC
                                \ref KS_META_CPU
                                \ref KS_META_128
                                \ref KS_META_163
                                \ref KS_META_192
                                \ref KS_META_224
                                \ref KS_META_233
                                \ref KS_META_255
                                \ref KS_META_256
                                \ref KS_META_BOOT
                                \ref KS_META_READABLE
                                \ref KS_META_PRIV
                                \ref KS_META_NONPRIV
                                \ref KS_META_SECURE
                                \ref KS_META_NONSECUR

  * @param[out] au32Key     The buffer to store the key
  * @param[in]  u32WordCnt  The word (32-bit) count of the key buffer size
  * @retval     0           Successful
  * @retval     -1          Fail
  * @details    This function is used to write a key to OTP key store.
  */
int32_t KS_WriteOTP(int32_t i32KeyIdx, uint32_t u32Meta, uint32_t au32Key[])
{
    const uint16_t au8CntTbl[7] = {4, 6, 6, 7, 8, 8, 8};
    int32_t i32Cnt;
    uint32_t u32Cont;
    int32_t offset, i, cnt, sidx;
    uint32_t u32TimeOutCount;

    /* Just return when key store is in busy */
    if(KS->STS & KS_STS_BUSY_Msk)
        return -1;

    /* Specify the key address */
    KS->METADATA = ((uint32_t)KS_OTP << KS_METADATA_DST_Pos) | u32Meta | KS_TOMETAKEY(i32KeyIdx);

    /* Get size index */
    sidx = (u32Meta >> KS_METADATA_SIZE_Pos) & 0xful;

    /* OTP only support maximum 256 bits */
    if(sidx >= 7)
        return -1;

    i32Cnt = au8CntTbl[sidx];

    /* Clear error flag */
    KS->STS = KS_STS_EIF_Msk;
    offset = 0;
    u32Cont = 0;
    do
    {
        /* Prepare the key to write */
        cnt = i32Cnt;
        if(cnt > 8)
            cnt = 8;
        for(i = 0; i < cnt; i++)
        {
            KS->KEY[i] = au32Key[offset + i];
        }

        /* Clear Status */
        KS->STS = KS_STS_EIF_Msk | KS_STS_IF_Msk;

        /* Write the key */
        KS->CTL = u32Cont | KS_OP_WRITE | KS_CTL_START_Msk;

        u32Cont = KS_CTL_CONT_Msk;
        i32Cnt -= 8;
        offset += 8;

        /* Waiting for key store processing */
        u32TimeOutCount = KS_TIMEOUT;
        while(KS->STS & KS_STS_BUSY_Msk)
        {
            if(--u32TimeOutCount == 0)
                return -1;
        }

    }
    while(i32Cnt > 0);

    /* Check error flag */
    if(KS->STS & KS_STS_EIF_Msk)
    {
        //printf("KS_WriteOTP. EIF!\n");
        return -1;
    }

    return i32KeyIdx;
}


/**
  * @brief      Trigger to inverse the date in KS_SRAM.
  * @retval     1           The data in KS SRAM is inverted.
  * @retval     0           The data in KS SRAM is non-inverted.
  * @retval     -1          Fail to invert the date in KS SRAM.
  * @details    This function is used to trigger anti-remanence procedure by inverse the data in SRAM.
  *             This won't change the reading key.
  */

int32_t KS_ToggleSRAM(void)
{
    uint32_t u32TimeOutCount = KS_TIMEOUT;

    /* Just return when key store is in busy */
    if(KS->STS & KS_STS_BUSY_Msk)
        return -1;


    /* Specify the key address */
    KS->METADATA = ((uint32_t)KS_SRAM << KS_METADATA_DST_Pos);

    /* Clear error flag */
    KS->STS = KS_STS_EIF_Msk | KS_STS_IF_Msk;
    /* Trigger to do anti-remanence procedure */
    KS->CTL = KS_OP_REMAN | KS_CTL_START_Msk;

    /* Waiting for key store processing */
    while(KS->STS & KS_STS_BUSY_Msk)
    {
        if(--u32TimeOutCount == 0)
            return -1;
    }

    /* Check error flag */
    if(KS->STS & KS_STS_EIF_Msk)
        return -1;

    return ((KS->STS & KS_STS_RAMINV_Msk) > 0);
}


/**@}*/ /* end of group KS_EXPORTED_FUNCTIONS */

/**@}*/ /* end of group KS_Driver */

/**@}*/ /* end of group Standard_Driver */