diff --git a/config/nrfconnect/chip-module/CMakeLists.txt b/config/nrfconnect/chip-module/CMakeLists.txt index a55e2a2d6f..db1a9b4240 100644 --- a/config/nrfconnect/chip-module/CMakeLists.txt +++ b/config/nrfconnect/chip-module/CMakeLists.txt @@ -192,6 +192,7 @@ endif() if (CONFIG_CHIP_CRYPTO_PSA) matter_add_gn_arg_string("chip_crypto" "psa") matter_add_gn_arg_bool ("chip_crypto_psa_spake2p" CONFIG_PSA_WANT_ALG_SPAKE2P_MATTER) + matter_add_gn_arg_bool ("chip_crypto_kmu" CONFIG_CHIP_CRYPTO_USE_KMU) endif() if (BOARD STREQUAL "native_posix") diff --git a/config/nrfconnect/chip-module/Kconfig.features b/config/nrfconnect/chip-module/Kconfig.features index 350e67caaa..90dedf309f 100644 --- a/config/nrfconnect/chip-module/Kconfig.features +++ b/config/nrfconnect/chip-module/Kconfig.features @@ -289,4 +289,13 @@ config CHIP_LAST_FABRIC_REMOVED_ACTION_DELAY an action chosen by the CHIP_LAST_FABRIC_REMOVED_ACTION option. This schedule will allow for avoiding race conditions before the device removes non-volatile data. +config CHIP_CRYPTO_USE_KMU + bool "Use CRACEN KMU driver for storing security materials" + depends on PSA_NEED_CRACEN_KMU_DRIVER + depends on CHIP_CRYPTO_PSA + help + Store security materials in the CRACEN KMU space instead of PSA ITS. + KMU slots 100-180 are dedicated for Matter purposes. + The solution is currently limited to maximum 5 Matter fabrics. + endif # CHIP diff --git a/src/crypto/BUILD.gn b/src/crypto/BUILD.gn index 6f82c93c19..0f8cc520be 100644 --- a/src/crypto/BUILD.gn +++ b/src/crypto/BUILD.gn @@ -55,6 +55,7 @@ buildconfig_header("crypto_buildconfig") { "CHIP_CRYPTO_OPENSSL=${chip_crypto_openssl}", "CHIP_CRYPTO_BORINGSSL=${chip_crypto_boringssl}", "CHIP_CRYPTO_PLATFORM=${chip_crypto_platform}", + "CHIP_CRYPTO_KMU=${chip_crypto_kmu}", ] } @@ -124,6 +125,13 @@ if (chip_crypto == "openssl") { "CHIPCryptoPALmbedTLS.h", "CHIPCryptoPALmbedTLSCert.cpp", ] + + if (chip_crypto_kmu) { + sources += [ + "KMUKeystoreAdaptation.h" + ] + } + public_deps = [ ":public_headers" ] if (!chip_external_mbedtls) { diff --git a/src/crypto/CHIPCryptoPALPSA.cpp b/src/crypto/CHIPCryptoPALPSA.cpp index 604125e7ef..ef997f8cf7 100644 --- a/src/crypto/CHIPCryptoPALPSA.cpp +++ b/src/crypto/CHIPCryptoPALPSA.cpp @@ -22,6 +22,9 @@ #include "CHIPCryptoPALPSA.h" #include "CHIPCryptoPALmbedTLS.h" +#if CHIP_CRYPTO_KMU +#include "KMUKeystoreAdaptation.h" +#endif #include #include @@ -272,6 +275,10 @@ CHIP_ERROR FindFreeKeySlotInRange(psa_key_id_t & keyId, psa_key_id_t start, uint for (keyId = start; keyId < end; keyId++) { +#if CHIP_CRYPTO_KMU + CHIP_ERROR error = KMU::GetSlot(&keyId, &attributes); + VerifyOrReturnError(error == CHIP_NO_ERROR, error); +#endif psa_status_t status = psa_get_key_attributes(keyId, &attributes); if (status == PSA_ERROR_INVALID_HANDLE) { diff --git a/src/crypto/KMUKeystoreAdaptation.h b/src/crypto/KMUKeystoreAdaptation.h new file mode 100644 index 0000000000..62c860c123 --- /dev/null +++ b/src/crypto/KMUKeystoreAdaptation.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "CHIPCryptoPALPSA.h" + +#include +#include + +/* KMU Slots for Matter purpose: + * + * DAC private key 176-180 (1 key) + * NOC private keys (Operational) 155-175 (5 fabrics max) + * ICD keys 113-153 (5 fabrics max) + * + * DAC private key needs 4 KMU slots (Encrypted) + * NOC private key needs 4 KMU slots (Encrypted) + * ICD key needs 3 KMU slots (Not encrypted) + */ +#define KMU_ADAPTATION_USE_ENCRYPTION 0 + +namespace chip { +namespace Crypto { +namespace KMU { + +inline constexpr static uint8_t NOC_Offset = 155; +inline constexpr static uint8_t ICD_Offset = 113; +inline constexpr static uint8_t NOC_KeyMax = 5; +inline constexpr static uint8_t ICD_KeyMax = 5; +inline constexpr static uint8_t NOC_SingleKeySlots = 2; +inline constexpr static uint8_t ICD_SingleKeySlots = 1; +inline constexpr static uint8_t EncryptionOverhead = 2; + +inline CHIP_ERROR GetSlot(psa_key_id_t * keyID, psa_key_attributes_t * attributes) +{ + if (!keyID) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + if (static_cast(*keyID) >= static_cast(KeyIdBase::Operational) && + static_cast(*keyID) < static_cast(KeyIdBase::DACPrivKey)) + { + if (static_cast(*keyID) > static_cast(NOC_KeyMax)) + { + return CHIP_ERROR_PERSISTED_STORAGE_FAILED; + } + + psa_key_id_t newId = NOC_Offset + ((NOC_SingleKeySlots + EncryptionOverhead) * (*keyID - 1)); + *keyID = static_cast(PSA_KEY_HANDLE_FROM_CRACEN_KMU_SLOT(CRACEN_KMU_KEY_USAGE_SCHEME_ENCRYPTED, newId)); + if (attributes) + { + psa_set_key_lifetime( + attributes, + PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_PERSISTENCE_DEFAULT, PSA_KEY_LOCATION_CRACEN_KMU)); + } + + return CHIP_NO_ERROR; + } + else if (static_cast(*keyID) >= static_cast(KeyIdBase::ICDKeyRangeStart) && + static_cast(*keyID) < static_cast(KeyIdBase::Maximum)) + { + if (static_cast(*keyID) > static_cast(NOC_KeyMax)) + { + return CHIP_ERROR_PERSISTED_STORAGE_FAILED; + } + + psa_key_id_t newId = ICD_Offset + (ICD_SingleKeySlots * (*keyID - 1)); + *keyID = static_cast(PSA_KEY_HANDLE_FROM_CRACEN_KMU_SLOT(CRACEN_KMU_KEY_USAGE_SCHEME_RAW, newId)); + + if (attributes) + { + // Cracen KMU supports only PSA_ALG_CCM algorithm, so convert it. + if (psa_get_key_algorithm(attributes) == PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_CCM, 8)) + { + psa_set_key_algorithm(attributes, PSA_ALG_CCM); + } + + psa_set_key_lifetime( + attributes, + PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_PERSISTENCE_DEFAULT, PSA_KEY_LOCATION_CRACEN_KMU)); + } + return CHIP_NO_ERROR; + } + + return CHIP_ERROR_INVALID_ARGUMENT; +} +} // namespace KMU +} // namespace Crypto +} // namespace chip diff --git a/src/crypto/PSAOperationalKeystore.cpp b/src/crypto/PSAOperationalKeystore.cpp index 09e00bc9b5..6416335957 100644 --- a/src/crypto/PSAOperationalKeystore.cpp +++ b/src/crypto/PSAOperationalKeystore.cpp @@ -22,13 +22,27 @@ #include +#if CHIP_CRYPTO_KMU +#include "KMUKeystoreAdaptation.h" +#endif + namespace chip { namespace Crypto { PSAOperationalKeystore::PersistentP256Keypair::PersistentP256Keypair(FabricIndex fabricIndex) { ToPsaContext(mKeypair).key_id = MakeOperationalKeyId(fabricIndex); - mInitialized = true; + +#if CHIP_CRYPTO_KMU + if (CHIP_NO_ERROR != KMU::GetSlot(&ToPsaContext(mKeypair).key_id, nullptr)) + { + ToPsaContext(mKeypair).key_id = 0; + mInitialized = false; + return; + } +#endif + + mInitialized = true; } PSAOperationalKeystore::PersistentP256Keypair::~PersistentP256Keypair() @@ -66,9 +80,15 @@ CHIP_ERROR PSAOperationalKeystore::PersistentP256Keypair::Generate() // Type based on ECC with the elliptic curve SECP256r1 -> PSA_ECC_FAMILY_SECP_R1 psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_bits(&attributes, kP256_PrivateKey_Length * 8); - psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE); +#if CHIP_CRYPTO_KMU + psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)); + psa_set_key_lifetime(&attributes, + PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_PERSISTENCE_DEFAULT, PSA_KEY_LOCATION_CRACEN_KMU)); +#else + psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT); +#endif psa_set_key_id(&attributes, GetKeyId()); status = psa_generate_key(&attributes, &keyId); @@ -149,9 +169,15 @@ CHIP_ERROR PSAOperationalKeystore::PersistentP256Keypair::Deserialize(P256Serial // Type based on ECC with the elliptic curve SECP256r1 -> PSA_ECC_FAMILY_SECP_R1 psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_bits(&attributes, kP256_PrivateKey_Length * 8); - psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE); +#if CHIP_CRYPTO_KMU + psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)); + psa_set_key_lifetime(&attributes, + PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_PERSISTENCE_DEFAULT, PSA_KEY_LOCATION_CRACEN_KMU)); +#else + psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT); +#endif psa_set_key_id(&attributes, GetKeyId()); status = psa_import_key(&attributes, input.ConstBytes() + mPublicKey.Length(), kP256_PrivateKey_Length, &keyId); diff --git a/src/crypto/PSASessionKeystore.cpp b/src/crypto/PSASessionKeystore.cpp index ece38e74ff..545b320efb 100644 --- a/src/crypto/PSASessionKeystore.cpp +++ b/src/crypto/PSASessionKeystore.cpp @@ -202,8 +202,10 @@ CHIP_ERROR PSASessionKeystore::PersistICDKey(Symmetric128BitsKeyHandle & key) return CHIP_NO_ERROR; } - SuccessOrExit(err = Crypto::FindFreeKeySlotInRange(newKeyId, to_underlying(KeyIdBase::ICDKeyRangeStart), kMaxICDClientKeys)); - psa_set_key_lifetime(&attrs, PSA_KEY_LIFETIME_PERSISTENT); +SuccessOrExit(err = Crypto::FindFreeKeySlotInRange(newKeyId, to_underlying(KeyIdBase::ICDKeyRangeStart), kMaxICDClientKeys)); +#if !CHIP_CRYPTO_KMU + psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT); +#endif psa_set_key_id(&attrs, newKeyId); VerifyOrExit(psa_copy_key(key.As(), &attrs, &newKeyId) == PSA_SUCCESS, err = CHIP_ERROR_INTERNAL); diff --git a/src/crypto/crypto.gni b/src/crypto/crypto.gni index 96f506033b..1cf440fc53 100644 --- a/src/crypto/crypto.gni +++ b/src/crypto/crypto.gni @@ -22,6 +22,9 @@ declare_args() { # Use PSA Spake2+ implementation. Only used if chip_crypto == "psa" chip_crypto_psa_spake2p = false + + # Provide KMU support for nRF54L15 devices. Only used if chip_crypto == "psa" + chip_crypto_kmu = false } assert(