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 232 : constexpr chip::TLV::Tag AsTlvContextTag(Enum e)
95 : {
96 232 : 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 32 : PASESession::~PASESession()
119 : {
120 : // Let's clear out any security state stored in the object, before destroying it.
121 32 : Clear();
122 32 : }
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 12 : void PASESession::Finish()
132 : {
133 12 : mPairingComplete = true;
134 12 : PairingSession::Finish();
135 12 : }
136 :
137 83 : 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 83 : memset(&mPASEVerifier, 0, sizeof(mPASEVerifier));
143 83 : mNextExpectedMsg.ClearValue();
144 :
145 83 : mSpake2p.Clear();
146 83 : mCommissioningHash.Clear();
147 :
148 83 : mIterationCount = 0;
149 83 : mSaltLength = 0;
150 83 : if (mSalt != nullptr)
151 : {
152 14 : chip::Platform::MemoryFree(mSalt);
153 14 : mSalt = nullptr;
154 : }
155 83 : mPairingComplete = false;
156 83 : PairingSession::Clear();
157 83 : }
158 :
159 23 : CHIP_ERROR PASESession::Init(SessionManager & sessionManager, uint32_t setupCode, SessionEstablishmentDelegate * delegate)
160 : {
161 : MATTER_TRACE_SCOPE("Init", "PASESession");
162 23 : VerifyOrReturnError(sessionManager.GetSessionKeystore() != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
163 23 : 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 23 : Clear();
167 :
168 23 : ReturnErrorOnFailure(mCommissioningHash.Begin());
169 23 : ReturnErrorOnFailure(mCommissioningHash.AddData(ByteSpan{ Uint8::from_const_char(kSpake2pContext), strlen(kSpake2pContext) }));
170 :
171 23 : mDelegate = delegate;
172 23 : ReturnErrorOnFailure(AllocateSecureSession(sessionManager));
173 23 : VerifyOrReturnError(GetLocalSessionId().HasValue(), CHIP_ERROR_INCORRECT_STATE);
174 23 : ChipLogDetail(SecureChannel, "Assigned local session key ID %u", GetLocalSessionId().Value());
175 :
176 23 : VerifyOrReturnError(setupCode < (1 << kSetupPINCodeFieldLengthInBits), CHIP_ERROR_INVALID_ARGUMENT);
177 23 : mSetupPINCode = setupCode;
178 :
179 23 : 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 14 : CHIP_ERROR PASESession::SetupSpake2p()
196 : {
197 : MATTER_TRACE_SCOPE("SetupSpake2p", "PASESession");
198 14 : uint8_t context[kSHA256_Hash_Length] = { 0 };
199 14 : MutableByteSpan contextSpan{ context };
200 :
201 14 : ReturnErrorOnFailure(mCommissioningHash.Finish(contextSpan));
202 14 : ReturnErrorOnFailure(mSpake2p.Init(contextSpan.data(), contextSpan.size()));
203 :
204 14 : return CHIP_NO_ERROR;
205 : }
206 :
207 17 : 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 17 : VerifyOrReturnError(!salt.empty(), CHIP_ERROR_INVALID_ARGUMENT);
213 16 : VerifyOrReturnError(salt.data() != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
214 16 : VerifyOrReturnError(salt.size() >= kSpake2p_Min_PBKDF_Salt_Length && salt.size() <= kSpake2p_Max_PBKDF_Salt_Length,
215 : CHIP_ERROR_INVALID_ARGUMENT);
216 :
217 14 : 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 14 : SuccessOrExit(err);
221 :
222 14 : mRole = CryptoContext::SessionRole::kResponder;
223 :
224 14 : VerifyOrExit(CanCastTo<uint16_t>(salt.size()), err = CHIP_ERROR_INVALID_ARGUMENT);
225 14 : mSaltLength = static_cast<uint16_t>(salt.size());
226 :
227 14 : if (mSalt != nullptr)
228 : {
229 0 : chip::Platform::MemoryFree(mSalt);
230 0 : mSalt = nullptr;
231 : }
232 :
233 14 : mSalt = static_cast<uint8_t *>(chip::Platform::MemoryAlloc(mSaltLength));
234 14 : VerifyOrExit(mSalt != nullptr, err = CHIP_ERROR_NO_MEMORY);
235 :
236 14 : memmove(mSalt, salt.data(), mSaltLength);
237 14 : memmove(&mPASEVerifier, &verifier, sizeof(verifier));
238 :
239 14 : mIterationCount = pbkdf2IterCount;
240 14 : mNextExpectedMsg.SetValue(MsgType::PBKDFParamRequest);
241 14 : mPairingComplete = false;
242 14 : mLocalMRPConfig = MakeOptional(mrpLocalConfig.ValueOr(GetDefaultMRPConfig()));
243 :
244 14 : ChipLogDetail(SecureChannel, "Waiting for PBKDF param request");
245 :
246 0 : exit:
247 28 : if (err != CHIP_NO_ERROR)
248 : {
249 0 : Clear();
250 : }
251 14 : return err;
252 : }
253 :
254 10 : 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 10 : VerifyOrReturnError(exchangeCtxt != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
260 9 : CHIP_ERROR err = Init(sessionManager, peerSetUpPINCode, delegate);
261 9 : SuccessOrExit(err);
262 :
263 9 : mRole = CryptoContext::SessionRole::kInitiator;
264 :
265 9 : mExchangeCtxt.Emplace(*exchangeCtxt);
266 :
267 : // When commissioning starts, the peer is assumed to be active.
268 9 : mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->MarkActiveRx();
269 :
270 9 : mExchangeCtxt.Value()->UseSuggestedResponseTimeout(kExpectedLowProcessingTime);
271 :
272 9 : mLocalMRPConfig = MakeOptional(mrpLocalConfig.ValueOr(GetDefaultMRPConfig()));
273 :
274 9 : err = SendPBKDFParamRequest();
275 9 : SuccessOrExit(err);
276 :
277 8 : mDelegate->OnSessionEstablishmentStarted();
278 :
279 9 : exit:
280 18 : 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 9 : 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 12 : CHIP_ERROR PASESession::DeriveSecureSession(CryptoContext & session)
314 : {
315 12 : VerifyOrReturnError(mPairingComplete, CHIP_ERROR_INCORRECT_STATE);
316 :
317 12 : SessionKeystore & keystore = *mSessionManager->GetSessionKeystore();
318 12 : AutoReleaseSymmetricKey<HkdfKeyHandle> hkdfKey(keystore);
319 :
320 12 : ReturnErrorOnFailure(mSpake2p.GetKeys(keystore, hkdfKey.KeyHandle()));
321 12 : ReturnErrorOnFailure(session.InitFromSecret(keystore, hkdfKey.KeyHandle(), ByteSpan{} /* salt */,
322 : CryptoContext::SessionInfoType::kSessionEstablishment, mRole));
323 :
324 12 : return CHIP_NO_ERROR;
325 12 : }
326 :
327 14 : CHIP_ERROR PASESession::ReadSessionParamsIfPresent(const TLV::Tag & expectedSessionParamsTag,
328 : System::PacketBufferTLVReader & tlvReader)
329 : {
330 14 : CHIP_ERROR err = CHIP_NO_ERROR;
331 :
332 14 : err = tlvReader.Next();
333 28 : if (err == CHIP_NO_ERROR && tlvReader.GetTag() == expectedSessionParamsTag)
334 : {
335 14 : ReturnErrorOnFailure(DecodeSessionParametersIfPresent(expectedSessionParamsTag, tlvReader, mRemoteSessionParams));
336 14 : mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters(
337 : GetRemoteSessionParameters());
338 :
339 14 : err = tlvReader.Next();
340 : }
341 14 : return err;
342 : }
343 :
344 9 : CHIP_ERROR PASESession::SendPBKDFParamRequest()
345 : {
346 : MATTER_TRACE_SCOPE("SendPBKDFParamRequest", "PASESession");
347 :
348 9 : VerifyOrReturnError(GetLocalSessionId().HasValue(), CHIP_ERROR_INCORRECT_STATE);
349 :
350 9 : ReturnErrorOnFailure(DRBG_get_bytes(mPBKDFLocalRandomData, sizeof(mPBKDFLocalRandomData)));
351 :
352 9 : 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 9 : System::PacketBufferHandle req = System::PacketBufferHandle::New(max_msg_len);
360 9 : VerifyOrReturnError(!req.IsNull(), CHIP_ERROR_NO_MEMORY);
361 :
362 9 : System::PacketBufferTLVWriter tlvWriter;
363 9 : tlvWriter.Init(std::move(req));
364 :
365 9 : TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified;
366 9 : ReturnErrorOnFailure(tlvWriter.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainerType));
367 9 : ReturnErrorOnFailure(tlvWriter.PutBytes(AsTlvContextTag(PBKDFParamRequestTags::kInitiatorRandom), mPBKDFLocalRandomData,
368 : sizeof(mPBKDFLocalRandomData)));
369 9 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(PBKDFParamRequestTags::kInitiatorSessionId), GetLocalSessionId().Value()));
370 9 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(PBKDFParamRequestTags::kPasscodeId), kDefaultCommissioningPasscodeId));
371 9 : ReturnErrorOnFailure(tlvWriter.PutBoolean(AsTlvContextTag(PBKDFParamRequestTags::kHasPBKDFParameters), mHavePBKDFParameters));
372 :
373 9 : VerifyOrReturnError(mLocalMRPConfig.HasValue(), CHIP_ERROR_INCORRECT_STATE);
374 :
375 9 : ReturnErrorOnFailure(EncodeSessionParameters(AsTlvContextTag(PBKDFParamRequestTags::kInitiatorSessionParams),
376 : mLocalMRPConfig.Value(), tlvWriter));
377 :
378 9 : ReturnErrorOnFailure(tlvWriter.EndContainer(outerContainerType));
379 9 : ReturnErrorOnFailure(tlvWriter.Finalize(&req));
380 :
381 : // Update commissioning hash with the pbkdf2 param request that's being sent.
382 9 : ReturnErrorOnFailure(mCommissioningHash.AddData(ByteSpan{ req->Start(), req->DataLength() }));
383 :
384 9 : ReturnErrorOnFailure(mExchangeCtxt.Value()->SendMessage(MsgType::PBKDFParamRequest, std::move(req),
385 : SendFlags(SendMessageFlags::kExpectResponse)));
386 :
387 8 : mNextExpectedMsg.SetValue(MsgType::PBKDFParamResponse);
388 :
389 : #if CHIP_PROGRESS_LOGGING
390 8 : const auto localMRPConfig = mLocalMRPConfig.Value();
391 : #endif // CHIP_PROGRESS_LOGGING
392 8 : 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 8 : return CHIP_NO_ERROR;
397 9 : }
398 :
399 7 : CHIP_ERROR PASESession::HandlePBKDFParamRequest(System::PacketBufferHandle && msg)
400 : {
401 : MATTER_TRACE_SCOPE("HandlePBKDFParamRequest", "PASESession");
402 7 : CHIP_ERROR err = CHIP_NO_ERROR;
403 :
404 7 : System::PacketBufferTLVReader tlvReader;
405 7 : TLV::TLVType containerType = TLV::kTLVType_Structure;
406 :
407 : uint16_t initiatorSessionId;
408 : uint8_t initiatorRandom[kPBKDFParamRandomNumberSize];
409 :
410 7 : PasscodeId passcodeId = kDefaultCommissioningPasscodeId;
411 7 : bool hasPBKDFParameters = false;
412 :
413 7 : ChipLogDetail(SecureChannel, "Received PBKDF param request");
414 :
415 7 : SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ msg->Start(), msg->DataLength() }));
416 :
417 7 : tlvReader.Init(std::move(msg));
418 7 : SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag()));
419 7 : SuccessOrExit(err = tlvReader.EnterContainer(containerType));
420 :
421 7 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParamRequestTags::kInitiatorRandom)));
422 7 : VerifyOrExit(tlvReader.GetLength() == kPBKDFParamRandomNumberSize, err = CHIP_ERROR_INVALID_TLV_ELEMENT);
423 7 : SuccessOrExit(err = tlvReader.GetBytes(initiatorRandom, sizeof(initiatorRandom)));
424 :
425 7 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParamRequestTags::kInitiatorSessionId)));
426 7 : SuccessOrExit(err = tlvReader.Get(initiatorSessionId));
427 :
428 7 : ChipLogDetail(SecureChannel, "Peer assigned session ID %d", initiatorSessionId);
429 7 : SetPeerSessionId(initiatorSessionId);
430 :
431 7 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParamRequestTags::kPasscodeId)));
432 7 : SuccessOrExit(err = tlvReader.Get(passcodeId));
433 7 : VerifyOrExit(passcodeId == kDefaultCommissioningPasscodeId, err = CHIP_ERROR_INVALID_PASE_PARAMETER);
434 :
435 7 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParamRequestTags::kHasPBKDFParameters)));
436 7 : SuccessOrExit(err = tlvReader.Get(hasPBKDFParameters));
437 :
438 7 : 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 14 : 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 7 : SuccessOrExit(err = tlvReader.ExitContainer(containerType));
449 :
450 7 : err = SendPBKDFParamResponse(ByteSpan(initiatorRandom), hasPBKDFParameters);
451 7 : SuccessOrExit(err);
452 :
453 7 : mDelegate->OnSessionEstablishmentStarted();
454 :
455 7 : exit:
456 :
457 14 : if (err != CHIP_NO_ERROR)
458 : {
459 0 : SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam);
460 : }
461 14 : return err;
462 7 : }
463 :
464 7 : CHIP_ERROR PASESession::SendPBKDFParamResponse(ByteSpan initiatorRandom, bool initiatorHasPBKDFParams)
465 : {
466 : MATTER_TRACE_SCOPE("SendPBKDFParamResponse", "PASESession");
467 :
468 7 : VerifyOrReturnError(GetLocalSessionId().HasValue(), CHIP_ERROR_INCORRECT_STATE);
469 :
470 7 : ReturnErrorOnFailure(DRBG_get_bytes(mPBKDFLocalRandomData, sizeof(mPBKDFLocalRandomData)));
471 :
472 : const size_t max_msg_len =
473 7 : TLV::EstimateStructOverhead(kPBKDFParamRandomNumberSize, // initiatorRandom
474 : kPBKDFParamRandomNumberSize, // responderRandom
475 : sizeof(uint16_t), // responderSessionId
476 7 : TLV::EstimateStructOverhead(sizeof(uint32_t), mSaltLength), // pbkdf_parameters
477 : SessionParameters::kEstimatedTLVSize // Session Parameters
478 : );
479 :
480 7 : System::PacketBufferHandle resp = System::PacketBufferHandle::New(max_msg_len);
481 7 : VerifyOrReturnError(!resp.IsNull(), CHIP_ERROR_NO_MEMORY);
482 :
483 7 : System::PacketBufferTLVWriter tlvWriter;
484 7 : tlvWriter.Init(std::move(resp));
485 :
486 7 : TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified;
487 7 : 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 7 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(PBKDFParamResponseTags::kInitiatorRandom), initiatorRandom));
490 7 : ReturnErrorOnFailure(tlvWriter.PutBytes(AsTlvContextTag(PBKDFParamResponseTags::kResponderRandom), mPBKDFLocalRandomData,
491 : sizeof(mPBKDFLocalRandomData)));
492 7 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(PBKDFParamResponseTags::kResponderSessionId), GetLocalSessionId().Value()));
493 :
494 7 : if (!initiatorHasPBKDFParams)
495 : {
496 : TLV::TLVType pbkdfParamContainer;
497 7 : ReturnErrorOnFailure(tlvWriter.StartContainer(AsTlvContextTag(PBKDFParamResponseTags::kPbkdfParameters),
498 : TLV::kTLVType_Structure, pbkdfParamContainer));
499 7 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(PBKDFParameterSetTags::kIterations), mIterationCount));
500 7 : ReturnErrorOnFailure(tlvWriter.PutBytes(AsTlvContextTag(PBKDFParameterSetTags::kSalt), mSalt, mSaltLength));
501 7 : ReturnErrorOnFailure(tlvWriter.EndContainer(pbkdfParamContainer));
502 : }
503 :
504 7 : VerifyOrReturnError(mLocalMRPConfig.HasValue(), CHIP_ERROR_INCORRECT_STATE);
505 7 : ReturnErrorOnFailure(EncodeSessionParameters(AsTlvContextTag(PBKDFParamResponseTags::kResponderSessionParams),
506 : mLocalMRPConfig.Value(), tlvWriter));
507 :
508 7 : ReturnErrorOnFailure(tlvWriter.EndContainer(outerContainerType));
509 7 : ReturnErrorOnFailure(tlvWriter.Finalize(&resp));
510 :
511 : // Update commissioning hash with the pbkdf2 param response that's being sent.
512 7 : ReturnErrorOnFailure(mCommissioningHash.AddData(ByteSpan{ resp->Start(), resp->DataLength() }));
513 7 : ReturnErrorOnFailure(SetupSpake2p());
514 :
515 7 : ReturnErrorOnFailure(mExchangeCtxt.Value()->SendMessage(MsgType::PBKDFParamResponse, std::move(resp),
516 : SendFlags(SendMessageFlags::kExpectResponse)));
517 7 : ChipLogDetail(SecureChannel, "Sent PBKDF param response");
518 :
519 7 : mNextExpectedMsg.SetValue(MsgType::PASE_Pake1);
520 :
521 7 : return CHIP_NO_ERROR;
522 7 : }
523 :
524 7 : CHIP_ERROR PASESession::HandlePBKDFParamResponse(System::PacketBufferHandle && msg)
525 : {
526 : MATTER_TRACE_SCOPE("HandlePBKDFParamResponse", "PASESession");
527 7 : CHIP_ERROR err = CHIP_NO_ERROR;
528 :
529 7 : System::PacketBufferTLVReader tlvReader;
530 7 : TLV::TLVType containerType = TLV::kTLVType_Structure;
531 :
532 : uint16_t responderSessionId;
533 : uint8_t random[kPBKDFParamRandomNumberSize];
534 :
535 7 : ByteSpan salt;
536 7 : uint8_t serializedWS[kSpake2p_WS_Length * 2] = { 0 };
537 :
538 7 : ChipLogDetail(SecureChannel, "Received PBKDF param response");
539 :
540 7 : SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ msg->Start(), msg->DataLength() }));
541 :
542 7 : tlvReader.Init(std::move(msg));
543 7 : SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag()));
544 7 : SuccessOrExit(err = tlvReader.EnterContainer(containerType));
545 :
546 : // Initiator's random value
547 7 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParamResponseTags::kInitiatorRandom)));
548 7 : SuccessOrExit(err = tlvReader.GetBytes(random, sizeof(random)));
549 7 : VerifyOrExit(ByteSpan(random).data_equal(ByteSpan(mPBKDFLocalRandomData)), err = CHIP_ERROR_INVALID_PASE_PARAMETER);
550 :
551 : // Responder's random value
552 7 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParamResponseTags::kResponderRandom)));
553 7 : VerifyOrExit(tlvReader.GetLength() == kPBKDFParamRandomNumberSize, err = CHIP_ERROR_INVALID_TLV_ELEMENT);
554 7 : SuccessOrExit(err = tlvReader.GetBytes(random, sizeof(random)));
555 :
556 7 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParamResponseTags::kResponderSessionId)));
557 7 : SuccessOrExit(err = tlvReader.Get(responderSessionId));
558 :
559 7 : ChipLogDetail(SecureChannel, "Peer assigned session ID %d", responderSessionId);
560 7 : SetPeerSessionId(responderSessionId);
561 :
562 7 : 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 7 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParamResponseTags::kPbkdfParameters)));
576 7 : SuccessOrExit(err = tlvReader.EnterContainer(containerType));
577 :
578 7 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParameterSetTags::kIterations)));
579 7 : SuccessOrExit(err = tlvReader.Get(mIterationCount));
580 :
581 7 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(PBKDFParameterSetTags::kSalt)));
582 7 : VerifyOrExit(tlvReader.GetLength() >= kSpake2p_Min_PBKDF_Salt_Length &&
583 : tlvReader.GetLength() <= kSpake2p_Max_PBKDF_Salt_Length,
584 : err = CHIP_ERROR_INVALID_TLV_ELEMENT);
585 7 : SuccessOrExit(err = tlvReader.Get(salt));
586 :
587 7 : SuccessOrExit(err = tlvReader.ExitContainer(containerType));
588 :
589 7 : 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 14 : 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 7 : SuccessOrExit(err = tlvReader.ExitContainer(containerType));
601 :
602 7 : err = SetupSpake2p();
603 7 : SuccessOrExit(err);
604 :
605 7 : err = Spake2pVerifier::ComputeWS(mIterationCount, salt, mSetupPINCode, serializedWS, sizeof(serializedWS));
606 7 : SuccessOrExit(err);
607 :
608 7 : err = mSpake2p.BeginProver(nullptr, 0, nullptr, 0, &serializedWS[0], kSpake2p_WS_Length, &serializedWS[kSpake2p_WS_Length],
609 : kSpake2p_WS_Length);
610 7 : SuccessOrExit(err);
611 :
612 7 : err = SendMsg1();
613 7 : SuccessOrExit(err);
614 :
615 7 : exit:
616 14 : if (err != CHIP_NO_ERROR)
617 : {
618 0 : SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam);
619 : }
620 14 : return err;
621 7 : }
622 :
623 7 : CHIP_ERROR PASESession::SendMsg1()
624 : {
625 : MATTER_TRACE_SCOPE("SendMsg1", "PASESession");
626 7 : const size_t max_msg_len = TLV::EstimateStructOverhead(kMAX_Point_Length);
627 7 : System::PacketBufferHandle msg = System::PacketBufferHandle::New(max_msg_len);
628 7 : VerifyOrReturnError(!msg.IsNull(), CHIP_ERROR_NO_MEMORY);
629 :
630 7 : System::PacketBufferTLVWriter tlvWriter;
631 7 : tlvWriter.Init(std::move(msg));
632 :
633 7 : TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified;
634 7 : ReturnErrorOnFailure(tlvWriter.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainerType));
635 :
636 : uint8_t X[kMAX_Point_Length];
637 7 : size_t X_len = sizeof(X);
638 :
639 7 : ReturnErrorOnFailure(mSpake2p.ComputeRoundOne(nullptr, 0, X, &X_len));
640 7 : VerifyOrReturnError(X_len == sizeof(X), CHIP_ERROR_INTERNAL);
641 7 : ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(Pake1Tags::kPa), ByteSpan(X)));
642 7 : ReturnErrorOnFailure(tlvWriter.EndContainer(outerContainerType));
643 7 : ReturnErrorOnFailure(tlvWriter.Finalize(&msg));
644 :
645 7 : ReturnErrorOnFailure(
646 : mExchangeCtxt.Value()->SendMessage(MsgType::PASE_Pake1, std::move(msg), SendFlags(SendMessageFlags::kExpectResponse)));
647 7 : ChipLogDetail(SecureChannel, "Sent spake2p msg1");
648 :
649 7 : mNextExpectedMsg.SetValue(MsgType::PASE_Pake2);
650 :
651 7 : return CHIP_NO_ERROR;
652 7 : }
653 :
654 7 : CHIP_ERROR PASESession::HandleMsg1_and_SendMsg2(System::PacketBufferHandle && msg1)
655 : {
656 : MATTER_TRACE_SCOPE("HandleMsg1_and_SendMsg2", "PASESession");
657 7 : CHIP_ERROR err = CHIP_NO_ERROR;
658 :
659 : uint8_t Y[kMAX_Point_Length];
660 7 : size_t Y_len = sizeof(Y);
661 :
662 : uint8_t verifier[kMAX_Hash_Length];
663 7 : size_t verifier_len = kMAX_Hash_Length;
664 :
665 7 : ChipLogDetail(SecureChannel, "Received spake2p msg1");
666 : MATTER_TRACE_SCOPE("Pake1", "PASESession");
667 :
668 7 : System::PacketBufferTLVReader tlvReader;
669 7 : TLV::TLVType containerType = TLV::kTLVType_Structure;
670 :
671 : const uint8_t * X;
672 7 : size_t X_len = 0;
673 :
674 7 : tlvReader.Init(std::move(msg1));
675 7 : SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag()));
676 7 : SuccessOrExit(err = tlvReader.EnterContainer(containerType));
677 :
678 7 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(Pake1Tags::kPa)));
679 7 : X_len = tlvReader.GetLength();
680 7 : VerifyOrExit(X_len == kMAX_Point_Length, err = CHIP_ERROR_INVALID_TLV_ELEMENT);
681 7 : SuccessOrExit(err = tlvReader.GetDataPtr(X));
682 :
683 7 : SuccessOrExit(err = tlvReader.ExitContainer(containerType));
684 :
685 7 : SuccessOrExit(err = mSpake2p.BeginVerifier(nullptr, 0, nullptr, 0, mPASEVerifier.mW0, kP256_FE_Length, mPASEVerifier.mL,
686 : kP256_Point_Length));
687 :
688 7 : SuccessOrExit(err = mSpake2p.ComputeRoundOne(X, X_len, Y, &Y_len));
689 7 : VerifyOrReturnError(Y_len == sizeof(Y), CHIP_ERROR_INTERNAL);
690 7 : SuccessOrExit(err = mSpake2p.ComputeRoundTwo(X, X_len, verifier, &verifier_len));
691 7 : msg1 = nullptr;
692 :
693 : {
694 7 : const size_t max_msg_len = TLV::EstimateStructOverhead(Y_len, verifier_len);
695 :
696 7 : System::PacketBufferHandle msg2 = System::PacketBufferHandle::New(max_msg_len);
697 7 : VerifyOrExit(!msg2.IsNull(), err = CHIP_ERROR_NO_MEMORY);
698 :
699 7 : System::PacketBufferTLVWriter tlvWriter;
700 7 : tlvWriter.Init(std::move(msg2));
701 :
702 7 : TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified;
703 7 : SuccessOrExit(err = tlvWriter.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainerType));
704 7 : SuccessOrExit(err = tlvWriter.Put(AsTlvContextTag(Pake2Tags::kPb), ByteSpan(Y)));
705 7 : SuccessOrExit(err = tlvWriter.Put(AsTlvContextTag(Pake2Tags::kCb), ByteSpan(verifier, verifier_len)));
706 7 : SuccessOrExit(err = tlvWriter.EndContainer(outerContainerType));
707 7 : SuccessOrExit(err = tlvWriter.Finalize(&msg2));
708 :
709 : err =
710 7 : mExchangeCtxt.Value()->SendMessage(MsgType::PASE_Pake2, std::move(msg2), SendFlags(SendMessageFlags::kExpectResponse));
711 7 : SuccessOrExit(err);
712 :
713 7 : mNextExpectedMsg.SetValue(MsgType::PASE_Pake3);
714 7 : }
715 :
716 7 : ChipLogDetail(SecureChannel, "Sent spake2p msg2");
717 : MATTER_TRACE_COUNTER("Pake2");
718 :
719 0 : exit:
720 :
721 14 : if (err != CHIP_NO_ERROR)
722 : {
723 0 : SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam);
724 : }
725 7 : return err;
726 7 : }
727 :
728 7 : CHIP_ERROR PASESession::HandleMsg2_and_SendMsg3(System::PacketBufferHandle && msg2)
729 : {
730 : MATTER_TRACE_SCOPE("HandleMsg2_and_SendMsg3", "PASESession");
731 7 : CHIP_ERROR err = CHIP_NO_ERROR;
732 :
733 : uint8_t verifier[kMAX_Hash_Length];
734 7 : size_t verifier_len = kMAX_Hash_Length;
735 :
736 7 : System::PacketBufferHandle resp;
737 :
738 7 : ChipLogDetail(SecureChannel, "Received spake2p msg2");
739 :
740 7 : System::PacketBufferTLVReader tlvReader;
741 7 : TLV::TLVType containerType = TLV::kTLVType_Structure;
742 :
743 : const uint8_t * Y;
744 7 : size_t Y_len = 0;
745 :
746 : const uint8_t * peer_verifier;
747 7 : size_t peer_verifier_len = 0;
748 :
749 7 : tlvReader.Init(std::move(msg2));
750 7 : SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag()));
751 7 : SuccessOrExit(err = tlvReader.EnterContainer(containerType));
752 :
753 7 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(Pake2Tags::kPb)));
754 7 : Y_len = tlvReader.GetLength();
755 7 : VerifyOrExit(Y_len == kMAX_Point_Length, err = CHIP_ERROR_INVALID_TLV_ELEMENT);
756 7 : SuccessOrExit(err = tlvReader.GetDataPtr(Y));
757 :
758 7 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(Pake2Tags::kCb)));
759 7 : peer_verifier_len = tlvReader.GetLength();
760 7 : VerifyOrExit(peer_verifier_len == kMAX_Hash_Length, err = CHIP_ERROR_INVALID_TLV_ELEMENT);
761 7 : 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 7 : SuccessOrExit(err = tlvReader.ExitContainer(containerType));
768 :
769 7 : SuccessOrExit(err = mSpake2p.ComputeRoundTwo(Y, Y_len, verifier, &verifier_len));
770 :
771 7 : SuccessOrExit(err = mSpake2p.KeyConfirm(peer_verifier, peer_verifier_len));
772 6 : msg2 = nullptr;
773 :
774 : {
775 6 : const size_t max_msg_len = TLV::EstimateStructOverhead(verifier_len);
776 :
777 6 : System::PacketBufferHandle msg3 = System::PacketBufferHandle::New(max_msg_len);
778 6 : VerifyOrExit(!msg3.IsNull(), err = CHIP_ERROR_NO_MEMORY);
779 :
780 6 : System::PacketBufferTLVWriter tlvWriter;
781 6 : tlvWriter.Init(std::move(msg3));
782 :
783 6 : TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified;
784 6 : SuccessOrExit(err = tlvWriter.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainerType));
785 6 : SuccessOrExit(err = tlvWriter.Put(AsTlvContextTag(Pake3Tags::kCa), ByteSpan(verifier, verifier_len)));
786 6 : SuccessOrExit(err = tlvWriter.EndContainer(outerContainerType));
787 6 : SuccessOrExit(err = tlvWriter.Finalize(&msg3));
788 :
789 : err =
790 6 : mExchangeCtxt.Value()->SendMessage(MsgType::PASE_Pake3, std::move(msg3), SendFlags(SendMessageFlags::kExpectResponse));
791 6 : SuccessOrExit(err);
792 :
793 6 : mNextExpectedMsg.SetValue(MsgType::StatusReport);
794 6 : }
795 6 : ChipLogDetail(SecureChannel, "Sent spake2p msg3");
796 :
797 0 : exit:
798 :
799 14 : if (err != CHIP_NO_ERROR)
800 : {
801 1 : SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam);
802 : }
803 14 : return err;
804 7 : }
805 :
806 6 : CHIP_ERROR PASESession::HandleMsg3(System::PacketBufferHandle && msg)
807 : {
808 : MATTER_TRACE_SCOPE("HandleMsg3", "PASESession");
809 6 : CHIP_ERROR err = CHIP_NO_ERROR;
810 :
811 6 : ChipLogDetail(SecureChannel, "Received spake2p msg3");
812 : MATTER_TRACE_COUNTER("Pake3");
813 :
814 6 : mNextExpectedMsg.ClearValue();
815 :
816 6 : System::PacketBufferTLVReader tlvReader;
817 6 : TLV::TLVType containerType = TLV::kTLVType_Structure;
818 :
819 : const uint8_t * peer_verifier;
820 6 : size_t peer_verifier_len = 0;
821 :
822 6 : tlvReader.Init(std::move(msg));
823 6 : SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag()));
824 6 : SuccessOrExit(err = tlvReader.EnterContainer(containerType));
825 :
826 6 : SuccessOrExit(err = tlvReader.Next(AsTlvContextTag(Pake3Tags::kCa)));
827 6 : peer_verifier_len = tlvReader.GetLength();
828 6 : VerifyOrExit(peer_verifier_len == kMAX_Hash_Length, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
829 6 : 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 6 : SuccessOrExit(err = tlvReader.ExitContainer(containerType));
836 :
837 6 : 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 6 : SendStatusReport(mExchangeCtxt, kProtocolCodeSuccess);
841 :
842 6 : Finish();
843 6 : exit:
844 :
845 12 : if (err != CHIP_NO_ERROR)
846 : {
847 0 : SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam);
848 : }
849 12 : return err;
850 6 : }
851 :
852 6 : void PASESession::OnSuccessStatusReport()
853 : {
854 6 : Finish();
855 6 : }
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 41 : CHIP_ERROR PASESession::ValidateReceivedMessage(ExchangeContext * exchange, const PayloadHeader & payloadHeader,
877 : const System::PacketBufferHandle & msg)
878 : {
879 41 : 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 41 : if (mExchangeCtxt.HasValue())
885 : {
886 34 : if (&mExchangeCtxt.Value().Get() != exchange)
887 : {
888 0 : ReturnErrorOnFailure(CHIP_ERROR_INVALID_ARGUMENT);
889 : }
890 : }
891 : else
892 : {
893 7 : mExchangeCtxt.Emplace(*exchange);
894 : }
895 :
896 41 : 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 41 : mExchangeCtxt.Value()->UseSuggestedResponseTimeout(kExpectedHighProcessingTime);
903 :
904 41 : VerifyOrReturnError(!msg.IsNull(), CHIP_ERROR_INVALID_ARGUMENT);
905 41 : VerifyOrReturnError((mNextExpectedMsg.HasValue() && payloadHeader.HasMessageType(mNextExpectedMsg.Value())) ||
906 : payloadHeader.HasMessageType(MsgType::StatusReport),
907 : CHIP_ERROR_INVALID_MESSAGE_TYPE);
908 :
909 41 : 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 41 : CHIP_ERROR PASESession::OnMessageReceived(ExchangeContext * exchange, const PayloadHeader & payloadHeader,
920 : System::PacketBufferHandle && msg)
921 : {
922 : MATTER_TRACE_SCOPE("OnMessageReceived", "PASESession");
923 41 : CHIP_ERROR err = ValidateReceivedMessage(exchange, payloadHeader, msg);
924 41 : MsgType msgType = static_cast<MsgType>(payloadHeader.GetMessageType());
925 41 : 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 41 : switch (msgType)
936 : {
937 7 : case MsgType::PBKDFParamRequest:
938 7 : err = HandlePBKDFParamRequest(std::move(msg));
939 7 : break;
940 :
941 7 : case MsgType::PBKDFParamResponse:
942 7 : err = HandlePBKDFParamResponse(std::move(msg));
943 7 : break;
944 :
945 7 : case MsgType::PASE_Pake1:
946 7 : err = HandleMsg1_and_SendMsg2(std::move(msg));
947 7 : break;
948 :
949 7 : case MsgType::PASE_Pake2:
950 7 : err = HandleMsg2_and_SendMsg3(std::move(msg));
951 7 : break;
952 :
953 6 : case MsgType::PASE_Pake3:
954 6 : err = HandleMsg3(std::move(msg));
955 6 : break;
956 :
957 7 : case MsgType::StatusReport:
958 : err =
959 7 : HandleStatusReport(std::move(msg), mNextExpectedMsg.HasValue() && (mNextExpectedMsg.Value() == MsgType::StatusReport));
960 7 : break;
961 :
962 0 : default:
963 0 : err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
964 0 : break;
965 : };
966 :
967 41 : exit:
968 :
969 : // Call delegate to indicate pairing failure
970 82 : 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 41 : return err;
982 : }
983 :
984 : } // namespace chip
|