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