Deriving ECC keys from HUK with TF-M

Hi!

I am currently testing out the TF-M features on the nRF9160 and I want to encrypt some data on the device. So my plan here is to use symmetric encryption with a key derived using ECDH.

To accomplish this I want to only exchange public keys between the device and the receiver. My idea is that I can use the HUK in the device to derive an ECC(secp256r1) key pair on the device and print the pulbic key as well as flashing the other public key onto the device during production.

I have tried to change the hw_unique_key sample to generate an ECC key pair, but with no luck. Do you have any clues for me?

I am currently on NCS 2.1.1 and this is the code I am currently using which gives me a "psa_key_derivation_output_key returned error: -147" which should indicate a hardware error:

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <string.h>
#include <stdio.h>
#include <hw_unique_key.h>
#include <psa/crypto.h>
#include <tfm_crypto_defs.h>

psa_key_id_t derive_asymmetric_key(psa_key_attributes_t *attributes) {
	psa_status_t status;
	psa_key_derivation_operation_t op = PSA_KEY_DERIVATION_OPERATION_INIT;
	psa_key_id_t key_id_out = 0;

	/* Set up a key derivation operation with HUK derivation as the alg */
	status = psa_key_derivation_setup(&op, TFM_CRYPTO_ALG_HUK_DERIVATION);
	if (status != PSA_SUCCESS) {
		printk("psa_key_derivation_setup returned error: %d\n", status);
		return 0;
	}

	/* Create the encryption key from the key derivation operation */
	status = psa_key_derivation_output_key(attributes, &op, &key_id_out);
	if (status != PSA_SUCCESS) {
		printk("psa_key_derivation_output_key returned error: %d\n", status);
		return 0;
	}
	printk("(Key resides internally in TF-M)\n");

	/* Free resources associated with the key derivation operation */
	status = psa_key_derivation_abort(&op);
	if (status != PSA_SUCCESS) {
		printk("psa_key_derivation_abort returned error: %d\n", status);
		return 0;
	}

	return key_id_out;
}

void hex_dump(uint8_t *buff, uint32_t len)
{
	for (int i = 0; i < len; i++) {
		printk("%s%02x", i && ((i % 32) == 0) ? "\n" : "", buff[i]);
	}
	printk("\n");
}

void main(void)
{
	psa_status_t status;
	psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
	psa_key_id_t key_id_out = 0;

	printk("Derive a key, then use it to encrypt a message.\n\n");

	status = psa_crypto_init();
	if (status != PSA_SUCCESS) {
		printk("psa_crypto_init returned error: %d\n", status);
		return;
	}

	printk("Deriving key\n");

	/* Set the key attributes for the storage key */
	psa_set_key_usage_flags(&attributes,
			(PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_VERIFY_DERIVATION));
	psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
	psa_set_key_bits(&attributes, 256);

	key_id_out = derive_asymmetric_key(&attributes);
	if (key_id_out == 0) {
		return;
	}

	printk("Key ID: 0x%x\n\n", key_id_out);

        uint8_t public_key[256];
        size_t public_key_size = 0;
        status = psa_export_public_key(key_id_out, public_key, sizeof(public_key), &public_key_size);
        if (status != PSA_SUCCESS) {
            printk("Could not export public key: %d\n", status);
        }
        printk("Public key:\n");
        hex_dump(public_key, public_key_size);
}

Parents
  • Hello,

    Generating asymmetric keys using huk derivation directly is not currently supported. Ideally you can use the psa_generate_key to create a random private key. In case you necessarily wants the key to be derived from HUK then they can use the psa_key_derivation_output_bytes function and try to do a psa_import_key. The issue with this solution is that there is a chance that psa_import_key will fail since the derived bytes don't necessarily correspond to a private key. So the solution with HUK will have to try multiple calls of the psa_key_derivation_output_bytes until psa_import_key succeeds.

    Also as a note, which is not relevant based on the answer above:


    armmbed.github.io/.../kdf.html


    Ths key_derivation_output_key does as attributes expects the new key attributes. So for example for an ECC key it will expect usage attributes such as: PSA_KEY_USAGE_SIGN_HASH

  • I am not sure if I understand why the derived bytes would not correspond to a private key. Why is this the case?

    To add a little context, this whole operation would be a part of a device provisioning on the assembly line.

    Another possible solution for me could be to generate the ECC key, export the public key, then use the secondary, provisioned public key to generate a symmetric key and store that. Though it would be nice to keep the ECC key as well.

    Perhaps it would be a good idea to store the key in the protected storage provided by TF-M.

    Another, perhaps unrelated question, is protected storage and the key psa_crypto interfaces experimental using NCS? this seems to indicate so, but I am not entirely sure what parts of TF-M that is considered experimental and not.

    And thanks for the note on the key usage, it feels a bit convoluted to figure out which parameters to set as of now.

  • Oskar said:
    I am not sure if I understand why the derived bytes would not correspond to a private key. Why is this the case?

    The derived bytes command just returns you some random bytes. The ECC private keys depending on the curve will have some requirements. Meaning that you need to follow some characteristics, like for example that if you perform a scalar multiplication with the private ECC key and the base point of the curve then the result (public key) need to be a point inside the curve (defined by the algorithm that you choose). The important thing is that a random number may or may not be a private key, that's why we need the psa_import_key to confirm this.

    Oskar said:
    Another, perhaps unrelated question, is protected storage and the key psa_crypto interfaces experimental using NCS? this seems to indicate so, but I am not entirely sure what parts of TF-M that is considered experimental and not.

    Only the minimal profile of TF-M is in production stage. Everything else is experimental, the minimal profile doesn't have the protected storage AFAIK.

Reply
  • Oskar said:
    I am not sure if I understand why the derived bytes would not correspond to a private key. Why is this the case?

    The derived bytes command just returns you some random bytes. The ECC private keys depending on the curve will have some requirements. Meaning that you need to follow some characteristics, like for example that if you perform a scalar multiplication with the private ECC key and the base point of the curve then the result (public key) need to be a point inside the curve (defined by the algorithm that you choose). The important thing is that a random number may or may not be a private key, that's why we need the psa_import_key to confirm this.

    Oskar said:
    Another, perhaps unrelated question, is protected storage and the key psa_crypto interfaces experimental using NCS? this seems to indicate so, but I am not entirely sure what parts of TF-M that is considered experimental and not.

    Only the minimal profile of TF-M is in production stage. Everything else is experimental, the minimal profile doesn't have the protected storage AFAIK.

Children
Related