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?

  • Hello,

    Thanks for creating this ticket. Is this a fully customized bootloader, or can I assume it’s mostly the same as the one from the nRF5 SDK with the addition of external flash support? Also, was the init command with the signature created using nrfutil (the *.dat file)? If so, it should be sufficient to store the entire contents of that file in the .init_command member of the settings struct and let the bootloader handle the decoding and retrieval of the signature key using the stored_init_cmd_decode() function.

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

    Our bootloader expects little endian.

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

    Please see this post  ECDSA with nrfutil generated key  

    Best regards,

    Vidar

  • Okay, first thanks for answering :)


    Here is how i understand the flow should be in my case:
    I use these commands to generate: key, signature and hash:

    - nrfutil keys generate private.pem (private key)

    - nrfutil keys display --key pk --format code private.pem --out_file public_key.c (public key in BE)

    - nrfutil pkg generate --application slow_71.hex --application-version 1 --hw-version 52 --sd-req 0x00 --key-file private.pem app_signed_dfu.zip

    - nrfutil pkg display app_signed_dfu.zip (hash and signature both in LE)



    In the bootloader this is the flow:


    Calculate new hash on the firmware using:

    nrf_crypto_hash_init, 
    nrf_crypto_hash_update,
    nrf_crypto_hash_finalize,

    Where i expect the output hash to be in BE, so in need to swap endian (nrf_crypto_internal_swap_endian) to match it with the saved meta data hash. This succeds.


    Then handling the key that i generated:
    First i swap it with nrf_crypto_internal_double_swap_endian, then i use nrf_crypto_ecc_public_key_from_raw just like the crypto_init in the other post you referenced to.


    Then to verifying the signature:

    I use the swapped hash (BE), i swap the signature with nrf_crypto_internal_double_swap_endian so its also BE and i take the &pub_key i got from nrf_crypto_ecc_public_key_from_raw.

    This is inserted into:
    nrf_crypto_ecdsa_verify(&verify_ctx, &pub_key, hash_be, HASH_LEN, signature_be, SIGNATURE_LEN);

    And that fails. Feel like i have tried every combination of LE and BE of each of the 3 (hash, signature and key)
  • It sounds like you are using the hash digest of the new FW image? Please note that the signature included in the init command (content of the *.data file created by nrfutil, see init packet) is for the init command itself. This means you need to calculate the hash digest of the init packet and not the FW to validate the signature. You can see how this is done by the nrf_dfu_validation_signature_check() function in the SDK bootloader.

    (Ref. https://docs.nordicsemi.com/bundle/sdk_nrf5_v17.1.0/page/lib_secure_boot.html

  • Were you able to make any progress on this after validating the signature of the init command instead of the FW image itself?

  • I have been working on building and flashing the bootloader with a newer development setup in VScode we have, and are not fully there yet. My plan was to go back to using more of the NRF bootloader example and then adding on the functionality that i need. From what i understand i should be able to save the content of the nrf zip package in the xflash and then handle that in the bootloader

Related