I'm working on adding encryption to the secure bootloader in SDK v16.0.0. I've based my changes largely on the framework and code provided in this thread. I believe I have made the required modifications to nrfutil and the secure bootloader but my DFU fails in the postvalidate hash verification. I believe it is failing because the data is not being properly decrypted.
I've updated the version 6.1 of nrfutil to add the nonce as described in the linked thread. There are a few difference between the steps Matthew described and what I did, but I believe these are a result of the different starting versions of nrfutil. I can successfully generate an init packet with the added nonce using my custom version of nrfutil.
DFU Package: <.\FW_encrypted_0.2.20_1.zip>:
|
|- Image count: 1
|
|- Image #0:
|- Type: application
|- Image file: app.bin
|- Init packet file: app.dat
|
|- op_code: INIT
|- signature_type: ECDSA_P256_SHA256
|- signature (little-endian): b'd4960c693f643c72198d36b67ac401a06074256be78debdeaf8f3f300138c28d2521f6e2baf71188ec356da91bcdff846bcbff8ad370e1585d1ba00a2f959a70'
|
|- fw_version: 0x00000002 (2)
|- hw_version 0x00000034 (52)
|- sd_req: 0xCB
|- type: APPLICATION
|- sd_size: 0
|- bl_size: 0
|- app_size: 166812
|
|- hash_type: SHA256
|- hash (little-endian): b'59345485a00fb7e415a6882a4f90d423fbf2db3188c341a478c033dfa190e807'
|
|- boot_validation_type: ['VALIDATE_GENERATED_CRC']
|- boot_validation_signature (little-endian): [b'']
|
|- is_debug: False
|- nonce (little-endian): b'badb25b5dee5e4ffaf79626b'
I've also completed the modifications to the secure bootloader. I've also added more logging to assist with debugging. I added a print of the encrypted and decrypted in the on_data_obj_write_request() function.
static void on_data_obj_write_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
{
NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_WRITE (data)");
if (!nrf_dfu_validation_init_cmd_present())
{
/* Can't accept data because DFU isn't initialized by init command. */
p_res->result = NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED;
return;
}
uint32_t const data_object_offset = s_dfu_settings.progress.firmware_image_offset -
s_dfu_settings.progress.firmware_image_offset_last;
if ((p_req->write.len + data_object_offset) > s_dfu_settings.progress.data_object_size)
{
/* Can't accept data because too much data has been received. */
NRF_LOG_ERROR("Write request too long");
p_res->result = NRF_DFU_RES_CODE_INVALID_PARAMETER;
return;
}
uint32_t const write_addr = m_firmware_start_addr + s_dfu_settings.write_offset;
/* CRC must be calculated before handing off the data to fstorage because the data is
* freed on write completion. CRC must be computed on the encrypted data.
*/
uint32_t const next_crc =
crc32_compute(p_req->write.p_data, p_req->write.len, &s_dfu_settings.progress.firmware_image_crc);
ASSERT(p_req->callback.write);
//Decrypt the data
if (nrf_dfu_validation_crypt_required())
{
NRF_LOG_INFO("Encrypted data");
NRF_LOG_HEXDUMP_DEBUG(&p_req->write.p_data, p_req->write.len);
for (int i = 0; i < p_req->write.len; i++)
{
if (nrf_dfu_validation_crypt(&p_req->write.p_data[i]) != NRF_SUCCESS)
{
NRF_LOG_WARNING("Decryption error!");
}
}
NRF_LOG_INFO("Decrypted data");
NRF_LOG_HEXDUMP_DEBUG(&p_req->write.p_data, p_req->write.len);
}
ret_code_t ret =
nrf_dfu_flash_store(write_addr, p_req->write.p_data, p_req->write.len, p_req->callback.write);
if (ret != NRF_SUCCESS)
{
/* When nrf_dfu_flash_store() fails because there is no space in the queue,
* stop processing the request so that the peer can detect a CRC error
* and retransmit this object. Remember to manually free the buffer !
*/
p_req->callback.write((void*)p_req->write.p_data);
return;
}
/* Update the CRC of the firmware image. */
s_dfu_settings.write_offset += p_req->write.len;
s_dfu_settings.progress.firmware_image_offset += p_req->write.len;
s_dfu_settings.progress.firmware_image_crc = next_crc;
/* This is only used when the PRN is triggered and the 'write' message
* is answered with a CRC message and these field are copied into the response.
*/
p_res->write.crc = s_dfu_settings.progress.firmware_image_crc;
p_res->write.offset = s_dfu_settings.progress.firmware_image_offset;
}
Here's my nrf_dfu_validation_crypt() function which was taken from the linked thread.
uint32_t nrf_dfu_validation_crypt(uint8_t * buf)
{
uint32_t err_code;
if (!m_initialized)
{
return NRF_ERROR_INVALID_STATE;
}
if (m_index == 0)
{
err_code = sd_ecb_block_encrypt(&m_ecb_data);
if (NRF_SUCCESS != err_code)
{
return err_code;
}
}
* buf ^= m_ecb_data.ciphertext[m_index];
m_index++;
if (m_index == 16)
{
m_index = 0;
//Increment the counter
m_counter++;
m_ecb_data.cleartext[ECB_KEY_LEN-1] = (uint8_t)(m_counter & 0xFF);
m_ecb_data.cleartext[ECB_KEY_LEN-2] = (uint8_t)((m_counter >> 8) & 0xFF);
m_ecb_data.cleartext[ECB_KEY_LEN-3] = (uint8_t)((m_counter >> 16) & 0xFF);
m_ecb_data.cleartext[ECB_KEY_LEN-4] = (uint8_t)((m_counter >> 24) & 0xFF);
}
return NRF_SUCCESS;
}
Here's the log of the first two packets being decrypted.
00> <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_CREATE (data) 00> <debug> nrf_dfu_flash: nrf_fstorage_erase(addr=0x0x00026000, len=1 pages), queue usage: 0 00> <debug> nrf_dfu_req_handler: Creating object with size: 4096. Offset: 0x00000000, CRC: 0x00000000 00> <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1 00> <debug> nrf_dfu_flash: Flash erase success: addr=0x00026000, pending 0 00> <debug> nrf_dfu_ble: Buffer 0x200082B0 acquired, len 244 (244) 00> <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_WRITE (data) 00> <info> nrf_dfu_req_handler: Encrypted data 00> <debug> nrf_dfu_req_handler: B0 82 00 20 F4 00 00 00|... .... 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <info> nrf_dfu_req_handler: Decrypted data 00> <debug> nrf_dfu_req_handler: B0 82 00 20 F4 00 00 00|... .... 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_flash: nrf_fstorage_write(addr=0x00026000, src=0x200082B0, len=244 bytes), queue usage: 0 00> <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1 00> <debug> nrf_dfu_ble: Buffer 0x200083A4 acquired, len 244 (244) 00> <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_WRITE (data) 00> <info> nrf_dfu_req_handler: Encrypted data 00> <debug> nrf_dfu_req_handler: A4 83 00 20 F4 00 00 00|... .... 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <info> nrf_dfu_req_handler: Decrypted data 00> <debug> nrf_dfu_req_handler: A4 83 00 20 F4 00 00 00|... .... 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_req_handler: 00 00 00 00 00 00 00 00|........ 00> <debug> nrf_dfu_flash: nrf_fstorage_write(addr=0x000260F4, src=0x200083A4, len=244 bytes), queue usage: 1 00> <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
As you can see the logged encrypted and decrypted data is the same. Is there a change that I need to make to nrf_dfu_validation_crypt() for SDK v16.0.0? Or is is issue indicative of an error in a different part of the encrypted DFU process?