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 <crypto/CHIPCryptoPAL.h>
24 : #include <lib/core/CHIPCallback.h>
25 : #include <lib/core/CHIPError.h>
26 : #include <lib/core/NodeId.h>
27 : #include <lib/core/Optional.h>
28 : #include <setup_payload/SetupPayload.h>
29 : #include <system/SystemClock.h>
30 :
31 : namespace chip {
32 : namespace Controller {
33 :
34 : // Passing SetupPayload by value on purpose, in case a consumer decides to reuse
35 : // this object from inside the callback.
36 : typedef void (*OnOpenCommissioningWindow)(void * context, NodeId deviceId, CHIP_ERROR status, SetupPayload payload);
37 : typedef void (*OnOpenBasicCommissioningWindow)(void * context, NodeId deviceId, CHIP_ERROR status);
38 :
39 : /**
40 : * A helper class to open a commissioning window given some parameters.
41 : */
42 : class CommissioningWindowOpener
43 : {
44 : public:
45 0 : CommissioningWindowOpener(DeviceController * controller) :
46 0 : mController(controller), mDeviceConnected(&OnDeviceConnectedCallback, this),
47 0 : mDeviceConnectionFailure(&OnDeviceConnectionFailureCallback, this)
48 0 : {}
49 :
50 : enum class CommissioningWindowOption : uint8_t
51 : {
52 : kOriginalSetupCode = 0,
53 : kTokenWithRandomPIN,
54 : kTokenWithProvidedPIN,
55 : };
56 :
57 : /*
58 : * @brief
59 : * Try to look up the device attached to our controller with the given
60 : * node id and ask it to re-enter commissioning mode with its original
61 : * PASE verifier, discriminator, etc. The device will exit commissioning
62 : * mode after a successful commissioning, or after the given `timeout`
63 : * time.
64 : *
65 : * @param[in] deviceId The device Id.
66 : * @param[in] timeout The commissioning mode should terminate after this much time.
67 : * @param[in] callback The callback to call once the commissioning window is
68 : * open or if an error occurs.
69 : */
70 : CHIP_ERROR OpenBasicCommissioningWindow(NodeId deviceId, System::Clock::Seconds16 timeout,
71 : Callback::Callback<OnOpenBasicCommissioningWindow> * callback);
72 :
73 : /**
74 : * @brief
75 : * Try to look up the device attached to our controller with the given
76 : * node id and ask it to re-enter commissioning mode with a PASE verifier
77 : * derived from the given information and the given discriminator. The
78 : * device will exit commissioning mode after a successful commissioning,
79 : * or after the given `timeout` time.
80 : *
81 : * @param[in] deviceId The device Id.
82 : * @param[in] timeout The commissioning mode should terminate after this much time.
83 : * @param[in] iteration The PAKE iteration count associated with the PAKE Passcode ID and ephemeral
84 : * PAKE passcode verifier to be used for this commissioning.
85 : * @param[in] discriminator The long discriminator for the DNS-SD advertisement.
86 : * @param[in] setupPIN The setup PIN to use, or NullOptional to use a randomly-generated one.
87 : * @param[in] salt The salt to use, or NullOptional to use a
88 : * randomly-generated one. If provided, must be at
89 : * least kSpake2p_Min_PBKDF_Salt_Length bytes and
90 : * at most kSpake2p_Max_PBKDF_Salt_Length bytes in
91 : * length.
92 : * @param[in] callback The function to be called on success or failure of opening of commissioning window.
93 : * @param[out] payload The setup payload, not including the VID/PID bits,
94 : * even if those were asked for, that is generated
95 : * based on the passed-in information. The payload
96 : * provided to the callback function, unlike this
97 : * out parameter, will include the VID/PID bits if
98 : * readVIDPIDAttributes is true.
99 : *
100 : * @param[in] readVIDPIDAttributes Should the API internally read VID and PID from the device while opening the
101 : * commissioning window. If this argument is `true`, the API will read VID and
102 : * PID from the device and include them in the setup payload passed to the
103 : * callback.
104 : */
105 : CHIP_ERROR OpenCommissioningWindow(NodeId deviceId, System::Clock::Seconds16 timeout, uint32_t iteration,
106 : uint16_t discriminator, Optional<uint32_t> setupPIN, Optional<ByteSpan> salt,
107 : Callback::Callback<OnOpenCommissioningWindow> * callback, SetupPayload & payload,
108 : bool readVIDPIDAttributes = false);
109 :
110 : private:
111 : enum class Step : uint8_t
112 : {
113 : // Ready to start opening a commissioning window.
114 : kAcceptCommissioningStart,
115 : // Need to read VID.
116 : kReadVID,
117 : // Need to read PID.
118 : kReadPID,
119 : // Need to open commissioning window.
120 : kOpenCommissioningWindow,
121 : };
122 :
123 : CHIP_ERROR OpenCommissioningWindowInternal(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle);
124 : static void OnPIDReadResponse(void * context, uint16_t value);
125 : static void OnVIDReadResponse(void * context, VendorId value);
126 : static void OnVIDPIDReadFailureResponse(void * context, CHIP_ERROR error);
127 : static void OnOpenCommissioningWindowSuccess(void * context, const app::DataModel::NullObjectType &);
128 : static void OnOpenCommissioningWindowFailure(void * context, CHIP_ERROR error);
129 : static void OnDeviceConnectedCallback(void * context, Messaging::ExchangeManager & exchangeMgr,
130 : const SessionHandle & sessionHandle);
131 : static void OnDeviceConnectionFailureCallback(void * context, const ScopedNodeId & peerId, CHIP_ERROR error);
132 :
133 : DeviceController * const mController = nullptr;
134 : Step mNextStep = Step::kAcceptCommissioningStart;
135 :
136 : Callback::Callback<OnOpenCommissioningWindow> * mCommissioningWindowCallback = nullptr;
137 : Callback::Callback<OnOpenBasicCommissioningWindow> * mBasicCommissioningWindowCallback = nullptr;
138 : SetupPayload mSetupPayload;
139 : NodeId mNodeId = kUndefinedNodeId;
140 : System::Clock::Seconds16 mCommissioningWindowTimeout = System::Clock::kZero;
141 : CommissioningWindowOption mCommissioningWindowOption = CommissioningWindowOption::kOriginalSetupCode;
142 : Spake2pVerifier mVerifier; // Used for non-basic commissioning.
143 : // Parameters needed for non-basic commissioning.
144 : uint32_t mPBKDFIterations = 0;
145 : uint8_t mPBKDFSaltBuffer[kSpake2p_Max_PBKDF_Salt_Length];
146 : ByteSpan mPBKDFSalt;
147 :
148 : Callback::Callback<OnDeviceConnected> mDeviceConnected;
149 : Callback::Callback<OnDeviceConnectionFailure> mDeviceConnectionFailure;
150 : };
151 :
152 : /**
153 : * A helper class that can be used by consumers that don't care about the callback from the
154 : * open-commissioning-window process and just want automatic cleanup of the CommissioningWindowOpener when done
155 : * with it.
156 : */
157 : class AutoCommissioningWindowOpener : private CommissioningWindowOpener
158 : {
159 : public:
160 : // Takes the same arguments as CommissioningWindowOpener::OpenBasicCommissioningWindow except without the
161 : // callback.
162 : static CHIP_ERROR OpenBasicCommissioningWindow(DeviceController * controller, NodeId deviceId,
163 : System::Clock::Seconds16 timeout);
164 : // Takes the same arguments as CommissioningWindowOpener::OpenCommissioningWindow except without the
165 : // callback.
166 : static CHIP_ERROR OpenCommissioningWindow(DeviceController * controller, NodeId deviceId, System::Clock::Seconds16 timeout,
167 : uint32_t iteration, uint16_t discriminator, Optional<uint32_t> setupPIN,
168 : Optional<ByteSpan> salt, SetupPayload & payload, bool readVIDPIDAttributes = false);
169 :
170 : private:
171 : AutoCommissioningWindowOpener(DeviceController * controller);
172 :
173 : static void OnOpenCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload);
174 : static void OnOpenBasicCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status);
175 :
176 : chip::Callback::Callback<chip::Controller::OnOpenCommissioningWindow> mOnOpenCommissioningWindowCallback;
177 : chip::Callback::Callback<chip::Controller::OnOpenBasicCommissioningWindow> mOnOpenBasicCommissioningWindowCallback;
178 : };
179 :
180 : } // Namespace Controller
181 : } // namespace chip
|