1098 lines
41 KiB
C
1098 lines
41 KiB
C
/*
|
|
* Copyright 2018 - 2023 NXP
|
|
* All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include "fsl_prince.h"
|
|
|
|
/*******************************************************************************
|
|
* Definitions
|
|
******************************************************************************/
|
|
/* Component ID definition, used by tools. */
|
|
|
|
#ifndef FSL_COMPONENT_ID
|
|
#define FSL_COMPONENT_ID "platform.drivers.prince"
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
* Variables
|
|
******************************************************************************/
|
|
|
|
/*******************************************************************************
|
|
* Code
|
|
******************************************************************************/
|
|
#if !defined(FSL_PRINCE_DRIVER_LPC55S3x)
|
|
static secure_bool_t PRINCE_CheckerAlgorithm(uint32_t address,
|
|
uint32_t length,
|
|
prince_flags_t flag,
|
|
flash_config_t *flash_context)
|
|
{
|
|
uint32_t temp_base = 0, temp_sr = 0, region_index = 0, contiguous_start_index = 0, contiguous_end_index = 32;
|
|
secure_bool_t is_prince_region_contiguous = kSECURE_TRUE;
|
|
uint8_t prince_iv_code[FLASH_FFR_IV_CODE_SIZE] = {0};
|
|
|
|
if (address >= flash_context->ffrConfig.ffrBlockBase)
|
|
{
|
|
/* If it is not in flash region, return true to allow erase/write operation. */
|
|
return kSECURE_TRUE;
|
|
}
|
|
|
|
/* Iterate for all PRINCE regions */
|
|
for (region_index = (uint32_t)kPRINCE_Region0; region_index <= (uint32_t)kPRINCE_Region2; region_index++)
|
|
{
|
|
contiguous_start_index = 0;
|
|
contiguous_end_index = 32;
|
|
switch (region_index)
|
|
{
|
|
case (uint32_t)kPRINCE_Region0:
|
|
temp_base = PRINCE->BASE_ADDR0;
|
|
temp_sr = PRINCE->SR_ENABLE0;
|
|
break;
|
|
|
|
case (uint32_t)kPRINCE_Region1:
|
|
temp_base = PRINCE->BASE_ADDR1;
|
|
temp_sr = PRINCE->SR_ENABLE1;
|
|
break;
|
|
|
|
case (uint32_t)kPRINCE_Region2:
|
|
temp_base = PRINCE->BASE_ADDR2;
|
|
temp_sr = PRINCE->SR_ENABLE2;
|
|
break;
|
|
|
|
default:
|
|
/* All the cases have been listed above, the default clause should not be reached. */
|
|
break;
|
|
}
|
|
|
|
if (((address >= temp_base) &&
|
|
((address + length) < (temp_base + (FSL_PRINCE_DRIVER_SUBREGION_SIZE_IN_KB * 32U * 1024U)))) &&
|
|
(temp_sr != 0U))
|
|
{
|
|
/* Check if the mask is contiguous */
|
|
secure_bool_t first_set_bit_found = kSECURE_FALSE;
|
|
secure_bool_t contiguous_end_found = kSECURE_FALSE;
|
|
for (uint32_t i = 0; i < 32U; i++)
|
|
{
|
|
if (0U != (temp_sr & (1UL << i)))
|
|
{
|
|
if (kSECURE_FALSE == first_set_bit_found)
|
|
{
|
|
first_set_bit_found = kSECURE_TRUE;
|
|
contiguous_start_index = i;
|
|
}
|
|
if (kSECURE_TRUE == contiguous_end_found)
|
|
{
|
|
is_prince_region_contiguous = kSECURE_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((kSECURE_TRUE == first_set_bit_found) && (kSECURE_FALSE == contiguous_end_found))
|
|
{
|
|
contiguous_end_found = kSECURE_TRUE;
|
|
contiguous_end_index = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
continue; /* No encryption enabled, continue with the next region checking. */
|
|
}
|
|
|
|
/* Check if the provided memory range covers all addresses defined in the SR mask */
|
|
if ((kSECURE_TRUE == is_prince_region_contiguous) &&
|
|
((address <= (temp_base + (contiguous_start_index * FSL_PRINCE_DRIVER_SUBREGION_SIZE_IN_KB * 1024U)))) &&
|
|
(((address + length) >=
|
|
(temp_base + (contiguous_end_index * FSL_PRINCE_DRIVER_SUBREGION_SIZE_IN_KB * 1024U)))))
|
|
{
|
|
/* In case of erase operation, invalidate the old PRINCE IV by regenerating the new one */
|
|
if (kPRINCE_Flag_EraseCheck == flag)
|
|
{
|
|
/* Re-generate the PRINCE IV in case of erase operation */
|
|
|
|
/* Generate new IV code for the PRINCE region and store the new IV into the respective FFRs */
|
|
if (kStatus_Success ==
|
|
PRINCE_GenNewIV((prince_region_t)region_index, &prince_iv_code[0], true, flash_context))
|
|
{
|
|
/* Store the new IV for the PRINCE region into PRINCE registers. */
|
|
if (kStatus_Success == PRINCE_LoadIV((prince_region_t)region_index, &prince_iv_code[0]))
|
|
{
|
|
/* Encryption is enabled, all subregions are to be erased/written at once, IV successfully
|
|
* regenerated, return true to allow erase operation. */
|
|
return kSECURE_TRUE;
|
|
}
|
|
}
|
|
/* Encryption is enabled, all subregions are to be erased/written at once but IV has not been correctly
|
|
* regenerated, return false to disable erase operation. */
|
|
return kSECURE_FALSE;
|
|
}
|
|
|
|
/* Encryption is enabled and all subregions are to be erased/written at once, return true to allow
|
|
* erase/write operation. */
|
|
return kSECURE_TRUE;
|
|
}
|
|
/* The provided memory range does not cover all addresses defined in the SR mask. */
|
|
else
|
|
{
|
|
/* Is the provided memory range outside the addresses defined by the SR mask? */
|
|
if ((kSECURE_TRUE == is_prince_region_contiguous) &&
|
|
((((address + length) <=
|
|
(temp_base + (contiguous_start_index * FSL_PRINCE_DRIVER_SUBREGION_SIZE_IN_KB * 1024U)))) ||
|
|
((address >= (temp_base + (contiguous_end_index * FSL_PRINCE_DRIVER_SUBREGION_SIZE_IN_KB * 1024U))))))
|
|
{
|
|
/* No encryption enabled for the provided memory range, true could be returned to allow erase/write
|
|
operation, but due to the same base address for all three prince regions on Niobe4Mini we should
|
|
continue with other regions (SR mask) checking. */
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
/* Encryption is enabled but not all subregions are to be erased/written at once, return false to
|
|
* disable erase/write operation. */
|
|
return kSECURE_FALSE;
|
|
}
|
|
}
|
|
}
|
|
return kSECURE_TRUE;
|
|
}
|
|
#endif /* !defined(FSL_PRINCE_DRIVER_LPC55S3x) */
|
|
|
|
#if !defined(FSL_PRINCE_DRIVER_LPC55S3x)
|
|
/*!
|
|
* @brief Generate new IV code.
|
|
*
|
|
* This function generates new IV code and stores it into the persistent memory.
|
|
* Ensure about 800 bytes free space on the stack when calling this routine with the store parameter set to true!
|
|
*
|
|
* @param region PRINCE region index.
|
|
* @param iv_code IV code pointer used for storing the newly generated 52 bytes long IV code.
|
|
* @param store flag to allow storing the newly generated IV code into the persistent memory (FFR).
|
|
* @param flash_context pointer to the flash driver context structure.
|
|
*
|
|
* @return kStatus_Success upon success
|
|
* @return kStatus_Fail otherwise, kStatus_Fail is also returned if the key code for the particular
|
|
* PRINCE region is not present in the keystore (though new IV code has been provided)
|
|
*/
|
|
status_t PRINCE_GenNewIV(prince_region_t region, uint8_t *iv_code, bool store, flash_config_t *flash_context)
|
|
{
|
|
status_t status = kStatus_Fail;
|
|
uint8_t prince_iv_code[FLASH_FFR_IV_CODE_SIZE] = {0};
|
|
uint8_t tempBuffer[FLASH_FFR_MAX_PAGE_SIZE] = {0};
|
|
|
|
/* Make sure PUF is started to allow key and IV code decryption and generation */
|
|
if (true != PUF_IsGetKeyAllowed(PUF))
|
|
{
|
|
return status;
|
|
}
|
|
|
|
/* Generate new IV code for the PRINCE region */
|
|
status =
|
|
PUF_SetIntrinsicKey(PUF, (puf_key_index_register_t)(uint32_t)((uint32_t)kPUF_KeyIndex_02 + (uint32_t)region), 8,
|
|
&prince_iv_code[0], FLASH_FFR_IV_CODE_SIZE);
|
|
if ((kStatus_Success == status) && (true == store))
|
|
{
|
|
/* Store the new IV code for the PRINCE region into the respective FFRs. */
|
|
/* Create a new version of "Customer Field Programmable" (CFP) page. */
|
|
if ((int32_t)kStatus_FLASH_Success ==
|
|
FFR_GetCustomerInfieldData(flash_context, (uint8_t *)tempBuffer, 0, FLASH_FFR_MAX_PAGE_SIZE))
|
|
{
|
|
/* Set the IV code in the page */
|
|
(void)memcpy(&tempBuffer[offsetof(cfpa_cfg_info_t, ivCodePrinceRegion) +
|
|
(((uint32_t)region * sizeof(cfpa_cfg_iv_code_t))) + 4U],
|
|
&prince_iv_code[0], FLASH_FFR_IV_CODE_SIZE);
|
|
|
|
uint32_t *p32 = (uint32_t *)(uint32_t)tempBuffer;
|
|
uint32_t version = p32[1];
|
|
if (version == 0xFFFFFFFFu)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
version++;
|
|
p32[1] = version;
|
|
|
|
/* Program the page and enable firewall for "Customer field area" */
|
|
if ((int32_t)kStatus_FLASH_Success ==
|
|
FFR_InfieldPageWrite(flash_context, (uint8_t *)tempBuffer, FLASH_FFR_MAX_PAGE_SIZE))
|
|
{
|
|
status = kStatus_Success;
|
|
}
|
|
else
|
|
{
|
|
status = kStatus_Fail;
|
|
}
|
|
}
|
|
}
|
|
if (status == kStatus_Success)
|
|
{
|
|
/* Pass the new IV code */
|
|
(void)memcpy(iv_code, &prince_iv_code[0], FLASH_FFR_IV_CODE_SIZE);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
#endif /* !defined(FSL_PRINCE_DRIVER_LPC55S3x) */
|
|
|
|
#if !defined(FSL_PRINCE_DRIVER_LPC55S3x)
|
|
/*!
|
|
* @brief Load IV code.
|
|
*
|
|
* This function enables IV code loading into the PRINCE bus encryption engine.
|
|
*
|
|
* @param region PRINCE region index.
|
|
* @param iv_code IV code pointer used for passing the IV code.
|
|
*
|
|
* @return kStatus_Success upon success
|
|
* @return kStatus_Fail otherwise
|
|
*/
|
|
status_t PRINCE_LoadIV(prince_region_t region, uint8_t *iv_code)
|
|
{
|
|
status_t status = kStatus_Fail;
|
|
uint32_t keyIndex = (0x0Fu & (uint32_t)iv_code[1]);
|
|
uint8_t prince_iv[8] = {0};
|
|
|
|
/* Make sure PUF is started to allow key and IV code decryption and generation */
|
|
if (true != PUF_IsGetKeyAllowed(PUF))
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
/* Check if region number matches the PUF index value */
|
|
if (((uint32_t)kPUF_KeyIndex_02 + (uint32_t)region) == (uint32_t)keyIndex)
|
|
{
|
|
/* Decrypt the IV */
|
|
if (kStatus_Success == PUF_GetKey(PUF, iv_code, FLASH_FFR_IV_CODE_SIZE, &prince_iv[0], 8))
|
|
{
|
|
/* Store the new IV for the PRINCE region into PRINCE registers. */
|
|
(void)PRINCE_SetRegionIV(PRINCE, (prince_region_t)region, prince_iv);
|
|
status = kStatus_Success;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
#endif /* !defined(FSL_PRINCE_DRIVER_LPC55S3x) */
|
|
|
|
#if !defined(FSL_PRINCE_DRIVER_LPC55S3x)
|
|
/*!
|
|
* @brief Allow encryption/decryption for specified address range.
|
|
*
|
|
* This function sets the encryption/decryption for specified address range.
|
|
* The SR mask value for the selected Prince region is calculated from provided
|
|
* start_address and length parameters. This calculated value is OR'ed with the
|
|
* actual SR mask value and stored into the PRINCE SR_ENABLE register and also
|
|
* into the persistent memory (FFR) to be used after the device reset. It is
|
|
* possible to define several nonadjacent encrypted areas within one Prince
|
|
* region when calling this function repeatedly. If the length parameter is set
|
|
* to 0, the SR mask value is set to 0 and thus the encryption/decryption for
|
|
* the whole selected Prince region is disabled.
|
|
* Ensure about 800 bytes free space on the stack when calling this routine!
|
|
*
|
|
* @param region PRINCE region index.
|
|
* @param start_address start address of the area to be encrypted/decrypted.
|
|
* @param length length of the area to be encrypted/decrypted.
|
|
* @param flash_context pointer to the flash driver context structure.
|
|
* @param regenerate_iv flag to allow IV code regenerating, storing into
|
|
* the persistent memory (FFR) and loading into the PRINCE engine
|
|
*
|
|
* @return kStatus_Success upon success
|
|
* @return kStatus_Fail otherwise
|
|
*/
|
|
status_t PRINCE_SetEncryptForAddressRange(
|
|
prince_region_t region, uint32_t start_address, uint32_t length, flash_config_t *flash_context, bool regenerate_iv)
|
|
{
|
|
status_t status = kStatus_Fail;
|
|
uint32_t srEnableRegister = 0;
|
|
uint32_t alignedStartAddress;
|
|
uint32_t prince_base_addr_ffr_word = 0;
|
|
uint32_t end_address = start_address + length;
|
|
uint32_t prince_region_base_address = 0;
|
|
uint8_t tempBuffer[FLASH_FFR_MAX_PAGE_SIZE] = {0};
|
|
|
|
/* Check input parameters. */
|
|
if (NULL == flash_context)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
/* Check the address range, region borders crossing. */
|
|
#if (defined(FSL_PRINCE_DRIVER_LPC55S0x)) || (defined(FSL_PRINCE_DRIVER_LPC55S1x)) || \
|
|
(defined(FSL_PRINCE_DRIVER_LPC55S2x)) || (defined(FSL_PRINCE_DRIVER_LPC55S3x))
|
|
if ((start_address > FSL_PRINCE_DRIVER_MAX_FLASH_ADDR) ||
|
|
((start_address < FSL_PRINCE_DRIVER_MAX_FLASH_ADDR) && (end_address > FSL_PRINCE_DRIVER_MAX_FLASH_ADDR)))
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
#endif
|
|
#if (defined(FSL_PRINCE_DRIVER_LPC55S6x))
|
|
if ((start_address > FSL_PRINCE_DRIVER_MAX_FLASH_ADDR) ||
|
|
((start_address < 0x40000U) && (end_address > 0x40000U)) ||
|
|
((start_address < 0x80000U) && (end_address > 0x80000U)) ||
|
|
((start_address < FSL_PRINCE_DRIVER_MAX_FLASH_ADDR) && (end_address > FSL_PRINCE_DRIVER_MAX_FLASH_ADDR)))
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
#endif
|
|
|
|
if (true == regenerate_iv)
|
|
{
|
|
uint8_t prince_iv_code[FLASH_FFR_IV_CODE_SIZE] = {0};
|
|
|
|
/* Generate new IV code for the PRINCE region and store the new IV into the respective FFRs */
|
|
status = PRINCE_GenNewIV((prince_region_t)region, &prince_iv_code[0], true, flash_context);
|
|
if (kStatus_Success != status)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
/* Store the new IV for the PRINCE region into PRINCE registers. */
|
|
status = PRINCE_LoadIV((prince_region_t)region, &prince_iv_code[0]);
|
|
if (kStatus_Success != status)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
}
|
|
|
|
alignedStartAddress = ALIGN_DOWN(start_address, (int32_t)FSL_PRINCE_DRIVER_SUBREGION_SIZE_IN_KB * 1024);
|
|
|
|
uint32_t subregion = alignedStartAddress / (FSL_PRINCE_DRIVER_SUBREGION_SIZE_IN_KB * 1024U);
|
|
if (subregion < (32U))
|
|
{
|
|
/* PRINCE_Region0 */
|
|
prince_region_base_address = 0;
|
|
}
|
|
else if (subregion < (64U))
|
|
{
|
|
/* PRINCE_Region1 */
|
|
subregion = subregion - 32U;
|
|
prince_region_base_address = 0x40000;
|
|
}
|
|
else
|
|
{
|
|
/* PRINCE_Region2 */
|
|
subregion = subregion - 64U;
|
|
prince_region_base_address = 0x80000;
|
|
}
|
|
|
|
/* If length > 0 then srEnableRegister mask is set based on the alignedStartAddress and the length.
|
|
If the length is 0, srEnableRegister should be kept 0 (no subregion enabled). */
|
|
if (length != 0U)
|
|
{
|
|
srEnableRegister = (1UL << subregion);
|
|
alignedStartAddress += (FSL_PRINCE_DRIVER_SUBREGION_SIZE_IN_KB * 1024U);
|
|
|
|
while (alignedStartAddress < (start_address + length))
|
|
{
|
|
subregion++;
|
|
srEnableRegister |= (1UL << subregion);
|
|
alignedStartAddress += (FSL_PRINCE_DRIVER_SUBREGION_SIZE_IN_KB * 1024U);
|
|
}
|
|
|
|
uint32_t srEnableRegisterActual = 0;
|
|
(void)PRINCE_GetRegionSREnable(PRINCE, (prince_region_t)region, &srEnableRegisterActual);
|
|
srEnableRegister |= srEnableRegisterActual;
|
|
}
|
|
|
|
/* Store BASE_ADDR into PRINCE register before storing the SR to avoid en/decryption triggering
|
|
from addresses being defined by current BASE_ADDR register content (could be 0 and the decryption
|
|
of actually executed code can be started causing the hardfault then). */
|
|
status = PRINCE_SetRegionBaseAddress(PRINCE, (prince_region_t)region, prince_region_base_address);
|
|
if (kStatus_Success != status)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
/* Store SR into PRINCE register */
|
|
status = PRINCE_SetRegionSREnable(PRINCE, (prince_region_t)region, srEnableRegister);
|
|
if (kStatus_Success != status)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
/* Store SR and BASE_ADDR into CMPA FFR */
|
|
if (kStatus_Success == FFR_GetCustomerData(flash_context, (uint8_t *)&tempBuffer, 0, FLASH_FFR_MAX_PAGE_SIZE))
|
|
{
|
|
/* Set the PRINCE_SR_X in the page */
|
|
(void)memcpy((uint32_t *)(uintptr_t)&tempBuffer[offsetof(cmpa_cfg_info_t, princeSr) +
|
|
((uint32_t)region * sizeof(uint32_t))],
|
|
&srEnableRegister, sizeof(uint32_t));
|
|
/* Set the ADDRX_PRG in the page */
|
|
(void)memcpy(&prince_base_addr_ffr_word,
|
|
(const uint32_t *)(uintptr_t)&tempBuffer[offsetof(cmpa_cfg_info_t, princeBaseAddr)],
|
|
sizeof(uint32_t));
|
|
prince_base_addr_ffr_word &=
|
|
~(((uint32_t)FLASH_CMPA_PRINCE_BASE_ADDR_ADDR0_PRG_MASK) << ((uint32_t)region * 4U));
|
|
prince_base_addr_ffr_word |= (((prince_region_base_address >> PRINCE_BASE_ADDR0_ADDR_PRG_SHIFT) &
|
|
FLASH_CMPA_PRINCE_BASE_ADDR_ADDR0_PRG_MASK)
|
|
<< ((uint32_t)region * 4U));
|
|
(void)memcpy((uint32_t *)(uintptr_t)&tempBuffer[offsetof(cmpa_cfg_info_t, princeBaseAddr)],
|
|
&prince_base_addr_ffr_word, sizeof(uint32_t));
|
|
|
|
/* Program the CMPA page, set seal_part parameter to false (used during development to avoid sealing the
|
|
* part)
|
|
*/
|
|
status = FFR_CustFactoryPageWrite(flash_context, (uint8_t *)tempBuffer, false);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
#endif /* !defined(FSL_PRINCE_DRIVER_LPC55S3x) */
|
|
|
|
/*!
|
|
* @brief Gets the PRINCE Sub-Region Enable register.
|
|
*
|
|
* This function gets PRINCE SR_ENABLE register.
|
|
*
|
|
* @param base PRINCE peripheral address.
|
|
* @param region PRINCE region index.
|
|
* @param sr_enable Sub-Region Enable register pointer.
|
|
*
|
|
* @return kStatus_Success upon success
|
|
* @return kStatus_InvalidArgument
|
|
*/
|
|
status_t PRINCE_GetRegionSREnable(PRINCE_Type *base, prince_region_t region, uint32_t *sr_enable)
|
|
{
|
|
status_t status = kStatus_Success;
|
|
|
|
switch (region)
|
|
{
|
|
case kPRINCE_Region0:
|
|
*sr_enable = base->SR_ENABLE0;
|
|
break;
|
|
|
|
case kPRINCE_Region1:
|
|
*sr_enable = base->SR_ENABLE1;
|
|
break;
|
|
|
|
case kPRINCE_Region2:
|
|
*sr_enable = base->SR_ENABLE2;
|
|
break;
|
|
|
|
default:
|
|
status = kStatus_InvalidArgument;
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*!
|
|
* @brief Gets the PRINCE region base address register.
|
|
*
|
|
* This function gets PRINCE BASE_ADDR register.
|
|
*
|
|
* @param base PRINCE peripheral address.
|
|
* @param region PRINCE region index.
|
|
* @param region_base_addr Region base address pointer.
|
|
*
|
|
* @return kStatus_Success upon success
|
|
* @return kStatus_InvalidArgument
|
|
*/
|
|
status_t PRINCE_GetRegionBaseAddress(PRINCE_Type *base, prince_region_t region, uint32_t *region_base_addr)
|
|
{
|
|
status_t status = kStatus_Success;
|
|
|
|
switch (region)
|
|
{
|
|
case kPRINCE_Region0:
|
|
*region_base_addr = base->BASE_ADDR0;
|
|
break;
|
|
|
|
case kPRINCE_Region1:
|
|
*region_base_addr = base->BASE_ADDR1;
|
|
break;
|
|
|
|
case kPRINCE_Region2:
|
|
*region_base_addr = base->BASE_ADDR2;
|
|
break;
|
|
|
|
default:
|
|
status = kStatus_InvalidArgument;
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*!
|
|
* @brief Sets the PRINCE region IV.
|
|
*
|
|
* This function sets specified AES IV for the given region.
|
|
*
|
|
* @param base PRINCE peripheral address.
|
|
* @param region Selection of the PRINCE region to be configured.
|
|
* @param iv 64-bit AES IV in little-endian byte order.
|
|
*
|
|
* @return kStatus_Success upon success
|
|
* @return kStatus_InvalidArgument
|
|
*/
|
|
status_t PRINCE_SetRegionIV(PRINCE_Type *base, prince_region_t region, const uint8_t iv[8])
|
|
{
|
|
status_t status = kStatus_Fail;
|
|
volatile uint32_t *IVMsb_reg = NULL;
|
|
volatile uint32_t *IVLsb_reg = NULL;
|
|
|
|
switch (region)
|
|
{
|
|
case kPRINCE_Region0:
|
|
IVLsb_reg = &base->IV_LSB0;
|
|
IVMsb_reg = &base->IV_MSB0;
|
|
break;
|
|
|
|
case kPRINCE_Region1:
|
|
IVLsb_reg = &base->IV_LSB1;
|
|
IVMsb_reg = &base->IV_MSB1;
|
|
break;
|
|
|
|
case kPRINCE_Region2:
|
|
IVLsb_reg = &base->IV_LSB2;
|
|
IVMsb_reg = &base->IV_MSB2;
|
|
break;
|
|
|
|
default:
|
|
status = kStatus_InvalidArgument;
|
|
break;
|
|
}
|
|
|
|
if (status != kStatus_InvalidArgument)
|
|
{
|
|
*IVLsb_reg = ((uint32_t *)(uintptr_t)iv)[0];
|
|
*IVMsb_reg = ((uint32_t *)(uintptr_t)iv)[1];
|
|
status = kStatus_Success;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*!
|
|
* @brief Sets the PRINCE region base address.
|
|
*
|
|
* This function configures PRINCE region base address.
|
|
*
|
|
* @param base PRINCE peripheral address.
|
|
* @param region Selection of the PRINCE region to be configured.
|
|
* @param region_base_addr Base Address for region.
|
|
*
|
|
* @return kStatus_Success upon success
|
|
* @return kStatus_InvalidArgument
|
|
*/
|
|
status_t PRINCE_SetRegionBaseAddress(PRINCE_Type *base, prince_region_t region, uint32_t region_base_addr)
|
|
{
|
|
status_t status = kStatus_Success;
|
|
|
|
/* Check input parameters. */
|
|
#if (defined(FSL_PRINCE_DRIVER_LPC55S0x)) || (defined(FSL_PRINCE_DRIVER_LPC55S1x)) || \
|
|
(defined(FSL_PRINCE_DRIVER_LPC55S2x)) || (defined(FSL_PRINCE_DRIVER_LPC55S3x))
|
|
if (region_base_addr > 0U)
|
|
{
|
|
return kStatus_InvalidArgument;
|
|
}
|
|
#endif
|
|
#if (defined(FSL_PRINCE_DRIVER_LPC55S6x))
|
|
if (region_base_addr > 0x80000U)
|
|
{
|
|
return kStatus_InvalidArgument;
|
|
}
|
|
#endif
|
|
|
|
switch (region)
|
|
{
|
|
case kPRINCE_Region0:
|
|
base->BASE_ADDR0 = region_base_addr;
|
|
break;
|
|
|
|
case kPRINCE_Region1:
|
|
base->BASE_ADDR1 = region_base_addr;
|
|
break;
|
|
|
|
case kPRINCE_Region2:
|
|
base->BASE_ADDR2 = region_base_addr;
|
|
break;
|
|
|
|
default:
|
|
status = kStatus_InvalidArgument;
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*!
|
|
* @brief Sets the PRINCE Sub-Region Enable register.
|
|
*
|
|
* This function configures PRINCE SR_ENABLE register.
|
|
*
|
|
* @param base PRINCE peripheral address.
|
|
* @param region Selection of the PRINCE region to be configured.
|
|
* @param sr_enable Sub-Region Enable register value.
|
|
*
|
|
* @return kStatus_Success upon success
|
|
* @return kStatus_InvalidArgument
|
|
*/
|
|
status_t PRINCE_SetRegionSREnable(PRINCE_Type *base, prince_region_t region, uint32_t sr_enable)
|
|
{
|
|
status_t status = kStatus_Success;
|
|
|
|
switch (region)
|
|
{
|
|
case kPRINCE_Region0:
|
|
base->SR_ENABLE0 = sr_enable;
|
|
break;
|
|
|
|
case kPRINCE_Region1:
|
|
base->SR_ENABLE1 = sr_enable;
|
|
break;
|
|
|
|
case kPRINCE_Region2:
|
|
base->SR_ENABLE2 = sr_enable;
|
|
break;
|
|
|
|
default:
|
|
status = kStatus_InvalidArgument;
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
#if !defined(FSL_PRINCE_DRIVER_LPC55S3x)
|
|
/*!
|
|
* @brief Erases the flash sectors encompassed by parameters passed into function.
|
|
*
|
|
* This function erases the appropriate number of flash sectors based on the
|
|
* desired start address and length. It deals with the flash erase function
|
|
* complenentary to the standard erase API of the IAP1 driver. This implementation
|
|
* additionally checks if the whole encrypted PRINCE subregions are erased at once
|
|
* to avoid secrets revealing. The checker implementation is limited to one contiguous
|
|
* PRINCE-controlled memory area.
|
|
*
|
|
* @param config The pointer to the flash driver context structure.
|
|
* @param start The start address of the desired flash memory to be erased.
|
|
* The start address needs to be prince-sburegion-aligned.
|
|
* @param lengthInBytes The length, given in bytes (not words or long-words)
|
|
* to be erased. Must be prince-sburegion-size-aligned.
|
|
* @param key The value used to validate all flash erase APIs.
|
|
*
|
|
* @return #kStatus_FLASH_Success API was executed successfully.
|
|
* @return #kStatus_FLASH_InvalidArgument An invalid argument is provided.
|
|
* @return #kStatus_FLASH_AlignmentError The parameter is not aligned with the specified baseline.
|
|
* @return #kStatus_FLASH_AddressError The address is out of range.
|
|
* @return #kStatus_FLASH_EraseKeyError The API erase key is invalid.
|
|
* @return #kStatus_FLASH_CommandFailure Run-time error during the command execution.
|
|
* @return #kStatus_FLASH_CommandNotSupported Flash API is not supported.
|
|
* @return #kStatus_FLASH_EccError A correctable or uncorrectable error during command execution.
|
|
* @return #kStatus_FLASH_EncryptedRegionsEraseNotDoneAtOnce Encrypted flash subregions are not erased at once.
|
|
*/
|
|
status_t PRINCE_FlashEraseWithChecker(flash_config_t *config, uint32_t start, uint32_t lengthInBytes, uint32_t key)
|
|
{
|
|
/* Check input parameters. */
|
|
if (NULL == config)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
/* Check that the whole encrypted region is erased at once. */
|
|
if (kSECURE_TRUE != PRINCE_CheckerAlgorithm(start, lengthInBytes, kPRINCE_Flag_EraseCheck, config))
|
|
{
|
|
return (int32_t)kStatus_FLASH_EncryptedRegionsEraseNotDoneAtOnce;
|
|
}
|
|
return FLASH_Erase(config, start, lengthInBytes, key);
|
|
}
|
|
|
|
/*!
|
|
* @brief Programs flash with data at locations passed in through parameters.
|
|
*
|
|
* This function programs the flash memory with the desired data for a given
|
|
* flash area as determined by the start address and the length. It deals with the
|
|
* flash program function complenentary to the standard program API of the IAP1 driver.
|
|
* This implementation additionally checks if the whole PRINCE subregions are
|
|
* programmed at once to avoid secrets revealing. The checker implementation is limited
|
|
* to one contiguous PRINCE-controlled memory area.
|
|
*
|
|
* @param config The pointer to the flash driver context structure.
|
|
* @param start The start address of the desired flash memory to be programmed. Must be
|
|
* prince-sburegion-aligned.
|
|
* @param src A pointer to the source buffer of data that is to be programmed
|
|
* into the flash.
|
|
* @param lengthInBytes The length, given in bytes (not words or long-words),
|
|
* to be programmed. Must be prince-sburegion-size-aligned.
|
|
*
|
|
* @return #kStatus_FLASH_Success API was executed successfully.
|
|
* @return #kStatus_FLASH_InvalidArgument An invalid argument is provided.
|
|
* @return #kStatus_FLASH_AlignmentError Parameter is not aligned with the specified baseline.
|
|
* @return #kStatus_FLASH_AddressError Address is out of range.
|
|
* @return #kStatus_FLASH_AccessError Invalid instruction codes and out-of bounds addresses.
|
|
* @return #kStatus_FLASH_CommandFailure Run-time error during the command execution.
|
|
* @return #kStatus_FLASH_CommandFailure Run-time error during the command execution.
|
|
* @return #kStatus_FLASH_CommandNotSupported Flash API is not supported.
|
|
* @return #kStatus_FLASH_EccError A correctable or uncorrectable error during command execution.
|
|
* @return #kStatus_FLASH_SizeError Encrypted flash subregions are not programmed at once.
|
|
*/
|
|
status_t PRINCE_FlashProgramWithChecker(flash_config_t *config, uint32_t start, uint8_t *src, uint32_t lengthInBytes)
|
|
{
|
|
/* Check input parameters. */
|
|
if (NULL == config)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
/* Check that the whole encrypted subregions will be writen at once. */
|
|
if (kSECURE_TRUE != PRINCE_CheckerAlgorithm(start, lengthInBytes, kPRINCE_Flag_WriteCheck, config))
|
|
{
|
|
return (int32_t)kStatus_FLASH_SizeError;
|
|
}
|
|
return FLASH_Program(config, start, src, lengthInBytes);
|
|
}
|
|
#endif /* !defined(FSL_PRINCE_DRIVER_LPC55S3x) */
|
|
|
|
#if defined(FSL_PRINCE_DRIVER_LPC55S3x)
|
|
static status_t PRINCE_ELS_generate_random(uint8_t *output, size_t outputByteLen);
|
|
static status_t PRINCE_ELS_check_key(uint8_t keyIdx, mcuxClEls_KeyProp_t *pKeyProp);
|
|
static status_t PRINCE_ELS_gen_iv_key(void);
|
|
static status_t PRINCE_ELS_enable(void);
|
|
static status_t PRINCE_ELS_calculate_iv(uint32_t *IvReg);
|
|
|
|
/*!
|
|
* @brief Configures PRINCE setting.
|
|
*
|
|
* This function does the initial PRINCE configuration via ROM IAP API call.
|
|
* PRINCE_SR_x configuration for each region configuration is stored into FFR (CMPA).
|
|
* PRINCE IV erase counters (MCTR_INT_IV_CTRx) in CFPA are updated accordingly.
|
|
*
|
|
* Note: This function is expected to be called once in the device lifetime,
|
|
* typically during the initial device provisioning, since it is programming the CMPA pages in PFR flash.
|
|
*
|
|
* @param coreCtx The pointer to the ROM API driver context structure.
|
|
* @param config The pointer to the PRINCE driver configuration structure.
|
|
*
|
|
* @retval #kStatus_Success
|
|
* @retval #kStatus_CommandUnsupported
|
|
* @retval #kStatus_InvalidArgument
|
|
* @retval #kStatus_FLASH_ModifyProtectedAreaDisallowed
|
|
* @retval #kStatusMemoryRangeInvalid
|
|
* @retval #kStatus_Fail
|
|
* @retval #kStatus_OutOfRange
|
|
* @retval #kStatus_SPI_BaudrateNotSupport
|
|
*/
|
|
status_t PRINCE_Configure(api_core_context_t *coreCtx, prince_prot_region_arg_t *config)
|
|
{
|
|
/* Enable ELS and check keys */
|
|
if (kStatus_Success != PRINCE_ELS_enable())
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
return MEM_Config(coreCtx, (uint32_t *)config, kMemoryInternal);
|
|
}
|
|
|
|
/*!
|
|
* @brief Configures PRINCE setting.
|
|
*
|
|
* This function is used to re-configure PRINCE IP based on configuration stored in FFR.
|
|
* This function also needs to be called after wake up from power-down mode to regenerate IV
|
|
* encryption key in ELS key store whose presence is necessary for correct PRINCE operation
|
|
* during erase and write operations to encrypted regions of internal flash memory
|
|
* (dependency for correct operation of MEM_Erase() and MEM_Write() after wake up from power-down mode).
|
|
*
|
|
* @param coreCtx The pointer to the ROM API driver context structure.
|
|
*
|
|
* @retval #kStatus_Success
|
|
* @retval #kStatus_Fail
|
|
*/
|
|
status_t PRINCE_Reconfigure(api_core_context_t *coreCtx)
|
|
{
|
|
status_t status = kStatus_Fail;
|
|
uint64_t princeMask;
|
|
uint32_t IvReg[4] = {0};
|
|
uint32_t ivEraseCounter[3];
|
|
uint32_t srEnable[3];
|
|
uint32_t uuid[4];
|
|
flash_config_t flash_config;
|
|
uint32_t lockWord;
|
|
uint8_t lock[3];
|
|
|
|
/* Enable ELS and check keys */
|
|
status = PRINCE_ELS_enable();
|
|
if (kStatus_Success != status)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
/* Set PRINCE mask value. */
|
|
status = PRINCE_ELS_generate_random((uint8_t *)&princeMask, sizeof(princeMask));
|
|
if (kStatus_Success != status)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
PRINCE_SetMask(PRINCE, princeMask);
|
|
|
|
/* Clean up Flash driver structure and Init*/
|
|
memset(&flash_config, 0, sizeof(flash_config_t));
|
|
if (FLASH_Init(&flash_config) != kStatus_Success)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
/* FFR Init */
|
|
if (FFR_Init(&flash_config) != kStatus_Success)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
/* Get UUID from FFR */
|
|
status = FFR_GetUUID(&flash_config, (uint8_t *)uuid);
|
|
if (kStatus_Success != status)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
/* Check version of CFPA scratch first */
|
|
uint32_t cfpaScratchVer = 0u;
|
|
memcpy(&cfpaScratchVer, (void *)(CFPA_SCRATCH_VER), sizeof(uint32_t));
|
|
|
|
/* Get CFPA version using FFR ROM API */
|
|
uint32_t cfpaVer = 0u;
|
|
if (kStatus_Success !=
|
|
FFR_GetCustomerInfieldData(&flash_config, (uint8_t *)&cfpaVer, CFPA_VER_OFFSET, sizeof(uint32_t)))
|
|
{
|
|
status = kStatus_Fail;
|
|
return status;
|
|
}
|
|
|
|
/* Compare the version of CFPA scratch and version of CFPA returned by ROM API */
|
|
if (cfpaScratchVer > cfpaVer)
|
|
{
|
|
/* Get PRINCE_IV_CTRs from CFPA scratch */
|
|
memcpy(&ivEraseCounter, (void *)CFPA_SCRATCH_IV, sizeof(uint32_t) * PRINCE_REGION_COUNT);
|
|
}
|
|
else
|
|
{
|
|
/* Get PRINCE_IV_CTRs IVs from CFPA ping/pong page */
|
|
status = FFR_GetCustomerInfieldData(&flash_config, (uint8_t *)ivEraseCounter, CFPA_PRINCE_IV_OFFSET,
|
|
sizeof(uint32_t) * PRINCE_REGION_COUNT);
|
|
if (kStatus_Success != status)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
}
|
|
|
|
/* Get PRINCE sub-region enable word from FFR */
|
|
status = FFR_GetCustomerData(&flash_config, (uint8_t *)srEnable, CMPA_PRINCE_SR_OFFSET,
|
|
sizeof(uint32_t) * PRINCE_REGION_COUNT);
|
|
if (kStatus_Success != status)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
/* Get PRINCE lock setting from FFR */
|
|
status = FFR_GetCustomerData(&flash_config, (uint8_t *)&lockWord, CMPA_PRINCE_LOCK_OFFSET, sizeof(uint32_t));
|
|
if (kStatus_Success != status)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
lock[0] = (lockWord & PRINCE_BASE_ADDR_LOCK_REG0_MASK) >> PRINCE_BASE_ADDR_LOCK_REG0_SHIFT;
|
|
lock[1] = (lockWord & PRINCE_BASE_ADDR_LOCK_REG1_MASK) >> PRINCE_BASE_ADDR_LOCK_REG1_SHIFT;
|
|
lock[2] = (lockWord & PRINCE_BASE_ADDR_LOCK_REG2_MASK) >> PRINCE_BASE_ADDR_LOCK_REG2_SHIFT;
|
|
|
|
/* Iterate for all internal PRINCE regions */
|
|
for (prince_region_t region = kPRINCE_Region0; region <= kPRINCE_Region2; region++)
|
|
{
|
|
/* Set region base address. Should be always 0x0 on LPC55S36 */
|
|
status = PRINCE_SetRegionBaseAddress(PRINCE, (prince_region_t)region, 0x0u);
|
|
if (kStatus_Success != status)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
status = PRINCE_SetRegionSREnable(PRINCE, region, srEnable[region]);
|
|
if (kStatus_Success != status)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
/* Prepare ivSeed for current region */
|
|
IvReg[0] = uuid[0];
|
|
IvReg[1] = uuid[1];
|
|
IvReg[2] = uuid[2] ^ region;
|
|
IvReg[3] = ivEraseCounter[region];
|
|
|
|
/* Calculate IV as IvReg = AES_ECB_ENC(DUK_derived_key, {ctx_erase_counter, ctx_id}) */
|
|
status = PRINCE_ELS_calculate_iv(IvReg);
|
|
if (status != kStatus_Success)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
/* Load IV into PRINCE registers */
|
|
status = PRINCE_SetRegionIV(PRINCE, (prince_region_t)region, (uint8_t *)IvReg);
|
|
if (status != kStatus_Success)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
/* Lock region if required */
|
|
if ((lock[region] == 0x1u) || (lock[region] == 0x2u) || (lock[region] == 0x3u))
|
|
{
|
|
PRINCE_SetLock(PRINCE, (kPRINCE_Region0Lock << region));
|
|
}
|
|
}
|
|
|
|
/* Break the main loop in case that error occured during PRINCE configuration */
|
|
if (status != kStatus_Success)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
/* When ENC_ENABLE is set, reading from PRINCE-encrypted regions is disabled. */
|
|
/* For LPC55S36, the ENC_ENABLE is self-cleared after programming memory. */
|
|
PRINCE_EncryptDisable(PRINCE);
|
|
return status;
|
|
}
|
|
|
|
static status_t PRINCE_ELS_generate_random(uint8_t *output, size_t outputByteLen)
|
|
{
|
|
status_t status = kStatus_Fail;
|
|
|
|
// PRNG needs to be initialized; this can be done by calling mcuxClEls_KeyDelete_Async
|
|
// (delete any key slot, can be empty)
|
|
MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(result, token, mcuxClEls_KeyDelete_Async(18));
|
|
// mcuxClEls_KeyDelete_Async is a flow-protected function: Check the protection token and the return value
|
|
if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_KeyDelete_Async) != token) || (MCUXCLELS_STATUS_OK_WAIT != result))
|
|
return kStatus_Fail; // Expect that no error occurred, meaning that the mcuxClEls_KeyDelete_Async operation was
|
|
// started.
|
|
MCUX_CSSL_FP_FUNCTION_CALL_END();
|
|
|
|
// Wait for operation to finish
|
|
MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(result, token, mcuxClEls_WaitForOperation(MCUXCLELS_ERROR_FLAGS_CLEAR));
|
|
// mcuxClEls_WaitForOperation is a flow-protected function: Check the protection token and the return value
|
|
if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_WaitForOperation) != token) || (MCUXCLELS_STATUS_OK != result))
|
|
return kStatus_Fail; // Expect that no error occurred, meaning that the mcuxClEls_WaitForOperation operation was
|
|
// started.
|
|
MCUX_CSSL_FP_FUNCTION_CALL_END();
|
|
|
|
MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(result, token, mcuxClEls_Prng_GetRandom(output, outputByteLen));
|
|
if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_Prng_GetRandom) != token) || (MCUXCLELS_STATUS_OK != result))
|
|
return kStatus_Fail;
|
|
MCUX_CSSL_FP_FUNCTION_CALL_END();
|
|
|
|
status = kStatus_Success;
|
|
return status;
|
|
}
|
|
|
|
static status_t PRINCE_ELS_check_key(uint8_t keyIdx, mcuxClEls_KeyProp_t *pKeyProp)
|
|
{
|
|
/* Check if ELS required keys are available in ELS keystore */
|
|
MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(result, token,
|
|
mcuxClEls_GetKeyProperties(keyIdx, pKeyProp)); // Get key properties from the ELS.
|
|
// mcuxClEls_GetKeyProperties is a flow-protected function: Check the protection token and the return value
|
|
if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_GetKeyProperties) != token) || (MCUXCLELS_STATUS_OK != result))
|
|
return kStatus_Fail;
|
|
MCUX_CSSL_FP_FUNCTION_CALL_END();
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
static status_t PRINCE_ELS_gen_iv_key(void)
|
|
{
|
|
/* The NXP_DIE_MEM_IV_ENC_SK is not loaded and needs to be regenerated (power-down wakeup) */
|
|
/* Set KDF mask and key properties for NXP_DIE_MEM_IV_ENC_SK */
|
|
SYSCON->ELS_KDF_MASK = SYSCON_ELS_KDF_MASK;
|
|
static const uint32_t ddata2[3] = {0x62032504, 0x72f04280, 0x87a2bbae};
|
|
mcuxClEls_KeyProp_t keyProp;
|
|
/* Set key properties in structure */
|
|
keyProp.word.value = MCUXCLELS_KEYPROPERTY_VALUE_AES | MCUXCLELS_KEYPROPERTY_VALUE_GENERAL_PURPOSE_SLOT |
|
|
MCUXCLELS_KEYPROPERTY_VALUE_ACTIVE;
|
|
status_t status = kStatus_Fail;
|
|
|
|
/* Generate the key using CKDF */
|
|
MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(
|
|
result, token,
|
|
mcuxClEls_Ckdf_Sp800108_Async((mcuxClEls_KeyIndex_t)0, (mcuxClEls_KeyIndex_t)NXP_DIE_MEM_IV_ENC_SK, keyProp,
|
|
(uint8_t const *)ddata2));
|
|
if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_Ckdf_Sp800108_Async) != token) && (MCUXCLELS_STATUS_OK != result))
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
MCUX_CSSL_FP_FUNCTION_CALL_END();
|
|
|
|
/* Wait for CKDF to finish */
|
|
MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(result, token, mcuxClEls_WaitForOperation(MCUXCLELS_ERROR_FLAGS_CLEAR));
|
|
if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_WaitForOperation) == token) && (MCUXCLELS_STATUS_OK == result))
|
|
{
|
|
status = kStatus_Success;
|
|
}
|
|
MCUX_CSSL_FP_FUNCTION_CALL_END();
|
|
|
|
return status;
|
|
}
|
|
|
|
static status_t PRINCE_ELS_enable(void)
|
|
{
|
|
mcuxClEls_KeyProp_t key_properties;
|
|
status_t status = kStatus_Fail;
|
|
|
|
/* Enable ELS and related clocks */
|
|
status = ELS_PowerDownWakeupInit(ELS);
|
|
if (status != kStatus_Success)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
/* Check if MEM_ENC_SK key is available in ELS keystore */
|
|
status = PRINCE_ELS_check_key(NXP_DIE_MEM_ENC_SK, &key_properties);
|
|
if (status != kStatus_Success || key_properties.bits.kactv != 1u)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
/* Check if MEM_IV_ENC_SK key is available in ELS keystore */
|
|
status = PRINCE_ELS_check_key(NXP_DIE_MEM_IV_ENC_SK, &key_properties);
|
|
if (status != kStatus_Success || key_properties.bits.kactv != 1u)
|
|
{
|
|
return PRINCE_ELS_gen_iv_key();
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
static status_t PRINCE_ELS_calculate_iv(uint32_t *IvReg)
|
|
{
|
|
mcuxClEls_CipherOption_t cipherOptions = {0};
|
|
status_t status = kStatus_Fail;
|
|
|
|
/* Configure ELS for AES ECB-128, using NXP_DIE_MEM_IV_ENC_SK key */
|
|
cipherOptions.bits.cphmde = MCUXCLELS_CIPHERPARAM_ALGORITHM_AES_ECB;
|
|
cipherOptions.bits.dcrpt = MCUXCLELS_CIPHER_ENCRYPT;
|
|
cipherOptions.bits.extkey = MCUXCLELS_CIPHER_INTERNAL_KEY;
|
|
|
|
do
|
|
{
|
|
/* Calculate IV as IvReg = AES_ECB_ENC(NXP_DIE_MEM_IV_ENC_SK, ivSeed[127:0]) */
|
|
/* ivSeed[127:0] = {UUID[96:0] ^ regionNumber[1:0], ivEraseCounter[31:0]} */
|
|
MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(
|
|
result, token,
|
|
mcuxClEls_Cipher_Async(cipherOptions, (mcuxClEls_KeyIndex_t)NXP_DIE_MEM_IV_ENC_SK, NULL,
|
|
MCUXCLELS_CIPHER_KEY_SIZE_AES_128, (uint8_t *)IvReg, MCUXCLELS_CIPHER_BLOCK_SIZE_AES,
|
|
NULL, (uint8_t *)IvReg));
|
|
if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_Cipher_Async) != token) || (MCUXCLELS_STATUS_OK_WAIT != result))
|
|
break;
|
|
MCUX_CSSL_FP_FUNCTION_CALL_END();
|
|
|
|
MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(
|
|
result, token,
|
|
mcuxClEls_WaitForOperation(
|
|
MCUXCLELS_ERROR_FLAGS_CLEAR)); // Wait for the mcuxClEls_Enable_Async operation to complete.
|
|
// mcuxClEls_WaitForOperation is a flow-protected function: Check the protection token and the return value
|
|
if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_WaitForOperation) == token) && (MCUXCLELS_STATUS_OK == result))
|
|
{
|
|
status = kStatus_Success;
|
|
}
|
|
MCUX_CSSL_FP_FUNCTION_CALL_END();
|
|
} while (0);
|
|
|
|
return status;
|
|
}
|
|
#endif /* defined(FSL_PRINCE_DRIVER_LPC55S3x) */
|