From 3e930285b5b90e4c7d247e2453f9f6c6021d733e Mon Sep 17 00:00:00 2001 From: Pankaj Garg Date: Tue, 27 Jul 2021 15:56:49 -0700 Subject: [PATCH] implement cluster logic --- examples/all-clusters-app/esp32/main/main.cpp | 2 +- .../administrator-commissioning-server.cpp | 42 ++++-- src/app/server/RendezvousServer.cpp | 12 +- src/app/server/RendezvousServer.h | 5 +- src/app/server/Server.cpp | 142 +++++++++--------- src/app/server/Server.h | 13 +- src/controller/CHIPDevice.cpp | 48 +++--- src/controller/CHIPDevice.h | 16 +- src/protocols/secure_channel/PASESession.cpp | 58 ++++--- src/protocols/secure_channel/PASESession.h | 30 ++-- .../secure_channel/tests/TestPASESession.cpp | 12 +- 11 files changed, 219 insertions(+), 161 deletions(-) diff --git a/examples/all-clusters-app/esp32/main/main.cpp b/examples/all-clusters-app/esp32/main/main.cpp index b4c6da1f2cbb24..642b00f026bcc0 100644 --- a/examples/all-clusters-app/esp32/main/main.cpp +++ b/examples/all-clusters-app/esp32/main/main.cpp @@ -348,7 +348,7 @@ class SetupListModel : public ListScreen::Model else if (i == 2) { app::Mdns::AdvertiseCommissionableNode(); - OpenDefaultPairingWindow(ResetFabrics::kYes, PairingWindowAdvertisement::kMdns); + OpenDefaultPairingWindow(ResetFabrics::kYes, kNoCommissioningTimeout, PairingWindowAdvertisement::kMdns); } } diff --git a/src/app/clusters/administrator-commissioning-server/administrator-commissioning-server.cpp b/src/app/clusters/administrator-commissioning-server/administrator-commissioning-server.cpp index c95195c1d2a3a8..7e0920c9b77a10 100644 --- a/src/app/clusters/administrator-commissioning-server/administrator-commissioning-server.cpp +++ b/src/app/clusters/administrator-commissioning-server/administrator-commissioning-server.cpp @@ -21,15 +21,9 @@ ***************************************************************************/ #include -#include -#include -#include -#include -#include #include #include #include -#include #include using namespace chip; @@ -39,7 +33,22 @@ bool emberAfAdministratorCommissioningClusterOpenCommissioningWindowCallback(app uint16_t Discriminator, uint32_t Iterations, ByteSpan Salt, uint16_t PasscodeID) { - EmberAfStatus status = EMBER_ZCL_STATUS_FAILURE; + EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS; + PASEVerifier verifier; + ChipLogProgress(Zcl, "Received command to open commissioning window"); + VerifyOrExit(!IsPairingWindowOpen(), status = EMBER_ZCL_STATUS_FAILURE); + VerifyOrExit(sizeof(verifier) == PAKEVerifier.size(), status = EMBER_ZCL_STATUS_FAILURE); + memcpy(&verifier[0][0], PAKEVerifier.data(), PAKEVerifier.size()); + VerifyOrExit(OpenPairingWindowUsingVerifier(CommissioningTimeout, Discriminator, verifier, Iterations, Salt, PasscodeID) == + CHIP_NO_ERROR, + status = EMBER_ZCL_STATUS_FAILURE); + ChipLogProgress(Zcl, "Commissioning window is now open"); + +exit: + if (status != EMBER_ZCL_STATUS_SUCCESS) + { + ChipLogError(Zcl, "Failed to open commissioning window. Status %d", status); + } emberAfSendImmediateDefaultResponse(status); return true; } @@ -47,14 +56,27 @@ bool emberAfAdministratorCommissioningClusterOpenCommissioningWindowCallback(app bool emberAfAdministratorCommissioningClusterOpenBasicCommissioningWindowCallback(app::CommandHandler * commandObj, uint16_t CommissioningTimeout) { - EmberAfStatus status = EMBER_ZCL_STATUS_FAILURE; + EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS; + ChipLogProgress(Zcl, "Received command to open basic commissioning window"); + VerifyOrExit(!IsPairingWindowOpen(), status = EMBER_ZCL_STATUS_FAILURE); + VerifyOrExit(OpenDefaultPairingWindow(ResetFabrics::kNo, CommissioningTimeout) == CHIP_NO_ERROR, + status = EMBER_ZCL_STATUS_FAILURE); + ChipLogProgress(Zcl, "Commissioning window is now open"); + +exit: + if (status != EMBER_ZCL_STATUS_SUCCESS) + { + ChipLogError(Zcl, "Failed to open commissioning window. Status %d", status); + } emberAfSendImmediateDefaultResponse(status); return true; } bool emberAfAdministratorCommissioningClusterRevokeCommissioningCallback(app::CommandHandler * commandObj) { - EmberAfStatus status = EMBER_ZCL_STATUS_FAILURE; - emberAfSendImmediateDefaultResponse(status); + ChipLogProgress(Zcl, "Received command to close commissioning window"); + ClosePairingWindow(); + ChipLogProgress(Zcl, "Commissioning window is now closed"); + emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); return true; } diff --git a/src/app/server/RendezvousServer.cpp b/src/app/server/RendezvousServer.cpp index cff03e3790894f..58b6b52b01300a 100644 --- a/src/app/server/RendezvousServer.cpp +++ b/src/app/server/RendezvousServer.cpp @@ -42,8 +42,6 @@ void OnPlatformEventWrapper(const DeviceLayer::ChipDeviceEvent * event, intptr_t server->OnPlatformEvent(event); } } // namespace -static constexpr uint32_t kSpake2p_Iteration_Count = 100; -static const char * kSpake2pKeyExchangeSalt = "SPAKE2P Key Salt"; void RendezvousServer::OnPlatformEvent(const DeviceLayer::ChipDeviceEvent * event) { @@ -67,7 +65,8 @@ void RendezvousServer::OnPlatformEvent(const DeviceLayer::ChipDeviceEvent * even } } -CHIP_ERROR RendezvousServer::WaitForPairing(const RendezvousParameters & params, Messaging::ExchangeManager * exchangeManager, +CHIP_ERROR RendezvousServer::WaitForPairing(const RendezvousParameters & params, uint32_t pbkdf2IterCount, const ByteSpan & salt, + uint16_t passcodeID, Messaging::ExchangeManager * exchangeManager, TransportMgrBase * transportMgr, SecureSessionMgr * sessionMgr, Transport::FabricInfo * fabric) { @@ -106,13 +105,12 @@ CHIP_ERROR RendezvousServer::WaitForPairing(const RendezvousParameters & params, if (params.HasPASEVerifier()) { - ReturnErrorOnFailure(mPairingSession.WaitForPairing(params.GetPASEVerifier(), keyID, this)); + ReturnErrorOnFailure( + mPairingSession.WaitForPairing(params.GetPASEVerifier(), pbkdf2IterCount, salt, passcodeID, keyID, this)); } else { - ReturnErrorOnFailure(mPairingSession.WaitForPairing(params.GetSetupPINCode(), kSpake2p_Iteration_Count, - reinterpret_cast(kSpake2pKeyExchangeSalt), - strlen(kSpake2pKeyExchangeSalt), keyID, this)); + ReturnErrorOnFailure(mPairingSession.WaitForPairing(params.GetSetupPINCode(), pbkdf2IterCount, salt, keyID, this)); } ReturnErrorOnFailure(mPairingSession.MessageDispatch().Init(transportMgr)); diff --git a/src/app/server/RendezvousServer.h b/src/app/server/RendezvousServer.h index ad7fc8fefdc362..df55bcf7697b5f 100644 --- a/src/app/server/RendezvousServer.h +++ b/src/app/server/RendezvousServer.h @@ -29,8 +29,9 @@ namespace chip { class RendezvousServer : public SessionEstablishmentDelegate { public: - CHIP_ERROR WaitForPairing(const RendezvousParameters & params, Messaging::ExchangeManager * exchangeManager, - TransportMgrBase * transportMgr, SecureSessionMgr * sessionMgr, Transport::FabricInfo * fabric); + CHIP_ERROR WaitForPairing(const RendezvousParameters & params, uint32_t pbkdf2IterCount, const ByteSpan & salt, + uint16_t passcodeID, Messaging::ExchangeManager * exchangeManager, TransportMgrBase * transportMgr, + SecureSessionMgr * sessionMgr, Transport::FabricInfo * fabric); CHIP_ERROR Init(AppDelegate * delegate, PersistentStorageDelegate * storage, SessionIDAllocator * idAllocator) { diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index 6cd072919b190f..93d97185a46701 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -247,6 +247,7 @@ class DeviceDiscriminatorCache DeviceDiscriminatorCache gDeviceDiscriminatorCache; FabricTable gFabrics; FabricIndex gNextAvailableFabricIndex = 0; +bool gPairingWindowOpen = false; class ServerRendezvousAdvertisementDelegate : public RendezvousAdvertisementDelegate { @@ -261,12 +262,15 @@ class ServerRendezvousAdvertisementDelegate : public RendezvousAdvertisementDele { mDelegate->OnPairingWindowOpened(); } + gPairingWindowOpen = true; return CHIP_NO_ERROR; } CHIP_ERROR StopAdvertisement() const override { gDeviceDiscriminatorCache.RestoreDiscriminator(); + gPairingWindowOpen = false; + if (isBLE) { ReturnErrorOnFailure(chip::DeviceLayer::ConnectivityMgr().SetBLEAdvertisingEnabled(false)); @@ -303,29 +307,6 @@ CASEServer gCASEServer; Messaging::ExchangeManager gExchangeMgr; ServerRendezvousAdvertisementDelegate gAdvDelegate; -static CHIP_ERROR OpenPairingWindowUsingVerifier(uint16_t discriminator, PASEVerifier & verifier) -{ - RendezvousParameters params; - - ReturnErrorOnFailure(gDeviceDiscriminatorCache.UpdateDiscriminator(discriminator)); - -#if CONFIG_NETWORK_LAYER_BLE - params.SetPASEVerifier(verifier) - .SetBleLayer(DeviceLayer::ConnectivityMgr().GetBleLayer()) - .SetPeerAddress(Transport::PeerAddress::BLE()) - .SetAdvertisementDelegate(&gAdvDelegate); -#else - params.SetPASEVerifier(verifier); -#endif // CONFIG_NETWORK_LAYER_BLE - - FabricIndex fabricIndex = gNextAvailableFabricIndex; - FabricInfo * fabricInfo = gFabrics.AssignFabricIndex(fabricIndex); - VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_NO_MEMORY); - gNextAvailableFabricIndex++; - - return gRendezvousServer.WaitForPairing(std::move(params), &gExchangeMgr, &gTransports, &gSessions, fabricInfo); -} - class ServerCallback : public ExchangeDelegate { public: @@ -345,52 +326,7 @@ class ServerCallback : public ExchangeDelegate ChipLogProgress(AppServer, "Packet received from Node 0x" ChipLogFormatX64 ": %u bytes", ChipLogValueX64(packetHeader.GetSourceNodeId().Value()), buffer->DataLength()); - // TODO: This code is temporary, and must be updated to use the Cluster API. - // Issue: https://github.com/project-chip/connectedhomeip/issues/4725 - if (payloadHeader.HasProtocol(chip::Protocols::ServiceProvisioning::Id)) - { - uint32_t timeout; - uint16_t discriminator; - PASEVerifier verifier; - - ChipLogProgress(AppServer, "Received service provisioning message. Treating it as OpenPairingWindow request"); - chip::System::PacketBufferTLVReader reader; - reader.Init(std::move(buffer)); - reader.ImplicitProfileId = chip::Protocols::ServiceProvisioning::Id.ToTLVProfileId(); - - SuccessOrExit(reader.Next(kTLVType_UnsignedInteger, TLV::ProfileTag(reader.ImplicitProfileId, 1))); - SuccessOrExit(reader.Get(timeout)); - - err = reader.Next(kTLVType_UnsignedInteger, TLV::ProfileTag(reader.ImplicitProfileId, 2)); - if (err == CHIP_NO_ERROR) - { - SuccessOrExit(reader.Get(discriminator)); - - err = reader.Next(kTLVType_ByteString, TLV::ProfileTag(reader.ImplicitProfileId, 3)); - if (err == CHIP_NO_ERROR) - { - SuccessOrExit(reader.GetBytes(reinterpret_cast(verifier), sizeof(verifier))); - } - } - - ChipLogProgress(AppServer, "Pairing Window timeout %" PRIu32 " seconds", timeout); - - if (err != CHIP_NO_ERROR) - { - SuccessOrExit(err = OpenDefaultPairingWindow(ResetFabrics::kNo)); - } - else - { - ChipLogProgress(AppServer, "Pairing Window discriminator %d", discriminator); - err = OpenPairingWindowUsingVerifier(discriminator, verifier); - SuccessOrExit(err); - } - ChipLogProgress(AppServer, "Opened the pairing window"); - } - else - { - HandleDataModelMessage(exchangeContext, std::move(buffer)); - } + HandleDataModelMessage(exchangeContext, std::move(buffer)); exit: return err; @@ -423,9 +359,15 @@ chip::Protocols::UserDirectedCommissioning::UserDirectedCommissioningClient gUDC #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT +void HandlePairingWindowTimeout(System::Layer * aSystemLayer, void * aAppState, CHIP_ERROR aError) +{ + ClosePairingWindow(); +} + } // namespace -CHIP_ERROR OpenDefaultPairingWindow(ResetFabrics resetFabrics, chip::PairingWindowAdvertisement advertisementMode) +CHIP_ERROR OpenDefaultPairingWindow(ResetFabrics resetFabrics, uint16_t commissioningTimeoutSeconds, + chip::PairingWindowAdvertisement advertisementMode) { // TODO(cecille): If this is re-called when the window is already open, what should happen? gDeviceDiscriminatorCache.RestoreDiscriminator(); @@ -460,7 +402,65 @@ CHIP_ERROR OpenDefaultPairingWindow(ResetFabrics resetFabrics, chip::PairingWind VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_NO_MEMORY); gNextAvailableFabricIndex++; - return gRendezvousServer.WaitForPairing(std::move(params), &gExchangeMgr, &gTransports, &gSessions, fabricInfo); + ReturnErrorOnFailure(gRendezvousServer.WaitForPairing( + std::move(params), kSpake2p_Iteration_Count, + ByteSpan(reinterpret_cast(kSpake2pKeyExchangeSalt), strlen(kSpake2pKeyExchangeSalt)), 0, + &gExchangeMgr, &gTransports, &gSessions, fabricInfo)); + + if (commissioningTimeoutSeconds != kNoCommissioningTimeout) + { + ReturnErrorOnFailure( + DeviceLayer::SystemLayer.StartTimer(commissioningTimeoutSeconds * 1000, HandlePairingWindowTimeout, nullptr)); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OpenPairingWindowUsingVerifier(uint16_t commissioningTimeoutSeconds, uint16_t discriminator, PASEVerifier & verifier, + uint32_t iterations, ByteSpan salt, uint16_t passcodeID) +{ + RendezvousParameters params; + + ReturnErrorOnFailure(gDeviceDiscriminatorCache.UpdateDiscriminator(discriminator)); + +#if CONFIG_NETWORK_LAYER_BLE + params.SetPASEVerifier(verifier) + .SetBleLayer(DeviceLayer::ConnectivityMgr().GetBleLayer()) + .SetPeerAddress(Transport::PeerAddress::BLE()) + .SetAdvertisementDelegate(&gAdvDelegate); +#else + params.SetPASEVerifier(verifier); +#endif // CONFIG_NETWORK_LAYER_BLE + + FabricIndex fabricIndex = gNextAvailableFabricIndex; + FabricInfo * fabricInfo = gFabrics.AssignFabricIndex(fabricIndex); + VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_NO_MEMORY); + gNextAvailableFabricIndex++; + + ReturnErrorOnFailure(gRendezvousServer.WaitForPairing(std::move(params), iterations, salt, passcodeID, &gExchangeMgr, + &gTransports, &gSessions, fabricInfo)); + + if (commissioningTimeoutSeconds != kNoCommissioningTimeout) + { + ReturnErrorOnFailure( + DeviceLayer::SystemLayer.StartTimer(commissioningTimeoutSeconds * 1000, HandlePairingWindowTimeout, nullptr)); + } + + return CHIP_NO_ERROR; +} + +void ClosePairingWindow() +{ + if (gPairingWindowOpen) + { + ChipLogProgress(AppServer, "Closing pairing window"); + gRendezvousServer.Cleanup(); + } +} + +bool IsPairingWindowOpen() +{ + return gPairingWindowOpen; } // The function will initialize datamodel handler and then start the server diff --git a/src/app/server/Server.h b/src/app/server/Server.h index af3b964dd45cf3..94f087f61e7a27 100644 --- a/src/app/server/Server.h +++ b/src/app/server/Server.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -71,8 +72,18 @@ enum class PairingWindowAdvertisement } // namespace chip +constexpr uint16_t kNoCommissioningTimeout = UINT16_MAX; + /** * Open the pairing window using default configured parameters. */ -CHIP_ERROR OpenDefaultPairingWindow(chip::ResetFabrics resetFabrics, +CHIP_ERROR OpenDefaultPairingWindow(chip::ResetFabrics resetFabrics, uint16_t commissioningTimeoutSeconds = kNoCommissioningTimeout, chip::PairingWindowAdvertisement advertisementMode = chip::PairingWindowAdvertisement::kBle); + +CHIP_ERROR OpenPairingWindowUsingVerifier(uint16_t commissioningTimeoutSeconds, uint16_t discriminator, + chip::PASEVerifier & verifier, uint32_t iterations, chip::ByteSpan salt, + uint16_t passcodeID); + +void ClosePairingWindow(); + +bool IsPairingWindowOpen(); diff --git a/src/controller/CHIPDevice.cpp b/src/controller/CHIPDevice.cpp index ceef00577aae94..51ddbc8ff8fb79 100644 --- a/src/controller/CHIPDevice.cpp +++ b/src/controller/CHIPDevice.cpp @@ -26,6 +26,8 @@ #include +#include + #if CONFIG_DEVICE_LAYER #include #endif @@ -344,36 +346,40 @@ CHIP_ERROR Device::OnMessageReceived(Messaging::ExchangeContext * exchange, cons void Device::OnResponseTimeout(Messaging::ExchangeContext * ec) {} -CHIP_ERROR Device::OpenPairingWindow(uint32_t timeout, PairingWindowOption option, SetupPayload & setupPayload) +void Device::OnOpenPairingWindowSuccessResponse(void * context) { - // TODO: This code is temporary, and must be updated to use the Cluster API. - // Issue: https://github.com/project-chip/connectedhomeip/issues/4725 + ChipLogProgress(Controller, "Successfully opened pairing window on the device"); +} - // Construct and send "open pairing window" message to the device - System::PacketBufferHandle buf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); - System::PacketBufferTLVWriter writer; +void Device::OnOpenPairingWindowFailureResponse(void * context, uint8_t status) +{ + ChipLogError(Controller, "Failed to open pairing window on the device. Status %d", status); +} - writer.Init(std::move(buf)); - writer.ImplicitProfileId = chip::Protocols::ServiceProvisioning::Id.ToTLVProfileId(); +CHIP_ERROR Device::OpenPairingWindow(uint16_t timeout, PairingWindowOption option, SetupPayload & setupPayload) +{ + chip::Controller::AdministratorCommissioningCluster cluster; + cluster.Associate(this, 0); - ReturnErrorOnFailure(writer.Put(TLV::ProfileTag(writer.ImplicitProfileId, 1), timeout)); + Callback::Cancelable * successCallback = mOpenPairingSuccessCallback.Cancel(); + Callback::Cancelable * failureCallback = mOpenPairingFailureCallback.Cancel(); if (option != PairingWindowOption::kOriginalSetupCode) { - ReturnErrorOnFailure(writer.Put(TLV::ProfileTag(writer.ImplicitProfileId, 2), setupPayload.discriminator)); - - PASEVerifier verifier; bool randomSetupPIN = (option == PairingWindowOption::kTokenWithRandomPIN); - ReturnErrorOnFailure(PASESession::GeneratePASEVerifier(verifier, randomSetupPIN, setupPayload.setUpPINCode)); - ReturnErrorOnFailure(writer.PutBytes(TLV::ProfileTag(writer.ImplicitProfileId, 3), - reinterpret_cast(verifier), sizeof(verifier))); - } - - System::PacketBufferHandle outBuffer; - ReturnErrorOnFailure(writer.Finalize(&outBuffer)); + PASEVerifier verifier; + ByteSpan salt(reinterpret_cast(kSpake2pKeyExchangeSalt), strlen(kSpake2pKeyExchangeSalt)); + ReturnErrorOnFailure( + PASESession::GeneratePASEVerifier(verifier, kSpake2p_Iteration_Count, salt, randomSetupPIN, setupPayload.setUpPINCode)); - ReturnErrorOnFailure(SendMessage(Protocols::ServiceProvisioning::MsgType::ServiceProvisioningRequest, - Messaging::SendMessageFlags::kNone, std::move(outBuffer))); + ReturnErrorOnFailure( + cluster.OpenCommissioningWindow(successCallback, failureCallback, timeout, ByteSpan(&verifier[0][0], sizeof(verifier)), + setupPayload.discriminator, kSpake2p_Iteration_Count, salt, mPAKEVerifierID++)); + } + else + { + ReturnErrorOnFailure(cluster.OpenBasicCommissioningWindow(successCallback, failureCallback, timeout)); + } setupPayload.version = 0; setupPayload.rendezvousInformation = RendezvousInformationFlags(RendezvousInformationFlag::kBLE); diff --git a/src/controller/CHIPDevice.h b/src/controller/CHIPDevice.h index ca2fea5046056a..6a1969ce18c5e8 100644 --- a/src/controller/CHIPDevice.h +++ b/src/controller/CHIPDevice.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -98,7 +99,10 @@ class DLL_EXPORT Device : public Messaging::ExchangeDelegate, public SessionEsta { public: ~Device(); - Device() = default; + Device() : + mOpenPairingSuccessCallback(OnOpenPairingWindowSuccessResponse, this), + mOpenPairingFailureCallback(OnOpenPairingWindowFailureResponse, this) + {} Device(const Device &) = delete; enum class PairingWindowOption @@ -299,7 +303,7 @@ class DLL_EXPORT Device : public Messaging::ExchangeDelegate, public SessionEsta * * @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error */ - CHIP_ERROR OpenPairingWindow(uint32_t timeout, PairingWindowOption option, SetupPayload & setupPayload); + CHIP_ERROR OpenPairingWindow(uint16_t timeout, PairingWindowOption option, SetupPayload & setupPayload); /** * In case there exists an open session to the device, mark it as expired. @@ -472,6 +476,9 @@ class DLL_EXPORT Device : public Messaging::ExchangeDelegate, public SessionEsta CHIP_ERROR WarmupCASESession(); + static void OnOpenPairingWindowSuccessResponse(void * context); + static void OnOpenPairingWindowFailureResponse(void * context, uint8_t status); + uint16_t mListenPort; FabricIndex mFabricIndex = Transport::kUndefinedFabricIndex; @@ -488,8 +495,13 @@ class DLL_EXPORT Device : public Messaging::ExchangeDelegate, public SessionEsta SessionIDAllocator * mIDAllocator = nullptr; + uint16_t mPAKEVerifierID = 1; + Callback::CallbackDeque mConnectionSuccess; Callback::CallbackDeque mConnectionFailure; + + Callback::Callback mOpenPairingSuccessCallback; + Callback::Callback mOpenPairingFailureCallback; }; /** diff --git a/src/protocols/secure_channel/PASESession.cpp b/src/protocols/secure_channel/PASESession.cpp index fc8dbf2cee994e..a86b93b0efed7c 100644 --- a/src/protocols/secure_channel/PASESession.cpp +++ b/src/protocols/secure_channel/PASESession.cpp @@ -54,8 +54,7 @@ const char * kSpake2pContext = "CHIP PAKE V1 Commissioning"; const char * kSpake2pI2RSessionInfo = "Commissioning I2R Key"; const char * kSpake2pR2ISessionInfo = "Commissioning R2I Key"; -static constexpr uint32_t kSpake2p_Iteration_Count = 100; -static const char * kSpake2pKeyExchangeSalt = "SPAKE2P Key Salt"; +const char * kSpake2pKeyExchangeSalt = "SPAKE2P Key Salt"; // Wait at most 30 seconds for the response from the peer. // This timeout value assumes the underlying transport is reliable. @@ -198,18 +197,23 @@ CHIP_ERROR PASESession::Init(uint16_t myKeyId, uint32_t setupCode, SessionEstabl return CHIP_NO_ERROR; } -CHIP_ERROR PASESession::ComputePASEVerifier(uint32_t setUpPINCode, uint32_t pbkdf2IterCount, const uint8_t * salt, size_t saltLen, +CHIP_ERROR PASESession::ComputePASEVerifier(uint32_t setUpPINCode, uint32_t pbkdf2IterCount, const ByteSpan & salt, PASEVerifier & verifier) { + ReturnErrorCodeIf(salt.empty(), CHIP_ERROR_INVALID_ARGUMENT); + ReturnErrorCodeIf(salt.data() == nullptr, CHIP_ERROR_INVALID_ARGUMENT); + ReturnErrorCodeIf(setUpPINCode >= (1 << kSetupPINCodeFieldLengthInBits), CHIP_ERROR_INVALID_ARGUMENT); + PBKDF2_sha256_crypto mPBKDF; uint8_t littleEndianSetupPINCode[sizeof(uint32_t)]; Encoding::LittleEndian::Put32(littleEndianSetupPINCode, setUpPINCode); - return mPBKDF.pbkdf2_sha256(littleEndianSetupPINCode, sizeof(littleEndianSetupPINCode), salt, saltLen, pbkdf2IterCount, - sizeof(PASEVerifier), &verifier[0][0]); + return mPBKDF.pbkdf2_sha256(littleEndianSetupPINCode, sizeof(littleEndianSetupPINCode), salt.data(), salt.size(), + pbkdf2IterCount, sizeof(PASEVerifier), &verifier[0][0]); } -CHIP_ERROR PASESession::GeneratePASEVerifier(PASEVerifier & verifier, bool useRandomPIN, uint32_t & setupPIN) +CHIP_ERROR PASESession::GeneratePASEVerifier(PASEVerifier & verifier, uint32_t pbkdf2IterCount, const ByteSpan & salt, + bool useRandomPIN, uint32_t & setupPIN) { if (useRandomPIN) { @@ -218,17 +222,11 @@ CHIP_ERROR PASESession::GeneratePASEVerifier(PASEVerifier & verifier, bool useRa // Use only kSetupPINCodeFieldLengthInBits bits out of the code setupPIN &= ((1 << kSetupPINCodeFieldLengthInBits) - 1); } - else if (setupPIN >= (1 << kSetupPINCodeFieldLengthInBits)) - { - return CHIP_ERROR_INVALID_ARGUMENT; - } - return PASESession::ComputePASEVerifier(setupPIN, kSpake2p_Iteration_Count, - reinterpret_cast(kSpake2pKeyExchangeSalt), - strlen(kSpake2pKeyExchangeSalt), verifier); + return PASESession::ComputePASEVerifier(setupPIN, pbkdf2IterCount, salt, verifier); } -CHIP_ERROR PASESession::SetupSpake2p(uint32_t pbkdf2IterCount, const uint8_t * salt, size_t saltLen) +CHIP_ERROR PASESession::SetupSpake2p(uint32_t pbkdf2IterCount, const ByteSpan & salt) { uint8_t context[kSHA256_Hash_Length] = { 0, @@ -236,10 +234,7 @@ CHIP_ERROR PASESession::SetupSpake2p(uint32_t pbkdf2IterCount, const uint8_t * s if (mComputeVerifier) { - VerifyOrReturnError(salt != nullptr, CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrReturnError(saltLen > 0, CHIP_ERROR_INVALID_ARGUMENT); - - ReturnErrorOnFailure(PASESession::ComputePASEVerifier(mSetupPINCode, pbkdf2IterCount, salt, saltLen, mPASEVerifier)); + ReturnErrorOnFailure(PASESession::ComputePASEVerifier(mSetupPINCode, pbkdf2IterCount, salt, mPASEVerifier)); } MutableByteSpan contextSpan{ context, sizeof(context) }; @@ -250,20 +245,20 @@ CHIP_ERROR PASESession::SetupSpake2p(uint32_t pbkdf2IterCount, const uint8_t * s return CHIP_NO_ERROR; } -CHIP_ERROR PASESession::WaitForPairing(uint32_t mySetUpPINCode, uint32_t pbkdf2IterCount, const uint8_t * salt, size_t saltLen, - uint16_t myKeyId, SessionEstablishmentDelegate * delegate) +CHIP_ERROR PASESession::WaitForPairing(uint32_t mySetUpPINCode, uint32_t pbkdf2IterCount, const ByteSpan & salt, uint16_t myKeyId, + SessionEstablishmentDelegate * delegate) { // Return early on error here, as we have not initalized any state yet - ReturnErrorCodeIf(salt == nullptr, CHIP_ERROR_INVALID_ARGUMENT); - ReturnErrorCodeIf(saltLen == 0, CHIP_ERROR_INVALID_ARGUMENT); + ReturnErrorCodeIf(salt.empty(), CHIP_ERROR_INVALID_ARGUMENT); + ReturnErrorCodeIf(salt.data() == nullptr, CHIP_ERROR_INVALID_ARGUMENT); CHIP_ERROR err = Init(myKeyId, mySetUpPINCode, delegate); // From here onwards, let's go to exit on error, as some state might have already // been initialized SuccessOrExit(err); - VerifyOrExit(CanCastTo(saltLen), err = CHIP_ERROR_INVALID_ARGUMENT); - mSaltLength = static_cast(saltLen); + VerifyOrExit(CanCastTo(salt.size()), err = CHIP_ERROR_INVALID_ARGUMENT); + mSaltLength = static_cast(salt.size()); if (mSalt != nullptr) { @@ -274,12 +269,13 @@ CHIP_ERROR PASESession::WaitForPairing(uint32_t mySetUpPINCode, uint32_t pbkdf2I mSalt = static_cast(chip::Platform::MemoryAlloc(mSaltLength)); VerifyOrExit(mSalt != nullptr, err = CHIP_ERROR_NO_MEMORY); - memmove(mSalt, salt, mSaltLength); + memmove(mSalt, salt.data(), mSaltLength); mIterationCount = pbkdf2IterCount; mNextExpectedMsg = Protocols::SecureChannel::MsgType::PBKDFParamRequest; mPairingComplete = false; + mPasscodeID = 0; ChipLogDetail(SecureChannel, "Waiting for PBKDF param request"); @@ -291,14 +287,14 @@ CHIP_ERROR PASESession::WaitForPairing(uint32_t mySetUpPINCode, uint32_t pbkdf2I return err; } -CHIP_ERROR PASESession::WaitForPairing(const PASEVerifier & verifier, uint16_t myKeyId, SessionEstablishmentDelegate * delegate) +CHIP_ERROR PASESession::WaitForPairing(const PASEVerifier & verifier, uint32_t pbkdf2IterCount, const ByteSpan & salt, + uint16_t passcodeID, uint16_t myKeyId, SessionEstablishmentDelegate * delegate) { - ReturnErrorOnFailure(WaitForPairing(0, kSpake2p_Iteration_Count, - reinterpret_cast(kSpake2pKeyExchangeSalt), - strlen(kSpake2pKeyExchangeSalt), myKeyId, delegate)); + ReturnErrorOnFailure(WaitForPairing(0, pbkdf2IterCount, salt, myKeyId, delegate)); memmove(&mPASEVerifier, verifier, sizeof(verifier)); mComputeVerifier = false; + mPasscodeID = passcodeID; return CHIP_NO_ERROR; } @@ -430,7 +426,7 @@ CHIP_ERROR PASESession::SendPBKDFParamResponse() // Update commissioning hash with the pbkdf2 param response that's being sent. ReturnErrorOnFailure(mCommissioningHash.AddData(ByteSpan{ resp->Start(), resp->DataLength() })); - ReturnErrorOnFailure(SetupSpake2p(mIterationCount, mSalt, mSaltLength)); + ReturnErrorOnFailure(SetupSpake2p(mIterationCount, ByteSpan(mSalt, mSaltLength))); ReturnErrorOnFailure(mSpake2p.ComputeL(mPoint, &sizeof_point, &mPASEVerifier[1][0], kSpake2p_WS_Length)); mNextExpectedMsg = Protocols::SecureChannel::MsgType::PASE_Spake2p1; @@ -476,7 +472,7 @@ CHIP_ERROR PASESession::HandlePBKDFParamResponse(const System::PacketBufferHandl err = mCommissioningHash.AddData(ByteSpan{ resp, resplen }); SuccessOrExit(err); - err = SetupSpake2p(static_cast(iterCount), msgptr, saltlen); + err = SetupSpake2p(static_cast(iterCount), ByteSpan(msgptr, saltlen)); SuccessOrExit(err); } diff --git a/src/protocols/secure_channel/PASESession.h b/src/protocols/secure_channel/PASESession.h index b2ec8e4f755ac2..4cdfa798860603 100644 --- a/src/protocols/secure_channel/PASESession.h +++ b/src/protocols/secure_channel/PASESession.h @@ -48,8 +48,10 @@ namespace chip { extern const char * kSpake2pI2RSessionInfo; extern const char * kSpake2pR2ISessionInfo; +extern const char * kSpake2pKeyExchangeSalt; constexpr uint16_t kPBKDFParamRandomNumberSize = 32; +constexpr uint32_t kSpake2p_Iteration_Count = 100; using namespace Crypto; @@ -86,26 +88,29 @@ class DLL_EXPORT PASESession : public Messaging::ExchangeDelegate, public Pairin * @param mySetUpPINCode Setup PIN code of the local device * @param pbkdf2IterCount Iteration count for PBKDF2 function * @param salt Salt to be used for SPAKE2P operation - * @param saltLen Length of salt * @param myKeyId Key ID to be assigned to the secure session on the peer node * @param delegate Callback object * * @return CHIP_ERROR The result of initialization */ - CHIP_ERROR WaitForPairing(uint32_t mySetUpPINCode, uint32_t pbkdf2IterCount, const uint8_t * salt, size_t saltLen, - uint16_t myKeyId, SessionEstablishmentDelegate * delegate); + CHIP_ERROR WaitForPairing(uint32_t mySetUpPINCode, uint32_t pbkdf2IterCount, const ByteSpan & salt, uint16_t myKeyId, + SessionEstablishmentDelegate * delegate); /** * @brief * Initialize using PASE verifier and wait for pairing requests. * * @param verifier PASE verifier to be used for SPAKE2P pairing + * @param pbkdf2IterCount Iteration count for PBKDF2 function + * @param salt Salt to be used for SPAKE2P operation + * @param passcodeID Passcode ID assigned by the administrator to this PASE verifier * @param myKeyId Key ID to be assigned to the secure session on the peer node * @param delegate Callback object * * @return CHIP_ERROR The result of initialization */ - CHIP_ERROR WaitForPairing(const PASEVerifier & verifier, uint16_t myKeyId, SessionEstablishmentDelegate * delegate); + CHIP_ERROR WaitForPairing(const PASEVerifier & verifier, uint32_t pbkdf2IterCount, const ByteSpan & salt, uint16_t passcodeID, + uint16_t myKeyId, SessionEstablishmentDelegate * delegate); /** * @brief @@ -129,13 +134,16 @@ class DLL_EXPORT PASESession : public Messaging::ExchangeDelegate, public Pairin * @brief * Generate a new PASE verifier. * - * @param verifier The generated PASE verifier - * @param useRandomPIN Generate a random setup PIN, if true. Else, use the provided PIN - * @param setupPIN Provided setup PIN (if useRandomPIN is false), or the generated PIN + * @param verifier The generated PASE verifier + * @param pbkdf2IterCount Iteration count for PBKDF2 function + * @param salt Salt to be used for SPAKE2P operation + * @param useRandomPIN Generate a random setup PIN, if true. Else, use the provided PIN + * @param setupPIN Provided setup PIN (if useRandomPIN is false), or the generated PIN * * @return CHIP_ERROR The result of PASE verifier generation */ - static CHIP_ERROR GeneratePASEVerifier(PASEVerifier & verifier, bool useRandomPIN, uint32_t & setupPIN); + static CHIP_ERROR GeneratePASEVerifier(PASEVerifier & verifier, uint32_t pbkdf2IterCount, const ByteSpan & salt, + bool useRandomPIN, uint32_t & setupPIN); /** * @brief @@ -246,10 +254,10 @@ class DLL_EXPORT PASESession : public Messaging::ExchangeDelegate, public Pairin CHIP_ERROR ValidateReceivedMessage(Messaging::ExchangeContext * exchange, const PacketHeader & packetHeader, const PayloadHeader & payloadHeader, System::PacketBufferHandle && msg); - static CHIP_ERROR ComputePASEVerifier(uint32_t mySetUpPINCode, uint32_t pbkdf2IterCount, const uint8_t * salt, size_t saltLen, + static CHIP_ERROR ComputePASEVerifier(uint32_t mySetUpPINCode, uint32_t pbkdf2IterCount, const ByteSpan & salt, PASEVerifier & verifier); - CHIP_ERROR SetupSpake2p(uint32_t pbkdf2IterCount, const uint8_t * salt, size_t saltLen); + CHIP_ERROR SetupSpake2p(uint32_t pbkdf2IterCount, const ByteSpan & salt); CHIP_ERROR SendPBKDFParamRequest(); CHIP_ERROR HandlePBKDFParamRequest(const System::PacketBufferHandle & msg); @@ -282,6 +290,8 @@ class DLL_EXPORT PASESession : public Messaging::ExchangeDelegate, public Pairin /* w0s and w1s */ PASEVerifier mPASEVerifier; + uint16_t mPasscodeID = 0; + uint32_t mSetupPINCode; bool mComputeVerifier = true; diff --git a/src/protocols/secure_channel/tests/TestPASESession.cpp b/src/protocols/secure_channel/tests/TestPASESession.cpp index a6b0eab50df90a..9f4e6fe2933e68 100644 --- a/src/protocols/secure_channel/tests/TestPASESession.cpp +++ b/src/protocols/secure_channel/tests/TestPASESession.cpp @@ -113,10 +113,12 @@ void SecurePairingWaitTest(nlTestSuite * inSuite, void * inContext) gLoopback.Reset(); - NL_TEST_ASSERT(inSuite, pairing.WaitForPairing(1234, 500, nullptr, 0, 0, &delegate) == CHIP_ERROR_INVALID_ARGUMENT); + NL_TEST_ASSERT(inSuite, pairing.WaitForPairing(1234, 500, ByteSpan(nullptr, 0), 0, &delegate) == CHIP_ERROR_INVALID_ARGUMENT); NL_TEST_ASSERT(inSuite, - pairing.WaitForPairing(1234, 500, (const uint8_t *) "saltSalt", 8, 0, nullptr) == CHIP_ERROR_INVALID_ARGUMENT); - NL_TEST_ASSERT(inSuite, pairing.WaitForPairing(1234, 500, (const uint8_t *) "saltSalt", 8, 0, &delegate) == CHIP_NO_ERROR); + pairing.WaitForPairing(1234, 500, ByteSpan((const uint8_t *) "saltSalt", 8), 0, nullptr) == + CHIP_ERROR_INVALID_ARGUMENT); + NL_TEST_ASSERT(inSuite, + pairing.WaitForPairing(1234, 500, ByteSpan((const uint8_t *) "saltSalt", 8), 0, &delegate) == CHIP_NO_ERROR); } void SecurePairingStartTest(nlTestSuite * inSuite, void * inContext) @@ -192,7 +194,7 @@ void SecurePairingHandshakeTestCommon(nlTestSuite * inSuite, void * inContext, P Protocols::SecureChannel::MsgType::PBKDFParamRequest, &pairingAccessory) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, - pairingAccessory.WaitForPairing(1234, 500, (const uint8_t *) "saltSALT", 8, 0, &delegateAccessory) == + pairingAccessory.WaitForPairing(1234, 500, ByteSpan((const uint8_t *) "saltSALT", 8), 0, &delegateAccessory) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, pairingCommissioner.Pair(Transport::PeerAddress(Transport::Type::kBle), 1234, 0, contextCommissioner, @@ -263,7 +265,7 @@ void SecurePairingFailedHandshake(nlTestSuite * inSuite, void * inContext) Protocols::SecureChannel::MsgType::PBKDFParamRequest, &pairingAccessory) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, - pairingAccessory.WaitForPairing(1234, 500, (const uint8_t *) "saltSALT", 8, 0, &delegateAccessory) == + pairingAccessory.WaitForPairing(1234, 500, ByteSpan((const uint8_t *) "saltSALT", 8), 0, &delegateAccessory) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, pairingCommissioner.Pair(Transport::PeerAddress(Transport::Type::kBle), 4321, 0, contextCommissioner,