Skip to content

Commit c792a74

Browse files
Merge branch 'master' into TC-SC-4.3-python-test
2 parents 1980b73 + 61724ee commit c792a74

22 files changed

+1057
-344
lines changed

src/credentials/CHIPCert.cpp

+42
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,48 @@ CHIP_ERROR ConvertECDSASignatureRawToDER(P256ECDSASignatureSpan rawSig, ASN1Writ
12031203
return err;
12041204
}
12051205

1206+
CHIP_ERROR ConvertECDSAKeypairRawToDER(const P256SerializedKeypair & rawKeypair, MutableByteSpan & outDerKeypair)
1207+
{
1208+
CHIP_ERROR err = CHIP_NO_ERROR;
1209+
1210+
// The raw key pair contains the public key followed by the private key
1211+
VerifyOrReturnError(rawKeypair.Length() == kP256_PublicKey_Length + kP256_PrivateKey_Length, CHIP_ERROR_INVALID_ARGUMENT);
1212+
FixedByteSpan<kP256_PublicKey_Length> publicKey(rawKeypair.ConstBytes());
1213+
FixedByteSpan<kP256_PrivateKey_Length> privateKey(rawKeypair.ConstBytes() + kP256_PublicKey_Length);
1214+
1215+
ASN1Writer writer;
1216+
writer.Init(outDerKeypair);
1217+
1218+
// ECPrivateKey ::= SEQUENCE
1219+
ASN1_START_SEQUENCE
1220+
{
1221+
// version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1)
1222+
ASN1_ENCODE_INTEGER(1);
1223+
1224+
// privateKey OCTET STRING
1225+
ASN1_ENCODE_OCTET_STRING(privateKey.data(), privateKey.size());
1226+
1227+
// parameters [0] ECParameters {{ NamedCurve }} OPTIONAL
1228+
ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0);
1229+
{
1230+
ASN1_ENCODE_OBJECT_ID(kOID_EllipticCurve_prime256v1);
1231+
}
1232+
ASN1_END_CONSTRUCTED;
1233+
1234+
// publicKey [1] BIT STRING OPTIONAL
1235+
ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 1);
1236+
{
1237+
ReturnErrorOnFailure(writer.PutBitString(0, publicKey.data(), publicKey.size()));
1238+
}
1239+
ASN1_END_CONSTRUCTED;
1240+
}
1241+
ASN1_END_SEQUENCE;
1242+
1243+
outDerKeypair.reduce_size(writer.GetLengthWritten());
1244+
exit:
1245+
return err;
1246+
}
1247+
12061248
CHIP_ERROR ExtractNodeIdFabricIdFromOpCert(const ChipCertificateData & opcert, NodeId * outNodeId, FabricId * outFabricId)
12071249
{
12081250
// Since we assume the cert is pre-validated, we are going to assume that

src/credentials/CHIPCert.h

+14
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ static constexpr uint32_t kMaxDERCertLength = 600;
5757
// As per spec section 11.24 (Wi-Fi Authentication with Per-Device Credentials)
5858
inline constexpr uint32_t kMaxCHIPCompactNetworkIdentityLength = 137;
5959

60+
// Length of a ASN.1 DER encoded ECPrivateKey structure (RFC 5915) for a P256 key pair.
61+
inline constexpr uint32_t kP256ECPrivateKeyDERLength = 121;
62+
6063
/** Data Element Tags for the CHIP Certificate
6164
*/
6265
enum
@@ -730,6 +733,17 @@ CHIP_ERROR ConvertECDSASignatureRawToDER(P256ECDSASignatureSpan rawSig, ASN1::AS
730733
*/
731734
CHIP_ERROR ConvertECDSASignatureDERToRaw(ASN1::ASN1Reader & reader, chip::TLV::TLVWriter & writer, uint64_t tag);
732735

736+
/**
737+
* @brief Convert a raw ECDSA P256 key pair to an ASN.1 DER encoded ECPrivateKey structure (RFC 5915).
738+
*
739+
* @param rawKeypair The raw P256 key pair.
740+
* @param outDerKeypair Output buffer to receive the ASN.1 DER encoded key pair.
741+
* Must have a capacity of at least `kP256ECPrivateKeyDERLength` bytes.
742+
*
743+
* @retval #CHIP_NO_ERROR If the key pair was successfully converted, or a CHIP_ERROR otherwise.
744+
*/
745+
CHIP_ERROR ConvertECDSAKeypairRawToDER(const Crypto::P256SerializedKeypair & rawKeypair, MutableByteSpan & outDerKeypair);
746+
733747
/**
734748
* Extract the Fabric ID from an operational certificate that has already been
735749
* parsed.

src/credentials/tests/CHIPCert_test_vectors.cpp

+86-200
Large diffs are not rendered by default.

src/credentials/tests/CHIPCert_test_vectors.h

+4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include <credentials/CHIPCert.h>
3030
#include <credentials/CHIPCertificateSet.h>
31+
#include <crypto/CHIPCryptoPAL.h>
3132
#include <lib/support/CodeUtils.h>
3233

3334
namespace chip {
@@ -72,6 +73,8 @@ enum class TestCertLoadFlags : uint8_t
7273
extern CHIP_ERROR GetTestCert(TestCert certType, BitFlags<TestCertLoadFlags> certLoadFlags, ByteSpan & cert);
7374
extern const char * GetTestCertName(TestCert certType);
7475
extern CHIP_ERROR GetTestCertPubkey(TestCert certType, ByteSpan & pubkey);
76+
extern CHIP_ERROR GetTestCertPrivkey(TestCert certType, ByteSpan & privkey);
77+
extern CHIP_ERROR GetTestCertKeypair(TestCert certType, Crypto::P256SerializedKeypair & keypair);
7578
extern CHIP_ERROR GetTestCertSKID(TestCert certType, ByteSpan & skid);
7679
extern CHIP_ERROR GetTestCertAKID(TestCert certType, ByteSpan & akid);
7780

@@ -213,6 +216,7 @@ extern const ByteSpan sTestCert_PDCID01_PrivateKey;
213216
extern const ByteSpan sTestCert_PDCID01_SubjectKeyId; // empty
214217
extern const ByteSpan sTestCert_PDCID01_AuthorityKeyId; // empty
215218
extern const ByteSpan sTestCert_PDCID01_KeyId;
219+
extern const ByteSpan sTestCert_PDCID01_KeypairDER;
216220

217221
} // namespace TestCerts
218222
} // namespace chip

src/credentials/tests/TestChipCert.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <lib/core/TLV.h>
3434
#include <lib/support/CHIPMem.h>
3535
#include <lib/support/CodeUtils.h>
36+
#include <lib/support/UnitTestExtendedAssertions.h>
3637
#include <lib/support/UnitTestRegistration.h>
3738

3839
#include <nlunit-test.h>
@@ -2191,6 +2192,20 @@ static void TestChipCert_PDCIdentityGeneration(nlTestSuite * inSuite, void * inC
21912192
NL_TEST_ASSERT(inSuite, ValidateChipNetworkIdentity(tlvCert) == CHIP_NO_ERROR);
21922193
}
21932194

2195+
static void TestChipCert_KeypairConversion(nlTestSuite * inSuite, void * inContext)
2196+
{
2197+
P256SerializedKeypair keypair;
2198+
NL_TEST_ASSERT_SUCCESS(inSuite, GetTestCertKeypair(kPDCID01, keypair));
2199+
2200+
uint8_t buffer[kP256ECPrivateKeyDERLength];
2201+
MutableByteSpan keypairDer(buffer);
2202+
NL_TEST_ASSERT_SUCCESS(inSuite, ConvertECDSAKeypairRawToDER(keypair, keypairDer));
2203+
2204+
// Technically the curve name and public key are optional in the DER format,
2205+
// but both our code and standard tools include them, so we can just compare.
2206+
NL_TEST_ASSERT(inSuite, keypairDer.data_equal(sTestCert_PDCID01_KeypairDER));
2207+
}
2208+
21942209
/**
21952210
* Set up the test suite.
21962211
*/
@@ -2251,6 +2266,7 @@ static const nlTest sTests[] = {
22512266
NL_TEST_DEF("Test extracting PublicKey and SKID from chip certificate", TestChipCert_ExtractPublicKeyAndSKID),
22522267
NL_TEST_DEF("Test PDC Identity Validation", TestChipCert_PDCIdentityValidation),
22532268
NL_TEST_DEF("Test PDC Identity Generation", TestChipCert_PDCIdentityGeneration),
2269+
NL_TEST_DEF("Test keypair conversion", TestChipCert_KeypairConversion),
22542270
NL_TEST_SENTINEL()
22552271
};
22562272
// clang-format on

src/crypto/CHIPCryptoPAL.h

+3
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,9 @@ struct alignas(size_t) P256KeypairContext
484484
uint8_t mBytes[kMAX_P256Keypair_Context_Size];
485485
};
486486

487+
/**
488+
* A serialized P256 key pair is the concatenation of the public and private keys, in that order.
489+
*/
487490
using P256SerializedKeypair = SensitiveDataBuffer<kP256_PublicKey_Length + kP256_PrivateKey_Length>;
488491

489492
class P256KeypairBase : public ECPKeypair<P256PublicKey, P256ECDHDerivedSecret, P256ECDSASignature>

src/crypto/OperationalKeystore.h

+40
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,46 @@ 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 other CHIP_ERROR value on internal storage or crypto engine errors.
156+
*/
157+
virtual CHIP_ERROR MigrateOpKeypairForFabric(FabricIndex fabricIndex, OperationalKeystore & operationalKeystore) const
158+
{
159+
return CHIP_ERROR_NOT_IMPLEMENTED;
160+
};
161+
122162
/**
123163
* @brief Permanently remove the keypair associated with a fabric
124164
*

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

0 commit comments

Comments
 (0)