Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021 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 : #include <protocols/secure_channel/PairingSession.h>
20 :
21 : #include <app/DataModelRevision.h>
22 : #include <app/InteractionModelRevision.h>
23 : #include <app/SpecificationVersion.h>
24 : #include <lib/core/CHIPConfig.h>
25 : #include <lib/core/TLVTypes.h>
26 : #include <lib/support/SafeInt.h>
27 :
28 : namespace chip {
29 :
30 54 : CHIP_ERROR PairingSession::AllocateSecureSession(SessionManager & sessionManager, const ScopedNodeId & sessionEvictionHint)
31 : {
32 54 : auto handle = sessionManager.AllocateSession(GetSecureSessionType(), sessionEvictionHint);
33 54 : VerifyOrReturnError(handle.HasValue(), CHIP_ERROR_NO_MEMORY);
34 54 : VerifyOrReturnError(mSecureSessionHolder.GrabPairingSession(handle.Value()), CHIP_ERROR_INTERNAL);
35 54 : mSessionManager = &sessionManager;
36 54 : return CHIP_NO_ERROR;
37 54 : }
38 :
39 26 : CHIP_ERROR PairingSession::ActivateSecureSession(const Transport::PeerAddress & peerAddress)
40 : {
41 : // Prepare SecureSession fields, including key derivation, first, before activation
42 26 : Transport::SecureSession * secureSession = mSecureSessionHolder->AsSecureSession();
43 26 : ReturnErrorOnFailure(DeriveSecureSession(secureSession->GetCryptoContext()));
44 :
45 26 : uint16_t peerSessionId = GetPeerSessionId();
46 26 : secureSession->SetPeerAddress(peerAddress);
47 26 : secureSession->GetSessionMessageCounter().GetPeerMessageCounter().SetCounter(Transport::PeerMessageCounter::kInitialSyncValue);
48 :
49 : // Call Activate last, otherwise errors on anything after would lead to
50 : // a partially valid session.
51 26 : secureSession->Activate(GetLocalScopedNodeId(), GetPeer(), GetPeerCATs(), peerSessionId, GetRemoteSessionParameters());
52 :
53 26 : ChipLogDetail(Inet, "New secure session activated for device " ChipLogFormatScopedNodeId ", LSID:%d PSID:%d!",
54 : ChipLogValueScopedNodeId(GetPeer()), secureSession->GetLocalSessionId(), peerSessionId);
55 :
56 26 : return CHIP_NO_ERROR;
57 : }
58 :
59 26 : void PairingSession::Finish()
60 : {
61 26 : Transport::PeerAddress address = mExchangeCtxt->GetSessionHandle()->AsUnauthenticatedSession()->GetPeerAddress();
62 :
63 : // Discard the exchange so that Clear() doesn't try closing it. The exchange will handle that.
64 26 : DiscardExchange();
65 :
66 26 : CHIP_ERROR err = ActivateSecureSession(address);
67 26 : if (err == CHIP_NO_ERROR)
68 : {
69 26 : VerifyOrDie(mSecureSessionHolder);
70 : // Make sure to null out mDelegate so we don't send it any other
71 : // notifications.
72 26 : auto * delegate = mDelegate;
73 26 : mDelegate = nullptr;
74 26 : delegate->OnSessionEstablished(mSecureSessionHolder.Get().Value());
75 : }
76 : else
77 : {
78 0 : NotifySessionEstablishmentError(err);
79 : }
80 26 : }
81 :
82 30 : void PairingSession::DiscardExchange()
83 : {
84 30 : if (mExchangeCtxt != nullptr)
85 : {
86 : // Make sure the exchange doesn't try to notify us when it closes,
87 : // since we might be dead by then.
88 30 : mExchangeCtxt->SetDelegate(nullptr);
89 : // Null out mExchangeCtxt so that Clear() doesn't try closing it. The
90 : // exchange will handle that.
91 30 : mExchangeCtxt = nullptr;
92 : }
93 30 : }
94 :
95 36 : CHIP_ERROR PairingSession::EncodeSessionParameters(TLV::Tag tag, const Optional<ReliableMessageProtocolConfig> & providedMrpConfig,
96 : TLV::TLVWriter & tlvWriter)
97 : {
98 : // TODO: https://github.com/project-chip/connectedhomeip/issues/30456. Based on the spec we need to send values here now,
99 : // but it is not entirely clear what we should be sending here when `providedMrpConfig.HasValue() == false`. For now we
100 : // are sending the default MRP config values.
101 36 : ReliableMessageProtocolConfig mrpLocalConfig = GetDefaultMRPConfig();
102 36 : if (providedMrpConfig.HasValue())
103 : {
104 7 : mrpLocalConfig = providedMrpConfig.Value();
105 : }
106 : TLV::TLVType mrpParamsContainer;
107 36 : ReturnErrorOnFailure(tlvWriter.StartContainer(tag, TLV::kTLVType_Structure, mrpParamsContainer));
108 36 : ReturnErrorOnFailure(
109 : tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kSessionIdleInterval), mrpLocalConfig.mIdleRetransTimeout.count()));
110 36 : ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kSessionActiveInterval),
111 : mrpLocalConfig.mActiveRetransTimeout.count()));
112 36 : ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kSessionActiveThreshold),
113 : mrpLocalConfig.mActiveThresholdTime.count()));
114 :
115 36 : uint16_t dataModel = CHIP_DEVICE_DATA_MODEL_REVISION;
116 36 : ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kDataModelRevision), dataModel));
117 :
118 36 : uint16_t interactionModel = CHIP_DEVICE_INTERACTION_MODEL_REVISION;
119 36 : ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kInteractionModelRevision), interactionModel));
120 :
121 36 : uint32_t specVersion = CHIP_DEVICE_SPECIFICATION_VERSION;
122 36 : ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kSpecificationVersion), specVersion));
123 :
124 36 : uint16_t maxPathsPerInvoke = CHIP_CONFIG_MAX_PATHS_PER_INVOKE;
125 36 : ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kMaxPathsPerInvoke), maxPathsPerInvoke));
126 36 : return tlvWriter.EndContainer(mrpParamsContainer);
127 : }
128 :
129 31 : CHIP_ERROR PairingSession::DecodeMRPParametersIfPresent(TLV::Tag expectedTag, TLV::ContiguousBufferTLVReader & tlvReader)
130 : {
131 31 : CHIP_ERROR err = CHIP_NO_ERROR;
132 :
133 : // The MRP parameters are optional.
134 31 : if (tlvReader.GetTag() != expectedTag)
135 : {
136 1 : return CHIP_NO_ERROR;
137 : }
138 :
139 30 : TLV::TLVType containerType = TLV::kTLVType_Structure;
140 30 : ReturnErrorOnFailure(tlvReader.EnterContainer(containerType));
141 :
142 30 : ReturnErrorOnFailure(tlvReader.Next());
143 :
144 30 : ChipLogDetail(SecureChannel, "Found MRP parameters in the message");
145 :
146 : // All TLV elements in the structure are optional. If the first element is present, process it and move
147 : // the TLV reader to the next element.
148 30 : if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kSessionIdleInterval)
149 : {
150 : uint32_t idleRetransTimeout;
151 30 : ReturnErrorOnFailure(tlvReader.Get(idleRetransTimeout));
152 30 : mRemoteSessionParams.SetMRPIdleRetransTimeout(System::Clock::Milliseconds32(idleRetransTimeout));
153 :
154 : // The next element is optional. If it's not present, return CHIP_NO_ERROR.
155 30 : SuccessOrExit(err = tlvReader.Next());
156 : }
157 :
158 30 : if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kSessionActiveInterval)
159 : {
160 : uint32_t activeRetransTimeout;
161 30 : ReturnErrorOnFailure(tlvReader.Get(activeRetransTimeout));
162 30 : mRemoteSessionParams.SetMRPActiveRetransTimeout(System::Clock::Milliseconds32(activeRetransTimeout));
163 :
164 : // The next element is optional. If it's not present, return CHIP_NO_ERROR.
165 30 : SuccessOrExit(err = tlvReader.Next());
166 : }
167 :
168 30 : if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kSessionActiveThreshold)
169 : {
170 : uint16_t activeThresholdTime;
171 30 : ReturnErrorOnFailure(tlvReader.Get(activeThresholdTime));
172 30 : mRemoteSessionParams.SetMRPActiveThresholdTime(System::Clock::Milliseconds16(activeThresholdTime));
173 :
174 : // The next element is optional. If it's not present, return CHIP_NO_ERROR.
175 30 : SuccessOrExit(err = tlvReader.Next());
176 : }
177 :
178 30 : if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kDataModelRevision)
179 : {
180 : uint16_t dataModelRevision;
181 30 : ReturnErrorOnFailure(tlvReader.Get(dataModelRevision));
182 30 : mRemoteSessionParams.SetDataModelRevision(dataModelRevision);
183 :
184 : // The next element is optional. If it's not present, return CHIP_NO_ERROR.
185 30 : SuccessOrExit(err = tlvReader.Next());
186 : }
187 :
188 30 : if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kInteractionModelRevision)
189 : {
190 : uint16_t interactionModelRevision;
191 30 : ReturnErrorOnFailure(tlvReader.Get(interactionModelRevision));
192 30 : mRemoteSessionParams.SetInteractionModelRevision(interactionModelRevision);
193 :
194 : // The next element is optional. If it's not present, return CHIP_NO_ERROR.
195 30 : SuccessOrExit(err = tlvReader.Next());
196 : }
197 :
198 30 : if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kSpecificationVersion)
199 : {
200 : uint32_t specificationVersion;
201 30 : ReturnErrorOnFailure(tlvReader.Get(specificationVersion));
202 30 : mRemoteSessionParams.SetSpecificationVersion(specificationVersion);
203 :
204 : // The next element is optional. If it's not present, return CHIP_NO_ERROR.
205 30 : SuccessOrExit(err = tlvReader.Next());
206 : }
207 :
208 30 : if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kMaxPathsPerInvoke)
209 : {
210 : uint16_t maxPathsPerInvoke;
211 30 : ReturnErrorOnFailure(tlvReader.Get(maxPathsPerInvoke));
212 30 : mRemoteSessionParams.SetMaxPathsPerInvoke(maxPathsPerInvoke);
213 :
214 : // The next element is optional. If it's not present, return CHIP_NO_ERROR.
215 30 : SuccessOrExit(err = tlvReader.Next());
216 : }
217 :
218 : // Future proofing - Don't error out if there are other tags
219 0 : exit:
220 30 : if (err == CHIP_END_OF_TLV)
221 : {
222 30 : return tlvReader.ExitContainer(containerType);
223 : }
224 0 : return err;
225 : }
226 :
227 4 : bool PairingSession::IsSessionEstablishmentInProgress()
228 : {
229 4 : if (!mSecureSessionHolder)
230 : {
231 0 : return false;
232 : }
233 :
234 4 : Transport::SecureSession * secureSession = mSecureSessionHolder->AsSecureSession();
235 4 : return secureSession->IsEstablishing();
236 : }
237 :
238 215 : void PairingSession::Clear()
239 : {
240 : // Clear acts like the destructor if PairingSession, if it is call during
241 : // middle of a pairing, means we should terminate the exchange. For normal
242 : // path, the exchange should already be discarded before calling Clear.
243 215 : if (mExchangeCtxt != nullptr)
244 : {
245 : // The only time we reach this is if we are getting destroyed in the
246 : // middle of our handshake. In that case, there is no point trying to
247 : // do MRP resends of the last message we sent, so abort the exchange
248 : // instead of just closing it.
249 6 : mExchangeCtxt->Abort();
250 6 : mExchangeCtxt = nullptr;
251 : }
252 :
253 215 : mSecureSessionHolder.Release();
254 215 : mPeerSessionId.ClearValue();
255 215 : mSessionManager = nullptr;
256 215 : }
257 :
258 18 : void PairingSession::NotifySessionEstablishmentError(CHIP_ERROR error, SessionEstablishmentStage stage)
259 : {
260 18 : if (mDelegate == nullptr)
261 : {
262 : // Already notified success or error.
263 10 : return;
264 : }
265 :
266 8 : auto * delegate = mDelegate;
267 8 : mDelegate = nullptr;
268 8 : delegate->OnSessionEstablishmentError(error, stage);
269 : }
270 :
271 13 : void PairingSession::OnSessionReleased()
272 : {
273 13 : if (mRole == CryptoContext::SessionRole::kInitiator)
274 : {
275 5 : NotifySessionEstablishmentError(CHIP_ERROR_CONNECTION_ABORTED);
276 5 : return;
277 : }
278 :
279 : // Send the error notification async, because our delegate is likely to want
280 : // to create a new session to listen for new connection attempts, and doing
281 : // that under an OnSessionReleased notification is not safe.
282 8 : if (!mSessionManager)
283 : {
284 0 : return;
285 : }
286 :
287 8 : mSessionManager->SystemLayer()->ScheduleWork(
288 7 : [](auto * systemLayer, auto * appState) -> void {
289 7 : ChipLogError(Inet, "ASYNC CASE Session establishment failed");
290 7 : auto * _this = static_cast<PairingSession *>(appState);
291 7 : _this->NotifySessionEstablishmentError(CHIP_ERROR_CONNECTION_ABORTED);
292 7 : },
293 : this);
294 : }
295 :
296 : } // namespace chip
|