From bb52fc2b03b2f6a26bbe985c0020c5512ed7efec Mon Sep 17 00:00:00 2001 From: Arkadiusz Balys Date: Mon, 8 Jan 2024 09:42:41 +0100 Subject: [PATCH] [crypto] Migrate Operational Keys from mbedTLS to PSA ITS 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. --- src/app/server/Server.cpp | 4 ++ src/app/server/Server.h | 13 ++++ src/crypto/PSAOperationalKeystore.cpp | 94 +++++++++++++++++++++++++++ src/crypto/PSAOperationalKeystore.h | 20 ++++++ 4 files changed, 131 insertions(+) diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index eb9b5835dd23d5..2d3b55b3c7449a 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -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; diff --git a/src/app/server/Server.h b/src/app/server/Server.h index e6263ccddc6431..c66c6789c5fe67 100644 --- a/src/app/server/Server.h +++ b/src/app/server/Server.h @@ -41,7 +41,11 @@ #include #include #include +#if CHIP_CRYPTO_PSA +#include +#else #include +#endif #include #include #include @@ -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 @@ -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; diff --git a/src/crypto/PSAOperationalKeystore.cpp b/src/crypto/PSAOperationalKeystore.cpp index 980994696e3047..73bb0589c3768e 100644 --- a/src/crypto/PSAOperationalKeystore.cpp +++ b/src/crypto/PSAOperationalKeystore.cpp @@ -17,13 +17,22 @@ #include "PSAOperationalKeystore.h" +#include #include +#include #include 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); @@ -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); @@ -209,5 +252,56 @@ void PSAOperationalKeystore::ReleasePendingKeypair() mIsPendingKeypairActive = false; } +CHIP_ERROR PSAOperationalKeystore::MoveOpKeysFromKVSToITS(PersistentStorageDelegate * storage) +{ + Crypto::SensitiveDataBuffer buf; + uint16_t keySize = static_cast(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(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 diff --git a/src/crypto/PSAOperationalKeystore.h b/src/crypto/PSAOperationalKeystore.h index 89c3edc7fe7607..6bd5539a43e416 100644 --- a/src/crypto/PSAOperationalKeystore.h +++ b/src/crypto/PSAOperationalKeystore.h @@ -19,6 +19,7 @@ #include #include +#include namespace chip { namespace Crypto { @@ -26,6 +27,21 @@ 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; @@ -53,6 +69,7 @@ class PSAOperationalKeystore final : public OperationalKeystore bool Exists() const; CHIP_ERROR Generate(); CHIP_ERROR Destroy(); + CHIP_ERROR Deserialize(P256SerializedKeypair & input); }; void ReleasePendingKeypair(); @@ -60,6 +77,9 @@ class PSAOperationalKeystore final : public OperationalKeystore PersistentP256Keypair * mPendingKeypair = nullptr; FabricIndex mPendingFabricIndex = kUndefinedFabricIndex; bool mIsPendingKeypairActive = false; + +private: + CHIP_ERROR MoveOpKeysFromKVSToITS(PersistentStorageDelegate * storage); }; } // namespace Crypto