| /* |
| * |
| * Copyright (c) 2021 Project CHIP Authors |
| * 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 |
| * This file implements unit tests for FabricTable implementation. |
| */ |
| |
| #include <errno.h> |
| #include <gtest/gtest.h> |
| |
| #include <lib/core/CHIPCore.h> |
| |
| #include <credentials/FabricTable.h> |
| |
| #include <credentials/PersistentStorageOpCertStore.h> |
| #include <credentials/TestOnlyLocalCertificateAuthority.h> |
| #include <credentials/tests/CHIPCert_test_vectors.h> |
| #include <crypto/CHIPCryptoPAL.h> |
| #include <crypto/PersistentStorageOperationalKeystore.h> |
| #include <lib/asn1/ASN1.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/TestPersistentStorageDelegate.h> |
| |
| #include <platform/ConfigurationManager.h> |
| |
| #include <lib/support/BytesToHex.h> |
| |
| #include <stdarg.h> |
| |
| using namespace chip; |
| using namespace chip::Credentials; |
| |
| namespace { |
| |
| class ScopedFabricTable |
| { |
| public: |
| ScopedFabricTable() {} |
| ~ScopedFabricTable() |
| { |
| mFabricTable.Shutdown(); |
| mOpCertStore.Finish(); |
| mOpKeyStore.Finish(); |
| } |
| |
| CHIP_ERROR Init(chip::TestPersistentStorageDelegate * storage) |
| { |
| chip::FabricTable::InitParams initParams; |
| initParams.storage = storage; |
| initParams.operationalKeystore = &mOpKeyStore; |
| initParams.opCertStore = &mOpCertStore; |
| |
| ReturnErrorOnFailure(mOpKeyStore.Init(storage)); |
| ReturnErrorOnFailure(mOpCertStore.Init(storage)); |
| return mFabricTable.Init(initParams); |
| } |
| |
| FabricTable & GetFabricTable() { return mFabricTable; } |
| |
| private: |
| chip::FabricTable mFabricTable; |
| chip::PersistentStorageOperationalKeystore mOpKeyStore; |
| chip::Credentials::PersistentStorageOpCertStore mOpCertStore; |
| }; |
| |
| /** |
| * Load a single test fabric with with the Root01:ICA01:Node01_01 identity. |
| */ |
| static CHIP_ERROR LoadTestFabric_Node01_01(FabricTable & fabricTable, bool doCommit) |
| { |
| Crypto::P256SerializedKeypair opKeysSerialized; |
| static Crypto::P256Keypair opKey_Node01_01; |
| |
| FabricIndex fabricIndex; |
| memcpy(opKeysSerialized.Bytes(), TestCerts::sTestCert_Node01_01_PublicKey.data(), |
| TestCerts::sTestCert_Node01_01_PublicKey.size()); |
| memcpy(opKeysSerialized.Bytes() + TestCerts::sTestCert_Node01_01_PublicKey.size(), |
| TestCerts::sTestCert_Node01_01_PrivateKey.data(), TestCerts::sTestCert_Node01_01_PrivateKey.size()); |
| |
| ByteSpan rcacSpan(TestCerts::sTestCert_Root01_Chip); |
| ByteSpan icacSpan(TestCerts::sTestCert_ICA01_Chip); |
| ByteSpan nocSpan(TestCerts::sTestCert_Node01_01_Chip); |
| |
| ReturnErrorOnFailure(opKeysSerialized.SetLength(TestCerts::sTestCert_Node01_01_PublicKey.size() + |
| TestCerts::sTestCert_Node01_01_PrivateKey.size())); |
| ReturnErrorOnFailure(opKey_Node01_01.Deserialize(opKeysSerialized)); |
| ReturnErrorOnFailure(fabricTable.AddNewPendingTrustedRootCert(rcacSpan)); |
| |
| ReturnErrorOnFailure(fabricTable.AddNewPendingFabricWithProvidedOpKey(nocSpan, icacSpan, VendorId::TestVendor1, |
| &opKey_Node01_01, |
| /*isExistingOpKeyExternallyOwned =*/true, &fabricIndex)); |
| if (doCommit) |
| { |
| ReturnErrorOnFailure(fabricTable.CommitPendingFabricData()); |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| static CHIP_ERROR LoadTestFabric_Node01_02(FabricTable & fabricTable, bool doCommit) |
| { |
| Crypto::P256SerializedKeypair opKeysSerialized; |
| FabricIndex fabricIndex; |
| static Crypto::P256Keypair opKey_Node01_02; |
| |
| memcpy(opKeysSerialized.Bytes(), TestCerts::sTestCert_Node01_02_PublicKey.data(), |
| TestCerts::sTestCert_Node01_02_PublicKey.size()); |
| memcpy(opKeysSerialized.Bytes() + TestCerts::sTestCert_Node01_02_PublicKey.size(), |
| TestCerts::sTestCert_Node01_02_PrivateKey.data(), TestCerts::sTestCert_Node01_02_PrivateKey.size()); |
| |
| ByteSpan rcacSpan(TestCerts::sTestCert_Root01_Chip); |
| ByteSpan nocSpan(TestCerts::sTestCert_Node01_02_Chip); |
| |
| ReturnErrorOnFailure(opKeysSerialized.SetLength(TestCerts::sTestCert_Node01_02_PublicKey.size() + |
| TestCerts::sTestCert_Node01_02_PrivateKey.size())); |
| ReturnErrorOnFailure(opKey_Node01_02.Deserialize(opKeysSerialized)); |
| |
| ReturnErrorOnFailure(fabricTable.AddNewPendingTrustedRootCert(rcacSpan)); |
| |
| ReturnErrorOnFailure(fabricTable.AddNewPendingFabricWithProvidedOpKey(nocSpan, {}, VendorId::TestVendor1, &opKey_Node01_02, |
| /*isExistingOpKeyExternallyOwned =*/true, &fabricIndex)); |
| if (doCommit) |
| { |
| ReturnErrorOnFailure(fabricTable.CommitPendingFabricData()); |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| /** |
| * Load a single test fabric with with the Root02:ICA02:Node02_01 identity. |
| */ |
| static CHIP_ERROR LoadTestFabric_Node02_01(FabricTable & fabricTable, bool doCommit, |
| FabricTable::AdvertiseIdentity advertiseIdentity = FabricTable::AdvertiseIdentity::Yes) |
| { |
| Crypto::P256SerializedKeypair opKeysSerialized; |
| FabricIndex fabricIndex; |
| static Crypto::P256Keypair opKey_Node02_01; |
| |
| memcpy(opKeysSerialized.Bytes(), TestCerts::sTestCert_Node02_01_PublicKey.data(), |
| TestCerts::sTestCert_Node02_01_PublicKey.size()); |
| memcpy(opKeysSerialized.Bytes() + TestCerts::sTestCert_Node02_01_PublicKey.size(), |
| TestCerts::sTestCert_Node02_01_PrivateKey.data(), TestCerts::sTestCert_Node02_01_PrivateKey.size()); |
| |
| ByteSpan rcacSpan(TestCerts::sTestCert_Root02_Chip); |
| ByteSpan icacSpan(TestCerts::sTestCert_ICA02_Chip); |
| ByteSpan nocSpan(TestCerts::sTestCert_Node02_01_Chip); |
| |
| EXPECT_EQ(opKeysSerialized.SetLength(TestCerts::sTestCert_Node02_01_PublicKey.size() + |
| TestCerts::sTestCert_Node02_01_PrivateKey.size()), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(opKey_Node02_01.Deserialize(opKeysSerialized), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcacSpan), CHIP_NO_ERROR); |
| |
| CHIP_ERROR err = |
| fabricTable.AddNewPendingFabricWithProvidedOpKey(nocSpan, icacSpan, VendorId::TestVendor1, &opKey_Node02_01, |
| /*isExistingOpKeyExternallyOwned =*/true, &fabricIndex, advertiseIdentity); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| if (doCommit) |
| { |
| err = fabricTable.CommitPendingFabricData(); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| } |
| |
| return err; |
| } |
| |
| struct TestFabricTable : public ::testing::Test |
| { |
| |
| static void SetUpTestSuite() |
| { |
| DeviceLayer::SetConfigurationMgr(&DeviceLayer::ConfigurationManagerImpl::GetDefaultInstance()); |
| ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); |
| } |
| static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); } |
| }; |
| |
| TEST_F(TestFabricTable, TestLastKnownGoodTimeInit) |
| { |
| // Fabric table init should init Last Known Good Time to the firmware build time. |
| chip::TestPersistentStorageDelegate testStorage; |
| ScopedFabricTable fabricTableHolder; |
| |
| EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR); |
| System::Clock::Seconds32 lastKnownGoodChipEpochTime; |
| |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodChipEpochTime), CHIP_NO_ERROR); |
| System::Clock::Seconds32 firmwareBuildTime; |
| EXPECT_EQ(DeviceLayer::ConfigurationMgr().GetFirmwareBuildChipEpochTime(firmwareBuildTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodChipEpochTime, firmwareBuildTime); |
| } |
| |
| TEST_F(TestFabricTable, TestCollidingFabrics) |
| { |
| chip::TestPersistentStorageDelegate testStorage; |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| // |
| // Start by loading NOCs for two nodes on the same fabric. The second one should fail since the FabricTable by default |
| // doesn't permit colliding fabrics. |
| // |
| EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR); |
| EXPECT_NE(LoadTestFabric_Node01_02(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR); |
| |
| // |
| // Revert the partially added NOC from the last call, permit colliding fabrics in the FabricTable and try again. |
| // This time, it should succeed |
| // |
| fabricTable.RevertPendingFabricData(); |
| fabricTable.PermitCollidingFabrics(); |
| EXPECT_EQ(LoadTestFabric_Node01_02(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR); |
| |
| ByteSpan rcacSpan(TestCerts::sTestCert_Root01_Chip); |
| Credentials::P256PublicKeySpan rootPublicKeySpan; |
| |
| EXPECT_EQ(Credentials::ExtractPublicKeyFromChipCert(rcacSpan, rootPublicKeySpan), CHIP_NO_ERROR); |
| |
| // |
| // Ensure we can find both node identities in the FabricTable. |
| // |
| { |
| chip::Platform::ScopedMemoryBuffer<uint8_t> nocBuf; |
| ByteSpan origNocSpan(TestCerts::sTestCert_Node01_01_Chip); |
| NodeId nodeId; |
| FabricId fabricId; |
| |
| EXPECT_EQ(ExtractNodeIdFabricIdFromOpCert(origNocSpan, &nodeId, &fabricId), CHIP_NO_ERROR); |
| EXPECT_NE(fabricTable.FindIdentity(rootPublicKeySpan, fabricId, nodeId), nullptr); |
| } |
| |
| { |
| chip::Platform::ScopedMemoryBuffer<uint8_t> nocBuf; |
| ByteSpan origNocSpan(TestCerts::sTestCert_Node01_02_Chip); |
| NodeId nodeId; |
| FabricId fabricId; |
| |
| EXPECT_EQ(ExtractNodeIdFabricIdFromOpCert(origNocSpan, &nodeId, &fabricId), CHIP_NO_ERROR); |
| EXPECT_NE(fabricTable.FindIdentity(rootPublicKeySpan, fabricId, nodeId), nullptr); |
| } |
| } |
| |
| TEST_F(TestFabricTable, TestUpdateLastKnownGoodTime) |
| { |
| // Adding a fabric should advance Last Known Good Time if any certificate's |
| // NotBefore time is later than the build time, and else should leave it |
| // to the initial build time value. |
| |
| // Test certs all have this NotBefore: Oct 15 14:23:43 2020 GMT |
| const ASN1::ASN1UniversalTime asn1Expected = { 2020, 10, 15, 14, 23, 43 }; |
| uint32_t testCertNotBeforeSeconds; |
| EXPECT_EQ(Credentials::ASN1ToChipEpochTime(asn1Expected, testCertNotBeforeSeconds), CHIP_NO_ERROR); |
| System::Clock::Seconds32 testCertNotBeforeTime = System::Clock::Seconds32(testCertNotBeforeSeconds); |
| |
| // Test that certificate NotBefore times that are before the Firmware build time |
| // do not advance Last Known Good Time. |
| System::Clock::Seconds32 afterNotBeforeBuildTimes[] = { System::Clock::Seconds32(testCertNotBeforeTime.count() + 1), |
| System::Clock::Seconds32(testCertNotBeforeTime.count() + 1000), |
| System::Clock::Seconds32(testCertNotBeforeTime.count() + 1000000) }; |
| for (auto buildTime : afterNotBeforeBuildTimes) |
| { |
| // Set build time to the desired value. |
| EXPECT_EQ(DeviceLayer::ConfigurationMgr().SetFirmwareBuildChipEpochTime(buildTime), CHIP_NO_ERROR); |
| chip::TestPersistentStorageDelegate testStorage; |
| |
| { |
| // Initialize a fabric table. |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| // Read back Last Known Good Time, which will have been initialized to firmware build time. |
| System::Clock::Seconds32 lastKnownGoodTime; |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, buildTime); |
| |
| // Load a test fabric, but do not commit. |
| EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ false), CHIP_NO_ERROR); |
| |
| // Read Last Known Good Time and verify that it hasn't moved forward. |
| // This test case was written after the test certs' NotBefore time and we |
| // are using a configuration manager that should reflect a real build time. |
| // Therefore, we expect that build time is after NotBefore and so Last |
| // Known Good Time will be set to the later of these, build time, even |
| // after installing the new fabric. |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, buildTime); |
| |
| // Verify that calling the fail-safe roll back interface does not change |
| // last known good time, as it hadn't been updated in the first place. |
| fabricTable.RevertPendingFabricData(); |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, buildTime); |
| |
| // Now reload the test fabric and commit this time. |
| EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR); |
| |
| // Read Last Known Good Time and verify that it hasn't moved forward. |
| // This test case was written after the test certs' NotBefore time and we |
| // are using a configuration manager that should reflect a real build time. |
| // Therefore, we expect that build time is after NotBefore and so Last |
| // Known Good Time will be set to the later of these, build time, even |
| // after installing the new fabric. |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, buildTime); |
| |
| // Call revert again. Since we've committed, this is a no-op. |
| // Last known good time should again be unchanged. |
| fabricTable.RevertPendingFabricData(); |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, buildTime); |
| } |
| { |
| // Test reloading last known good time from persistence. |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| // Verify that last known good time was retained. |
| System::Clock::Seconds32 lastKnownGoodTime; |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, buildTime); |
| } |
| } |
| |
| System::Clock::Seconds32 beforeNotBeforeBuildTimes[] = { testCertNotBeforeTime, |
| System::Clock::Seconds32(testCertNotBeforeTime.count() - 1), |
| System::Clock::Seconds32(testCertNotBeforeTime.count() - 1000), |
| System::Clock::Seconds32(testCertNotBeforeTime.count() - 1000000) }; |
| // Test that certificate NotBefore times that are at or after the Firmware |
| // build time do result in Last Known Good Times set to these. |
| // Verify behavior both for fail-safe roll back and commit scenarios. |
| for (auto buildTime : beforeNotBeforeBuildTimes) |
| { |
| // Set build time to the desired value. |
| EXPECT_EQ(DeviceLayer::ConfigurationMgr().SetFirmwareBuildChipEpochTime(buildTime), CHIP_NO_ERROR); |
| chip::TestPersistentStorageDelegate testStorage; |
| { |
| // Initialize a fabric table. |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| // Load a test fabric, but do not commit. |
| EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ false), CHIP_NO_ERROR); |
| |
| // Read Last Known Good Time and verify that it is now set to the certificate |
| // NotBefore time, as this should be at or after firmware build time. |
| System::Clock::Seconds32 lastKnownGoodTime; |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, testCertNotBeforeTime); |
| |
| // Now test revert. Last known good time should change back to the |
| // previous value. |
| fabricTable.RevertPendingFabricData(); |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, buildTime); |
| } |
| { |
| // Test reloading last known good time from persistence. |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| // Verify that the original last known good time was retained, since |
| // we reverted before. |
| System::Clock::Seconds32 lastKnownGoodTime; |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| } |
| { |
| // Now test loading a fabric and committing. |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR); |
| |
| // Read Last Known Good Time and verify that it is now set to the certificate |
| // NotBefore time, as this should be at or after firmware build time. |
| System::Clock::Seconds32 lastKnownGoodTime; |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, testCertNotBeforeTime); |
| |
| // Now test revert, which will be a no-op because we already |
| // committed. Verify that Last Known Good time is retained. |
| fabricTable.RevertPendingFabricData(); |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, testCertNotBeforeTime); |
| } |
| { |
| // Test reloading last known good time from persistence. |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| // Verify that the new last known good time was retained, since |
| // we committed. |
| System::Clock::Seconds32 lastKnownGoodTime; |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, testCertNotBeforeTime); |
| } |
| } |
| } |
| |
| TEST_F(TestFabricTable, TestSetLastKnownGoodTime) |
| { |
| // It is desirable for nodes to set Last Known Good Time whenever a good |
| // time source is available, including cases where this would set the time |
| // backward. However, it is impermissible to set last known good time to |
| // any time before the Firmware Build time or the latest NotBefore of any |
| // installed certificate. |
| |
| // Test certs all have this NotBefore: Oct 15 14:23:43 2020 GMT |
| const ASN1::ASN1UniversalTime asn1Expected = { 2020, 10, 15, 14, 23, 43 }; |
| uint32_t testCertNotBeforeSeconds; |
| EXPECT_EQ(Credentials::ASN1ToChipEpochTime(asn1Expected, testCertNotBeforeSeconds), CHIP_NO_ERROR); |
| System::Clock::Seconds32 testCertNotBeforeTime = System::Clock::Seconds32(testCertNotBeforeSeconds); |
| |
| // Iterate over two cases: one with build time prior to our certificates' NotBefore, one with build time after. |
| System::Clock::Seconds32 testCaseFirmwareBuildTimes[] = { System::Clock::Seconds32(testCertNotBeforeTime.count() - 100000), |
| System::Clock::Seconds32(testCertNotBeforeTime.count() + 100000) }; |
| |
| for (auto buildTime : testCaseFirmwareBuildTimes) |
| { |
| // Set build time to the desired value. |
| EXPECT_EQ(DeviceLayer::ConfigurationMgr().SetFirmwareBuildChipEpochTime(buildTime), CHIP_NO_ERROR); |
| chip::TestPersistentStorageDelegate testStorage; |
| System::Clock::Seconds32 newTime; |
| { |
| // Initialize a fabric table. |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| // Load a test fabric |
| EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit= */ true), CHIP_NO_ERROR); |
| |
| // Verify the Last Known Good Time matches our expected initial value. |
| System::Clock::Seconds32 initialLastKnownGoodTime = |
| buildTime > testCertNotBeforeTime ? buildTime : testCertNotBeforeTime; |
| System::Clock::Seconds32 lastKnownGoodTime; |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, initialLastKnownGoodTime); |
| |
| // Read Last Known Good Time and verify that it hasn't moved forward, since |
| // build time is later than the test certs' NotBefore times. |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, initialLastKnownGoodTime); |
| |
| // Attempt to set a Last Known Good Time that is before the firmware build time. This should fail. |
| newTime = System::Clock::Seconds32(buildTime.count() - 1000); |
| EXPECT_NE(fabricTable.SetLastKnownGoodChipEpochTime(newTime), CHIP_NO_ERROR); |
| |
| // Verify Last Known Good Time is unchanged. |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, initialLastKnownGoodTime); |
| |
| // Attempt to set a Last Known Good Time that is before our certificates' NotBefore times. This should fail. |
| newTime = System::Clock::Seconds32(testCertNotBeforeTime.count() - 1000); |
| EXPECT_NE(fabricTable.SetLastKnownGoodChipEpochTime(newTime), CHIP_NO_ERROR); |
| |
| // Verify Last Known Good Time is unchanged. |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, initialLastKnownGoodTime); |
| |
| // Attempt to set a Last Known Good Time that at our current value. |
| EXPECT_EQ(fabricTable.SetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| |
| // Verify Last Known Good Time is unchanged. |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, initialLastKnownGoodTime); |
| |
| // Attempt to set Last Known Good Times that is after our current value. |
| newTime = System::Clock::Seconds32(initialLastKnownGoodTime.count() + 1000); |
| EXPECT_EQ(fabricTable.SetLastKnownGoodChipEpochTime(newTime), CHIP_NO_ERROR); |
| |
| // Verify Last Known Good Time is updated. |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, newTime); |
| } |
| { |
| // Verify that Last Known Good Time was persisted. |
| |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| System::Clock::Seconds32 lastKnownGoodTime; |
| EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR); |
| EXPECT_EQ(lastKnownGoodTime, newTime); |
| } |
| } |
| } |
| |
| // Test adding 2 fabrics, updating 1, removing 1 |
| TEST_F(TestFabricTable, TestBasicAddNocUpdateNocFlow) |
| { |
| Credentials::TestOnlyLocalCertificateAuthority fabric11CertAuthority; |
| Credentials::TestOnlyLocalCertificateAuthority fabric44CertAuthority; |
| |
| chip::TestPersistentStorageDelegate storage; |
| |
| // Uncomment the next line for superior debugging powers if you blow-up this test |
| // storage.SetLoggingLevel(chip::TestPersistentStorageDelegate::LoggingLevel::kLogMutation); |
| |
| // Initialize test CA and a Fabric 11 externally owned key |
| EXPECT_TRUE(fabric11CertAuthority.Init().IsSuccess()); |
| EXPECT_TRUE(fabric44CertAuthority.Init().IsSuccess()); |
| |
| constexpr uint16_t kVendorId = 0xFFF1u; |
| |
| chip::Crypto::P256Keypair fabric11Node55Keypair; // Fabric ID 11, |
| EXPECT_EQ(fabric11Node55Keypair.Initialize(Crypto::ECPKeyTarget::ECDSA), CHIP_NO_ERROR); |
| |
| // Initialize a fabric table. |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| |
| { |
| FabricIndex nextFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(nextFabricIndex, 1); |
| } |
| |
| size_t numFabricsIterated = 0; |
| |
| size_t numStorageKeysAtStart = storage.GetNumKeys(); |
| |
| // Sequence 1: Add node ID 55 on fabric 11, using externally owned key and no ICAC --> Yield fabricIndex 1 |
| { |
| FabricId fabricId = 11; |
| NodeId nodeId = 55; |
| EXPECT_EQ(fabric11CertAuthority.SetIncludeIcac(false) |
| .GenerateNocChain(fabricId, nodeId, fabric11Node55Keypair.Pubkey()) |
| .GetStatus(), |
| CHIP_NO_ERROR); |
| ByteSpan rcac = fabric11CertAuthority.GetRcac(); |
| ByteSpan noc = fabric11CertAuthority.GetNoc(); |
| |
| // Validate iterator sees nothing yet |
| { |
| numFabricsIterated = 0; |
| bool saw1 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| saw1 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 0u); |
| EXPECT_FALSE(saw1); |
| } |
| |
| uint8_t rcacBuf[Credentials::kMaxCHIPCertLength]; |
| { |
| // No pending root cert yet. |
| MutableByteSpan fetchedSpan{ rcacBuf }; |
| EXPECT_EQ(fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan), CHIP_ERROR_NOT_FOUND); |
| } |
| |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| { |
| // Now have a pending root cert. |
| MutableByteSpan fetchedSpan{ rcacBuf }; |
| EXPECT_EQ(fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan), CHIP_NO_ERROR); |
| EXPECT_TRUE(fetchedSpan.data_equal(rcac)); |
| } |
| |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| bool keyIsExternallyOwned = true; |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithProvidedOpKey(noc, ByteSpan{}, kVendorId, &fabric11Node55Keypair, |
| keyIsExternallyOwned, &newFabricIndex), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(newFabricIndex, 1); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| { |
| // No more pending root cert; it's associated with a fabric now. |
| MutableByteSpan fetchedSpan{ rcacBuf }; |
| EXPECT_EQ(fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan), CHIP_ERROR_NOT_FOUND); |
| } |
| |
| // No storage yet |
| EXPECT_EQ(storage.GetNumKeys(), numStorageKeysAtStart); |
| |
| // Next fabric index has not been updated yet. |
| { |
| FabricIndex nextFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(nextFabricIndex, 1); |
| } |
| |
| // Validate iterator sees pending |
| { |
| numFabricsIterated = 0; |
| bool saw1 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), nodeId); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), fabricId); |
| saw1 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 1u); |
| EXPECT_TRUE(saw1); |
| } |
| |
| // Commit, now storage should have keys |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(storage.GetNumKeys(), (numStorageKeysAtStart + 4)); // 2 opcerts + fabric metadata + index |
| |
| // Next fabric index has been updated. |
| { |
| FabricIndex nextFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(nextFabricIndex, 2); |
| } |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(1); |
| ASSERT_NE(fabricInfo, nullptr); |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), newFabricIndex); |
| EXPECT_EQ(fabricInfo->GetNodeId(), nodeId); |
| EXPECT_EQ(fabricInfo->GetFabricId(), fabricId); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey)); |
| |
| // Validate that fabric has the correct operational key by verifying a signature |
| Crypto::P256ECDSASignature sig; |
| uint8_t message[] = { 'm', 's', 'g' }; |
| EXPECT_EQ(fabricTable.SignWithOpKeypair(newFabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR); |
| EXPECT_EQ(fabric11Node55Keypair.Pubkey().ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR); |
| |
| // Validate iterator sees committed |
| { |
| numFabricsIterated = 0; |
| bool saw1 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), nodeId); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), fabricId); |
| saw1 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 1u); |
| EXPECT_TRUE(saw1); |
| } |
| } |
| |
| size_t numStorageAfterFirstAdd = storage.GetNumKeys(); |
| |
| // Sequence 2: Add node ID 999 on fabric 44, using operational keystore and ICAC --> Yield fabricIndex 2 |
| { |
| FabricId fabricId = 44; |
| NodeId nodeId = 999; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), |
| CHIP_NO_ERROR); |
| ByteSpan rcac = fabric44CertAuthority.GetRcac(); |
| ByteSpan icac = fabric44CertAuthority.GetIcac(); |
| ByteSpan noc = fabric44CertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| |
| // Next fabric index should still be the same as before. |
| { |
| FabricIndex nextFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(nextFabricIndex, 2); |
| } |
| |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 2); |
| EXPECT_EQ(newFabricIndex, 2); |
| // No storage yet |
| EXPECT_EQ(storage.GetNumKeys(), numStorageAfterFirstAdd); |
| // Next fabric index has not been updated yet. |
| { |
| FabricIndex nextFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(nextFabricIndex, 2); |
| } |
| |
| // Commit, now storage should have keys |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 2); |
| |
| EXPECT_EQ(storage.GetNumKeys(), |
| (numStorageAfterFirstAdd + 5)); // 3 opcerts + fabric metadata + 1 operational key |
| |
| // Next fabric index has been updated. |
| { |
| FabricIndex nextFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(nextFabricIndex, 3); |
| } |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(2); |
| ASSERT_NE(fabricInfo, nullptr); |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), newFabricIndex); |
| EXPECT_EQ(fabricInfo->GetNodeId(), nodeId); |
| EXPECT_EQ(fabricInfo->GetFabricId(), fabricId); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey)); |
| |
| // Validate that fabric has the correct operational key by verifying a signature |
| { |
| Crypto::P256ECDSASignature sig; |
| uint8_t message[] = { 'm', 's', 'g' }; |
| |
| Crypto::P256PublicKey nocPubKey; |
| EXPECT_EQ(VerifyCertificateSigningRequest(csrSpan.data(), csrSpan.size(), nocPubKey), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricTable.SignWithOpKeypair(newFabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR); |
| EXPECT_EQ(nocPubKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR); |
| } |
| |
| // Verify we can now see 2 fabrics with the iterator |
| { |
| numFabricsIterated = 0; |
| bool saw1 = false; |
| bool saw2 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 55u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 11u); |
| saw1 = true; |
| } |
| if (iterFabricInfo.GetFabricIndex() == 2) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 999u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u); |
| saw2 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 2u); |
| EXPECT_TRUE(saw1); |
| EXPECT_TRUE(saw2); |
| } |
| } |
| |
| size_t numStorageAfterSecondAdd = storage.GetNumKeys(); |
| |
| // Sequence 3: Update node ID 999 to 1000 on fabric 44, using operational keystore and no ICAC --> Stays fabricIndex 2 |
| { |
| FabricId fabricId = 44; |
| NodeId nodeId = 1000; |
| FabricIndex fabricIndex = 2; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| |
| // Make sure to tag fabric index to pending opkey: otherwise the UpdateNOC fails |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::MakeOptional(static_cast<FabricIndex>(2)), csrSpan), |
| CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), |
| CHIP_NO_ERROR); |
| ByteSpan rcac = fabric44CertAuthority.GetRcac(); |
| ByteSpan noc = fabric44CertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 2); |
| EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(2, noc, ByteSpan{}, FabricTable::AdvertiseIdentity::No), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 2); |
| |
| // No storage yet |
| EXPECT_EQ(storage.GetNumKeys(), numStorageAfterSecondAdd); |
| |
| // Validate iterator sees the pending data |
| { |
| numFabricsIterated = 0; |
| bool saw1 = false; |
| bool saw2 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 55u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 11u); |
| EXPECT_TRUE(iterFabricInfo.ShouldAdvertiseIdentity()); |
| saw1 = true; |
| } |
| if (iterFabricInfo.GetFabricIndex() == 2) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 1000u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u); |
| EXPECT_FALSE(iterFabricInfo.ShouldAdvertiseIdentity()); |
| saw2 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 2u); |
| EXPECT_TRUE(saw1); |
| EXPECT_TRUE(saw2); |
| } |
| |
| // Commit, now storage should have keys |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 2); |
| |
| EXPECT_EQ(storage.GetNumKeys(), (numStorageAfterSecondAdd - 1)); // ICAC got deleted |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(2); |
| ASSERT_NE(fabricInfo, nullptr); |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), fabricIndex); |
| EXPECT_EQ(fabricInfo->GetNodeId(), nodeId); |
| EXPECT_EQ(fabricInfo->GetFabricId(), fabricId); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(fabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey)); |
| |
| // Validate that fabric has the correct operational key by verifying a signature |
| { |
| Crypto::P256ECDSASignature sig; |
| uint8_t message[] = { 'm', 's', 'g' }; |
| |
| Crypto::P256PublicKey nocPubKey; |
| EXPECT_EQ(VerifyCertificateSigningRequest(csrSpan.data(), csrSpan.size(), nocPubKey), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricTable.SignWithOpKeypair(fabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR); |
| EXPECT_EQ(nocPubKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR); |
| } |
| |
| // Validate iterator sees the committed update |
| { |
| numFabricsIterated = 0; |
| bool saw1 = false; |
| bool saw2 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 55u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 11u); |
| saw1 = true; |
| } |
| if (iterFabricInfo.GetFabricIndex() == 2) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 1000u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u); |
| saw2 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 2u); |
| EXPECT_TRUE(saw1); |
| EXPECT_TRUE(saw2); |
| } |
| |
| // Next fabric index has stayed the same. |
| { |
| FabricIndex nextFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(nextFabricIndex, 3); |
| } |
| } |
| |
| size_t numStorageAfterUpdate = storage.GetNumKeys(); |
| |
| // Sequence 4: Rename fabric index 2, applies immediately when nothing pending |
| { |
| EXPECT_EQ(fabricTable.FabricCount(), 2); |
| EXPECT_EQ(fabricTable.SetFabricLabel(2, "roboto"_span), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 2); |
| |
| EXPECT_EQ(storage.GetNumKeys(), numStorageAfterUpdate); // Number of keys unchanged |
| |
| // Validate basic contents |
| { |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(2); |
| ASSERT_NE(fabricInfo, nullptr); |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 2); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 1000u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 44u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_TRUE(fabricInfo->GetFabricLabel().data_equal(CharSpan{ "roboto", strlen("roboto") })); |
| } |
| |
| // Next fabric index has stayed the same. |
| { |
| FabricIndex nextFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(nextFabricIndex, 3); |
| } |
| } |
| |
| // Sequence 5: Remove FabricIndex 1 (FabricId 11, NodeId 55), make sure FabricIndex 2 (FabricId 44, NodeId 1000) still exists |
| { |
| // Remove the fabric: no commit needed |
| { |
| EXPECT_EQ(fabricTable.FabricCount(), 2); |
| EXPECT_EQ(fabricTable.Delete(1), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| |
| EXPECT_EQ(storage.GetNumKeys(), (numStorageAfterUpdate - 3)); // Deleted NOC, RCAC, Metadata |
| } |
| |
| // Next fabric index has stayed the same. |
| { |
| FabricIndex nextFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(nextFabricIndex, 3); |
| } |
| |
| // Validate contents of Fabric Index 2 is still OK |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(2); |
| ASSERT_NE(fabricInfo, nullptr); |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 2); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 1000u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 44u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_TRUE(fabricInfo->GetFabricLabel().data_equal(CharSpan{ "roboto", strlen("roboto") })); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(2, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| |
| // Validate that fabric has the correct operational key by verifying a signature |
| { |
| uint8_t nocBuf[Credentials::kMaxCHIPCertLength]; |
| MutableByteSpan nocSpan{ nocBuf }; |
| EXPECT_EQ(fabricTable.FetchNOCCert(2, nocSpan), CHIP_NO_ERROR); |
| |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(nocSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey nocPubKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| Crypto::P256ECDSASignature sig; |
| uint8_t message[] = { 'm', 's', 'g' }; |
| |
| EXPECT_EQ(fabricTable.SignWithOpKeypair(2, ByteSpan{ message }, sig), CHIP_NO_ERROR); |
| EXPECT_EQ(nocPubKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR); |
| } |
| |
| // Validate iterator only sees the remaining fabric |
| { |
| numFabricsIterated = 0; |
| bool saw1 = false; |
| bool saw2 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| saw1 = true; |
| } |
| if (iterFabricInfo.GetFabricIndex() == 2) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 1000u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u); |
| saw2 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 1u); |
| EXPECT_FALSE(saw1); |
| EXPECT_TRUE(saw2); |
| } |
| } |
| } |
| |
| TEST_F(TestFabricTable, TestAddMultipleSameRootDifferentFabricId) |
| { |
| Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority; |
| |
| chip::TestPersistentStorageDelegate storage; |
| EXPECT_TRUE(fabricCertAuthority.Init().IsSuccess()); |
| |
| constexpr uint16_t kVendorId = 0xFFF1u; |
| |
| // Initialize a fabric table. |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| |
| uint8_t rcac1Buf[kMaxCHIPCertLength]; |
| MutableByteSpan rcac1Span{ rcac1Buf }; |
| |
| // First scope: add FabricID 1111, node ID 55 |
| { |
| FabricId fabricId = 1111; |
| NodeId nodeId = 55; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR); |
| ByteSpan rcac = fabricCertAuthority.GetRcac(); |
| // Keep a copy for second scope check |
| CopySpanToMutableSpan(rcac, rcac1Span); |
| |
| ByteSpan icac = fabricCertAuthority.GetIcac(); |
| ByteSpan noc = fabricCertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(newFabricIndex, 1); |
| |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(1); |
| ASSERT_NE(fabricInfo, nullptr); |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 1); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 55u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 1111u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| } |
| size_t numStorageKeysAfterFirstAdd = storage.GetNumKeys(); |
| EXPECT_EQ(numStorageKeysAfterFirstAdd, 7u); // Metadata, index, 3 certs, 1 opkey, last known good time |
| |
| // Second scope: add FabricID 2222, node ID 66, same root as first |
| { |
| FabricId fabricId = 2222; |
| NodeId nodeId = 66; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR); |
| ByteSpan rcac2 = fabricCertAuthority.GetRcac(); |
| EXPECT_TRUE(rcac2.data_equal(rcac1Span)); |
| |
| ByteSpan icac = fabricCertAuthority.GetIcac(); |
| ByteSpan noc = fabricCertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac2), CHIP_NO_ERROR); |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 2); |
| EXPECT_EQ(newFabricIndex, 2); |
| |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(2); |
| ASSERT_NE(fabricInfo, nullptr); |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 2); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 66u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 2222u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| } |
| size_t numStorageKeysAfterSecondAdd = storage.GetNumKeys(); |
| EXPECT_EQ(numStorageKeysAfterSecondAdd, (numStorageKeysAfterFirstAdd + 5)); // Add 3 certs, 1 metadata, 1 opkey |
| } |
| |
| TEST_F(TestFabricTable, TestAddMultipleSameFabricIdDifferentRoot) |
| { |
| Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority1; |
| Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority2; |
| |
| chip::TestPersistentStorageDelegate storage; |
| EXPECT_TRUE(fabricCertAuthority1.Init().IsSuccess()); |
| EXPECT_TRUE(fabricCertAuthority2.Init().IsSuccess()); |
| |
| constexpr uint16_t kVendorId = 0xFFF1u; |
| |
| // Initialize a fabric table. |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| |
| uint8_t rcac1Buf[kMaxCHIPCertLength]; |
| MutableByteSpan rcac1Span{ rcac1Buf }; |
| |
| // First scope: add FabricID 1111, node ID 55 |
| { |
| FabricId fabricId = 1111; |
| NodeId nodeId = 55; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricCertAuthority1.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR); |
| ByteSpan rcac = fabricCertAuthority1.GetRcac(); |
| // Keep a copy for second scope check |
| CopySpanToMutableSpan(rcac, rcac1Span); |
| |
| ByteSpan icac = fabricCertAuthority1.GetIcac(); |
| ByteSpan noc = fabricCertAuthority1.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(newFabricIndex, 1); |
| |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(1); |
| ASSERT_NE(fabricInfo, nullptr); |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 1); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 55u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 1111u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| } |
| size_t numStorageKeysAfterFirstAdd = storage.GetNumKeys(); |
| EXPECT_EQ(numStorageKeysAfterFirstAdd, 7u); // Metadata, index, 3 certs, 1 opkey, last known good time |
| |
| // Second scope: add FabricID 1111, node ID 66, different root from first |
| { |
| FabricId fabricId = 1111; |
| NodeId nodeId = 66; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricCertAuthority2.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR); |
| ByteSpan rcac2 = fabricCertAuthority2.GetRcac(); |
| EXPECT_FALSE(rcac2.data_equal(rcac1Span)); |
| |
| ByteSpan icac = fabricCertAuthority2.GetIcac(); |
| ByteSpan noc = fabricCertAuthority2.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac2), CHIP_NO_ERROR); |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 2); |
| EXPECT_EQ(newFabricIndex, 2); |
| |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(2); |
| ASSERT_NE(fabricInfo, nullptr); |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 2); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 66u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 1111u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| } |
| size_t numStorageKeysAfterSecondAdd = storage.GetNumKeys(); |
| EXPECT_EQ(numStorageKeysAfterSecondAdd, (numStorageKeysAfterFirstAdd + 5)); // Add 3 certs, 1 metadata, 1 opkey |
| } |
| |
| TEST_F(TestFabricTable, TestPersistence) |
| { |
| /** |
| * |
| * - Create an outer scope with storage delegate |
| * - Keep buffer slots for the operational public keys of the 2 fabrics added in the next scope |
| * |
| * - Create a new scope with a ScopedFabricTable |
| * - Add 2 fabrics, fully committed, using OperationalKeystore-based storage (e.g. CSR for opkey) |
| * - Make sure to save public keys in other scope for next step |
| * |
| * - Create a new scope with a ScopedFabricTable |
| * - Validate that after init, it has 2 fabrics and the 2 fabrics match fabric indices expected, |
| * and that the fabric tables are usable, and that NOC can be extracted for each, and that |
| * its public key matches expectation, and that they match the public keys stored in outer scope |
| * and verify you can sign and verify messages with the opkey |
| * |
| */ |
| |
| Crypto::P256PublicKey fIdx1PublicKey; |
| Crypto::P256PublicKey fIdx2PublicKey; |
| |
| Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority; |
| |
| chip::TestPersistentStorageDelegate storage; |
| |
| EXPECT_TRUE(fabricCertAuthority.Init().IsSuccess()); |
| |
| constexpr uint16_t kVendorId = 0xFFF1u; |
| |
| // First scope: add 2 fabrics with same root: (1111, 2222), commit them, keep track of public keys |
| { |
| // Initialize a FabricTable |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| |
| // Add Fabric 1111 Node Id 55 |
| { |
| FabricId fabricId = 1111; |
| NodeId nodeId = 55; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), |
| CHIP_NO_ERROR); |
| ByteSpan rcac = fabricCertAuthority.GetRcac(); |
| ByteSpan icac = fabricCertAuthority.GetIcac(); |
| ByteSpan noc = fabricCertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(newFabricIndex, 1); |
| |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(1); |
| ASSERT_NE(fabricInfo, nullptr); |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 1); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 55u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 1111u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey)); |
| |
| // Validate that fabric has the correct operational key by verifying a signature |
| { |
| Crypto::P256ECDSASignature sig; |
| uint8_t message[] = { 'm', 's', 'g' }; |
| |
| EXPECT_EQ(VerifyCertificateSigningRequest(csrSpan.data(), csrSpan.size(), fIdx1PublicKey), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricTable.SignWithOpKeypair(newFabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR); |
| EXPECT_EQ(fIdx1PublicKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR); |
| } |
| } |
| |
| // Add Fabric 2222 Node Id 66, no ICAC |
| { |
| FabricId fabricId = 2222; |
| NodeId nodeId = 66; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), |
| CHIP_NO_ERROR); |
| ByteSpan rcac = fabricCertAuthority.GetRcac(); |
| ByteSpan noc = fabricCertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, ByteSpan{}, kVendorId, &newFabricIndex), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 2); |
| EXPECT_EQ(newFabricIndex, 2); |
| |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(2); |
| ASSERT_NE(fabricInfo, nullptr); |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 2); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 66u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 2222u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey)); |
| |
| // Validate that fabric has the correct operational key by verifying a signature |
| { |
| Crypto::P256ECDSASignature sig; |
| uint8_t message[] = { 'm', 's', 'g' }; |
| |
| EXPECT_EQ(VerifyCertificateSigningRequest(csrSpan.data(), csrSpan.size(), fIdx2PublicKey), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricTable.SignWithOpKeypair(newFabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR); |
| EXPECT_EQ(fIdx2PublicKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR); |
| } |
| } |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 2); |
| |
| // Verify we can now see 2 fabrics with the iterator |
| { |
| size_t numFabricsIterated = 0; |
| bool saw1 = false; |
| bool saw2 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 55u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 1111u); |
| saw1 = true; |
| } |
| if (iterFabricInfo.GetFabricIndex() == 2) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 66u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 2222u); |
| saw2 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 2u); |
| EXPECT_TRUE(saw1); |
| EXPECT_TRUE(saw2); |
| } |
| |
| // Next fabric index should now be 3, since we added 1 and 2 above. |
| { |
| FabricIndex nextFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(nextFabricIndex, 3); |
| } |
| } |
| |
| // Global: Last known good time + fabric index = 2 |
| // Fabric 1111: Metadata, 1 opkey, RCAC/ICAC/NOC = 5 |
| // Fabric 2222: Metadata, 1 opkey, RCAC/NOC = 4 |
| EXPECT_EQ(storage.GetNumKeys(), (2u + 5u + 4u)); |
| |
| // Second scope: Validate that a fresh FabricTable loads the previously committed fabrics on Init. |
| { |
| // Initialize a FabricTable |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 2); |
| |
| // Verify we can see 2 fabrics with the iterator |
| { |
| size_t numFabricsIterated = 0; |
| bool saw1 = false; |
| bool saw2 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 55u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 1111u); |
| saw1 = true; |
| } |
| if (iterFabricInfo.GetFabricIndex() == 2) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 66u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 2222u); |
| saw2 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 2u); |
| EXPECT_TRUE(saw1); |
| EXPECT_TRUE(saw2); |
| } |
| |
| // Validate contents of Fabric 2222 |
| { |
| uint8_t rcacBuf[Credentials::kMaxCHIPCertLength]; |
| MutableByteSpan rcacSpan{ rcacBuf }; |
| EXPECT_EQ(fabricTable.FetchRootCert(2, rcacSpan), CHIP_NO_ERROR); |
| |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(2); |
| ASSERT_NE(fabricInfo, nullptr); |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(rcacSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 2); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 66u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 2222u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(2, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey)); |
| |
| // Validate that fabric has the correct operational key by verifying a signature |
| { |
| Crypto::P256ECDSASignature sig; |
| uint8_t message[] = { 'm', 's', 'g' }; |
| |
| EXPECT_EQ(fabricTable.SignWithOpKeypair(2, ByteSpan{ message }, sig), CHIP_NO_ERROR); |
| EXPECT_EQ(fIdx2PublicKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR); |
| } |
| } |
| |
| // Validate contents of Fabric 1111 |
| { |
| uint8_t rcacBuf[Credentials::kMaxCHIPCertLength]; |
| MutableByteSpan rcacSpan{ rcacBuf }; |
| EXPECT_EQ(fabricTable.FetchRootCert(1, rcacSpan), CHIP_NO_ERROR); |
| |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(1); |
| ASSERT_NE(fabricInfo, nullptr); |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(rcacSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 1); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 55u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 1111u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(1, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey)); |
| |
| // Validate that fabric has the correct operational key by verifying a signature |
| { |
| Crypto::P256ECDSASignature sig; |
| uint8_t message[] = { 'm', 's', 'g' }; |
| |
| EXPECT_EQ(fabricTable.SignWithOpKeypair(1, ByteSpan{ message }, sig), CHIP_NO_ERROR); |
| EXPECT_EQ(fIdx1PublicKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR); |
| } |
| |
| // Validate that signing with Fabric index 2 fails to verify with fabric index 1 |
| { |
| Crypto::P256ECDSASignature sig; |
| uint8_t message[] = { 'm', 's', 'g' }; |
| |
| EXPECT_EQ(fabricTable.SignWithOpKeypair(2, ByteSpan{ message }, sig), CHIP_NO_ERROR); |
| EXPECT_EQ(fIdx1PublicKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), |
| CHIP_ERROR_INVALID_SIGNATURE); |
| } |
| } |
| |
| // Validate that next fabric index is still 3; |
| { |
| FabricIndex nextFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(nextFabricIndex, 3); |
| } |
| } |
| } |
| |
| TEST_F(TestFabricTable, TestAddNocFailSafe) |
| { |
| Credentials::TestOnlyLocalCertificateAuthority fabric11CertAuthority; |
| Credentials::TestOnlyLocalCertificateAuthority fabric44CertAuthority; |
| |
| chip::TestPersistentStorageDelegate storage; |
| |
| EXPECT_TRUE(fabric11CertAuthority.Init().IsSuccess()); |
| EXPECT_TRUE(fabric44CertAuthority.Init().IsSuccess()); |
| |
| constexpr uint16_t kVendorId = 0xFFF1u; |
| |
| // Initialize a fabric table. |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| |
| size_t numFabricsIterated = 0; |
| |
| size_t numStorageKeysAtStart = storage.GetNumKeys(); |
| |
| // Sequence 1: Add node ID 55 on fabric 11, see that pending works, and that revert works |
| { |
| FabricId fabricId = 11; |
| NodeId nodeId = 55; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabric11CertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), |
| CHIP_NO_ERROR); |
| ByteSpan rcac = fabric11CertAuthority.GetRcac(); |
| ByteSpan noc = fabric11CertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| |
| { |
| FabricIndex nextFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(nextFabricIndex, 1); |
| } |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, ByteSpan{}, kVendorId, &newFabricIndex), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(newFabricIndex, 1); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| |
| // No storage yet |
| EXPECT_EQ(storage.GetNumKeys(), numStorageKeysAtStart); // Nothing yet |
| |
| // Validate iterator sees pending |
| { |
| numFabricsIterated = 0; |
| bool saw1 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), nodeId); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), fabricId); |
| saw1 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 1u); |
| EXPECT_TRUE(saw1); |
| } |
| |
| // Revert, should see nothing yet |
| fabricTable.RevertPendingFabricData(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| |
| // No started except fabric index metadata |
| EXPECT_EQ(storage.GetNumKeys(), (numStorageKeysAtStart + 1)); |
| |
| // Validate iterator sees nothing |
| { |
| numFabricsIterated = 0; |
| bool saw1 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| saw1 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 0u); |
| EXPECT_FALSE(saw1); |
| } |
| |
| // Validate next fabric index has not changed. |
| { |
| FabricIndex nextFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(nextFabricIndex, 1); |
| } |
| } |
| |
| size_t numStorageAfterRevert = storage.GetNumKeys(); |
| |
| // Sequence 2: Add node ID 999 on fabric 44, using operational keystore and ICAC --> Yield fabricIndex 1 |
| { |
| FabricId fabricId = 44; |
| NodeId nodeId = 999; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), |
| CHIP_NO_ERROR); |
| ByteSpan rcac = fabric44CertAuthority.GetRcac(); |
| ByteSpan icac = fabric44CertAuthority.GetIcac(); |
| ByteSpan noc = fabric44CertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(newFabricIndex, 1); |
| // No storage yet |
| EXPECT_EQ(storage.GetNumKeys(), numStorageAfterRevert); |
| |
| // Commit, now storage should have keys |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| |
| EXPECT_EQ(storage.GetNumKeys(), |
| (numStorageAfterRevert + 5)); // 3 opcerts + fabric metadata + 1 operational key |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(1); |
| ASSERT_NE(fabricInfo, nullptr); |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), newFabricIndex); |
| EXPECT_EQ(fabricInfo->GetNodeId(), nodeId); |
| EXPECT_EQ(fabricInfo->GetFabricId(), fabricId); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey)); |
| |
| // Validate that fabric has the correct operational key by verifying a signature |
| { |
| Crypto::P256ECDSASignature sig; |
| uint8_t message[] = { 'm', 's', 'g' }; |
| |
| Crypto::P256PublicKey nocPubKey; |
| EXPECT_EQ(VerifyCertificateSigningRequest(csrSpan.data(), csrSpan.size(), nocPubKey), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricTable.SignWithOpKeypair(newFabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR); |
| EXPECT_EQ(nocPubKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR); |
| } |
| |
| // Verify we can now see the fabric with the iterator |
| { |
| numFabricsIterated = 0; |
| bool saw1 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 999u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u); |
| saw1 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 1u); |
| EXPECT_TRUE(saw1); |
| } |
| } |
| |
| size_t numStorageAfterAdd = storage.GetNumKeys(); |
| |
| // Sequence 3: Do a RevertPendingFabricData() again, see that it doesn't affect existing fabric |
| |
| { |
| // Revert, should should look like a no-op |
| fabricTable.RevertPendingFabricData(); |
| |
| // No change of storage |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(storage.GetNumKeys(), numStorageAfterAdd); |
| |
| // Verify we can still see the fabric with the iterator |
| { |
| numFabricsIterated = 0; |
| bool saw1 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 999u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u); |
| saw1 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 1u); |
| EXPECT_TRUE(saw1); |
| } |
| } |
| } |
| |
| TEST_F(TestFabricTable, TestUpdateNocFailSafe) |
| { |
| Credentials::TestOnlyLocalCertificateAuthority fabric11CertAuthority; |
| Credentials::TestOnlyLocalCertificateAuthority fabric44CertAuthority; |
| |
| chip::TestPersistentStorageDelegate storage; |
| |
| storage.SetLoggingLevel(chip::TestPersistentStorageDelegate::LoggingLevel::kLogMutation); |
| |
| EXPECT_TRUE(fabric11CertAuthority.Init().IsSuccess()); |
| EXPECT_TRUE(fabric44CertAuthority.Init().IsSuccess()); |
| |
| constexpr uint16_t kVendorId = 0xFFF1u; |
| |
| // Initialize a fabric table. |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| |
| size_t numFabricsIterated = 0; |
| |
| size_t numStorageKeysAtStart = storage.GetNumKeys(); |
| |
| // Sequence 1: Add node ID 999 on fabric 44, using operational keystore and ICAC --> Yield fabricIndex 1 |
| { |
| FabricId fabricId = 44; |
| NodeId nodeId = 999; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), |
| CHIP_NO_ERROR); |
| ByteSpan rcac = fabric44CertAuthority.GetRcac(); |
| ByteSpan icac = fabric44CertAuthority.GetIcac(); |
| ByteSpan noc = fabric44CertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(newFabricIndex, 1); |
| // No storage yet |
| EXPECT_EQ(storage.GetNumKeys(), numStorageKeysAtStart); |
| |
| // Commit, now storage should have keys |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| |
| EXPECT_EQ(storage.GetNumKeys(), |
| (numStorageKeysAtStart + 6)); // 3 opcerts + fabric metadata + 1 operational key + LKGT + fabric index |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(1); |
| ASSERT_NE(fabricInfo, nullptr); |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), newFabricIndex); |
| EXPECT_EQ(fabricInfo->GetNodeId(), nodeId); |
| EXPECT_EQ(fabricInfo->GetFabricId(), fabricId); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey)); |
| |
| // Validate that fabric has the correct operational key by verifying a signature |
| { |
| Crypto::P256ECDSASignature sig; |
| uint8_t message[] = { 'm', 's', 'g' }; |
| |
| Crypto::P256PublicKey nocPubKey; |
| EXPECT_EQ(VerifyCertificateSigningRequest(csrSpan.data(), csrSpan.size(), nocPubKey), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricTable.SignWithOpKeypair(newFabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR); |
| EXPECT_EQ(nocPubKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR); |
| } |
| |
| // Verify we can now see the fabric with the iterator |
| { |
| numFabricsIterated = 0; |
| bool saw1 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 999u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u); |
| saw1 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 1u); |
| EXPECT_TRUE(saw1); |
| } |
| } |
| |
| size_t numStorageAfterAdd = storage.GetNumKeys(); |
| |
| // Sequence 2: Do an Update to NodeId 1000, with no ICAC, but revert it |
| { |
| FabricId fabricId = 44; |
| NodeId nodeId = 1000; |
| FabricIndex fabricIndex = 1; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| |
| // Make sure to tag fabric index to pending opkey: otherwise the UpdateNOC fails |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::MakeOptional(static_cast<FabricIndex>(1)), csrSpan), |
| CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), |
| CHIP_NO_ERROR); |
| ByteSpan rcac = fabric44CertAuthority.GetRcac(); |
| ByteSpan noc = fabric44CertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(1, noc, ByteSpan{}), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| |
| // No storage yet |
| EXPECT_EQ(storage.GetNumKeys(), numStorageAfterAdd); |
| |
| // Validate iterator sees the pending data |
| { |
| numFabricsIterated = 0; |
| bool saw1 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 1000u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u); |
| EXPECT_TRUE(iterFabricInfo.ShouldAdvertiseIdentity()); |
| saw1 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 1u); |
| EXPECT_TRUE(saw1); |
| } |
| |
| // Revert, should see Node ID 999 again |
| fabricTable.RevertPendingFabricData(); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| |
| EXPECT_EQ(storage.GetNumKeys(), numStorageAfterAdd); |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(1); |
| ASSERT_NE(fabricInfo, nullptr); |
| { |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), fabricIndex); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 999u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 44u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(fabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey)); |
| } |
| |
| // Validate that fabric has the correct operational key by verifying a signature |
| { |
| uint8_t nocBuf[Credentials::kMaxCHIPCertLength]; |
| MutableByteSpan nocSpan{ nocBuf }; |
| EXPECT_EQ(fabricTable.FetchNOCCert(1, nocSpan), CHIP_NO_ERROR); |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(nocSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey nocPubKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| Crypto::P256ECDSASignature sig; |
| uint8_t message[] = { 'm', 's', 'g' }; |
| |
| EXPECT_EQ(fabricTable.SignWithOpKeypair(fabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR); |
| EXPECT_EQ(nocPubKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR); |
| } |
| |
| // Validate iterator sees the previous fabric |
| { |
| numFabricsIterated = 0; |
| bool saw1 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 999u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u); |
| saw1 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 1u); |
| EXPECT_TRUE(saw1); |
| } |
| } |
| |
| // Sequence 3: Do an Update to NodeId 1001, with no ICAC, but commit it |
| { |
| FabricId fabricId = 44; |
| NodeId nodeId = 1001; |
| FabricIndex fabricIndex = 1; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| |
| // Make sure to tag fabric index to pending opkey: otherwise the UpdateNOC fails |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::MakeOptional(static_cast<FabricIndex>(1)), csrSpan), |
| CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), |
| CHIP_NO_ERROR); |
| ByteSpan rcac = fabric44CertAuthority.GetRcac(); |
| ByteSpan noc = fabric44CertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(1, noc, ByteSpan{}), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| |
| // No storage yet |
| EXPECT_EQ(storage.GetNumKeys(), numStorageAfterAdd); |
| |
| // Validate iterator sees the pending data |
| { |
| numFabricsIterated = 0; |
| bool saw1 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 1001u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u); |
| EXPECT_TRUE(iterFabricInfo.ShouldAdvertiseIdentity()); |
| saw1 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 1u); |
| EXPECT_TRUE(saw1); |
| } |
| |
| // Commit, should see Node ID 1001, and 1 less cert in the storage |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(storage.GetNumKeys(), numStorageAfterAdd - 1); |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(1); |
| ASSERT_NE(fabricInfo, nullptr); |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), fabricIndex); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 1001u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 44u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(fabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey)); |
| |
| // Validate that fabric has the correct operational key by verifying a signature |
| { |
| Crypto::P256PublicKey nocPubKey; |
| EXPECT_EQ(VerifyCertificateSigningRequest(csrSpan.data(), csrSpan.size(), nocPubKey), CHIP_NO_ERROR); |
| |
| Crypto::P256ECDSASignature sig; |
| uint8_t message[] = { 'm', 's', 'g' }; |
| |
| EXPECT_EQ(fabricTable.SignWithOpKeypair(fabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR); |
| EXPECT_EQ(nocPubKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR); |
| } |
| |
| // Validate iterator sees the updated fabric |
| { |
| numFabricsIterated = 0; |
| bool saw1 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 1001u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u); |
| saw1 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 1u); |
| EXPECT_TRUE(saw1); |
| } |
| } |
| } |
| |
| TEST_F(TestFabricTable, TestAddRootCertFailSafe) |
| { |
| Credentials::TestOnlyLocalCertificateAuthority fabric11CertAuthority; |
| |
| chip::TestPersistentStorageDelegate storage; |
| |
| EXPECT_TRUE(fabric11CertAuthority.Init().IsSuccess()); |
| |
| // Initialize a fabric table. |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| |
| // Add a root cert, see that pending works, and that revert works |
| { |
| ByteSpan rcac = fabric11CertAuthority.GetRcac(); |
| |
| uint8_t rcacBuf[Credentials::kMaxCHIPCertLength]; |
| { |
| // No pending root cert yet. |
| MutableByteSpan fetchedSpan{ rcacBuf }; |
| EXPECT_EQ(fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan), CHIP_ERROR_NOT_FOUND); |
| } |
| |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| { |
| // Now have a pending root cert. |
| MutableByteSpan fetchedSpan{ rcacBuf }; |
| EXPECT_EQ(fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan), CHIP_NO_ERROR); |
| EXPECT_TRUE(fetchedSpan.data_equal(rcac)); |
| } |
| |
| // Revert |
| fabricTable.RevertPendingFabricData(); |
| |
| { |
| // No pending root cert anymore. |
| MutableByteSpan fetchedSpan{ rcacBuf }; |
| EXPECT_EQ(fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan), CHIP_ERROR_NOT_FOUND); |
| } |
| } |
| } |
| |
| TEST_F(TestFabricTable, TestSequenceErrors) |
| { |
| // TODO: Write test |
| } |
| |
| TEST_F(TestFabricTable, TestFabricLabelChange) |
| { |
| Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority; |
| |
| chip::TestPersistentStorageDelegate storage; |
| EXPECT_TRUE(fabricCertAuthority.Init().IsSuccess()); |
| |
| constexpr uint16_t kVendorId = 0xFFF1u; |
| |
| // Initialize a fabric table. |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| |
| // First scope: add FabricID 1111, node ID 55 |
| { |
| FabricId fabricId = 1111; |
| NodeId nodeId = 55; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR); |
| ByteSpan rcac = fabricCertAuthority.GetRcac(); |
| ByteSpan icac = fabricCertAuthority.GetIcac(); |
| ByteSpan noc = fabricCertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(newFabricIndex, 1); |
| |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(1); |
| ASSERT_NE(fabricInfo, nullptr); |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 1); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 55u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 1111u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| } |
| size_t numStorageKeysAfterFirstAdd = storage.GetNumKeys(); |
| EXPECT_EQ(numStorageKeysAfterFirstAdd, 7u); // Metadata, index, 3 certs, 1 opkey, last known good time |
| |
| // Second scope: set FabricLabel to "acme fabric", make sure it cannot be reverted |
| { |
| // Fabric label starts unset from prior scope |
| CharSpan fabricLabel = "placeholder"_span; |
| |
| EXPECT_EQ(fabricTable.GetFabricLabel(1, fabricLabel), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricLabel.size(), 0u); |
| |
| // Set a valid name |
| EXPECT_EQ(fabricTable.SetFabricLabel(1, "acme fabric"_span), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.GetFabricLabel(1, fabricLabel), CHIP_NO_ERROR); |
| EXPECT_TRUE(fabricLabel.data_equal("acme fabric"_span)); |
| |
| // Revert pending fabric data. Should not revert name since nothing pending. |
| fabricTable.RevertPendingFabricData(); |
| |
| fabricLabel = "placeholder"_span; |
| EXPECT_EQ(fabricTable.GetFabricLabel(1, fabricLabel), CHIP_NO_ERROR); |
| EXPECT_TRUE(fabricLabel.data_equal("acme fabric"_span)); |
| |
| // Verify we fail to set too large a label (> kFabricLabelMaxLengthInBytes) |
| CharSpan fabricLabelTooBig = "012345678901234567890123456789123456"_span; |
| EXPECT_GT(fabricLabelTooBig.size(), chip::kFabricLabelMaxLengthInBytes); |
| |
| EXPECT_EQ(fabricTable.SetFabricLabel(1, fabricLabelTooBig), CHIP_ERROR_INVALID_ARGUMENT); |
| |
| fabricLabel = "placeholder"_span; |
| EXPECT_EQ(fabricTable.GetFabricLabel(1, fabricLabel), CHIP_NO_ERROR); |
| EXPECT_TRUE(fabricLabel.data_equal("acme fabric"_span)); |
| } |
| |
| // Third scope: set fabric label after an update, it sticks, but then goes back after revert |
| { |
| FabricId fabricId = 1111; |
| NodeId nodeId = 66; // Update node ID from 55 to 66 |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::MakeOptional(static_cast<FabricIndex>(1)), csrSpan), |
| CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR); |
| ByteSpan icac = fabricCertAuthority.GetIcac(); |
| ByteSpan noc = fabricCertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(1, noc, icac), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| |
| // Validate contents prior to change/revert |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(1); |
| |
| ASSERT_NE(fabricInfo, nullptr); |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 1); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 66u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 1111u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| |
| CharSpan fabricLabel = fabricInfo->GetFabricLabel(); |
| EXPECT_TRUE(fabricLabel.data_equal("acme fabric"_span)); |
| |
| // Update fabric label |
| fabricLabel = "placeholder"_span; |
| EXPECT_EQ(fabricTable.SetFabricLabel(1, "roboto fabric"_span), CHIP_NO_ERROR); |
| |
| fabricLabel = "placeholder"_span; |
| EXPECT_EQ(fabricTable.GetFabricLabel(1, fabricLabel), CHIP_NO_ERROR); |
| EXPECT_TRUE(fabricLabel.data_equal("roboto fabric"_span)); |
| |
| // Revert pending fabric data. Should revert name to "acme fabric" |
| fabricTable.RevertPendingFabricData(); |
| |
| fabricLabel = "placeholder"_span; |
| EXPECT_EQ(fabricTable.GetFabricLabel(1, fabricLabel), CHIP_NO_ERROR); |
| EXPECT_TRUE(fabricLabel.data_equal("acme fabric"_span)); |
| } |
| } |
| |
| TEST_F(TestFabricTable, TestCompressedFabricId) |
| { |
| // TODO: Write test |
| } |
| |
| TEST_F(TestFabricTable, TestFabricLookup) |
| { |
| // Initialize a fabric table. |
| chip::TestPersistentStorageDelegate testStorage; |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR); |
| EXPECT_EQ(LoadTestFabric_Node02_01(fabricTable, /* doCommit = */ true, FabricTable::AdvertiseIdentity::No), CHIP_NO_ERROR); |
| |
| // Attempt lookup of the Root01 fabric. |
| { |
| Crypto::P256PublicKey key; |
| EXPECT_GE(key.Length(), TestCerts::sTestCert_Root01_PublicKey.size()); |
| if (key.Length() < TestCerts::sTestCert_Root01_PublicKey.size()) |
| { |
| return; |
| } |
| memcpy(key.Bytes(), TestCerts::sTestCert_Root01_PublicKey.data(), TestCerts::sTestCert_Root01_PublicKey.size()); |
| auto fabricInfo = fabricTable.FindFabric(key, 0xFAB000000000001D); |
| ASSERT_NE(fabricInfo, nullptr); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 1); |
| EXPECT_TRUE(fabricInfo->ShouldAdvertiseIdentity()); |
| } |
| |
| // Attempt lookup of the Root02 fabric. |
| { |
| Crypto::P256PublicKey key; |
| EXPECT_GE(key.Length(), TestCerts::sTestCert_Root02_PublicKey.size()); |
| if (key.Length() < TestCerts::sTestCert_Root02_PublicKey.size()) |
| { |
| return; |
| } |
| memcpy(key.Bytes(), TestCerts::sTestCert_Root02_PublicKey.data(), TestCerts::sTestCert_Root02_PublicKey.size()); |
| auto fabricInfo = fabricTable.FindFabric(key, 0xFAB000000000001D); |
| ASSERT_NE(fabricInfo, nullptr); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 2); |
| EXPECT_FALSE(fabricInfo->ShouldAdvertiseIdentity()); |
| } |
| |
| // Attempt lookup of FabricIndex 0 --> should always fail. |
| { |
| EXPECT_EQ(fabricTable.FindFabricWithIndex(0), nullptr); |
| } |
| } |
| |
| TEST_F(TestFabricTable, TestFetchCATs) |
| { |
| // Initialize a fabric table. |
| chip::TestPersistentStorageDelegate testStorage; |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR); |
| EXPECT_EQ(LoadTestFabric_Node02_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR); |
| |
| // Attempt Fetching fabric index 1 CATs and verify contents. |
| { |
| CATValues cats; |
| EXPECT_EQ(fabricTable.FetchCATs(1, cats), CHIP_NO_ERROR); |
| // Test fabric NOCs don't contain any CATs. |
| EXPECT_EQ(cats, kUndefinedCATs); |
| } |
| |
| // Attempt Fetching fabric index 2 CATs and verify contents. |
| { |
| CATValues cats; |
| EXPECT_EQ(fabricTable.FetchCATs(2, cats), CHIP_NO_ERROR); |
| // Test fabric NOCs don't contain any CATs. |
| EXPECT_EQ(cats, kUndefinedCATs); |
| } |
| |
| // TODO(#20335): Add test cases for NOCs that actually embed CATs |
| } |
| |
| // Validate that adding the same fabric twice fails (same root, same FabricId) |
| TEST_F(TestFabricTable, TestAddNocRootCollision) |
| { |
| Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority; |
| |
| chip::TestPersistentStorageDelegate storage; |
| EXPECT_TRUE(fabricCertAuthority.Init().IsSuccess()); |
| |
| constexpr uint16_t kVendorId = 0xFFF1u; |
| |
| // Initialize a fabric table. |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| |
| // First scope: add FabricID 1111, node ID 55 |
| { |
| FabricId fabricId = 1111; |
| NodeId nodeId = 55; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR); |
| ByteSpan rcac = fabricCertAuthority.GetRcac(); |
| ByteSpan icac = fabricCertAuthority.GetIcac(); |
| ByteSpan noc = fabricCertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex, |
| FabricTable::AdvertiseIdentity::No), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(newFabricIndex, 1); |
| |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(1); |
| ASSERT_NE(fabricInfo, nullptr); |
| EXPECT_FALSE(fabricInfo->ShouldAdvertiseIdentity()); |
| |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 1); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 55u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 1111u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey)); |
| } |
| size_t numStorageKeysAfterFirstAdd = storage.GetNumKeys(); |
| EXPECT_EQ(numStorageKeysAfterFirstAdd, 7u); // Metadata, index, 3 certs, 1 opkey, last known good time |
| |
| // Second scope: add FabricID 1111, node ID 55 *again* --> Collision of Root/FabricID with existing |
| { |
| FabricId fabricId = 1111; |
| NodeId nodeId = 55; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR); |
| ByteSpan rcac = fabricCertAuthority.GetRcac(); |
| ByteSpan icac = fabricCertAuthority.GetIcac(); |
| ByteSpan noc = fabricCertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), |
| CHIP_ERROR_FABRIC_EXISTS); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| |
| CHIP_ERROR err = fabricTable.CommitPendingFabricData(); |
| printf("err = %" CHIP_ERROR_FORMAT "\n", err.Format()); |
| EXPECT_EQ(err, CHIP_ERROR_INCORRECT_STATE); |
| |
| // Validate contents of Fabric index 1 still valid |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(1); |
| ASSERT_NE(fabricInfo, nullptr); |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 1); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 55u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 1111u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(1, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey)); |
| } |
| |
| // Ensure no new persisted keys after failed colliding add |
| EXPECT_EQ(storage.GetNumKeys(), numStorageKeysAfterFirstAdd); |
| |
| // Third scope: add FabricID 2222, node ID 55 --> Not colliding, should work. The failing commit above] |
| // should have been enough of a revert that this scope succeeds without any additional revert. |
| { |
| FabricId fabricId = 2222; |
| NodeId nodeId = 55; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR); |
| ByteSpan rcac = fabricCertAuthority.GetRcac(); |
| ByteSpan icac = fabricCertAuthority.GetIcac(); |
| ByteSpan noc = fabricCertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 2); |
| EXPECT_EQ(newFabricIndex, 2); |
| |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(2); |
| ASSERT_NE(fabricInfo, nullptr); |
| EXPECT_TRUE(fabricInfo->ShouldAdvertiseIdentity()); |
| |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 2); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 55u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 2222u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey)); |
| } |
| size_t numStorageKeysAfterSecondAdd = storage.GetNumKeys(); |
| |
| EXPECT_EQ(numStorageKeysAfterSecondAdd, (numStorageKeysAfterFirstAdd + 5)); // Metadata, 3 certs, 1 opkey |
| } |
| |
| TEST_F(TestFabricTable, TestInvalidChaining) |
| { |
| Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority; |
| Credentials::TestOnlyLocalCertificateAuthority differentCertAuthority; |
| |
| chip::TestPersistentStorageDelegate storage; |
| EXPECT_TRUE(fabricCertAuthority.Init().IsSuccess()); |
| EXPECT_TRUE(differentCertAuthority.Init().IsSuccess()); |
| |
| constexpr uint16_t kVendorId = 0xFFF1u; |
| |
| // Initialize a fabric table. |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| |
| // Try to add fabric with either the NOC not chaining properly, or ICAC not chaining properly, fail, |
| // then succeed with proper chaining |
| { |
| FabricId fabricId = 1111; |
| NodeId nodeId = 55; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| // Generate same cert chain from two different roots |
| EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR); |
| EXPECT_EQ(differentCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), |
| CHIP_NO_ERROR); |
| |
| ByteSpan rcac = fabricCertAuthority.GetRcac(); |
| ByteSpan icac = fabricCertAuthority.GetIcac(); |
| ByteSpan noc = fabricCertAuthority.GetNoc(); |
| |
| ByteSpan otherIcac = differentCertAuthority.GetIcac(); |
| ByteSpan otherNoc = differentCertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| |
| // Add with NOC not chaining to ICAC: fail |
| { |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| CHIP_ERROR err = fabricTable.AddNewPendingFabricWithOperationalKeystore(otherNoc, icac, kVendorId, &newFabricIndex); |
| EXPECT_NE(err, CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| } |
| |
| // Add with ICAC not chaining to root: fail |
| { |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| CHIP_ERROR err = fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, otherIcac, kVendorId, &newFabricIndex); |
| EXPECT_NE(err, CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| } |
| |
| // Add with NOC and ICAC chaining together, but not to root: fail |
| { |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| CHIP_ERROR err = |
| fabricTable.AddNewPendingFabricWithOperationalKeystore(otherNoc, otherIcac, kVendorId, &newFabricIndex); |
| EXPECT_NE(err, CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| } |
| |
| // Revert state, start tests without ICAC |
| fabricTable.RevertPendingFabricData(); |
| |
| // Generate same cert chain from two different roots |
| |
| csrSpan = MutableByteSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR); |
| EXPECT_EQ(differentCertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), |
| CHIP_NO_ERROR); |
| |
| rcac = fabricCertAuthority.GetRcac(); |
| noc = fabricCertAuthority.GetNoc(); |
| |
| otherNoc = differentCertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| |
| // Add with NOC not chaining to RCAC: fail |
| { |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| CHIP_ERROR err = |
| fabricTable.AddNewPendingFabricWithOperationalKeystore(otherNoc, ByteSpan{}, kVendorId, &newFabricIndex); |
| EXPECT_NE(err, CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| } |
| |
| // Add properly now |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, ByteSpan{}, kVendorId, &newFabricIndex), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(newFabricIndex, 1); |
| |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(1); |
| ASSERT_NE(fabricInfo, nullptr); |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 1); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 55u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 1111u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| } |
| } |
| |
| TEST_F(TestFabricTable, TestEphemeralKeys) |
| { |
| // Initialize a fabric table with operational keystore |
| { |
| chip::TestPersistentStorageDelegate storage; |
| |
| // Initialize a FabricTable |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| Crypto::P256ECDSASignature sig; |
| uint8_t message[] = { 'm', 's', 'g' }; |
| |
| Crypto::P256Keypair * ephemeralKeypair = fabricTable.AllocateEphemeralKeypairForCASE(); |
| ASSERT_NE(ephemeralKeypair, nullptr); |
| EXPECT_EQ(ephemeralKeypair->Initialize(Crypto::ECPKeyTarget::ECDSA), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(ephemeralKeypair->ECDSA_sign_msg(message, sizeof(message), sig), CHIP_NO_ERROR); |
| EXPECT_EQ(ephemeralKeypair->Pubkey().ECDSA_validate_msg_signature(message, sizeof(message), sig), CHIP_NO_ERROR); |
| |
| fabricTable.ReleaseEphemeralKeypair(ephemeralKeypair); |
| } |
| |
| // Use a fabric table without an operational keystore: should still work |
| { |
| chip::TestPersistentStorageDelegate storage; |
| |
| chip::Credentials::PersistentStorageOpCertStore opCertStore; |
| EXPECT_EQ(opCertStore.Init(&storage), CHIP_NO_ERROR); |
| |
| FabricTable fabricTable; |
| FabricTable::InitParams initParams; |
| initParams.storage = &storage; |
| initParams.opCertStore = &opCertStore; |
| |
| EXPECT_EQ(fabricTable.Init(initParams), CHIP_NO_ERROR); |
| |
| Crypto::P256ECDSASignature sig; |
| uint8_t message[] = { 'm', 's', 'g' }; |
| |
| Crypto::P256Keypair * ephemeralKeypair = fabricTable.AllocateEphemeralKeypairForCASE(); |
| ASSERT_NE(ephemeralKeypair, nullptr); |
| EXPECT_EQ(ephemeralKeypair->Initialize(Crypto::ECPKeyTarget::ECDSA), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(ephemeralKeypair->ECDSA_sign_msg(message, sizeof(message), sig), CHIP_NO_ERROR); |
| EXPECT_EQ(ephemeralKeypair->Pubkey().ECDSA_validate_msg_signature(message, sizeof(message), sig), CHIP_NO_ERROR); |
| |
| fabricTable.ReleaseEphemeralKeypair(ephemeralKeypair); |
| |
| fabricTable.Shutdown(); |
| opCertStore.Finish(); |
| } |
| } |
| |
| TEST_F(TestFabricTable, TestCommitMarker) |
| { |
| Crypto::P256PublicKey fIdx1PublicKey; |
| Crypto::P256PublicKey fIdx2PublicKey; |
| |
| Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority; |
| |
| chip::TestPersistentStorageDelegate storage; |
| |
| // Log verbosity on this test helps debug significantly |
| storage.SetLoggingLevel(chip::TestPersistentStorageDelegate::LoggingLevel::kLogMutationAndReads); |
| |
| EXPECT_TRUE(fabricCertAuthority.Init().IsSuccess()); |
| |
| constexpr uint16_t kVendorId = 0xFFF1u; |
| |
| size_t numStorageKeysAfterFirstAdd = 0; |
| |
| // First scope: add 2 fabrics with same root: |
| // - FabricID 1111, Node ID 55 |
| // - FabricID 2222, Node ID 66 |
| // - Abort commit on second fabric |
| { |
| // Initialize a fabric table |
| ScopedFabricTable fabricTableHolder; |
| EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| EXPECT_EQ(fabricTable.GetDeletedFabricFromCommitMarker(), kUndefinedFabricIndex); |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| |
| // Add Fabric 1111 Node Id 55 |
| { |
| FabricId fabricId = 1111; |
| NodeId nodeId = 55; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), |
| CHIP_NO_ERROR); |
| ByteSpan rcac = fabricCertAuthority.GetRcac(); |
| ByteSpan icac = fabricCertAuthority.GetIcac(); |
| ByteSpan noc = fabricCertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 0); |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(newFabricIndex, 1); |
| |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR); |
| |
| // Validate contents |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(1); |
| ASSERT_NE(fabricInfo, nullptr); |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 1); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 55u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 1111u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey)); |
| |
| // Validate that fabric has the correct operational key by verifying a signature |
| { |
| Crypto::P256ECDSASignature sig; |
| uint8_t message[] = { 'm', 's', 'g' }; |
| |
| EXPECT_EQ(VerifyCertificateSigningRequest(csrSpan.data(), csrSpan.size(), fIdx1PublicKey), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricTable.SignWithOpKeypair(newFabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR); |
| EXPECT_EQ(fIdx1PublicKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR); |
| } |
| } |
| numStorageKeysAfterFirstAdd = storage.GetNumKeys(); |
| |
| EXPECT_EQ(numStorageKeysAfterFirstAdd, 7u); // Metadata, index, 3 certs, 1 opkey, last known good time |
| |
| // The following test requires test methods not available on all builds. |
| // TODO: Debug why some CI jobs don't set it properly. |
| #if CONFIG_BUILD_FOR_HOST_UNIT_TEST |
| |
| // Add Fabric 2222 Node Id 66, no ICAC *** AND ABORT COMMIT *** |
| { |
| FabricId fabricId = 2222; |
| NodeId nodeId = 66; |
| |
| uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size]; |
| MutableByteSpan csrSpan{ csrBuf }; |
| EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR); |
| |
| EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), |
| CHIP_NO_ERROR); |
| ByteSpan rcac = fabricCertAuthority.GetRcac(); |
| ByteSpan noc = fabricCertAuthority.GetNoc(); |
| |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR); |
| FabricIndex newFabricIndex = kUndefinedFabricIndex; |
| EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, ByteSpan{}, kVendorId, &newFabricIndex), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(fabricTable.FabricCount(), 2); |
| EXPECT_EQ(newFabricIndex, 2); |
| |
| // Validate contents of pending |
| const auto * fabricInfo = fabricTable.FindFabricWithIndex(2); |
| ASSERT_NE(fabricInfo, nullptr); |
| Credentials::ChipCertificateSet certificates; |
| EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR); |
| EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR); |
| Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey); |
| |
| EXPECT_EQ(fabricInfo->GetFabricIndex(), 2); |
| EXPECT_EQ(fabricInfo->GetNodeId(), 66u); |
| EXPECT_EQ(fabricInfo->GetFabricId(), 2222u); |
| EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId); |
| EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u); |
| |
| Crypto::P256PublicKey rootPublicKeyOfFabric; |
| EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR); |
| EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey)); |
| |
| // Make sure no additional storage yet |
| EXPECT_EQ(storage.GetNumKeys(), numStorageKeysAfterFirstAdd); |
| |
| // --> FORCE AN ERROR ON COMMIT that will BYPASS commit clean-up (similar to reboot during commit) |
| fabricTable.SetForceAbortCommitForTest(true); |
| EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_ERROR_INTERNAL); |
| |
| // Check that there are more keys now, partially committed: at least a Commit Marker (+1) |
| // and some more keys from the aborted process. |
| EXPECT_GT(storage.GetNumKeys(), (numStorageKeysAfterFirstAdd + 1)); |
| } |
| #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST |
| } |
| |
| #if CONFIG_BUILD_FOR_HOST_UNIT_TEST |
| { |
| storage.DumpKeys(); |
| |
| // Initialize a FabricTable again. Make sure it succeeds in initing. |
| ScopedFabricTable fabricTableHolder; |
| |
| EXPECT_GT(storage.GetNumKeys(), (numStorageKeysAfterFirstAdd + 1)); |
| |
| EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR); |
| FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); |
| |
| // Make sure that after init, the fabricTable has only 1 fabric |
| EXPECT_EQ(fabricTable.FabricCount(), 1); |
| |
| // Make sure it caught the last partially committed fabric |
| EXPECT_EQ(fabricTable.GetDeletedFabricFromCommitMarker(), 2); |
| |
| // Second read must return kUndefinedFabricIndex |
| EXPECT_EQ(fabricTable.GetDeletedFabricFromCommitMarker(), kUndefinedFabricIndex); |
| |
| { |
| // Here we would do other clean-ups (e.g. see Server.cpp that uses the above) and then |
| // clear the commit marker after. |
| fabricTable.ClearCommitMarker(); |
| } |
| |
| // Make sure that all other pending storage got deleted |
| EXPECT_EQ(storage.GetNumKeys(), numStorageKeysAfterFirstAdd); |
| |
| // Verify we can only see 1 fabric with the iterator |
| { |
| size_t numFabricsIterated = 0; |
| bool saw1 = false; |
| bool saw2 = false; |
| for (const auto & iterFabricInfo : fabricTable) |
| { |
| ++numFabricsIterated; |
| if (iterFabricInfo.GetFabricIndex() == 1) |
| { |
| EXPECT_EQ(iterFabricInfo.GetNodeId(), 55u); |
| EXPECT_EQ(iterFabricInfo.GetFabricId(), 1111u); |
| saw1 = true; |
| } |
| if (iterFabricInfo.GetFabricIndex() == 2) |
| { |
| saw2 = true; |
| } |
| } |
| |
| EXPECT_EQ(numFabricsIterated, 1u); |
| EXPECT_TRUE(saw1); |
| EXPECT_FALSE(saw2); |
| } |
| } |
| #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST |
| } |
| |
| } // namespace |