From 1369703b5e2aed1c3e0d4f0885222d6b8640483d Mon Sep 17 00:00:00 2001 From: Karsten Sperling <113487422+ksperling-apple@users.noreply.github.com> Date: Fri, 17 Nov 2023 15:59:07 +1300 Subject: [PATCH] Implement PDC support in Network Commissioning cluster (#30511) * Small refactorings of network-commissioning.cpp - Tidy up EnumerateAcceptedCommands - Move constructors out of header - Simplify debug text logic - Simplify enumeration of Iterator - Use CHIP_ERROR_FORMAT for logging errors - Refer to Status type name consistently * Implement PDC support in Network Commissioning cluster and add the relevant APIs to NetworkCommissioning::WiFiDriver. --- .../network-commissioning.cpp | 427 ++++++++++++++---- .../network-commissioning.h | 27 +- src/include/platform/CHIPDeviceConfig.h | 9 + src/include/platform/NetworkCommissioning.h | 78 +++- .../Darwin/CHIPDevicePlatformConfig.h | 1 + src/platform/Linux/CHIPDevicePlatformConfig.h | 1 + 6 files changed, 435 insertions(+), 108 deletions(-) diff --git a/src/app/clusters/network-commissioning/network-commissioning.cpp b/src/app/clusters/network-commissioning/network-commissioning.cpp index 1026105160ffbb..99e2a4cbfd136e 100644 --- a/src/app/clusters/network-commissioning/network-commissioning.cpp +++ b/src/app/clusters/network-commissioning/network-commissioning.cpp @@ -23,32 +23,43 @@ #include #include #include +#include #include #include +#include #include #include #include +#include #include #include #include #include -using namespace chip; -using namespace chip::app; -using namespace chip::app::Clusters; -using namespace chip::app::Clusters::NetworkCommissioning; +#include +#include namespace chip { namespace app { namespace Clusters { namespace NetworkCommissioning { +using namespace Credentials; +using namespace DataModel; using namespace DeviceLayer::NetworkCommissioning; namespace { -// For WiFi and Thread scan results, each item will cose ~60 bytes in TLV, thus 15 is a safe upper bound of scan results. + +// For WiFi and Thread scan results, each item will cost ~60 bytes in TLV, thus 15 is a safe upper bound of scan results. constexpr size_t kMaxNetworksInScanResponse = 15; +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC +constexpr size_t kPossessionNonceSize = 32; +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + +// Note: CHIP_CONFIG_NETWORK_COMMISSIONING_DEBUG_TEXT_BUFFER_SIZE can be 0, this disables debug text +using DebugTextStorage = std::array; + enum ValidWiFiCredentialLength { kOpen = 0, @@ -58,11 +69,53 @@ enum ValidWiFiCredentialLength kWPAPSKHex = 64, }; +template +static void EnumerateAndRelease(Iterator * iterator, Func func) +{ + if (iterator != nullptr) + { + T element; + while (iterator->Next(element) && func(element) == Loop::Continue) + { + /* continue */ + } + iterator->Release(); + } +} + +BitFlags WiFiFeatures(WiFiDriver * driver) +{ + BitFlags features = Feature::kWiFiNetworkInterface; +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + features.Set(Feature::kPerDeviceCredentials, driver->SupportsPerDeviceCredentials()); +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + return features; +} + } // namespace +Instance::Instance(EndpointId aEndpointId, WiFiDriver * apDelegate) : + CommandHandlerInterface(Optional(aEndpointId), Id), AttributeAccessInterface(Optional(aEndpointId), Id), + mFeatureFlags(WiFiFeatures(apDelegate)), mpWirelessDriver(apDelegate), mpBaseDriver(apDelegate) +{ + mpDriver.Set(apDelegate); +} + +Instance::Instance(EndpointId aEndpointId, ThreadDriver * apDelegate) : + CommandHandlerInterface(Optional(aEndpointId), Id), AttributeAccessInterface(Optional(aEndpointId), Id), + mFeatureFlags(Feature::kThreadNetworkInterface), mpWirelessDriver(apDelegate), mpBaseDriver(apDelegate) +{ + mpDriver.Set(apDelegate); +} + +Instance::Instance(EndpointId aEndpointId, EthernetDriver * apDelegate) : + CommandHandlerInterface(Optional(aEndpointId), Id), AttributeAccessInterface(Optional(aEndpointId), Id), + mFeatureFlags(Feature::kEthernetNetworkInterface), mpWirelessDriver(nullptr), mpBaseDriver(apDelegate) +{} + CHIP_ERROR Instance::Init() { - ReturnErrorOnFailure(chip::app::InteractionModelEngine::GetInstance()->RegisterCommandHandler(this)); + ReturnErrorOnFailure(InteractionModelEngine::GetInstance()->RegisterCommandHandler(this)); VerifyOrReturnError(registerAttributeAccessOverride(this), CHIP_ERROR_INCORRECT_STATE); ReturnErrorOnFailure(DeviceLayer::PlatformMgrImpl().AddEventHandler(OnPlatformEventHandler, reinterpret_cast(this))); ReturnErrorOnFailure(mpBaseDriver->Init(this)); @@ -128,6 +181,13 @@ void Instance::InvokeCommand(HandlerContext & ctxt) HandleCommand( ctxt, [this](HandlerContext & ctx, const auto & req) { HandleReorderNetwork(ctx, req); }); return; +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + case Commands::QueryIdentity::Id: + VerifyOrReturn(mFeatureFlags.Has(Feature::kPerDeviceCredentials)); + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleQueryIdentity(ctx, req); }); + return; +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC } } @@ -140,22 +200,25 @@ CHIP_ERROR Instance::Read(const ConcreteReadAttributePath & aPath, AttributeValu case Attributes::Networks::Id: return aEncoder.EncodeList([this](const auto & encoder) { - auto networks = mpBaseDriver->GetNetworks(); CHIP_ERROR err = CHIP_NO_ERROR; Structs::NetworkInfoStruct::Type networkForEncode; - NetworkCommissioning::Network network; - for (; networks != nullptr && networks->Next(network);) - { + EnumerateAndRelease(mpBaseDriver->GetNetworks(), [&](const Network & network) { networkForEncode.networkID = ByteSpan(network.networkID, network.networkIDLen); networkForEncode.connected = network.connected; - SuccessOrExit(err = encoder.Encode(networkForEncode)); - } - exit: - if (networks != nullptr) - { - networks->Release(); - } +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + // These fields are both optional and nullable in NetworkInfoStruct. + // If PDC is supported, the fields are always present but may be null. + if (mFeatureFlags.Has(Feature::kPerDeviceCredentials)) + { + networkForEncode.networkIdentifier = MakeOptional(Nullable(network.networkIdentifier)); + networkForEncode.clientIdentifier = MakeOptional(Nullable(network.clientIdentifier)); + } +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + + err = encoder.Encode(networkForEncode); + return (err == CHIP_NO_ERROR) ? Loop::Continue : Loop::Break; + }); return err; }); @@ -213,8 +276,7 @@ CHIP_ERROR Instance::Write(const ConcreteDataAttributePath & aPath, AttributeVal } } -void Instance::OnNetworkingStatusChange(NetworkCommissioning::Status aCommissioningError, Optional aNetworkId, - Optional aConnectStatus) +void Instance::OnNetworkingStatusChange(Status aCommissioningError, Optional aNetworkId, Optional aConnectStatus) { if (aNetworkId.HasValue() && aNetworkId.Value().size() > kMaxNetworkIDLen) { @@ -285,14 +347,13 @@ void Instance::HandleScanNetworks(HandlerContext & ctx, const Commands::ScanNetw } namespace { - void FillDebugTextAndNetworkIndex(Commands::NetworkConfigResponse::Type & response, MutableCharSpan debugText, uint8_t networkIndex) { if (!debugText.empty()) { - response.debugText.SetValue(CharSpan(debugText.data(), debugText.size())); + response.debugText.SetValue(debugText); } - if (response.networkingStatus == NetworkCommissioningStatusEnum::kSuccess) + if (response.networkingStatus == Status::kSuccess) { response.networkIndex.SetValue(networkIndex); } @@ -319,6 +380,26 @@ void Instance::HandleAddOrUpdateWiFiNetwork(HandlerContext & ctx, const Commands VerifyOrReturn(CheckFailSafeArmed(ctx)); + if (req.ssid.empty() || req.ssid.size() > DeviceLayer::Internal::kMaxWiFiSSIDLength) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::InvalidCommand, "ssid"); + return; + } + + // Presence of a Network Identity indicates we're configuring for Per-Device Credentials + if (req.networkIdentity.HasValue()) + { +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + if (mFeatureFlags.Has(Feature::kWiFiNetworkInterface)) + { + HandleAddOrUpdateWiFiNetworkWithPDC(ctx, req); + return; + } +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::InvalidCommand); + return; + } + // Spec 11.8.8.4 // Valid Credentials length are: // - 0 bytes: Unsecured (open) connection @@ -355,22 +436,124 @@ void Instance::HandleAddOrUpdateWiFiNetwork(HandlerContext & ctx, const Commands } Commands::NetworkConfigResponse::Type response; - MutableCharSpan debugText; -#if CHIP_CONFIG_NETWORK_COMMISSIONING_DEBUG_TEXT_BUFFER_SIZE - char debugTextBuffer[CHIP_CONFIG_NETWORK_COMMISSIONING_DEBUG_TEXT_BUFFER_SIZE]; - debugText = MutableCharSpan(debugTextBuffer); -#endif + DebugTextStorage debugTextBuffer; + MutableCharSpan debugText(debugTextBuffer); uint8_t outNetworkIndex = 0; response.networkingStatus = mpDriver.Get()->AddOrUpdateNetwork(req.ssid, req.credentials, debugText, outNetworkIndex); FillDebugTextAndNetworkIndex(response, debugText, outNetworkIndex); ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); - if (response.networkingStatus == NetworkCommissioningStatusEnum::kSuccess) + if (response.networkingStatus == Status::kSuccess) { UpdateBreadcrumb(req.breadcrumb); } } +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC +void Instance::HandleAddOrUpdateWiFiNetworkWithPDC(HandlerContext & ctx, + const Commands::AddOrUpdateWiFiNetwork::DecodableType & req) +{ + // Credentials must be empty when configuring for PDC, it's only present to keep the command shape compatible. + if (!req.credentials.empty()) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::InvalidCommand, "credentials"); + return; + } + + auto && networkIdentity = req.networkIdentity.Value(); // presence checked by caller + if (networkIdentity.size() > kMaxCHIPCompactNetworkIdentityLength || + Credentials::ValidateChipNetworkIdentity(networkIdentity) != CHIP_NO_ERROR) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::InvalidCommand, "networkIdentity"); + return; + } + + if (req.clientIdentifier.HasValue() && req.clientIdentifier.Value().size() != CertificateKeyId::size()) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::InvalidCommand, "clientIdentifier"); + return; + } + + bool provePossession = req.possessionNonce.HasValue(); + if (provePossession && req.possessionNonce.Value().size() != kPossessionNonceSize) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::InvalidCommand, "possessionNonce"); + return; + } + + auto err = CHIP_NO_ERROR; + { + auto driver = mpDriver.Get(); + + // If the client is requesting re-use of a Client Identity, find the existing network it belongs to + Optional clientIdentityNetworkIndex; + if (req.clientIdentifier.HasValue()) + { + CertificateKeyId clientIdentifier(req.clientIdentifier.Value().data()); + uint8_t networkIndex = 0; + EnumerateAndRelease(driver->GetNetworks(), [&](const Network & network) { + if (network.clientIdentifier.HasValue() && clientIdentifier.data_equal(network.clientIdentifier.Value())) + { + clientIdentityNetworkIndex.SetValue(networkIndex); + return Loop::Break; + } + networkIndex++; + return Loop::Continue; + }); + if (!clientIdentityNetworkIndex.HasValue()) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::NotFound, "clientIdentifier"); + return; + } + } + + // Allocate a buffer to hold the client identity, and leave enough room to append the possession nonce if needed. + chip::Platform::ScopedMemoryBuffer identityBuffer; + size_t identityBufferSize = kMaxCHIPCompactNetworkIdentityLength + (provePossession ? kPossessionNonceSize : 0); + VerifyOrExit(identityBuffer.Alloc(identityBufferSize), /**/); + + // Add/Update the network at the driver level + MutableByteSpan clientIdentity(identityBuffer.Get(), kMaxCHIPCompactNetworkIdentityLength); + Optional possessionSignature; + Status status = Status::kUnknownError; + DebugTextStorage debugTextBuffer; + MutableCharSpan debugText(debugTextBuffer); + uint8_t networkIndex; + SuccessOrExit(err = driver->AddOrUpdateNetworkWithPDC(req.ssid, networkIdentity, clientIdentityNetworkIndex, status, + debugText, clientIdentity, networkIndex)); + + Commands::NetworkConfigResponse::Type response; + response.networkingStatus = status; + FillDebugTextAndNetworkIndex(response, debugText, networkIndex); + + if (status == Status::kSuccess) + { + response.clientIdentity.SetValue(clientIdentity); + + if (provePossession) + { + // PossessionSignature TBS message = (NetworkClientIdentity || PossessionNonce) + memcpy(clientIdentity.end(), req.possessionNonce.Value().data(), kPossessionNonceSize); + ByteSpan tbsMessage(clientIdentity.data(), clientIdentity.size() + kPossessionNonceSize); + SuccessOrExit(err = driver->SignWithClientIdentity(networkIndex, tbsMessage, possessionSignature.Emplace())); + response.possessionSignature.SetValue(possessionSignature.Value().Span()); + } + + UpdateBreadcrumb(req.breadcrumb); + } + + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + } + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "AddOrUpdateWiFiNetwork with PDC failed: %" CHIP_ERROR_FORMAT, err.Format()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::Failure); + } +} +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + void Instance::HandleAddOrUpdateThreadNetwork(HandlerContext & ctx, const Commands::AddOrUpdateThreadNetwork::DecodableType & req) { MATTER_TRACE_SCOPE("HandleAddOrUpdateThreadNetwork", "NetworkCommissioning"); @@ -378,17 +561,14 @@ void Instance::HandleAddOrUpdateThreadNetwork(HandlerContext & ctx, const Comman VerifyOrReturn(CheckFailSafeArmed(ctx)); Commands::NetworkConfigResponse::Type response; - MutableCharSpan debugText; -#if CHIP_CONFIG_NETWORK_COMMISSIONING_DEBUG_TEXT_BUFFER_SIZE - char debugTextBuffer[CHIP_CONFIG_NETWORK_COMMISSIONING_DEBUG_TEXT_BUFFER_SIZE]; - debugText = MutableCharSpan(debugTextBuffer); -#endif + DebugTextStorage debugTextBuffer; + MutableCharSpan debugText(debugTextBuffer); uint8_t outNetworkIndex = 0; response.networkingStatus = mpDriver.Get()->AddOrUpdateNetwork(req.operationalDataset, debugText, outNetworkIndex); FillDebugTextAndNetworkIndex(response, debugText, outNetworkIndex); ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); - if (response.networkingStatus == NetworkCommissioningStatusEnum::kSuccess) + if (response.networkingStatus == Status::kSuccess) { UpdateBreadcrumb(req.breadcrumb); } @@ -415,16 +595,13 @@ void Instance::HandleRemoveNetwork(HandlerContext & ctx, const Commands::RemoveN VerifyOrReturn(CheckFailSafeArmed(ctx)); Commands::NetworkConfigResponse::Type response; - MutableCharSpan debugText; -#if CHIP_CONFIG_NETWORK_COMMISSIONING_DEBUG_TEXT_BUFFER_SIZE - char debugTextBuffer[CHIP_CONFIG_NETWORK_COMMISSIONING_DEBUG_TEXT_BUFFER_SIZE]; - debugText = MutableCharSpan(debugTextBuffer); -#endif + DebugTextStorage debugTextBuffer; + MutableCharSpan debugText(debugTextBuffer); uint8_t outNetworkIndex = 0; response.networkingStatus = mpWirelessDriver->RemoveNetwork(req.networkID, debugText, outNetworkIndex); FillDebugTextAndNetworkIndex(response, debugText, outNetworkIndex); ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); - if (response.networkingStatus == NetworkCommissioningStatusEnum::kSuccess) + if (response.networkingStatus == Status::kSuccess) { UpdateBreadcrumb(req.breadcrumb); } @@ -433,7 +610,7 @@ void Instance::HandleRemoveNetwork(HandlerContext & ctx, const Commands::RemoveN void Instance::HandleConnectNetwork(HandlerContext & ctx, const Commands::ConnectNetwork::DecodableType & req) { MATTER_TRACE_SCOPE("HandleConnectNetwork", "NetworkCommissioning"); - if (req.networkID.size() > DeviceLayer::NetworkCommissioning::kMaxNetworkIDLen) + if (req.networkID.size() > kMaxNetworkIDLen) { ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::InvalidValue); return; @@ -452,20 +629,102 @@ void Instance::HandleReorderNetwork(HandlerContext & ctx, const Commands::Reorde { MATTER_TRACE_SCOPE("HandleReorderNetwork", "NetworkCommissioning"); Commands::NetworkConfigResponse::Type response; - MutableCharSpan debugText; -#if CHIP_CONFIG_NETWORK_COMMISSIONING_DEBUG_TEXT_BUFFER_SIZE - char debugTextBuffer[CHIP_CONFIG_NETWORK_COMMISSIONING_DEBUG_TEXT_BUFFER_SIZE]; - debugText = MutableCharSpan(debugTextBuffer); -#endif + DebugTextStorage debugTextBuffer; + MutableCharSpan debugText(debugTextBuffer); response.networkingStatus = mpWirelessDriver->ReorderNetwork(req.networkID, req.networkIndex, debugText); FillDebugTextAndNetworkIndex(response, debugText, req.networkIndex); ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); - if (response.networkingStatus == NetworkCommissioningStatusEnum::kSuccess) + if (response.networkingStatus == Status::kSuccess) { UpdateBreadcrumb(req.breadcrumb); } } +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC +void Instance::HandleQueryIdentity(HandlerContext & ctx, const Commands::QueryIdentity::DecodableType & req) +{ + MATTER_TRACE_SCOPE("HandleQueryIdentity", "NetworkCommissioning"); + + if (req.keyIdentifier.size() != CertificateKeyId::size()) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::InvalidCommand, "keyIdentifier"); + return; + } + CertificateKeyId keyIdentifier(req.keyIdentifier.data()); + + bool provePossession = req.possessionNonce.HasValue(); + if (provePossession && req.possessionNonce.Value().size() != kPossessionNonceSize) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::InvalidCommand, "possessionNonce"); + return; + } + + auto err = CHIP_NO_ERROR; + auto status = Protocols::InteractionModel::Status::Success; + auto driver = mpDriver.Get(); + auto networks = driver->GetNetworks(); + VerifyOrExit(networks != nullptr && networks->Count() > 0, status = Protocols::InteractionModel::Status::NotFound); + + { + // Allocate a buffer to hold the identity, and leave enough room to append the possession nonce if needed. + chip::Platform::ScopedMemoryBuffer identityBuffer; + size_t identityBufferSize = kMaxCHIPCompactNetworkIdentityLength + (provePossession ? kPossessionNonceSize : 0); + VerifyOrExit(identityBuffer.Alloc(identityBufferSize), /**/); + + MutableByteSpan identity(identityBuffer.Get(), kMaxCHIPCompactNetworkIdentityLength); + Optional possessionSignature; + + Network network; + for (uint8_t networkIndex = 0;; networkIndex++) + { + VerifyOrExit(networks->Next(network), status = Protocols::InteractionModel::Status::NotFound); + + if (network.clientIdentifier.HasValue() && keyIdentifier.data_equal(network.clientIdentifier.Value())) + { + SuccessOrExit(err = driver->GetClientIdentity(networkIndex, identity)); + if (provePossession) + { + // PossessionSignature TBS message = (NetworkClientIdentity || PossessionNonce) + memcpy(identity.end(), req.possessionNonce.Value().data(), kPossessionNonceSize); + ByteSpan tbsMessage(identity.data(), identity.size() + kPossessionNonceSize); + SuccessOrExit(err = driver->SignWithClientIdentity(networkIndex, tbsMessage, possessionSignature.Emplace())); + } + break; + } + if (!provePossession && // Proof-of-possession is not possible for network identities + network.networkIdentifier.HasValue() && keyIdentifier.data_equal(network.networkIdentifier.Value())) + { + SuccessOrExit(err = driver->GetNetworkIdentity(networkIndex, identity)); + break; + } + } + + Commands::QueryIdentityResponse::Type response; + response.identity = identity; + if (possessionSignature.HasValue()) + { + response.possessionSignature.SetValue(possessionSignature.Value().Span()); + } + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + } + +exit: + if (networks != nullptr) + { + networks->Release(); + } + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "QueryIdentity failed: %" CHIP_ERROR_FORMAT, err.Format()); + status = Protocols::InteractionModel::Status::Failure; + } + if (status != Protocols::InteractionModel::Status::Success) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); + } +} +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + void Instance::OnResult(Status commissioningError, CharSpan debugText, int32_t interfaceStatus) { auto commandHandleRef = std::move(mAsyncCommandHandle); @@ -500,7 +759,7 @@ void Instance::OnResult(Status commissioningError, CharSpan debugText, int32_t i mLastNetworkingStatusValue.SetNonNull(commissioningError); commandHandle->AddResponse(mPath, response); - if (commissioningError == NetworkCommissioningStatusEnum::kSuccess) + if (commissioningError == Status::kSuccess) { CommitSavedBreadcrumb(); } @@ -525,7 +784,7 @@ void Instance::OnFinished(Status status, CharSpan debugText, ThreadScanResponseI TLV::TLVWriter * writer; TLV::TLVType listContainerType; ThreadScanResponse scanResponse; - chip::Platform::ScopedMemoryBuffer scanResponseArray; + Platform::ScopedMemoryBuffer scanResponseArray; size_t scanResponseArrayLength = 0; uint8_t extendedAddressBuffer[Thread::kSizeExtendedPanId]; @@ -611,9 +870,9 @@ void Instance::OnFinished(Status status, CharSpan debugText, ThreadScanResponseI exit: if (err != CHIP_NO_ERROR) { - ChipLogError(Zcl, "Failed to encode response: %s", err.AsString()); + ChipLogError(Zcl, "Failed to encode response: %" CHIP_ERROR_FORMAT, err.Format()); } - if (status == NetworkCommissioningStatusEnum::kSuccess) + if (status == Status::kSuccess) { CommitSavedBreadcrumb(); } @@ -672,9 +931,9 @@ void Instance::OnFinished(Status status, CharSpan debugText, WiFiScanResponseIte exit: if (err != CHIP_NO_ERROR) { - ChipLogError(Zcl, "Failed to encode response: %s", err.AsString()); + ChipLogError(Zcl, "Failed to encode response: %" CHIP_ERROR_FORMAT, err.Format()); } - if (status == NetworkCommissioningStatusEnum::kSuccess) + if (status == Status::kSuccess) { CommitSavedBreadcrumb(); } @@ -719,39 +978,39 @@ CHIP_ERROR Instance::EnumerateAcceptedCommands(const ConcreteClusterPath & clust { using namespace Clusters::NetworkCommissioning::Commands; - constexpr CommandId acceptedCommandsListWiFi[] = { - ScanNetworks::Id, AddOrUpdateWiFiNetwork::Id, RemoveNetwork::Id, ConnectNetwork::Id, ReorderNetwork::Id, - }; - constexpr CommandId acceptedCommandsListThread[] = { - ScanNetworks::Id, AddOrUpdateThreadNetwork::Id, RemoveNetwork::Id, ConnectNetwork::Id, ReorderNetwork::Id, - }; - if (mFeatureFlags.Has(Feature::kThreadNetworkInterface)) { - for (const auto & cmd : acceptedCommandsListThread) + for (auto && cmd : { + ScanNetworks::Id, + AddOrUpdateThreadNetwork::Id, + RemoveNetwork::Id, + ConnectNetwork::Id, + ReorderNetwork::Id, + }) { - if (callback(cmd, context) != Loop::Continue) - { - break; - } + VerifyOrExit(callback(cmd, context) == Loop::Continue, /**/); } - - return CHIP_NO_ERROR; } - - if (mFeatureFlags.Has(Feature::kWiFiNetworkInterface)) + else if (mFeatureFlags.Has(Feature::kWiFiNetworkInterface)) { - for (const auto & cmd : acceptedCommandsListWiFi) + for (auto && cmd : { + ScanNetworks::Id, + AddOrUpdateWiFiNetwork::Id, + RemoveNetwork::Id, + ConnectNetwork::Id, + ReorderNetwork::Id, + }) { - if (callback(cmd, context) != Loop::Continue) - { - break; - } + VerifyOrExit(callback(cmd, context) == Loop::Continue, /**/); } + } - return CHIP_NO_ERROR; + if (mFeatureFlags.Has(Feature::kPerDeviceCredentials)) + { + VerifyOrExit(callback(QueryIdentity::Id, context) == Loop::Continue, /**/); } +exit: return CHIP_NO_ERROR; } @@ -759,20 +1018,20 @@ CHIP_ERROR Instance::EnumerateGeneratedCommands(const ConcreteClusterPath & clus { using namespace Clusters::NetworkCommissioning::Commands; - constexpr CommandId generatedCommandsListWireless[] = { ScanNetworksResponse::Id, NetworkConfigResponse::Id, - ConnectNetworkResponse::Id }; - if (mFeatureFlags.HasAny(Feature::kWiFiNetworkInterface, Feature::kThreadNetworkInterface)) { - for (const auto & cmd : generatedCommandsListWireless) + for (auto && cmd : { ScanNetworksResponse::Id, NetworkConfigResponse::Id, ConnectNetworkResponse::Id }) { - if (callback(cmd, context) != Loop::Continue) - { - break; - } + VerifyOrExit(callback(cmd, context) == Loop::Continue, /**/); } } + if (mFeatureFlags.Has(Feature::kPerDeviceCredentials)) + { + VerifyOrExit(callback(QueryIdentityResponse::Id, context) == Loop::Continue, /**/); + } + +exit: return CHIP_NO_ERROR; } @@ -788,7 +1047,7 @@ uint8_t NullNetworkDriver::GetMaxNetworks() return 1; } -DeviceLayer::NetworkCommissioning::NetworkIterator * NullNetworkDriver::GetNetworks() +NetworkIterator * NullNetworkDriver::GetNetworks() { // Instance::Read accepts nullptr as an empty NetworkIterator. return nullptr; diff --git a/src/app/clusters/network-commissioning/network-commissioning.h b/src/app/clusters/network-commissioning/network-commissioning.h index c822a3f88881b1..d11b118a5be854 100644 --- a/src/app/clusters/network-commissioning/network-commissioning.h +++ b/src/app/clusters/network-commissioning/network-commissioning.h @@ -112,34 +112,17 @@ class Instance : public CommandHandlerInterface, // Actual handlers of the commands void HandleScanNetworks(HandlerContext & ctx, const Commands::ScanNetworks::DecodableType & req); void HandleAddOrUpdateWiFiNetwork(HandlerContext & ctx, const Commands::AddOrUpdateWiFiNetwork::DecodableType & req); + void HandleAddOrUpdateWiFiNetworkWithPDC(HandlerContext & ctx, const Commands::AddOrUpdateWiFiNetwork::DecodableType & req); void HandleAddOrUpdateThreadNetwork(HandlerContext & ctx, const Commands::AddOrUpdateThreadNetwork::DecodableType & req); void HandleRemoveNetwork(HandlerContext & ctx, const Commands::RemoveNetwork::DecodableType & req); void HandleConnectNetwork(HandlerContext & ctx, const Commands::ConnectNetwork::DecodableType & req); void HandleReorderNetwork(HandlerContext & ctx, const Commands::ReorderNetwork::DecodableType & req); + void HandleQueryIdentity(HandlerContext & ctx, const Commands::QueryIdentity::DecodableType & req); public: - Instance(EndpointId aEndpointId, DeviceLayer::NetworkCommissioning::WiFiDriver * apDelegate) : - CommandHandlerInterface(Optional(aEndpointId), Id), - AttributeAccessInterface(Optional(aEndpointId), Id), mFeatureFlags(Feature::kWiFiNetworkInterface), - mpWirelessDriver(apDelegate), mpBaseDriver(apDelegate) - { - mpDriver.Set(apDelegate); - } - - Instance(EndpointId aEndpointId, DeviceLayer::NetworkCommissioning::ThreadDriver * apDelegate) : - CommandHandlerInterface(Optional(aEndpointId), Id), - AttributeAccessInterface(Optional(aEndpointId), Id), mFeatureFlags(Feature::kThreadNetworkInterface), - mpWirelessDriver(apDelegate), mpBaseDriver(apDelegate) - { - mpDriver.Set(apDelegate); - } - - Instance(EndpointId aEndpointId, DeviceLayer::NetworkCommissioning::EthernetDriver * apDelegate) : - CommandHandlerInterface(Optional(aEndpointId), Id), - AttributeAccessInterface(Optional(aEndpointId), Id), mFeatureFlags(Feature::kEthernetNetworkInterface), - mpWirelessDriver(nullptr), mpBaseDriver(apDelegate) - {} - + Instance(EndpointId aEndpointId, DeviceLayer::NetworkCommissioning::WiFiDriver * apDelegate); + Instance(EndpointId aEndpointId, DeviceLayer::NetworkCommissioning::ThreadDriver * apDelegate); + Instance(EndpointId aEndpointId, DeviceLayer::NetworkCommissioning::EthernetDriver * apDelegate); virtual ~Instance() = default; }; diff --git a/src/include/platform/CHIPDeviceConfig.h b/src/include/platform/CHIPDeviceConfig.h index e29c00161337db..c5b2359fdcab89 100644 --- a/src/include/platform/CHIPDeviceConfig.h +++ b/src/include/platform/CHIPDeviceConfig.h @@ -346,6 +346,15 @@ #define CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION 1 #endif +/** + * CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + * + * Enable support for WiFi Per-Device Credentials + */ +#ifndef CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC +#define CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC 0 +#endif + /** * CHIP_DEVICE_CONFIG_WIFI_STATION_RECONNECT_INTERVAL * diff --git a/src/include/platform/NetworkCommissioning.h b/src/include/platform/NetworkCommissioning.h index 8ae56f3cd80d0b..cc27053beb574b 100644 --- a/src/include/platform/NetworkCommissioning.h +++ b/src/include/platform/NetworkCommissioning.h @@ -23,10 +23,14 @@ #pragma once +#include +#include #include #include #include #include +#include +#include #include #include @@ -79,8 +83,12 @@ class Iterator struct Network { uint8_t networkID[kMaxNetworkIDLen]; - uint8_t networkIDLen; - bool connected; + uint8_t networkIDLen = 0; +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + Optional networkIdentifier; + Optional clientIdentifier; +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + bool connected = false; }; static_assert(sizeof(Network::networkID) <= std::numeric_limits::max(), @@ -276,6 +284,72 @@ class WiFiDriver : public Internal::WirelessDriver */ virtual void ScanNetworks(ByteSpan ssid, ScanCallback * callback) = 0; +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + virtual bool SupportsPerDeviceCredentials() { return false; }; + + /** + * @brief Adds or updates a WiFi network with Per-Device Credentials on the device. + * + * @param ssid The SSID of the network to be added / updated. + * @param networkIdentity The Network Identity of the network, in compact-pdc-identity format. + * @param clientIdentityNetworkIndex If present, the index of the existing network configuration of which the Client + * Identity is to be re-used. Otherwise a new Client Identity shall be generated. + * @param outStatus The application-level status code (Status::kSuccess on success). + * @param outDebugText A debug text buffer that may be populated by the driver when a Status is returned. + * The size of the span must be reduced to the length of text emitted (or 0, if none). + * @param outClientIdentity On success, the Client Identity that was generated or copied, depending on the + * presence of `clientIdentityNetworkIndex`. + * @param outNextworkIndex On success, the index of the network entry that was added or updated. + * + * @retval CHIP_NO_ERROR and outStatus == kSuccess on success. + * @retval CHIP_NO_ERROR and outStatus != kSuccess for application-level errors. outDebugText should be populated. + * @retval CHIP_ERROR_* on internal errors. None of the output parameters will be examined in this case. + */ + virtual CHIP_ERROR AddOrUpdateNetworkWithPDC(ByteSpan ssid, ByteSpan networkIdentity, + Optional clientIdentityNetworkIndex, Status & outStatus, + MutableCharSpan & outDebugText, MutableByteSpan & outClientIdentity, + uint8_t & outNetworkIndex) + { + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + } + + /** + * @brief Retrieves the Network Identity associated with a network. + * + * @param networkIndex The 0-based index of the network. + * @param outNetworkIdentity The output buffer to be populated with the Network + * Identity in compact-pdc-identity TLV format. + * + * @return CHIP_NO_ERROR on success or a CHIP_ERROR on failure. + */ + virtual CHIP_ERROR GetNetworkIdentity(uint8_t networkIndex, MutableByteSpan & outNetworkIdentity) + { + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + } + + /** + * @brief Retrieves the Network Client Identity associated with a network. + * + * @param networkIndex The 0-based index of the network. + * @param outNetworkIdentity The output buffer to be populated with the Network + * Client Identity in compact-pdc-identity TLV format. + * + * @return CHIP_NO_ERROR on success or a CHIP_ERROR on failure. + */ + virtual CHIP_ERROR GetClientIdentity(uint8_t networkIndex, MutableByteSpan & outClientIdentity) + { + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + } + + /** + * @brief Signs the specified message with the private key of a Network Client Identity. + */ + virtual CHIP_ERROR SignWithClientIdentity(uint8_t networkIndex, ByteSpan & message, Crypto::P256ECDSASignature & outSignature) + { + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + } +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC + ~WiFiDriver() override = default; }; diff --git a/src/platform/Darwin/CHIPDevicePlatformConfig.h b/src/platform/Darwin/CHIPDevicePlatformConfig.h index 8b4ee23eb78c9f..8cb779756bf95b 100644 --- a/src/platform/Darwin/CHIPDevicePlatformConfig.h +++ b/src/platform/Darwin/CHIPDevicePlatformConfig.h @@ -25,6 +25,7 @@ // ==================== Platform Adaptations ==================== +#define CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC 1 #define CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION 0 #define CHIP_DEVICE_CONFIG_ENABLE_WIFI_AP 0 diff --git a/src/platform/Linux/CHIPDevicePlatformConfig.h b/src/platform/Linux/CHIPDevicePlatformConfig.h index 530e2716308842..70ed39d62899d5 100644 --- a/src/platform/Linux/CHIPDevicePlatformConfig.h +++ b/src/platform/Linux/CHIPDevicePlatformConfig.h @@ -26,6 +26,7 @@ // ==================== Platform Adaptations ==================== #if CHIP_DEVICE_CONFIG_ENABLE_WIFI +#define CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC 1 #define CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION 1 #define CHIP_DEVICE_CONFIG_ENABLE_WIFI_AP 0 #else