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

MCUboot ECIES encryption

Hello, I am trying to follow and understand the MCUboot encrypted image design found here: https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/mcuboot/encrypted_images.html#ecies-encryption

Specifically these sections (marked blue below)

The whole key encryption can be summarized as:

  1. Generate a new private key and derive the public key; when using ECIES-P256 this is a secp256r1 keypair, when using ECIES-X25519 this will be a x25519 keypair. Those keys will be our ephemeral keys.
  2. Generate a new secret (DH) using the ephemeral private key and the public key that corresponds to the private key embedded in the HW.
  3. Derive the new keys from the secret using HKDF (built on HMAC-SHA256). We are not using a salt and using an info of MCUBoot_ECIES_v1, generating 48 bytes of key material.
  4. A new random encryption key of 16 bytes is generated (for AES-128). This is the AES key used to encrypt the images.
  5. The key is encrypted with AES-128-CTR and a nonce of 0 using the first 16 bytes of key material generated previously by the HKDF.
  6. The encrypted key now goes through a HMAC-SHA256 using the remaining 32 bytes of key material from the HKDF.

The final TLV is built from the 65 bytes for ECIES-P256 or 32 bytes for ECIES-X25519, which correspond to the ephemeral public key, followed by the 32 bytes of MAC tag and the 16 bytes of the encrypted key, resulting in a TLV of 113 bytes for ECIES-P256 or 80 bytes for ECIES-X25519.

Q1. Is the HKDF derivation something that can be done using openssl kdf like this:

    openssl kdf -keylen 16 -kdfopt mac:HMAC -kdfopt digest:SHA2-256 \
                -kdfopt hexkey:<Diffie-Helman-Shared-Secret> -kdfopt hexinfo:<ASCII-"MCUBoot_ECIES_v1"-in-Hex> \
                SSKDF

Q2. If the final TLV sends 3 things:
1. ephemeral public key
2. 32 bytes of MAC tag (is this the HMAC-SHA256 output from step 6?)
3. the encrypted-key (encrypted in step 5 with AES-128-CTR, nonce of 0 and key of the first 16-bytes of key material from HKDF)

What sequence of steps does the bootloader takes to get back to the image-encryption-key to decrypt the image?
I understand that the ephemeral public key + bootloader private key can generate the Diffie-Helman shared secret.
How does that works with the other two pieces of information to get the image-encryption-key (as generated in step 4 above).

Thank you for everyone's time!
  • Hello, I'm currently looking into your issue. I'm not too familiar with this topic but will investigate/ask internally and try to provide you with an answer today/tomorrow.

    Best regards,

    Simon

  • Thanks for the follow up Simon. I have been reading the material in nrf_crypto sections of the product spec as well and I think it has began to illuminate my understanding. I think the HKDF is performed on both the image-generator and the bootloader receiving the encrypted image and they need to compute to the same 48-byte key material. Will be doing experiments today to confirm.

    Also, If you can help me, also pass along this question internally. In the nrf_crypto section, what does it mean when a specific crypto backend "supports" a specific elliptical curve (e.g. secp256r1) - if I pick for example nrf_oberon as a backend, can it not do operations with `secp256k1`? 

  • I tried to investigate this a little, and I think I got a little smarter. I will share what I've figured out so far:

    • First you need to create a public key (pub_key_dev1) and private key (priv_key_dev1), the private key should be put inside ncs\bootloader\mcuboot\boot\zephyr\keys.c
      • This is the public/private keyset belonging to the device and will not change
      • This private/public keyset will be used to create the shared secret through the Elliptic curve Diffie Hellman method (ECDH). The ECDH method needs another public/private keyset (belonging to the deice/computer generating the DFU image), and this keyset will be regenerated (new unique key-pair) on every new image update.
    • Then, after you've created the public/private keyset (pub_key_dev1 and priv_key_dev1), put the private key in your software, create your product, and maybe a year later you want to do a DFU update, then you do the following:
      • On your computer create the ephemeral (temporary) keyset (public key = pub_key_eph, private key = priv_key_eph)
      • Then create a shared secret using the ephemeral private key (priv_key_eph) and the public key (pub_key_dev1) that correspond to the private key
        stored in ncs\bootloader\mcuboot\boot\zephyr\keys.c.
        • The same shared secret can be calculated later in the device as well using pub_key_eph (resides in the TLV area) and priv_key_dev1 (in keys.c)
      • The key derivation method HKDF can now be used to generate 48 bytes of key material (the salt is the string "MCUBoot_ECIES_v1")
        • The same 48 bytes of key material can also be calculated in the device (MCUboot), since it has the shared secret and uses the same salt "MCUBoot_ECIES_v1"
        • Let's call this key_hkdf
      • Then, on the computer you create a new random AES encryption key of 16 bytes (priv_key_AES)
      • Next, you encrypt the payload of the image (the executable firmware/code) using that key (priv_key_AES). The header and the TLV area is not encrypted as stated here
        • "When encrypting an image, only the payload (FW) is encrypted. The header, TLVs are still sent as plain data."
        • As you can see here, the header is stored
          first followed by the payload, and the TLV area is placed at the end, after the payload
      • Then you encrypt this key (priv_key_AES) using the keys derived from the shared secret (key_hkdf):
        • "The key is encrypted with AES-128-CTR and a nonce of 0 using the first 16 bytes of key material generated previously by the HKDF."
        • "The encrypted key now goes through a HMAC-SHA256 using the remaining 32 bytes of key material from the HKDF."
        • The aes key (priv_key_AES) is now encrypted. Let's call it priv_key_AES_cipher
      • The ephemeral public key (pub_key_eph) and the ciphered AES key (priv_key_AES_cipher) should be placed in the TLV area of the image that should be sent to the device through DFU.
      • When the device receives the image, it can now calculate the shared secret (using priv_key_dev1 and pub_key_eph) and get the key key_hkdf, which can be used to decrypt the priv_key_AES_cipher (in the TLV) to get the AES key (priv_key_AES) to decrypt the image.
        • All this is implemented in MCUboot.
        • Look at the function ncs\bootloader\mcuboot\boot\bootutil\src\loader.c-->boot_image_check()-->boot_enc_load()-->boot_enc_decrypt() which is used to get priv_key_AES
        • Look at the function ncs\bootloader\mcuboot\boot\bootutil\src\image_validate.c-->bootutil_img_validate()-->bootutil_img_hash()-->boot_encrypt(), which is used to decrypt the image payload.

    Be aware that I am still learning this, and this answer may contain errors/misunderstandings. I've not looked into the practicalities of this and how to actually do this, but if you have problems with this, don't hesitate to ask and I will look into it.

    Regarding your question about crypto backend, I will ask internally about this and get back to you.

    Best regards,
    Simon

  • Have you gotten any progress on this? Have you figured out the question about the crypto backend?

  • Hi Simon,

    based on you last message, I was under the impression that you are following up internally on your side on the crypto backend?


    Just so we are on the same page, I am trying to use the design from MCUBoot, but the codebase I am working on is based on nRF-SDK and its libraries.

    On my front I have figured out that the SDK allows configuration of specific curves on the crypto engine and uses curve specific data structures (e.g. nrf_crypto_ecdh_curve25519_shared_secret_t)

    I know that is driven by code and memory space saving.

    I am hoping that this does not mean that it cannot compute ECDH shared secrets generated by other curve parameters. I am curious because the actual API to compute ECDH (nrf_crypto_ecdh_compute) seems to be curve agnostic (as in it does not need to be told the curve parameters itself).

    hopefully the internal team can shed some light into this.

Related