Line data Source code
1 : /*
2 : * Copyright (c) 2022 Project CHIP Authors
3 : * All rights reserved.
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #pragma once
19 :
20 : #include <app/OperationalSessionSetup.h>
21 : #include <app/data-model/NullObject.h>
22 : #include <controller/CHIPDeviceController.h>
23 : #include <controller/CommissioningWindowParams.h>
24 : #include <crypto/CHIPCryptoPAL.h>
25 : #include <lib/core/CHIPCallback.h>
26 : #include <lib/core/CHIPError.h>
27 : #include <lib/core/NodeId.h>
28 : #include <lib/core/Optional.h>
29 : #include <platform/CHIPDeviceConfig.h>
30 : #include <setup_payload/SetupPayload.h>
31 :
32 : namespace chip {
33 : namespace Controller {
34 :
35 : /**
36 : * A helper class to open a commissioning window given some parameters.
37 : */
38 : class CommissioningWindowOpener
39 : {
40 : public:
41 8 : CommissioningWindowOpener(DeviceController * controller) :
42 8 : mController(controller), mDeviceConnected(&OnDeviceConnectedCallback, this),
43 16 : mDeviceConnectionFailure(&OnDeviceConnectionFailureCallback, this)
44 8 : {}
45 :
46 : enum class CommissioningWindowOption : uint8_t
47 : {
48 : kOriginalSetupCode = 0,
49 : kTokenWithRandomPIN,
50 : kTokenWithProvidedPIN,
51 : };
52 :
53 : /*
54 : * @brief
55 : * Try to look up the device attached to our controller with the given
56 : * node id and ask it to re-enter commissioning mode with its original
57 : * PASE verifier, discriminator, etc. The device will exit commissioning
58 : * mode after a successful commissioning, or after the given `timeout`
59 : * time.
60 : *
61 : * @param[in] deviceId The device Id.
62 : * @param[in] timeout The commissioning mode should terminate after this much time.
63 : * @param[in] callback The callback to call once the commissioning window is
64 : * open or if an error occurs.
65 : */
66 : CHIP_ERROR OpenBasicCommissioningWindow(NodeId deviceId, System::Clock::Seconds16 timeout,
67 : Callback::Callback<OnOpenBasicCommissioningWindow> * callback);
68 :
69 : /**
70 : * @brief
71 : * Try to look up the device attached to our controller with the given
72 : * node id and ask it to re-enter commissioning mode with a PASE verifier
73 : * derived from the given information and the given discriminator. The
74 : * device will exit commissioning mode after a successful commissioning,
75 : * or after the given `timeout` time.
76 : *
77 : * @param[in] deviceId The device Id.
78 : * @param[in] timeout The commissioning mode should terminate after this much time.
79 : * @param[in] iteration The PAKE iteration count associated with the PAKE Passcode ID and ephemeral
80 : * PAKE passcode verifier to be used for this commissioning.
81 : * @param[in] discriminator The long discriminator for the DNS-SD advertisement.
82 : * @param[in] setupPIN The setup PIN to use, or NullOptional to use a randomly-generated one.
83 : * @param[in] salt The salt to use, or NullOptional to use a
84 : * randomly-generated one. If provided, must be at
85 : * least kSpake2p_Min_PBKDF_Salt_Length bytes and
86 : * at most kSpake2p_Max_PBKDF_Salt_Length bytes in
87 : * length.
88 : * @param[in] callback The function to be called on success or failure of opening of commissioning window.
89 : * @param[out] payload The setup payload, not including the VID/PID bits,
90 : * even if those were asked for, that is generated
91 : * based on the passed-in information. The payload
92 : * provided to the callback function, unlike this
93 : * out parameter, will include the VID/PID bits if
94 : * readVIDPIDAttributes is true.
95 : *
96 : * @param[in] readVIDPIDAttributes Should the API internally read VID and PID from the device while opening the
97 : * commissioning window. If this argument is `true`, the API will read VID and
98 : * PID from the device and include them in the setup payload passed to the
99 : * callback.
100 : */
101 : CHIP_ERROR OpenCommissioningWindow(NodeId deviceId, System::Clock::Seconds16 timeout, uint32_t iteration,
102 : uint16_t discriminator, Optional<uint32_t> setupPIN, Optional<ByteSpan> salt,
103 : Callback::Callback<OnOpenCommissioningWindow> * callback, SetupPayload & payload,
104 : bool readVIDPIDAttributes = false);
105 :
106 : /**
107 : * @brief
108 : * Try to look up the device attached to our controller with the given
109 : * node id and ask it to re-enter commissioning mode with a PASE verifier
110 : * derived from the given information and the given discriminator. The
111 : * device will exit commissioning mode after a successful commissioning,
112 : * or after the given `timeout` time.
113 : *
114 : * @param[in] params The parameters required to open an enhanced commissioning window
115 : * with the provided or generated passcode.
116 : * @param[out] payload The setup payload, not including the VID/PID bits,
117 : * even if those were asked for, that is generated
118 : * based on the passed-in information. The payload
119 : * provided to the callback function, unlike this
120 : * out parameter, will include the VID/PID bits if
121 : * readVIDPIDAttributes is true.
122 : */
123 : CHIP_ERROR OpenCommissioningWindow(const CommissioningWindowPasscodeParams & params, SetupPayload & payload);
124 :
125 : #if CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
126 : /**
127 : * @brief
128 : * Try to look up the device attached to our controller with the given
129 : * node id and ask it to enter joint commissioning mode with a PASE verifier
130 : * derived from the given information and the given discriminator. The
131 : * device will exit joint commissioning mode after a successful joint commissioning,
132 : * or after the given `timeout` time.
133 : *
134 : * @param[in] params The parameters required to open a joint commissioning window
135 : * with the provided passcode.
136 : * @param[out] payload The setup payload, not including the VID/PID bits,
137 : * even if those were asked for, that is generated
138 : * based on the passed-in information. The payload
139 : * provided to the callback function, unlike this
140 : * out parameter, will include the VID/PID bits if
141 : * readVIDPIDAttributes is true.
142 : */
143 : CHIP_ERROR OpenJointCommissioningWindow(const CommissioningWindowPasscodeParams & params, SetupPayload & payload);
144 : #endif // CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
145 :
146 : /**
147 : * @brief
148 : * Try to look up the device attached to our controller with the given
149 : * node id and ask it to re-enter commissioning mode with a PASE verifier
150 : * derived from the given information and the given discriminator. The
151 : * device will exit commissioning mode after a successful commissioning,
152 : * or after the given `timeout` time.
153 : *
154 : * @param[in] params The parameters required to open an enhanced commissioning window
155 : * with the provided PAKE passcode verifier.
156 : */
157 : CHIP_ERROR OpenCommissioningWindow(const CommissioningWindowVerifierParams & params);
158 :
159 : private:
160 : enum class Step : uint8_t
161 : {
162 : // Ready to start opening a commissioning window.
163 : kAcceptCommissioningStart,
164 : // Need to read VID.
165 : kReadVID,
166 : // Need to read PID.
167 : kReadPID,
168 : // Need to open commissioning window.
169 : kOpenCommissioningWindow,
170 : };
171 :
172 : CHIP_ERROR OpenCommissioningWindowInternal(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle);
173 : static void OnPIDReadResponse(void * context, uint16_t value);
174 : static void OnVIDReadResponse(void * context, VendorId value);
175 : static void OnVIDPIDReadFailureResponse(void * context, CHIP_ERROR error);
176 : static void OnOpenCommissioningWindowSuccess(void * context, const app::DataModel::NullObjectType &);
177 : static void OnOpenCommissioningWindowFailure(void * context, CHIP_ERROR error);
178 : static void OnDeviceConnectedCallback(void * context, Messaging::ExchangeManager & exchangeMgr,
179 : const SessionHandle & sessionHandle);
180 : static void OnDeviceConnectionFailureCallback(void * context, const ScopedNodeId & peerId, CHIP_ERROR error);
181 :
182 : DeviceController * const mController = nullptr;
183 : Step mNextStep = Step::kAcceptCommissioningStart;
184 :
185 : Callback::Callback<OnOpenCommissioningWindow> * mCommissioningWindowCallback = nullptr;
186 : Callback::Callback<OnOpenCommissioningWindowWithVerifier> * mCommissioningWindowVerifierCallback = nullptr;
187 : Callback::Callback<OnOpenBasicCommissioningWindow> * mBasicCommissioningWindowCallback = nullptr;
188 : SetupPayload mSetupPayload;
189 : SetupDiscriminator mDiscriminator{};
190 : NodeId mNodeId = kUndefinedNodeId;
191 : EndpointId mTargetEndpointId = kRootEndpointId; // Default endpoint for Administrator Commissioning Cluster
192 : System::Clock::Seconds16 mCommissioningWindowTimeout = System::Clock::kZero;
193 : CommissioningWindowOption mCommissioningWindowOption = CommissioningWindowOption::kOriginalSetupCode;
194 : Crypto::Spake2pVerifier mVerifier; // Used for non-basic commissioning.
195 : // Parameters needed for non-basic commissioning.
196 : uint32_t mPBKDFIterations = 0;
197 : uint8_t mPBKDFSaltBuffer[Crypto::kSpake2p_Max_PBKDF_Salt_Length];
198 : ByteSpan mPBKDFSalt;
199 :
200 : Callback::Callback<OnDeviceConnected> mDeviceConnected;
201 : Callback::Callback<OnDeviceConnectionFailure> mDeviceConnectionFailure;
202 :
203 : #if CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
204 : bool mJointCommissioning = false;
205 : #endif // CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
206 : };
207 :
208 : /**
209 : * A helper class that can be used by consumers that don't care about the callback from the
210 : * open-commissioning-window process and just want automatic cleanup of the CommissioningWindowOpener when done
211 : * with it.
212 : */
213 : class AutoCommissioningWindowOpener : private CommissioningWindowOpener
214 : {
215 : public:
216 : // Takes the same arguments as CommissioningWindowOpener::OpenBasicCommissioningWindow except without the
217 : // callback.
218 : static CHIP_ERROR OpenBasicCommissioningWindow(DeviceController * controller, NodeId deviceId,
219 : System::Clock::Seconds16 timeout);
220 : // Takes the same arguments as CommissioningWindowOpener::OpenCommissioningWindow except without the
221 : // callback.
222 : static CHIP_ERROR OpenCommissioningWindow(DeviceController * controller, NodeId deviceId, System::Clock::Seconds16 timeout,
223 : uint32_t iteration, uint16_t discriminator, Optional<uint32_t> setupPIN,
224 : Optional<ByteSpan> salt, SetupPayload & payload, bool readVIDPIDAttributes = false);
225 :
226 : private:
227 : AutoCommissioningWindowOpener(DeviceController * controller);
228 :
229 : static void OnOpenCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload);
230 : static void OnOpenBasicCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status);
231 :
232 : chip::Callback::Callback<chip::Controller::OnOpenCommissioningWindow> mOnOpenCommissioningWindowCallback;
233 : chip::Callback::Callback<chip::Controller::OnOpenBasicCommissioningWindow> mOnOpenBasicCommissioningWindowCallback;
234 : };
235 :
236 : } // Namespace Controller
237 : } // namespace chip
|