blob: 145e6af2f5d9102ca78042ded7de15d2dde1c832 [file] [log] [blame]
/*
*
* Copyright (c) 2021 Project CHIP Authors
*
* 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.
*/
/**
* @brief Defines a table of fabrics that have provisioned the device.
*/
#include <lib/core/CHIPEncoding.h>
#include <lib/support/CHIPMem.h>
#include <lib/support/SafeInt.h>
#include <transport/FabricTable.h>
#if CHIP_CRYPTO_HSM
#include <crypto/hsm/CHIPCryptoPALHsm.h>
#endif
namespace chip {
using namespace Credentials;
using namespace Crypto;
namespace Transport {
CHIP_ERROR FabricInfo::SetFabricLabel(const uint8_t * fabricLabel)
{
const char * charFabricLabel = Uint8::to_const_char(fabricLabel);
size_t stringLength = strnlen(charFabricLabel, kFabricLabelMaxLengthInBytes);
memcpy(mFabricLabel, charFabricLabel, stringLength);
mFabricLabel[stringLength] = '\0'; // Set null terminator
return CHIP_NO_ERROR;
}
#pragma GCC diagnostic ignored "-Wstack-usage="
CHIP_ERROR FabricInfo::StoreIntoKVS(PersistentStorageDelegate * kvs)
{
CHIP_ERROR err = CHIP_NO_ERROR;
char key[KeySize()];
ReturnErrorOnFailure(GenerateKey(mFabric, key, sizeof(key)));
StorableFabricInfo * info = chip::Platform::New<StorableFabricInfo>();
ReturnErrorCodeIf(info == nullptr, CHIP_ERROR_NO_MEMORY);
info->mNodeId = Encoding::LittleEndian::HostSwap64(mOperationalId.GetNodeId());
info->mFabric = Encoding::LittleEndian::HostSwap16(mFabric);
info->mVendorId = Encoding::LittleEndian::HostSwap16(mVendorId);
info->mFabricId = Encoding::LittleEndian::HostSwap64(mFabricId);
size_t stringLength = strnlen(mFabricLabel, kFabricLabelMaxLengthInBytes);
memcpy(info->mFabricLabel, mFabricLabel, stringLength);
info->mFabricLabel[stringLength] = '\0'; // Set null terminator
if (mOperationalKey != nullptr)
{
SuccessOrExit(err = mOperationalKey->Serialize(info->mOperationalKey));
}
else
{
P256Keypair keypair;
SuccessOrExit(err = keypair.Initialize());
SuccessOrExit(err = keypair.Serialize(info->mOperationalKey));
}
if (mRootCert == nullptr || mRootCertLen == 0)
{
info->mRootCertLen = 0;
}
else
{
info->mRootCertLen = Encoding::LittleEndian::HostSwap16(mRootCertLen);
memcpy(info->mRootCert, mRootCert, mRootCertLen);
}
if (mOperationalCerts == nullptr || mOperationalCertsLen == 0)
{
info->mOperationalCertsLen = 0;
}
else
{
info->mOperationalCertsLen = Encoding::LittleEndian::HostSwap16(mOperationalCertsLen);
memcpy(info->mOperationalCerts, mOperationalCerts, mOperationalCertsLen);
}
err = kvs->SyncSetKeyValue(key, info, sizeof(StorableFabricInfo));
if (err != CHIP_NO_ERROR)
{
ChipLogError(Discovery, "Error occurred calling SyncSetKeyValue: %s", chip::ErrorStr(err));
}
exit:
if (info != nullptr)
{
chip::Platform::Delete(info);
}
return err;
}
CHIP_ERROR FabricInfo::FetchFromKVS(PersistentStorageDelegate * kvs)
{
CHIP_ERROR err = CHIP_NO_ERROR;
char key[KeySize()];
ReturnErrorOnFailure(GenerateKey(mFabric, key, sizeof(key)));
StorableFabricInfo * info = chip::Platform::New<StorableFabricInfo>();
ReturnErrorCodeIf(info == nullptr, CHIP_ERROR_NO_MEMORY);
uint16_t infoSize = sizeof(StorableFabricInfo);
uint16_t id;
uint16_t rootCertLen, opCertsLen;
size_t stringLength;
NodeId nodeId;
SuccessOrExit(err = kvs->SyncGetKeyValue(key, info, infoSize));
mFabricId = Encoding::LittleEndian::HostSwap64(info->mFabricId);
nodeId = Encoding::LittleEndian::HostSwap64(info->mNodeId);
id = Encoding::LittleEndian::HostSwap16(info->mFabric);
mVendorId = Encoding::LittleEndian::HostSwap16(info->mVendorId);
rootCertLen = Encoding::LittleEndian::HostSwap16(info->mRootCertLen);
opCertsLen = Encoding::LittleEndian::HostSwap16(info->mOperationalCertsLen);
stringLength = strnlen(info->mFabricLabel, kFabricLabelMaxLengthInBytes);
memcpy(mFabricLabel, info->mFabricLabel, stringLength);
mFabricLabel[stringLength] = '\0'; // Set null terminator
VerifyOrExit(mFabric == id, err = CHIP_ERROR_INCORRECT_STATE);
if (mOperationalKey == nullptr)
{
#ifdef ENABLE_HSM_CASE_OPS_KEY
mOperationalKey = chip::Platform::New<P256KeypairHSM>();
mOperationalKey->SetKeyId(CASE_OPS_KEY);
#else
mOperationalKey = chip::Platform::New<P256Keypair>();
#endif
}
VerifyOrExit(mOperationalKey != nullptr, err = CHIP_ERROR_NO_MEMORY);
SuccessOrExit(err = mOperationalKey->Deserialize(info->mOperationalKey));
ChipLogProgress(Inet, "Loading certs from KVS");
SuccessOrExit(err = SetRootCert(ByteSpan(info->mRootCert, rootCertLen)));
// The compressed fabric ID doesn't change for a fabric over time.
// Computing it here will save computational overhead when it's accessed by other
// parts of the code.
SuccessOrExit(err = GetCompressedId(mFabricId, nodeId, &mOperationalId));
SuccessOrExit(err = SetOperationalCertsFromCertArray(ByteSpan(info->mOperationalCerts, opCertsLen)));
exit:
if (info != nullptr)
{
chip::Platform::Delete(info);
}
return err;
}
CHIP_ERROR FabricInfo::GetCompressedId(FabricId fabricId, NodeId nodeId, PeerId * compressedPeerId) const
{
ReturnErrorCodeIf(compressedPeerId == nullptr, CHIP_ERROR_INVALID_ARGUMENT);
CompressedFabricId compressedFabricId = 0;
MutableByteSpan fabricIdSpan(reinterpret_cast<uint8_t *>(&compressedFabricId), sizeof(compressedFabricId));
ReturnErrorOnFailure(GenerateCompressedFabricId(mRootPubkey, fabricId, fabricIdSpan));
compressedPeerId->SetCompressedFabricId(compressedFabricId);
compressedPeerId->SetNodeId(nodeId);
return CHIP_NO_ERROR;
}
CHIP_ERROR FabricInfo::DeleteFromKVS(PersistentStorageDelegate * kvs, FabricIndex id)
{
CHIP_ERROR err = CHIP_NO_ERROR;
char key[KeySize()];
ReturnErrorOnFailure(GenerateKey(id, key, sizeof(key)));
err = kvs->SyncDeleteKeyValue(key);
if (err != CHIP_NO_ERROR)
{
ChipLogDetail(Discovery, "Fabric %d is not yet configured", id);
}
return err;
}
constexpr size_t FabricInfo::KeySize()
{
return sizeof(kFabricTableKeyPrefix) + 2 * sizeof(FabricIndex);
}
CHIP_ERROR FabricInfo::GenerateKey(FabricIndex id, char * key, size_t len)
{
VerifyOrReturnError(len >= KeySize(), CHIP_ERROR_INVALID_ARGUMENT);
int keySize = snprintf(key, len, "%s%x", kFabricTableKeyPrefix, id);
VerifyOrReturnError(keySize > 0, CHIP_ERROR_INTERNAL);
VerifyOrReturnError(len > (size_t) keySize, CHIP_ERROR_INTERNAL);
return CHIP_NO_ERROR;
}
CHIP_ERROR FabricInfo::SetEphemeralKey(const P256Keypair * key)
{
P256SerializedKeypair serialized;
ReturnErrorOnFailure(key->Serialize(serialized));
if (mOperationalKey == nullptr)
{
#ifdef ENABLE_HSM_CASE_OPS_KEY
mOperationalKey = chip::Platform::New<P256KeypairHSM>();
mOperationalKey->SetKeyId(CASE_OPS_KEY);
#else
mOperationalKey = chip::Platform::New<P256Keypair>();
#endif
}
VerifyOrReturnError(mOperationalKey != nullptr, CHIP_ERROR_NO_MEMORY);
return mOperationalKey->Deserialize(serialized);
}
void FabricInfo::ReleaseRootCert()
{
if (mRootCert != nullptr)
{
chip::Platform::MemoryFree(mRootCert);
}
mRootCertAllocatedLen = 0;
mRootCertLen = 0;
mRootCert = nullptr;
}
CHIP_ERROR FabricInfo::SetRootCert(const ByteSpan & cert)
{
if (cert.size() == 0)
{
ReleaseRootCert();
return CHIP_NO_ERROR;
}
VerifyOrReturnError(cert.size() <= kMaxCHIPCertLength, CHIP_ERROR_INVALID_ARGUMENT);
if (mRootCertLen != 0 && mRootCertAllocatedLen < cert.size())
{
ReleaseRootCert();
}
if (mRootCert == nullptr)
{
mRootCert = static_cast<uint8_t *>(chip::Platform::MemoryAlloc(cert.size()));
}
VerifyOrReturnError(mRootCert != nullptr, CHIP_ERROR_NO_MEMORY);
VerifyOrReturnError(CanCastTo<uint16_t>(cert.size()), CHIP_ERROR_INVALID_ARGUMENT);
mRootCertLen = static_cast<uint16_t>(cert.size());
// Find root key ID
ChipCertificateData certData;
ReturnErrorOnFailure(DecodeChipCert(cert, certData));
VerifyOrReturnError(certData.mAuthKeyId.size() <= sizeof(mRootKeyId), CHIP_ERROR_INVALID_ARGUMENT);
memcpy(mRootKeyId, certData.mAuthKeyId.data(), certData.mAuthKeyId.size());
mRootKeyIdLen = certData.mAuthKeyId.size();
mRootCertAllocatedLen = (mRootCertLen > mRootCertAllocatedLen) ? mRootCertLen : mRootCertAllocatedLen;
memcpy(mRootCert, cert.data(), mRootCertLen);
mRootPubkey = P256PublicKey(certData.mPublicKey);
return CHIP_NO_ERROR;
}
void FabricInfo::ReleaseOperationalCerts()
{
if (mOperationalCerts != nullptr)
{
chip::Platform::MemoryFree(mOperationalCerts);
}
mOperationalCertsLen = 0;
mOperationalCerts = nullptr;
}
CHIP_ERROR FabricInfo::SetOperationalCertsFromCertArray(const ByteSpan & certArray)
{
if (certArray.size() == 0)
{
ReleaseOperationalCerts();
return CHIP_NO_ERROR;
}
VerifyOrReturnError(certArray.size() <= kMaxCHIPOpCertArrayLength, CHIP_ERROR_INVALID_ARGUMENT);
if (mOperationalCertsLen != 0)
{
ReleaseOperationalCerts();
}
VerifyOrReturnError(CanCastTo<uint16_t>(certArray.size()), CHIP_ERROR_INVALID_ARGUMENT);
if (mOperationalCerts == nullptr)
{
mOperationalCerts = static_cast<uint8_t *>(chip::Platform::MemoryAlloc(certArray.size()));
}
VerifyOrReturnError(mOperationalCerts != nullptr, CHIP_ERROR_NO_MEMORY);
mOperationalCertsLen = static_cast<uint16_t>(certArray.size());
memcpy(mOperationalCerts, certArray.data(), mOperationalCertsLen);
return CHIP_NO_ERROR;
}
CHIP_ERROR FabricInfo::VerifyCredentials(const ByteSpan & opCertArray, ValidationContext & context, PeerId & nocPeerId,
FabricId & fabricId, Crypto::P256PublicKey & nocPubkey) const
{
// TODO - Optimize credentials verification logic
// The certificate chain construction and verification is a compute and memory intensive operation.
// It can be optimized by not loading certificate (i.e. rcac) that's local and implicitly trusted.
// The FindValidCert() algorithm will need updates to achieve this refactor.
ByteSpan rcac(mRootCert, mRootCertLen);
ByteSpan noc, icac;
ReturnErrorOnFailure(ExtractCertsFromCertArray(opCertArray, noc, icac));
constexpr uint8_t kMaxNumCertsInOpCreds = 3;
uint8_t nocCertIndex = 1;
ChipCertificateSet certificates;
ReturnErrorOnFailure(certificates.Init(kMaxNumCertsInOpCreds));
ReturnErrorOnFailure(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)));
if (!icac.empty())
{
ReturnErrorOnFailure(certificates.LoadCert(icac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)));
nocCertIndex = 2;
}
ReturnErrorOnFailure(certificates.LoadCert(noc, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)));
const ChipDN & nocSubjectDN = certificates.GetCertSet()[nocCertIndex].mSubjectDN;
const CertificateKeyId & nocSubjectKeyId = certificates.GetCertSet()[nocCertIndex].mSubjectKeyId;
const ChipCertificateData * resultCert = nullptr;
// FindValidCert() checks the certificate set constructed by loading noc, icac and rcac.
// It confirms that the certs link correctly (noc -> icac -> rcac), and have been correctly signed.
ReturnErrorOnFailure(certificates.FindValidCert(nocSubjectDN, nocSubjectKeyId, context, &resultCert));
NodeId nodeId;
ReturnErrorOnFailure(ExtractNodeIdFabricIdFromOpCert(certificates.GetCertSet()[nocCertIndex], &nodeId, &fabricId));
if (!icac.empty())
{
FabricId icacFabric = kUndefinedFabricId;
if (ExtractFabricIdFromCert(certificates.GetCertSet()[1], &icacFabric) == CHIP_NO_ERROR && icacFabric != kUndefinedFabricId)
{
ReturnErrorCodeIf(icacFabric != fabricId, CHIP_ERROR_FABRIC_MISMATCH_ON_ICA);
}
}
ReturnErrorOnFailure(GetCompressedId(fabricId, nodeId, &nocPeerId));
nocPubkey = P256PublicKey(certificates.GetCertSet()[nocCertIndex].mPublicKey);
return CHIP_NO_ERROR;
}
CHIP_ERROR FabricInfo::GenerateDestinationID(const ByteSpan & ipk, const ByteSpan & random, NodeId destNodeId,
MutableByteSpan & destinationId)
{
constexpr uint16_t kSigmaParamRandomNumberSize = 32;
constexpr size_t kDestinationMessageLen =
kSigmaParamRandomNumberSize + kP256_PublicKey_Length + sizeof(FabricId) + sizeof(NodeId);
HMAC_sha hmac;
uint8_t destinationMessage[kDestinationMessageLen];
Encoding::LittleEndian::BufferWriter bbuf(destinationMessage, sizeof(destinationMessage));
bbuf.Put(random.data(), random.size());
bbuf.Put(mRootPubkey.ConstBytes(), mRootPubkey.Length());
bbuf.Put64(mFabricId);
bbuf.Put64(destNodeId);
size_t written = 0;
VerifyOrReturnError(bbuf.Fit(written), CHIP_ERROR_BUFFER_TOO_SMALL);
CHIP_ERROR err =
hmac.HMAC_SHA256(ipk.data(), ipk.size(), destinationMessage, written, destinationId.data(), destinationId.size());
return err;
}
CHIP_ERROR FabricInfo::MatchDestinationID(const ByteSpan & targetDestinationId, const ByteSpan & initiatorRandom,
const ByteSpan * ipkList, size_t ipkListEntries)
{
uint8_t localDestID[kSHA256_Hash_Length] = { 0 };
MutableByteSpan localDestIDSpan(localDestID);
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
for (size_t ipkIdx = 0; ipkIdx < ipkListEntries; ++ipkIdx)
{
if (GenerateDestinationID(ipkList[ipkIdx], initiatorRandom, mOperationalId.GetNodeId(), localDestIDSpan) == CHIP_NO_ERROR &&
targetDestinationId.data_equal(localDestIDSpan))
{
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_CERT_NOT_TRUSTED;
}
void FabricTable::ReleaseFabricIndex(FabricIndex fabricIndex)
{
FabricInfo * fabric = FindFabricWithIndex(fabricIndex);
if (fabric != nullptr)
{
fabric->Reset();
}
}
FabricInfo * FabricTable::FindFabricWithIndex(FabricIndex fabricIndex)
{
if (fabricIndex >= kMinValidFabricIndex && fabricIndex <= kMaxValidFabricIndex)
{
FabricInfo * fabric = &mStates[fabricIndex - kMinValidFabricIndex];
LoadFromStorage(fabric);
return fabric;
}
return nullptr;
}
void FabricTable::Reset()
{
static_assert(kMaxValidFabricIndex <= UINT8_MAX, "Cannot create more fabrics than UINT8_MAX");
for (FabricIndex i = kMinValidFabricIndex; i <= kMaxValidFabricIndex; i++)
{
FabricInfo * fabric = FindFabricWithIndex(i);
if (fabric != nullptr)
{
fabric->Reset();
fabric->mFabric = i;
}
}
}
CHIP_ERROR FabricTable::Store(FabricIndex id)
{
CHIP_ERROR err = CHIP_NO_ERROR;
FabricInfo * fabric = nullptr;
VerifyOrExit(mStorage != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
fabric = FindFabricWithIndex(id);
VerifyOrExit(fabric != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
err = fabric->StoreIntoKVS(mStorage);
exit:
if (err == CHIP_NO_ERROR && mDelegate != nullptr)
{
ChipLogProgress(Discovery, "Fabric (%d) persisted to storage. Calling OnFabricPersistedToStorage", id);
mDelegate->OnFabricPersistedToStorage(fabric);
}
return err;
}
CHIP_ERROR FabricTable::LoadFromStorage(FabricInfo * fabric)
{
VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
if (!fabric->IsInitialized())
{
ReturnErrorOnFailure(fabric->FetchFromKVS(mStorage));
}
if (mDelegate != nullptr)
{
ChipLogProgress(Discovery, "Fabric (%d) loaded from storage. Calling OnFabricRetrievedFromStorage",
fabric->GetFabricIndex());
mDelegate->OnFabricRetrievedFromStorage(fabric);
}
return CHIP_NO_ERROR;
}
CHIP_ERROR FabricInfo::SetFabricInfo(FabricInfo & newFabric)
{
P256PublicKey pubkey;
ValidationContext validContext;
validContext.Reset();
validContext.mRequiredKeyUsages.Set(KeyUsageFlags::kDigitalSignature);
validContext.mRequiredKeyPurposes.Set(KeyPurposeFlags::kServerAuth);
SetEphemeralKey(newFabric.GetOperationalKey());
SetRootCert(ByteSpan(newFabric.mRootCert, newFabric.mRootCertLen));
ChipLogProgress(Discovery, "Verifying the received credentials");
ReturnErrorOnFailure(VerifyCredentials(ByteSpan(newFabric.mOperationalCerts, newFabric.mOperationalCertsLen), validContext,
mOperationalId, mFabricId, pubkey));
SetOperationalCertsFromCertArray(ByteSpan(newFabric.mOperationalCerts, newFabric.mOperationalCertsLen));
SetVendorId(newFabric.GetVendorId());
SetFabricLabel(newFabric.GetFabricLabel());
ChipLogProgress(Discovery, "Added new fabric at index: %d, Initialized: %d", GetFabricIndex(), IsInitialized());
ChipLogProgress(Discovery, "Assigned compressed fabric ID: 0x" ChipLogFormatX64 ", node ID: 0x" ChipLogFormatX64,
ChipLogValueX64(mOperationalId.GetCompressedFabricId()), ChipLogValueX64(mOperationalId.GetNodeId()));
return CHIP_NO_ERROR;
}
FabricIndex FabricTable::FindDestinationIDCandidate(const ByteSpan & destinationId, const ByteSpan & initiatorRandom,
const ByteSpan * ipkList, size_t ipkListEntries)
{
static_assert(kMaxValidFabricIndex <= UINT8_MAX, "Cannot create more fabrics than UINT8_MAX");
for (FabricIndex i = kMinValidFabricIndex; i <= kMaxValidFabricIndex; i++)
{
FabricInfo * fabric = FindFabricWithIndex(i);
if (fabric != nullptr &&
fabric->MatchDestinationID(destinationId, initiatorRandom, ipkList, ipkListEntries) == CHIP_NO_ERROR)
{
return i;
}
}
return kUndefinedFabricIndex;
}
CHIP_ERROR FabricTable::AddNewFabric(FabricInfo & newFabric, FabricIndex * outputIndex)
{
VerifyOrReturnError(outputIndex != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
static_assert(kMaxValidFabricIndex <= UINT8_MAX, "Cannot create more fabrics than UINT8_MAX");
for (FabricIndex i = mNextAvailableFabricIndex; i <= kMaxValidFabricIndex; i++)
{
FabricInfo * fabric = FindFabricWithIndex(i);
if (fabric != nullptr && !fabric->IsInitialized())
{
ReturnErrorOnFailure(fabric->SetFabricInfo(newFabric));
ReturnErrorOnFailure(Store(i));
mNextAvailableFabricIndex = static_cast<FabricIndex>((i + 1) % UINT8_MAX);
*outputIndex = i;
return CHIP_NO_ERROR;
}
}
for (FabricIndex i = kMinValidFabricIndex; i < kMaxValidFabricIndex; i++)
{
FabricInfo * fabric = FindFabricWithIndex(i);
if (fabric != nullptr && !fabric->IsInitialized())
{
ReturnErrorOnFailure(fabric->SetFabricInfo(newFabric));
ReturnErrorOnFailure(Store(i));
mNextAvailableFabricIndex = static_cast<FabricIndex>((i + 1) % UINT8_MAX);
*outputIndex = i;
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NO_MEMORY;
}
CHIP_ERROR FabricTable::Delete(FabricIndex id)
{
FabricInfo * fabric = nullptr;
CHIP_ERROR err = CHIP_NO_ERROR;
bool fabricIsInitialized = false;
VerifyOrExit(mStorage != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
fabric = FindFabricWithIndex(id);
fabricIsInitialized = fabric != nullptr && fabric->IsInitialized();
err = FabricInfo::DeleteFromKVS(mStorage, id); // Delete from storage regardless
exit:
if (err == CHIP_NO_ERROR)
{
ReleaseFabricIndex(id);
if (mDelegate != nullptr && fabricIsInitialized)
{
ChipLogProgress(Discovery, "Fabric (%d) deleted. Calling OnFabricDeletedFromStorage", id);
mDelegate->OnFabricDeletedFromStorage(id);
}
}
return CHIP_NO_ERROR;
}
void FabricTable::DeleteAllFabrics()
{
static_assert(kMaxValidFabricIndex <= UINT8_MAX, "Cannot create more fabrics than UINT8_MAX");
for (FabricIndex i = kMinValidFabricIndex; i <= kMaxValidFabricIndex; i++)
{
Delete(i);
}
}
CHIP_ERROR FabricTable::Init(PersistentStorageDelegate * storage)
{
VerifyOrReturnError(storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
mStorage = storage;
ChipLogDetail(Discovery, "Init fabric pairing table with server storage");
// Load the current fabrics from the storage. This is done here, since ConstFabricIterator
// iterator doesn't have mechanism to load fabric info from storage on demand.
// TODO - Update ConstFabricIterator to load fabric info from storage
static_assert(kMaxValidFabricIndex <= UINT8_MAX, "Cannot create more fabrics than UINT8_MAX");
for (FabricIndex i = kMinValidFabricIndex; i <= kMaxValidFabricIndex; i++)
{
FabricInfo * fabric = &mStates[i - kMinValidFabricIndex];
LoadFromStorage(fabric);
}
return CHIP_NO_ERROR;
}
CHIP_ERROR FabricTable::SetFabricDelegate(FabricTableDelegate * delegate)
{
VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
mDelegate = delegate;
ChipLogDetail(Discovery, "Set the fabric pairing table delegate");
return CHIP_NO_ERROR;
}
} // namespace Transport
} // namespace chip