Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021-2024 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #pragma once
20 : #include <app-common/zap-generated/cluster-objects.h>
21 : #include <app/AttributePathParams.h>
22 : #include <app/ClusterStateCache.h>
23 : #include <app/OperationalSessionSetup.h>
24 : #include <controller/CommissioneeDeviceProxy.h>
25 : #include <credentials/attestation_verifier/DeviceAttestationDelegate.h>
26 : #include <credentials/attestation_verifier/DeviceAttestationVerifier.h>
27 : #include <credentials/jcm/TrustVerification.h>
28 : #include <crypto/CHIPCryptoPAL.h>
29 : #include <lib/support/Span.h>
30 : #include <lib/support/Variant.h>
31 : #include <matter/tracing/build_config.h>
32 : #include <system/SystemClock.h>
33 :
34 : namespace chip {
35 : namespace Controller {
36 :
37 : class DeviceCommissioner;
38 :
39 : enum CommissioningStage : uint8_t
40 : {
41 : kError,
42 : kSecurePairing, ///< Establish a PASE session with the device
43 : kReadCommissioningInfo, ///< Query Attributes relevant to commissioning (can perform multiple read interactions)
44 : kArmFailsafe, ///< Send ArmFailSafe (0x30:0) command to the device
45 : kConfigRegulatory, ///< Send SetRegulatoryConfig (0x30:2) command to the device
46 : kConfigureUTCTime, ///< SetUTCTime if the DUT has a time cluster
47 : kConfigureTimeZone, ///< Configure a time zone if one is required and available
48 : kConfigureDSTOffset, ///< Configure DST offset if one is required and available
49 : kConfigureDefaultNTP, ///< Configure a default NTP server if one is required and available
50 : kSendPAICertificateRequest, ///< Send PAI CertificateChainRequest (0x3E:2) command to the device
51 : kSendDACCertificateRequest, ///< Send DAC CertificateChainRequest (0x3E:2) command to the device
52 : kSendAttestationRequest, ///< Send AttestationRequest (0x3E:0) command to the device
53 : kAttestationVerification, ///< Verify AttestationResponse (0x3E:1) validity
54 : kAttestationRevocationCheck, ///< Verify Revocation Status of device's DAC chain
55 : kJCMTrustVerification, ///< Perform JCM trust verification steps
56 : kSendOpCertSigningRequest, ///< Send CSRRequest (0x3E:4) command to the device
57 : kValidateCSR, ///< Verify CSRResponse (0x3E:5) validity
58 : kGenerateNOCChain, ///< TLV encode Node Operational Credentials (NOC) chain certs
59 : kSendTrustedRootCert, ///< Send AddTrustedRootCertificate (0x3E:11) command to the device
60 : kSendNOC, ///< Send AddNOC (0x3E:6) command to the device
61 : kConfigureTrustedTimeSource, ///< Configure a trusted time source if one is required and available (must be done after SendNOC)
62 : kICDGetRegistrationInfo, ///< Waiting for the higher layer to provide ICD registration informations.
63 : kICDRegistration, ///< Register for ICD management
64 :
65 : // NOTE: If any new steps are added between kWiFiNetworkSetup and kICDSendStayActive, double-check
66 : // whether the logic in AutoCommissioner::CommissioningStepFinished that checks for "network
67 : // failure" conditions still makes sense.
68 : kWiFiNetworkSetup, ///< Send AddOrUpdateWiFiNetwork (0x31:2) command to the device
69 : kThreadNetworkSetup, ///< Send AddOrUpdateThreadNetwork (0x31:3) command to the device
70 : kFailsafeBeforeWiFiEnable, ///< Extend the fail-safe before doing kWiFiNetworkEnable
71 : kFailsafeBeforeThreadEnable, ///< Extend the fail-safe before doing kThreadNetworkEnable
72 : kWiFiNetworkEnable, ///< Send ConnectNetwork (0x31:6) command to the device for the WiFi network
73 : kThreadNetworkEnable, ///< Send ConnectNetwork (0x31:6) command to the device for the Thread network
74 : kEvictPreviousCaseSessions, ///< Evict previous stale case sessions from a commissioned device with this node ID before
75 : kFindOperationalForStayActive, ///< Perform operational discovery and establish a CASE session with the device for ICD
76 : ///< StayActive command
77 : kFindOperationalForCommissioningComplete, ///< Perform operational discovery and establish a CASE session with the device for
78 : ///< Commissioning Complete command
79 : kSendComplete, ///< Send CommissioningComplete (0x30:4) command to the device
80 : kICDSendStayActive, ///< Send Keep Alive to ICD
81 : // NOTE: If any new steps are added between kWiFiNetworkSetup and kICDSendStayActive, double-check
82 : // whether the logic in AutoCommissioner::CommissioningStepFinished that checks for "network
83 : // failure" conditions still makes sense.
84 :
85 : /// Send ScanNetworks (0x31:0) command to the device.
86 : /// ScanNetworks can happen anytime after kArmFailsafe.
87 : kScanNetworks,
88 : /// Waiting for the higher layer to provide network credentials before continuing the workflow.
89 : /// Call CHIPDeviceController::NetworkCredentialsReady() when CommissioningParameters is populated with
90 : /// network credentials to use in kWiFiNetworkSetup or kThreadNetworkSetup steps.
91 : kNeedsNetworkCreds,
92 : kPrimaryOperationalNetworkFailed, ///< Indicate that the primary operational network (on root endpoint) failed, should remove
93 : ///< the primary network config later.
94 : kRemoveWiFiNetworkConfig, ///< Remove Wi-Fi network config.
95 : kRemoveThreadNetworkConfig, ///< Remove Thread network config.
96 : kConfigureTCAcknowledgments, ///< Send SetTCAcknowledgements (0x30:6) command to the device
97 : kRequestWiFiCredentials, ///< Wi-Fi credentials are needed; ask for those.
98 : kRequestThreadCredentials, ///< Thread credentials are needed; ask for those.
99 : kCleanup, ///< Call delegates with status, free memory, clear timers and state.
100 : #if CHIP_DEVICE_CONFIG_ENABLE_NFC_BASED_COMMISSIONING
101 : kUnpoweredPhaseComplete, ///< Commissioning completed until connect network for unpowered commissioning (NFC)
102 : #endif
103 : };
104 :
105 : enum class ICDRegistrationStrategy : uint8_t
106 : {
107 : kIgnore, ///< Do not check whether the device is an ICD during commissioning
108 : kBeforeComplete, ///< Do commissioner self-registration or external controller registration,
109 : ///< Controller should provide a ICDKey manager for generating symmetric key
110 : };
111 :
112 : const char * StageToString(CommissioningStage stage);
113 :
114 : #if MATTER_TRACING_ENABLED
115 : const char * MetricKeyForCommissioningStage(CommissioningStage stage);
116 : #endif
117 :
118 : struct WiFiCredentials
119 : {
120 : ByteSpan ssid;
121 : ByteSpan credentials;
122 13 : WiFiCredentials(ByteSpan newSsid, ByteSpan newCreds) : ssid(newSsid), credentials(newCreds) {}
123 : };
124 :
125 : struct TermsAndConditionsAcknowledgement
126 : {
127 : uint16_t acceptedTermsAndConditions;
128 : uint16_t acceptedTermsAndConditionsVersion;
129 : };
130 :
131 : struct NOCChainGenerationParameters
132 : {
133 : ByteSpan nocsrElements;
134 : ByteSpan signature;
135 : };
136 :
137 : struct CompletionStatus
138 : {
139 127 : CompletionStatus() : err(CHIP_NO_ERROR), failedStage(NullOptional), attestationResult(NullOptional) {}
140 : CHIP_ERROR err;
141 : Optional<CommissioningStage> failedStage;
142 : Optional<Credentials::AttestationVerificationResult> attestationResult;
143 : Optional<app::Clusters::GeneralCommissioning::CommissioningErrorEnum> commissioningError;
144 : Optional<app::Clusters::NetworkCommissioning::NetworkCommissioningStatusEnum> networkCommissioningStatus;
145 : };
146 :
147 : inline constexpr uint16_t kDefaultFailsafeTimeout = 60;
148 :
149 : // Per spec, all commands that are sent with the failsafe armed need at least
150 : // a 30s timeout.
151 : inline constexpr System::Clock::Timeout kMinimumCommissioningStepTimeout = System::Clock::Seconds16(30);
152 :
153 : class CommissioningParameters
154 : {
155 : public:
156 : static constexpr size_t kMaxThreadDatasetLen = 254;
157 : static constexpr size_t kMaxSsidLen = 32;
158 : static constexpr size_t kMaxCredentialsLen = 64;
159 : static constexpr size_t kMaxCountryCodeLen = 2;
160 :
161 : // Value to use when setting the commissioning failsafe timer on the node being commissioned.
162 : // If the failsafe timer value is passed in as part of the commissioning parameters, that value will be used. If not supplied,
163 : // the AutoCommissioner will set this to the recommended value read from the node. If that is not set, it will fall back to the
164 : // default kDefaultFailsafeTimeout.
165 : // This value should be set before running PerformCommissioningStep for the kArmFailsafe step.
166 2 : const Optional<uint16_t> GetFailsafeTimerSeconds() const { return mFailsafeTimerSeconds; }
167 :
168 : // Value to use when re-setting the commissioning failsafe timer immediately prior to operational discovery.
169 : // If a CASE failsafe timer value is passed in as part of the commissioning parameters, then the failsafe timer
170 : // will be reset using this value before operational discovery begins. If not supplied, then the AutoCommissioner
171 : // will not automatically reset the failsafe timer before operational discovery begins. It can be useful for the
172 : // commissioner to set the CASE failsafe timer to a small value (ex. 30s) when the regular failsafe timer is set
173 : // to a larger value to accommodate user interaction during setup (network credential selection, user consent
174 : // after device attestation).
175 3 : const Optional<uint16_t> GetCASEFailsafeTimerSeconds() const { return mCASEFailsafeTimerSeconds; }
176 :
177 : // The location (indoor/outdoor) of the node being commissioned.
178 : // The node regulartory location (indoor/outdoor) should be set by the commissioner explicitly as it may be different than the
179 : // location of the commissioner. This location will be set on the node if the node supports configurable regulatory location
180 : // (from GetLocationCapability - see below). If the regulatory location is not supplied, this will fall back to the location in
181 : // GetDefaultRegulatoryLocation and then to Outdoor (most restrictive).
182 : // This value should be set before calling PerformCommissioningStep for the kConfigRegulatory step.
183 1 : const Optional<app::Clusters::GeneralCommissioning::RegulatoryLocationTypeEnum> GetDeviceRegulatoryLocation() const
184 : {
185 1 : return mDeviceRegulatoryLocation;
186 : }
187 :
188 : // Value to determine whether the node supports Concurrent Connections as read from the GeneralCommissioning cluster.
189 : // In the AutoCommissioner, this is automatically set from from the kReadCommissioningInfo stage.
190 8 : Optional<bool> GetSupportsConcurrentConnection() const { return mSupportsConcurrentConnection; }
191 :
192 : // The country code to be used for the node, if set.
193 65 : Optional<CharSpan> GetCountryCode() const { return mCountryCode; }
194 :
195 2 : Optional<TermsAndConditionsAcknowledgement> GetTermsAndConditionsAcknowledgement() const
196 : {
197 2 : return mTermsAndConditionsAcknowledgement;
198 : }
199 :
200 : // Time zone to set for the node
201 : // If required, this will be truncated to fit the max size allowable on the node
202 72 : Optional<app::DataModel::List<app::Clusters::TimeSynchronization::Structs::TimeZoneStruct::Type>> GetTimeZone() const
203 : {
204 72 : return mTimeZone;
205 : }
206 :
207 : // DST offset list. If required, this will be truncated to fit the max size allowable on the node
208 : // DST list will only be sent if the commissionee requires DST offsets, as indicated in the SetTimeZone response
209 68 : Optional<app::DataModel::List<app::Clusters::TimeSynchronization::Structs::DSTOffsetStruct::Type>> GetDSTOffsets() const
210 : {
211 68 : return mDSTOffsets;
212 : }
213 :
214 : // Default NTP to set on the node if supported and required
215 : // Default implementation will not overide a value already set on the commissionee
216 : // TODO: Add a force option?
217 71 : Optional<app::DataModel::Nullable<CharSpan>> GetDefaultNTP() const { return mDefaultNTP; }
218 :
219 : // Trusted time source
220 : // Default implementation will not override a value already set on the commissionee
221 : // TODO: Add a force option?
222 : Optional<app::DataModel::Nullable<app::Clusters::TimeSynchronization::Structs::FabricScopedTrustedTimeSourceStruct::Type>>
223 2 : GetTrustedTimeSource() const
224 : {
225 2 : return mTrustedTimeSource;
226 : }
227 :
228 : // Nonce sent to the node to use during the CSR request.
229 : // When using the AutoCommissioner, this value will be ignored in favour of the value supplied by the
230 : // OperationalCredentialsDelegate ObtainCsrNonce function. If the credential delegate is not supplied, the value supplied here
231 : // will be used. If neither is supplied random value will be used as a fallback.
232 : // This value must be set before calling PerformCommissioningStep for the kSendOpCertSigningRequest step.
233 62 : const Optional<ByteSpan> GetCSRNonce() const { return mCSRNonce; }
234 :
235 : // Nonce value sent to the node to use during the attestation request.
236 : // When using the AutoCommissioner, this value will fall back to random if not supplied.
237 : // If a non-random value is to be used, the value must be set before calling PerformCommissioningStep for the
238 : // kSendAttestationRequest step.
239 63 : const Optional<ByteSpan> GetAttestationNonce() const { return mAttestationNonce; }
240 :
241 : // WiFi SSID and credentials to use when adding/updating and enabling WiFi on the node.
242 : // This value must be set before calling PerformCommissioningStep for the kWiFiNetworkSetup or kWiFiNetworkEnable steps.
243 73 : const Optional<WiFiCredentials> GetWiFiCredentials() const { return mWiFiCreds; }
244 :
245 : // Thread operational dataset to use when adding/updating and enabling the thread network on the node.
246 : // This value must be set before calling PerformCommissioningStep for the kThreadNetworkSetup or kThreadNetworkEnable steps.
247 74 : const Optional<ByteSpan> GetThreadOperationalDataset() const { return mThreadOperationalDataset; }
248 :
249 : // The NOCSR parameters (elements and signature) returned from the node. In the AutoCommissioner, this is set using the data
250 : // returned from the kSendOpCertSigningRequest stage.
251 : // This value must be set before calling PerformCommissioningStep for the kGenerateNOCChain step.
252 2 : const Optional<NOCChainGenerationParameters> GetNOCChainGenerationParameters() const { return mNOCChainGenerationParameters; }
253 :
254 : // The root certificate for the operational certificate chain. In the auto commissioner, this is set by by the kGenerateNOCChain
255 : // stage through the OperationalCredentialsDelegate.
256 : // This value must be set before calling PerformCommissioningStep for the kSendTrustedRootCert step.
257 2 : const Optional<ByteSpan> GetRootCert() const { return mRootCert; }
258 :
259 : // The node operational certificate for the node being commissioned. In the AutoCommissioner, this is set by by the
260 : // kGenerateNOCChain stage through the OperationalCredentialsDelegate.
261 : // This value must be set before calling PerformCommissioningStep for the kSendNOC step.
262 : // This value must also be set before calling PerformCommissioningStep for the kSendTrustedRootCert step, as it is used to set
263 : // the node id in the DeviceProxy.
264 2 : const Optional<ByteSpan> GetNoc() const { return mNoc; }
265 :
266 : // The intermediate certificate for the node being commissioned. In the AutoCommissioner, this is set by by the
267 : // kGenerateNOCChain stage through the OperationalCredentialsDelegate.
268 : // This value should be set before calling PerformCommissioningStep for the kSendNOC step.
269 2 : const Optional<ByteSpan> GetIcac() const { return mIcac; }
270 :
271 : // Epoch key for the identity protection key for the node being commissioned. In the AutoCommissioner, this is set by by the
272 : // kGenerateNOCChain stage through the OperationalCredentialsDelegate.
273 : // This value must be set before calling PerformCommissioningStep for the kSendNOC step.
274 2 : const Optional<Crypto::IdentityProtectionKeySpan> GetIpk() const
275 : {
276 4 : return mIpk.HasValue() ? MakeOptional(mIpk.Value().Span()) : NullOptional;
277 : }
278 :
279 : // Admin subject id used for the case access control entry created if the AddNOC command succeeds. In the AutoCommissioner, this
280 : // is set by by the kGenerateNOCChain stage through the OperationalCredentialsDelegate.
281 : // This must be set before calling PerformCommissioningStep for the kSendNOC step.
282 1 : const Optional<NodeId> GetAdminSubject() const { return mAdminSubject; }
283 :
284 : // Attestation elements from the node. These are obtained from node in response to the AttestationRequest command. In the
285 : // AutoCommissioner, this is automatically set from the report from the kSendAttestationRequest stage.
286 : // This must be set before calling PerformCommissioningStep for the kAttestationVerification step.
287 3 : const Optional<ByteSpan> GetAttestationElements() const { return mAttestationElements; }
288 :
289 : // Attestation signature from the node. This is obtained from node in response to the AttestationRequest command. In the
290 : // AutoCommissioner, this is automatically set from the report from the kSendAttestationRequest stage.
291 : // This must be set before calling PerformCommissioningStep for the kAttestationVerification step.
292 3 : const Optional<ByteSpan> GetAttestationSignature() const { return mAttestationSignature; }
293 :
294 : // Product attestation intermediate certificate from the node. This is obtained from the node in response to the
295 : // CertificateChainRequest command for the PAI. In the AutoCommissioner, this is automatically set from the report from the
296 : // kSendPAICertificateRequest stage.
297 : // This must be set before calling PerformCommissioningStep for the kAttestationVerificationstep.
298 3 : const Optional<ByteSpan> GetPAI() const { return mPAI; }
299 :
300 : // Device attestation certificate from the node. This is obtained from the node in response to the CertificateChainRequest
301 : // command for the DAC. In the AutoCommissioner, this is automatically set from the report from the kSendDACCertificateRequest
302 : // stage.
303 : // This must be set before calling PerformCommissioningStep for the kAttestationVerification step.
304 3 : const Optional<ByteSpan> GetDAC() const { return mDAC; }
305 :
306 : // Node ID when a matching fabric is found in the Node Operational Credentials cluster.
307 : // In the AutoCommissioner, this is set from kReadCommissioningInfo stage.
308 2 : const Optional<NodeId> GetRemoteNodeId() const { return mRemoteNodeId; }
309 :
310 : // Node vendor ID from the basic information cluster. In the AutoCommissioner, this is automatically set from report from the
311 : // kReadCommissioningInfo stage.
312 : // This must be set before calling PerformCommissioningStep for the kAttestationVerification step.
313 1 : const Optional<VendorId> GetRemoteVendorId() const { return mRemoteVendorId; }
314 :
315 : // Node product ID from the basic information cluster. In the AutoCommissioner, this is automatically set from report from the
316 : // kReadCommissioningInfo stage.
317 : // This must be set before calling PerformCommissioningStep for the kAttestationVerification step.
318 1 : const Optional<uint16_t> GetRemoteProductId() const { return mRemoteProductId; }
319 :
320 : // Default regulatory location set by the node, as read from the GeneralCommissioning cluster. In the AutoCommissioner, this is
321 : // automatically set from report from the kReadCommissioningInfo stage.
322 : // This should be set before calling PerformCommissioningStep for the kConfigRegulatory step.
323 1 : const Optional<app::Clusters::GeneralCommissioning::RegulatoryLocationTypeEnum> GetDefaultRegulatoryLocation() const
324 : {
325 1 : return mDefaultRegulatoryLocation;
326 : }
327 :
328 : // Location capabilities of the node, as read from the GeneralCommissioning cluster. In the AutoCommissioner, this is
329 : // automatically set from report from the kReadCommissioningInfo stage.
330 : // This should be set before calling PerformCommissioningStep for the kConfigRegulatory step.
331 1 : const Optional<app::Clusters::GeneralCommissioning::RegulatoryLocationTypeEnum> GetLocationCapability() const
332 : {
333 1 : return mLocationCapability;
334 : }
335 :
336 : // Status to send when calling CommissioningComplete on the PairingDelegate during the kCleanup step. The AutoCommissioner uses
337 : // this to pass through any error messages received during commissioning.
338 2 : const CompletionStatus & GetCompletionStatus() const { return completionStatus; }
339 :
340 1 : CommissioningParameters & SetFailsafeTimerSeconds(uint16_t seconds)
341 : {
342 1 : mFailsafeTimerSeconds.SetValue(seconds);
343 1 : return *this;
344 : }
345 :
346 1 : CommissioningParameters & SetCASEFailsafeTimerSeconds(uint16_t seconds)
347 : {
348 1 : mCASEFailsafeTimerSeconds.SetValue(seconds);
349 1 : return *this;
350 : }
351 :
352 1 : CommissioningParameters & SetDeviceRegulatoryLocation(app::Clusters::GeneralCommissioning::RegulatoryLocationTypeEnum location)
353 : {
354 1 : mDeviceRegulatoryLocation.SetValue(location);
355 1 : return *this;
356 : }
357 :
358 7 : CommissioningParameters & SetSupportsConcurrentConnection(bool concurrentConnection)
359 : {
360 7 : mSupportsConcurrentConnection.SetValue(concurrentConnection);
361 7 : return *this;
362 : }
363 :
364 : // The lifetime of the buffer countryCode is pointing to should exceed the
365 : // lifetime of CommissioningParameters object.
366 2 : CommissioningParameters & SetCountryCode(CharSpan countryCode)
367 : {
368 2 : mCountryCode.SetValue(countryCode);
369 2 : return *this;
370 : }
371 :
372 : CommissioningParameters &
373 1 : SetTermsAndConditionsAcknowledgement(TermsAndConditionsAcknowledgement termsAndConditionsAcknowledgement)
374 : {
375 1 : mTermsAndConditionsAcknowledgement.SetValue(termsAndConditionsAcknowledgement);
376 1 : return *this;
377 : }
378 :
379 : // The lifetime of the list buffer needs to exceed the lifetime of the CommissioningParameters object.
380 : CommissioningParameters &
381 3 : SetTimeZone(app::DataModel::List<app::Clusters::TimeSynchronization::Structs::TimeZoneStruct::Type> timeZone)
382 : {
383 3 : mTimeZone.SetValue(timeZone);
384 3 : return *this;
385 : }
386 :
387 : // The lifetime of the list buffer needs to exceed the lifetime of the CommissioningParameters object.
388 : CommissioningParameters &
389 3 : SetDSTOffsets(app::DataModel::List<app::Clusters::TimeSynchronization::Structs::DSTOffsetStruct::Type> dstOffsets)
390 : {
391 3 : mDSTOffsets.SetValue(dstOffsets);
392 3 : return *this;
393 : }
394 :
395 : // The lifetime of the char span needs to exceed the lifetime of the CommissioningParameters
396 4 : CommissioningParameters & SetDefaultNTP(app::DataModel::Nullable<CharSpan> defaultNTP)
397 : {
398 4 : mDefaultNTP.SetValue(defaultNTP);
399 4 : return *this;
400 : }
401 :
402 1 : CommissioningParameters & SetTrustedTimeSource(
403 : app::DataModel::Nullable<app::Clusters::TimeSynchronization::Structs::FabricScopedTrustedTimeSourceStruct::Type>
404 : trustedTimeSource)
405 : {
406 1 : mTrustedTimeSource.SetValue(trustedTimeSource);
407 1 : return *this;
408 : }
409 :
410 : // The lifetime of the buffer csrNonce is pointing to, should exceed the lifetime of CommissioningParameters object.
411 60 : CommissioningParameters & SetCSRNonce(ByteSpan csrNonce)
412 : {
413 60 : mCSRNonce.SetValue(csrNonce);
414 60 : return *this;
415 : }
416 :
417 : // The lifetime of the buffer attestationNonce is pointing to, should exceed the lifetime of CommissioningParameters object.
418 61 : CommissioningParameters & SetAttestationNonce(ByteSpan attestationNonce)
419 : {
420 61 : mAttestationNonce.SetValue(attestationNonce);
421 61 : return *this;
422 : }
423 :
424 : // If a WiFiCredentials is provided, then the WiFiNetworkScan will not be attempted
425 12 : CommissioningParameters & SetWiFiCredentials(WiFiCredentials wifiCreds)
426 : {
427 12 : mWiFiCreds.SetValue(wifiCreds);
428 12 : mAttemptWiFiNetworkScan.SetValue(false);
429 12 : return *this;
430 : }
431 :
432 : // If a ThreadOperationalDataset is provided, then the ThreadNetworkScan will not be attempted
433 12 : CommissioningParameters & SetThreadOperationalDataset(ByteSpan threadOperationalDataset)
434 : {
435 :
436 12 : mThreadOperationalDataset.SetValue(threadOperationalDataset);
437 12 : mAttemptThreadNetworkScan = MakeOptional(static_cast<bool>(false));
438 12 : return *this;
439 : }
440 : // This parameter should be set with the information returned from kSendOpCertSigningRequest. It must be set before calling
441 : // kGenerateNOCChain.
442 1 : CommissioningParameters & SetNOCChainGenerationParameters(const NOCChainGenerationParameters & params)
443 : {
444 1 : mNOCChainGenerationParameters.SetValue(params);
445 1 : return *this;
446 : }
447 : // Root certs can be generated from the kGenerateNOCChain step. This must be set before calling kSendTrustedRootCert.
448 3 : CommissioningParameters & SetRootCert(const ByteSpan & rcac)
449 : {
450 3 : mRootCert.SetValue(rcac);
451 3 : return *this;
452 : }
453 : // NOC and intermediate cert can be generated from the kGenerateNOCChain step. NOC must be set before calling
454 : // kSendTrustedRootCert. ICAC and NOC must be set before calling kSendNOC
455 1 : CommissioningParameters & SetNoc(const ByteSpan & noc)
456 : {
457 1 : mNoc.SetValue(noc);
458 1 : return *this;
459 : }
460 1 : CommissioningParameters & SetIcac(const ByteSpan & icac)
461 : {
462 1 : mIcac.SetValue(icac);
463 1 : return *this;
464 : }
465 1 : CommissioningParameters & SetIpk(const Crypto::IdentityProtectionKeySpan ipk)
466 : {
467 1 : mIpk.SetValue(Crypto::IdentityProtectionKey(ipk));
468 1 : return *this;
469 : }
470 1 : CommissioningParameters & SetAdminSubject(const NodeId adminSubject)
471 : {
472 1 : mAdminSubject.SetValue(adminSubject);
473 1 : return *this;
474 : }
475 1 : CommissioningParameters & SetAttestationElements(const ByteSpan & attestationElements)
476 : {
477 1 : mAttestationElements = MakeOptional(attestationElements);
478 1 : return *this;
479 : }
480 1 : CommissioningParameters & SetAttestationSignature(const ByteSpan & attestationSignature)
481 : {
482 1 : mAttestationSignature = MakeOptional(attestationSignature);
483 1 : return *this;
484 : }
485 1 : CommissioningParameters & SetPAI(const ByteSpan & pai)
486 : {
487 1 : mPAI = MakeOptional(pai);
488 1 : return *this;
489 : }
490 1 : CommissioningParameters & SetDAC(const ByteSpan & dac)
491 : {
492 1 : mDAC = MakeOptional(dac);
493 1 : return *this;
494 : }
495 1 : CommissioningParameters & SetRemoteNodeId(NodeId id)
496 : {
497 1 : mRemoteNodeId = MakeOptional(id);
498 1 : return *this;
499 : }
500 1 : CommissioningParameters & SetRemoteVendorId(VendorId id)
501 : {
502 1 : mRemoteVendorId = MakeOptional(id);
503 1 : return *this;
504 : }
505 1 : CommissioningParameters & SetRemoteProductId(uint16_t id)
506 : {
507 1 : mRemoteProductId = MakeOptional(id);
508 1 : return *this;
509 : }
510 1 : CommissioningParameters & SetDefaultRegulatoryLocation(app::Clusters::GeneralCommissioning::RegulatoryLocationTypeEnum location)
511 : {
512 1 : mDefaultRegulatoryLocation = MakeOptional(location);
513 1 : return *this;
514 : }
515 1 : CommissioningParameters & SetLocationCapability(app::Clusters::GeneralCommissioning::RegulatoryLocationTypeEnum capability)
516 : {
517 1 : mLocationCapability = MakeOptional(capability);
518 1 : return *this;
519 : }
520 1 : void SetCompletionStatus(const CompletionStatus & status) { completionStatus = status; }
521 :
522 1 : CommissioningParameters & SetDeviceAttestationDelegate(Credentials::DeviceAttestationDelegate * deviceAttestationDelegate)
523 : {
524 1 : mDeviceAttestationDelegate = deviceAttestationDelegate;
525 1 : return *this;
526 : }
527 :
528 3 : Credentials::DeviceAttestationDelegate * GetDeviceAttestationDelegate() const { return mDeviceAttestationDelegate; }
529 :
530 : // If an SSID is provided, and AttemptWiFiNetworkScan is true,
531 : // then a directed scan will be performed using the SSID provided in the WiFiCredentials object
532 7 : Optional<bool> GetAttemptWiFiNetworkScan() const { return mAttemptWiFiNetworkScan; }
533 4 : CommissioningParameters & SetAttemptWiFiNetworkScan(bool attemptWiFiNetworkScan)
534 : {
535 4 : mAttemptWiFiNetworkScan = MakeOptional(attemptWiFiNetworkScan);
536 4 : return *this;
537 : }
538 :
539 : // If a ThreadOperationalDataset is provided, then the ThreadNetworkScan will not be attempted
540 7 : Optional<bool> GetAttemptThreadNetworkScan() const { return mAttemptThreadNetworkScan; }
541 5 : CommissioningParameters & SetAttemptThreadNetworkScan(bool attemptThreadNetworkScan)
542 : {
543 5 : if (!mThreadOperationalDataset.HasValue())
544 : {
545 4 : mAttemptThreadNetworkScan = MakeOptional(attemptThreadNetworkScan);
546 : }
547 5 : return *this;
548 : }
549 :
550 : // Only perform the PASE steps of commissioning.
551 : // Commissioning will be completed by another admin on the network.
552 5 : Optional<bool> GetSkipCommissioningComplete() const { return mSkipCommissioningComplete; }
553 1 : CommissioningParameters & SetSkipCommissioningComplete(bool skipCommissioningComplete)
554 : {
555 1 : mSkipCommissioningComplete = MakeOptional(skipCommissioningComplete);
556 1 : return *this;
557 : }
558 :
559 : // Check for matching fabric on target device by reading fabric list and looking for a
560 : // fabricId and RootCert match. If a match is detected, then use GetNodeId() to
561 : // access the nodeId for the device on the matching fabric.
562 1 : bool GetCheckForMatchingFabric() const { return mCheckForMatchingFabric; }
563 1 : CommissioningParameters & SetCheckForMatchingFabric(bool checkForMatchingFabric)
564 : {
565 1 : mCheckForMatchingFabric = checkForMatchingFabric;
566 1 : return *this;
567 : }
568 :
569 : #if CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
570 : // Check for Joint Commissioning Method
571 : Optional<bool> GetUseJCM() const { return mUseJCM; }
572 :
573 : // Set the Joint Commissioning Method
574 : CommissioningParameters & SetUseJCM(bool useJCM)
575 : {
576 : mUseJCM = MakeOptional(useJCM);
577 : return *this;
578 : }
579 : #endif // CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
580 :
581 61 : ICDRegistrationStrategy GetICDRegistrationStrategy() const { return mICDRegistrationStrategy; }
582 2 : CommissioningParameters & SetICDRegistrationStrategy(ICDRegistrationStrategy icdRegistrationStrategy)
583 : {
584 2 : mICDRegistrationStrategy = icdRegistrationStrategy;
585 2 : return *this;
586 : }
587 :
588 6 : Optional<NodeId> GetICDCheckInNodeId() const { return mICDCheckInNodeId; }
589 3 : CommissioningParameters & SetICDCheckInNodeId(NodeId icdCheckInNodeId)
590 : {
591 3 : mICDCheckInNodeId = MakeOptional(icdCheckInNodeId);
592 3 : return *this;
593 : }
594 :
595 6 : Optional<uint64_t> GetICDMonitoredSubject() const { return mICDMonitoredSubject; }
596 3 : CommissioningParameters & SetICDMonitoredSubject(uint64_t icdMonitoredSubject)
597 : {
598 3 : mICDMonitoredSubject = MakeOptional(icdMonitoredSubject);
599 3 : return *this;
600 : }
601 :
602 12 : Optional<ByteSpan> GetICDSymmetricKey() const { return mICDSymmetricKey; }
603 3 : CommissioningParameters & SetICDSymmetricKey(ByteSpan icdSymmetricKey)
604 : {
605 3 : mICDSymmetricKey = MakeOptional(icdSymmetricKey);
606 3 : return *this;
607 : }
608 :
609 6 : Optional<app::Clusters::IcdManagement::ClientTypeEnum> GetICDClientType() const { return mICDClientType; }
610 3 : CommissioningParameters & SetICDClientType(app::Clusters::IcdManagement::ClientTypeEnum icdClientType)
611 : {
612 3 : mICDClientType = MakeOptional(icdClientType);
613 3 : return *this;
614 : }
615 :
616 3 : Optional<uint32_t> GetICDStayActiveDurationMsec() const { return mICDStayActiveDurationMsec; }
617 1 : CommissioningParameters & SetICDStayActiveDurationMsec(uint32_t stayActiveDurationMsec)
618 : {
619 1 : mICDStayActiveDurationMsec = MakeOptional(stayActiveDurationMsec);
620 1 : return *this;
621 : }
622 1 : void ClearICDStayActiveDurationMsec() { mICDStayActiveDurationMsec.ClearValue(); }
623 :
624 62 : Span<const app::AttributePathParams> GetExtraReadPaths() const { return mExtraReadPaths; }
625 :
626 : // Additional attribute paths to read as part of the kReadCommissioningInfo stage.
627 : // These values read from the device will be available in ReadCommissioningInfo.attributes.
628 : // Clients should avoid requesting paths that are already read internally by the commissioner
629 : // as no consolidation of internally read and extra paths provided here will be performed.
630 2 : CommissioningParameters & SetExtraReadPaths(Span<const app::AttributePathParams> paths)
631 : {
632 2 : mExtraReadPaths = paths;
633 2 : return *this;
634 : }
635 :
636 : // Clear all members that depend on some sort of external buffer. Can be
637 : // used to make sure that we are not holding any dangling pointers.
638 64 : void ClearExternalBufferDependentValues()
639 : {
640 64 : mCSRNonce.ClearValue();
641 64 : mAttestationNonce.ClearValue();
642 64 : mWiFiCreds.ClearValue();
643 64 : mCountryCode.ClearValue();
644 64 : mThreadOperationalDataset.ClearValue();
645 64 : mNOCChainGenerationParameters.ClearValue();
646 64 : mRootCert.ClearValue();
647 64 : mNoc.ClearValue();
648 64 : mIcac.ClearValue();
649 64 : mIpk.ClearValue();
650 64 : mAttestationElements.ClearValue();
651 64 : mAttestationSignature.ClearValue();
652 64 : mPAI.ClearValue();
653 64 : mDAC.ClearValue();
654 64 : mTimeZone.ClearValue();
655 64 : mDSTOffsets.ClearValue();
656 64 : mDefaultNTP.ClearValue();
657 64 : mICDSymmetricKey.ClearValue();
658 64 : mExtraReadPaths = decltype(mExtraReadPaths)();
659 64 : }
660 :
661 : private:
662 : // Items that can be set by the commissioner
663 : Optional<uint16_t> mFailsafeTimerSeconds;
664 : Optional<uint16_t> mCASEFailsafeTimerSeconds;
665 : Optional<app::Clusters::GeneralCommissioning::RegulatoryLocationTypeEnum> mDeviceRegulatoryLocation;
666 : Optional<app::DataModel::List<app::Clusters::TimeSynchronization::Structs::TimeZoneStruct::Type>> mTimeZone;
667 : Optional<app::DataModel::List<app::Clusters::TimeSynchronization::Structs::DSTOffsetStruct::Type>> mDSTOffsets;
668 : Optional<app::DataModel::Nullable<CharSpan>> mDefaultNTP;
669 : Optional<app::DataModel::Nullable<app::Clusters::TimeSynchronization::Structs::FabricScopedTrustedTimeSourceStruct::Type>>
670 : mTrustedTimeSource;
671 : Optional<ByteSpan> mCSRNonce;
672 : Optional<ByteSpan> mAttestationNonce;
673 : Optional<WiFiCredentials> mWiFiCreds;
674 : Optional<CharSpan> mCountryCode;
675 : Optional<TermsAndConditionsAcknowledgement> mTermsAndConditionsAcknowledgement;
676 : Optional<ByteSpan> mThreadOperationalDataset;
677 : Optional<NOCChainGenerationParameters> mNOCChainGenerationParameters;
678 : Optional<ByteSpan> mRootCert;
679 : Optional<ByteSpan> mNoc;
680 : Optional<ByteSpan> mIcac;
681 : Optional<Crypto::IdentityProtectionKey> mIpk;
682 : Optional<NodeId> mAdminSubject;
683 : // Items that come from the device in commissioning steps
684 : Optional<ByteSpan> mAttestationElements;
685 : Optional<ByteSpan> mAttestationSignature;
686 : Optional<ByteSpan> mPAI;
687 : Optional<ByteSpan> mDAC;
688 : Optional<NodeId> mRemoteNodeId;
689 : Optional<VendorId> mRemoteVendorId;
690 : Optional<uint16_t> mRemoteProductId;
691 : Optional<app::Clusters::GeneralCommissioning::RegulatoryLocationTypeEnum> mDefaultRegulatoryLocation;
692 : Optional<app::Clusters::GeneralCommissioning::RegulatoryLocationTypeEnum> mLocationCapability;
693 : Optional<bool> mSupportsConcurrentConnection;
694 : CompletionStatus completionStatus;
695 : Credentials::DeviceAttestationDelegate * mDeviceAttestationDelegate =
696 : nullptr; // Delegate to handle device attestation failures during commissioning
697 : Optional<bool> mAttemptWiFiNetworkScan;
698 : Optional<bool> mAttemptThreadNetworkScan; // This automatically gets set to false when a ThreadOperationalDataset is set
699 : Optional<bool> mSkipCommissioningComplete;
700 :
701 : Optional<NodeId> mICDCheckInNodeId;
702 : Optional<uint64_t> mICDMonitoredSubject;
703 : Optional<ByteSpan> mICDSymmetricKey;
704 : Optional<app::Clusters::IcdManagement::ClientTypeEnum> mICDClientType;
705 : Optional<uint32_t> mICDStayActiveDurationMsec;
706 : ICDRegistrationStrategy mICDRegistrationStrategy = ICDRegistrationStrategy::kIgnore;
707 : bool mCheckForMatchingFabric = false;
708 : Span<const app::AttributePathParams> mExtraReadPaths;
709 :
710 : Optional<bool> mUseJCM;
711 : };
712 :
713 : struct RequestedCertificate
714 : {
715 1 : RequestedCertificate(ByteSpan newCertificate) : certificate(newCertificate) {}
716 : ByteSpan certificate;
717 : };
718 :
719 : struct AttestationResponse
720 : {
721 1 : AttestationResponse(ByteSpan newAttestationElements, ByteSpan newSignature) :
722 1 : attestationElements(newAttestationElements), signature(newSignature)
723 1 : {}
724 : ByteSpan attestationElements;
725 : ByteSpan signature;
726 : };
727 :
728 : struct CSRResponse
729 : {
730 1 : CSRResponse(ByteSpan elements, ByteSpan newSignature) : nocsrElements(elements), signature(newSignature) {}
731 : ByteSpan nocsrElements;
732 : ByteSpan signature;
733 : };
734 :
735 : struct NocChain
736 : {
737 0 : NocChain(ByteSpan newNoc, ByteSpan newIcac, ByteSpan newRcac, Crypto::IdentityProtectionKeySpan newIpk,
738 0 : NodeId newAdminSubject) :
739 0 : noc(newNoc),
740 0 : icac(newIcac), rcac(newRcac), ipk(newIpk), adminSubject(newAdminSubject)
741 0 : {}
742 : ByteSpan noc;
743 : ByteSpan icac;
744 : ByteSpan rcac;
745 : Crypto::IdentityProtectionKeySpan ipk;
746 : NodeId adminSubject;
747 : };
748 :
749 : struct OperationalNodeFoundData
750 : {
751 0 : OperationalNodeFoundData(OperationalDeviceProxy proxy) : operationalProxy(proxy) {}
752 : OperationalDeviceProxy operationalProxy;
753 : };
754 :
755 : struct NetworkClusterInfo
756 : {
757 : EndpointId endpoint = kInvalidEndpointId;
758 : app::Clusters::NetworkCommissioning::Attributes::ConnectMaxTimeSeconds::TypeInfo::DecodableType minConnectionTime = 0;
759 : // maxScanTime == 0 means we don't know; normal commissioning step timeouts
760 : // will apply in that case.
761 : app::Clusters::NetworkCommissioning::Attributes::ScanMaxTimeSeconds::TypeInfo::DecodableType maxScanTime = 0;
762 : };
763 : struct NetworkClusters
764 : {
765 : NetworkClusterInfo wifi;
766 : NetworkClusterInfo thread;
767 : NetworkClusterInfo eth;
768 : };
769 : struct BasicClusterInfo
770 : {
771 : VendorId vendorId = VendorId::Common;
772 : uint16_t productId = 0;
773 : };
774 : struct GeneralCommissioningInfo
775 : {
776 : uint64_t breadcrumb = 0;
777 : uint16_t recommendedFailsafe = 0;
778 : app::Clusters::GeneralCommissioning::RegulatoryLocationTypeEnum currentRegulatoryLocation =
779 : app::Clusters::GeneralCommissioning::RegulatoryLocationTypeEnum::kIndoorOutdoor;
780 : app::Clusters::GeneralCommissioning::RegulatoryLocationTypeEnum locationCapability =
781 : app::Clusters::GeneralCommissioning::RegulatoryLocationTypeEnum::kIndoorOutdoor;
782 : bool isCommissioningWithoutPower = false;
783 : };
784 :
785 : // ICDManagementClusterInfo is populated when the controller reads information from
786 : // the ICD Management cluster, and is used to communicate that information.
787 : struct ICDManagementClusterInfo
788 : {
789 : // Whether the ICD is capable of functioning as a LIT device. If false, the ICD can only be a SIT device.
790 : bool isLIT = false;
791 : // Whether the ICD supports the check-in protocol. LIT devices have to support it, but SIT devices
792 : // might or might not.
793 : bool checkInProtocolSupport = false;
794 : // Indicate the maximum interval in seconds the server can stay in idle mode.
795 : uint32_t idleModeDuration = 0;
796 : // Indicate the minimum interval in milliseconds the server typically will stay in active mode after initial transition out of
797 : // idle mode.
798 : uint32_t activeModeDuration = 0;
799 : // Indicate the minimum amount of time in milliseconds the server typically will stay active after network activity when in
800 : // active mode.
801 : uint16_t activeModeThreshold = 0;
802 : // userActiveModeTriggerHint indicates which user action(s) will trigger the ICD to switch to Active mode.
803 : // For a LIT: The device is required to provide a value for the bitmap.
804 : // For a SIT: The device may not provide a value. In that case, none of the bits will be set.
805 : //
806 : // userActiveModeTriggerInstruction may provide additional information for users for some specific
807 : // userActiveModeTriggerHint values.
808 : BitMask<app::Clusters::IcdManagement::UserActiveModeTriggerBitmap> userActiveModeTriggerHint;
809 : CharSpan userActiveModeTriggerInstruction;
810 : };
811 :
812 : struct ReadCommissioningInfo
813 : {
814 : #if CHIP_CONFIG_ENABLE_READ_CLIENT
815 : app::ClusterStateCache const * attributes = nullptr;
816 : #endif
817 : NetworkClusters network;
818 : BasicClusterInfo basic;
819 : GeneralCommissioningInfo general;
820 : bool requiresUTC = false;
821 : bool requiresTimeZone = false;
822 : bool requiresDefaultNTP = false;
823 : bool requiresTrustedTimeSource = false;
824 : uint8_t maxTimeZoneSize = 1;
825 : uint8_t maxDSTSize = 1;
826 : NodeId remoteNodeId = kUndefinedNodeId;
827 : bool supportsConcurrentConnection = true;
828 : ICDManagementClusterInfo icd;
829 : };
830 :
831 : struct TimeZoneResponseInfo
832 : {
833 : bool requiresDSTOffsets;
834 : };
835 :
836 : struct AttestationErrorInfo
837 : {
838 0 : AttestationErrorInfo(Credentials::AttestationVerificationResult result) : attestationResult(result) {}
839 : Credentials::AttestationVerificationResult attestationResult;
840 : };
841 :
842 : struct CommissioningErrorInfo
843 : {
844 0 : CommissioningErrorInfo(app::Clusters::GeneralCommissioning::CommissioningErrorEnum result) : commissioningError(result) {}
845 : app::Clusters::GeneralCommissioning::CommissioningErrorEnum commissioningError;
846 : };
847 :
848 : struct NetworkCommissioningStatusInfo
849 : {
850 0 : NetworkCommissioningStatusInfo(app::Clusters::NetworkCommissioning::NetworkCommissioningStatusEnum result) :
851 0 : networkCommissioningStatus(result)
852 0 : {}
853 : app::Clusters::NetworkCommissioning::NetworkCommissioningStatusEnum networkCommissioningStatus;
854 : };
855 :
856 : class CommissioningDelegate
857 : {
858 : public:
859 37 : virtual ~CommissioningDelegate(){};
860 : /* CommissioningReport is returned after each commissioning step is completed. The reports for each step are:
861 : * kReadCommissioningInfo: ReadCommissioningInfo
862 : * kArmFailsafe: CommissioningErrorInfo if there is an error
863 : * kConfigRegulatory: CommissioningErrorInfo if there is an error
864 : * kConfigureUTCTime: None
865 : * kConfigureTimeZone: TimeZoneResponseInfo
866 : * kConfigureDSTOffset: None
867 : * kConfigureDefaultNTP: None
868 : * kSendPAICertificateRequest: RequestedCertificate
869 : * kSendDACCertificateRequest: RequestedCertificate
870 : * kSendAttestationRequest: AttestationResponse
871 : * kAttestationVerification: AttestationErrorInfo if there is an error
872 : * kAttestationRevocationCheck: AttestationErrorInfo if there is an error
873 : * kJCMTrustVerification: JCMTrustVerificationError if there is an error
874 : * kSendOpCertSigningRequest: CSRResponse
875 : * kGenerateNOCChain: NocChain
876 : * kSendTrustedRootCert: None
877 : * kSendNOC: None
878 : * kConfigureTrustedTimeSource: None
879 : * kWiFiNetworkSetup: NetworkCommissioningStatusInfo if there is an error
880 : * kThreadNetworkSetup: NetworkCommissioningStatusInfo if there is an error
881 : * kWiFiNetworkEnable: NetworkCommissioningStatusInfo if there is an error
882 : * kThreadNetworkEnable: NetworkCommissioningStatusInfo if there is an error
883 : * kEvictPreviousCaseSessions: None
884 : * kFindOperationalForStayActive OperationalNodeFoundData
885 : * kFindOperationalForCommissioningComplete: OperationalNodeFoundData
886 : * kICDSendStayActive: CommissioningErrorInfo if there is an error
887 : * kSendComplete: CommissioningErrorInfo if there is an error
888 : * kCleanup: None
889 : */
890 : struct CommissioningReport
891 : : Variant<RequestedCertificate, AttestationResponse, CSRResponse, NocChain, OperationalNodeFoundData, ReadCommissioningInfo,
892 : AttestationErrorInfo, CommissioningErrorInfo, NetworkCommissioningStatusInfo, TimeZoneResponseInfo,
893 : Credentials::JCM::TrustVerificationError>
894 : {
895 1 : CommissioningReport() : stageCompleted(CommissioningStage::kError) {}
896 : CommissioningStage stageCompleted;
897 : };
898 : virtual CHIP_ERROR SetCommissioningParameters(const CommissioningParameters & params) = 0;
899 : virtual const CommissioningParameters & GetCommissioningParameters() const = 0;
900 : virtual void SetOperationalCredentialsDelegate(OperationalCredentialsDelegate * operationalCredentialsDelegate) = 0;
901 : virtual CHIP_ERROR StartCommissioning(DeviceCommissioner * commissioner, CommissioneeDeviceProxy * proxy) = 0;
902 : virtual CHIP_ERROR CommissioningStepFinished(CHIP_ERROR err, CommissioningReport report) = 0;
903 : };
904 :
905 : } // namespace Controller
906 : } // namespace chip
|