Today I'd like to discuss a scenario that I will encounter in practical application.
In the design phase, the Serial Nor flash is usually used as a code storage device for RT series MCUs, such as QSPI, HyperFlash, etc, as these devices all support XIP features. The flash usually is not only occupied by the code but also used to store data, such as device parameters, and log information, and even used as a file system. So we need to evaluate Flash's size.
Is there any constraint to the manipulation of data space in the secure boot mode? And how to keep the data confidential? We'll talk about it below and let's get started.
The bootable image is plaintext, and it may be changed after the writing operation of the FlexSPI module. If the digest algorithm obtains the different values will lead to the verification process fails, as the writing operation destroys the integrity of the data, regarding confidentiality, data is stored in plaintext in Serial Nor flash under HAB boot.
Fig1 Signature and verification process
After enabling the Encrypted XIP boot mode, what is the impact on FlexSPI's read and write data operations? The first point is that the read data will be treated as encrypted data and decrypted by the BEE or OTFAD module. However, if a write operation is performed, the BEE or OTFAD module will be bypassed, in another word, the data will be written directly to the Serial Nor flash. in a short, it is not affected by the Encrypted XIP boot mode.
As shown in Fig 2, the encrypted code and data stored in Serial Nor flash need to be decrypted before they are sent to the CPU for execution. This is also the implementation mechanism of the Encrypted XIP boot mode. To enable the encrypted XIP boot mode, it needs to burn the keys to eFuse, after that, eFuse is impossible to restore, so the test cost seems a bit high, so I recommend you refer to the source code of the 《How to Enable the On-the-fly Decryption》application note to dynamically configure the key of the BEE module and read the encrypted array by DCP from Flash, then compare to plaintext array to verify BEE module participle the decryption flow.
Fig 2
Modify the source code of the above application note, define s_nor_program_buffer [256], then set the values through the following code, and burn them to the 20th sector, the offset address is 0x14000.
for (i = 0; i < 0xFFU; i++)
{
s_nor_program_buffer[i] = i;
}
status =
flexspi_nor_flash_page_program(EXAMPLE_FLEXSPI, EXAMPLE_SECTOR * SECTOR_SIZE, (void *)s_nor_program_buffer);
if (status != kStatus_Success)
{
PRINTF("Page program failure !\r\n");
return -1;
}
DCACHE_InvalidateByRange(EXAMPLE_FLEXSPI_AMBA_BASE + EXAMPLE_SECTOR * SECTOR_SIZE, FLASH_PAGE_SIZE);
memcpy(s_nor_read_buffer, (void *)(EXAMPLE_FLEXSPI_AMBA_BASE + EXAMPLE_SECTOR * SECTOR_SIZE),
sizeof(s_nor_read_buffer));
for(uint32_t i = 0; i < 256; i++)
{
PRINTF("The %d data in the second region is 0x%x\r\n", i, s_nor_read_buffer[i]);
}
After the programming finishes, connect to Jlink and use J-flash to check whether the burned array is correct. The results prove that the write operation will bypass the BEE or OTFAD module and write the data directly to the Serial Nor flash, which is consistent with Fig 2.
Fig3
As mentioned at the beginning, in the real project, we may need to use Flash to store data, such as device parameters, log information, or even as a file system, and the saved data is usually a bit sensitive and should prevent being easily obtained by others. For example, in SLN-VIZNAS-IoT, there is a dedicated area for storing facial feature data.
Fig 4
Although the purely facial feature data is only meaningful for specific recognition algorithms, in another word, even if a third-party application obtains the original data, it is useless for hackers.
In the development of real face recognization projects, it is usually to declare other data items associated with facial feature data, such as name, age, preferences, etc, as shown below:
typedef union
{
struct
{
/*put char/unsigned char together to avoid padding*/
unsigned char magic;
char name[FEATUREDATA_NAME_MAX_LEN];
int index;
// this id identify a feature uniquely,we should use it as a handler for feature add/del/update/rename
uint16_t id;
uint16_t pad;
// Add a new component
uint16_t coffee_taste;
/*put feature in the last so, we can take it as dynamic, size limitation:
* (FEATUREDATA_FLASH_PAGE_SIZE * 2 - 1 - FEATUREDATA_NAME_MAX_LEN - 4 - 4 -2)/4*/
float feature[0];
};
unsigned char raw[FEATUREDATA_FLASH_PAGE_SIZE * 2];
} FeatureItem; // 1kB
After enabling the Encrypted XIP boot mode, the above write operation test has proved that FlexSPI's write operation will program the data into Serial Nor flash directly, but during the reading process, the data will be decrypted by BEE or OTFAD, so we'd better use DCP or other modules to encrypt the data prior to writing, otherwise, the read operation will get the values that the plaintext goes through the decryption calculation.
Assume XIP encrypted boot is enabled, and whether it's okay to send the encrypted bootable image sent to the OEM for mass production. Moreover, is it able to allow the customers to access the encrypted bootable image without worrying about application image leakage?
In order to verify the above guesses, I do the following testing on MIMXRT1060-EVK.
Fig5
Fig 6
Fig 7
Open then 9_21_readback.Bin and compare it with the plain text image on the right border, they are the same actually, in other words, the plaintext image was leaked.
Fig 8
As the above Fig 2 shows, the encrypted code and data stored in Serial Nor flash need to be decrypted before they are sent to the CPU for execution.
When Jlink connects to the target MCU, it will load the corresponding flash driver algorithm to run in the FlexRAM. If the flash driver algorithm detects the boot type of the MCU just like the following code, then configures the BEE or OTFAD module according to the detecting result, after that, when reading the ciphertext in the Nor Flash, the data will be automatically decrypted.
status = SLN_AUTH_check_context(SLN_CRYPTO_CTX_1);
configPRINTF(("Context check status %d\r\n", status));
// DEBUG_LOG_DELAY_MS(1000); // Optional delay, enable for debugging to ensure log is printed before a crash
if (SLN_AUTH_NO_CONTEXT == status)
{
configPRINTF(("Ensuring context...\r\n"));
// DEBUG_LOG_DELAY_MS(1000); // Optional delay, enable for debugging to ensure log is printed before a crash
// Load crypto contexts and make sure they are valid (our own context should be good to get to this point!)
status = bl_nor_encrypt_ensure_context();
if (kStatus_Fail == status)
{
configPRINTF(("Failed to load crypto context...\r\n"));
// DEBUG_LOG_DELAY_MS(1000); // Optional delay, enable for debugging to ensure log is printed before a crash
// Double check if encrypted XIP is enabled
if (!bl_nor_encrypt_is_enabled())
{
configPRINTF(("Not running in encrypted XIP mode, ignore error.\r\n"));
// DEBUG_LOG_DELAY_MS(1000); // Optional delay, enable for debugging to ensure log is printed before a
// crash
// No encrypted XIP enabled, we can ignore the bad status
status = kStatus_Success;
}
}
else if (kStatus_ReadOnly ==
status) // Using this status from standard status to indicate that we need to split PRDB
{
volatile uint32_t delay = 1000000;
// Set up context as needed for this application
status = bl_nor_encrypt_split_prdb();
configPRINTF(("Restarting BOOTLOADER...\r\n"));
while (delay--)
;
// Restart
DbgConsole_Deinit();
NVIC_DisableIRQ(LPUART6_IRQn);
NVIC_SystemReset();
}
}
else if (SLN_AUTH_OK == status)
{
configPRINTF(("Ensuring context...\r\n"));
// DEBUG_LOG_DELAY_MS(1000); // Optional delay, enable for debugging to ensure log is printed before a crash
// We will check to see if we need to update the backup to the reduced scope PRDB0 for bootloader space
status = bl_nor_encrypt_ensure_context();
if (kStatus_Fail == status)
{
configPRINTF(("Failed to load crypto context...\r\n"));
// DEBUG_LOG_DELAY_MS(1000); // Optional delay, enable for debugging to ensure log is printed before a crash
// Double check if encrypted XIP is enabled
if (!bl_nor_encrypt_is_enabled())
{
configPRINTF(("Not running in encrypted XIP mode, ignore error.\r\n"));
// No encrypted XIP enabled, we can ignore the bad status
status = kStatus_Success;
}
}
else if (kStatus_Success == status) // We have good PRDBs so we can update the backup
{
bool isMatch = false;
bool isOriginal = false;
configPRINTF(("Checking backup context...\r\n"));
// DEBUG_LOG_DELAY_MS(1000); // Optional delay, enable for debugging to ensure log is printed before a crash
// Check if we have identical KIBs and initial CTR
status = bl_nor_crypto_ctx_compare_backup(&isMatch, &isOriginal, SLN_CRYPTO_CTX_0);
if (kStatus_Success == status)
{
if (isMatch && isOriginal)
{
configPRINTF(("Updating backup context with valid address space...\r\n"));
// DEBUG_LOG_DELAY_MS(1000); // Optional delay, enable for debugging to ensure log is printed before
// a crash
// Update backup PRDB0
status = SLN_AUTH_backup_context(SLN_CRYPTO_CTX_0);
}
}
}
}
Now we already understand the cause of the leak, we must prohibit external tools from loading flashloader or flash driver algorithms into the FlexRAM to run, so in addition to disabling the Debug port, we also need to disable the Serial download method to prevent the hackers take advantage of the Serial Downloader method to make the ROM code load a special flashloader to run in RAM, then configure the BEE or OTFAD module prior to reading the image.
However, compared to simply prohibiting the debug port, I'd highly recommend you select the Secure Debug method, because the debug feature requirement is important to return/filed testing, Secure Debug just is like adding a sturdy lock to the debug port, and only the authorized one can open this lock to enter the debugging mode successfully.