This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Adding encryption to secure DFU SDK v16.0.0 - Failed decryption

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 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?

Parents
  • Hi,

    First of all, please note that the code posted in the other thread by devzone user Matthew, is not official code from Nordic Semiconductor. That said, the code has been very useful for several users that wants to add support for DFU encryption.

    I believe the post from Matthew is written for SDK v15.0. There were several changes in the bootloader and nrfutil from SDK v15.0 to SDK v16.0. There was e.g. a new bootloader settings versions introduced in SDK v15.3 because of the changes. See this page

    Some questions:

    • Were you able to follow the steps in the guide, and add support for DFU encryption in SDK v15, before trying to port it to SDK v16 ?
    • Did you check that the nrf_dfu_validation_crypt_init() function was able to properly copy/save the ecb_key and nonce to m_ecb_data? And that the same key is actually used in nrf_dfu_validation_crypt() / sd_ecb_block_encrypt() ?
    • Did you see this post ? Devzone user ibeckermayer tried to port it to SDK v15.2 in that post, he had some issues, but there might be some useful pointers there. 
  • Hi Sigurd,

    Absolutely, I understand. I'm posting this here in hopes that this may help others in the future as well.

    • No, I did not try that. I was hoping to avoid making the changes twice.
    • Yes, I added debug prints to those functions in the following locations -
      static void ctr_init(const uint8_t * p_nonce, const uint8_t * p_ecb_key)
      {
          m_initialized = true;
          m_index = 0;
          m_counter = 0;
      
          // Copy the nonce.
          memcpy(&m_ecb_data.cleartext[0], p_nonce, ECB_KEY_LEN);
      
          /* Reverse the array as ECB expects it in big-endian format */
          for (uint8_t i=0; i<ECB_KEY_LEN; i++)
          {
              // Save the key.
              m_ecb_data.key[i] = p_ecb_key[ECB_KEY_LEN-1-i];
          }
      
          //TODO remove prints
          NRF_LOG_INFO("Init Nonce: ");
          NRF_LOG_HEXDUMP_DEBUG(m_ecb_data.cleartext, ECB_KEY_LEN);
          NRF_LOG_INFO("Init ECB Key: ");
          NRF_LOG_HEXDUMP_DEBUG(m_ecb_data.key, ECB_KEY_LEN);
      }

      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)
          {
              //TODO remove prints
              NRF_LOG_INFO("Working Nonce: ");
              NRF_LOG_HEXDUMP_DEBUG(m_ecb_data.cleartext, ECB_KEY_LEN);
              NRF_LOG_INFO("Working ECB Key: ");
              NRF_LOG_HEXDUMP_DEBUG(m_ecb_data.key, ECB_KEY_LEN);
      
              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;
      }


      The relevant sections of the log show that the nonce and ECB key are being copied into m_ecb_data and are big-endian. The nonce and key match the nonce in key used when generating the DFU zip.
      00> <debug> nrf_dfu_validation: PB: Init packet data len: 78
      00> <info> nrf_dfu_validation: Init Nonce: 
      00> <debug> nrf_dfu_validation:  FF E5 87 F1 2E 3F 7A 1A|.....?z.
      00> <debug> nrf_dfu_validation:  7B 62 FE 75 00 00 00 00|{b.u....
      00> <info> nrf_dfu_validation: Init ECB Key: 
      00> <debug> nrf_dfu_validation:  5F 12 D1 50 98 87 12 E3|_..P....
      00> <debug> nrf_dfu_validation:  18 5B 37 6B AD 0F 33 93|.[7k..3.
      00> <info> nrf_dfu_validation: nrf_dfu_validation_crypt_init()

      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> <info> nrf_dfu_validation: Working Nonce: 
      00> <debug> nrf_dfu_validation:  FF E5 87 F1 2E 3F 7A 1A|.....?z.
      00> <debug> nrf_dfu_validation:  7B 62 FE 75 00 00 00 05|{b.u....
      00> <info> nrf_dfu_validation: Working ECB Key: 
      00> <debug> nrf_dfu_validation:  5F 12 D1 50 98 87 12 E3|_..P....
      00> <debug> nrf_dfu_validation:  18 5B 37 6B AD 0F 33 93|.[7k..3.
      00> <info> nrf_dfu_validation: Working Nonce: 
      00> <debug> nrf_dfu_validation:  FF E5 87 F1 2E 3F 7A 1A|.....?z.
      00> <debug> nrf_dfu_validation:  7B 62 FE 75 00 00 00 06|{b.u....
      00> <info> nrf_dfu_validation: Working ECB Key: 
      00> <debug> nrf_dfu_validation:  5F 12 D1 50 98 87 12 E3|_..P....
      00> <debug> nrf_dfu_validation:  18 5B 37 6B AD 0F 33 93|.[7k..3.
      00> <info> nrf_dfu_validation: Working Nonce: 
      00> <debug> nrf_dfu_validation:  FF E5 87 F1 2E 3F 7A 1A|.....?z.
      00> <debug> nrf_dfu_validation:  7B 62 FE 75 00 00 00 07|{b.u....


      The log shown above for the working nonce/ECB key shows the first calls to nrf_dfu_validation_crypt. I've just noticed that the 4 byte counter in the 4 LSBs of the m_ecb_data.cleartext is starting at 5. Should this be starting at 0?
    • Yes, I saw that post but ibeckermayer's issue seems to be different. My DFU failure seems to be caused by a hash verification failure. My suspicion was that this was caused by the decryption issue.
      00> <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_EXECUTE (data)
      00> <debug> nrf_dfu_req_handler: Whole firmware image received. Postvalidating.
      00> <info> nrf_dfu_validation: in postvalidate()
      00> <debug> nrf_dfu_validation: Hash verification. start address: 0x26000, size: 0x28B9C
      00> <warning> nrf_dfu_validation: Hash verification failed.
      00> <debug> nrf_dfu_validation: Expected FW hash:
      00> <debug> nrf_dfu_validation:  CE 5F D2 8D 99 8B DE 7A|._.....z
      00> <debug> nrf_dfu_validation:  B6 8E 67 CA 32 46 E9 5C|..g.2F.\
      00> <debug> nrf_dfu_validation:  68 87 52 27 48 BF 6A 5A|h.R'H.jZ
      00> <debug> nrf_dfu_validation:  1D F7 1C 8A 18 B9 F9 91|........
      00> <debug> nrf_dfu_validation: Actual FW hash:
      00> <debug> nrf_dfu_validation:  86 64 66 BB 8C 6E C7 46|.df..n.F
      00> <debug> nrf_dfu_validation:  81 38 67 8A C8 E5 CE 23|.8g....#
      00> <debug> nrf_dfu_validation:  95 16 FB D3 9E 98 1E 35|.......5
      00> <debug> nrf_dfu_validation:  9D 93 1B DB 00 55 01 44|.....U.D
      00> <warning> nrf_dfu_ble: DFU request 4 failed with error: 0xB

  • Thanks for the help. I got it working, I was passing the little-endian versions of the ECB KEY and nonce into the openssl encryption command. Once I got that fixed everything worked as expected.

Reply Children
No Data
Related