Skip to content

Commit

Permalink
[crypto] Migrate Operational Keys from mbedTLS to PSA ITS
Browse files Browse the repository at this point in the history
Implemented a mechanism to detect all existing mbedTLS-related
operational keys stored in non-secure storage and move them to
the PSA ITS secure storage.

After the migration all keys will be removed from non-secure
storage.
  • Loading branch information
ArekBalysNordic committed Jan 8, 2024
1 parent 7381d76 commit 7739120
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/app/server/Server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,11 @@ void Server::ResumeSubscriptions()
Credentials::IgnoreCertificateValidityPeriodPolicy Server::sDefaultCertValidityPolicy;

KvsPersistentStorageDelegate CommonCaseDeviceServerInitParams::sKvsPersistenStorageDelegate;
#if CHIP_CRYPTO_PSA
PSAOperationalKeystore CommonCaseDeviceServerInitParams::sPSAOperationalKeystore;
#else
PersistentStorageOperationalKeystore CommonCaseDeviceServerInitParams::sPersistentStorageOperationalKeystore;
#endif
Credentials::PersistentStorageOpCertStore CommonCaseDeviceServerInitParams::sPersistentStorageOpCertStore;
Credentials::GroupDataProviderImpl CommonCaseDeviceServerInitParams::sGroupDataProvider;
app::DefaultTimerDelegate CommonCaseDeviceServerInitParams::sTimerDelegate;
Expand Down
13 changes: 13 additions & 0 deletions src/app/server/Server.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@
#include <credentials/PersistentStorageOpCertStore.h>
#include <crypto/DefaultSessionKeystore.h>
#include <crypto/OperationalKeystore.h>
#if CHIP_CRYPTO_PSA
#include <crypto/PSAOperationalKeystore.h>
#else
#include <crypto/PersistentStorageOperationalKeystore.h>
#endif
#include <inet/InetConfig.h>
#include <lib/core/CHIPConfig.h>
#include <lib/support/SafeInt.h>
Expand Down Expand Up @@ -214,10 +218,15 @@ struct CommonCaseDeviceServerInitParams : public ServerInitParams
// PersistentStorageDelegate "software-based" operational key access injection
if (this->operationalKeystore == nullptr)
{
#if CHIP_CRYPTO_PSA
ReturnErrorOnFailure(sPSAOperationalKeystore.Init(this->persistentStorageDelegate));
this->operationalKeystore = &sPSAOperationalKeystore;
#else
// WARNING: PersistentStorageOperationalKeystore::Finish() is never called. It's fine for
// for examples and for now.
ReturnErrorOnFailure(sPersistentStorageOperationalKeystore.Init(this->persistentStorageDelegate));
this->operationalKeystore = &sPersistentStorageOperationalKeystore;
#endif
}

// OpCertStore can be injected but default to persistent storage default
Expand Down Expand Up @@ -273,7 +282,11 @@ struct CommonCaseDeviceServerInitParams : public ServerInitParams

private:
static KvsPersistentStorageDelegate sKvsPersistenStorageDelegate;
#if CHIP_CRYPTO_PSA
static PSAOperationalKeystore sPSAOperationalKeystore;
#else
static PersistentStorageOperationalKeystore sPersistentStorageOperationalKeystore;
#endif
static Credentials::PersistentStorageOpCertStore sPersistentStorageOpCertStore;
static Credentials::GroupDataProviderImpl sGroupDataProvider;
static chip::app::DefaultTimerDelegate sTimerDelegate;
Expand Down
94 changes: 94 additions & 0 deletions src/crypto/PSAOperationalKeystore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,22 @@

#include "PSAOperationalKeystore.h"

#include <lib/core/TLV.h>
#include <lib/support/CHIPMem.h>
#include <lib/support/DefaultStorageKeyAllocator.h>

#include <psa/crypto.h>

namespace chip {
namespace Crypto {

namespace {
// Tags for our operational keypair storage copied from PersistentStorageOperationalKeystore.cpp
constexpr TLV::Tag kOpKeyVersionTag = TLV::ContextTag(0);
constexpr TLV::Tag kOpKeyDataTag = TLV::ContextTag(1);
constexpr uint16_t kOpKeyVersion = 1;
} // namespace

PSAOperationalKeystore::PersistentP256Keypair::PersistentP256Keypair(FabricIndex fabricIndex)
{
ToPsaContext(mKeypair).key_id = MakeOperationalKeyId(fabricIndex);
Expand Down Expand Up @@ -135,6 +144,40 @@ CHIP_ERROR PSAOperationalKeystore::NewOpKeypairForFabric(FabricIndex fabricIndex
return CHIP_NO_ERROR;
}

CHIP_ERROR PSAOperationalKeystore::PersistentP256Keypair::Deserialize(P256SerializedKeypair & input)
{
CHIP_ERROR error = CHIP_NO_ERROR;
psa_status_t status = PSA_SUCCESS;
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_key_id_t keyId = 0;
Encoding::BufferWriter bbuf(mPublicKey, mPublicKey.Length());

Destroy();

// Type based on ECC with the elliptic curve SECP256r1 -> PSA_ECC_FAMILY_SECP_R1
psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&attributes, kP256_PrivateKey_Length * 8);
psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE);
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT);
psa_set_key_id(&attributes, GetKeyId());

status = psa_import_key(&attributes, input.ConstBytes() + mPublicKey.Length(), kP256_PrivateKey_Length, &keyId);
VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL);

bbuf.Put(input.ConstBytes(), mPublicKey.Length());
if (!bbuf.Fit())
{
psa_destroy_key(keyId);
error = CHIP_ERROR_NO_MEMORY;
}

exit:
psa_reset_key_attributes(&attributes);

return error;
}

CHIP_ERROR PSAOperationalKeystore::ActivateOpKeypairForFabric(FabricIndex fabricIndex, const Crypto::P256PublicKey & nocPublicKey)
{
VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && mPendingFabricIndex == fabricIndex, CHIP_ERROR_INVALID_FABRIC_INDEX);
Expand Down Expand Up @@ -209,5 +252,56 @@ void PSAOperationalKeystore::ReleasePendingKeypair()
mIsPendingKeypairActive = false;
}

CHIP_ERROR PSAOperationalKeystore::MoveOpKeysFromKVSToITS(PersistentStorageDelegate * storage)
{
Crypto::SensitiveDataBuffer<TLV::EstimateStructOverhead(sizeof(uint16_t), P256SerializedKeypair::Capacity())> buf;
uint16_t keySize = static_cast<uint16_t>(buf.Capacity());

for (FabricIndex index = 0; index < CHIP_CONFIG_MAX_FABRICS; index++)
{
CHIP_ERROR err = storage->SyncGetKeyValue(DefaultStorageKeyAllocator::FabricOpKey(index).KeyName(), buf.Bytes(), keySize);
if (CHIP_NO_ERROR == err)
{
PersistentP256Keypair keyPair(index);

// Do not allow overwriting the existing key and just remove it from KVS
if (keyPair.Exists())
{
ReturnErrorOnFailure(storage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::FabricOpKey(index).KeyName()));
continue;
}

// Read the operational private and public keys from the TLV entry
buf.SetLength(static_cast<size_t>(keySize));
TLV::ContiguousBufferTLVReader reader;
reader.Init(buf.Bytes(), buf.Length());
ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
TLV::TLVType containerType;
ReturnErrorOnFailure(reader.EnterContainer(containerType));
ReturnErrorOnFailure(reader.Next(kOpKeyVersionTag));
uint16_t opKeyVersion;
ReturnErrorOnFailure(reader.Get(opKeyVersion));
VerifyOrReturnError(opKeyVersion == kOpKeyVersion, CHIP_ERROR_VERSION_MISMATCH);
ReturnErrorOnFailure(reader.Next(kOpKeyDataTag));

ByteSpan keyData;
ReturnErrorOnFailure(reader.GetByteView(keyData));
ReturnErrorOnFailure(reader.ExitContainer(containerType));
ReturnErrorOnFailure(reader.VerifyEndOfContainer());

if (keyData.size() == kP256_PrivateKey_Length + kP256_PublicKey_Length)
{
P256SerializedKeypair serializedKeypair;
ReturnErrorOnFailure(serializedKeypair.SetLength(kP256_PrivateKey_Length + kP256_PublicKey_Length));
memcpy(serializedKeypair.Bytes(), keyData.data(), kP256_PrivateKey_Length + kP256_PublicKey_Length);
ReturnErrorOnFailure(keyPair.Deserialize(serializedKeypair));
ReturnErrorOnFailure(storage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::FabricOpKey(index).KeyName()));
}
}
}

return CHIP_NO_ERROR;
}

} // namespace Crypto
} // namespace chip
20 changes: 20 additions & 0 deletions src/crypto/PSAOperationalKeystore.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,29 @@

#include <crypto/CHIPCryptoPALPSA.h>
#include <crypto/OperationalKeystore.h>
#include <lib/core/CHIPPersistentStorageDelegate.h>

namespace chip {
namespace Crypto {

class PSAOperationalKeystore final : public OperationalKeystore
{
public:
/**
* @brief Initialize the PSA Operational Keystore and move all sensitive keys from storage to ITS secure storage.
*
* After moving all sensitive keys to ITS, the old one stored in the storage will be removed and not available anymore.
*
* @param storage Pointer to persistent storage delegate to use.
* @retval CHIP_NO_ERROR on success
* @retval CHIP_ERROR_INCORRECT_STATE if already initialized
*/
CHIP_ERROR Init(PersistentStorageDelegate * storage)
{
VerifyOrReturnError(storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
return MoveOpKeysFromKVSToITS(storage);
}

bool HasPendingOpKeypair() const override;
bool HasOpKeypairForFabric(FabricIndex fabricIndex) const override;
CHIP_ERROR NewOpKeypairForFabric(FabricIndex fabricIndex, MutableByteSpan & outCertificateSigningRequest) override;
Expand Down Expand Up @@ -53,13 +69,17 @@ class PSAOperationalKeystore final : public OperationalKeystore
bool Exists() const;
CHIP_ERROR Generate();
CHIP_ERROR Destroy();
CHIP_ERROR Deserialize(P256SerializedKeypair & input);
};

void ReleasePendingKeypair();

PersistentP256Keypair * mPendingKeypair = nullptr;
FabricIndex mPendingFabricIndex = kUndefinedFabricIndex;
bool mIsPendingKeypairActive = false;

private:
CHIP_ERROR MoveOpKeysFromKVSToITS(PersistentStorageDelegate * storage);
};

} // namespace Crypto
Expand Down

0 comments on commit 7739120

Please sign in to comment.