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?

Parents
  • 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)
Reply
  • 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)
Children
Related