diff --git a/config/ameba/args.gni b/config/ameba/args.gni index a60eee6db2887c..0fa0fe461190ce 100755 --- a/config/ameba/args.gni +++ b/config/ameba/args.gni @@ -15,7 +15,9 @@ # Options from standalone-chip.mk that differ from configure defaults. These # options are used from examples/. +import("//build_overrides/chip.gni") import("//build_overrides/pigweed.gni") +import("${chip_root}/src/crypto/crypto.gni") chip_device_platform = "ameba" @@ -23,7 +25,10 @@ chip_project_config_include = "" chip_system_project_config_include = "" chip_ble_project_config_include = "" -mbedtls_target = "//mbedtls:mbedtls" +if (chip_crypto == "") { + mbedtls_target = "//mbedtls:mbedtls" +} + lwip_platform = "external" chip_build_tests = false diff --git a/examples/air-purifier-app/ameba/main/chipinterface.cpp b/examples/air-purifier-app/ameba/main/chipinterface.cpp index fd1c568afbf44f..0829bb041e4882 100644 --- a/examples/air-purifier-app/ameba/main/chipinterface.cpp +++ b/examples/air-purifier-app/ameba/main/chipinterface.cpp @@ -37,6 +37,9 @@ #include #include #include +#if CONFIG_ENABLE_AMEBA_CRYPTO +#include +#endif #include @@ -130,6 +133,12 @@ static void InitServer(intptr_t context) // Init ZCL Data Model and CHIP App Server static chip::CommonCaseDeviceServerInitParams initParams; (void) initParams.InitializeStaticResourcesBeforeServerInit(); +#if CONFIG_ENABLE_AMEBA_CRYPTO + ChipLogProgress(DeviceLayer, "platform crypto enabled!"); + static chip::AmebaPersistentStorageOperationalKeystore sAmebaPersistentStorageOpKeystore; + VerifyOrDie((sAmebaPersistentStorageOpKeystore.Init(initParams.persistentStorageDelegate)) == CHIP_NO_ERROR); + initParams.operationalKeystore = &sAmebaPersistentStorageOpKeystore; +#endif chip::Server::GetInstance().Init(initParams); gExampleDeviceInfoProvider.SetStorageDelegate(&Server::GetInstance().GetPersistentStorage()); chip::DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider); diff --git a/examples/all-clusters-app/ameba/main/chipinterface.cpp b/examples/all-clusters-app/ameba/main/chipinterface.cpp index aa7cc4afe75047..2a35d86eadc3a2 100644 --- a/examples/all-clusters-app/ameba/main/chipinterface.cpp +++ b/examples/all-clusters-app/ameba/main/chipinterface.cpp @@ -41,6 +41,9 @@ #include #include #include +#if CONFIG_ENABLE_AMEBA_CRYPTO +#include +#endif #include #include #include @@ -153,6 +156,13 @@ static void InitServer(intptr_t context) initParams.InitializeStaticResourcesBeforeServerInit(); +#if CONFIG_ENABLE_AMEBA_CRYPTO + ChipLogProgress(DeviceLayer, "platform crypto enabled!"); + static chip::AmebaPersistentStorageOperationalKeystore sAmebaPersistentStorageOpKeystore; + VerifyOrDie((sAmebaPersistentStorageOpKeystore.Init(initParams.persistentStorageDelegate)) == CHIP_NO_ERROR); + initParams.operationalKeystore = &sAmebaPersistentStorageOpKeystore; +#endif + chip::Server::GetInstance().Init(initParams); gExampleDeviceInfoProvider.SetStorageDelegate(&Server::GetInstance().GetPersistentStorage()); // TODO: Use our own DeviceInfoProvider diff --git a/examples/light-switch-app/ameba/main/chipinterface.cpp b/examples/light-switch-app/ameba/main/chipinterface.cpp index c141769ba7d483..35d11da6342ba5 100644 --- a/examples/light-switch-app/ameba/main/chipinterface.cpp +++ b/examples/light-switch-app/ameba/main/chipinterface.cpp @@ -34,6 +34,9 @@ #include #include #include +#if CONFIG_ENABLE_AMEBA_CRYPTO +#include +#endif #include #include #include @@ -100,6 +103,12 @@ static void InitServer(intptr_t context) // Init ZCL Data Model and CHIP App Server static chip::CommonCaseDeviceServerInitParams initParams; initParams.InitializeStaticResourcesBeforeServerInit(); +#if CONFIG_ENABLE_AMEBA_CRYPTO + ChipLogProgress(DeviceLayer, "platform crypto enabled!"); + static chip::AmebaPersistentStorageOperationalKeystore sAmebaPersistentStorageOpKeystore; + VerifyOrDie((sAmebaPersistentStorageOpKeystore.Init(initParams.persistentStorageDelegate)) == CHIP_NO_ERROR); + initParams.operationalKeystore = &sAmebaPersistentStorageOpKeystore; +#endif chip::Server::GetInstance().Init(initParams); gExampleDeviceInfoProvider.SetStorageDelegate(&Server::GetInstance().GetPersistentStorage()); chip::DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider); diff --git a/examples/lighting-app/ameba/main/chipinterface.cpp b/examples/lighting-app/ameba/main/chipinterface.cpp index 461b8bea6143f6..76459b728a545b 100644 --- a/examples/lighting-app/ameba/main/chipinterface.cpp +++ b/examples/lighting-app/ameba/main/chipinterface.cpp @@ -35,12 +35,14 @@ #include #include #include +#if CONFIG_ENABLE_AMEBA_CRYPTO +#include +#endif +#include #include #include #include -#include - #if CONFIG_ENABLE_PW_RPC #include "Rpc.h" #endif @@ -121,6 +123,12 @@ static void InitServer(intptr_t context) // Init ZCL Data Model and CHIP App Server static chip::CommonCaseDeviceServerInitParams initParams; (void) initParams.InitializeStaticResourcesBeforeServerInit(); +#if CONFIG_ENABLE_AMEBA_CRYPTO + ChipLogProgress(DeviceLayer, "platform crypto enabled!"); + static chip::AmebaPersistentStorageOperationalKeystore sAmebaPersistentStorageOpKeystore; + VerifyOrDie((sAmebaPersistentStorageOpKeystore.Init(initParams.persistentStorageDelegate)) == CHIP_NO_ERROR); + initParams.operationalKeystore = &sAmebaPersistentStorageOpKeystore; +#endif chip::Server::GetInstance().Init(initParams); gExampleDeviceInfoProvider.SetStorageDelegate(&Server::GetInstance().GetPersistentStorage()); chip::DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider); diff --git a/src/platform/Ameba/BUILD.gn b/src/platform/Ameba/BUILD.gn index ff05b1fdeb165a..811055bd90906d 100755 --- a/src/platform/Ameba/BUILD.gn +++ b/src/platform/Ameba/BUILD.gn @@ -14,8 +14,8 @@ import("//build_overrides/chip.gni") +import("${chip_root}/src/crypto/crypto.gni") import("${chip_root}/src/platform/device.gni") - assert(chip_device_platform == "ameba") static_library("Ameba") { @@ -71,4 +71,14 @@ static_library("Ameba") { "AmebaOTAImageProcessor.h", ] } + + if (chip_crypto == "platform") { + sources += [ + "${chip_root}/src/crypto/CHIPCryptoPALmbedTLS.cpp", + "${chip_root}/src/crypto/CHIPCryptoPALmbedTLS.h", + "${chip_root}/src/crypto/CHIPCryptoPALmbedTLSCert.cpp", + "crypto/AmebaPersistentStorageOperationalKeystore.cpp", + "crypto/AmebaPersistentStorageOperationalKeystore.h", + ] + } } diff --git a/src/platform/Ameba/FactoryDataDecoder.cpp b/src/platform/Ameba/FactoryDataDecoder.cpp old mode 100644 new mode 100755 index 4e3536e3de1455..250487342bf0d9 --- a/src/platform/Ameba/FactoryDataDecoder.cpp +++ b/src/platform/Ameba/FactoryDataDecoder.cpp @@ -41,5 +41,16 @@ CHIP_ERROR FactoryDataDecoder::DecodeFactoryData(uint8_t * buffer, FactoryData * return err; } +#if CONFIG_ENABLE_AMEBA_CRYPTO +CHIP_ERROR FactoryDataDecoder::GetSign(uint8_t * PublicKeyData, size_t PublicKeySize, const unsigned char * MessageData, + size_t MessageSize, unsigned char * Signature) +{ + int32_t error = matter_get_signature(PublicKeyData, PublicKeySize, MessageData, MessageSize, Signature); + CHIP_ERROR err = CHIP_NO_ERROR; + + return err; +} +#endif + } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/Ameba/FactoryDataDecoder.h b/src/platform/Ameba/FactoryDataDecoder.h old mode 100644 new mode 100755 index 623eb8e102d093..1aa295d6a4c5e8 --- a/src/platform/Ameba/FactoryDataDecoder.h +++ b/src/platform/Ameba/FactoryDataDecoder.h @@ -27,6 +27,10 @@ class FactoryDataDecoder public: CHIP_ERROR ReadFactoryData(uint8_t * buffer, uint16_t * pfactorydata_len); CHIP_ERROR DecodeFactoryData(uint8_t * buffer, FactoryData * fdata, uint16_t factorydata_len); +#if CONFIG_ENABLE_AMEBA_CRYPTO + CHIP_ERROR GetSign(uint8_t * PublicKeyData, size_t PublicKeySize, const unsigned char * MessageData, size_t MessageSize, + unsigned char * Signature); +#endif static FactoryDataDecoder & GetInstance() { static FactoryDataDecoder instance; diff --git a/src/platform/Ameba/FactoryDataProvider.cpp b/src/platform/Ameba/FactoryDataProvider.cpp index 71243a1b7cfe84..d41f85669014b8 100644 --- a/src/platform/Ameba/FactoryDataProvider.cpp +++ b/src/platform/Ameba/FactoryDataProvider.cpp @@ -16,7 +16,6 @@ */ #include "FactoryDataProvider.h" - #include "FactoryDataDecoder.h" #include #include @@ -254,6 +253,19 @@ CHIP_ERROR FactoryDataProvider::SignWithDeviceAttestationKey(const ByteSpan & me if (kReadFromFlash) { +#if CONFIG_ENABLE_AMEBA_CRYPTO + ReturnErrorCodeIf(!mFactoryData.dac.dac_cert.value, CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND); + // Extract public key from DAC cert. + ByteSpan dacCertSpan{ reinterpret_cast(mFactoryData.dac.dac_cert.value), mFactoryData.dac.dac_cert.len }; + chip::Crypto::P256PublicKey dacPublicKey; + + ReturnErrorOnFailure(chip::Crypto::ExtractPubkeyFromX509Cert(dacCertSpan, dacPublicKey)); + + CHIP_ERROR err = CHIP_NO_ERROR; + FactoryDataDecoder decoder = FactoryDataDecoder::GetInstance(); + err = decoder.GetSign(dacPublicKey.Bytes(), dacPublicKey.Length(), messageToSign.data(), messageToSign.size(), + signature.Bytes()); +#else ReturnErrorCodeIf(!mFactoryData.dac.dac_cert.value, CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND); ReturnErrorCodeIf(!mFactoryData.dac.dac_key.value, CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND); // Extract public key from DAC cert. @@ -261,18 +273,23 @@ CHIP_ERROR FactoryDataProvider::SignWithDeviceAttestationKey(const ByteSpan & me chip::Crypto::P256PublicKey dacPublicKey; ReturnErrorOnFailure(chip::Crypto::ExtractPubkeyFromX509Cert(dacCertSpan, dacPublicKey)); + ReturnErrorOnFailure( LoadKeypairFromRaw(ByteSpan(reinterpret_cast(mFactoryData.dac.dac_key.value), mFactoryData.dac.dac_key.len), ByteSpan(dacPublicKey.Bytes(), dacPublicKey.Length()), keypair)); +#endif } else { ReturnErrorOnFailure(LoadKeypairFromRaw(ByteSpan(kDacPrivateKey), ByteSpan(kDacPublicKey), keypair)); } - +#if CONFIG_ENABLE_AMEBA_CRYPTO + VerifyOrReturnError(signature.SetLength(chip::Crypto::kP256_ECDSA_Signature_Length_Raw) == CHIP_NO_ERROR, CHIP_ERROR_INTERNAL); + return CopySpanToMutableSpan(ByteSpan{ signature.ConstBytes(), signature.Length() }, outSignBuffer); +#else ReturnErrorOnFailure(keypair.ECDSA_sign_msg(messageToSign.data(), messageToSign.size(), signature)); - return CopySpanToMutableSpan(ByteSpan{ signature.ConstBytes(), signature.Length() }, outSignBuffer); +#endif } CHIP_ERROR FactoryDataProvider::GetSetupDiscriminator(uint16_t & setupDiscriminator) diff --git a/src/platform/Ameba/args.gni b/src/platform/Ameba/args.gni index dd6b1bcdb9ca8f..87c0571d362132 100755 --- a/src/platform/Ameba/args.gni +++ b/src/platform/Ameba/args.gni @@ -13,11 +13,15 @@ # limitations under the License. import("//build_overrides/chip.gni") +import("${chip_root}/src/crypto/crypto.gni") chip_device_platform = "ameba" lwip_platform = "external" -mbedtls_target = "//mbedtls:mbedtls" + +if (chip_crypto == "") { + mbedtls_target = "//mbedtls:mbedtls" +} chip_build_tests = false chip_inet_config_enable_tun_endpoint = false diff --git a/src/platform/Ameba/crypto/AmebaPersistentStorageOperationalKeystore.cpp b/src/platform/Ameba/crypto/AmebaPersistentStorageOperationalKeystore.cpp new file mode 100644 index 00000000000000..e33806418191d1 --- /dev/null +++ b/src/platform/Ameba/crypto/AmebaPersistentStorageOperationalKeystore.cpp @@ -0,0 +1,443 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +namespace chip { + +using namespace chip::Crypto; + +static inline mbedtls_ecp_keypair * to_keypair(P256KeypairContext * context) +{ + return SafePointerCast(context); +} + +static int CryptoRNG(void * ctxt, uint8_t * out_buffer, size_t out_length) +{ + return (chip::Crypto::DRBG_get_bytes(out_buffer, out_length) == CHIP_NO_ERROR) ? 0 : 1; +} + +CHIP_ERROR AmebaP256Keypair::Initialize(Crypto::ECPKeyTarget key_target) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + + Clear(); + + mbedtls_ecp_keypair * keypair = to_keypair(&mKeypair); + + VerifyOrExit(matter_get_publickey(Uint8::to_uchar(mPublicKey), mPublicKey.Length()) != 0, + error = CHIP_ERROR_INVALID_PUBLIC_KEY); + + keypair = nullptr; + mInitialized = true; + +exit: + if (keypair != nullptr) + { + keypair = nullptr; + } + + _log_mbedTLS_error(result); + return error; +} + +void AmebaP256Keypair::Clear() +{ + if (mInitialized) + { + mbedtls_ecp_keypair * keypair = to_keypair(&mKeypair); + mbedtls_ecp_keypair_free(keypair); + mInitialized = false; + } +} + +AmebaP256Keypair::~AmebaP256Keypair() +{ + Clear(); +} + +namespace { + +// Tags for our operational keypair storage. +constexpr TLV::Tag kOpKeyVersionTag = TLV::ContextTag(0); +constexpr TLV::Tag kOpKeyDataTag = TLV::ContextTag(1); + +// If this version grows beyond UINT16_MAX, adjust OpKeypairTLVMaxSize +// accordingly. +constexpr uint16_t kOpKeyVersion = 1; + +constexpr size_t OpKeyTLVMaxSize() +{ + // Version and serialized key + return TLV::EstimateStructOverhead(sizeof(uint16_t), Crypto::P256SerializedKeypair::Capacity()); +} + +/** WARNING: This can leave the operational key on the stack somewhere, since many of the platform + * APIs use stack buffers and do not sanitize! This implementation is for example purposes + * only of the API and it is recommended to avoid directly accessing raw private key bits + * in storage. + */ +CHIP_ERROR StoreOperationalKey(FabricIndex fabricIndex, PersistentStorageDelegate * storage, P256Keypair * keypair) +{ + VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (storage != nullptr) && (keypair != nullptr), + CHIP_ERROR_INVALID_ARGUMENT); + + // Use a SensitiveDataBuffer to get RAII secret data clearing on scope exit. + Crypto::SensitiveDataBuffer buf; + TLV::TLVWriter writer; + + writer.Init(buf.Bytes(), buf.Capacity()); + + TLV::TLVType outerType; + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerType)); + + ReturnErrorOnFailure(writer.Put(kOpKeyVersionTag, kOpKeyVersion)); + + { + // P256SerializedKeypair has RAII secret clearing + Crypto::P256SerializedKeypair serializedOpKey; + size_t len = serializedOpKey.Length() == 0 ? serializedOpKey.Capacity() : serializedOpKey.Length(); + + int result = matter_serialize(serializedOpKey.Bytes(), len); + if (result != 0) + { + return CHIP_ERROR_INTERNAL; + } + ReturnErrorOnFailure(writer.Put(kOpKeyDataTag, ByteSpan(serializedOpKey.Bytes(), len))); + } + + ReturnErrorOnFailure(writer.EndContainer(outerType)); + + const auto opKeyLength = writer.GetLengthWritten(); + VerifyOrReturnError(CanCastTo(opKeyLength), CHIP_ERROR_BUFFER_TOO_SMALL); + ReturnErrorOnFailure(storage->SyncSetKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName(), buf.ConstBytes(), + static_cast(opKeyLength))); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR ExportStoredOpKey(FabricIndex fabricIndex, PersistentStorageDelegate * storage, + Crypto::P256SerializedKeypair & serializedOpKey) +{ + VerifyOrReturnError(storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); + + // Use a SensitiveDataBuffer to get RAII secret data clearing on scope exit. + Crypto::SensitiveDataBuffer buf; + + // Load up the operational key structure from storage + uint16_t size = static_cast(buf.Capacity()); + ReturnErrorOnFailure( + storage->SyncGetKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName(), buf.Bytes(), size)); + + buf.SetLength(static_cast(size)); + + // Read-out the operational key TLV entry. + 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)); + + // Unfortunately, we have to copy the data into a P256SerializedKeypair. + VerifyOrReturnError(keyData.size() <= serializedOpKey.Capacity(), CHIP_ERROR_BUFFER_TOO_SMALL); + + ReturnErrorOnFailure(reader.ExitContainer(containerType)); + + memcpy(serializedOpKey.Bytes(), keyData.data(), keyData.size()); + serializedOpKey.SetLength(keyData.size()); + } + + return CHIP_NO_ERROR; +} + +/** WARNING: This can leave the operational key on the stack somewhere, since many of the platform + * APIs use stack buffers and do not sanitize! This implementation is for example purposes + * only of the API and it is recommended to avoid directly accessing raw private key bits + * in storage. + */ +CHIP_ERROR SignWithStoredOpKey(FabricIndex fabricIndex, PersistentStorageDelegate * storage, const ByteSpan & message, + P256ECDSASignature & outSignature) +{ + VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (storage != nullptr), CHIP_ERROR_INVALID_ARGUMENT); + + // Use RAII scoping for the transient keypair, to make sure it doesn't get leaked on any error paths. + // Key is put in heap since signature is a costly stack operation and P256Keypair is + // a costly class depending on the backend. + auto transientOperationalKeypair = Platform::MakeUnique(); + if (!transientOperationalKeypair) + { + return CHIP_ERROR_NO_MEMORY; + } + + // Scope 1: Load up the keypair data from storage + P256SerializedKeypair serializedOpKey; + CHIP_ERROR err = ExportStoredOpKey(fabricIndex, storage, serializedOpKey); + if (CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND == err) + { + return CHIP_ERROR_INVALID_FABRIC_INDEX; + } + + // Load-up key material + // WARNING: This makes use of the raw key bits + int result = matter_deserialize(serializedOpKey.Bytes(), serializedOpKey.Length()); + if (result != 0) + { + return CHIP_ERROR_INTERNAL; + } + + // Scope 2: Sign message with the keypair + result = matter_ecdsa_sign_msg(message.data(), message.size(), outSignature.Bytes()); + if (result != 0) + { + return CHIP_ERROR_INTERNAL; + } + + VerifyOrReturnError(outSignature.SetLength(kP256_ECDSA_Signature_Length_Raw) == CHIP_NO_ERROR, err = CHIP_ERROR_INTERNAL); + return err; +} + +} // namespace + +bool AmebaPersistentStorageOperationalKeystore::HasOpKeypairForFabric(FabricIndex fabricIndex) const +{ + VerifyOrReturnError(mStorage != nullptr, 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; + } + + // TODO(#16958): need to actually read the key to know if it's there due to platforms not + // properly enforcing CHIP_ERROR_BUFFER_TOO_SMALL behavior needed by + // PersistentStorageDelegate. Very unfortunate, needs fixing ASAP. + + // Use a SensitiveDataBuffer to get RAII secret data clearing on scope exit. + Crypto::SensitiveDataBuffer buf; + + uint16_t keySize = static_cast(buf.Capacity()); + CHIP_ERROR err = + mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName(), buf.Bytes(), keySize); + + return (err == CHIP_NO_ERROR); +} + +CHIP_ERROR AmebaPersistentStorageOperationalKeystore::NewOpKeypairForFabric(FabricIndex fabricIndex, + MutableByteSpan & outCertificateSigningRequest) +{ + VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE); + 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::kMIN_CSR_Buffer_Size, CHIP_ERROR_BUFFER_TOO_SMALL); + + // Replace previous pending keypair, if any was previously allocated + ResetPendingKey(); + + mPendingKeypair = Platform::New(); + VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_NO_MEMORY); + + size_t TempLength = outCertificateSigningRequest.size(); + size_t csrLength = matter_gen_new_csr(outCertificateSigningRequest.data(), TempLength); + + if (csrLength <= 0) + { + ResetPendingKey(); + return CHIP_ERROR_INTERNAL; + } + + outCertificateSigningRequest.reduce_size(csrLength); + mPendingFabricIndex = fabricIndex; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR AmebaPersistentStorageOperationalKeystore::ActivateOpKeypairForFabric(FabricIndex fabricIndex, + const Crypto::P256PublicKey & nocPublicKey) +{ + VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE); + 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 + mPendingKeypair = Platform::New(); + VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_NO_MEMORY); + mPendingKeypair->Initialize(Crypto::ECPKeyTarget::ECDSA); + VerifyOrReturnError(mPendingKeypair->Pubkey().Matches(nocPublicKey), CHIP_ERROR_INVALID_PUBLIC_KEY); + + mIsPendingKeypairActive = true; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR AmebaPersistentStorageOperationalKeystore::CommitOpKeypairForFabric(FabricIndex fabricIndex) +{ + VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE); + 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); + + // Try to store persistent key. On failure, leave everything pending as-is + CHIP_ERROR err = StoreOperationalKey(fabricIndex, mStorage, mPendingKeypair); + ReturnErrorOnFailure(err); + + // If we got here, we succeeded and can reset the pending key: next `SignWithOpKeypair` will use the stored key. + ResetPendingKey(); + return CHIP_NO_ERROR; +} + +CHIP_ERROR AmebaPersistentStorageOperationalKeystore::ExportOpKeypairForFabric(FabricIndex fabricIndex, + Crypto::P256SerializedKeypair & outKeypair) +{ + VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE); + return ExportStoredOpKey(fabricIndex, mStorage, outKeypair); +} + +CHIP_ERROR AmebaPersistentStorageOperationalKeystore::RemoveOpKeypairForFabric(FabricIndex fabricIndex) +{ + VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); + + // Remove pending state if matching + if ((mPendingKeypair != nullptr) && (fabricIndex == mPendingFabricIndex)) + { + RevertPendingKeypair(); + } + + CHIP_ERROR err = mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName()); + if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) + { + err = CHIP_ERROR_INVALID_FABRIC_INDEX; + } + + return err; +} + +void AmebaPersistentStorageOperationalKeystore::RevertPendingKeypair() +{ + VerifyOrReturn(mStorage != nullptr); + + // Just reset the pending key, we never stored anything + ResetPendingKey(); +} + +CHIP_ERROR AmebaPersistentStorageOperationalKeystore::SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message, + Crypto::P256ECDSASignature & outSignature) const +{ + VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); + + if (mIsPendingKeypairActive && (fabricIndex == mPendingFabricIndex)) + { + VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_INTERNAL); + // We have an override key: sign with it! + CHIP_ERROR err = CHIP_NO_ERROR; + if (matter_ecdsa_sign_msg(message.data(), message.size(), outSignature.Bytes()) != 0) + { + return CHIP_ERROR_INTERNAL; + } + VerifyOrReturnError(outSignature.SetLength(kP256_ECDSA_Signature_Length_Raw) == CHIP_NO_ERROR, err = CHIP_ERROR_INTERNAL); + return err; + } + + return SignWithStoredOpKey(fabricIndex, mStorage, message, outSignature); +} + +Crypto::P256Keypair * AmebaPersistentStorageOperationalKeystore::AllocateEphemeralKeypairForCASE() +{ + // DO NOT CUT AND PASTE without considering the ReleaseEphemeralKeypair(). + // If allocating a derived class, then `ReleaseEphemeralKeypair` MUST + // de-allocate the derived class after up-casting the base class pointer. + return Platform::New(); +} + +void AmebaPersistentStorageOperationalKeystore::ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair) +{ + // DO NOT CUT AND PASTE without considering the AllocateEphemeralKeypairForCASE(). + // This must delete the same concrete class as allocated in `AllocateEphemeralKeypairForCASE` + Platform::Delete(keypair); +} + +CHIP_ERROR AmebaPersistentStorageOperationalKeystore::MigrateOpKeypairForFabric(FabricIndex fabricIndex, + OperationalKeystore & operationalKeystore) const +{ + VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); + + P256SerializedKeypair serializedKeypair; + + // Do not allow overwriting the existing key and just remove it from the previous Operational Keystore if needed. + if (!HasOpKeypairForFabric(fabricIndex)) + { + ReturnErrorOnFailure(operationalKeystore.ExportOpKeypairForFabric(fabricIndex, serializedKeypair)); + + auto operationalKeypair = Platform::MakeUnique(); + if (!operationalKeypair) + { + return CHIP_ERROR_NO_MEMORY; + } + + ReturnErrorOnFailure(operationalKeypair->Deserialize(serializedKeypair)); + ReturnErrorOnFailure(StoreOperationalKey(fabricIndex, mStorage, operationalKeypair.get())); + + ReturnErrorOnFailure(operationalKeystore.RemoveOpKeypairForFabric(fabricIndex)); + } + else if (operationalKeystore.HasOpKeypairForFabric(fabricIndex)) + { + ReturnErrorOnFailure(operationalKeystore.RemoveOpKeypairForFabric(fabricIndex)); + } + + return CHIP_NO_ERROR; +} + +} // namespace chip diff --git a/src/platform/Ameba/crypto/AmebaPersistentStorageOperationalKeystore.h b/src/platform/Ameba/crypto/AmebaPersistentStorageOperationalKeystore.h new file mode 100644 index 00000000000000..425f654c68ae01 --- /dev/null +++ b/src/platform/Ameba/crypto/AmebaPersistentStorageOperationalKeystore.h @@ -0,0 +1,144 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace chip { + +class AmebaP256Keypair : public Crypto::P256Keypair +{ +public: + AmebaP256Keypair() {} + ~AmebaP256Keypair() override; + + /** + * @brief Initialize the keypair. + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + CHIP_ERROR Initialize(Crypto::ECPKeyTarget key_target) override; + + /** @brief Return public key for the keypair. + **/ + const Crypto::P256PublicKey & Pubkey() const override { return mPublicKey; } + + /** Release resources associated with this key pair */ + void Clear(); + +protected: + Crypto::P256PublicKey mPublicKey; + mutable Crypto::P256KeypairContext mKeypair; + bool mInitialized = false; +}; + +/** + * @brief OperationalKeystore implementation making use of PersistentStorageDelegate + * to load/store keypairs. This is the legacy behavior of `FabricTable` prior + * to refactors to use `OperationalKeystore` and exists as a baseline example + * of how to use the interface. + * + * WARNING: Ensure that any implementation that uses this one as a starting point + * DOES NOT have the raw key material (in usable form) passed up/down to + * direct storage APIs that may make copies on heap/stack without sanitization. + */ +class AmebaPersistentStorageOperationalKeystore : public Crypto::OperationalKeystore +{ +public: + AmebaPersistentStorageOperationalKeystore() = default; + virtual ~AmebaPersistentStorageOperationalKeystore() { Finish(); } + + // Non-copyable + AmebaPersistentStorageOperationalKeystore(AmebaPersistentStorageOperationalKeystore const &) = delete; + void operator=(AmebaPersistentStorageOperationalKeystore const &) = delete; + + /** + * @brief Initialize the Operational Keystore to map to a given storage delegate. + * + * @param storage Pointer to persistent storage delegate to use. Must outlive this instance. + * @retval CHIP_NO_ERROR on success + * @retval CHIP_ERROR_INCORRECT_STATE if already initialized + */ + CHIP_ERROR Init(PersistentStorageDelegate * storage) + { + VerifyOrReturnError(mStorage == nullptr, CHIP_ERROR_INCORRECT_STATE); + mPendingFabricIndex = kUndefinedFabricIndex; + mIsExternallyOwnedKeypair = false; + mStorage = storage; + mPendingKeypair = nullptr; + mIsPendingKeypairActive = false; + return CHIP_NO_ERROR; + } + + /** + * @brief Finalize the keystore, so that subsequent operations fail + */ + void Finish() + { + VerifyOrReturn(mStorage != nullptr); + + ResetPendingKey(); + mStorage = nullptr; + } + + bool HasPendingOpKeypair() const override { return (mPendingKeypair != nullptr); } + + bool HasOpKeypairForFabric(FabricIndex fabricIndex) const override; + CHIP_ERROR NewOpKeypairForFabric(FabricIndex fabricIndex, MutableByteSpan & outCertificateSigningRequest) override; + CHIP_ERROR ActivateOpKeypairForFabric(FabricIndex fabricIndex, const Crypto::P256PublicKey & nocPublicKey) override; + CHIP_ERROR CommitOpKeypairForFabric(FabricIndex fabricIndex) override; + CHIP_ERROR ExportOpKeypairForFabric(FabricIndex fabricIndex, Crypto::P256SerializedKeypair & outKeypair) override; + CHIP_ERROR RemoveOpKeypairForFabric(FabricIndex fabricIndex) override; + void RevertPendingKeypair() override; + CHIP_ERROR SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message, + Crypto::P256ECDSASignature & outSignature) const override; + Crypto::P256Keypair * AllocateEphemeralKeypairForCASE() override; + void ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair) override; + CHIP_ERROR MigrateOpKeypairForFabric(FabricIndex fabricIndex, OperationalKeystore & operationalKeystore) const override; + +protected: + void ResetPendingKey() + { + if (!mIsExternallyOwnedKeypair && (mPendingKeypair != nullptr)) + { + Platform::Delete(mPendingKeypair); + } + mPendingKeypair = nullptr; + mIsExternallyOwnedKeypair = false; + mIsPendingKeypairActive = false; + mPendingFabricIndex = kUndefinedFabricIndex; + } + + PersistentStorageDelegate * mStorage = nullptr; + + // This pending fabric index is `kUndefinedFabricIndex` if there isn't a pending keypair override for a given fabric. + FabricIndex mPendingFabricIndex = kUndefinedFabricIndex; + Crypto::P256Keypair * mPendingKeypair = nullptr; + bool mIsPendingKeypairActive = false; + + // If overridding NewOpKeypairForFabric method in a subclass, set this to true in + // `NewOpKeypairForFabric` if the mPendingKeypair should not be deleted when no longer in use. + bool mIsExternallyOwnedKeypair = false; +}; + +} // namespace chip