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 12 : CommissioningWindowOpener(DeviceController * controller) :
42 12 : mController(controller), mDeviceConnected(&OnDeviceConnectedCallback, this),
43 24 : mDeviceConnectionFailure(&OnDeviceConnectionFailureCallback, this)
44 12 : {}
45 :
46 : // mPBKDFSalt spans into this object's own mPBKDFSaltBuffer, so a copy would alias the source's.
47 : // Forbid copying explicitly, pinning the invariant to the buffer rather than to the Callback members.
48 : CommissioningWindowOpener(const CommissioningWindowOpener &) = delete;
49 : CommissioningWindowOpener & operator=(const CommissioningWindowOpener &) = delete;
50 :
51 : enum class CommissioningWindowOption : uint8_t
52 : {
53 : kOriginalSetupCode = 0,
54 : kTokenWithRandomPIN,
55 : kTokenWithProvidedPIN,
56 : };
57 :
58 : /*
59 : * @brief
60 : * Try to look up the device attached to our controller with the given
61 : * node id and ask it to re-enter commissioning mode with its original
62 : * PASE verifier, discriminator, etc. The device will exit commissioning
63 : * mode after a successful commissioning, or after the given `timeout`
64 : * time.
65 : *
66 : * @param[in] deviceId The device Id.
67 : * @param[in] timeout The commissioning mode should terminate after this much time.
68 : * @param[in] callback The callback to call once the commissioning window is
69 : * open or if an error occurs.
70 : */
71 : CHIP_ERROR OpenBasicCommissioningWindow(NodeId deviceId, System::Clock::Seconds16 timeout,
72 : Callback::Callback<OnOpenBasicCommissioningWindow> * callback);
73 :
74 : /**
75 : * @brief
76 : * Try to look up the device attached to our controller with the given
77 : * node id and ask it to re-enter commissioning mode with a PASE verifier
78 : * derived from the given information and the given discriminator. The
79 : * device will exit commissioning mode after a successful commissioning,
80 : * or after the given `timeout` time.
81 : *
82 : * @param[in] deviceId The device Id.
83 : * @param[in] timeout The commissioning mode should terminate after this much time.
84 : * @param[in] iteration The PAKE iteration count associated with the PAKE Passcode ID and ephemeral
85 : * PAKE passcode verifier to be used for this commissioning.
86 : * @param[in] discriminator The long discriminator for the DNS-SD advertisement.
87 : * @param[in] setupPIN The setup PIN to use, or NullOptional to use a randomly-generated one.
88 : * @param[in] salt The salt to use, or NullOptional to use a
89 : * randomly-generated one. If provided, must be at
90 : * least kSpake2p_Min_PBKDF_Salt_Length bytes and
91 : * at most kSpake2p_Max_PBKDF_Salt_Length bytes in
92 : * length.
93 : * @param[in] callback The function to be called on success or failure of opening of commissioning window.
94 : * @param[out] payload The setup payload, not including the VID/PID bits,
95 : * even if those were asked for, that is generated
96 : * based on the passed-in information. The payload
97 : * provided to the callback function, unlike this
98 : * out parameter, will include the VID/PID bits if
99 : * readVIDPIDAttributes is true.
100 : *
101 : * @param[in] readVIDPIDAttributes Should the API internally read VID and PID from the device while opening the
102 : * commissioning window. If this argument is `true`, the API will read VID and
103 : * PID from the device and include them in the setup payload passed to the
104 : * callback.
105 : */
106 : CHIP_ERROR OpenCommissioningWindow(NodeId deviceId, System::Clock::Seconds16 timeout, uint32_t iteration,
107 : uint16_t discriminator, Optional<uint32_t> setupPIN, Optional<ByteSpan> salt,
108 : Callback::Callback<OnOpenCommissioningWindow> * callback, SetupPayload & payload,
109 : bool readVIDPIDAttributes = false);
110 :
111 : /**
112 : * @brief
113 : * Try to look up the device attached to our controller with the given
114 : * node id and ask it to re-enter commissioning mode with a PASE verifier
115 : * derived from the given information and the given discriminator. The
116 : * device will exit commissioning mode after a successful commissioning,
117 : * or after the given `timeout` time.
118 : *
119 : * @param[in] params The parameters required to open an enhanced commissioning window
120 : * with the provided or generated passcode.
121 : * @param[out] payload The setup payload, not including the VID/PID bits,
122 : * even if those were asked for, that is generated
123 : * based on the passed-in information. The payload
124 : * provided to the callback function, unlike this
125 : * out parameter, will include the VID/PID bits if
126 : * readVIDPIDAttributes is true.
127 : */
128 : CHIP_ERROR OpenCommissioningWindow(const CommissioningWindowPasscodeParams & params, SetupPayload & payload);
129 :
130 : #if CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
131 : /**
132 : * @brief
133 : * Try to look up the device attached to our controller with the given
134 : * node id and ask it to enter joint commissioning mode with a PASE verifier
135 : * derived from the given information and the given discriminator. The
136 : * device will exit joint commissioning mode after a successful joint commissioning,
137 : * or after the given `timeout` time.
138 : *
139 : * @param[in] params The parameters required to open a joint commissioning window
140 : * with the provided passcode.
141 : * @param[out] payload The setup payload, not including the VID/PID bits,
142 : * even if those were asked for, that is generated
143 : * based on the passed-in information. The payload
144 : * provided to the callback function, unlike this
145 : * out parameter, will include the VID/PID bits if
146 : * readVIDPIDAttributes is true.
147 : */
148 : CHIP_ERROR OpenJointCommissioningWindow(const CommissioningWindowPasscodeParams & params, SetupPayload & payload);
149 : #endif // CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
150 :
151 : /**
152 : * @brief
153 : * Try to look up the device attached to our controller with the given
154 : * node id and ask it to re-enter commissioning mode with a PASE verifier
155 : * derived from the given information and the given discriminator. The
156 : * device will exit commissioning mode after a successful commissioning,
157 : * or after the given `timeout` time.
158 : *
159 : * @param[in] params The parameters required to open an enhanced commissioning window
160 : * with the provided PAKE passcode verifier.
161 : */
162 : CHIP_ERROR OpenCommissioningWindow(const CommissioningWindowVerifierParams & params);
163 :
164 : private:
165 : enum class Step : uint8_t
166 : {
167 : // Ready to start opening a commissioning window.
168 : kAcceptCommissioningStart,
169 : // Need to read VID.
170 : kReadVID,
171 : // Need to read PID.
172 : kReadPID,
173 : // Need to open commissioning window.
174 : kOpenCommissioningWindow,
175 : };
176 :
177 : CHIP_ERROR OpenCommissioningWindowInternal(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle);
178 : static void OnPIDReadResponse(void * context, uint16_t value);
179 : static void OnVIDReadResponse(void * context, VendorId value);
180 : static void OnVIDPIDReadFailureResponse(void * context, CHIP_ERROR error);
181 : static void OnOpenCommissioningWindowSuccess(void * context, const app::DataModel::NullObjectType &);
182 : static void OnOpenCommissioningWindowFailure(void * context, CHIP_ERROR error);
183 : static void OnDeviceConnectedCallback(void * context, Messaging::ExchangeManager & exchangeMgr,
184 : const SessionHandle & sessionHandle);
185 : static void OnDeviceConnectionFailureCallback(void * context, const ScopedNodeId & peerId, CHIP_ERROR error);
186 :
187 : DeviceController * const mController = nullptr;
188 : Step mNextStep = Step::kAcceptCommissioningStart;
189 :
190 : Callback::Callback<OnOpenCommissioningWindow> * mCommissioningWindowCallback = nullptr;
191 : Callback::Callback<OnOpenCommissioningWindowWithVerifier> * mCommissioningWindowVerifierCallback = nullptr;
192 : Callback::Callback<OnOpenBasicCommissioningWindow> * mBasicCommissioningWindowCallback = nullptr;
193 : SetupPayload mSetupPayload;
194 : SetupDiscriminator mDiscriminator{};
195 : NodeId mNodeId = kUndefinedNodeId;
196 : EndpointId mTargetEndpointId = kRootEndpointId; // Default endpoint for Administrator Commissioning Cluster
197 : System::Clock::Seconds16 mCommissioningWindowTimeout = System::Clock::kZero;
198 : CommissioningWindowOption mCommissioningWindowOption = CommissioningWindowOption::kOriginalSetupCode;
199 : Crypto::Spake2pVerifier mVerifier; // Used for non-basic commissioning.
200 : // Parameters needed for non-basic commissioning.
201 : uint32_t mPBKDFIterations = 0;
202 : uint8_t mPBKDFSaltBuffer[Crypto::kSpake2p_Max_PBKDF_Salt_Length];
203 : ByteSpan mPBKDFSalt;
204 :
205 : Callback::Callback<OnDeviceConnected> mDeviceConnected;
206 : Callback::Callback<OnDeviceConnectionFailure> mDeviceConnectionFailure;
207 :
208 : #if CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
209 : bool mJointCommissioning = false;
210 : #endif // CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
211 : };
212 :
213 : /**
214 : * A helper class that can be used by consumers that don't care about the callback from the
215 : * open-commissioning-window process and just want automatic cleanup of the CommissioningWindowOpener when done
216 : * with it.
217 : */
218 : class AutoCommissioningWindowOpener : private CommissioningWindowOpener
219 : {
220 : public:
221 : // Takes the same arguments as CommissioningWindowOpener::OpenBasicCommissioningWindow except without the
222 : // callback.
223 : static CHIP_ERROR OpenBasicCommissioningWindow(DeviceController * controller, NodeId deviceId,
224 : System::Clock::Seconds16 timeout);
225 : // Takes the same arguments as CommissioningWindowOpener::OpenCommissioningWindow except without the
226 : // callback.
227 : static CHIP_ERROR OpenCommissioningWindow(DeviceController * controller, NodeId deviceId, System::Clock::Seconds16 timeout,
228 : uint32_t iteration, uint16_t discriminator, Optional<uint32_t> setupPIN,
229 : Optional<ByteSpan> salt, SetupPayload & payload, bool readVIDPIDAttributes = false);
230 :
231 : private:
232 : AutoCommissioningWindowOpener(DeviceController * controller);
233 :
234 : static void OnOpenCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload);
235 : static void OnOpenBasicCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status);
236 :
237 : chip::Callback::Callback<chip::Controller::OnOpenCommissioningWindow> mOnOpenCommissioningWindowCallback;
238 : chip::Callback::Callback<chip::Controller::OnOpenBasicCommissioningWindow> mOnOpenBasicCommissioningWindowCallback;
239 : };
240 :
241 : } // Namespace Controller
242 : } // namespace chip
|