From 9217d22c95f385e2cf9fce16a17fba18d5cf550b Mon Sep 17 00:00:00 2001 From: Michael Spang Date: Mon, 3 Jun 2024 18:22:50 -0400 Subject: [PATCH] Add ability to add pending fabric at a chosen fabric index (#33646) Currently it's only possible to allocate fabric indexes sequentially starting from 1. Sparse fabric tables could be produced by adding & removing fabrics, but not directly. This allow the application to control the assignment of fabric ids directly by providing a function that overrides the next index to use. eg If an application has 3 fabrics, it can recreate the fabric table from scratch while keeping consistent indexes. --- src/credentials/FabricTable.cpp | 12 +++ src/credentials/FabricTable.h | 7 ++ src/credentials/tests/CHIPCert_test_vectors.h | 20 ++++ src/credentials/tests/TestFabricTable.cpp | 97 ++++++++++++++++--- 4 files changed, 120 insertions(+), 16 deletions(-) diff --git a/src/credentials/FabricTable.cpp b/src/credentials/FabricTable.cpp index ade5189a738f27..b847addbe10773 100644 --- a/src/credentials/FabricTable.cpp +++ b/src/credentials/FabricTable.cpp @@ -2124,4 +2124,16 @@ CHIP_ERROR FabricTable::PeekFabricIndexForNextAddition(FabricIndex & outIndex) return CHIP_NO_ERROR; } +CHIP_ERROR FabricTable::SetFabricIndexForNextAddition(FabricIndex fabricIndex) +{ + VerifyOrReturnError(!mStateFlags.Has(StateFlags::kIsPendingFabricDataPresent), CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); + + const FabricInfo * fabricInfo = FindFabricWithIndex(fabricIndex); + VerifyOrReturnError(fabricInfo == nullptr, CHIP_ERROR_FABRIC_EXISTS); + + mNextAvailableFabricIndex.SetValue(fabricIndex); + return CHIP_NO_ERROR; +} + } // namespace chip diff --git a/src/credentials/FabricTable.h b/src/credentials/FabricTable.h index 55c1f6965e8c32..aef60f6a17fbbe 100644 --- a/src/credentials/FabricTable.h +++ b/src/credentials/FabricTable.h @@ -1003,6 +1003,13 @@ class DLL_EXPORT FabricTable */ CHIP_ERROR PeekFabricIndexForNextAddition(FabricIndex & outIndex); + /** + * Set the fabric index that will be used fo the next fabric added. + * + * Returns an error if the |fabricIndex| is already in use. + */ + CHIP_ERROR SetFabricIndexForNextAddition(FabricIndex fabricIndex); + private: enum class StateFlags : uint16_t { diff --git a/src/credentials/tests/CHIPCert_test_vectors.h b/src/credentials/tests/CHIPCert_test_vectors.h index 104436e5acc107..ef6ce7bb46c049 100644 --- a/src/credentials/tests/CHIPCert_test_vectors.h +++ b/src/credentials/tests/CHIPCert_test_vectors.h @@ -140,6 +140,8 @@ extern const ByteSpan sTestCert_Node01_01_PublicKey; extern const ByteSpan sTestCert_Node01_01_PrivateKey; extern const ByteSpan sTestCert_Node01_01_SubjectKeyId; extern const ByteSpan sTestCert_Node01_01_AuthorityKeyId; +inline constexpr NodeId kTestCert_Node01_01_NodeId = 0xDEDEDEDE00010001; +inline constexpr FabricId kTestCert_Node01_01_FabricId = 0xFAB000000000001D; extern const ByteSpan sTestCert_Node01_01_Err01_Chip; @@ -149,6 +151,8 @@ extern const ByteSpan sTestCert_Node01_02_PublicKey; extern const ByteSpan sTestCert_Node01_02_PrivateKey; extern const ByteSpan sTestCert_Node01_02_SubjectKeyId; extern const ByteSpan sTestCert_Node01_02_AuthorityKeyId; +inline constexpr NodeId kTestCert_Node01_02_NodeId = 0xDEDEDEDE00010002; +inline constexpr FabricId kTestCert_Node01_02_FabricId = 0xFAB000000000001D; extern const ByteSpan sTestCert_Node02_01_Chip; extern const ByteSpan sTestCert_Node02_01_DER; @@ -156,6 +160,8 @@ extern const ByteSpan sTestCert_Node02_01_PublicKey; extern const ByteSpan sTestCert_Node02_01_PrivateKey; extern const ByteSpan sTestCert_Node02_01_SubjectKeyId; extern const ByteSpan sTestCert_Node02_01_AuthorityKeyId; +inline constexpr NodeId kTestCert_Node02_01_NodeId = 0xDEDEDEDE00020001; +inline constexpr FabricId kTestCert_Node02_01_FabricId = 0xFAB000000000001D; extern const ByteSpan sTestCert_Node02_02_Chip; extern const ByteSpan sTestCert_Node02_02_DER; @@ -163,6 +169,8 @@ extern const ByteSpan sTestCert_Node02_02_PublicKey; extern const ByteSpan sTestCert_Node02_02_PrivateKey; extern const ByteSpan sTestCert_Node02_02_SubjectKeyId; extern const ByteSpan sTestCert_Node02_02_AuthorityKeyId; +inline constexpr NodeId kTestCert_Node02_02_NodeId = 0xDEDEDEDE00020002; +inline constexpr FabricId kTestCert_Node02_02_FabricId = 0xFAB000000000001D; extern const ByteSpan sTestCert_Node02_03_Chip; extern const ByteSpan sTestCert_Node02_03_DER; @@ -170,6 +178,8 @@ extern const ByteSpan sTestCert_Node02_03_PublicKey; extern const ByteSpan sTestCert_Node02_03_PrivateKey; extern const ByteSpan sTestCert_Node02_03_SubjectKeyId; extern const ByteSpan sTestCert_Node02_03_AuthorityKeyId; +inline constexpr NodeId kTestCert_Node02_03_NodeId = 0xDEDEDEDE00020003; +inline constexpr FabricId kTestCert_Node02_03_FabricId = 0xFAB000000000001D; extern const ByteSpan sTestCert_Node02_04_Chip; extern const ByteSpan sTestCert_Node02_04_DER; @@ -177,6 +187,8 @@ extern const ByteSpan sTestCert_Node02_04_PublicKey; extern const ByteSpan sTestCert_Node02_04_PrivateKey; extern const ByteSpan sTestCert_Node02_04_SubjectKeyId; extern const ByteSpan sTestCert_Node02_04_AuthorityKeyId; +inline constexpr NodeId kTestCert_Node02_04_NodeId = 0xDEDEDEDE00020004; +inline constexpr FabricId kTestCert_Node02_04_FabricId = 0xFAB000000000001D; extern const ByteSpan sTestCert_Node02_05_Chip; extern const ByteSpan sTestCert_Node02_05_DER; @@ -184,6 +196,8 @@ extern const ByteSpan sTestCert_Node02_05_PublicKey; extern const ByteSpan sTestCert_Node02_05_PrivateKey; extern const ByteSpan sTestCert_Node02_05_SubjectKeyId; extern const ByteSpan sTestCert_Node02_05_AuthorityKeyId; +inline constexpr NodeId kTestCert_Node02_05_NodeId = 0xDEDEDEDE00020005; +inline constexpr FabricId kTestCert_Node02_05_FabricId = 0xFAB000000000001D; extern const ByteSpan sTestCert_Node02_06_Chip; extern const ByteSpan sTestCert_Node02_06_DER; @@ -191,6 +205,8 @@ extern const ByteSpan sTestCert_Node02_06_PublicKey; extern const ByteSpan sTestCert_Node02_06_PrivateKey; extern const ByteSpan sTestCert_Node02_06_SubjectKeyId; extern const ByteSpan sTestCert_Node02_06_AuthorityKeyId; +inline constexpr NodeId kTestCert_Node02_06_NodeId = 0xDEDEDEDE00020006; +inline constexpr FabricId kTestCert_Node02_06_FabricId = 0xFAB000000000001D; extern const ByteSpan sTestCert_Node02_07_Chip; extern const ByteSpan sTestCert_Node02_07_DER; @@ -198,6 +214,8 @@ extern const ByteSpan sTestCert_Node02_07_PublicKey; extern const ByteSpan sTestCert_Node02_07_PrivateKey; extern const ByteSpan sTestCert_Node02_07_SubjectKeyId; extern const ByteSpan sTestCert_Node02_07_AuthorityKeyId; +inline constexpr NodeId kTestCert_Node02_07_NodeId = 0xDEDEDEDE00020007; +inline constexpr FabricId kTestCert_Node02_07_FabricId = 0xFAB000000000001D; extern const ByteSpan sTestCert_Node02_08_Chip; extern const ByteSpan sTestCert_Node02_08_DER; @@ -205,6 +223,8 @@ extern const ByteSpan sTestCert_Node02_08_PublicKey; extern const ByteSpan sTestCert_Node02_08_PrivateKey; extern const ByteSpan sTestCert_Node02_08_SubjectKeyId; extern const ByteSpan sTestCert_Node02_08_AuthorityKeyId; +inline constexpr NodeId kTestCert_Node02_08_NodeId = 0xDEDEDEDE00020008; +inline constexpr FabricId kTestCert_Node02_08_FabricId = 0xFAB000000000001D; extern const ByteSpan sTestCert_PDCID01_Chip; extern const ByteSpan sTestCert_PDCID01_ChipCompact; diff --git a/src/credentials/tests/TestFabricTable.cpp b/src/credentials/tests/TestFabricTable.cpp index a6b9f2ee6bc467..75f51c1c2cb402 100644 --- a/src/credentials/tests/TestFabricTable.cpp +++ b/src/credentials/tests/TestFabricTable.cpp @@ -183,6 +183,18 @@ static CHIP_ERROR LoadTestFabric_Node02_01(FabricTable & fabricTable, bool doCom return err; } +const FabricInfo * FindFabric(FabricTable & fabricTable, ByteSpan rootPublicKey, FabricId fabricId) +{ + Crypto::P256PublicKey key; + EXPECT_GE(key.Length(), rootPublicKey.size()); + if (key.Length() < rootPublicKey.size()) + { + return nullptr; + } + memcpy(key.Bytes(), rootPublicKey.data(), rootPublicKey.size()); + return fabricTable.FindFabric(key, fabricId); +} + struct TestFabricTable : public ::testing::Test { @@ -2279,16 +2291,13 @@ TEST_F(TestFabricTable, TestFabricLookup) 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); + // These two NOCs have the same fabric id on purpose; only the trust root is + // different. + constexpr FabricId kNode01_01_and_02_01_FabricId = 0xFAB000000000001D; + // 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); + auto fabricInfo = FindFabric(fabricTable, TestCerts::sTestCert_Root01_PublicKey, kNode01_01_and_02_01_FabricId); ASSERT_NE(fabricInfo, nullptr); EXPECT_EQ(fabricInfo->GetFabricIndex(), 1); @@ -2297,14 +2306,7 @@ TEST_F(TestFabricTable, TestFabricLookup) // 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); + auto fabricInfo = FindFabric(fabricTable, TestCerts::sTestCert_Root02_PublicKey, kNode01_01_and_02_01_FabricId); ASSERT_NE(fabricInfo, nullptr); EXPECT_EQ(fabricInfo->GetFabricIndex(), 2); @@ -2317,6 +2319,69 @@ TEST_F(TestFabricTable, TestFabricLookup) } } +TEST_F(TestFabricTable, ShouldFailSetFabricIndexWithInvalidIndex) +{ + chip::TestPersistentStorageDelegate testStorage; + ScopedFabricTable fabricTableHolder; + EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR); + FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); + + EXPECT_EQ(fabricTable.SetFabricIndexForNextAddition(kUndefinedFabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); +} + +TEST_F(TestFabricTable, ShouldFailSetFabricIndexWithPendingFabric) +{ + chip::TestPersistentStorageDelegate testStorage; + ScopedFabricTable fabricTableHolder; + EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR); + FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); + + EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(ByteSpan(TestCerts::sTestCert_Root01_Chip)), CHIP_NO_ERROR); + + EXPECT_EQ(fabricTable.SetFabricIndexForNextAddition(1), CHIP_ERROR_INCORRECT_STATE); +} + +TEST_F(TestFabricTable, ShouldFailSetFabricIndexWhenInUse) +{ + 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(fabricTable.SetFabricIndexForNextAddition(1), CHIP_ERROR_FABRIC_EXISTS); +} + +TEST_F(TestFabricTable, ShouldAddFabricAtRequestedIndex) +{ + chip::TestPersistentStorageDelegate testStorage; + ScopedFabricTable fabricTableHolder; + EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR); + FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); + + EXPECT_EQ(fabricTable.SetFabricIndexForNextAddition(2), CHIP_NO_ERROR); + EXPECT_EQ(LoadTestFabric_Node02_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR); + + EXPECT_EQ(fabricTable.SetFabricIndexForNextAddition(1), CHIP_NO_ERROR); + EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR); + + { + auto fabricInfo = FindFabric(fabricTable, TestCerts::sTestCert_Root01_PublicKey, TestCerts::kTestCert_Node01_01_FabricId); + ASSERT_NE(fabricInfo, nullptr); + EXPECT_EQ(fabricInfo->GetFabricIndex(), 1); + EXPECT_EQ(fabricInfo->GetNodeId(), TestCerts::kTestCert_Node01_01_NodeId); + EXPECT_EQ(fabricInfo->GetFabricId(), TestCerts::kTestCert_Node01_01_FabricId); + } + + { + auto fabricInfo = FindFabric(fabricTable, TestCerts::sTestCert_Root02_PublicKey, TestCerts::kTestCert_Node02_01_FabricId); + ASSERT_NE(fabricInfo, nullptr); + EXPECT_EQ(fabricInfo->GetFabricIndex(), 2); + EXPECT_EQ(fabricInfo->GetNodeId(), TestCerts::kTestCert_Node02_01_NodeId); + EXPECT_EQ(fabricInfo->GetFabricId(), TestCerts::kTestCert_Node02_01_FabricId); + } +} + TEST_F(TestFabricTable, TestFetchCATs) { // Initialize a fabric table.