/* * 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) */