blob: ceb554f473e4a3ae13e7cf4362cacb1837b4db26 [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 admins that have provisioned the device.
*/
#include <core/CHIPEncoding.h>
#include <support/CHIPMem.h>
#include <support/SafeInt.h>
#include <transport/AdminPairingTable.h>
namespace chip {
using namespace Credentials;
using namespace Crypto;
namespace Transport {
CHIP_ERROR AdminPairingInfo::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;
}
CHIP_ERROR AdminPairingInfo::StoreIntoKVS(PersistentStorageDelegate * kvs)
{
CHIP_ERROR err = CHIP_NO_ERROR;
char key[KeySize()];
ReturnErrorOnFailure(GenerateKey(mAdmin, key, sizeof(key)));
StorableAdminPairingInfo * info = chip::Platform::New<StorableAdminPairingInfo>();
ReturnErrorCodeIf(info == nullptr, CHIP_ERROR_NO_MEMORY);
info->mNodeId = Encoding::LittleEndian::HostSwap64(mNodeId);
info->mAdmin = Encoding::LittleEndian::HostSwap16(mAdmin);
info->mFabricId = Encoding::LittleEndian::HostSwap64(mFabricId);
info->mVendorId = Encoding::LittleEndian::HostSwap16(mVendorId);
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 (mOperationalCert == nullptr || mOpCertLen == 0)
{
info->mOpCertLen = 0;
}
else
{
info->mOpCertLen = Encoding::LittleEndian::HostSwap16(mOpCertLen);
memcpy(info->mOperationalCert, mOperationalCert, mOpCertLen);
}
err = kvs->SyncSetKeyValue(key, info, sizeof(StorableAdminPairingInfo));
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 AdminPairingInfo::FetchFromKVS(PersistentStorageDelegate * kvs)
{
CHIP_ERROR err = CHIP_NO_ERROR;
char key[KeySize()];
ReturnErrorOnFailure(GenerateKey(mAdmin, key, sizeof(key)));
StorableAdminPairingInfo * info = chip::Platform::New<StorableAdminPairingInfo>();
ReturnErrorCodeIf(info == nullptr, CHIP_ERROR_NO_MEMORY);
uint16_t infoSize = sizeof(StorableAdminPairingInfo);
AdminId id;
uint16_t rootCertLen, opCertLen;
size_t stringLength;
SuccessOrExit(err = kvs->SyncGetKeyValue(key, info, infoSize));
mNodeId = Encoding::LittleEndian::HostSwap64(info->mNodeId);
id = Encoding::LittleEndian::HostSwap16(info->mAdmin);
mFabricId = Encoding::LittleEndian::HostSwap64(info->mFabricId);
mVendorId = Encoding::LittleEndian::HostSwap16(info->mVendorId);
rootCertLen = Encoding::LittleEndian::HostSwap16(info->mRootCertLen);
opCertLen = Encoding::LittleEndian::HostSwap16(info->mOpCertLen);
stringLength = strnlen(info->mFabricLabel, kFabricLabelMaxLengthInBytes);
memcpy(mFabricLabel, info->mFabricLabel, stringLength);
mFabricLabel[stringLength] = '\0'; // Set null terminator
VerifyOrExit(mAdmin == id, err = CHIP_ERROR_INCORRECT_STATE);
if (mOperationalKey == nullptr)
{
mOperationalKey = chip::Platform::New<P256Keypair>();
}
VerifyOrExit(mOperationalKey != nullptr, err = CHIP_ERROR_NO_MEMORY);
SuccessOrExit(err = mOperationalKey->Deserialize(info->mOperationalKey));
ChipLogProgress(Inet, "Loading certs from KVS");
SuccessOrExit(SetRootCert(ByteSpan(info->mRootCert, rootCertLen)));
SuccessOrExit(SetOperationalCert(ByteSpan(info->mOperationalCert, opCertLen)));
exit:
if (info != nullptr)
{
chip::Platform::Delete(info);
}
return CHIP_NO_ERROR;
}
CHIP_ERROR AdminPairingInfo::DeleteFromKVS(PersistentStorageDelegate * kvs, AdminId 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)
{
ChipLogError(Discovery, "Error occurred calling SyncDeleteKeyValue");
}
return err;
}
constexpr size_t AdminPairingInfo::KeySize()
{
return sizeof(kAdminTableKeyPrefix) + 2 * sizeof(AdminId);
}
CHIP_ERROR AdminPairingInfo::GenerateKey(AdminId id, char * key, size_t len)
{
VerifyOrReturnError(len >= KeySize(), CHIP_ERROR_INVALID_ARGUMENT);
int keySize = snprintf(key, len, "%s%x", kAdminTableKeyPrefix, id);
VerifyOrReturnError(keySize > 0, CHIP_ERROR_INTERNAL);
VerifyOrReturnError(len > (size_t) keySize, CHIP_ERROR_INTERNAL);
return CHIP_NO_ERROR;
}
CHIP_ERROR AdminPairingInfo::SetOperationalKey(const P256Keypair & key)
{
P256SerializedKeypair serialized;
ReturnErrorOnFailure(key.Serialize(serialized));
if (mOperationalKey == nullptr)
{
mOperationalKey = chip::Platform::New<P256Keypair>();
}
VerifyOrReturnError(mOperationalKey != nullptr, CHIP_ERROR_NO_MEMORY);
return mOperationalKey->Deserialize(serialized);
}
void AdminPairingInfo::ReleaseRootCert()
{
if (mRootCert != nullptr)
{
chip::Platform::MemoryFree(mRootCert);
}
mRootCertAllocatedLen = 0;
mRootCertLen = 0;
mRootCert = nullptr;
}
CHIP_ERROR AdminPairingInfo::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());
mRootCertAllocatedLen = (mRootCertLen > mRootCertAllocatedLen) ? mRootCertLen : mRootCertAllocatedLen;
memcpy(mRootCert, cert.data(), mRootCertLen);
return CHIP_NO_ERROR;
}
void AdminPairingInfo::ReleaseOperationalCert()
{
if (mOperationalCert != nullptr)
{
chip::Platform::MemoryFree(mOperationalCert);
}
mOpCertAllocatedLen = 0;
mOpCertLen = 0;
mOperationalCert = nullptr;
}
CHIP_ERROR AdminPairingInfo::SetOperationalCert(const ByteSpan & cert)
{
if (cert.size() == 0)
{
ReleaseOperationalCert();
return CHIP_NO_ERROR;
}
// There could be two certs in the set -> ICA and NOC
VerifyOrReturnError(cert.size() <= kMaxCHIPCertLength * 2, CHIP_ERROR_INVALID_ARGUMENT);
if (mOpCertLen != 0 && mOpCertAllocatedLen < cert.size())
{
ReleaseOperationalCert();
}
if (mOperationalCert == nullptr)
{
mOperationalCert = static_cast<uint8_t *>(chip::Platform::MemoryAlloc(cert.size()));
}
VerifyOrReturnError(mOperationalCert != nullptr, CHIP_ERROR_NO_MEMORY);
VerifyOrReturnError(CanCastTo<uint16_t>(cert.size()), CHIP_ERROR_INVALID_ARGUMENT);
mOpCertLen = static_cast<uint16_t>(cert.size());
mOpCertAllocatedLen = (mOpCertLen > mOpCertAllocatedLen) ? mOpCertLen : mOpCertAllocatedLen;
memcpy(mOperationalCert, cert.data(), mOpCertLen);
return CHIP_NO_ERROR;
}
CHIP_ERROR AdminPairingInfo::GetCredentials(OperationalCredentialSet & credentials, ChipCertificateSet & certificates,
CertificateKeyId & rootKeyId)
{
constexpr uint8_t kMaxNumCertsInOpCreds = 3;
ReturnErrorOnFailure(certificates.Init(kMaxNumCertsInOpCreds, kMaxCHIPCertLength * kMaxNumCertsInOpCreds));
ReturnErrorOnFailure(
certificates.LoadCert(mRootCert, mRootCertLen,
BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor).Set(CertDecodeFlags::kGenerateTBSHash)));
credentials.Release();
ReturnErrorOnFailure(credentials.Init(&certificates, certificates.GetCertCount()));
const CertificateKeyId * id = credentials.GetTrustedRootId(0);
rootKeyId.mId = id->mId;
rootKeyId.mLen = id->mLen;
ReturnErrorOnFailure(credentials.SetDevOpCred(rootKeyId, mOperationalCert, mOpCertLen));
ReturnErrorOnFailure(credentials.SetDevOpCredKeypair(rootKeyId, mOperationalKey));
return CHIP_NO_ERROR;
}
AdminPairingInfo * AdminPairingTable::AssignAdminId(AdminId adminId)
{
for (size_t i = 0; i < CHIP_CONFIG_MAX_DEVICE_ADMINS; i++)
{
if (!mStates[i].IsInitialized())
{
mStates[i].SetAdminId(adminId);
return &mStates[i];
}
}
return nullptr;
}
AdminPairingInfo * AdminPairingTable::AssignAdminId(AdminId adminId, NodeId nodeId)
{
AdminPairingInfo * admin = AssignAdminId(adminId);
if (admin != nullptr)
{
admin->SetNodeId(nodeId);
}
return admin;
}
void AdminPairingTable::ReleaseAdminId(AdminId adminId)
{
AdminPairingInfo * admin = FindAdminWithId(adminId);
if (admin != nullptr)
{
admin->Reset();
}
}
AdminPairingInfo * AdminPairingTable::FindAdminWithId(AdminId adminId)
{
for (auto & state : mStates)
{
if (state.IsInitialized() && state.GetAdminId() == adminId)
{
return &state;
}
}
return nullptr;
}
AdminPairingInfo * AdminPairingTable::FindAdminForNode(FabricId fabricId, NodeId nodeId, uint16_t vendorId)
{
uint32_t index = 0;
for (auto & state : mStates)
{
if (state.IsInitialized())
{
ChipLogProgress(Discovery,
"Checking ind:%" PRIu32 " [fabricId 0x" ChipLogFormatX64 " nodeId 0x" ChipLogFormatX64
" vendorId %" PRIu16 "] vs"
" [fabricId 0x" ChipLogFormatX64 " nodeId 0x" ChipLogFormatX64 " vendorId %d]",
index, ChipLogValueX64(state.GetFabricId()), ChipLogValueX64(state.GetNodeId()), state.GetVendorId(),
ChipLogValueX64(fabricId), ChipLogValueX64(nodeId), vendorId);
}
if (state.IsInitialized() && state.GetFabricId() == fabricId &&
(nodeId == kUndefinedNodeId || state.GetNodeId() == nodeId) &&
(vendorId == kUndefinedVendorId || state.GetVendorId() == vendorId))
{
ChipLogProgress(Discovery, "Found a match!");
return &state;
}
index++;
}
return nullptr;
}
void AdminPairingTable::Reset()
{
for (size_t i = 0; i < CHIP_CONFIG_MAX_DEVICE_ADMINS; i++)
{
mStates[i].Reset();
}
}
CHIP_ERROR AdminPairingTable::Store(AdminId id)
{
CHIP_ERROR err = CHIP_NO_ERROR;
AdminPairingInfo * admin = nullptr;
VerifyOrExit(mStorage != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
admin = FindAdminWithId(id);
VerifyOrExit(admin != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
err = admin->StoreIntoKVS(mStorage);
exit:
if (err == CHIP_NO_ERROR && mDelegate != nullptr)
{
ChipLogProgress(Discovery, "Admin (%d) persisted to storage. Calling OnAdminPersistedToStorage", id);
mDelegate->OnAdminPersistedToStorage(admin);
}
return err;
}
CHIP_ERROR AdminPairingTable::LoadFromStorage(AdminId id)
{
CHIP_ERROR err = CHIP_NO_ERROR;
AdminPairingInfo * admin = nullptr;
bool didCreateAdmin = false;
VerifyOrExit(mStorage != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
admin = FindAdminWithId(id);
if (admin == nullptr)
{
admin = AssignAdminId(id);
didCreateAdmin = true;
}
VerifyOrExit(admin != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
err = admin->FetchFromKVS(mStorage);
exit:
if (err != CHIP_NO_ERROR && didCreateAdmin)
{
ReleaseAdminId(id);
}
else if (err == CHIP_NO_ERROR && mDelegate != nullptr)
{
ChipLogProgress(Discovery, "Admin (%d) loaded from storage. Calling OnAdminRetrievedFromStorage", id);
mDelegate->OnAdminRetrievedFromStorage(admin);
}
return err;
}
CHIP_ERROR AdminPairingTable::Delete(AdminId id)
{
AdminPairingInfo * admin = nullptr;
CHIP_ERROR err = CHIP_NO_ERROR;
bool adminIsInitialized = false;
VerifyOrExit(mStorage != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
admin = FindAdminWithId(id);
adminIsInitialized = admin != nullptr && admin->IsInitialized();
err = AdminPairingInfo::DeleteFromKVS(mStorage, id); // Delete from storage regardless
exit:
if (err == CHIP_NO_ERROR)
{
ReleaseAdminId(id);
if (mDelegate != nullptr && adminIsInitialized)
{
ChipLogProgress(Discovery, "Admin (%d) deleted. Calling OnAdminDeletedFromStorage", id);
mDelegate->OnAdminDeletedFromStorage(id);
}
}
return CHIP_NO_ERROR;
}
CHIP_ERROR AdminPairingTable::Init(PersistentStorageDelegate * storage)
{
VerifyOrReturnError(storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
mStorage = storage;
ChipLogDetail(Discovery, "Init admin pairing table with server storage");
return CHIP_NO_ERROR;
}
CHIP_ERROR AdminPairingTable::SetAdminPairingDelegate(AdminPairingTableDelegate * delegate)
{
VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
mDelegate = delegate;
ChipLogDetail(Discovery, "Set the admin pairing table delegate");
return CHIP_NO_ERROR;
}
} // namespace Transport
} // namespace chip