Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[nrfconnect] Add platform crypto for KMU usage #539

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

ArekBalysNordic
Copy link
Contributor

@ArekBalysNordic ArekBalysNordic commented Jan 24, 2025

Let's review the code here first, before pushing it to the upstream repo.

Changes:

  • Allowed setting a custom SessionKeystore in the Matter server.
  • Implemented PSAKeyAllocator
    • If the custom implementation is not provided, the default one is used. The default implementation works in the same way as it was previously done.
    • It allows the injection of the custom implementation, which is crucial for KMU.
    • It allows us to overwrite the key attributes so we can modify them.
  • Used the new PSAKeyAllocator in the PSAOperationalKeystore and PSASessionKeystore.
  • Implemented KMUKeyAllocator to prepare all keys to be written in the proper slots.
  • Overwritten the PersistICDKey method of the PSASessionKeystore class in the KMUSessionKeystore, because currently it is forbidden to use psa_copy_key to copy key from RAM to KMU slot.

src/crypto/crypto.gni Outdated Show resolved Hide resolved
config/nrfconnect/chip-module/Kconfig.features Outdated Show resolved Hide resolved
src/platform/nrfconnect/crypto/KMUSessionKeystore.cpp Outdated Show resolved Hide resolved
@Damian-Nordic
Copy link
Contributor

Damian-Nordic commented Jan 29, 2025

The solution is nice but I wonder if we can avoid this code duplication even more and also make it easier for customers to make a decision what to use the KMU slots for.

I have an alternative to propose but I'll let you decide which way to go :).

  1. Add PSA key allocator interface:
    // PSAKeyAllocator.h
    class PSAKeyAllocator
    {
      virtual psa_key_id_t GetDacKeyId() = 0;
      virtual psa_key_id_t GetOpKeyId(FabricIndex) = 0;
      virtual psa_key_id_t AllocIcdKeyId() = 0;
      virtual psa_key_lifetime_t GetKeyLifetime(psa_key_id_t) = 0;
    };
    
  2. Add GN switch chip_crypto_psa_key_allocator. Setting this switch would:
    • enable global SetPSAKeyAllocator and GetPSAKeyAllocator functions for setting a custom key allocator
    • modify existing PSAOperationalKeystore and PSASessionKeystore to use the custom key allocator instead of the default behavior.
  3. For KMU support, implement this interface and set it before initializing CHIP.

The benefit is that if customers want to slightly modify the key allocation scheme (because e.g. they have their own keys to store in KMU and they don't care if ICD keys are in KMU or ITS), they don't need to make another copy of the key stores.

Allow setting session keystore by platform. If any keystore is not
set, assign the default one.

Signed-off-by: Arkadiusz Balys <[email protected]>
@ArekBalysNordic
Copy link
Contributor Author

@Damian-Nordic I've introduced the new PSAKeyAllocator as you suggested. Thanks to that there is a reduced number of overwrites in our platform. Please take a look at that. Do you think we can go with something like that?

Copy link
Contributor

@Damian-Nordic Damian-Nordic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great. Some mostly minor suggestions from me.

config/nrfconnect/chip-module/Kconfig.features Outdated Show resolved Hide resolved
config/nrfconnect/chip-module/Kconfig.features Outdated Show resolved Hide resolved
src/crypto/PSAKeyAllocator.h Outdated Show resolved Hide resolved
src/crypto/PSAKeyAllocator.h Outdated Show resolved Hide resolved
src/crypto/PSAKeyAllocator.h Outdated Show resolved Hide resolved
#include <crypto/PSAKeyAllocator.h>
// Define the number of slots per NOC and ICD key.
#define KMU_MATTER_SLOTS_PER_NOC_KEY 2
#define KMU_MATTER_SLOTS_PER_ICD_KEY 2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically it's more like ICD slots per fabric than #slots per ICD key but that's fine :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right, but it is consistent with NOC :) I will add a comment there.

src/platform/nrfconnect/KMUKeyAllocator.h Outdated Show resolved Hide resolved
src/platform/nrfconnect/KMUKeyAllocator.h Outdated Show resolved Hide resolved
if (psa_get_key_algorithm(attrs) == PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_CCM, 8))
{
psa_set_key_algorithm(attrs, PSA_ALG_CCM);
psa_set_key_usage_flags(attrs, psa_get_key_usage_flags(attrs) | PSA_KEY_USAGE_EXPORT);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The export flag is only needed when creating volatile keys. Is export supported at all when using KMU? Or perhaps, it's not even checked? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if it is supported in KMU, indeed we should disable it while storing it there :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a change to the KMUSessionKeystore. Now the attributes for the new KMU keys don't contain these flags.

#define KMU_MATTER_SLOTS_PER_NOC_KEY 2
#define KMU_MATTER_SLOTS_PER_ICD_KEY 2
// Define the maximum number of fabrics supported by the KMU.
#if CONFIG_CHIP_ICD_CHECK_IN_SUPPORT
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if we want to base this scheme on the current configuration. If someone upgrades from non-ICD to ICD device, then the KMU slots will change their meaning. But I'm not sure if this scenario needs to be supported.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll discuss it with the team shortly if we even plan such a case.

Copy link
Contributor Author

@ArekBalysNordic ArekBalysNordic Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kkasperczyk-no @LuDuda what do you think? Should we always reserve slots for ICD to be ready for such a conversion?

Copy link
Collaborator

@LuDuda LuDuda Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should somehow allow customer for upgradability (which of course is harder with KMU), even if customer will need to modify manually KMUKeyAllocator implementation to allocate keys from outside of the Matter range . We need to describe it in the documentation thoroughly. If i correctly reading the code, someone would be able to modify the code for it's own purpose and manage key_ids as he wants, right? Even for example to store ICD keys to ITS instead of KMUs (with known side-effects)?

The same issue is also with increasing number of supported Fabrics. So we need to make it clear in documentation later on, that changing these parameters have impact on KMU slot allocation. Wonder.. if we can somehow prevent issues which can be hard to catch during testing.

Not sure if we should always blindly reserve slots which will not be used.. Maybe we need F2F discussion. Is it possible to "move" keys from slot A to slot B?

Copy link
Contributor

@Damian-Nordic Damian-Nordic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes to reduce the duplicated code! Approving assuming you will be discussing the non-ICD to ICD upgrade in the team.

Copy link
Collaborator

@LuDuda LuDuda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very clean! Great work 👍

Few nits and questions below.

config/nrfconnect/chip-module/Kconfig Outdated Show resolved Hide resolved
config/nrfconnect/chip-module/Kconfig Outdated Show resolved Hide resolved
config/nrfconnect/chip-module/Kconfig Outdated Show resolved Hide resolved
config/nrfconnect/chip-module/Kconfig Outdated Show resolved Hide resolved
config/nrfconnect/chip-module/Kconfig Outdated Show resolved Hide resolved
src/crypto/PSAKeyAllocator.h Show resolved Hide resolved
* @brief Allocate a new Intermittently Connected Devices (ICD) key ID.
*
* ICD uses two keys - AES CCM and HMAC(SHA 256).
* Each key must be allocated separately.
Copy link
Collaborator

@LuDuda LuDuda Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what is the key_id_t returned here? We are making assumption that HMAC(SHA256) is always returned value + 1? Maybe, we should return some structure here actually?

Just a thought from scalability perspective @ArekBalysNordic @Damian-Nordic ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no such requirement. We save the key_id in zephyr settings, so no matter which slot it gets.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's discuss it F2F - not sure I understand how it works then if we want to make operation HMAC(SHA256). On which Key ID we do this? Even if it's stored in settings, how we get it's number?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During the ICD registration, we create two keys - HMAC(SHA256) and AES CCM. Originally, we put them into RAM and save their key IDs to Zephyr settings. If we need to use them, we get the key id from the specific Zephyr settings entry (used for ICD purposes) and provide it to PSA API. Recently we added moving these keys to PSA ITS (Now to KMU). Now during the ICD registration, we call an additional function "PersistICDKey" on each key and thanks to that we move the keys from RAM to ITS/KMU and we update the key ID saved in settings. So, to sum up, in settings we have something like a look-up table and key IDs assigned to specific ICD settings entries. Therefore, we don't need to take care of a specific slot for those keys, because we already have this information saved in zephyr settings. :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part i get, but i don't understand what is the Key ID for AES CCM and what is the Key ID for HMAC(SHA256)? Looking at the code, I assume the AllocateICDKeyId returns Key ID for AES CCM slot only - so what happens with HMAC(SHA256)? Or this is not needed to be persist?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AllocateICDKeyId just allocates a key ID from a given range. The caller then uses the key ID to either create AES or HMAC key. It then stores in setting that for a given ICD entry, AES key ID is, say, 0x27 and HMAC key ID is, say, 0x32.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fortunately, both AES and HMAC costs 1 KMU slot, so we can do it in the same way as for ITS. During the registration both keys are created one by one and uses the same allocate method. So Aes is created, allocate metod returns the first available key id within the given range, then HMAC is created and allocate method returns the next available key id in the same range. The difference is during the key creation. We use different attributes for each key, and then we don't need do check the key type during the allocation, just allocate the following ids.

Copy link
Collaborator

@LuDuda LuDuda Jan 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right.. Somehow i thought that during allocation of the slot, we also do need to know/set a proper key_type, but it's more about just get an ID from a range. I think it works then fine.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still wound't be more scalable to return struct with two key_ids already? As you mentioned it just happened that they both use 1 slot of KMU, what if this change for any reason in future? Else, i would expand the documentation to make it a bit more clear, maybe:

 * This method is used to allocate both AES-CCM and HMAC (SHA-256) keys independently.
 * The caller is responsible for storing the key ID in non-volatile memory
 * and setting the appropriate key type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will change the documentation then, and we can consider changing it to struct as a follow-up because it will require some changes in the Matter stack.

src/platform/nrfconnect/KMUKeyAllocator.h Show resolved Hide resolved
#define KMU_MATTER_SLOTS_PER_NOC_KEY 2
#define KMU_MATTER_SLOTS_PER_ICD_KEY 2
// Define the maximum number of fabrics supported by the KMU.
#if CONFIG_CHIP_ICD_CHECK_IN_SUPPORT
Copy link
Collaborator

@LuDuda LuDuda Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should somehow allow customer for upgradability (which of course is harder with KMU), even if customer will need to modify manually KMUKeyAllocator implementation to allocate keys from outside of the Matter range . We need to describe it in the documentation thoroughly. If i correctly reading the code, someone would be able to modify the code for it's own purpose and manage key_ids as he wants, right? Even for example to store ICD keys to ITS instead of KMUs (with known side-effects)?

The same issue is also with increasing number of supported Fabrics. So we need to make it clear in documentation later on, that changing these parameters have impact on KMU slot allocation. Wonder.. if we can somehow prevent issues which can be hard to catch during testing.

Not sure if we should always blindly reserve slots which will not be used.. Maybe we need F2F discussion. Is it possible to "move" keys from slot A to slot B?

src/platform/nrfconnect/KMUKeyAllocator.h Outdated Show resolved Hide resolved
Copy link
Collaborator

@LuDuda LuDuda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good for me too. Let's discuss F2F the approach for upgradability.

- Moved the PSA key definitions from CHIPCryptoPALPSA.h file to
the newly created PSAKeyAllocator.

- The new PSAKeyAllocator class allows for the allocation of keys
in secure storage. Users can create their own PSAKeyAllocator
implementation and set it to be used by the Matter stack.

- If the custom implementation is not provided the default one is
used and it works as the legacy solution and the mechanism is
about stored keys in the PSA ITS storage.

Signed-off-by: Arkadiusz Balys <[email protected]>
KMUKeyAllocator is used to overwrite PSAKeyAllocator for the
specific use-cases of KMU driver.

This feature is available only on the device which run Cracen.

Signed-off-by: Arkadiusz Balys <[email protected]>
We need to overwrite the PersistICDKey method of
the PSASessionKeystore because we must export the volatile key first
and then import it again into the KMU slot instead of copying it.

Currently copying keys from the RAM location to the KMU slot is not
supported.

Signed-off-by: Arkadiusz Balys <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants