Skip to content

Commit 2338544

Browse files
PDC: Identifier calculation, Identity generation, and handling of compact-pdc-identity format (project-chip#30159)
* PDC: Add key id calculation for network identities * PDC: Add identity generation and parsing for the compact TLV format * PDC: Add support for identity generation to the chip-cert tool * Address review comments * Address further review comments * Prefix out parameters in new APIs Fixes project-chip#29856
1 parent d058b33 commit 2338544

11 files changed

+556
-205
lines changed

src/credentials/CHIPCert.cpp

+99-10
Original file line numberDiff line numberDiff line change
@@ -1095,12 +1095,19 @@ DLL_EXPORT CHIP_ERROR ChipEpochToASN1Time(uint32_t epochTime, chip::ASN1::ASN1Un
10951095
return CHIP_NO_ERROR;
10961096
}
10971097

1098+
static CHIP_ERROR ValidateCertificateType(const ChipCertificateData & certData, CertType expectedType)
1099+
{
1100+
CertType certType;
1101+
ReturnErrorOnFailure(certData.mSubjectDN.GetCertType(certType));
1102+
VerifyOrReturnError(certType == expectedType, CHIP_ERROR_WRONG_CERT_TYPE);
1103+
return CHIP_NO_ERROR;
1104+
}
1105+
10981106
CHIP_ERROR ValidateChipRCAC(const ByteSpan & rcac)
10991107
{
11001108
ChipCertificateSet certSet;
11011109
ChipCertificateData certData;
11021110
ValidationContext validContext;
1103-
CertType certType;
11041111

11051112
// Note that this function doesn't check RCAC NotBefore / NotAfter time validity.
11061113
// It is assumed that RCAC should be valid at the time of installation by definition.
@@ -1109,8 +1116,7 @@ CHIP_ERROR ValidateChipRCAC(const ByteSpan & rcac)
11091116

11101117
ReturnErrorOnFailure(certSet.LoadCert(rcac, CertDecodeFlags::kGenerateTBSHash));
11111118

1112-
ReturnErrorOnFailure(certData.mSubjectDN.GetCertType(certType));
1113-
VerifyOrReturnError(certType == CertType::kRoot, CHIP_ERROR_WRONG_CERT_TYPE);
1119+
ReturnErrorOnFailure(ValidateCertificateType(certData, CertType::kRoot));
11141120

11151121
VerifyOrReturnError(certData.mSubjectDN.IsEqual(certData.mIssuerDN), CHIP_ERROR_WRONG_CERT_TYPE);
11161122

@@ -1427,16 +1433,27 @@ CHIP_ERROR CertificateValidityPolicy::ApplyDefaultPolicy(const ChipCertificateDa
14271433
}
14281434
}
14291435

1430-
CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert)
1436+
void InitNetworkIdentitySubject(ChipDN & name)
14311437
{
1432-
ChipCertificateData certData;
1433-
ReturnErrorOnFailure(DecodeChipCert(cert, certData, CertDecodeFlags::kGenerateTBSHash));
1438+
name.Clear();
1439+
CHIP_ERROR err = name.AddAttribute_CommonName(kNetworkIdentityCN, /* not printable */ false);
1440+
VerifyOrDie(err == CHIP_NO_ERROR); // AddAttribute can't fail in this case
1441+
}
14341442

1435-
CertType certType;
1436-
ReturnErrorOnFailure(certData.mSubjectDN.GetCertType(certType));
1437-
VerifyOrReturnError(certType == CertType::kNetworkIdentity, CHIP_ERROR_WRONG_CERT_TYPE);
1443+
static CHIP_ERROR CalculateKeyIdentifierSha256(const P256PublicKeySpan & publicKey, MutableCertificateKeyId outKeyId)
1444+
{
1445+
uint8_t hash[kSHA256_Hash_Length];
1446+
static_assert(outKeyId.size() <= sizeof(hash)); // truncating 32 bytes down to 20
1447+
ReturnErrorOnFailure(Hash_SHA256(publicKey.data(), publicKey.size(), hash));
1448+
memcpy(outKeyId.data(), hash, outKeyId.size());
1449+
return CHIP_NO_ERROR;
1450+
}
1451+
1452+
static CHIP_ERROR ValidateChipNetworkIdentity(const ChipCertificateData & certData)
1453+
{
1454+
ReturnErrorOnFailure(ValidateCertificateType(certData, CertType::kNetworkIdentity));
14381455

1439-
VerifyOrReturnError(certData.mSerialNumber.data_equal(kNetworkIdentitySerialNumber), CHIP_ERROR_WRONG_CERT_TYPE);
1456+
VerifyOrReturnError(certData.mSerialNumber.data_equal(kNetworkIdentitySerialNumberBytes), CHIP_ERROR_WRONG_CERT_TYPE);
14401457
VerifyOrReturnError(certData.mNotBeforeTime == kNetworkIdentityNotBeforeTime, CHIP_ERROR_WRONG_CERT_TYPE);
14411458
VerifyOrReturnError(certData.mNotAfterTime == kNetworkIdentityNotAfterTime, CHIP_ERROR_WRONG_CERT_TYPE);
14421459
VerifyOrReturnError(certData.mIssuerDN.IsEqual(certData.mSubjectDN), CHIP_ERROR_WRONG_CERT_TYPE);
@@ -1455,5 +1472,77 @@ CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert)
14551472
return CHIP_NO_ERROR;
14561473
}
14571474

1475+
CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert)
1476+
{
1477+
ChipCertificateData certData;
1478+
ReturnErrorOnFailure(DecodeChipCert(cert, certData, CertDecodeFlags::kGenerateTBSHash));
1479+
ReturnErrorOnFailure(ValidateChipNetworkIdentity(certData));
1480+
return CHIP_NO_ERROR;
1481+
}
1482+
1483+
CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId outKeyId)
1484+
{
1485+
ChipCertificateData certData;
1486+
ReturnErrorOnFailure(DecodeChipCert(cert, certData, CertDecodeFlags::kGenerateTBSHash));
1487+
ReturnErrorOnFailure(ValidateChipNetworkIdentity(certData));
1488+
ReturnErrorOnFailure(CalculateKeyIdentifierSha256(certData.mPublicKey, outKeyId));
1489+
return CHIP_NO_ERROR;
1490+
}
1491+
1492+
CHIP_ERROR ExtractIdentifierFromChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId outKeyId)
1493+
{
1494+
ChipCertificateData certData;
1495+
ReturnErrorOnFailure(DecodeChipCert(cert, certData));
1496+
ReturnErrorOnFailure(ValidateCertificateType(certData, CertType::kNetworkIdentity));
1497+
ReturnErrorOnFailure(CalculateKeyIdentifierSha256(certData.mPublicKey, outKeyId));
1498+
return CHIP_NO_ERROR;
1499+
}
1500+
1501+
static CHIP_ERROR GenerateNetworkIdentitySignature(const P256Keypair & keypair, P256ECDSASignature & signature)
1502+
{
1503+
// Create a buffer and writer to capture the TBS (to-be-signed) portion of the certificate.
1504+
chip::Platform::ScopedMemoryBuffer<uint8_t> asn1TBSBuf;
1505+
VerifyOrReturnError(asn1TBSBuf.Alloc(kNetworkIdentityTBSLength), CHIP_ERROR_NO_MEMORY);
1506+
1507+
ASN1Writer writer;
1508+
writer.Init(asn1TBSBuf.Get(), kNetworkIdentityTBSLength);
1509+
1510+
// Generate the TBSCertificate and sign it
1511+
ReturnErrorOnFailure(EncodeNetworkIdentityTBSCert(keypair.Pubkey(), writer));
1512+
ReturnErrorOnFailure(keypair.ECDSA_sign_msg(asn1TBSBuf.Get(), writer.GetLengthWritten(), signature));
1513+
1514+
return CHIP_NO_ERROR;
1515+
}
1516+
1517+
static CHIP_ERROR EncodeCompactIdentityCert(TLVWriter & writer, Tag tag, const P256PublicKeySpan & publicKey,
1518+
const P256ECDSASignatureSpan & signature)
1519+
{
1520+
TLVType containerType;
1521+
ReturnErrorOnFailure(writer.StartContainer(tag, kTLVType_Structure, containerType));
1522+
ReturnErrorOnFailure(writer.Put(ContextTag(kTag_EllipticCurvePublicKey), publicKey));
1523+
ReturnErrorOnFailure(writer.Put(ContextTag(kTag_ECDSASignature), signature));
1524+
ReturnErrorOnFailure(writer.EndContainer(containerType));
1525+
return CHIP_NO_ERROR;
1526+
}
1527+
1528+
CHIP_ERROR NewChipNetworkIdentity(const Crypto::P256Keypair & keypair, MutableByteSpan & outCompactCert)
1529+
{
1530+
VerifyOrReturnError(!outCompactCert.empty(), CHIP_ERROR_INVALID_ARGUMENT);
1531+
VerifyOrReturnError(CanCastTo<uint32_t>(outCompactCert.size()), CHIP_ERROR_INVALID_ARGUMENT);
1532+
1533+
Crypto::P256ECDSASignature signature;
1534+
ReturnErrorOnFailure(GenerateNetworkIdentitySignature(keypair, signature));
1535+
1536+
TLVWriter writer;
1537+
writer.Init(outCompactCert);
1538+
1539+
P256PublicKeySpan publicKeySpan(keypair.Pubkey().ConstBytes());
1540+
P256ECDSASignatureSpan signatureSpan(signature.ConstBytes());
1541+
ReturnErrorOnFailure(EncodeCompactIdentityCert(writer, AnonymousTag(), publicKeySpan, signatureSpan));
1542+
1543+
outCompactCert.reduce_size(writer.GetLengthWritten());
1544+
return CHIP_NO_ERROR;
1545+
}
1546+
14581547
} // namespace Credentials
14591548
} // namespace chip

src/credentials/CHIPCert.h

+43-2
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ static constexpr uint16_t kX509NoWellDefinedExpirationDateYear = 9999;
5353
static constexpr uint32_t kMaxCHIPCertLength = 400;
5454
static constexpr uint32_t kMaxDERCertLength = 600;
5555

56-
// The decode buffer is used to reconstruct TBS section of X.509 certificate, which doesn't include signature.
57-
static constexpr uint32_t kMaxCHIPCertDecodeBufLength = kMaxDERCertLength - Crypto::kMax_ECDSA_Signature_Length_Der;
56+
// As per spec section 11.24 (Wi-Fi Authentication with Per-Device Credentials)
57+
inline constexpr uint32_t kMaxCHIPCompactNetworkIdentityLength = 137;
5858

5959
/** Data Element Tags for the CHIP Certificate
6060
*/
@@ -395,6 +395,16 @@ class ChipDN
395395
*/
396396
using CertificateKeyId = FixedByteSpan<kKeyIdentifierLength>;
397397

398+
/**
399+
* @brief A mutable `CertificateKeyId`.
400+
*/
401+
using MutableCertificateKeyId = FixedSpan<uint8_t, kKeyIdentifierLength>;
402+
403+
/**
404+
* @brief A storage type for `CertificateKeyId` and `MutableCertificateKeyId`.
405+
*/
406+
using CertificateKeyIdStorage = std::array<uint8_t, kKeyIdentifierLength>;
407+
398408
/**
399409
* @brief A data structure for holding a P256 ECDSA signature, without the ownership of it.
400410
*/
@@ -532,10 +542,13 @@ CHIP_ERROR ValidateChipRCAC(const ByteSpan & rcac);
532542

533543
/**
534544
* Validates a Network (Client) Identity in TLV-encoded form.
545+
* Accepts either a full certificate or the compact-pdc-identity format.
535546
*
536547
* This function parses the certificate, ensures the rigid fields have the values mandated by the
537548
* specification, and validates the certificate signature.
538549
*
550+
* @param cert The network identity certificate to validate.
551+
*
539552
* @return CHIP_NO_ERROR on success, CHIP_ERROR_WRONG_CERT_TYPE if the certificate does
540553
* not conform to the requirements for a Network Identity, CHIP_ERROR_INVALID_SIGNATURE
541554
* if the certificate has an invalid signature, or another CHIP_ERROR.
@@ -544,6 +557,14 @@ CHIP_ERROR ValidateChipRCAC(const ByteSpan & rcac);
544557
*/
545558
CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert);
546559

560+
/**
561+
* Convenience variant of `ValidateChipNetworkIdentity` that, upon successful validation, also
562+
* calculates the key identifier for the Network (Client) Identity.
563+
* @see ValidateChipNetworkIdentity
564+
* @see ExtractIdentifierFromChipNetworkIdentity
565+
*/
566+
CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId outKeyId);
567+
547568
struct FutureExtension
548569
{
549570
ByteSpan OID;
@@ -598,6 +619,17 @@ CHIP_ERROR NewICAX509Cert(const X509CertRequestParams & requestParams, const Cry
598619
CHIP_ERROR NewNodeOperationalX509Cert(const X509CertRequestParams & requestParams, const Crypto::P256PublicKey & subjectPubkey,
599620
const Crypto::P256Keypair & issuerKeypair, MutableByteSpan & x509Cert);
600621

622+
/**
623+
* @brief Generates a Network (Client) Identity certificate in TLV-encoded form.
624+
*
625+
* @param keypair The key pair underlying the identity.
626+
* @param outCompactCert Buffer to store the signed certificate in compact-pdc-identity TLV format.
627+
* Must be at least `kMaxCHIPCompactNetworkIdentityLength` bytes long.
628+
*
629+
* @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
630+
**/
631+
CHIP_ERROR NewChipNetworkIdentity(const Crypto::P256Keypair & keypair, MutableByteSpan & outCompactCert);
632+
601633
/**
602634
* @brief
603635
* Convert a certificate date/time (in the form of an ASN.1 universal time structure) into a CHIP Epoch time.
@@ -827,5 +859,14 @@ CHIP_ERROR ExtractSubjectDNFromChipCert(const ByteSpan & chipCert, ChipDN & dn);
827859
*/
828860
CHIP_ERROR ExtractSubjectDNFromX509Cert(const ByteSpan & x509Cert, ChipDN & dn);
829861

862+
/**
863+
* Extracts the key identifier from a Network (Client) Identity in TLV-encoded form.
864+
* Does NOT perform full validation of the identity certificate.
865+
*
866+
* @return CHIP_NO_ERROR on success, CHIP_ERROR_WRONG_CERT_TYPE if the certificate is
867+
* not a Network (Client) Identity, or another CHIP_ERROR if parsing fails.
868+
*/
869+
CHIP_ERROR ExtractIdentifierFromChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId outKeyId);
870+
830871
} // namespace Credentials
831872
} // namespace chip

src/credentials/CHIPCertToX509.cpp

+51-4
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
#include <inttypes.h>
3232
#include <stddef.h>
3333

34-
#include <credentials/CHIPCert.h>
34+
#include <credentials/CHIPCert_Internal.h>
3535
#include <lib/asn1/ASN1.h>
3636
#include <lib/asn1/ASN1Macros.h>
3737
#include <lib/core/CHIPCore.h>
@@ -482,6 +482,8 @@ static CHIP_ERROR DecodeConvertECDSASignature(TLVReader & reader, ASN1Writer & w
482482
* @param certData Structure containing data extracted from the TBS portion of the
483483
* CHIP certificate.
484484
*
485+
* Note: The reader must be positioned on the SerialNumber element.
486+
*
485487
* @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
486488
**/
487489
static CHIP_ERROR DecodeConvertTBSCert(TLVReader & reader, ASN1Writer & writer, ChipCertificateData & certData)
@@ -502,7 +504,7 @@ static CHIP_ERROR DecodeConvertTBSCert(TLVReader & reader, ASN1Writer & writer,
502504

503505
// serialNumber CertificateSerialNumber
504506
// CertificateSerialNumber ::= INTEGER
505-
ReturnErrorOnFailure(reader.Next(kTLVType_ByteString, ContextTag(kTag_SerialNumber)));
507+
ReturnErrorOnFailure(reader.Expect(kTLVType_ByteString, ContextTag(kTag_SerialNumber)));
506508
ReturnErrorOnFailure(reader.Get(certData.mSerialNumber));
507509
ReturnErrorOnFailure(writer.PutValue(kASN1TagClass_Universal, kASN1UniversalTag_Integer, false,
508510
certData.mSerialNumber.data(), static_cast<uint16_t>(certData.mSerialNumber.size())));
@@ -543,6 +545,41 @@ static CHIP_ERROR DecodeConvertTBSCert(TLVReader & reader, ASN1Writer & writer,
543545
return err;
544546
}
545547

548+
/**
549+
* Variant of DecodeConvertTBSCert that handles reading a compact-pdc-identity
550+
* where only the subject public key is actually encoded. All other values are
551+
* populated / written as the well-known values mandated by the specification.
552+
*
553+
* Note: The reader must be positioned on the EllipticCurvePublicKey element.
554+
*/
555+
static CHIP_ERROR DecodeConvertTBSCertCompactIdentity(TLVReader & reader, ASN1Writer & writer, ChipCertificateData & certData)
556+
{
557+
// Decode the public key, everything else is rigid
558+
ReturnErrorOnFailure(reader.Expect(kTLVType_ByteString, ContextTag(kTag_EllipticCurvePublicKey)));
559+
ReturnErrorOnFailure(reader.Get(certData.mPublicKey));
560+
561+
// Populate rigid ChipCertificateData fields
562+
certData.mSerialNumber = kNetworkIdentitySerialNumberBytes;
563+
certData.mSigAlgoOID = kOID_SigAlgo_ECDSAWithSHA256;
564+
InitNetworkIdentitySubject(certData.mIssuerDN);
565+
certData.mNotBeforeTime = kNetworkIdentityNotBeforeTime;
566+
certData.mNotAfterTime = kNetworkIdentityNotAfterTime;
567+
InitNetworkIdentitySubject(certData.mSubjectDN);
568+
certData.mPubKeyAlgoOID = kOID_PubKeyAlgo_ECPublicKey;
569+
certData.mPubKeyCurveOID = kOID_EllipticCurve_prime256v1;
570+
certData.mCertFlags.Set(CertFlags::kExtPresent_BasicConstraints);
571+
certData.mCertFlags.Set(CertFlags::kExtPresent_KeyUsage);
572+
certData.mKeyUsageFlags = kNetworkIdentityKeyUsage;
573+
certData.mCertFlags.Set(CertFlags::kExtPresent_ExtendedKeyUsage);
574+
certData.mKeyPurposeFlags = kNetworkIdentityKeyPurpose;
575+
576+
if (!writer.IsNullWriter())
577+
{
578+
ReturnErrorOnFailure(EncodeNetworkIdentityTBSCert(certData.mPublicKey, writer));
579+
}
580+
return CHIP_NO_ERROR;
581+
}
582+
546583
/**
547584
* Decode a CHIP TLV certificate and convert it to X.509 DER form.
548585
*
@@ -570,7 +607,17 @@ static CHIP_ERROR DecodeConvertCert(TLVReader & reader, ASN1Writer & writer, ASN
570607
ASN1_START_SEQUENCE
571608
{
572609
// tbsCertificate TBSCertificate,
573-
ReturnErrorOnFailure(DecodeConvertTBSCert(reader, tbsWriter, certData));
610+
reader.Next();
611+
if (reader.GetTag() == ContextTag(kTag_EllipticCurvePublicKey))
612+
{
613+
// If the struct starts with the ec-pub-key we're dealing with a
614+
// Network (Client) Identity in compact-pdc-identity format.
615+
DecodeConvertTBSCertCompactIdentity(reader, tbsWriter, certData);
616+
}
617+
else
618+
{
619+
ReturnErrorOnFailure(DecodeConvertTBSCert(reader, tbsWriter, certData));
620+
}
574621

575622
// signatureAlgorithm AlgorithmIdentifier
576623
// AlgorithmIdentifier ::= SEQUENCE
@@ -640,7 +687,7 @@ CHIP_ERROR DecodeChipCert(TLVReader & reader, ChipCertificateData & certData, Bi
640687

641688
// Hash the encoded TBS certificate. Only SHA256 is supported.
642689
VerifyOrReturnError(certData.mSigAlgoOID == kOID_SigAlgo_ECDSAWithSHA256, CHIP_ERROR_UNSUPPORTED_SIGNATURE_TYPE);
643-
chip::Crypto::Hash_SHA256(asn1TBSBuf.Get(), tbsWriter.GetLengthWritten(), certData.mTBSHash);
690+
ReturnErrorOnFailure(Hash_SHA256(asn1TBSBuf.Get(), tbsWriter.GetLengthWritten(), certData.mTBSHash));
644691
certData.mCertFlags.Set(CertFlags::kTBSHashPresent);
645692
}
646693
else

src/credentials/CHIPCert_Internal.h

+15-2
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,17 @@
2323
namespace chip {
2424
namespace Credentials {
2525

26+
// The decode buffer is used to reconstruct TBS section of X.509 certificate, which doesn't include signature.
27+
inline constexpr size_t kMaxCHIPCertDecodeBufLength = kMaxDERCertLength - Crypto::kMax_ECDSA_Signature_Length_Der;
28+
29+
// The TBSCerticate of a Network (Client) Identity has a fixed (smaller) size.
30+
inline constexpr size_t kNetworkIdentityTBSLength = 244;
31+
2632
// Constants for Network (Client) Identities as per section 11.24 (Wi-Fi
2733
// Authentication with Per-Device Credentials) of the Matter spec.
28-
inline constexpr CharSpan kNetworkIdentityCN = "*"_span;
29-
inline constexpr ByteSpan kNetworkIdentitySerialNumber = ByteSpan((uint8_t[1]){ 1 });
34+
inline constexpr CharSpan kNetworkIdentityCN = "*"_span;
35+
inline constexpr uint8_t kNetworkIdentitySerialNumber = 1;
36+
inline constexpr ByteSpan kNetworkIdentitySerialNumberBytes = ByteSpan((uint8_t[1]){ kNetworkIdentitySerialNumber });
3037

3138
inline constexpr uint32_t kNetworkIdentityNotBeforeTime = 1;
3239
inline constexpr uint32_t kNetworkIdentityNotAfterTime = kNullCertTime;
@@ -35,5 +42,11 @@ inline constexpr auto kNetworkIdentityKeyUsage = BitFlags<KeyUsageFlags>(KeyUsag
3542
inline constexpr auto kNetworkIdentityKeyPurpose =
3643
BitFlags<KeyPurposeFlags>(KeyPurposeFlags::kClientAuth, KeyPurposeFlags::kServerAuth);
3744

45+
// Initializes a ChipDN as CN=kNetworkIdentityCN
46+
void InitNetworkIdentitySubject(ChipDN & name);
47+
48+
// Emits a X.509 TBSCertificate for a Network (Client) Identity based on the specified key.
49+
CHIP_ERROR EncodeNetworkIdentityTBSCert(const Crypto::P256PublicKey & pubkey, ASN1::ASN1Writer & writer);
50+
3851
} // namespace Credentials
3952
} // namespace chip

0 commit comments

Comments
 (0)