Hi Lukas,
Thank you for your quick reply. This is a viable option to work with for us. I assumed the CMD_DBG_CHAL and CMD_DBG_AUTH commands should be run by a debugger.
I tried to implement both options you mentioned on another device:
- Running CMD_DBG_CHAL and CMD_DBG_AUTH commands to reset the CSEc so a Mass Erase can unsecure the device.
- Running the verify backdoor access command and thus unsecuring the device and then erasing the first sector and overwriting it with an unsecured flash configuration.
Everything worked perfectly when the device was unsecured and the CSEc was reset (mainly visible by the keys being programmable with counter 1 and the FlexNVM partitioning being reset) and the FSEC register was rewritting.
After having build some confidence in the solution I secured the flash with the same code still running (the unsecuring was done in the application, the flash config is part of the bootloader). However now the device is still secured and does not unsecure itself. Also the Device did become unresponsive again on the CAN bus so I assume something might get messed up when securing the device and therefore it no longer runs the code to unsecure it correctly.
Do you maybe have any idea what might cause the device to no longer function when being secured?
Thanks,
Frank
EDIT:
In an attempt to isolate the issue I started running examples 1 and 5 from AN5401SW. Using example 1 to configure and load keys into the CSEc and using example 5 to reset the CSEc to factory default conditions. This works well when I build example 1 to run from RAM and example 5 to run from flash. (When the CSEc is configured from flash the device crashes, as expected).
I can secure the device and recover it using a mass erase using the following steps:
- Load example 1 into RAM (unsecured) to configure the CSEc and load keys
- Load example 5 into FLASH (unsecured) to reset the CSEc
- Load example 5 into FLASH (secured: FSEC set to 0xFF).
- Perform a mass erase to unsecure the device.
- I am able to upload any code and debug as normal now.
However when I perform skip step 2 the device is bricked:
- Load example 1 into RAM (unsecured) to configure the CSEc and load keys
- Load example 5 into FLASH (secured: FSEC set to 0xFF) to secure the device and then also reset the CSEc.
- Perform a mass erase to unsecure the device, however this fails
- The device is now bricked and I can no longer attach a debugger.
So it seems that the device is unable to reset the CSEc when the device is secured. When I have reset the CSEc beforehand the mass erase does work. Should I run the command to reset the CSEc from RAM when the device is secured or perform any additional steps?
The software that was used for this test is attached.
The flashconfig:
.section .FlashConfig, "a"
.long 0x76543210 // Backdoor comparison key
.long 0xFEDCBA98 // Backdoor comparison key
.long 0xFFFFFFFF // FPROT0-3
.long 0xFFFF7FBB /* FDPROT:FEPROT:FOPT:FSEC */
The code I am using:
int main(void) {
uint16_t __attribute__((unused)) csec_error = 0; //1 means No Error
csec_error = INIT_RNG(); /* Initialize the Random Number Generator before generating challenge */
// Load Master ECU key
csec_error = loadMasterECUKey();
// Erase all keys and reset the part to the factory state
uint32_t MASTER_ECU_KEY_VALUE[4] ={0xa2435381, 0x2c4b929f, 0x4c044a8a,0xfbf4b729};
csec_error = resetCSEc(MASTER_ECU_KEY_VALUE);
// Initialize program flash memory controller
PROGRAMFLASH_memoryInit();
/* Check the Verify backdoor access key command */
PROGRAMFLASH_unsecureDevice();
}
// Reset the CSEc to delete the keys and reconfigure partition
uint16_t resetCSEc(uint32_t *master_ecu_key_value){
uint16_t csec_error = 0; //1 means No Error
// Factory reset the CSEc
uint32_t dbg_challenge_out[4] = {0,0,0,0};
csec_error = DBG_CHAL(dbg_challenge_out);
csec_error = DBG_AUTH(dbg_challenge_out,master_ecu_key_value);
return csec_error;
}
/* Issue Debug challenge command */
uint16_t DBG_CHAL(uint32_t *dbg_challenge_out)
{
while((FTFC->FSTAT & FTFC_FSTAT_CCIF_MASK) != FTFC_FSTAT_CCIF_MASK); //Check for the ongoing FLASH command
FTFC->FSTAT = (FTFC_FSTAT_FPVIOL_MASK | FTFC_FSTAT_ACCERR_MASK);// Write 1 to clear error flags */
/* Start command by wring Header */
command_header= (CMD_DBG_CHAL << 24) | (CMD_FORMAT_COPY << 16) | (CALL_SEQ_FIRST << | 0x00; //Write to Page0 Word0, Value = 0x12000000
Start_Command(command_header);
csec_error_bits = CSE_PRAM->RAMn[1].DATA_32 >> 16; //Read Page0 Word1, Error Bits
for(i=4,j=0;i<8;i++,j++)
dbg_challenge_out[j] = CSE_PRAM->RAMn[i].DATA_32;
return csec_error_bits;
}
/* Issue Debug Authorization command */
uint16_t DBG_AUTH(uint32_t *dbg_challenge_out,uint32_t *master_ecu_key_value)
{
uint32_t K_out[4];
uint32_t UID[4] = {0,0,0,0};
uint32_t UID_MAC[4] = {0,0,0,0};
uint32_t authorization[4];
//First Calculate the Authorization
csec_error_bits = GET_UID(UID, UID_MAC);
csec_error_bits = KDF(K_out, master_ecu_key_value, DEBUG_KEY_C);
csec_error_bits = LOAD_RAM_KEY(K_out, RAM_KEY);
uint32_t DATA[8];
for(i=0; i<4; i++)
DATA[i] = dbg_challenge_out[i];
for(;i<8;i++)
DATA[i] = UID[i-4];
csec_error_bits = CMAC(authorization, DATA, RAM_KEY, 248);
//Now actually issue authorization command
while((FTFC->FSTAT & FTFC_FSTAT_CCIF_MASK) != FTFC_FSTAT_CCIF_MASK); //Check for the ongoing FLASH command
FTFC->FSTAT = (FTFC_FSTAT_FPVIOL_MASK | FTFC_FSTAT_ACCERR_MASK); // Write 1 to clear error flags
for(i=4,j=0; i<8; i++,j++) //Write to Page1
CSE_PRAM->RAMn[i].DATA_32 = authorization[j];
/* Start command by wring Header */
command_header= (CMD_DBG_AUTH << 24) | (CMD_FORMAT_COPY << 16) | (CALL_SEQ_FIRST << | 0x00; // Write to Page0 Word0, Value = 0x13000000
Start_Command(command_header);
csec_error_bits = CSE_PRAM->RAMn[1].DATA_32 >> 16; //Read Page0 Word1, Error Bits
return csec_error_bits;
}
// Unsecure device
void PROGRAMFLASH_unsecureDevice() {
flash_drv_status_t ret; /* Store the driver api return code */
const uint8_t backdoorAccessKey[8] = {0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE};
// Disable interrupts during flash commands
INT_SYS_DisableIRQGlobal();
ret = FlashSecurityBypass(&flashSSDConfig, backdoorAccessKey,pCmdSequence);
INT_SYS_EnableIRQGlobal();
}
flash_drv_status_t FlashSecurityBypass(const flash_ssd_config_t * pSSDConfig, \
const uint8_t* keyBuffer, \
flash_command_sequence_t pFlashCommandSequence)
{
#ifdef DEV_ERROR_DETECT
DEV_ASSERT(pSSDConfig != NULL);
DEV_ASSERT(keyBuffer != NULL);
DEV_ASSERT(pFlashCommandSequence != NULL);
#endif
flash_drv_status_t ret; /* Return code variable */
uint32_t temp; /* Temporary variable */
uint8_t i;
/* Clear RDCOLERR & ACCERR & FPVIOL flag in flash status register. Write 1 to clear */
CLEAR_FTFx_FSTAT_ERROR_BITS;
/* Passing parameter to the command */
FTFx_FCCOB0 = FTFx_SECURITY_BY_PASS;
for (i = 0U; i < 8U; i++)
{
temp = FTFx_BASE + i + 0x08U;
*(uint8_t *)temp = keyBuffer[i];
}
ret = pFlashCommandSequence(pSSDConfig);
return (ret);
}