blob: de983e247e6782d9e3f80c6ec1060bdaab3b1414 [file] [log] [blame]
/*
* Copyright (c) 2022 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.
*/
#include "Efr32PsaOperationalKeystore.h"
#include <crypto/OperationalKeystore.h>
#include <lib/core/CHIPError.h>
#include <lib/core/CHIPTLV.h>
#include <lib/core/DataModelTypes.h>
#include <lib/support/CHIPMem.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/SafeInt.h>
#include "EFR32Config.h"
#include "Efr32OpaqueKeypair.h"
namespace chip {
namespace DeviceLayer {
namespace Internal {
static_assert((sizeof(FabricIndex) == 1), "Implementation is not prepared for large fabric indices");
static_assert(SL_MATTER_MAX_STORED_OP_KEYS <= (kEFR32OpaqueKeyIdPersistentMax - kEFR32OpaqueKeyIdPersistentMin),
"Not enough opaque keys available to cover all requested operational keys");
static_assert((CHIP_CONFIG_MAX_FABRICS + 1) <= SL_MATTER_MAX_STORED_OP_KEYS,
"Not enough operational keys requested to cover all potential fabrics (+1 staging for fabric update)");
static_assert(SL_MATTER_MAX_STORED_OP_KEYS >= 1, "Minimum supported amount of operational keys is 1");
using namespace chip::Crypto;
using chip::Platform::MemoryCalloc;
using chip::Platform::MemoryFree;
Efr32PsaOperationalKeystore::~Efr32PsaOperationalKeystore()
{
Deinit();
}
CHIP_ERROR Efr32PsaOperationalKeystore::Init()
{
// Detect existing keymap size
CHIP_ERROR error = CHIP_NO_ERROR;
size_t wantedLen = SL_MATTER_MAX_STORED_OP_KEYS * sizeof(FabricIndex);
size_t existingLen = 0;
bool update_cache = false;
if (EFR32Config::ConfigValueExists(EFR32Config::kConfigKey_OpKeyMap, existingLen))
{
// There's a pre-existing key map on disk. Size the map to read it fully.
size_t outLen = 0;
if (existingLen > (kEFR32OpaqueKeyIdPersistentMax - kEFR32OpaqueKeyIdPersistentMin) * sizeof(FabricIndex))
{
return CHIP_ERROR_INTERNAL;
}
// Upsize the map if the config was changed
if (existingLen < wantedLen)
{
existingLen = wantedLen;
}
mKeyMap = (FabricIndex *) MemoryCalloc(1, existingLen);
VerifyOrExit(mKeyMap, error = CHIP_ERROR_NO_MEMORY);
// Read the existing key map
error = EFR32Config::ReadConfigValueBin(EFR32Config::kConfigKey_OpKeyMap, (uint8_t *) mKeyMap, existingLen, outLen);
SuccessOrExit(error);
// If upsizing, extend the map with undefined indices
for (size_t i = (outLen / sizeof(FabricIndex)); i < (existingLen / sizeof(FabricIndex)); i++)
{
mKeyMap[i] = kUndefinedFabricIndex;
}
// If the config has changed, check whether it can be downsized fully or partially
if (existingLen > wantedLen)
{
size_t highest_found_index = 0;
for (size_t i = (wantedLen / sizeof(FabricIndex)); i < (existingLen / sizeof(FabricIndex)); i++)
{
if (mKeyMap[i] != kUndefinedFabricIndex)
{
highest_found_index = i;
}
}
// set size to the smallest that will fit the upper opaque key ID in use
if (highest_found_index > 0)
{
existingLen = (highest_found_index + 1) * sizeof(FabricIndex);
update_cache = true;
}
}
// Set the key map size
mKeyMapSize = existingLen;
}
else
{
// No key map on disk. Create and initialize a new one.
mKeyMap = (FabricIndex *) MemoryCalloc(1, wantedLen);
VerifyOrExit(mKeyMap, error = CHIP_ERROR_NO_MEMORY);
for (size_t i = 0; i < (wantedLen / sizeof(FabricIndex)); i++)
{
mKeyMap[i] = kUndefinedFabricIndex;
}
mKeyMapSize = wantedLen;
update_cache = true;
}
// Write-out keymap if needed
if (update_cache)
{
error = EFR32Config::WriteConfigValueBin(EFR32Config::kConfigKey_OpKeyMap, mKeyMap, mKeyMapSize);
SuccessOrExit(error);
}
// Initialize cache key
mCachedKey = Platform::New<EFR32OpaqueP256Keypair>();
VerifyOrExit(mCachedKey, error = CHIP_ERROR_NO_MEMORY);
exit:
if (error != CHIP_NO_ERROR)
{
Deinit();
return error;
}
mIsInitialized = true;
return CHIP_NO_ERROR;
}
EFR32OpaqueKeyId Efr32PsaOperationalKeystore::FindKeyIdForFabric(FabricIndex fabricIndex) const
{
// Search the map linearly to find a matching index slot
for (size_t i = 0; i < (mKeyMapSize / sizeof(FabricIndex)); i++)
{
if (mKeyMap[i] == fabricIndex)
{
// Found a match
return i + kEFR32OpaqueKeyIdPersistentMin;
}
}
return kEFR32OpaqueKeyIdUnknown;
}
bool Efr32PsaOperationalKeystore::HasOpKeypairForFabric(FabricIndex fabricIndex) const
{
VerifyOrReturnError(mIsInitialized, false);
VerifyOrReturnError(IsValidFabricIndex(fabricIndex), false);
// If there was a pending keypair, then there's really a usable key
if (mIsPendingKeypairActive && (fabricIndex == mPendingFabricIndex) && (mPendingKeypair != nullptr))
{
return true;
}
// Check whether we have a match in the map
if (FindKeyIdForFabric(fabricIndex) != kEFR32OpaqueKeyIdUnknown)
{
return true;
}
return false;
}
CHIP_ERROR Efr32PsaOperationalKeystore::NewOpKeypairForFabric(FabricIndex fabricIndex,
MutableByteSpan & outCertificateSigningRequest)
{
CHIP_ERROR error = CHIP_NO_ERROR;
VerifyOrReturnError(mIsInitialized, CHIP_ERROR_WELL_UNINITIALIZED);
VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
// If a key is pending, we cannot generate for a different fabric index until we commit or revert.
if ((mPendingFabricIndex != kUndefinedFabricIndex) && (fabricIndex != mPendingFabricIndex))
{
return CHIP_ERROR_INVALID_FABRIC_INDEX;
}
VerifyOrReturnError(outCertificateSigningRequest.size() >= Crypto::kMAX_CSR_Length, CHIP_ERROR_BUFFER_TOO_SMALL);
// Generate new key
EFR32OpaqueKeyId id = kEFR32OpaqueKeyIdUnknown;
if (mPendingFabricIndex != kUndefinedFabricIndex)
{
// If we already have a pending key, delete it and put a new one in its place
id = mPendingKeypair->GetKeyId();
if (id == kEFR32OpaqueKeyIdUnknown)
{
ResetPendingKey();
}
else
{
mPendingKeypair->Delete();
if (id == kEFR32OpaqueKeyIdVolatile)
{
id = kEFR32OpaqueKeyIdUnknown;
}
}
}
if (id == kEFR32OpaqueKeyIdUnknown)
{
// Try to find an available opaque ID in the map
id = FindKeyIdForFabric(kUndefinedFabricIndex);
if (!mPendingKeypair)
{
mPendingKeypair = Platform::New<EFR32OpaqueP256Keypair>();
}
}
if (id == kEFR32OpaqueKeyIdUnknown)
{
// Could not find a free spot in the map
return CHIP_ERROR_NO_MEMORY;
}
// Create new key on the old or found key ID
error = mPendingKeypair->Create(id, EFR32OpaqueKeyUsages::ECDSA_P256_SHA256);
if (error != CHIP_NO_ERROR)
{
ResetPendingKey();
return error;
}
// Set CSR and state
size_t csrLength = outCertificateSigningRequest.size();
error = mPendingKeypair->NewCertificateSigningRequest(outCertificateSigningRequest.data(), csrLength);
if (error != CHIP_NO_ERROR)
{
ResetPendingKey();
return error;
}
outCertificateSigningRequest.reduce_size(csrLength);
mPendingFabricIndex = fabricIndex;
return CHIP_NO_ERROR;
}
CHIP_ERROR Efr32PsaOperationalKeystore::ActivateOpKeypairForFabric(FabricIndex fabricIndex,
const Crypto::P256PublicKey & nocPublicKey)
{
VerifyOrReturnError(mIsInitialized, CHIP_ERROR_WELL_UNINITIALIZED);
VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (fabricIndex == mPendingFabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
// Validate public key being activated matches last generated pending keypair
VerifyOrReturnError(mPendingKeypair->Pubkey().Matches(nocPublicKey), CHIP_ERROR_INVALID_PUBLIC_KEY);
mIsPendingKeypairActive = true;
return CHIP_NO_ERROR;
}
CHIP_ERROR Efr32PsaOperationalKeystore::CommitOpKeypairForFabric(FabricIndex fabricIndex)
{
VerifyOrReturnError(mIsInitialized, CHIP_ERROR_WELL_UNINITIALIZED);
VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (fabricIndex == mPendingFabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
VerifyOrReturnError(mIsPendingKeypairActive == true, CHIP_ERROR_INCORRECT_STATE);
// Add key association to key map
EFR32OpaqueKeyId id = mPendingKeypair->GetKeyId();
if (id == kEFR32OpaqueKeyIdUnknown || id == kEFR32OpaqueKeyIdVolatile)
{
ResetPendingKey();
return CHIP_ERROR_INTERNAL;
}
// Guard against array out-of-bounds (should not happen with correctly initialised keys)
size_t keymap_index = id - kEFR32OpaqueKeyIdPersistentMin;
if (keymap_index >= (mKeyMapSize / sizeof(FabricIndex)))
{
return CHIP_ERROR_INTERNAL;
}
if (mKeyMap[keymap_index] != kUndefinedFabricIndex)
{
ResetPendingKey();
return CHIP_ERROR_INTERNAL;
}
mKeyMap[keymap_index] = fabricIndex;
// Persist key map
CHIP_ERROR error = EFR32Config::WriteConfigValueBin(EFR32Config::kConfigKey_OpKeyMap, mKeyMap, mKeyMapSize);
if (error != CHIP_NO_ERROR)
{
return error;
}
// There's a good chance we'll need the key again soon
mCachedKey->Load(id);
mPendingKeypair = nullptr;
mIsPendingKeypairActive = false;
mPendingFabricIndex = kUndefinedFabricIndex;
return CHIP_NO_ERROR;
}
CHIP_ERROR Efr32PsaOperationalKeystore::RemoveOpKeypairForFabric(FabricIndex fabricIndex)
{
VerifyOrReturnError(mIsInitialized, CHIP_ERROR_WELL_UNINITIALIZED);
VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
// Remove pending keypair if we have it and the fabric ID matches
if ((mPendingKeypair != nullptr) && (fabricIndex == mPendingFabricIndex))
{
RevertPendingKeypair();
}
EFR32OpaqueKeyId id = FindKeyIdForFabric(fabricIndex);
if (id == kEFR32OpaqueKeyIdUnknown)
{
// Fabric is not in the map, so assume it's gone already
return CHIP_NO_ERROR;
}
// Guard against array out-of-bounds (should not happen with correctly initialised keys)
size_t keymap_index = id - kEFR32OpaqueKeyIdPersistentMin;
if (keymap_index >= (mKeyMapSize / sizeof(FabricIndex)))
{
return CHIP_ERROR_INTERNAL;
}
// Reset the key mapping since we'll be deleting this key
mKeyMap[keymap_index] = kUndefinedFabricIndex;
// Persist key map
CHIP_ERROR error = EFR32Config::WriteConfigValueBin(EFR32Config::kConfigKey_OpKeyMap, mKeyMap, mKeyMapSize);
if (error != CHIP_NO_ERROR)
{
return error;
}
// Check if key is cached
EFR32OpaqueKeyId cachedId = mCachedKey->GetKeyId();
if (id == cachedId)
{
// Delete from persistent storage and unload
mCachedKey->Delete();
return CHIP_NO_ERROR;
}
// Load it for purposes of deletion
error = mCachedKey->Load(id);
if (error != CHIP_NO_ERROR && error != CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND)
{
return CHIP_ERROR_INTERNAL;
}
mCachedKey->Delete();
return CHIP_NO_ERROR;
}
void Efr32PsaOperationalKeystore::RevertPendingKeypair()
{
if (mIsInitialized)
{
// Just delete the pending key from storage
ResetPendingKey();
}
}
CHIP_ERROR Efr32PsaOperationalKeystore::SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message,
Crypto::P256ECDSASignature & outSignature) const
{
VerifyOrReturnError(mIsInitialized, CHIP_ERROR_WELL_UNINITIALIZED);
VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
// Check to see whether the key is an activated pending key
if (mIsPendingKeypairActive && (fabricIndex == mPendingFabricIndex))
{
VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_INTERNAL);
return mPendingKeypair->ECDSA_sign_msg(message.data(), message.size(), outSignature);
}
// Figure out which key ID we're looking for
EFR32OpaqueKeyId id = FindKeyIdForFabric(fabricIndex);
if (id == kEFR32OpaqueKeyIdUnknown)
{
// Fabric is not in the map, but the caller thinks it's there?
return CHIP_ERROR_INTERNAL;
}
// Check whether we have the key in cache
EFR32OpaqueKeyId cachedId = mCachedKey->GetKeyId();
if (id == cachedId)
{
return mCachedKey->ECDSA_sign_msg(message.data(), message.size(), outSignature);
}
// If not, we need to recreate from the backend
CHIP_ERROR error = mCachedKey->Load(id);
if (error != CHIP_NO_ERROR)
{
return CHIP_ERROR_INTERNAL;
}
// Sign with retrieved key
error = mCachedKey->ECDSA_sign_msg(message.data(), message.size(), outSignature);
if (error != CHIP_NO_ERROR)
{
return CHIP_ERROR_INTERNAL;
}
return CHIP_NO_ERROR;
}
Crypto::P256Keypair * Efr32PsaOperationalKeystore::AllocateEphemeralKeypairForCASE()
{
EFR32OpaqueP256Keypair * new_key = Platform::New<EFR32OpaqueP256Keypair>();
if (new_key != nullptr)
{
new_key->Create(kEFR32OpaqueKeyIdVolatile, EFR32OpaqueKeyUsages::ECDH_P256);
}
return new_key;
}
void Efr32PsaOperationalKeystore::ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair)
{
Platform::Delete<EFR32OpaqueP256Keypair>((EFR32OpaqueP256Keypair *) keypair);
}
} // namespace Internal
} // namespace DeviceLayer
} // namespace chip