From 2925dfafd119160844863aee2d16ab396f703776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20Kr=C3=B3lik?= <66667989+Damian-Nordic@users.noreply.github.com> Date: Fri, 17 Feb 2023 13:56:47 +0100 Subject: [PATCH] [crypto] Introduce AES key abstraction (#23792) * [crypto] Introduce AES key abstraction Add Aes128KeyHandle class that represents a platform-specific AES key, either in the form of raw key material or key reference. Also add SessionKeystore interface for managing the lifetime of symmetric keys. This is needed to enable more secure key management for crypto backends that support invoking crypto operations using key references, such as PSA crypto API. Thanks to this class the session can only hold key references while having the actual keys stored securely. Group key management is still to be improved though. * Update PSA implementation * Code review p.2 * Code review p.3 1. Align AES key handle as uintptr_t instead of size_t. 2. Replace temporary implementation of PSA key derivation with a proper one that does not reveal derived keys. 3. Do not use DefaultSessionKeystore in controllers. * Fix build --- .../chip-tool/commands/common/CHIPCommand.cpp | 2 + .../chip-tool/commands/common/CHIPCommand.h | 2 + examples/platform/linux/AppMain.cpp | 1 - examples/platform/linux/CommissionerMain.cpp | 4 + examples/platform/nxp/se05x/linux/AppMain.cpp | 8 +- src/app/server/Server.cpp | 18 +- src/app/server/Server.h | 44 ++-- src/app/tests/TestWriteInteraction.cpp | 3 + .../tests/integration/chip_im_initiator.cpp | 2 +- .../tests/integration/chip_im_responder.cpp | 2 +- src/app/tests/integration/common.cpp | 1 + src/app/tests/integration/common.h | 2 + .../CHIPDeviceControllerFactory.cpp | 5 +- src/controller/CHIPDeviceControllerFactory.h | 7 +- .../CHIPDeviceControllerSystemState.h | 9 +- .../java/AndroidDeviceControllerWrapper.cpp | 2 + .../java/AndroidDeviceControllerWrapper.h | 3 + .../ChipDeviceController-ScriptBinding.cpp | 4 + .../python/chip/internal/CommissionerImpl.cpp | 4 + src/credentials/GroupDataProviderImpl.cpp | 25 +- src/credentials/GroupDataProviderImpl.h | 41 +++- .../tests/TestGroupDataProvider.cpp | 3 + src/crypto/BUILD.gn | 18 +- src/crypto/CHIPCryptoPAL.cpp | 4 +- src/crypto/CHIPCryptoPAL.h | 65 ++++- src/crypto/CHIPCryptoPALOpenSSL.cpp | 19 +- src/crypto/CHIPCryptoPALPSA.cpp | 230 ++++++++++++------ src/crypto/CHIPCryptoPALPSA.h | 110 +++++++-- src/crypto/CHIPCryptoPALmbedTLS.cpp | 30 +-- src/crypto/DefaultSessionKeystore.h | 44 ++++ src/crypto/PSAOperationalKeystore.cpp | 8 +- src/crypto/PSAOperationalKeystore.h | 2 +- src/crypto/PSASessionKeystore.cpp | 108 ++++++++ src/crypto/PSASessionKeystore.h | 37 +++ src/crypto/RawKeySessionKeystore.cpp | 70 ++++++ src/crypto/RawKeySessionKeystore.h | 37 +++ src/crypto/SessionKeystore.h | 101 ++++++++ src/crypto/tests/BUILD.gn | 5 +- src/crypto/tests/CHIPCryptoPALTest.cpp | 112 ++++----- src/crypto/tests/TestSessionKeystore.cpp | 227 +++++++++++++++++ .../CHIP/MTRDeviceControllerFactory.mm | 15 ++ src/messaging/tests/MessagingContext.cpp | 3 +- src/messaging/tests/MessagingContext.h | 3 + src/messaging/tests/echo/common.cpp | 1 + src/messaging/tests/echo/common.h | 2 + src/messaging/tests/echo/echo_requester.cpp | 4 +- src/messaging/tests/echo/echo_responder.cpp | 4 +- .../common/crypto/CHIPCryptoPALTinyCrypt.cpp | 30 +-- .../crypto/CHIPCryptoPALNXPUltrafastP256.cpp | 30 +-- .../silabs/efr32/CHIPCryptoPALPsaEfr32.cpp | 17 +- src/protocols/secure_channel/CASESession.cpp | 117 ++++----- src/protocols/secure_channel/CASESession.h | 3 +- src/protocols/secure_channel/PASESession.cpp | 3 +- .../secure_channel/tests/TestCASESession.cpp | 7 +- .../secure_channel/tests/TestPASESession.cpp | 2 +- src/transport/CryptoContext.cpp | 71 ++---- src/transport/CryptoContext.h | 42 ++-- src/transport/SessionManager.cpp | 8 +- src/transport/SessionManager.h | 16 +- src/transport/tests/TestSecureSession.cpp | 26 +- src/transport/tests/TestSessionManager.cpp | 37 +-- .../tests/TestSessionManagerDispatch.cpp | 6 +- 62 files changed, 1351 insertions(+), 515 deletions(-) create mode 100644 src/crypto/DefaultSessionKeystore.h create mode 100644 src/crypto/PSASessionKeystore.cpp create mode 100644 src/crypto/PSASessionKeystore.h create mode 100644 src/crypto/RawKeySessionKeystore.cpp create mode 100644 src/crypto/RawKeySessionKeystore.h create mode 100644 src/crypto/SessionKeystore.h create mode 100644 src/crypto/tests/TestSessionKeystore.cpp diff --git a/examples/chip-tool/commands/common/CHIPCommand.cpp b/examples/chip-tool/commands/common/CHIPCommand.cpp index b445ab88ce434c..c97f2c17090b64 100644 --- a/examples/chip-tool/commands/common/CHIPCommand.cpp +++ b/examples/chip-tool/commands/common/CHIPCommand.cpp @@ -103,6 +103,7 @@ CHIP_ERROR CHIPCommand::MaybeSetUpStack() factoryInitParams.operationalKeystore = &mOperationalKeystore; factoryInitParams.opCertStore = &mOpCertStore; factoryInitParams.enableServerInteractions = NeedsOperationalAdvertising(); + factoryInitParams.sessionKeystore = &mSessionKeystore; // Init group data provider that will be used for all group keys and IPKs for the // chip-tool-configured fabrics. This is OK to do once since the fabric tables @@ -110,6 +111,7 @@ CHIP_ERROR CHIPCommand::MaybeSetUpStack() // Different commissioner implementations may want to use alternate implementations // of GroupDataProvider for injection through factoryInitParams. sGroupDataProvider.SetStorageDelegate(&mDefaultStorage); + sGroupDataProvider.SetSessionKeystore(factoryInitParams.sessionKeystore); ReturnLogErrorOnFailure(sGroupDataProvider.Init()); chip::Credentials::SetGroupDataProvider(&sGroupDataProvider); factoryInitParams.groupDataProvider = &sGroupDataProvider; diff --git a/examples/chip-tool/commands/common/CHIPCommand.h b/examples/chip-tool/commands/common/CHIPCommand.h index ee46ada917b944..cbe09472f3ea8d 100644 --- a/examples/chip-tool/commands/common/CHIPCommand.h +++ b/examples/chip-tool/commands/common/CHIPCommand.h @@ -29,6 +29,7 @@ #include #include #include +#include #pragma once @@ -139,6 +140,7 @@ class CHIPCommand : public Command #endif // CONFIG_USE_LOCAL_STORAGE chip::PersistentStorageOperationalKeystore mOperationalKeystore; chip::Credentials::PersistentStorageOpCertStore mOpCertStore; + chip::Crypto::RawKeySessionKeystore mSessionKeystore; static chip::Credentials::GroupDataProviderImpl sGroupDataProvider; CredentialIssuerCommands * mCredIssuerCmds; diff --git a/examples/platform/linux/AppMain.cpp b/examples/platform/linux/AppMain.cpp index f5bcce727d6489..1ad6c3212b84cc 100644 --- a/examples/platform/linux/AppMain.cpp +++ b/examples/platform/linux/AppMain.cpp @@ -28,7 +28,6 @@ #include #include -#include #include #include diff --git a/examples/platform/linux/CommissionerMain.cpp b/examples/platform/linux/CommissionerMain.cpp index fca5222acce4a7..f59ff10325c156 100644 --- a/examples/platform/linux/CommissionerMain.cpp +++ b/examples/platform/linux/CommissionerMain.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -122,6 +123,7 @@ MyServerStorageDelegate gServerStorage; ExampleOperationalCredentialsIssuer gOpCredsIssuer; NodeId gLocalId = kMaxOperationalNodeId; Credentials::GroupDataProviderImpl gGroupDataProvider; +Crypto::RawKeySessionKeystore gSessionKeystore; CHIP_ERROR InitCommissioner(uint16_t commissionerPort, uint16_t udcListenPort, FabricId fabricId) { @@ -132,8 +134,10 @@ CHIP_ERROR InitCommissioner(uint16_t commissionerPort, uint16_t udcListenPort, F factoryParams.listenPort = commissionerPort; factoryParams.fabricIndependentStorage = &gServerStorage; factoryParams.fabricTable = &Server::GetInstance().GetFabricTable(); + factoryParams.sessionKeystore = &gSessionKeystore; gGroupDataProvider.SetStorageDelegate(&gServerStorage); + gGroupDataProvider.SetSessionKeystore(factoryParams.sessionKeystore); ReturnErrorOnFailure(gGroupDataProvider.Init()); factoryParams.groupDataProvider = &gGroupDataProvider; diff --git a/examples/platform/nxp/se05x/linux/AppMain.cpp b/examples/platform/nxp/se05x/linux/AppMain.cpp index 6129298cbf3853..9d83e354cce466 100644 --- a/examples/platform/nxp/se05x/linux/AppMain.cpp +++ b/examples/platform/nxp/se05x/linux/AppMain.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -292,13 +293,14 @@ int ChipLinuxAppInit(int argc, char * const argv[], OptionSet * customOptions) struct CommonCaseDeviceServerInitParams_Se05x : public CommonCaseDeviceServerInitParams { - CHIP_ERROR InitializeStaticResourcesBeforeServerInit() override + CHIP_ERROR InitializeStaticResourcesBeforeServerInit() { static chip::KvsPersistentStorageDelegate sKvsPersistenStorageDelegate; static chip::PersistentStorageOperationalKeystoreHSM sPersistentStorageOperationalKeystore; static chip::Credentials::PersistentStorageOpCertStore sPersistentStorageOpCertStore; static chip::Credentials::GroupDataProviderImpl sGroupDataProvider; static IgnoreCertificateValidityPolicy sDefaultCertValidityPolicy; + static chip::Crypto::DefaultSessionKeystore sSessionKeystore; #if CHIP_CONFIG_ENABLE_SESSION_RESUMPTION static chip::SimpleSessionResumptionStorage sSessionResumptionStorage; @@ -333,8 +335,12 @@ struct CommonCaseDeviceServerInitParams_Se05x : public CommonCaseDeviceServerIni this->opCertStore = &sPersistentStorageOpCertStore; } + // Session Keystore injection + this->sessionKeystore = &sSessionKeystore; + // Group Data provider injection sGroupDataProvider.SetStorageDelegate(this->persistentStorageDelegate); + sGroupDataProvider.SetSessionKeystore(this->sessionKeystore); ReturnErrorOnFailure(sGroupDataProvider.Init()); this->groupDataProvider = &sGroupDataProvider; diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index 56c02358b0ec97..fa11ff0a71b044 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -123,6 +123,7 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams) VerifyOrExit(initParams.accessDelegate != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(initParams.aclStorage != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(initParams.groupDataProvider != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(initParams.sessionKeystore != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(initParams.operationalKeystore != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(initParams.opCertStore != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); @@ -205,7 +206,8 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams) #endif SuccessOrExit(err); - err = mSessions.Init(&DeviceLayer::SystemLayer(), &mTransports, &mMessageCounterManager, mDeviceStorage, &GetFabricTable()); + err = mSessions.Init(&DeviceLayer::SystemLayer(), &mTransports, &mMessageCounterManager, mDeviceStorage, &GetFabricTable(), + *initParams.sessionKeystore); SuccessOrExit(err); err = mFabricDelegate.Init(this); @@ -538,4 +540,18 @@ void Server::ResumeSubscriptions() } #endif +KvsPersistentStorageDelegate CommonCaseDeviceServerInitParams::sKvsPersistenStorageDelegate; +PersistentStorageOperationalKeystore CommonCaseDeviceServerInitParams::sPersistentStorageOperationalKeystore; +Credentials::PersistentStorageOpCertStore CommonCaseDeviceServerInitParams::sPersistentStorageOpCertStore; +Credentials::GroupDataProviderImpl CommonCaseDeviceServerInitParams::sGroupDataProvider; +IgnoreCertificateValidityPolicy CommonCaseDeviceServerInitParams::sDefaultCertValidityPolicy; +#if CHIP_CONFIG_ENABLE_SESSION_RESUMPTION +SimpleSessionResumptionStorage CommonCaseDeviceServerInitParams::sSessionResumptionStorage; +#endif +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS +app::SimpleSubscriptionResumptionStorage CommonCaseDeviceServerInitParams::sSubscriptionResumptionStorage; +#endif +app::DefaultAclStorage CommonCaseDeviceServerInitParams::sAclStorage; +Crypto::DefaultSessionKeystore CommonCaseDeviceServerInitParams::sSessionKeystore; + } // namespace chip diff --git a/src/app/server/Server.h b/src/app/server/Server.h index 638c918cb46ca1..e1a8a6cd9847a5 100644 --- a/src/app/server/Server.h +++ b/src/app/server/Server.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -85,8 +86,7 @@ using ServerTransportMgr = chip::TransportMgropCertStore = &sPersistentStorageOpCertStore; } + // Session Keystore injection + this->sessionKeystore = &sSessionKeystore; + // Group Data provider injection sGroupDataProvider.SetStorageDelegate(this->persistentStorageDelegate); + sGroupDataProvider.SetSessionKeystore(this->sessionKeystore); ReturnErrorOnFailure(sGroupDataProvider.Init()); this->groupDataProvider = &sGroupDataProvider; @@ -291,6 +283,21 @@ struct CommonCaseDeviceServerInitParams : public ServerInitParams return CHIP_NO_ERROR; } + +private: + static KvsPersistentStorageDelegate sKvsPersistenStorageDelegate; + static PersistentStorageOperationalKeystore sPersistentStorageOperationalKeystore; + static Credentials::PersistentStorageOpCertStore sPersistentStorageOpCertStore; + static Credentials::GroupDataProviderImpl sGroupDataProvider; + static IgnoreCertificateValidityPolicy sDefaultCertValidityPolicy; +#if CHIP_CONFIG_ENABLE_SESSION_RESUMPTION + static SimpleSessionResumptionStorage sSessionResumptionStorage; +#endif +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + static app::SimpleSubscriptionResumptionStorage sSubscriptionResumptionStorage; +#endif + static app::DefaultAclStorage sAclStorage; + static Crypto::DefaultSessionKeystore sSessionKeystore; }; /** @@ -342,6 +349,8 @@ class Server Credentials::GroupDataProvider * GetGroupDataProvider() { return mGroupsProvider; } + Crypto::SessionKeystore * GetSessionKeystore() const { return mSessionKeystore; } + #if CONFIG_NETWORK_LAYER_BLE Ble::BleLayer * GetBleLayerObject() { return mBleLayer; } #endif @@ -556,6 +565,7 @@ class Server app::SubscriptionResumptionStorage * mSubscriptionResumptionStorage; Credentials::CertificateValidityPolicy * mCertificateValidityPolicy; Credentials::GroupDataProvider * mGroupsProvider; + Crypto::SessionKeystore * mSessionKeystore; app::DefaultAttributePersistenceProvider mAttributePersister; GroupDataProviderListener mListener; ServerFabricDelegate mFabricDelegate; diff --git a/src/app/tests/TestWriteInteraction.cpp b/src/app/tests/TestWriteInteraction.cpp index 6026333050665c..4729f9e71b10f5 100644 --- a/src/app/tests/TestWriteInteraction.cpp +++ b/src/app/tests/TestWriteInteraction.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,7 @@ constexpr uint16_t kMaxGroupsPerFabric = 5; constexpr uint16_t kMaxGroupKeysPerFabric = 8; chip::TestPersistentStorageDelegate gTestStorage; +chip::Crypto::DefaultSessionKeystore gSessionKeystore; chip::Credentials::GroupDataProviderImpl gGroupsProvider(kMaxGroupsPerFabric, kMaxGroupKeysPerFabric); } // namespace @@ -1042,6 +1044,7 @@ int Test_Setup(void * inContext) TestContext & ctx = *static_cast(inContext); gTestStorage.ClearStorage(); gGroupsProvider.SetStorageDelegate(&gTestStorage); + gGroupsProvider.SetSessionKeystore(&gSessionKeystore); VerifyOrReturnError(CHIP_NO_ERROR == gGroupsProvider.Init(), FAILURE); chip::Credentials::SetGroupDataProvider(&gGroupsProvider); diff --git a/src/app/tests/integration/chip_im_initiator.cpp b/src/app/tests/integration/chip_im_initiator.cpp index 5457bbb3fd79ef..3f822623e49b91 100644 --- a/src/app/tests/integration/chip_im_initiator.cpp +++ b/src/app/tests/integration/chip_im_initiator.cpp @@ -716,7 +716,7 @@ int main(int argc, char * argv[]) SuccessOrExit(err); err = gSessionManager.Init(&chip::DeviceLayer::SystemLayer(), &gTransportManager, &gMessageCounterManager, &gStorage, - &gFabricTable); + &gFabricTable, gSessionKeystore); SuccessOrExit(err); err = gExchangeManager.Init(&gSessionManager); diff --git a/src/app/tests/integration/chip_im_responder.cpp b/src/app/tests/integration/chip_im_responder.cpp index 34978611e4f3bf..03215e52fbe31b 100644 --- a/src/app/tests/integration/chip_im_responder.cpp +++ b/src/app/tests/integration/chip_im_responder.cpp @@ -197,7 +197,7 @@ int main(int argc, char * argv[]) SuccessOrExit(err); err = gSessionManager.Init(&chip::DeviceLayer::SystemLayer(), &gTransportManager, &gMessageCounterManager, &gStorage, - &gFabricTable); + &gFabricTable, gSessionKeystore); SuccessOrExit(err); err = gExchangeManager.Init(&gSessionManager); diff --git a/src/app/tests/integration/common.cpp b/src/app/tests/integration/common.cpp index 5b3c231d3f9459..87627aeb9fddf0 100644 --- a/src/app/tests/integration/common.cpp +++ b/src/app/tests/integration/common.cpp @@ -43,6 +43,7 @@ chip::SessionHolder gSession; chip::TestPersistentStorageDelegate gStorage; chip::PersistentStorageOperationalKeystore gOperationalKeystore; chip::Credentials::PersistentStorageOpCertStore gOpCertStore; +chip::Crypto::DefaultSessionKeystore gSessionKeystore; void InitializeChip() { diff --git a/src/app/tests/integration/common.h b/src/app/tests/integration/common.h index 2b1865561c259a..4766b9d6160500 100644 --- a/src/app/tests/integration/common.h +++ b/src/app/tests/integration/common.h @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -40,6 +41,7 @@ extern chip::SessionManager gSessionManager; extern chip::secure_channel::MessageCounterManager gMessageCounterManager; extern chip::SessionHolder gSession; extern chip::TestPersistentStorageDelegate gStorage; +extern chip::Crypto::DefaultSessionKeystore gSessionKeystore; constexpr chip::NodeId kTestNodeId = 0x1ULL; constexpr chip::NodeId kTestNodeId1 = 0x2ULL; diff --git a/src/controller/CHIPDeviceControllerFactory.cpp b/src/controller/CHIPDeviceControllerFactory.cpp index 25457368d945a3..1261b0f7af5db5 100644 --- a/src/controller/CHIPDeviceControllerFactory.cpp +++ b/src/controller/CHIPDeviceControllerFactory.cpp @@ -86,6 +86,7 @@ CHIP_ERROR DeviceControllerFactory::InitSystemState() params.fabricIndependentStorage = mFabricIndependentStorage; params.enableServerInteractions = mEnableServerInteractions; params.groupDataProvider = mSystemState->GetGroupDataProvider(); + params.sessionKeystore = mSystemState->GetSessionKeystore(); params.fabricTable = mSystemState->Fabrics(); params.operationalKeystore = mOperationalKeystore; params.opCertStore = mOpCertStore; @@ -129,6 +130,7 @@ CHIP_ERROR DeviceControllerFactory::InitSystemState(FactoryInitParams params) // OperationalCertificateStore needs to be provided to init the fabric table if fabric table is // not provided wholesale. ReturnErrorCodeIf((params.fabricTable == nullptr) && (params.opCertStore == nullptr), CHIP_ERROR_INVALID_ARGUMENT); + ReturnErrorCodeIf(params.sessionKeystore == nullptr, CHIP_ERROR_INVALID_ARGUMENT); #if CONFIG_NETWORK_LAYER_BLE #if CONFIG_DEVICE_LAYER @@ -166,6 +168,7 @@ CHIP_ERROR DeviceControllerFactory::InitSystemState(FactoryInitParams params) stateParams.exchangeMgr = chip::Platform::New(); stateParams.messageCounterManager = chip::Platform::New(); stateParams.groupDataProvider = params.groupDataProvider; + stateParams.sessionKeystore = params.sessionKeystore; // if no fabricTable was provided, create one and track it in stateParams for cleanup stateParams.fabricTable = params.fabricTable; @@ -198,7 +201,7 @@ CHIP_ERROR DeviceControllerFactory::InitSystemState(FactoryInitParams params) ReturnErrorOnFailure(stateParams.sessionMgr->Init(stateParams.systemLayer, stateParams.transportMgr, stateParams.messageCounterManager, params.fabricIndependentStorage, - stateParams.fabricTable)); + stateParams.fabricTable, *stateParams.sessionKeystore)); ReturnErrorOnFailure(stateParams.exchangeMgr->Init(stateParams.sessionMgr)); ReturnErrorOnFailure(stateParams.messageCounterManager->Init(stateParams.exchangeMgr)); ReturnErrorOnFailure(stateParams.unsolicitedStatusHandler->Init(stateParams.exchangeMgr)); diff --git a/src/controller/CHIPDeviceControllerFactory.h b/src/controller/CHIPDeviceControllerFactory.h index 351580ebf7715e..fd77c3779017a9 100644 --- a/src/controller/CHIPDeviceControllerFactory.h +++ b/src/controller/CHIPDeviceControllerFactory.h @@ -94,15 +94,16 @@ struct SetupParams CommissioningDelegate * defaultCommissioner = nullptr; }; -// TODO everything other than the fabric storage, group data provider, OperationalKeystore -// and OperationalCertificateStore here should be removed. We're blocked because of the -// need to support !CHIP_DEVICE_LAYER +// TODO everything other than the fabric storage, group data provider, OperationalKeystore, +// OperationalCertificateStore and SessionKeystore here should be removed. We're blocked +// because of the need to support !CHIP_DEVICE_LAYER struct FactoryInitParams { System::Layer * systemLayer = nullptr; PersistentStorageDelegate * fabricIndependentStorage = nullptr; Credentials::CertificateValidityPolicy * certificateValidityPolicy = nullptr; Credentials::GroupDataProvider * groupDataProvider = nullptr; + Crypto::SessionKeystore * sessionKeystore = nullptr; Inet::EndPointManager * tcpEndPointManager = nullptr; Inet::EndPointManager * udpEndPointManager = nullptr; FabricTable * fabricTable = nullptr; diff --git a/src/controller/CHIPDeviceControllerSystemState.h b/src/controller/CHIPDeviceControllerSystemState.h index 8cf83063464c5c..739b9f113754d9 100644 --- a/src/controller/CHIPDeviceControllerSystemState.h +++ b/src/controller/CHIPDeviceControllerSystemState.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -81,6 +82,7 @@ struct DeviceControllerSystemStateParams Ble::BleLayer * bleLayer = nullptr; #endif Credentials::GroupDataProvider * groupDataProvider = nullptr; + Crypto::SessionKeystore * sessionKeystore = nullptr; // Params that will be deallocated via Platform::Delete in // DeviceControllerSystemState::Shutdown. @@ -126,7 +128,8 @@ class DeviceControllerSystemState mMessageCounterManager(params.messageCounterManager), mFabrics(params.fabricTable), mCASEServer(params.caseServer), mCASESessionManager(params.caseSessionManager), mSessionSetupPool(params.sessionSetupPool), mCASEClientPool(params.caseClientPool), mGroupDataProvider(params.groupDataProvider), - mFabricTableDelegate(params.fabricTableDelegate), mSessionResumptionStorage(std::move(params.sessionResumptionStorage)) + mSessionKeystore(params.sessionKeystore), mFabricTableDelegate(params.fabricTableDelegate), + mSessionResumptionStorage(std::move(params.sessionResumptionStorage)) { #if CONFIG_NETWORK_LAYER_BLE mBleLayer = params.bleLayer; @@ -165,7 +168,7 @@ class DeviceControllerSystemState return mSystemLayer != nullptr && mUDPEndPointManager != nullptr && mTransportMgr != nullptr && mSessionMgr != nullptr && mUnsolicitedStatusHandler != nullptr && mExchangeMgr != nullptr && mMessageCounterManager != nullptr && mFabrics != nullptr && mCASESessionManager != nullptr && mSessionSetupPool != nullptr && mCASEClientPool != nullptr && - mGroupDataProvider != nullptr; + mGroupDataProvider != nullptr && mSessionKeystore != nullptr; }; System::Layer * SystemLayer() const { return mSystemLayer; }; @@ -181,6 +184,7 @@ class DeviceControllerSystemState #endif CASESessionManager * CASESessionMgr() const { return mCASESessionManager; } Credentials::GroupDataProvider * GetGroupDataProvider() const { return mGroupDataProvider; } + Crypto::SessionKeystore * GetSessionKeystore() const { return mSessionKeystore; } void SetTempFabricTable(FabricTable * tempFabricTable) { mTempFabricTable = tempFabricTable; } private: @@ -203,6 +207,7 @@ class DeviceControllerSystemState SessionSetupPool * mSessionSetupPool = nullptr; CASEClientPool * mCASEClientPool = nullptr; Credentials::GroupDataProvider * mGroupDataProvider = nullptr; + Crypto::SessionKeystore * mSessionKeystore = nullptr; FabricTable::Delegate * mFabricTableDelegate = nullptr; Platform::UniquePtr mSessionResumptionStorage; diff --git a/src/controller/java/AndroidDeviceControllerWrapper.cpp b/src/controller/java/AndroidDeviceControllerWrapper.cpp index 701d6d6683aad7..b9fe506bb2d617 100644 --- a/src/controller/java/AndroidDeviceControllerWrapper.cpp +++ b/src/controller/java/AndroidDeviceControllerWrapper.cpp @@ -177,8 +177,10 @@ AndroidDeviceControllerWrapper * AndroidDeviceControllerWrapper::AllocateNew( setupParams.operationalCredentialsDelegate = opCredsIssuer; setupParams.defaultCommissioner = &wrapper->mAutoCommissioner; initParams.fabricIndependentStorage = wrapperStorage; + initParams.sessionKeystore = &wrapper->mSessionKeystore; wrapper->mGroupDataProvider.SetStorageDelegate(wrapperStorage); + wrapper->mGroupDataProvider.SetSessionKeystore(initParams.sessionKeystore); CommissioningParameters params = wrapper->mAutoCommissioner.GetCommissioningParameters(); params.SetFailsafeTimerSeconds(failsafeTimerSeconds); diff --git a/src/controller/java/AndroidDeviceControllerWrapper.h b/src/controller/java/AndroidDeviceControllerWrapper.h index 3267f7fc4519b5..0700591bf77580 100644 --- a/src/controller/java/AndroidDeviceControllerWrapper.h +++ b/src/controller/java/AndroidDeviceControllerWrapper.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -205,6 +206,8 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel chip::Credentials::GroupDataProviderImpl mGroupDataProvider; // TODO: This may need to be injected as an OperationalCertificateStore * chip::Credentials::PersistentStorageOpCertStore mOpCertStore; + // TODO: This may need to be injected as a SessionKeystore* + chip::Crypto::RawKeySessionKeystore mSessionKeystore; JavaVM * mJavaVM = nullptr; jobject mJavaObjectRef = nullptr; diff --git a/src/controller/python/ChipDeviceController-ScriptBinding.cpp b/src/controller/python/ChipDeviceController-ScriptBinding.cpp index 0be9b561ab0b51..e9cf070756f207 100644 --- a/src/controller/python/ChipDeviceController-ScriptBinding.cpp +++ b/src/controller/python/ChipDeviceController-ScriptBinding.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include @@ -102,6 +103,7 @@ chip::Controller::ScriptDevicePairingDelegate sPairingDelegate; chip::Controller::ScriptPairingDeviceDiscoveryDelegate sPairingDeviceDiscoveryDelegate; chip::Credentials::GroupDataProviderImpl sGroupDataProvider; chip::Credentials::PersistentStorageOpCertStore sPersistentStorageOpCertStore; +chip::Crypto::RawKeySessionKeystore sSessionKeystore; // NOTE: Remote device ID is in sync with the echo server device id // At some point, we may want to add an option to connect to a device without @@ -231,8 +233,10 @@ PyChipError pychip_DeviceController_StackInit(Controller::Python::StorageAdapter FactoryInitParams factoryParams; factoryParams.fabricIndependentStorage = storageAdapter; + factoryParams.sessionKeystore = &sSessionKeystore; sGroupDataProvider.SetStorageDelegate(storageAdapter); + sGroupDataProvider.SetSessionKeystore(factoryParams.sessionKeystore); PyReturnErrorOnFailure(ToPyChipError(sGroupDataProvider.Init())); factoryParams.groupDataProvider = &sGroupDataProvider; diff --git a/src/controller/python/chip/internal/CommissionerImpl.cpp b/src/controller/python/chip/internal/CommissionerImpl.cpp index 25102783ac223f..c95bdaa7c12059 100644 --- a/src/controller/python/chip/internal/CommissionerImpl.cpp +++ b/src/controller/python/chip/internal/CommissionerImpl.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -98,6 +99,7 @@ ScriptDevicePairingDelegate gPairingDelegate; chip::Credentials::GroupDataProviderImpl gGroupDataProvider; chip::Credentials::PersistentStorageOpCertStore gPersistentStorageOpCertStore; chip::Controller::ExampleOperationalCredentialsIssuer gOperationalCredentialsIssuer; +chip::Crypto::RawKeySessionKeystore gSessionKeystore; } // namespace @@ -132,9 +134,11 @@ extern "C" chip::Controller::DeviceCommissioner * pychip_internal_Commissioner_N chip::Credentials::SetDeviceAttestationVerifier(chip::Credentials::GetDefaultDACVerifier(testingRootStore)); factoryParams.fabricIndependentStorage = &gServerStorage; + factoryParams.sessionKeystore = &gSessionKeystore; // Initialize group data provider for local group key state and IPKs gGroupDataProvider.SetStorageDelegate(&gServerStorage); + gGroupDataProvider.SetSessionKeystore(factoryParams.sessionKeystore); err = gGroupDataProvider.Init(); SuccessOrExit(err); factoryParams.groupDataProvider = &gGroupDataProvider; diff --git a/src/credentials/GroupDataProviderImpl.cpp b/src/credentials/GroupDataProviderImpl.cpp index 3ebbb6575255c1..36e9baaef398aa 100644 --- a/src/credentials/GroupDataProviderImpl.cpp +++ b/src/credentials/GroupDataProviderImpl.cpp @@ -817,7 +817,7 @@ constexpr size_t GroupDataProviderImpl::kIteratorsMax; CHIP_ERROR GroupDataProviderImpl::Init() { - if (mStorage == nullptr) + if (mStorage == nullptr || mSessionKeystore == nullptr) { return CHIP_ERROR_INCORRECT_STATE; } @@ -1747,9 +1747,7 @@ Crypto::SymmetricKeyContext * GroupDataProviderImpl::GetKeyContext(FabricIndex f Crypto::GroupOperationalCredentials * creds = keyset.GetCurrentGroupCredentials(); if (nullptr != creds) { - return mGroupKeyContexPool.CreateObject( - *this, ByteSpan(creds->encryption_key, Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES), creds->hash, - ByteSpan(creds->privacy_key, Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES)); + return mGroupKeyContexPool.CreateObject(*this, creds->encryption_key, creds->hash, creds->privacy_key); } } } @@ -1789,8 +1787,7 @@ CHIP_ERROR GroupDataProviderImpl::GetIpkKeySet(FabricIndex fabric_index, KeySet void GroupDataProviderImpl::GroupKeyContext::Release() { - memset(mEncryptionKey, 0, sizeof(mEncryptionKey)); - memset(mPrivacyKey, 0, sizeof(mPrivacyKey)); + ReleaseKeys(); mProvider.mGroupKeyContexPool.ReleaseObject(this); } @@ -1799,8 +1796,8 @@ CHIP_ERROR GroupDataProviderImpl::GroupKeyContext::MessageEncrypt(const ByteSpan MutableByteSpan & ciphertext) const { uint8_t * output = ciphertext.data(); - return Crypto::AES_CCM_encrypt(plaintext.data(), plaintext.size(), aad.data(), aad.size(), mEncryptionKey, - Crypto::kAES_CCM128_Key_Length, nonce.data(), nonce.size(), output, mic.data(), mic.size()); + return Crypto::AES_CCM_encrypt(plaintext.data(), plaintext.size(), aad.data(), aad.size(), mEncryptionKey, nonce.data(), + nonce.size(), output, mic.data(), mic.size()); } CHIP_ERROR GroupDataProviderImpl::GroupKeyContext::MessageDecrypt(const ByteSpan & ciphertext, const ByteSpan & aad, @@ -1809,21 +1806,19 @@ CHIP_ERROR GroupDataProviderImpl::GroupKeyContext::MessageDecrypt(const ByteSpan { uint8_t * output = plaintext.data(); return Crypto::AES_CCM_decrypt(ciphertext.data(), ciphertext.size(), aad.data(), aad.size(), mic.data(), mic.size(), - mEncryptionKey, Crypto::kAES_CCM128_Key_Length, nonce.data(), nonce.size(), output); + mEncryptionKey, nonce.data(), nonce.size(), output); } CHIP_ERROR GroupDataProviderImpl::GroupKeyContext::PrivacyEncrypt(const ByteSpan & input, const ByteSpan & nonce, MutableByteSpan & output) const { - return Crypto::AES_CTR_crypt(input.data(), input.size(), mPrivacyKey, Crypto::kAES_CCM128_Key_Length, nonce.data(), - nonce.size(), output.data()); + return Crypto::AES_CTR_crypt(input.data(), input.size(), mPrivacyKey, nonce.data(), nonce.size(), output.data()); } CHIP_ERROR GroupDataProviderImpl::GroupKeyContext::PrivacyDecrypt(const ByteSpan & input, const ByteSpan & nonce, MutableByteSpan & output) const { - return Crypto::AES_CTR_crypt(input.data(), input.size(), mPrivacyKey, Crypto::kAES_CCM128_Key_Length, nonce.data(), - nonce.size(), output.data()); + return Crypto::AES_CTR_crypt(input.data(), input.size(), mPrivacyKey, nonce.data(), nonce.size(), output.data()); } GroupDataProviderImpl::GroupSessionIterator * GroupDataProviderImpl::IterateGroupSessions(uint16_t session_id) @@ -1928,8 +1923,7 @@ bool GroupDataProviderImpl::GroupSessionIteratorImpl::Next(GroupSession & output Crypto::GroupOperationalCredentials & creds = keyset.operational_keys[mKeyIndex++]; if (creds.hash == mSessionId) { - mGroupKeyContext.SetKey(ByteSpan(creds.encryption_key, sizeof(creds.encryption_key)), mSessionId); - mGroupKeyContext.SetPrivacyKey(ByteSpan(creds.privacy_key, sizeof(creds.privacy_key))); + mGroupKeyContext.Initialize(creds.encryption_key, mSessionId, creds.privacy_key); output.fabric_index = fabric.fabric_index; output.group_id = mapping.group_id; output.security_policy = keyset.policy; @@ -1943,6 +1937,7 @@ bool GroupDataProviderImpl::GroupSessionIteratorImpl::Next(GroupSession & output void GroupDataProviderImpl::GroupSessionIteratorImpl::Release() { + mGroupKeyContext.ReleaseKeys(); mProvider.mGroupSessionsIterator.ReleaseObject(this); } diff --git a/src/credentials/GroupDataProviderImpl.h b/src/credentials/GroupDataProviderImpl.h index 73c8bc2faadb24..5c1d0d67e3417f 100644 --- a/src/credentials/GroupDataProviderImpl.h +++ b/src/credentials/GroupDataProviderImpl.h @@ -17,6 +17,7 @@ #pragma once #include +#include #include #include @@ -42,6 +43,9 @@ class GroupDataProviderImpl : public GroupDataProvider */ void SetStorageDelegate(PersistentStorageDelegate * storage); + void SetSessionKeystore(Crypto::SessionKeystore * keystore) { mSessionKeystore = keystore; } + Crypto::SessionKeystore * GetSessionKeystore() const { return mSessionKeystore; } + CHIP_ERROR Init() override; void Finish() override; @@ -152,23 +156,35 @@ class GroupDataProviderImpl : public GroupDataProvider public: GroupKeyContext(GroupDataProviderImpl & provider) : mProvider(provider) {} - GroupKeyContext(GroupDataProviderImpl & provider, const ByteSpan & encryptionKey, uint16_t hash, - const ByteSpan & privacyKey) : + GroupKeyContext(GroupDataProviderImpl & provider, const Crypto::Aes128KeyByteArray & encryptionKey, uint16_t hash, + const Crypto::Aes128KeyByteArray & privacyKey) : mProvider(provider) + { - SetKey(encryptionKey, hash); - SetPrivacyKey(privacyKey); + Initialize(encryptionKey, hash, privacyKey); } - void SetKey(const ByteSpan & encryptionKey, uint16_t hash) + void Initialize(const Crypto::Aes128KeyByteArray & encryptionKey, uint16_t hash, + const Crypto::Aes128KeyByteArray & privacyKey) { + ReleaseKeys(); mKeyHash = hash; - memcpy(mEncryptionKey, encryptionKey.data(), std::min(encryptionKey.size(), sizeof(mEncryptionKey))); + // TODO: Load group keys to the session keystore upon loading from persistent storage + // + // Group keys should be transformed into a key handle as soon as possible or even + // the key storage should be taken over by SessionKeystore interface, but this looks + // like more work, so let's use the transitional code below for now. + + Crypto::SessionKeystore * keystore = mProvider.GetSessionKeystore(); + keystore->CreateKey(encryptionKey, mEncryptionKey); + keystore->CreateKey(privacyKey, mPrivacyKey); } - void SetPrivacyKey(const ByteSpan & privacyKey) + void ReleaseKeys() { - memcpy(mPrivacyKey, privacyKey.data(), std::min(privacyKey.size(), sizeof(mPrivacyKey))); + Crypto::SessionKeystore * keystore = mProvider.GetSessionKeystore(); + keystore->DestroyKey(mEncryptionKey); + keystore->DestroyKey(mPrivacyKey); } uint16_t GetKeyHash() override { return mKeyHash; } @@ -184,9 +200,9 @@ class GroupDataProviderImpl : public GroupDataProvider protected: GroupDataProviderImpl & mProvider; - uint16_t mKeyHash = 0; - uint8_t mEncryptionKey[Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES] = { 0 }; - uint8_t mPrivacyKey[Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES] = { 0 }; + uint16_t mKeyHash = 0; + Crypto::Aes128KeyHandle mEncryptionKey; + Crypto::Aes128KeyHandle mPrivacyKey; }; class KeySetIteratorImpl : public KeySetIterator @@ -230,7 +246,8 @@ class GroupDataProviderImpl : public GroupDataProvider bool IsInitialized() { return (mStorage != nullptr); } CHIP_ERROR RemoveEndpoints(FabricIndex fabric_index, GroupId group_id); - chip::PersistentStorageDelegate * mStorage = nullptr; + PersistentStorageDelegate * mStorage = nullptr; + Crypto::SessionKeystore * mSessionKeystore = nullptr; ObjectPool mGroupInfoIterators; ObjectPool mGroupKeyIterators; ObjectPool mEndpointIterators; diff --git a/src/credentials/tests/TestGroupDataProvider.cpp b/src/credentials/tests/TestGroupDataProvider.cpp index deafdf5958de3a..56a6424557b8a4 100644 --- a/src/credentials/tests/TestGroupDataProvider.cpp +++ b/src/credentials/tests/TestGroupDataProvider.cpp @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -1196,6 +1197,7 @@ void TestGroupDecryption(nlTestSuite * apSuite, void * apContext) namespace { static chip::TestPersistentStorageDelegate sDelegate; +static chip::Crypto::DefaultSessionKeystore sSessionKeystore; static GroupDataProviderImpl sProvider(chip::app::TestGroups::kMaxGroupsPerFabric, chip::app::TestGroups::kMaxGroupKeysPerFabric); static EpochKey kEpochKeys0[] = { @@ -1228,6 +1230,7 @@ int Test_Setup(void * inContext) // Initialize Group Data Provider sProvider.SetStorageDelegate(&sDelegate); + sProvider.SetSessionKeystore(&sSessionKeystore); sProvider.SetListener(&chip::app::TestGroups::sListener); VerifyOrReturnError(CHIP_NO_ERROR == sProvider.Init(), FAILURE); SetGroupDataProvider(&sProvider); diff --git a/src/crypto/BUILD.gn b/src/crypto/BUILD.gn index bcf20280c0551e..d97c89737c15bc 100644 --- a/src/crypto/BUILD.gn +++ b/src/crypto/BUILD.gn @@ -69,6 +69,7 @@ source_set("public_headers") { sources = [ "CHIPCryptoPAL.h", "OperationalKeystore.h", + "SessionKeystore.h", ] public_deps = [ @@ -126,9 +127,7 @@ if (chip_crypto == "openssl") { ] public_deps = [ ":public_headers" ] - external_mbedtls = current_os == "zephyr" - - if (!external_mbedtls) { + if (!chip_external_mbedtls) { public_deps += [ "${mbedtls_root}:mbedtls" ] } } @@ -139,12 +138,25 @@ static_library("crypto") { sources = [ "CHIPCryptoPAL.cpp", + "DefaultSessionKeystore.h", "PersistentStorageOperationalKeystore.cpp", "PersistentStorageOperationalKeystore.h", "RandUtils.cpp", "RandUtils.h", ] + if (chip_crypto == "psa") { + sources += [ + "PSASessionKeystore.cpp", + "PSASessionKeystore.h", + ] + } else { + sources += [ + "RawKeySessionKeystore.cpp", + "RawKeySessionKeystore.h", + ] + } + public_configs = [] cflags = [ "-Wconversion" ] diff --git a/src/crypto/CHIPCryptoPAL.cpp b/src/crypto/CHIPCryptoPAL.cpp index ca15a30b9cf33c..69c3f575867ab6 100644 --- a/src/crypto/CHIPCryptoPAL.cpp +++ b/src/crypto/CHIPCryptoPAL.cpp @@ -706,14 +706,14 @@ CHIP_ERROR EcdsaAsn1SignatureToRaw(size_t fe_length_bytes, const ByteSpan & asn1 return CHIP_NO_ERROR; } -CHIP_ERROR AES_CTR_crypt(const uint8_t * input, size_t input_length, const uint8_t * key, size_t key_length, const uint8_t * nonce, +CHIP_ERROR AES_CTR_crypt(const uint8_t * input, size_t input_length, const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * output) { // Discard tag portion of CCM to apply only CTR mode encryption/decryption. constexpr size_t kTagLen = Crypto::kAES_CCM128_Tag_Length; uint8_t tag[kTagLen]; - return AES_CCM_encrypt(input, input_length, nullptr, 0, key, key_length, nonce, nonce_length, output, tag, kTagLen); + return AES_CCM_encrypt(input, input_length, nullptr, 0, key, nonce, nonce_length, output, tag, kTagLen); } CHIP_ERROR GenerateCompressedFabricId(const Crypto::P256PublicKey & root_public_key, uint64_t fabric_id, diff --git a/src/crypto/CHIPCryptoPAL.h b/src/crypto/CHIPCryptoPAL.h index 7a5cb1f191d706..6df3dc3a631572 100644 --- a/src/crypto/CHIPCryptoPAL.h +++ b/src/crypto/CHIPCryptoPAL.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -345,6 +346,11 @@ class SensitiveDataFixedBuffer */ FixedByteSpan Span() const { return FixedByteSpan(mBytes); } + /** + * @brief Returns capacity of the buffer + */ + static constexpr size_t Capacity() { return kCapacity; } + private: uint8_t mBytes[kCapacity]; }; @@ -355,6 +361,8 @@ using P256ECDHDerivedSecret = SensitiveDataBuffer; using IdentityProtectionKey = SensitiveDataFixedBuffer; using IdentityProtectionKeySpan = FixedByteSpan; +using AttestationChallenge = SensitiveDataFixedBuffer; + class P256PublicKey : public ECPKey { public: @@ -541,6 +549,54 @@ class P256Keypair : public P256KeypairBase bool mInitialized = false; }; +using Aes128KeyByteArray = uint8_t[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES]; + +/** + * @brief Platform-specific AES key + * + * The class represents AES key used by Matter stack either in the form of raw key material or key + * reference, depending on the platform. To achieve that, it contains an opaque context that can be + * cast to a concrete representation used by the given platform. Note that currently Matter uses + * 128-bit symmetric keys only. + */ +class Aes128KeyHandle +{ +public: + Aes128KeyHandle() = default; + ~Aes128KeyHandle() { ClearSecretData(mContext.mOpaque); } + + Aes128KeyHandle(const Aes128KeyHandle &) = delete; + Aes128KeyHandle(Aes128KeyHandle &&) = delete; + void operator=(const Aes128KeyHandle &) = delete; + void operator=(Aes128KeyHandle &&) = delete; + + /** + * @brief Get internal context cast to the desired key representation + */ + template + const T & As() const + { + return *SafePointerCast(&mContext); + } + + /** + * @brief Get internal context cast to the desired, mutable key representation + */ + template + T & AsMutable() + { + return *SafePointerCast(&mContext); + } + +private: + static constexpr size_t kContextSize = CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES; + + struct alignas(uintptr_t) OpaqueContext + { + uint8_t mOpaque[kContextSize] = {}; + } mContext; +}; + /** * @brief Convert a raw ECDSA signature to ASN.1 signature (per X9.62) as used by TLS libraries. * @@ -612,7 +668,6 @@ CHIP_ERROR ConvertIntegerRawToDerWithoutTag(const ByteSpan & raw_integer, Mutabl * @param aad Additional authentication data * @param aad_length Length of additional authentication data * @param key Encryption key - * @param key_length Length of encryption key (in bytes) * @param nonce Encryption nonce * @param nonce_length Length of encryption nonce * @param ciphertext Buffer to write ciphertext into. Caller must ensure this is large enough to hold the ciphertext @@ -621,7 +676,7 @@ CHIP_ERROR ConvertIntegerRawToDerWithoutTag(const ByteSpan & raw_integer, Mutabl * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise * */ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, const uint8_t * aad, size_t aad_length, - const uint8_t * key, size_t key_length, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, + const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, uint8_t * tag, size_t tag_length); /** @@ -639,14 +694,13 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c * @param tag Tag to use to decrypt * @param tag_length Length of tag * @param key Decryption key - * @param key_length Length of Decryption key (in bytes) * @param nonce Encryption nonce * @param nonce_length Length of encryption nonce * @param plaintext Buffer to write plaintext into * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise **/ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_length, const uint8_t * aad, size_t aad_length, - const uint8_t * tag, size_t tag_length, const uint8_t * key, size_t key_length, const uint8_t * nonce, + const uint8_t * tag, size_t tag_length, const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * plaintext); /** @@ -660,13 +714,12 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_length, * @param input Input text to encrypt/decrypt * @param input_length Length of ciphertext * @param key Decryption key - * @param key_length Length of Decryption key (in bytes) * @param nonce Encryption nonce * @param nonce_length Length of encryption nonce * @param output Buffer to write output into * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise **/ -CHIP_ERROR AES_CTR_crypt(const uint8_t * input, size_t input_length, const uint8_t * key, size_t key_length, const uint8_t * nonce, +CHIP_ERROR AES_CTR_crypt(const uint8_t * input, size_t input_length, const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * output); /** diff --git a/src/crypto/CHIPCryptoPALOpenSSL.cpp b/src/crypto/CHIPCryptoPALOpenSSL.cpp index f914b2a59b742e..ad3180447e8022 100644 --- a/src/crypto/CHIPCryptoPALOpenSSL.cpp +++ b/src/crypto/CHIPCryptoPALOpenSSL.cpp @@ -147,7 +147,7 @@ static int _compareDaysAndSeconds(const int days, const int seconds) } CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, const uint8_t * aad, size_t aad_length, - const uint8_t * key, size_t key_length, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, + const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, uint8_t * tag, size_t tag_length) { #if CHIP_CRYPTO_BORINGSSL @@ -187,12 +187,9 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c } } - VerifyOrExit(key_length == kAES_CCM128_Key_Length, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit((plaintext_length != 0) || ciphertext_was_null, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(plaintext != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(ciphertext != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(key_length == kAES_CCM128_Key_Length, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(CanCastTo(nonce_length), error = CHIP_ERROR_INVALID_ARGUMENT); @@ -207,7 +204,7 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c #if CHIP_CRYPTO_BORINGSSL aead = EVP_aead_aes_128_ccm_matter(); - context = EVP_AEAD_CTX_new(aead, Uint8::to_const_uchar(key), key_length, tag_length); + context = EVP_AEAD_CTX_new(aead, key.As(), sizeof(Aes128KeyByteArray), tag_length); VerifyOrExit(context != nullptr, error = CHIP_ERROR_NO_MEMORY); result = EVP_AEAD_CTX_seal_scatter(context, ciphertext, tag, &written_tag_len, tag_length, nonce, nonce_length, plaintext, @@ -234,7 +231,8 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); // Pass in key + nonce - result = EVP_EncryptInit_ex(context, nullptr, nullptr, Uint8::to_const_uchar(key), Uint8::to_const_uchar(nonce)); + static_assert(kAES_CCM128_Key_Length == sizeof(Aes128KeyByteArray), "Unexpected key length"); + result = EVP_EncryptInit_ex(context, nullptr, nullptr, key.As(), Uint8::to_const_uchar(nonce)); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); // Pass in plain text length @@ -284,7 +282,7 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c } CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_length, const uint8_t * aad, size_t aad_length, - const uint8_t * tag, size_t tag_length, const uint8_t * key, size_t key_length, const uint8_t * nonce, + const uint8_t * tag, size_t tag_length, const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * plaintext) { #if CHIP_CRYPTO_BORINGSSL @@ -332,15 +330,13 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_length, VerifyOrExit(tag_length == 8 || tag_length == 12 || tag_length == CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, error = CHIP_ERROR_INVALID_ARGUMENT); #endif // CHIP_CRYPTO_BORINGSSL - VerifyOrExit(key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(key_length == kAES_CCM128_Key_Length, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); #if CHIP_CRYPTO_BORINGSSL aead = EVP_aead_aes_128_ccm_matter(); - context = EVP_AEAD_CTX_new(aead, Uint8::to_const_uchar(key), key_length, tag_length); + context = EVP_AEAD_CTX_new(aead, key.As(), sizeof(Aes128KeyByteArray), tag_length); VerifyOrExit(context != nullptr, error = CHIP_ERROR_NO_MEMORY); result = EVP_AEAD_CTX_open_gather(context, plaintext, nonce, nonce_length, ciphertext, ciphertext_length, tag, tag_length, aad, @@ -370,7 +366,8 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_length, VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); // Pass in key + nonce - result = EVP_DecryptInit_ex(context, nullptr, nullptr, Uint8::to_const_uchar(key), Uint8::to_const_uchar(nonce)); + static_assert(kAES_CCM128_Key_Length == sizeof(Aes128KeyByteArray), "Unexpected key length"); + result = EVP_DecryptInit_ex(context, nullptr, nullptr, key.As(), Uint8::to_const_uchar(nonce)); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); // Pass in cipher text length diff --git a/src/crypto/CHIPCryptoPALPSA.cpp b/src/crypto/CHIPCryptoPALPSA.cpp index 1a063a97cb7cee..3fc7c31e5dd4f2 100644 --- a/src/crypto/CHIPCryptoPALPSA.cpp +++ b/src/crypto/CHIPCryptoPALPSA.cpp @@ -107,112 +107,82 @@ bool isValidTag(const uint8_t * tag, size_t tag_length) } // namespace CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, const uint8_t * aad, size_t aad_length, - const uint8_t * key, size_t key_length, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, + const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, uint8_t * tag, size_t tag_length) { - VerifyOrReturnError(isBufferNonEmpty(key, key_length), CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(isBufferNonEmpty(nonce, nonce_length), CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(isValidTag(tag, tag_length), CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError((ciphertext != nullptr && plaintext != nullptr) || plaintext_length == 0, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(aad != nullptr || aad_length == 0, CHIP_ERROR_INVALID_ARGUMENT); const psa_algorithm_t algorithm = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_length); - CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_SUCCESS; - psa_key_attributes_t attrs = PSA_KEY_ATTRIBUTES_INIT; - psa_key_id_t keyId = 0; psa_aead_operation_t operation = PSA_AEAD_OPERATION_INIT; size_t out_length; size_t tag_out_length; - psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); - psa_set_key_algorithm(&attrs, algorithm); - psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); - - status = psa_import_key(&attrs, key, key_length, &keyId); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); - - status = psa_aead_encrypt_setup(&operation, keyId, algorithm); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); + status = psa_aead_encrypt_setup(&operation, key.As(), algorithm); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); status = psa_aead_set_lengths(&operation, aad_length, plaintext_length); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); status = psa_aead_set_nonce(&operation, nonce, nonce_length); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); status = psa_aead_update_ad(&operation, aad, aad_length); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); status = psa_aead_update(&operation, plaintext, plaintext_length, ciphertext, PSA_AEAD_UPDATE_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm, plaintext_length), &out_length); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); ciphertext += out_length; status = psa_aead_finish(&operation, ciphertext, PSA_AEAD_FINISH_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm), &out_length, tag, tag_length, &tag_out_length); - VerifyOrExit(status == PSA_SUCCESS && tag_length == tag_out_length, error = CHIP_ERROR_INTERNAL); - -exit: - psa_destroy_key(keyId); - psa_reset_key_attributes(&attrs); + VerifyOrReturnError(status == PSA_SUCCESS && tag_length == tag_out_length, CHIP_ERROR_INTERNAL); - return error; + return CHIP_NO_ERROR; } CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_length, const uint8_t * aad, size_t aad_length, - const uint8_t * tag, size_t tag_length, const uint8_t * key, size_t key_length, const uint8_t * nonce, + const uint8_t * tag, size_t tag_length, const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * plaintext) { - VerifyOrReturnError(isBufferNonEmpty(key, key_length), CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(isBufferNonEmpty(nonce, nonce_length), CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(isValidTag(tag, tag_length), CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError((ciphertext != nullptr && plaintext != nullptr) || ciphertext_length == 0, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(aad != nullptr || aad_length == 0, CHIP_ERROR_INVALID_ARGUMENT); const psa_algorithm_t algorithm = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_length); - CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_SUCCESS; - psa_key_attributes_t attrs = PSA_KEY_ATTRIBUTES_INIT; - psa_key_id_t keyId = 0; psa_aead_operation_t operation = PSA_AEAD_OPERATION_INIT; size_t outLength; - psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); - psa_set_key_algorithm(&attrs, algorithm); - psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DECRYPT); - - status = psa_import_key(&attrs, key, key_length, &keyId); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); - - status = psa_aead_decrypt_setup(&operation, keyId, algorithm); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); + status = psa_aead_decrypt_setup(&operation, key.As(), algorithm); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); status = psa_aead_set_lengths(&operation, aad_length, ciphertext_length); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); status = psa_aead_set_nonce(&operation, nonce, nonce_length); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); status = psa_aead_update_ad(&operation, aad, aad_length); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); status = psa_aead_update(&operation, ciphertext, ciphertext_length, plaintext, PSA_AEAD_UPDATE_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm, ciphertext_length), &outLength); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); plaintext += outLength; status = psa_aead_verify(&operation, plaintext, PSA_AEAD_VERIFY_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm), &outLength, tag, tag_length); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); - -exit: - psa_destroy_key(keyId); - psa_reset_key_attributes(&attrs); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); - return error; + return CHIP_NO_ERROR; } CHIP_ERROR Hash_SHA256(const uint8_t * data, const size_t data_length, uint8_t * out_buffer) @@ -310,40 +280,67 @@ void Hash_SHA256_stream::Clear() psa_hash_abort(toHashOperation(&mContext)); } -CHIP_ERROR HKDF_sha::HKDF_SHA256(const uint8_t * secret, const size_t secret_length, const uint8_t * salt, const size_t salt_length, - const uint8_t * info, const size_t info_length, uint8_t * out_buffer, size_t out_length) +CHIP_ERROR PsaKdf::Init(psa_algorithm_t algorithm, const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info) { - VerifyOrReturnError(isBufferNonEmpty(secret, secret_length), CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrReturnError(isBufferNonEmpty(info, info_length), CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrReturnError(isBufferNonEmpty(out_buffer, out_length), CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrReturnError(salt != nullptr || salt_length == 0, CHIP_ERROR_INVALID_ARGUMENT); + psa_status_t status = PSA_SUCCESS; + psa_key_attributes_t attrs = PSA_KEY_ATTRIBUTES_INIT; - CHIP_ERROR error = CHIP_NO_ERROR; - psa_status_t status = PSA_SUCCESS; - psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT; + psa_set_key_type(&attrs, PSA_KEY_TYPE_DERIVE); + psa_set_key_algorithm(&attrs, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); - status = psa_key_derivation_setup(&operation, PSA_ALG_HKDF(PSA_ALG_SHA_256)); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); + status = psa_import_key(&attrs, secret.data(), secret.size(), &mSecretKeyId); + psa_reset_key_attributes(&attrs); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + status = psa_key_derivation_setup(&mOperation, algorithm); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); - if (salt_length > 0) + if (salt.size() > 0) { - status = psa_key_derivation_input_bytes(&operation, PSA_KEY_DERIVATION_INPUT_SALT, salt, salt_length); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); + status = psa_key_derivation_input_bytes(&mOperation, PSA_KEY_DERIVATION_INPUT_SALT, salt.data(), salt.size()); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); } - status = psa_key_derivation_input_bytes(&operation, PSA_KEY_DERIVATION_INPUT_SECRET, secret, secret_length); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); + status = psa_key_derivation_input_key(&mOperation, PSA_KEY_DERIVATION_INPUT_SECRET, mSecretKeyId); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); - status = psa_key_derivation_input_bytes(&operation, PSA_KEY_DERIVATION_INPUT_INFO, info, info_length); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); + status = psa_key_derivation_input_bytes(&mOperation, PSA_KEY_DERIVATION_INPUT_INFO, info.data(), info.size()); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); - status = psa_key_derivation_output_bytes(&operation, out_buffer, out_length); - VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); + return CHIP_NO_ERROR; +} -exit: - psa_key_derivation_abort(&operation); +CHIP_ERROR PsaKdf::DeriveBytes(const MutableByteSpan & output) +{ + psa_status_t status = psa_key_derivation_output_bytes(&mOperation, output.data(), output.size()); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); - return error; + return CHIP_NO_ERROR; +} + +CHIP_ERROR PsaKdf::DeriveKey(const psa_key_attributes_t & attributes, psa_key_id_t & keyId) +{ + psa_status_t status = psa_key_derivation_output_key(&attributes, &mOperation, &keyId); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR HKDF_sha::HKDF_SHA256(const uint8_t * secret, const size_t secret_length, const uint8_t * salt, const size_t salt_length, + const uint8_t * info, const size_t info_length, uint8_t * out_buffer, size_t out_length) +{ + VerifyOrReturnError(isBufferNonEmpty(secret, secret_length), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(isBufferNonEmpty(info, info_length), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(isBufferNonEmpty(out_buffer, out_length), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(salt != nullptr || salt_length == 0, CHIP_ERROR_INVALID_ARGUMENT); + + PsaKdf kdf; + + ReturnErrorOnFailure(kdf.Init(PSA_ALG_HKDF(PSA_ALG_SHA_256), ByteSpan(secret, secret_length), ByteSpan(salt, salt_length), + ByteSpan(info, info_length))); + + return kdf.DeriveBytes(MutableByteSpan(out_buffer, out_length)); } CHIP_ERROR HMAC_sha::HMAC_SHA256(const uint8_t * key, size_t key_length, const uint8_t * message, size_t message_length, @@ -508,7 +505,7 @@ CHIP_ERROR P256Keypair::ECDSA_sign_msg(const uint8_t * msg, const size_t msg_len CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_SUCCESS; size_t outputLen = 0; - const PSAP256KeypairContext & context = toConstPSAContext(mKeypair); + const PsaP256KeypairContext & context = ToConstPsaContext(mKeypair); status = psa_sign_message(context.key_id, PSA_ALG_ECDSA(PSA_ALG_SHA_256), msg, msg_length, out_signature.Bytes(), out_signature.Capacity(), &outputLen); @@ -585,7 +582,7 @@ CHIP_ERROR P256Keypair::ECDH_derive_secret(const P256PublicKey & remote_public_k CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_SUCCESS; - const PSAP256KeypairContext & context = toConstPSAContext(mKeypair); + const PsaP256KeypairContext & context = ToConstPsaContext(mKeypair); const size_t outputSize = (out_secret.Length() == 0) ? out_secret.Capacity() : out_secret.Length(); size_t outputLength; @@ -638,7 +635,7 @@ CHIP_ERROR P256Keypair::Initialize(ECPKeyTarget key_target) CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_SUCCESS; psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; - PSAP256KeypairContext & context = toPSAContext(mKeypair); + PsaP256KeypairContext & context = ToPsaContext(mKeypair); size_t publicKeyLength = 0; // Type based on ECC with the elliptic curve SECP256r1 -> PSA_ECC_FAMILY_SECP_R1 @@ -680,7 +677,7 @@ CHIP_ERROR P256Keypair::Serialize(P256SerializedKeypair & output) const { CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_SUCCESS; - const PSAP256KeypairContext & context = toConstPSAContext(mKeypair); + const PsaP256KeypairContext & context = ToConstPsaContext(mKeypair); const size_t outputSize = output.Length() == 0 ? output.Capacity() : output.Length(); Encoding::BufferWriter bbuf(output.Bytes(), outputSize); uint8_t privateKey[kP256_PrivateKey_Length]; @@ -708,7 +705,7 @@ CHIP_ERROR P256Keypair::Deserialize(P256SerializedKeypair & input) CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_SUCCESS; psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; - PSAP256KeypairContext & context = toPSAContext(mKeypair); + PsaP256KeypairContext & context = ToPsaContext(mKeypair); Encoding::BufferWriter bbuf(mPublicKey, mPublicKey.Length()); Clear(); @@ -736,7 +733,7 @@ void P256Keypair::Clear() { if (mInitialized) { - PSAP256KeypairContext & context = toPSAContext(mKeypair); + PsaP256KeypairContext & context = ToPsaContext(mKeypair); psa_destroy_key(context.key_id); memset(&context, 0, sizeof(context)); mInitialized = false; @@ -1710,5 +1707,84 @@ CHIP_ERROR ExtractVIDPIDFromX509Cert(const ByteSpan & certificate, AttestationCe return error; } +namespace { + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +CHIP_ERROR ExtractRawSubjectFromX509Cert(const ByteSpan & certificate, MutableByteSpan & subject) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + uint8_t * p = nullptr; + size_t len = 0; + mbedtls_x509_crt mbedCertificate; + + ReturnErrorCodeIf(certificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); + + mbedtls_x509_crt_init(&mbedCertificate); + result = mbedtls_x509_crt_parse(&mbedCertificate, Uint8::to_const_uchar(certificate.data()), certificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + len = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(subject_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(len); + p = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(subject_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(p); + + VerifyOrExit(len <= subject.size(), error = CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(subject.data(), p, len); + subject.reduce_size(len); + +exit: + logMbedTLSError(result); + mbedtls_x509_crt_free(&mbedCertificate); + + return error; +} +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + +} // namespace + +CHIP_ERROR ReplaceCertIfResignedCertFound(const ByteSpan & referenceCertificate, const ByteSpan * candidateCertificates, + size_t candidateCertificatesCount, ByteSpan & outCertificate) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + constexpr size_t kMaxCertificateSubjectLength = 150; + uint8_t referenceSubjectBuf[kMaxCertificateSubjectLength]; + uint8_t referenceSKIDBuf[kSubjectKeyIdentifierLength]; + MutableByteSpan referenceSubject(referenceSubjectBuf); + MutableByteSpan referenceSKID(referenceSKIDBuf); + + outCertificate = referenceCertificate; + + ReturnErrorCodeIf(candidateCertificates == nullptr || candidateCertificatesCount == 0, CHIP_NO_ERROR); + + ReturnErrorOnFailure(ExtractRawSubjectFromX509Cert(referenceCertificate, referenceSubject)); + ReturnErrorOnFailure(ExtractSKIDFromX509Cert(referenceCertificate, referenceSKID)); + + for (size_t i = 0; i < candidateCertificatesCount; i++) + { + const ByteSpan candidateCertificate = candidateCertificates[i]; + uint8_t candidateSubjectBuf[kMaxCertificateSubjectLength]; + uint8_t candidateSKIDBuf[kSubjectKeyIdentifierLength]; + MutableByteSpan candidateSubject(candidateSubjectBuf); + MutableByteSpan candidateSKID(candidateSKIDBuf); + + ReturnErrorOnFailure(ExtractRawSubjectFromX509Cert(candidateCertificate, candidateSubject)); + ReturnErrorOnFailure(ExtractSKIDFromX509Cert(candidateCertificate, candidateSKID)); + + if (referenceSKID.data_equal(candidateSKID) && referenceSubject.data_equal(candidateSubject)) + { + outCertificate = candidateCertificate; + return CHIP_NO_ERROR; + } + } + + return CHIP_NO_ERROR; +#else + (void) referenceCertificate; + (void) candidateCertificates; + (void) candidateCertificatesCount; + (void) outCertificate; + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) +} + } // namespace Crypto } // namespace chip diff --git a/src/crypto/CHIPCryptoPALPSA.h b/src/crypto/CHIPCryptoPALPSA.h index 6774cef7622378..1a64c1f8794dfa 100644 --- a/src/crypto/CHIPCryptoPALPSA.h +++ b/src/crypto/CHIPCryptoPALPSA.h @@ -16,6 +16,14 @@ * limitations under the License. */ +/** + * @file + * Header file that contains private definitions used by PSA crypto backend. + * + * This file should not be included directly by the application. Instead, use + * cryptographic primitives defined in CHIPCryptoPAL.h or SessionKeystore.h. + */ + #pragma once #include "CHIPCryptoPAL.h" @@ -28,54 +36,112 @@ namespace chip { namespace Crypto { /** - * @def CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE + * @def CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE * - * @brief - * Base for PSA key identifier range used by Matter. + * @brief + * Base of the PSA key identifier range used by Matter. * - * Cryptographic keys stored in the PSA Internal Trusted Storage must have - * a user-assigned identifer from the range PSA_KEY_ID_USER_MIN to - * PSA_KEY_ID_USER_MAX. This option allows to override the base used to derive - * key identifiers used by Matter to avoid overlapping with other firmware - * components that also use PSA crypto API. The default value was selected - * not to interfere with OpenThread's default base that is 0x20000. + * Cryptographic keys stored in the PSA Internal Trusted Storage must have + * a user-assigned identifer from the range PSA_KEY_ID_USER_MIN to + * PSA_KEY_ID_USER_MAX. This option allows to override the base used to derive + * key identifiers used by Matter to avoid overlapping with other firmware + * components that also use PSA crypto API. The default value was selected + * not to interfere with OpenThread's default base that is 0x20000. * - * Note that volatile keys like ephemeral keys used for ECDH have identifiers - * auto-assigned by the PSA backend. + * Note that volatile keys like ephemeral keys used for ECDH have identifiers + * auto-assigned by the PSA backend. */ #ifndef CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE #define CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE 0x30000 #endif // CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE -static_assert(CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE >= PSA_KEY_ID_USER_MIN && - CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE <= PSA_KEY_ID_USER_MAX, - "PSA key ID base out of allowed range"); - +/** + * @brief Defines subranges of the PSA key identifier space used by Matter. + */ enum class KeyIdBase : psa_key_id_t { - // Define key ID range for Node Operational Certificate private keys - Operational = CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE + Minimum = CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE, + Operational = Minimum, ///< Base of the PSA key ID range for Node Operational Certificate private keys + Maximum = Operational + kMaxValidFabricIndex, }; +static_assert(to_underlying(KeyIdBase::Minimum) >= PSA_KEY_ID_USER_MIN && to_underlying(KeyIdBase::Maximum) <= PSA_KEY_ID_USER_MAX, + "PSA key ID base out of allowed range"); + +/** + * @brief Calculates PSA key ID for Node Operational Certificate private key for the given fabric. + */ constexpr psa_key_id_t MakeOperationalKeyId(FabricIndex fabricIndex) { return to_underlying(KeyIdBase::Operational) + static_cast(fabricIndex); } -struct PSAP256KeypairContext +/** + * @brief Concrete P256 keypair context used by PSA crypto backend. + */ +struct PsaP256KeypairContext { psa_key_id_t key_id; }; -static inline PSAP256KeypairContext & toPSAContext(P256KeypairContext & context) +inline PsaP256KeypairContext & ToPsaContext(P256KeypairContext & context) { - return *SafePointerCast(&context); + return *SafePointerCast(&context); } -static inline const PSAP256KeypairContext & toConstPSAContext(const P256KeypairContext & context) +inline const PsaP256KeypairContext & ToConstPsaContext(const P256KeypairContext & context) { - return *SafePointerCast(&context); + return *SafePointerCast(&context); } +/** + * @brief Wrapper for PSA key derivation API. + */ +class PsaKdf +{ +public: + ~PsaKdf() + { + psa_key_derivation_abort(&mOperation); + psa_destroy_key(mSecretKeyId); + } + + /** + * @brief Initializes the key derivation operation. + */ + CHIP_ERROR Init(psa_algorithm_t algorithm, const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info); + + /** + * @brief Derives raw key material from the operation. + * + * This method together with @p DeriveKeys can be called multiple times to + * derive several keys. + * + * @param[out] output Span that provides location and length for the derived key material. + * + * @retval CHIP_NO_ERROR On success. + * @retval CHIP_ERROR_INTERNAL On PSA crypto API error. + */ + CHIP_ERROR DeriveBytes(const MutableByteSpan & output); + + /** + * @brief Derives a key from the operation. + * + * This method together with @p DeriveBytes can be called multiple times to + * derive several keys. + * + * @param[in] attributes Attributes of the derived key. + * @param[out] keyId PSA key ID of the derived key. + * + * @retval CHIP_NO_ERROR On success. + * @retval CHIP_ERROR_INTERNAL On PSA crypto API error. + */ + CHIP_ERROR DeriveKey(const psa_key_attributes_t & attributes, psa_key_id_t & keyId); + +private: + psa_key_id_t mSecretKeyId = 0; + psa_key_derivation_operation_t mOperation = PSA_KEY_DERIVATION_OPERATION_INIT; +}; + } // namespace Crypto } // namespace chip diff --git a/src/crypto/CHIPCryptoPALmbedTLS.cpp b/src/crypto/CHIPCryptoPALmbedTLS.cpp index 95c44e43a0241c..324f9a3aca442a 100644 --- a/src/crypto/CHIPCryptoPALmbedTLS.cpp +++ b/src/crypto/CHIPCryptoPALmbedTLS.cpp @@ -108,18 +108,8 @@ static bool _isValidTagLength(size_t tag_length) return false; } -static bool _isValidKeyLength(size_t length) -{ - // 16 bytes key for AES-CCM-128, 32 for AES-CCM-256 - if (length == 16 || length == 32) - { - return true; - } - return false; -} - CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, const uint8_t * aad, size_t aad_length, - const uint8_t * key, size_t key_length, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, + const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, uint8_t * tag, size_t tag_length) { CHIP_ERROR error = CHIP_NO_ERROR; @@ -130,8 +120,6 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c VerifyOrExit(plaintext != nullptr || plaintext_length == 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(ciphertext != nullptr || plaintext_length == 0, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(_isValidKeyLength(key_length), error = CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE); VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(tag != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); @@ -141,10 +129,8 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c VerifyOrExit(aad != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); } - // Size of key = key_length * number of bits in a byte (8) - // Cast is safe because we called _isValidKeyLength above. - result = - mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, Uint8::to_const_uchar(key), static_cast(key_length * 8)); + // Size of key is expressed in bits, hence the multiplication by 8. + result = mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, key.As(), sizeof(Aes128KeyByteArray) * 8); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); // Encrypt @@ -160,7 +146,7 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c } CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, const uint8_t * aad, size_t aad_len, - const uint8_t * tag, size_t tag_length, const uint8_t * key, size_t key_length, const uint8_t * nonce, + const uint8_t * tag, size_t tag_length, const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * plaintext) { CHIP_ERROR error = CHIP_NO_ERROR; @@ -173,8 +159,6 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, co VerifyOrExit(ciphertext != nullptr || ciphertext_len == 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(tag != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(_isValidTagLength(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(_isValidKeyLength(key_length), error = CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE); VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); if (aad_len > 0) @@ -182,10 +166,8 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, co VerifyOrExit(aad != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); } - // Size of key = key_length * number of bits in a byte (8) - // Cast is safe because we called _isValidKeyLength above. - result = - mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, Uint8::to_const_uchar(key), static_cast(key_length * 8)); + // Size of key is expressed in bits, hence the multiplication by 8. + result = mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, key.As(), sizeof(Aes128KeyByteArray) * 8); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); // Decrypt diff --git a/src/crypto/DefaultSessionKeystore.h b/src/crypto/DefaultSessionKeystore.h new file mode 100644 index 00000000000000..8fa6121af5c20d --- /dev/null +++ b/src/crypto/DefaultSessionKeystore.h @@ -0,0 +1,44 @@ +/* + * 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 + +#if CHIP_HAVE_CONFIG_H +#include +#endif + +#if CHIP_CRYPTO_PSA +#include +#else +#include +#endif + +namespace chip { +namespace Crypto { + +// Define DefaultSessionKeystore type alias to reduce boilerplate code related to the fact that +// when the PSA crypto backend is used, AES encryption/decryption function assume that the input +// key handle carries a key reference instead of raw key material, so PSASessionKeystore must be +// used instead of RawKeySessionKeystore to initialize the key handle. +#if CHIP_CRYPTO_PSA +using DefaultSessionKeystore = PSASessionKeystore; +#else +using DefaultSessionKeystore = RawKeySessionKeystore; +#endif + +} // namespace Crypto +} // namespace chip diff --git a/src/crypto/PSAOperationalKeystore.cpp b/src/crypto/PSAOperationalKeystore.cpp index ccde8d1d9eb667..980994696e3047 100644 --- a/src/crypto/PSAOperationalKeystore.cpp +++ b/src/crypto/PSAOperationalKeystore.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Project CHIP Authors + * Copyright (c) 2023 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,7 +26,7 @@ namespace Crypto { PSAOperationalKeystore::PersistentP256Keypair::PersistentP256Keypair(FabricIndex fabricIndex) { - toPSAContext(mKeypair).key_id = MakeOperationalKeyId(fabricIndex); + ToPsaContext(mKeypair).key_id = MakeOperationalKeyId(fabricIndex); mInitialized = true; } @@ -34,12 +34,12 @@ PSAOperationalKeystore::PersistentP256Keypair::~PersistentP256Keypair() { // This class requires explicit control of the key lifetime. Therefore, clear the key ID // to prevent it from being destroyed by the base class destructor. - toPSAContext(mKeypair).key_id = 0; + ToPsaContext(mKeypair).key_id = 0; } inline psa_key_id_t PSAOperationalKeystore::PersistentP256Keypair::GetKeyId() const { - return toConstPSAContext(mKeypair).key_id; + return ToConstPsaContext(mKeypair).key_id; } bool PSAOperationalKeystore::PersistentP256Keypair::Exists() const diff --git a/src/crypto/PSAOperationalKeystore.h b/src/crypto/PSAOperationalKeystore.h index a0937c257e2ca8..89c3edc7fe7607 100644 --- a/src/crypto/PSAOperationalKeystore.h +++ b/src/crypto/PSAOperationalKeystore.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Project CHIP Authors + * Copyright (c) 2023 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/crypto/PSASessionKeystore.cpp b/src/crypto/PSASessionKeystore.cpp new file mode 100644 index 00000000000000..0b8a237a9d405b --- /dev/null +++ b/src/crypto/PSASessionKeystore.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2023 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 "PSASessionKeystore.h" + +#include + +#include + +namespace chip { +namespace Crypto { + +namespace { + +class AesKeyAttributes +{ +public: + AesKeyAttributes() + { + constexpr psa_algorithm_t kAlgorithm = PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_CCM, 8); + + psa_set_key_type(&mAttrs, PSA_KEY_TYPE_AES); + psa_set_key_algorithm(&mAttrs, kAlgorithm); + psa_set_key_usage_flags(&mAttrs, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_bits(&mAttrs, CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES * 8); + } + + ~AesKeyAttributes() { psa_reset_key_attributes(&mAttrs); } + + const psa_key_attributes_t & Get() { return mAttrs; } + +private: + psa_key_attributes_t mAttrs = PSA_KEY_ATTRIBUTES_INIT; +}; + +} // namespace + +CHIP_ERROR PSASessionKeystore::CreateKey(const Aes128KeyByteArray & keyMaterial, Aes128KeyHandle & key) +{ + // Destroy the old key if already allocated + psa_destroy_key(key.As()); + + AesKeyAttributes attrs; + psa_status_t status = psa_import_key(&attrs.Get(), keyMaterial, sizeof(Aes128KeyByteArray), &key.AsMutable()); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR PSASessionKeystore::DeriveKey(const P256ECDHDerivedSecret & secret, const ByteSpan & salt, const ByteSpan & info, + Aes128KeyHandle & key) +{ + PsaKdf kdf; + ReturnErrorOnFailure(kdf.Init(PSA_ALG_HKDF(PSA_ALG_SHA_256), secret.Span(), salt, info)); + + AesKeyAttributes attrs; + + return kdf.DeriveKey(attrs.Get(), key.AsMutable()); +} + +CHIP_ERROR PSASessionKeystore::DeriveSessionKeys(const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info, + Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey, + AttestationChallenge & attestationChallenge) +{ + PsaKdf kdf; + ReturnErrorOnFailure(kdf.Init(PSA_ALG_HKDF(PSA_ALG_SHA_256), secret, salt, info)); + + CHIP_ERROR error; + AesKeyAttributes attrs; + + SuccessOrExit(error = kdf.DeriveKey(attrs.Get(), i2rKey.AsMutable())); + SuccessOrExit(error = kdf.DeriveKey(attrs.Get(), r2iKey.AsMutable())); + SuccessOrExit(error = kdf.DeriveBytes(MutableByteSpan(attestationChallenge.Bytes(), AttestationChallenge::Capacity()))); + +exit: + if (error != CHIP_NO_ERROR) + { + DestroyKey(i2rKey); + DestroyKey(r2iKey); + } + + return error; +} + +void PSASessionKeystore::DestroyKey(Aes128KeyHandle & key) +{ + auto & keyId = key.AsMutable(); + + psa_destroy_key(keyId); + keyId = 0; +} + +} // namespace Crypto +} // namespace chip diff --git a/src/crypto/PSASessionKeystore.h b/src/crypto/PSASessionKeystore.h new file mode 100644 index 00000000000000..c448d7923a6575 --- /dev/null +++ b/src/crypto/PSASessionKeystore.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 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 + +namespace chip { +namespace Crypto { + +class PSASessionKeystore : public SessionKeystore +{ +public: + CHIP_ERROR CreateKey(const Aes128KeyByteArray & keyMaterial, Aes128KeyHandle & key) override; + CHIP_ERROR DeriveKey(const P256ECDHDerivedSecret & secret, const ByteSpan & salt, const ByteSpan & info, + Aes128KeyHandle & key) override; + CHIP_ERROR DeriveSessionKeys(const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info, Aes128KeyHandle & i2rKey, + Aes128KeyHandle & r2iKey, AttestationChallenge & attestationChallenge) override; + void DestroyKey(Aes128KeyHandle & key) override; +}; + +} // namespace Crypto +} // namespace chip diff --git a/src/crypto/RawKeySessionKeystore.cpp b/src/crypto/RawKeySessionKeystore.cpp new file mode 100644 index 00000000000000..97666569345d04 --- /dev/null +++ b/src/crypto/RawKeySessionKeystore.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023 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 + +namespace chip { +namespace Crypto { + +#ifdef ENABLE_HSM_HKDF +using HKDF_sha_crypto = HKDF_shaHSM; +#else +using HKDF_sha_crypto = HKDF_sha; +#endif + +CHIP_ERROR RawKeySessionKeystore::CreateKey(const Aes128KeyByteArray & keyMaterial, Aes128KeyHandle & key) +{ + memcpy(key.AsMutable(), keyMaterial, sizeof(Aes128KeyByteArray)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR RawKeySessionKeystore::DeriveKey(const P256ECDHDerivedSecret & secret, const ByteSpan & salt, const ByteSpan & info, + Aes128KeyHandle & key) +{ + HKDF_sha_crypto hkdf; + + return hkdf.HKDF_SHA256(secret.ConstBytes(), secret.Length(), salt.data(), salt.size(), info.data(), info.size(), + key.AsMutable(), sizeof(Aes128KeyByteArray)); +} + +CHIP_ERROR RawKeySessionKeystore::DeriveSessionKeys(const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info, + Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey, + AttestationChallenge & attestationChallenge) +{ + HKDF_sha_crypto hkdf; + uint8_t keyMaterial[2 * sizeof(Aes128KeyByteArray) + AttestationChallenge::Capacity()]; + + ReturnErrorOnFailure(hkdf.HKDF_SHA256(secret.data(), secret.size(), salt.data(), salt.size(), info.data(), info.size(), + keyMaterial, sizeof(keyMaterial))); + + Encoding::LittleEndian::Reader reader(keyMaterial, sizeof(keyMaterial)); + + return reader.ReadBytes(i2rKey.AsMutable(), sizeof(Aes128KeyByteArray)) + .ReadBytes(r2iKey.AsMutable(), sizeof(Aes128KeyByteArray)) + .ReadBytes(attestationChallenge.Bytes(), AttestationChallenge::Capacity()) + .StatusCode(); +} + +void RawKeySessionKeystore::DestroyKey(Aes128KeyHandle & key) +{ + ClearSecretData(key.AsMutable()); +} + +} // namespace Crypto +} // namespace chip diff --git a/src/crypto/RawKeySessionKeystore.h b/src/crypto/RawKeySessionKeystore.h new file mode 100644 index 00000000000000..c8db3eda069518 --- /dev/null +++ b/src/crypto/RawKeySessionKeystore.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 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 + +namespace chip { +namespace Crypto { + +class RawKeySessionKeystore : public SessionKeystore +{ +public: + CHIP_ERROR CreateKey(const Aes128KeyByteArray & keyMaterial, Aes128KeyHandle & key) override; + CHIP_ERROR DeriveKey(const P256ECDHDerivedSecret & secret, const ByteSpan & salt, const ByteSpan & info, + Aes128KeyHandle & key) override; + CHIP_ERROR DeriveSessionKeys(const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info, Aes128KeyHandle & i2rKey, + Aes128KeyHandle & r2iKey, AttestationChallenge & attestationChallenge) override; + void DestroyKey(Aes128KeyHandle & key) override; +}; + +} // namespace Crypto +} // namespace chip diff --git a/src/crypto/SessionKeystore.h b/src/crypto/SessionKeystore.h new file mode 100644 index 00000000000000..edc31dc788dbea --- /dev/null +++ b/src/crypto/SessionKeystore.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2023 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 + +namespace chip { +namespace Crypto { + +/** + * @brief Interface for deriving session keys and managing their lifetime. + * + * The session keystore interface provides an abstraction that allows the application to store + * session keys in a secure environment. It uses the concept of key handles that isolate the + * application from the actual key material. + */ +class SessionKeystore +{ +public: + virtual ~SessionKeystore() {} + + /** + * @brief Import raw key material and return a key handle. + * + * @note This method should only be used when using the raw key material in the Matter stack + * cannot be avoided. Ideally, crypto interfaces should allow platforms to perform all the + * cryptographic operations in a secure environment. + * + * If the method returns no error, the application is responsible for destroying the handle + * using DestroyKey() method when the key is no longer needed. + */ + virtual CHIP_ERROR CreateKey(const Aes128KeyByteArray & keyMaterial, Aes128KeyHandle & key) = 0; + + /** + * @brief Derive key from a shared secret. + * + * Use HKDF as defined in the Matter specification to derive an AES key from the shared secret. + * + * If the method returns no error, the application is responsible for destroying the handle + * using DestroyKey() method when the key is no longer needed. + */ + virtual CHIP_ERROR DeriveKey(const P256ECDHDerivedSecret & secret, const ByteSpan & salt, const ByteSpan & info, + Aes128KeyHandle & key) = 0; + + /** + * @brief Derive session keys from a shared secret. + * + * Use HKDF as defined in the Matter specification to derive AES keys for both directions, and + * the attestation challenge from the shared secret. + * + * If the method returns no error, the application is responsible for destroying the handles + * using DestroyKey() method when the keys are no longer needed. On failure, the method must + * release all handles that it allocated so far. + */ + virtual CHIP_ERROR DeriveSessionKeys(const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info, + Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey, + AttestationChallenge & attestationChallenge) = 0; + + /** + * @brief Destroy key. + * + * The method can take an uninitialized handle in which case it is a no-op. + * As a result of calling this method, the handle is put in the uninitialized state. + */ + virtual void DestroyKey(Aes128KeyHandle & key) = 0; +}; + +/** + * @brief RAII class to hold a temporary key handle that is destroyed on scope exit. + */ +class AutoReleaseSessionKey +{ +public: + explicit AutoReleaseSessionKey(SessionKeystore & keystore) : mKeystore(keystore) {} + ~AutoReleaseSessionKey() { mKeystore.DestroyKey(mKeyHandle); } + + Aes128KeyHandle & KeyHandle() { return mKeyHandle; } + +private: + SessionKeystore & mKeystore; + Aes128KeyHandle mKeyHandle; +}; + +} // namespace Crypto +} // namespace chip diff --git a/src/crypto/tests/BUILD.gn b/src/crypto/tests/BUILD.gn index 7e41fe86fae985..ea3ee0dc124f5d 100644 --- a/src/crypto/tests/BUILD.gn +++ b/src/crypto/tests/BUILD.gn @@ -44,7 +44,10 @@ chip_test_suite("tests") { "TestCryptoLayer.h", ] - test_sources = [ "TestGroupOperationalCredentials.cpp" ] + test_sources = [ + "TestGroupOperationalCredentials.cpp", + "TestSessionKeystore.cpp", + ] if (chip_crypto == "psa") { test_sources += [ "TestPSAOpKeyStore.cpp" ] diff --git a/src/crypto/tests/CHIPCryptoPALTest.cpp b/src/crypto/tests/CHIPCryptoPALTest.cpp index ffbc72090f5beb..f8d4b476d719c4 100644 --- a/src/crypto/tests/CHIPCryptoPALTest.cpp +++ b/src/crypto/tests/CHIPCryptoPALTest.cpp @@ -36,6 +36,7 @@ #include "SPAKE2P_RFC_test_vectors.h" #include +#include #if CHIP_CRYPTO_HSM #include #endif @@ -165,6 +166,9 @@ static int test_entropy_source(void * data, uint8_t * output, size_t len, size_t return 0; } +constexpr size_t KEY_LENGTH = Crypto::kAES_CCM128_Key_Length; +constexpr size_t NONCE_LENGTH = Crypto::kAES_CCM128_Nonce_Length; + struct AesCtrTestEntry { const uint8_t * key; ///< Key to use for AES-CTR-128 encryption/decryption -- 16 byte length @@ -204,8 +208,23 @@ const AesCtrTestEntry theAesCtrTestVector[] = { } }; -constexpr size_t KEY_LENGTH = Crypto::kAES_CCM128_Key_Length; -constexpr size_t NONCE_LENGTH = Crypto::kAES_CCM128_Nonce_Length; +struct TestAesKey +{ +public: + TestAesKey(nlTestSuite * inSuite, const uint8_t * keyBytes, size_t keyLength) + { + Crypto::Aes128KeyByteArray keyMaterial; + memcpy(&keyMaterial, keyBytes, keyLength); + + CHIP_ERROR err = keystore.CreateKey(keyMaterial, key); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + } + + ~TestAesKey() { keystore.DestroyKey(key); } + + DefaultSessionKeystore keystore; + Aes128KeyHandle key; +}; static void TestAES_CTR_128_Encrypt(nlTestSuite * inSuite, const AesCtrTestEntry * vector) { @@ -213,8 +232,9 @@ static void TestAES_CTR_128_Encrypt(nlTestSuite * inSuite, const AesCtrTestEntry outBuffer.Alloc(vector->ciphertextLen); NL_TEST_ASSERT(inSuite, outBuffer); - CHIP_ERROR err = AES_CTR_crypt(vector->plaintext, vector->plaintextLen, vector->key, KEY_LENGTH, vector->nonce, NONCE_LENGTH, - outBuffer.Get()); + TestAesKey key(inSuite, vector->key, KEY_LENGTH); + + CHIP_ERROR err = AES_CTR_crypt(vector->plaintext, vector->plaintextLen, key.key, vector->nonce, NONCE_LENGTH, outBuffer.Get()); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); bool outputMatches = memcmp(outBuffer.Get(), vector->ciphertext, vector->ciphertextLen) == 0; @@ -231,8 +251,10 @@ static void TestAES_CTR_128_Decrypt(nlTestSuite * inSuite, const AesCtrTestEntry outBuffer.Alloc(vector->plaintextLen); NL_TEST_ASSERT(inSuite, outBuffer); - CHIP_ERROR err = AES_CTR_crypt(vector->ciphertext, vector->ciphertextLen, vector->key, KEY_LENGTH, vector->nonce, NONCE_LENGTH, - outBuffer.Get()); + TestAesKey key(inSuite, vector->key, KEY_LENGTH); + + CHIP_ERROR err = + AES_CTR_crypt(vector->ciphertext, vector->ciphertextLen, key.key, vector->nonce, NONCE_LENGTH, outBuffer.Get()); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); bool outputMatches = memcmp(outBuffer.Get(), vector->plaintext, vector->plaintextLen) == 0; @@ -277,8 +299,10 @@ static void TestAES_CCM_128EncryptTestVectors(nlTestSuite * inSuite, void * inCo out_tag.Alloc(vector->tag_len); NL_TEST_ASSERT(inSuite, out_tag); - CHIP_ERROR err = AES_CCM_encrypt(vector->pt, vector->pt_len, vector->aad, vector->aad_len, vector->key, vector->key_len, - vector->nonce, vector->nonce_len, out_ct.Get(), out_tag.Get(), vector->tag_len); + TestAesKey key(inSuite, vector->key, vector->key_len); + + CHIP_ERROR err = AES_CCM_encrypt(vector->pt, vector->pt_len, vector->aad, vector->aad_len, key.key, vector->nonce, + vector->nonce_len, out_ct.Get(), out_tag.Get(), vector->tag_len); NL_TEST_ASSERT(inSuite, err == vector->result); if (vector->result == CHIP_NO_ERROR) @@ -315,8 +339,11 @@ static void TestAES_CCM_128DecryptTestVectors(nlTestSuite * inSuite, void * inCo chip::Platform::ScopedMemoryBuffer out_pt; out_pt.Alloc(vector->pt_len); NL_TEST_ASSERT(inSuite, out_pt); + + TestAesKey key(inSuite, vector->key, vector->key_len); + CHIP_ERROR err = AES_CCM_decrypt(vector->ct, vector->ct_len, vector->aad, vector->aad_len, vector->tag, vector->tag_len, - vector->key, vector->key_len, vector->nonce, vector->nonce_len, out_pt.Get()); + key.key, vector->nonce, vector->nonce_len, out_pt.Get()); NL_TEST_ASSERT(inSuite, err == vector->result); if (vector->result == CHIP_NO_ERROR) @@ -333,33 +360,6 @@ static void TestAES_CCM_128DecryptTestVectors(nlTestSuite * inSuite, void * inCo NL_TEST_ASSERT(inSuite, numOfTestsRan > 0); } -static void TestAES_CCM_128EncryptNilKey(nlTestSuite * inSuite, void * inContext) -{ - HeapChecker heapChecker(inSuite); - int numOfTestVectors = ArraySize(ccm_128_test_vectors); - int numOfTestsRan = 0; - for (int vectorIndex = 0; vectorIndex < numOfTestVectors; vectorIndex++) - { - const ccm_128_test_vector * vector = ccm_128_test_vectors[vectorIndex]; - if (vector->pt_len > 0) - { - numOfTestsRan++; - chip::Platform::ScopedMemoryBuffer out_ct; - out_ct.Alloc(vector->ct_len); - NL_TEST_ASSERT(inSuite, out_ct); - chip::Platform::ScopedMemoryBuffer out_tag; - out_tag.Alloc(vector->tag_len); - NL_TEST_ASSERT(inSuite, out_tag); - - CHIP_ERROR err = AES_CCM_encrypt(vector->pt, vector->pt_len, vector->aad, vector->aad_len, nullptr, 0, vector->nonce, - vector->nonce_len, out_ct.Get(), out_tag.Get(), vector->tag_len); - NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_INVALID_ARGUMENT); - break; - } - } - NL_TEST_ASSERT(inSuite, numOfTestsRan > 0); -} - static void TestAES_CCM_128EncryptInvalidNonceLen(nlTestSuite * inSuite, void * inContext) { HeapChecker heapChecker(inSuite); @@ -378,8 +378,10 @@ static void TestAES_CCM_128EncryptInvalidNonceLen(nlTestSuite * inSuite, void * out_tag.Alloc(vector->tag_len); NL_TEST_ASSERT(inSuite, out_tag); - CHIP_ERROR err = AES_CCM_encrypt(vector->pt, vector->pt_len, vector->aad, vector->aad_len, vector->key, vector->key_len, - vector->nonce, 0, out_ct.Get(), out_tag.Get(), vector->tag_len); + TestAesKey key(inSuite, vector->key, vector->key_len); + + CHIP_ERROR err = AES_CCM_encrypt(vector->pt, vector->pt_len, vector->aad, vector->aad_len, key.key, vector->nonce, 0, + out_ct.Get(), out_tag.Get(), vector->tag_len); NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_INVALID_ARGUMENT); break; } @@ -405,31 +407,10 @@ static void TestAES_CCM_128EncryptInvalidTagLen(nlTestSuite * inSuite, void * in out_tag.Alloc(vector->tag_len); NL_TEST_ASSERT(inSuite, out_tag); - CHIP_ERROR err = AES_CCM_encrypt(vector->pt, vector->pt_len, vector->aad, vector->aad_len, vector->key, vector->key_len, - vector->nonce, vector->nonce_len, out_ct.Get(), out_tag.Get(), 13); - NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_INVALID_ARGUMENT); - break; - } - } - NL_TEST_ASSERT(inSuite, numOfTestsRan > 0); -} + TestAesKey key(inSuite, vector->key, vector->key_len); -static void TestAES_CCM_128DecryptInvalidKey(nlTestSuite * inSuite, void * inContext) -{ - HeapChecker heapChecker(inSuite); - int numOfTestVectors = ArraySize(ccm_128_test_vectors); - int numOfTestsRan = 0; - for (int vectorIndex = 0; vectorIndex < numOfTestVectors; vectorIndex++) - { - const ccm_128_test_vector * vector = ccm_128_test_vectors[vectorIndex]; - if (vector->pt_len > 0) - { - numOfTestsRan++; - Platform::ScopedMemoryBuffer out_pt; - out_pt.Alloc(vector->pt_len); - NL_TEST_ASSERT(inSuite, out_pt); - CHIP_ERROR err = AES_CCM_decrypt(vector->ct, vector->ct_len, vector->aad, vector->aad_len, vector->tag, vector->tag_len, - nullptr, 0, vector->nonce, vector->nonce_len, out_pt.Get()); + CHIP_ERROR err = AES_CCM_encrypt(vector->pt, vector->pt_len, vector->aad, vector->aad_len, key.key, vector->nonce, + vector->nonce_len, out_ct.Get(), out_tag.Get(), 13); NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_INVALID_ARGUMENT); break; } @@ -451,8 +432,11 @@ static void TestAES_CCM_128DecryptInvalidNonceLen(nlTestSuite * inSuite, void * Platform::ScopedMemoryBuffer out_pt; out_pt.Alloc(vector->pt_len); NL_TEST_ASSERT(inSuite, out_pt); + + TestAesKey key(inSuite, vector->key, vector->key_len); + CHIP_ERROR err = AES_CCM_decrypt(vector->ct, vector->ct_len, vector->aad, vector->aad_len, vector->tag, vector->tag_len, - vector->key, vector->key_len, vector->nonce, 0, out_pt.Get()); + key.key, vector->nonce, 0, out_pt.Get()); NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_INVALID_ARGUMENT); break; } @@ -2531,10 +2515,8 @@ static const nlTest sTests[] = { NL_TEST_DEF("Test encrypting AES-CCM-128 test vectors", TestAES_CCM_128EncryptTestVectors), NL_TEST_DEF("Test decrypting AES-CCM-128 test vectors", TestAES_CCM_128DecryptTestVectors), - NL_TEST_DEF("Test encrypting AES-CCM-128 using nil key", TestAES_CCM_128EncryptNilKey), NL_TEST_DEF("Test encrypting AES-CCM-128 using invalid nonce", TestAES_CCM_128EncryptInvalidNonceLen), NL_TEST_DEF("Test encrypting AES-CCM-128 using invalid tag", TestAES_CCM_128EncryptInvalidTagLen), - NL_TEST_DEF("Test decrypting AES-CCM-128 invalid key", TestAES_CCM_128DecryptInvalidKey), NL_TEST_DEF("Test decrypting AES-CCM-128 invalid nonce", TestAES_CCM_128DecryptInvalidNonceLen), NL_TEST_DEF("Test encrypt/decrypt AES-CTR-128 test vectors", TestAES_CTR_128CryptTestVectors), NL_TEST_DEF("Test ASN.1 signature conversion routines", TestAsn1Conversions), diff --git a/src/crypto/tests/TestSessionKeystore.cpp b/src/crypto/tests/TestSessionKeystore.cpp new file mode 100644 index 00000000000000..a1f786d51a3dc1 --- /dev/null +++ b/src/crypto/tests/TestSessionKeystore.cpp @@ -0,0 +1,227 @@ +/* + * + * 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 "AES_CCM_128_test_vectors.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if CHIP_CRYPTO_PSA +#include +#endif + +using namespace chip; +using namespace chip::Crypto; + +namespace { + +using TestSessionKeystoreImpl = DefaultSessionKeystore; + +struct DeriveKeyTestVector +{ + // KDF parameters + const char * secret; + const char * salt; + const char * info; + // AES CTR input + uint8_t plaintext[16]; + uint8_t nonce[13]; + // Expected AES CTR output + uint8_t ciphertext[16]; +}; + +struct DeriveSessionKeysTestVector +{ + // KDF parameters + const char * secret; + const char * salt; + const char * info; + // AES CTR input + uint8_t plaintext[16]; + uint8_t nonce[13]; + // Expected AES CTR output + uint8_t i2rCiphertext[16]; + uint8_t r2iCiphertext[16]; + uint8_t attestationChallenge[16]; +}; + +DeriveKeyTestVector deriveKeyTestVectors[] = { + { + .secret = "secret", + .salt = "salt123", + .info = "info123", + // derived key: a134e284e8628486f4d620a711f3cb50 + .plaintext = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, + .nonce = { 0 }, + .ciphertext = { 0x62, 0x1b, 0x8d, 0x7a, 0x6f, 0xea, 0x7f, 0xca, 0x03, 0x64, 0x21, 0xb4, 0x3c, 0xbc, 0xa9, 0xbb }, + }, +}; + +DeriveSessionKeysTestVector deriveSessionKeysTestVectors[] = { + { + .secret = "secret", + .salt = "salt123", + .info = "info123", + // derived keys: a134e284e8628486f4d620a711f3cb50 + // 8a84a74c1550cf1dc57e5f8a099dcf37 + // 739184dd1465856473706661f5116be5 + .plaintext = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, + .nonce = { 0 }, + .i2rCiphertext = { 0x62, 0x1b, 0x8d, 0x7a, 0x6f, 0xea, 0x7f, 0xca, 0x03, 0x64, 0x21, 0xb4, 0x3c, 0xbc, 0xa9, 0xbb }, + .r2iCiphertext = { 0x65, 0x90, 0xf8, 0xab, 0x85, 0x55, 0x02, 0xcf, 0x87, 0xc5, 0xd9, 0x45, 0x75, 0xcd, 0xdb, 0x01 }, + .attestationChallenge = { 0x73, 0x91, 0x84, 0xdd, 0x14, 0x65, 0x85, 0x64, 0x73, 0x70, 0x66, 0x61, 0xf5, 0x11, 0x6b, 0xe5 }, + }, +}; + +ByteSpan ToSpan(const char * str) +{ + return ByteSpan(reinterpret_cast(str), strlen(str)); +} + +void TestBasicImport(nlTestSuite * inSuite, void * inContext) +{ + TestSessionKeystoreImpl keystore; + + // Verify that keys imported to the keystore behave as expected. + for (const ccm_128_test_vector * testPtr : ccm_128_test_vectors) + { + const ccm_128_test_vector & test = *testPtr; + + Aes128KeyByteArray keyMaterial; + memcpy(keyMaterial, test.key, test.key_len); + + Aes128KeyHandle keyHandle; + NL_TEST_ASSERT_SUCCESS(inSuite, keystore.CreateKey(keyMaterial, keyHandle)); + + Platform::ScopedMemoryBuffer ciphertext; + Platform::ScopedMemoryBuffer tag; + NL_TEST_ASSERT(inSuite, ciphertext.Alloc(test.ct_len)); + NL_TEST_ASSERT(inSuite, tag.Alloc(test.tag_len)); + NL_TEST_ASSERT(inSuite, + AES_CCM_encrypt(test.pt, test.pt_len, test.aad, test.aad_len, keyHandle, test.nonce, test.nonce_len, + ciphertext.Get(), tag.Get(), test.tag_len) == test.result); + NL_TEST_ASSERT(inSuite, memcmp(ciphertext.Get(), test.ct, test.ct_len) == 0); + NL_TEST_ASSERT(inSuite, memcmp(tag.Get(), test.tag, test.tag_len) == 0); + + keystore.DestroyKey(keyHandle); + } +} + +void TestDeriveKey(nlTestSuite * inSuite, void * inContext) +{ + TestSessionKeystoreImpl keystore; + + for (const DeriveKeyTestVector & test : deriveKeyTestVectors) + { + P256ECDHDerivedSecret secret; + memcpy(secret.Bytes(), test.secret, strlen(test.secret)); + secret.SetLength(strlen(test.secret)); + + Aes128KeyHandle keyHandle; + NL_TEST_ASSERT_SUCCESS(inSuite, keystore.DeriveKey(secret, ToSpan(test.salt), ToSpan(test.info), keyHandle)); + + uint8_t ciphertext[sizeof(test.ciphertext)]; + NL_TEST_ASSERT_SUCCESS( + inSuite, AES_CTR_crypt(test.plaintext, sizeof(test.plaintext), keyHandle, test.nonce, sizeof(test.nonce), ciphertext)); + NL_TEST_ASSERT(inSuite, memcmp(ciphertext, test.ciphertext, sizeof(test.ciphertext)) == 0); + + keystore.DestroyKey(keyHandle); + } +} + +void TestDeriveSessionKeys(nlTestSuite * inSuite, void * inContext) +{ + TestSessionKeystoreImpl keystore; + + for (const DeriveSessionKeysTestVector & test : deriveSessionKeysTestVectors) + { + P256ECDHDerivedSecret secret; + memcpy(secret.Bytes(), test.secret, strlen(test.secret)); + secret.SetLength(strlen(test.secret)); + + Aes128KeyHandle i2r; + Aes128KeyHandle r2i; + AttestationChallenge challenge; + NL_TEST_ASSERT_SUCCESS( + inSuite, keystore.DeriveSessionKeys(ToSpan(test.secret), ToSpan(test.salt), ToSpan(test.info), i2r, r2i, challenge)); + + uint8_t ciphertext[sizeof(test.i2rCiphertext)]; + + // Test I2R key + NL_TEST_ASSERT_SUCCESS( + inSuite, AES_CTR_crypt(test.plaintext, sizeof(test.plaintext), i2r, test.nonce, sizeof(test.nonce), ciphertext)); + NL_TEST_ASSERT(inSuite, memcmp(ciphertext, test.i2rCiphertext, sizeof(test.i2rCiphertext)) == 0); + + // Test R2I key + NL_TEST_ASSERT_SUCCESS( + inSuite, AES_CTR_crypt(test.plaintext, sizeof(test.plaintext), r2i, test.nonce, sizeof(test.nonce), ciphertext)); + NL_TEST_ASSERT(inSuite, memcmp(ciphertext, test.r2iCiphertext, sizeof(test.r2iCiphertext)) == 0); + + // Check attestation challenge + NL_TEST_ASSERT(inSuite, memcmp(challenge.Bytes(), test.attestationChallenge, sizeof(test.attestationChallenge)) == 0); + + keystore.DestroyKey(i2r); + keystore.DestroyKey(r2i); + } +} + +const nlTest sTests[] = { NL_TEST_DEF("Test basic import", TestBasicImport), NL_TEST_DEF("Test derive key", TestDeriveKey), + NL_TEST_DEF("Test derive session keys", TestDeriveSessionKeys), NL_TEST_SENTINEL() }; + +int Test_Setup(void * inContext) +{ + CHIP_ERROR error = Platform::MemoryInit(); + VerifyOrReturnError(error == CHIP_NO_ERROR, FAILURE); + +#if CHIP_CRYPTO_PSA + psa_crypto_init(); +#endif + + return SUCCESS; +} + +int Test_Teardown(void * inContext) +{ + Platform::MemoryShutdown(); + + return SUCCESS; +} + +} // namespace + +/** + * Main + */ +int TestSessionKeystore() +{ + nlTestSuite theSuite = { "SessionKeystore tests", &sTests[0], Test_Setup, Test_Teardown }; + + // Run test suite againt one context. + nlTestRunner(&theSuite, nullptr); + return nlTestRunnerStats(&theSuite); +} + +CHIP_REGISTER_TEST_SUITE(TestSessionKeystore) diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm index 885b118a31cc65..b47f192520bc61 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,7 @@ static NSString * const kErrorCertStoreInit = @"Init failure while initializing persistent storage operational certificate store"; static NSString * const kErrorCDCertStoreInit = @"Init failure while initializing Certificate Declaration Signing Keys store"; static NSString * const kErrorOtaProviderInit = @"Init failure while creating an OTA provider delegate"; +static NSString * const kErrorSessionKeystoreInit = @"Init failure while initializing session keystore"; static bool sExitHandlerRegistered = false; static void ShutdownOnExit() { [[MTRDeviceControllerFactory sharedInstance] stopControllerFactory]; } @@ -70,6 +72,7 @@ @interface MTRDeviceControllerFactory () @property (readonly) MTRPersistentStorageDelegateBridge * persistentStorageDelegateBridge; @property (readonly) MTRAttestationTrustStoreBridge * attestationTrustStoreBridge; @property (readonly) MTROTAProviderDelegateBridge * otaProviderDelegateBridge; +@property (readonly) Crypto::RawKeySessionKeystore * sessionKeystore; // We use TestPersistentStorageDelegate just to get an in-memory store to back // our group data provider impl. We initialize this store correctly on every // controller startup, so don't need to actually persist it. @@ -115,6 +118,11 @@ - (instancetype)init _chipWorkQueue = DeviceLayer::PlatformMgrImpl().GetWorkQueue(); _controllerFactory = &DeviceControllerFactory::GetInstance(); + _sessionKeystore = new chip::Crypto::RawKeySessionKeystore(); + if ([self checkForInitError:(_sessionKeystore != nullptr) logMsg:kErrorSessionKeystoreInit]) { + return nil; + } + _groupStorageDelegate = new chip::TestPersistentStorageDelegate(); if ([self checkForInitError:(_groupStorageDelegate != nullptr) logMsg:kErrorGroupProviderInit]) { return nil; @@ -127,6 +135,7 @@ - (instancetype)init } _groupDataProvider->SetStorageDelegate(_groupStorageDelegate); + _groupDataProvider->SetSessionKeystore(_sessionKeystore); CHIP_ERROR errorCode = _groupDataProvider->Init(); if ([self checkForInitError:(CHIP_NO_ERROR == errorCode) logMsg:kErrorGroupProviderInit]) { return nil; @@ -186,6 +195,11 @@ - (void)cleanupInitObjects delete _groupStorageDelegate; _groupStorageDelegate = nullptr; } + + if (_sessionKeystore) { + delete _sessionKeystore; + _sessionKeystore = nullptr; + } } - (void)cleanupStartupObjects @@ -375,6 +389,7 @@ - (BOOL)startControllerFactory:(MTRDeviceControllerFactoryParams *)startupParams } params.groupDataProvider = _groupDataProvider; + params.sessionKeystore = _sessionKeystore; params.fabricIndependentStorage = _persistentStorageDelegateBridge; params.operationalKeystore = _keystore; params.opCertStore = _opCertStore; diff --git a/src/messaging/tests/MessagingContext.cpp b/src/messaging/tests/MessagingContext.cpp index a6955aaf88acaa..295518d0811006 100644 --- a/src/messaging/tests/MessagingContext.cpp +++ b/src/messaging/tests/MessagingContext.cpp @@ -48,7 +48,8 @@ CHIP_ERROR MessagingContext::Init(TransportMgrBase * transport, IOContext * ioCo ReturnErrorOnFailure(mFabricTable.Init(initParams)); - ReturnErrorOnFailure(mSessionManager.Init(&GetSystemLayer(), transport, &mMessageCounterManager, &mStorage, &mFabricTable)); + ReturnErrorOnFailure( + mSessionManager.Init(&GetSystemLayer(), transport, &mMessageCounterManager, &mStorage, &mFabricTable, mSessionKeystore)); ReturnErrorOnFailure(mExchangeManager.Init(&mSessionManager)); ReturnErrorOnFailure(mMessageCounterManager.Init(&mExchangeManager)); diff --git a/src/messaging/tests/MessagingContext.h b/src/messaging/tests/MessagingContext.h index fea896bdaa153a..156bb78bf3609e 100644 --- a/src/messaging/tests/MessagingContext.h +++ b/src/messaging/tests/MessagingContext.h @@ -17,6 +17,7 @@ #pragma once #include +#include #include #include #include @@ -131,6 +132,7 @@ class MessagingContext : public PlatformMemoryUser Messaging::ExchangeManager & GetExchangeManager() { return mExchangeManager; } secure_channel::MessageCounterManager & GetMessageCounterManager() { return mMessageCounterManager; } FabricTable & GetFabricTable() { return mFabricTable; } + Crypto::DefaultSessionKeystore & GetSessionKeystore() { return mSessionKeystore; } FabricIndex GetAliceFabricIndex() { return mAliceFabricIndex; } FabricIndex GetBobFabricIndex() { return mBobFabricIndex; } @@ -182,6 +184,7 @@ class MessagingContext : public PlatformMemoryUser chip::TestPersistentStorageDelegate mStorage; // for SessionManagerInit chip::PersistentStorageOperationalKeystore mOpKeyStore; chip::Credentials::PersistentStorageOpCertStore mOpCertStore; + chip::Crypto::DefaultSessionKeystore mSessionKeystore; FabricIndex mAliceFabricIndex = kUndefinedFabricIndex; FabricIndex mBobFabricIndex = kUndefinedFabricIndex; diff --git a/src/messaging/tests/echo/common.cpp b/src/messaging/tests/echo/common.cpp index 22a21ca391c95c..146854083de2e9 100644 --- a/src/messaging/tests/echo/common.cpp +++ b/src/messaging/tests/echo/common.cpp @@ -35,6 +35,7 @@ chip::SessionManager gSessionManager; chip::Messaging::ExchangeManager gExchangeManager; chip::secure_channel::MessageCounterManager gMessageCounterManager; chip::TestPersistentStorageDelegate gStorage; +chip::Crypto::DefaultSessionKeystore gSessionKeystore; void InitializeChip() { diff --git a/src/messaging/tests/echo/common.h b/src/messaging/tests/echo/common.h index ba02d83df3a2b6..03d2431d3b7a8a 100644 --- a/src/messaging/tests/echo/common.h +++ b/src/messaging/tests/echo/common.h @@ -25,6 +25,7 @@ #pragma once #include +#include #include #include #include @@ -39,6 +40,7 @@ extern chip::SessionManager gSessionManager; extern chip::Messaging::ExchangeManager gExchangeManager; extern chip::secure_channel::MessageCounterManager gMessageCounterManager; extern chip::TestPersistentStorageDelegate gStorage; +extern chip::Crypto::DefaultSessionKeystore gSessionKeystore; void InitializeChip(void); void ShutdownChip(void); diff --git a/src/messaging/tests/echo/echo_requester.cpp b/src/messaging/tests/echo/echo_requester.cpp index 2635b3fdc81670..58b827d7914d53 100644 --- a/src/messaging/tests/echo/echo_requester.cpp +++ b/src/messaging/tests/echo/echo_requester.cpp @@ -227,7 +227,7 @@ int main(int argc, char * argv[]) SuccessOrExit(err); err = gSessionManager.Init(&chip::DeviceLayer::SystemLayer(), &gTCPManager, &gMessageCounterManager, &gStorage, - &gFabricTable); + &gFabricTable, gSessionKeystore); SuccessOrExit(err); } else @@ -238,7 +238,7 @@ int main(int argc, char * argv[]) SuccessOrExit(err); err = gSessionManager.Init(&chip::DeviceLayer::SystemLayer(), &gUDPManager, &gMessageCounterManager, &gStorage, - &gFabricTable); + &gFabricTable, gSessionKeystore); SuccessOrExit(err); } diff --git a/src/messaging/tests/echo/echo_responder.cpp b/src/messaging/tests/echo/echo_responder.cpp index 846680c20f8563..9d449e1ec5c579 100644 --- a/src/messaging/tests/echo/echo_responder.cpp +++ b/src/messaging/tests/echo/echo_responder.cpp @@ -93,7 +93,7 @@ int main(int argc, char * argv[]) SuccessOrExit(err); err = gSessionManager.Init(&chip::DeviceLayer::SystemLayer(), &gTCPManager, &gMessageCounterManager, &gStorage, - &gFabricTable); + &gFabricTable, gSessionKeystore); SuccessOrExit(err); } else @@ -103,7 +103,7 @@ int main(int argc, char * argv[]) SuccessOrExit(err); err = gSessionManager.Init(&chip::DeviceLayer::SystemLayer(), &gUDPManager, &gMessageCounterManager, &gStorage, - &gFabricTable); + &gFabricTable, gSessionKeystore); SuccessOrExit(err); } diff --git a/src/platform/nxp/common/crypto/CHIPCryptoPALTinyCrypt.cpp b/src/platform/nxp/common/crypto/CHIPCryptoPALTinyCrypt.cpp index b7866f97af1de5..4bf07e11a93ffc 100644 --- a/src/platform/nxp/common/crypto/CHIPCryptoPALTinyCrypt.cpp +++ b/src/platform/nxp/common/crypto/CHIPCryptoPALTinyCrypt.cpp @@ -123,18 +123,8 @@ static bool _isValidTagLength(size_t tag_length) return false; } -static bool _isValidKeyLength(size_t length) -{ - // 16 bytes key for AES-CCM-128, 32 for AES-CCM-256 - if (length == 16 || length == 32) - { - return true; - } - return false; -} - CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, const uint8_t * aad, size_t aad_length, - const uint8_t * key, size_t key_length, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, + const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, uint8_t * tag, size_t tag_length) { CHIP_ERROR error = CHIP_NO_ERROR; @@ -145,8 +135,6 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c VerifyOrExit(plaintext != nullptr || plaintext_length == 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(ciphertext != nullptr || plaintext_length == 0, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(_isValidKeyLength(key_length), error = CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE); VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(tag != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); @@ -156,10 +144,8 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c VerifyOrExit(aad != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); } - // Size of key = key_length * number of bits in a byte (8) - // Cast is safe because we called _isValidKeyLength above. - result = - mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, Uint8::to_const_uchar(key), static_cast(key_length * 8)); + // Size of key is expressed in bits, hence the multiplication by 8. + result = mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, key.As(), sizeof(Aes128KeyByteArray) * 8); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); // Encrypt @@ -175,7 +161,7 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c } CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, const uint8_t * aad, size_t aad_len, - const uint8_t * tag, size_t tag_length, const uint8_t * key, size_t key_length, const uint8_t * nonce, + const uint8_t * tag, size_t tag_length, const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * plaintext) { CHIP_ERROR error = CHIP_NO_ERROR; @@ -188,8 +174,6 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, co VerifyOrExit(ciphertext != nullptr || ciphertext_len == 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(tag != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(_isValidTagLength(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(_isValidKeyLength(key_length), error = CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE); VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); if (aad_len > 0) @@ -197,10 +181,8 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, co VerifyOrExit(aad != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); } - // Size of key = key_length * number of bits in a byte (8) - // Cast is safe because we called _isValidKeyLength above. - result = - mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, Uint8::to_const_uchar(key), static_cast(key_length * 8)); + // Size of key is expressed in bits, hence the multiplication by 8. + result = mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, key.As(), sizeof(Aes128KeyByteArray) * 8); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); // Decrypt diff --git a/src/platform/nxp/k32w/k32w0/crypto/CHIPCryptoPALNXPUltrafastP256.cpp b/src/platform/nxp/k32w/k32w0/crypto/CHIPCryptoPALNXPUltrafastP256.cpp index 9c706e396575ee..032438dd042101 100644 --- a/src/platform/nxp/k32w/k32w0/crypto/CHIPCryptoPALNXPUltrafastP256.cpp +++ b/src/platform/nxp/k32w/k32w0/crypto/CHIPCryptoPALNXPUltrafastP256.cpp @@ -114,18 +114,8 @@ static bool _isValidTagLength(size_t tag_length) return false; } -static bool _isValidKeyLength(size_t length) -{ - // 16 bytes key for AES-CCM-128, 32 for AES-CCM-256 - if (length == 16 || length == 32) - { - return true; - } - return false; -} - CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, const uint8_t * aad, size_t aad_length, - const uint8_t * key, size_t key_length, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, + const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, uint8_t * tag, size_t tag_length) { CHIP_ERROR error = CHIP_NO_ERROR; @@ -136,8 +126,6 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c VerifyOrExit(plaintext != nullptr || plaintext_length == 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(ciphertext != nullptr || plaintext_length == 0, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(_isValidKeyLength(key_length), error = CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE); VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(tag != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); @@ -147,10 +135,8 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c VerifyOrExit(aad != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); } - // Size of key = key_length * number of bits in a byte (8) - // Cast is safe because we called _isValidKeyLength above. - result = - mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, Uint8::to_const_uchar(key), static_cast(key_length * 8)); + // Size of key is expressed in bits, hence the multiplication by 8. + result = mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, key.As(), sizeof(Aes128KeyByteArray) * 8); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); // Encrypt @@ -166,7 +152,7 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c } CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, const uint8_t * aad, size_t aad_len, - const uint8_t * tag, size_t tag_length, const uint8_t * key, size_t key_length, const uint8_t * nonce, + const uint8_t * tag, size_t tag_length, const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * plaintext) { CHIP_ERROR error = CHIP_NO_ERROR; @@ -179,8 +165,6 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, co VerifyOrExit(ciphertext != nullptr || ciphertext_len == 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(tag != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(_isValidTagLength(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(_isValidKeyLength(key_length), error = CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE); VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); if (aad_len > 0) @@ -188,10 +172,8 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, co VerifyOrExit(aad != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); } - // Size of key = key_length * number of bits in a byte (8) - // Cast is safe because we called _isValidKeyLength above. - result = - mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, Uint8::to_const_uchar(key), static_cast(key_length * 8)); + // Size of key is expressed in bits, hence the multiplication by 8. + result = mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, key.As(), sizeof(Aes128KeyByteArray) * 8); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); // Decrypt diff --git a/src/platform/silabs/efr32/CHIPCryptoPALPsaEfr32.cpp b/src/platform/silabs/efr32/CHIPCryptoPALPsaEfr32.cpp index 6972da95aff7a8..955b4489906887 100644 --- a/src/platform/silabs/efr32/CHIPCryptoPALPsaEfr32.cpp +++ b/src/platform/silabs/efr32/CHIPCryptoPALPsaEfr32.cpp @@ -158,7 +158,7 @@ static int timeCompare(mbedtls_x509_time * t1, mbedtls_x509_time * t2) } CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, const uint8_t * aad, size_t aad_length, - const uint8_t * key, size_t key_length, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, + const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, uint8_t * tag, size_t tag_length) { CHIP_ERROR error = CHIP_NO_ERROR; @@ -169,9 +169,6 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c bool allocated_buffer = false; VerifyOrExit(_isValidTagLength(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(key_length == kAES_CCM128_Key_Length, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(key_length == kAES_CCM128_Key_Length, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(CanCastTo(nonce_length), error = CHIP_ERROR_INVALID_ARGUMENT); @@ -188,12 +185,12 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c psa_crypto_init(); psa_set_key_type(&attr, PSA_KEY_TYPE_AES); - psa_set_key_bits(&attr, key_length * 8); + psa_set_key_bits(&attr, sizeof(Aes128KeyByteArray) * 8); psa_set_key_algorithm(&attr, PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_CCM, 8)); psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_ENCRYPT); status = psa_driver_wrapper_aead_encrypt( - &attr, Uint8::to_const_uchar(key), key_length, PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_length), + &attr, key.As(), sizeof(Aes128KeyByteArray), PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_length), Uint8::to_const_uchar(nonce), nonce_length, Uint8::to_const_uchar(aad), aad_length, Uint8::to_const_uchar(plaintext), plaintext_length, allocated_buffer ? buffer : ciphertext, plaintext_length + tag_length, &output_length); @@ -217,7 +214,7 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c } CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, const uint8_t * aad, size_t aad_len, - const uint8_t * tag, size_t tag_length, const uint8_t * key, size_t key_length, const uint8_t * nonce, + const uint8_t * tag, size_t tag_length, const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * plaintext) { CHIP_ERROR error = CHIP_NO_ERROR; @@ -229,8 +226,6 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, co VerifyOrExit(_isValidTagLength(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(tag != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(key_length == kAES_CCM128_Key_Length, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); @@ -245,7 +240,7 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, co psa_crypto_init(); psa_set_key_type(&attr, PSA_KEY_TYPE_AES); - psa_set_key_bits(&attr, key_length * 8); + psa_set_key_bits(&attr, sizeof(Aes128KeyByteArray) * 8); psa_set_key_algorithm(&attr, PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_CCM, 8)); psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_DECRYPT); @@ -256,7 +251,7 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, co } status = psa_driver_wrapper_aead_decrypt( - &attr, Uint8::to_const_uchar(key), key_length, PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_length), + &attr, key.As(), sizeof(Aes128KeyByteArray), PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_length), Uint8::to_const_uchar(nonce), nonce_length, Uint8::to_const_uchar(aad), aad_len, allocated_buffer ? buffer : ciphertext, ciphertext_len + tag_length, plaintext, ciphertext_len, &output_length); diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index 7c00272155624e..3e3cbda767847d 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -103,7 +103,6 @@ using namespace Protocols::SecureChannel; constexpr uint8_t kKDFSR2Info[] = { 0x53, 0x69, 0x67, 0x6d, 0x61, 0x32 }; constexpr uint8_t kKDFSR3Info[] = { 0x53, 0x69, 0x67, 0x6d, 0x61, 0x33 }; -constexpr size_t kKDFInfoLength = sizeof(kKDFSR2Info); constexpr uint8_t kKDFS1RKeyInfo[] = { 0x53, 0x69, 0x67, 0x6d, 0x61, 0x31, 0x5f, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65 }; constexpr uint8_t kKDFS2RKeyInfo[] = { 0x53, 0x69, 0x67, 0x6d, 0x61, 0x32, 0x5f, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65 }; @@ -119,12 +118,6 @@ constexpr uint8_t kTBEData3_Nonce[] = constexpr size_t kTBEDataNonceLength = sizeof(kTBEData2_Nonce); static_assert(sizeof(kTBEData2_Nonce) == sizeof(kTBEData3_Nonce), "TBEData2_Nonce and TBEData3_Nonce must be same size"); -#ifdef ENABLE_HSM_HKDF -using HKDF_sha_crypto = HKDF_shaHSM; -#else -using HKDF_sha_crypto = HKDF_sha; -#endif - // Amounts of time to allow for server-side processing of messages. // // These timeout values only allow for the server-side processing and assume that any transport-specific @@ -190,6 +183,7 @@ CHIP_ERROR CASESession::Init(SessionManager & sessionManager, Credentials::Certi { VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(mGroupDataProvider != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(sessionManager.GetSessionKeystore() != nullptr, CHIP_ERROR_INVALID_ARGUMENT); Clear(); @@ -331,8 +325,9 @@ CHIP_ERROR CASESession::DeriveSecureSession(CryptoContext & session) const VerifyOrReturnError(bbuf.Fit(), CHIP_ERROR_BUFFER_TOO_SMALL); } - ReturnErrorOnFailure(session.InitFromSecret(mSharedSecret.Span(), ByteSpan(msg_salt), - CryptoContext::SessionInfoType::kSessionEstablishment, mRole)); + ReturnErrorOnFailure(session.InitFromSecret(*mSessionManager->GetSessionKeystore(), mSharedSecret.Span(), + ByteSpan(msg_salt), CryptoContext::SessionInfoType::kSessionEstablishment, + mRole)); return CHIP_NO_ERROR; } @@ -347,8 +342,8 @@ CHIP_ERROR CASESession::DeriveSecureSession(CryptoContext & session) const VerifyOrReturnError(bbuf.Fit(), CHIP_ERROR_BUFFER_TOO_SMALL); } - ReturnErrorOnFailure(session.InitFromSecret(mSharedSecret.Span(), ByteSpan(msg_salt), - CryptoContext::SessionInfoType::kSessionResumption, mRole)); + ReturnErrorOnFailure(session.InitFromSecret(*mSessionManager->GetSessionKeystore(), mSharedSecret.Span(), + ByteSpan(msg_salt), CryptoContext::SessionInfoType::kSessionResumption, mRole)); return CHIP_NO_ERROR; } @@ -757,10 +752,8 @@ CHIP_ERROR CASESession::SendSigma2() MutableByteSpan saltSpan(msg_salt); ReturnErrorOnFailure(ConstructSaltSigma2(ByteSpan(msg_rand), mEphemeralKey->Pubkey(), ByteSpan(mIPK), saltSpan)); - HKDF_sha_crypto mHKDF; - uint8_t sr2k[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES]; - ReturnErrorOnFailure(mHKDF.HKDF_SHA256(mSharedSecret.ConstBytes(), mSharedSecret.Length(), saltSpan.data(), saltSpan.size(), - kKDFSR2Info, kKDFInfoLength, sr2k, CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES)); + AutoReleaseSessionKey sr2k(*mSessionManager->GetSessionKeystore()); + ReturnErrorOnFailure(DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR2Info), sr2k)); // Construct Sigma2 TBS Data size_t msg_r2_signed_len = @@ -817,10 +810,9 @@ CHIP_ERROR CASESession::SendSigma2() msg_r2_signed_enc_len = static_cast(tlvWriter.GetLengthWritten()); // Generate the encrypted data blob - ReturnErrorOnFailure(AES_CCM_encrypt(msg_R2_Encrypted.Get(), msg_r2_signed_enc_len, nullptr, 0, sr2k, - CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES, kTBEData2_Nonce, kTBEDataNonceLength, - msg_R2_Encrypted.Get(), msg_R2_Encrypted.Get() + msg_r2_signed_enc_len, - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES)); + ReturnErrorOnFailure(AES_CCM_encrypt(msg_R2_Encrypted.Get(), msg_r2_signed_enc_len, nullptr, 0, sr2k.KeyHandle(), + kTBEData2_Nonce, kTBEDataNonceLength, msg_R2_Encrypted.Get(), + msg_R2_Encrypted.Get() + msg_r2_signed_enc_len, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES)); // Construct Sigma2 Msg const size_t mrpParamsSize = mLocalMRPConfig.HasValue() ? TLV::EstimateStructOverhead(sizeof(uint16_t), sizeof(uint16_t)) : 0; @@ -959,7 +951,7 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) size_t max_msg_r2_signed_enc_len; constexpr size_t kCaseOverheadForFutureTbeData = 128; - uint8_t sr2k[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES]; + AutoReleaseSessionKey sr2k(*mSessionManager->GetSessionKeystore()); P256ECDSASignature tbsData2Signature; @@ -1010,13 +1002,8 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) // Generate the S2K key { MutableByteSpan saltSpan(msg_salt); - err = ConstructSaltSigma2(ByteSpan(responderRandom), mRemotePubKey, ByteSpan(mIPK), saltSpan); - SuccessOrExit(err); - - HKDF_sha_crypto mHKDF; - err = mHKDF.HKDF_SHA256(mSharedSecret.ConstBytes(), mSharedSecret.Length(), saltSpan.data(), saltSpan.size(), kKDFSR2Info, - kKDFInfoLength, sr2k, CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES); - SuccessOrExit(err); + SuccessOrExit(err = ConstructSaltSigma2(ByteSpan(responderRandom), mRemotePubKey, ByteSpan(mIPK), saltSpan)); + SuccessOrExit(err = DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR2Info), sr2k)); } SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ buf, buflen })); @@ -1038,9 +1025,8 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) msg_r2_encrypted_len = msg_r2_encrypted_len_with_tag - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; SuccessOrExit(err = AES_CCM_decrypt(msg_R2_Encrypted.Get(), msg_r2_encrypted_len, nullptr, 0, - msg_R2_Encrypted.Get() + msg_r2_encrypted_len, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, sr2k, - CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES, kTBEData2_Nonce, kTBEDataNonceLength, - msg_R2_Encrypted.Get())); + msg_R2_Encrypted.Get() + msg_r2_encrypted_len, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, + sr2k.KeyHandle(), kTBEData2_Nonce, kTBEDataNonceLength, msg_R2_Encrypted.Get())); decryptedDataTlvReader.Init(msg_R2_Encrypted.Get(), msg_r2_encrypted_len); containerType = TLV::kTLVType_Structure; @@ -1126,7 +1112,7 @@ CHIP_ERROR CASESession::SendSigma3() uint8_t msg_salt[kIPKSize + kSHA256_Hash_Length]; - uint8_t sr3k[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES]; + AutoReleaseSessionKey sr3k(*mSessionManager->GetSessionKeystore()); chip::Platform::ScopedMemoryBuffer msg_R3_Signed; size_t msg_r3_signed_len; @@ -1201,20 +1187,14 @@ CHIP_ERROR CASESession::SendSigma3() // Generate S3K key { MutableByteSpan saltSpan(msg_salt); - err = ConstructSaltSigma3(ByteSpan(mIPK), saltSpan); - SuccessOrExit(err); - - HKDF_sha_crypto mHKDF; - err = mHKDF.HKDF_SHA256(mSharedSecret.ConstBytes(), mSharedSecret.Length(), saltSpan.data(), saltSpan.size(), kKDFSR3Info, - kKDFInfoLength, sr3k, CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES); - SuccessOrExit(err); + SuccessOrExit(err = ConstructSaltSigma3(ByteSpan(mIPK), saltSpan)); + SuccessOrExit(err = DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR3Info), sr3k)); } // Generated Encrypted data blob - err = AES_CCM_encrypt(msg_R3_Encrypted.Get(), msg_r3_encrypted_len, nullptr, 0, sr3k, CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES, - kTBEData3_Nonce, kTBEDataNonceLength, msg_R3_Encrypted.Get(), - msg_R3_Encrypted.Get() + msg_r3_encrypted_len, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES); - SuccessOrExit(err); + SuccessOrExit(err = AES_CCM_encrypt(msg_R3_Encrypted.Get(), msg_r3_encrypted_len, nullptr, 0, sr3k.KeyHandle(), kTBEData3_Nonce, + kTBEDataNonceLength, msg_R3_Encrypted.Get(), msg_R3_Encrypted.Get() + msg_r3_encrypted_len, + CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES)); // Generate Sigma3 Msg data_len = TLV::EstimateStructOverhead(CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, msg_r3_encrypted_len); @@ -1310,7 +1290,7 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) size_t msg_r3_encrypted_len_with_tag = 0; size_t max_msg_r3_signed_enc_len; - uint8_t sr3k[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES]; + AutoReleaseSessionKey sr3k(*mSessionManager->GetSessionKeystore()); uint8_t msg_salt[kIPKSize + kSHA256_Hash_Length]; @@ -1360,22 +1340,16 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) // Step 1 { MutableByteSpan saltSpan(msg_salt); - err = ConstructSaltSigma3(ByteSpan(mIPK), saltSpan); - SuccessOrExit(err); - - HKDF_sha_crypto mHKDF; - err = mHKDF.HKDF_SHA256(mSharedSecret.ConstBytes(), mSharedSecret.Length(), saltSpan.data(), saltSpan.size(), - kKDFSR3Info, kKDFInfoLength, sr3k, CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES); - SuccessOrExit(err); + SuccessOrExit(err = ConstructSaltSigma3(ByteSpan(mIPK), saltSpan)); + SuccessOrExit(err = DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR3Info), sr3k)); } SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ buf, bufLen })); // Step 2 - Decrypt data blob SuccessOrExit(err = AES_CCM_decrypt(msg_R3_Encrypted.Get(), msg_r3_encrypted_len, nullptr, 0, - msg_R3_Encrypted.Get() + msg_r3_encrypted_len, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, sr3k, - CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES, kTBEData3_Nonce, kTBEDataNonceLength, - msg_R3_Encrypted.Get())); + msg_R3_Encrypted.Get() + msg_r3_encrypted_len, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, + sr3k.KeyHandle(), kTBEData3_Nonce, kTBEDataNonceLength, msg_R3_Encrypted.Get())); decryptedDataTlvReader.Init(msg_R3_Encrypted.Get(), msg_r3_encrypted_len); containerType = TLV::kTLVType_Structure; @@ -1563,6 +1537,11 @@ CHIP_ERROR CASESession::HandleSigma3c(Sigma3Work & work) return err; } +CHIP_ERROR CASESession::DeriveSigmaKey(const ByteSpan & salt, const ByteSpan & info, AutoReleaseSessionKey & key) const +{ + return mSessionManager->GetSessionKeystore()->DeriveKey(mSharedSecret, salt, info, key.KeyHandle()); +} + CHIP_ERROR CASESession::ConstructSaltSigma2(const ByteSpan & rand, const Crypto::P256PublicKey & pubkey, const ByteSpan & ipk, MutableByteSpan & salt) { @@ -1603,10 +1582,8 @@ CHIP_ERROR CASESession::ConstructSaltSigma3(const ByteSpan & ipk, MutableByteSpa } CHIP_ERROR CASESession::ConstructSigmaResumeKey(const ByteSpan & initiatorRandom, const ByteSpan & resumptionID, - const ByteSpan & skInfo, const ByteSpan & nonce, MutableByteSpan & resumeKey) + const ByteSpan & skInfo, const ByteSpan & nonce, AutoReleaseSessionKey & resumeKey) { - VerifyOrReturnError(resumeKey.size() >= CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES, CHIP_ERROR_BUFFER_TOO_SMALL); - constexpr size_t saltSize = kSigmaParamRandomNumberSize + SessionResumptionStorage::kResumptionIdSize; uint8_t salt[saltSize]; @@ -1619,11 +1596,7 @@ CHIP_ERROR CASESession::ConstructSigmaResumeKey(const ByteSpan & initiatorRandom size_t saltWritten = 0; VerifyOrReturnError(bbuf.Fit(saltWritten), CHIP_ERROR_BUFFER_TOO_SMALL); - HKDF_sha_crypto mHKDF; - ReturnErrorOnFailure(mHKDF.HKDF_SHA256(mSharedSecret.ConstBytes(), mSharedSecret.Length(), salt, saltWritten, skInfo.data(), - skInfo.size(), resumeKey.data(), CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES)); - resumeKey.reduce_size(CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES); - return CHIP_NO_ERROR; + return DeriveSigmaKey(ByteSpan(salt, saltWritten), skInfo, resumeKey); } CHIP_ERROR CASESession::GenerateSigmaResumeMIC(const ByteSpan & initiatorRandom, const ByteSpan & resumptionID, @@ -1631,13 +1604,10 @@ CHIP_ERROR CASESession::GenerateSigmaResumeMIC(const ByteSpan & initiatorRandom, { VerifyOrReturnError(resumeMIC.size() >= CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, CHIP_ERROR_BUFFER_TOO_SMALL); - uint8_t srk[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES]; - MutableByteSpan resumeKey(srk); - - ReturnErrorOnFailure(ConstructSigmaResumeKey(initiatorRandom, resumptionID, skInfo, nonce, resumeKey)); - - ReturnErrorOnFailure(AES_CCM_encrypt(nullptr, 0, nullptr, 0, resumeKey.data(), resumeKey.size(), nonce.data(), nonce.size(), - nullptr, resumeMIC.data(), CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES)); + AutoReleaseSessionKey srk(*mSessionManager->GetSessionKeystore()); + ReturnErrorOnFailure(ConstructSigmaResumeKey(initiatorRandom, resumptionID, skInfo, nonce, srk)); + ReturnErrorOnFailure(AES_CCM_encrypt(nullptr, 0, nullptr, 0, srk.KeyHandle(), nonce.data(), nonce.size(), nullptr, + resumeMIC.data(), CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES)); resumeMIC.reduce_size(CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES); return CHIP_NO_ERROR; @@ -1648,13 +1618,10 @@ CHIP_ERROR CASESession::ValidateSigmaResumeMIC(const ByteSpan & resumeMIC, const { VerifyOrReturnError(resumeMIC.size() == CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, CHIP_ERROR_BUFFER_TOO_SMALL); - uint8_t srk[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES]; - MutableByteSpan resumeKey(srk); - - ReturnErrorOnFailure(ConstructSigmaResumeKey(initiatorRandom, resumptionID, skInfo, nonce, resumeKey)); - - ReturnErrorOnFailure(AES_CCM_decrypt(nullptr, 0, nullptr, 0, resumeMIC.data(), resumeMIC.size(), resumeKey.data(), - resumeKey.size(), nonce.data(), nonce.size(), nullptr)); + AutoReleaseSessionKey srk(*mSessionManager->GetSessionKeystore()); + ReturnErrorOnFailure(ConstructSigmaResumeKey(initiatorRandom, resumptionID, skInfo, nonce, srk)); + ReturnErrorOnFailure(AES_CCM_decrypt(nullptr, 0, nullptr, 0, resumeMIC.data(), resumeMIC.size(), srk.KeyHandle(), nonce.data(), + nonce.size(), nullptr)); return CHIP_NO_ERROR; } diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h index 844425d655f41b..1800fef789d775 100644 --- a/src/protocols/secure_channel/CASESession.h +++ b/src/protocols/secure_channel/CASESession.h @@ -231,6 +231,7 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, CHIP_ERROR SendSigma2Resume(); + CHIP_ERROR DeriveSigmaKey(const ByteSpan & salt, const ByteSpan & info, Crypto::AutoReleaseSessionKey & key) const; CHIP_ERROR ConstructSaltSigma2(const ByteSpan & rand, const Crypto::P256PublicKey & pubkey, const ByteSpan & ipk, MutableByteSpan & salt); CHIP_ERROR ConstructTBSData(const ByteSpan & senderNOC, const ByteSpan & senderICAC, const ByteSpan & senderPubKey, @@ -238,7 +239,7 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, CHIP_ERROR ConstructSaltSigma3(const ByteSpan & ipk, MutableByteSpan & salt); CHIP_ERROR ConstructSigmaResumeKey(const ByteSpan & initiatorRandom, const ByteSpan & resumptionID, const ByteSpan & skInfo, - const ByteSpan & nonce, MutableByteSpan & resumeKey); + const ByteSpan & nonce, Crypto::AutoReleaseSessionKey & resumeKey); CHIP_ERROR GenerateSigmaResumeMIC(const ByteSpan & initiatorRandom, const ByteSpan & resumptionID, const ByteSpan & skInfo, const ByteSpan & nonce, MutableByteSpan & resumeMIC); diff --git a/src/protocols/secure_channel/PASESession.cpp b/src/protocols/secure_channel/PASESession.cpp index 16139a1f9301a3..9be51e42ed90e2 100644 --- a/src/protocols/secure_channel/PASESession.cpp +++ b/src/protocols/secure_channel/PASESession.cpp @@ -111,6 +111,7 @@ void PASESession::Clear() CHIP_ERROR PASESession::Init(SessionManager & sessionManager, uint32_t setupCode, SessionEstablishmentDelegate * delegate) { + VerifyOrReturnError(sessionManager.GetSessionKeystore() != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT); // Reset any state maintained by PASESession object (in case it's being reused for pairing) @@ -257,7 +258,7 @@ void PASESession::OnResponseTimeout(ExchangeContext * ec) CHIP_ERROR PASESession::DeriveSecureSession(CryptoContext & session) const { VerifyOrReturnError(mPairingComplete, CHIP_ERROR_INCORRECT_STATE); - return session.InitFromSecret(ByteSpan(mKe, mKeLen), ByteSpan(nullptr, 0), + return session.InitFromSecret(*mSessionManager->GetSessionKeystore(), ByteSpan(mKe, mKeLen), ByteSpan(nullptr, 0), CryptoContext::SessionInfoType::kSessionEstablishment, mRole); } diff --git a/src/protocols/secure_channel/tests/TestCASESession.cpp b/src/protocols/secure_channel/tests/TestCASESession.cpp index fb881cb60579d9..a863b3ca57dae8 100644 --- a/src/protocols/secure_channel/tests/TestCASESession.cpp +++ b/src/protocols/secure_channel/tests/TestCASESession.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -79,7 +80,7 @@ class TemporarySessionManager NL_TEST_ASSERT(suite, CHIP_NO_ERROR == mSessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &ctx.GetMessageCounterManager(), - &mStorage, &ctx.GetFabricTable())); + &mStorage, &ctx.GetFabricTable(), ctx.GetSessionKeystore())); // The setup here is really weird: we are using one session manager for // the actual messages we send (the PASE handshake, so the // unauthenticated sessions) and a different one for allocating the PASE @@ -194,12 +195,14 @@ FabricTable gCommissionerFabrics; FabricIndex gCommissionerFabricIndex; GroupDataProviderImpl gCommissionerGroupDataProvider; TestPersistentStorageDelegate gCommissionerStorageDelegate; +Crypto::DefaultSessionKeystore gCommissionerSessionKeystore; FabricTable gDeviceFabrics; FabricIndex gDeviceFabricIndex; GroupDataProviderImpl gDeviceGroupDataProvider; TestPersistentStorageDelegate gDeviceStorageDelegate; TestOperationalKeystore gDeviceOperationalKeystore; +Crypto::DefaultSessionKeystore gDeviceSessionKeystore; Credentials::PersistentStorageOpCertStore gCommissionerOpCertStore; Credentials::PersistentStorageOpCertStore gDeviceOpCertStore; @@ -233,6 +236,7 @@ CHIP_ERROR InitCredentialSets() { gCommissionerStorageDelegate.ClearStorage(); gCommissionerGroupDataProvider.SetStorageDelegate(&gCommissionerStorageDelegate); + gCommissionerGroupDataProvider.SetSessionKeystore(&gCommissionerSessionKeystore); ReturnErrorOnFailure(gCommissionerGroupDataProvider.Init()); FabricInfo commissionerFabric; @@ -261,6 +265,7 @@ CHIP_ERROR InitCredentialSets() gDeviceStorageDelegate.ClearStorage(); gDeviceGroupDataProvider.SetStorageDelegate(&gDeviceStorageDelegate); + gDeviceGroupDataProvider.SetSessionKeystore(&gDeviceSessionKeystore); ReturnErrorOnFailure(gDeviceGroupDataProvider.Init()); FabricInfo deviceFabric; diff --git a/src/protocols/secure_channel/tests/TestPASESession.cpp b/src/protocols/secure_channel/tests/TestPASESession.cpp index 85e7bb8429c212..66a6a74d2d9dc0 100644 --- a/src/protocols/secure_channel/tests/TestPASESession.cpp +++ b/src/protocols/secure_channel/tests/TestPASESession.cpp @@ -119,7 +119,7 @@ class TemporarySessionManager NL_TEST_ASSERT(suite, CHIP_NO_ERROR == mSessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &ctx.GetMessageCounterManager(), - &mStorage, &ctx.GetFabricTable())); + &mStorage, &ctx.GetFabricTable(), ctx.GetSessionKeystore())); // The setup here is really weird: we are using one session manager for // the actual messages we send (the PASE handshake, so the // unauthenticated sessions) and a different one for allocating the PASE diff --git a/src/transport/CryptoContext.cpp b/src/transport/CryptoContext.cpp index 44306bf3be0bde..6b39af59696028 100644 --- a/src/transport/CryptoContext.cpp +++ b/src/transport/CryptoContext.cpp @@ -23,6 +23,7 @@ */ #include +#include #include #include #include @@ -50,39 +51,34 @@ constexpr uint8_t RSEKeysInfo[] = { 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x using namespace Crypto; -#ifdef ENABLE_HSM_HKDF -using HKDF_sha_crypto = HKDF_shaHSM; -#else -using HKDF_sha_crypto = HKDF_sha; -#endif - CryptoContext::CryptoContext() : mKeyAvailable(false) {} CryptoContext::~CryptoContext() { - for (auto & key : mKeys) + if (mKeystore) { - ClearSecretData(key, sizeof(CryptoKey)); + mKeystore->DestroyKey(mEncryptionKey); + mKeystore->DestroyKey(mDecryptionKey); } + + mKeystore = nullptr; mKeyContext = nullptr; } -CHIP_ERROR CryptoContext::InitFromSecret(const ByteSpan & secret, const ByteSpan & salt, SessionInfoType infoType, SessionRole role) +CHIP_ERROR CryptoContext::InitFromSecret(SessionKeystore & keystore, const ByteSpan & secret, const ByteSpan & salt, + SessionInfoType infoType, SessionRole role) { - HKDF_sha_crypto mHKDF; VerifyOrReturnError(mKeyAvailable == false, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(secret.data() != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(secret.size() > 0, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError((salt.size() == 0) || (salt.data() != nullptr), CHIP_ERROR_INVALID_ARGUMENT); - const uint8_t * info = SEKeysInfo; - size_t infoLen = sizeof(SEKeysInfo); + ByteSpan info = (infoType == SessionInfoType::kSessionResumption) ? ByteSpan(RSEKeysInfo) : ByteSpan(SEKeysInfo); - if (infoType == SessionInfoType::kSessionResumption) - { - info = RSEKeysInfo; - infoLen = sizeof(RSEKeysInfo); - } + // If the secure session is created by session initiator, use the I2R key to encrypt + // messages being transmitted. Otherwise, use the R2I key. + auto & i2rKey = (role == SessionRole::kInitiator) ? mEncryptionKey : mDecryptionKey; + auto & r2iKey = (role == SessionRole::kInitiator) ? mDecryptionKey : mEncryptionKey; #if CHIP_CONFIG_SECURITY_TEST_MODE @@ -94,7 +90,6 @@ CHIP_ERROR CryptoContext::InitFromSecret(const ByteSpan & secret, const ByteSpan "CHIP_CONFIG_TEST_SHARED_SECRET_VALUE must be 32 bytes"); const ByteSpan & testSalt = ByteSpan(nullptr, 0); (void) info; - (void) infoLen; #warning \ "Warning: CHIP_CONFIG_SECURITY_TEST_MODE=1 bypassing key negotiation... All sessions will use known, fixed test key, and NodeID=0 in NONCE. Node can only communicate with other nodes built with this flag set. Requires build flag 'treat_warnings_as_errors=false'." @@ -104,22 +99,22 @@ CHIP_ERROR CryptoContext::InitFromSecret(const ByteSpan & secret, const ByteSpan "and NodeID=0 in NONCE. " "Node can only communicate with other nodes built with this flag set."); - ReturnErrorOnFailure(mHKDF.HKDF_SHA256(kTestSharedSecret, CHIP_CONFIG_TEST_SHARED_SECRET_LENGTH, testSalt.data(), - testSalt.size(), SEKeysInfo, sizeof(SEKeysInfo), &mKeys[0][0], sizeof(mKeys))); + ReturnErrorOnFailure(keystore.DeriveSessionKeys(ByteSpan(kTestSharedSecret), testSalt, ByteSpan(SEKeysInfo), i2rKey, r2iKey, + mAttestationChallenge)); #else - ReturnErrorOnFailure( - mHKDF.HKDF_SHA256(secret.data(), secret.size(), salt.data(), salt.size(), info, infoLen, &mKeys[0][0], sizeof(mKeys))); + ReturnErrorOnFailure(keystore.DeriveSessionKeys(secret, salt, info, i2rKey, r2iKey, mAttestationChallenge)); #endif mKeyAvailable = true; mSessionRole = role; + mKeystore = &keystore; return CHIP_NO_ERROR; } -CHIP_ERROR CryptoContext::InitFromKeyPair(const Crypto::P256Keypair & local_keypair, +CHIP_ERROR CryptoContext::InitFromKeyPair(SessionKeystore & keystore, const Crypto::P256Keypair & local_keypair, const Crypto::P256PublicKey & remote_public_key, const ByteSpan & salt, SessionInfoType infoType, SessionRole role) { @@ -129,7 +124,7 @@ CHIP_ERROR CryptoContext::InitFromKeyPair(const Crypto::P256Keypair & local_keyp P256ECDHDerivedSecret secret; ReturnErrorOnFailure(local_keypair.ECDH_derive_secret(remote_public_key, secret)); - return InitFromSecret(secret.Span(), salt, infoType, role); + return InitFromSecret(keystore, secret.Span(), salt, infoType, role); } CHIP_ERROR CryptoContext::BuildNonce(NonceView nonce, uint8_t securityFlags, uint32_t messageCounter, NodeId nodeId) @@ -202,18 +197,8 @@ CHIP_ERROR CryptoContext::Encrypt(const uint8_t * input, size_t input_length, ui else { VerifyOrReturnError(mKeyAvailable, CHIP_ERROR_INVALID_USE_OF_SESSION_KEY); - KeyUsage usage = kR2IKey; - - // Message is encrypted before sending. If the secure session was created by session - // initiator, we'll use I2R key to encrypt the message that's being transmitted. - // Otherwise, we'll use R2I key, as the responder is sending the message. - if (mSessionRole == SessionRole::kInitiator) - { - usage = kI2RKey; - } - - ReturnErrorOnFailure(AES_CCM_encrypt(input, input_length, AAD, aadLen, mKeys[usage], Crypto::kAES_CCM128_Key_Length, - nonce.data(), nonce.size(), output, tag, taglen)); + ReturnErrorOnFailure( + AES_CCM_encrypt(input, input_length, AAD, aadLen, mEncryptionKey, nonce.data(), nonce.size(), output, tag, taglen)); } mac.SetTag(&header, tag, taglen); @@ -247,18 +232,8 @@ CHIP_ERROR CryptoContext::Decrypt(const uint8_t * input, size_t input_length, ui else { VerifyOrReturnError(mKeyAvailable, CHIP_ERROR_INVALID_USE_OF_SESSION_KEY); - KeyUsage usage = kI2RKey; - - // Message is decrypted on receive. If the secure session was created by session - // initiator, we'll use R2I key to decrypt the message (as it was sent by responder). - // Otherwise, we'll use I2R key, as the responder is sending the message. - if (mSessionRole == SessionRole::kInitiator) - { - usage = kR2IKey; - } - - ReturnErrorOnFailure(AES_CCM_decrypt(input, input_length, AAD, aadLen, tag, taglen, mKeys[usage], - Crypto::kAES_CCM128_Key_Length, nonce.data(), nonce.size(), output)); + ReturnErrorOnFailure( + AES_CCM_decrypt(input, input_length, AAD, aadLen, tag, taglen, mDecryptionKey, nonce.data(), nonce.size(), output)); } return CHIP_NO_ERROR; } diff --git a/src/transport/CryptoContext.h b/src/transport/CryptoContext.h index 9ab7b1383ca95e..ca2100b455c631 100644 --- a/src/transport/CryptoContext.h +++ b/src/transport/CryptoContext.h @@ -26,6 +26,7 @@ #pragma once #include +#include #include #include #include @@ -44,22 +45,22 @@ class DLL_EXPORT CryptoContext CryptoContext(); ~CryptoContext(); - CryptoContext(CryptoContext &&) = default; - CryptoContext(const CryptoContext &) = default; - CryptoContext(Crypto::SymmetricKeyContext * context) : mKeyContext(context){}; - CryptoContext & operator=(const CryptoContext &) = default; - CryptoContext & operator=(CryptoContext &&) = default; + CryptoContext(CryptoContext &&) = delete; + CryptoContext(const CryptoContext &) = delete; + explicit CryptoContext(Crypto::SymmetricKeyContext * context) : mKeyContext(context) {} + CryptoContext & operator=(const CryptoContext &) = delete; + CryptoContext & operator=(CryptoContext &&) = delete; /** * Whether the current node initiated the session, or it is responded to a session request. */ - enum class SessionRole + enum class SessionRole : uint8_t { kInitiator, /**< We initiated the session. */ kResponder, /**< We responded to the session request. */ }; - enum class SessionInfoType + enum class SessionInfoType : uint8_t { kSessionEstablishment, /**< A new secure session is established. */ kSessionResumption, /**< An old session is being resumed. */ @@ -70,6 +71,7 @@ class DLL_EXPORT CryptoContext * Derive a shared key. The derived key will be used for encrypting/decrypting * data exchanged on the secure channel. * + * @param keystore Session keystore for management of symmetric encryption keys * @param local_keypair A reference to local ECP keypair * @param remote_public_key A reference to peer's public key * @param salt A reference to the initial salt used for deriving the keys @@ -77,21 +79,24 @@ class DLL_EXPORT CryptoContext * @param role Role of the new session (initiator or responder) * @return CHIP_ERROR The result of key derivation */ - CHIP_ERROR InitFromKeyPair(const Crypto::P256Keypair & local_keypair, const Crypto::P256PublicKey & remote_public_key, - const ByteSpan & salt, SessionInfoType infoType, SessionRole role); + CHIP_ERROR InitFromKeyPair(Crypto::SessionKeystore & keystore, const Crypto::P256Keypair & local_keypair, + const Crypto::P256PublicKey & remote_public_key, const ByteSpan & salt, SessionInfoType infoType, + SessionRole role); /** * @brief * Derive a shared key. The derived key will be used for encrypting/decrypting * data exchanged on the secure channel. * + * @param keystore Session keystore for management of symmetric encryption keys * @param secret A reference to the shared secret * @param salt A reference to the initial salt used for deriving the keys * @param infoType The info buffer to use for deriving session keys * @param role Role of the new session (initiator or responder) * @return CHIP_ERROR The result of key derivation */ - CHIP_ERROR InitFromSecret(const ByteSpan & secret, const ByteSpan & salt, SessionInfoType infoType, SessionRole role); + CHIP_ERROR InitFromSecret(Crypto::SessionKeystore & keystore, const ByteSpan & secret, const ByteSpan & salt, + SessionInfoType infoType, SessionRole role); /** @brief Build a Nonce buffer using given parameters for encrypt or decrypt. */ static CHIP_ERROR BuildNonce(NonceView nonce, uint8_t securityFlags, uint32_t messageCounter, NodeId nodeId); @@ -136,7 +141,7 @@ class DLL_EXPORT CryptoContext CHIP_ERROR PrivacyDecrypt(const uint8_t * input, size_t input_length, uint8_t * output, const PacketHeader & header, const MessageAuthenticationCode & mac) const; - ByteSpan GetAttestationChallenge() const { return ByteSpan(mKeys[kAttestationChallengeKey], Crypto::kAES_CCM128_Key_Length); } + ByteSpan GetAttestationChallenge() const { return mAttestationChallenge.Span(); } /** * @brief @@ -152,20 +157,13 @@ class DLL_EXPORT CryptoContext bool IsResponder() const { return mKeyAvailable && mSessionRole == SessionRole::kResponder; } private: - typedef uint8_t CryptoKey[Crypto::kAES_CCM128_Key_Length]; - - enum KeyUsage - { - kI2RKey = 0, - kR2IKey = 1, - kAttestationChallengeKey = 2, - kNumCryptoKeys = 3 - }; - SessionRole mSessionRole; bool mKeyAvailable; - CryptoKey mKeys[KeyUsage::kNumCryptoKeys]; + Crypto::Aes128KeyHandle mEncryptionKey; + Crypto::Aes128KeyHandle mDecryptionKey; + Crypto::AttestationChallenge mAttestationChallenge; + Crypto::SessionKeystore * mKeystore = nullptr; Crypto::SymmetricKeyContext * mKeyContext = nullptr; // Use unencrypted header as additional authenticated data (AAD) during encryption and decryption. diff --git a/src/transport/SessionManager.cpp b/src/transport/SessionManager.cpp index 56f3d16db5039c..e3c424fee20dc0 100644 --- a/src/transport/SessionManager.cpp +++ b/src/transport/SessionManager.cpp @@ -79,7 +79,8 @@ SessionManager::~SessionManager() CHIP_ERROR SessionManager::Init(System::Layer * systemLayer, TransportMgrBase * transportMgr, Transport::MessageCounterManagerInterface * messageCounterManager, - chip::PersistentStorageDelegate * storageDelegate, FabricTable * fabricTable) + chip::PersistentStorageDelegate * storageDelegate, FabricTable * fabricTable, + Crypto::SessionKeystore & sessionKeystore) { VerifyOrReturnError(mState == State::kNotReady, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(transportMgr != nullptr, CHIP_ERROR_INVALID_ARGUMENT); @@ -92,6 +93,7 @@ CHIP_ERROR SessionManager::Init(System::Layer * systemLayer, TransportMgrBase * mTransportMgr = transportMgr; mMessageCounterManager = messageCounterManager; mFabricTable = fabricTable; + mSessionKeystore = &sessionKeystore; mSecureSessions.Init(); @@ -512,7 +514,7 @@ CHIP_ERROR SessionManager::InjectPaseSessionWithTestKey(SessionHolder & sessionH size_t secretLen = CHIP_CONFIG_TEST_SHARED_SECRET_LENGTH; ByteSpan secret(reinterpret_cast(CHIP_CONFIG_TEST_SHARED_SECRET_VALUE), secretLen); ReturnErrorOnFailure(secureSession->GetCryptoContext().InitFromSecret( - secret, ByteSpan(nullptr, 0), CryptoContext::SessionInfoType::kSessionEstablishment, role)); + *mSessionKeystore, secret, ByteSpan(nullptr, 0), CryptoContext::SessionInfoType::kSessionEstablishment, role)); secureSession->GetSessionMessageCounter().GetPeerMessageCounter().SetCounter(Transport::PeerMessageCounter::kInitialSyncValue); sessionHolder.Grab(session.Value()); return CHIP_NO_ERROR; @@ -533,7 +535,7 @@ CHIP_ERROR SessionManager::InjectCaseSessionWithTestKey(SessionHolder & sessionH size_t secretLen = CHIP_CONFIG_TEST_SHARED_SECRET_LENGTH; ByteSpan secret(reinterpret_cast(CHIP_CONFIG_TEST_SHARED_SECRET_VALUE), secretLen); ReturnErrorOnFailure(secureSession->GetCryptoContext().InitFromSecret( - secret, ByteSpan(nullptr, 0), CryptoContext::SessionInfoType::kSessionEstablishment, role)); + *mSessionKeystore, secret, ByteSpan(nullptr, 0), CryptoContext::SessionInfoType::kSessionEstablishment, role)); secureSession->GetSessionMessageCounter().GetPeerMessageCounter().SetCounter(Transport::PeerMessageCounter::kInitialSyncValue); sessionHolder.Grab(session.Value()); return CHIP_NO_ERROR; diff --git a/src/transport/SessionManager.h b/src/transport/SessionManager.h index 29f72c7a6ac2b7..87b9174750bbfa 100644 --- a/src/transport/SessionManager.h +++ b/src/transport/SessionManager.h @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -379,13 +380,17 @@ class DLL_EXPORT SessionManager : public TransportMgrDelegate, public FabricTabl * @brief * Initialize a Secure Session Manager * - * @param systemLayer System, layer to use + * @param systemLayer System layer to use * @param transportMgr Transport to use * @param messageCounterManager The message counter manager + * @param storageDelegate Persistent storage implementation + * @param fabricTable Fabric table to hold information about joined fabrics + * @param sessionKeystore Session keystore for management of symmetric encryption keys */ CHIP_ERROR Init(System::Layer * systemLayer, TransportMgrBase * transportMgr, Transport::MessageCounterManagerInterface * messageCounterManager, - chip::PersistentStorageDelegate * storageDelegate, FabricTable * fabricTable); + chip::PersistentStorageDelegate * storageDelegate, FabricTable * fabricTable, + Crypto::SessionKeystore & sessionKeystore); /** * @brief @@ -446,6 +451,8 @@ class DLL_EXPORT SessionManager : public TransportMgrDelegate, public FabricTabl FabricTable * GetFabricTable() const { return mFabricTable; } + Crypto::SessionKeystore * GetSessionKeystore() const { return mSessionKeystore; } + private: /** * The State of a secure transport object. @@ -462,8 +469,9 @@ class DLL_EXPORT SessionManager : public TransportMgrDelegate, public FabricTabl kPayloadIsUnencrypted, }; - System::Layer * mSystemLayer = nullptr; - FabricTable * mFabricTable = nullptr; + System::Layer * mSystemLayer = nullptr; + FabricTable * mFabricTable = nullptr; + Crypto::SessionKeystore * mSessionKeystore = nullptr; Transport::UnauthenticatedSessionTable mUnauthenticatedSessions; Transport::SecureSessionTable mSecureSessions; State mState; // < Initialization state of the object diff --git a/src/transport/tests/TestSecureSession.cpp b/src/transport/tests/TestSecureSession.cpp index 67b3f68b5d785f..d6efc341ee4fc9 100644 --- a/src/transport/tests/TestSecureSession.cpp +++ b/src/transport/tests/TestSecureSession.cpp @@ -24,11 +24,12 @@ #include #include +#include #include -#include - #include #include +#include + #include using namespace chip; @@ -36,6 +37,7 @@ using namespace Crypto; void SecureChannelInitTest(nlTestSuite * inSuite, void * inContext) { + Crypto::DefaultSessionKeystore sessionKeystore; CryptoContext channel; P256Keypair keypair; @@ -46,19 +48,19 @@ void SecureChannelInitTest(nlTestSuite * inSuite, void * inContext) // Test all combinations of invalid parameters NL_TEST_ASSERT(inSuite, - channel.InitFromKeyPair(keypair, keypair2.Pubkey(), ByteSpan(nullptr, 10), + channel.InitFromKeyPair(sessionKeystore, keypair, keypair2.Pubkey(), ByteSpan(nullptr, 10), CryptoContext::SessionInfoType::kSessionEstablishment, CryptoContext::SessionRole::kInitiator) == CHIP_ERROR_INVALID_ARGUMENT); // Test the channel is successfully created with valid parameters NL_TEST_ASSERT(inSuite, - channel.InitFromKeyPair(keypair, keypair2.Pubkey(), ByteSpan(nullptr, 0), + channel.InitFromKeyPair(sessionKeystore, keypair, keypair2.Pubkey(), ByteSpan(nullptr, 0), CryptoContext::SessionInfoType::kSessionEstablishment, CryptoContext::SessionRole::kInitiator) == CHIP_NO_ERROR); // Test the channel cannot be reinitialized NL_TEST_ASSERT(inSuite, - channel.InitFromKeyPair(keypair, keypair2.Pubkey(), ByteSpan(nullptr, 0), + channel.InitFromKeyPair(sessionKeystore, keypair, keypair2.Pubkey(), ByteSpan(nullptr, 0), CryptoContext::SessionInfoType::kSessionEstablishment, CryptoContext::SessionRole::kInitiator) == CHIP_ERROR_INCORRECT_STATE); @@ -66,13 +68,15 @@ void SecureChannelInitTest(nlTestSuite * inSuite, void * inContext) const char * salt = "Test Salt"; CryptoContext channel2; NL_TEST_ASSERT(inSuite, - channel2.InitFromKeyPair(keypair, keypair2.Pubkey(), ByteSpan((const uint8_t *) salt, strlen(salt)), + channel2.InitFromKeyPair(sessionKeystore, keypair, keypair2.Pubkey(), + ByteSpan((const uint8_t *) salt, strlen(salt)), CryptoContext::SessionInfoType::kSessionEstablishment, CryptoContext::SessionRole::kInitiator) == CHIP_NO_ERROR); } void SecureChannelEncryptTest(nlTestSuite * inSuite, void * inContext) { + Crypto::DefaultSessionKeystore sessionKeystore; CryptoContext channel; const uint8_t plain_text[] = { 0x86, 0x74, 0x64, 0xe5, 0x0b, 0xd4, 0x0d, 0x90, 0xe1, 0x17, 0xa3, 0x2d, 0x4b, 0xd4, 0xe1, 0xe6 }; uint8_t output[128]; @@ -99,7 +103,8 @@ void SecureChannelEncryptTest(nlTestSuite * inSuite, void * inContext) const char * salt = "Test Salt"; NL_TEST_ASSERT(inSuite, - channel.InitFromKeyPair(keypair, keypair2.Pubkey(), ByteSpan((const uint8_t *) salt, strlen(salt)), + channel.InitFromKeyPair(sessionKeystore, keypair, keypair2.Pubkey(), + ByteSpan((const uint8_t *) salt, strlen(salt)), CryptoContext::SessionInfoType::kSessionEstablishment, CryptoContext::SessionRole::kInitiator) == CHIP_NO_ERROR); @@ -115,6 +120,7 @@ void SecureChannelEncryptTest(nlTestSuite * inSuite, void * inContext) void SecureChannelDecryptTest(nlTestSuite * inSuite, void * inContext) { + Crypto::DefaultSessionKeystore sessionKeystore; CryptoContext channel; const uint8_t plain_text[] = { 0x86, 0x74, 0x64, 0xe5, 0x0b, 0xd4, 0x0d, 0x90, 0xe1, 0x17, 0xa3, 0x2d, 0x4b, 0xd4, 0xe1, 0xe6 }; uint8_t encrypted[128]; @@ -137,7 +143,8 @@ void SecureChannelDecryptTest(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, keypair2.Initialize(ECPKeyTarget::ECDH) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, - channel.InitFromKeyPair(keypair, keypair2.Pubkey(), ByteSpan((const uint8_t *) salt, strlen(salt)), + channel.InitFromKeyPair(sessionKeystore, keypair, keypair2.Pubkey(), + ByteSpan((const uint8_t *) salt, strlen(salt)), CryptoContext::SessionInfoType::kSessionEstablishment, CryptoContext::SessionRole::kInitiator) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, channel.Encrypt(plain_text, sizeof(plain_text), encrypted, nonce, packetHeader, mac) == CHIP_NO_ERROR); @@ -149,7 +156,8 @@ void SecureChannelDecryptTest(nlTestSuite * inSuite, void * inContext) channel2.Decrypt(encrypted, sizeof(plain_text), output, nonce, packetHeader, mac) == CHIP_ERROR_INVALID_USE_OF_SESSION_KEY); NL_TEST_ASSERT(inSuite, - channel2.InitFromKeyPair(keypair2, keypair.Pubkey(), ByteSpan((const uint8_t *) salt, strlen(salt)), + channel2.InitFromKeyPair(sessionKeystore, keypair2, keypair.Pubkey(), + ByteSpan((const uint8_t *) salt, strlen(salt)), CryptoContext::SessionInfoType::kSessionEstablishment, CryptoContext::SessionRole::kResponder) == CHIP_NO_ERROR); diff --git a/src/transport/tests/TestSessionManager.cpp b/src/transport/tests/TestSessionManager.cpp index fcc4d7621b3af9..12dc46ae360cc6 100644 --- a/src/transport/tests/TestSessionManager.cpp +++ b/src/transport/tests/TestSessionManager.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -130,12 +131,13 @@ void CheckSimpleInitTest(nlTestSuite * inSuite, void * inContext) SessionManager sessionManager; secure_channel::MessageCounterManager gMessageCounterManager; chip::TestPersistentStorageDelegate deviceStorage; + chip::Crypto::DefaultSessionKeystore sessionKeystore; NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == fabricTableHolder.Init()); NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == sessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &gMessageCounterManager, &deviceStorage, - &fabricTableHolder.GetFabricTable())); + &fabricTableHolder.GetFabricTable(), sessionKeystore)); } void CheckMessageTest(nlTestSuite * inSuite, void * inContext) @@ -158,6 +160,7 @@ void CheckMessageTest(nlTestSuite * inSuite, void * inContext) SessionManager sessionManager; secure_channel::MessageCounterManager gMessageCounterManager; chip::TestPersistentStorageDelegate deviceStorage; + chip::Crypto::DefaultSessionKeystore sessionKeystore; FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); FabricIndex aliceFabricIndex = kUndefinedFabricIndex; FabricIndex bobFabricIndex = kUndefinedFabricIndex; @@ -166,7 +169,7 @@ void CheckMessageTest(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == sessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &gMessageCounterManager, &deviceStorage, - &fabricTableHolder.GetFabricTable())); + &fabricTableHolder.GetFabricTable(), sessionKeystore)); callback.mSuite = inSuite; @@ -266,6 +269,7 @@ void SendEncryptedPacketTest(nlTestSuite * inSuite, void * inContext) SessionManager sessionManager; secure_channel::MessageCounterManager gMessageCounterManager; chip::TestPersistentStorageDelegate deviceStorage; + chip::Crypto::DefaultSessionKeystore sessionKeystore; FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); FabricIndex aliceFabricIndex = kUndefinedFabricIndex; FabricIndex bobFabricIndex = kUndefinedFabricIndex; @@ -274,7 +278,7 @@ void SendEncryptedPacketTest(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == sessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &gMessageCounterManager, &deviceStorage, - &fabricTableHolder.GetFabricTable())); + &fabricTableHolder.GetFabricTable(), sessionKeystore)); callback.mSuite = inSuite; @@ -359,6 +363,7 @@ void SendBadEncryptedPacketTest(nlTestSuite * inSuite, void * inContext) SessionManager sessionManager; secure_channel::MessageCounterManager gMessageCounterManager; chip::TestPersistentStorageDelegate deviceStorage; + chip::Crypto::DefaultSessionKeystore sessionKeystore; FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); FabricIndex aliceFabricIndex = kUndefinedFabricIndex; FabricIndex bobFabricIndex = kUndefinedFabricIndex; @@ -367,7 +372,7 @@ void SendBadEncryptedPacketTest(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == sessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &gMessageCounterManager, &deviceStorage, - &fabricTableHolder.GetFabricTable())); + &fabricTableHolder.GetFabricTable(), sessionKeystore)); callback.mSuite = inSuite; @@ -490,6 +495,7 @@ void SendPacketWithOldCounterTest(nlTestSuite * inSuite, void * inContext) SessionManager sessionManager; secure_channel::MessageCounterManager gMessageCounterManager; chip::TestPersistentStorageDelegate deviceStorage; + chip::Crypto::DefaultSessionKeystore sessionKeystore; FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); FabricIndex aliceFabricIndex = kUndefinedFabricIndex; FabricIndex bobFabricIndex = kUndefinedFabricIndex; @@ -498,7 +504,7 @@ void SendPacketWithOldCounterTest(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == sessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &gMessageCounterManager, &deviceStorage, - &fabricTableHolder.GetFabricTable())); + &fabricTableHolder.GetFabricTable(), sessionKeystore)); callback.mSuite = inSuite; @@ -597,6 +603,7 @@ void SendPacketWithTooOldCounterTest(nlTestSuite * inSuite, void * inContext) SessionManager sessionManager; secure_channel::MessageCounterManager gMessageCounterManager; chip::TestPersistentStorageDelegate deviceStorage; + chip::Crypto::DefaultSessionKeystore sessionKeystore; FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); FabricIndex aliceFabricIndex = kUndefinedFabricIndex; FabricIndex bobFabricIndex = kUndefinedFabricIndex; @@ -605,7 +612,7 @@ void SendPacketWithTooOldCounterTest(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == sessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &gMessageCounterManager, &deviceStorage, - &fabricTableHolder.GetFabricTable())); + &fabricTableHolder.GetFabricTable(), sessionKeystore)); callback.mSuite = inSuite; sessionManager.SetMessageDelegate(&callback); @@ -709,12 +716,13 @@ void SessionAllocationTest(nlTestSuite * inSuite, void * inContext) secure_channel::MessageCounterManager messageCounterManager; TestPersistentStorageDelegate deviceStorage1, deviceStorage2; - + chip::Crypto::DefaultSessionKeystore sessionKeystore; SessionManager sessionManager; + NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == sessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &messageCounterManager, &deviceStorage1, - &fabricTableHolder.GetFabricTable())); + &fabricTableHolder.GetFabricTable(), sessionKeystore)); // Allocate a session. uint16_t sessionId1; @@ -753,7 +761,7 @@ void SessionAllocationTest(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == sessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &messageCounterManager, &deviceStorage2, - &fabricTableHolder.GetFabricTable())); + &fabricTableHolder.GetFabricTable(), sessionKeystore)); // Allocate a single session so we know what random id we are starting at. { @@ -845,6 +853,7 @@ void SessionCounterExhaustedTest(nlTestSuite * inSuite, void * inContext) SessionManager sessionManager; secure_channel::MessageCounterManager gMessageCounterManager; chip::TestPersistentStorageDelegate deviceStorage; + chip::Crypto::DefaultSessionKeystore sessionKeystore; FabricTable & fabricTable = fabricTableHolder.GetFabricTable(); FabricIndex aliceFabricIndex = kUndefinedFabricIndex; FabricIndex bobFabricIndex = kUndefinedFabricIndex; @@ -853,7 +862,7 @@ void SessionCounterExhaustedTest(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == sessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &gMessageCounterManager, &deviceStorage, - &fabricTableHolder.GetFabricTable())); + &fabricTableHolder.GetFabricTable(), sessionKeystore)); Transport::PeerAddress peer(Transport::PeerAddress::UDP(addr, CHIP_PORT)); @@ -925,14 +934,14 @@ static void SessionShiftingTest(nlTestSuite * inSuite, void * inContext) FabricTableHolder fabricTableHolder; secure_channel::MessageCounterManager messageCounterManager; TestPersistentStorageDelegate deviceStorage; - + chip::Crypto::DefaultSessionKeystore sessionKeystore; SessionManager sessionManager; NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == fabricTableHolder.Init()); NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == sessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &messageCounterManager, &deviceStorage, - &fabricTableHolder.GetFabricTable())); + &fabricTableHolder.GetFabricTable(), sessionKeystore)); Transport::PeerAddress peer(Transport::PeerAddress::UDP(addr, CHIP_PORT)); @@ -1005,14 +1014,14 @@ static void TestFindSecureSessionForNode(nlTestSuite * inSuite, void * inContext FabricTableHolder fabricTableHolder; secure_channel::MessageCounterManager messageCounterManager; TestPersistentStorageDelegate deviceStorage; - + chip::Crypto::DefaultSessionKeystore sessionKeystore; SessionManager sessionManager; NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == fabricTableHolder.Init()); NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == sessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &messageCounterManager, &deviceStorage, - &fabricTableHolder.GetFabricTable())); + &fabricTableHolder.GetFabricTable(), sessionKeystore)); Transport::PeerAddress peer(Transport::PeerAddress::UDP(addr, CHIP_PORT)); diff --git a/src/transport/tests/TestSessionManagerDispatch.cpp b/src/transport/tests/TestSessionManagerDispatch.cpp index 72b8a2e27a4905..4e320961267bd7 100644 --- a/src/transport/tests/TestSessionManagerDispatch.cpp +++ b/src/transport/tests/TestSessionManagerDispatch.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -417,6 +418,7 @@ constexpr uint16_t kMaxGroupsPerFabric = 5; constexpr uint16_t kMaxGroupKeysPerFabric = 8; static chip::TestPersistentStorageDelegate sStorageDelegate; +static chip::Crypto::DefaultSessionKeystore sSessionKeystore; static GroupDataProviderImpl sProvider(kMaxGroupsPerFabric, kMaxGroupKeysPerFabric); class FabricTableHolder { @@ -436,6 +438,7 @@ class FabricTableHolder // Initialize Group Data Provider sProvider.SetStorageDelegate(&sStorageDelegate); + sProvider.SetSessionKeystore(&sSessionKeystore); // sProvider.SetListener(&chip::app::TestGroups::sListener); ReturnErrorOnFailure(sProvider.Init()); Credentials::SetGroupDataProvider(&sProvider); @@ -507,12 +510,13 @@ void TestSessionManagerInit(nlTestSuite * inSuite, TestContext & ctx, SessionMan static FabricTableHolder fabricTableHolder; static secure_channel::MessageCounterManager gMessageCounterManager; static chip::TestPersistentStorageDelegate deviceStorage; + static chip::Crypto::DefaultSessionKeystore sessionKeystore; NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == fabricTableHolder.Init()); NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == sessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &gMessageCounterManager, &deviceStorage, - &fabricTableHolder.GetFabricTable())); + &fabricTableHolder.GetFabricTable(), sessionKeystore)); } // constexpr chip::FabricId kFabricId1 = 0x2906C908D115D362;