Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021-2022 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 : /**
20 : * @file
21 : * This file implements the the CHIP CASE Session object that provides
22 : * APIs for constructing a secure session using a certificate from the device's
23 : * operational credentials.
24 : *
25 : */
26 : #include <protocols/secure_channel/CASESession.h>
27 :
28 : #include <atomic>
29 : #include <inttypes.h>
30 : #include <memory>
31 : #include <string.h>
32 :
33 : #include <lib/core/CHIPEncoding.h>
34 : #include <lib/core/CHIPSafeCasts.h>
35 : #include <lib/support/CHIPMem.h>
36 : #include <lib/support/CodeUtils.h>
37 : #include <lib/support/SafeInt.h>
38 : #include <lib/support/ScopedBuffer.h>
39 : #include <lib/support/TypeTraits.h>
40 : #include <messaging/SessionParameters.h>
41 : #include <platform/PlatformManager.h>
42 : #include <protocols/Protocols.h>
43 : #include <protocols/secure_channel/CASEDestinationId.h>
44 : #include <protocols/secure_channel/PairingSession.h>
45 : #include <protocols/secure_channel/SessionResumptionStorage.h>
46 : #include <protocols/secure_channel/StatusReport.h>
47 : #include <system/SystemClock.h>
48 : #include <tracing/macros.h>
49 : #include <tracing/metric_event.h>
50 : #include <transport/SessionManager.h>
51 :
52 : namespace {
53 : // TBEDataTags works for both sigma-2-tbedata and sigma-3-tbedata as they have the same tag numbers for the elements common between
54 : // them.
55 : enum class TBEDataTags : uint8_t
56 : {
57 : kSenderNOC = 1,
58 : kSenderICAC = 2,
59 : kSignature = 3,
60 : kResumptionID = 4,
61 : };
62 :
63 : // TBSDataTags works for both sigma-2-tbsdata and sigma-3-tbsdata as they have the same tag numbers for the elements common between
64 : // them.
65 : enum class TBSDataTags : uint8_t
66 : {
67 : kSenderNOC = 1,
68 : kSenderICAC = 2,
69 : kSenderPubKey = 3,
70 : kReceiverPubKey = 4,
71 : };
72 :
73 : enum class Sigma1Tags : uint8_t
74 : {
75 : kInitiatorRandom = 1,
76 : kInitiatorSessionId = 2,
77 : kDestinationId = 3,
78 : kInitiatorEphPubKey = 4,
79 : kInitiatorSessionParams = 5,
80 : kResumptionID = 6,
81 : kResume1MIC = 7,
82 : };
83 :
84 : enum class Sigma2Tags : uint8_t
85 : {
86 : kResponderRandom = 1,
87 : kResponderSessionId = 2,
88 : kResponderEphPubKey = 3,
89 : kEncrypted2 = 4,
90 : kResponderSessionParams = 5,
91 : };
92 :
93 : enum class Sigma2ResumeTags : uint8_t
94 : {
95 : kResumptionID = 1,
96 : kSigma2ResumeMIC = 2,
97 : kResponderSessionID = 3,
98 : kResponderSessionParams = 4,
99 : };
100 :
101 : enum class Sigma3Tags : uint8_t
102 : {
103 : kEncrypted3 = 1,
104 : };
105 :
106 : // Utility to extract the underlying value of TLV Tag enum classes, used in TLV encoding and parsing.
107 : template <typename Enum>
108 804 : constexpr chip::TLV::Tag AsTlvContextTag(Enum e)
109 : {
110 804 : return chip::TLV::ContextTag(chip::to_underlying(e));
111 : }
112 :
113 : constexpr size_t kCaseOverheadForFutureTBEData = 128;
114 :
115 : } // namespace
116 :
117 : namespace chip {
118 :
119 : using namespace Crypto;
120 : using namespace Credentials;
121 : using namespace Messaging;
122 : using namespace Encoding;
123 : using namespace Protocols::SecureChannel;
124 : using namespace Tracing;
125 : using namespace TLV;
126 :
127 : constexpr uint8_t kKDFSR2Info[] = { 0x53, 0x69, 0x67, 0x6d, 0x61, 0x32 };
128 : constexpr uint8_t kKDFSR3Info[] = { 0x53, 0x69, 0x67, 0x6d, 0x61, 0x33 };
129 :
130 : constexpr uint8_t kKDFS1RKeyInfo[] = { 0x53, 0x69, 0x67, 0x6d, 0x61, 0x31, 0x5f, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65 };
131 : constexpr uint8_t kKDFS2RKeyInfo[] = { 0x53, 0x69, 0x67, 0x6d, 0x61, 0x32, 0x5f, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65 };
132 :
133 : constexpr uint8_t kResume1MIC_Nonce[] =
134 : /* "NCASE_SigmaS1" */ { 0x4e, 0x43, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x53, 0x31 };
135 : constexpr uint8_t kResume2MIC_Nonce[] =
136 : /* "NCASE_SigmaS2" */ { 0x4e, 0x43, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x53, 0x32 };
137 : constexpr uint8_t kTBEData2_Nonce[] =
138 : /* "NCASE_Sigma2N" */ { 0x4e, 0x43, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x32, 0x4e };
139 : constexpr uint8_t kTBEData3_Nonce[] =
140 : /* "NCASE_Sigma3N" */ { 0x4e, 0x43, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x33, 0x4e };
141 : constexpr size_t kTBEDataNonceLength = sizeof(kTBEData2_Nonce);
142 : static_assert(sizeof(kTBEData2_Nonce) == sizeof(kTBEData3_Nonce), "TBEData2_Nonce and TBEData3_Nonce must be same size");
143 :
144 : // Amounts of time to allow for server-side processing of messages.
145 : //
146 : // These timeout values only allow for the server-side processing and assume that any transport-specific
147 : // latency will be added to them.
148 : //
149 : // The session establishment fails if the response is not received within the resulting timeout window,
150 : // which accounts for both transport latency and the server-side latency.
151 : static constexpr ExchangeContext::Timeout kExpectedLowProcessingTime = System::Clock::Seconds16(2);
152 : static constexpr ExchangeContext::Timeout kExpectedSigma1ProcessingTime = kExpectedLowProcessingTime;
153 : static constexpr ExchangeContext::Timeout kExpectedHighProcessingTime = System::Clock::Seconds16(30);
154 :
155 : // Helper for managing a session's outstanding work.
156 : // Holds work data which is provided to a scheduled work callback (standalone),
157 : // then (if not canceled) to a scheduled after work callback (on the session).
158 : template <class DATA>
159 : class CASESession::WorkHelper
160 : {
161 : public:
162 : // Work callback, processed in the background via `PlatformManager::ScheduleBackgroundWork`.
163 : // This is a non-member function which does not use the associated session.
164 : // The return value is passed to the after work callback (called afterward).
165 : // Set `cancel` to true if calling the after work callback is not necessary.
166 : typedef CHIP_ERROR (*WorkCallback)(DATA & data, bool & cancel);
167 :
168 : // After work callback, processed in the main Matter task via `PlatformManager::ScheduleWork`.
169 : // This is a member function to be called on the associated session after the work callback.
170 : // The `status` value is the result of the work callback (called beforehand), or the status of
171 : // queueing the after work callback back to the Matter thread, if the work callback succeeds
172 : // but queueing fails.
173 : //
174 : // When this callback is called asynchronously (i.e. via ScheduleWork), the helper guarantees
175 : // that it will keep itself (and hence `data`) alive until the callback completes.
176 : typedef CHIP_ERROR (CASESession::*AfterWorkCallback)(DATA & data, CHIP_ERROR status);
177 :
178 : public:
179 : // Create a work helper using the specified session, work callback, after work callback, and data (template arg).
180 : // Lifetime is managed by sharing between the caller (typically the session) and the helper itself (while work is scheduled).
181 14 : static Platform::SharedPtr<WorkHelper> Create(CASESession & session, WorkCallback workCallback,
182 : AfterWorkCallback afterWorkCallback)
183 : {
184 : struct EnableShared : public WorkHelper
185 : {
186 14 : EnableShared(CASESession & session, WorkCallback workCallback, AfterWorkCallback afterWorkCallback) :
187 14 : WorkHelper(session, workCallback, afterWorkCallback)
188 14 : {}
189 : };
190 14 : auto ptr = Platform::MakeShared<EnableShared>(session, workCallback, afterWorkCallback);
191 14 : if (ptr)
192 : {
193 14 : ptr->mWeakPtr = ptr; // used by `ScheduleWork`
194 : }
195 14 : return ptr;
196 14 : }
197 :
198 : // Do the work immediately.
199 : // No scheduling, no outstanding work, no shared lifetime management.
200 : //
201 : // The caller must guarantee that it keeps the helper alive across this call, most likely by
202 : // holding a reference to it on the stack.
203 7 : CHIP_ERROR DoWork()
204 : {
205 : // Ensure that this function is being called from main Matter thread
206 7 : assertChipStackLockedByCurrentThread();
207 :
208 7 : VerifyOrReturnError(mSession && mWorkCallback && mAfterWorkCallback, CHIP_ERROR_INCORRECT_STATE);
209 7 : auto * helper = this;
210 7 : bool cancel = false;
211 7 : helper->mStatus = helper->mWorkCallback(helper->mData, cancel);
212 7 : if (!cancel)
213 : {
214 7 : helper->mStatus = (helper->mSession->*(helper->mAfterWorkCallback))(helper->mData, helper->mStatus);
215 : }
216 7 : return helper->mStatus;
217 : }
218 :
219 : // Schedule the work for later execution.
220 : // If lifetime is managed, the helper shares management while work is outstanding.
221 7 : CHIP_ERROR ScheduleWork()
222 : {
223 7 : VerifyOrReturnError(mSession && mWorkCallback && mAfterWorkCallback, CHIP_ERROR_INCORRECT_STATE);
224 : // Hold strong ptr while work is outstanding
225 7 : mStrongPtr = mWeakPtr.lock(); // set in `Create`
226 7 : auto status = DeviceLayer::PlatformMgr().ScheduleBackgroundWork(WorkHandler, reinterpret_cast<intptr_t>(this));
227 7 : if (status != CHIP_NO_ERROR)
228 : {
229 : // Release strong ptr since scheduling failed.
230 0 : mStrongPtr.reset();
231 : }
232 7 : return status;
233 : }
234 :
235 : // Cancel the work, by clearing the associated session.
236 6 : void CancelWork() { mSession.store(nullptr); }
237 :
238 14 : bool IsCancelled() const { return mSession.load() == nullptr; }
239 :
240 : // This API returns true when background thread fails to schedule the AfterWorkCallback
241 0 : bool UnableToScheduleAfterWorkCallback() { return mScheduleAfterWorkFailed.load(); }
242 :
243 : // Do after work immediately.
244 : // No scheduling, no outstanding work, no shared lifetime management.
245 0 : void DoAfterWork()
246 : {
247 0 : VerifyOrDie(UnableToScheduleAfterWorkCallback());
248 0 : AfterWorkHandler(reinterpret_cast<intptr_t>(this));
249 0 : }
250 :
251 : private:
252 : // Create a work helper using the specified session, work callback, after work callback, and data (template arg).
253 : // Lifetime is not managed, see `Create` for that option.
254 14 : WorkHelper(CASESession & session, WorkCallback workCallback, AfterWorkCallback afterWorkCallback) :
255 14 : mSession(&session), mWorkCallback(workCallback), mAfterWorkCallback(afterWorkCallback)
256 14 : {}
257 :
258 : // Handler for the work callback.
259 7 : static void WorkHandler(intptr_t arg)
260 : {
261 7 : auto * helper = reinterpret_cast<WorkHelper *>(arg);
262 : // Hold strong ptr while work is handled
263 7 : auto strongPtr(std::move(helper->mStrongPtr));
264 7 : VerifyOrReturn(!helper->IsCancelled());
265 7 : bool cancel = false;
266 : // Execute callback in background thread; data must be OK with this
267 7 : helper->mStatus = helper->mWorkCallback(helper->mData, cancel);
268 7 : VerifyOrReturn(!cancel && !helper->IsCancelled());
269 : // Hold strong ptr to ourselves while work is outstanding
270 7 : helper->mStrongPtr.swap(strongPtr);
271 7 : auto status = DeviceLayer::PlatformMgr().ScheduleWork(AfterWorkHandler, reinterpret_cast<intptr_t>(helper));
272 7 : if (status != CHIP_NO_ERROR)
273 : {
274 0 : ChipLogError(SecureChannel, "Failed to Schedule the AfterWorkCallback on foreground thread: %" CHIP_ERROR_FORMAT,
275 : status.Format());
276 :
277 : // We failed to schedule after work callback, so setting mScheduleAfterWorkFailed flag to true
278 : // This can be checked from foreground thread and after work callback can be retried
279 0 : helper->mStatus = status;
280 :
281 : // Release strong ptr to self since scheduling failed, because nothing guarantees
282 : // that AfterWorkHandler will get called at this point to release the reference,
283 : // and we don't want to leak. That said, we want to ensure that "helper" stays
284 : // alive through the end of this function (so we can set mScheduleAfterWorkFailed
285 : // on it), but also want to avoid racing on the single SharedPtr instance in
286 : // helper->mStrongPtr. That means we need to not touch helper->mStrongPtr after
287 : // writing to mScheduleAfterWorkFailed.
288 : //
289 : // The simplest way to do this is to move the reference in helper->mStrongPtr to
290 : // our stack, where it outlives all our accesses to "helper".
291 0 : strongPtr.swap(helper->mStrongPtr);
292 :
293 : // helper and any of its state should not be touched after storing mScheduleAfterWorkFailed.
294 0 : helper->mScheduleAfterWorkFailed.store(true);
295 : }
296 7 : }
297 :
298 : // Handler for the after work callback.
299 7 : static void AfterWorkHandler(intptr_t arg)
300 : {
301 : // Ensure that this function is being called from main Matter thread
302 7 : assertChipStackLockedByCurrentThread();
303 :
304 7 : auto * helper = reinterpret_cast<WorkHelper *>(arg);
305 : // Hold strong ptr while work is handled, and ensure that helper->mStrongPtr does not keep
306 : // holding a reference.
307 7 : auto strongPtr(std::move(helper->mStrongPtr));
308 7 : if (!strongPtr)
309 : {
310 : // This can happen if scheduling AfterWorkHandler failed. Just grab a strong ref
311 : // to handler directly, to fulfill our API contract of holding a strong reference
312 : // across the after-work callback. At this point, we are guaranteed that the
313 : // background thread is not touching the helper anymore.
314 0 : strongPtr = helper->mWeakPtr.lock();
315 : }
316 7 : if (auto * session = helper->mSession.load())
317 : {
318 : // Execute callback in Matter thread; session should be OK with this
319 7 : (session->*(helper->mAfterWorkCallback))(helper->mData, helper->mStatus);
320 : }
321 7 : }
322 :
323 : private:
324 : // Lifetime management: `ScheduleWork` sets `mStrongPtr` from `mWeakPtr`.
325 : Platform::WeakPtr<WorkHelper> mWeakPtr;
326 :
327 : // Lifetime management: `ScheduleWork` sets `mStrongPtr` from `mWeakPtr`.
328 : Platform::SharedPtr<WorkHelper> mStrongPtr;
329 :
330 : // Associated session, cleared by `CancelWork`.
331 : std::atomic<CASESession *> mSession;
332 :
333 : // Work callback, called by `WorkHandler`.
334 : WorkCallback mWorkCallback;
335 :
336 : // After work callback, called by `AfterWorkHandler`.
337 : AfterWorkCallback mAfterWorkCallback;
338 :
339 : // Return value of `mWorkCallback`, passed to `mAfterWorkCallback`.
340 : CHIP_ERROR mStatus;
341 :
342 : // If background thread fails to schedule AfterWorkCallback then this flag is set to true
343 : // and CASEServer then can check this one and run the AfterWorkCallback for us.
344 : //
345 : // When this happens, the write to this boolean _must_ be the last code that touches this
346 : // object on the background thread. After that, the Matter thread owns the object.
347 : std::atomic<bool> mScheduleAfterWorkFailed{ false };
348 :
349 : public:
350 : // Data passed to `mWorkCallback` and `mAfterWorkCallback`.
351 : DATA mData;
352 : };
353 :
354 18 : CASESession::~CASESession()
355 : {
356 : // Let's clear out any security state stored in the object, before destroying it.
357 18 : Clear();
358 18 : }
359 :
360 0 : void CASESession::OnSessionReleased()
361 : {
362 : // Call into our super-class before we clear our state.
363 0 : PairingSession::OnSessionReleased();
364 0 : Clear();
365 0 : }
366 :
367 79 : void CASESession::Clear()
368 : {
369 : MATTER_TRACE_SCOPE("Clear", "CASESession");
370 : // Cancel any outstanding work.
371 79 : if (mSendSigma3Helper)
372 : {
373 0 : mSendSigma3Helper->CancelWork();
374 0 : mSendSigma3Helper.reset();
375 : }
376 79 : if (mHandleSigma3Helper)
377 : {
378 6 : mHandleSigma3Helper->CancelWork();
379 6 : mHandleSigma3Helper.reset();
380 : }
381 :
382 : // This function zeroes out and resets the memory used by the object.
383 : // It's done so that no security related information will be leaked.
384 79 : mCommissioningHash.Clear();
385 79 : PairingSession::Clear();
386 :
387 79 : mState = State::kInitialized;
388 79 : Crypto::ClearSecretData(mIPK);
389 :
390 79 : if (mFabricsTable != nullptr)
391 : {
392 30 : mFabricsTable->RemoveFabricDelegate(this);
393 :
394 30 : mFabricsTable->ReleaseEphemeralKeypair(mEphemeralKey);
395 30 : mEphemeralKey = nullptr;
396 : }
397 :
398 79 : mLocalNodeId = kUndefinedNodeId;
399 79 : mPeerNodeId = kUndefinedNodeId;
400 79 : mFabricsTable = nullptr;
401 79 : mFabricIndex = kUndefinedFabricIndex;
402 : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
403 : // Clear the context object.
404 79 : mTCPConnCbCtxt.appContext = nullptr;
405 79 : mTCPConnCbCtxt.connCompleteCb = nullptr;
406 79 : mTCPConnCbCtxt.connClosedCb = nullptr;
407 79 : mTCPConnCbCtxt.connReceivedCb = nullptr;
408 :
409 79 : if (mPeerConnState)
410 : {
411 : // Set the app state callback object in the Connection state to null
412 : // to prevent any dangling pointer to memory(mTCPConnCbCtxt) owned
413 : // by the CASESession object, that is now getting cleared.
414 0 : mPeerConnState->mAppState = nullptr;
415 :
416 0 : if (mPeerConnState->mConnectionState != Transport::TCPState::kConnected)
417 : {
418 : // Abort the connection if the CASESession is being destroyed and the
419 : // connection is in the middle of being set up.
420 0 : mSessionManager->TCPDisconnect(mPeerConnState, /* shouldAbort = */ true);
421 0 : mPeerConnState = nullptr;
422 : }
423 : }
424 : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
425 79 : }
426 :
427 5 : void CASESession::InvalidateIfPendingEstablishmentOnFabric(FabricIndex fabricIndex)
428 : {
429 5 : if (mFabricIndex != fabricIndex)
430 : {
431 1 : return;
432 : }
433 4 : if (!IsSessionEstablishmentInProgress())
434 : {
435 2 : return;
436 : }
437 2 : AbortPendingEstablish(CHIP_ERROR_CANCELLED);
438 : }
439 :
440 30 : CHIP_ERROR CASESession::Init(SessionManager & sessionManager, Credentials::CertificateValidityPolicy * policy,
441 : SessionEstablishmentDelegate * delegate, const ScopedNodeId & sessionEvictionHint)
442 : {
443 : MATTER_TRACE_SCOPE("Init", "CASESession");
444 30 : VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
445 30 : VerifyOrReturnError(mGroupDataProvider != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
446 30 : VerifyOrReturnError(sessionManager.GetSessionKeystore() != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
447 :
448 30 : Clear();
449 :
450 30 : ReturnErrorOnFailure(mCommissioningHash.Begin());
451 :
452 30 : mDelegate = delegate;
453 30 : mSessionManager = &sessionManager;
454 :
455 30 : ReturnErrorOnFailure(AllocateSecureSession(sessionManager, sessionEvictionHint));
456 :
457 30 : mValidContext.Reset();
458 30 : mValidContext.mRequiredKeyUsages.Set(KeyUsageFlags::kDigitalSignature);
459 30 : mValidContext.mRequiredKeyPurposes.Set(KeyPurposeFlags::kServerAuth);
460 30 : mValidContext.mValidityPolicy = policy;
461 :
462 : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
463 30 : mTCPConnCbCtxt.appContext = this;
464 30 : mTCPConnCbCtxt.connCompleteCb = HandleConnectionAttemptComplete;
465 30 : mTCPConnCbCtxt.connClosedCb = HandleConnectionClosed;
466 : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
467 30 : return CHIP_NO_ERROR;
468 : }
469 :
470 : CHIP_ERROR
471 20 : CASESession::PrepareForSessionEstablishment(SessionManager & sessionManager, FabricTable * fabricTable,
472 : SessionResumptionStorage * sessionResumptionStorage,
473 : Credentials::CertificateValidityPolicy * policy,
474 : SessionEstablishmentDelegate * delegate, const ScopedNodeId & previouslyEstablishedPeer,
475 : Optional<ReliableMessageProtocolConfig> mrpLocalConfig)
476 : {
477 : MATTER_TRACE_SCOPE("PrepareForSessionEstablishment", "CASESession");
478 : // Below VerifyOrReturnError is not SuccessOrExit since we only want to goto `exit:` after
479 : // Init has been successfully called.
480 20 : VerifyOrReturnError(fabricTable != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
481 18 : ReturnErrorOnFailure(Init(sessionManager, policy, delegate, previouslyEstablishedPeer));
482 :
483 18 : CHIP_ERROR err = CHIP_NO_ERROR;
484 :
485 18 : SuccessOrExit(err = fabricTable->AddFabricDelegate(this));
486 :
487 18 : mFabricsTable = fabricTable;
488 18 : mRole = CryptoContext::SessionRole::kResponder;
489 18 : mSessionResumptionStorage = sessionResumptionStorage;
490 18 : mLocalMRPConfig = MakeOptional(mrpLocalConfig.ValueOr(GetDefaultMRPConfig()));
491 :
492 18 : ChipLogDetail(SecureChannel, "Allocated SecureSession (%p) - waiting for Sigma1 msg",
493 : mSecureSessionHolder.Get().Value()->AsSecureSession());
494 :
495 0 : exit:
496 18 : if (err != CHIP_NO_ERROR)
497 : {
498 0 : Clear();
499 : }
500 18 : return err;
501 : }
502 :
503 14 : CHIP_ERROR CASESession::EstablishSession(SessionManager & sessionManager, FabricTable * fabricTable, ScopedNodeId peerScopedNodeId,
504 : ExchangeContext * exchangeCtxt, SessionResumptionStorage * sessionResumptionStorage,
505 : Credentials::CertificateValidityPolicy * policy, SessionEstablishmentDelegate * delegate,
506 : Optional<ReliableMessageProtocolConfig> mrpLocalConfig)
507 : {
508 : MATTER_TRACE_SCOPE("EstablishSession", "CASESession");
509 14 : CHIP_ERROR err = CHIP_NO_ERROR;
510 :
511 : // Return early on error here, as we have not initialized any state yet
512 14 : VerifyOrReturnErrorWithMetric(kMetricDeviceCASESession, exchangeCtxt != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
513 12 : VerifyOrReturnErrorWithMetric(kMetricDeviceCASESession, fabricTable != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
514 :
515 : // Use FabricTable directly to avoid situation of dangling index from stale FabricInfo
516 : // until we factor-out any FabricInfo direct usage.
517 12 : VerifyOrReturnErrorWithMetric(kMetricDeviceCASESession, peerScopedNodeId.GetFabricIndex() != kUndefinedFabricIndex,
518 : CHIP_ERROR_INVALID_ARGUMENT);
519 12 : const auto * fabricInfo = fabricTable->FindFabricWithIndex(peerScopedNodeId.GetFabricIndex());
520 12 : VerifyOrReturnErrorWithMetric(kMetricDeviceCASESession, fabricInfo != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
521 :
522 12 : err = Init(sessionManager, policy, delegate, peerScopedNodeId);
523 :
524 12 : mRole = CryptoContext::SessionRole::kInitiator;
525 :
526 : // We are setting the exchange context specifically before checking for error.
527 : // This is to make sure the exchange will get closed if Init() returned an error.
528 12 : mExchangeCtxt.Emplace(*exchangeCtxt);
529 :
530 12 : Transport::PeerAddress peerAddress = mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->GetPeerAddress();
531 :
532 : // From here onwards, let's go to exit on error, as some state might have already
533 : // been initialized
534 12 : SuccessOrExitWithMetric(kMetricDeviceCASESession, err);
535 :
536 12 : SuccessOrExitWithMetric(kMetricDeviceCASESession, err = fabricTable->AddFabricDelegate(this));
537 :
538 : MATTER_LOG_METRIC_BEGIN(kMetricDeviceCASESession);
539 :
540 : // Set the PeerAddress in the secure session up front to indicate the
541 : // Transport Type of the session that is being set up.
542 12 : mSecureSessionHolder->AsSecureSession()->SetPeerAddress(peerAddress);
543 :
544 12 : mFabricsTable = fabricTable;
545 12 : mFabricIndex = fabricInfo->GetFabricIndex();
546 12 : mSessionResumptionStorage = sessionResumptionStorage;
547 12 : mLocalMRPConfig = MakeOptional(mrpLocalConfig.ValueOr(GetDefaultMRPConfig()));
548 :
549 12 : mExchangeCtxt.Value()->UseSuggestedResponseTimeout(kExpectedSigma1ProcessingTime);
550 12 : mPeerNodeId = peerScopedNodeId.GetNodeId();
551 12 : mLocalNodeId = fabricInfo->GetNodeId();
552 :
553 12 : ChipLogProgress(SecureChannel, "Initiating session on local FabricIndex %u from 0x" ChipLogFormatX64 " -> 0x" ChipLogFormatX64,
554 : static_cast<unsigned>(mFabricIndex), ChipLogValueX64(mLocalNodeId), ChipLogValueX64(mPeerNodeId));
555 :
556 12 : if (peerAddress.GetTransportType() == Transport::Type::kTcp)
557 : {
558 : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
559 0 : err = sessionManager.TCPConnect(peerAddress, &mTCPConnCbCtxt, &mPeerConnState);
560 0 : SuccessOrExit(err);
561 : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
562 : }
563 : else
564 : {
565 : MATTER_LOG_METRIC_BEGIN(kMetricDeviceCASESessionSigma1);
566 12 : err = SendSigma1();
567 12 : SuccessOrExit(err);
568 : }
569 :
570 12 : exit:
571 12 : if (err != CHIP_NO_ERROR)
572 : {
573 : MATTER_LOG_METRIC_END(kMetricDeviceCASESessionSigma1, err);
574 : MATTER_LOG_METRIC_END(kMetricDeviceCASESession, err);
575 1 : Clear();
576 : }
577 12 : return err;
578 : }
579 :
580 0 : void CASESession::OnResponseTimeout(ExchangeContext * ec)
581 : {
582 : MATTER_TRACE_SCOPE("OnResponseTimeout", "CASESession");
583 0 : VerifyOrReturn(ec != nullptr, ChipLogError(SecureChannel, "CASESession::OnResponseTimeout was called by null exchange"));
584 0 : VerifyOrReturn(mExchangeCtxt.HasValue() && (&mExchangeCtxt.Value().Get() == ec),
585 : ChipLogError(SecureChannel, "CASESession::OnResponseTimeout exchange doesn't match"));
586 0 : ChipLogError(SecureChannel,
587 : "CASESession timed out while waiting for a response from peer " ChipLogFormatScopedNodeId ". Current state was %u",
588 : ChipLogValueScopedNodeId(GetPeer()), to_underlying(mState));
589 : MATTER_TRACE_COUNTER("CASETimeout");
590 : // Discard the exchange so that Clear() doesn't try aborting it. The
591 : // exchange will handle that.
592 0 : DiscardExchange();
593 0 : AbortPendingEstablish(CHIP_ERROR_TIMEOUT);
594 : }
595 :
596 4 : void CASESession::AbortPendingEstablish(CHIP_ERROR err)
597 : {
598 : MATTER_LOG_METRIC_END(kMetricDeviceCASESession, err);
599 : MATTER_TRACE_SCOPE("AbortPendingEstablish", "CASESession");
600 : // This needs to come before Clear() which will reset mState.
601 4 : SessionEstablishmentStage state = MapCASEStateToSessionEstablishmentStage(mState);
602 4 : Clear();
603 : // Do this last in case the delegate frees us.
604 4 : NotifySessionEstablishmentError(err, state);
605 4 : }
606 :
607 16 : CHIP_ERROR CASESession::DeriveSecureSession(CryptoContext & session)
608 : {
609 16 : switch (mState)
610 : {
611 14 : case State::kFinished: {
612 : std::array<uint8_t, sizeof(mIPK) + kSHA256_Hash_Length> msg_salt;
613 :
614 : {
615 14 : Encoding::LittleEndian::BufferWriter bbuf(msg_salt);
616 14 : bbuf.Put(mIPK, sizeof(mIPK));
617 14 : bbuf.Put(mMessageDigest, sizeof(mMessageDigest));
618 :
619 14 : VerifyOrReturnError(bbuf.Fit(), CHIP_ERROR_BUFFER_TOO_SMALL);
620 : }
621 :
622 14 : ReturnErrorOnFailure(session.InitFromSecret(*mSessionManager->GetSessionKeystore(), mSharedSecret.Span(),
623 : ByteSpan(msg_salt), CryptoContext::SessionInfoType::kSessionEstablishment,
624 : mRole));
625 :
626 14 : return CHIP_NO_ERROR;
627 : }
628 2 : case State::kFinishedViaResume: {
629 : std::array<uint8_t, sizeof(mInitiatorRandom) + decltype(mResumeResumptionId)().size()> msg_salt;
630 :
631 : {
632 2 : Encoding::LittleEndian::BufferWriter bbuf(msg_salt);
633 2 : bbuf.Put(mInitiatorRandom, sizeof(mInitiatorRandom));
634 2 : bbuf.Put(mResumeResumptionId.data(), mResumeResumptionId.size());
635 :
636 2 : VerifyOrReturnError(bbuf.Fit(), CHIP_ERROR_BUFFER_TOO_SMALL);
637 : }
638 :
639 2 : ReturnErrorOnFailure(session.InitFromSecret(*mSessionManager->GetSessionKeystore(), mSharedSecret.Span(),
640 : ByteSpan(msg_salt), CryptoContext::SessionInfoType::kSessionResumption, mRole));
641 :
642 2 : return CHIP_NO_ERROR;
643 : }
644 0 : default:
645 0 : return CHIP_ERROR_INCORRECT_STATE;
646 : }
647 : }
648 :
649 12 : CHIP_ERROR CASESession::RecoverInitiatorIpk()
650 : {
651 12 : Credentials::GroupDataProvider::KeySet ipkKeySet;
652 :
653 12 : CHIP_ERROR err = mGroupDataProvider->GetIpkKeySet(mFabricIndex, ipkKeySet);
654 :
655 12 : if (err != CHIP_NO_ERROR)
656 : {
657 0 : ChipLogError(SecureChannel, "Failed to obtain IPK for initiating: %" CHIP_ERROR_FORMAT, err.Format());
658 0 : return err;
659 : }
660 12 : if ((ipkKeySet.num_keys_used == 0) || (ipkKeySet.num_keys_used > Credentials::GroupDataProvider::KeySet::kEpochKeysMax))
661 : {
662 0 : ChipLogError(SecureChannel, "Found invalid IPK keyset for initiator.");
663 0 : return CHIP_ERROR_INTERNAL;
664 : }
665 :
666 : // For the generation of the Destination Identifier,
667 : // the originator SHALL use the operational group key with the second oldest
668 : // EpochStartTime, if one exists, otherwise it SHALL use the single operational
669 : // group key available. The EpochStartTime are already ordered
670 12 : size_t ipkIndex = (ipkKeySet.num_keys_used > 1) ? ((ipkKeySet.num_keys_used - 1) - 1) : 0;
671 12 : memcpy(&mIPK[0], ipkKeySet.epoch_keys[ipkIndex].key, sizeof(mIPK));
672 :
673 : // Leaving this logging code for debug, but this cannot be enabled at runtime
674 : // since it leaks private security material.
675 : #if 0
676 : ChipLogProgress(SecureChannel, "RecoverInitiatorIpk: GroupDataProvider %p, Got IPK for FabricIndex %u", mGroupDataProvider,
677 : static_cast<unsigned>(mFabricIndex));
678 : ChipLogByteSpan(SecureChannel, ByteSpan(mIPK));
679 : #endif
680 :
681 12 : return CHIP_NO_ERROR;
682 : }
683 :
684 : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
685 0 : void CASESession::HandleConnectionAttemptComplete(Transport::ActiveTCPConnectionState * conn, CHIP_ERROR err)
686 : {
687 0 : VerifyOrReturn(conn != nullptr);
688 : // conn->mAppState should not be NULL. SessionManager has already checked
689 : // before calling this callback.
690 0 : VerifyOrDie(conn->mAppState != nullptr);
691 :
692 : char peerAddrBuf[chip::Transport::PeerAddress::kMaxToStringSize];
693 0 : conn->mPeerAddr.ToString(peerAddrBuf);
694 :
695 0 : CASESession * caseSession = reinterpret_cast<CASESession *>(conn->mAppState->appContext);
696 0 : VerifyOrReturn(caseSession != nullptr);
697 :
698 : // Exit and disconnect if connection setup encountered an error.
699 0 : SuccessOrExit(err);
700 :
701 0 : ChipLogDetail(SecureChannel, "TCP Connection established with %s before session establishment", peerAddrBuf);
702 :
703 : // Associate the connection with the current unauthenticated session for the
704 : // CASE exchange.
705 0 : caseSession->mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetTCPConnection(conn);
706 :
707 : // Associate the connection with the current secure session that is being
708 : // set up.
709 0 : caseSession->mSecureSessionHolder.Get().Value()->AsSecureSession()->SetTCPConnection(conn);
710 :
711 : // Send Sigma1 after connection is established for sessions over TCP
712 0 : err = caseSession->SendSigma1();
713 0 : SuccessOrExit(err);
714 :
715 0 : exit:
716 0 : if (err != CHIP_NO_ERROR)
717 : {
718 0 : ChipLogError(SecureChannel, "Connection establishment failed with peer at %s: %" CHIP_ERROR_FORMAT, peerAddrBuf,
719 : err.Format());
720 :
721 : // Close the underlying connection and ensure that the CASESession is
722 : // not holding on to a stale ActiveTCPConnectionState. We call
723 : // TCPDisconnect() here explicitly in order to abort the connection
724 : // even after it establishes successfully, but SendSigma1() fails for
725 : // some reason.
726 0 : caseSession->mSessionManager->TCPDisconnect(conn, /* shouldAbort = */ true);
727 0 : caseSession->mPeerConnState = nullptr;
728 :
729 0 : caseSession->Clear();
730 : }
731 : }
732 :
733 0 : void CASESession::HandleConnectionClosed(Transport::ActiveTCPConnectionState * conn, CHIP_ERROR conErr)
734 : {
735 0 : VerifyOrReturn(conn != nullptr);
736 : // conn->mAppState should not be NULL. SessionManager has already checked
737 : // before calling this callback.
738 0 : VerifyOrDie(conn->mAppState != nullptr);
739 :
740 0 : CASESession * caseSession = reinterpret_cast<CASESession *>(conn->mAppState->appContext);
741 0 : VerifyOrReturn(caseSession != nullptr);
742 :
743 : // Drop our pointer to the now-invalid connection state.
744 : //
745 : // Since the connection is closed, message sends over the ExchangeContext
746 : // will just fail and be handled like normal send errors.
747 : //
748 : // Additionally, SessionManager notifies (via ExchangeMgr) all ExchangeContexts on the
749 : // connection closures for the attached sessions and the ExchangeContexts
750 : // can close proactively if that's appropriate.
751 0 : caseSession->mPeerConnState = nullptr;
752 0 : ChipLogDetail(SecureChannel, "TCP Connection for this session has closed");
753 : }
754 : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
755 :
756 12 : CHIP_ERROR CASESession::SendSigma1()
757 : {
758 : MATTER_TRACE_SCOPE("SendSigma1", "CASESession");
759 :
760 12 : uint8_t destinationIdentifier[kSHA256_Hash_Length] = { 0 };
761 :
762 : // Struct that will be used as input to EncodeSigma1() method
763 12 : EncodeSigma1Inputs encodeSigma1Inputs;
764 :
765 : // Lookup fabric info.
766 12 : const auto * fabricInfo = mFabricsTable->FindFabricWithIndex(mFabricIndex);
767 12 : VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_INCORRECT_STATE);
768 :
769 : // Validate that we have a session ID allocated.
770 12 : VerifyOrReturnError(GetLocalSessionId().HasValue(), CHIP_ERROR_INCORRECT_STATE);
771 12 : encodeSigma1Inputs.initiatorSessionId = GetLocalSessionId().Value();
772 :
773 : // Generate an ephemeral keypair
774 12 : mEphemeralKey = mFabricsTable->AllocateEphemeralKeypairForCASE();
775 12 : VerifyOrReturnError(mEphemeralKey != nullptr, CHIP_ERROR_NO_MEMORY);
776 12 : ReturnErrorOnFailure(mEphemeralKey->Initialize(ECPKeyTarget::ECDH));
777 12 : encodeSigma1Inputs.initiatorEphPubKey = &mEphemeralKey->Pubkey();
778 :
779 : // Fill in the random value
780 12 : ReturnErrorOnFailure(DRBG_get_bytes(mInitiatorRandom, sizeof(mInitiatorRandom)));
781 12 : encodeSigma1Inputs.initiatorRandom = ByteSpan(mInitiatorRandom);
782 :
783 : // Generate a Destination Identifier based on the node we are attempting to reach
784 : {
785 : // Obtain originator IPK matching the fabric where we are trying to open a session. mIPK
786 : // will be properly set thereafter.
787 12 : ReturnErrorOnFailure(RecoverInitiatorIpk());
788 :
789 12 : FabricId fabricId = fabricInfo->GetFabricId();
790 12 : Crypto::P256PublicKey rootPubKey;
791 12 : ReturnErrorOnFailure(mFabricsTable->FetchRootPubkey(mFabricIndex, rootPubKey));
792 12 : Credentials::P256PublicKeySpan rootPubKeySpan{ rootPubKey.ConstBytes() };
793 :
794 12 : MutableByteSpan destinationIdSpan(destinationIdentifier);
795 12 : ReturnErrorOnFailure(GenerateCaseDestinationId(ByteSpan(mIPK), encodeSigma1Inputs.initiatorRandom, rootPubKeySpan, fabricId,
796 : mPeerNodeId, destinationIdSpan));
797 12 : encodeSigma1Inputs.destinationId = destinationIdSpan;
798 12 : }
799 :
800 12 : VerifyOrReturnError(mLocalMRPConfig.HasValue(), CHIP_ERROR_INCORRECT_STATE);
801 12 : encodeSigma1Inputs.initiatorMrpConfig = &mLocalMRPConfig.Value();
802 :
803 : // Try to find persistent session, and resume it.
804 12 : if (mSessionResumptionStorage != nullptr)
805 : {
806 4 : CHIP_ERROR err = mSessionResumptionStorage->FindByScopedNodeId(fabricInfo->GetScopedNodeIdForNode(mPeerNodeId),
807 4 : mResumeResumptionId, mSharedSecret, mPeerCATs);
808 4 : if (err == CHIP_NO_ERROR)
809 : {
810 : // Found valid resumption state, try to resume the session.
811 3 : encodeSigma1Inputs.resumptionId = mResumeResumptionId;
812 3 : MutableByteSpan resumeMICSpan(encodeSigma1Inputs.initiatorResume1MICBuffer);
813 3 : ReturnErrorOnFailure(GenerateSigmaResumeMIC(encodeSigma1Inputs.initiatorRandom, encodeSigma1Inputs.resumptionId,
814 : ByteSpan(kKDFS1RKeyInfo), ByteSpan(kResume1MIC_Nonce), resumeMICSpan));
815 :
816 3 : encodeSigma1Inputs.initiatorResumeMIC = resumeMICSpan;
817 3 : encodeSigma1Inputs.sessionResumptionRequested = true;
818 : }
819 : }
820 :
821 12 : System::PacketBufferHandle msgR1;
822 :
823 : // Encode Sigma1 in CHIP TLV Format
824 12 : ReturnErrorOnFailure(EncodeSigma1(msgR1, encodeSigma1Inputs));
825 :
826 12 : ReturnErrorOnFailure(mCommissioningHash.AddData(ByteSpan{ msgR1->Start(), msgR1->DataLength() }));
827 :
828 : // Call delegate to send the msg to peer
829 12 : ReturnErrorOnFailure(mExchangeCtxt.Value()->SendMessage(Protocols::SecureChannel::MsgType::CASE_Sigma1, std::move(msgR1),
830 : SendFlags(SendMessageFlags::kExpectResponse)));
831 :
832 11 : if (encodeSigma1Inputs.sessionResumptionRequested)
833 : {
834 3 : mState = State::kSentSigma1Resume;
835 :
836 : // Flags that Resume is being attempted
837 : MATTER_LOG_METRIC(kMetricDeviceCASESessionSigma1Resume);
838 : }
839 : else
840 : {
841 8 : mState = State::kSentSigma1;
842 : }
843 :
844 : #if CHIP_PROGRESS_LOGGING
845 11 : const auto localMRPConfig = mLocalMRPConfig.Value();
846 : #endif // CHIP_PROGRESS_LOGGING
847 11 : ChipLogProgress(SecureChannel, "Sent Sigma1 msg to " ChipLogFormatScopedNodeId " [II:%" PRIu32 "ms AI:%" PRIu32 "ms AT:%ums]",
848 : ChipLogValueScopedNodeId(GetPeer()), localMRPConfig.mIdleRetransTimeout.count(),
849 : localMRPConfig.mActiveRetransTimeout.count(), localMRPConfig.mActiveThresholdTime.count());
850 :
851 11 : mDelegate->OnSessionEstablishmentStarted();
852 :
853 11 : return CHIP_NO_ERROR;
854 12 : }
855 :
856 18 : CHIP_ERROR CASESession::EncodeSigma1(System::PacketBufferHandle & msg, EncodeSigma1Inputs & input)
857 : {
858 : MATTER_TRACE_SCOPE("EncodeSigma1", "CASESession");
859 :
860 18 : VerifyOrReturnError(input.initiatorEphPubKey != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
861 :
862 17 : size_t dataLen = EstimateStructOverhead(kSigmaParamRandomNumberSize, // initiatorRandom
863 : sizeof(uint16_t), // initiatorSessionId,
864 : kSHA256_Hash_Length, // destinationId
865 : kP256_PublicKey_Length, // InitiatorEphPubKey,
866 : SessionParameters::kEstimatedTLVSize, // initiatorSessionParams
867 : SessionResumptionStorage::kResumptionIdSize, // resumptionId
868 : CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES // initiatorResumeMIC
869 : );
870 :
871 17 : msg = System::PacketBufferHandle::New(dataLen);
872 17 : VerifyOrReturnError(!msg.IsNull(), CHIP_ERROR_NO_MEMORY);
873 :
874 17 : System::PacketBufferTLVWriter tlvWriter;
875 17 : tlvWriter.Init(std::move(msg));
876 :
877 17 : TLVType outerContainerType = kTLVType_NotSpecified;
878 17 : ReturnErrorOnFailure(tlvWriter.StartContainer(AnonymousTag(), kTLVType_Structure, outerContainerType));
879 17 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(Sigma1Tags::kInitiatorRandom), input.initiatorRandom));
880 17 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(Sigma1Tags::kInitiatorSessionId), input.initiatorSessionId));
881 17 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(Sigma1Tags::kDestinationId), input.destinationId));
882 :
883 17 : ReturnErrorOnFailure(tlvWriter.PutBytes(AsTlvContextTag(Sigma1Tags::kInitiatorEphPubKey), *input.initiatorEphPubKey,
884 : static_cast<uint32_t>(input.initiatorEphPubKey->Length())));
885 :
886 17 : VerifyOrReturnError(input.initiatorMrpConfig != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
887 16 : ReturnErrorOnFailure(
888 : EncodeSessionParameters(AsTlvContextTag(Sigma1Tags::kInitiatorSessionParams), *input.initiatorMrpConfig, tlvWriter));
889 :
890 16 : if (input.sessionResumptionRequested)
891 : {
892 4 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(Sigma1Tags::kResumptionID), input.resumptionId));
893 4 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(Sigma1Tags::kResume1MIC), input.initiatorResumeMIC));
894 : }
895 :
896 16 : ReturnErrorOnFailure(tlvWriter.EndContainer(outerContainerType));
897 16 : ReturnErrorOnFailure(tlvWriter.Finalize(&msg));
898 :
899 16 : return CHIP_NO_ERROR;
900 17 : }
901 :
902 10 : CHIP_ERROR CASESession::HandleSigma1_and_SendSigma2(System::PacketBufferHandle && msg)
903 : {
904 : MATTER_TRACE_SCOPE("HandleSigma1_and_SendSigma2", "CASESession");
905 :
906 10 : CHIP_ERROR err = CHIP_NO_ERROR;
907 :
908 : // Parse and Validate Received Sigma1, and decide next step
909 10 : NextStep nextStep = HandleSigma1(std::move(msg));
910 10 : VerifyOrExit(nextStep.Is<Step>(), err = nextStep.Get<CHIP_ERROR>());
911 :
912 9 : switch (nextStep.Get<Step>())
913 : {
914 8 : case Step::kSendSigma2: {
915 :
916 8 : System::PacketBufferHandle msgR2;
917 8 : EncodeSigma2Inputs encodeSigma2;
918 :
919 8 : SuccessOrExit(err = PrepareSigma2(encodeSigma2));
920 8 : SuccessOrExit(err = EncodeSigma2(msgR2, encodeSigma2));
921 :
922 : MATTER_LOG_METRIC_BEGIN(kMetricDeviceCASESessionSigma2);
923 8 : SuccessOrExitAction(err = SendSigma2(std::move(msgR2)), MATTER_LOG_METRIC_END(kMetricDeviceCASESessionSigma2, err));
924 :
925 8 : mDelegate->OnSessionEstablishmentStarted();
926 8 : break;
927 16 : }
928 1 : case Step::kSendSigma2Resume: {
929 :
930 1 : System::PacketBufferHandle msgR2Resume;
931 1 : EncodeSigma2ResumeInputs encodeSigma2Resume;
932 :
933 1 : SuccessOrExit(err = PrepareSigma2Resume(encodeSigma2Resume));
934 1 : SuccessOrExit(err = EncodeSigma2Resume(msgR2Resume, encodeSigma2Resume));
935 :
936 : MATTER_LOG_METRIC_BEGIN(kMetricDeviceCASESessionSigma2Resume);
937 1 : SuccessOrExitAction(err = SendSigma2Resume(std::move(msgR2Resume)),
938 : MATTER_LOG_METRIC_END(kMetricDeviceCASESessionSigma2Resume, err));
939 :
940 1 : mDelegate->OnSessionEstablishmentStarted();
941 1 : break;
942 1 : }
943 0 : default:
944 0 : break;
945 : }
946 :
947 10 : exit:
948 10 : if (err == CHIP_ERROR_KEY_NOT_FOUND)
949 : {
950 1 : SendStatusReport(mExchangeCtxt, kProtocolCodeNoSharedRoot);
951 1 : mState = State::kInitialized;
952 : }
953 9 : else if (err != CHIP_NO_ERROR)
954 : {
955 0 : SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam);
956 0 : mState = State::kInitialized;
957 : }
958 10 : return err;
959 10 : }
960 :
961 9 : CHIP_ERROR CASESession::FindLocalNodeFromDestinationId(const ByteSpan & destinationId, const ByteSpan & initiatorRandom)
962 : {
963 : MATTER_TRACE_SCOPE("FindLocalNodeFromDestinationId", "CASESession");
964 9 : VerifyOrReturnError(mFabricsTable != nullptr, CHIP_ERROR_INCORRECT_STATE);
965 :
966 9 : bool found = false;
967 10 : for (const FabricInfo & fabricInfo : *mFabricsTable)
968 : {
969 : // Basic data for candidate fabric, used to compute candidate destination identifiers
970 9 : FabricId fabricId = fabricInfo.GetFabricId();
971 9 : NodeId nodeId = fabricInfo.GetNodeId();
972 9 : Crypto::P256PublicKey rootPubKey;
973 9 : ReturnErrorOnFailure(mFabricsTable->FetchRootPubkey(fabricInfo.GetFabricIndex(), rootPubKey));
974 9 : Credentials::P256PublicKeySpan rootPubKeySpan{ rootPubKey.ConstBytes() };
975 :
976 : // Get IPK operational group key set for current candidate fabric
977 9 : GroupDataProvider::KeySet ipkKeySet;
978 9 : CHIP_ERROR err = mGroupDataProvider->GetIpkKeySet(fabricInfo.GetFabricIndex(), ipkKeySet);
979 18 : if ((err != CHIP_NO_ERROR) ||
980 9 : ((ipkKeySet.num_keys_used == 0) || (ipkKeySet.num_keys_used > Credentials::GroupDataProvider::KeySet::kEpochKeysMax)))
981 : {
982 0 : continue;
983 : }
984 :
985 : // Try every IPK candidate we have for a match
986 10 : for (size_t keyIdx = 0; keyIdx < ipkKeySet.num_keys_used; ++keyIdx)
987 : {
988 : uint8_t candidateDestinationId[kSHA256_Hash_Length];
989 9 : MutableByteSpan candidateDestinationIdSpan(candidateDestinationId);
990 9 : ByteSpan candidateIpkSpan(ipkKeySet.epoch_keys[keyIdx].key);
991 :
992 9 : err = GenerateCaseDestinationId(candidateIpkSpan, initiatorRandom, rootPubKeySpan, fabricId, nodeId,
993 : candidateDestinationIdSpan);
994 9 : if ((err == CHIP_NO_ERROR) && (candidateDestinationIdSpan.data_equal(destinationId)))
995 : {
996 : // Found a match, stop working, cache IPK, update local fabric context
997 8 : found = true;
998 8 : MutableByteSpan ipkSpan(mIPK);
999 8 : CopySpanToMutableSpan(candidateIpkSpan, ipkSpan);
1000 8 : mFabricIndex = fabricInfo.GetFabricIndex();
1001 8 : mLocalNodeId = nodeId;
1002 8 : break;
1003 : }
1004 : }
1005 :
1006 9 : if (found)
1007 : {
1008 8 : break;
1009 : }
1010 9 : }
1011 :
1012 9 : return found ? CHIP_NO_ERROR : CHIP_ERROR_KEY_NOT_FOUND;
1013 : }
1014 :
1015 3 : CHIP_ERROR CASESession::TryResumeSession(SessionResumptionStorage::ConstResumptionIdView resumptionId, ByteSpan resume1MIC,
1016 : ByteSpan initiatorRandom)
1017 : {
1018 : MATTER_TRACE_SCOPE("TryResumeSession", "CASESession");
1019 3 : VerifyOrReturnError(mSessionResumptionStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
1020 3 : VerifyOrReturnError(mFabricsTable != nullptr, CHIP_ERROR_INCORRECT_STATE);
1021 :
1022 3 : SessionResumptionStorage::ConstResumptionIdView resumptionIdSpan(resumptionId);
1023 3 : ScopedNodeId node;
1024 3 : ReturnErrorOnFailure(mSessionResumptionStorage->FindByResumptionId(resumptionIdSpan, node, mSharedSecret, mPeerCATs));
1025 :
1026 : // Cross check resume1MIC with the shared secret
1027 2 : ReturnErrorOnFailure(
1028 : ValidateSigmaResumeMIC(resume1MIC, initiatorRandom, resumptionId, ByteSpan(kKDFS1RKeyInfo), ByteSpan(kResume1MIC_Nonce)));
1029 :
1030 1 : const auto * fabricInfo = mFabricsTable->FindFabricWithIndex(node.GetFabricIndex());
1031 1 : VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_INCORRECT_STATE);
1032 :
1033 1 : mFabricIndex = node.GetFabricIndex();
1034 1 : mPeerNodeId = node.GetNodeId();
1035 1 : mLocalNodeId = fabricInfo->GetNodeId();
1036 :
1037 1 : return CHIP_NO_ERROR;
1038 : }
1039 10 : CASESession::NextStep CASESession::HandleSigma1(System::PacketBufferHandle && msg)
1040 : {
1041 : MATTER_TRACE_SCOPE("HandleSigma1", "CASESession");
1042 10 : ChipLogProgress(SecureChannel, "Received Sigma1 msg");
1043 : MATTER_TRACE_COUNTER("Sigma1");
1044 :
1045 10 : VerifyOrReturnError(mFabricsTable != nullptr, NextStep::Create<CHIP_ERROR>(CHIP_ERROR_INCORRECT_STATE));
1046 :
1047 10 : ReturnErrorVariantOnFailure(NextStep, mCommissioningHash.AddData(ByteSpan{ msg->Start(), msg->DataLength() }));
1048 :
1049 10 : System::PacketBufferTLVReader tlvReader;
1050 10 : tlvReader.Init(std::move(msg));
1051 :
1052 : // Struct that will serve as output in ParseSigma1
1053 10 : ParsedSigma1 parsedSigma1;
1054 :
1055 10 : ReturnErrorVariantOnFailure(NextStep, ParseSigma1(tlvReader, parsedSigma1));
1056 :
1057 10 : ChipLogDetail(SecureChannel, "Peer (Initiator) assigned session ID %d", parsedSigma1.initiatorSessionId);
1058 10 : SetPeerSessionId(parsedSigma1.initiatorSessionId);
1059 :
1060 : // Set the Session parameters provided in the Sigma1 message
1061 10 : if (parsedSigma1.initiatorSessionParamStructPresent)
1062 : {
1063 9 : SetRemoteSessionParameters(parsedSigma1.initiatorSessionParams);
1064 9 : mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters(
1065 : GetRemoteSessionParameters());
1066 : }
1067 :
1068 3 : if (parsedSigma1.sessionResumptionRequested &&
1069 13 : parsedSigma1.resumptionId.size() == SessionResumptionStorage::kResumptionIdSize &&
1070 3 : CHIP_NO_ERROR ==
1071 13 : TryResumeSession(SessionResumptionStorage::ConstResumptionIdView(parsedSigma1.resumptionId.data()),
1072 3 : parsedSigma1.initiatorResumeMIC, parsedSigma1.initiatorRandom))
1073 : {
1074 1 : std::copy(parsedSigma1.initiatorRandom.begin(), parsedSigma1.initiatorRandom.end(), mInitiatorRandom);
1075 1 : std::copy(parsedSigma1.resumptionId.begin(), parsedSigma1.resumptionId.end(), mResumeResumptionId.begin());
1076 :
1077 : // Early returning here, since the next Step is known to be Sigma2Resume, and no further processing is needed for the
1078 : // Sigma1 message
1079 1 : return NextStep::Create<Step>(Step::kSendSigma2Resume);
1080 : }
1081 :
1082 : // ParseSigma1 ensures that:
1083 : // mRemotePubKey.Length() == initiatorPubKey.size() == kP256_PublicKey_Length.
1084 9 : memcpy(mRemotePubKey.Bytes(), parsedSigma1.initiatorEphPubKey.data(), mRemotePubKey.Length());
1085 :
1086 9 : CHIP_ERROR err = CHIP_NO_ERROR;
1087 :
1088 : // Attempt to match the initiator's desired destination based on local fabric table.
1089 9 : err = FindLocalNodeFromDestinationId(parsedSigma1.destinationId, parsedSigma1.initiatorRandom);
1090 9 : if (err == CHIP_NO_ERROR)
1091 : {
1092 8 : ChipLogProgress(SecureChannel, "CASE matched destination ID: fabricIndex %u, NodeID 0x" ChipLogFormatX64,
1093 : static_cast<unsigned>(mFabricIndex), ChipLogValueX64(mLocalNodeId));
1094 :
1095 : // Side-effect of FindLocalNodeFromDestinationId success was that mFabricIndex/mLocalNodeId are now
1096 : // set to the local fabric and associated NodeId that was targeted by the initiator.
1097 :
1098 8 : return NextStep::Create<Step>(Step::kSendSigma2);
1099 : }
1100 :
1101 1 : ChipLogError(SecureChannel, "CASE failed to match destination ID with local fabrics");
1102 1 : ChipLogByteSpan(SecureChannel, parsedSigma1.destinationId);
1103 :
1104 : // FindLocalNodeFromDestinationId returns CHIP_ERROR_KEY_NOT_FOUND if Sigma1's DestinationId does not match any
1105 : // candidateDestinationId, this will trigger a status Report with ProtocolCode = NoSharedTrustRoots.
1106 :
1107 : // Returning a CHIP_ERROR variant that will trigger a corresponding Status Report.
1108 1 : return NextStep::Create<CHIP_ERROR>(err);
1109 10 : }
1110 :
1111 1 : CHIP_ERROR CASESession::PrepareSigma2Resume(EncodeSigma2ResumeInputs & outSigma2ResData)
1112 : {
1113 : MATTER_TRACE_SCOPE("PrepareSigma2Resume", "CASESession");
1114 :
1115 1 : VerifyOrReturnError(mLocalMRPConfig.HasValue(), CHIP_ERROR_INCORRECT_STATE);
1116 :
1117 : // Validate that we have a session ID allocated.
1118 1 : VerifyOrReturnError(GetLocalSessionId().HasValue(), CHIP_ERROR_INCORRECT_STATE);
1119 1 : outSigma2ResData.responderSessionId = GetLocalSessionId().Value();
1120 :
1121 : // Generate a new resumption ID
1122 1 : ReturnErrorOnFailure(DRBG_get_bytes(mNewResumptionId.data(), mNewResumptionId.size()));
1123 1 : outSigma2ResData.resumptionId = mNewResumptionId;
1124 :
1125 1 : ReturnErrorOnFailure(GenerateSigmaResumeMIC(ByteSpan(mInitiatorRandom), mNewResumptionId, ByteSpan(kKDFS2RKeyInfo),
1126 : ByteSpan(kResume2MIC_Nonce), outSigma2ResData.sigma2ResumeMIC));
1127 :
1128 1 : outSigma2ResData.responderMrpConfig = &mLocalMRPConfig.Value();
1129 :
1130 1 : return CHIP_NO_ERROR;
1131 : }
1132 :
1133 5 : CHIP_ERROR CASESession::EncodeSigma2Resume(System::PacketBufferHandle & msgR2Resume, EncodeSigma2ResumeInputs & input)
1134 : {
1135 : MATTER_TRACE_SCOPE("EncodeSigma2Resume", "CASESession");
1136 :
1137 5 : VerifyOrReturnError(input.responderMrpConfig != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
1138 :
1139 4 : size_t maxDatalLen = EstimateStructOverhead(SessionResumptionStorage::kResumptionIdSize, // resumptionID
1140 : CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, // sigma2ResumeMIC
1141 : sizeof(uint16_t), // responderSessionID
1142 : SessionParameters::kEstimatedTLVSize // responderSessionParams
1143 : );
1144 :
1145 4 : msgR2Resume = System::PacketBufferHandle::New(maxDatalLen);
1146 4 : VerifyOrReturnError(!msgR2Resume.IsNull(), CHIP_ERROR_NO_MEMORY);
1147 :
1148 4 : System::PacketBufferTLVWriter tlvWriter;
1149 4 : tlvWriter.Init(std::move(msgR2Resume));
1150 :
1151 4 : TLVType outerContainerType = kTLVType_NotSpecified;
1152 :
1153 4 : ReturnErrorOnFailure(tlvWriter.StartContainer(AnonymousTag(), kTLVType_Structure, outerContainerType));
1154 4 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(Sigma2ResumeTags::kResumptionID), input.resumptionId));
1155 4 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(Sigma2ResumeTags::kSigma2ResumeMIC), input.sigma2ResumeMIC));
1156 4 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(Sigma2ResumeTags::kResponderSessionID), input.responderSessionId));
1157 :
1158 4 : ReturnErrorOnFailure(
1159 : EncodeSessionParameters(AsTlvContextTag(Sigma2ResumeTags::kResponderSessionParams), *input.responderMrpConfig, tlvWriter));
1160 :
1161 4 : ReturnErrorOnFailure(tlvWriter.EndContainer(outerContainerType));
1162 4 : ReturnErrorOnFailure(tlvWriter.Finalize(&msgR2Resume));
1163 :
1164 4 : return CHIP_NO_ERROR;
1165 4 : }
1166 :
1167 1 : CHIP_ERROR CASESession::SendSigma2Resume(System::PacketBufferHandle && msgR2Resume)
1168 : {
1169 :
1170 : // Call delegate to send the msg to peer
1171 1 : ReturnErrorOnFailure(mExchangeCtxt.Value()->SendMessage(Protocols::SecureChannel::MsgType::CASE_Sigma2Resume,
1172 : std::move(msgR2Resume), SendFlags(SendMessageFlags::kExpectResponse)));
1173 :
1174 1 : mState = State::kSentSigma2Resume;
1175 :
1176 1 : ChipLogDetail(SecureChannel, "Sent Sigma2Resume msg");
1177 :
1178 1 : return CHIP_NO_ERROR;
1179 : }
1180 :
1181 8 : CHIP_ERROR CASESession::PrepareSigma2(EncodeSigma2Inputs & outSigma2Data)
1182 : {
1183 :
1184 : MATTER_TRACE_SCOPE("PrepareSigma2", "CASESession");
1185 :
1186 8 : VerifyOrReturnError(mFabricsTable != nullptr, CHIP_ERROR_INCORRECT_STATE);
1187 8 : VerifyOrReturnError(mLocalMRPConfig.HasValue(), CHIP_ERROR_INCORRECT_STATE);
1188 8 : VerifyOrReturnError(GetLocalSessionId().HasValue(), CHIP_ERROR_INCORRECT_STATE);
1189 8 : outSigma2Data.responderSessionId = GetLocalSessionId().Value();
1190 :
1191 8 : chip::Platform::ScopedMemoryBuffer<uint8_t> icacBuf;
1192 8 : VerifyOrReturnError(icacBuf.Alloc(kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
1193 :
1194 8 : chip::Platform::ScopedMemoryBuffer<uint8_t> nocBuf;
1195 8 : VerifyOrReturnError(nocBuf.Alloc(kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
1196 :
1197 8 : MutableByteSpan icaCert{ icacBuf.Get(), kMaxCHIPCertLength };
1198 8 : ReturnErrorOnFailure(mFabricsTable->FetchICACert(mFabricIndex, icaCert));
1199 :
1200 8 : MutableByteSpan nocCert{ nocBuf.Get(), kMaxCHIPCertLength };
1201 8 : ReturnErrorOnFailure(mFabricsTable->FetchNOCCert(mFabricIndex, nocCert));
1202 :
1203 : // Fill in the random value
1204 8 : ReturnErrorOnFailure(DRBG_get_bytes(&outSigma2Data.responderRandom[0], sizeof(outSigma2Data.responderRandom)));
1205 :
1206 : // Generate an ephemeral keypair
1207 8 : mEphemeralKey = mFabricsTable->AllocateEphemeralKeypairForCASE();
1208 8 : VerifyOrReturnError(mEphemeralKey != nullptr, CHIP_ERROR_NO_MEMORY);
1209 8 : ReturnErrorOnFailure(mEphemeralKey->Initialize(ECPKeyTarget::ECDH));
1210 8 : outSigma2Data.responderEphPubKey = &mEphemeralKey->Pubkey();
1211 :
1212 : // Generate a Shared Secret
1213 8 : ReturnErrorOnFailure(mEphemeralKey->ECDH_derive_secret(mRemotePubKey, mSharedSecret));
1214 :
1215 : uint8_t msgSalt[kIPKSize + kSigmaParamRandomNumberSize + kP256_PublicKey_Length + kSHA256_Hash_Length];
1216 :
1217 8 : MutableByteSpan saltSpan(msgSalt);
1218 8 : ReturnErrorOnFailure(
1219 : ConstructSaltSigma2(ByteSpan(outSigma2Data.responderRandom), mEphemeralKey->Pubkey(), ByteSpan(mIPK), saltSpan));
1220 :
1221 8 : AutoReleaseSessionKey sr2k(*mSessionManager->GetSessionKeystore());
1222 8 : ReturnErrorOnFailure(DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR2Info), sr2k));
1223 :
1224 : // Construct Sigma2 TBS Data
1225 8 : P256ECDSASignature tbsData2Signature;
1226 : {
1227 8 : size_t msgR2SignedLen = EstimateStructOverhead(kMaxCHIPCertLength, // responderNoc
1228 : kMaxCHIPCertLength, // responderICAC
1229 : kP256_PublicKey_Length, // responderEphPubKey
1230 : kP256_PublicKey_Length // InitiatorEphPubKey
1231 : );
1232 :
1233 8 : chip::Platform::ScopedMemoryBuffer<uint8_t> msgR2Signed;
1234 8 : VerifyOrReturnError(msgR2Signed.Alloc(msgR2SignedLen), CHIP_ERROR_NO_MEMORY);
1235 8 : MutableByteSpan msgR2SignedSpan{ msgR2Signed.Get(), msgR2SignedLen };
1236 :
1237 8 : ReturnErrorOnFailure(ConstructTBSData(nocCert, icaCert, ByteSpan(mEphemeralKey->Pubkey(), mEphemeralKey->Pubkey().Length()),
1238 : ByteSpan(mRemotePubKey, mRemotePubKey.Length()), msgR2SignedSpan));
1239 :
1240 : // Generate a Signature
1241 8 : ReturnErrorOnFailure(mFabricsTable->SignWithOpKeypair(mFabricIndex, msgR2SignedSpan, tbsData2Signature));
1242 8 : }
1243 : // Construct Sigma2 TBE Data
1244 8 : size_t msgR2SignedEncLen = EstimateStructOverhead(nocCert.size(), // responderNoc
1245 : icaCert.size(), // responderICAC
1246 : tbsData2Signature.Length(), // signature
1247 : SessionResumptionStorage::kResumptionIdSize // resumptionID
1248 : );
1249 :
1250 8 : VerifyOrReturnError(outSigma2Data.msgR2Encrypted.Alloc(msgR2SignedEncLen + CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES),
1251 : CHIP_ERROR_NO_MEMORY);
1252 :
1253 8 : TLVWriter tlvWriter;
1254 8 : tlvWriter.Init(outSigma2Data.msgR2Encrypted.Get(), msgR2SignedEncLen);
1255 :
1256 8 : TLVType outerContainerType = kTLVType_NotSpecified;
1257 :
1258 8 : ReturnErrorOnFailure(tlvWriter.StartContainer(AnonymousTag(), kTLVType_Structure, outerContainerType));
1259 8 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(TBEDataTags::kSenderNOC), nocCert));
1260 8 : if (!icaCert.empty())
1261 : {
1262 8 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(TBEDataTags::kSenderICAC), icaCert));
1263 : }
1264 :
1265 : // We are now done with ICAC and NOC certs so we can release the memory.
1266 : {
1267 8 : icacBuf.Free();
1268 8 : icaCert = MutableByteSpan{};
1269 :
1270 8 : nocBuf.Free();
1271 8 : nocCert = MutableByteSpan{};
1272 : }
1273 :
1274 8 : ReturnErrorOnFailure(tlvWriter.PutBytes(AsTlvContextTag(TBEDataTags::kSignature), tbsData2Signature.ConstBytes(),
1275 : static_cast<uint32_t>(tbsData2Signature.Length())));
1276 :
1277 : // Generate a new resumption ID
1278 8 : ReturnErrorOnFailure(DRBG_get_bytes(mNewResumptionId.data(), mNewResumptionId.size()));
1279 8 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(TBEDataTags::kResumptionID), mNewResumptionId));
1280 :
1281 8 : ReturnErrorOnFailure(tlvWriter.EndContainer(outerContainerType));
1282 8 : ReturnErrorOnFailure(tlvWriter.Finalize());
1283 8 : msgR2SignedEncLen = static_cast<size_t>(tlvWriter.GetLengthWritten());
1284 8 : outSigma2Data.encrypted2Length = msgR2SignedEncLen + CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES;
1285 : // Generate the encrypted data blob
1286 8 : ReturnErrorOnFailure(AES_CCM_encrypt(outSigma2Data.msgR2Encrypted.Get(), msgR2SignedEncLen, nullptr, 0, sr2k.KeyHandle(),
1287 : kTBEData2_Nonce, kTBEDataNonceLength, outSigma2Data.msgR2Encrypted.Get(),
1288 : outSigma2Data.msgR2Encrypted.Get() + msgR2SignedEncLen,
1289 : CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES));
1290 :
1291 8 : outSigma2Data.responderMrpConfig = &mLocalMRPConfig.Value();
1292 :
1293 8 : return CHIP_NO_ERROR;
1294 8 : }
1295 :
1296 17 : CHIP_ERROR CASESession::EncodeSigma2(System::PacketBufferHandle & msgR2, EncodeSigma2Inputs & input)
1297 : {
1298 17 : VerifyOrReturnError(input.responderEphPubKey != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
1299 16 : VerifyOrReturnError(input.msgR2Encrypted, CHIP_ERROR_INCORRECT_STATE);
1300 : // Check if length of msgR2Encrypted is set and is at least larger than the MIC length
1301 15 : VerifyOrReturnError(input.encrypted2Length > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, CHIP_ERROR_INCORRECT_STATE);
1302 14 : VerifyOrReturnError(input.responderMrpConfig != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
1303 :
1304 13 : size_t dataLen = EstimateStructOverhead(kSigmaParamRandomNumberSize, // responderRandom
1305 : sizeof(uint16_t), // responderSessionId
1306 : kP256_PublicKey_Length, // responderEphPubKey
1307 : input.encrypted2Length, // encrypted2
1308 : SessionParameters::kEstimatedTLVSize // responderSessionParams
1309 : );
1310 :
1311 13 : msgR2 = System::PacketBufferHandle::New(dataLen);
1312 13 : VerifyOrReturnError(!msgR2.IsNull(), CHIP_ERROR_NO_MEMORY);
1313 :
1314 13 : System::PacketBufferTLVWriter tlvWriterMsg2;
1315 13 : tlvWriterMsg2.Init(std::move(msgR2));
1316 :
1317 13 : TLVType outerContainerType = kTLVType_NotSpecified;
1318 :
1319 13 : ReturnErrorOnFailure(tlvWriterMsg2.StartContainer(AnonymousTag(), kTLVType_Structure, outerContainerType));
1320 :
1321 13 : ReturnErrorOnFailure(tlvWriterMsg2.PutBytes(AsTlvContextTag(Sigma2Tags::kResponderRandom), &input.responderRandom[0],
1322 : sizeof(input.responderRandom)));
1323 13 : ReturnErrorOnFailure(tlvWriterMsg2.Put(AsTlvContextTag(Sigma2Tags::kResponderSessionId), input.responderSessionId));
1324 :
1325 13 : ReturnErrorOnFailure(tlvWriterMsg2.PutBytes(AsTlvContextTag(Sigma2Tags::kResponderEphPubKey), *input.responderEphPubKey,
1326 : static_cast<uint32_t>(input.responderEphPubKey->Length())));
1327 :
1328 13 : ReturnErrorOnFailure(tlvWriterMsg2.PutBytes(AsTlvContextTag(Sigma2Tags::kEncrypted2), input.msgR2Encrypted.Get(),
1329 : static_cast<uint32_t>(input.encrypted2Length)));
1330 13 : input.msgR2Encrypted.Free();
1331 :
1332 13 : ReturnErrorOnFailure(
1333 : EncodeSessionParameters(AsTlvContextTag(Sigma2Tags::kResponderSessionParams), *input.responderMrpConfig, tlvWriterMsg2));
1334 :
1335 13 : ReturnErrorOnFailure(tlvWriterMsg2.EndContainer(outerContainerType));
1336 13 : ReturnErrorOnFailure(tlvWriterMsg2.Finalize(&msgR2));
1337 :
1338 13 : return CHIP_NO_ERROR;
1339 13 : }
1340 :
1341 8 : CHIP_ERROR CASESession::SendSigma2(System::PacketBufferHandle && msgR2)
1342 : {
1343 : MATTER_TRACE_SCOPE("SendSigma2", "CASESession");
1344 :
1345 8 : ReturnErrorOnFailure(mCommissioningHash.AddData(ByteSpan{ msgR2->Start(), msgR2->DataLength() }));
1346 :
1347 : // Call delegate to send the msg to peer
1348 8 : ReturnErrorOnFailure(mExchangeCtxt.Value()->SendMessage(Protocols::SecureChannel::MsgType::CASE_Sigma2, std::move(msgR2),
1349 : SendFlags(SendMessageFlags::kExpectResponse)));
1350 :
1351 8 : mState = State::kSentSigma2;
1352 :
1353 8 : ChipLogProgress(SecureChannel, "Sent Sigma2 msg");
1354 : MATTER_TRACE_COUNTER("Sigma2");
1355 :
1356 8 : return CHIP_NO_ERROR;
1357 : }
1358 :
1359 1 : CHIP_ERROR CASESession::HandleSigma2Resume(System::PacketBufferHandle && msg)
1360 : {
1361 : MATTER_TRACE_SCOPE("HandleSigma2Resume", "CASESession");
1362 1 : CHIP_ERROR err = CHIP_NO_ERROR;
1363 :
1364 1 : ChipLogDetail(SecureChannel, "Received Sigma2Resume msg");
1365 : MATTER_TRACE_COUNTER("Sigma2Resume");
1366 : MATTER_LOG_METRIC_END(kMetricDeviceCASESessionSigma1, err);
1367 :
1368 1 : System::PacketBufferTLVReader tlvReader;
1369 1 : tlvReader.Init(std::move(msg));
1370 1 : ParsedSigma2Resume parsedSigma2Resume;
1371 1 : SuccessOrExit(err = ParseSigma2Resume(tlvReader, parsedSigma2Resume));
1372 :
1373 1 : SuccessOrExit(err = ValidateSigmaResumeMIC(parsedSigma2Resume.sigma2ResumeMIC, ByteSpan(mInitiatorRandom),
1374 : parsedSigma2Resume.resumptionId, ByteSpan(kKDFS2RKeyInfo),
1375 : ByteSpan(kResume2MIC_Nonce)));
1376 :
1377 1 : if (parsedSigma2Resume.responderSessionParamStructPresent)
1378 : {
1379 1 : SetRemoteSessionParameters(parsedSigma2Resume.responderSessionParams);
1380 1 : mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters(
1381 : GetRemoteSessionParameters());
1382 : }
1383 :
1384 1 : ChipLogDetail(SecureChannel, "Peer " ChipLogFormatScopedNodeId " assigned session ID %d", ChipLogValueScopedNodeId(GetPeer()),
1385 : parsedSigma2Resume.responderSessionId);
1386 1 : SetPeerSessionId(parsedSigma2Resume.responderSessionId);
1387 :
1388 1 : if (mSessionResumptionStorage != nullptr)
1389 : {
1390 2 : CHIP_ERROR err2 = mSessionResumptionStorage->Save(
1391 1 : GetPeer(), SessionResumptionStorage::ConstResumptionIdView(parsedSigma2Resume.resumptionId.data()), mSharedSecret,
1392 1 : mPeerCATs);
1393 1 : if (err2 != CHIP_NO_ERROR)
1394 0 : ChipLogError(SecureChannel, "Unable to save session resumption state: %" CHIP_ERROR_FORMAT, err2.Format());
1395 : }
1396 :
1397 : MATTER_LOG_METRIC(kMetricDeviceCASESessionSigmaFinished);
1398 1 : SendStatusReport(mExchangeCtxt, kProtocolCodeSuccess);
1399 :
1400 1 : mState = State::kFinishedViaResume;
1401 1 : Finish();
1402 :
1403 1 : exit:
1404 1 : if (err != CHIP_NO_ERROR)
1405 : {
1406 0 : SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam);
1407 : }
1408 1 : return err;
1409 1 : }
1410 :
1411 13 : CHIP_ERROR CASESession::ParseSigma2Resume(ContiguousBufferTLVReader & tlvReader, ParsedSigma2Resume & outParsedSigma2Resume)
1412 : {
1413 13 : TLVType containerType = kTLVType_Structure;
1414 :
1415 13 : ReturnErrorOnFailure(tlvReader.Next(containerType, AnonymousTag()));
1416 13 : ReturnErrorOnFailure(tlvReader.EnterContainer(containerType));
1417 :
1418 13 : ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2ResumeTags::kResumptionID)));
1419 12 : ReturnErrorOnFailure(tlvReader.GetByteView(outParsedSigma2Resume.resumptionId));
1420 12 : VerifyOrReturnError(outParsedSigma2Resume.resumptionId.size() == SessionResumptionStorage::kResumptionIdSize,
1421 : CHIP_ERROR_INVALID_CASE_PARAMETER);
1422 :
1423 10 : ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2ResumeTags::kSigma2ResumeMIC)));
1424 10 : ReturnErrorOnFailure(tlvReader.GetByteView(outParsedSigma2Resume.sigma2ResumeMIC));
1425 10 : VerifyOrReturnError(outParsedSigma2Resume.sigma2ResumeMIC.size() == CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES,
1426 : CHIP_ERROR_INVALID_CASE_PARAMETER);
1427 :
1428 8 : ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2ResumeTags::kResponderSessionID)));
1429 8 : ReturnErrorOnFailure(tlvReader.Get(outParsedSigma2Resume.responderSessionId));
1430 :
1431 7 : CHIP_ERROR err = tlvReader.Next();
1432 7 : if (err == CHIP_NO_ERROR && tlvReader.GetTag() == AsTlvContextTag(Sigma2ResumeTags::kResponderSessionParams))
1433 : {
1434 2 : ReturnErrorOnFailure(DecodeSessionParametersIfPresent(AsTlvContextTag(Sigma2ResumeTags::kResponderSessionParams), tlvReader,
1435 : outParsedSigma2Resume.responderSessionParams));
1436 2 : outParsedSigma2Resume.responderSessionParamStructPresent = true;
1437 :
1438 2 : err = tlvReader.Next();
1439 : }
1440 :
1441 : // Future-proofing: CHIP_NO_ERROR will be returned by Next() if we have additional non-parsed TLV Elements, which could
1442 : // happen in the future if additional elements are added to the specification.
1443 7 : VerifyOrReturnError(err == CHIP_END_OF_TLV || err == CHIP_NO_ERROR, err);
1444 : // Exit Container will fail (return CHIP_END_OF_TLV) if the received encoded message is not properly terminated with an
1445 : // EndOfContainer TLV Element.
1446 7 : ReturnErrorOnFailure(tlvReader.ExitContainer(containerType));
1447 :
1448 5 : return CHIP_NO_ERROR;
1449 : }
1450 :
1451 7 : CHIP_ERROR CASESession::HandleSigma2_and_SendSigma3(System::PacketBufferHandle && msg)
1452 : {
1453 : MATTER_TRACE_SCOPE("HandleSigma2_and_SendSigma3", "CASESession");
1454 7 : CHIP_ERROR err = HandleSigma2(std::move(msg));
1455 : MATTER_LOG_METRIC_END(kMetricDeviceCASESessionSigma1, err);
1456 7 : SuccessOrExit(err);
1457 :
1458 : MATTER_LOG_METRIC_BEGIN(kMetricDeviceCASESessionSigma3);
1459 7 : err = SendSigma3a();
1460 7 : if (CHIP_NO_ERROR != err)
1461 : {
1462 : MATTER_LOG_METRIC_END(kMetricDeviceCASESessionSigma3, err);
1463 : }
1464 :
1465 7 : exit:
1466 7 : if (CHIP_NO_ERROR != err)
1467 : {
1468 0 : SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam);
1469 0 : mState = State::kInitialized;
1470 : }
1471 7 : return err;
1472 : }
1473 :
1474 7 : CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg)
1475 : {
1476 : MATTER_TRACE_SCOPE("HandleSigma2", "CASESession");
1477 7 : ChipLogProgress(SecureChannel, "Received Sigma2 msg");
1478 :
1479 7 : VerifyOrReturnError(mEphemeralKey != nullptr, CHIP_ERROR_INTERNAL);
1480 :
1481 7 : const uint8_t * buf = msg->Start();
1482 7 : size_t buflen = msg->DataLength();
1483 7 : VerifyOrReturnError(buf != nullptr, CHIP_ERROR_MESSAGE_INCOMPLETE);
1484 :
1485 7 : FabricId fabricId = kUndefinedFabricId;
1486 : {
1487 7 : VerifyOrReturnError(mFabricsTable != nullptr, CHIP_ERROR_INCORRECT_STATE);
1488 7 : const auto * fabricInfo = mFabricsTable->FindFabricWithIndex(mFabricIndex);
1489 7 : VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_INCORRECT_STATE);
1490 7 : fabricId = fabricInfo->GetFabricId();
1491 : }
1492 :
1493 7 : System::PacketBufferTLVReader tlvReader;
1494 7 : tlvReader.Init(std::move(msg));
1495 7 : ParsedSigma2 parsedSigma2;
1496 7 : ReturnErrorOnFailure(ParseSigma2(tlvReader, parsedSigma2));
1497 :
1498 : // ParseSigma2 ensures that:
1499 : // mRemotePubKey.Length() == responderEphPubKey.size() == kP256_PublicKey_Length.
1500 7 : memcpy(mRemotePubKey.Bytes(), parsedSigma2.responderEphPubKey.data(), mRemotePubKey.Length());
1501 :
1502 : // Generate a Shared Secret
1503 7 : ReturnErrorOnFailure(mEphemeralKey->ECDH_derive_secret(mRemotePubKey, mSharedSecret));
1504 :
1505 : // Generate the S2K key
1506 7 : AutoReleaseSessionKey sr2k(*mSessionManager->GetSessionKeystore());
1507 : {
1508 : uint8_t msg_salt[kIPKSize + kSigmaParamRandomNumberSize + kP256_PublicKey_Length + kSHA256_Hash_Length];
1509 7 : MutableByteSpan saltSpan(msg_salt);
1510 7 : ReturnErrorOnFailure(ConstructSaltSigma2(parsedSigma2.responderRandom, mRemotePubKey, ByteSpan(mIPK), saltSpan));
1511 7 : ReturnErrorOnFailure(DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR2Info), sr2k));
1512 : }
1513 : // Msg2 should only be added to MessageDigest after we construct SaltSigma2 that is used to derive S2K,
1514 : // Because constructing SaltSigma2 uses the MessageDigest at a point when it should only include Msg1.
1515 7 : ReturnErrorOnFailure(mCommissioningHash.AddData(ByteSpan{ buf, buflen }));
1516 :
1517 7 : ReturnErrorOnFailure(AES_CCM_decrypt(parsedSigma2.msgR2EncryptedPayload.data(), parsedSigma2.msgR2EncryptedPayload.size(),
1518 : nullptr, 0, parsedSigma2.msgR2MIC.data(), parsedSigma2.msgR2MIC.size(), sr2k.KeyHandle(),
1519 : kTBEData2_Nonce, kTBEDataNonceLength, parsedSigma2.msgR2EncryptedPayload.data()));
1520 :
1521 7 : parsedSigma2.msgR2Decrypted = std::move(parsedSigma2.msgR2Encrypted);
1522 7 : size_t msgR2DecryptedLength = parsedSigma2.msgR2EncryptedPayload.size();
1523 :
1524 7 : ContiguousBufferTLVReader decryptedDataTlvReader;
1525 7 : decryptedDataTlvReader.Init(parsedSigma2.msgR2Decrypted.Get(), msgR2DecryptedLength);
1526 7 : ParsedSigma2TBEData parsedSigma2TBEData;
1527 7 : ReturnErrorOnFailure(ParseSigma2TBEData(decryptedDataTlvReader, parsedSigma2TBEData));
1528 :
1529 : // Validate responder identity located in msgR2Decrypted
1530 : // Constructing responder identity
1531 7 : P256PublicKey responderPublicKey;
1532 : {
1533 : NodeId responderNodeId;
1534 :
1535 : CompressedFabricId unused;
1536 : FabricId responderFabricId;
1537 7 : ReturnErrorOnFailure(SetEffectiveTime());
1538 7 : ReturnErrorOnFailure(mFabricsTable->VerifyCredentials(mFabricIndex, parsedSigma2TBEData.responderNOC,
1539 : parsedSigma2TBEData.responderICAC, mValidContext, unused,
1540 : responderFabricId, responderNodeId, responderPublicKey));
1541 7 : VerifyOrReturnError(fabricId == responderFabricId, CHIP_ERROR_INVALID_CASE_PARAMETER);
1542 : // Verify that responderNodeId (from responderNOC) matches one that was included
1543 : // in the computation of the Destination Identifier when generating Sigma1.
1544 7 : VerifyOrReturnError(mPeerNodeId == responderNodeId, CHIP_ERROR_INVALID_CASE_PARAMETER);
1545 : }
1546 :
1547 : // Construct msgR2Signed and validate the signature in msgR2Decrypted.
1548 7 : size_t msgR2SignedLen = EstimateStructOverhead(parsedSigma2TBEData.responderNOC.size(), // resonderNOC
1549 : parsedSigma2TBEData.responderICAC.size(), // responderICAC
1550 : kP256_PublicKey_Length, // responderEphPubKey
1551 : kP256_PublicKey_Length // initiatorEphPubKey
1552 : );
1553 :
1554 7 : chip::Platform::ScopedMemoryBuffer<uint8_t> msgR2Signed;
1555 7 : VerifyOrReturnError(msgR2Signed.Alloc(msgR2SignedLen), CHIP_ERROR_NO_MEMORY);
1556 7 : MutableByteSpan msgR2SignedSpan{ msgR2Signed.Get(), msgR2SignedLen };
1557 :
1558 7 : ReturnErrorOnFailure(ConstructTBSData(parsedSigma2TBEData.responderNOC, parsedSigma2TBEData.responderICAC,
1559 : ByteSpan(mRemotePubKey, mRemotePubKey.Length()),
1560 : ByteSpan(mEphemeralKey->Pubkey(), mEphemeralKey->Pubkey().Length()), msgR2SignedSpan));
1561 :
1562 : // Validate signature
1563 7 : ReturnErrorOnFailure(responderPublicKey.ECDSA_validate_msg_signature(msgR2SignedSpan.data(), msgR2SignedSpan.size(),
1564 : parsedSigma2TBEData.tbsData2Signature));
1565 :
1566 7 : ChipLogDetail(SecureChannel, "Peer " ChipLogFormatScopedNodeId " assigned session ID %d", ChipLogValueScopedNodeId(GetPeer()),
1567 : parsedSigma2.responderSessionId);
1568 7 : SetPeerSessionId(parsedSigma2.responderSessionId);
1569 :
1570 7 : std::copy(parsedSigma2TBEData.resumptionId.begin(), parsedSigma2TBEData.resumptionId.end(), mNewResumptionId.begin());
1571 :
1572 : // Retrieve peer CASE Authenticated Tags (CATs) from peer's NOC.
1573 7 : ReturnErrorOnFailure(ExtractCATsFromOpCert(parsedSigma2TBEData.responderNOC, mPeerCATs));
1574 :
1575 7 : if (parsedSigma2.responderSessionParamStructPresent)
1576 : {
1577 7 : SetRemoteSessionParameters(parsedSigma2.responderSessionParams);
1578 7 : mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters(
1579 : GetRemoteSessionParameters());
1580 : }
1581 :
1582 7 : return CHIP_NO_ERROR;
1583 7 : }
1584 :
1585 21 : CHIP_ERROR CASESession::ParseSigma2(ContiguousBufferTLVReader & tlvReader, ParsedSigma2 & outParsedSigma2)
1586 : {
1587 21 : TLVType containerType = kTLVType_Structure;
1588 :
1589 21 : ReturnErrorOnFailure(tlvReader.Next(containerType, AnonymousTag()));
1590 21 : ReturnErrorOnFailure(tlvReader.EnterContainer(containerType));
1591 :
1592 : // Retrieve Responder's Random value
1593 21 : ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2Tags::kResponderRandom)));
1594 20 : ReturnErrorOnFailure(tlvReader.GetByteView(outParsedSigma2.responderRandom));
1595 20 : VerifyOrReturnError(outParsedSigma2.responderRandom.size() == kSigmaParamRandomNumberSize, CHIP_ERROR_INVALID_CASE_PARAMETER);
1596 :
1597 : // Assign Session ID
1598 18 : ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2Tags::kResponderSessionId)));
1599 18 : ReturnErrorOnFailure(tlvReader.Get(outParsedSigma2.responderSessionId));
1600 :
1601 : // Retrieve Responder's Ephemeral Pubkey
1602 17 : ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2Tags::kResponderEphPubKey)));
1603 17 : ReturnErrorOnFailure(tlvReader.GetByteView(outParsedSigma2.responderEphPubKey));
1604 17 : VerifyOrReturnError(outParsedSigma2.responderEphPubKey.size() == kP256_PublicKey_Length, CHIP_ERROR_INVALID_CASE_PARAMETER);
1605 :
1606 : // Generate decrypted data
1607 15 : ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2Tags::kEncrypted2)));
1608 :
1609 15 : size_t maxMsgR2SignedEncLen = EstimateStructOverhead(kMaxCHIPCertLength, // responderNOC
1610 : kMaxCHIPCertLength, // responderICAC
1611 : kMax_ECDSA_Signature_Length, // signature
1612 : SessionResumptionStorage::kResumptionIdSize, // resumptionID
1613 : kCaseOverheadForFutureTBEData // extra bytes for future-proofing
1614 : );
1615 :
1616 15 : size_t msgR2EncryptedLenWithTag = tlvReader.GetLength();
1617 :
1618 : // Validate we did not receive a buffer larger than legal
1619 15 : VerifyOrReturnError(msgR2EncryptedLenWithTag <= maxMsgR2SignedEncLen, CHIP_ERROR_INVALID_TLV_ELEMENT);
1620 14 : VerifyOrReturnError(msgR2EncryptedLenWithTag > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, CHIP_ERROR_INVALID_TLV_ELEMENT);
1621 13 : VerifyOrReturnError(outParsedSigma2.msgR2Encrypted.Alloc(msgR2EncryptedLenWithTag), CHIP_ERROR_NO_MEMORY);
1622 13 : ReturnErrorOnFailure(tlvReader.GetBytes(outParsedSigma2.msgR2Encrypted.Get(), outParsedSigma2.msgR2Encrypted.AllocatedSize()));
1623 :
1624 13 : size_t msgR2EncryptedPayloadLen = msgR2EncryptedLenWithTag - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES;
1625 13 : outParsedSigma2.msgR2EncryptedPayload = MutableByteSpan(outParsedSigma2.msgR2Encrypted.Get(), msgR2EncryptedPayloadLen);
1626 13 : outParsedSigma2.msgR2MIC =
1627 13 : ByteSpan(outParsedSigma2.msgR2Encrypted.Get() + msgR2EncryptedPayloadLen, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES);
1628 :
1629 : // Retrieve responderSessionParams if present
1630 13 : CHIP_ERROR err = tlvReader.Next();
1631 13 : if (err == CHIP_NO_ERROR && tlvReader.GetTag() == AsTlvContextTag(Sigma2Tags::kResponderSessionParams))
1632 : {
1633 8 : ReturnErrorOnFailure(DecodeSessionParametersIfPresent(AsTlvContextTag(Sigma2Tags::kResponderSessionParams), tlvReader,
1634 : outParsedSigma2.responderSessionParams));
1635 8 : outParsedSigma2.responderSessionParamStructPresent = true;
1636 :
1637 8 : err = tlvReader.Next();
1638 : }
1639 :
1640 : // Future-proofing: CHIP_NO_ERROR will be returned by Next() if we have additional non-parsed TLV Elements, which could
1641 : // happen in the future if additional elements are added to the specification.
1642 13 : VerifyOrReturnError(err == CHIP_END_OF_TLV || err == CHIP_NO_ERROR, err);
1643 :
1644 : // Exit Container will fail (return CHIP_END_OF_TLV) if the received encoded message is not properly terminated with an
1645 : // EndOfContainer TLV Element.
1646 13 : ReturnErrorOnFailure(tlvReader.ExitContainer(containerType));
1647 :
1648 11 : return CHIP_NO_ERROR;
1649 : }
1650 :
1651 18 : CHIP_ERROR CASESession::ParseSigma2TBEData(ContiguousBufferTLVReader & decryptedDataTlvReader,
1652 : ParsedSigma2TBEData & outParsedSigma2TBE)
1653 : {
1654 18 : TLVType containerType = kTLVType_Structure;
1655 :
1656 18 : ReturnErrorOnFailure(decryptedDataTlvReader.Next(containerType, AnonymousTag()));
1657 18 : ReturnErrorOnFailure(decryptedDataTlvReader.EnterContainer(containerType));
1658 :
1659 18 : ReturnErrorOnFailure(decryptedDataTlvReader.Next(AsTlvContextTag(TBEDataTags::kSenderNOC)));
1660 17 : ReturnErrorOnFailure(decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.responderNOC));
1661 17 : VerifyOrReturnError(outParsedSigma2TBE.responderNOC.size() <= kMaxCHIPCertLength, CHIP_ERROR_INVALID_CASE_PARAMETER);
1662 :
1663 16 : ReturnErrorOnFailure(decryptedDataTlvReader.Next());
1664 16 : if (decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSenderICAC))
1665 : {
1666 16 : ReturnErrorOnFailure(decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.responderICAC));
1667 16 : VerifyOrReturnError(outParsedSigma2TBE.responderICAC.size() <= kMaxCHIPCertLength, CHIP_ERROR_INVALID_CASE_PARAMETER);
1668 :
1669 15 : ReturnErrorOnFailure(decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSignature)));
1670 : }
1671 :
1672 15 : VerifyOrReturnError(decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSignature), CHIP_ERROR_INVALID_TLV_TAG);
1673 : // tbsData2Signature's length should equal kMax_ECDSA_Signature_Length as per the Specification
1674 15 : size_t signatureLen = decryptedDataTlvReader.GetLength();
1675 15 : VerifyOrReturnError(outParsedSigma2TBE.tbsData2Signature.Capacity() == signatureLen, CHIP_ERROR_INVALID_TLV_ELEMENT);
1676 13 : outParsedSigma2TBE.tbsData2Signature.SetLength(signatureLen);
1677 13 : ReturnErrorOnFailure(decryptedDataTlvReader.GetBytes(outParsedSigma2TBE.tbsData2Signature.Bytes(),
1678 : outParsedSigma2TBE.tbsData2Signature.Length()));
1679 :
1680 : // Retrieve session resumption ID
1681 13 : ReturnErrorOnFailure(decryptedDataTlvReader.Next(AsTlvContextTag(TBEDataTags::kResumptionID)));
1682 13 : ReturnErrorOnFailure(decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.resumptionId));
1683 13 : VerifyOrReturnError(outParsedSigma2TBE.resumptionId.size() == SessionResumptionStorage::kResumptionIdSize,
1684 : CHIP_ERROR_INVALID_CASE_PARAMETER);
1685 :
1686 : // Exit Container will fail (return CHIP_END_OF_TLV) if the received encoded message is not properly terminated with an
1687 : // EndOfContainer TLV Element.
1688 11 : ReturnErrorOnFailure(decryptedDataTlvReader.ExitContainer(containerType));
1689 :
1690 9 : return CHIP_NO_ERROR;
1691 : }
1692 :
1693 7 : CHIP_ERROR CASESession::SendSigma3a()
1694 : {
1695 : MATTER_TRACE_SCOPE("SendSigma3", "CASESession");
1696 :
1697 7 : ChipLogDetail(SecureChannel, "Sending Sigma3");
1698 :
1699 7 : auto helper = WorkHelper<SendSigma3Data>::Create(*this, &SendSigma3b, &CASESession::SendSigma3c);
1700 7 : VerifyOrReturnError(helper, CHIP_ERROR_NO_MEMORY);
1701 : {
1702 7 : auto & data = helper->mData;
1703 :
1704 7 : VerifyOrReturnError(mFabricsTable != nullptr, CHIP_ERROR_INCORRECT_STATE);
1705 7 : data.fabricIndex = mFabricIndex;
1706 7 : data.fabricTable = nullptr;
1707 7 : data.keystore = nullptr;
1708 :
1709 : {
1710 7 : const FabricInfo * fabricInfo = mFabricsTable->FindFabricWithIndex(mFabricIndex);
1711 7 : VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
1712 7 : auto * keystore = mFabricsTable->GetOperationalKeystore();
1713 7 : if (!fabricInfo->HasOperationalKey() && keystore != nullptr && keystore->SupportsSignWithOpKeypairInBackground())
1714 : {
1715 : // NOTE: used to sign in background.
1716 0 : data.keystore = keystore;
1717 : }
1718 : else
1719 : {
1720 : // NOTE: used to sign in foreground.
1721 7 : data.fabricTable = mFabricsTable;
1722 : }
1723 : }
1724 :
1725 7 : VerifyOrReturnError(mEphemeralKey != nullptr, CHIP_ERROR_INTERNAL);
1726 :
1727 7 : VerifyOrReturnError(data.icacBuf.Alloc(kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
1728 7 : data.icaCert = MutableByteSpan{ data.icacBuf.Get(), kMaxCHIPCertLength };
1729 :
1730 7 : VerifyOrReturnError(data.nocBuf.Alloc(kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
1731 7 : data.nocCert = MutableByteSpan{ data.nocBuf.Get(), kMaxCHIPCertLength };
1732 :
1733 7 : ReturnErrorOnFailure(mFabricsTable->FetchICACert(mFabricIndex, data.icaCert));
1734 7 : ReturnErrorOnFailure(mFabricsTable->FetchNOCCert(mFabricIndex, data.nocCert));
1735 :
1736 : // Prepare Sigma3 TBS Data Blob
1737 7 : size_t msgR3SignedLen = EstimateStructOverhead(data.nocCert.size(), // initiatorNOC
1738 : data.icaCert.size(), // initiatorICAC
1739 : kP256_PublicKey_Length, // initiatorEphPubKey
1740 : kP256_PublicKey_Length // responderEphPubKey
1741 : );
1742 :
1743 7 : VerifyOrReturnError(data.msgR3Signed.Alloc(msgR3SignedLen), CHIP_ERROR_NO_MEMORY);
1744 7 : data.msgR3SignedSpan = MutableByteSpan{ data.msgR3Signed.Get(), msgR3SignedLen };
1745 :
1746 7 : ReturnErrorOnFailure(ConstructTBSData(data.nocCert, data.icaCert,
1747 : ByteSpan(mEphemeralKey->Pubkey(), mEphemeralKey->Pubkey().Length()),
1748 : ByteSpan(mRemotePubKey, mRemotePubKey.Length()), data.msgR3SignedSpan));
1749 :
1750 7 : if (data.keystore != nullptr)
1751 : {
1752 0 : ReturnErrorOnFailure(helper->ScheduleWork());
1753 0 : mSendSigma3Helper = helper;
1754 0 : mExchangeCtxt.Value()->WillSendMessage();
1755 0 : mState = State::kSendSigma3Pending;
1756 : }
1757 : else
1758 : {
1759 7 : ReturnErrorOnFailure(helper->DoWork());
1760 : }
1761 : }
1762 :
1763 7 : return CHIP_NO_ERROR;
1764 7 : }
1765 :
1766 7 : CHIP_ERROR CASESession::SendSigma3b(SendSigma3Data & data, bool & cancel)
1767 : {
1768 : // Generate a signature
1769 7 : if (data.keystore != nullptr)
1770 : {
1771 : // Recommended case: delegate to operational keystore
1772 0 : ReturnErrorOnFailure(data.keystore->SignWithOpKeypair(data.fabricIndex, data.msgR3SignedSpan, data.tbsData3Signature));
1773 : }
1774 : else
1775 : {
1776 : // Legacy case: delegate to fabric table fabric info
1777 7 : ReturnErrorOnFailure(data.fabricTable->SignWithOpKeypair(data.fabricIndex, data.msgR3SignedSpan, data.tbsData3Signature));
1778 : }
1779 :
1780 : // Prepare Sigma3 TBE Data Blob
1781 7 : data.msg_r3_encrypted_len =
1782 7 : TLV::EstimateStructOverhead(data.nocCert.size(), data.icaCert.size(), data.tbsData3Signature.Length());
1783 :
1784 7 : VerifyOrReturnError(data.msg_R3_Encrypted.Alloc(data.msg_r3_encrypted_len + CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES),
1785 : CHIP_ERROR_NO_MEMORY);
1786 :
1787 : {
1788 7 : TLVWriter tlvWriter;
1789 7 : TLVType outerContainerType = kTLVType_NotSpecified;
1790 :
1791 7 : tlvWriter.Init(data.msg_R3_Encrypted.Get(), data.msg_r3_encrypted_len);
1792 7 : ReturnErrorOnFailure(tlvWriter.StartContainer(AnonymousTag(), kTLVType_Structure, outerContainerType));
1793 7 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(TBEDataTags::kSenderNOC), data.nocCert));
1794 7 : if (!data.icaCert.empty())
1795 : {
1796 7 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(TBEDataTags::kSenderICAC), data.icaCert));
1797 : }
1798 :
1799 : // We are now done with ICAC and NOC certs so we can release the memory.
1800 : {
1801 7 : data.icacBuf.Free();
1802 7 : data.icaCert = MutableByteSpan{};
1803 :
1804 7 : data.nocBuf.Free();
1805 7 : data.nocCert = MutableByteSpan{};
1806 : }
1807 :
1808 7 : ReturnErrorOnFailure(tlvWriter.PutBytes(AsTlvContextTag(TBEDataTags::kSignature), data.tbsData3Signature.ConstBytes(),
1809 : static_cast<uint32_t>(data.tbsData3Signature.Length())));
1810 7 : ReturnErrorOnFailure(tlvWriter.EndContainer(outerContainerType));
1811 7 : ReturnErrorOnFailure(tlvWriter.Finalize());
1812 7 : data.msg_r3_encrypted_len = static_cast<size_t>(tlvWriter.GetLengthWritten());
1813 : }
1814 :
1815 7 : return CHIP_NO_ERROR;
1816 : }
1817 :
1818 7 : CHIP_ERROR CASESession::SendSigma3c(SendSigma3Data & data, CHIP_ERROR status)
1819 : {
1820 7 : CHIP_ERROR err = CHIP_NO_ERROR;
1821 :
1822 7 : System::PacketBufferHandle msg_R3;
1823 : size_t data_len;
1824 :
1825 : uint8_t msg_salt[kIPKSize + kSHA256_Hash_Length];
1826 :
1827 7 : AutoReleaseSessionKey sr3k(*mSessionManager->GetSessionKeystore());
1828 :
1829 7 : VerifyOrDieWithMsg(data.keystore == nullptr || mState == State::kSendSigma3Pending, SecureChannel, "Bad internal state.");
1830 :
1831 7 : SuccessOrExit(err = status);
1832 :
1833 : // Generate S3K key
1834 : {
1835 7 : MutableByteSpan saltSpan(msg_salt);
1836 7 : SuccessOrExit(err = ConstructSaltSigma3(ByteSpan(mIPK), saltSpan));
1837 7 : SuccessOrExit(err = DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR3Info), sr3k));
1838 : }
1839 :
1840 : // Generated Encrypted data blob
1841 7 : SuccessOrExit(err =
1842 : AES_CCM_encrypt(data.msg_R3_Encrypted.Get(), data.msg_r3_encrypted_len, nullptr, 0, sr3k.KeyHandle(),
1843 : kTBEData3_Nonce, kTBEDataNonceLength, data.msg_R3_Encrypted.Get(),
1844 : data.msg_R3_Encrypted.Get() + data.msg_r3_encrypted_len, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES));
1845 :
1846 : // Generate Sigma3 Msg
1847 7 : data_len = TLV::EstimateStructOverhead(CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, data.msg_r3_encrypted_len);
1848 :
1849 7 : msg_R3 = System::PacketBufferHandle::New(data_len);
1850 7 : VerifyOrExit(!msg_R3.IsNull(), err = CHIP_ERROR_NO_MEMORY);
1851 :
1852 : {
1853 7 : System::PacketBufferTLVWriter tlvWriter;
1854 7 : TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified;
1855 :
1856 7 : tlvWriter.Init(std::move(msg_R3));
1857 7 : err = tlvWriter.StartContainer(AnonymousTag(), kTLVType_Structure, outerContainerType);
1858 7 : SuccessOrExit(err);
1859 7 : err = tlvWriter.PutBytes(AsTlvContextTag(Sigma3Tags::kEncrypted3), data.msg_R3_Encrypted.Get(),
1860 7 : static_cast<uint32_t>(data.msg_r3_encrypted_len + CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES));
1861 7 : SuccessOrExit(err);
1862 7 : err = tlvWriter.EndContainer(outerContainerType);
1863 7 : SuccessOrExit(err);
1864 7 : err = tlvWriter.Finalize(&msg_R3);
1865 7 : SuccessOrExit(err);
1866 7 : }
1867 :
1868 7 : err = mCommissioningHash.AddData(ByteSpan{ msg_R3->Start(), msg_R3->DataLength() });
1869 7 : SuccessOrExit(err);
1870 :
1871 : // Call delegate to send the Msg3 to peer
1872 14 : err = mExchangeCtxt.Value()->SendMessage(Protocols::SecureChannel::MsgType::CASE_Sigma3, std::move(msg_R3),
1873 7 : SendFlags(SendMessageFlags::kExpectResponse));
1874 7 : SuccessOrExit(err);
1875 :
1876 7 : ChipLogProgress(SecureChannel, "Sent Sigma3 msg");
1877 :
1878 : {
1879 7 : MutableByteSpan messageDigestSpan(mMessageDigest);
1880 7 : SuccessOrExit(err = mCommissioningHash.Finish(messageDigestSpan));
1881 : }
1882 :
1883 7 : mState = State::kSentSigma3;
1884 :
1885 7 : exit:
1886 7 : mSendSigma3Helper.reset();
1887 :
1888 : // If data.keystore is set, processing occurred in the background, so if an error occurred,
1889 : // need to send status report (normally occurs in SendSigma3a), and discard exchange and
1890 : // abort pending establish (normally occurs in OnMessageReceived).
1891 7 : if (data.keystore != nullptr && err != CHIP_NO_ERROR)
1892 : {
1893 0 : SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam);
1894 0 : DiscardExchange();
1895 0 : AbortPendingEstablish(err);
1896 : }
1897 :
1898 7 : return err;
1899 7 : }
1900 :
1901 7 : CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg)
1902 : {
1903 : MATTER_TRACE_SCOPE("HandleSigma3", "CASESession");
1904 7 : CHIP_ERROR err = CHIP_NO_ERROR;
1905 7 : ContiguousBufferTLVReader decryptedDataTlvReader;
1906 7 : TLVType containerType = kTLVType_Structure;
1907 :
1908 7 : const uint8_t * buf = msg->Start();
1909 7 : const size_t bufLen = msg->DataLength();
1910 :
1911 7 : AutoReleaseSessionKey sr3k(*mSessionManager->GetSessionKeystore());
1912 :
1913 : uint8_t msg_salt[kIPKSize + kSHA256_Hash_Length];
1914 :
1915 7 : ChipLogProgress(SecureChannel, "Received Sigma3 msg");
1916 : MATTER_TRACE_COUNTER("Sigma3");
1917 : MATTER_LOG_METRIC_END(kMetricDeviceCASESessionSigma2, err);
1918 :
1919 7 : auto helper = WorkHelper<HandleSigma3Data>::Create(*this, &HandleSigma3b, &CASESession::HandleSigma3c);
1920 7 : VerifyOrExit(helper, err = CHIP_ERROR_NO_MEMORY);
1921 : {
1922 7 : auto & data = helper->mData;
1923 :
1924 : {
1925 7 : VerifyOrExit(mFabricsTable != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
1926 7 : const auto * fabricInfo = mFabricsTable->FindFabricWithIndex(mFabricIndex);
1927 7 : VerifyOrExit(fabricInfo != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
1928 7 : data.fabricId = fabricInfo->GetFabricId();
1929 : }
1930 :
1931 7 : VerifyOrExit(mEphemeralKey != nullptr, err = CHIP_ERROR_INTERNAL);
1932 :
1933 : // Step 1
1934 : // msgR3Encrypted will be allocated and initialised within ParseSigma3()
1935 7 : Platform::ScopedMemoryBufferWithSize<uint8_t> msgR3Encrypted;
1936 : // both msgR3EncryptedPayload and msgR3MIC will become backed by msgR3Encrypted in ParseSigma3()
1937 7 : MutableByteSpan msgR3EncryptedPayload;
1938 7 : ByteSpan msgR3MIC;
1939 : {
1940 7 : System::PacketBufferTLVReader tlvReader;
1941 7 : tlvReader.Init(std::move(msg));
1942 7 : SuccessOrExit(err = ParseSigma3(tlvReader, msgR3Encrypted, msgR3EncryptedPayload, msgR3MIC));
1943 :
1944 : // Generate the S3K key
1945 7 : MutableByteSpan saltSpan(msg_salt);
1946 7 : SuccessOrExit(err = ConstructSaltSigma3(ByteSpan(mIPK), saltSpan));
1947 7 : SuccessOrExit(err = DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR3Info), sr3k));
1948 :
1949 : // Add Sigma3 to the TranscriptHash which will be used to generate the Session Encryption Keys
1950 7 : SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ buf, bufLen }));
1951 7 : }
1952 : // Step 2 - Decrypt data blob
1953 7 : SuccessOrExit(err = AES_CCM_decrypt(msgR3EncryptedPayload.data(), msgR3EncryptedPayload.size(), nullptr, 0, msgR3MIC.data(),
1954 : msgR3MIC.size(), sr3k.KeyHandle(), kTBEData3_Nonce, kTBEDataNonceLength,
1955 : msgR3EncryptedPayload.data()));
1956 :
1957 7 : decryptedDataTlvReader.Init(msgR3EncryptedPayload.data(), msgR3EncryptedPayload.size());
1958 7 : SuccessOrExit(err = ParseSigma3TBEData(decryptedDataTlvReader, data));
1959 :
1960 : // Step 3 - Construct Sigma3 TBS Data
1961 7 : size_t msgR3SignedLen = TLV::EstimateStructOverhead(data.initiatorNOC.size(), // initiatorNOC
1962 : data.initiatorICAC.size(), // initiatorICAC
1963 : kP256_PublicKey_Length, // initiatorEphPubKey
1964 : kP256_PublicKey_Length // responderEphPubKey
1965 : );
1966 :
1967 7 : VerifyOrExit(data.msgR3Signed.Alloc(msgR3SignedLen), err = CHIP_ERROR_NO_MEMORY);
1968 7 : data.msgR3SignedSpan = MutableByteSpan{ data.msgR3Signed.Get(), msgR3SignedLen };
1969 :
1970 7 : SuccessOrExit(err = ConstructTBSData(data.initiatorNOC, data.initiatorICAC, ByteSpan(mRemotePubKey, mRemotePubKey.Length()),
1971 : ByteSpan(mEphemeralKey->Pubkey(), mEphemeralKey->Pubkey().Length()),
1972 : data.msgR3SignedSpan));
1973 :
1974 : // Prepare for Step 4/5
1975 : {
1976 7 : MutableByteSpan fabricRCAC{ data.rootCertBuf };
1977 7 : SuccessOrExit(err = mFabricsTable->FetchRootCert(mFabricIndex, fabricRCAC));
1978 7 : data.fabricRCAC = fabricRCAC;
1979 : // TODO probably should make SetEffectiveTime static and call closer to VerifyCredentials
1980 7 : SuccessOrExit(err = SetEffectiveTime());
1981 : }
1982 :
1983 : // Copy remaining needed data into work structure
1984 : {
1985 7 : data.validContext = mValidContext;
1986 :
1987 : // initiatorNOC and initiatorICAC are spans into msgR3Encrypted
1988 : // which is going away, so to save memory, redirect them to their
1989 : // copies in msgR3Signed, which is staying around
1990 7 : TLV::ContiguousBufferTLVReader signedDataTlvReader;
1991 7 : signedDataTlvReader.Init(data.msgR3SignedSpan);
1992 7 : SuccessOrExit(err = signedDataTlvReader.Next(containerType, AnonymousTag()));
1993 7 : SuccessOrExit(err = signedDataTlvReader.EnterContainer(containerType));
1994 :
1995 7 : SuccessOrExit(err = signedDataTlvReader.Next(AsTlvContextTag(TBSDataTags::kSenderNOC)));
1996 7 : SuccessOrExit(err = signedDataTlvReader.GetByteView(data.initiatorNOC));
1997 :
1998 7 : if (!data.initiatorICAC.empty())
1999 : {
2000 7 : SuccessOrExit(err = signedDataTlvReader.Next(AsTlvContextTag(TBSDataTags::kSenderICAC)));
2001 7 : SuccessOrExit(err = signedDataTlvReader.GetByteView(data.initiatorICAC));
2002 : }
2003 :
2004 7 : SuccessOrExit(err = signedDataTlvReader.ExitContainer(containerType));
2005 : }
2006 :
2007 7 : SuccessOrExit(err = helper->ScheduleWork());
2008 7 : mHandleSigma3Helper = helper;
2009 7 : mExchangeCtxt.Value()->WillSendMessage();
2010 7 : mState = State::kHandleSigma3Pending;
2011 7 : }
2012 :
2013 7 : exit:
2014 7 : if (err != CHIP_NO_ERROR)
2015 : {
2016 0 : SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam);
2017 : }
2018 :
2019 7 : return err;
2020 7 : }
2021 :
2022 14 : CHIP_ERROR CASESession::ParseSigma3(ContiguousBufferTLVReader & tlvReader,
2023 : Platform::ScopedMemoryBufferWithSize<uint8_t> & outMsgR3Encrypted,
2024 : MutableByteSpan & outMsgR3EncryptedPayload, ByteSpan & outMsgR3MIC)
2025 : {
2026 14 : TLVType containerType = kTLVType_Structure;
2027 :
2028 14 : ReturnErrorOnFailure(tlvReader.Next(containerType, AnonymousTag()));
2029 14 : ReturnErrorOnFailure(tlvReader.EnterContainer(containerType));
2030 :
2031 : // Fetch encrypted data
2032 14 : ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma3Tags::kEncrypted3)));
2033 :
2034 13 : size_t maxMsgR3SignedEncLen = EstimateStructOverhead(kMaxCHIPCertLength, // initiatorNOC
2035 : kMaxCHIPCertLength, // initiatorICAC
2036 : kMax_ECDSA_Signature_Length, // signature
2037 : kCaseOverheadForFutureTBEData // extra bytes for future-proofing
2038 : );
2039 :
2040 13 : size_t msgR3EncryptedLenWithTag = tlvReader.GetLength();
2041 :
2042 : // Validate we did not receive a buffer larger than legal
2043 13 : VerifyOrReturnError(msgR3EncryptedLenWithTag <= maxMsgR3SignedEncLen, CHIP_ERROR_INVALID_TLV_ELEMENT);
2044 12 : VerifyOrReturnError(msgR3EncryptedLenWithTag > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, CHIP_ERROR_INVALID_TLV_ELEMENT);
2045 11 : VerifyOrReturnError(outMsgR3Encrypted.Alloc(msgR3EncryptedLenWithTag), CHIP_ERROR_NO_MEMORY);
2046 11 : ReturnErrorOnFailure(tlvReader.GetBytes(outMsgR3Encrypted.Get(), outMsgR3Encrypted.AllocatedSize()));
2047 :
2048 11 : size_t msgR3EncryptedPayloadLen = msgR3EncryptedLenWithTag - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES;
2049 11 : outMsgR3EncryptedPayload = MutableByteSpan(outMsgR3Encrypted.Get(), msgR3EncryptedPayloadLen);
2050 11 : outMsgR3MIC = ByteSpan(outMsgR3Encrypted.Get() + msgR3EncryptedPayloadLen, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES);
2051 :
2052 11 : ReturnErrorOnFailure(tlvReader.ExitContainer(containerType));
2053 :
2054 9 : return CHIP_NO_ERROR;
2055 : }
2056 :
2057 16 : CHIP_ERROR CASESession::ParseSigma3TBEData(ContiguousBufferTLVReader & decryptedDataTlvReader,
2058 : HandleSigma3Data & outHandleSigma3TBEData)
2059 : {
2060 :
2061 16 : TLVType containerType = kTLVType_Structure;
2062 16 : ReturnErrorOnFailure(decryptedDataTlvReader.Next(containerType, TLV::AnonymousTag()));
2063 16 : ReturnErrorOnFailure(decryptedDataTlvReader.EnterContainer(containerType));
2064 :
2065 16 : ReturnErrorOnFailure(decryptedDataTlvReader.Next(AsTlvContextTag(TBEDataTags::kSenderNOC)));
2066 15 : ReturnErrorOnFailure(decryptedDataTlvReader.GetByteView(outHandleSigma3TBEData.initiatorNOC));
2067 15 : VerifyOrReturnError(outHandleSigma3TBEData.initiatorNOC.size() <= kMaxCHIPCertLength, CHIP_ERROR_INVALID_CASE_PARAMETER);
2068 :
2069 14 : ReturnErrorOnFailure(decryptedDataTlvReader.Next());
2070 14 : if (decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSenderICAC))
2071 : {
2072 14 : ReturnErrorOnFailure(decryptedDataTlvReader.GetByteView(outHandleSigma3TBEData.initiatorICAC));
2073 14 : VerifyOrReturnError(outHandleSigma3TBEData.initiatorICAC.size() <= kMaxCHIPCertLength, CHIP_ERROR_INVALID_CASE_PARAMETER);
2074 13 : ReturnErrorOnFailure(decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSignature)));
2075 : }
2076 :
2077 13 : VerifyOrReturnError(decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSignature), CHIP_ERROR_INVALID_TLV_TAG);
2078 13 : size_t signatureLen = decryptedDataTlvReader.GetLength();
2079 13 : VerifyOrReturnError(outHandleSigma3TBEData.tbsData3Signature.Capacity() == signatureLen, CHIP_ERROR_INVALID_TLV_ELEMENT);
2080 11 : outHandleSigma3TBEData.tbsData3Signature.SetLength(signatureLen);
2081 11 : ReturnErrorOnFailure(decryptedDataTlvReader.GetBytes(outHandleSigma3TBEData.tbsData3Signature.Bytes(),
2082 : outHandleSigma3TBEData.tbsData3Signature.Length()));
2083 :
2084 11 : ReturnErrorOnFailure(decryptedDataTlvReader.ExitContainer(containerType));
2085 :
2086 9 : return CHIP_NO_ERROR;
2087 : }
2088 :
2089 7 : CHIP_ERROR CASESession::HandleSigma3b(HandleSigma3Data & data, bool & cancel)
2090 : {
2091 : // Step 5/6
2092 : // Validate initiator identity located in msg->Start()
2093 : // Constructing responder identity
2094 : CompressedFabricId unused;
2095 : FabricId initiatorFabricId;
2096 7 : P256PublicKey initiatorPublicKey;
2097 7 : ReturnErrorOnFailure(FabricTable::VerifyCredentials(data.initiatorNOC, data.initiatorICAC, data.fabricRCAC, data.validContext,
2098 : unused, initiatorFabricId, data.initiatorNodeId, initiatorPublicKey));
2099 7 : VerifyOrReturnError(data.fabricId == initiatorFabricId, CHIP_ERROR_INVALID_CASE_PARAMETER);
2100 :
2101 : // Step 7 - Validate Signature
2102 7 : ReturnErrorOnFailure(initiatorPublicKey.ECDSA_validate_msg_signature(data.msgR3SignedSpan.data(), data.msgR3SignedSpan.size(),
2103 : data.tbsData3Signature));
2104 :
2105 7 : return CHIP_NO_ERROR;
2106 7 : }
2107 :
2108 7 : CHIP_ERROR CASESession::HandleSigma3c(HandleSigma3Data & data, CHIP_ERROR status)
2109 : {
2110 7 : CHIP_ERROR err = CHIP_NO_ERROR;
2111 :
2112 7 : VerifyOrExit(mState == State::kHandleSigma3Pending, err = CHIP_ERROR_INCORRECT_STATE);
2113 :
2114 7 : SuccessOrExit(err = status);
2115 :
2116 7 : mPeerNodeId = data.initiatorNodeId;
2117 :
2118 : {
2119 7 : MutableByteSpan messageDigestSpan(mMessageDigest);
2120 7 : SuccessOrExit(err = mCommissioningHash.Finish(messageDigestSpan));
2121 : }
2122 :
2123 : // Retrieve peer CASE Authenticated Tags (CATs) from peer's NOC.
2124 : {
2125 7 : SuccessOrExit(err = ExtractCATsFromOpCert(data.initiatorNOC, mPeerCATs));
2126 : }
2127 :
2128 7 : if (mSessionResumptionStorage != nullptr)
2129 : {
2130 3 : CHIP_ERROR err2 = mSessionResumptionStorage->Save(GetPeer(), mNewResumptionId, mSharedSecret, mPeerCATs);
2131 3 : if (err2 != CHIP_NO_ERROR)
2132 : {
2133 0 : ChipLogError(SecureChannel, "Unable to save session resumption state: %" CHIP_ERROR_FORMAT, err2.Format());
2134 : }
2135 : }
2136 :
2137 : MATTER_LOG_METRIC(kMetricDeviceCASESessionSigmaFinished);
2138 7 : SendStatusReport(mExchangeCtxt, kProtocolCodeSuccess);
2139 :
2140 7 : mState = State::kFinished;
2141 7 : Finish();
2142 :
2143 7 : exit:
2144 7 : mHandleSigma3Helper.reset();
2145 :
2146 7 : if (err != CHIP_NO_ERROR)
2147 : {
2148 0 : SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam);
2149 : // Abort the pending establish, which is normally done by CASESession::OnMessageReceived,
2150 : // but in the background processing case must be done here.
2151 0 : DiscardExchange();
2152 0 : AbortPendingEstablish(err);
2153 : }
2154 :
2155 7 : return err;
2156 : }
2157 :
2158 36 : CHIP_ERROR CASESession::DeriveSigmaKey(const ByteSpan & salt, const ByteSpan & info, AutoReleaseSessionKey & key) const
2159 : {
2160 36 : return mSessionManager->GetSessionKeystore()->DeriveKey(mSharedSecret, salt, info, key.KeyHandle());
2161 : }
2162 :
2163 15 : CHIP_ERROR CASESession::ConstructSaltSigma2(const ByteSpan & rand, const Crypto::P256PublicKey & pubkey, const ByteSpan & ipk,
2164 : MutableByteSpan & salt)
2165 : {
2166 : uint8_t md[kSHA256_Hash_Length];
2167 15 : memset(salt.data(), 0, salt.size());
2168 15 : Encoding::LittleEndian::BufferWriter bbuf(salt.data(), salt.size());
2169 :
2170 15 : bbuf.Put(ipk.data(), ipk.size());
2171 15 : bbuf.Put(rand.data(), kSigmaParamRandomNumberSize);
2172 15 : bbuf.Put(pubkey, pubkey.Length());
2173 15 : MutableByteSpan messageDigestSpan(md);
2174 15 : ReturnErrorOnFailure(mCommissioningHash.GetDigest(messageDigestSpan));
2175 15 : bbuf.Put(messageDigestSpan.data(), messageDigestSpan.size());
2176 :
2177 15 : size_t saltWritten = 0;
2178 15 : VerifyOrReturnError(bbuf.Fit(saltWritten), CHIP_ERROR_BUFFER_TOO_SMALL);
2179 15 : salt = salt.SubSpan(0, saltWritten);
2180 :
2181 15 : return CHIP_NO_ERROR;
2182 : }
2183 :
2184 14 : CHIP_ERROR CASESession::ConstructSaltSigma3(const ByteSpan & ipk, MutableByteSpan & salt)
2185 : {
2186 : uint8_t md[kSHA256_Hash_Length];
2187 14 : memset(salt.data(), 0, salt.size());
2188 14 : Encoding::LittleEndian::BufferWriter bbuf(salt.data(), salt.size());
2189 :
2190 14 : bbuf.Put(ipk.data(), ipk.size());
2191 14 : MutableByteSpan messageDigestSpan(md);
2192 14 : ReturnErrorOnFailure(mCommissioningHash.GetDigest(messageDigestSpan));
2193 14 : bbuf.Put(messageDigestSpan.data(), messageDigestSpan.size());
2194 :
2195 14 : size_t saltWritten = 0;
2196 14 : VerifyOrReturnError(bbuf.Fit(saltWritten), CHIP_ERROR_BUFFER_TOO_SMALL);
2197 14 : salt = salt.SubSpan(0, saltWritten);
2198 :
2199 14 : return CHIP_NO_ERROR;
2200 : }
2201 :
2202 7 : CHIP_ERROR CASESession::ConstructSigmaResumeKey(const ByteSpan & initiatorRandom, const ByteSpan & resumptionID,
2203 : const ByteSpan & skInfo, const ByteSpan & nonce, AutoReleaseSessionKey & resumeKey)
2204 : {
2205 7 : constexpr size_t saltSize = kSigmaParamRandomNumberSize + SessionResumptionStorage::kResumptionIdSize;
2206 : uint8_t salt[saltSize];
2207 :
2208 7 : memset(salt, 0, saltSize);
2209 7 : Encoding::LittleEndian::BufferWriter bbuf(salt, saltSize);
2210 :
2211 7 : bbuf.Put(initiatorRandom.data(), initiatorRandom.size());
2212 7 : bbuf.Put(resumptionID.data(), resumptionID.size());
2213 :
2214 7 : size_t saltWritten = 0;
2215 7 : VerifyOrReturnError(bbuf.Fit(saltWritten), CHIP_ERROR_BUFFER_TOO_SMALL);
2216 :
2217 7 : return DeriveSigmaKey(ByteSpan(salt, saltWritten), skInfo, resumeKey);
2218 : }
2219 :
2220 4 : CHIP_ERROR CASESession::GenerateSigmaResumeMIC(const ByteSpan & initiatorRandom, const ByteSpan & resumptionID,
2221 : const ByteSpan & skInfo, const ByteSpan & nonce, MutableByteSpan & resumeMIC)
2222 : {
2223 4 : VerifyOrReturnError(resumeMIC.size() >= CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, CHIP_ERROR_BUFFER_TOO_SMALL);
2224 :
2225 4 : AutoReleaseSessionKey srk(*mSessionManager->GetSessionKeystore());
2226 4 : ReturnErrorOnFailure(ConstructSigmaResumeKey(initiatorRandom, resumptionID, skInfo, nonce, srk));
2227 4 : ReturnErrorOnFailure(AES_CCM_encrypt(nullptr, 0, nullptr, 0, srk.KeyHandle(), nonce.data(), nonce.size(), nullptr,
2228 : resumeMIC.data(), CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES));
2229 4 : resumeMIC.reduce_size(CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES);
2230 :
2231 4 : return CHIP_NO_ERROR;
2232 4 : }
2233 :
2234 3 : CHIP_ERROR CASESession::ValidateSigmaResumeMIC(const ByteSpan & resumeMIC, const ByteSpan & initiatorRandom,
2235 : const ByteSpan & resumptionID, const ByteSpan & skInfo, const ByteSpan & nonce)
2236 : {
2237 3 : VerifyOrReturnError(resumeMIC.size() == CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, CHIP_ERROR_BUFFER_TOO_SMALL);
2238 :
2239 3 : AutoReleaseSessionKey srk(*mSessionManager->GetSessionKeystore());
2240 3 : ReturnErrorOnFailure(ConstructSigmaResumeKey(initiatorRandom, resumptionID, skInfo, nonce, srk));
2241 3 : ReturnErrorOnFailure(AES_CCM_decrypt(nullptr, 0, nullptr, 0, resumeMIC.data(), resumeMIC.size(), srk.KeyHandle(), nonce.data(),
2242 : nonce.size(), nullptr));
2243 :
2244 2 : return CHIP_NO_ERROR;
2245 3 : }
2246 :
2247 29 : CHIP_ERROR CASESession::ConstructTBSData(const ByteSpan & senderNOC, const ByteSpan & senderICAC, const ByteSpan & senderPubKey,
2248 : const ByteSpan & receiverPubKey, MutableByteSpan & outTbsData)
2249 : {
2250 29 : TLVWriter tlvWriter;
2251 29 : TLVType outerContainerType = kTLVType_NotSpecified;
2252 :
2253 29 : tlvWriter.Init(outTbsData);
2254 29 : ReturnErrorOnFailure(tlvWriter.StartContainer(AnonymousTag(), kTLVType_Structure, outerContainerType));
2255 29 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(TBSDataTags::kSenderNOC), senderNOC));
2256 29 : if (!senderICAC.empty())
2257 : {
2258 29 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(TBSDataTags::kSenderICAC), senderICAC));
2259 : }
2260 29 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(TBSDataTags::kSenderPubKey), senderPubKey));
2261 29 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(TBSDataTags::kReceiverPubKey), receiverPubKey));
2262 29 : ReturnErrorOnFailure(tlvWriter.EndContainer(outerContainerType));
2263 29 : ReturnErrorOnFailure(tlvWriter.Finalize());
2264 29 : outTbsData.reduce_size(static_cast<size_t>(tlvWriter.GetLengthWritten()));
2265 :
2266 29 : return CHIP_NO_ERROR;
2267 : }
2268 :
2269 14 : CHIP_ERROR CASESession::SetEffectiveTime()
2270 : {
2271 : System::Clock::Milliseconds64 currentUnixTimeMS;
2272 14 : CHIP_ERROR err = System::SystemClock().GetClock_RealTimeMS(currentUnixTimeMS);
2273 :
2274 14 : if (err == CHIP_NO_ERROR)
2275 : {
2276 : // If the system has given us a wall clock time, we must use it or
2277 : // fail. Conversion failures here are therefore always an error.
2278 14 : System::Clock::Seconds32 currentUnixTime = std::chrono::duration_cast<System::Clock::Seconds32>(currentUnixTimeMS);
2279 14 : ReturnErrorOnFailure(mValidContext.SetEffectiveTimeFromUnixTime<CurrentChipEpochTime>(currentUnixTime));
2280 : }
2281 : else
2282 : {
2283 : // If we don't have wall clock time, the spec dictates that we should
2284 : // fall back to Last Known Good Time. Ultimately, the calling application's
2285 : // validity policy will determine whether this is permissible.
2286 : System::Clock::Seconds32 lastKnownGoodChipEpochTime;
2287 0 : ChipLogError(SecureChannel,
2288 : "The device does not support GetClock_RealTimeMS() API: %" CHIP_ERROR_FORMAT
2289 : ". Falling back to Last Known Good UTC Time",
2290 : err.Format());
2291 0 : VerifyOrReturnError(mFabricsTable != nullptr, CHIP_ERROR_INCORRECT_STATE);
2292 0 : err = mFabricsTable->GetLastKnownGoodChipEpochTime(lastKnownGoodChipEpochTime);
2293 0 : if (err != CHIP_NO_ERROR)
2294 : {
2295 : // If we have no time available, the Validity Policy will
2296 : // determine what to do.
2297 0 : ChipLogError(SecureChannel, "Failed to retrieve Last Known Good UTC Time");
2298 : }
2299 : else
2300 : {
2301 0 : mValidContext.SetEffectiveTime<LastKnownGoodChipEpochTime>(lastKnownGoodChipEpochTime);
2302 : }
2303 : }
2304 14 : return CHIP_NO_ERROR;
2305 : }
2306 :
2307 8 : void CASESession::OnSuccessStatusReport()
2308 : {
2309 8 : ChipLogProgress(SecureChannel, "Success status report received. Session was established");
2310 :
2311 8 : if (mSessionResumptionStorage != nullptr)
2312 : {
2313 4 : CHIP_ERROR err2 = mSessionResumptionStorage->Save(GetPeer(), mNewResumptionId, mSharedSecret, mPeerCATs);
2314 4 : if (err2 != CHIP_NO_ERROR)
2315 0 : ChipLogError(SecureChannel, "Unable to save session resumption state: %" CHIP_ERROR_FORMAT, err2.Format());
2316 : }
2317 :
2318 8 : switch (mState)
2319 : {
2320 7 : case State::kSentSigma3:
2321 7 : mState = State::kFinished;
2322 7 : break;
2323 1 : case State::kSentSigma2Resume:
2324 1 : mState = State::kFinishedViaResume;
2325 1 : break;
2326 0 : default:
2327 0 : VerifyOrDie(false && "Reached invalid internal state keeping in CASE session");
2328 : break;
2329 : }
2330 :
2331 8 : Finish();
2332 8 : }
2333 :
2334 1 : CHIP_ERROR CASESession::OnFailureStatusReport(Protocols::SecureChannel::GeneralStatusCode generalCode, uint16_t protocolCode,
2335 : Optional<uintptr_t> protocolData)
2336 : {
2337 1 : CHIP_ERROR err = CHIP_NO_ERROR;
2338 1 : switch (protocolCode)
2339 : {
2340 0 : case kProtocolCodeInvalidParam:
2341 0 : err = CHIP_ERROR_INVALID_CASE_PARAMETER;
2342 0 : break;
2343 :
2344 0 : case kProtocolCodeNoSharedRoot:
2345 0 : err = CHIP_ERROR_NO_SHARED_TRUSTED_ROOT;
2346 0 : break;
2347 :
2348 1 : case kProtocolCodeBusy:
2349 1 : err = CHIP_ERROR_BUSY;
2350 1 : if (protocolData.HasValue())
2351 : {
2352 1 : mDelegate->OnResponderBusy(System::Clock::Milliseconds16(static_cast<uint16_t>(protocolData.Value())));
2353 : }
2354 1 : break;
2355 :
2356 0 : default:
2357 0 : err = CHIP_ERROR_INTERNAL;
2358 0 : break;
2359 : };
2360 1 : mState = State::kInitialized;
2361 1 : ChipLogError(SecureChannel, "Received error (protocol code %d) during pairing process: %" CHIP_ERROR_FORMAT, protocolCode,
2362 : err.Format());
2363 1 : return err;
2364 : }
2365 :
2366 32 : CHIP_ERROR CASESession::ParseSigma1(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma1 & outParsedSigma1)
2367 : {
2368 :
2369 32 : TLVType containerType = kTLVType_Structure;
2370 32 : ReturnErrorOnFailure(tlvReader.Next(containerType, AnonymousTag()));
2371 32 : ReturnErrorOnFailure(tlvReader.EnterContainer(containerType));
2372 :
2373 32 : ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma1Tags::kInitiatorRandom)));
2374 31 : ReturnErrorOnFailure(tlvReader.GetByteView(outParsedSigma1.initiatorRandom));
2375 31 : VerifyOrReturnError(outParsedSigma1.initiatorRandom.size() == kSigmaParamRandomNumberSize, CHIP_ERROR_INVALID_CASE_PARAMETER);
2376 :
2377 29 : ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma1Tags::kInitiatorSessionId)));
2378 29 : ReturnErrorOnFailure(tlvReader.Get(outParsedSigma1.initiatorSessionId));
2379 :
2380 28 : ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma1Tags::kDestinationId)));
2381 28 : ReturnErrorOnFailure(tlvReader.GetByteView(outParsedSigma1.destinationId));
2382 28 : VerifyOrReturnError(outParsedSigma1.destinationId.size() == kSHA256_Hash_Length, CHIP_ERROR_INVALID_CASE_PARAMETER);
2383 :
2384 26 : ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma1Tags::kInitiatorEphPubKey)));
2385 26 : ReturnErrorOnFailure(tlvReader.GetByteView(outParsedSigma1.initiatorEphPubKey));
2386 26 : VerifyOrReturnError(outParsedSigma1.initiatorEphPubKey.size() == kP256_PublicKey_Length, CHIP_ERROR_INVALID_CASE_PARAMETER);
2387 :
2388 : // Optional members start here.
2389 24 : CHIP_ERROR err = tlvReader.Next();
2390 24 : if (err == CHIP_NO_ERROR && tlvReader.GetTag() == AsTlvContextTag(Sigma1Tags::kInitiatorSessionParams))
2391 : {
2392 11 : ReturnErrorOnFailure(DecodeSessionParametersIfPresent(AsTlvContextTag(Sigma1Tags::kInitiatorSessionParams), tlvReader,
2393 : outParsedSigma1.initiatorSessionParams));
2394 11 : outParsedSigma1.initiatorSessionParamStructPresent = true;
2395 :
2396 11 : err = tlvReader.Next();
2397 : }
2398 :
2399 24 : bool resumptionIDTagFound = false;
2400 24 : bool resume1MICTagFound = false;
2401 :
2402 24 : if (err == CHIP_NO_ERROR && tlvReader.GetTag() == AsTlvContextTag(Sigma1Tags::kResumptionID))
2403 : {
2404 10 : resumptionIDTagFound = true;
2405 10 : ReturnErrorOnFailure(tlvReader.GetByteView(outParsedSigma1.resumptionId));
2406 10 : VerifyOrReturnError(outParsedSigma1.resumptionId.size() == SessionResumptionStorage::kResumptionIdSize,
2407 : CHIP_ERROR_INVALID_CASE_PARAMETER);
2408 7 : err = tlvReader.Next();
2409 : }
2410 :
2411 21 : if (err == CHIP_NO_ERROR && tlvReader.GetTag() == AsTlvContextTag(Sigma1Tags::kResume1MIC))
2412 : {
2413 7 : resume1MICTagFound = true;
2414 7 : ReturnErrorOnFailure(tlvReader.GetByteView(outParsedSigma1.initiatorResumeMIC));
2415 7 : VerifyOrReturnError(outParsedSigma1.initiatorResumeMIC.size() == CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES,
2416 : CHIP_ERROR_INVALID_CASE_PARAMETER);
2417 6 : err = tlvReader.Next();
2418 : }
2419 :
2420 20 : if (err == CHIP_END_OF_TLV)
2421 : {
2422 : // We ran out of struct members, but that's OK, because they were optional.
2423 18 : err = CHIP_NO_ERROR;
2424 : }
2425 :
2426 20 : ReturnErrorOnFailure(err);
2427 20 : ReturnErrorOnFailure(tlvReader.ExitContainer(containerType));
2428 :
2429 18 : if (resumptionIDTagFound && resume1MICTagFound)
2430 : {
2431 5 : outParsedSigma1.sessionResumptionRequested = true;
2432 : }
2433 13 : else if (!resumptionIDTagFound && !resume1MICTagFound)
2434 : {
2435 11 : outParsedSigma1.sessionResumptionRequested = false;
2436 : }
2437 : else
2438 : {
2439 2 : return CHIP_ERROR_UNEXPECTED_TLV_ELEMENT;
2440 : }
2441 :
2442 16 : return CHIP_NO_ERROR;
2443 : }
2444 :
2445 35 : CHIP_ERROR CASESession::ValidateReceivedMessage(ExchangeContext * ec, const PayloadHeader & payloadHeader,
2446 : const System::PacketBufferHandle & msg)
2447 : {
2448 35 : VerifyOrReturnError(ec != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
2449 :
2450 : // mExchangeCtxt can be nullptr if this is the first message (CASE_Sigma1) received by CASESession
2451 : // via UnsolicitedMessageHandler. The exchange context is allocated by exchange manager and provided
2452 : // to the handler (CASESession object).
2453 35 : if (mExchangeCtxt.HasValue())
2454 : {
2455 25 : if (&mExchangeCtxt.Value().Get() != ec)
2456 : {
2457 0 : ReturnErrorOnFailure(CHIP_ERROR_INVALID_ARGUMENT);
2458 : }
2459 : }
2460 : else
2461 : {
2462 10 : mExchangeCtxt.Emplace(*ec);
2463 : }
2464 35 : mExchangeCtxt.Value()->UseSuggestedResponseTimeout(kExpectedHighProcessingTime);
2465 :
2466 35 : VerifyOrReturnError(!msg.IsNull(), CHIP_ERROR_INVALID_ARGUMENT);
2467 35 : return CHIP_NO_ERROR;
2468 : }
2469 :
2470 35 : CHIP_ERROR CASESession::OnMessageReceived(ExchangeContext * ec, const PayloadHeader & payloadHeader,
2471 : System::PacketBufferHandle && msg)
2472 : {
2473 : MATTER_TRACE_SCOPE("OnMessageReceived", "CASESession");
2474 35 : CHIP_ERROR err = ValidateReceivedMessage(ec, payloadHeader, msg);
2475 35 : Protocols::SecureChannel::MsgType msgType = static_cast<Protocols::SecureChannel::MsgType>(payloadHeader.GetMessageType());
2476 35 : SuccessOrExit(err);
2477 :
2478 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
2479 35 : if (mStopHandshakeAtState.HasValue() && mState == mStopHandshakeAtState.Value())
2480 : {
2481 1 : mStopHandshakeAtState = Optional<State>::Missing();
2482 : // For testing purposes we are trying to stop a successful CASESession from happening by dropping part of the
2483 : // handshake in the middle. We are trying to keep both sides of the CASESession establishment in an active
2484 : // pending state. In order to keep this side open we have to tell the exchange context that we will send an
2485 : // async message.
2486 : //
2487 : // Should you need to resume the CASESession, you could theoretically pass along the msg to a callback that gets
2488 : // registered when setting mStopHandshakeAtState.
2489 1 : mExchangeCtxt.Value()->WillSendMessage();
2490 1 : return CHIP_NO_ERROR;
2491 : }
2492 : #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
2493 :
2494 : #if CHIP_CONFIG_SLOW_CRYPTO
2495 : if ((msgType == Protocols::SecureChannel::MsgType::CASE_Sigma1 || msgType == Protocols::SecureChannel::MsgType::CASE_Sigma2 ||
2496 : msgType == Protocols::SecureChannel::MsgType::CASE_Sigma2Resume ||
2497 : msgType == Protocols::SecureChannel::MsgType::CASE_Sigma3) &&
2498 : mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->GetPeerAddress().GetTransportType() !=
2499 : Transport::Type::kTcp)
2500 : {
2501 : // TODO: Rename FlushAcks() to something more semantically correct and
2502 : // call unconditionally for TCP or MRP from here. Inside, the
2503 : // PeerAddress type could be consulted to selectively flush MRP Acks
2504 : // when transport is not TCP. Issue #33183
2505 : SuccessOrExit(err = mExchangeCtxt.Value()->FlushAcks());
2506 : }
2507 : #endif // CHIP_CONFIG_SLOW_CRYPTO
2508 :
2509 : // By default, CHIP_ERROR_INVALID_MESSAGE_TYPE is returned if in the current state
2510 : // a message handler is not defined for the received message type.
2511 34 : err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
2512 :
2513 34 : switch (mState)
2514 : {
2515 10 : case State::kInitialized:
2516 10 : if (msgType == Protocols::SecureChannel::MsgType::CASE_Sigma1)
2517 : {
2518 10 : err = HandleSigma1_and_SendSigma2(std::move(msg));
2519 : }
2520 10 : break;
2521 6 : case State::kSentSigma1:
2522 6 : switch (static_cast<Protocols::SecureChannel::MsgType>(payloadHeader.GetMessageType()))
2523 : {
2524 5 : case Protocols::SecureChannel::MsgType::CASE_Sigma2:
2525 5 : err = HandleSigma2_and_SendSigma3(std::move(msg));
2526 5 : break;
2527 :
2528 1 : case MsgType::StatusReport:
2529 1 : err = HandleStatusReport(std::move(msg), /* successExpected*/ false);
2530 : MATTER_LOG_METRIC_END(kMetricDeviceCASESessionSigma1, err);
2531 1 : break;
2532 :
2533 0 : default:
2534 : // Return the default error that was set above
2535 0 : break;
2536 : };
2537 6 : break;
2538 3 : case State::kSentSigma1Resume:
2539 3 : switch (static_cast<Protocols::SecureChannel::MsgType>(payloadHeader.GetMessageType()))
2540 : {
2541 2 : case Protocols::SecureChannel::MsgType::CASE_Sigma2:
2542 2 : err = HandleSigma2_and_SendSigma3(std::move(msg));
2543 2 : break;
2544 :
2545 1 : case Protocols::SecureChannel::MsgType::CASE_Sigma2Resume:
2546 1 : err = HandleSigma2Resume(std::move(msg));
2547 1 : break;
2548 :
2549 0 : case MsgType::StatusReport:
2550 0 : err = HandleStatusReport(std::move(msg), /* successExpected*/ false);
2551 : MATTER_LOG_METRIC_END(kMetricDeviceCASESessionSigma1, err);
2552 0 : break;
2553 :
2554 0 : default:
2555 : // Return the default error that was set above
2556 0 : break;
2557 : };
2558 3 : break;
2559 7 : case State::kSentSigma2:
2560 7 : switch (static_cast<Protocols::SecureChannel::MsgType>(payloadHeader.GetMessageType()))
2561 : {
2562 7 : case Protocols::SecureChannel::MsgType::CASE_Sigma3:
2563 7 : err = HandleSigma3a(std::move(msg));
2564 7 : break;
2565 :
2566 0 : case MsgType::StatusReport:
2567 0 : err = HandleStatusReport(std::move(msg), /* successExpected*/ false);
2568 : MATTER_LOG_METRIC_END(kMetricDeviceCASESessionSigma2, err);
2569 0 : break;
2570 :
2571 0 : default:
2572 : // Return the default error that was set above
2573 0 : break;
2574 : };
2575 7 : break;
2576 8 : case State::kSentSigma3:
2577 : case State::kSentSigma2Resume:
2578 8 : if (msgType == Protocols::SecureChannel::MsgType::StatusReport)
2579 : {
2580 : // Need to capture before invoking status report since 'this' might be deallocated on successful completion of
2581 : // sigma3
2582 8 : MetricKey key = (mState == State::kSentSigma3) ? kMetricDeviceCASESessionSigma3 : kMetricDeviceCASESessionSigma2Resume;
2583 8 : err = HandleStatusReport(std::move(msg), /* successExpected*/ true);
2584 : MATTER_LOG_METRIC_END(key, err);
2585 : IgnoreUnusedVariable(key);
2586 : }
2587 8 : break;
2588 0 : default:
2589 : // Return the default error that was set above
2590 0 : break;
2591 : };
2592 :
2593 34 : exit:
2594 :
2595 34 : if (err == CHIP_ERROR_INVALID_MESSAGE_TYPE)
2596 : {
2597 0 : ChipLogError(SecureChannel, "Received message (type %d) cannot be handled in %d state.", to_underlying(msgType),
2598 : to_underlying(mState));
2599 : }
2600 :
2601 : // Call delegate to indicate session establishment failure.
2602 34 : if (err != CHIP_NO_ERROR)
2603 : {
2604 : // Discard the exchange so that Clear() doesn't try aborting it. The
2605 : // exchange will handle that.
2606 2 : DiscardExchange();
2607 2 : AbortPendingEstablish(err);
2608 : }
2609 34 : return err;
2610 : }
2611 :
2612 : namespace {
2613 1 : System::Clock::Timeout ComputeRoundTripTimeout(ExchangeContext::Timeout serverProcessingTime,
2614 : const ReliableMessageProtocolConfig & remoteMrpConfig)
2615 : {
2616 : // TODO: This is duplicating logic from Session::ComputeRoundTripTimeout. Unfortunately, it's called by
2617 : // consumers who do not have a session.
2618 1 : const auto & maybeLocalMRPConfig = GetLocalMRPConfig();
2619 1 : const auto & defaultMRRPConfig = GetDefaultMRPConfig();
2620 1 : const auto & localMRPConfig = maybeLocalMRPConfig.ValueOr(defaultMRRPConfig);
2621 1 : return GetRetransmissionTimeout(remoteMrpConfig.mActiveRetransTimeout, remoteMrpConfig.mIdleRetransTimeout,
2622 : // Assume peer is idle, as a worst-case assumption (probably true for
2623 : // Sigma1, since that will be our initial message on the session, but less
2624 : // so for Sigma2).
2625 1 : System::Clock::kZero, remoteMrpConfig.mActiveThresholdTime) +
2626 1 : serverProcessingTime +
2627 1 : GetRetransmissionTimeout(localMRPConfig.mActiveRetransTimeout, localMRPConfig.mIdleRetransTimeout,
2628 : // Peer will assume we are active, since it's
2629 : // responding to our message.
2630 2 : System::SystemClock().GetMonotonicTimestamp(), localMRPConfig.mActiveThresholdTime);
2631 : }
2632 : } // anonymous namespace
2633 :
2634 0 : System::Clock::Timeout CASESession::ComputeSigma1ResponseTimeout(const ReliableMessageProtocolConfig & remoteMrpConfig)
2635 : {
2636 0 : return ComputeRoundTripTimeout(kExpectedSigma1ProcessingTime, remoteMrpConfig);
2637 : }
2638 :
2639 1 : System::Clock::Timeout CASESession::ComputeSigma2ResponseTimeout(const ReliableMessageProtocolConfig & remoteMrpConfig)
2640 : {
2641 1 : return ComputeRoundTripTimeout(kExpectedHighProcessingTime, remoteMrpConfig);
2642 : }
2643 :
2644 1 : bool CASESession::InvokeBackgroundWorkWatchdog()
2645 : {
2646 1 : bool watchdogFired = false;
2647 :
2648 1 : if (mSendSigma3Helper && mSendSigma3Helper->UnableToScheduleAfterWorkCallback())
2649 : {
2650 0 : ChipLogError(SecureChannel, "SendSigma3Helper was unable to schedule the AfterWorkCallback");
2651 0 : mSendSigma3Helper->DoAfterWork();
2652 0 : watchdogFired = true;
2653 : }
2654 :
2655 1 : if (mHandleSigma3Helper && mHandleSigma3Helper->UnableToScheduleAfterWorkCallback())
2656 : {
2657 0 : ChipLogError(SecureChannel, "HandleSigma3Helper was unable to schedule the AfterWorkCallback");
2658 0 : mHandleSigma3Helper->DoAfterWork();
2659 0 : watchdogFired = true;
2660 : }
2661 :
2662 1 : return watchdogFired;
2663 : }
2664 :
2665 : // Helper function to map CASESession::State to SessionEstablishmentStage
2666 4 : SessionEstablishmentStage CASESession::MapCASEStateToSessionEstablishmentStage(State caseState)
2667 : {
2668 4 : switch (caseState)
2669 : {
2670 2 : case State::kInitialized:
2671 2 : return SessionEstablishmentStage::kNotInKeyExchange;
2672 1 : case State::kSentSigma1:
2673 : case State::kSentSigma1Resume:
2674 1 : return SessionEstablishmentStage::kSentSigma1;
2675 1 : case State::kSentSigma2:
2676 : case State::kSentSigma2Resume:
2677 1 : return SessionEstablishmentStage::kSentSigma2;
2678 0 : case State::kSendSigma3Pending:
2679 0 : return SessionEstablishmentStage::kReceivedSigma2;
2680 0 : case State::kSentSigma3:
2681 0 : return SessionEstablishmentStage::kSentSigma3;
2682 0 : case State::kHandleSigma3Pending:
2683 0 : return SessionEstablishmentStage::kReceivedSigma3;
2684 : // Add more mappings here for other states
2685 0 : default:
2686 0 : return SessionEstablishmentStage::kUnknown; // Default mapping
2687 : }
2688 : }
2689 :
2690 : } // namespace chip
|