Skip to content

Commit 04e21d9

Browse files
[ICD] Implement storing of persistent keys in PSA for ICD server (project-chip#34925)
* Refactor Key Attributes classes Implement `keyAttributesBase` and add persistent lifetime functionality. * Implement `FindFreeKeySlotInRange` for PSA key slots * Fix ICD CIP related build dependencies * Remake `ICDMonitoringTable::Find()` not to overwrite all entry fields If entry is not found its key handle field must not be filled with last checked entry when using PSA as it will cause key slot to be cleared by accident. * Add ICD CIP and DAC key slots for PSA * Implement `PersistICDKey` API * Implement setting key persitence for ICD server * Address PR comments Modify implementation of `PersistICDKey` not to depend on type of ICD key handle (AES, HMAC)
1 parent 9de3c6b commit 04e21d9

14 files changed

+207
-61
lines changed

src/app/icd/server/BUILD.gn

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ source_set("manager") {
9393
deps = [ ":icd-server-config" ]
9494

9595
public_deps = [
96-
":check-in-back-off",
9796
":configuration-data",
9897
":notifier",
9998
":observer",
@@ -106,6 +105,7 @@ source_set("manager") {
106105

107106
if (chip_enable_icd_checkin) {
108107
public_deps += [
108+
":check-in-back-off",
109109
":monitoring-table",
110110
":sender",
111111
"${chip_root}/src/app:app_config",

src/app/icd/server/ICDManager.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
#include <app/AppConfig.h>
2222
#include <app/SubscriptionsInfoProvider.h>
2323
#include <app/TestEventTriggerDelegate.h>
24-
#include <app/icd/server/ICDCheckInBackOffStrategy.h>
2524
#include <app/icd/server/ICDConfigurationData.h>
2625
#include <app/icd/server/ICDNotifier.h>
2726
#include <app/icd/server/ICDStateObserver.h>
@@ -34,9 +33,10 @@
3433
#include <system/SystemClock.h>
3534

3635
#if CHIP_CONFIG_ENABLE_ICD_CIP
37-
#include <app/icd/server/ICDCheckInSender.h> // nogncheck
38-
#include <app/icd/server/ICDMonitoringTable.h> // nogncheck
39-
#endif // CHIP_CONFIG_ENABLE_ICD_CIP
36+
#include <app/icd/server/ICDCheckInBackOffStrategy.h> // nogncheck
37+
#include <app/icd/server/ICDCheckInSender.h> // nogncheck
38+
#include <app/icd/server/ICDMonitoringTable.h> // nogncheck
39+
#endif // CHIP_CONFIG_ENABLE_ICD_CIP
4040

4141
namespace chip {
4242
namespace Crypto {

src/app/icd/server/ICDMonitoringTable.cpp

+25-9
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@ CHIP_ERROR ICDMonitoringEntry::SetKey(ByteSpan keyData)
140140
Crypto::Symmetric128BitsKeyByteArray keyMaterial;
141141
memcpy(keyMaterial, keyData.data(), sizeof(Crypto::Symmetric128BitsKeyByteArray));
142142

143-
// TODO - Add function to set PSA key lifetime
144143
ReturnErrorOnFailure(symmetricKeystore->CreateKey(keyMaterial, aesKeyHandle));
145144
CHIP_ERROR error = symmetricKeystore->CreateKey(keyMaterial, hmacKeyHandle);
146145

@@ -242,15 +241,22 @@ CHIP_ERROR ICDMonitoringTable::Get(uint16_t index, ICDMonitoringEntry & entry) c
242241

243242
CHIP_ERROR ICDMonitoringTable::Find(NodeId id, ICDMonitoringEntry & entry)
244243
{
245-
uint16_t index = 0;
246-
while (index < this->Limit())
244+
uint16_t index;
245+
ICDMonitoringEntry tempEntry(mSymmetricKeystore);
246+
247+
for (index = 0; index < this->Limit(); index++)
247248
{
248-
ReturnErrorOnFailure(this->Get(index++, entry));
249-
if (id == entry.checkInNodeID)
249+
if (this->Get(index, tempEntry) != CHIP_NO_ERROR)
250+
{
251+
break;
252+
}
253+
if (id == tempEntry.checkInNodeID)
250254
{
255+
entry = tempEntry;
251256
return CHIP_NO_ERROR;
252257
}
253258
}
259+
254260
entry.index = index;
255261
return CHIP_ERROR_NOT_FOUND;
256262
}
@@ -263,16 +269,26 @@ CHIP_ERROR ICDMonitoringTable::Set(uint16_t index, const ICDMonitoringEntry & en
263269
VerifyOrReturnError(entry.keyHandleValid, CHIP_ERROR_INVALID_ARGUMENT);
264270

265271
ICDMonitoringEntry e(this->mFabric, index);
266-
e.checkInNodeID = entry.checkInNodeID;
267-
e.monitoredSubject = entry.monitoredSubject;
268-
e.clientType = entry.clientType;
269-
e.index = index;
272+
e.checkInNodeID = entry.checkInNodeID;
273+
e.monitoredSubject = entry.monitoredSubject;
274+
e.clientType = entry.clientType;
275+
e.index = index;
276+
e.symmetricKeystore = entry.symmetricKeystore;
270277

271278
memcpy(e.aesKeyHandle.AsMutable<Crypto::Symmetric128BitsKeyByteArray>(),
272279
entry.aesKeyHandle.As<Crypto::Symmetric128BitsKeyByteArray>(), sizeof(Crypto::Symmetric128BitsKeyByteArray));
273280
memcpy(e.hmacKeyHandle.AsMutable<Crypto::Symmetric128BitsKeyByteArray>(),
274281
entry.hmacKeyHandle.As<Crypto::Symmetric128BitsKeyByteArray>(), sizeof(Crypto::Symmetric128BitsKeyByteArray));
275282

283+
ReturnErrorOnFailure(e.symmetricKeystore->PersistICDKey(e.aesKeyHandle));
284+
CHIP_ERROR error = e.symmetricKeystore->PersistICDKey(e.hmacKeyHandle);
285+
if (error != CHIP_NO_ERROR)
286+
{
287+
// If setting the persistence of the HmacKeyHandle failed, we need to delete the AesKeyHandle to avoid a key leak
288+
e.symmetricKeystore->DestroyKey(e.aesKeyHandle);
289+
return error;
290+
}
291+
276292
return e.Save(this->mStorage);
277293
}
278294

src/app/server/BUILD.gn

-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ static_library("server") {
5454
public_deps = [
5555
"${chip_root}/src/app",
5656
"${chip_root}/src/app:test-event-trigger",
57-
"${chip_root}/src/app/icd/server:check-in-back-off",
5857
"${chip_root}/src/app/icd/server:icd-server-config",
5958
"${chip_root}/src/app/icd/server:observer",
6059
"${chip_root}/src/lib/address_resolve",

src/app/server/Server.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,14 @@
7272
#include <app/reporting/ReportSchedulerImpl.h>
7373
#include <transport/raw/UDP.h>
7474

75-
#include <app/icd/server/ICDCheckInBackOffStrategy.h>
7675
#if CHIP_CONFIG_ENABLE_ICD_SERVER
7776
#include <app/icd/server/ICDManager.h> // nogncheck
7877

7978
#if CHIP_CONFIG_ENABLE_ICD_CIP
8079
#include <app/icd/server/DefaultICDCheckInBackOffStrategy.h> // nogncheck
81-
#endif
82-
#endif
80+
#include <app/icd/server/ICDCheckInBackOffStrategy.h> // nogncheck
81+
#endif // CHIP_CONFIG_ENABLE_ICD_CIP
82+
#endif // CHIP_CONFIG_ENABLE_ICD_SERVER
8383

8484
namespace chip {
8585

@@ -183,9 +183,11 @@ struct ServerInitParams
183183
Credentials::OperationalCertificateStore * opCertStore = nullptr;
184184
// Required, if not provided, the Server::Init() WILL fail.
185185
app::reporting::ReportScheduler * reportScheduler = nullptr;
186+
#if CHIP_CONFIG_ENABLE_ICD_CIP
186187
// Optional. Support for the ICD Check-In BackOff strategy. Must be initialized before being provided.
187188
// If the ICD Check-In protocol use-case is supported and no strategy is provided, server will use the default strategy.
188189
app::ICDCheckInBackOffStrategy * icdCheckInBackOffStrategy = nullptr;
190+
#endif // CHIP_CONFIG_ENABLE_ICD_CIP
189191
};
190192

191193
/**

src/crypto/BUILD.gn

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ source_set("public_headers") {
6767

6868
public_deps = [
6969
":crypto_buildconfig",
70+
"${chip_root}/src/app/icd/server:icd-server-config",
7071
"${chip_root}/src/lib/asn1",
7172
"${chip_root}/src/lib/core",
7273
"${chip_root}/src/lib/core:types",

src/crypto/CHIPCryptoPALPSA.cpp

+22
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,28 @@ void Hash_SHA256_stream::Clear()
263263
psa_hash_abort(toHashOperation(&mContext));
264264
}
265265

266+
CHIP_ERROR FindFreeKeySlotInRange(psa_key_id_t & keyId, psa_key_id_t start, uint32_t range)
267+
{
268+
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
269+
psa_key_id_t end = start + range;
270+
271+
VerifyOrReturnError(start >= PSA_KEY_ID_USER_MIN && end - 1 <= PSA_KEY_ID_USER_MAX, CHIP_ERROR_INVALID_ARGUMENT);
272+
273+
for (keyId = start; keyId < end; keyId++)
274+
{
275+
psa_status_t status = psa_get_key_attributes(keyId, &attributes);
276+
if (status == PSA_ERROR_INVALID_HANDLE)
277+
{
278+
return CHIP_NO_ERROR;
279+
}
280+
else if (status != PSA_SUCCESS)
281+
{
282+
return CHIP_ERROR_INTERNAL;
283+
}
284+
}
285+
return CHIP_ERROR_NOT_FOUND;
286+
}
287+
266288
CHIP_ERROR PsaKdf::Init(const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info)
267289
{
268290
psa_status_t status = PSA_SUCCESS;

src/crypto/CHIPCryptoPALPSA.h

+43-4
Original file line numberDiff line numberDiff line change
@@ -55,19 +55,58 @@ namespace Crypto {
5555
#define CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE 0x30000
5656
#endif // CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE
5757

58+
/**
59+
* @def CHIP_CONFIG_CRYPTO_PSA_KEY_ID_END
60+
*
61+
* @brief
62+
* End of the PSA key identifier range used by Matter.
63+
*
64+
* This setting establishes the maximum limit for the key range specific to Matter, in order to
65+
* prevent any overlap with other firmware components that also employ the PSA crypto API.
66+
*/
67+
#ifndef CHIP_CONFIG_CRYPTO_PSA_KEY_ID_END
68+
#define CHIP_CONFIG_CRYPTO_PSA_KEY_ID_END 0x3FFFF
69+
#endif // CHIP_CONFIG_CRYPTO_PSA_KEY_ID_END
70+
71+
static_assert(PSA_KEY_ID_USER_MIN <= CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE && CHIP_CONFIG_CRYPTO_PSA_KEY_ID_END <= PSA_KEY_ID_USER_MAX,
72+
"Matter specific PSA key range doesn't fit within PSA allowed range");
73+
74+
// Each ICD client requires storing two keys- AES and HMAC
75+
static constexpr uint32_t kMaxICDClientKeys = 2 * CHIP_CONFIG_CRYPTO_PSA_ICD_MAX_CLIENTS;
76+
77+
static_assert(kMaxICDClientKeys >= CHIP_CONFIG_ICD_CLIENTS_SUPPORTED_PER_FABRIC * CHIP_CONFIG_MAX_FABRICS,
78+
"Number of allocated ICD key slots is lower than maximum number of supported ICD clients");
79+
5880
/**
5981
* @brief Defines subranges of the PSA key identifier space used by Matter.
6082
*/
6183
enum class KeyIdBase : psa_key_id_t
6284
{
63-
Minimum = CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE,
64-
Operational = Minimum, ///< Base of the PSA key ID range for Node Operational Certificate private keys
65-
Maximum = Operational + kMaxValidFabricIndex,
85+
Minimum = CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE,
86+
Operational = Minimum, ///< Base of the PSA key ID range for Node Operational Certificate private keys
87+
DACPrivKey = Operational + kMaxValidFabricIndex + 1,
88+
ICDKeyRangeStart = DACPrivKey + 1,
89+
Maximum = ICDKeyRangeStart + kMaxICDClientKeys,
6690
};
6791

68-
static_assert(to_underlying(KeyIdBase::Minimum) >= PSA_KEY_ID_USER_MIN && to_underlying(KeyIdBase::Maximum) <= PSA_KEY_ID_USER_MAX,
92+
static_assert(to_underlying(KeyIdBase::Minimum) >= CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE &&
93+
to_underlying(KeyIdBase::Maximum) <= CHIP_CONFIG_CRYPTO_PSA_KEY_ID_END,
6994
"PSA key ID base out of allowed range");
7095

96+
/**
97+
* @brief Finds first free persistent Key slot ID within range.
98+
*
99+
* @param[out] keyId Key ID handler to which free ID will be set.
100+
* @param[in] start Starting ID in search range.
101+
* @param[in] range Search range.
102+
*
103+
* @retval CHIP_NO_ERROR On success.
104+
* @retval CHIP_ERROR_INTERNAL On PSA crypto API error.
105+
* @retval CHIP_ERROR_NOT_FOUND On no free Key ID within range.
106+
* @retval CHIP_ERROR_INVALID_ARGUMENT On search arguments out of PSA allowed range.
107+
*/
108+
CHIP_ERROR FindFreeKeySlotInRange(psa_key_id_t & keyId, psa_key_id_t start, uint32_t range);
109+
71110
/**
72111
* @brief Calculates PSA key ID for Node Operational Certificate private key for the given fabric.
73112
*/

src/crypto/PSASessionKeystore.cpp

+65-39
Original file line numberDiff line numberDiff line change
@@ -24,70 +24,62 @@ namespace Crypto {
2424

2525
namespace {
2626

27-
class AesKeyAttributes
27+
class KeyAttributesBase
2828
{
2929
public:
30-
AesKeyAttributes()
30+
KeyAttributesBase(psa_key_type_t type, psa_algorithm_t algorithm, psa_key_usage_t usageFlags, size_t bits)
3131
{
32-
constexpr psa_algorithm_t kAlgorithm = PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_CCM, 8);
33-
34-
psa_set_key_type(&mAttrs, PSA_KEY_TYPE_AES);
35-
psa_set_key_algorithm(&mAttrs, kAlgorithm);
36-
psa_set_key_usage_flags(&mAttrs, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
37-
psa_set_key_bits(&mAttrs, CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES * 8);
32+
psa_set_key_type(&mAttrs, type);
33+
psa_set_key_algorithm(&mAttrs, algorithm);
34+
psa_set_key_usage_flags(&mAttrs, usageFlags);
35+
psa_set_key_bits(&mAttrs, bits);
3836
}
3937

40-
~AesKeyAttributes() { psa_reset_key_attributes(&mAttrs); }
38+
~KeyAttributesBase() { psa_reset_key_attributes(&mAttrs); }
4139

4240
const psa_key_attributes_t & Get() { return mAttrs; }
4341

4442
private:
4543
psa_key_attributes_t mAttrs = PSA_KEY_ATTRIBUTES_INIT;
4644
};
4745

48-
class HmacKeyAttributes
46+
class AesKeyAttributes : public KeyAttributesBase
4947
{
5048
public:
51-
HmacKeyAttributes()
52-
{
53-
psa_set_key_type(&mAttrs, PSA_KEY_TYPE_HMAC);
54-
psa_set_key_algorithm(&mAttrs, PSA_ALG_HMAC(PSA_ALG_SHA_256));
55-
psa_set_key_usage_flags(&mAttrs, PSA_KEY_USAGE_SIGN_MESSAGE);
56-
psa_set_key_bits(&mAttrs, CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES * 8);
57-
}
58-
59-
~HmacKeyAttributes() { psa_reset_key_attributes(&mAttrs); }
60-
61-
const psa_key_attributes_t & Get() { return mAttrs; }
62-
63-
private:
64-
psa_key_attributes_t mAttrs = PSA_KEY_ATTRIBUTES_INIT;
49+
AesKeyAttributes() :
50+
KeyAttributesBase(PSA_KEY_TYPE_AES, PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_CCM, 8),
51+
PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT | PSA_KEY_USAGE_COPY,
52+
CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES * 8)
53+
{}
6554
};
6655

67-
class HkdfKeyAttributes
56+
class HmacKeyAttributes : public KeyAttributesBase
6857
{
6958
public:
70-
HkdfKeyAttributes()
71-
{
72-
psa_set_key_type(&mAttrs, PSA_KEY_TYPE_DERIVE);
73-
psa_set_key_algorithm(&mAttrs, PSA_ALG_HKDF(PSA_ALG_SHA_256));
74-
psa_set_key_usage_flags(&mAttrs, PSA_KEY_USAGE_DERIVE);
75-
}
76-
77-
~HkdfKeyAttributes() { psa_reset_key_attributes(&mAttrs); }
78-
79-
const psa_key_attributes_t & Get() { return mAttrs; }
59+
HmacKeyAttributes() :
60+
KeyAttributesBase(PSA_KEY_TYPE_HMAC, PSA_ALG_HMAC(PSA_ALG_SHA_256), PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_COPY,
61+
CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES * 8)
62+
{}
63+
};
8064

81-
private:
82-
psa_key_attributes_t mAttrs = PSA_KEY_ATTRIBUTES_INIT;
65+
class HkdfKeyAttributes : public KeyAttributesBase
66+
{
67+
public:
68+
HkdfKeyAttributes() : KeyAttributesBase(PSA_KEY_TYPE_DERIVE, PSA_ALG_HKDF(PSA_ALG_SHA_256), PSA_KEY_USAGE_DERIVE, 0) {}
8369
};
8470

71+
void SetKeyId(Symmetric128BitsKeyHandle & key, psa_key_id_t newKeyId)
72+
{
73+
auto & KeyId = key.AsMutable<psa_key_id_t>();
74+
75+
KeyId = newKeyId;
76+
}
8577
} // namespace
8678

8779
CHIP_ERROR PSASessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Aes128KeyHandle & key)
8880
{
8981
// Destroy the old key if already allocated
90-
psa_destroy_key(key.As<psa_key_id_t>());
82+
DestroyKey(key);
9183

9284
AesKeyAttributes attrs;
9385
psa_status_t status =
@@ -101,7 +93,7 @@ CHIP_ERROR PSASessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & ke
10193
CHIP_ERROR PSASessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Hmac128KeyHandle & key)
10294
{
10395
// Destroy the old key if already allocated
104-
psa_destroy_key(key.As<psa_key_id_t>());
96+
DestroyKey(key);
10597

10698
HmacKeyAttributes attrs;
10799
psa_status_t status =
@@ -192,5 +184,39 @@ void PSASessionKeystore::DestroyKey(HkdfKeyHandle & key)
192184
keyId = PSA_KEY_ID_NULL;
193185
}
194186

187+
#if CHIP_CONFIG_ENABLE_ICD_CIP
188+
CHIP_ERROR PSASessionKeystore::PersistICDKey(Symmetric128BitsKeyHandle & key)
189+
{
190+
CHIP_ERROR err;
191+
psa_key_id_t newKeyId = PSA_KEY_ID_NULL;
192+
psa_key_attributes_t attrs;
193+
194+
psa_get_key_attributes(key.As<psa_key_id_t>(), &attrs);
195+
196+
// Exit early if key is already persistent
197+
if (psa_get_key_lifetime(&attrs) == PSA_KEY_LIFETIME_PERSISTENT)
198+
{
199+
psa_reset_key_attributes(&attrs);
200+
return CHIP_NO_ERROR;
201+
}
202+
203+
SuccessOrExit(err = Crypto::FindFreeKeySlotInRange(newKeyId, to_underlying(KeyIdBase::ICDKeyRangeStart), kMaxICDClientKeys));
204+
psa_set_key_lifetime(&attrs, PSA_KEY_LIFETIME_PERSISTENT);
205+
psa_set_key_id(&attrs, newKeyId);
206+
VerifyOrExit(psa_copy_key(key.As<psa_key_id_t>(), &attrs, &newKeyId) == PSA_SUCCESS, err = CHIP_ERROR_INTERNAL);
207+
208+
exit:
209+
DestroyKey(key);
210+
psa_reset_key_attributes(&attrs);
211+
212+
if (err == CHIP_NO_ERROR)
213+
{
214+
SetKeyId(key, newKeyId);
215+
}
216+
217+
return err;
218+
}
219+
#endif
220+
195221
} // namespace Crypto
196222
} // namespace chip

0 commit comments

Comments
 (0)