/* * * Copyright (c) 2020-2022 Project CHIP Authors * Copyright (c) 2019 Google LLC. * Copyright (c) 2013-2017 Nest Labs, Inc. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file * Unit tests for CHIP certificate functionality. * */ #include #include #include #include #include #include #include #include #include #include #include #include "CHIPCert_error_test_vectors.h" #include "CHIPCert_test_vectors.h" using namespace chip; using namespace chip::ASN1; using namespace chip::TLV; using namespace chip::Credentials; using namespace chip::TestCerts; using namespace chip::Crypto; enum { kStandardCertsCount = 3, }; static constexpr BitFlags sNullDecodeFlag; static constexpr BitFlags sGenTBSHashFlag(CertDecodeFlags::kGenerateTBSHash); static constexpr BitFlags sTrustAnchorFlag(CertDecodeFlags::kIsTrustAnchor); static constexpr BitFlags sNullLoadFlag; static constexpr BitFlags sDerFormFlag(TestCertLoadFlags::kDERForm); static constexpr BitFlags sSupIsCAFlag(TestCertLoadFlags::kSuppressIsCA); static constexpr BitFlags sSupKeyUsageFlag(TestCertLoadFlags::kSuppressKeyUsage); static constexpr BitFlags sSupKeyCertSignFlag(TestCertLoadFlags::kSuppressKeyCertSign); static constexpr BitFlags sPathLenZeroFlag(TestCertLoadFlags::kSetPathLenConstZero); static constexpr BitFlags sNullKPFlag; static constexpr BitFlags sSA(KeyPurposeFlags::kServerAuth); static constexpr BitFlags sCA(KeyPurposeFlags::kClientAuth); static constexpr BitFlags sCS(KeyPurposeFlags::kCodeSigning); static constexpr BitFlags sEP(KeyPurposeFlags::kEmailProtection); static constexpr BitFlags sTS(KeyPurposeFlags::kTimeStamping); // static constexpr BitFlags sOS(KeyPurposeFlags::kOCSPSigning); // unused static constexpr BitFlags sSAandCA(sSA, sCA); static constexpr BitFlags sSAandCS(sSA, sCS); static constexpr BitFlags sSAandEP(sSA, sEP); static constexpr BitFlags sSAandTS(sSA, sTS); static constexpr BitFlags sNullKUFlag; static constexpr BitFlags sDS(KeyUsageFlags::kDigitalSignature); static constexpr BitFlags sNR(KeyUsageFlags::kNonRepudiation); static constexpr BitFlags sKE(KeyUsageFlags::kKeyEncipherment); static constexpr BitFlags sDE(KeyUsageFlags::kDataEncipherment); static constexpr BitFlags sKA(KeyUsageFlags::kKeyAgreement); static constexpr BitFlags sKC(KeyUsageFlags::kKeyCertSign); static constexpr BitFlags sCR(KeyUsageFlags::kCRLSign); static constexpr BitFlags sEO(KeyUsageFlags::kEncipherOnly); static constexpr BitFlags sDO(KeyUsageFlags::kDecipherOnly); static constexpr BitFlags sDSandNR(sDS, sNR); static constexpr BitFlags sDSandKE(sDS, sKE); static constexpr BitFlags sDSandDE(sDS, sDE); static constexpr BitFlags sDSandKA(sDS, sKA); static constexpr BitFlags sDSandKC(sDS, sKC); static constexpr BitFlags sDSandCR(sDS, sCR); static constexpr BitFlags sDSandEO(sDS, sEO); static constexpr BitFlags sDSandDO(sDS, sDO); static constexpr BitFlags sKCandDS(sKC, sDS); static constexpr BitFlags sKCandNR(sKC, sNR); static constexpr BitFlags sKCandKE(sKC, sKE); static constexpr BitFlags sKCandDE(sKC, sDE); static constexpr BitFlags sKCandKA(sKC, sKA); static constexpr BitFlags sKCandCR(sKC, sCR); static constexpr BitFlags sKCandEO(sKC, sEO); static constexpr BitFlags sKCandDO(sKC, sDO); constexpr uint8_t sOID_Extension_SubjectAltName[] = { 0x55, 0x1d, 0x11 }; constexpr char kExtension_SubjectAltName[] = "test@example.com"; FutureExtension ext{ ByteSpan(sOID_Extension_SubjectAltName), ByteSpan(reinterpret_cast(const_cast(kExtension_SubjectAltName)), strlen(kExtension_SubjectAltName)) }; Optional kSubjectAltNameAsFutureExt(ext); static CHIP_ERROR LoadTestCertSet01(ChipCertificateSet & certSet) { CHIP_ERROR err; err = LoadTestCert(certSet, TestCert::kRoot01, sNullLoadFlag, sTrustAnchorFlag); SuccessOrExit(err); err = LoadTestCert(certSet, TestCert::kICA01, sNullLoadFlag, sGenTBSHashFlag); SuccessOrExit(err); err = LoadTestCert(certSet, TestCert::kNode01_01, sNullLoadFlag, sGenTBSHashFlag); SuccessOrExit(err); exit: return err; } static CHIP_ERROR SetCurrentTime(ValidationContext & validContext, uint16_t year, uint8_t mon, uint8_t day, uint8_t hour = 0, uint8_t min = 0, uint8_t sec = 0) { ASN1UniversalTime currentTime; currentTime.Year = year; currentTime.Month = mon; currentTime.Day = day; currentTime.Hour = hour; currentTime.Minute = min; currentTime.Second = sec; return validContext.SetEffectiveTimeFromAsn1Time(currentTime); } static CHIP_ERROR SetLastKnownGoodTime(ValidationContext & validContext, uint16_t year, uint8_t mon, uint8_t day, uint8_t hour = 0, uint8_t min = 0, uint8_t sec = 0) { ASN1UniversalTime lastKnownGoodTime; lastKnownGoodTime.Year = year; lastKnownGoodTime.Month = mon; lastKnownGoodTime.Day = day; lastKnownGoodTime.Hour = hour; lastKnownGoodTime.Minute = min; lastKnownGoodTime.Second = sec; return validContext.SetEffectiveTimeFromAsn1Time(lastKnownGoodTime); } static void ClearTimeSource(ValidationContext & validContext) { validContext.mEffectiveTime = EffectiveTime{}; } struct TestChipCert : public ::testing::Test { static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); } static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); } }; TEST_F(TestChipCert, TestChipCert_ChipToX509) { CHIP_ERROR err; ByteSpan inCert; ByteSpan expectedOutCert; uint8_t outCertBuf[kMaxDERCertLength]; for (size_t i = 0; i < gNumTestCerts; i++) { TestCert certType = gTestCerts[i]; err = GetTestCert(certType, sNullLoadFlag, inCert); EXPECT_EQ(err, CHIP_NO_ERROR); err = GetTestCert(certType, sDerFormFlag, expectedOutCert); EXPECT_EQ(err, CHIP_NO_ERROR); MutableByteSpan outCert(outCertBuf); err = ConvertChipCertToX509Cert(inCert, outCert); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_TRUE(expectedOutCert.data_equal(outCert)); } // Error Case: MutableByteSpan outCert(outCertBuf); err = ConvertChipCertToX509Cert(sTestCert_Node01_01_Err01_Chip, outCert); EXPECT_EQ(err, CHIP_ERROR_INVALID_TLV_TAG); } TEST_F(TestChipCert, TestChipCert_ChipToX509_ErrorCases) { CHIP_ERROR err; uint8_t outCertBuf[kMaxDERCertLength]; for (auto chipCert : gTestCert_ChipToX509_ErrorCases) { MutableByteSpan outCert(outCertBuf); err = ConvertChipCertToX509Cert(chipCert, outCert); EXPECT_NE(err, CHIP_NO_ERROR); } } TEST_F(TestChipCert, TestChipCert_ChipCertLoad_ErrorCases) { CHIP_ERROR err; ChipCertificateSet certSet; err = certSet.Init(1); EXPECT_EQ(err, CHIP_NO_ERROR); for (auto chipCert : gTestCert_ChipCertLoad_ErrorCases) { err = certSet.LoadCert(chipCert, sNullDecodeFlag); EXPECT_NE(err, CHIP_NO_ERROR); certSet.Clear(); } certSet.Release(); } TEST_F(TestChipCert, TestChipCert_ValidateChipRCAC_ErrorCases) { CHIP_ERROR err; for (auto chipCert : gTestCert_ValidateChipRCAC_ErrorCases) { err = ValidateChipRCAC(chipCert); EXPECT_NE(err, CHIP_NO_ERROR); } } TEST_F(TestChipCert, TestChipCert_GetCertType_ErrorCases) { CHIP_ERROR err; ChipCertificateSet certSet; err = certSet.Init(1); EXPECT_EQ(err, CHIP_NO_ERROR); for (auto chipCert : gTestCert_GetCertType_ErrorCases) { CertType certType; err = certSet.LoadCert(chipCert, sNullDecodeFlag); EXPECT_EQ(err, CHIP_NO_ERROR); err = certSet.GetCertSet()->mSubjectDN.GetCertType(certType); EXPECT_TRUE(err != CHIP_NO_ERROR || certType == CertType::kNotSpecified); certSet.Clear(); } certSet.Release(); } TEST_F(TestChipCert, TestChipCert_X509ToChip) { CHIP_ERROR err; ByteSpan inCert; ByteSpan expectedOutCert; uint8_t outCertBuf[kMaxCHIPCertLength]; for (size_t i = 0; i < gNumTestCerts; i++) { TestCert certType = gTestCerts[i]; err = GetTestCert(certType, sDerFormFlag, inCert); EXPECT_EQ(err, CHIP_NO_ERROR); err = GetTestCert(certType, sNullLoadFlag, expectedOutCert); EXPECT_EQ(err, CHIP_NO_ERROR); MutableByteSpan outCert(outCertBuf); err = ConvertX509CertToChipCert(inCert, outCert); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_TRUE(expectedOutCert.data_equal(outCert)); } } TEST_F(TestChipCert, TestChipCert_X509ToChip_ErrorCases) { CHIP_ERROR err; uint8_t outCertBuf[kMaxCHIPCertLength]; for (auto derCert : gTestCert_X509ToChip_ErrorCases) { MutableByteSpan outCert(outCertBuf); err = ConvertX509CertToChipCert(derCert, outCert); EXPECT_NE(err, CHIP_NO_ERROR); } } TEST_F(TestChipCert, TestChipCert_ChipDN) { const static char noc_rdn[] = "Test NOC"; const static char noc_rdn2[] = "John"; const static CATValues noc_cats = { { 0xABCD0001, chip::kUndefinedCAT, chip::kUndefinedCAT } }; ChipDN chip_dn; CertType certType = CertType::kFirmwareSigning; // Start with non-default value EXPECT_TRUE(chip_dn.IsEmpty()); EXPECT_EQ(chip_dn.RDNCount(), 0); EXPECT_EQ(chip_dn.GetCertType(certType), CHIP_NO_ERROR); EXPECT_TRUE(chip_dn.IsEmpty()); EXPECT_EQ(certType, CertType::kNotSpecified); EXPECT_EQ(chip_dn.AddAttribute_CommonName(CharSpan(noc_rdn, strlen(noc_rdn)), false), CHIP_NO_ERROR); EXPECT_EQ(chip_dn.AddAttribute_MatterNodeId(0xAAAABBBBCCCCDDDD), CHIP_NO_ERROR); EXPECT_EQ(chip_dn.AddAttribute_MatterFabricId(0xFAB00000FAB00001), CHIP_NO_ERROR); EXPECT_EQ(chip_dn.AddAttribute_GivenName(CharSpan(noc_rdn2, strlen(noc_rdn2)), true), CHIP_NO_ERROR); EXPECT_EQ(chip_dn.AddCATs(noc_cats), CHIP_NO_ERROR); EXPECT_EQ(chip_dn.RDNCount(), 5); EXPECT_EQ(chip_dn.AddAttribute_GivenName(CharSpan(noc_rdn2, strlen(noc_rdn2)), true), CHIP_ERROR_NO_MEMORY); EXPECT_EQ(chip_dn.RDNCount(), 5); EXPECT_EQ(chip_dn.GetCertType(certType), CHIP_NO_ERROR); EXPECT_EQ(certType, CertType::kNode); uint64_t certId; EXPECT_EQ(chip_dn.GetCertChipId(certId), CHIP_NO_ERROR); EXPECT_EQ(certId, 0xAAAABBBBCCCCDDDD); uint64_t fabricId; EXPECT_EQ(chip_dn.GetCertFabricId(fabricId), CHIP_NO_ERROR); EXPECT_EQ(fabricId, 0xFAB00000FAB00001); chip_dn.Clear(); EXPECT_EQ(chip_dn.GetCertType(certType), CHIP_NO_ERROR); EXPECT_TRUE(chip_dn.IsEmpty()); EXPECT_EQ(certType, CertType::kNotSpecified); CATValues noc_cats2; chip::CATValues::Serialized serializedCATs; EXPECT_EQ(noc_cats.Serialize(serializedCATs), CHIP_NO_ERROR); EXPECT_EQ(noc_cats2.Deserialize(serializedCATs), CHIP_NO_ERROR); EXPECT_EQ(memcmp(&noc_cats, &noc_cats2, chip::CATValues::kSerializedLength), 0); CATValues noc_cats3 = { { 0xABCD0001, 0xFFEEAA00, 0x0001F012 } }; EXPECT_EQ(noc_cats3.Serialize(serializedCATs), CHIP_NO_ERROR); EXPECT_EQ(noc_cats2.Deserialize(serializedCATs), CHIP_NO_ERROR); EXPECT_EQ(memcmp(&noc_cats3, &noc_cats2, chip::CATValues::kSerializedLength), 0); } TEST_F(TestChipCert, TestChipCert_CertValidation) { CHIP_ERROR err; ChipCertificateSet certSet; ValidationContext validContext; enum { kMaxCertsPerTestCase = 10 }; struct ValidationTestCase { int mSubjectCertIndex; uint8_t mValidateFlags; CertType mRequiredCertType; CHIP_ERROR mExpectedResult; int mExpectedCertIndex; int mExpectedTrustAnchorIndex; struct { TestCert Type; BitFlags DecodeFlags; BitFlags LoadFlags; } InputCerts[kMaxCertsPerTestCase]; }; // Short-hand names to make the test cases table more concise. const auto CTNS = CertType::kNotSpecified; const auto CTCA = CertType::kICA; const auto CTNode = CertType::kNode; // clang-format off static const ValidationTestCase sValidationTestCases[] = { // Reqd Exp Exp Cert Cert // Subj Valid Cert Cert TA Cert Decode Load // Ind Flags Type Expected Result Index Index Type Flags Flags // ================================================================================================================================== // Basic validation of node certificate with different load orders. { 2, 0, CTNS, CHIP_NO_ERROR, 2, 0, { { TestCert::kRoot01, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kICA01, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kNode01_01, sGenTBSHashFlag, sNullLoadFlag } } }, { 1, 0, CTNS, CHIP_NO_ERROR, 1, 0, { { TestCert::kRoot01, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kNode01_01, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kICA01, sGenTBSHashFlag, sNullLoadFlag } } }, { 0, 0, CTNS, CHIP_NO_ERROR, 0, 2, { { TestCert::kNode01_01, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kICA01, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kRoot01, sTrustAnchorFlag, sNullLoadFlag } } }, // Basic validation of certificate, which is signed by Root. { 0, 0, CTNS, CHIP_NO_ERROR, 0, 1, { { TestCert::kNode01_02, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kRoot01, sTrustAnchorFlag, sNullLoadFlag } } }, { 0, 0, CTNS, CHIP_NO_ERROR, 0, 2, { { TestCert::kNode01_02, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kICA01, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kRoot01, sTrustAnchorFlag, sNullLoadFlag } } }, { 0, 0, CTNS, CHIP_NO_ERROR, 0, 3, { { TestCert::kNode01_02, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kICA01, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kRoot02, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kRoot01, sTrustAnchorFlag, sNullLoadFlag } } }, // Basic validation of node certificates chaining up to another root. { 2, 0, CTNS, CHIP_NO_ERROR, 2, 0, { { TestCert::kRoot02, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kICA02, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kNode02_01, sGenTBSHashFlag, sNullLoadFlag } } }, { 1, 0, CTNS, CHIP_NO_ERROR, 1, 0, { { TestCert::kRoot02, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kNode02_01, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kICA02, sGenTBSHashFlag, sNullLoadFlag } } }, { 0, 0, CTNS, CHIP_NO_ERROR, 0, 2, { { TestCert::kNode02_01, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kICA02, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kRoot02, sTrustAnchorFlag, sNullLoadFlag } } }, { 0, 0, CTNS, CHIP_NO_ERROR, 0, 2, { { TestCert::kNode02_02, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kICA02, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kRoot02, sTrustAnchorFlag, sNullLoadFlag } } }, { 0, 0, CTNS, CHIP_NO_ERROR, 0, 2, { { TestCert::kNode02_03, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kICA02, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kRoot02, sTrustAnchorFlag, sNullLoadFlag } } }, { 0, 0, CTNS, CHIP_NO_ERROR, 0, 2, { { TestCert::kNode02_04, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kICA02, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kRoot02, sTrustAnchorFlag, sNullLoadFlag } } }, { 0, 0, CTNS, CHIP_NO_ERROR, 0, 2, { { TestCert::kNode02_05, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kICA02, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kRoot02, sTrustAnchorFlag, sNullLoadFlag } } }, { 0, 0, CTNS, CHIP_NO_ERROR, 0, 2, { { TestCert::kNode02_06, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kICA02, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kRoot02, sTrustAnchorFlag, sNullLoadFlag } } }, // Failure due to presence of "critical" future-extension in the kNode02_07 node certificate. { 0, 0, CTNS, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED, 0, 2, { { TestCert::kNode02_07, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kICA02, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kRoot02, sTrustAnchorFlag, sNullLoadFlag } } }, // Validation with two copies of root certificate, one trusted, one untrusted. { 2, 0, CTNS, CHIP_NO_ERROR, 2, 1, { { TestCert::kRoot01, sNullDecodeFlag, sNullLoadFlag }, { TestCert::kRoot01, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kNode01_01, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kICA01, sGenTBSHashFlag, sNullLoadFlag } } }, // Validation with two trusted certificates. { 2, 0, CTNS, CHIP_NO_ERROR, 2, 1, { { TestCert::kRoot02, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kRoot01, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kNode01_01, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kICA01, sGenTBSHashFlag, sNullLoadFlag } } }, // Failure due to missing CA certificate. { 1, 0, CTNS, CHIP_ERROR_CA_CERT_NOT_FOUND, -1, -1, { { TestCert::kRoot01, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kNode01_01, sGenTBSHashFlag, sNullLoadFlag } } }, { 2, 0, CTNS, CHIP_ERROR_CA_CERT_NOT_FOUND, -1, -1, { { TestCert::kRoot01, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kICA02, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kNode01_01, sGenTBSHashFlag, sNullLoadFlag } } }, // Failure due to missing root certificate. { 1, 0, CTNS, CHIP_ERROR_CA_CERT_NOT_FOUND, -1, -1, { { TestCert::kICA01, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kNode01_01, sGenTBSHashFlag, sNullLoadFlag } } }, { 1, 0, CTNS, CHIP_ERROR_CA_CERT_NOT_FOUND, -1, -1, { { TestCert::kRoot02, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kNode01_01, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kICA01, sGenTBSHashFlag, sNullLoadFlag } } }, // Failure due to lack of TBS hash. { 1, 0, CTNS, CHIP_ERROR_INVALID_ARGUMENT, -1, -1, { { TestCert::kRoot01, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kNode01_01, sNullDecodeFlag, sNullLoadFlag }, { TestCert::kICA01, sGenTBSHashFlag, sNullLoadFlag } } }, // Failure due to untrusted root. { 1, 0, CTNS, CHIP_ERROR_CA_CERT_NOT_FOUND, -1, -1, { { TestCert::kRoot01, sNullDecodeFlag, sNullLoadFlag }, { TestCert::kNode01_01, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kICA01, sGenTBSHashFlag, sNullLoadFlag } } }, // Failure due to intermediate cert with isCA flag = false { 2, 0, CTNS, CHIP_ERROR_CA_CERT_NOT_FOUND, -1, -1, { { TestCert::kRoot01, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kICA01, sGenTBSHashFlag, sSupIsCAFlag }, { TestCert::kNode01_01, sGenTBSHashFlag, sNullLoadFlag } } }, // Failure due to CA cert with no key usage. { 2, 0, CTNS, CHIP_ERROR_CA_CERT_NOT_FOUND, -1, -1, { { TestCert::kRoot01, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kICA01, sGenTBSHashFlag, sSupKeyUsageFlag }, { TestCert::kNode01_01, sGenTBSHashFlag, sNullLoadFlag } } }, // Failure due to CA cert with no cert sign key usage. { 2, 0, CTNS, CHIP_ERROR_CA_CERT_NOT_FOUND, -1, -1, { { TestCert::kRoot01, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kICA01, sGenTBSHashFlag, sSupKeyCertSignFlag }, { TestCert::kNode01_01, sGenTBSHashFlag, sNullLoadFlag } } }, // Failure due to 3-level deep cert chain and root cert with path constraint == 0 { 2, 0, CTNS, CHIP_ERROR_CA_CERT_NOT_FOUND, -1, -1, { { TestCert::kRoot01, sTrustAnchorFlag, sPathLenZeroFlag }, { TestCert::kICA01, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kNode01_01, sGenTBSHashFlag, sNullLoadFlag } } }, // Require a specific certificate type. { 2, 0, CTNode, CHIP_NO_ERROR, 2, 0, { { TestCert::kRoot01, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kICA01, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kNode01_01, sGenTBSHashFlag, sNullLoadFlag } } }, // Failure due to required certificate type not found. { 2, 0, CTCA, CHIP_ERROR_WRONG_CERT_TYPE, -1, -1, { { TestCert::kRoot01, sTrustAnchorFlag, sNullLoadFlag }, { TestCert::kICA01, sGenTBSHashFlag, sNullLoadFlag }, { TestCert::kNode01_01, sGenTBSHashFlag, sNullLoadFlag } } }, }; // clang-format on for (const auto & testCase : sValidationTestCases) { const ChipCertificateData * resultCert = nullptr; err = certSet.Init(kMaxCertsPerTestCase); EXPECT_EQ(err, CHIP_NO_ERROR); for (auto inputCert : testCase.InputCerts) { if (inputCert.Type != TestCert::kNone) { err = LoadTestCert(certSet, inputCert.Type, inputCert.LoadFlags, inputCert.DecodeFlags); EXPECT_EQ(err, CHIP_NO_ERROR); } } // Make sure the test case is valid. EXPECT_TRUE(testCase.mSubjectCertIndex >= 0 && testCase.mSubjectCertIndex < certSet.GetCertCount()); if (testCase.mExpectedResult == CHIP_NO_ERROR) { EXPECT_TRUE(testCase.mExpectedCertIndex >= 0 && testCase.mExpectedCertIndex < certSet.GetCertCount()); EXPECT_TRUE(testCase.mExpectedTrustAnchorIndex >= 0 && testCase.mExpectedTrustAnchorIndex < certSet.GetCertCount()); } // Initialize the validation context. validContext.Reset(); err = SetCurrentTime(validContext, 2021, 1, 1); EXPECT_EQ(err, CHIP_NO_ERROR); validContext.mRequiredKeyUsages.Set(KeyUsageFlags::kDigitalSignature); validContext.mRequiredKeyPurposes.Set(KeyPurposeFlags::kServerAuth); validContext.mRequiredKeyPurposes.Set(KeyPurposeFlags::kClientAuth); validContext.mRequiredCertType = testCase.mRequiredCertType; // Locate the subject DN and key id that will be used as input the FindValidCert() method. const ChipDN & subjectDN = certSet.GetCertSet()[testCase.mSubjectCertIndex].mSubjectDN; const CertificateKeyId & subjectKeyId = certSet.GetCertSet()[testCase.mSubjectCertIndex].mSubjectKeyId; // Invoke the FindValidCert() method (the method being tested). err = certSet.FindValidCert(subjectDN, subjectKeyId, validContext, &resultCert); EXPECT_EQ(err, testCase.mExpectedResult); // If the test case is expected to be successful... if (err == CHIP_NO_ERROR) { // Verify that the method found the correct certificate. EXPECT_EQ(resultCert, &certSet.GetCertSet()[testCase.mExpectedCertIndex]); // Verify that the method selected the correct trust anchor. EXPECT_EQ(validContext.mTrustAnchor, &certSet.GetCertSet()[testCase.mExpectedTrustAnchorIndex]); } // Clear the certificate set. certSet.Release(); } } TEST_F(TestChipCert, TestChipCert_CertValidTime) { CHIP_ERROR err; ChipCertificateSet certSet; ValidationContext validContext; err = certSet.Init(kStandardCertsCount); EXPECT_EQ(err, CHIP_NO_ERROR); err = LoadTestCertSet01(certSet); EXPECT_EQ(err, CHIP_NO_ERROR); validContext.Reset(); validContext.mRequiredKeyUsages.Set(KeyUsageFlags::kDigitalSignature); validContext.mRequiredKeyPurposes.Set(KeyPurposeFlags::kServerAuth); validContext.mRequiredKeyPurposes.Set(KeyPurposeFlags::kClientAuth); Credentials::StrictCertificateValidityPolicyExample strictCertificateValidityPolicy; Credentials::LastKnownGoodTimeCertificateValidityPolicyExample lastKnownGoodTimeValidityPolicy; // No time source available. ClearTimeSource(validContext); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Strict policy validContext.mValidityPolicy = &strictCertificateValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimeValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Current time before certificate validity period. err = SetCurrentTime(validContext, 2020, 1, 3); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_NOT_VALID_YET); // Strict policy validContext.mValidityPolicy = &strictCertificateValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_NOT_VALID_YET); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimeValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_NOT_VALID_YET); // Current time 1 second before validity period. err = SetCurrentTime(validContext, 2020, 10, 15, 14, 23, 42); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_NOT_VALID_YET); // Strict policy validContext.mValidityPolicy = &strictCertificateValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_NOT_VALID_YET); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimeValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_NOT_VALID_YET); // Current time 1st second of validity period. err = SetCurrentTime(validContext, 2020, 10, 15, 14, 23, 43); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Strict policy validContext.mValidityPolicy = &strictCertificateValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimeValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Current time within validity period. err = SetCurrentTime(validContext, 2022, 02, 23, 12, 30, 01); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Strict policy validContext.mValidityPolicy = &strictCertificateValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimeValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Current time at last second of validity period. err = SetCurrentTime(validContext, 2040, 10, 15, 14, 23, 42); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Strict policy validContext.mValidityPolicy = &strictCertificateValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimeValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Current time at 1 second after end of certificate validity period. err = SetCurrentTime(validContext, 2040, 10, 15, 14, 23, 43); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); // Strict policy validContext.mValidityPolicy = &strictCertificateValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimeValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); // Current time after end of certificate validity period. err = SetCurrentTime(validContext, 2042, 4, 25, 0, 0, 0); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); // Strict policy validContext.mValidityPolicy = &strictCertificateValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimeValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); // Last known good time before certificate validity period. // We can't invalidate based on NotBefore with Last Known Good Time. // Hence, we expect CHIP_NO_ERROR. err = SetLastKnownGoodTime(validContext, 2020, 1, 3); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Strict policy validContext.mValidityPolicy = &strictCertificateValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimeValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Last known good time 1 second before certificate validity period. // We can't invalidate based on NotBefore with Last Known Good Time. // Hence, we expect CHIP_NO_ERROR. err = SetLastKnownGoodTime(validContext, 2020, 10, 15, 14, 23, 42); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Strict policy validContext.mValidityPolicy = &strictCertificateValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimeValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Last Known Good Time 1st second of validity period. err = SetLastKnownGoodTime(validContext, 2020, 10, 15, 14, 23, 43); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Strict policy validContext.mValidityPolicy = &strictCertificateValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimeValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Last Known Good Time within validity period. err = SetLastKnownGoodTime(validContext, 2022, 02, 23, 12, 30, 01); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Strict policy validContext.mValidityPolicy = &strictCertificateValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimeValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Last Known Good Time at last second of validity period. err = SetLastKnownGoodTime(validContext, 2040, 10, 15, 14, 23, 42); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Strict policy validContext.mValidityPolicy = &strictCertificateValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimeValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Last Known Good Time at 1 second after end of certificate validity period. err = SetLastKnownGoodTime(validContext, 2040, 10, 15, 14, 23, 43); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Strict policy validContext.mValidityPolicy = &strictCertificateValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimeValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); // Last Known Good Time after end of certificate validity period. err = SetLastKnownGoodTime(validContext, 2042, 4, 25, 0, 0, 0); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Strict policy validContext.mValidityPolicy = &strictCertificateValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimeValidityPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); certSet.Release(); } TEST_F(TestChipCert, TestChipCert_ValidateChipRCAC) { struct RCACTestCase { TestCert Cert; CHIP_ERROR mExpectedResult; }; // clang-format off static RCACTestCase sRCACTestCases[] = { // Cert Expected Result // ==================================================== { TestCert::kRoot01, CHIP_NO_ERROR }, { TestCert::kRoot02, CHIP_NO_ERROR }, { TestCert::kICA01, CHIP_ERROR_WRONG_CERT_TYPE }, { TestCert::kICA02, CHIP_ERROR_WRONG_CERT_TYPE }, { TestCert::kICA01_1, CHIP_ERROR_WRONG_CERT_TYPE }, { TestCert::kFWSign01, CHIP_ERROR_WRONG_CERT_TYPE }, { TestCert::kNode01_01, CHIP_ERROR_WRONG_CERT_TYPE }, { TestCert::kNode02_08, CHIP_ERROR_WRONG_CERT_TYPE }, }; // clang-format on for (auto & testCase : sRCACTestCases) { ByteSpan cert; EXPECT_EQ(GetTestCert(testCase.Cert, sNullLoadFlag, cert), CHIP_NO_ERROR); EXPECT_EQ(ValidateChipRCAC(cert), testCase.mExpectedResult); } } class AlwaysAcceptValidityPolicy : public CertificateValidityPolicy { public: ~AlwaysAcceptValidityPolicy() {} CHIP_ERROR ApplyCertificateValidityPolicy(const ChipCertificateData * cert, uint8_t depth, CertificateValidityResult result) override { return CHIP_NO_ERROR; } }; class AlwaysRejectValidityPolicy : public CertificateValidityPolicy { public: ~AlwaysRejectValidityPolicy() {} CHIP_ERROR ApplyCertificateValidityPolicy(const ChipCertificateData * cert, uint8_t depth, CertificateValidityResult result) override { return CHIP_ERROR_CERT_EXPIRED; } }; TEST_F(TestChipCert, TestChipCert_CertValidityPolicyInjection) { CHIP_ERROR err; ChipCertificateSet certSet; ValidationContext validContext; StrictCertificateValidityPolicyExample strictPolicy; LastKnownGoodTimeCertificateValidityPolicyExample lastKnownGoodTimePolicy; AlwaysAcceptValidityPolicy alwaysAcceptPolicy; AlwaysRejectValidityPolicy alwaysRejectPolicy; err = certSet.Init(kStandardCertsCount); EXPECT_EQ(err, CHIP_NO_ERROR); err = LoadTestCertSet01(certSet); EXPECT_EQ(err, CHIP_NO_ERROR); validContext.Reset(); validContext.mRequiredKeyUsages.Set(KeyUsageFlags::kDigitalSignature); validContext.mRequiredKeyPurposes.Set(KeyPurposeFlags::kServerAuth); validContext.mRequiredKeyPurposes.Set(KeyPurposeFlags::kClientAuth); // Current time unknown. // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Strict policy validContext.mValidityPolicy = &strictPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimePolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Always accept policy validContext.mValidityPolicy = &alwaysAcceptPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Always reject policy validContext.mValidityPolicy = &alwaysRejectPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_NE(err, CHIP_NO_ERROR); // Curent time before certificate validity period. err = SetCurrentTime(validContext, 2020, 1, 3); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_NOT_VALID_YET); // Strict policy validContext.mValidityPolicy = &strictPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_NOT_VALID_YET); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimePolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_NOT_VALID_YET); // Always accept policy validContext.mValidityPolicy = &alwaysAcceptPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Always reject policy validContext.mValidityPolicy = &alwaysRejectPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_NE(err, CHIP_NO_ERROR); // Last known good time before certificate validity period. err = SetLastKnownGoodTime(validContext, 2020, 1, 3); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Strict policy validContext.mValidityPolicy = &strictPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Always accept policy validContext.mValidityPolicy = &alwaysAcceptPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Always reject policy validContext.mValidityPolicy = &alwaysRejectPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_NE(err, CHIP_NO_ERROR); // Current time during validity period err = SetCurrentTime(validContext, 2022, 02, 23, 12, 30, 01); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Strict policy validContext.mValidityPolicy = &strictPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Always accept policy validContext.mValidityPolicy = &alwaysAcceptPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Always reject policy validContext.mValidityPolicy = &alwaysRejectPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_NE(err, CHIP_NO_ERROR); // Last Known Good Time during validity period err = SetLastKnownGoodTime(validContext, 2022, 02, 23, 12, 30, 01); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Strict policy validContext.mValidityPolicy = &strictPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimePolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Always accept policy validContext.mValidityPolicy = &alwaysAcceptPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Always reject policy validContext.mValidityPolicy = &alwaysRejectPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_NE(err, CHIP_NO_ERROR); // Current time after end of certificate validity period. err = SetCurrentTime(validContext, 2042, 4, 25, 0, 0, 0); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); // Strict policy validContext.mValidityPolicy = &strictPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimePolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); // Always accept policy validContext.mValidityPolicy = &alwaysAcceptPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Always reject policy validContext.mValidityPolicy = &alwaysRejectPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_NE(err, CHIP_NO_ERROR); // Last Known Good Time after end of certificate validity period. err = SetLastKnownGoodTime(validContext, 2042, 4, 25, 0, 0, 0); EXPECT_EQ(err, CHIP_NO_ERROR); // Default policy validContext.mValidityPolicy = nullptr; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Strict policy validContext.mValidityPolicy = &strictPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); // Last Known Good Time policy validContext.mValidityPolicy = &lastKnownGoodTimePolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_ERROR_CERT_EXPIRED); // Always accept policy validContext.mValidityPolicy = &alwaysAcceptPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_EQ(err, CHIP_NO_ERROR); // Always reject policy validContext.mValidityPolicy = &alwaysRejectPolicy; err = certSet.ValidateCert(certSet.GetLastCert(), validContext); EXPECT_NE(err, CHIP_NO_ERROR); certSet.Release(); } TEST_F(TestChipCert, TestChipCert_CertUsage) { CHIP_ERROR err; ChipCertificateSet certSet; ValidationContext validContext; ChipCertificateData certDataArray[kStandardCertsCount]; struct UsageTestCase { uint8_t mCertIndex; BitFlags mRequiredKeyUsages; BitFlags mRequiredKeyPurposes; CHIP_ERROR mExpectedResult; }; // clang-format off static UsageTestCase sUsageTestCases[] = { // Cert Key // Ind Usages Key Purposes Expected Result // ========================================================================= // ----- Key Usages for leaf Certificate ----- { 2, sDS, sNullKPFlag, CHIP_NO_ERROR }, { 2, sNR, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sKE, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sDE, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sKA, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sKC, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sCR, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sEO, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sDO, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sDSandNR, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sDSandKE, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sDSandDE, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sDSandKA, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sDSandKC, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sDSandCR, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sDSandEO, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sDSandDO, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, // ----- Key Usages for CA Certificate ----- { 1, sDS, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sNR, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sKE, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sDE, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sKA, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sKC, sNullKPFlag, CHIP_NO_ERROR }, { 1, sCR, sNullKPFlag, CHIP_NO_ERROR }, { 1, sEO, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sDO, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sKCandDS, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sKCandNR, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sKCandKE, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sKCandDE, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sKCandKA, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sKCandCR, sNullKPFlag, CHIP_NO_ERROR }, { 1, sKCandEO, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sKCandDO, sNullKPFlag, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, // ----- Key Purposes for leaf Certificate ----- { 2, sNullKUFlag, sSA, CHIP_NO_ERROR }, { 2, sNullKUFlag, sCA, CHIP_NO_ERROR }, { 2, sNullKUFlag, sCS, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sNullKUFlag, sEP, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sNullKUFlag, sTS, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sNullKUFlag, sSAandCA, CHIP_NO_ERROR }, { 2, sNullKUFlag, sSAandCS, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sNullKUFlag, sSAandEP, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sNullKUFlag, sSAandTS, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, // ----- Key Purposes for CA Certificate ----- { 1, sNullKUFlag, sSA, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sNullKUFlag, sCA, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sNullKUFlag, sCS, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sNullKUFlag, sEP, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sNullKUFlag, sTS, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sNullKUFlag, sSAandCA, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sNullKUFlag, sSAandCS, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sNullKUFlag, sSAandEP, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 1, sNullKUFlag, sSAandTS, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, // ----- Combinations ----- { 2, sDSandNR, sSAandCA, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sDS, sSAandCS, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sDSandKE, sSAandCA, CHIP_ERROR_CERT_USAGE_NOT_ALLOWED }, { 2, sDS, sSAandCA, CHIP_NO_ERROR }, }; // clang-format on size_t sNumUsageTestCases = ArraySize(sUsageTestCases); err = certSet.Init(certDataArray, ArraySize(certDataArray)); EXPECT_EQ(err, CHIP_NO_ERROR); err = LoadTestCertSet01(certSet); EXPECT_EQ(err, CHIP_NO_ERROR); for (size_t i = 0; i < sNumUsageTestCases; i++) { validContext.Reset(); validContext.mRequiredKeyUsages = sUsageTestCases[i].mRequiredKeyUsages; validContext.mRequiredKeyPurposes = sUsageTestCases[i].mRequiredKeyPurposes; err = SetCurrentTime(validContext, 2020, 10, 16); EXPECT_EQ(err, CHIP_NO_ERROR); err = certSet.ValidateCert(&certSet.GetCertSet()[sUsageTestCases[i].mCertIndex], validContext); EXPECT_EQ(err, sUsageTestCases[i].mExpectedResult); } certSet.Release(); } TEST_F(TestChipCert, TestChipCert_CertType) { CHIP_ERROR err; ChipCertificateData certData; struct TestCase { TestCert Cert; CertType ExpectedCertType; }; // clang-format off static TestCase sTestCases[] = { // Cert ExpectedCertType // ============================================================= { TestCert::kRoot01, CertType::kRoot }, { TestCert::kRoot02, CertType::kRoot }, { TestCert::kICA01, CertType::kICA }, { TestCert::kICA02, CertType::kICA }, { TestCert::kICA01_1, CertType::kICA }, { TestCert::kFWSign01, CertType::kFirmwareSigning }, { TestCert::kNode01_01, CertType::kNode }, { TestCert::kNode01_02, CertType::kNode }, { TestCert::kNode02_01, CertType::kNode }, { TestCert::kNode02_02, CertType::kNode }, { TestCert::kPDCID01, CertType::kNetworkIdentity }, }; // clang-format on for (const auto & testCase : sTestCases) { CertType certType; err = DecodeTestCert(certData, testCase.Cert); EXPECT_EQ(err, CHIP_NO_ERROR); err = certData.mSubjectDN.GetCertType(certType); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_EQ(certType, testCase.ExpectedCertType); } } TEST_F(TestChipCert, TestChipCert_CertId) { CHIP_ERROR err; ChipCertificateData certData; struct TestCase { TestCert Cert; uint64_t ExpectedCertId; }; // clang-format off static TestCase sTestCases[] = { // Cert ExpectedCertId // ============================================================= { TestCert::kRoot01, 0xCACACACA00000001 }, { TestCert::kRoot02, 0xCACACACA00000002 }, { TestCert::kICA01, 0xCACACACA00000003 }, { TestCert::kICA02, 0xCACACACA00000004 }, { TestCert::kICA01_1, 0xCACACACA00000005 }, { TestCert::kFWSign01, 0xFFFFFFFF00000001 }, { TestCert::kNode01_01, 0xDEDEDEDE00010001 }, { TestCert::kNode01_02, 0xDEDEDEDE00010002 }, { TestCert::kNode02_01, 0xDEDEDEDE00020001 }, { TestCert::kNode02_02, 0xDEDEDEDE00020002 }, { TestCert::kPDCID01, 0 }, }; // clang-format on for (const auto & testCase : sTestCases) { uint64_t chipId; err = DecodeTestCert(certData, testCase.Cert); EXPECT_EQ(err, CHIP_NO_ERROR); err = certData.mSubjectDN.GetCertChipId(chipId); if (testCase.ExpectedCertId != 0) { EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_EQ(chipId, testCase.ExpectedCertId); } else { EXPECT_EQ(err, CHIP_ERROR_WRONG_CERT_DN); } } } TEST_F(TestChipCert, TestChipCert_DecodingOptions) { CHIP_ERROR err; ByteSpan cert; ChipCertificateData certData; err = GetTestCert(TestCert::kRoot01, sNullLoadFlag, cert); EXPECT_EQ(err, CHIP_NO_ERROR); // Decode with default (null) options err = DecodeChipCert(cert, certData); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_FALSE(certData.mCertFlags.Has(CertFlags::kIsTrustAnchor)); // Decode as trust anchor err = DecodeChipCert(cert, certData, CertDecodeFlags::kIsTrustAnchor); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_TRUE(certData.mCertFlags.Has(CertFlags::kIsTrustAnchor)); // Decode with TBS Hash calculation err = DecodeChipCert(cert, certData, CertDecodeFlags::kGenerateTBSHash); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_TRUE(certData.mCertFlags.Has(CertFlags::kTBSHashPresent)); // When the TBS hash is available signature verification should work err = VerifyCertSignature(certData, certData); // test cert is a self-signed root EXPECT_EQ(err, CHIP_NO_ERROR); } TEST_F(TestChipCert, TestChipCert_LoadDuplicateCerts) { CHIP_ERROR err; ChipCertificateSet certSet; ValidationContext validContext; err = certSet.Init(kStandardCertsCount); EXPECT_EQ(err, CHIP_NO_ERROR); // Let's load two distinct certificates, and make sure cert count is 2 err = LoadTestCert(certSet, TestCert::kRoot01, sNullLoadFlag, sTrustAnchorFlag); EXPECT_EQ(err, CHIP_NO_ERROR); err = LoadTestCert(certSet, TestCert::kICA01, sNullLoadFlag, sGenTBSHashFlag); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_EQ(certSet.GetCertCount(), 2); // Let's load a previously loaded cert and make sure cert count is still 2 err = LoadTestCert(certSet, TestCert::kRoot01, sNullLoadFlag, sTrustAnchorFlag); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_EQ(certSet.GetCertCount(), 2); // Let's load the other previously loaded cert and make sure cert count is still 2 err = LoadTestCert(certSet, TestCert::kICA01, sNullLoadFlag, sGenTBSHashFlag); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_EQ(certSet.GetCertCount(), 2); // Let's load a new cert and make sure cert count updates to 3 err = LoadTestCert(certSet, TestCert::kNode01_01, sNullLoadFlag, sGenTBSHashFlag); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_EQ(certSet.GetCertCount(), 3); } TEST_F(TestChipCert, TestChipCert_GenerateRootCert) { // Generate a new keypair for cert signing P256Keypair keypair; EXPECT_EQ(keypair.Initialize(ECPKeyTarget::ECDSA), CHIP_NO_ERROR); uint8_t signed_cert[kMaxDERCertLength]; ChipCertificateData certData; ChipDN root_dn; EXPECT_EQ(root_dn.AddAttribute_MatterRCACId(0xabcdabcd), CHIP_NO_ERROR); X509CertRequestParams root_params = { 1234, 631161876, 729942000, root_dn, root_dn }; MutableByteSpan signed_cert_span(signed_cert); EXPECT_EQ(NewRootX509Cert(root_params, keypair, signed_cert_span), CHIP_NO_ERROR); uint8_t outCertBuf[kMaxCHIPCertLength]; MutableByteSpan outCert(outCertBuf); EXPECT_EQ(ConvertX509CertToChipCert(signed_cert_span, outCert), CHIP_NO_ERROR); EXPECT_EQ(DecodeChipCert(outCert, certData), CHIP_NO_ERROR); // Test with FutureExtension X509CertRequestParams root_params2 = { 1234, 631161876, 729942000, root_dn, root_dn, kSubjectAltNameAsFutureExt }; MutableByteSpan signed_cert_span2(signed_cert); EXPECT_EQ(NewRootX509Cert(root_params2, keypair, signed_cert_span2), CHIP_NO_ERROR); outCert = MutableByteSpan(outCertBuf); EXPECT_EQ(ConvertX509CertToChipCert(signed_cert_span2, outCert), CHIP_NO_ERROR); EXPECT_EQ(DecodeChipCert(outCert, certData), CHIP_NO_ERROR); // Test with no defined notAfter time. { X509CertRequestParams root_params3 = { .SerialNumber = 1234, .ValidityStart = 631161876, .ValidityEnd = kNullCertTime, .SubjectDN = root_dn, .IssuerDN = root_dn }; MutableByteSpan signed_cert_span_no_expiry(signed_cert); EXPECT_EQ(NewRootX509Cert(root_params3, keypair, signed_cert_span_no_expiry), CHIP_NO_ERROR); outCert = MutableByteSpan(outCertBuf); EXPECT_EQ(ConvertX509CertToChipCert(signed_cert_span_no_expiry, outCert), CHIP_NO_ERROR); EXPECT_EQ(DecodeChipCert(outCert, certData), CHIP_NO_ERROR); EXPECT_EQ(certData.mNotAfterTime, kNullCertTime); } // Test error case: root cert subject provided ICA OID Attribute. root_params.SubjectDN.Clear(); EXPECT_EQ(root_params.SubjectDN.AddAttribute_MatterICACId(0xabcdabcd), CHIP_NO_ERROR); root_params.IssuerDN.Clear(); EXPECT_EQ(root_params.IssuerDN.AddAttribute_MatterICACId(0xabcdabcd), CHIP_NO_ERROR); MutableByteSpan signed_cert_span1(signed_cert); EXPECT_EQ(NewRootX509Cert(root_params, keypair, signed_cert_span1), CHIP_ERROR_INVALID_ARGUMENT); // Test error case: root cert provided different subject and issuer DNs. root_params.SubjectDN.Clear(); EXPECT_EQ(root_params.SubjectDN.AddAttribute_MatterRCACId(0xabcdabcd), CHIP_NO_ERROR); root_params.IssuerDN.Clear(); EXPECT_EQ(root_params.IssuerDN.AddAttribute_MatterRCACId(0xffffeeee), CHIP_NO_ERROR); EXPECT_EQ(NewRootX509Cert(root_params, keypair, signed_cert_span1), CHIP_ERROR_INVALID_ARGUMENT); // Test that serial number cannot be negative root_params.IssuerDN.Clear(); EXPECT_EQ(root_params.IssuerDN.AddAttribute_MatterRCACId(0xabcdabcd), CHIP_NO_ERROR); root_params.SerialNumber = -1; EXPECT_EQ(NewRootX509Cert(root_params, keypair, signed_cert_span1), CHIP_ERROR_INVALID_ARGUMENT); } TEST_F(TestChipCert, TestChipCert_GenerateRootFabCert) { // Generate a new keypair for cert signing P256Keypair keypair; EXPECT_EQ(keypair.Initialize(ECPKeyTarget::ECDSA), CHIP_NO_ERROR); uint8_t signed_cert[kMaxDERCertLength]; ChipCertificateData certData; uint8_t outCertBuf[kMaxCHIPCertLength]; MutableByteSpan outCert(outCertBuf); ChipDN root_dn; EXPECT_EQ(root_dn.AddAttribute_MatterRCACId(0xabcdabcd), CHIP_NO_ERROR); EXPECT_EQ(root_dn.AddAttribute_MatterFabricId(0xabcd), CHIP_NO_ERROR); X509CertRequestParams root_params_fabric = { 1234, 631161876, 729942000, root_dn, root_dn }; MutableByteSpan signed_cert_span(signed_cert); EXPECT_EQ(NewRootX509Cert(root_params_fabric, keypair, signed_cert_span), CHIP_NO_ERROR); EXPECT_EQ(ConvertX509CertToChipCert(signed_cert_span, outCert), CHIP_NO_ERROR); EXPECT_EQ(DecodeChipCert(outCert, certData), CHIP_NO_ERROR); } TEST_F(TestChipCert, TestChipCert_GenerateICACert) { // Generate a new keypair for cert signing P256Keypair keypair; EXPECT_EQ(keypair.Initialize(ECPKeyTarget::ECDSA), CHIP_NO_ERROR); uint8_t signed_cert[kMaxDERCertLength]; uint8_t outCertBuf[kMaxCHIPCertLength]; MutableByteSpan outCert(outCertBuf); ChipCertificateData certData; ChipDN ica_dn; EXPECT_EQ(ica_dn.AddAttribute_MatterICACId(0xABCDABCDABCDABCD), CHIP_NO_ERROR); ChipDN issuer_dn; EXPECT_EQ(issuer_dn.AddAttribute_MatterRCACId(0x43215678FEDCABCD), CHIP_NO_ERROR); X509CertRequestParams ica_params = { 1234, 631161876, 729942000, ica_dn, issuer_dn }; P256Keypair ica_keypair; EXPECT_EQ(ica_keypair.Initialize(ECPKeyTarget::ECDSA), CHIP_NO_ERROR); MutableByteSpan signed_cert_span(signed_cert); EXPECT_EQ(NewICAX509Cert(ica_params, ica_keypair.Pubkey(), keypair, signed_cert_span), CHIP_NO_ERROR); EXPECT_EQ(ConvertX509CertToChipCert(signed_cert_span, outCert), CHIP_NO_ERROR); EXPECT_EQ(DecodeChipCert(outCert, certData), CHIP_NO_ERROR); // Test with FutureExtension X509CertRequestParams ica_params2 = { 1234, 631161876, 729942000, ica_dn, issuer_dn, kSubjectAltNameAsFutureExt }; MutableByteSpan signed_cert_span2(signed_cert); EXPECT_EQ(NewICAX509Cert(ica_params2, ica_keypair.Pubkey(), keypair, signed_cert_span2), CHIP_NO_ERROR); outCert = MutableByteSpan(outCertBuf); EXPECT_EQ(ConvertX509CertToChipCert(signed_cert_span2, outCert), CHIP_NO_ERROR); EXPECT_EQ(DecodeChipCert(outCert, certData), CHIP_NO_ERROR); // Test error case: ICA cert subject provided a node ID attribute ica_params.SubjectDN.Clear(); EXPECT_EQ(ica_params.SubjectDN.AddAttribute_MatterNodeId(0xABCDABCDABCDABCD), CHIP_NO_ERROR); EXPECT_EQ(ica_params.SubjectDN.AddAttribute_MatterFabricId(0xFAB00000FAB00001), CHIP_NO_ERROR); MutableByteSpan signed_cert_span1(signed_cert); EXPECT_EQ(NewICAX509Cert(ica_params, ica_keypair.Pubkey(), keypair, signed_cert_span1), CHIP_ERROR_INVALID_ARGUMENT); // Test that serial number cannot be negative ica_params.SubjectDN.Clear(); EXPECT_EQ(ica_params.SubjectDN.AddAttribute_MatterICACId(0xABCDABCDABCDABCD), CHIP_NO_ERROR); ica_params.SerialNumber = -1; EXPECT_EQ(NewICAX509Cert(ica_params, ica_keypair.Pubkey(), keypair, signed_cert_span1), CHIP_ERROR_INVALID_ARGUMENT); } TEST_F(TestChipCert, TestChipCert_GenerateNOCRoot) { // Generate a new keypair for cert signing P256Keypair keypair; EXPECT_EQ(keypair.Initialize(ECPKeyTarget::ECDSA), CHIP_NO_ERROR); uint8_t signed_cert[kMaxDERCertLength]; uint8_t outCertBuf[kMaxCHIPCertLength]; MutableByteSpan outCert(outCertBuf); ChipCertificateData certData; ChipDN noc_dn; EXPECT_EQ(noc_dn.AddAttribute_MatterNodeId(0xABCDABCDABCDABCD), CHIP_NO_ERROR); EXPECT_EQ(noc_dn.AddAttribute_MatterFabricId(0xFAB00000FAB00001), CHIP_NO_ERROR); ChipDN issuer_dn; EXPECT_EQ(issuer_dn.AddAttribute_MatterRCACId(0x8888999944442222), CHIP_NO_ERROR); X509CertRequestParams noc_params = { 123456, 631161876, 729942000, noc_dn, issuer_dn }; P256Keypair noc_keypair; EXPECT_EQ(noc_keypair.Initialize(ECPKeyTarget::ECDSA), CHIP_NO_ERROR); MutableByteSpan signed_cert_span(signed_cert, sizeof(signed_cert)); EXPECT_EQ(NewNodeOperationalX509Cert(noc_params, noc_keypair.Pubkey(), keypair, signed_cert_span), CHIP_NO_ERROR); EXPECT_EQ(ConvertX509CertToChipCert(signed_cert_span, outCert), CHIP_NO_ERROR); EXPECT_EQ(DecodeChipCert(outCert, certData), CHIP_NO_ERROR); // Test with FutureExtension X509CertRequestParams noc_params2 = { 123456, 631161876, 729942000, noc_dn, issuer_dn, kSubjectAltNameAsFutureExt }; MutableByteSpan signed_cert_span2(signed_cert); EXPECT_EQ(NewNodeOperationalX509Cert(noc_params2, noc_keypair.Pubkey(), keypair, signed_cert_span2), CHIP_NO_ERROR); outCert = MutableByteSpan(outCertBuf); EXPECT_EQ(ConvertX509CertToChipCert(signed_cert_span2, outCert), CHIP_NO_ERROR); EXPECT_EQ(DecodeChipCert(outCert, certData), CHIP_NO_ERROR); // Test error case: NOC cert subject doesn't have NodeId attribute noc_params.SubjectDN.Clear(); EXPECT_EQ(noc_params.SubjectDN.AddAttribute_MatterFabricId(0xFAB00000FAB00001), CHIP_NO_ERROR); MutableByteSpan signed_cert_span1(signed_cert, sizeof(signed_cert)); EXPECT_EQ(NewNodeOperationalX509Cert(noc_params, noc_keypair.Pubkey(), keypair, signed_cert_span1), CHIP_ERROR_INVALID_ARGUMENT); // Test error case: NOC cert subject doesn't have fabric ID attribute noc_params.SubjectDN.Clear(); EXPECT_EQ(noc_params.SubjectDN.AddAttribute_MatterNodeId(0xABCDABCDABCDABCD), CHIP_NO_ERROR); EXPECT_EQ(NewNodeOperationalX509Cert(noc_params, noc_keypair.Pubkey(), keypair, signed_cert_span1), CHIP_ERROR_WRONG_CERT_DN); // Test error case: issuer cert DN type is Node certificate noc_params.SubjectDN.Clear(); EXPECT_EQ(noc_params.SubjectDN.AddAttribute_MatterNodeId(0xABCDABCDABCDABCD), CHIP_NO_ERROR); EXPECT_EQ(noc_params.SubjectDN.AddAttribute_MatterFabricId(0xFAB00000FAB00001), CHIP_NO_ERROR); noc_params.IssuerDN.Clear(); EXPECT_EQ(noc_params.IssuerDN.AddAttribute_MatterNodeId(0x8888999944442222), CHIP_NO_ERROR); EXPECT_EQ(noc_params.IssuerDN.AddAttribute_MatterFabricId(0xFAB00000FAB00001), CHIP_NO_ERROR); EXPECT_EQ(NewNodeOperationalX509Cert(noc_params, noc_keypair.Pubkey(), keypair, signed_cert_span1), CHIP_ERROR_INVALID_ARGUMENT); } TEST_F(TestChipCert, TestChipCert_GenerateNOCICA) { // Generate a new keypair for cert signing P256Keypair keypair; EXPECT_EQ(keypair.Initialize(ECPKeyTarget::ECDSA), CHIP_NO_ERROR); uint8_t signed_cert[kMaxDERCertLength]; uint8_t outCertBuf[kMaxCHIPCertLength]; MutableByteSpan outCert(outCertBuf); uint8_t outCertDERBuf[kMaxDERCertLength]; MutableByteSpan outCertDER(outCertDERBuf); ChipCertificateData certData; const static char noc_cn_rdn[] = "Test NOC"; const static char noc_givenname_rdn[] = "John"; const static char noc_name_rdn[] = "Smith"; ChipDN noc_dn; EXPECT_EQ(noc_dn.AddAttribute_CommonName(CharSpan(noc_cn_rdn, strlen(noc_cn_rdn)), false), CHIP_NO_ERROR); EXPECT_EQ(noc_dn.AddAttribute_MatterNodeId(0xAAAABBBBCCCCDDDD), CHIP_NO_ERROR); EXPECT_EQ(noc_dn.AddAttribute_MatterFabricId(0xFAB00000FAB00001), CHIP_NO_ERROR); EXPECT_EQ(noc_dn.AddAttribute_GivenName(CharSpan(noc_givenname_rdn, strlen(noc_givenname_rdn)), true), CHIP_NO_ERROR); EXPECT_EQ(noc_dn.AddAttribute_Name(CharSpan(noc_name_rdn, strlen(noc_name_rdn)), true), CHIP_NO_ERROR); ChipDN ica_dn; EXPECT_EQ(ica_dn.AddAttribute_MatterICACId(0x8888999944442222), CHIP_NO_ERROR); EXPECT_EQ(ica_dn.AddAttribute_MatterFabricId(0xFAB00000FAB00001), CHIP_NO_ERROR); X509CertRequestParams noc_params = { 12348765, 631161876, 729942000, noc_dn, ica_dn }; P256Keypair noc_keypair; EXPECT_EQ(noc_keypair.Initialize(ECPKeyTarget::ECDSA), CHIP_NO_ERROR); MutableByteSpan signed_cert_span(signed_cert); EXPECT_EQ(NewNodeOperationalX509Cert(noc_params, noc_keypair.Pubkey(), keypair, signed_cert_span), CHIP_NO_ERROR); EXPECT_EQ(ConvertX509CertToChipCert(signed_cert_span, outCert), CHIP_NO_ERROR); EXPECT_EQ(ConvertChipCertToX509Cert(outCert, outCertDER), CHIP_NO_ERROR); EXPECT_TRUE(signed_cert_span.data_equal(outCertDER)); EXPECT_EQ(DecodeChipCert(outCert, certData), CHIP_NO_ERROR); } TEST_F(TestChipCert, TestChipCert_VerifyGeneratedCerts) { // Generate a new keypair for cert signing P256Keypair keypair; EXPECT_EQ(keypair.Initialize(ECPKeyTarget::ECDSA), CHIP_NO_ERROR); static uint8_t root_cert[kMaxDERCertLength]; ChipDN root_dn; EXPECT_EQ(root_dn.AddAttribute_MatterRCACId(0xAAAABBBBCCCCDDDD), CHIP_NO_ERROR); EXPECT_EQ(root_dn.AddAttribute_MatterFabricId(0xFAB0000000008888), CHIP_NO_ERROR); X509CertRequestParams root_params = { 1234, 631161876, 729942000, root_dn, root_dn }; MutableByteSpan root_cert_span(root_cert); EXPECT_EQ(NewRootX509Cert(root_params, keypair, root_cert_span), CHIP_NO_ERROR); static uint8_t ica_cert[kMaxDERCertLength]; ChipDN ica_dn; EXPECT_EQ(ica_dn.AddAttribute_MatterICACId(0xAABBCCDDAABBCCDD), CHIP_NO_ERROR); EXPECT_EQ(ica_dn.AddAttribute_MatterFabricId(0xFAB0000000008888), CHIP_NO_ERROR); X509CertRequestParams ica_params = { 12345, 631161876, 729942000, ica_dn, root_dn }; P256Keypair ica_keypair; EXPECT_EQ(ica_keypair.Initialize(ECPKeyTarget::ECDSA), CHIP_NO_ERROR); MutableByteSpan ica_cert_span(ica_cert); EXPECT_EQ(NewICAX509Cert(ica_params, ica_keypair.Pubkey(), keypair, ica_cert_span), CHIP_NO_ERROR); static uint8_t noc_cert[kMaxDERCertLength]; ChipDN noc_dn; EXPECT_EQ(noc_dn.AddAttribute_MatterNodeId(0xAABBCCDDAABBCCDD), CHIP_NO_ERROR); EXPECT_EQ(noc_dn.AddAttribute_MatterFabricId(0xFAB0000000008888), CHIP_NO_ERROR); X509CertRequestParams noc_params = { 123456, 631161876, 729942000, noc_dn, ica_dn }; P256Keypair noc_keypair; EXPECT_EQ(noc_keypair.Initialize(ECPKeyTarget::ECDSA), CHIP_NO_ERROR); MutableByteSpan noc_cert_span(noc_cert, sizeof(noc_cert)); EXPECT_EQ(NewNodeOperationalX509Cert(noc_params, noc_keypair.Pubkey(), ica_keypair, noc_cert_span), CHIP_NO_ERROR); ChipCertificateSet certSet; EXPECT_EQ(certSet.Init(3), CHIP_NO_ERROR); static uint8_t chipRootCertBuf[kMaxCHIPCertLength]; static uint8_t chipICACertBuf[kMaxCHIPCertLength]; static uint8_t chipNOCCertBuf[kMaxCHIPCertLength]; MutableByteSpan chipRootCert(chipRootCertBuf); MutableByteSpan chipICACert(chipICACertBuf); MutableByteSpan chipNOCCert(chipNOCCertBuf); EXPECT_EQ(ConvertX509CertToChipCert(root_cert_span, chipRootCert), CHIP_NO_ERROR); EXPECT_EQ(certSet.LoadCert(chipRootCert, sTrustAnchorFlag), CHIP_NO_ERROR); EXPECT_EQ(ConvertX509CertToChipCert(ica_cert_span, chipICACert), CHIP_NO_ERROR); EXPECT_EQ(certSet.LoadCert(chipICACert, sGenTBSHashFlag), CHIP_NO_ERROR); EXPECT_EQ(ConvertX509CertToChipCert(noc_cert_span, chipNOCCert), CHIP_NO_ERROR); EXPECT_EQ(certSet.LoadCert(chipNOCCert, sGenTBSHashFlag), CHIP_NO_ERROR); ValidationContext validContext; validContext.Reset(); EXPECT_EQ(SetCurrentTime(validContext, 2022, 1, 1), CHIP_NO_ERROR); validContext.mRequiredKeyUsages.Set(KeyUsageFlags::kDigitalSignature); validContext.mRequiredKeyPurposes.Set(KeyPurposeFlags::kServerAuth); validContext.mRequiredKeyPurposes.Set(KeyPurposeFlags::kClientAuth); // Locate the subject DN and key id that will be used as input the FindValidCert() method. const ChipDN & subjectDN = certSet.GetCertSet()[2].mSubjectDN; const CertificateKeyId & subjectKeyId = certSet.GetCertSet()[2].mSubjectKeyId; const ChipCertificateData * resultCert = nullptr; EXPECT_EQ(certSet.FindValidCert(subjectDN, subjectKeyId, validContext, &resultCert), CHIP_NO_ERROR); } TEST_F(TestChipCert, TestChipCert_VerifyGeneratedCertsNoICA) { // Generate a new keypair for cert signing P256Keypair keypair; EXPECT_EQ(keypair.Initialize(ECPKeyTarget::ECDSA), CHIP_NO_ERROR); static uint8_t root_cert[kMaxDERCertLength]; const static char root_cn_rdn[] = "Test Root Operational Cert"; ChipDN root_dn; EXPECT_EQ(root_dn.AddAttribute_CommonName(CharSpan(root_cn_rdn, strlen(root_cn_rdn)), false), CHIP_NO_ERROR); EXPECT_EQ(root_dn.AddAttribute_MatterRCACId(0xAAAABBBBCCCCDDDD), CHIP_NO_ERROR); EXPECT_EQ(root_dn.AddAttribute_MatterFabricId(0xFAB0000000008888), CHIP_NO_ERROR); X509CertRequestParams root_params = { 1234, 631161876, 729942000, root_dn, root_dn }; MutableByteSpan root_cert_span(root_cert); EXPECT_EQ(NewRootX509Cert(root_params, keypair, root_cert_span), CHIP_NO_ERROR); static uint8_t noc_cert[kMaxDERCertLength]; const static char noc_cn_rdn[] = "Test NOC"; ChipDN noc_dn; EXPECT_EQ(noc_dn.AddAttribute_CommonName(CharSpan(noc_cn_rdn, strlen(noc_cn_rdn)), true), CHIP_NO_ERROR); EXPECT_EQ(noc_dn.AddAttribute_MatterNodeId(0xAABBCCDDAABBCCDD), CHIP_NO_ERROR); EXPECT_EQ(noc_dn.AddAttribute_MatterFabricId(0xFAB0000000008888), CHIP_NO_ERROR); EXPECT_EQ(noc_dn.AddAttribute_MatterCASEAuthTag(0xABCD0010), CHIP_NO_ERROR); X509CertRequestParams noc_params = { 1234, 631161876, 729942000, noc_dn, root_dn }; P256Keypair noc_keypair; EXPECT_EQ(noc_keypair.Initialize(ECPKeyTarget::ECDSA), CHIP_NO_ERROR); MutableByteSpan noc_cert_span(noc_cert); EXPECT_EQ(NewNodeOperationalX509Cert(noc_params, noc_keypair.Pubkey(), keypair, noc_cert_span), CHIP_NO_ERROR); ChipCertificateSet certSet; EXPECT_EQ(certSet.Init(2), CHIP_NO_ERROR); static uint8_t chipRootCertBuf[kMaxCHIPCertLength]; static uint8_t chipNOCCertBuf[kMaxCHIPCertLength]; MutableByteSpan chipRootCert(chipRootCertBuf); MutableByteSpan chipNOCCert(chipNOCCertBuf); EXPECT_EQ(ConvertX509CertToChipCert(root_cert_span, chipRootCert), CHIP_NO_ERROR); EXPECT_EQ(certSet.LoadCert(chipRootCert, sTrustAnchorFlag), CHIP_NO_ERROR); EXPECT_EQ(ConvertX509CertToChipCert(noc_cert_span, chipNOCCert), CHIP_NO_ERROR); EXPECT_EQ(certSet.LoadCert(chipNOCCert, sGenTBSHashFlag), CHIP_NO_ERROR); ValidationContext validContext; validContext.Reset(); EXPECT_EQ(SetCurrentTime(validContext, 2022, 1, 1), CHIP_NO_ERROR); validContext.mRequiredKeyUsages.Set(KeyUsageFlags::kDigitalSignature); validContext.mRequiredKeyPurposes.Set(KeyPurposeFlags::kServerAuth); validContext.mRequiredKeyPurposes.Set(KeyPurposeFlags::kClientAuth); // Locate the subject DN and key id that will be used as input the FindValidCert() method. const ChipDN & subjectDN = certSet.GetCertSet()[1].mSubjectDN; const CertificateKeyId & subjectKeyId = certSet.GetCertSet()[1].mSubjectKeyId; const ChipCertificateData * resultCert = nullptr; EXPECT_EQ(certSet.FindValidCert(subjectDN, subjectKeyId, validContext, &resultCert), CHIP_NO_ERROR); } TEST_F(TestChipCert, TestChipCert_ExtractNodeIdFabricId) { struct TestCase { TestCert Cert; TestCert ICACert; uint64_t ExpectedNodeId; uint64_t ExpectedFabricId; }; // clang-format off static constexpr TestCase sTestCases[] = { // Cert ICA ExpectedNodeId ExpectedFabricId // ============================================================= { TestCert::kNode01_01, TestCert::kICA01, 0xDEDEDEDE00010001, 0xFAB000000000001D }, { TestCert::kNode01_02, TestCert::kNone, 0xDEDEDEDE00010002, 0xFAB000000000001D }, { TestCert::kNode02_01, TestCert::kICA02, 0xDEDEDEDE00020001, 0xFAB000000000001D }, { TestCert::kNode02_02, TestCert::kICA02, 0xDEDEDEDE00020002, 0xFAB000000000001D }, { TestCert::kNode02_03, TestCert::kICA02, 0xDEDEDEDE00020003, 0xFAB000000000001D }, { TestCert::kNode02_04, TestCert::kICA02, 0xDEDEDEDE00020004, 0xFAB000000000001D }, { TestCert::kNode02_05, TestCert::kICA02, 0xDEDEDEDE00020005, 0xFAB000000000001D }, { TestCert::kNode02_06, TestCert::kICA02, 0xDEDEDEDE00020006, 0xFAB000000000001D }, { TestCert::kNode02_07, TestCert::kICA02, 0xDEDEDEDE00020007, 0xFAB000000000001D }, { TestCert::kNode02_08, TestCert::kICA02, 0xDEDEDEDE00020008, 0xFAB000000000001D }, }; // clang-format on // Test node ID and fabric ID extraction from the raw ByteSpan form. for (auto & testCase : sTestCases) { ByteSpan cert; CHIP_ERROR err = GetTestCert(testCase.Cert, sNullLoadFlag, cert); EXPECT_EQ(err, CHIP_NO_ERROR); NodeId nodeId; FabricId fabricId; err = ExtractNodeIdFabricIdFromOpCert(cert, &nodeId, &fabricId); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_EQ(nodeId, testCase.ExpectedNodeId); EXPECT_EQ(fabricId, testCase.ExpectedFabricId); } // Test node ID and fabric ID extraction from the parsed form. ChipCertificateSet certSet; for (auto & testCase : sTestCases) { CHIP_ERROR err = certSet.Init(1); EXPECT_EQ(err, CHIP_NO_ERROR); err = LoadTestCert(certSet, testCase.Cert, sNullLoadFlag, sNullDecodeFlag); EXPECT_EQ(err, CHIP_NO_ERROR); NodeId nodeId; FabricId fabricId; err = ExtractNodeIdFabricIdFromOpCert(certSet.GetCertSet()[0], &nodeId, &fabricId); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_EQ(nodeId, testCase.ExpectedNodeId); EXPECT_EQ(fabricId, testCase.ExpectedFabricId); certSet.Release(); } // Test fabric ID extraction from the raw ByteSpan form. for (auto & testCase : sTestCases) { ByteSpan cert; CHIP_ERROR err = GetTestCert(testCase.Cert, sNullLoadFlag, cert); EXPECT_EQ(err, CHIP_NO_ERROR); FabricId fabricId; err = ExtractFabricIdFromCert(cert, &fabricId); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_EQ(fabricId, testCase.ExpectedFabricId); } // Test fabric ID extraction from the parsed form. for (auto & testCase : sTestCases) { CHIP_ERROR err = certSet.Init(1); EXPECT_EQ(err, CHIP_NO_ERROR); err = LoadTestCert(certSet, testCase.Cert, sNullLoadFlag, sNullDecodeFlag); EXPECT_EQ(err, CHIP_NO_ERROR); FabricId fabricId; err = ExtractFabricIdFromCert(certSet.GetCertSet()[0], &fabricId); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_EQ(fabricId, testCase.ExpectedFabricId); certSet.Release(); } // Test fabric ID extraction from the raw ByteSpan form of ICA Cert that doesn't have FabricId. { ByteSpan cert; CHIP_ERROR err = GetTestCert(TestCert::kICA01, sNullLoadFlag, cert); EXPECT_EQ(err, CHIP_NO_ERROR); FabricId fabricId; err = ExtractFabricIdFromCert(cert, &fabricId); EXPECT_EQ(err, CHIP_ERROR_NOT_FOUND); } // Test extraction from the parsed form of ICA Cert that doesn't have FabricId. { CHIP_ERROR err = certSet.Init(1); EXPECT_EQ(err, CHIP_NO_ERROR); err = LoadTestCert(certSet, TestCert::kICA01, sNullLoadFlag, sNullDecodeFlag); EXPECT_EQ(err, CHIP_NO_ERROR); FabricId fabricId; err = ExtractFabricIdFromCert(certSet.GetCertSet()[0], &fabricId); EXPECT_EQ(err, CHIP_ERROR_NOT_FOUND); certSet.Release(); } } TEST_F(TestChipCert, TestChipCert_ExtractOperationalDiscoveryId) { struct TestCase { TestCert Noc; TestCert Rcac; uint64_t ExpectedNodeId; uint64_t ExpectedFabricId; uint64_t ExpectedCompressedFabricId; }; // clang-format off static constexpr TestCase sTestCases[] = { // Cert ICA ExpectedNodeId ExpectedFabricId ExpectedCompressedFabricId // =========================================================================================================== { TestCert::kNode01_01, TestCert::kRoot01, 0xDEDEDEDE00010001, 0xFAB000000000001D, 0x3893C4324526C775 }, { TestCert::kNode01_02, TestCert::kRoot01, 0xDEDEDEDE00010002, 0xFAB000000000001D, 0x3893C4324526C775 }, { TestCert::kNode02_01, TestCert::kRoot02, 0xDEDEDEDE00020001, 0xFAB000000000001D, 0x89E8911178DAC089 }, { TestCert::kNode02_02, TestCert::kRoot02, 0xDEDEDEDE00020002, 0xFAB000000000001D, 0x89E8911178DAC089 }, { TestCert::kNode02_03, TestCert::kRoot02, 0xDEDEDEDE00020003, 0xFAB000000000001D, 0x89E8911178DAC089 }, { TestCert::kNode02_04, TestCert::kRoot02, 0xDEDEDEDE00020004, 0xFAB000000000001D, 0x89E8911178DAC089 }, { TestCert::kNode02_05, TestCert::kRoot02, 0xDEDEDEDE00020005, 0xFAB000000000001D, 0x89E8911178DAC089 }, { TestCert::kNode02_06, TestCert::kRoot02, 0xDEDEDEDE00020006, 0xFAB000000000001D, 0x89E8911178DAC089 }, { TestCert::kNode02_07, TestCert::kRoot02, 0xDEDEDEDE00020007, 0xFAB000000000001D, 0x89E8911178DAC089 }, { TestCert::kNode02_08, TestCert::kRoot02, 0xDEDEDEDE00020008, 0xFAB000000000001D, 0x89E8911178DAC089 }, }; // clang-format on for (auto & testCase : sTestCases) { ByteSpan noc; ByteSpan rcac; CHIP_ERROR err = GetTestCert(testCase.Noc, sNullLoadFlag, noc); EXPECT_EQ(err, CHIP_NO_ERROR); err = GetTestCert(testCase.Rcac, sNullLoadFlag, rcac); EXPECT_EQ(err, CHIP_NO_ERROR); // Extract Node ID and Fabric ID from the leaf node certificate. NodeId nodeId; FabricId fabricId; err = ExtractNodeIdFabricIdFromOpCert(noc, &nodeId, &fabricId); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_EQ(nodeId, testCase.ExpectedNodeId); EXPECT_EQ(fabricId, testCase.ExpectedFabricId); // Extract Node ID, Fabric ID and Compressed Fabric ID from the // NOC and root certificate. CompressedFabricId compressedFabricId; err = ExtractNodeIdFabricIdCompressedFabricIdFromOpCerts(rcac, noc, compressedFabricId, fabricId, nodeId); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_EQ(compressedFabricId, testCase.ExpectedCompressedFabricId); EXPECT_EQ(fabricId, testCase.ExpectedFabricId); EXPECT_EQ(nodeId, testCase.ExpectedNodeId); } } TEST_F(TestChipCert, TestChipCert_ExtractAndValidateCATsFromOpCert) { struct TestCase { TestCert Cert; CATValues ExpectedCATs; }; // clang-format off static constexpr TestCase sTestCases[] = { // Cert CATs // ============================================================================ { TestCert::kNode01_01, { { kUndefinedCAT, kUndefinedCAT, kUndefinedCAT } } }, { TestCert::kNode01_02, { { kUndefinedCAT, kUndefinedCAT, kUndefinedCAT } } }, { TestCert::kNode02_01, { { kUndefinedCAT, kUndefinedCAT, kUndefinedCAT } } }, { TestCert::kNode02_02, { { kUndefinedCAT, kUndefinedCAT, kUndefinedCAT } } }, { TestCert::kNode02_03, { { 0xABCD0001, kUndefinedCAT, kUndefinedCAT } } }, { TestCert::kNode02_04, { { 0xABCE1002, 0xABCD0003, kUndefinedCAT } } }, { TestCert::kNode02_05, { { 0xABCD0010, 0xABCE1008, kUndefinedCAT } } }, { TestCert::kNode02_06, { { kUndefinedCAT, kUndefinedCAT, kUndefinedCAT } } }, { TestCert::kNode02_07, { { kUndefinedCAT, kUndefinedCAT, kUndefinedCAT } } }, { TestCert::kNode02_08, { { 0xABCF00A0, 0xABCD0020, 0xABCE0100 } } }, }; // clang-format on // Test extraction from the raw ByteSpan form. for (auto & testCase : sTestCases) { ByteSpan cert; CHIP_ERROR err = GetTestCert(testCase.Cert, sNullLoadFlag, cert); EXPECT_EQ(err, CHIP_NO_ERROR); CATValues cats; err = ExtractCATsFromOpCert(cert, cats); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_EQ(memcmp(&cats, &testCase.ExpectedCATs, sizeof(cats)), 0); } // Test extraction from the parsed form. ChipCertificateSet certSet; for (auto & testCase : sTestCases) { CHIP_ERROR err = certSet.Init(1); EXPECT_EQ(err, CHIP_NO_ERROR); err = LoadTestCert(certSet, testCase.Cert, sNullLoadFlag, sNullDecodeFlag); EXPECT_EQ(err, CHIP_NO_ERROR); CATValues cats; err = ExtractCATsFromOpCert(certSet.GetCertSet()[0], cats); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_EQ(memcmp(&cats, &testCase.ExpectedCATs, sizeof(cats)), 0); certSet.Release(); } // Error case: trying to extract CAT from Root Cert. { CHIP_ERROR err = certSet.Init(1); EXPECT_EQ(err, CHIP_NO_ERROR); err = LoadTestCert(certSet, TestCert::kRoot01, sNullLoadFlag, sNullDecodeFlag); EXPECT_EQ(err, CHIP_NO_ERROR); CATValues cats; err = ExtractCATsFromOpCert(certSet.GetCertSet()[0], cats); EXPECT_EQ(err, CHIP_ERROR_INVALID_ARGUMENT); certSet.Release(); } // Error case: NOC with invalid CAT version. { CATValues cats; CHIP_ERROR err = ExtractCATsFromOpCert(ByteSpan(sChipTest_NOC_Subject_CAT_Invalid_Cert_CHIP), cats); EXPECT_EQ(err, CHIP_ERROR_INVALID_ARGUMENT); } // Error case: NOC with multiple versions of the same CAT tag. { CATValues cats; CHIP_ERROR err = ExtractCATsFromOpCert(ByteSpan(sChipTest_NOC_Subject_CAT_Twice_Cert_CHIP), cats); EXPECT_EQ(err, CHIP_ERROR_WRONG_CERT_DN); } } TEST_F(TestChipCert, TestChipCert_ExtractSubjectDNFromChipCert) { struct TestCase { TestCert Cert; ChipDN ExpectedSubjectDN; }; ChipDN expectedSubjectDN_Root01; EXPECT_EQ(expectedSubjectDN_Root01.AddAttribute_MatterRCACId(0xCACACACA00000001), CHIP_NO_ERROR); ChipDN expectedSubjectDN_Root02; EXPECT_EQ(expectedSubjectDN_Root02.AddAttribute_MatterRCACId(0xCACACACA00000002), CHIP_NO_ERROR); EXPECT_EQ(expectedSubjectDN_Root02.AddAttribute_MatterFabricId(0xFAB000000000001D), CHIP_NO_ERROR); ChipDN expectedSubjectDN_ICA02; EXPECT_EQ(expectedSubjectDN_ICA02.AddAttribute_MatterICACId(0xCACACACA00000004), CHIP_NO_ERROR); EXPECT_EQ(expectedSubjectDN_ICA02.AddAttribute_MatterFabricId(0xFAB000000000001D), CHIP_NO_ERROR); ChipDN expectedSubjectDN_Node01_01; EXPECT_EQ(expectedSubjectDN_Node01_01.AddAttribute_MatterNodeId(0xDEDEDEDE00010001), CHIP_NO_ERROR); EXPECT_EQ(expectedSubjectDN_Node01_01.AddAttribute_MatterFabricId(0xFAB000000000001D), CHIP_NO_ERROR); const static char commonName_RDN[] = "TestCert02_03"; ChipDN expectedSubjectDN_Node02_03; EXPECT_EQ(expectedSubjectDN_Node02_03.AddAttribute_MatterNodeId(0xDEDEDEDE00020003), CHIP_NO_ERROR); EXPECT_EQ(expectedSubjectDN_Node02_03.AddAttribute_MatterFabricId(0xFAB000000000001D), CHIP_NO_ERROR); EXPECT_EQ(expectedSubjectDN_Node02_03.AddAttribute_CommonName(CharSpan(commonName_RDN, strlen(commonName_RDN)), false), CHIP_NO_ERROR); EXPECT_EQ(expectedSubjectDN_Node02_03.AddAttribute_MatterCASEAuthTag(0xABCD0001), CHIP_NO_ERROR); // clang-format off TestCase sTestCases[] = { // Cert SubjectDN // ============================================================================ { TestCert::kRoot01, expectedSubjectDN_Root01 }, { TestCert::kRoot02, expectedSubjectDN_Root02 }, { TestCert::kICA02, expectedSubjectDN_ICA02 }, { TestCert::kNode01_01, expectedSubjectDN_Node01_01 }, { TestCert::kNode02_03, expectedSubjectDN_Node02_03 }, { TestCert::kPDCID01, {} }, }; // clang-format on // Test extraction from the raw ByteSpan form. for (auto & testCase : sTestCases) { ByteSpan cert; CHIP_ERROR err = GetTestCert(testCase.Cert, sNullLoadFlag, cert); EXPECT_EQ(err, CHIP_NO_ERROR); ChipDN subjectDN; err = ExtractSubjectDNFromChipCert(cert, subjectDN); EXPECT_EQ(err, CHIP_NO_ERROR); if (!testCase.ExpectedSubjectDN.IsEmpty()) { EXPECT_TRUE(subjectDN.IsEqual(testCase.ExpectedSubjectDN)); } } // Test extraction from the X509 ByteSpan form. for (auto & testCase : sTestCases) { ByteSpan cert; CHIP_ERROR err = GetTestCert(testCase.Cert, TestCertLoadFlags::kDERForm, cert); EXPECT_EQ(err, CHIP_NO_ERROR); ChipDN subjectDN; err = ExtractSubjectDNFromX509Cert(cert, subjectDN); EXPECT_EQ(err, CHIP_NO_ERROR); if (!testCase.ExpectedSubjectDN.IsEmpty()) { EXPECT_TRUE(subjectDN.IsEqual(testCase.ExpectedSubjectDN)); } } } TEST_F(TestChipCert, TestChipCert_ExtractPublicKeyAndSKID) { struct TestCase { TestCert Cert; ByteSpan ExpectedPublicKey; ByteSpan ExpectedSKID; }; // clang-format off static const TestCase sTestCases[] = { // Cert ExpectedPublicKey ExpectedSKID // ======================================================================================= { TestCert::kRoot01, sTestCert_Root01_PublicKey, sTestCert_Root01_SubjectKeyId }, { TestCert::kRoot02, sTestCert_Root02_PublicKey, sTestCert_Root02_SubjectKeyId }, { TestCert::kICA01, sTestCert_ICA01_PublicKey, sTestCert_ICA01_SubjectKeyId }, { TestCert::kICA02, sTestCert_ICA02_PublicKey, sTestCert_ICA02_SubjectKeyId }, { TestCert::kICA01_1, sTestCert_ICA01_1_PublicKey, sTestCert_ICA01_1_SubjectKeyId }, { TestCert::kNode01_01, sTestCert_Node01_01_PublicKey, sTestCert_Node01_01_SubjectKeyId }, { TestCert::kNode01_02, sTestCert_Node01_02_PublicKey, sTestCert_Node01_02_SubjectKeyId }, { TestCert::kNode02_01, sTestCert_Node02_01_PublicKey, sTestCert_Node02_01_SubjectKeyId }, { TestCert::kNode02_02, sTestCert_Node02_02_PublicKey, sTestCert_Node02_02_SubjectKeyId }, { TestCert::kNode02_03, sTestCert_Node02_03_PublicKey, sTestCert_Node02_03_SubjectKeyId }, { TestCert::kNode02_04, sTestCert_Node02_04_PublicKey, sTestCert_Node02_04_SubjectKeyId }, { TestCert::kNode02_05, sTestCert_Node02_05_PublicKey, sTestCert_Node02_05_SubjectKeyId }, { TestCert::kNode02_06, sTestCert_Node02_06_PublicKey, sTestCert_Node02_06_SubjectKeyId }, { TestCert::kNode02_07, sTestCert_Node02_07_PublicKey, sTestCert_Node02_07_SubjectKeyId }, { TestCert::kNode02_08, sTestCert_Node02_08_PublicKey, sTestCert_Node02_08_SubjectKeyId }, { TestCert::kPDCID01, sTestCert_PDCID01_PublicKey, ByteSpan() }, }; // clang-format on for (auto & testCase : sTestCases) { ByteSpan cert; CHIP_ERROR err = GetTestCert(testCase.Cert, sNullLoadFlag, cert); EXPECT_EQ(err, CHIP_NO_ERROR); P256PublicKeySpan publicKey; err = ExtractPublicKeyFromChipCert(cert, publicKey); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_TRUE(publicKey.data_equal(testCase.ExpectedPublicKey)); CertificateKeyId skid; err = ExtractSKIDFromChipCert(cert, skid); if (!testCase.ExpectedSKID.empty()) { EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_TRUE(skid.data_equal(testCase.ExpectedSKID)); } else { EXPECT_EQ(err, CHIP_ERROR_NOT_FOUND); } } } TEST_F(TestChipCert, TestChipCert_PDCIdentityValidation) { CertificateKeyIdStorage keyId; // Test with both the full and compact TLV representations for (auto && cert : { sTestCert_PDCID01_Chip, sTestCert_PDCID01_ChipCompact }) { // Validate only EXPECT_EQ(ValidateChipNetworkIdentity(cert), CHIP_NO_ERROR); // Validate and calculate identifier keyId.fill(0xaa); EXPECT_EQ(ValidateChipNetworkIdentity(cert, keyId), CHIP_NO_ERROR); EXPECT_TRUE(CertificateKeyId(keyId).data_equal(sTestCert_PDCID01_KeyId)); // Extract identifier only keyId.fill(0xaa); EXPECT_EQ(ExtractIdentifierFromChipNetworkIdentity(cert, keyId), CHIP_NO_ERROR); EXPECT_TRUE(CertificateKeyId(keyId).data_equal(sTestCert_PDCID01_KeyId)); } } TEST_F(TestChipCert, TestChipCert_PDCIdentityGeneration) { // Generate a new keypair P256Keypair keypair; EXPECT_EQ(keypair.Initialize(ECPKeyTarget::ECDSA), CHIP_NO_ERROR); // Generate an identity certificate based on the keypair uint8_t buffer[kMaxCHIPCompactNetworkIdentityLength]; MutableByteSpan cert(buffer); EXPECT_EQ(NewChipNetworkIdentity(keypair, cert), CHIP_NO_ERROR); EXPECT_EQ(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); EXPECT_EQ(ConvertChipCertToX509Cert(cert, derCert), CHIP_NO_ERROR); uint8_t tlvBuffer[kMaxCHIPCertLength]; MutableByteSpan tlvCert(tlvBuffer); // won't be compact after round-tripping EXPECT_EQ(ConvertX509CertToChipCert(derCert, tlvCert), CHIP_NO_ERROR); EXPECT_EQ(ValidateChipNetworkIdentity(tlvCert), CHIP_NO_ERROR); } TEST_F(TestChipCert, TestChipCert_KeypairConversion) { P256SerializedKeypair keypair; EXPECT_EQ(GetTestCertKeypair(kPDCID01, keypair), CHIP_NO_ERROR); uint8_t buffer[kP256ECPrivateKeyDERLength]; MutableByteSpan keypairDer(buffer); EXPECT_EQ(ConvertECDSAKeypairRawToDER(keypair, keypairDer), CHIP_NO_ERROR); // Technically the curve name and public key are optional in the DER format, // but both our code and standard tools include them, so we can just compare. EXPECT_TRUE(keypairDer.data_equal(sTestCert_PDCID01_KeypairDER)); }