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