nrf_crypto_ecdsa_verify fails when verifying signature in custom bootloader

Hi,

I am implementing a custom bootloader on an nRF52840. The firmware update is downloaded while the main application is running. The flow is as follows:

  1. The application receives update information over MQTT (version, firmware size, hash, signature, and URL).

  2. The application downloads the new firmware from the given URL into external flash.

  3. The metadata (hash + signature + version info) is stored in the bootloader settings area.

  4. The device is reset.

  5. On startup, the bootloader checks if new firmware exists in the external flash region.

  6. The bootloader calculates the hash of the firmware and compares it with the stored hash.

  7. If the hash matches, the bootloader attempts to verify the signature using nrf_crypto_ecdsa_verify().

Issue:

The call to nrf_crypto_ecdsa_verify() always fails. I am using curve SECP256R1.
I have tried:

  • Swapping public key and signature endianness (both LE and BE).

  • Splitting the signature and key into two 32-byte components and swapping individually.

  • Confirming the hash is correct. (Comparing the saved and calculated hash succeeds)

To validate the correctness of hash, signature, and public key, I wrote a Python script using cryptography.hazmat.primitives.asymmetric.ec with SECP256R1, and there the verification succeeds. So the signature and data are correct.

Questions:

  1. Does nrf_crypto_ecdsa_verify expect the hash, signature, and key in raw big-endian or little-endian format?

  2. For the public key, should it be passed as a concatenated buffer [X || Y] or in some other internal representation?

  3. For the signature, should it be provided as raw 64 bytes (r || s), or DER encoded?

  • Below is the flash layout we are working with:

    Internal flash:

    +------------------------+ 0x100000
    | DFU settings             |
    +------------------------+ 
    | Bootloader                |
    +------------------------+
    | Bank1 (Factory FW) | <-- always valid fallback
    +------------------------+ 
    | Bank0 (Active FW)   | <-- app normally runs from here
    +------------------------+
    | MBR (4 KB)              |
    +------------------------+ 0x000000


    External flash:
    New update package (.dat + .bin)

    The update process always writes the new firmware to Bank 0, while Bank 1 contains the factory firmware and is used as a recovery fallback if the updated application fails to start correctly.


    Our current challenge is the security of the firmware while it resides in external flash.
    There is a window of time—after the application downloads the update into external flash and before the bootloader copies it into internal flash—where power may be lost, or the external flash could potentially be accessed by an attacker. During that period the firmware remains unprotected and could be read out or copied.

    We need to address how to secure the update package during this staging phase in external flash.



  • Thanks for the clarification. This approach you outlined seems fine to me. 

    Our current challenge is the security of the firmware while it resides in external flash.
    There is a window of time—after the application downloads the update into external flash and before the bootloader copies it into internal flash—where power may be lost, or the external flash could potentially be accessed by an attacker. During that period the firmware remains unprotected and could be read out or copied.

    Can you expand on this a bit? Specifically what prevents a potential attacker from accessing the FW residing in external only during the update window? Is the SPI flash locked before and after DFU? Is the data encrypted?

Related