Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FabricTable utility methods #20332

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/credentials/FabricTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,32 @@ const FabricInfo * FabricTable::FindFabricWithCompressedId(CompressedFabricId co
return nullptr;
}

FabricInfo * FabricTable::FindFabricWithRootPubKeyFabricId(const Crypto::P256PublicKey & rootPubKey, const FabricId & fabricId)
msandstedt marked this conversation as resolved.
Show resolved Hide resolved
msandstedt marked this conversation as resolved.
Show resolved Hide resolved
{
for (auto & fabric : mStates)
{
if (!fabric.IsInitialized())
{
continue;
}
if (fabricId != fabric.GetFabricId())
{
continue;
}
Crypto::P256PublicKey candidateKey;
if (fabric.FetchRootPubkey(candidateKey) != CHIP_NO_ERROR)
{
continue;
}
if (memcmp(rootPubKey.ConstBytes(), candidateKey.ConstBytes(), candidateKey.Length()) != 0)
{
continue;
}
return &fabric;
}
return nullptr;
}

CHIP_ERROR FabricTable::FetchRootCert(FabricIndex fabricIndex, MutableByteSpan & outCert) const
{
VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
Expand Down Expand Up @@ -576,6 +602,15 @@ CHIP_ERROR FabricTable::FetchRootPubkey(FabricIndex fabricIndex, Crypto::P256Pub
return fabricInfo->FetchRootPubkey(outPublicKey);
}

CHIP_ERROR FabricTable::FetchCats(const FabricIndex fabricIndex, CATValues & cats) const
{
uint8_t nocBuf[Credentials::kMaxCHIPCertLength];
MutableByteSpan nocSpan{ nocBuf };
ReturnErrorOnFailure(FetchNOCCert(fabricIndex, nocSpan));
ReturnErrorOnFailure(ExtractCATsFromOpCert(nocSpan, cats));
return CHIP_NO_ERROR;
}

CHIP_ERROR FabricTable::StoreFabricMetadata(const FabricInfo * fabricInfo) const
{
VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
Expand Down
12 changes: 12 additions & 0 deletions src/credentials/FabricTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ class DLL_EXPORT FabricTable
const FabricInfo * FindFabric(const Crypto::P256PublicKey & rootPubKey, FabricId fabricId) const;
const FabricInfo * FindFabricWithIndex(FabricIndex fabricIndex) const;
const FabricInfo * FindFabricWithCompressedId(CompressedFabricId compressedFabricId) const;
FabricInfo * FindFabricWithRootPubKeyFabricId(const Crypto::P256PublicKey & rootPubKey, const FabricId & fabricId);

CHIP_ERROR Init(const FabricTable::InitParams & initParams);
void Shutdown();
Expand Down Expand Up @@ -561,6 +562,17 @@ class DLL_EXPORT FabricTable
*/
CHIP_ERROR FetchRootPubkey(FabricIndex fabricIndex, Crypto::P256PublicKey & outPublicKey) const;

/**
* @brief Get the CASE Authenticated Tags from the NOC for the given `fabricIndex`.
*
* @param fabricIndex - Fabric for which to get the root public key (subject public key of RCAC)
* @param outCats - CATValues struct to write the NOC CATs for the given fabric index
* @retval CHIP_NO_ERROR on success
* @retval CHIP_ERROR_INVALID_FABRIC_INDEX if not found/available, or `fabricIndex` has a bad value
* @retval other CHIP_ERROR values on other invalid arguments or internal errors.
*/
CHIP_ERROR FetchCats(const FabricIndex fabricIndex, CATValues & outCats) const;
msandstedt marked this conversation as resolved.
Show resolved Hide resolved

/**
* @brief Sign a message with a given fabric's operational keypair. This is used for
* CASE and the only way the key should be used.
Expand Down
131 changes: 123 additions & 8 deletions src/credentials/tests/TestFabricTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ using namespace chip::Credentials;
namespace {

Crypto::P256Keypair gFabric1OpKey;
Crypto::P256Keypair gFabric2OpKey;

class ScopedFabricTable
{
Expand Down Expand Up @@ -85,7 +86,7 @@ class ScopedFabricTable
/**
* Load a single test fabric with with the Root01:ICA01:Node01_01 identity.
*/
static CHIP_ERROR LoadTestFabric(nlTestSuite * inSuite, FabricTable & fabricTable, bool doCommit)
static CHIP_ERROR LoadTestFabric_Node01_01(nlTestSuite * inSuite, FabricTable & fabricTable, bool doCommit)
{
Crypto::P256SerializedKeypair opKeysSerialized;
Crypto::P256Keypair opKey;
Expand All @@ -100,8 +101,8 @@ static CHIP_ERROR LoadTestFabric(nlTestSuite * inSuite, FabricTable & fabricTabl
ByteSpan nocSpan(TestCerts::sTestCert_Node01_01_Chip, TestCerts::sTestCert_Node01_01_Chip_Len);

NL_TEST_ASSERT(inSuite,
opKeysSerialized.SetLength(TestCerts::sTestCert_Node01_02_PublicKey_Len +
TestCerts::sTestCert_Node01_02_PrivateKey_Len) == CHIP_NO_ERROR);
opKeysSerialized.SetLength(TestCerts::sTestCert_Node01_01_PublicKey_Len +
TestCerts::sTestCert_Node01_01_PrivateKey_Len) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, gFabric1OpKey.Deserialize(opKeysSerialized) == CHIP_NO_ERROR);

NL_TEST_ASSERT(inSuite, fabricTable.AddNewPendingTrustedRootCert(rcacSpan) == CHIP_NO_ERROR);
Expand All @@ -119,6 +120,42 @@ static CHIP_ERROR LoadTestFabric(nlTestSuite * inSuite, FabricTable & fabricTabl
return err;
}

/**
* Load a single test fabric with with the Root02:ICA02:Node02_01 identity.
*/
static CHIP_ERROR LoadTestFabric_Node02_01(nlTestSuite * inSuite, FabricTable & fabricTable, bool doCommit)
{
Crypto::P256SerializedKeypair opKeysSerialized;
Crypto::P256Keypair opKey;
FabricInfo fabricInfo;
msandstedt marked this conversation as resolved.
Show resolved Hide resolved
FabricIndex fabricIndex;
memcpy((uint8_t *) (opKeysSerialized), TestCerts::sTestCert_Node02_01_PublicKey, TestCerts::sTestCert_Node02_01_PublicKey_Len);
memcpy((uint8_t *) (opKeysSerialized) + TestCerts::sTestCert_Node02_01_PublicKey_Len, TestCerts::sTestCert_Node02_01_PrivateKey,
TestCerts::sTestCert_Node02_01_PrivateKey_Len);

ByteSpan rcacSpan(TestCerts::sTestCert_Root02_Chip, TestCerts::sTestCert_Root02_Chip_Len);
ByteSpan icacSpan(TestCerts::sTestCert_ICA02_Chip, TestCerts::sTestCert_ICA02_Chip_Len);
ByteSpan nocSpan(TestCerts::sTestCert_Node02_01_Chip, TestCerts::sTestCert_Node02_01_Chip_Len);

NL_TEST_ASSERT(inSuite,
opKeysSerialized.SetLength(TestCerts::sTestCert_Node02_01_PublicKey_Len +
TestCerts::sTestCert_Node02_01_PrivateKey_Len) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, gFabric2OpKey.Deserialize(opKeysSerialized) == CHIP_NO_ERROR);

NL_TEST_ASSERT(inSuite, fabricTable.AddNewPendingTrustedRootCert(rcacSpan) == CHIP_NO_ERROR);

CHIP_ERROR err = fabricTable.AddNewPendingFabricWithProvidedOpKey(nocSpan, icacSpan, VendorId::TestVendor1, &gFabric2OpKey,
msandstedt marked this conversation as resolved.
Show resolved Hide resolved
/*isExistingOpKeyExternallyOwned =*/true, &fabricIndex);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);

if (doCommit)
{
err = fabricTable.CommitPendingFabricData();
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
}

return err;
}
void TestLastKnownGoodTimeInit(nlTestSuite * inSuite, void * inContext)
{
// Fabric table init should init Last Known Good Time to the firmware build time.
Expand Down Expand Up @@ -170,7 +207,7 @@ void TestUpdateLastKnownGoodTime(nlTestSuite * inSuite, void * inContext)
NL_TEST_ASSERT(inSuite, lastKnownGoodTime == buildTime);

// Load a test fabric, but do not commit.
NL_TEST_ASSERT(inSuite, LoadTestFabric(inSuite, fabricTable, /* doCommit = */ false) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, LoadTestFabric_Node01_01(inSuite, 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
Expand All @@ -188,7 +225,7 @@ void TestUpdateLastKnownGoodTime(nlTestSuite * inSuite, void * inContext)
NL_TEST_ASSERT(inSuite, lastKnownGoodTime == buildTime);

// Now reload the test fabric and commit this time.
NL_TEST_ASSERT(inSuite, LoadTestFabric(inSuite, fabricTable, /* doCommit = */ true) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, LoadTestFabric_Node01_01(inSuite, 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
Expand Down Expand Up @@ -237,7 +274,7 @@ void TestUpdateLastKnownGoodTime(nlTestSuite * inSuite, void * inContext)
FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

// Load a test fabric, but do not commit.
NL_TEST_ASSERT(inSuite, LoadTestFabric(inSuite, fabricTable, /* doCommit = */ false) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, LoadTestFabric_Node01_01(inSuite, 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.
Expand Down Expand Up @@ -267,7 +304,7 @@ void TestUpdateLastKnownGoodTime(nlTestSuite * inSuite, void * inContext)
ScopedFabricTable fabricTableHolder;
NL_TEST_ASSERT(inSuite, fabricTableHolder.Init(&testStorage) == CHIP_NO_ERROR);
FabricTable & fabricTable = fabricTableHolder.GetFabricTable();
NL_TEST_ASSERT(inSuite, LoadTestFabric(inSuite, fabricTable, /* doCommit = */ true) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, LoadTestFabric_Node01_01(inSuite, 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.
Expand Down Expand Up @@ -327,7 +364,7 @@ void TestSetLastKnownGoodTime(nlTestSuite * inSuite, void * inContext)
FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

// Load a test fabric
NL_TEST_ASSERT(inSuite, LoadTestFabric(inSuite, fabricTable, /* doCommit= */ true) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, LoadTestFabric_Node01_01(inSuite, fabricTable, /* doCommit= */ true) == CHIP_NO_ERROR);

// Verify the Last Known Good Time matches our expected initial value.
System::Clock::Seconds32 initialLastKnownGoodTime =
Expand Down Expand Up @@ -1732,6 +1769,82 @@ void TestCompressedFabricId(nlTestSuite * inSuite, void * inContext)
// TODO: Write test
}

void TestFabricLookup(nlTestSuite * inSuite, void * inContext)
{
// Initialize a fabric table.
chip::TestPersistentStorageDelegate testStorage;
ScopedFabricTable fabricTableHolder;
NL_TEST_ASSERT(inSuite, fabricTableHolder.Init(&testStorage) == CHIP_NO_ERROR);
FabricTable & fabricTable = fabricTableHolder.GetFabricTable();
NL_TEST_ASSERT(inSuite, LoadTestFabric_Node01_01(inSuite, fabricTable, /* doCommit = */ true) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, LoadTestFabric_Node02_01(inSuite, fabricTable, /* doCommit = */ true) == CHIP_NO_ERROR);

// Attempt lookup of the Root01 fabric.
do
{
Crypto::P256PublicKey key;
NL_TEST_ASSERT(inSuite, key.Length() >= TestCerts::sTestCert_Root01_PublicKey_Len);
if (key.Length() < TestCerts::sTestCert_Root01_PublicKey_Len)
{
break;
}
memcpy(key.Bytes(), TestCerts::sTestCert_Root01_PublicKey, TestCerts::sTestCert_Root01_PublicKey_Len);
FabricInfo * fabricInfo = fabricTable.FindFabricWithRootPubKeyFabricId(key, 0xFAB000000000001D);
msandstedt marked this conversation as resolved.
Show resolved Hide resolved
NL_TEST_ASSERT(inSuite, fabricInfo != nullptr);
if (fabricInfo == nullptr)
{
break;
}
NL_TEST_ASSERT(inSuite, fabricInfo->GetFabricIndex() == 1);
msandstedt marked this conversation as resolved.
Show resolved Hide resolved
} while (0);

// Attempt lookup of the Root02 fabric.
do
{
Crypto::P256PublicKey key;
NL_TEST_ASSERT(inSuite, key.Length() >= TestCerts::sTestCert_Root02_PublicKey_Len);
if (key.Length() < TestCerts::sTestCert_Root02_PublicKey_Len)
{
break;
}
memcpy(key.Bytes(), TestCerts::sTestCert_Root02_PublicKey, TestCerts::sTestCert_Root02_PublicKey_Len);
FabricInfo * fabricInfo = fabricTable.FindFabricWithRootPubKeyFabricId(key, 0xFAB000000000001D);
NL_TEST_ASSERT(inSuite, fabricInfo != nullptr);
if (fabricInfo == nullptr)
{
break;
}
NL_TEST_ASSERT(inSuite, fabricInfo->GetFabricIndex() == 2);
} while (0);
}

void TestFetchCats(nlTestSuite * inSuite, void * inContext)
{
// Initialize a fabric table.
chip::TestPersistentStorageDelegate testStorage;
ScopedFabricTable fabricTableHolder;
NL_TEST_ASSERT(inSuite, fabricTableHolder.Init(&testStorage) == CHIP_NO_ERROR);
FabricTable & fabricTable = fabricTableHolder.GetFabricTable();
NL_TEST_ASSERT(inSuite, LoadTestFabric_Node01_01(inSuite, fabricTable, /* doCommit = */ true) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, LoadTestFabric_Node02_01(inSuite, fabricTable, /* doCommit = */ true) == CHIP_NO_ERROR);

// Attempt Fetching fabric index 1 CATs and verify contents.
{
CATValues cats;
NL_TEST_ASSERT(inSuite, fabricTable.FetchCats(1, cats) == CHIP_NO_ERROR);
// Test fabric NOCs don't contain any CATs.
NL_TEST_ASSERT(inSuite, cats == kUndefinedCATs);
}

// Attempt Fetching fabric index 2 CATs and verify contents.
{
CATValues cats;
NL_TEST_ASSERT(inSuite, fabricTable.FetchCats(2, cats) == CHIP_NO_ERROR);
// Test fabric NOCs don't contain any CATs.
NL_TEST_ASSERT(inSuite, cats == kUndefinedCATs);
}
}

void TestAddNocRootCollision(nlTestSuite * inSuite, void * inContext)
{
// TODO: Write test
Expand Down Expand Up @@ -2029,6 +2142,8 @@ static const nlTest sTests[] =
NL_TEST_DEF("Test interlock sequencing errors", TestSequenceErrors),
NL_TEST_DEF("Test fabric label changes", TestFabricLabelChange),
NL_TEST_DEF("Test compressed fabric ID is properly generated", TestCompressedFabricId),
NL_TEST_DEF("Test fabric lookup by <root public key, fabric ID>", TestFabricLookup),
NL_TEST_DEF("Test Fetching CATs", TestFetchCats),
NL_TEST_DEF("Test AddNOC root collision", TestAddNocRootCollision),
NL_TEST_DEF("Test invalid chaining in AddNOC and UpdateNOC", TestInvalidChaining),
NL_TEST_DEF("Test ephemeral keys allocation", TestEphemeralKeys),
Expand Down