Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-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 CHIP SPAKE2P Session object that provides
22 : * APIs for constructing spake2p messages and establishing encryption
23 : * keys.
24 : *
25 : * The protocol for handling pA, pB, cB and cA is defined in SPAKE2
26 : * Plus specifications.
27 : * (https://www.ietf.org/id/draft-bar-cfrg-spake2plus-01.html)
28 : *
29 : */
30 : #include <protocols/secure_channel/PASESession.h>
31 :
32 : #include <inttypes.h>
33 : #include <string.h>
34 :
35 : #include <lib/core/CHIPEncoding.h>
36 : #include <lib/core/CHIPSafeCasts.h>
37 : #include <lib/support/BufferWriter.h>
38 : #include <lib/support/CHIPMem.h>
39 : #include <lib/support/CodeUtils.h>
40 : #include <lib/support/SafeInt.h>
41 : #include <lib/support/TypeTraits.h>
42 : #include <messaging/SessionParameters.h>
43 : #include <protocols/Protocols.h>
44 : #include <protocols/secure_channel/Constants.h>
45 : #include <protocols/secure_channel/StatusReport.h>
46 : #include <setup_payload/SetupPayload.h>
47 : #include <system/TLVPacketBufferBackingStore.h>
48 : #include <tracing/macros.h>
49 : #include <transport/SessionManager.h>
50 :
51 : namespace {
52 :
53 : enum class PBKDFParamRequestTags : uint8_t
54 : {
55 : kInitiatorRandom = 1,
56 : kInitiatorSessionId = 2,
57 : kPasscodeId = 3,
58 : kHasPBKDFParameters = 4,
59 : kInitiatorSessionParams = 5,
60 : };
61 :
62 : enum class PBKDFParamResponseTags : uint8_t
63 : {
64 : kInitiatorRandom = 1,
65 : kResponderRandom = 2,
66 : kResponderSessionId = 3,
67 : kPbkdfParameters = 4,
68 : kResponderSessionParams = 5,
69 : };
70 :
71 : enum class PBKDFParameterSetTags : uint8_t
72 : {
73 : kIterations = 1,
74 : kSalt = 2,
75 : };
76 :
77 : enum class Pake1Tags : uint8_t
78 : {
79 : kPa = 1,
80 : };
81 :
82 : enum class Pake2Tags : uint8_t
83 : {
84 : kPb = 1,
85 : kCb = 2,
86 : };
87 : enum class Pake3Tags : uint8_t
88 : {
89 : kCa = 1,
90 : };
91 :
92 : // Utility to extract the underlying value of TLV Tag enum classes, used in TLV encoding and parsing.
93 : template <typename Enum>
94 200 : constexpr chip::TLV::Tag AsTlvContextTag(Enum e)
95 : {
96 200 : return chip::TLV::ContextTag(chip::to_underlying(e));
97 : }
98 :
99 : } // namespace
100 : namespace chip {
101 :
102 : using namespace Crypto;
103 : using namespace Messaging;
104 : using namespace Protocols::SecureChannel;
105 :
106 : const char kSpake2pContext[] = "CHIP PAKE V1 Commissioning";
107 :
108 : // Amounts of time to allow for server-side processing of messages.
109 : //
110 : // These timeout values only allow for the server-side processing and assume that any transport-specific
111 : // latency will be added to them.
112 : //
113 : // The session establishment fails if the response is not received within the resulting timeout window,
114 : // which accounts for both transport latency and the server-side latency.
115 : static constexpr ExchangeContext::Timeout kExpectedLowProcessingTime = System::Clock::Seconds16(2);
116 : static constexpr ExchangeContext::Timeout kExpectedHighProcessingTime = System::Clock::Seconds16(30);
117 :
118 16 : PASESession::~PASESession()
119 : {
120 : // Let's clear out any security state stored in the object, before destroying it.
121 16 : Clear();
122 16 : }
123 :
124 10 : void PASESession::OnSessionReleased()
125 : {
126 : // Call into our super-class before we clear our state.
127 10 : PairingSession::OnSessionReleased();
128 10 : Clear();
129 10 : }
130 :
131 10 : void PASESession::Finish()
132 : {
133 10 : mPairingComplete = true;
134 10 : PairingSession::Finish();
135 10 : }
136 :
137 61 : void PASESession::Clear()
138 : {
139 : MATTER_TRACE_SCOPE("Clear", "PASESession");
140 : // This function zeroes out and resets the memory used by the object.
141 : // It's done so that no security related information will be leaked.
142 61 : memset(&mPASEVerifier, 0, sizeof(mPASEVerifier));
143 61 : mNextExpectedMsg.ClearValue();
144 :
145 61 : mSpake2p.Clear();
146 61 : mCommissioningHash.Clear();
147 :
148 61 : mIterationCount = 0;
149 61 : mSaltLength = 0;
150 61 : if (mSalt != nullptr)
151 : {
152 13 : chip::Platform::MemoryFree(mSalt);
153 13 : mSalt = nullptr;
154 : }
155 61 : mPairingComplete = false;
156 61 : PairingSession::Clear();
157 61 : }
158 :
159 21 : CHIP_ERROR PASESession::Init(SessionManager & sessionManager, uint32_t setupCode, SessionEstablishmentDelegate * delegate)
160 : {
161 : MATTER_TRACE_SCOPE("Init", "PASESession");
162 21 : VerifyOrReturnError(sessionManager.GetSessionKeystore() != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
163 21 : VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
164 :
165 : // Reset any state maintained by PASESession object (in case it's being reused for pairing)
166 21 : Clear();
167 :
168 21 : ReturnErrorOnFailure(mCommissioningHash.Begin());
169 21 : ReturnErrorOnFailure(mCommissioningHash.AddData(ByteSpan{ Uint8::from_const_char(kSpake2pContext), strlen(kSpake2pContext) }));
170 :
171 21 : mDelegate = delegate;
172 21 : ReturnErrorOnFailure(AllocateSecureSession(sessionManager));
173 21 : VerifyOrReturnError(GetLocalSessionId().HasValue(), CHIP_ERROR_INCORRECT_STATE);
174 21 : ChipLogDetail(SecureChannel, "Assigned local session key ID %u", GetLocalSessionId().Value());
175 :
176 21 : VerifyOrReturnError(setupCode < (1 << kSetupPINCodeFieldLengthInBits), CHIP_ERROR_INVALID_ARGUMENT);
177 21 : mSetupPINCode = setupCode;
178 :
179 21 : return CHIP_NO_ERROR;
180 : }
181 :
182 3 : CHIP_ERROR PASESession::GeneratePASEVerifier(Spake2pVerifier & verifier, uint32_t pbkdf2IterCount, const ByteSpan & salt,
183 : bool useRandomPIN, uint32_t & setupPINCode)
184 : {
185 : MATTER_TRACE_SCOPE("GeneratePASEVerifier", "PASESession");
186 :
187 3 : if (useRandomPIN)
188 : {
189 1 : ReturnErrorOnFailure(SetupPayload::generateRandomSetupPin(setupPINCode));
190 : }
191 :
192 3 : return verifier.Generate(pbkdf2IterCount, salt, setupPINCode);
193 : }
194 :
195 12 : CHIP_ERROR PASESession::SetupSpake2p()
196 : {
197 : MATTER_TRACE_SCOPE("SetupSpake2p", "PASESession");
198 12 : uint8_t context[kSHA256_Hash_Length] = { 0 };
199 12 : MutableByteSpan contextSpan{ context };
200 :
201 12 : ReturnErrorOnFailure(mCommissioningHash.Finish(contextSpan));
202 12 : ReturnErrorOnFailure(mSpake2p.Init(contextSpan.data(), contextSpan.size()));
203 :
204 12 : return CHIP_NO_ERROR;
205 : }
206 :
207 16 : CHIP_ERROR PASESession::WaitForPairing(SessionManager & sessionManager, const Spake2pVerifier & verifier, uint32_t pbkdf2IterCount,
208 : const ByteSpan & salt, Optional<ReliableMessageProtocolConfig> mrpLocalConfig,
209 : SessionEstablishmentDelegate * delegate)
210 : {
211 : // Return early on error here, as we have not initialized any state yet
212 16 : VerifyOrReturnError(!salt.empty(), CHIP_ERROR_INVALID_ARGUMENT);
213 15 : VerifyOrReturnError(salt.data() != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
214 15 : VerifyOrReturnError(salt.size() >= kSpake2p_Min_PBKDF_Salt_Length && salt.size() <= kSpake2p_Max_PBKDF_Salt_Length,
215 : CHIP_ERROR_INVALID_ARGUMENT);
216 :
217 13 : CHIP_ERROR err = Init(sessionManager, kSetupPINCodeUndefinedValue, delegate);
218 : // From here onwards, let's go to exit on error, as some state might have already
219 : // been initialized
220 13 : SuccessOrExit(err);
221 :
222 13 : mRole = CryptoContext::SessionRole::kResponder;
223 :
224 13 : VerifyOrExit(CanCastTo<uint16_t>(salt.size()), err = CHIP_ERROR_INVALID_ARGUMENT);
225 13 : mSaltLength = static_cast<uint16_t>(salt.size());
226 :
227 13 : if (mSalt != nullptr)
228 : {
229 0 : chip::Platform::MemoryFree(mSalt);
230 0 : mSalt = nullptr;
231 : }
232 :
233 13 : mSalt = static_cast<uint8_t *>(chip::Platform::MemoryAlloc(mSaltLength));
234 13 : VerifyOrExit(mSalt != nullptr, err = CHIP_ERROR_NO_MEMORY);
235 :
236 13 : memmove(mSalt, salt.data(), mSaltLength);
237 13 : memmove(&mPASEVerifier, &verifier, sizeof(verifier));
238 :
239 13 : mIterationCount = pbkdf2IterCount;
240 13 : mNextExpectedMsg.SetValue(MsgType::PBKDFParamRequest);
241 13 : mPairingComplete = false;
242 13 : mLocalMRPConfig = MakeOptional(mrpLocalConfig.ValueOr(GetDefaultMRPConfig()));
243 :
244 13 : ChipLogDetail(SecureChannel, "Waiting for PBKDF param request");
245 :
246 0 : exit:
247 13 : if (err != CHIP_NO_ERROR)
248 : {
249 0 : Clear();
250 : }
251 13 : return err;
252 : }
253 :
254 9 : CHIP_ERROR PASESession::Pair(SessionManager & sessionManager, uint32_t peerSetUpPINCode,
255 : Optional<ReliableMessageProtocolConfig> mrpLocalConfig, Messaging::ExchangeContext * exchangeCtxt,
256 : SessionEstablishmentDelegate * delegate)
257 : {
258 : MATTER_TRACE_SCOPE("Pair", "PASESession");
259 9 : VerifyOrReturnError(exchangeCtxt != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
260 8 : CHIP_ERROR err = Init(sessionManager, peerSetUpPINCode, delegate);
261 8 : SuccessOrExit(err);
262 :
263 8 : mRole = CryptoContext::SessionRole::kInitiator;
264 :
265 8 : mExchangeCtxt.Emplace(*exchangeCtxt);
266 :
267 : // When commissioning starts, the peer is assumed to be active.
268 8 : mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->MarkActiveRx();
269 :
270 8 : mExchangeCtxt.Value()->UseSuggestedResponseTimeout(kExpectedLowProcessingTime);
271 :
272 8 : mLocalMRPConfig = MakeOptional(mrpLocalConfig.ValueOr(GetDefaultMRPConfig()));
273 :
274 8 : err = SendPBKDFParamRequest();
275 8 : SuccessOrExit(err);
276 :
277 7 : mDelegate->OnSessionEstablishmentStarted();
278 :
279 8 : exit:
280 8 : if (err != CHIP_NO_ERROR)
281 : {
282 : // If a failure happens before we have placed the incoming exchange into `mExchangeCtxt`, we need to make
283 : // sure to close the exchange to fulfill our API contract.
284 1 : if (!mExchangeCtxt.HasValue())
285 : {
286 0 : exchangeCtxt->Close();
287 : }
288 1 : Clear();
289 1 : ChipLogError(SecureChannel, "Failed during PASE session pairing request: %" CHIP_ERROR_FORMAT, err.Format());
290 : MATTER_TRACE_COUNTER("PASEFail");
291 : }
292 8 : return err;
293 : }
294 :
295 0 : void PASESession::OnResponseTimeout(ExchangeContext * ec)
296 : {
297 : MATTER_TRACE_SCOPE("OnResponseTimeout", "PASESession");
298 0 : VerifyOrReturn(ec != nullptr, ChipLogError(SecureChannel, "PASESession::OnResponseTimeout was called by null exchange"));
299 0 : VerifyOrReturn(!mExchangeCtxt.HasValue() || &mExchangeCtxt.Value().Get() == ec,
300 : ChipLogError(SecureChannel, "PASESession::OnResponseTimeout exchange doesn't match"));
301 : // If we were waiting for something, mNextExpectedMsg had better have a value.
302 0 : ChipLogError(SecureChannel, "PASESession timed out while waiting for a response from the peer. Expected message type was %u",
303 : to_underlying(mNextExpectedMsg.Value()));
304 : MATTER_TRACE_COUNTER("PASETimeout");
305 : // Discard the exchange so that Clear() doesn't try closing it. The
306 : // exchange will handle that.
307 0 : DiscardExchange();
308 0 : Clear();
309 : // Do this last in case the delegate frees us.
310 0 : NotifySessionEstablishmentError(CHIP_ERROR_TIMEOUT);
311 : }
312 :
313 10 : CHIP_ERROR PASESession::DeriveSecureSession(CryptoContext & session)
314 : {
315 10 : VerifyOrReturnError(mPairingComplete, CHIP_ERROR_INCORRECT_STATE);
316 :
317 10 : SessionKeystore & keystore = *mSessionManager->GetSessionKeystore();
318 10 : AutoReleaseSymmetricKey<HkdfKeyHandle> hkdfKey(keystore);
319 :
320 10 : ReturnErrorOnFailure(mSpake2p.GetKeys(keystore, hkdfKey.KeyHandle()));
321 10 : ReturnErrorOnFailure(session.InitFromSecret(keystore, hkdfKey.KeyHandle(), ByteSpan{} /* salt */,
322 : CryptoContext::SessionInfoType::kSessionEstablishment, mRole));
323 :
324 10 : return CHIP_NO_ERROR;
325 10 : }
326 :
327 12 : CHIP_ERROR PASESession::ReadSessionParamsIfPresent(const TLV::Tag & expectedSessionParamsTag,
328 : System::PacketBufferTLVReader & tlvReader)
329 : {
330 12 : CHIP_ERROR err = CHIP_NO_ERROR;
331 :
332 12 : err = tlvReader.Next();
333 12 : if (err == CHIP_NO_ERROR && tlvReader.GetTag() == expectedSessionParamsTag)
334 : {
335 12 : ReturnErrorOnFailure(DecodeSessionParametersIfPresent(expectedSessionParamsTag, tlvReader, mRemoteSessionParams));
336 12 : mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters(
337 : GetRemoteSessionParameters());
338 :
339 12 : err = tlvReader.Next();
340 : }
341 12 : return err;
342 : }
343 :
344 8 : CHIP_ERROR PASESession::SendPBKDFParamRequest()
345 : {
346 : MATTER_TRACE_SCOPE("SendPBKDFParamRequest", "PASESession");
347 :
348 8 : VerifyOrReturnError(GetLocalSessionId().HasValue(), CHIP_ERROR_INCORRECT_STATE);
349 :
350 8 : ReturnErrorOnFailure(DRBG_get_bytes(mPBKDFLocalRandomData, sizeof(mPBKDFLocalRandomData)));
351 :
352 8 : const size_t max_msg_len = TLV::EstimateStructOverhead(kPBKDFParamRandomNumberSize, // initiatorRandom,
353 : sizeof(uint16_t), // initiatorSessionId
354 : sizeof(PasscodeId), // passcodeId,
355 : sizeof(uint8_t), // hasPBKDFParameters
356 : SessionParameters::kEstimatedTLVSize // Session Parameters
357 : );
358 :
359 8 : System::PacketBufferHandle req = System::PacketBufferHandle::New(max_msg_len);
360 8 : VerifyOrReturnError(!req.IsNull(), CHIP_ERROR_NO_MEMORY);
361 :
362 8 : System::PacketBufferTLVWriter tlvWriter;
363 8 : tlvWriter.Init(std::move(req));
364 :
365 8 : TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified;
366 8 : ReturnErrorOnFailure(tlvWriter.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainerType));
367 8 : ReturnErrorOnFailure(tlvWriter.PutBytes(AsTlvContextTag(PBKDFParamRequestTags::kInitiatorRandom), mPBKDFLocalRandomData,
368 : sizeof(mPBKDFLocalRandomData)));
369 8 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(PBKDFParamRequestTags::kInitiatorSessionId), GetLocalSessionId().Value()));
370 8 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(PBKDFParamRequestTags::kPasscodeId), kDefaultCommissioningPasscodeId));
371 8 : ReturnErrorOnFailure(tlvWriter.PutBoolean(AsTlvContextTag(PBKDFParamRequestTags::kHasPBKDFParameters), mHavePBKDFParameters));
372 :
373 8 : VerifyOrReturnError(mLocalMRPConfig.HasValue(), CHIP_ERROR_INCORRECT_STATE);
374 :
375 8 : ReturnErrorOnFailure(EncodeSessionParameters(AsTlvContextTag(PBKDFParamRequestTags::kInitiatorSessionParams),
376 : mLocalMRPConfig.Value(), tlvWriter));
377 :
378 8 : ReturnErrorOnFailure(tlvWriter.EndContainer(outerContainerType));
379 8 : ReturnErrorOnFailure(tlvWriter.Finalize(&req));
380 :
381 : // Update commissioning hash with the pbkdf2 param request that's being sent.
382 8 : ReturnErrorOnFailure(mCommissioningHash.AddData(ByteSpan{ req->Start(), req->DataLength() }));
383 :
384 8 : ReturnErrorOnFailure(mExchangeCtxt.Value()->SendMessage(MsgType::PBKDFParamRequest, std::move(req),
385 : SendFlags(SendMessageFlags::kExpectResponse)));
386 :
387 7 : mNextExpectedMsg.SetValue(MsgType::PBKDFParamResponse);
388 :
389 : #if CHIP_PROGRESS_LOGGING
390 7 : const auto localMRPConfig = mLocalMRPConfig.Value();
391 : #endif // CHIP_PROGRESS_LOGGING
392 7 : ChipLogProgress(SecureChannel, "Sent PBKDF param request [II:%" PRIu32 "ms AI:%" PRIu32 "ms AT:%ums)",
393 : localMRPConfig.mIdleRetransTimeout.count(), localMRPConfig.mActiveRetransTimeout.count(),
394 : localMRPConfig.mActiveThresholdTime.count());
395 :
396 7 : return CHIP_NO_ERROR;
397 8 : }
398 :
399 6 : CHIP_ERROR PASESession::HandlePBKDFParamRequest(System::PacketBufferHandle && msg)
400 : {
401 : MATTER_TRACE_SCOPE("HandlePBKDFParamRequest", "PASESession");
402 6 : CHIP_ERROR err = CHIP_NO_ERROR;
403 :
404 6 : System::PacketBufferTLVReader tlvReader;
405 6 : TLV::TLVType containerType = TLV::kTLVType_Structure;
406 :
407 : uint16_t initiatorSessionId;
408 : uint8_t initiatorRandom[kPBKDFParamRandomNumberSize];
409 :
410 6 : PasscodeId passcodeId = kDefaultCommissioningPasscodeId;
411 6 : bool hasPBKDFParameters = false;
412 :
413 6 : ChipLogDetail(SecureChannel, "Received PBKDF param request");
414 :
415 6 : SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ msg->Start(), msg->DataLength() }));
416 :
417 6 : tlvReader.Init(std::move(msg));
418 6 : SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag()));
419 6 : SuccessOrExit(err = tlvReader.EnterContainer(containerType));
420 :
421 6 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParamRequestTags::kInitiatorRandom)));
422 6 : VerifyOrExit(tlvReader.GetLength() == kPBKDFParamRandomNumberSize, err = CHIP_ERROR_INVALID_TLV_ELEMENT);
423 6 : SuccessOrExit(err = tlvReader.GetBytes(initiatorRandom, sizeof(initiatorRandom)));
424 :
425 6 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParamRequestTags::kInitiatorSessionId)));
426 6 : SuccessOrExit(err = tlvReader.Get(initiatorSessionId));
427 :
428 6 : ChipLogDetail(SecureChannel, "Peer assigned session ID %d", initiatorSessionId);
429 6 : SetPeerSessionId(initiatorSessionId);
430 :
431 6 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParamRequestTags::kPasscodeId)));
432 6 : SuccessOrExit(err = tlvReader.Get(passcodeId));
433 6 : VerifyOrExit(passcodeId == kDefaultCommissioningPasscodeId, err = CHIP_ERROR_INVALID_PASE_PARAMETER);
434 :
435 6 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParamRequestTags::kHasPBKDFParameters)));
436 6 : SuccessOrExit(err = tlvReader.Get(hasPBKDFParameters));
437 :
438 6 : err = ReadSessionParamsIfPresent(AsTlvContextTag(PBKDFParamRequestTags::kInitiatorSessionParams), tlvReader);
439 :
440 : // Future-proofing: CHIP_NO_ERROR will be returned by Next() within ReadSessionParamsIfPresent() if we have additional
441 : // non-parsed TLV Elements, which could happen in the future if additional elements are added to the specification.
442 6 : VerifyOrExit(err == CHIP_END_OF_TLV || err == CHIP_NO_ERROR, /* No Action */);
443 :
444 : // ExitContainer() acts as a safeguard to ensure that the received encoded message is properly terminated with an EndOfContainer
445 : // TLV element. It is called as an extra validation step to enforce input data structure integrity. Without it, the message may
446 : // still parse correctly, but malformed or incomplete data might go undetected.
447 : // ExitContainer() will return CHIP_END_OF_TLV if the EndOfContainer TLV element terminator is missing.
448 6 : SuccessOrExit(err = tlvReader.ExitContainer(containerType));
449 :
450 6 : err = SendPBKDFParamResponse(ByteSpan(initiatorRandom), hasPBKDFParameters);
451 6 : SuccessOrExit(err);
452 :
453 6 : mDelegate->OnSessionEstablishmentStarted();
454 :
455 6 : exit:
456 :
457 6 : if (err != CHIP_NO_ERROR)
458 : {
459 0 : SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam);
460 : }
461 6 : return err;
462 6 : }
463 :
464 6 : CHIP_ERROR PASESession::SendPBKDFParamResponse(ByteSpan initiatorRandom, bool initiatorHasPBKDFParams)
465 : {
466 : MATTER_TRACE_SCOPE("SendPBKDFParamResponse", "PASESession");
467 :
468 6 : VerifyOrReturnError(GetLocalSessionId().HasValue(), CHIP_ERROR_INCORRECT_STATE);
469 :
470 6 : ReturnErrorOnFailure(DRBG_get_bytes(mPBKDFLocalRandomData, sizeof(mPBKDFLocalRandomData)));
471 :
472 : const size_t max_msg_len =
473 6 : TLV::EstimateStructOverhead(kPBKDFParamRandomNumberSize, // initiatorRandom
474 : kPBKDFParamRandomNumberSize, // responderRandom
475 : sizeof(uint16_t), // responderSessionId
476 6 : TLV::EstimateStructOverhead(sizeof(uint32_t), mSaltLength), // pbkdf_parameters
477 : SessionParameters::kEstimatedTLVSize // Session Parameters
478 : );
479 :
480 6 : System::PacketBufferHandle resp = System::PacketBufferHandle::New(max_msg_len);
481 6 : VerifyOrReturnError(!resp.IsNull(), CHIP_ERROR_NO_MEMORY);
482 :
483 6 : System::PacketBufferTLVWriter tlvWriter;
484 6 : tlvWriter.Init(std::move(resp));
485 :
486 6 : TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified;
487 6 : ReturnErrorOnFailure(tlvWriter.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainerType));
488 : // The initiator random value is being sent back in the response as required by the specifications
489 6 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(PBKDFParamResponseTags::kInitiatorRandom), initiatorRandom));
490 6 : ReturnErrorOnFailure(tlvWriter.PutBytes(AsTlvContextTag(PBKDFParamResponseTags::kResponderRandom), mPBKDFLocalRandomData,
491 : sizeof(mPBKDFLocalRandomData)));
492 6 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(PBKDFParamResponseTags::kResponderSessionId), GetLocalSessionId().Value()));
493 :
494 6 : if (!initiatorHasPBKDFParams)
495 : {
496 : TLV::TLVType pbkdfParamContainer;
497 6 : ReturnErrorOnFailure(tlvWriter.StartContainer(AsTlvContextTag(PBKDFParamResponseTags::kPbkdfParameters),
498 : TLV::kTLVType_Structure, pbkdfParamContainer));
499 6 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(PBKDFParameterSetTags::kIterations), mIterationCount));
500 6 : ReturnErrorOnFailure(tlvWriter.PutBytes(AsTlvContextTag(PBKDFParameterSetTags::kSalt), mSalt, mSaltLength));
501 6 : ReturnErrorOnFailure(tlvWriter.EndContainer(pbkdfParamContainer));
502 : }
503 :
504 6 : VerifyOrReturnError(mLocalMRPConfig.HasValue(), CHIP_ERROR_INCORRECT_STATE);
505 6 : ReturnErrorOnFailure(EncodeSessionParameters(AsTlvContextTag(PBKDFParamResponseTags::kResponderSessionParams),
506 : mLocalMRPConfig.Value(), tlvWriter));
507 :
508 6 : ReturnErrorOnFailure(tlvWriter.EndContainer(outerContainerType));
509 6 : ReturnErrorOnFailure(tlvWriter.Finalize(&resp));
510 :
511 : // Update commissioning hash with the pbkdf2 param response that's being sent.
512 6 : ReturnErrorOnFailure(mCommissioningHash.AddData(ByteSpan{ resp->Start(), resp->DataLength() }));
513 6 : ReturnErrorOnFailure(SetupSpake2p());
514 :
515 6 : ReturnErrorOnFailure(mExchangeCtxt.Value()->SendMessage(MsgType::PBKDFParamResponse, std::move(resp),
516 : SendFlags(SendMessageFlags::kExpectResponse)));
517 6 : ChipLogDetail(SecureChannel, "Sent PBKDF param response");
518 :
519 6 : mNextExpectedMsg.SetValue(MsgType::PASE_Pake1);
520 :
521 6 : return CHIP_NO_ERROR;
522 6 : }
523 :
524 6 : CHIP_ERROR PASESession::HandlePBKDFParamResponse(System::PacketBufferHandle && msg)
525 : {
526 : MATTER_TRACE_SCOPE("HandlePBKDFParamResponse", "PASESession");
527 6 : CHIP_ERROR err = CHIP_NO_ERROR;
528 :
529 6 : System::PacketBufferTLVReader tlvReader;
530 6 : TLV::TLVType containerType = TLV::kTLVType_Structure;
531 :
532 : uint16_t responderSessionId;
533 : uint8_t random[kPBKDFParamRandomNumberSize];
534 :
535 6 : ByteSpan salt;
536 6 : uint8_t serializedWS[kSpake2p_WS_Length * 2] = { 0 };
537 :
538 6 : ChipLogDetail(SecureChannel, "Received PBKDF param response");
539 :
540 6 : SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ msg->Start(), msg->DataLength() }));
541 :
542 6 : tlvReader.Init(std::move(msg));
543 6 : SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag()));
544 6 : SuccessOrExit(err = tlvReader.EnterContainer(containerType));
545 :
546 : // Initiator's random value
547 6 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParamResponseTags::kInitiatorRandom)));
548 6 : SuccessOrExit(err = tlvReader.GetBytes(random, sizeof(random)));
549 6 : VerifyOrExit(ByteSpan(random).data_equal(ByteSpan(mPBKDFLocalRandomData)), err = CHIP_ERROR_INVALID_PASE_PARAMETER);
550 :
551 : // Responder's random value
552 6 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParamResponseTags::kResponderRandom)));
553 6 : VerifyOrExit(tlvReader.GetLength() == kPBKDFParamRandomNumberSize, err = CHIP_ERROR_INVALID_TLV_ELEMENT);
554 6 : SuccessOrExit(err = tlvReader.GetBytes(random, sizeof(random)));
555 :
556 6 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParamResponseTags::kResponderSessionId)));
557 6 : SuccessOrExit(err = tlvReader.Get(responderSessionId));
558 :
559 6 : ChipLogDetail(SecureChannel, "Peer assigned session ID %d", responderSessionId);
560 6 : SetPeerSessionId(responderSessionId);
561 :
562 6 : if (mHavePBKDFParameters)
563 : {
564 0 : err = ReadSessionParamsIfPresent(AsTlvContextTag(PBKDFParamResponseTags::kResponderSessionParams), tlvReader);
565 :
566 : // Future-proofing: CHIP_NO_ERROR will be returned by Next() within ReadSessionParamsIfPresent() if we have additional
567 : // non-parsed TLV Elements, which could happen in the future if additional elements are added to the specification.
568 0 : VerifyOrExit(err == CHIP_END_OF_TLV || err == CHIP_NO_ERROR, /* No Action */);
569 :
570 : // TODO - Add a unit test that exercises mHavePBKDFParameters path
571 0 : salt = ByteSpan(mSalt, mSaltLength);
572 : }
573 : else
574 : {
575 6 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParamResponseTags::kPbkdfParameters)));
576 6 : SuccessOrExit(err = tlvReader.EnterContainer(containerType));
577 :
578 6 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParameterSetTags::kIterations)));
579 6 : SuccessOrExit(err = tlvReader.Get(mIterationCount));
580 :
581 6 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParameterSetTags::kSalt)));
582 6 : VerifyOrExit(tlvReader.GetLength() >= kSpake2p_Min_PBKDF_Salt_Length &&
583 : tlvReader.GetLength() <= kSpake2p_Max_PBKDF_Salt_Length,
584 : err = CHIP_ERROR_INVALID_TLV_ELEMENT);
585 6 : SuccessOrExit(err = tlvReader.Get(salt));
586 :
587 6 : SuccessOrExit(err = tlvReader.ExitContainer(containerType));
588 :
589 6 : err = ReadSessionParamsIfPresent(AsTlvContextTag(PBKDFParamResponseTags::kResponderSessionParams), tlvReader);
590 :
591 : // Future-proofing: CHIP_NO_ERROR will be returned by Next() within ReadSessionParamsIfPresent() if we have additional
592 : // non-parsed TLV Elements, which could happen in the future if additional elements are added to the specification.
593 6 : VerifyOrExit(err == CHIP_END_OF_TLV || err == CHIP_NO_ERROR, /* No Action */);
594 : }
595 :
596 : // ExitContainer() acts as a safeguard to ensure that the received encoded message is properly terminated with an EndOfContainer
597 : // TLV element. It is called as an extra validation step to enforce input data structure integrity. Without it, the message may
598 : // still parse correctly, but malformed or incomplete data might go undetected.
599 : // ExitContainer() will return CHIP_END_OF_TLV if the EndOfContainer TLV element terminator is missing.
600 6 : SuccessOrExit(err = tlvReader.ExitContainer(containerType));
601 :
602 6 : err = SetupSpake2p();
603 6 : SuccessOrExit(err);
604 :
605 6 : err = Spake2pVerifier::ComputeWS(mIterationCount, salt, mSetupPINCode, serializedWS, sizeof(serializedWS));
606 6 : SuccessOrExit(err);
607 :
608 6 : err = mSpake2p.BeginProver(nullptr, 0, nullptr, 0, &serializedWS[0], kSpake2p_WS_Length, &serializedWS[kSpake2p_WS_Length],
609 : kSpake2p_WS_Length);
610 6 : SuccessOrExit(err);
611 :
612 6 : err = SendMsg1();
613 6 : SuccessOrExit(err);
614 :
615 6 : exit:
616 6 : if (err != CHIP_NO_ERROR)
617 : {
618 0 : SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam);
619 : }
620 6 : return err;
621 6 : }
622 :
623 6 : CHIP_ERROR PASESession::SendMsg1()
624 : {
625 : MATTER_TRACE_SCOPE("SendMsg1", "PASESession");
626 6 : const size_t max_msg_len = TLV::EstimateStructOverhead(kMAX_Point_Length);
627 6 : System::PacketBufferHandle msg = System::PacketBufferHandle::New(max_msg_len);
628 6 : VerifyOrReturnError(!msg.IsNull(), CHIP_ERROR_NO_MEMORY);
629 :
630 6 : System::PacketBufferTLVWriter tlvWriter;
631 6 : tlvWriter.Init(std::move(msg));
632 :
633 6 : TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified;
634 6 : ReturnErrorOnFailure(tlvWriter.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainerType));
635 :
636 : uint8_t X[kMAX_Point_Length];
637 6 : size_t X_len = sizeof(X);
638 :
639 6 : ReturnErrorOnFailure(mSpake2p.ComputeRoundOne(nullptr, 0, X, &X_len));
640 6 : VerifyOrReturnError(X_len == sizeof(X), CHIP_ERROR_INTERNAL);
641 6 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(Pake1Tags::kPa), ByteSpan(X)));
642 6 : ReturnErrorOnFailure(tlvWriter.EndContainer(outerContainerType));
643 6 : ReturnErrorOnFailure(tlvWriter.Finalize(&msg));
644 :
645 6 : ReturnErrorOnFailure(
646 : mExchangeCtxt.Value()->SendMessage(MsgType::PASE_Pake1, std::move(msg), SendFlags(SendMessageFlags::kExpectResponse)));
647 6 : ChipLogDetail(SecureChannel, "Sent spake2p msg1");
648 :
649 6 : mNextExpectedMsg.SetValue(MsgType::PASE_Pake2);
650 :
651 6 : return CHIP_NO_ERROR;
652 6 : }
653 :
654 6 : CHIP_ERROR PASESession::HandleMsg1_and_SendMsg2(System::PacketBufferHandle && msg1)
655 : {
656 : MATTER_TRACE_SCOPE("HandleMsg1_and_SendMsg2", "PASESession");
657 6 : CHIP_ERROR err = CHIP_NO_ERROR;
658 :
659 : uint8_t Y[kMAX_Point_Length];
660 6 : size_t Y_len = sizeof(Y);
661 :
662 : uint8_t verifier[kMAX_Hash_Length];
663 6 : size_t verifier_len = kMAX_Hash_Length;
664 :
665 6 : ChipLogDetail(SecureChannel, "Received spake2p msg1");
666 : MATTER_TRACE_SCOPE("Pake1", "PASESession");
667 :
668 6 : System::PacketBufferTLVReader tlvReader;
669 6 : TLV::TLVType containerType = TLV::kTLVType_Structure;
670 :
671 : const uint8_t * X;
672 6 : size_t X_len = 0;
673 :
674 6 : tlvReader.Init(std::move(msg1));
675 6 : SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag()));
676 6 : SuccessOrExit(err = tlvReader.EnterContainer(containerType));
677 :
678 6 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(Pake1Tags::kPa)));
679 6 : X_len = tlvReader.GetLength();
680 6 : VerifyOrExit(X_len == kMAX_Point_Length, err = CHIP_ERROR_INVALID_TLV_ELEMENT);
681 6 : SuccessOrExit(err = tlvReader.GetDataPtr(X));
682 :
683 6 : SuccessOrExit(err = tlvReader.ExitContainer(containerType));
684 :
685 6 : SuccessOrExit(err = mSpake2p.BeginVerifier(nullptr, 0, nullptr, 0, mPASEVerifier.mW0, kP256_FE_Length, mPASEVerifier.mL,
686 : kP256_Point_Length));
687 :
688 6 : SuccessOrExit(err = mSpake2p.ComputeRoundOne(X, X_len, Y, &Y_len));
689 6 : VerifyOrReturnError(Y_len == sizeof(Y), CHIP_ERROR_INTERNAL);
690 6 : SuccessOrExit(err = mSpake2p.ComputeRoundTwo(X, X_len, verifier, &verifier_len));
691 6 : msg1 = nullptr;
692 :
693 : {
694 6 : const size_t max_msg_len = TLV::EstimateStructOverhead(Y_len, verifier_len);
695 :
696 6 : System::PacketBufferHandle msg2 = System::PacketBufferHandle::New(max_msg_len);
697 6 : VerifyOrExit(!msg2.IsNull(), err = CHIP_ERROR_NO_MEMORY);
698 :
699 6 : System::PacketBufferTLVWriter tlvWriter;
700 6 : tlvWriter.Init(std::move(msg2));
701 :
702 6 : TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified;
703 6 : SuccessOrExit(err = tlvWriter.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainerType));
704 6 : SuccessOrExit(err = tlvWriter.Put(AsTlvContextTag(Pake2Tags::kPb), ByteSpan(Y)));
705 6 : SuccessOrExit(err = tlvWriter.Put(AsTlvContextTag(Pake2Tags::kCb), ByteSpan(verifier, verifier_len)));
706 6 : SuccessOrExit(err = tlvWriter.EndContainer(outerContainerType));
707 6 : SuccessOrExit(err = tlvWriter.Finalize(&msg2));
708 :
709 6 : err =
710 6 : mExchangeCtxt.Value()->SendMessage(MsgType::PASE_Pake2, std::move(msg2), SendFlags(SendMessageFlags::kExpectResponse));
711 6 : SuccessOrExit(err);
712 :
713 6 : mNextExpectedMsg.SetValue(MsgType::PASE_Pake3);
714 6 : }
715 :
716 6 : ChipLogDetail(SecureChannel, "Sent spake2p msg2");
717 : MATTER_TRACE_COUNTER("Pake2");
718 :
719 0 : exit:
720 :
721 6 : if (err != CHIP_NO_ERROR)
722 : {
723 0 : SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam);
724 : }
725 6 : return err;
726 6 : }
727 :
728 6 : CHIP_ERROR PASESession::HandleMsg2_and_SendMsg3(System::PacketBufferHandle && msg2)
729 : {
730 : MATTER_TRACE_SCOPE("HandleMsg2_and_SendMsg3", "PASESession");
731 6 : CHIP_ERROR err = CHIP_NO_ERROR;
732 :
733 : uint8_t verifier[kMAX_Hash_Length];
734 6 : size_t verifier_len = kMAX_Hash_Length;
735 :
736 6 : System::PacketBufferHandle resp;
737 :
738 6 : ChipLogDetail(SecureChannel, "Received spake2p msg2");
739 :
740 6 : System::PacketBufferTLVReader tlvReader;
741 6 : TLV::TLVType containerType = TLV::kTLVType_Structure;
742 :
743 : const uint8_t * Y;
744 6 : size_t Y_len = 0;
745 :
746 : const uint8_t * peer_verifier;
747 6 : size_t peer_verifier_len = 0;
748 :
749 6 : tlvReader.Init(std::move(msg2));
750 6 : SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag()));
751 6 : SuccessOrExit(err = tlvReader.EnterContainer(containerType));
752 :
753 6 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(Pake2Tags::kPb)));
754 6 : Y_len = tlvReader.GetLength();
755 6 : VerifyOrExit(Y_len == kMAX_Point_Length, err = CHIP_ERROR_INVALID_TLV_ELEMENT);
756 6 : SuccessOrExit(err = tlvReader.GetDataPtr(Y));
757 :
758 6 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(Pake2Tags::kCb)));
759 6 : peer_verifier_len = tlvReader.GetLength();
760 6 : VerifyOrExit(peer_verifier_len == kMAX_Hash_Length, err = CHIP_ERROR_INVALID_TLV_ELEMENT);
761 6 : SuccessOrExit(err = tlvReader.GetDataPtr(peer_verifier));
762 :
763 : // ExitContainer() acts as a safeguard to ensure that the received encoded message is properly terminated with an EndOfContainer
764 : // TLV element. It is called as an extra validation step to enforce input data structure integrity. Without it, the message may
765 : // still parse correctly, but malformed or incomplete data might go undetected.
766 : // ExitContainer() will return CHIP_END_OF_TLV if the EndOfContainer TLV element terminator is missing.
767 6 : SuccessOrExit(err = tlvReader.ExitContainer(containerType));
768 :
769 6 : SuccessOrExit(err = mSpake2p.ComputeRoundTwo(Y, Y_len, verifier, &verifier_len));
770 :
771 6 : SuccessOrExit(err = mSpake2p.KeyConfirm(peer_verifier, peer_verifier_len));
772 5 : msg2 = nullptr;
773 :
774 : {
775 5 : const size_t max_msg_len = TLV::EstimateStructOverhead(verifier_len);
776 :
777 5 : System::PacketBufferHandle msg3 = System::PacketBufferHandle::New(max_msg_len);
778 5 : VerifyOrExit(!msg3.IsNull(), err = CHIP_ERROR_NO_MEMORY);
779 :
780 5 : System::PacketBufferTLVWriter tlvWriter;
781 5 : tlvWriter.Init(std::move(msg3));
782 :
783 5 : TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified;
784 5 : SuccessOrExit(err = tlvWriter.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainerType));
785 5 : SuccessOrExit(err = tlvWriter.Put(AsTlvContextTag(Pake3Tags::kCa), ByteSpan(verifier, verifier_len)));
786 5 : SuccessOrExit(err = tlvWriter.EndContainer(outerContainerType));
787 5 : SuccessOrExit(err = tlvWriter.Finalize(&msg3));
788 :
789 5 : err =
790 5 : mExchangeCtxt.Value()->SendMessage(MsgType::PASE_Pake3, std::move(msg3), SendFlags(SendMessageFlags::kExpectResponse));
791 5 : SuccessOrExit(err);
792 :
793 5 : mNextExpectedMsg.SetValue(MsgType::StatusReport);
794 5 : }
795 5 : ChipLogDetail(SecureChannel, "Sent spake2p msg3");
796 :
797 0 : exit:
798 :
799 6 : if (err != CHIP_NO_ERROR)
800 : {
801 1 : SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam);
802 : }
803 6 : return err;
804 6 : }
805 :
806 5 : CHIP_ERROR PASESession::HandleMsg3(System::PacketBufferHandle && msg)
807 : {
808 : MATTER_TRACE_SCOPE("HandleMsg3", "PASESession");
809 5 : CHIP_ERROR err = CHIP_NO_ERROR;
810 :
811 5 : ChipLogDetail(SecureChannel, "Received spake2p msg3");
812 : MATTER_TRACE_COUNTER("Pake3");
813 :
814 5 : mNextExpectedMsg.ClearValue();
815 :
816 5 : System::PacketBufferTLVReader tlvReader;
817 5 : TLV::TLVType containerType = TLV::kTLVType_Structure;
818 :
819 : const uint8_t * peer_verifier;
820 5 : size_t peer_verifier_len = 0;
821 :
822 5 : tlvReader.Init(std::move(msg));
823 5 : SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag()));
824 5 : SuccessOrExit(err = tlvReader.EnterContainer(containerType));
825 :
826 5 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(Pake3Tags::kCa)));
827 5 : peer_verifier_len = tlvReader.GetLength();
828 5 : VerifyOrExit(peer_verifier_len == kMAX_Hash_Length, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
829 5 : SuccessOrExit(err = tlvReader.GetDataPtr(peer_verifier));
830 :
831 : // ExitContainer() acts as a safeguard to ensure that the received encoded message is properly terminated with an EndOfContainer
832 : // TLV element. It is called as an extra validation step to enforce input data structure integrity. Without it, the message may
833 : // still parse correctly, but malformed or incomplete data might go undetected.
834 : // ExitContainer() will return CHIP_END_OF_TLV if the EndOfContainer TLV element terminator is missing.
835 5 : SuccessOrExit(err = tlvReader.ExitContainer(containerType));
836 :
837 5 : SuccessOrExit(err = mSpake2p.KeyConfirm(peer_verifier, peer_verifier_len));
838 :
839 : // Send confirmation to peer that we succeeded so they can start using the session.
840 5 : SendStatusReport(mExchangeCtxt, kProtocolCodeSuccess);
841 :
842 5 : Finish();
843 5 : exit:
844 :
845 5 : if (err != CHIP_NO_ERROR)
846 : {
847 0 : SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam);
848 : }
849 5 : return err;
850 5 : }
851 :
852 5 : void PASESession::OnSuccessStatusReport()
853 : {
854 5 : Finish();
855 5 : }
856 :
857 1 : CHIP_ERROR PASESession::OnFailureStatusReport(Protocols::SecureChannel::GeneralStatusCode generalCode, uint16_t protocolCode,
858 : Optional<uintptr_t> protocolData)
859 : {
860 1 : CHIP_ERROR err = CHIP_NO_ERROR;
861 1 : switch (protocolCode)
862 : {
863 1 : case kProtocolCodeInvalidParam:
864 1 : err = CHIP_ERROR_INVALID_PASE_PARAMETER;
865 1 : break;
866 :
867 0 : default:
868 0 : err = CHIP_ERROR_INTERNAL;
869 0 : break;
870 : };
871 1 : ChipLogError(SecureChannel, "Received error (protocol code %d) during PASE process: %" CHIP_ERROR_FORMAT, protocolCode,
872 : err.Format());
873 1 : return err;
874 : }
875 :
876 35 : CHIP_ERROR PASESession::ValidateReceivedMessage(ExchangeContext * exchange, const PayloadHeader & payloadHeader,
877 : const System::PacketBufferHandle & msg)
878 : {
879 35 : VerifyOrReturnError(exchange != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
880 :
881 : // mExchangeCtxt can be nullptr if this is the first message (PBKDFParamRequest) received by PASESession
882 : // via UnsolicitedMessageHandler. The exchange context is allocated by exchange manager and provided
883 : // to the handler (PASESession object).
884 35 : if (mExchangeCtxt.HasValue())
885 : {
886 29 : if (&mExchangeCtxt.Value().Get() != exchange)
887 : {
888 0 : ReturnErrorOnFailure(CHIP_ERROR_INVALID_ARGUMENT);
889 : }
890 : }
891 : else
892 : {
893 6 : mExchangeCtxt.Emplace(*exchange);
894 : }
895 :
896 35 : if (!mExchangeCtxt.Value()->GetSessionHandle()->IsUnauthenticatedSession())
897 : {
898 0 : ChipLogError(SecureChannel, "PASESession received PBKDFParamRequest over encrypted session. Ignoring.");
899 0 : return CHIP_ERROR_INCORRECT_STATE;
900 : }
901 :
902 35 : mExchangeCtxt.Value()->UseSuggestedResponseTimeout(kExpectedHighProcessingTime);
903 :
904 35 : VerifyOrReturnError(!msg.IsNull(), CHIP_ERROR_INVALID_ARGUMENT);
905 35 : VerifyOrReturnError((mNextExpectedMsg.HasValue() && payloadHeader.HasMessageType(mNextExpectedMsg.Value())) ||
906 : payloadHeader.HasMessageType(MsgType::StatusReport),
907 : CHIP_ERROR_INVALID_MESSAGE_TYPE);
908 :
909 35 : return CHIP_NO_ERROR;
910 : }
911 :
912 6 : CHIP_ERROR PASESession::OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate)
913 : {
914 : // Handle messages by myself
915 6 : newDelegate = this;
916 6 : return CHIP_NO_ERROR;
917 : }
918 :
919 35 : CHIP_ERROR PASESession::OnMessageReceived(ExchangeContext * exchange, const PayloadHeader & payloadHeader,
920 : System::PacketBufferHandle && msg)
921 : {
922 : MATTER_TRACE_SCOPE("OnMessageReceived", "PASESession");
923 35 : CHIP_ERROR err = ValidateReceivedMessage(exchange, payloadHeader, msg);
924 35 : MsgType msgType = static_cast<MsgType>(payloadHeader.GetMessageType());
925 35 : SuccessOrExit(err);
926 :
927 : #if CHIP_CONFIG_SLOW_CRYPTO
928 : if (msgType == MsgType::PBKDFParamRequest || msgType == MsgType::PBKDFParamResponse || msgType == MsgType::PASE_Pake1 ||
929 : msgType == MsgType::PASE_Pake2 || msgType == MsgType::PASE_Pake3)
930 : {
931 : SuccessOrExit(err = mExchangeCtxt.Value()->FlushAcks());
932 : }
933 : #endif // CHIP_CONFIG_SLOW_CRYPTO
934 :
935 35 : switch (msgType)
936 : {
937 6 : case MsgType::PBKDFParamRequest:
938 6 : err = HandlePBKDFParamRequest(std::move(msg));
939 6 : break;
940 :
941 6 : case MsgType::PBKDFParamResponse:
942 6 : err = HandlePBKDFParamResponse(std::move(msg));
943 6 : break;
944 :
945 6 : case MsgType::PASE_Pake1:
946 6 : err = HandleMsg1_and_SendMsg2(std::move(msg));
947 6 : break;
948 :
949 6 : case MsgType::PASE_Pake2:
950 6 : err = HandleMsg2_and_SendMsg3(std::move(msg));
951 6 : break;
952 :
953 5 : case MsgType::PASE_Pake3:
954 5 : err = HandleMsg3(std::move(msg));
955 5 : break;
956 :
957 6 : case MsgType::StatusReport:
958 6 : err =
959 6 : HandleStatusReport(std::move(msg), mNextExpectedMsg.HasValue() && (mNextExpectedMsg.Value() == MsgType::StatusReport));
960 6 : break;
961 :
962 0 : default:
963 0 : err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
964 0 : break;
965 : };
966 :
967 35 : exit:
968 :
969 : // Call delegate to indicate pairing failure
970 35 : if (err != CHIP_NO_ERROR)
971 : {
972 : // Discard the exchange so that Clear() doesn't try closing it. The
973 : // exchange will handle that.
974 2 : DiscardExchange();
975 2 : Clear();
976 2 : ChipLogError(SecureChannel, "Failed during PASE session setup: %" CHIP_ERROR_FORMAT, err.Format());
977 : MATTER_TRACE_COUNTER("PASEFail");
978 : // Do this last in case the delegate frees us.
979 2 : NotifySessionEstablishmentError(err);
980 : }
981 35 : return err;
982 : }
983 :
984 : } // namespace chip
|