Skip to content

Commit 75b319c

Browse files
[nrf fromlist][crypto] Extend the OperationalKeystore API to allow migration
- Extended the OperationalKeystore API by mechanism for migration of operational keys stored in one Operational Keystore implementation to another. - Extended the OperationalKeystore API by exporting keypair for Fabric. - Added Unit tests to PersistentStorageOpKeyStore and PSAOpKeystore regarding the new OperationalKeystore for migration and exporting OpKeys. Added first unit tests, created export method Aligned NXP and Silabs platform to the recent OperationalKeystore changes.
1 parent ce00758 commit 75b319c

7 files changed

+491
-50
lines changed

src/crypto/OperationalKeystore.h

+41
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,47 @@ class OperationalKeystore
119119
*/
120120
virtual CHIP_ERROR CommitOpKeypairForFabric(FabricIndex fabricIndex) = 0;
121121

122+
/**
123+
* @brief Try to read out the permanently committed operational keypair and save it to the buffer.
124+
*
125+
* @param fabricIndex - FabricIndex from which the keypair will be exported
126+
* @param outKeypair - a reference to P256SerializedKeypair object to store the exported key.
127+
* @retval CHIP_ERROR on success.
128+
* @retval CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE if the key cannot be exported due to security restrictions.
129+
* @retval CHIP_ERROR_NOT_IMPLEMENTED if the exporting is not implemented for the cryptography backend.
130+
* @retval CHIP_ERROR_INVALID_FABRIC_INDEX if provided wrong value of `fabricIndex`.
131+
* @retval CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND if there is no keypair found for `fabricIndex`.
132+
* @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outKeyPair` buffer is too small to store the read out keypair.
133+
* @retval other CHIP_ERROR value on internal storage or cryptography backend errors.
134+
*/
135+
virtual CHIP_ERROR ExportOpKeypairForFabric(FabricIndex fabricIndex, Crypto::P256SerializedKeypair & outKeypair)
136+
{
137+
return CHIP_ERROR_NOT_IMPLEMENTED;
138+
};
139+
140+
/**
141+
* @brief Migrate the operational keypair from another Operational keystore (`operationalKeystore`) implementation to this one.
142+
*
143+
* This method assumes that the operational key for given `fabricIndex` exists in the `operationalKeystore` storage or it has
144+
* been already migrated to the new Operational Storage. If a key for the given `fabricIndex` does not exist in any of those
145+
* keystores, then the method retuns CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND.
146+
*
147+
* @param fabricIndex - FabricIndex for which to migrate the operational key
148+
* @param operationalKeystore - a reference to the operationalKeystore implementation that may contain saved operational key
149+
* for Fabric
150+
* @retval CHIP_ERROR on success
151+
* @retval CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE if the key cannot be exported due to security restrictions.
152+
* @retval CHIP_ERROR_NOT_IMPLEMENTED if the exporting is not implemented for the cryptography backend.
153+
* @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no keypair found for `fabricIndex`.
154+
* @retval CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND if there is no keypair found for `fabricIndex`.
155+
* @retval CHIP_ERROR_BUFFER_TOO_SMALL if `keyPair` buffer is too small to store the read out keypair.
156+
* @retval other CHIP_ERROR value on internal storage or crypto engine errors.
157+
*/
158+
virtual CHIP_ERROR MigrateOpKeypairForFabric(FabricIndex fabricIndex, OperationalKeystore & operationalKeystore) const
159+
{
160+
return CHIP_ERROR_NOT_IMPLEMENTED;
161+
};
162+
122163
/**
123164
* @brief Permanently remove the keypair associated with a fabric
124165
*

src/crypto/PSAOperationalKeystore.cpp

+90
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
#include "PSAOperationalKeystore.h"
19+
#include "PersistentStorageOperationalKeystore.h"
1920

2021
#include <lib/support/CHIPMem.h>
2122

@@ -135,6 +136,35 @@ CHIP_ERROR PSAOperationalKeystore::NewOpKeypairForFabric(FabricIndex fabricIndex
135136
return CHIP_NO_ERROR;
136137
}
137138

139+
CHIP_ERROR PSAOperationalKeystore::PersistentP256Keypair::Deserialize(P256SerializedKeypair & input)
140+
{
141+
CHIP_ERROR error = CHIP_NO_ERROR;
142+
psa_status_t status = PSA_SUCCESS;
143+
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
144+
psa_key_id_t keyId = 0;
145+
VerifyOrReturnError(input.Length() == mPublicKey.Length() + kP256_PrivateKey_Length, CHIP_ERROR_INVALID_ARGUMENT);
146+
147+
Destroy();
148+
149+
// Type based on ECC with the elliptic curve SECP256r1 -> PSA_ECC_FAMILY_SECP_R1
150+
psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
151+
psa_set_key_bits(&attributes, kP256_PrivateKey_Length * 8);
152+
psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
153+
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE);
154+
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT);
155+
psa_set_key_id(&attributes, GetKeyId());
156+
157+
status = psa_import_key(&attributes, input.ConstBytes() + mPublicKey.Length(), kP256_PrivateKey_Length, &keyId);
158+
VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL);
159+
160+
memcpy(mPublicKey.Bytes(), input.ConstBytes(), mPublicKey.Length());
161+
162+
exit:
163+
psa_reset_key_attributes(&attributes);
164+
165+
return error;
166+
}
167+
138168
CHIP_ERROR PSAOperationalKeystore::ActivateOpKeypairForFabric(FabricIndex fabricIndex, const Crypto::P256PublicKey & nocPublicKey)
139169
{
140170
VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && mPendingFabricIndex == fabricIndex, CHIP_ERROR_INVALID_FABRIC_INDEX);
@@ -154,6 +184,40 @@ CHIP_ERROR PSAOperationalKeystore::CommitOpKeypairForFabric(FabricIndex fabricIn
154184
return CHIP_NO_ERROR;
155185
}
156186

187+
CHIP_ERROR PSAOperationalKeystore::ExportOpKeypairForFabric(FabricIndex fabricIndex, Crypto::P256SerializedKeypair & outKeypair)
188+
{
189+
// Currently exporting the key is forbidden in PSAOperationalKeystore because the PSA_KEY_USAGE_EXPORT usage flag is not set, so
190+
// there is no need to compile the code for the device, but there should be an implementation for test purposes to verify if
191+
// the psa_export_key returns an error.
192+
#if CHIP_CONFIG_TEST
193+
VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
194+
VerifyOrReturnError(HasOpKeypairForFabric(fabricIndex), CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND);
195+
196+
size_t outSize = 0;
197+
psa_status_t status =
198+
psa_export_key(PersistentP256Keypair(fabricIndex).GetKeyId(), outKeypair.Bytes(), outKeypair.Capacity(), &outSize);
199+
200+
if (status == PSA_ERROR_BUFFER_TOO_SMALL)
201+
{
202+
return CHIP_ERROR_BUFFER_TOO_SMALL;
203+
}
204+
else if (status == PSA_ERROR_NOT_PERMITTED)
205+
{
206+
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
207+
}
208+
else if (status != PSA_SUCCESS)
209+
{
210+
return CHIP_ERROR_INTERNAL;
211+
}
212+
213+
outKeypair.SetLength(outSize);
214+
215+
return CHIP_NO_ERROR;
216+
#else
217+
return CHIP_ERROR_NOT_IMPLEMENTED;
218+
#endif
219+
}
220+
157221
CHIP_ERROR PSAOperationalKeystore::RemoveOpKeypairForFabric(FabricIndex fabricIndex)
158222
{
159223
VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
@@ -209,5 +273,31 @@ void PSAOperationalKeystore::ReleasePendingKeypair()
209273
mIsPendingKeypairActive = false;
210274
}
211275

276+
CHIP_ERROR PSAOperationalKeystore::MigrateOpKeypairForFabric(FabricIndex fabricIndex,
277+
OperationalKeystore & operationalKeystore) const
278+
{
279+
VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
280+
281+
P256SerializedKeypair serializedKeypair;
282+
283+
// Do not allow overwriting the existing key and just remove it from the previous Operational Keystore if needed.
284+
if (!HasOpKeypairForFabric(fabricIndex))
285+
{
286+
ReturnErrorOnFailure(operationalKeystore.ExportOpKeypairForFabric(fabricIndex, serializedKeypair));
287+
288+
PersistentP256Keypair keypair(fabricIndex);
289+
ReturnErrorOnFailure(keypair.Deserialize(serializedKeypair));
290+
291+
// Migrated key is not useful anymore, remove it from the previous keystore.
292+
ReturnErrorOnFailure(operationalKeystore.RemoveOpKeypairForFabric(fabricIndex));
293+
}
294+
else if (operationalKeystore.HasOpKeypairForFabric(fabricIndex))
295+
{
296+
ReturnErrorOnFailure(operationalKeystore.RemoveOpKeypairForFabric(fabricIndex));
297+
}
298+
299+
return CHIP_NO_ERROR;
300+
}
301+
212302
} // namespace Crypto
213303
} // namespace chip

src/crypto/PSAOperationalKeystore.h

+5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include <crypto/CHIPCryptoPALPSA.h>
2121
#include <crypto/OperationalKeystore.h>
22+
#include <lib/core/CHIPPersistentStorageDelegate.h>
2223

2324
namespace chip {
2425
namespace Crypto {
@@ -31,7 +32,9 @@ class PSAOperationalKeystore final : public OperationalKeystore
3132
CHIP_ERROR NewOpKeypairForFabric(FabricIndex fabricIndex, MutableByteSpan & outCertificateSigningRequest) override;
3233
CHIP_ERROR ActivateOpKeypairForFabric(FabricIndex fabricIndex, const Crypto::P256PublicKey & nocPublicKey) override;
3334
CHIP_ERROR CommitOpKeypairForFabric(FabricIndex fabricIndex) override;
35+
CHIP_ERROR ExportOpKeypairForFabric(FabricIndex fabricIndex, Crypto::P256SerializedKeypair & outKeypair) override;
3436
CHIP_ERROR RemoveOpKeypairForFabric(FabricIndex fabricIndex) override;
37+
CHIP_ERROR MigrateOpKeypairForFabric(FabricIndex fabricIndex, OperationalKeystore & operationalKeystore) const;
3538
void RevertPendingKeypair() override;
3639
CHIP_ERROR SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message,
3740
Crypto::P256ECDSASignature & outSignature) const override;
@@ -53,13 +56,15 @@ class PSAOperationalKeystore final : public OperationalKeystore
5356
bool Exists() const;
5457
CHIP_ERROR Generate();
5558
CHIP_ERROR Destroy();
59+
CHIP_ERROR Deserialize(P256SerializedKeypair & input);
5660
};
5761

5862
void ReleasePendingKeypair();
5963

6064
PersistentP256Keypair * mPendingKeypair = nullptr;
6165
FabricIndex mPendingFabricIndex = kUndefinedFabricIndex;
6266
bool mIsPendingKeypairActive = false;
67+
PersistentStorageDelegate * mStorage = nullptr;
6368
};
6469

6570
} // namespace Crypto

src/crypto/PersistentStorageOperationalKeystore.cpp

+95-47
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,54 @@ CHIP_ERROR StoreOperationalKey(FabricIndex fabricIndex, PersistentStorageDelegat
8686
return CHIP_NO_ERROR;
8787
}
8888

89+
CHIP_ERROR ExportStoredOpKey(FabricIndex fabricIndex, PersistentStorageDelegate * storage,
90+
Crypto::P256SerializedKeypair & serializedOpKey)
91+
{
92+
VerifyOrReturnError(storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
93+
VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
94+
95+
// Use a SensitiveDataBuffer to get RAII secret data clearing on scope exit.
96+
Crypto::SensitiveDataBuffer<OpKeyTLVMaxSize()> buf;
97+
98+
// Load up the operational key structure from storage
99+
uint16_t size = static_cast<uint16_t>(buf.Capacity());
100+
ReturnErrorOnFailure(
101+
storage->SyncGetKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName(), buf.Bytes(), size));
102+
103+
buf.SetLength(static_cast<size_t>(size));
104+
105+
// Read-out the operational key TLV entry.
106+
TLV::ContiguousBufferTLVReader reader;
107+
reader.Init(buf.Bytes(), buf.Length());
108+
109+
ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
110+
TLV::TLVType containerType;
111+
ReturnErrorOnFailure(reader.EnterContainer(containerType));
112+
113+
ReturnErrorOnFailure(reader.Next(kOpKeyVersionTag));
114+
uint16_t opKeyVersion;
115+
ReturnErrorOnFailure(reader.Get(opKeyVersion));
116+
VerifyOrReturnError(opKeyVersion == kOpKeyVersion, CHIP_ERROR_VERSION_MISMATCH);
117+
118+
ReturnErrorOnFailure(reader.Next(kOpKeyDataTag));
119+
{
120+
ByteSpan keyData;
121+
ReturnErrorOnFailure(reader.GetByteView(keyData));
122+
123+
// Unfortunately, we have to copy the data into a P256SerializedKeypair.
124+
VerifyOrReturnError(keyData.size() <= serializedOpKey.Capacity(), CHIP_ERROR_BUFFER_TOO_SMALL);
125+
126+
// Before doing anything with the key, validate format further.
127+
ReturnErrorOnFailure(reader.ExitContainer(containerType));
128+
ReturnErrorOnFailure(reader.VerifyEndOfContainer());
129+
130+
memcpy(serializedOpKey.Bytes(), keyData.data(), keyData.size());
131+
serializedOpKey.SetLength(keyData.size());
132+
}
133+
134+
return CHIP_NO_ERROR;
135+
}
136+
89137
/** WARNING: This can leave the operational key on the stack somewhere, since many of the platform
90138
* APIs use stack buffers and do not sanitize! This implementation is for example purposes
91139
* only of the API and it is recommended to avoid directly accessing raw private key bits
@@ -106,56 +154,17 @@ CHIP_ERROR SignWithStoredOpKey(FabricIndex fabricIndex, PersistentStorageDelegat
106154
}
107155

108156
// Scope 1: Load up the keypair data from storage
157+
P256SerializedKeypair serializedOpKey;
158+
CHIP_ERROR err = ExportStoredOpKey(fabricIndex, storage, serializedOpKey);
159+
if (CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND == err)
109160
{
110-
// Use a SensitiveDataBuffer to get RAII secret data clearing on scope exit.
111-
Crypto::SensitiveDataBuffer<OpKeyTLVMaxSize()> buf;
112-
113-
// Load up the operational key structure from storage
114-
uint16_t size = static_cast<uint16_t>(buf.Capacity());
115-
CHIP_ERROR err =
116-
storage->SyncGetKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName(), buf.Bytes(), size);
117-
if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
118-
{
119-
err = CHIP_ERROR_INVALID_FABRIC_INDEX;
120-
}
121-
ReturnErrorOnFailure(err);
122-
buf.SetLength(static_cast<size_t>(size));
123-
124-
// Read-out the operational key TLV entry.
125-
TLV::ContiguousBufferTLVReader reader;
126-
reader.Init(buf.Bytes(), buf.Length());
127-
128-
ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
129-
TLV::TLVType containerType;
130-
ReturnErrorOnFailure(reader.EnterContainer(containerType));
131-
132-
ReturnErrorOnFailure(reader.Next(kOpKeyVersionTag));
133-
uint16_t opKeyVersion;
134-
ReturnErrorOnFailure(reader.Get(opKeyVersion));
135-
VerifyOrReturnError(opKeyVersion == kOpKeyVersion, CHIP_ERROR_VERSION_MISMATCH);
136-
137-
ReturnErrorOnFailure(reader.Next(kOpKeyDataTag));
138-
{
139-
ByteSpan keyData;
140-
Crypto::P256SerializedKeypair serializedOpKey;
141-
ReturnErrorOnFailure(reader.GetByteView(keyData));
142-
143-
// Unfortunately, we have to copy the data into a P256SerializedKeypair.
144-
VerifyOrReturnError(keyData.size() <= serializedOpKey.Capacity(), CHIP_ERROR_BUFFER_TOO_SMALL);
145-
146-
// Before doing anything with the key, validate format further.
147-
ReturnErrorOnFailure(reader.ExitContainer(containerType));
148-
ReturnErrorOnFailure(reader.VerifyEndOfContainer());
149-
150-
memcpy(serializedOpKey.Bytes(), keyData.data(), keyData.size());
151-
serializedOpKey.SetLength(keyData.size());
152-
153-
// Load-up key material
154-
// WARNING: This makes use of the raw key bits
155-
ReturnErrorOnFailure(transientOperationalKeypair->Deserialize(serializedOpKey));
156-
}
161+
return CHIP_ERROR_INVALID_FABRIC_INDEX;
157162
}
158163

164+
// Load-up key material
165+
// WARNING: This makes use of the raw key bits
166+
ReturnErrorOnFailure(transientOperationalKeypair->Deserialize(serializedOpKey));
167+
159168
// Scope 2: Sign message with the keypair
160169
return transientOperationalKeypair->ECDSA_sign_msg(message.data(), message.size(), outSignature);
161170
}
@@ -251,6 +260,13 @@ CHIP_ERROR PersistentStorageOperationalKeystore::CommitOpKeypairForFabric(Fabric
251260
return CHIP_NO_ERROR;
252261
}
253262

263+
CHIP_ERROR PersistentStorageOperationalKeystore::ExportOpKeypairForFabric(FabricIndex fabricIndex,
264+
Crypto::P256SerializedKeypair & outKeypair)
265+
{
266+
VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
267+
return ExportStoredOpKey(fabricIndex, mStorage, outKeypair);
268+
}
269+
254270
CHIP_ERROR PersistentStorageOperationalKeystore::RemoveOpKeypairForFabric(FabricIndex fabricIndex)
255271
{
256272
VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
@@ -310,4 +326,36 @@ void PersistentStorageOperationalKeystore::ReleaseEphemeralKeypair(Crypto::P256K
310326
Platform::Delete<Crypto::P256Keypair>(keypair);
311327
}
312328

329+
CHIP_ERROR PersistentStorageOperationalKeystore::MigrateOpKeypairForFabric(FabricIndex fabricIndex,
330+
OperationalKeystore & operationalKeystore) const
331+
{
332+
VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
333+
VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
334+
335+
P256SerializedKeypair serializedKeypair;
336+
337+
// Do not allow overwriting the existing key and just remove it from the previous Operational Keystore if needed.
338+
if (!HasOpKeypairForFabric(fabricIndex))
339+
{
340+
ReturnErrorOnFailure(operationalKeystore.ExportOpKeypairForFabric(fabricIndex, serializedKeypair));
341+
342+
auto operationalKeypair = Platform::MakeUnique<P256Keypair>();
343+
if (!operationalKeypair)
344+
{
345+
return CHIP_ERROR_NO_MEMORY;
346+
}
347+
348+
ReturnErrorOnFailure(operationalKeypair->Deserialize(serializedKeypair));
349+
ReturnErrorOnFailure(StoreOperationalKey(fabricIndex, mStorage, operationalKeypair.get()));
350+
351+
ReturnErrorOnFailure(operationalKeystore.RemoveOpKeypairForFabric(fabricIndex));
352+
}
353+
else if (operationalKeystore.HasOpKeypairForFabric(fabricIndex))
354+
{
355+
ReturnErrorOnFailure(operationalKeystore.RemoveOpKeypairForFabric(fabricIndex));
356+
}
357+
358+
return CHIP_NO_ERROR;
359+
}
360+
313361
} // namespace chip

src/crypto/PersistentStorageOperationalKeystore.h

+2
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,14 @@ class PersistentStorageOperationalKeystore : public Crypto::OperationalKeystore
8181
CHIP_ERROR NewOpKeypairForFabric(FabricIndex fabricIndex, MutableByteSpan & outCertificateSigningRequest) override;
8282
CHIP_ERROR ActivateOpKeypairForFabric(FabricIndex fabricIndex, const Crypto::P256PublicKey & nocPublicKey) override;
8383
CHIP_ERROR CommitOpKeypairForFabric(FabricIndex fabricIndex) override;
84+
CHIP_ERROR ExportOpKeypairForFabric(FabricIndex fabricIndex, Crypto::P256SerializedKeypair & outKeypair) override;
8485
CHIP_ERROR RemoveOpKeypairForFabric(FabricIndex fabricIndex) override;
8586
void RevertPendingKeypair() override;
8687
CHIP_ERROR SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message,
8788
Crypto::P256ECDSASignature & outSignature) const override;
8889
Crypto::P256Keypair * AllocateEphemeralKeypairForCASE() override;
8990
void ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair) override;
91+
CHIP_ERROR MigrateOpKeypairForFabric(FabricIndex fabricIndex, OperationalKeystore & operationalKeystore) const override;
9092

9193
protected:
9294
void ResetPendingKey()

0 commit comments

Comments
 (0)