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

Include non-fabric-associated pending roots in the RootCertificates attribute. #21016

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,24 @@ CHIP_ERROR OperationalCredentialsAttrAccess::ReadRootCertificates(EndpointId end
ReturnErrorOnFailure(encoder.Encode(ByteSpan{ cert }));
}

{
uint8_t certBuf[kMaxCHIPCertLength];
MutableByteSpan cert{ certBuf };
CHIP_ERROR err = fabricTable.FetchPendingNonFabricAssociatedRootCert(cert);
if (err == CHIP_ERROR_NOT_FOUND)
{
// No pending root cert, do nothing
}
else if (err != CHIP_NO_ERROR)
{
return err;
}
else
{
ReturnErrorOnFailure(encoder.Encode(ByteSpan{ cert }));
}
}

return CHIP_NO_ERROR;
});
}
Expand Down
18 changes: 18 additions & 0 deletions src/credentials/FabricTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,24 @@ CHIP_ERROR FabricTable::FetchRootCert(FabricIndex fabricIndex, MutableByteSpan &
return mOpCertStore->GetCertificate(fabricIndex, CertChainElement::kRcac, outCert);
}

CHIP_ERROR FabricTable::FetchPendingNonFabricAssociatedRootCert(MutableByteSpan & outCert) const
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
{
VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
if (!mStateFlags.Has(StateFlags::kIsTrustedRootPending))
{
return CHIP_ERROR_NOT_FOUND;
}

if (mStateFlags.Has(StateFlags::kIsAddPending))
{
// The root certificate is already associated with a pending fabric, so
// does not exist for purposes of this API.
return CHIP_ERROR_NOT_FOUND;
}

return FetchRootCert(mFabricIndexWithPendingState, outCert);
}

CHIP_ERROR FabricTable::FetchICACert(FabricIndex fabricIndex, MutableByteSpan & outCert) const
{
VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
Expand Down
42 changes: 29 additions & 13 deletions src/credentials/FabricTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -514,19 +514,6 @@ class DLL_EXPORT FabricTable
ConstFabricIterator begin() const { return cbegin(); }
ConstFabricIterator end() const { return cend(); }

/**
* @brief Get a mutable FabricInfo entry from the table by FabricIndex.
*
* NOTE: This is private for use within the FabricTable itself. All mutations have to go through the
* FabricTable public methods that take a FabricIndex so that there are no mutations about which
* the FabricTable is unaware, since this would break expectations regarding shadow/pending
* entries used during fail-safe.
*
* @param fabricIndex - fabric index for which to get a mutable FabricInfo entry
* @return the FabricInfo entry for the fabricIndex if found, or nullptr if not found
*/
FabricInfo * GetMutableFabricByIndex(FabricIndex fabricIndex);

/**
* @brief Get the RCAC (operational root certificate) associated with a fabric.
*
Expand All @@ -541,6 +528,22 @@ class DLL_EXPORT FabricTable
*/
CHIP_ERROR FetchRootCert(FabricIndex fabricIndex, MutableByteSpan & outCert) const;

/**
* @brief Get the pending root certificate which is not associated with a fabric, if there is one.
*
* If a root is pending from `AddNewPendingTrustedRootCert`, and there is no
* fabric associated with the corresponding fabric index yet
* (i.e. `AddNewPendingFabric*` has not been called yet) it is returned.
*
* @param outCert - MutableByteSpan to receive the certificate. Resized to actual size.
* @retval CHIP_NO_ERROR on success
* @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outCert` is too small.
* @retval CHIP_ERROR_NOT_FOUND if there is no pending root certificate
* that's not yet associated with a fabric.
* @retval other CHIP_ERROR values on invalid arguments or internal errors.
*/
CHIP_ERROR FetchPendingNonFabricAssociatedRootCert(MutableByteSpan & outCert) const;

/**
* @brief Get the ICAC (operational intermediate certificate) associated with a fabric.
*
Expand Down Expand Up @@ -948,6 +951,19 @@ class DLL_EXPORT FabricTable
bool isAddition = false;
};

/**
* @brief Get a mutable FabricInfo entry from the table by FabricIndex.
*
* NOTE: This is private for use within the FabricTable itself. All mutations have to go through the
* FabricTable public methods that take a FabricIndex so that there are no mutations about which
* the FabricTable is unaware, since this would break expectations regarding shadow/pending
* entries used during fail-safe.
*
* @param fabricIndex - fabric index for which to get a mutable FabricInfo entry
* @return the FabricInfo entry for the fabricIndex if found, or nullptr if not found
*/
FabricInfo * GetMutableFabricByIndex(FabricIndex fabricIndex);
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved

// Load a FabricInfo metatada item from storage for a given new fabric index. Returns internal error on failure.
CHIP_ERROR LoadFromStorage(FabricInfo * fabric, FabricIndex newFabricIndex);

Expand Down
65 changes: 65 additions & 0 deletions src/credentials/tests/TestFabricTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,21 @@ void TestBasicAddNocUpdateNocFlow(nlTestSuite * inSuite, void * inContext)
NL_TEST_ASSERT(inSuite, saw1 == false);
}

uint8_t rcacBuf[Credentials::kMaxCHIPCertLength];
{
// No pending root cert yet.
MutableByteSpan fetchedSpan{ rcacBuf };
NL_TEST_ASSERT(inSuite, fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan) == CHIP_ERROR_NOT_FOUND);
}

NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.AddNewPendingTrustedRootCert(rcac));
{
// Now have a pending root cert.
MutableByteSpan fetchedSpan{ rcacBuf };
NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan));
NL_TEST_ASSERT(inSuite, fetchedSpan.data_equal(rcac));
}

FabricIndex newFabricIndex = kUndefinedFabricIndex;
bool keyIsExternallyOwned = true;

Expand All @@ -488,6 +502,11 @@ void TestBasicAddNocUpdateNocFlow(nlTestSuite * inSuite, void * inContext)
keyIsExternallyOwned, &newFabricIndex));
NL_TEST_ASSERT(inSuite, newFabricIndex == 1);
NL_TEST_ASSERT_EQUALS(inSuite, fabricTable.FabricCount(), 1);
{
// No more pending root cert; it's associated with a fabric now.
MutableByteSpan fetchedSpan{ rcacBuf };
NL_TEST_ASSERT(inSuite, fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan) == CHIP_ERROR_NOT_FOUND);
}

// No storage yet
NL_TEST_ASSERT(inSuite, storage.GetNumKeys() == numStorageKeysAtStart);
Expand Down Expand Up @@ -1750,6 +1769,51 @@ void TestUpdateNocFailSafe(nlTestSuite * inSuite, void * inContext)
}
}

void TestAddRootCertFailSafe(nlTestSuite * inSuite, void * inContext)
{
Credentials::TestOnlyLocalCertificateAuthority fabric11CertAuthority;

chip::TestPersistentStorageDelegate storage;

NL_TEST_ASSERT(inSuite, fabric11CertAuthority.Init().IsSuccess());

// Initialize a fabric table.
ScopedFabricTable fabricTableHolder;
NL_TEST_ASSERT(inSuite, fabricTableHolder.Init(&storage) == CHIP_NO_ERROR);
FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

NL_TEST_ASSERT_EQUALS(inSuite, 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 };
NL_TEST_ASSERT(inSuite, fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan) == CHIP_ERROR_NOT_FOUND);
}

NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.AddNewPendingTrustedRootCert(rcac));
{
// Now have a pending root cert.
MutableByteSpan fetchedSpan{ rcacBuf };
NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan));
NL_TEST_ASSERT(inSuite, fetchedSpan.data_equal(rcac));
}

// Revert
fabricTable.RevertPendingFabricData();

{
// No pending root cert anymore.
MutableByteSpan fetchedSpan{ rcacBuf };
NL_TEST_ASSERT(inSuite, fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan) == CHIP_ERROR_NOT_FOUND);
}
}
}

void TestSequenceErrors(nlTestSuite * inSuite, void * inContext)
{
// TODO: Write test
Expand Down Expand Up @@ -2436,6 +2500,7 @@ static const nlTest sTests[] =
NL_TEST_DEF("Validate fabrics are loaded from persistence at FabricTable::init", TestPersistence),
NL_TEST_DEF("Test fail-safe handling during AddNOC", TestAddNocFailSafe),
NL_TEST_DEF("Test fail-safe handling during UpdateNoc", TestUpdateNocFailSafe),
NL_TEST_DEF("Test fail-safe handling for root cert", TestAddRootCertFailSafe),
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),
Expand Down