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