From dfb501c7e8be7cdafc1b4dcc268ccfa4dc0e73a0 Mon Sep 17 00:00:00 2001 From: Karsten Sperling Date: Thu, 2 Nov 2023 16:05:10 +1300 Subject: [PATCH 1/6] PDC: Add key id calculation for network identities --- src/credentials/CHIPCert.cpp | 53 +++++++++++++++---- src/credentials/CHIPCert.h | 29 ++++++++++ .../tests/CHIPCert_test_vectors.cpp | 14 +++++ src/credentials/tests/CHIPCert_test_vectors.h | 6 ++- src/credentials/tests/TestChipCert.cpp | 14 +++++ 5 files changed, 105 insertions(+), 11 deletions(-) diff --git a/src/credentials/CHIPCert.cpp b/src/credentials/CHIPCert.cpp index 93f924f42051a7..d0c5d56d5a48b1 100644 --- a/src/credentials/CHIPCert.cpp +++ b/src/credentials/CHIPCert.cpp @@ -1095,12 +1095,19 @@ DLL_EXPORT CHIP_ERROR ChipEpochToASN1Time(uint32_t epochTime, chip::ASN1::ASN1Un return CHIP_NO_ERROR; } +static CHIP_ERROR ValidateCertificateType(ChipCertificateData const & certData, CertType expectedType) +{ + CertType certType; + ReturnErrorOnFailure(certData.mSubjectDN.GetCertType(certType)); + VerifyOrReturnError(certType == expectedType, CHIP_ERROR_WRONG_CERT_TYPE); + return CHIP_NO_ERROR; +} + CHIP_ERROR ValidateChipRCAC(const ByteSpan & rcac) { ChipCertificateSet certSet; ChipCertificateData certData; ValidationContext validContext; - CertType certType; // Note that this function doesn't check RCAC NotBefore / NotAfter time validity. // 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) ReturnErrorOnFailure(certSet.LoadCert(rcac, CertDecodeFlags::kGenerateTBSHash)); - ReturnErrorOnFailure(certData.mSubjectDN.GetCertType(certType)); - VerifyOrReturnError(certType == CertType::kRoot, CHIP_ERROR_WRONG_CERT_TYPE); + ReturnErrorOnFailure(ValidateCertificateType(certData, CertType::kRoot)); VerifyOrReturnError(certData.mSubjectDN.IsEqual(certData.mIssuerDN), CHIP_ERROR_WRONG_CERT_TYPE); @@ -1427,14 +1433,17 @@ CHIP_ERROR CertificateValidityPolicy::ApplyDefaultPolicy(const ChipCertificateDa } } -CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert) +static void CalculateKeyIdentifierSha256(const P256PublicKeySpan & publicKey, MutableCertificateKeyId keyId) { - ChipCertificateData certData; - ReturnErrorOnFailure(DecodeChipCert(cert, certData, CertDecodeFlags::kGenerateTBSHash)); + uint8_t hash[kSHA256_Hash_Length]; + static_assert(keyId.size() <= sizeof(hash)); // truncating 32 bytes down to 20 + chip::Crypto::Hash_SHA256(publicKey.data(), publicKey.size(), hash); + memcpy(keyId.data(), hash, keyId.size()); +} - CertType certType; - ReturnErrorOnFailure(certData.mSubjectDN.GetCertType(certType)); - VerifyOrReturnError(certType == CertType::kNetworkIdentity, CHIP_ERROR_WRONG_CERT_TYPE); +static CHIP_ERROR ValidateChipNetworkIdentity(const ChipCertificateData & certData) +{ + ReturnErrorOnFailure(ValidateCertificateType(certData, CertType::kNetworkIdentity)); VerifyOrReturnError(certData.mSerialNumber.data_equal(kNetworkIdentitySerialNumber), CHIP_ERROR_WRONG_CERT_TYPE); VerifyOrReturnError(certData.mNotBeforeTime == kNetworkIdentityNotBeforeTime, CHIP_ERROR_WRONG_CERT_TYPE); @@ -1455,5 +1464,31 @@ CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert) return CHIP_NO_ERROR; } +CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert) +{ + ChipCertificateData certData; + ReturnErrorOnFailure(DecodeChipCert(cert, certData, CertDecodeFlags::kGenerateTBSHash)); + ReturnErrorOnFailure(ValidateChipNetworkIdentity(certData)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId keyId) +{ + ChipCertificateData certData; + ReturnErrorOnFailure(DecodeChipCert(cert, certData, CertDecodeFlags::kGenerateTBSHash)); + ReturnErrorOnFailure(ValidateChipNetworkIdentity(certData)); + CalculateKeyIdentifierSha256(certData.mPublicKey, keyId); + return CHIP_NO_ERROR; +} + +CHIP_ERROR ExtractIdentifierFromChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId keyId) +{ + ChipCertificateData certData; + ReturnErrorOnFailure(DecodeChipCert(cert, certData)); + ReturnErrorOnFailure(ValidateCertificateType(certData, CertType::kNetworkIdentity)); + CalculateKeyIdentifierSha256(certData.mPublicKey, keyId); + return CHIP_NO_ERROR; +} + } // namespace Credentials } // namespace chip diff --git a/src/credentials/CHIPCert.h b/src/credentials/CHIPCert.h index af2c5c1916de4b..ecf5f9f041d984 100644 --- a/src/credentials/CHIPCert.h +++ b/src/credentials/CHIPCert.h @@ -395,6 +395,16 @@ class ChipDN */ using CertificateKeyId = FixedByteSpan; +/** + * @brief A mutable `CertificateKeyId`. + */ +using MutableCertificateKeyId = FixedSpan; + +/** + * @brief A storage type for `CertificateKeyId` and `MutableCertificateKeyId`. + */ +using CertificateKeyIdStorage = std::array; + /** * @brief A data structure for holding a P256 ECDSA signature, without the ownership of it. */ @@ -536,6 +546,8 @@ CHIP_ERROR ValidateChipRCAC(const ByteSpan & rcac); * This function parses the certificate, ensures the rigid fields have the values mandated by the * specification, and validates the certificate signature. * + * @param cert The network identity certificate to validate. + * * @return CHIP_NO_ERROR on success, CHIP_ERROR_WRONG_CERT_TYPE if the certificate does * not conform to the requirements for a Network Identity, CHIP_ERROR_INVALID_SIGNATURE * if the certificate has an invalid signature, or another CHIP_ERROR. @@ -544,6 +556,14 @@ CHIP_ERROR ValidateChipRCAC(const ByteSpan & rcac); */ CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert); +/** + * Convenience variant of `ValidateChipNetworkIdentity` that, upon successful validation, also + * calculates the key identifier for the Network (Client) Identity. + * @see ValidateChipNetworkIdentity + * @see ExtractIdentifierFromChipNetworkIdentity + */ +CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId keyId); + struct FutureExtension { ByteSpan OID; @@ -827,5 +847,14 @@ CHIP_ERROR ExtractSubjectDNFromChipCert(const ByteSpan & chipCert, ChipDN & dn); */ CHIP_ERROR ExtractSubjectDNFromX509Cert(const ByteSpan & x509Cert, ChipDN & dn); +/** + * Extracts the key identifier from a Network (Client) Identity in TLV-encoded form. + * Does NOT perform full validation of the identity certificate. + * + * @return CHIP_NO_ERROR on success, CHIP_ERROR_WRONG_CERT_TYPE if the certificate is + * not a Network (Client) Identity, or another CHIP_ERROR if parsing fails. + */ +CHIP_ERROR ExtractIdentifierFromChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId keyId); + } // namespace Credentials } // namespace chip diff --git a/src/credentials/tests/CHIPCert_test_vectors.cpp b/src/credentials/tests/CHIPCert_test_vectors.cpp index 4d457551110e04..90de7a9a8c722f 100644 --- a/src/credentials/tests/CHIPCert_test_vectors.cpp +++ b/src/credentials/tests/CHIPCert_test_vectors.cpp @@ -2528,6 +2528,16 @@ extern constexpr ByteSpan sTestCert_PDCID01_Chip((const uint8_t[]){ 0x2f, 0x91, 0x93, 0xfd, 0x2c, 0xbc, 0x4e, 0x23, 0x50, 0x0a, 0x31, 0x7d, 0xa4, 0xa0, 0x79, 0xb2, 0xdb, 0x69, 0x42, 0x18, }); +extern constexpr ByteSpan sTestCert_PDCID01_ChipCompact((const uint8_t[]){ + 0x15, 0x30, 0x09, 0x41, 0x04, 0x39, 0xcc, 0xd4, 0xac, 0x7e, 0x59, 0x68, 0xbd, 0xf1, 0xb0, 0x80, 0x11, 0x1d, 0x92, 0x53, + 0x64, 0x94, 0xfc, 0x51, 0x62, 0x4c, 0x70, 0xaa, 0x6d, 0x73, 0x08, 0xda, 0xed, 0xf3, 0xa1, 0x5e, 0x38, 0x69, 0x17, 0x7b, + 0x1b, 0xf3, 0xd0, 0x90, 0x47, 0xeb, 0xf0, 0x6b, 0xe8, 0xdd, 0x17, 0xbe, 0x23, 0xf2, 0xfb, 0x3d, 0x63, 0x90, 0xc6, 0xcf, + 0x82, 0x80, 0x2f, 0x62, 0xd0, 0x53, 0x62, 0xc0, 0x08, 0x30, 0x0b, 0x40, 0xe9, 0x43, 0xb6, 0x16, 0xf3, 0x1a, 0xf3, 0x81, + 0x52, 0xe7, 0x37, 0x41, 0xf7, 0x47, 0x28, 0xdb, 0x7d, 0x56, 0x87, 0xb5, 0x94, 0xc0, 0x2e, 0x0f, 0x5f, 0xee, 0x3a, 0x60, + 0xbf, 0x36, 0x44, 0xa9, 0x1c, 0x85, 0x2c, 0xfd, 0x6b, 0x15, 0xd8, 0xf8, 0xfe, 0x19, 0x93, 0x4d, 0xaf, 0x2f, 0x91, 0x93, + 0xfd, 0x2c, 0xbc, 0x4e, 0x23, 0x50, 0x0a, 0x31, 0x7d, 0xa4, 0xa0, 0x79, 0xb2, 0xdb, 0x69, 0x42, 0x18, +}); + extern constexpr ByteSpan sTestCert_PDCID01_DER((const uint8_t[]){ 0x30, 0x82, 0x01, 0x4a, 0x30, 0x81, 0xf1, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x0c, 0x31, 0x0a, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x2a, 0x30, @@ -2563,5 +2573,9 @@ extern constexpr ByteSpan sTestCert_PDCID01_SubjectKeyId{}; extern constexpr ByteSpan sTestCert_PDCID01_AuthorityKeyId{}; +extern constexpr ByteSpan sTestCert_PDCID01_KeyId((const uint8_t[]){ + 0x3a, 0x0e, 0x71, 0xe2, 0x09, 0x9a, 0x49, 0xda, 0xc9, 0x74, 0xfe, 0xd0, 0x5e, 0xa5, 0x3e, 0xba, 0xce, 0x29, 0x33, 0x90, +}); + } // namespace TestCerts } // namespace chip diff --git a/src/credentials/tests/CHIPCert_test_vectors.h b/src/credentials/tests/CHIPCert_test_vectors.h index d14cad56ffcab0..98f600a9384611 100644 --- a/src/credentials/tests/CHIPCert_test_vectors.h +++ b/src/credentials/tests/CHIPCert_test_vectors.h @@ -206,11 +206,13 @@ extern const ByteSpan sTestCert_Node02_08_SubjectKeyId; extern const ByteSpan sTestCert_Node02_08_AuthorityKeyId; extern const ByteSpan sTestCert_PDCID01_Chip; +extern const ByteSpan sTestCert_PDCID01_ChipCompact; extern const ByteSpan sTestCert_PDCID01_DER; extern const ByteSpan sTestCert_PDCID01_PublicKey; extern const ByteSpan sTestCert_PDCID01_PrivateKey; -extern const ByteSpan sTestCert_PDCID01_SubjectKeyId; -extern const ByteSpan sTestCert_PDCID01_AuthorityKeyId; +extern const ByteSpan sTestCert_PDCID01_SubjectKeyId; // empty +extern const ByteSpan sTestCert_PDCID01_AuthorityKeyId; // empty +extern const ByteSpan sTestCert_PDCID01_KeyId; } // namespace TestCerts } // namespace chip diff --git a/src/credentials/tests/TestChipCert.cpp b/src/credentials/tests/TestChipCert.cpp index d16cd7efc26231..3e23f022387d0b 100644 --- a/src/credentials/tests/TestChipCert.cpp +++ b/src/credentials/tests/TestChipCert.cpp @@ -2150,9 +2150,23 @@ static void TestChipCert_ExtractPublicKeyAndSKID(nlTestSuite * inSuite, void * i static void TestChipCert_PDCIdentityValidation(nlTestSuite * inSuite, void * inContext) { CHIP_ERROR err; + CertificateKeyIdStorage keyId; + // Validate only err = ValidateChipNetworkIdentity(sTestCert_PDCID01_Chip); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + // Validate and calculate identifier + keyId.fill(0xaa); + err = ValidateChipNetworkIdentity(sTestCert_PDCID01_Chip, keyId); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, CertificateKeyId(keyId).data_equal(sTestCert_PDCID01_KeyId)); + + // Extract identifier only + keyId.fill(0xaa); + err = ExtractIdentifierFromChipNetworkIdentity(sTestCert_PDCID01_Chip, keyId); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, CertificateKeyId(keyId).data_equal(sTestCert_PDCID01_KeyId)); } /** From 0550db9ce2355d802d6d6f6568916f7c40f88a8e Mon Sep 17 00:00:00 2001 From: Karsten Sperling Date: Thu, 2 Nov 2023 17:58:56 +1300 Subject: [PATCH 2/6] PDC: Add identity generation and parsing for the compact TLV format --- src/credentials/CHIPCert.cpp | 56 +++++++++++++++++++++- src/credentials/CHIPCert.h | 16 ++++++- src/credentials/CHIPCertToX509.cpp | 53 +++++++++++++++++++-- src/credentials/CHIPCert_Internal.h | 17 ++++++- src/credentials/GenerateChipX509Cert.cpp | 59 ++++++++++++++++++++++-- src/credentials/tests/TestChipCert.cpp | 51 ++++++++++++++------ 6 files changed, 227 insertions(+), 25 deletions(-) diff --git a/src/credentials/CHIPCert.cpp b/src/credentials/CHIPCert.cpp index d0c5d56d5a48b1..46451b45b34647 100644 --- a/src/credentials/CHIPCert.cpp +++ b/src/credentials/CHIPCert.cpp @@ -1433,6 +1433,13 @@ CHIP_ERROR CertificateValidityPolicy::ApplyDefaultPolicy(const ChipCertificateDa } } +void InitNetworkIdentitySubject(ChipDN & name) +{ + name.Clear(); + ChipError err = name.AddAttribute_CommonName(kNetworkIdentityCN, /* not printable */ false); + VerifyOrDie(err == CHIP_NO_ERROR); // AddAttribute can't fail in this case +} + static void CalculateKeyIdentifierSha256(const P256PublicKeySpan & publicKey, MutableCertificateKeyId keyId) { uint8_t hash[kSHA256_Hash_Length]; @@ -1445,7 +1452,7 @@ static CHIP_ERROR ValidateChipNetworkIdentity(const ChipCertificateData & certDa { ReturnErrorOnFailure(ValidateCertificateType(certData, CertType::kNetworkIdentity)); - VerifyOrReturnError(certData.mSerialNumber.data_equal(kNetworkIdentitySerialNumber), CHIP_ERROR_WRONG_CERT_TYPE); + VerifyOrReturnError(certData.mSerialNumber.data_equal(kNetworkIdentitySerialNumberBytes), CHIP_ERROR_WRONG_CERT_TYPE); VerifyOrReturnError(certData.mNotBeforeTime == kNetworkIdentityNotBeforeTime, CHIP_ERROR_WRONG_CERT_TYPE); VerifyOrReturnError(certData.mNotAfterTime == kNetworkIdentityNotAfterTime, CHIP_ERROR_WRONG_CERT_TYPE); VerifyOrReturnError(certData.mIssuerDN.IsEqual(certData.mSubjectDN), CHIP_ERROR_WRONG_CERT_TYPE); @@ -1490,5 +1497,52 @@ CHIP_ERROR ExtractIdentifierFromChipNetworkIdentity(const ByteSpan & cert, Mutab return CHIP_NO_ERROR; } +static CHIP_ERROR GenerateNetworkIdentitySignature(const P256Keypair & keypair, P256ECDSASignature & signature) +{ + // Create a buffer and writer to capture the TBS (to-be-signed) portion of the certificate. + chip::Platform::ScopedMemoryBuffer asn1TBSBuf; + VerifyOrReturnError(asn1TBSBuf.Alloc(kNetworkIdenitityTBSLength), CHIP_ERROR_NO_MEMORY); + + ASN1Writer writer; + writer.Init(asn1TBSBuf.Get(), kNetworkIdenitityTBSLength); + + // Generate the TBSCertificate and sign it + ReturnErrorOnFailure(EncodeNetworkIdentityTBSCert(keypair.Pubkey(), writer)); + ReturnErrorOnFailure(keypair.ECDSA_sign_msg(asn1TBSBuf.Get(), writer.GetLengthWritten(), signature)); + + return CHIP_NO_ERROR; +} + +static CHIP_ERROR EncodeCompactIdentityCert(TLVWriter & writer, Tag tag, P256PublicKeySpan const & publicKey, + P256ECDSASignatureSpan const & signature) +{ + TLVType containerType; + ReturnErrorOnFailure(writer.StartContainer(tag, kTLVType_Structure, containerType)); + ReturnErrorOnFailure(writer.Put(ContextTag(kTag_EllipticCurvePublicKey), publicKey)); + ReturnErrorOnFailure(writer.Put(ContextTag(kTag_ECDSASignature), signature)); + ReturnErrorOnFailure(writer.EndContainer(containerType)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR NewChipNetworkIdentity(const Crypto::P256Keypair & keypair, MutableByteSpan & compactIdentityCert) +{ + VerifyOrReturnError(!compactIdentityCert.empty(), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(CanCastTo(compactIdentityCert.size()), CHIP_ERROR_INVALID_ARGUMENT); + + Crypto::P256ECDSASignature signature; + ReturnErrorOnFailure(GenerateNetworkIdentitySignature(keypair, signature)); + + TLVWriter writer; + writer.Init(compactIdentityCert); + + P256PublicKeySpan publicKeySpan(keypair.Pubkey().ConstBytes()); + P256ECDSASignatureSpan signatureSpan(signature.ConstBytes()); + VerifyOrDie(signature.Length() == signatureSpan.size()); + ReturnErrorOnFailure(EncodeCompactIdentityCert(writer, AnonymousTag(), publicKeySpan, signatureSpan)); + + compactIdentityCert.reduce_size(writer.GetLengthWritten()); + return CHIP_NO_ERROR; +} + } // namespace Credentials } // namespace chip diff --git a/src/credentials/CHIPCert.h b/src/credentials/CHIPCert.h index ecf5f9f041d984..03f7fe70199601 100644 --- a/src/credentials/CHIPCert.h +++ b/src/credentials/CHIPCert.h @@ -53,8 +53,8 @@ static constexpr uint16_t kX509NoWellDefinedExpirationDateYear = 9999; static constexpr uint32_t kMaxCHIPCertLength = 400; static constexpr uint32_t kMaxDERCertLength = 600; -// The decode buffer is used to reconstruct TBS section of X.509 certificate, which doesn't include signature. -static constexpr uint32_t kMaxCHIPCertDecodeBufLength = kMaxDERCertLength - Crypto::kMax_ECDSA_Signature_Length_Der; +// As per spec section 11.24 (Wi-Fi Authentication with Per-Device Credentials) +inline constexpr uint32_t kMaxCHIPCompactNetworkIdentityLength = 140; /** Data Element Tags for the CHIP Certificate */ @@ -542,6 +542,7 @@ CHIP_ERROR ValidateChipRCAC(const ByteSpan & rcac); /** * Validates a Network (Client) Identity in TLV-encoded form. + * Accepts either a full certificate or the compact-pdc-identity format. * * This function parses the certificate, ensures the rigid fields have the values mandated by the * specification, and validates the certificate signature. @@ -618,6 +619,17 @@ CHIP_ERROR NewICAX509Cert(const X509CertRequestParams & requestParams, const Cry CHIP_ERROR NewNodeOperationalX509Cert(const X509CertRequestParams & requestParams, const Crypto::P256PublicKey & subjectPubkey, const Crypto::P256Keypair & issuerKeypair, MutableByteSpan & x509Cert); +/** + * @brief Generates a Network (Client) Identity certificate in TLV-encoded form. + * + * @param keypair The key pair underlying the identity. + * @param cert Buffer to store the signed certificate in compact-pdc-identity TLV format. + * Must be at least `kMaxCHIPCompactNetworkIdentityLength` bytes long. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ +CHIP_ERROR NewChipNetworkIdentity(const Crypto::P256Keypair & keypair, MutableByteSpan & cert); + /** * @brief * Convert a certificate date/time (in the form of an ASN.1 universal time structure) into a CHIP Epoch time. diff --git a/src/credentials/CHIPCertToX509.cpp b/src/credentials/CHIPCertToX509.cpp index 7001e51dd66faf..4b8c65e05ebc49 100644 --- a/src/credentials/CHIPCertToX509.cpp +++ b/src/credentials/CHIPCertToX509.cpp @@ -31,7 +31,7 @@ #include #include -#include +#include #include #include #include @@ -482,6 +482,8 @@ static CHIP_ERROR DecodeConvertECDSASignature(TLVReader & reader, ASN1Writer & w * @param certData Structure containing data extracted from the TBS portion of the * CHIP certificate. * + * Note: The reader must be positioned on the SerialNumber element. + * * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise **/ static CHIP_ERROR DecodeConvertTBSCert(TLVReader & reader, ASN1Writer & writer, ChipCertificateData & certData) @@ -502,7 +504,7 @@ static CHIP_ERROR DecodeConvertTBSCert(TLVReader & reader, ASN1Writer & writer, // serialNumber CertificateSerialNumber // CertificateSerialNumber ::= INTEGER - ReturnErrorOnFailure(reader.Next(kTLVType_ByteString, ContextTag(kTag_SerialNumber))); + ReturnErrorOnFailure(reader.Expect(kTLVType_ByteString, ContextTag(kTag_SerialNumber))); ReturnErrorOnFailure(reader.Get(certData.mSerialNumber)); ReturnErrorOnFailure(writer.PutValue(kASN1TagClass_Universal, kASN1UniversalTag_Integer, false, certData.mSerialNumber.data(), static_cast(certData.mSerialNumber.size()))); @@ -543,6 +545,41 @@ static CHIP_ERROR DecodeConvertTBSCert(TLVReader & reader, ASN1Writer & writer, return err; } +/** + * Variant of DecodeConvertTBSCert that handles reading a compact-pdc-identity + * where only the subject public key is actually encoded. All other values are + * populated / written as the well-known values mandated by the specification. + * + * Note: The reader must be positioned on the EllipticCurvePublicKey element. + */ +static CHIP_ERROR DecodeConvertTBSCertCompactIdentity(TLVReader & reader, ASN1Writer & writer, ChipCertificateData & certData) +{ + // Decode the public key, everything else is rigid + ReturnErrorOnFailure(reader.Expect(kTLVType_ByteString, ContextTag(kTag_EllipticCurvePublicKey))); + ReturnErrorOnFailure(reader.Get(certData.mPublicKey)); + + // Populate rigid ChipCertificateData fields + certData.mSerialNumber = kNetworkIdentitySerialNumberBytes; + certData.mSigAlgoOID = kOID_SigAlgo_ECDSAWithSHA256; + InitNetworkIdentitySubject(certData.mIssuerDN); + certData.mNotBeforeTime = kNetworkIdentityNotBeforeTime; + certData.mNotAfterTime = kNetworkIdentityNotAfterTime; + InitNetworkIdentitySubject(certData.mSubjectDN); + certData.mPubKeyAlgoOID = kOID_PubKeyAlgo_ECPublicKey; + certData.mPubKeyCurveOID = kOID_EllipticCurve_prime256v1; + certData.mCertFlags.Set(CertFlags::kExtPresent_BasicConstraints); + certData.mCertFlags.Set(CertFlags::kExtPresent_KeyUsage); + certData.mKeyUsageFlags = kNetworkIdentityKeyUsage; + certData.mCertFlags.Set(CertFlags::kExtPresent_ExtendedKeyUsage); + certData.mKeyPurposeFlags = kNetworkIdentityKeyPurpose; + + if (!writer.IsNullWriter()) + { + ReturnErrorOnFailure(EncodeNetworkIdentityTBSCert(certData.mPublicKey, writer)); + } + return CHIP_NO_ERROR; +} + /** * Decode a CHIP TLV certificate and convert it to X.509 DER form. * @@ -570,7 +607,17 @@ static CHIP_ERROR DecodeConvertCert(TLVReader & reader, ASN1Writer & writer, ASN ASN1_START_SEQUENCE { // tbsCertificate TBSCertificate, - ReturnErrorOnFailure(DecodeConvertTBSCert(reader, tbsWriter, certData)); + reader.Next(); + if (reader.GetTag() == ContextTag(kTag_EllipticCurvePublicKey)) + { + // If the struct starts with the ec-pub-key we're dealing with a + // Network (Client) Identity in compact-pdc-identity format. + DecodeConvertTBSCertCompactIdentity(reader, tbsWriter, certData); + } + else + { + ReturnErrorOnFailure(DecodeConvertTBSCert(reader, tbsWriter, certData)); + } // signatureAlgorithm AlgorithmIdentifier // AlgorithmIdentifier ::= SEQUENCE diff --git a/src/credentials/CHIPCert_Internal.h b/src/credentials/CHIPCert_Internal.h index 9e61905b538c6f..11557305f97e55 100644 --- a/src/credentials/CHIPCert_Internal.h +++ b/src/credentials/CHIPCert_Internal.h @@ -23,10 +23,17 @@ namespace chip { namespace Credentials { +// The decode buffer is used to reconstruct TBS section of X.509 certificate, which doesn't include signature. +inline constexpr size_t kMaxCHIPCertDecodeBufLength = kMaxDERCertLength - Crypto::kMax_ECDSA_Signature_Length_Der; + +// The TBSCerticate of a Network (Client) Identity has a fixed (smaller) size. +inline constexpr size_t kNetworkIdenitityTBSLength = 244; + // Constants for Network (Client) Identities as per section 11.24 (Wi-Fi // Authentication with Per-Device Credentials) of the Matter spec. -inline constexpr CharSpan kNetworkIdentityCN = "*"_span; -inline constexpr ByteSpan kNetworkIdentitySerialNumber = ByteSpan((uint8_t[1]){ 1 }); +inline constexpr CharSpan kNetworkIdentityCN = "*"_span; +inline constexpr uint8_t kNetworkIdentitySerialNumber = 1; +inline constexpr ByteSpan kNetworkIdentitySerialNumberBytes = ByteSpan((uint8_t[1]){ kNetworkIdentitySerialNumber }); inline constexpr uint32_t kNetworkIdentityNotBeforeTime = 1; inline constexpr uint32_t kNetworkIdentityNotAfterTime = kNullCertTime; @@ -35,5 +42,11 @@ inline constexpr auto kNetworkIdentityKeyUsage = BitFlags(KeyUsag inline constexpr auto kNetworkIdentityKeyPurpose = BitFlags(KeyPurposeFlags::kClientAuth, KeyPurposeFlags::kServerAuth); +// Initializes a ChipDN as CN=kNetworkIdentityCN +void InitNetworkIdentitySubject(ChipDN & name); + +// Emits a X.509 TBSCertificate for a Network (Client) Identity based on the specified key. +CHIP_ERROR EncodeNetworkIdentityTBSCert(const Crypto::P256PublicKey & pubkey, ASN1::ASN1Writer & writer); + } // namespace Credentials } // namespace chip diff --git a/src/credentials/GenerateChipX509Cert.cpp b/src/credentials/GenerateChipX509Cert.cpp index 8297b85c4c5909..0dccaaa3a49fa6 100644 --- a/src/credentials/GenerateChipX509Cert.cpp +++ b/src/credentials/GenerateChipX509Cert.cpp @@ -31,7 +31,7 @@ #include #include -#include +#include #include #include #include @@ -313,8 +313,6 @@ CHIP_ERROR EncodeChipECDSASignature(Crypto::P256ECDSASignature & signature, ASN1 return err; } -} // namespace - CHIP_ERROR EncodeTBSCert(const X509CertRequestParams & requestParams, const Crypto::P256PublicKey & subjectPubkey, const Crypto::P256PublicKey & issuerPubkey, ASN1Writer & writer) { @@ -367,6 +365,61 @@ CHIP_ERROR EncodeTBSCert(const X509CertRequestParams & requestParams, const Cryp return err; } +} // namespace + +CHIP_ERROR EncodeNetworkIdentityTBSCert(const P256PublicKey & pubkey, ASN1Writer & writer) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + ChipDN issuerAndSubject; + InitNetworkIdentitySubject(issuerAndSubject); + + ASN1_START_SEQUENCE + { + // version [0] EXPLICIT Version DEFAULT v1 + ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0) + { + ASN1_ENCODE_INTEGER(2); // Version ::= INTEGER { v1(0), v2(1), v3(2) } + } + ASN1_END_CONSTRUCTED; + + ReturnErrorOnFailure(writer.PutInteger(kNetworkIdentitySerialNumber)); + + ASN1_START_SEQUENCE + { + ASN1_ENCODE_OBJECT_ID(kOID_SigAlgo_ECDSAWithSHA256); + } + ASN1_END_SEQUENCE; + + // issuer Name + ReturnErrorOnFailure(issuerAndSubject.EncodeToASN1(writer)); + + // validity Validity, + ReturnErrorOnFailure(EncodeValidity(kNetworkIdentityNotBeforeTime, kNetworkIdentityNotAfterTime, writer)); + + // subject Name + ReturnErrorOnFailure(issuerAndSubject.EncodeToASN1(writer)); + + ReturnErrorOnFailure(EncodeSubjectPublicKeyInfo(pubkey, writer)); + + // certificate extensions + ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 3) + { + ASN1_START_SEQUENCE + { + EncodeIsCAExtension(kNotCACert, writer); + EncodeKeyUsageExtension(KeyUsageFlags::kDigitalSignature, writer); + EncodeExtKeyUsageExtension({ kOID_KeyPurpose_ClientAuth, kOID_KeyPurpose_ServerAuth }, writer); + } + ASN1_END_SEQUENCE; + } + ASN1_END_CONSTRUCTED; + } + ASN1_END_SEQUENCE; + +exit: + return err; +} + CHIP_ERROR NewChipX509Cert(const X509CertRequestParams & requestParams, const Crypto::P256PublicKey & subjectPubkey, const Crypto::P256Keypair & issuerKeypair, MutableByteSpan & x509Cert) { diff --git a/src/credentials/tests/TestChipCert.cpp b/src/credentials/tests/TestChipCert.cpp index 3e23f022387d0b..2179561367b9cc 100644 --- a/src/credentials/tests/TestChipCert.cpp +++ b/src/credentials/tests/TestChipCert.cpp @@ -2149,24 +2149,46 @@ static void TestChipCert_ExtractPublicKeyAndSKID(nlTestSuite * inSuite, void * i static void TestChipCert_PDCIdentityValidation(nlTestSuite * inSuite, void * inContext) { - CHIP_ERROR err; CertificateKeyIdStorage keyId; - // Validate only - err = ValidateChipNetworkIdentity(sTestCert_PDCID01_Chip); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + // Test with both the full and compact TLV representations + for (auto && cert : { sTestCert_PDCID01_Chip, sTestCert_PDCID01_ChipCompact }) + { + // Validate only + NL_TEST_ASSERT(inSuite, ValidateChipNetworkIdentity(cert) == CHIP_NO_ERROR); + + // Validate and calculate identifier + keyId.fill(0xaa); + NL_TEST_ASSERT(inSuite, ValidateChipNetworkIdentity(cert, keyId) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, CertificateKeyId(keyId).data_equal(sTestCert_PDCID01_KeyId)); + + // Extract identifier only + keyId.fill(0xaa); + NL_TEST_ASSERT(inSuite, ExtractIdentifierFromChipNetworkIdentity(cert, keyId) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, CertificateKeyId(keyId).data_equal(sTestCert_PDCID01_KeyId)); + } +} - // Validate and calculate identifier - keyId.fill(0xaa); - err = ValidateChipNetworkIdentity(sTestCert_PDCID01_Chip, keyId); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, CertificateKeyId(keyId).data_equal(sTestCert_PDCID01_KeyId)); +static void TestChipCert_PDCIdentityGeneration(nlTestSuite * inSuite, void * inContext) +{ + // Generate a new keypair + P256Keypair keypair; + NL_TEST_ASSERT(inSuite, keypair.Initialize(ECPKeyTarget::ECDSA) == CHIP_NO_ERROR); - // Extract identifier only - keyId.fill(0xaa); - err = ExtractIdentifierFromChipNetworkIdentity(sTestCert_PDCID01_Chip, keyId); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, CertificateKeyId(keyId).data_equal(sTestCert_PDCID01_KeyId)); + // Generate an identity certificate based on the keypair + uint8_t buffer[kMaxCHIPCompactNetworkIdentityLength]; + MutableByteSpan cert(buffer); + NL_TEST_ASSERT(inSuite, NewChipNetworkIdentity(keypair, cert) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, ValidateChipNetworkIdentity(cert) == CHIP_NO_ERROR); + + // It should round-trip to X.509 DER and back, and remain valid. + uint8_t derBuffer[kMaxDERCertLength]; + MutableByteSpan derCert(derBuffer); + NL_TEST_ASSERT(inSuite, ConvertChipCertToX509Cert(cert, derCert) == CHIP_NO_ERROR); + uint8_t tlvBuffer[kMaxCHIPCertLength]; + MutableByteSpan tlvCert(tlvBuffer); // won't be compact after round-tripping + NL_TEST_ASSERT(inSuite, ConvertX509CertToChipCert(derCert, tlvCert) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, ValidateChipNetworkIdentity(tlvCert) == CHIP_NO_ERROR); } /** @@ -2228,6 +2250,7 @@ static const nlTest sTests[] = { NL_TEST_DEF("Test extracting Subject DN from chip certificate", TestChipCert_ExtractSubjectDNFromChipCert), NL_TEST_DEF("Test extracting PublicKey and SKID from chip certificate", TestChipCert_ExtractPublicKeyAndSKID), NL_TEST_DEF("Test PDC Identity Validation", TestChipCert_PDCIdentityValidation), + NL_TEST_DEF("Test PDC Identity Generation", TestChipCert_PDCIdentityGeneration), NL_TEST_SENTINEL() }; // clang-format on From d4578205d9776534b52fad81c53efc45e9afd2a3 Mon Sep 17 00:00:00 2001 From: Karsten Sperling Date: Fri, 3 Nov 2023 11:42:58 +1300 Subject: [PATCH 3/6] PDC: Add support for identity generation to the chip-cert tool --- src/tools/chip-cert/CertUtils.cpp | 282 ++++++++++++++++------------ src/tools/chip-cert/Cmd_GenCert.cpp | 17 +- src/tools/chip-cert/chip-cert.h | 2 + 3 files changed, 178 insertions(+), 123 deletions(-) diff --git a/src/tools/chip-cert/CertUtils.cpp b/src/tools/chip-cert/CertUtils.cpp index 7ca215b526017a..f4db7f1f96bd62 100644 --- a/src/tools/chip-cert/CertUtils.cpp +++ b/src/tools/chip-cert/CertUtils.cpp @@ -216,38 +216,53 @@ void ToolChipDN::PrintDN(FILE * file, const char * name) const namespace { +template +bool HasRawPrefix(const uint8_t * buffer, size_t len, const uint8_t (&prefix)[N]) +{ + return len >= N && memcmp(buffer, prefix, N) == 0; +} + +bool HasStringPrefix(const uint8_t * buffer, size_t len, const char * prefix) +{ + size_t prefixLen = strlen(prefix); + return len >= prefixLen && memcmp(buffer, prefix, prefixLen) == 0; +} + CertFormat DetectCertFormat(const uint8_t * cert, uint32_t certLen) { - static const uint8_t chipRawPrefix[] = { 0x15, 0x30, 0x01 }; - static const char * chipHexPrefix = "153001"; - static const char * chipB64Prefix = "FTAB"; - static const uint8_t derRawPrefix[] = { 0x30, 0x82 }; - static const char * derHexPrefix = "30820"; - static const char * pemMarker = "-----BEGIN CERTIFICATE-----"; + static const uint8_t chipRawPrefix[] = { 0x15, 0x30, 0x01 }; + static const char * chipHexPrefix = "153001"; + static const char * chipB64Prefix = "FTAB"; + static const uint8_t chipCompactPdcRawPrefix[] = { 0x15, 0x30, 0x09 }; + static const char * chipCompactPdcHexPrefix = "153009"; + static const char * chipCompactPdcB64Prefix = "FTAJ"; + static const uint8_t derRawPrefix[] = { 0x30, 0x82 }; + static const char * derHexPrefix = "30820"; + static const char * pemMarker = "-----BEGIN CERTIFICATE-----"; VerifyOrReturnError(cert != nullptr, kCertFormat_Unknown); - if ((certLen > sizeof(chipRawPrefix)) && (memcmp(cert, chipRawPrefix, sizeof(chipRawPrefix)) == 0)) + if (HasRawPrefix(cert, certLen, chipRawPrefix) || HasRawPrefix(cert, certLen, chipCompactPdcRawPrefix)) { return kCertFormat_Chip_Raw; } - if ((certLen > strlen(chipHexPrefix)) && (memcmp(cert, chipHexPrefix, strlen(chipHexPrefix)) == 0)) + if (HasStringPrefix(cert, certLen, chipHexPrefix) || HasStringPrefix(cert, certLen, chipCompactPdcHexPrefix)) { return kCertFormat_Chip_Hex; } - if ((certLen > strlen(chipB64Prefix)) && (memcmp(cert, chipB64Prefix, strlen(chipB64Prefix)) == 0)) + if (HasStringPrefix(cert, certLen, chipB64Prefix) || HasStringPrefix(cert, certLen, chipCompactPdcB64Prefix)) { return kCertFormat_Chip_Base64; } - if ((certLen > sizeof(derRawPrefix)) && (memcmp(cert, derRawPrefix, sizeof(derRawPrefix)) == 0)) + if (HasRawPrefix(cert, certLen, derRawPrefix)) { return kCertFormat_X509_DER; } - if ((certLen > strlen(derHexPrefix)) && (memcmp(cert, derHexPrefix, strlen(derHexPrefix)) == 0)) + if (HasStringPrefix(cert, certLen, derHexPrefix)) { return kCertFormat_X509_Hex; } @@ -260,23 +275,26 @@ CertFormat DetectCertFormat(const uint8_t * cert, uint32_t certLen) return kCertFormat_Unknown; } -bool SetCertSerialNumber(X509 * cert) +bool SetCertSerialNumber(X509 * cert, uint64_t value = kUseRandomSerialNumber) { bool res = true; uint64_t rnd; ASN1_INTEGER * snInt = X509_get_serialNumber(cert); - // Generate a random value to be used as the serial number. - if (!RAND_bytes(reinterpret_cast(&rnd), sizeof(rnd))) + if (value == kUseRandomSerialNumber) { - ReportOpenSSLErrorAndExit("RAND_bytes", res = false); - } + // Generate a random value to be used as the serial number. + if (!RAND_bytes(reinterpret_cast(&rnd), sizeof(rnd))) + { + ReportOpenSSLErrorAndExit("RAND_bytes", res = false); + } - // Avoid negative numbers. - rnd &= 0x7FFFFFFFFFFFFFFF; + // Avoid negative numbers. + value = rnd & 0x7FFFFFFFFFFFFFFF; + } // Store the serial number as an ASN1 integer value within the certificate. - if (ASN1_INTEGER_set_uint64(snInt, rnd) == 0) + if (ASN1_INTEGER_set_uint64(snInt, value) == 0) { ReportOpenSSLErrorAndExit("ASN1_INTEGER_set_uint64", res = false); } @@ -842,7 +860,7 @@ bool MakeCert(CertType certType, const ToolChipDN * subjectDN, X509 * caCert, EV EVP_PKEY * newKey, CertStructConfig & certConfig) { bool res = true; - bool isCA = (certType != CertType::kNode); + bool isCA = (certType != CertType::kNode && certType != CertType::kNetworkIdentity); VerifyOrExit(subjectDN != nullptr, res = false); VerifyOrExit(caCert != nullptr, res = false); @@ -859,7 +877,7 @@ bool MakeCert(CertType certType, const ToolChipDN * subjectDN, X509 * caCert, EV // Generate a serial number for the cert. if (certConfig.IsSerialNumberPresent()) { - res = SetCertSerialNumber(newCert); + res = SetCertSerialNumber(newCert, (certType == CertType::kNetworkIdentity ? 1 : kUseRandomSerialNumber)); VerifyTrueOrExit(res); } @@ -930,10 +948,15 @@ bool MakeCert(CertType certType, const ToolChipDN * subjectDN, X509 * caCert, EV res = AddExtension(newCert, NID_ext_key_usage, "critical,codeSigning"); VerifyTrueOrExit(res); } + else if (certType == CertType::kNetworkIdentity) + { + res = AddExtension(newCert, NID_ext_key_usage, "critical,clientAuth,serverAuth"); + VerifyTrueOrExit(res); + } } // Add a subject key id extension for the certificate. - if (certConfig.IsExtensionSKIDPresent()) + if (certType != CertType::kNetworkIdentity && certConfig.IsExtensionSKIDPresent()) { res = AddSubjectKeyId(newCert, certConfig.IsExtensionSKIDLengthValid()); VerifyTrueOrExit(res); @@ -941,7 +964,7 @@ bool MakeCert(CertType certType, const ToolChipDN * subjectDN, X509 * caCert, EV // Add the authority key id extension from the signing certificate. For self-signed cert's this will // be the same as new cert's subject key id extension. - if (certConfig.IsExtensionAKIDPresent()) + if (certType != CertType::kNetworkIdentity && certConfig.IsExtensionAKIDPresent()) { if ((certType == CertType::kRoot) && !certConfig.IsExtensionSKIDPresent()) { @@ -1012,6 +1035,10 @@ CHIP_ERROR MakeCertTLV(CertType certType, const ToolChipDN * subjectDN, X509 * c isCA = (certType == CertType::kICA || certType == CertType::kRoot); + // If error testing is enabled, let field inclusion be controller that way, + // otherwise use compact identity format for Network (Client) Identities. + bool useCompactIdentityFormat = (!certConfig.IsErrorTestCaseEnabled() && certType == CertType::kNetworkIdentity); + uint8_t * p = subjectPubkey; VerifyOrReturnError(i2o_ECPublicKey(EVP_PKEY_get0_EC_KEY(newKey), &p) == chip::Crypto::CHIP_CRYPTO_PUBLIC_KEY_SIZE_BYTES, CHIP_ERROR_INVALID_ARGUMENT); @@ -1025,7 +1052,7 @@ CHIP_ERROR MakeCertTLV(CertType certType, const ToolChipDN * subjectDN, X509 * c ReturnErrorOnFailure(writer.StartContainer(AnonymousTag(), kTLVType_Structure, containerType)); // serial number - if (certConfig.IsSerialNumberPresent()) + if (!useCompactIdentityFormat && certConfig.IsSerialNumberPresent()) { ASN1_INTEGER * asn1Integer = X509_get_serialNumber(x509Cert); uint64_t serialNumber; @@ -1036,10 +1063,13 @@ CHIP_ERROR MakeCertTLV(CertType certType, const ToolChipDN * subjectDN, X509 * c } // signature algorithm - ReturnErrorOnFailure(writer.Put(ContextTag(kTag_SignatureAlgorithm), certConfig.GetSignatureAlgorithmTLVEnum())); + if (!useCompactIdentityFormat) + { + ReturnErrorOnFailure(writer.Put(ContextTag(kTag_SignatureAlgorithm), certConfig.GetSignatureAlgorithmTLVEnum())); + } // issuer Name - if (certConfig.IsIssuerPresent()) + if (!useCompactIdentityFormat && certConfig.IsIssuerPresent()) { if (certType == CertType::kRoot) { @@ -1081,27 +1111,30 @@ CHIP_ERROR MakeCertTLV(CertType certType, const ToolChipDN * subjectDN, X509 * c validFromChipEpoch = validToChipEpoch; validToChipEpoch = validTemp; } - if (certConfig.IsValidityNotBeforePresent()) + if (!useCompactIdentityFormat && certConfig.IsValidityNotBeforePresent()) { ReturnErrorOnFailure(writer.Put(ContextTag(kTag_NotBefore), validFromChipEpoch)); } - if (certConfig.IsValidityNotAfterPresent()) + if (!useCompactIdentityFormat && certConfig.IsValidityNotAfterPresent()) { ReturnErrorOnFailure(writer.Put(ContextTag(kTag_NotAfter), validToChipEpoch)); } // subject Name - if (certConfig.IsSubjectPresent()) + if (!useCompactIdentityFormat && certConfig.IsSubjectPresent()) { ReturnErrorOnFailure(subjectDN->EncodeToTLV(writer, ContextTag(kTag_Subject))); } // public key algorithm - ReturnErrorOnFailure(writer.Put(ContextTag(kTag_PublicKeyAlgorithm), GetOIDEnum(kOID_PubKeyAlgo_ECPublicKey))); + if (!useCompactIdentityFormat) + { + ReturnErrorOnFailure(writer.Put(ContextTag(kTag_PublicKeyAlgorithm), GetOIDEnum(kOID_PubKeyAlgo_ECPublicKey))); - // public key curve Id - uint8_t ecCurveEnum = certConfig.IsSigCurveWrong() ? 0x02 : GetOIDEnum(kOID_EllipticCurve_prime256v1); - ReturnErrorOnFailure(writer.Put(ContextTag(kTag_EllipticCurveIdentifier), ecCurveEnum)); + // public key curve Id + uint8_t ecCurveEnum = certConfig.IsSigCurveWrong() ? 0x02 : GetOIDEnum(kOID_EllipticCurve_prime256v1); + ReturnErrorOnFailure(writer.Put(ContextTag(kTag_EllipticCurveIdentifier), ecCurveEnum)); + } // public key if (certConfig.IsPublicKeyError()) @@ -1112,121 +1145,126 @@ CHIP_ERROR MakeCertTLV(CertType certType, const ToolChipDN * subjectDN, X509 * c writer.PutBytes(ContextTag(kTag_EllipticCurvePublicKey), subjectPubkey, chip::Crypto::CHIP_CRYPTO_PUBLIC_KEY_SIZE_BYTES)); // extensions - ReturnErrorOnFailure(writer.StartContainer(ContextTag(kTag_Extensions), kTLVType_List, containerType2)); + if (!useCompactIdentityFormat) { - if (isCA) + ReturnErrorOnFailure(writer.StartContainer(ContextTag(kTag_Extensions), kTLVType_List, containerType2)); { - // basic constraints - if (certConfig.IsExtensionBasicPresent()) + if (isCA) { - ReturnErrorOnFailure(writer.StartContainer(ContextTag(kTag_BasicConstraints), kTLVType_Structure, containerType3)); - if (certConfig.IsExtensionBasicCAPresent()) - { - ReturnErrorOnFailure(writer.PutBoolean(ContextTag(kTag_BasicConstraints_IsCA), - certConfig.IsExtensionBasicCACorrect() ? isCA : !isCA)); - } - // TODO - if (pathLen != kPathLength_NotSpecified) + // basic constraints + if (certConfig.IsExtensionBasicPresent()) { ReturnErrorOnFailure( - writer.Put(ContextTag(kTag_BasicConstraints_PathLenConstraint), static_cast(pathLen))); + writer.StartContainer(ContextTag(kTag_BasicConstraints), kTLVType_Structure, containerType3)); + if (certConfig.IsExtensionBasicCAPresent()) + { + ReturnErrorOnFailure(writer.PutBoolean(ContextTag(kTag_BasicConstraints_IsCA), + certConfig.IsExtensionBasicCACorrect() ? isCA : !isCA)); + } + // TODO + if (pathLen != kPathLength_NotSpecified) + { + ReturnErrorOnFailure( + writer.Put(ContextTag(kTag_BasicConstraints_PathLenConstraint), static_cast(pathLen))); + } + ReturnErrorOnFailure(writer.EndContainer(containerType3)); } - ReturnErrorOnFailure(writer.EndContainer(containerType3)); - } - // key usage - if (certConfig.IsExtensionKeyUsagePresent()) - { - BitFlags keyUsage; - if (!certConfig.IsExtensionKeyUsageDigitalSigCorrect()) - { - keyUsage.Set(KeyUsageFlags::kDigitalSignature); - } - if (certConfig.IsExtensionKeyUsageKeyCertSignCorrect()) - { - keyUsage.Set(KeyUsageFlags::kKeyCertSign); - } - if (certConfig.IsExtensionKeyUsageCRLSignCorrect()) + // key usage + if (certConfig.IsExtensionKeyUsagePresent()) { - keyUsage.Set(KeyUsageFlags::kCRLSign); + BitFlags keyUsage; + if (!certConfig.IsExtensionKeyUsageDigitalSigCorrect()) + { + keyUsage.Set(KeyUsageFlags::kDigitalSignature); + } + if (certConfig.IsExtensionKeyUsageKeyCertSignCorrect()) + { + keyUsage.Set(KeyUsageFlags::kKeyCertSign); + } + if (certConfig.IsExtensionKeyUsageCRLSignCorrect()) + { + keyUsage.Set(KeyUsageFlags::kCRLSign); + } + ReturnErrorOnFailure(writer.Put(ContextTag(kTag_KeyUsage), keyUsage.Raw())); } - ReturnErrorOnFailure(writer.Put(ContextTag(kTag_KeyUsage), keyUsage.Raw())); } - } - else - { - // basic constraints - if (certConfig.IsExtensionBasicPresent()) + else { - ReturnErrorOnFailure(writer.StartContainer(ContextTag(kTag_BasicConstraints), kTLVType_Structure, containerType3)); - if (certConfig.IsExtensionBasicCAPresent()) + // basic constraints + if (certConfig.IsExtensionBasicPresent()) { - ReturnErrorOnFailure(writer.PutBoolean(ContextTag(kTag_BasicConstraints_IsCA), - certConfig.IsExtensionBasicCACorrect() ? isCA : !isCA)); + ReturnErrorOnFailure( + writer.StartContainer(ContextTag(kTag_BasicConstraints), kTLVType_Structure, containerType3)); + if (certConfig.IsExtensionBasicCAPresent()) + { + ReturnErrorOnFailure(writer.PutBoolean(ContextTag(kTag_BasicConstraints_IsCA), + certConfig.IsExtensionBasicCACorrect() ? isCA : !isCA)); + } + ReturnErrorOnFailure(writer.EndContainer(containerType3)); } - ReturnErrorOnFailure(writer.EndContainer(containerType3)); - } - // key usage - if (certConfig.IsExtensionKeyUsagePresent()) - { - BitFlags keyUsage; - if (certConfig.IsExtensionKeyUsageDigitalSigCorrect()) + // key usage + if (certConfig.IsExtensionKeyUsagePresent()) { - keyUsage.Set(KeyUsageFlags::kDigitalSignature); + BitFlags keyUsage; + if (certConfig.IsExtensionKeyUsageDigitalSigCorrect()) + { + keyUsage.Set(KeyUsageFlags::kDigitalSignature); + } + if (!certConfig.IsExtensionKeyUsageKeyCertSignCorrect()) + { + keyUsage.Set(KeyUsageFlags::kKeyCertSign); + } + if (!certConfig.IsExtensionKeyUsageCRLSignCorrect()) + { + keyUsage.Set(KeyUsageFlags::kCRLSign); + } + ReturnErrorOnFailure(writer.Put(ContextTag(kTag_KeyUsage), keyUsage)); } - if (!certConfig.IsExtensionKeyUsageKeyCertSignCorrect()) - { - keyUsage.Set(KeyUsageFlags::kKeyCertSign); - } - if (!certConfig.IsExtensionKeyUsageCRLSignCorrect()) + + // extended key usage + if (!certConfig.IsExtensionExtendedKeyUsageMissing() && (certType == CertType::kNode)) { - keyUsage.Set(KeyUsageFlags::kCRLSign); + ReturnErrorOnFailure(writer.StartContainer(ContextTag(kTag_ExtendedKeyUsage), kTLVType_Array, containerType3)); + if (certType == CertType::kNode) + { + ReturnErrorOnFailure(writer.Put(AnonymousTag(), GetOIDEnum(kOID_KeyPurpose_ClientAuth))); + ReturnErrorOnFailure(writer.Put(AnonymousTag(), GetOIDEnum(kOID_KeyPurpose_ServerAuth))); + } + else if (certType == CertType::kFirmwareSigning) + { + ReturnErrorOnFailure(writer.Put(AnonymousTag(), GetOIDEnum(kOID_KeyPurpose_CodeSigning))); + } + ReturnErrorOnFailure(writer.EndContainer(containerType3)); } - ReturnErrorOnFailure(writer.Put(ContextTag(kTag_KeyUsage), keyUsage)); } - // extended key usage - if (!certConfig.IsExtensionExtendedKeyUsageMissing() && (certType == CertType::kNode)) + // subject key identifier + if (certConfig.IsExtensionSKIDPresent()) { - ReturnErrorOnFailure(writer.StartContainer(ContextTag(kTag_ExtendedKeyUsage), kTLVType_Array, containerType3)); - if (certType == CertType::kNode) - { - ReturnErrorOnFailure(writer.Put(AnonymousTag(), GetOIDEnum(kOID_KeyPurpose_ClientAuth))); - ReturnErrorOnFailure(writer.Put(AnonymousTag(), GetOIDEnum(kOID_KeyPurpose_ServerAuth))); - } - else if (certType == CertType::kFirmwareSigning) - { - ReturnErrorOnFailure(writer.Put(AnonymousTag(), GetOIDEnum(kOID_KeyPurpose_CodeSigning))); - } - ReturnErrorOnFailure(writer.EndContainer(containerType3)); + ReturnErrorOnFailure(Crypto::Hash_SHA1(subjectPubkey, sizeof(subjectPubkey), keyid)); + size_t keyIdLen = certConfig.IsExtensionSKIDLengthValid() ? sizeof(keyid) : sizeof(keyid) - 1; + ReturnErrorOnFailure(writer.Put(ContextTag(kTag_SubjectKeyIdentifier), ByteSpan(keyid, keyIdLen))); } - } - - // subject key identifier - if (certConfig.IsExtensionSKIDPresent()) - { - ReturnErrorOnFailure(Crypto::Hash_SHA1(subjectPubkey, sizeof(subjectPubkey), keyid)); - size_t keyIdLen = certConfig.IsExtensionSKIDLengthValid() ? sizeof(keyid) : sizeof(keyid) - 1; - ReturnErrorOnFailure(writer.Put(ContextTag(kTag_SubjectKeyIdentifier), ByteSpan(keyid, keyIdLen))); - } - // authority key identifier - if (certConfig.IsExtensionAKIDPresent()) - { - ReturnErrorOnFailure(Crypto::Hash_SHA1(issuerPubkey, sizeof(issuerPubkey), keyid)); - size_t keyIdLen = certConfig.IsExtensionAKIDLengthValid() ? sizeof(keyid) : sizeof(keyid) - 1; - ReturnErrorOnFailure(writer.Put(ContextTag(kTag_AuthorityKeyIdentifier), ByteSpan(keyid, keyIdLen))); - } + // authority key identifier + if (certConfig.IsExtensionAKIDPresent()) + { + ReturnErrorOnFailure(Crypto::Hash_SHA1(issuerPubkey, sizeof(issuerPubkey), keyid)); + size_t keyIdLen = certConfig.IsExtensionAKIDLengthValid() ? sizeof(keyid) : sizeof(keyid) - 1; + ReturnErrorOnFailure(writer.Put(ContextTag(kTag_AuthorityKeyIdentifier), ByteSpan(keyid, keyIdLen))); + } - for (uint8_t i = 0; i < futureExtsCount; i++) - { - ReturnErrorOnFailure( - writer.Put(ContextTag(kTag_FutureExtension), - ByteSpan(reinterpret_cast(futureExts[i].info), strlen(futureExts[i].info)))); + for (uint8_t i = 0; i < futureExtsCount; i++) + { + ReturnErrorOnFailure( + writer.Put(ContextTag(kTag_FutureExtension), + ByteSpan(reinterpret_cast(futureExts[i].info), strlen(futureExts[i].info)))); + } } + ReturnErrorOnFailure(writer.EndContainer(containerType2)); } - ReturnErrorOnFailure(writer.EndContainer(containerType2)); // signature const ASN1_BIT_STRING * asn1Signature = nullptr; diff --git a/src/tools/chip-cert/Cmd_GenCert.cpp b/src/tools/chip-cert/Cmd_GenCert.cpp index 7d8767e8c9d7ef..730677d125fc88 100644 --- a/src/tools/chip-cert/Cmd_GenCert.cpp +++ b/src/tools/chip-cert/Cmd_GenCert.cpp @@ -35,6 +35,7 @@ namespace { using namespace chip::ArgParser; using namespace chip::Credentials; using namespace chip::ASN1; +using namespace chip::literals; #define CMD_NAME "chip-cert gen-cert" @@ -104,6 +105,7 @@ const char * const gCmdOptionHelp = " c - CA certificate\n" " n - node certificate\n" " f - firmware signing certificate\n" + " p - Wi-Fi PDC identity\n" "\n" " -i, --subject-chip-id \n" "\n" @@ -341,6 +343,19 @@ bool HandleOption(const char * progName, OptionSet * optSet, int id, const char gCertType = CertType::kRoot; gSelfSign = true; } + else if (*arg == 'p') + { + gCertType = CertType::kNetworkIdentity; + gSelfSign = true; + gSubjectDN.AddAttribute_CommonName("*"_span, false); + // Not Before Jan 1 00:00:01 2000 GMT + gValidFrom = { 0 }; + gValidFrom.tm_year = (2000 - 1900); + gValidFrom.tm_mday = 1; + gValidFrom.tm_sec = 1; + // Not After Dec 31 23:59:59 9999 GMT + gValidDays = kCertValidDays_NoWellDefinedExpiration; + } } if (gCertType == CertType::kNotSpecified) @@ -1176,7 +1191,7 @@ bool Cmd_GenCert(int argc, char * argv[]) gFutureExtensionsCount, newCert.get(), newKey.get(), gCertConfig); VerifyTrueOrExit(res); - if (gCertConfig.IsErrorTestCaseEnabled() && IsChipCertFormat(gOutCertFormat)) + if (IsChipCertFormat(gOutCertFormat) && (gCertConfig.IsErrorTestCaseEnabled() || gCertType == CertType::kNetworkIdentity)) { uint32_t chipCertBufLen = kMaxCHIPCertLength + gCertConfig.GetExtraCertLength(); std::unique_ptr chipCertBuf = std::unique_ptr(new uint8_t[chipCertBufLen]); diff --git a/src/tools/chip-cert/chip-cert.h b/src/tools/chip-cert/chip-cert.h index 81f5ab733133bb..182dfc47f80170 100644 --- a/src/tools/chip-cert/chip-cert.h +++ b/src/tools/chip-cert/chip-cert.h @@ -88,6 +88,8 @@ enum kPathLength_NotSpecified = -1, }; +inline constexpr uint64_t kUseRandomSerialNumber = 0; + enum CertFormat { kCertFormat_Unknown = 0, From 9b21b59973e6e2d96bca33af2c4d76cd530ed9c1 Mon Sep 17 00:00:00 2001 From: Karsten Sperling Date: Fri, 3 Nov 2023 20:29:26 +1300 Subject: [PATCH 4/6] Address review comments --- src/credentials/CHIPCert.cpp | 8 ++++---- src/credentials/CHIPCert.h | 2 +- src/tools/chip-cert/CertUtils.cpp | 2 +- src/tools/chip-cert/Cmd_GenCert.cpp | 2 ++ 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/credentials/CHIPCert.cpp b/src/credentials/CHIPCert.cpp index 46451b45b34647..5f1d7e6bcaff95 100644 --- a/src/credentials/CHIPCert.cpp +++ b/src/credentials/CHIPCert.cpp @@ -1095,7 +1095,7 @@ DLL_EXPORT CHIP_ERROR ChipEpochToASN1Time(uint32_t epochTime, chip::ASN1::ASN1Un return CHIP_NO_ERROR; } -static CHIP_ERROR ValidateCertificateType(ChipCertificateData const & certData, CertType expectedType) +static CHIP_ERROR ValidateCertificateType(const ChipCertificateData & certData, CertType expectedType) { CertType certType; ReturnErrorOnFailure(certData.mSubjectDN.GetCertType(certType)); @@ -1436,7 +1436,7 @@ CHIP_ERROR CertificateValidityPolicy::ApplyDefaultPolicy(const ChipCertificateDa void InitNetworkIdentitySubject(ChipDN & name) { name.Clear(); - ChipError err = name.AddAttribute_CommonName(kNetworkIdentityCN, /* not printable */ false); + CHIP_ERROR err = name.AddAttribute_CommonName(kNetworkIdentityCN, /* not printable */ false); VerifyOrDie(err == CHIP_NO_ERROR); // AddAttribute can't fail in this case } @@ -1513,8 +1513,8 @@ static CHIP_ERROR GenerateNetworkIdentitySignature(const P256Keypair & keypair, return CHIP_NO_ERROR; } -static CHIP_ERROR EncodeCompactIdentityCert(TLVWriter & writer, Tag tag, P256PublicKeySpan const & publicKey, - P256ECDSASignatureSpan const & signature) +static CHIP_ERROR EncodeCompactIdentityCert(TLVWriter & writer, Tag tag, const P256PublicKeySpan & publicKey, + const P256ECDSASignatureSpan & signature) { TLVType containerType; ReturnErrorOnFailure(writer.StartContainer(tag, kTLVType_Structure, containerType)); diff --git a/src/credentials/CHIPCert.h b/src/credentials/CHIPCert.h index 03f7fe70199601..7d731df7edd3d6 100644 --- a/src/credentials/CHIPCert.h +++ b/src/credentials/CHIPCert.h @@ -54,7 +54,7 @@ static constexpr uint32_t kMaxCHIPCertLength = 400; static constexpr uint32_t kMaxDERCertLength = 600; // As per spec section 11.24 (Wi-Fi Authentication with Per-Device Credentials) -inline constexpr uint32_t kMaxCHIPCompactNetworkIdentityLength = 140; +inline constexpr uint32_t kMaxCHIPCompactNetworkIdentityLength = 137; /** Data Element Tags for the CHIP Certificate */ diff --git a/src/tools/chip-cert/CertUtils.cpp b/src/tools/chip-cert/CertUtils.cpp index f4db7f1f96bd62..2d2ea6fbfcd383 100644 --- a/src/tools/chip-cert/CertUtils.cpp +++ b/src/tools/chip-cert/CertUtils.cpp @@ -1035,7 +1035,7 @@ CHIP_ERROR MakeCertTLV(CertType certType, const ToolChipDN * subjectDN, X509 * c isCA = (certType == CertType::kICA || certType == CertType::kRoot); - // If error testing is enabled, let field inclusion be controller that way, + // If error testing is enabled, let field inclusion be controlled that way, // otherwise use compact identity format for Network (Client) Identities. bool useCompactIdentityFormat = (!certConfig.IsErrorTestCaseEnabled() && certType == CertType::kNetworkIdentity); diff --git a/src/tools/chip-cert/Cmd_GenCert.cpp b/src/tools/chip-cert/Cmd_GenCert.cpp index 730677d125fc88..9bc74c9226e6f0 100644 --- a/src/tools/chip-cert/Cmd_GenCert.cpp +++ b/src/tools/chip-cert/Cmd_GenCert.cpp @@ -1191,6 +1191,8 @@ bool Cmd_GenCert(int argc, char * argv[]) gFutureExtensionsCount, newCert.get(), newKey.get(), gCertConfig); VerifyTrueOrExit(res); + // Always use MakeCertTLV() instead of the standard WriteCert() / ConvertX509CertToChipCert() for + // Network (Client) Identities in CHIP format so that we can write it in the compact-pdc-identity format. if (IsChipCertFormat(gOutCertFormat) && (gCertConfig.IsErrorTestCaseEnabled() || gCertType == CertType::kNetworkIdentity)) { uint32_t chipCertBufLen = kMaxCHIPCertLength + gCertConfig.GetExtraCertLength(); From 87db9cf945cf481b1fd57078b0f19648dbe8d599 Mon Sep 17 00:00:00 2001 From: Karsten Sperling Date: Mon, 6 Nov 2023 19:52:35 +1300 Subject: [PATCH 5/6] Address further review comments --- src/credentials/CHIPCert.cpp | 14 +-- src/credentials/CHIPCertToX509.cpp | 2 +- src/credentials/CHIPCert_Internal.h | 2 +- src/tools/chip-cert/CertUtils.cpp | 138 ++++++++++++++-------------- 4 files changed, 77 insertions(+), 79 deletions(-) diff --git a/src/credentials/CHIPCert.cpp b/src/credentials/CHIPCert.cpp index 5f1d7e6bcaff95..2b15597b411cee 100644 --- a/src/credentials/CHIPCert.cpp +++ b/src/credentials/CHIPCert.cpp @@ -1440,12 +1440,13 @@ void InitNetworkIdentitySubject(ChipDN & name) VerifyOrDie(err == CHIP_NO_ERROR); // AddAttribute can't fail in this case } -static void CalculateKeyIdentifierSha256(const P256PublicKeySpan & publicKey, MutableCertificateKeyId keyId) +static CHIP_ERROR CalculateKeyIdentifierSha256(const P256PublicKeySpan & publicKey, MutableCertificateKeyId keyId) { uint8_t hash[kSHA256_Hash_Length]; static_assert(keyId.size() <= sizeof(hash)); // truncating 32 bytes down to 20 - chip::Crypto::Hash_SHA256(publicKey.data(), publicKey.size(), hash); + ReturnErrorOnFailure(Hash_SHA256(publicKey.data(), publicKey.size(), hash)); memcpy(keyId.data(), hash, keyId.size()); + return CHIP_NO_ERROR; } static CHIP_ERROR ValidateChipNetworkIdentity(const ChipCertificateData & certData) @@ -1484,7 +1485,7 @@ CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert, MutableCertificate ChipCertificateData certData; ReturnErrorOnFailure(DecodeChipCert(cert, certData, CertDecodeFlags::kGenerateTBSHash)); ReturnErrorOnFailure(ValidateChipNetworkIdentity(certData)); - CalculateKeyIdentifierSha256(certData.mPublicKey, keyId); + ReturnErrorOnFailure(CalculateKeyIdentifierSha256(certData.mPublicKey, keyId)); return CHIP_NO_ERROR; } @@ -1493,7 +1494,7 @@ CHIP_ERROR ExtractIdentifierFromChipNetworkIdentity(const ByteSpan & cert, Mutab ChipCertificateData certData; ReturnErrorOnFailure(DecodeChipCert(cert, certData)); ReturnErrorOnFailure(ValidateCertificateType(certData, CertType::kNetworkIdentity)); - CalculateKeyIdentifierSha256(certData.mPublicKey, keyId); + ReturnErrorOnFailure(CalculateKeyIdentifierSha256(certData.mPublicKey, keyId)); return CHIP_NO_ERROR; } @@ -1501,10 +1502,10 @@ static CHIP_ERROR GenerateNetworkIdentitySignature(const P256Keypair & keypair, { // Create a buffer and writer to capture the TBS (to-be-signed) portion of the certificate. chip::Platform::ScopedMemoryBuffer asn1TBSBuf; - VerifyOrReturnError(asn1TBSBuf.Alloc(kNetworkIdenitityTBSLength), CHIP_ERROR_NO_MEMORY); + VerifyOrReturnError(asn1TBSBuf.Alloc(kNetworkIdentityTBSLength), CHIP_ERROR_NO_MEMORY); ASN1Writer writer; - writer.Init(asn1TBSBuf.Get(), kNetworkIdenitityTBSLength); + writer.Init(asn1TBSBuf.Get(), kNetworkIdentityTBSLength); // Generate the TBSCertificate and sign it ReturnErrorOnFailure(EncodeNetworkIdentityTBSCert(keypair.Pubkey(), writer)); @@ -1537,7 +1538,6 @@ CHIP_ERROR NewChipNetworkIdentity(const Crypto::P256Keypair & keypair, MutableBy P256PublicKeySpan publicKeySpan(keypair.Pubkey().ConstBytes()); P256ECDSASignatureSpan signatureSpan(signature.ConstBytes()); - VerifyOrDie(signature.Length() == signatureSpan.size()); ReturnErrorOnFailure(EncodeCompactIdentityCert(writer, AnonymousTag(), publicKeySpan, signatureSpan)); compactIdentityCert.reduce_size(writer.GetLengthWritten()); diff --git a/src/credentials/CHIPCertToX509.cpp b/src/credentials/CHIPCertToX509.cpp index 4b8c65e05ebc49..3793062231ce7f 100644 --- a/src/credentials/CHIPCertToX509.cpp +++ b/src/credentials/CHIPCertToX509.cpp @@ -687,7 +687,7 @@ CHIP_ERROR DecodeChipCert(TLVReader & reader, ChipCertificateData & certData, Bi // Hash the encoded TBS certificate. Only SHA256 is supported. VerifyOrReturnError(certData.mSigAlgoOID == kOID_SigAlgo_ECDSAWithSHA256, CHIP_ERROR_UNSUPPORTED_SIGNATURE_TYPE); - chip::Crypto::Hash_SHA256(asn1TBSBuf.Get(), tbsWriter.GetLengthWritten(), certData.mTBSHash); + ReturnErrorOnFailure(Hash_SHA256(asn1TBSBuf.Get(), tbsWriter.GetLengthWritten(), certData.mTBSHash)); certData.mCertFlags.Set(CertFlags::kTBSHashPresent); } else diff --git a/src/credentials/CHIPCert_Internal.h b/src/credentials/CHIPCert_Internal.h index 11557305f97e55..0e1ef5bf3a910a 100644 --- a/src/credentials/CHIPCert_Internal.h +++ b/src/credentials/CHIPCert_Internal.h @@ -27,7 +27,7 @@ namespace Credentials { inline constexpr size_t kMaxCHIPCertDecodeBufLength = kMaxDERCertLength - Crypto::kMax_ECDSA_Signature_Length_Der; // The TBSCerticate of a Network (Client) Identity has a fixed (smaller) size. -inline constexpr size_t kNetworkIdenitityTBSLength = 244; +inline constexpr size_t kNetworkIdentityTBSLength = 244; // Constants for Network (Client) Identities as per section 11.24 (Wi-Fi // Authentication with Per-Device Credentials) of the Matter spec. diff --git a/src/tools/chip-cert/CertUtils.cpp b/src/tools/chip-cert/CertUtils.cpp index 2d2ea6fbfcd383..a8857ae72baa58 100644 --- a/src/tools/chip-cert/CertUtils.cpp +++ b/src/tools/chip-cert/CertUtils.cpp @@ -290,7 +290,7 @@ bool SetCertSerialNumber(X509 * cert, uint64_t value = kUseRandomSerialNumber) } // Avoid negative numbers. - value = rnd & 0x7FFFFFFFFFFFFFFF; + value = rnd & 0x7FFFFFFFFFFFFFFFULL; } // Store the serial number as an ASN1 integer value within the certificate. @@ -860,7 +860,7 @@ bool MakeCert(CertType certType, const ToolChipDN * subjectDN, X509 * caCert, EV EVP_PKEY * newKey, CertStructConfig & certConfig) { bool res = true; - bool isCA = (certType != CertType::kNode && certType != CertType::kNetworkIdentity); + bool isCA = (certType == CertType::kRoot || certType == CertType::kICA); VerifyOrExit(subjectDN != nullptr, res = false); VerifyOrExit(caCert != nullptr, res = false); @@ -1051,84 +1051,83 @@ CHIP_ERROR MakeCertTLV(CertType certType, const ToolChipDN * subjectDN, X509 * c ReturnErrorOnFailure(writer.StartContainer(AnonymousTag(), kTLVType_Structure, containerType)); - // serial number - if (!useCompactIdentityFormat && certConfig.IsSerialNumberPresent()) - { - ASN1_INTEGER * asn1Integer = X509_get_serialNumber(x509Cert); - uint64_t serialNumber; - uint8_t serialNumberArray[sizeof(uint64_t)]; - VerifyOrReturnError(1 == ASN1_INTEGER_get_uint64(&serialNumber, asn1Integer), CHIP_ERROR_INVALID_ARGUMENT); - Encoding::BigEndian::Put64(serialNumberArray, serialNumber); - ReturnErrorOnFailure(writer.PutBytes(ContextTag(kTag_SerialNumber), serialNumberArray, sizeof(serialNumberArray))); - } - - // signature algorithm if (!useCompactIdentityFormat) { + // serial number + if (certConfig.IsSerialNumberPresent()) + { + ASN1_INTEGER * asn1Integer = X509_get_serialNumber(x509Cert); + uint64_t serialNumber; + uint8_t serialNumberArray[sizeof(uint64_t)]; + VerifyOrReturnError(1 == ASN1_INTEGER_get_uint64(&serialNumber, asn1Integer), CHIP_ERROR_INVALID_ARGUMENT); + Encoding::BigEndian::Put64(serialNumberArray, serialNumber); + ReturnErrorOnFailure(writer.PutBytes(ContextTag(kTag_SerialNumber), serialNumberArray, sizeof(serialNumberArray))); + } + + // signature algorithm ReturnErrorOnFailure(writer.Put(ContextTag(kTag_SignatureAlgorithm), certConfig.GetSignatureAlgorithmTLVEnum())); - } - // issuer Name - if (!useCompactIdentityFormat && certConfig.IsIssuerPresent()) - { - if (certType == CertType::kRoot) + // issuer Name + if (certConfig.IsIssuerPresent()) { - ReturnErrorOnFailure(subjectDN->EncodeToTLV(writer, ContextTag(kTag_Issuer))); + if (certType == CertType::kRoot) + { + ReturnErrorOnFailure(subjectDN->EncodeToTLV(writer, ContextTag(kTag_Issuer))); + } + else + { + uint8_t caChipCertBuf[kMaxCHIPCertLength]; + MutableByteSpan caChipCert(caChipCertBuf); + VerifyOrReturnError(true == X509ToChipCert(caCert, caChipCert), CHIP_ERROR_INVALID_ARGUMENT); + ChipDN issuerDN; + ReturnErrorOnFailure(ExtractSubjectDNFromChipCert(caChipCert, issuerDN)); + ReturnErrorOnFailure(issuerDN.EncodeToTLV(writer, ContextTag(kTag_Issuer))); + } + } + + // validity + uint32_t validFromChipEpoch; + uint32_t validToChipEpoch; + + VerifyOrReturnError( + true == + CalendarToChipEpochTime(static_cast(validFrom.tm_year + 1900), static_cast(validFrom.tm_mon + 1), + static_cast(validFrom.tm_mday), static_cast(validFrom.tm_hour), + static_cast(validFrom.tm_min), static_cast(validFrom.tm_sec), + validFromChipEpoch), + CHIP_ERROR_INVALID_ARGUMENT); + if (validDays == kCertValidDays_NoWellDefinedExpiration) + { + validToChipEpoch = 0; } else { - uint8_t caChipCertBuf[kMaxCHIPCertLength]; - MutableByteSpan caChipCert(caChipCertBuf); - VerifyOrReturnError(true == X509ToChipCert(caCert, caChipCert), CHIP_ERROR_INVALID_ARGUMENT); - ChipDN issuerDN; - ReturnErrorOnFailure(ExtractSubjectDNFromChipCert(caChipCert, issuerDN)); - ReturnErrorOnFailure(issuerDN.EncodeToTLV(writer, ContextTag(kTag_Issuer))); + VerifyOrReturnError(CanCastTo(validFromChipEpoch + validDays * kSecondsPerDay - 1), + CHIP_ERROR_INVALID_ARGUMENT); + validToChipEpoch = validFromChipEpoch + validDays * kSecondsPerDay - 1; + } + if (!certConfig.IsValidityCorrect()) + { + uint32_t validTemp = validFromChipEpoch; + validFromChipEpoch = validToChipEpoch; + validToChipEpoch = validTemp; + } + if (certConfig.IsValidityNotBeforePresent()) + { + ReturnErrorOnFailure(writer.Put(ContextTag(kTag_NotBefore), validFromChipEpoch)); + } + if (certConfig.IsValidityNotAfterPresent()) + { + ReturnErrorOnFailure(writer.Put(ContextTag(kTag_NotAfter), validToChipEpoch)); } - } - - // validity - uint32_t validFromChipEpoch; - uint32_t validToChipEpoch; - - VerifyOrReturnError(true == - CalendarToChipEpochTime( - static_cast(validFrom.tm_year + 1900), static_cast(validFrom.tm_mon + 1), - static_cast(validFrom.tm_mday), static_cast(validFrom.tm_hour), - static_cast(validFrom.tm_min), static_cast(validFrom.tm_sec), validFromChipEpoch), - CHIP_ERROR_INVALID_ARGUMENT); - if (validDays == kCertValidDays_NoWellDefinedExpiration) - { - validToChipEpoch = 0; - } - else - { - VerifyOrReturnError(CanCastTo(validFromChipEpoch + validDays * kSecondsPerDay - 1), CHIP_ERROR_INVALID_ARGUMENT); - validToChipEpoch = validFromChipEpoch + validDays * kSecondsPerDay - 1; - } - if (!certConfig.IsValidityCorrect()) - { - uint32_t validTemp = validFromChipEpoch; - validFromChipEpoch = validToChipEpoch; - validToChipEpoch = validTemp; - } - if (!useCompactIdentityFormat && certConfig.IsValidityNotBeforePresent()) - { - ReturnErrorOnFailure(writer.Put(ContextTag(kTag_NotBefore), validFromChipEpoch)); - } - if (!useCompactIdentityFormat && certConfig.IsValidityNotAfterPresent()) - { - ReturnErrorOnFailure(writer.Put(ContextTag(kTag_NotAfter), validToChipEpoch)); - } - // subject Name - if (!useCompactIdentityFormat && certConfig.IsSubjectPresent()) - { - ReturnErrorOnFailure(subjectDN->EncodeToTLV(writer, ContextTag(kTag_Subject))); - } + // subject Name + if (certConfig.IsSubjectPresent()) + { + ReturnErrorOnFailure(subjectDN->EncodeToTLV(writer, ContextTag(kTag_Subject))); + } - // public key algorithm - if (!useCompactIdentityFormat) - { + // public key algorithm ReturnErrorOnFailure(writer.Put(ContextTag(kTag_PublicKeyAlgorithm), GetOIDEnum(kOID_PubKeyAlgo_ECPublicKey))); // public key curve Id @@ -1161,7 +1160,6 @@ CHIP_ERROR MakeCertTLV(CertType certType, const ToolChipDN * subjectDN, X509 * c ReturnErrorOnFailure(writer.PutBoolean(ContextTag(kTag_BasicConstraints_IsCA), certConfig.IsExtensionBasicCACorrect() ? isCA : !isCA)); } - // TODO if (pathLen != kPathLength_NotSpecified) { ReturnErrorOnFailure( From 2d4d453b3aa6134fe4387e3d3c9595146e49157b Mon Sep 17 00:00:00 2001 From: Karsten Sperling Date: Tue, 7 Nov 2023 11:02:05 +1300 Subject: [PATCH 6/6] Prefix out parameters in new APIs --- src/credentials/CHIPCert.cpp | 24 ++++++++++++------------ src/credentials/CHIPCert.h | 10 +++++----- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/credentials/CHIPCert.cpp b/src/credentials/CHIPCert.cpp index 2b15597b411cee..159526e296f738 100644 --- a/src/credentials/CHIPCert.cpp +++ b/src/credentials/CHIPCert.cpp @@ -1440,12 +1440,12 @@ void InitNetworkIdentitySubject(ChipDN & name) VerifyOrDie(err == CHIP_NO_ERROR); // AddAttribute can't fail in this case } -static CHIP_ERROR CalculateKeyIdentifierSha256(const P256PublicKeySpan & publicKey, MutableCertificateKeyId keyId) +static CHIP_ERROR CalculateKeyIdentifierSha256(const P256PublicKeySpan & publicKey, MutableCertificateKeyId outKeyId) { uint8_t hash[kSHA256_Hash_Length]; - static_assert(keyId.size() <= sizeof(hash)); // truncating 32 bytes down to 20 + static_assert(outKeyId.size() <= sizeof(hash)); // truncating 32 bytes down to 20 ReturnErrorOnFailure(Hash_SHA256(publicKey.data(), publicKey.size(), hash)); - memcpy(keyId.data(), hash, keyId.size()); + memcpy(outKeyId.data(), hash, outKeyId.size()); return CHIP_NO_ERROR; } @@ -1480,21 +1480,21 @@ CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert) return CHIP_NO_ERROR; } -CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId keyId) +CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId outKeyId) { ChipCertificateData certData; ReturnErrorOnFailure(DecodeChipCert(cert, certData, CertDecodeFlags::kGenerateTBSHash)); ReturnErrorOnFailure(ValidateChipNetworkIdentity(certData)); - ReturnErrorOnFailure(CalculateKeyIdentifierSha256(certData.mPublicKey, keyId)); + ReturnErrorOnFailure(CalculateKeyIdentifierSha256(certData.mPublicKey, outKeyId)); return CHIP_NO_ERROR; } -CHIP_ERROR ExtractIdentifierFromChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId keyId) +CHIP_ERROR ExtractIdentifierFromChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId outKeyId) { ChipCertificateData certData; ReturnErrorOnFailure(DecodeChipCert(cert, certData)); ReturnErrorOnFailure(ValidateCertificateType(certData, CertType::kNetworkIdentity)); - ReturnErrorOnFailure(CalculateKeyIdentifierSha256(certData.mPublicKey, keyId)); + ReturnErrorOnFailure(CalculateKeyIdentifierSha256(certData.mPublicKey, outKeyId)); return CHIP_NO_ERROR; } @@ -1525,22 +1525,22 @@ static CHIP_ERROR EncodeCompactIdentityCert(TLVWriter & writer, Tag tag, const P return CHIP_NO_ERROR; } -CHIP_ERROR NewChipNetworkIdentity(const Crypto::P256Keypair & keypair, MutableByteSpan & compactIdentityCert) +CHIP_ERROR NewChipNetworkIdentity(const Crypto::P256Keypair & keypair, MutableByteSpan & outCompactCert) { - VerifyOrReturnError(!compactIdentityCert.empty(), CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrReturnError(CanCastTo(compactIdentityCert.size()), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(!outCompactCert.empty(), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(CanCastTo(outCompactCert.size()), CHIP_ERROR_INVALID_ARGUMENT); Crypto::P256ECDSASignature signature; ReturnErrorOnFailure(GenerateNetworkIdentitySignature(keypair, signature)); TLVWriter writer; - writer.Init(compactIdentityCert); + writer.Init(outCompactCert); P256PublicKeySpan publicKeySpan(keypair.Pubkey().ConstBytes()); P256ECDSASignatureSpan signatureSpan(signature.ConstBytes()); ReturnErrorOnFailure(EncodeCompactIdentityCert(writer, AnonymousTag(), publicKeySpan, signatureSpan)); - compactIdentityCert.reduce_size(writer.GetLengthWritten()); + outCompactCert.reduce_size(writer.GetLengthWritten()); return CHIP_NO_ERROR; } diff --git a/src/credentials/CHIPCert.h b/src/credentials/CHIPCert.h index 7d731df7edd3d6..e2cf99da5263c9 100644 --- a/src/credentials/CHIPCert.h +++ b/src/credentials/CHIPCert.h @@ -563,7 +563,7 @@ CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert); * @see ValidateChipNetworkIdentity * @see ExtractIdentifierFromChipNetworkIdentity */ -CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId keyId); +CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId outKeyId); struct FutureExtension { @@ -623,12 +623,12 @@ CHIP_ERROR NewNodeOperationalX509Cert(const X509CertRequestParams & requestParam * @brief Generates a Network (Client) Identity certificate in TLV-encoded form. * * @param keypair The key pair underlying the identity. - * @param cert Buffer to store the signed certificate in compact-pdc-identity TLV format. - * Must be at least `kMaxCHIPCompactNetworkIdentityLength` bytes long. + * @param outCompactCert Buffer to store the signed certificate in compact-pdc-identity TLV format. + * Must be at least `kMaxCHIPCompactNetworkIdentityLength` bytes long. * * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise **/ -CHIP_ERROR NewChipNetworkIdentity(const Crypto::P256Keypair & keypair, MutableByteSpan & cert); +CHIP_ERROR NewChipNetworkIdentity(const Crypto::P256Keypair & keypair, MutableByteSpan & outCompactCert); /** * @brief @@ -866,7 +866,7 @@ CHIP_ERROR ExtractSubjectDNFromX509Cert(const ByteSpan & x509Cert, ChipDN & dn); * @return CHIP_NO_ERROR on success, CHIP_ERROR_WRONG_CERT_TYPE if the certificate is * not a Network (Client) Identity, or another CHIP_ERROR if parsing fails. */ -CHIP_ERROR ExtractIdentifierFromChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId keyId); +CHIP_ERROR ExtractIdentifierFromChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId outKeyId); } // namespace Credentials } // namespace chip