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