blob: ac5e26bb94036eb64b00932cde065f7e54efd163 [file] [log] [blame]
/*
*
* 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 <stdarg.h>
#include <pw_unit_test/framework.h>
#include <lib/core/CHIPCore.h>
#include <lib/core/StringBuilderAdapters.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>
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);
}
CHIP_ERROR ReinitFabricTable(chip::TestPersistentStorageDelegate * storage)
{
chip::FabricTable::InitParams initParams;
initParams.storage = storage;
initParams.operationalKeystore = &mOpKeyStore;
initParams.opCertStore = &mOpCertStore;
return mFabricTable.Init(initParams);
}
FabricTable & GetFabricTable() { return mFabricTable; }
Credentials::PersistentStorageOpCertStore & GetOpCertStore() { return mOpCertStore; }
private:
FabricTable mFabricTable;
PersistentStorageOperationalKeystore mOpKeyStore;
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;
}
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);
}
CHIP_ERROR VerifySignatureWithNocPublicKey(FabricTable & fabricTable, FabricIndex fabricIndex, ByteSpan message,
ByteSpan signatureBytes)
{
uint8_t nocCertBuf[kMaxCHIPCertLength];
MutableByteSpan nocCertSpan{ nocCertBuf };
ReturnErrorOnFailure(fabricTable.FetchNOCCert(fabricIndex, nocCertSpan));
P256PublicKeySpan nocPublicKeySpan;
ReturnErrorOnFailure(ExtractPublicKeyFromChipCert(nocCertSpan, nocPublicKeySpan));
Crypto::P256PublicKey nocPublicKey(nocPublicKeySpan);
VerifyOrReturnError(signatureBytes.size() >= Crypto::P256ECDSASignature::Capacity(), CHIP_ERROR_BUFFER_TOO_SMALL);
Crypto::P256ECDSASignature signature;
memcpy(signature.Bytes(), signatureBytes.data(), signature.Capacity());
signature.SetLength(signature.Capacity());
return nocPublicKey.ECDSA_validate_msg_signature(message.data(), message.size(), signature);
}
struct TestFabricTable : public ::testing::Test
{
static void SetUpTestSuite()
{
DeviceLayer::SetConfigurationMgr(&DeviceLayer::ConfigurationManagerImpl::GetDefaultInstance());
ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR);
#if CHIP_CRYPTO_PSA
ASSERT_EQ(psa_crypto_init(), PSA_SUCCESS);
#endif
}
static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); }
FabricIndex CreateBasicFabricForVidVerification(FabricTable & fabricTable, Crypto::P256SerializedKeypair & rootKeyForTest,
FabricId fabricId, uint16_t vendorId)
{
constexpr NodeId kNodeId = 1;
Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority;
VerifyOrDie(fabricCertAuthority.Init(rootKeyForTest).IsSuccess());
uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
MutableByteSpan csrSpan{ csrBuf };
VerifyOrDie(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan) == CHIP_NO_ERROR);
VerifyOrDie(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, kNodeId, csrSpan).GetStatus() ==
CHIP_NO_ERROR);
ByteSpan rcac = fabricCertAuthority.GetRcac();
ByteSpan icac = fabricCertAuthority.GetIcac();
ByteSpan noc = fabricCertAuthority.GetNoc();
VerifyOrDie(fabricTable.AddNewPendingTrustedRootCert(rcac) == CHIP_NO_ERROR);
FabricIndex newFabricIndex = kUndefinedFabricIndex;
VerifyOrDie(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, vendorId, &newFabricIndex) == CHIP_NO_ERROR);
VerifyOrDie(fabricTable.CommitPendingFabricData() == CHIP_NO_ERROR);
// Validate contents
const auto * fabricInfo = fabricTable.FindFabricWithIndex(newFabricIndex);
VerifyOrDie(fabricInfo != nullptr);
VerifyOrDie(fabricInfo->GetFabricId() == fabricId);
VerifyOrDie(fabricInfo->GetVendorId() == vendorId);
return fabricInfo->GetFabricIndex();
}
};
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);
EXPECT_EQ(fabricTable.GetPendingNewFabricIndex(), kUndefinedFabricIndex);
{
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));
}
EXPECT_EQ(fabricTable.GetPendingNewFabricIndex(), kUndefinedFabricIndex);
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);
// After adding the pending new fabric (equivalent of AddNOC processing), the new
// fabric must be pending.
EXPECT_EQ(fabricTable.GetPendingNewFabricIndex(), 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);
}
// Fabric can't be pending anymore.
EXPECT_EQ(fabricTable.GetPendingNewFabricIndex(), kUndefinedFabricIndex);
// 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);
EXPECT_TRUE(iterFabricInfo.ShouldAdvertiseIdentity());
saw1 = true;
}
}
EXPECT_EQ(numFabricsIterated, 1u);
EXPECT_TRUE(saw1);
}
}
size_t numStorageAfterFirstAdd = storage.GetNumKeys();
// Sequence 2: Add VVS and VVSC (VVSC possible since fabric 11 doesn't have ICAC). Make sure additional storage is present.
{
constexpr FabricIndex kFirstFabricIndex = 1u;
// VVSC contents is not checked, so can be just zero bytes.
uint8_t vvsc[kMaxCHIPCertLength];
memset(&vvsc[0], 0x00, sizeof(vvsc));
ByteSpan vvscSpan{ vvsc };
// VVS contents is not checked, except first by that must be 0x01.
uint8_t vvs[Crypto::kVendorIdVerificationStatementV1Size];
memset(&vvs[0], 0x01, sizeof(vvs));
ByteSpan vvsSpan{ vvs };
// Set new VVS, VVSC. Applies immediately due to no intermediate NOC update flow present.
bool fabricTableWasChanged = false;
EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(kFirstFabricIndex, NullOptional, MakeOptional(vvsSpan),
MakeOptional(vvscSpan), fabricTableWasChanged),
CHIP_NO_ERROR);
EXPECT_EQ(fabricTableWasChanged, true);
EXPECT_EQ(storage.GetNumKeys(), numStorageAfterFirstAdd + 2); // VVSC and VVS added.
// Make sure VVSC was stored.
{
uint8_t readBackVvscBuf[kMaxCHIPCertLength];
memset(&readBackVvscBuf[0], 0x11, sizeof(readBackVvscBuf));
MutableByteSpan readBackVvscSpan{ readBackVvscBuf };
EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
kFirstFabricIndex, OperationalCertificateStore::VidVerificationElement::kVvsc, readBackVvscSpan),
CHIP_NO_ERROR);
EXPECT_TRUE(readBackVvscSpan.data_equal(vvscSpan));
}
// Make sure VVS was also set pending
{
uint8_t readBackVvsBuf[Crypto::kVendorIdVerificationStatementV1Size];
memset(&readBackVvsBuf[0], 0x00, sizeof(readBackVvsBuf));
MutableByteSpan readBackVvsSpan{ readBackVvsBuf };
EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
kFirstFabricIndex, OperationalCertificateStore::VidVerificationElement::kVidVerificationStatement,
readBackVvsSpan),
CHIP_NO_ERROR);
EXPECT_TRUE(readBackVvsSpan.data_equal(vvsSpan));
}
}
numStorageAfterFirstAdd = storage.GetNumKeys(); // Accounts now for VVSC + VVS
// Sequence 3: 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);
EXPECT_EQ(fabricTable.GetPendingNewFabricIndex(), kUndefinedFabricIndex);
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);
EXPECT_EQ(fabricTable.GetPendingNewFabricIndex(), 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);
EXPECT_TRUE(iterFabricInfo.ShouldAdvertiseIdentity());
saw1 = true;
}
if (iterFabricInfo.GetFabricIndex() == 2)
{
EXPECT_EQ(iterFabricInfo.GetNodeId(), 999u);
EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u);
EXPECT_TRUE(iterFabricInfo.ShouldAdvertiseIdentity());
saw2 = true;
}
}
EXPECT_EQ(numFabricsIterated, 2u);
EXPECT_TRUE(saw1);
EXPECT_TRUE(saw2);
}
}
size_t numStorageAfterSecondAdd = storage.GetNumKeys();
// Sequence 4: 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 5: 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 6: 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 - 5)); // Deleted NOC, RCAC, Metadata, VVS, VVSC
}
// 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,
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_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);
EXPECT_FALSE(fabricInfo->ShouldAdvertiseIdentity());
EXPECT_EQ(fabricTable.SetShouldAdvertiseIdentity(newFabricIndex, FabricTable::AdvertiseIdentity::Yes), CHIP_NO_ERROR);
EXPECT_TRUE(fabricInfo->ShouldAdvertiseIdentity());
// Check that for indices we don't have a fabric for, SetShouldAdvertiseIdentity fails.
EXPECT_EQ(fabricTable.SetShouldAdvertiseIdentity(kUndefinedFabricIndex, FabricTable::AdvertiseIdentity::No),
CHIP_ERROR_INVALID_FABRIC_INDEX);
EXPECT_EQ(fabricTable.SetShouldAdvertiseIdentity(kUndefinedFabricIndex, FabricTable::AdvertiseIdentity::Yes),
CHIP_ERROR_INVALID_FABRIC_INDEX);
EXPECT_EQ(fabricTable.SetShouldAdvertiseIdentity(2, FabricTable::AdvertiseIdentity::Yes), CHIP_ERROR_INVALID_FABRIC_INDEX);
EXPECT_EQ(fabricTable.SetShouldAdvertiseIdentity(2, FabricTable::AdvertiseIdentity::No), CHIP_ERROR_INVALID_FABRIC_INDEX);
EXPECT_TRUE(fabricInfo->ShouldAdvertiseIdentity());
EXPECT_EQ(fabricTable.SetShouldAdvertiseIdentity(newFabricIndex, FabricTable::AdvertiseIdentity::No), CHIP_NO_ERROR);
EXPECT_FALSE(fabricInfo->ShouldAdvertiseIdentity());
}
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);
EXPECT_TRUE(fabricInfo->ShouldAdvertiseIdentity());
EXPECT_EQ(fabricTable.SetShouldAdvertiseIdentity(newFabricIndex, FabricTable::AdvertiseIdentity::No), CHIP_NO_ERROR);
EXPECT_FALSE(fabricInfo->ShouldAdvertiseIdentity());
EXPECT_EQ(fabricTable.SetShouldAdvertiseIdentity(newFabricIndex, FabricTable::AdvertiseIdentity::Yes), CHIP_NO_ERROR);
EXPECT_TRUE(fabricInfo->ShouldAdvertiseIdentity());
}
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 };
EXPECT_EQ(fabricTable.GetPendingNewFabricIndex(), kUndefinedFabricIndex);
// 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.GetPendingNewFabricIndex(), kUndefinedFabricIndex);
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(fabricTable.GetPendingNewFabricIndex(), kUndefinedFabricIndex);
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);
// 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.
{
auto fabricInfo = FindFabric(fabricTable, TestCerts::sTestCert_Root01_PublicKey, kNode01_01_and_02_01_FabricId);
ASSERT_NE(fabricInfo, nullptr);
EXPECT_EQ(fabricInfo->GetFabricIndex(), 1);
EXPECT_TRUE(fabricInfo->ShouldAdvertiseIdentity());
}
// Attempt lookup of the Root02 fabric.
{
auto fabricInfo = FindFabric(fabricTable, TestCerts::sTestCert_Root02_PublicKey, kNode01_01_and_02_01_FabricId);
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, 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.
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
}
class TestFabricTableDelegate : public FabricTable::Delegate
{
public:
void FabricWillBeRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override { willBeRemovedCalled = true; }
void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override { onRemovedCalled = true; }
bool willBeRemovedCalled = false;
bool onRemovedCalled = false;
};
TEST_F(TestFabricTable, DeleteFabricCallsDelegate)
{
Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority;
EXPECT_TRUE(fabricCertAuthority.Init().IsSuccess());
chip::TestPersistentStorageDelegate storage;
ScopedFabricTable fabricTableHolder;
EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
FabricTable & fabricTable = fabricTableHolder.GetFabricTable();
FabricId fabricId = 1;
NodeId nodeId = 10;
// Simulate AddNOC
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();
fabricTable.AddNewPendingTrustedRootCert(rcac);
constexpr uint16_t kVendorId = 0xFFF1u;
FabricIndex newFabricIndex = kUndefinedFabricIndex;
EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR);
// Reinitialize FabricTable to simulate device reboot
EXPECT_EQ(fabricTableHolder.ReinitFabricTable(&storage), CHIP_NO_ERROR);
TestFabricTableDelegate fabricDelegate;
fabricTable.AddFabricDelegate(&fabricDelegate);
// Check if calling Delete invokes OnFabricRemoved on delegates
fabricTable.Delete(newFabricIndex);
EXPECT_TRUE(fabricDelegate.willBeRemovedCalled);
EXPECT_TRUE(fabricDelegate.onRemovedCalled);
fabricTable.RemoveFabricDelegate(&fabricDelegate);
}
TEST_F(TestFabricTable, VidVerificationSigningWorksWithoutVvs)
{
chip::TestPersistentStorageDelegate storage;
Crypto::P256SerializedKeypair rootKeyForTestSerialized;
// Setup a known key so that test vectors are also known and validated.
// DO NOT CHANGE WITHOUT RE-AUDITING THE TEST VECTORS!
{
static const uint8_t kTestCert_Root02_PublicKey_Buffer[] = {
0x04, 0x27, 0x50, 0x0b, 0x20, 0x60, 0x52, 0xce, 0x33, 0x77, 0x6c, 0x63, 0x08, 0x3f, 0x1c, 0xf1, 0x03,
0x6e, 0xa4, 0xcc, 0x7f, 0xfd, 0x61, 0x7c, 0x17, 0x6d, 0x4c, 0xad, 0xf5, 0x51, 0xbb, 0xb4, 0xb0, 0xd9,
0x97, 0xca, 0xe5, 0x55, 0xdb, 0xf9, 0xbc, 0xa8, 0x56, 0xe4, 0xcc, 0x7a, 0x8e, 0xde, 0x91, 0xe0, 0xa7,
0x33, 0xe1, 0x67, 0xc0, 0x41, 0x67, 0xa6, 0xc2, 0xc9, 0xfa, 0x48, 0xf1, 0x4f, 0x0b,
};
const ByteSpan kTestCert_Root02_PublicKey{ kTestCert_Root02_PublicKey_Buffer };
static const uint8_t kTestCert_Root02_PrivateKey_Buffer[] = {
0xf9, 0xb7, 0x22, 0x87, 0xd6, 0xa4, 0x8d, 0xd5, 0x10, 0x93, 0xc4, 0x12, 0xe6, 0x1b, 0x43, 0xaf,
0xe1, 0x22, 0x15, 0x28, 0x96, 0x71, 0xbf, 0x33, 0x96, 0xff, 0x97, 0xf6, 0xb6, 0x21, 0xb2, 0xac,
};
const ByteSpan kTestCert_Root02_PrivateKey{ kTestCert_Root02_PrivateKey_Buffer };
memcpy(rootKeyForTestSerialized.Bytes(), kTestCert_Root02_PublicKey.data(), kTestCert_Root02_PublicKey.size());
memcpy(rootKeyForTestSerialized.Bytes() + kTestCert_Root02_PublicKey.size(), kTestCert_Root02_PrivateKey.data(),
kTestCert_Root02_PrivateKey.size());
rootKeyForTestSerialized.SetLength(rootKeyForTestSerialized.Capacity());
}
// 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 basic fabric, don't care about NodeID.
constexpr FabricId kFabricId1 = 0x1122334455667788;
constexpr uint16_t kVendorId1 = 0xFFF1u;
{
FabricIndex fabricIndex1 =
CreateBasicFabricForVidVerification(fabricTable, rootKeyForTestSerialized, kFabricId1, kVendorId1);
ASSERT_EQ(fabricIndex1, 1u);
ASSERT_EQ(fabricTable.FabricCount(), 1u);
}
// Add a second basic fabric, don't care about NodeID.
constexpr FabricId kFabricId2 = 0xA1A2A3A4A5A6A7A8;
constexpr uint16_t kVendorId2 = 0xFFF2u;
{
FabricIndex fabricIndex2 =
CreateBasicFabricForVidVerification(fabricTable, rootKeyForTestSerialized, kFabricId2, kVendorId2);
ASSERT_EQ(fabricIndex2, 2u);
ASSERT_EQ(fabricTable.FabricCount(), 2u);
}
// Ask for SignVIDVerification for both fabrics.
FabricTable::SignVIDVerificationResponseData responseData1;
{
FabricIndex kFabricIndex1 = 1u;
const uint8_t kAttestationChallenge1[16] = {
0x90, 0x4b, 0x82, 0xe6, 0x6e, 0x98, 0x57, 0x85, 0xb4, 0xa3, 0xff, 0x18, 0xc2, 0x59, 0x47, 0xf3,
};
ByteSpan kAttestationChallengeSpan1{ kAttestationChallenge1 };
const uint8_t kClientChallenge1[32] = {
0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0,
0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0,
};
ByteSpan kClientChallengeSpan1{ kClientChallenge1 };
ASSERT_EQ(
fabricTable.SignVIDVerificationRequest(kFabricIndex1, kClientChallengeSpan1, kAttestationChallengeSpan1, responseData1),
CHIP_NO_ERROR);
EXPECT_EQ(responseData1.fabricBindingVersion, to_underlying(Crypto::FabricBindingVersion::kVersion1));
EXPECT_EQ(responseData1.fabricIndex, kFabricIndex1);
const uint8_t kExpectedUnderlyingTbs1[126] = {
0x01, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1,
0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0x90, 0x4b, 0x82,
0xe6, 0x6e, 0x98, 0x57, 0x85, 0xb4, 0xa3, 0xff, 0x18, 0xc2, 0x59, 0x47, 0xf3, 0x01, 0x01, 0x04, 0x27, 0x50,
0x0b, 0x20, 0x60, 0x52, 0xce, 0x33, 0x77, 0x6c, 0x63, 0x08, 0x3f, 0x1c, 0xf1, 0x03, 0x6e, 0xa4, 0xcc, 0x7f,
0xfd, 0x61, 0x7c, 0x17, 0x6d, 0x4c, 0xad, 0xf5, 0x51, 0xbb, 0xb4, 0xb0, 0xd9, 0x97, 0xca, 0xe5, 0x55, 0xdb,
0xf9, 0xbc, 0xa8, 0x56, 0xe4, 0xcc, 0x7a, 0x8e, 0xde, 0x91, 0xe0, 0xa7, 0x33, 0xe1, 0x67, 0xc0, 0x41, 0x67,
0xa6, 0xc2, 0xc9, 0xfa, 0x48, 0xf1, 0x4f, 0x0b, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xff, 0xf1,
};
EXPECT_EQ(VerifySignatureWithNocPublicKey(fabricTable, kFabricIndex1, ByteSpan{ kExpectedUnderlyingTbs1 },
responseData1.signature.Span()),
CHIP_NO_ERROR);
}
FabricTable::SignVIDVerificationResponseData responseData2;
{
FabricIndex kFabricIndex2 = 2u;
const uint8_t kAttestationChallenge2[16] = {
0x14, 0x5f, 0x2e, 0xf3, 0x9e, 0x3e, 0x4f, 0xfc, 0xf5, 0x64, 0x9c, 0x09, 0x09, 0xcb, 0xc8, 0xe4,
};
ByteSpan kAttestationChallengeSpan2{ kAttestationChallenge2 };
const uint8_t kClientChallenge2[32] = {
0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0,
};
ByteSpan kClientChallengeSpan2{ kClientChallenge2 };
ASSERT_EQ(
fabricTable.SignVIDVerificationRequest(kFabricIndex2, kClientChallengeSpan2, kAttestationChallengeSpan2, responseData2),
CHIP_NO_ERROR);
EXPECT_EQ(responseData2.fabricBindingVersion, to_underlying(Crypto::FabricBindingVersion::kVersion1));
EXPECT_EQ(responseData2.fabricIndex, kFabricIndex2);
const uint8_t kExpectedUnderlyingTbs2[126] = {
0x01, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1,
0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0x14, 0x5f, 0x2e,
0xf3, 0x9e, 0x3e, 0x4f, 0xfc, 0xf5, 0x64, 0x9c, 0x09, 0x09, 0xcb, 0xc8, 0xe4, 0x02, 0x01, 0x04, 0x27, 0x50,
0x0b, 0x20, 0x60, 0x52, 0xce, 0x33, 0x77, 0x6c, 0x63, 0x08, 0x3f, 0x1c, 0xf1, 0x03, 0x6e, 0xa4, 0xcc, 0x7f,
0xfd, 0x61, 0x7c, 0x17, 0x6d, 0x4c, 0xad, 0xf5, 0x51, 0xbb, 0xb4, 0xb0, 0xd9, 0x97, 0xca, 0xe5, 0x55, 0xdb,
0xf9, 0xbc, 0xa8, 0x56, 0xe4, 0xcc, 0x7a, 0x8e, 0xde, 0x91, 0xe0, 0xa7, 0x33, 0xe1, 0x67, 0xc0, 0x41, 0x67,
0xa6, 0xc2, 0xc9, 0xfa, 0x48, 0xf1, 0x4f, 0x0b, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xff, 0xf2,
};
EXPECT_EQ(VerifySignatureWithNocPublicKey(fabricTable, kFabricIndex2, ByteSpan{ kExpectedUnderlyingTbs2 },
responseData2.signature.Span()),
CHIP_NO_ERROR);
}
}
TEST_F(TestFabricTable, SettingVvscFailsWithIcacAndSucceedWithout)
{
chip::TestPersistentStorageDelegate storage;
Crypto::P256SerializedKeypair rootKeyForTestSerialized;
chip::TestPersistentStorageDelegate testStorage;
ScopedFabricTable fabricTableHolder;
EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
FabricTable & fabricTable = fabricTableHolder.GetFabricTable();
// We are doing logic checks one fabric at a time, agains the FabricTable. Therefore
// it's OK to have 2 fabrics with same <root, fabric ID> together in fabric table to
// test logic simpler.
// Has ICAC, will be FabricIndex 1.
fabricTable.PermitCollidingFabrics();
EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR);
// Does not have ICAC, will be Fabric Index 2.
fabricTable.PermitCollidingFabrics();
EXPECT_EQ(LoadTestFabric_Node01_02(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR);
ASSERT_NE(fabricTable.FindFabricWithIndex(1), nullptr);
ASSERT_NE(fabricTable.FindFabricWithIndex(2), nullptr);
uint8_t vvsc[kMaxCHIPCertLength];
memset(&vvsc[0], 0xff, sizeof(vvsc));
ByteSpan vvscSpan{ vvsc };
uint8_t vvs[Crypto::kVendorIdVerificationStatementV1Size];
memset(&vvs[0], 0x01, sizeof(vvs));
ByteSpan vvsSpan{ vvs };
ByteSpan emptyVvscSpan{};
ByteSpan emptyVvsSpan{};
// Try to set VVSC when ICAC set --> failure.
{
bool fabricTableWasChanged = false;
EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(/* fabricIndex = */ 1, /* vendorId = */ chip::NullOptional,
NullOptional, chip::MakeOptional<>(vvscSpan),
fabricTableWasChanged),
CHIP_ERROR_INCORRECT_STATE);
EXPECT_EQ(fabricTableWasChanged, false);
}
// Try to set VVSC when ICAC not set --> success.
{
bool fabricTableWasChanged = false;
EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(/* fabricIndex = */ 2, /* vendorId = */ chip::NullOptional,
NullOptional, chip::MakeOptional<>(vvscSpan),
fabricTableWasChanged),
CHIP_NO_ERROR);
EXPECT_EQ(fabricTableWasChanged, false);
// Change a byte, should succeed again.
vvsc[1] = 0x00;
fabricTableWasChanged = false;
EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(/* fabricIndex = */ 2, /* vendorId = */ chip::NullOptional,
NullOptional, chip::MakeOptional<>(vvscSpan),
fabricTableWasChanged),
CHIP_NO_ERROR);
EXPECT_EQ(fabricTableWasChanged, false);
// The VVSC should match by the end.
uint8_t actualVvsc[kMaxCHIPCertLength];
MutableByteSpan actualVvscSpan{ actualVvsc };
EXPECT_EQ(
fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
/* fabricIndex = */ 2, Credentials::OperationalCertificateStore::VidVerificationElement::kVvsc, actualVvscSpan),
CHIP_NO_ERROR);
EXPECT_TRUE(actualVvscSpan.data_equal(vvscSpan));
// Erase VVSC with empty span.
fabricTableWasChanged = false;
EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(/* fabricIndex = */ 2, /* vendorId = */ chip::NullOptional,
NullOptional, chip::MakeOptional<>(emptyVvscSpan),
fabricTableWasChanged),
CHIP_NO_ERROR);
EXPECT_EQ(fabricTableWasChanged, false);
// The VVSC should be empty now.
actualVvscSpan = MutableByteSpan{ actualVvsc };
EXPECT_EQ(
fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
/* fabricIndex = */ 2, Credentials::OperationalCertificateStore::VidVerificationElement::kVvsc, actualVvscSpan),
CHIP_NO_ERROR);
EXPECT_TRUE(actualVvscSpan.empty());
}
// VVS updating flow. Always works whether ICAC present or not.
{
bool fabricTableWasChanged = false;
EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(/* fabricIndex = */ 2, /* vendorId = */ chip::NullOptional,
chip::MakeOptional<>(vvsSpan), NullOptional,
fabricTableWasChanged),
CHIP_NO_ERROR);
EXPECT_EQ(fabricTableWasChanged, true);
// Re-set VVS, should succeed again, but no fabric table changes seen.
fabricTableWasChanged = false;
EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(/* fabricIndex = */ 2, /* vendorId = */ chip::NullOptional,
chip::MakeOptional<>(vvsSpan), NullOptional,
fabricTableWasChanged),
CHIP_NO_ERROR);
EXPECT_EQ(fabricTableWasChanged, false);
// Change a byte, should succeed again, but changes notifed.
vvs[1] = 0x02;
fabricTableWasChanged = false;
EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(/* fabricIndex = */ 2, /* vendorId = */ chip::NullOptional,
chip::MakeOptional<>(vvsSpan), NullOptional,
fabricTableWasChanged),
CHIP_NO_ERROR);
EXPECT_EQ(fabricTableWasChanged, true);
// The VVS should match by the end.
uint8_t actualVvs[Crypto::kVendorIdVerificationStatementV1Size];
MutableByteSpan actualVvsSpan{ actualVvs };
EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
/* fabricIndex = */ 2,
Credentials::OperationalCertificateStore::VidVerificationElement::kVidVerificationStatement, actualVvsSpan),
CHIP_NO_ERROR);
EXPECT_TRUE(actualVvsSpan.data_equal(vvsSpan));
// Erase VVS with empty span.
fabricTableWasChanged = false;
EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(/* fabricIndex = */ 2, /* vendorId = */ chip::NullOptional,
chip::MakeOptional<>(emptyVvsSpan), NullOptional,
fabricTableWasChanged),
CHIP_NO_ERROR);
EXPECT_EQ(fabricTableWasChanged, true);
// The VVS should be empty now.
actualVvsSpan = MutableByteSpan{ actualVvs };
EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
/* fabricIndex = */ 2,
Credentials::OperationalCertificateStore::VidVerificationElement::kVidVerificationStatement, actualVvsSpan),
CHIP_NO_ERROR);
EXPECT_TRUE(actualVvsSpan.empty());
}
}
TEST_F(TestFabricTable, UpdateNocWithIcacFailsWithVvscSucceedsWithout)
{
// Credentials::TestOnlyLocalCertificateAuthority fabric11CertAuthority;
Credentials::TestOnlyLocalCertificateAuthority fabric44CertAuthority;
EXPECT_TRUE(fabric44CertAuthority.Init().IsSuccess());
// Initialize a fabric table.
chip::TestPersistentStorageDelegate storage;
ScopedFabricTable fabricTableHolder;
EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
FabricTable & fabricTable = fabricTableHolder.GetFabricTable();
constexpr uint16_t kVendorId = 0xFFF1u;
constexpr uint16_t kNewVendorId = 0xFFF2u;
FabricIndex newFabricIndex = kUndefinedFabricIndex;
// VVSC contents is not checked, so can be just zero bytes.
uint8_t vvsc[kMaxCHIPCertLength];
memset(&vvsc[0], 0x00, sizeof(vvsc));
ByteSpan vvscSpan{ vvsc };
// VVS contents is not checked, except first by that must be 0x01.
uint8_t vvs[Crypto::kVendorIdVerificationStatementV1Size];
memset(&vvs[0], 0x01, sizeof(vvs));
ByteSpan vvsSpan{ vvs };
uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
// Fabric Index 1: Add node ID 33 on fabric 44, using operational keystore and no ICAC. Set a default VVSC.
{
FabricId fabricId = 44;
NodeId nodeId = 333;
MutableByteSpan csrSpan{ csrBuf };
EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, 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.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, /*icac = */ ByteSpan{}, kVendorId, &newFabricIndex),
CHIP_NO_ERROR);
EXPECT_EQ(newFabricIndex, 1u);
EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);
EXPECT_EQ(fabricTable.FabricCount(), 1);
bool fabricTableWasChanged = false;
EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(newFabricIndex, NullOptional, NullOptional,
MakeOptional(vvscSpan), fabricTableWasChanged),
CHIP_NO_ERROR);
// Changing VVSC doesn't change Fabrics table itself.
EXPECT_EQ(fabricTableWasChanged, false);
// Make sure VVSC was stored.
{
uint8_t readBackVvscBuf[kMaxCHIPCertLength];
memset(&readBackVvscBuf[0], 0x11, sizeof(readBackVvscBuf));
MutableByteSpan readBackVvscSpan{ readBackVvscBuf };
EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
newFabricIndex, OperationalCertificateStore::VidVerificationElement::kVvsc, readBackVvscSpan),
CHIP_NO_ERROR);
EXPECT_TRUE(readBackVvscSpan.data_equal(vvscSpan));
}
}
// Try to update node ID to 33 on fabric 44 WITHOUT ICAC. Must succeed even if VVSC present.
// Then remove VVSC, and update NOC chain to include ICAC, and this should work.
// Then update NOC chain to WITHOUT ICAC, and add VVSC and VVS while pending. Then revert. Should go back to prior state.
{
MutableByteSpan csrSpan{ csrBuf };
FabricIndex existingFabricIndex = 1u;
// Update to NodeID 33 with ICAC
{
FabricId fabricId = 44;
NodeId nodeId = 33;
ASSERT_EQ(fabricTable.AllocatePendingOperationalKey(MakeOptional(existingFabricIndex), csrSpan), CHIP_NO_ERROR);
EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
CHIP_NO_ERROR);
ByteSpan noc = fabric44CertAuthority.GetNoc();
EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(existingFabricIndex, noc, ByteSpan{},
FabricTable::AdvertiseIdentity::No),
CHIP_NO_ERROR);
ASSERT_NE(fabricTable.FindFabricWithIndex(newFabricIndex), nullptr);
EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetFabricId(), fabricId);
EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetNodeId(), nodeId);
EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);
EXPECT_EQ(fabricTable.FabricCount(), 1);
}
// Remove VVSC and update to NodeID 66 WITH ICAC
{
{
bool fabricTableWasChanged = false;
EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(newFabricIndex, NullOptional, NullOptional,
MakeOptional(ByteSpan{}), fabricTableWasChanged),
CHIP_NO_ERROR);
// Changing VVSC doesn't change Fabrics table itself.
EXPECT_EQ(fabricTableWasChanged, false);
// Make sure VVSC appears removed.
{
uint8_t readBackVvscBuf[kMaxCHIPCertLength];
memset(&readBackVvscBuf[0], 0x11, sizeof(readBackVvscBuf));
MutableByteSpan readBackVvscSpan{ readBackVvscBuf };
EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
newFabricIndex, OperationalCertificateStore::VidVerificationElement::kVvsc, readBackVvscSpan),
CHIP_NO_ERROR);
EXPECT_TRUE(readBackVvscSpan.empty());
}
}
FabricId fabricId = 44;
NodeId nodeId = 66;
csrSpan = MutableByteSpan{ csrBuf };
ASSERT_EQ(fabricTable.AllocatePendingOperationalKey(MakeOptional(existingFabricIndex), csrSpan), CHIP_NO_ERROR);
EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
CHIP_NO_ERROR);
ByteSpan icac = fabric44CertAuthority.GetIcac();
ByteSpan noc = fabric44CertAuthority.GetNoc();
EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(existingFabricIndex, noc, icac,
FabricTable::AdvertiseIdentity::No),
CHIP_NO_ERROR);
ASSERT_NE(fabricTable.FindFabricWithIndex(newFabricIndex), nullptr);
EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetFabricId(), fabricId);
EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetNodeId(), nodeId);
EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);
EXPECT_EQ(fabricTable.FabricCount(), 1);
}
// Update to Node ID 88 without ICAC. Set VVS/VVSC/VendorID before reverting. Revert and make sure VVSC/VVS/VendorID are
// reverted.
{
FabricId fabricId = 44;
NodeId nodeId = 88;
csrSpan = MutableByteSpan{ csrBuf };
ASSERT_EQ(fabricTable.AllocatePendingOperationalKey(MakeOptional(existingFabricIndex), csrSpan), CHIP_NO_ERROR);
EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
CHIP_NO_ERROR);
ByteSpan noc = fabric44CertAuthority.GetNoc();
EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(existingFabricIndex, noc, ByteSpan{},
FabricTable::AdvertiseIdentity::No),
CHIP_NO_ERROR);
ASSERT_NE(fabricTable.FindFabricWithIndex(newFabricIndex), nullptr);
EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetFabricId(), fabricId);
EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetNodeId(), nodeId);
EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetVendorId(), kVendorId);
// Set new pending VVS, VVSC and VendorId.
bool fabricTableWasChanged = false;
EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(newFabricIndex, MakeOptional(kNewVendorId),
MakeOptional(vvsSpan), MakeOptional(vvscSpan),
fabricTableWasChanged),
CHIP_NO_ERROR);
EXPECT_EQ(fabricTableWasChanged, true);
// Make sure VVSC was set pending.
{
uint8_t readBackVvscBuf[kMaxCHIPCertLength];
memset(&readBackVvscBuf[0], 0x11, sizeof(readBackVvscBuf));
MutableByteSpan readBackVvscSpan{ readBackVvscBuf };
EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
newFabricIndex, OperationalCertificateStore::VidVerificationElement::kVvsc, readBackVvscSpan),
CHIP_NO_ERROR);
EXPECT_TRUE(readBackVvscSpan.data_equal(vvscSpan));
}
// Make sure VVS was also set pending
{
uint8_t readBackVvsBuf[Crypto::kVendorIdVerificationStatementV1Size];
memset(&readBackVvsBuf[0], 0x00, sizeof(readBackVvsBuf));
MutableByteSpan readBackVvsSpan{ readBackVvsBuf };
EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
newFabricIndex, OperationalCertificateStore::VidVerificationElement::kVidVerificationStatement,
readBackVvsSpan),
CHIP_NO_ERROR);
EXPECT_TRUE(readBackVvsSpan.data_equal(vvsSpan));
}
ASSERT_NE(fabricTable.FindFabricWithIndex(newFabricIndex), nullptr);
EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetVendorId(), kNewVendorId);
// Revert state, expect previous fabric data, and empty VVSC/VVS again.
fabricTable.RevertPendingFabricData();
EXPECT_EQ(fabricTable.FabricCount(), 1);
ASSERT_NE(fabricTable.FindFabricWithIndex(newFabricIndex), nullptr);
EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetFabricId(), 44u);
EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetNodeId(), 66u);
EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetVendorId(), kVendorId);
// Make sure VVSC got reverted.
{
uint8_t readBackVvscBuf[kMaxCHIPCertLength];
memset(&readBackVvscBuf[0], 0x11, sizeof(readBackVvscBuf));
MutableByteSpan readBackVvscSpan{ readBackVvscBuf };
EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
newFabricIndex, OperationalCertificateStore::VidVerificationElement::kVvsc, readBackVvscSpan),
CHIP_NO_ERROR);
EXPECT_TRUE(readBackVvscSpan.empty());
}
// Make sure VVS got reverted.
{
uint8_t readBackVvsBuf[Crypto::kVendorIdVerificationStatementV1Size];
memset(&readBackVvsBuf[0], 0x00, sizeof(readBackVvsBuf));
MutableByteSpan readBackVvsSpan{ readBackVvsBuf };
EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
newFabricIndex, OperationalCertificateStore::VidVerificationElement::kVidVerificationStatement,
readBackVvsSpan),
CHIP_NO_ERROR);
EXPECT_TRUE(readBackVvsSpan.empty());
}
}
}
// Update to NodeID 33 without ICAC
{
FabricId fabricId = 44;
NodeId nodeId = 33;
FabricIndex existingFabricIndex = 1u;
MutableByteSpan csrSpan{ csrBuf };
ASSERT_EQ(fabricTable.AllocatePendingOperationalKey(MakeOptional(existingFabricIndex), csrSpan), CHIP_NO_ERROR);
EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
CHIP_NO_ERROR);
ByteSpan noc = fabric44CertAuthority.GetNoc();
EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(existingFabricIndex, noc, ByteSpan{},
FabricTable::AdvertiseIdentity::No),
CHIP_NO_ERROR);
ASSERT_NE(fabricTable.FindFabricWithIndex(newFabricIndex), nullptr);
EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetFabricId(), fabricId);
EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetNodeId(), nodeId);
EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);
EXPECT_EQ(fabricTable.FabricCount(), 1);
}
{
// Set VVSC. Should succeed due to lack of ICAC.
bool fabricTableWasChanged = false;
EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(newFabricIndex, NullOptional, NullOptional,
MakeOptional(vvscSpan), fabricTableWasChanged),
CHIP_NO_ERROR);
EXPECT_EQ(fabricTableWasChanged, false);
// Make sure VVSC was stored.
{
uint8_t readBackVvscBuf[kMaxCHIPCertLength];
memset(&readBackVvscBuf[0], 0x11, sizeof(readBackVvscBuf));
MutableByteSpan readBackVvscSpan{ readBackVvscBuf };
EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
newFabricIndex, OperationalCertificateStore::VidVerificationElement::kVvsc, readBackVvscSpan),
CHIP_NO_ERROR);
EXPECT_TRUE(readBackVvscSpan.data_equal(vvscSpan));
}
}
// Fabric Index 1: Try to update node ID to 55 on fabric 44. Must fail due to VVSC present.
{
FabricId fabricId = 44;
NodeId nodeId = 55;
MutableByteSpan csrSpan{ csrBuf };
FabricIndex existingFabricIndex = 1u;
EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(MakeOptional(existingFabricIndex), csrSpan), CHIP_NO_ERROR);
EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
CHIP_NO_ERROR);
ByteSpan icac = fabric44CertAuthority.GetIcac();
ByteSpan noc = fabric44CertAuthority.GetNoc();
EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(existingFabricIndex, noc, icac,
FabricTable::AdvertiseIdentity::No),
CHIP_ERROR_INCORRECT_STATE);
fabricTable.RevertPendingFabricData();
EXPECT_EQ(fabricTable.FabricCount(), 1);
}
}
TEST_F(TestFabricTable, VidVerificationSigningFailsOnBadInput)
{
chip::TestPersistentStorageDelegate storage;
Crypto::P256SerializedKeypair rootKeyForTestSerialized;
// Setup a known key so that test vectors are also known and validated.
// DO NOT CHANGE WITHOUT RE-AUDITING THE TEST VECTORS!
{
static const uint8_t kTestCert_Root02_PublicKey_Buffer[] = {
0x04, 0x27, 0x50, 0x0b, 0x20, 0x60, 0x52, 0xce, 0x33, 0x77, 0x6c, 0x63, 0x08, 0x3f, 0x1c, 0xf1, 0x03,
0x6e, 0xa4, 0xcc, 0x7f, 0xfd, 0x61, 0x7c, 0x17, 0x6d, 0x4c, 0xad, 0xf5, 0x51, 0xbb, 0xb4, 0xb0, 0xd9,
0x97, 0xca, 0xe5, 0x55, 0xdb, 0xf9, 0xbc, 0xa8, 0x56, 0xe4, 0xcc, 0x7a, 0x8e, 0xde, 0x91, 0xe0, 0xa7,
0x33, 0xe1, 0x67, 0xc0, 0x41, 0x67, 0xa6, 0xc2, 0xc9, 0xfa, 0x48, 0xf1, 0x4f, 0x0b,
};
const ByteSpan kTestCert_Root02_PublicKey{ kTestCert_Root02_PublicKey_Buffer };
static const uint8_t kTestCert_Root02_PrivateKey_Buffer[] = {
0xf9, 0xb7, 0x22, 0x87, 0xd6, 0xa4, 0x8d, 0xd5, 0x10, 0x93, 0xc4, 0x12, 0xe6, 0x1b, 0x43, 0xaf,
0xe1, 0x22, 0x15, 0x28, 0x96, 0x71, 0xbf, 0x33, 0x96, 0xff, 0x97, 0xf6, 0xb6, 0x21, 0xb2, 0xac,
};
const ByteSpan kTestCert_Root02_PrivateKey{ kTestCert_Root02_PrivateKey_Buffer };
memcpy(rootKeyForTestSerialized.Bytes(), kTestCert_Root02_PublicKey.data(), kTestCert_Root02_PublicKey.size());
memcpy(rootKeyForTestSerialized.Bytes() + kTestCert_Root02_PublicKey.size(), kTestCert_Root02_PrivateKey.data(),
kTestCert_Root02_PrivateKey.size());
rootKeyForTestSerialized.SetLength(rootKeyForTestSerialized.Capacity());
}
// 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 basic fabric, don't care about NodeID.
constexpr FabricId kFabricId1 = 0x1122334455667788;
constexpr uint16_t kVendorId1 = 0xFFF1u;
FabricIndex kFabricIndex1 = 1u;
{
FabricIndex fabricIndex1 =
CreateBasicFabricForVidVerification(fabricTable, rootKeyForTestSerialized, kFabricId1, kVendorId1);
ASSERT_EQ(fabricIndex1, kFabricIndex1);
ASSERT_EQ(fabricTable.FabricCount(), 1u);
}
const uint8_t kAttestationChallenge[16] = {
0x90, 0x4b, 0x82, 0xe6, 0x6e, 0x98, 0x57, 0x85, 0xb4, 0xa3, 0xff, 0x18, 0xc2, 0x59, 0x47, 0xf3,
};
ByteSpan kAttestationChallengeSpan{ kAttestationChallenge };
const uint8_t kClientChallenge[32] = {
0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0,
0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0,
};
ByteSpan kClientChallengeSpan{ kClientChallenge };
// Non-existent fabricIndex should fail.
{
FabricTable::SignVIDVerificationResponseData responseData;
FabricIndex kNonExistentFabricIndex = 123u;
ASSERT_EQ(fabricTable.SignVIDVerificationRequest(kNonExistentFabricIndex, kClientChallengeSpan, kAttestationChallengeSpan,
responseData),
CHIP_ERROR_INVALID_ARGUMENT);
}
// Invalid attestationChallenge size should fail. Only check too small as the underlying data copied will be caught by just the
// signature failing.
{
FabricTable::SignVIDVerificationResponseData responseData;
ByteSpan attestationChallengeTooSmall{ kAttestationChallengeSpan.data(), kAttestationChallengeSpan.size() - 1 };
ASSERT_EQ(
fabricTable.SignVIDVerificationRequest(kFabricIndex1, kClientChallengeSpan, attestationChallengeTooSmall, responseData),
CHIP_ERROR_INVALID_ARGUMENT);
}
// Invalid client challenge size should fail.
{
FabricTable::SignVIDVerificationResponseData responseData;
ByteSpan clientChallengeTooSmall{ kClientChallengeSpan.data(), kClientChallengeSpan.size() - 1 };
ASSERT_EQ(
fabricTable.SignVIDVerificationRequest(kFabricIndex1, clientChallengeTooSmall, kAttestationChallengeSpan, responseData),
CHIP_ERROR_INVALID_ARGUMENT);
uint8_t clientChallengeBigBuffer[Crypto::kVendorIdVerificationClientChallengeSize + 1];
ByteSpan clientChallengeTooBig{ clientChallengeBigBuffer };
ASSERT_EQ(
fabricTable.SignVIDVerificationRequest(kFabricIndex1, clientChallengeTooBig, kAttestationChallengeSpan, responseData),
CHIP_ERROR_INVALID_ARGUMENT);
}
}
} // namespace