Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a5fad88

Browse files
committedJan 24, 2025·
[nrfconnect] Add platform crypto for KMU usage
Added a platform crypto implementation to store crypto materials in KMU(Key management unit) for devices that use it.
1 parent dfecf34 commit a5fad88

File tree

8 files changed

+691
-5
lines changed

8 files changed

+691
-5
lines changed
 

‎config/nrfconnect/chip-module/CMakeLists.txt

+7-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,13 @@ else()
190190
endif()
191191

192192
if (CONFIG_CHIP_CRYPTO_PSA)
193-
matter_add_gn_arg_string("chip_crypto" "psa")
193+
if (CONFIG_CHIP_KMU_SUPPORT)
194+
matter_add_gn_arg_string ("chip_crypto" "platform")
195+
matter_add_gn_arg_bool ("chip_external_mbedtls" FALSE)
196+
matter_add_gn_arg_bool ("chip_use_cracen_kmu" TRUE)
197+
else()
198+
matter_add_gn_arg_string("chip_crypto" "psa")
199+
endif()
194200
matter_add_gn_arg_bool ("chip_crypto_psa_spake2p" CONFIG_PSA_WANT_ALG_SPAKE2P_MATTER)
195201
endif()
196202

‎config/nrfconnect/chip-module/Kconfig.features

+39
Original file line numberDiff line numberDiff line change
@@ -289,4 +289,43 @@ config CHIP_LAST_FABRIC_REMOVED_ACTION_DELAY
289289
an action chosen by the CHIP_LAST_FABRIC_REMOVED_ACTION option. This schedule will allow for
290290
avoiding race conditions before the device removes non-volatile data.
291291

292+
config CHIP_KMU_SUPPORT
293+
bool "Use KMU to store crypto keys"
294+
default y
295+
depends on CRACEN_LIB_KMU
296+
help
297+
Uses KMU to store keys if it is available on the device. This option changes the Matter crypto
298+
implementation to "platform" and uses custom crypto implementation of Session Keystore and
299+
Operational Keystore.
300+
301+
if CHIP_KMU_SUPPORT
302+
303+
config CHIP_CORE_KMU_SLOT_START
304+
int "Initial KMU slot number for Matter keys"
305+
default 100
306+
help
307+
Starting slot number dedicated for Matter purposes.
308+
This config does not include DAC private key.
309+
310+
config CHIP_CORE_KMU_SLOT_END
311+
int
312+
default 175 if CHIP_CRYPTO_PSA_DAC_PRIV_KEY_KMU
313+
default 180
314+
help
315+
The last available KMU slot for Matter purposes.
316+
This slot does not include DAC private key.
317+
318+
config CHIP_KMU_MAX_FABRICS
319+
int
320+
default 18 if (CHIP_ENABLE_ICD_SUPPORT && CHIP_CRYPTO_PSA_DAC_PRIV_KEY_KMU)
321+
default 37 if CHIP_CRYPTO_PSA_DAC_PRIV_KEY_KMU
322+
default 20 if CHIP_ENABLE_ICD_SUPPORT
323+
default 40
324+
help
325+
Maximum possible Matter fabrics to store NOC keys in the Key Management Unit.
326+
The slots number is limited, so there is the limited NOC keys possible to store as well.
327+
Currently, each NOC key uses 2 KMU slots.
328+
329+
endif
330+
292331
endif # CHIP

‎src/crypto/PSASpake2p.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717

1818
#include "PSASpake2p.h"
1919

20-
#include "CHIPCryptoPALPSA.h"
21-
2220
#include <lib/support/CodeUtils.h>
2321

2422
#include <psa/crypto.h>

‎src/platform/nrfconnect/BUILD.gn

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2021 Project CHIP Authors
1+
# Copyright (c) 2021-2025 Project CHIP Authors
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -14,6 +14,7 @@
1414

1515
import("//build_overrides/chip.gni")
1616

17+
import("${chip_root}/src/crypto/crypto.gni")
1718
import("${chip_root}/src/platform/device.gni")
1819
import("${chip_root}/src/platform/nrfconnect/args.gni")
1920

@@ -132,6 +133,21 @@ static_library("nrfconnect") {
132133
sources += [ "../Zephyr/SysHeapMalloc.cpp" ]
133134
}
134135

136+
if (chip_use_cracen_kmu) {
137+
# Use platform crypto implementation for KMU support
138+
sources += [
139+
"crypto/KMUOperationalKeystore.cpp",
140+
"crypto/KMUSessionKeystore.cpp",
141+
142+
# Sources from common crypto core
143+
"${chip_root}/src/crypto/CHIPCryptoPALPSA.cpp",
144+
"${chip_root}/src/crypto/CHIPCryptoPALPSA.h",
145+
"${chip_root}/src/crypto/PSAOperationalKeystore.h",
146+
"${chip_root}/src/crypto/PSASpake2p.cpp",
147+
"${chip_root}/src/crypto/PSASpake2p.h",
148+
]
149+
}
150+
135151
cflags = [ "-Wconversion" ]
136152
}
137153

‎src/platform/nrfconnect/args.gni

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2020 Project CHIP Authors
1+
# Copyright (c) 2020-2025 Project CHIP Authors
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -17,4 +17,8 @@ declare_args() {
1717

1818
# Enable factory data support
1919
chip_enable_factory_data = false
20+
21+
# Use KMU to store cryptographic materials.
22+
# This requires a dedicated platform-specific crypto implementation.
23+
chip_use_cracen_kmu = false
2024
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
/*
2+
* Copyright (c) 2025 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#include "KMUSlotDefinitions.h"
19+
#include "crypto/PSAOperationalKeystore.h"
20+
#include "crypto/PersistentStorageOperationalKeystore.h"
21+
22+
#include <lib/support/CHIPMem.h>
23+
#include <lib/support/logging/CHIPLogging.h>
24+
25+
#include <cracen_psa_kmu.h>
26+
#include <psa/crypto.h>
27+
28+
namespace chip {
29+
namespace Crypto {
30+
31+
PSAOperationalKeystore::PersistentP256Keypair::PersistentP256Keypair(FabricIndex fabricIndex)
32+
{
33+
ToPsaContext(mKeypair).key_id = static_cast<psa_key_id_t>(KMU_MATTER_NOC_SLOT_START + ((fabricIndex - 1) * 2));
34+
35+
mInitialized = true;
36+
}
37+
38+
PSAOperationalKeystore::PersistentP256Keypair::~PersistentP256Keypair()
39+
{
40+
// This class requires explicit control of the key lifetime. Therefore, clear the key ID
41+
// to prevent it from being destroyed by the base class destructor.
42+
ToPsaContext(mKeypair).key_id = 0;
43+
}
44+
45+
inline psa_key_id_t PSAOperationalKeystore::PersistentP256Keypair::GetKeyId() const
46+
{
47+
return ToConstPsaContext(mKeypair).key_id;
48+
}
49+
50+
bool PSAOperationalKeystore::PersistentP256Keypair::Exists() const
51+
{
52+
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
53+
psa_status_t status = psa_get_key_attributes(GetKeyId(), &attributes);
54+
55+
psa_reset_key_attributes(&attributes);
56+
57+
return status == PSA_SUCCESS;
58+
}
59+
60+
CHIP_ERROR PSAOperationalKeystore::PersistentP256Keypair::Generate()
61+
{
62+
CHIP_ERROR error = CHIP_NO_ERROR;
63+
psa_status_t status = PSA_SUCCESS;
64+
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
65+
psa_key_id_t keyId = 0;
66+
size_t publicKeyLength;
67+
68+
Destroy();
69+
70+
// Type based on ECC with the elliptic curve SECP256r1 -> PSA_ECC_FAMILY_SECP_R1
71+
psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
72+
psa_set_key_bits(&attributes, kP256_PrivateKey_Length * 8);
73+
psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_ANY_HASH));
74+
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE);
75+
psa_set_key_lifetime(&attributes,
76+
PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_PERSISTENCE_DEFAULT, PSA_KEY_LOCATION_CRACEN_KMU));
77+
psa_set_key_id(&attributes, GetKeyId());
78+
79+
status = psa_generate_key(&attributes, &keyId);
80+
VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL);
81+
82+
status = psa_export_public_key(keyId, mPublicKey.Bytes(), mPublicKey.Length(), &publicKeyLength);
83+
VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL);
84+
VerifyOrExit(publicKeyLength == kP256_PublicKey_Length, error = CHIP_ERROR_INTERNAL);
85+
86+
exit:
87+
psa_reset_key_attributes(&attributes);
88+
89+
return error;
90+
}
91+
92+
CHIP_ERROR PSAOperationalKeystore::PersistentP256Keypair::Destroy()
93+
{
94+
psa_status_t status = psa_destroy_key(GetKeyId());
95+
96+
ReturnErrorCodeIf(status == PSA_ERROR_INVALID_HANDLE, CHIP_ERROR_INVALID_FABRIC_INDEX);
97+
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
98+
99+
return CHIP_NO_ERROR;
100+
}
101+
102+
bool PSAOperationalKeystore::HasPendingOpKeypair() const
103+
{
104+
return mPendingFabricIndex != kUndefinedFabricIndex;
105+
}
106+
107+
bool PSAOperationalKeystore::HasOpKeypairForFabric(FabricIndex fabricIndex) const
108+
{
109+
VerifyOrReturnError(IsValidFabricIndex(fabricIndex), false);
110+
111+
if (mPendingFabricIndex == fabricIndex)
112+
{
113+
return mIsPendingKeypairActive;
114+
}
115+
116+
return PersistentP256Keypair(fabricIndex).Exists();
117+
}
118+
119+
CHIP_ERROR PSAOperationalKeystore::NewOpKeypairForFabric(FabricIndex fabricIndex, MutableByteSpan & outCertificateSigningRequest)
120+
{
121+
VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
122+
123+
if (HasPendingOpKeypair())
124+
{
125+
VerifyOrReturnError(fabricIndex == mPendingFabricIndex, CHIP_ERROR_INVALID_FABRIC_INDEX);
126+
}
127+
128+
if (mPendingKeypair == nullptr)
129+
{
130+
mPendingKeypair = Platform::New<PersistentP256Keypair>(fabricIndex);
131+
}
132+
133+
VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_NO_MEMORY);
134+
ReturnErrorOnFailure(mPendingKeypair->Generate());
135+
136+
size_t csrLength = outCertificateSigningRequest.size();
137+
ReturnErrorOnFailure(mPendingKeypair->NewCertificateSigningRequest(outCertificateSigningRequest.data(), csrLength));
138+
outCertificateSigningRequest.reduce_size(csrLength);
139+
mPendingFabricIndex = fabricIndex;
140+
141+
return CHIP_NO_ERROR;
142+
}
143+
144+
CHIP_ERROR PSAOperationalKeystore::PersistentP256Keypair::Deserialize(P256SerializedKeypair & input)
145+
{
146+
CHIP_ERROR error = CHIP_NO_ERROR;
147+
psa_status_t status = PSA_SUCCESS;
148+
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
149+
psa_key_id_t keyId = 0;
150+
VerifyOrReturnError(input.Length() == mPublicKey.Length() + kP256_PrivateKey_Length, CHIP_ERROR_INVALID_ARGUMENT);
151+
152+
Destroy();
153+
154+
// Type based on ECC with the elliptic curve SECP256r1 -> PSA_ECC_FAMILY_SECP_R1
155+
psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
156+
psa_set_key_bits(&attributes, kP256_PrivateKey_Length * 8);
157+
psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_ANY_HASH));
158+
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE);
159+
psa_set_key_lifetime(&attributes,
160+
PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_PERSISTENCE_DEFAULT, PSA_KEY_LOCATION_CRACEN_KMU));
161+
psa_set_key_id(&attributes, GetKeyId());
162+
163+
status = psa_import_key(&attributes, input.ConstBytes() + mPublicKey.Length(), kP256_PrivateKey_Length, &keyId);
164+
VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL);
165+
166+
memcpy(mPublicKey.Bytes(), input.ConstBytes(), mPublicKey.Length());
167+
168+
exit:
169+
LogPsaError(status);
170+
psa_reset_key_attributes(&attributes);
171+
172+
return error;
173+
}
174+
175+
CHIP_ERROR PSAOperationalKeystore::ActivateOpKeypairForFabric(FabricIndex fabricIndex, const Crypto::P256PublicKey & nocPublicKey)
176+
{
177+
VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && mPendingFabricIndex == fabricIndex, CHIP_ERROR_INVALID_FABRIC_INDEX);
178+
VerifyOrReturnError(mPendingKeypair->Pubkey().Matches(nocPublicKey), CHIP_ERROR_INVALID_PUBLIC_KEY);
179+
mIsPendingKeypairActive = true;
180+
181+
return CHIP_NO_ERROR;
182+
}
183+
184+
CHIP_ERROR PSAOperationalKeystore::CommitOpKeypairForFabric(FabricIndex fabricIndex)
185+
{
186+
VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && mPendingFabricIndex == fabricIndex, CHIP_ERROR_INVALID_FABRIC_INDEX);
187+
VerifyOrReturnError(mIsPendingKeypairActive, CHIP_ERROR_INCORRECT_STATE);
188+
189+
ReleasePendingKeypair();
190+
191+
return CHIP_NO_ERROR;
192+
}
193+
194+
CHIP_ERROR PSAOperationalKeystore::ExportOpKeypairForFabric(FabricIndex fabricIndex, Crypto::P256SerializedKeypair & outKeypair)
195+
{
196+
// Currently exporting the key is forbidden in PSAOperationalKeystore because the PSA_KEY_USAGE_EXPORT usage flag is not set, so
197+
// there is no need to compile the code for the device, but there should be an implementation for test purposes to verify if
198+
// the psa_export_key returns an error.
199+
#if CHIP_CONFIG_TEST
200+
VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
201+
VerifyOrReturnError(HasOpKeypairForFabric(fabricIndex), CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND);
202+
203+
size_t outSize = 0;
204+
psa_status_t status =
205+
psa_export_key(PersistentP256Keypair(fabricIndex).GetKeyId(), outKeypair.Bytes(), outKeypair.Capacity(), &outSize);
206+
207+
if (status == PSA_ERROR_BUFFER_TOO_SMALL)
208+
{
209+
return CHIP_ERROR_BUFFER_TOO_SMALL;
210+
}
211+
else if (status == PSA_ERROR_NOT_PERMITTED)
212+
{
213+
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
214+
}
215+
else if (status != PSA_SUCCESS)
216+
{
217+
return CHIP_ERROR_INTERNAL;
218+
}
219+
220+
outKeypair.SetLength(outSize);
221+
222+
return CHIP_NO_ERROR;
223+
#else
224+
return CHIP_ERROR_NOT_IMPLEMENTED;
225+
#endif
226+
}
227+
228+
CHIP_ERROR PSAOperationalKeystore::RemoveOpKeypairForFabric(FabricIndex fabricIndex)
229+
{
230+
VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
231+
232+
if (mPendingFabricIndex == fabricIndex)
233+
{
234+
RevertPendingKeypair();
235+
return CHIP_NO_ERROR;
236+
}
237+
238+
return PersistentP256Keypair(fabricIndex).Destroy();
239+
}
240+
241+
void PSAOperationalKeystore::RevertPendingKeypair()
242+
{
243+
VerifyOrReturn(HasPendingOpKeypair());
244+
mPendingKeypair->Destroy();
245+
ReleasePendingKeypair();
246+
}
247+
248+
CHIP_ERROR PSAOperationalKeystore::SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message,
249+
Crypto::P256ECDSASignature & outSignature) const
250+
{
251+
VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
252+
253+
if (mPendingFabricIndex == fabricIndex)
254+
{
255+
VerifyOrReturnError(mIsPendingKeypairActive, CHIP_ERROR_INVALID_FABRIC_INDEX);
256+
return mPendingKeypair->ECDSA_sign_msg(message.data(), message.size(), outSignature);
257+
}
258+
259+
PersistentP256Keypair keypair(fabricIndex);
260+
VerifyOrReturnError(keypair.Exists(), CHIP_ERROR_INVALID_FABRIC_INDEX);
261+
262+
return keypair.ECDSA_sign_msg(message.data(), message.size(), outSignature);
263+
}
264+
265+
Crypto::P256Keypair * PSAOperationalKeystore::AllocateEphemeralKeypairForCASE()
266+
{
267+
return Platform::New<Crypto::P256Keypair>();
268+
}
269+
270+
void PSAOperationalKeystore::ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair)
271+
{
272+
Platform::Delete(keypair);
273+
}
274+
275+
void PSAOperationalKeystore::ReleasePendingKeypair()
276+
{
277+
Platform::Delete(mPendingKeypair);
278+
mPendingKeypair = nullptr;
279+
mPendingFabricIndex = kUndefinedFabricIndex;
280+
mIsPendingKeypairActive = false;
281+
}
282+
283+
CHIP_ERROR PSAOperationalKeystore::MigrateOpKeypairForFabric(FabricIndex fabricIndex,
284+
OperationalKeystore & operationalKeystore) const
285+
{
286+
VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
287+
288+
P256SerializedKeypair serializedKeypair;
289+
290+
// Do not allow overwriting the existing key and just remove it from the previous Operational Keystore if needed.
291+
if (!HasOpKeypairForFabric(fabricIndex))
292+
{
293+
ReturnErrorOnFailure(operationalKeystore.ExportOpKeypairForFabric(fabricIndex, serializedKeypair));
294+
295+
PersistentP256Keypair keypair(fabricIndex);
296+
ReturnErrorOnFailure(keypair.Deserialize(serializedKeypair));
297+
298+
// Migrated key is not useful anymore, remove it from the previous keystore.
299+
ReturnErrorOnFailure(operationalKeystore.RemoveOpKeypairForFabric(fabricIndex));
300+
}
301+
else if (operationalKeystore.HasOpKeypairForFabric(fabricIndex))
302+
{
303+
ReturnErrorOnFailure(operationalKeystore.RemoveOpKeypairForFabric(fabricIndex));
304+
}
305+
306+
return CHIP_NO_ERROR;
307+
}
308+
309+
} // namespace Crypto
310+
} // namespace chip
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
/*
2+
* Copyright (c) 2025 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#include "KMUSlotDefinitions.h"
19+
#include "crypto/PSASessionKeystore.h"
20+
21+
#include <cracen_psa_kmu.h>
22+
#include <psa/crypto.h>
23+
24+
namespace chip {
25+
namespace Crypto {
26+
27+
namespace {
28+
29+
class KeyAttributesBase
30+
{
31+
public:
32+
KeyAttributesBase(psa_key_type_t type, psa_algorithm_t algorithm, psa_key_usage_t usageFlags, size_t bits)
33+
{
34+
psa_set_key_type(&mAttrs, type);
35+
psa_set_key_algorithm(&mAttrs, algorithm);
36+
psa_set_key_usage_flags(&mAttrs, usageFlags);
37+
psa_set_key_bits(&mAttrs, bits);
38+
}
39+
40+
~KeyAttributesBase() { psa_reset_key_attributes(&mAttrs); }
41+
42+
const psa_key_attributes_t & Get() { return mAttrs; }
43+
44+
private:
45+
psa_key_attributes_t mAttrs = PSA_KEY_ATTRIBUTES_INIT;
46+
};
47+
48+
class AesKeyAttributes : public KeyAttributesBase
49+
{
50+
public:
51+
// TODO: Cracen KMU supports only PSA_ALG_CCM algorithm change it to PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_CCM, 8)
52+
// once it is available. Also we cannot copy this key, because the key cannot be copied from ITS to cracen. We need to export it
53+
// and import it back.
54+
AesKeyAttributes() :
55+
KeyAttributesBase(PSA_KEY_TYPE_AES, PSA_ALG_CCM, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT | PSA_KEY_USAGE_EXPORT,
56+
CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES * 8)
57+
{}
58+
};
59+
60+
class HmacKeyAttributes : public KeyAttributesBase
61+
{
62+
public:
63+
// TODO: We cannot copy this key, because the key cannot be copied from ITS to cracen. We need to export it
64+
// and import it back.
65+
HmacKeyAttributes() :
66+
KeyAttributesBase(PSA_KEY_TYPE_HMAC, PSA_ALG_HMAC(PSA_ALG_SHA_256), PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_EXPORT,
67+
CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES * 8)
68+
{}
69+
};
70+
71+
class HkdfKeyAttributes : public KeyAttributesBase
72+
{
73+
public:
74+
HkdfKeyAttributes() : KeyAttributesBase(PSA_KEY_TYPE_DERIVE, PSA_ALG_HKDF(PSA_ALG_SHA_256), PSA_KEY_USAGE_DERIVE, 0) {}
75+
};
76+
77+
#if CHIP_CONFIG_ENABLE_ICD_CIP
78+
void SetKeyId(Symmetric128BitsKeyHandle & key, psa_key_id_t newKeyId)
79+
{
80+
auto & KeyId = key.AsMutable<psa_key_id_t>();
81+
82+
KeyId = newKeyId;
83+
}
84+
#endif
85+
} // namespace
86+
87+
CHIP_ERROR PSASessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Aes128KeyHandle & key)
88+
{
89+
// Destroy the old key if already allocated
90+
DestroyKey(key);
91+
92+
AesKeyAttributes attrs;
93+
psa_status_t status =
94+
psa_import_key(&attrs.Get(), keyMaterial, sizeof(Symmetric128BitsKeyByteArray), &key.AsMutable<psa_key_id_t>());
95+
LogPsaError(status);
96+
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
97+
98+
return CHIP_NO_ERROR;
99+
}
100+
101+
CHIP_ERROR PSASessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Hmac128KeyHandle & key)
102+
{
103+
// Destroy the old key if already allocated
104+
DestroyKey(key);
105+
106+
HmacKeyAttributes attrs;
107+
psa_status_t status =
108+
psa_import_key(&attrs.Get(), keyMaterial, sizeof(Symmetric128BitsKeyByteArray), &key.AsMutable<psa_key_id_t>());
109+
LogPsaError(status);
110+
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
111+
112+
return CHIP_NO_ERROR;
113+
}
114+
115+
CHIP_ERROR PSASessionKeystore::CreateKey(const ByteSpan & keyMaterial, HkdfKeyHandle & key)
116+
{
117+
// Destroy the old key if already allocated
118+
psa_destroy_key(key.As<psa_key_id_t>());
119+
120+
HkdfKeyAttributes attrs;
121+
psa_status_t status = psa_import_key(&attrs.Get(), keyMaterial.data(), keyMaterial.size(), &key.AsMutable<psa_key_id_t>());
122+
LogPsaError(status);
123+
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
124+
125+
return CHIP_NO_ERROR;
126+
}
127+
128+
CHIP_ERROR PSASessionKeystore::DeriveKey(const P256ECDHDerivedSecret & secret, const ByteSpan & salt, const ByteSpan & info,
129+
Aes128KeyHandle & key)
130+
{
131+
PsaKdf kdf;
132+
ReturnErrorOnFailure(kdf.Init(secret.Span(), salt, info));
133+
134+
AesKeyAttributes attrs;
135+
136+
return kdf.DeriveKey(attrs.Get(), key.AsMutable<psa_key_id_t>());
137+
}
138+
139+
CHIP_ERROR PSASessionKeystore::DeriveSessionKeys(const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info,
140+
Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
141+
AttestationChallenge & attestationChallenge)
142+
{
143+
PsaKdf kdf;
144+
ReturnErrorOnFailure(kdf.Init(secret, salt, info));
145+
146+
return DeriveSessionKeys(kdf, i2rKey, r2iKey, attestationChallenge);
147+
}
148+
149+
CHIP_ERROR PSASessionKeystore::DeriveSessionKeys(const HkdfKeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info,
150+
Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
151+
AttestationChallenge & attestationChallenge)
152+
{
153+
PsaKdf kdf;
154+
ReturnErrorOnFailure(kdf.Init(hkdfKey, salt, info));
155+
156+
return DeriveSessionKeys(kdf, i2rKey, r2iKey, attestationChallenge);
157+
}
158+
159+
CHIP_ERROR PSASessionKeystore::DeriveSessionKeys(PsaKdf & kdf, Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
160+
AttestationChallenge & attestationChallenge)
161+
{
162+
CHIP_ERROR error;
163+
AesKeyAttributes attrs;
164+
165+
SuccessOrExit(error = kdf.DeriveKey(attrs.Get(), i2rKey.AsMutable<psa_key_id_t>()));
166+
SuccessOrExit(error = kdf.DeriveKey(attrs.Get(), r2iKey.AsMutable<psa_key_id_t>()));
167+
SuccessOrExit(error = kdf.DeriveBytes(MutableByteSpan(attestationChallenge.Bytes(), AttestationChallenge::Capacity())));
168+
169+
exit:
170+
if (error != CHIP_NO_ERROR)
171+
{
172+
DestroyKey(i2rKey);
173+
DestroyKey(r2iKey);
174+
}
175+
176+
return error;
177+
}
178+
179+
void PSASessionKeystore::DestroyKey(Symmetric128BitsKeyHandle & key)
180+
{
181+
auto & keyId = key.AsMutable<psa_key_id_t>();
182+
183+
psa_destroy_key(keyId);
184+
keyId = 0;
185+
}
186+
187+
void PSASessionKeystore::DestroyKey(HkdfKeyHandle & key)
188+
{
189+
auto & keyId = key.AsMutable<psa_key_id_t>();
190+
191+
psa_destroy_key(keyId);
192+
keyId = PSA_KEY_ID_NULL;
193+
}
194+
195+
#if CHIP_CONFIG_ENABLE_ICD_CIP
196+
CHIP_ERROR PSASessionKeystore::PersistICDKey(Symmetric128BitsKeyHandle & key)
197+
{
198+
CHIP_ERROR err;
199+
psa_key_id_t newKeyId = PSA_KEY_ID_NULL;
200+
psa_key_attributes_t attrs;
201+
psa_status_t s = PSA_SUCCESS;
202+
uint8_t keyEx[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES];
203+
size_t keyExSize = sizeof(keyEx);
204+
205+
psa_get_key_attributes(key.As<psa_key_id_t>(), &attrs);
206+
207+
// Exit early if key is already persistent
208+
if (psa_get_key_lifetime(&attrs) ==
209+
PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_PERSISTENCE_DEFAULT, PSA_KEY_LOCATION_CRACEN_KMU))
210+
{
211+
psa_reset_key_attributes(&attrs);
212+
return CHIP_NO_ERROR;
213+
}
214+
215+
// Set the key lifetime to persistent and the location to CRACEN_KMU
216+
psa_set_key_lifetime(&attrs,
217+
PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_PERSISTENCE_DEFAULT, PSA_KEY_LOCATION_CRACEN_KMU));
218+
219+
// Use dedicated finding function because the FindFreeKeySlotInRange requires a key id within the user range
220+
// Whereas the KMU slots are in the Vendor range
221+
auto findFreeSlot = [](psa_key_id_t & keyId, psa_key_id_t start, psa_key_id_t end) -> CHIP_ERROR {
222+
psa_key_attributes_t attributes;
223+
psa_status_t status = PSA_SUCCESS;
224+
for (keyId = start; keyId < end; ++keyId)
225+
{
226+
status = psa_get_key_attributes(keyId, &attributes);
227+
if (status == PSA_ERROR_INVALID_HANDLE)
228+
{
229+
return CHIP_NO_ERROR;
230+
}
231+
else if (status != PSA_SUCCESS)
232+
{
233+
return CHIP_ERROR_INTERNAL;
234+
}
235+
psa_reset_key_attributes(&attributes);
236+
}
237+
return CHIP_ERROR_NOT_FOUND;
238+
};
239+
240+
SuccessOrExit(err = findFreeSlot(newKeyId, KMU_MATTER_ICD_SLOT_START, KMU_MATTER_ICD_SLOT_START + KMU_MATTER_ICD_SLOT_COUNT));
241+
psa_set_key_id(&attrs, newKeyId);
242+
// We cannot use psa_copy_key here because the source and target keys are stored in different locations.
243+
// Instead we need to export this key and import once again.
244+
VerifyOrExit(psa_export_key(key.As<psa_key_id_t>(), keyEx, sizeof(keyEx), &keyExSize) == PSA_SUCCESS,
245+
err = CHIP_ERROR_INTERNAL);
246+
VerifyOrExit(psa_import_key(&attrs, keyEx, keyExSize, &newKeyId) == PSA_SUCCESS, err = CHIP_ERROR_INTERNAL);
247+
248+
exit:
249+
DestroyKey(key);
250+
memset(keyEx, 0, sizeof(keyEx));
251+
psa_reset_key_attributes(&attrs);
252+
253+
if (err == CHIP_NO_ERROR)
254+
{
255+
SetKeyId(key, newKeyId);
256+
}
257+
258+
return err;
259+
}
260+
#endif
261+
262+
} // namespace Crypto
263+
} // namespace chip
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2025 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#pragma once
19+
20+
// Check whether the DAC KMU slot doesn not overlap with the KMU slots dedicated for Matter core.
21+
#if defined(CONFIG_CHIP_CRYPTO_PSA_DAC_PRIV_KEY_KMU) && \
22+
(CONFIG_CHIP_CRYPTO_PSA_DAC_PRIV_KEY_KMU_SLOT_ID >= CONFIG_CHIP_CORE_KMU_SLOT_START && \
23+
CONFIG_CHIP_CRYPTO_PSA_DAC_PRIV_KEY_KMU_SLOT_ID <= CONFIG_CHIP_CORE_KMU_SLOT_END)
24+
#error "CONFIG_CHIP_CRYPTO_PSA_DAC_PRIV_KEY_KMU_SLOT_ID Cannot overlaps with KMU slots dedicated for Matter core"
25+
#endif
26+
27+
// Define how many slots are available for Matter.
28+
#define KMU_MATTER_SLOT_COUNT (CONFIG_CHIP_CORE_KMU_SLOT_END - CONFIG_CHIP_CORE_KMU_SLOT_START)
29+
// For NOC we need 2 slots per fabric (ESDSA)
30+
#define KMU_MATTER_NOC_SLOT_COUNT (CONFIG_CHIP_KMU_MAX_FABRICS * 2)
31+
#ifdef CONFIG_CHIP_ENABLE_ICD_SUPPORT
32+
// For ICD we need 2 slots per ICD entry (AES + HMAC)
33+
#define KMU_MATTER_ICD_SLOT_COUNT (CONFIG_CHIP_KMU_MAX_FABRICS * 2)
34+
#else
35+
#define KMU_MATTER_ICD_SLOT_COUNT 0
36+
#endif
37+
38+
// Define the start of the KMU slots for Matter.
39+
#define KMU_MATTER_NOC_SLOT_START \
40+
PSA_KEY_HANDLE_FROM_CRACEN_KMU_SLOT(CRACEN_KMU_KEY_USAGE_SCHEME_RAW, CONFIG_CHIP_CORE_KMU_SLOT_START)
41+
#define KMU_MATTER_ICD_SLOT_START \
42+
PSA_KEY_HANDLE_FROM_CRACEN_KMU_SLOT(CRACEN_KMU_KEY_USAGE_SCHEME_RAW, \
43+
(CONFIG_CHIP_CORE_KMU_SLOT_START + KMU_MATTER_NOC_SLOT_COUNT))
44+
45+
// Check if the maximum of used slots is not greater than the available slots.
46+
// Each fabric requires 2 slots (ESDSA), one ICD entry requires 2 slots (AES + HMAC).
47+
// If migration DAC to KMU is enabled we need to reserve slots for it.
48+
#if (KMU_MATTER_NOC_SLOT_COUNT + KMU_MATTER_ICD_SLOT_COUNT) > KMU_MATTER_SLOT_COUNT
49+
#error "Not enough KMU slots for the requested configuration"
50+
#endif

0 commit comments

Comments
 (0)
Please sign in to comment.