From aaab48e7f4cc476c64d476a40bbadf4dc556d7e4 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Fri, 27 Dec 2024 16:14:51 +0100 Subject: [PATCH 01/15] - Creating an overload of DecodeMRPParametersIfPresent that takes SessionParameters as an outparam and does not touch PairingSession class member mRemoteSessionParameters - a utility SetRemoteSessionParameters to be able to set PairingSession class member mRemoteSessionParameters --- .../secure_channel/PairingSession.cpp | 97 +++++++++++++++++++ src/protocols/secure_channel/PairingSession.h | 16 ++- 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/src/protocols/secure_channel/PairingSession.cpp b/src/protocols/secure_channel/PairingSession.cpp index 49f80713d8ed8c..7dae15e096225d 100644 --- a/src/protocols/secure_channel/PairingSession.cpp +++ b/src/protocols/secure_channel/PairingSession.cpp @@ -241,7 +241,104 @@ CHIP_ERROR PairingSession::DecodeMRPParametersIfPresent(TLV::Tag expectedTag, TL } return err; } +CHIP_ERROR PairingSession::DecodeMRPParametersIfPresent(TLV::Tag expectedTag, TLV::ContiguousBufferTLVReader & tlvReader, + SessionParameters & sessionParameters) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + // The MRP parameters are optional. + if (tlvReader.GetTag() != expectedTag) + { + return CHIP_NO_ERROR; + } + + TLV::TLVType containerType = TLV::kTLVType_Structure; + ReturnErrorOnFailure(tlvReader.EnterContainer(containerType)); + + ReturnErrorOnFailure(tlvReader.Next()); + + ChipLogDetail(SecureChannel, "Found MRP parameters in the message"); + + // All TLV elements in the structure are optional. If the first element is present, process it and move + // the TLV reader to the next element. + if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kSessionIdleInterval) + { + uint32_t idleRetransTimeout; + ReturnErrorOnFailure(tlvReader.Get(idleRetransTimeout)); + sessionParameters.SetMRPIdleRetransTimeout(System::Clock::Milliseconds32(idleRetransTimeout)); + + // The next element is optional. If it's not present, return CHIP_NO_ERROR. + SuccessOrExit(err = tlvReader.Next()); + } + + if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kSessionActiveInterval) + { + uint32_t activeRetransTimeout; + ReturnErrorOnFailure(tlvReader.Get(activeRetransTimeout)); + sessionParameters.SetMRPActiveRetransTimeout(System::Clock::Milliseconds32(activeRetransTimeout)); + + // The next element is optional. If it's not present, return CHIP_NO_ERROR. + SuccessOrExit(err = tlvReader.Next()); + } + + if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kSessionActiveThreshold) + { + uint16_t activeThresholdTime; + ReturnErrorOnFailure(tlvReader.Get(activeThresholdTime)); + sessionParameters.SetMRPActiveThresholdTime(System::Clock::Milliseconds16(activeThresholdTime)); + + // The next element is optional. If it's not present, return CHIP_NO_ERROR. + SuccessOrExit(err = tlvReader.Next()); + } + if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kDataModelRevision) + { + uint16_t dataModelRevision; + ReturnErrorOnFailure(tlvReader.Get(dataModelRevision)); + sessionParameters.SetDataModelRevision(dataModelRevision); + + // The next element is optional. If it's not present, return CHIP_NO_ERROR. + SuccessOrExit(err = tlvReader.Next()); + } + + if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kInteractionModelRevision) + { + uint16_t interactionModelRevision; + ReturnErrorOnFailure(tlvReader.Get(interactionModelRevision)); + sessionParameters.SetInteractionModelRevision(interactionModelRevision); + + // The next element is optional. If it's not present, return CHIP_NO_ERROR. + SuccessOrExit(err = tlvReader.Next()); + } + + if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kSpecificationVersion) + { + uint32_t specificationVersion; + ReturnErrorOnFailure(tlvReader.Get(specificationVersion)); + sessionParameters.SetSpecificationVersion(specificationVersion); + + // The next element is optional. If it's not present, return CHIP_NO_ERROR. + SuccessOrExit(err = tlvReader.Next()); + } + + if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kMaxPathsPerInvoke) + { + uint16_t maxPathsPerInvoke; + ReturnErrorOnFailure(tlvReader.Get(maxPathsPerInvoke)); + sessionParameters.SetMaxPathsPerInvoke(maxPathsPerInvoke); + + // The next element is optional. If it's not present, return CHIP_NO_ERROR. + SuccessOrExit(err = tlvReader.Next()); + } + + // Future proofing - Don't error out if there are other tags +exit: + if (err == CHIP_END_OF_TLV) + { + return tlvReader.ExitContainer(containerType); + } + return err; +} bool PairingSession::IsSessionEstablishmentInProgress() { if (!mSecureSessionHolder) diff --git a/src/protocols/secure_channel/PairingSession.h b/src/protocols/secure_channel/PairingSession.h index abdb69786c59f6..85e0cd17edaf08 100644 --- a/src/protocols/secure_channel/PairingSession.h +++ b/src/protocols/secure_channel/PairingSession.h @@ -100,6 +100,7 @@ class DLL_EXPORT PairingSession : public SessionDelegate const ReliableMessageProtocolConfig & GetRemoteMRPConfig() const { return mRemoteSessionParams.GetMRPConfig(); } const SessionParameters & GetRemoteSessionParameters() const { return mRemoteSessionParams; } + void SetRemoteSessionParameters(const SessionParameters & sessionParams) { mRemoteSessionParams = sessionParams; } void SetRemoteMRPConfig(const ReliableMessageProtocolConfig & config) { mRemoteSessionParams.SetMRPConfig(config); } /** @@ -208,7 +209,7 @@ class DLL_EXPORT PairingSession : public SessionDelegate /** * Try to decode the current element (pointed by the TLV reader) as MRP parameters. - * If the MRP parameters are found, mRemoteSessionParams is updated with the devoded values. + * If the MRP parameters are found, mRemoteSessionParams is updated with the decoded values. * * MRP parameters are optional. So, if the TLV reader is not pointing to the MRP parameters, * the function is a noop. @@ -218,6 +219,19 @@ class DLL_EXPORT PairingSession : public SessionDelegate */ CHIP_ERROR DecodeMRPParametersIfPresent(TLV::Tag expectedTag, TLV::ContiguousBufferTLVReader & tlvReader); + /** + * Try to decode the current element (pointed by the TLV reader) as MRP parameters. + * If the MRP parameters are found, outparam sessionParameters is updated with the decoded values. + * + * MRP parameters are optional. So, if the TLV reader is not pointing to the MRP parameters, + * the function is a noop. + * + * If the parameters are present, but TLV reader fails to correctly parse it, the function will + * return the corresponding error. + */ + static CHIP_ERROR DecodeMRPParametersIfPresent(TLV::Tag expectedTag, TLV::ContiguousBufferTLVReader & tlvReader, + SessionParameters & sessionParameters); + bool IsSessionEstablishmentInProgress(); // TODO: remove Clear, we should create a new instance instead reset the old instance. From 2ca952f63f268d0ab401e991eae9f0a06a917ba5 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Fri, 27 Dec 2024 16:16:53 +0100 Subject: [PATCH 02/15] Making ParseSigma1 static and not dependent on class state --- src/protocols/secure_channel/CASESession.cpp | 4 +++- src/protocols/secure_channel/CASESession.h | 3 ++- .../secure_channel/tests/TestCASESession.cpp | 13 +++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index 6242cc2aa6199c..2cdc2b1f1af67e 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -1100,6 +1100,7 @@ CASESession::NextStep CASESession::HandleSigma1(System::PacketBufferHandle && ms // Set the MRP parameters provided in the Sigma1 message if (parsedSigma1.initiatorMrpParamsPresent) { + SetRemoteSessionParameters(parsedSigma1.initiatorSessionParams); mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters( GetRemoteSessionParameters()); } @@ -2324,7 +2325,8 @@ CHIP_ERROR CASESession::ParseSigma1(TLV::ContiguousBufferTLVReader & tlvReader, CHIP_ERROR err = tlvReader.Next(); if (err == CHIP_NO_ERROR && tlvReader.GetTag() == AsTlvContextTag(Sigma1Tags::kInitiatorSessionParams)) { - ReturnErrorOnFailure(DecodeMRPParametersIfPresent(AsTlvContextTag(Sigma1Tags::kInitiatorSessionParams), tlvReader)); + ReturnErrorOnFailure(DecodeMRPParametersIfPresent(AsTlvContextTag(Sigma1Tags::kInitiatorSessionParams), tlvReader, + outParsedSigma1.initiatorSessionParams)); outParsedSigma1.initiatorMrpParamsPresent = true; err = tlvReader.Next(); diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h index a9d43194d0f56d..31afb08a889f09 100644 --- a/src/protocols/secure_channel/CASESession.h +++ b/src/protocols/secure_channel/CASESession.h @@ -223,6 +223,7 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, { ByteSpan initiatorEphPubKey; bool initiatorMrpParamsPresent = false; + SessionParameters initiatorSessionParams; }; struct EncodeSigma2Inputs @@ -275,7 +276,7 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, * and the resumptionID and initiatorResumeMIC fields will be set to * valid values, or the sessionResumptionRequested field will be set to false. */ - CHIP_ERROR ParseSigma1(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma1 & parsedMessage); + static CHIP_ERROR ParseSigma1(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma1 & parsedMessage); /** * @brief Encodes a Sigma2 message into TLV format and allocates a buffer for it, which is owned by the PacketBufferHandle diff --git a/src/protocols/secure_channel/tests/TestCASESession.cpp b/src/protocols/secure_channel/tests/TestCASESession.cpp index 81a2142c716021..d302ffe6500546 100644 --- a/src/protocols/secure_channel/tests/TestCASESession.cpp +++ b/src/protocols/secure_channel/tests/TestCASESession.cpp @@ -760,10 +760,9 @@ static CHIP_ERROR EncodeSigma1Helper(MutableByteSpan & buf) \ TLV::ContiguousBufferTLVReader reader; \ reader.Init(buf); \ - CASESessionAccess session; \ CASESessionAccess::ParsedSigma1 parsedSigma1; \ \ - EXPECT_EQ(session.ParseSigma1(reader, parsedSigma1) == CHIP_NO_ERROR, params::kExpectSuccess); \ + EXPECT_EQ(CASESessionAccess::ParseSigma1(reader, parsedSigma1) == CHIP_NO_ERROR, params::kExpectSuccess); \ if (params::kExpectSuccess) \ { \ EXPECT_EQ(parsedSigma1.sessionResumptionRequested, \ @@ -936,10 +935,9 @@ TEST_F(TestCASESession, EncodeSigma1Test) System::PacketBufferTLVReader tlvReader; tlvReader.Init(std::move(msg1)); - CASESessionAccess session; CASESessionAccess::ParsedSigma1 parsedMessage; - EXPECT_EQ(CHIP_NO_ERROR, session.ParseSigma1(tlvReader, parsedMessage)); + EXPECT_EQ(CHIP_NO_ERROR, CASESessionAccess::ParseSigma1(tlvReader, parsedMessage)); // compare parsed values with original values EXPECT_TRUE(parsedMessage.initiatorRandom.data_equal(encodeParams.initiatorRandom)); @@ -970,10 +968,9 @@ TEST_F(TestCASESession, EncodeSigma1Test) System::PacketBufferTLVReader tlvReader; tlvReader.Init(std::move(msg2)); - CASESessionAccess session; CASESessionAccess::ParsedSigma1 parsedMessage; - EXPECT_EQ(CHIP_NO_ERROR, session.ParseSigma1(tlvReader, parsedMessage)); + EXPECT_EQ(CHIP_NO_ERROR, CASESessionAccess::ParseSigma1(tlvReader, parsedMessage)); // RoundTrip EXPECT_TRUE(parsedMessage.initiatorRandom.data_equal(encodeParams.initiatorRandom)); @@ -1111,8 +1108,8 @@ struct SessionResumptionTestStorage : SessionResumptionStorage { SessionResumptionTestStorage(CHIP_ERROR findMethodReturnCode, ScopedNodeId peerNodeId, ResumptionIdStorage * resumptionId, Crypto::P256ECDHDerivedSecret * sharedSecret) : - mFindMethodReturnCode(findMethodReturnCode), - mPeerNodeId(peerNodeId), mResumptionId(resumptionId), mSharedSecret(sharedSecret) + mFindMethodReturnCode(findMethodReturnCode), mPeerNodeId(peerNodeId), mResumptionId(resumptionId), + mSharedSecret(sharedSecret) {} SessionResumptionTestStorage(CHIP_ERROR findMethodReturnCode) : mFindMethodReturnCode(findMethodReturnCode) {} CHIP_ERROR FindByScopedNodeId(const ScopedNodeId & node, ResumptionIdStorage & resumptionId, From d7853615aad037d464500780421fca967d135c2c Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Fri, 27 Dec 2024 11:11:14 +0100 Subject: [PATCH 03/15] factoring out ParseSigma2 --- src/protocols/secure_channel/CASESession.cpp | 177 ++++++++++++------- src/protocols/secure_channel/CASESession.h | 16 ++ 2 files changed, 128 insertions(+), 65 deletions(-) diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index 2cdc2b1f1af67e..57f784e4cf2ea3 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -1442,7 +1442,7 @@ CHIP_ERROR CASESession::HandleSigma2Resume(System::PacketBufferHandle && msg) GetRemoteSessionParameters()); } - ChipLogDetail(SecureChannel, "Peer assigned session session ID %d", responderSessionId); + ChipLogDetail(SecureChannel, "Peer assigned session key ID %d", responderSessionId); SetPeerSessionId(responderSessionId); if (mSessionResumptionStorage != nullptr) @@ -1485,10 +1485,7 @@ CHIP_ERROR CASESession::HandleSigma2_and_SendSigma3(System::PacketBufferHandle & CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) { MATTER_TRACE_SCOPE("HandleSigma2", "CASESession"); - CHIP_ERROR err = CHIP_NO_ERROR; - System::PacketBufferTLVReader tlvReader; - TLV::TLVReader decryptedDataTlvReader; - TLV::TLVType containerType = TLV::kTLVType_Structure; + TLVReader decryptedDataTlvReader; const uint8_t * buf = msg->Start(); size_t buflen = msg->DataLength(); @@ -1496,13 +1493,8 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) uint8_t msg_salt[kIPKSize + kSigmaParamRandomNumberSize + kP256_PublicKey_Length + kSHA256_Hash_Length]; chip::Platform::ScopedMemoryBuffer msg_R2_Encrypted; - size_t msg_r2_encrypted_len = 0; - size_t msg_r2_encrypted_len_with_tag = 0; chip::Platform::ScopedMemoryBuffer msg_R2_Signed; - size_t msg_r2_signed_len; - size_t max_msg_r2_signed_enc_len; - constexpr size_t kCaseOverheadForFutureTbeData = 128; AutoReleaseSessionKey sr2k(*mSessionManager->GetSessionKeystore()); @@ -1511,13 +1503,24 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) NodeId responderNodeId; P256PublicKey responderPublicKey; - uint8_t responderRandom[kSigmaParamRandomNumberSize]; + // uint8_t responderRandom[kSigmaParamRandomNumberSize]; ByteSpan responderNOC; ByteSpan responderICAC; - uint16_t responderSessionId; + // uint16_t responderSessionId; + + TLVType containerType = kTLVType_Structure; + size_t msg_r2_signed_len; + + size_t msg_r2_encrypted_len = 0; ChipLogProgress(SecureChannel, "Received Sigma2 msg"); + CHIP_ERROR err = CHIP_NO_ERROR; + + System::PacketBufferTLVReader tlvReader; + tlvReader.Init(std::move(msg)); + + ParsedSigma2 parsedSigma2; FabricId fabricId = kUndefinedFabricId; { @@ -1530,24 +1533,16 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) VerifyOrExit(mEphemeralKey != nullptr, err = CHIP_ERROR_INTERNAL); VerifyOrExit(buf != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE); - tlvReader.Init(std::move(msg)); - SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag())); - SuccessOrExit(err = tlvReader.EnterContainer(containerType)); - - // Retrieve Responder's Random value - SuccessOrExit(err = tlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(Sigma2Tags::kResponderRandom))); - SuccessOrExit(err = tlvReader.GetBytes(responderRandom, sizeof(responderRandom))); + SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ buf, buflen })); - // Assign Session ID - SuccessOrExit(err = tlvReader.Next(TLV::kTLVType_UnsignedInteger, AsTlvContextTag(Sigma2Tags::kResponderSessionId))); - SuccessOrExit(err = tlvReader.Get(responderSessionId)); + ParseSigma2(tlvReader, parsedSigma2); - ChipLogDetail(SecureChannel, "Peer assigned session session ID %d", responderSessionId); - SetPeerSessionId(responderSessionId); + ChipLogDetail(SecureChannel, "Peer assigned session key ID %d", parsedSigma2.responderSessionId); + SetPeerSessionId(parsedSigma2.responderSessionId); - // Retrieve Responder's Ephemeral Pubkey - SuccessOrExit(err = tlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(Sigma2Tags::kResponderEphPubKey))); - SuccessOrExit(err = tlvReader.GetBytes(mRemotePubKey, static_cast(mRemotePubKey.Length()))); + // ParseSigma2 ensures that: + // mRemotePubKey.Length() == responderEphPubKey.size() == kP256_PublicKey_Length. + memcpy(mRemotePubKey.Bytes(), parsedSigma2.responderEphPubKey.data(), mRemotePubKey.Length()); // Generate a Shared Secret SuccessOrExit(err = mEphemeralKey->ECDH_derive_secret(mRemotePubKey, mSharedSecret)); @@ -1555,46 +1550,39 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) // Generate the S2K key { MutableByteSpan saltSpan(msg_salt); - SuccessOrExit(err = ConstructSaltSigma2(ByteSpan(responderRandom), mRemotePubKey, ByteSpan(mIPK), saltSpan)); - SuccessOrExit(err = DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR2Info), sr2k)); + ReturnErrorOnFailure( + err = ConstructSaltSigma2(ByteSpan(parsedSigma2.responderRandom), mRemotePubKey, ByteSpan(mIPK), saltSpan)); + ReturnErrorOnFailure(err = DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR2Info), sr2k)); } - SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ buf, buflen })); - - // Generate decrypted data - SuccessOrExit(err = tlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(Sigma2Tags::kEncrypted2))); - - max_msg_r2_signed_enc_len = - TLV::EstimateStructOverhead(Credentials::kMaxCHIPCertLength, Credentials::kMaxCHIPCertLength, tbsData2Signature.Length(), - SessionResumptionStorage::kResumptionIdSize, kCaseOverheadForFutureTbeData); - msg_r2_encrypted_len_with_tag = tlvReader.GetLength(); - - // Validate we did not receive a buffer larger than legal - VerifyOrExit(msg_r2_encrypted_len_with_tag <= max_msg_r2_signed_enc_len, err = CHIP_ERROR_INVALID_TLV_ELEMENT); - VerifyOrExit(msg_r2_encrypted_len_with_tag > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, err = CHIP_ERROR_INVALID_TLV_ELEMENT); - VerifyOrExit(msg_R2_Encrypted.Alloc(msg_r2_encrypted_len_with_tag), err = CHIP_ERROR_NO_MEMORY); + if (parsedSigma2.responderMrpParamsPresent) + { + SetRemoteSessionParameters(parsedSigma2.responderSessionParams); + mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters( + GetRemoteSessionParameters()); + } + /********************************************************************************************************************************** + */ - SuccessOrExit(err = tlvReader.GetBytes(msg_R2_Encrypted.Get(), static_cast(msg_r2_encrypted_len_with_tag))); - msg_r2_encrypted_len = msg_r2_encrypted_len_with_tag - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; + msg_r2_encrypted_len = parsedSigma2.msgR2Encrypted.AllocatedSize() - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; - SuccessOrExit(err = AES_CCM_decrypt(msg_R2_Encrypted.Get(), msg_r2_encrypted_len, nullptr, 0, - msg_R2_Encrypted.Get() + msg_r2_encrypted_len, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, - sr2k.KeyHandle(), kTBEData2_Nonce, kTBEDataNonceLength, msg_R2_Encrypted.Get())); + SuccessOrExit(err = AES_CCM_decrypt(parsedSigma2.msgR2Encrypted.Get(), msg_r2_encrypted_len, nullptr, 0, + parsedSigma2.msgR2Encrypted.Get() + msg_r2_encrypted_len, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, + sr2k.KeyHandle(), kTBEData2_Nonce, kTBEDataNonceLength, parsedSigma2.msgR2Encrypted.Get())); - decryptedDataTlvReader.Init(msg_R2_Encrypted.Get(), msg_r2_encrypted_len); - containerType = TLV::kTLVType_Structure; - SuccessOrExit(err = decryptedDataTlvReader.Next(containerType, TLV::AnonymousTag())); + decryptedDataTlvReader.Init(parsedSigma2.msgR2Encrypted.Get(), msg_r2_encrypted_len); + SuccessOrExit(err = decryptedDataTlvReader.Next(containerType, AnonymousTag())); SuccessOrExit(err = decryptedDataTlvReader.EnterContainer(containerType)); - SuccessOrExit(err = decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSenderNOC))); + SuccessOrExit(err = decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSenderNOC))); SuccessOrExit(err = decryptedDataTlvReader.Get(responderNOC)); SuccessOrExit(err = decryptedDataTlvReader.Next()); if (decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSenderICAC)) { - VerifyOrExit(decryptedDataTlvReader.GetType() == TLV::kTLVType_ByteString, err = CHIP_ERROR_WRONG_TLV_TYPE); + VerifyOrExit(decryptedDataTlvReader.GetType() == kTLVType_ByteString, err = CHIP_ERROR_WRONG_TLV_TYPE); SuccessOrExit(err = decryptedDataTlvReader.Get(responderICAC)); - SuccessOrExit(err = decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSignature))); + SuccessOrExit(err = decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSignature))); } // Validate responder identity located in msg_r2_encrypted @@ -1612,8 +1600,8 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) } // Construct msg_R2_Signed and validate the signature in msg_r2_encrypted - msg_r2_signed_len = TLV::EstimateStructOverhead(sizeof(uint16_t), responderNOC.size(), responderICAC.size(), - kP256_PublicKey_Length, kP256_PublicKey_Length); + msg_r2_signed_len = EstimateStructOverhead(sizeof(uint16_t), responderNOC.size(), responderICAC.size(), kP256_PublicKey_Length, + kP256_PublicKey_Length); VerifyOrExit(msg_R2_Signed.Alloc(msg_r2_signed_len), err = CHIP_ERROR_NO_MEMORY); @@ -1630,20 +1618,12 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) SuccessOrExit(err = responderPublicKey.ECDSA_validate_msg_signature(msg_R2_Signed.Get(), msg_r2_signed_len, tbsData2Signature)); // Retrieve session resumption ID - SuccessOrExit(err = decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kResumptionID))); + SuccessOrExit(err = decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kResumptionID))); SuccessOrExit(err = decryptedDataTlvReader.GetBytes(mNewResumptionId.data(), mNewResumptionId.size())); // Retrieve peer CASE Authenticated Tags (CATs) from peer's NOC. SuccessOrExit(err = ExtractCATsFromOpCert(responderNOC, mPeerCATs)); - // Retrieve responderMRPParams if present - if (tlvReader.Next() != CHIP_END_OF_TLV) - { - SuccessOrExit(err = DecodeMRPParametersIfPresent(AsTlvContextTag(Sigma2Tags::kResponderSessionParams), tlvReader)); - mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters( - GetRemoteSessionParameters()); - } - exit: if (err != CHIP_NO_ERROR) { @@ -2378,6 +2358,73 @@ CHIP_ERROR CASESession::ParseSigma1(TLV::ContiguousBufferTLVReader & tlvReader, return CHIP_NO_ERROR; } +CHIP_ERROR CASESession::ParseSigma2(ContiguousBufferTLVReader & tlvReader, ParsedSigma2 & outParsedSigma2) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + TLVType containerType = kTLVType_Structure; + + // FIND A SOLUTION TO THIS: + P256ECDSASignature tbsData2Signature; + + ReturnErrorOnFailure(err = tlvReader.Next(containerType, AnonymousTag())); + ReturnErrorOnFailure(err = tlvReader.EnterContainer(containerType)); + + // Retrieve Responder's Random value + ReturnErrorOnFailure(err = tlvReader.Next(kTLVType_ByteString, AsTlvContextTag(Sigma2Tags::kResponderRandom))); + ReturnErrorOnFailure(err = tlvReader.GetByteView(outParsedSigma2.responderRandom)); + VerifyOrReturnError(outParsedSigma2.responderRandom.size() == kSigmaParamRandomNumberSize, CHIP_ERROR_INVALID_CASE_PARAMETER); + + // Assign Session ID + ReturnErrorOnFailure(err = tlvReader.Next(kTLVType_UnsignedInteger, AsTlvContextTag(Sigma2Tags::kResponderSessionId))); + ReturnErrorOnFailure(err = tlvReader.Get(outParsedSigma2.responderSessionId)); + + // Retrieve Responder's Ephemeral Pubkey + ReturnErrorOnFailure(err = tlvReader.Next(kTLVType_ByteString, AsTlvContextTag(Sigma2Tags::kResponderEphPubKey))); + ReturnErrorOnFailure(tlvReader.GetByteView(outParsedSigma2.responderEphPubKey)); + VerifyOrReturnError(outParsedSigma2.responderEphPubKey.size() == kP256_PublicKey_Length, CHIP_ERROR_INVALID_CASE_PARAMETER); + + // ReturnErrorOnFailure(err = tlvReader.GetBytes(mRemotePubKey, static_cast(mRemotePubKey.Length()))); + + // Generate decrypted data + ReturnErrorOnFailure(err = tlvReader.Next(kTLVType_ByteString, AsTlvContextTag(Sigma2Tags::kEncrypted2))); + + // TODO find a solution to this + // size_t msg_r2_encrypted_len = 0; + + size_t msg_r2_encrypted_len_with_tag = 0; + + constexpr size_t kCaseOverheadForFutureTbeData = 128; + + size_t max_msg_r2_signed_enc_len; + + max_msg_r2_signed_enc_len = + EstimateStructOverhead(Credentials::kMaxCHIPCertLength, Credentials::kMaxCHIPCertLength, tbsData2Signature.Length(), + SessionResumptionStorage::kResumptionIdSize, kCaseOverheadForFutureTbeData); + msg_r2_encrypted_len_with_tag = tlvReader.GetLength(); + + // Validate we did not receive a buffer larger than legal + // TODO why are comparing this to the "signed one" + // Maybe it is signed encrypted, but is the estimation calculated well? document it + VerifyOrReturnError(msg_r2_encrypted_len_with_tag <= max_msg_r2_signed_enc_len, err = CHIP_ERROR_INVALID_TLV_ELEMENT); + VerifyOrReturnError(msg_r2_encrypted_len_with_tag > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, err = CHIP_ERROR_INVALID_TLV_ELEMENT); + VerifyOrReturnError(outParsedSigma2.msgR2Encrypted.Alloc(msg_r2_encrypted_len_with_tag), err = CHIP_ERROR_NO_MEMORY); + + // TODO, should I keep this as GetBytes? or should I use GetByteView for consistency and do something else? + ReturnErrorOnFailure( + err = tlvReader.GetBytes(outParsedSigma2.msgR2Encrypted.Get(), outParsedSigma2.msgR2Encrypted.AllocatedSize())); + // msg_r2_encrypted_len = msg_r2_encrypted_len_with_tag - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; + + // Retrieve responderMRPParams if present + if (tlvReader.Next() != CHIP_END_OF_TLV) + { + ReturnErrorOnFailure(err = DecodeMRPParametersIfPresent(AsTlvContextTag(Sigma2Tags::kResponderSessionParams), tlvReader, + outParsedSigma2.responderSessionParams)); + outParsedSigma2.responderMrpParamsPresent = true; + } + + return CHIP_NO_ERROR; +} + CHIP_ERROR CASESession::ValidateReceivedMessage(ExchangeContext * ec, const PayloadHeader & payloadHeader, const System::PacketBufferHandle & msg) { diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h index 31afb08a889f09..fddb42304c1890 100644 --- a/src/protocols/secure_channel/CASESession.h +++ b/src/protocols/secure_channel/CASESession.h @@ -247,6 +247,20 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, const ReliableMessageProtocolConfig * responderMrpConfig; }; + struct ParsedSigma2 + { + // TODO responderRandom is ByteSpan in ParsedSigma2 but is an array in EncodeSigma2Inputs + ByteSpan responderRandom; + uint16_t responderSessionId; + // TODO, If I will inherit, remember that this different from EncodeSigma2Inputs + ByteSpan responderEphPubKey; + + Platform::ScopedMemoryBufferWithSize msgR2Encrypted; + size_t encrypted2Length = 0; + const ReliableMessageProtocolConfig * responderMrpConfig; + bool responderMrpParamsPresent = false; + SessionParameters responderSessionParams; + }; /** * @brief Encodes a Sigma1 message into TLV format and allocates a buffer for it, which is owned by the PacketBufferHandle * outparam. @@ -278,6 +292,8 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, */ static CHIP_ERROR ParseSigma1(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma1 & parsedMessage); + static CHIP_ERROR ParseSigma2(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma2 & parsedMessage); + /** * @brief Encodes a Sigma2 message into TLV format and allocates a buffer for it, which is owned by the PacketBufferHandle * outparam. From 403d4eed71bb5afd37792a5a65988b617dc0f5af Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Fri, 27 Dec 2024 20:11:27 +0100 Subject: [PATCH 04/15] factor out Parser for decrypted Sigma2 --- src/protocols/secure_channel/CASESession.cpp | 89 ++++++++++++------- src/protocols/secure_channel/CASESession.h | 10 +++ .../secure_channel/tests/TestCASESession.cpp | 4 +- 3 files changed, 68 insertions(+), 35 deletions(-) diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index 57f784e4cf2ea3..561ff476d05f89 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -1485,7 +1485,7 @@ CHIP_ERROR CASESession::HandleSigma2_and_SendSigma3(System::PacketBufferHandle & CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) { MATTER_TRACE_SCOPE("HandleSigma2", "CASESession"); - TLVReader decryptedDataTlvReader; + ContiguousBufferTLVReader decryptedDataTlvReader; const uint8_t * buf = msg->Start(); size_t buflen = msg->DataLength(); @@ -1504,12 +1504,12 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) P256PublicKey responderPublicKey; // uint8_t responderRandom[kSigmaParamRandomNumberSize]; - ByteSpan responderNOC; - ByteSpan responderICAC; + // ByteSpan responderNOC; + // ByteSpan responderICAC; // uint16_t responderSessionId; - TLVType containerType = kTLVType_Structure; + // TLVType containerType = kTLVType_Structure; size_t msg_r2_signed_len; size_t msg_r2_encrypted_len = 0; @@ -1521,6 +1521,7 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) tlvReader.Init(std::move(msg)); ParsedSigma2 parsedSigma2; + ParsedSigma2TBEData parsedSigma2TBEData; FabricId fabricId = kUndefinedFabricId; { @@ -1533,8 +1534,6 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) VerifyOrExit(mEphemeralKey != nullptr, err = CHIP_ERROR_INTERNAL); VerifyOrExit(buf != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE); - SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ buf, buflen })); - ParseSigma2(tlvReader, parsedSigma2); ChipLogDetail(SecureChannel, "Peer assigned session key ID %d", parsedSigma2.responderSessionId); @@ -1545,7 +1544,7 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) memcpy(mRemotePubKey.Bytes(), parsedSigma2.responderEphPubKey.data(), mRemotePubKey.Length()); // Generate a Shared Secret - SuccessOrExit(err = mEphemeralKey->ECDH_derive_secret(mRemotePubKey, mSharedSecret)); + ReturnErrorOnFailure(err = mEphemeralKey->ECDH_derive_secret(mRemotePubKey, mSharedSecret)); // Generate the S2K key { @@ -1554,6 +1553,9 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) err = ConstructSaltSigma2(ByteSpan(parsedSigma2.responderRandom), mRemotePubKey, ByteSpan(mIPK), saltSpan)); ReturnErrorOnFailure(err = DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR2Info), sr2k)); } + // TODO verify location of this + // TODO why does this trigger a fialure if i move it just after call to PArseSigma2 + ReturnErrorOnFailure(err = mCommissioningHash.AddData(ByteSpan{ buf, buflen })); if (parsedSigma2.responderMrpParamsPresent) { @@ -1571,19 +1573,10 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) sr2k.KeyHandle(), kTBEData2_Nonce, kTBEDataNonceLength, parsedSigma2.msgR2Encrypted.Get())); decryptedDataTlvReader.Init(parsedSigma2.msgR2Encrypted.Get(), msg_r2_encrypted_len); - SuccessOrExit(err = decryptedDataTlvReader.Next(containerType, AnonymousTag())); - SuccessOrExit(err = decryptedDataTlvReader.EnterContainer(containerType)); - SuccessOrExit(err = decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSenderNOC))); - SuccessOrExit(err = decryptedDataTlvReader.Get(responderNOC)); + ParseSigma2TBEData(decryptedDataTlvReader, parsedSigma2TBEData); - SuccessOrExit(err = decryptedDataTlvReader.Next()); - if (decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSenderICAC)) - { - VerifyOrExit(decryptedDataTlvReader.GetType() == kTLVType_ByteString, err = CHIP_ERROR_WRONG_TLV_TYPE); - SuccessOrExit(err = decryptedDataTlvReader.Get(responderICAC)); - SuccessOrExit(err = decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSignature))); - } + std::copy(parsedSigma2TBEData.resumptionId.begin(), parsedSigma2TBEData.resumptionId.end(), mNewResumptionId.begin()); // Validate responder identity located in msg_r2_encrypted // Constructing responder identity @@ -1591,7 +1584,8 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) CompressedFabricId unused; FabricId responderFabricId; SuccessOrExit(err = SetEffectiveTime()); - SuccessOrExit(err = mFabricsTable->VerifyCredentials(mFabricIndex, responderNOC, responderICAC, mValidContext, unused, + SuccessOrExit(err = mFabricsTable->VerifyCredentials(mFabricIndex, parsedSigma2TBEData.responderNOC, + parsedSigma2TBEData.responderICAC, mValidContext, unused, responderFabricId, responderNodeId, responderPublicKey)); VerifyOrExit(fabricId == responderFabricId, err = CHIP_ERROR_INVALID_CASE_PARAMETER); // Verify that responderNodeId (from responderNOC) matches one that was included @@ -1600,29 +1594,23 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) } // Construct msg_R2_Signed and validate the signature in msg_r2_encrypted - msg_r2_signed_len = EstimateStructOverhead(sizeof(uint16_t), responderNOC.size(), responderICAC.size(), kP256_PublicKey_Length, - kP256_PublicKey_Length); + msg_r2_signed_len = + EstimateStructOverhead(sizeof(uint16_t), parsedSigma2TBEData.responderNOC.size(), parsedSigma2TBEData.responderICAC.size(), + kP256_PublicKey_Length, kP256_PublicKey_Length); VerifyOrExit(msg_R2_Signed.Alloc(msg_r2_signed_len), err = CHIP_ERROR_NO_MEMORY); - SuccessOrExit(err = ConstructTBSData(responderNOC, responderICAC, ByteSpan(mRemotePubKey, mRemotePubKey.Length()), + SuccessOrExit(err = ConstructTBSData(parsedSigma2TBEData.responderNOC, parsedSigma2TBEData.responderICAC, + ByteSpan(mRemotePubKey, mRemotePubKey.Length()), ByteSpan(mEphemeralKey->Pubkey(), mEphemeralKey->Pubkey().Length()), msg_R2_Signed.Get(), msg_r2_signed_len)); - VerifyOrExit(decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSignature), err = CHIP_ERROR_INVALID_TLV_TAG); - VerifyOrExit(tbsData2Signature.Capacity() >= decryptedDataTlvReader.GetLength(), err = CHIP_ERROR_INVALID_TLV_ELEMENT); - tbsData2Signature.SetLength(decryptedDataTlvReader.GetLength()); - SuccessOrExit(err = decryptedDataTlvReader.GetBytes(tbsData2Signature.Bytes(), tbsData2Signature.Length())); - // Validate signature - SuccessOrExit(err = responderPublicKey.ECDSA_validate_msg_signature(msg_R2_Signed.Get(), msg_r2_signed_len, tbsData2Signature)); - - // Retrieve session resumption ID - SuccessOrExit(err = decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kResumptionID))); - SuccessOrExit(err = decryptedDataTlvReader.GetBytes(mNewResumptionId.data(), mNewResumptionId.size())); + SuccessOrExit(err = responderPublicKey.ECDSA_validate_msg_signature(msg_R2_Signed.Get(), msg_r2_signed_len, + parsedSigma2TBEData.tbsData2Signature)); // Retrieve peer CASE Authenticated Tags (CATs) from peer's NOC. - SuccessOrExit(err = ExtractCATsFromOpCert(responderNOC, mPeerCATs)); + SuccessOrExit(err = ExtractCATsFromOpCert(parsedSigma2TBEData.responderNOC, mPeerCATs)); exit: if (err != CHIP_NO_ERROR) @@ -1632,6 +1620,41 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) return err; } +CHIP_ERROR CASESession::ParseSigma2TBEData(ContiguousBufferTLVReader & decryptedDataTlvReader, + ParsedSigma2TBEData & outParsedSigma2TBE) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + TLVType containerType = kTLVType_Structure; + + ReturnErrorOnFailure(err = decryptedDataTlvReader.Next(containerType, AnonymousTag())); + ReturnErrorOnFailure(err = decryptedDataTlvReader.EnterContainer(containerType)); + + ReturnErrorOnFailure(err = decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSenderNOC))); + ReturnErrorOnFailure(err = decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.responderNOC)); + + ReturnErrorOnFailure(err = decryptedDataTlvReader.Next()); + if (decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSenderICAC)) + { + VerifyOrReturnError(decryptedDataTlvReader.GetType() == kTLVType_ByteString, err = CHIP_ERROR_WRONG_TLV_TYPE); + ReturnErrorOnFailure(err = decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.responderICAC)); + ReturnErrorOnFailure(err = decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSignature))); + } + + VerifyOrReturnError(decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSignature), + err = CHIP_ERROR_INVALID_TLV_TAG); + VerifyOrReturnError(outParsedSigma2TBE.tbsData2Signature.Capacity() >= decryptedDataTlvReader.GetLength(), + err = CHIP_ERROR_INVALID_TLV_ELEMENT); + outParsedSigma2TBE.tbsData2Signature.SetLength(decryptedDataTlvReader.GetLength()); + ReturnErrorOnFailure(err = decryptedDataTlvReader.GetBytes(outParsedSigma2TBE.tbsData2Signature.Bytes(), + outParsedSigma2TBE.tbsData2Signature.Length())); + + // Retrieve session resumption ID + ReturnErrorOnFailure(err = decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kResumptionID))); + ReturnErrorOnFailure(err = decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.resumptionId)); + + return CHIP_NO_ERROR; +} + CHIP_ERROR CASESession::SendSigma3a() { MATTER_TRACE_SCOPE("SendSigma3", "CASESession"); diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h index fddb42304c1890..cdc4628f5150a8 100644 --- a/src/protocols/secure_channel/CASESession.h +++ b/src/protocols/secure_channel/CASESession.h @@ -261,6 +261,15 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, bool responderMrpParamsPresent = false; SessionParameters responderSessionParams; }; + + struct ParsedSigma2TBEData + { + ByteSpan responderNOC; + ByteSpan responderICAC; + Crypto::P256ECDSASignature tbsData2Signature; + ByteSpan resumptionId; + }; + /** * @brief Encodes a Sigma1 message into TLV format and allocates a buffer for it, which is owned by the PacketBufferHandle * outparam. @@ -294,6 +303,7 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, static CHIP_ERROR ParseSigma2(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma2 & parsedMessage); + static CHIP_ERROR ParseSigma2TBEData(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma2TBEData & parsedMessage); /** * @brief Encodes a Sigma2 message into TLV format and allocates a buffer for it, which is owned by the PacketBufferHandle * outparam. diff --git a/src/protocols/secure_channel/tests/TestCASESession.cpp b/src/protocols/secure_channel/tests/TestCASESession.cpp index d302ffe6500546..a330d58197ff4d 100644 --- a/src/protocols/secure_channel/tests/TestCASESession.cpp +++ b/src/protocols/secure_channel/tests/TestCASESession.cpp @@ -1108,8 +1108,8 @@ struct SessionResumptionTestStorage : SessionResumptionStorage { SessionResumptionTestStorage(CHIP_ERROR findMethodReturnCode, ScopedNodeId peerNodeId, ResumptionIdStorage * resumptionId, Crypto::P256ECDHDerivedSecret * sharedSecret) : - mFindMethodReturnCode(findMethodReturnCode), mPeerNodeId(peerNodeId), mResumptionId(resumptionId), - mSharedSecret(sharedSecret) + mFindMethodReturnCode(findMethodReturnCode), + mPeerNodeId(peerNodeId), mResumptionId(resumptionId), mSharedSecret(sharedSecret) {} SessionResumptionTestStorage(CHIP_ERROR findMethodReturnCode) : mFindMethodReturnCode(findMethodReturnCode) {} CHIP_ERROR FindByScopedNodeId(const ScopedNodeId & node, ResumptionIdStorage & resumptionId, From b085862b8cf12918b2124c0d6394411487efb6d9 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Sun, 29 Dec 2024 21:04:39 +0100 Subject: [PATCH 05/15] various cleanup --- src/protocols/secure_channel/CASESession.cpp | 64 +++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index 561ff476d05f89..c3ee4bf24482aa 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -1265,7 +1265,7 @@ CHIP_ERROR CASESession::PrepareSigma2(EncodeSigma2Inputs & outSigma2Data) size_t msgR2SignedLen = EstimateStructOverhead(kMaxCHIPCertLength, // responderNoc kMaxCHIPCertLength, // responderICAC kP256_PublicKey_Length, // responderEphPubKey - kP256_PublicKey_Length // InitiatorEphPubKey + kP256_PublicKey_Length // initiatorEphPubKey ); P256ECDSASignature tbsData2Signature; @@ -1494,7 +1494,7 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) chip::Platform::ScopedMemoryBuffer msg_R2_Encrypted; - chip::Platform::ScopedMemoryBuffer msg_R2_Signed; + chip::Platform::ScopedMemoryBuffer msgR2Signed; AutoReleaseSessionKey sr2k(*mSessionManager->GetSessionKeystore()); @@ -1510,9 +1510,9 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) // uint16_t responderSessionId; // TLVType containerType = kTLVType_Structure; - size_t msg_r2_signed_len; + size_t msgR2SignedLen; - size_t msg_r2_encrypted_len = 0; + size_t msgR2EncryptedLen = 0; ChipLogProgress(SecureChannel, "Received Sigma2 msg"); CHIP_ERROR err = CHIP_NO_ERROR; @@ -1534,7 +1534,7 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) VerifyOrExit(mEphemeralKey != nullptr, err = CHIP_ERROR_INTERNAL); VerifyOrExit(buf != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE); - ParseSigma2(tlvReader, parsedSigma2); + SuccessOrExit(err = ParseSigma2(tlvReader, parsedSigma2)); ChipLogDetail(SecureChannel, "Peer assigned session key ID %d", parsedSigma2.responderSessionId); SetPeerSessionId(parsedSigma2.responderSessionId); @@ -1553,8 +1553,7 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) err = ConstructSaltSigma2(ByteSpan(parsedSigma2.responderRandom), mRemotePubKey, ByteSpan(mIPK), saltSpan)); ReturnErrorOnFailure(err = DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR2Info), sr2k)); } - // TODO verify location of this - // TODO why does this trigger a fialure if i move it just after call to PArseSigma2 + // Msg2 should only be added to MessageDigest after we construct SaltSigma2 that is used to derive S2K ReturnErrorOnFailure(err = mCommissioningHash.AddData(ByteSpan{ buf, buflen })); if (parsedSigma2.responderMrpParamsPresent) @@ -1566,19 +1565,19 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) /********************************************************************************************************************************** */ - msg_r2_encrypted_len = parsedSigma2.msgR2Encrypted.AllocatedSize() - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; + msgR2EncryptedLen = parsedSigma2.msgR2Encrypted.AllocatedSize() - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; - SuccessOrExit(err = AES_CCM_decrypt(parsedSigma2.msgR2Encrypted.Get(), msg_r2_encrypted_len, nullptr, 0, - parsedSigma2.msgR2Encrypted.Get() + msg_r2_encrypted_len, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, + SuccessOrExit(err = AES_CCM_decrypt(parsedSigma2.msgR2Encrypted.Get(), msgR2EncryptedLen, nullptr, 0, + parsedSigma2.msgR2Encrypted.Get() + msgR2EncryptedLen, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, sr2k.KeyHandle(), kTBEData2_Nonce, kTBEDataNonceLength, parsedSigma2.msgR2Encrypted.Get())); - decryptedDataTlvReader.Init(parsedSigma2.msgR2Encrypted.Get(), msg_r2_encrypted_len); + decryptedDataTlvReader.Init(parsedSigma2.msgR2Encrypted.Get(), msgR2EncryptedLen); - ParseSigma2TBEData(decryptedDataTlvReader, parsedSigma2TBEData); + SuccessOrExit(err = ParseSigma2TBEData(decryptedDataTlvReader, parsedSigma2TBEData)); std::copy(parsedSigma2TBEData.resumptionId.begin(), parsedSigma2TBEData.resumptionId.end(), mNewResumptionId.begin()); - // Validate responder identity located in msg_r2_encrypted + // Validate responder identity located in msgR2Encrypted // Constructing responder identity { CompressedFabricId unused; @@ -1593,20 +1592,20 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) VerifyOrExit(mPeerNodeId == responderNodeId, err = CHIP_ERROR_INVALID_CASE_PARAMETER); } - // Construct msg_R2_Signed and validate the signature in msg_r2_encrypted - msg_r2_signed_len = + // Construct msgR2Signed and validate the signature in msgR2Encrypted. + msgR2SignedLen = EstimateStructOverhead(sizeof(uint16_t), parsedSigma2TBEData.responderNOC.size(), parsedSigma2TBEData.responderICAC.size(), kP256_PublicKey_Length, kP256_PublicKey_Length); - VerifyOrExit(msg_R2_Signed.Alloc(msg_r2_signed_len), err = CHIP_ERROR_NO_MEMORY); + VerifyOrExit(msgR2Signed.Alloc(msgR2SignedLen), err = CHIP_ERROR_NO_MEMORY); SuccessOrExit(err = ConstructTBSData(parsedSigma2TBEData.responderNOC, parsedSigma2TBEData.responderICAC, ByteSpan(mRemotePubKey, mRemotePubKey.Length()), - ByteSpan(mEphemeralKey->Pubkey(), mEphemeralKey->Pubkey().Length()), msg_R2_Signed.Get(), - msg_r2_signed_len)); + ByteSpan(mEphemeralKey->Pubkey(), mEphemeralKey->Pubkey().Length()), msgR2Signed.Get(), + msgR2SignedLen)); // Validate signature - SuccessOrExit(err = responderPublicKey.ECDSA_validate_msg_signature(msg_R2_Signed.Get(), msg_r2_signed_len, + SuccessOrExit(err = responderPublicKey.ECDSA_validate_msg_signature(msgR2Signed.Get(), msgR2SignedLen, parsedSigma2TBEData.tbsData2Signature)); // Retrieve peer CASE Authenticated Tags (CATs) from peer's NOC. @@ -1631,12 +1630,15 @@ CHIP_ERROR CASESession::ParseSigma2TBEData(ContiguousBufferTLVReader & decrypted ReturnErrorOnFailure(err = decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSenderNOC))); ReturnErrorOnFailure(err = decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.responderNOC)); + VerifyOrReturnError(outParsedSigma2TBE.responderNOC.size() <= kMaxCHIPCertLength, CHIP_ERROR_INVALID_CASE_PARAMETER); ReturnErrorOnFailure(err = decryptedDataTlvReader.Next()); if (decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSenderICAC)) { VerifyOrReturnError(decryptedDataTlvReader.GetType() == kTLVType_ByteString, err = CHIP_ERROR_WRONG_TLV_TYPE); ReturnErrorOnFailure(err = decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.responderICAC)); + VerifyOrReturnError(outParsedSigma2TBE.responderICAC.size() <= kMaxCHIPCertLength, CHIP_ERROR_INVALID_CASE_PARAMETER); + ReturnErrorOnFailure(err = decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSignature))); } @@ -1651,6 +1653,8 @@ CHIP_ERROR CASESession::ParseSigma2TBEData(ContiguousBufferTLVReader & decrypted // Retrieve session resumption ID ReturnErrorOnFailure(err = decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kResumptionID))); ReturnErrorOnFailure(err = decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.resumptionId)); + VerifyOrReturnError(outParsedSigma2TBE.resumptionId.size() == SessionResumptionStorage::kResumptionIdSize, + CHIP_ERROR_INVALID_CASE_PARAMETER); return CHIP_NO_ERROR; } @@ -2412,30 +2416,30 @@ CHIP_ERROR CASESession::ParseSigma2(ContiguousBufferTLVReader & tlvReader, Parse ReturnErrorOnFailure(err = tlvReader.Next(kTLVType_ByteString, AsTlvContextTag(Sigma2Tags::kEncrypted2))); // TODO find a solution to this - // size_t msg_r2_encrypted_len = 0; + // size_t msgR2EncryptedLen = 0; - size_t msg_r2_encrypted_len_with_tag = 0; + size_t msgR2EncryptedLenWithTag = 0; constexpr size_t kCaseOverheadForFutureTbeData = 128; - size_t max_msg_r2_signed_enc_len; + size_t maxMsgR2SignedEncLen; - max_msg_r2_signed_enc_len = - EstimateStructOverhead(Credentials::kMaxCHIPCertLength, Credentials::kMaxCHIPCertLength, tbsData2Signature.Length(), - SessionResumptionStorage::kResumptionIdSize, kCaseOverheadForFutureTbeData); - msg_r2_encrypted_len_with_tag = tlvReader.GetLength(); + maxMsgR2SignedEncLen = EstimateStructOverhead(kMaxCHIPCertLength, kMaxCHIPCertLength, tbsData2Signature.Length(), + SessionResumptionStorage::kResumptionIdSize, kCaseOverheadForFutureTbeData); + msgR2EncryptedLenWithTag = tlvReader.GetLength(); // Validate we did not receive a buffer larger than legal // TODO why are comparing this to the "signed one" // Maybe it is signed encrypted, but is the estimation calculated well? document it - VerifyOrReturnError(msg_r2_encrypted_len_with_tag <= max_msg_r2_signed_enc_len, err = CHIP_ERROR_INVALID_TLV_ELEMENT); - VerifyOrReturnError(msg_r2_encrypted_len_with_tag > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, err = CHIP_ERROR_INVALID_TLV_ELEMENT); - VerifyOrReturnError(outParsedSigma2.msgR2Encrypted.Alloc(msg_r2_encrypted_len_with_tag), err = CHIP_ERROR_NO_MEMORY); + VerifyOrReturnError(msgR2EncryptedLenWithTag <= maxMsgR2SignedEncLen, err = CHIP_ERROR_INVALID_TLV_ELEMENT); + VerifyOrReturnError(msgR2EncryptedLenWithTag > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, err = CHIP_ERROR_INVALID_TLV_ELEMENT); + // Allocate after making sure that msgR2EncryptedLenWithTag is within bounds + VerifyOrReturnError(outParsedSigma2.msgR2Encrypted.Alloc(msgR2EncryptedLenWithTag), err = CHIP_ERROR_NO_MEMORY); // TODO, should I keep this as GetBytes? or should I use GetByteView for consistency and do something else? ReturnErrorOnFailure( err = tlvReader.GetBytes(outParsedSigma2.msgR2Encrypted.Get(), outParsedSigma2.msgR2Encrypted.AllocatedSize())); - // msg_r2_encrypted_len = msg_r2_encrypted_len_with_tag - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; + // msgR2EncryptedLen = msgR2EncryptedLenWithTag - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; // Retrieve responderMRPParams if present if (tlvReader.Next() != CHIP_END_OF_TLV) From cc77e8cb3bf36bfedc24320075d37c12c843b12b Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Mon, 30 Dec 2024 20:05:49 +0100 Subject: [PATCH 06/15] factor out ParseSigma2Resume --- src/protocols/secure_channel/CASESession.cpp | 82 ++++++++++++-------- src/protocols/secure_channel/CASESession.h | 14 ++++ 2 files changed, 65 insertions(+), 31 deletions(-) diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index c3ee4bf24482aa..9d24e9fc8d30c0 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -1401,53 +1401,38 @@ CHIP_ERROR CASESession::HandleSigma2Resume(System::PacketBufferHandle && msg) MATTER_TRACE_SCOPE("HandleSigma2Resume", "CASESession"); CHIP_ERROR err = CHIP_NO_ERROR; System::PacketBufferTLVReader tlvReader; - TLV::TLVType containerType = TLV::kTLVType_Structure; - - uint16_t responderSessionId; - - uint32_t decodeTagIdSeq = 0; ChipLogDetail(SecureChannel, "Received Sigma2Resume msg"); MATTER_TRACE_COUNTER("Sigma2Resume"); MATTER_LOG_METRIC_END(kMetricDeviceCASESessionSigma1, err); - uint8_t sigma2ResumeMIC[CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES]; - - tlvReader.Init(std::move(msg)); - SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag())); - SuccessOrExit(err = tlvReader.EnterContainer(containerType)); - - SuccessOrExit(err = tlvReader.Next()); - VerifyOrExit(TLV::TagNumFromTag(tlvReader.GetTag()) == ++decodeTagIdSeq, err = CHIP_ERROR_INVALID_TLV_TAG); - SessionResumptionStorage::ResumptionIdStorage resumptionId; - VerifyOrExit(tlvReader.GetLength() == resumptionId.size(), err = CHIP_ERROR_INVALID_TLV_ELEMENT); - SuccessOrExit(err = tlvReader.GetBytes(resumptionId.data(), resumptionId.size())); + // uint8_t sigma2ResumeMIC[CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES]; - SuccessOrExit(err = tlvReader.Next()); - VerifyOrExit(TLV::TagNumFromTag(tlvReader.GetTag()) == ++decodeTagIdSeq, err = CHIP_ERROR_INVALID_TLV_TAG); - VerifyOrExit(tlvReader.GetLength() == CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, err = CHIP_ERROR_INVALID_TLV_ELEMENT); - SuccessOrExit(err = tlvReader.GetBytes(sigma2ResumeMIC, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES)); + ParsedSigma2Resume parsedSigma2Resume; - SuccessOrExit(err = ValidateSigmaResumeMIC(ByteSpan(sigma2ResumeMIC), ByteSpan(mInitiatorRandom), resumptionId, - ByteSpan(kKDFS2RKeyInfo), ByteSpan(kResume2MIC_Nonce))); + tlvReader.Init(std::move(msg)); - SuccessOrExit(err = tlvReader.Next()); - VerifyOrExit(TLV::TagNumFromTag(tlvReader.GetTag()) == ++decodeTagIdSeq, err = CHIP_ERROR_INVALID_TLV_TAG); - SuccessOrExit(err = tlvReader.Get(responderSessionId)); + SuccessOrExit(err = ParseSigma2Resume(tlvReader, parsedSigma2Resume)); - if (tlvReader.Next() != CHIP_END_OF_TLV) + if (parsedSigma2Resume.responderMrpParamsPresent) { - SuccessOrExit(err = DecodeMRPParametersIfPresent(TLV::ContextTag(4), tlvReader)); + SetRemoteSessionParameters(parsedSigma2Resume.responderSessionParams); mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters( GetRemoteSessionParameters()); } - ChipLogDetail(SecureChannel, "Peer assigned session key ID %d", responderSessionId); - SetPeerSessionId(responderSessionId); + SuccessOrExit(err = ValidateSigmaResumeMIC(parsedSigma2Resume.sigma2ResumeMIC, ByteSpan(mInitiatorRandom), + parsedSigma2Resume.resumptionId, ByteSpan(kKDFS2RKeyInfo), + ByteSpan(kResume2MIC_Nonce))); + + ChipLogDetail(SecureChannel, "Peer assigned session key ID %d", parsedSigma2Resume.responderSessionId); + SetPeerSessionId(parsedSigma2Resume.responderSessionId); if (mSessionResumptionStorage != nullptr) { - CHIP_ERROR err2 = mSessionResumptionStorage->Save(GetPeer(), resumptionId, mSharedSecret, mPeerCATs); + CHIP_ERROR err2 = mSessionResumptionStorage->Save( + GetPeer(), SessionResumptionStorage::ConstResumptionIdView(parsedSigma2Resume.resumptionId.data()), mSharedSecret, + mPeerCATs); if (err2 != CHIP_NO_ERROR) ChipLogError(SecureChannel, "Unable to save session resumption state: %" CHIP_ERROR_FORMAT, err2.Format()); } @@ -1466,6 +1451,40 @@ CHIP_ERROR CASESession::HandleSigma2Resume(System::PacketBufferHandle && msg) return err; } +CHIP_ERROR CASESession::ParseSigma2Resume(ContiguousBufferTLVReader & tlvReader, ParsedSigma2Resume & outParsedSigma2Resume) +{ + + CHIP_ERROR err = CHIP_NO_ERROR; + TLVType containerType = kTLVType_Structure; + + ReturnErrorOnFailure(err = tlvReader.Next(containerType, AnonymousTag())); + ReturnErrorOnFailure(err = tlvReader.EnterContainer(containerType)); + + ReturnErrorOnFailure(err = tlvReader.Next()); + VerifyOrReturnError(tlvReader.GetTag() == AsTlvContextTag(Sigma2ResumeTags::kResumptionID), CHIP_ERROR_INVALID_TLV_TAG); + ReturnErrorOnFailure(err = tlvReader.GetByteView(outParsedSigma2Resume.resumptionId)); + VerifyOrReturnError(outParsedSigma2Resume.resumptionId.size() == SessionResumptionStorage::kResumptionIdSize, + CHIP_ERROR_INVALID_CASE_PARAMETER); + + ReturnErrorOnFailure(err = tlvReader.Next()); + VerifyOrReturnError(tlvReader.GetTag() == AsTlvContextTag(Sigma2ResumeTags::kSigma2ResumeMIC), CHIP_ERROR_INVALID_TLV_TAG); + ReturnErrorOnFailure(err = tlvReader.GetByteView(outParsedSigma2Resume.sigma2ResumeMIC)); + VerifyOrReturnError(outParsedSigma2Resume.sigma2ResumeMIC.size() == CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, + CHIP_ERROR_INVALID_CASE_PARAMETER); + + ReturnErrorOnFailure(err = tlvReader.Next()); + VerifyOrReturnError(tlvReader.GetTag() == AsTlvContextTag(Sigma2ResumeTags::kResponderSessionID), CHIP_ERROR_INVALID_TLV_TAG); + ReturnErrorOnFailure(err = tlvReader.Get(outParsedSigma2Resume.responderSessionId)); + + if (tlvReader.Next() != CHIP_END_OF_TLV) + { + ReturnErrorOnFailure(err = DecodeMRPParametersIfPresent(AsTlvContextTag(Sigma2ResumeTags::kResponderSessionID), tlvReader, + outParsedSigma2Resume.responderSessionParams)); + } + + return CHIP_NO_ERROR; +} + CHIP_ERROR CASESession::HandleSigma2_and_SendSigma3(System::PacketBufferHandle && msg) { MATTER_TRACE_SCOPE("HandleSigma2_and_SendSigma3", "CASESession"); @@ -2587,7 +2606,8 @@ CHIP_ERROR CASESession::OnMessageReceived(ExchangeContext * ec, const PayloadHea case State::kSentSigma2Resume: if (msgType == Protocols::SecureChannel::MsgType::StatusReport) { - // Need to capture before invoking status report since 'this' might be deallocated on successful completion of sigma3 + // Need to capture before invoking status report since 'this' might be deallocated on successful completion of + // sigma3 MetricKey key = (mState == State::kSentSigma3) ? kMetricDeviceCASESessionSigma3 : kMetricDeviceCASESessionSigma2Resume; err = HandleStatusReport(std::move(msg), /* successExpected*/ true); MATTER_LOG_METRIC_END(key, err); diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h index cdc4628f5150a8..51499f0a2afd5d 100644 --- a/src/protocols/secure_channel/CASESession.h +++ b/src/protocols/secure_channel/CASESession.h @@ -270,6 +270,17 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, ByteSpan resumptionId; }; + struct ParsedSigma2Resume + { + ByteSpan resumptionId; + ByteSpan sigma2ResumeMIC; + uint16_t responderSessionId; + const ReliableMessageProtocolConfig * responderMrpConfig; + SessionParameters responderSessionParams; + // TODO consider removing this? + bool responderMrpParamsPresent = false; + }; + /** * @brief Encodes a Sigma1 message into TLV format and allocates a buffer for it, which is owned by the PacketBufferHandle * outparam. @@ -304,6 +315,9 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, static CHIP_ERROR ParseSigma2(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma2 & parsedMessage); static CHIP_ERROR ParseSigma2TBEData(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma2TBEData & parsedMessage); + + static CHIP_ERROR ParseSigma2Resume(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma2Resume & outParsedSigma2Resume); + /** * @brief Encodes a Sigma2 message into TLV format and allocates a buffer for it, which is owned by the PacketBufferHandle * outparam. From 7cfb310d770b6e374ce97e232c2e443db6a4a67b Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Fri, 3 Jan 2025 16:12:59 +0100 Subject: [PATCH 07/15] Adding Unit Tests --- .../secure_channel/tests/TestCASESession.cpp | 477 +++++++++++++++++- 1 file changed, 469 insertions(+), 8 deletions(-) diff --git a/src/protocols/secure_channel/tests/TestCASESession.cpp b/src/protocols/secure_channel/tests/TestCASESession.cpp index a330d58197ff4d..902b7c2af4f5f0 100644 --- a/src/protocols/secure_channel/tests/TestCASESession.cpp +++ b/src/protocols/secure_channel/tests/TestCASESession.cpp @@ -69,11 +69,17 @@ class CASESessionAccess : public CASESession using CASESession::EncodeSigma2Inputs; using CASESession::EncodeSigma2ResumeInputs; using CASESession::ParsedSigma1; + using CASESession::ParsedSigma2; + using CASESession::ParsedSigma2Resume; + using CASESession::ParsedSigma2TBEData; using CASESession::EncodeSigma1; using CASESession::EncodeSigma2; using CASESession::EncodeSigma2Resume; using CASESession::ParseSigma1; + using CASESession::ParseSigma2; + using CASESession::ParseSigma2Resume; + using CASESession::ParseSigma2TBEData; }; class TestCASESession : public Test::LoopbackMessagingContext @@ -981,12 +987,146 @@ TEST_F(TestCASESession, EncodeSigma1Test) EXPECT_TRUE(parsedMessage.resumptionId.data_equal(encodeParams.resumptionId)); EXPECT_TRUE(parsedMessage.initiatorResumeMIC.data_equal(encodeParams.initiatorResumeMIC)); - EXPECT_TRUE(parsedMessage.initiatorMrpParamsPresent); + EXPECT_TRUE(parsedMessage.sessionResumptionRequested); } // Release EphemeralKeyPair gDeviceOperationalKeystore.ReleaseEphemeralKeypair(ephemeralKey); } +struct Sigma2Params +{ + // Purposefully not using constants like kSigmaParamRandomNumberSize that + // the code uses, so we have a cross-check. + static constexpr size_t kResponderRandomLen = 32; + static constexpr uint16_t kResponderSessionId = 0; + static constexpr size_t kResponderEphPubKeyLen = 65; + static constexpr size_t kEncrypted2Len = + CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES + 1; // Needs to be at least CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES + static constexpr size_t kResponderSessionParamsLen = 0; // Nonzero means include it. + + static constexpr uint8_t kResponderRandomTag = 1; + static constexpr uint8_t kResponderSessionIdTag = 2; + static constexpr uint8_t kResponderEphPubKeyTag = 3; + static constexpr uint8_t kEncrypted2Tag = 4; + static constexpr uint8_t kResponderSessionParamsTag = 5; // TODO SessionParams are not tested in non of ParseSigmaTests + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } + + static constexpr bool kIncludeStructEnd = true; + + static constexpr bool kExpectSuccess = true; +}; + +template +static CHIP_ERROR EncodeSigma2Helper(MutableByteSpan & buf) +{ + using namespace TLV; + + TLVWriter writer; + writer.Init(buf); + + TLVType containerType; + ReturnErrorOnFailure(writer.StartContainer(AnonymousTag(), kTLVType_Structure, containerType)); + uint8_t responderRandom[Params::kResponderRandomLen] = { 1 }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResponderRandomTag), ByteSpan(responderRandom))); + + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResponderSessionIdTag), Params::kResponderSessionId)); + + uint8_t responderEphPubKey[Params::kResponderEphPubKeyLen] = { 2 }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResponderEphPubKeyTag), ByteSpan(responderEphPubKey))); + + uint8_t encrypted2[Params::kEncrypted2Len] = { 3 }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kEncrypted2Tag), ByteSpan(encrypted2))); + + if constexpr (Params::kIncludeStructEnd) + { + ReturnErrorOnFailure(writer.EndContainer(containerType)); + } + + buf.reduce_size(writer.GetLengthWritten()); + return CHIP_NO_ERROR; +} + +// A macro, so we can tell which test failed based on line number. +#define TestSigma2Parsing(mem, bufferSize, params) \ + do \ + { \ + MutableByteSpan buf(mem.Get(), bufferSize); \ + EXPECT_EQ(EncodeSigma2Helper(buf), CHIP_NO_ERROR); \ + \ + TLV::ContiguousBufferTLVReader reader; \ + reader.Init(buf); \ + CASESessionAccess::ParsedSigma2 parsedSigma2; \ + \ + EXPECT_EQ(CASESessionAccess::ParseSigma2(reader, parsedSigma2) == CHIP_NO_ERROR, params::kExpectSuccess); \ + if (params::kExpectSuccess) \ + { \ + /* Add other verification tests here as desired */ \ + } \ + } while (0) + +struct BadSigma2ParamsBase : public Sigma2Params +{ + static constexpr bool kExpectSuccess = false; +}; + +struct Sigma2NoStructEnd : public BadSigma2ParamsBase +{ + static constexpr bool kIncludeStructEnd = false; +}; + +struct Sigma2WrongTags : public BadSigma2ParamsBase +{ + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ProfileTag(0, num); } +}; + +struct Sigma2TooLongRandom : public BadSigma2ParamsBase +{ + static constexpr size_t kResponderRandomLen = 33; +}; + +struct Sigma2TooShortRandom : public BadSigma2ParamsBase +{ + static constexpr size_t kResponderRandomLen = 31; +}; + +struct Sigma2TooLongPubkey : public BadSigma2ParamsBase +{ + static constexpr size_t kResponderEphPubKeyLen = 66; +}; + +struct Sigma2TooShortPubkey : public BadSigma2ParamsBase +{ + static constexpr size_t kResponderEphPubKeyLen = 64; +}; + +struct Sigma2SessionIdMax : public Sigma2Params +{ + static constexpr uint32_t kResponderSessionId = UINT16_MAX; +}; + +struct Sigma2SessionIdTooBig : public BadSigma2ParamsBase +{ + static constexpr uint32_t kResponderSessionId = UINT16_MAX + 1; +}; +// TODO: Consider making this test (and similar ones) to Value-Parameterized tests once pw_unit_test:light starts supporting them +TEST_F(TestCASESession, Sigma2ParsingTest) +{ + // 1280 bytes must be enough by definition. + constexpr size_t bufferSize = 1280; + chip::Platform::ScopedMemoryBuffer mem; + EXPECT_TRUE(mem.Calloc(bufferSize)); + + TestSigma2Parsing(mem, bufferSize, Sigma2Params); + TestSigma2Parsing(mem, bufferSize, Sigma2NoStructEnd); + TestSigma2Parsing(mem, bufferSize, Sigma2WrongTags); + TestSigma2Parsing(mem, bufferSize, Sigma2TooLongRandom); + TestSigma2Parsing(mem, bufferSize, Sigma2TooShortRandom); + TestSigma2Parsing(mem, bufferSize, Sigma2TooLongPubkey); + TestSigma2Parsing(mem, bufferSize, Sigma2TooShortPubkey); + TestSigma2Parsing(mem, bufferSize, Sigma2SessionIdMax); + TestSigma2Parsing(mem, bufferSize, Sigma2SessionIdTooBig); +} + TEST_F(TestCASESession, EncodeSigma2Test) { CASESessionAccess::EncodeSigma2Inputs encodeParams; @@ -1006,8 +1146,10 @@ TEST_F(TestCASESession, EncodeSigma2Test) encodeParams.msgR2Encrypted.Alloc(encodeParams.encrypted2Length); // responder Session Parameters - ReliableMessageProtocolConfig mrpConfig = GetDefaultMRPConfig(); - encodeParams.responderMrpConfig = &mrpConfig; + ReliableMessageProtocolConfig mrpConfig(System::Clock::Milliseconds32(100), System::Clock::Milliseconds32(200), + System::Clock::Milliseconds16(4000)); + + encodeParams.responderMrpConfig = &mrpConfig; { System::PacketBufferHandle msg; @@ -1069,20 +1211,185 @@ TEST_F(TestCASESession, EncodeSigma2Test) // Succeed when MRP Config is provided encodeParams.responderMrpConfig = &mrpConfig; EXPECT_EQ(CHIP_NO_ERROR, CASESessionAccess::EncodeSigma2(msg, encodeParams)); + // EncodeSigma2 frees msgR2Encrypted after encoding it + encodeParams.msgR2Encrypted.Alloc(encodeParams.encrypted2Length); } + + // Round Trip Test: Encode then Parse Sigma2 + { + System::PacketBufferHandle msg; + // Succeed when MRP Config is provided + encodeParams.responderMrpConfig = &mrpConfig; + EXPECT_EQ(CHIP_NO_ERROR, CASESessionAccess::EncodeSigma2(msg, encodeParams)); + + System::PacketBufferTLVReader tlvReader; + tlvReader.Init(std::move(msg)); + CASESessionAccess::ParsedSigma2 parsedMessage; + + EXPECT_EQ(CHIP_NO_ERROR, CASESessionAccess::ParseSigma2(tlvReader, parsedMessage)); + + // compare parsed values with original values + EXPECT_TRUE(parsedMessage.responderRandom.data_equal(ByteSpan(encodeParams.responderRandom))); + EXPECT_EQ(parsedMessage.responderSessionId, encodeParams.responderSessionId); + EXPECT_TRUE(parsedMessage.responderEphPubKey.data_equal( + ByteSpan(encodeParams.responderEphPubKey->ConstBytes(), encodeParams.responderEphPubKey->Length()))); + + EXPECT_EQ(parsedMessage.responderSessionParams.GetMRPConfig(), *encodeParams.responderMrpConfig); + } + // Release EphemeralKeyPair gDeviceOperationalKeystore.ReleaseEphemeralKeypair(ephemeralKey); } +struct Sigma2ResumeParams +{ + // Purposefully not using constants like kSigmaParamRandomNumberSize that + // the code uses, so we have a cross-check. + static constexpr size_t kResumptionIdLen = 16; + static constexpr size_t kSigma2ResumeMICLen = 16; + static constexpr uint16_t kResponderSessionId = 0; + static constexpr size_t kResponderSessionParamsLen = 0; // Nonzero means include it. TODO should I remove this + + static constexpr uint8_t kResumptionIdTag = 1; + static constexpr uint8_t kSigma2ResumeMICTag = 2; + static constexpr uint8_t kResponderSessionIdTag = 3; + static constexpr uint8_t kResponderSessionParamsTag = 4; // TODO SessionParams are not tested in non of ParseSigmaTests + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } + + static constexpr bool kIncludeStructEnd = true; + + static constexpr bool kExpectSuccess = true; +}; + +template +static CHIP_ERROR EncodeSigma2ResumeHelper(MutableByteSpan & buf) +{ + using namespace TLV; + + TLVWriter writer; + writer.Init(buf); + + TLVType containerType; + ReturnErrorOnFailure(writer.StartContainer(AnonymousTag(), kTLVType_Structure, containerType)); + + uint8_t resumptionID[Params::kResumptionIdLen] = { 1 }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResumptionIdTag), ByteSpan(resumptionID))); + + uint8_t sigma2ResumeMIC[Params::kSigma2ResumeMICLen] = { 2 }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kSigma2ResumeMICTag), ByteSpan(sigma2ResumeMIC))); + + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResponderSessionIdTag), Params::kResponderSessionId)); + + if constexpr (Params::kIncludeStructEnd) + { + ReturnErrorOnFailure(writer.EndContainer(containerType)); + } + + buf.reduce_size(writer.GetLengthWritten()); + return CHIP_NO_ERROR; +} + +// A macro, so we can tell which test failed based on line number. +#define TestSigma2ResumeParsing(mem, bufferSize, params) \ + do \ + { \ + MutableByteSpan buf(mem.Get(), bufferSize); \ + EXPECT_EQ(EncodeSigma2ResumeHelper(buf), CHIP_NO_ERROR); \ + \ + TLV::ContiguousBufferTLVReader reader; \ + reader.Init(buf); \ + CASESessionAccess::ParsedSigma2Resume parsedSigma2Resume; \ + \ + EXPECT_EQ(CASESessionAccess::ParseSigma2Resume(reader, parsedSigma2Resume) == CHIP_NO_ERROR, params::kExpectSuccess); \ + if (params::kExpectSuccess) \ + { \ + /* Add other verification tests here as desired */ \ + } \ + } while (0) + +struct BadSigma2ResumeParamsBase : public Sigma2ResumeParams +{ + static constexpr bool kExpectSuccess = false; +}; + +struct Sigma2ResumeNoStructEnd : public BadSigma2ResumeParamsBase +{ + static constexpr bool kIncludeStructEnd = false; +}; + +struct Sigma2ResumeWrongTags : public BadSigma2ResumeParamsBase +{ + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ProfileTag(0, num); } +}; + +struct Sigma2ResumeTooLongResumptionID : public BadSigma2ResumeParamsBase +{ + static constexpr size_t kResumptionIdLen = 17; +}; + +struct Sigma2ResumeTooShortResumptionID : public BadSigma2ResumeParamsBase +{ + static constexpr size_t kResumptionIdLen = 15; +}; + +struct Sigma2ResumeTooLongResumeMIC : public BadSigma2ResumeParamsBase +{ + static constexpr size_t kSigma2ResumeMICLen = 17; +}; + +struct Sigma2ResumeTooShortResumeMIC : public BadSigma2ResumeParamsBase +{ + static constexpr size_t kSigma2ResumeMICLen = 15; +}; + +struct Sigma2ResumeSessionIdMax : public Sigma2ResumeParams +{ + static constexpr uint32_t kResponderSessionId = UINT16_MAX; +}; + +struct Sigma2ResumeSessionIdTooBig : public BadSigma2ResumeParamsBase +{ + static constexpr uint32_t kResponderSessionId = UINT16_MAX + 1; +}; + +TEST_F(TestCASESession, ParseSigma2Resume) +{ + // 1280 bytes must be enough by definition. + constexpr size_t bufferSize = 1280; + chip::Platform::ScopedMemoryBuffer mem; + EXPECT_TRUE(mem.Calloc(bufferSize)); + + TestSigma2ResumeParsing(mem, bufferSize, Sigma2ResumeParams); + TestSigma2ResumeParsing(mem, bufferSize, Sigma2ResumeNoStructEnd); + TestSigma2ResumeParsing(mem, bufferSize, Sigma2ResumeWrongTags); + TestSigma2ResumeParsing(mem, bufferSize, Sigma2ResumeTooLongResumptionID); + TestSigma2ResumeParsing(mem, bufferSize, Sigma2ResumeTooShortResumptionID); + TestSigma2ResumeParsing(mem, bufferSize, Sigma2ResumeTooLongResumeMIC); + TestSigma2ResumeParsing(mem, bufferSize, Sigma2ResumeTooShortResumeMIC); + TestSigma2ResumeParsing(mem, bufferSize, Sigma2ResumeSessionIdMax); + TestSigma2ResumeParsing(mem, bufferSize, Sigma2ResumeSessionIdTooBig); +} + TEST_F(TestCASESession, EncodeSigma2ResumeTest) { CASESessionAccess::EncodeSigma2ResumeInputs encodeParams; + // Set ResumptionID + SessionResumptionStorage::ResumptionIdStorage resumptionId; + EXPECT_EQ(chip::Crypto::DRBG_get_bytes(resumptionId.data(), resumptionId.size()), CHIP_NO_ERROR); + encodeParams.resumptionId = ByteSpan(resumptionId.data(), resumptionId.size()); + + // Set Sigma2ResumeMIC + EXPECT_EQ(chip::Crypto::DRBG_get_bytes(&encodeParams.sigma2ResumeMICBuffer[0], sizeof(encodeParams.sigma2ResumeMICBuffer)), + CHIP_NO_ERROR); + + // Set Responder Session ID encodeParams.responderSessionId = 7315; - // responder Session Parameters - ReliableMessageProtocolConfig mrpConfig = GetDefaultMRPConfig(); - encodeParams.responderMrpConfig = &mrpConfig; + // Set responder MRP Parameters + ReliableMessageProtocolConfig mrpConfig(System::Clock::Milliseconds32(100), System::Clock::Milliseconds32(200), + System::Clock::Milliseconds16(4000)); + encodeParams.responderMrpConfig = &mrpConfig; { System::PacketBufferHandle msg; @@ -1102,14 +1409,34 @@ TEST_F(TestCASESession, EncodeSigma2ResumeTest) encodeParams.responderMrpConfig = &mrpConfig; EXPECT_EQ(CHIP_NO_ERROR, CASESessionAccess::EncodeSigma2Resume(msg, encodeParams)); } + + // Round Trip Test: Encode Parse Sigma2Resume + { + System::PacketBufferHandle msg; + // Succeed when MRP Config is provided + encodeParams.responderMrpConfig = &mrpConfig; + EXPECT_EQ(CHIP_NO_ERROR, CASESessionAccess::EncodeSigma2Resume(msg, encodeParams)); + + System::PacketBufferTLVReader tlvReader; + tlvReader.Init(std::move(msg)); + CASESessionAccess::ParsedSigma2Resume parsedMessage; + + EXPECT_EQ(CHIP_NO_ERROR, CASESessionAccess::ParseSigma2Resume(tlvReader, parsedMessage)); + + // compare parsed values with original values + EXPECT_TRUE(parsedMessage.resumptionId.data_equal(encodeParams.resumptionId)); + EXPECT_TRUE(parsedMessage.sigma2ResumeMIC.data_equal(encodeParams.sigma2ResumeMIC)); + EXPECT_EQ(parsedMessage.responderSessionId, encodeParams.responderSessionId); + EXPECT_EQ(parsedMessage.responderSessionParams.GetMRPConfig(), *encodeParams.responderMrpConfig); + } } struct SessionResumptionTestStorage : SessionResumptionStorage { SessionResumptionTestStorage(CHIP_ERROR findMethodReturnCode, ScopedNodeId peerNodeId, ResumptionIdStorage * resumptionId, Crypto::P256ECDHDerivedSecret * sharedSecret) : - mFindMethodReturnCode(findMethodReturnCode), - mPeerNodeId(peerNodeId), mResumptionId(resumptionId), mSharedSecret(sharedSecret) + mFindMethodReturnCode(findMethodReturnCode), mPeerNodeId(peerNodeId), mResumptionId(resumptionId), + mSharedSecret(sharedSecret) {} SessionResumptionTestStorage(CHIP_ERROR findMethodReturnCode) : mFindMethodReturnCode(findMethodReturnCode) {} CHIP_ERROR FindByScopedNodeId(const ScopedNodeId & node, ResumptionIdStorage & resumptionId, @@ -1386,4 +1713,138 @@ TEST_F(TestCASESession, Sigma1BadDestinationIdTest) caseSession.Clear(); } +struct Sigma2TBEDataParams +{ + // Purposefully not using constants like kSigmaParamRandomNumberSize that + // the code uses, so we have a cross-check. + static constexpr size_t kResponderNOCLen = 400; + static constexpr size_t kResponderICACLen = 400; + static constexpr size_t kSignatureLen = 64; + static constexpr size_t kResumptionIdLen = 16; + + static constexpr uint8_t kResponderNOCTag = 1; + static constexpr uint8_t kResponderICACTag = 2; + static constexpr uint8_t kSignatureTag = 3; + static constexpr uint8_t kResumptionIdTag = 4; + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } + + static constexpr bool kIncludeStructEnd = true; + + static constexpr bool kExpectSuccess = true; +}; + +template +static CHIP_ERROR EncodeSigma2TBEDataHelper(MutableByteSpan & buf) +{ + using namespace TLV; + + TLVWriter writer; + writer.Init(buf); + + TLVType containerType; + ReturnErrorOnFailure(writer.StartContainer(AnonymousTag(), kTLVType_Structure, containerType)); + uint8_t responderNOC[Params::kResponderNOCLen] = { 1 }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResponderNOCTag), ByteSpan(responderNOC))); + + uint8_t responderICAC[Params::kResponderICACLen] = { 13 }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResponderICACTag), ByteSpan(responderICAC))); + + uint8_t signature[Params::kSignatureLen] = { 17 }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kSignatureTag), ByteSpan(signature))); + + uint8_t resumptionId[Params::kResumptionIdLen] = { 22 }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResumptionIdTag), ByteSpan(resumptionId))); + + uint8_t resumptionIssd[20] = { 22 }; + ReturnErrorOnFailure(writer.Put(TLV::ContextTag(5), ByteSpan(resumptionIssd))); + + if constexpr (Params::kIncludeStructEnd) + { + ReturnErrorOnFailure(writer.EndContainer(containerType)); + } + + buf.reduce_size(writer.GetLengthWritten()); + return CHIP_NO_ERROR; +} + +// A macro, so we can tell which test failed based on line number. +#define TestSigma2TBEParsing(mem, bufferSize, params) \ + do \ + { \ + MutableByteSpan buf(mem.Get(), bufferSize); \ + EXPECT_EQ(EncodeSigma2TBEDataHelper(buf), CHIP_NO_ERROR); \ + \ + TLV::ContiguousBufferTLVReader reader; \ + reader.Init(buf); \ + CASESessionAccess::ParsedSigma2TBEData parsedSigma2TBEData; \ + \ + EXPECT_EQ(CASESessionAccess::ParseSigma2TBEData(reader, parsedSigma2TBEData) == CHIP_NO_ERROR, params::kExpectSuccess); \ + if (params::kExpectSuccess) \ + { \ + /* Add other verification tests here as desired */ \ + } \ + } while (0) + +struct BadSigma2TBEParamsBase : public Sigma2TBEDataParams +{ + static constexpr bool kExpectSuccess = false; +}; + +struct Sigma2TBENoStructEnd : public BadSigma2TBEParamsBase +{ + static constexpr bool kIncludeStructEnd = false; +}; + +struct Sigma2TBEWrongTags : public BadSigma2TBEParamsBase +{ + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ProfileTag(0, num); } +}; + +struct Sigma2TBETooLongNOC : public BadSigma2TBEParamsBase +{ + static constexpr size_t kResponderNOCLen = 401; +}; + +struct Sigma2TBETooLongICAC : public BadSigma2TBEParamsBase +{ + static constexpr size_t kResponderICACLen = 401; +}; + +struct Sigma2TBETooLongSignature : public BadSigma2TBEParamsBase +{ + static constexpr size_t kSignatureLen = 65; +}; +struct Sigma2TBETooShortSignature : public BadSigma2TBEParamsBase +{ + static constexpr size_t kSignatureLen = 63; +}; + +struct Sigma2TBETooLongResumptionID : public BadSigma2TBEParamsBase +{ + static constexpr size_t kResumptionIdLen = 17; +}; + +struct Sigma2TBETooShortResumptionID : public BadSigma2TBEParamsBase +{ + static constexpr size_t kResumptionIdLen = 15; +}; + +TEST_F(TestCASESession, ParseSigma2TBEData) +{ + // 1280 bytes must be enough by definition. + constexpr size_t bufferSize = 1280; + chip::Platform::ScopedMemoryBuffer mem; + EXPECT_TRUE(mem.Calloc(bufferSize)); + + TestSigma2TBEParsing(mem, bufferSize, Sigma2TBEDataParams); + TestSigma2TBEParsing(mem, bufferSize, Sigma2TBENoStructEnd); + TestSigma2TBEParsing(mem, bufferSize, Sigma2TBEWrongTags); + TestSigma2TBEParsing(mem, bufferSize, Sigma2TBETooLongNOC); + TestSigma2TBEParsing(mem, bufferSize, Sigma2TBETooLongICAC); + TestSigma2TBEParsing(mem, bufferSize, Sigma2TBETooLongSignature); + TestSigma2TBEParsing(mem, bufferSize, Sigma2TBETooShortSignature); + TestSigma2TBEParsing(mem, bufferSize, Sigma2TBETooLongResumptionID); + TestSigma2TBEParsing(mem, bufferSize, Sigma2TBETooShortResumptionID); +} + } // namespace chip From 4e033de24a3d863b92301f33ab756b77c4522f48 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Fri, 3 Jan 2025 16:13:23 +0100 Subject: [PATCH 08/15] More Various Cleanup --- src/protocols/secure_channel/CASESession.cpp | 401 +++++++++--------- src/protocols/secure_channel/CASESession.h | 13 +- .../secure_channel/tests/TestCASESession.cpp | 133 +++++- 3 files changed, 312 insertions(+), 235 deletions(-) diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index 9d24e9fc8d30c0..a612f336459c44 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -1097,8 +1097,8 @@ CASESession::NextStep CASESession::HandleSigma1(System::PacketBufferHandle && ms ChipLogDetail(SecureChannel, "Peer assigned session key ID %d", parsedSigma1.initiatorSessionId); SetPeerSessionId(parsedSigma1.initiatorSessionId); - // Set the MRP parameters provided in the Sigma1 message - if (parsedSigma1.initiatorMrpParamsPresent) + // Set the Session parameters provided in the Sigma1 message + if (parsedSigma1.initiatorSessionParamsPresent) { SetRemoteSessionParameters(parsedSigma1.initiatorSessionParams); mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters( @@ -1163,7 +1163,7 @@ CHIP_ERROR CASESession::PrepareSigma2Resume(EncodeSigma2ResumeInputs & outSigma2 outSigma2ResData.resumptionId = mNewResumptionId; ReturnErrorOnFailure(GenerateSigmaResumeMIC(ByteSpan(mInitiatorRandom), mNewResumptionId, ByteSpan(kKDFS2RKeyInfo), - ByteSpan(kResume2MIC_Nonce), outSigma2ResData.resumeMIC)); + ByteSpan(kResume2MIC_Nonce), outSigma2ResData.sigma2ResumeMIC)); outSigma2ResData.responderMrpConfig = &mLocalMRPConfig.Value(); @@ -1192,7 +1192,7 @@ CHIP_ERROR CASESession::EncodeSigma2Resume(System::PacketBufferHandle & msgR2Res ReturnErrorOnFailure(tlvWriter.StartContainer(AnonymousTag(), kTLVType_Structure, outerContainerType)); ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(Sigma2ResumeTags::kResumptionID), input.resumptionId)); - ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(Sigma2ResumeTags::kSigma2ResumeMIC), input.resumeMIC)); + ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(Sigma2ResumeTags::kSigma2ResumeMIC), input.sigma2ResumeMIC)); ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(Sigma2ResumeTags::kResponderSessionID), input.responderSessionId)); ReturnErrorOnFailure( @@ -1262,7 +1262,7 @@ CHIP_ERROR CASESession::PrepareSigma2(EncodeSigma2Inputs & outSigma2Data) ReturnErrorOnFailure(DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR2Info), sr2k)); // Construct Sigma2 TBS Data - size_t msgR2SignedLen = EstimateStructOverhead(kMaxCHIPCertLength, // responderNoc + size_t msgR2SignedLen = EstimateStructOverhead(kMaxCHIPCertLength, // responderNOC kMaxCHIPCertLength, // responderICAC kP256_PublicKey_Length, // responderEphPubKey kP256_PublicKey_Length // initiatorEphPubKey @@ -1281,7 +1281,7 @@ CHIP_ERROR CASESession::PrepareSigma2(EncodeSigma2Inputs & outSigma2Data) mFabricsTable->SignWithOpKeypair(mFabricIndex, ByteSpan{ msgR2Signed.Get(), msgR2SignedLen }, tbsData2Signature)); } // Construct Sigma2 TBE Data - size_t msgR2SignedEncLen = EstimateStructOverhead(nocCert.size(), // responderNoc + size_t msgR2SignedEncLen = EstimateStructOverhead(nocCert.size(), // responderNOC icaCert.size(), // responderICAC tbsData2Signature.Length(), // signature SessionResumptionStorage::kResumptionIdSize // resumptionID @@ -1414,7 +1414,7 @@ CHIP_ERROR CASESession::HandleSigma2Resume(System::PacketBufferHandle && msg) SuccessOrExit(err = ParseSigma2Resume(tlvReader, parsedSigma2Resume)); - if (parsedSigma2Resume.responderMrpParamsPresent) + if (parsedSigma2Resume.responderSessionParamStructPresent) { SetRemoteSessionParameters(parsedSigma2Resume.responderSessionParams); mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters( @@ -1453,35 +1453,39 @@ CHIP_ERROR CASESession::HandleSigma2Resume(System::PacketBufferHandle && msg) CHIP_ERROR CASESession::ParseSigma2Resume(ContiguousBufferTLVReader & tlvReader, ParsedSigma2Resume & outParsedSigma2Resume) { - - CHIP_ERROR err = CHIP_NO_ERROR; TLVType containerType = kTLVType_Structure; - ReturnErrorOnFailure(err = tlvReader.Next(containerType, AnonymousTag())); - ReturnErrorOnFailure(err = tlvReader.EnterContainer(containerType)); + ReturnErrorOnFailure(tlvReader.Next(containerType, AnonymousTag())); + ReturnErrorOnFailure(tlvReader.EnterContainer(containerType)); - ReturnErrorOnFailure(err = tlvReader.Next()); - VerifyOrReturnError(tlvReader.GetTag() == AsTlvContextTag(Sigma2ResumeTags::kResumptionID), CHIP_ERROR_INVALID_TLV_TAG); - ReturnErrorOnFailure(err = tlvReader.GetByteView(outParsedSigma2Resume.resumptionId)); + ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2ResumeTags::kResumptionID))); + ReturnErrorOnFailure(tlvReader.GetByteView(outParsedSigma2Resume.resumptionId)); VerifyOrReturnError(outParsedSigma2Resume.resumptionId.size() == SessionResumptionStorage::kResumptionIdSize, CHIP_ERROR_INVALID_CASE_PARAMETER); - ReturnErrorOnFailure(err = tlvReader.Next()); - VerifyOrReturnError(tlvReader.GetTag() == AsTlvContextTag(Sigma2ResumeTags::kSigma2ResumeMIC), CHIP_ERROR_INVALID_TLV_TAG); - ReturnErrorOnFailure(err = tlvReader.GetByteView(outParsedSigma2Resume.sigma2ResumeMIC)); + ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2ResumeTags::kSigma2ResumeMIC))); + ReturnErrorOnFailure(tlvReader.GetByteView(outParsedSigma2Resume.sigma2ResumeMIC)); VerifyOrReturnError(outParsedSigma2Resume.sigma2ResumeMIC.size() == CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, CHIP_ERROR_INVALID_CASE_PARAMETER); - ReturnErrorOnFailure(err = tlvReader.Next()); - VerifyOrReturnError(tlvReader.GetTag() == AsTlvContextTag(Sigma2ResumeTags::kResponderSessionID), CHIP_ERROR_INVALID_TLV_TAG); - ReturnErrorOnFailure(err = tlvReader.Get(outParsedSigma2Resume.responderSessionId)); + ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2ResumeTags::kResponderSessionID))); + ReturnErrorOnFailure(tlvReader.Get(outParsedSigma2Resume.responderSessionId)); - if (tlvReader.Next() != CHIP_END_OF_TLV) + CHIP_ERROR err = tlvReader.Next(); + if (err == CHIP_NO_ERROR && tlvReader.GetTag() == AsTlvContextTag(Sigma2ResumeTags::kResponderSessionParams)) { - ReturnErrorOnFailure(err = DecodeMRPParametersIfPresent(AsTlvContextTag(Sigma2ResumeTags::kResponderSessionID), tlvReader, - outParsedSigma2Resume.responderSessionParams)); + ReturnErrorOnFailure(DecodeMRPParametersIfPresent(AsTlvContextTag(Sigma2ResumeTags::kResponderSessionParams), tlvReader, + outParsedSigma2Resume.responderSessionParams)); + err = tlvReader.Next(); } + // Future-proofing: CHIP_NO_ERROR will be returned by Next() if we have additional non-parsed TLV Elements, which could + // happen in the future if the spec changes and we have additional elements. + VerifyOrReturnError(err == CHIP_END_OF_TLV || err == CHIP_NO_ERROR, err); + // Exit Container will fail (return CHIP_END_OF_TLV) if the received encoded message does not call EndContainer in the end of + // the TLV message. + ReturnErrorOnFailure(tlvReader.ExitContainer(containerType)); + return CHIP_NO_ERROR; } @@ -1490,7 +1494,7 @@ CHIP_ERROR CASESession::HandleSigma2_and_SendSigma3(System::PacketBufferHandle & MATTER_TRACE_SCOPE("HandleSigma2_and_SendSigma3", "CASESession"); CHIP_ERROR err = HandleSigma2(std::move(msg)); MATTER_LOG_METRIC_END(kMetricDeviceCASESessionSigma1, err); - ReturnErrorOnFailure(err); + SuccessOrExit(err); MATTER_LOG_METRIC_BEGIN(kMetricDeviceCASESessionSigma3); err = SendSigma3a(); @@ -1498,62 +1502,40 @@ CHIP_ERROR CASESession::HandleSigma2_and_SendSigma3(System::PacketBufferHandle & { MATTER_LOG_METRIC_END(kMetricDeviceCASESessionSigma3, err); } + +exit: + if (CHIP_NO_ERROR != err) + { + SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam); + mState = State::kInitialized; + } return err; } CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) { MATTER_TRACE_SCOPE("HandleSigma2", "CASESession"); - ContiguousBufferTLVReader decryptedDataTlvReader; - - const uint8_t * buf = msg->Start(); - size_t buflen = msg->DataLength(); - - uint8_t msg_salt[kIPKSize + kSigmaParamRandomNumberSize + kP256_PublicKey_Length + kSHA256_Hash_Length]; - - chip::Platform::ScopedMemoryBuffer msg_R2_Encrypted; - - chip::Platform::ScopedMemoryBuffer msgR2Signed; - - AutoReleaseSessionKey sr2k(*mSessionManager->GetSessionKeystore()); - - P256ECDSASignature tbsData2Signature; - - NodeId responderNodeId; - P256PublicKey responderPublicKey; - - // uint8_t responderRandom[kSigmaParamRandomNumberSize]; - // ByteSpan responderNOC; - // ByteSpan responderICAC; - - // uint16_t responderSessionId; - - // TLVType containerType = kTLVType_Structure; - size_t msgR2SignedLen; - - size_t msgR2EncryptedLen = 0; - ChipLogProgress(SecureChannel, "Received Sigma2 msg"); - CHIP_ERROR err = CHIP_NO_ERROR; - System::PacketBufferTLVReader tlvReader; - tlvReader.Init(std::move(msg)); + VerifyOrReturnError(mEphemeralKey != nullptr, CHIP_ERROR_INTERNAL); - ParsedSigma2 parsedSigma2; - ParsedSigma2TBEData parsedSigma2TBEData; + const uint8_t * buf = msg->Start(); + size_t buflen = msg->DataLength(); + VerifyOrReturnError(buf != nullptr, CHIP_ERROR_MESSAGE_INCOMPLETE); FabricId fabricId = kUndefinedFabricId; { - VerifyOrExit(mFabricsTable != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mFabricsTable != nullptr, CHIP_ERROR_INCORRECT_STATE); const auto * fabricInfo = mFabricsTable->FindFabricWithIndex(mFabricIndex); - VerifyOrExit(fabricInfo != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_INCORRECT_STATE); fabricId = fabricInfo->GetFabricId(); } - VerifyOrExit(mEphemeralKey != nullptr, err = CHIP_ERROR_INTERNAL); - VerifyOrExit(buf != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE); + System::PacketBufferTLVReader tlvReader; + tlvReader.Init(std::move(msg)); + ParsedSigma2 parsedSigma2; - SuccessOrExit(err = ParseSigma2(tlvReader, parsedSigma2)); + ReturnErrorOnFailure(ParseSigma2(tlvReader, parsedSigma2)); ChipLogDetail(SecureChannel, "Peer assigned session key ID %d", parsedSigma2.responderSessionId); SetPeerSessionId(parsedSigma2.responderSessionId); @@ -1563,19 +1545,20 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) memcpy(mRemotePubKey.Bytes(), parsedSigma2.responderEphPubKey.data(), mRemotePubKey.Length()); // Generate a Shared Secret - ReturnErrorOnFailure(err = mEphemeralKey->ECDH_derive_secret(mRemotePubKey, mSharedSecret)); + ReturnErrorOnFailure(mEphemeralKey->ECDH_derive_secret(mRemotePubKey, mSharedSecret)); // Generate the S2K key + AutoReleaseSessionKey sr2k(*mSessionManager->GetSessionKeystore()); { + uint8_t msg_salt[kIPKSize + kSigmaParamRandomNumberSize + kP256_PublicKey_Length + kSHA256_Hash_Length]; MutableByteSpan saltSpan(msg_salt); - ReturnErrorOnFailure( - err = ConstructSaltSigma2(ByteSpan(parsedSigma2.responderRandom), mRemotePubKey, ByteSpan(mIPK), saltSpan)); - ReturnErrorOnFailure(err = DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR2Info), sr2k)); + ReturnErrorOnFailure(ConstructSaltSigma2(ByteSpan(parsedSigma2.responderRandom), mRemotePubKey, ByteSpan(mIPK), saltSpan)); + ReturnErrorOnFailure(DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR2Info), sr2k)); } // Msg2 should only be added to MessageDigest after we construct SaltSigma2 that is used to derive S2K - ReturnErrorOnFailure(err = mCommissioningHash.AddData(ByteSpan{ buf, buflen })); + ReturnErrorOnFailure(mCommissioningHash.AddData(ByteSpan{ buf, buflen })); - if (parsedSigma2.responderMrpParamsPresent) + if (parsedSigma2.responderSessionParamStructPresent) { SetRemoteSessionParameters(parsedSigma2.responderSessionParams); mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters( @@ -1584,120 +1567,194 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) /********************************************************************************************************************************** */ - msgR2EncryptedLen = parsedSigma2.msgR2Encrypted.AllocatedSize() - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; + size_t msgR2EncryptedLen = 0; + msgR2EncryptedLen = parsedSigma2.msgR2Encrypted.AllocatedSize() - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; - SuccessOrExit(err = AES_CCM_decrypt(parsedSigma2.msgR2Encrypted.Get(), msgR2EncryptedLen, nullptr, 0, - parsedSigma2.msgR2Encrypted.Get() + msgR2EncryptedLen, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, - sr2k.KeyHandle(), kTBEData2_Nonce, kTBEDataNonceLength, parsedSigma2.msgR2Encrypted.Get())); + ReturnErrorOnFailure(AES_CCM_decrypt(parsedSigma2.msgR2Encrypted.Get(), msgR2EncryptedLen, nullptr, 0, + parsedSigma2.msgR2Encrypted.Get() + msgR2EncryptedLen, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, + sr2k.KeyHandle(), kTBEData2_Nonce, kTBEDataNonceLength, + parsedSigma2.msgR2Encrypted.Get())); + ContiguousBufferTLVReader decryptedDataTlvReader; decryptedDataTlvReader.Init(parsedSigma2.msgR2Encrypted.Get(), msgR2EncryptedLen); + ParsedSigma2TBEData parsedSigma2TBEData; - SuccessOrExit(err = ParseSigma2TBEData(decryptedDataTlvReader, parsedSigma2TBEData)); + ReturnErrorOnFailure(ParseSigma2TBEData(decryptedDataTlvReader, parsedSigma2TBEData)); std::copy(parsedSigma2TBEData.resumptionId.begin(), parsedSigma2TBEData.resumptionId.end(), mNewResumptionId.begin()); // Validate responder identity located in msgR2Encrypted // Constructing responder identity + P256PublicKey responderPublicKey; { + NodeId responderNodeId; + CompressedFabricId unused; FabricId responderFabricId; - SuccessOrExit(err = SetEffectiveTime()); - SuccessOrExit(err = mFabricsTable->VerifyCredentials(mFabricIndex, parsedSigma2TBEData.responderNOC, - parsedSigma2TBEData.responderICAC, mValidContext, unused, - responderFabricId, responderNodeId, responderPublicKey)); - VerifyOrExit(fabricId == responderFabricId, err = CHIP_ERROR_INVALID_CASE_PARAMETER); + ReturnErrorOnFailure(SetEffectiveTime()); + ReturnErrorOnFailure(mFabricsTable->VerifyCredentials(mFabricIndex, parsedSigma2TBEData.responderNOC, + parsedSigma2TBEData.responderICAC, mValidContext, unused, + responderFabricId, responderNodeId, responderPublicKey)); + VerifyOrReturnError(fabricId == responderFabricId, CHIP_ERROR_INVALID_CASE_PARAMETER); // Verify that responderNodeId (from responderNOC) matches one that was included // in the computation of the Destination Identifier when generating Sigma1. - VerifyOrExit(mPeerNodeId == responderNodeId, err = CHIP_ERROR_INVALID_CASE_PARAMETER); + VerifyOrReturnError(mPeerNodeId == responderNodeId, CHIP_ERROR_INVALID_CASE_PARAMETER); } // Construct msgR2Signed and validate the signature in msgR2Encrypted. - msgR2SignedLen = - EstimateStructOverhead(sizeof(uint16_t), parsedSigma2TBEData.responderNOC.size(), parsedSigma2TBEData.responderICAC.size(), - kP256_PublicKey_Length, kP256_PublicKey_Length); + size_t msgR2SignedLen; + msgR2SignedLen = EstimateStructOverhead(parsedSigma2TBEData.responderNOC.size(), // resonderNOC + parsedSigma2TBEData.responderICAC.size(), // responderICAC + kP256_PublicKey_Length, // responderEphPubKey + kP256_PublicKey_Length // initiatorEphPubKey + ); - VerifyOrExit(msgR2Signed.Alloc(msgR2SignedLen), err = CHIP_ERROR_NO_MEMORY); + chip::Platform::ScopedMemoryBuffer msgR2Signed; + VerifyOrReturnError(msgR2Signed.Alloc(msgR2SignedLen), CHIP_ERROR_NO_MEMORY); - SuccessOrExit(err = ConstructTBSData(parsedSigma2TBEData.responderNOC, parsedSigma2TBEData.responderICAC, - ByteSpan(mRemotePubKey, mRemotePubKey.Length()), - ByteSpan(mEphemeralKey->Pubkey(), mEphemeralKey->Pubkey().Length()), msgR2Signed.Get(), - msgR2SignedLen)); + ReturnErrorOnFailure(ConstructTBSData( + parsedSigma2TBEData.responderNOC, parsedSigma2TBEData.responderICAC, ByteSpan(mRemotePubKey, mRemotePubKey.Length()), + ByteSpan(mEphemeralKey->Pubkey(), mEphemeralKey->Pubkey().Length()), msgR2Signed.Get(), msgR2SignedLen)); // Validate signature - SuccessOrExit(err = responderPublicKey.ECDSA_validate_msg_signature(msgR2Signed.Get(), msgR2SignedLen, - parsedSigma2TBEData.tbsData2Signature)); + ReturnErrorOnFailure( + responderPublicKey.ECDSA_validate_msg_signature(msgR2Signed.Get(), msgR2SignedLen, parsedSigma2TBEData.tbsData2Signature)); // Retrieve peer CASE Authenticated Tags (CATs) from peer's NOC. - SuccessOrExit(err = ExtractCATsFromOpCert(parsedSigma2TBEData.responderNOC, mPeerCATs)); + ReturnErrorOnFailure(ExtractCATsFromOpCert(parsedSigma2TBEData.responderNOC, mPeerCATs)); -exit: - if (err != CHIP_NO_ERROR) + return CHIP_NO_ERROR; +} + +CHIP_ERROR CASESession::ParseSigma2(ContiguousBufferTLVReader & tlvReader, ParsedSigma2 & outParsedSigma2) +{ + TLVType containerType = kTLVType_Structure; + + ReturnErrorOnFailure(tlvReader.Next(containerType, AnonymousTag())); + ReturnErrorOnFailure(tlvReader.EnterContainer(containerType)); + + // Retrieve Responder's Random value + ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2Tags::kResponderRandom))); + ReturnErrorOnFailure(tlvReader.GetByteView(outParsedSigma2.responderRandom)); + VerifyOrReturnError(outParsedSigma2.responderRandom.size() == kSigmaParamRandomNumberSize, CHIP_ERROR_INVALID_CASE_PARAMETER); + + // Assign Session ID + ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2Tags::kResponderSessionId))); + ReturnErrorOnFailure(tlvReader.Get(outParsedSigma2.responderSessionId)); + + // Retrieve Responder's Ephemeral Pubkey + ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2Tags::kResponderEphPubKey))); + ReturnErrorOnFailure(tlvReader.GetByteView(outParsedSigma2.responderEphPubKey)); + VerifyOrReturnError(outParsedSigma2.responderEphPubKey.size() == kP256_PublicKey_Length, CHIP_ERROR_INVALID_CASE_PARAMETER); + + // Generate decrypted data + ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2Tags::kEncrypted2))); + + constexpr size_t kCaseOverheadForFutureTbeData = 128; + size_t maxMsgR2SignedEncLen; + + maxMsgR2SignedEncLen = EstimateStructOverhead(kMaxCHIPCertLength, // responderNOC + kMaxCHIPCertLength, // responderICAC + kMax_ECDSA_Signature_Length, // signature + SessionResumptionStorage::kResumptionIdSize, // resumptionID + kCaseOverheadForFutureTbeData // extra bytes for future-proofing + ); + + size_t msgR2EncryptedLenWithTag = 0; + msgR2EncryptedLenWithTag = tlvReader.GetLength(); + + // Validate we did not receive a buffer larger than legal + // TODO why are comparing this to the "signed one" + // Maybe it is signed encrypted, but is the estimation calculated well? document it + VerifyOrReturnError(msgR2EncryptedLenWithTag <= maxMsgR2SignedEncLen, CHIP_ERROR_INVALID_TLV_ELEMENT); + VerifyOrReturnError(msgR2EncryptedLenWithTag > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, CHIP_ERROR_INVALID_TLV_ELEMENT); + // Allocate after making sure that msgR2EncryptedLenWithTag is within bounds + VerifyOrReturnError(outParsedSigma2.msgR2Encrypted.Alloc(msgR2EncryptedLenWithTag), CHIP_ERROR_NO_MEMORY); + + // TODO, should I keep this as GetBytes? or should I use GetByteView for consistency and do something else? + ReturnErrorOnFailure(tlvReader.GetBytes(outParsedSigma2.msgR2Encrypted.Get(), outParsedSigma2.msgR2Encrypted.AllocatedSize())); + + // Retrieve responderSessionParams if present + // TODO the below check is dangerous, it would allow us to ignore all other errors and go into DecodeMRPParameters even if we + // dont have CHIP_NO_ERROR + CHIP_ERROR err = tlvReader.Next(); + if (err == CHIP_NO_ERROR && tlvReader.GetTag() == AsTlvContextTag(Sigma2Tags::kResponderSessionParams)) { - SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam); + ReturnErrorOnFailure(DecodeMRPParametersIfPresent(AsTlvContextTag(Sigma2Tags::kResponderSessionParams), tlvReader, + outParsedSigma2.responderSessionParams)); + outParsedSigma2.responderSessionParamStructPresent = true; + + err = tlvReader.Next(); } - return err; + // Future-proofing: CHIP_NO_ERROR will be returned by Next() if we have additional non-parsed TLV Elements received, which could + // happen in the future if the spec changes and we have additional elements. + VerifyOrReturnError(err == CHIP_END_OF_TLV || err == CHIP_NO_ERROR, err); + // Exit Container will fail if the received encoded message does not call EndContainer in the end of the TLV message. + ReturnErrorOnFailure(tlvReader.ExitContainer(containerType)); + + return CHIP_NO_ERROR; } CHIP_ERROR CASESession::ParseSigma2TBEData(ContiguousBufferTLVReader & decryptedDataTlvReader, ParsedSigma2TBEData & outParsedSigma2TBE) { - CHIP_ERROR err = CHIP_NO_ERROR; TLVType containerType = kTLVType_Structure; - ReturnErrorOnFailure(err = decryptedDataTlvReader.Next(containerType, AnonymousTag())); - ReturnErrorOnFailure(err = decryptedDataTlvReader.EnterContainer(containerType)); + ReturnErrorOnFailure(decryptedDataTlvReader.Next(containerType, AnonymousTag())); + ReturnErrorOnFailure(decryptedDataTlvReader.EnterContainer(containerType)); - ReturnErrorOnFailure(err = decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSenderNOC))); - ReturnErrorOnFailure(err = decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.responderNOC)); + ReturnErrorOnFailure(decryptedDataTlvReader.Next(AsTlvContextTag(TBEDataTags::kSenderNOC))); + ReturnErrorOnFailure(decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.responderNOC)); VerifyOrReturnError(outParsedSigma2TBE.responderNOC.size() <= kMaxCHIPCertLength, CHIP_ERROR_INVALID_CASE_PARAMETER); - ReturnErrorOnFailure(err = decryptedDataTlvReader.Next()); + ReturnErrorOnFailure(decryptedDataTlvReader.Next()); if (decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSenderICAC)) { - VerifyOrReturnError(decryptedDataTlvReader.GetType() == kTLVType_ByteString, err = CHIP_ERROR_WRONG_TLV_TYPE); - ReturnErrorOnFailure(err = decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.responderICAC)); + ReturnErrorOnFailure(decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.responderICAC)); VerifyOrReturnError(outParsedSigma2TBE.responderICAC.size() <= kMaxCHIPCertLength, CHIP_ERROR_INVALID_CASE_PARAMETER); - ReturnErrorOnFailure(err = decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSignature))); + ReturnErrorOnFailure(decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSignature))); } - VerifyOrReturnError(decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSignature), - err = CHIP_ERROR_INVALID_TLV_TAG); - VerifyOrReturnError(outParsedSigma2TBE.tbsData2Signature.Capacity() >= decryptedDataTlvReader.GetLength(), - err = CHIP_ERROR_INVALID_TLV_ELEMENT); + VerifyOrReturnError(decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSignature), CHIP_ERROR_INVALID_TLV_TAG); + // tbsData2Signature's length should equal kMax_ECDSA_Signature_Length as per the Specification + VerifyOrReturnError(outParsedSigma2TBE.tbsData2Signature.Capacity() == decryptedDataTlvReader.GetLength(), + CHIP_ERROR_INVALID_TLV_ELEMENT); outParsedSigma2TBE.tbsData2Signature.SetLength(decryptedDataTlvReader.GetLength()); - ReturnErrorOnFailure(err = decryptedDataTlvReader.GetBytes(outParsedSigma2TBE.tbsData2Signature.Bytes(), - outParsedSigma2TBE.tbsData2Signature.Length())); + ReturnErrorOnFailure(decryptedDataTlvReader.GetBytes(outParsedSigma2TBE.tbsData2Signature.Bytes(), + outParsedSigma2TBE.tbsData2Signature.Length())); // Retrieve session resumption ID - ReturnErrorOnFailure(err = decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kResumptionID))); - ReturnErrorOnFailure(err = decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.resumptionId)); + ReturnErrorOnFailure(decryptedDataTlvReader.Next(AsTlvContextTag(TBEDataTags::kResumptionID))); + ReturnErrorOnFailure(decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.resumptionId)); VerifyOrReturnError(outParsedSigma2TBE.resumptionId.size() == SessionResumptionStorage::kResumptionIdSize, CHIP_ERROR_INVALID_CASE_PARAMETER); + // Exit Container will fail if the received encoded message did not add EndOfContainer at the end of the TLV message. + ReturnErrorOnFailure(decryptedDataTlvReader.ExitContainer(containerType)); + return CHIP_NO_ERROR; } CHIP_ERROR CASESession::SendSigma3a() { MATTER_TRACE_SCOPE("SendSigma3", "CASESession"); - CHIP_ERROR err = CHIP_NO_ERROR; ChipLogDetail(SecureChannel, "Sending Sigma3"); auto helper = WorkHelper::Create(*this, &SendSigma3b, &CASESession::SendSigma3c); - VerifyOrExit(helper, err = CHIP_ERROR_NO_MEMORY); + VerifyOrReturnError(helper, CHIP_ERROR_NO_MEMORY); { auto & data = helper->mData; - VerifyOrExit(mFabricsTable != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mFabricsTable != nullptr, CHIP_ERROR_INCORRECT_STATE); data.fabricIndex = mFabricIndex; data.fabricTable = nullptr; data.keystore = nullptr; { const FabricInfo * fabricInfo = mFabricsTable->FindFabricWithIndex(mFabricIndex); - VerifyOrExit(fabricInfo != nullptr, err = CHIP_ERROR_KEY_NOT_FOUND); + VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_KEY_NOT_FOUND); auto * keystore = mFabricsTable->GetOperationalKeystore(); if (!fabricInfo->HasOperationalKey() && keystore != nullptr && keystore->SupportsSignWithOpKeypairInBackground()) { @@ -1711,48 +1768,41 @@ CHIP_ERROR CASESession::SendSigma3a() } } - VerifyOrExit(mEphemeralKey != nullptr, err = CHIP_ERROR_INTERNAL); + VerifyOrReturnError(mEphemeralKey != nullptr, CHIP_ERROR_INTERNAL); - VerifyOrExit(data.icacBuf.Alloc(kMaxCHIPCertLength), err = CHIP_ERROR_NO_MEMORY); + VerifyOrReturnError(data.icacBuf.Alloc(kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY); data.icaCert = MutableByteSpan{ data.icacBuf.Get(), kMaxCHIPCertLength }; - VerifyOrExit(data.nocBuf.Alloc(kMaxCHIPCertLength), err = CHIP_ERROR_NO_MEMORY); + VerifyOrReturnError(data.nocBuf.Alloc(kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY); data.nocCert = MutableByteSpan{ data.nocBuf.Get(), kMaxCHIPCertLength }; - SuccessOrExit(err = mFabricsTable->FetchICACert(mFabricIndex, data.icaCert)); - SuccessOrExit(err = mFabricsTable->FetchNOCCert(mFabricIndex, data.nocCert)); + ReturnErrorOnFailure(mFabricsTable->FetchICACert(mFabricIndex, data.icaCert)); + ReturnErrorOnFailure(mFabricsTable->FetchNOCCert(mFabricIndex, data.nocCert)); // Prepare Sigma3 TBS Data Blob data.msg_r3_signed_len = TLV::EstimateStructOverhead(data.icaCert.size(), data.nocCert.size(), kP256_PublicKey_Length, kP256_PublicKey_Length); - VerifyOrExit(data.msg_R3_Signed.Alloc(data.msg_r3_signed_len), err = CHIP_ERROR_NO_MEMORY); + VerifyOrReturnError(data.msg_R3_Signed.Alloc(data.msg_r3_signed_len), CHIP_ERROR_NO_MEMORY); - SuccessOrExit(err = ConstructTBSData( - data.nocCert, data.icaCert, ByteSpan(mEphemeralKey->Pubkey(), mEphemeralKey->Pubkey().Length()), - ByteSpan(mRemotePubKey, mRemotePubKey.Length()), data.msg_R3_Signed.Get(), data.msg_r3_signed_len)); + ReturnErrorOnFailure( + ConstructTBSData(data.nocCert, data.icaCert, ByteSpan(mEphemeralKey->Pubkey(), mEphemeralKey->Pubkey().Length()), + ByteSpan(mRemotePubKey, mRemotePubKey.Length()), data.msg_R3_Signed.Get(), data.msg_r3_signed_len)); if (data.keystore != nullptr) { - SuccessOrExit(err = helper->ScheduleWork()); + ReturnErrorOnFailure(helper->ScheduleWork()); mSendSigma3Helper = helper; mExchangeCtxt.Value()->WillSendMessage(); mState = State::kSendSigma3Pending; } else { - SuccessOrExit(err = helper->DoWork()); + ReturnErrorOnFailure(helper->DoWork()); } } -exit: - if (err != CHIP_NO_ERROR) - { - SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam); - mState = State::kInitialized; - } - - return err; + return CHIP_NO_ERROR; } CHIP_ERROR CASESession::SendSigma3b(SendSigma3Data & data, bool & cancel) @@ -1848,9 +1898,9 @@ CHIP_ERROR CASESession::SendSigma3c(SendSigma3Data & data, CHIP_ERROR status) TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified; tlvWriter.Init(std::move(msg_R3)); - err = tlvWriter.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainerType); + err = tlvWriter.StartContainer(AnonymousTag(), kTLVType_Structure, outerContainerType); SuccessOrExit(err); - err = tlvWriter.PutBytes(TLV::ContextTag(1), data.msg_R3_Encrypted.Get(), + err = tlvWriter.PutBytes(AsTlvContextTag(Sigma3Tags::kEncrypted3), data.msg_R3_Encrypted.Get(), static_cast(data.msg_r3_encrypted_len + CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES)); SuccessOrExit(err); err = tlvWriter.EndContainer(outerContainerType); @@ -1983,7 +2033,7 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) } // Step 4 - Construct Sigma3 TBS Data - data.msg_r3_signed_len = TLV::EstimateStructOverhead(sizeof(uint16_t), data.initiatorNOC.size(), data.initiatorICAC.size(), + data.msg_r3_signed_len = TLV::EstimateStructOverhead(data.initiatorNOC.size(), data.initiatorICAC.size(), kP256_PublicKey_Length, kP256_PublicKey_Length); VerifyOrExit(data.msg_R3_Signed.Alloc(data.msg_r3_signed_len), err = CHIP_ERROR_NO_MEMORY); @@ -2209,11 +2259,11 @@ CHIP_ERROR CASESession::ValidateSigmaResumeMIC(const ByteSpan & resumeMIC, const CHIP_ERROR CASESession::ConstructTBSData(const ByteSpan & senderNOC, const ByteSpan & senderICAC, const ByteSpan & senderPubKey, const ByteSpan & receiverPubKey, uint8_t * tbsData, size_t & tbsDataLen) { - TLV::TLVWriter tlvWriter; - TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified; + TLVWriter tlvWriter; + TLVType outerContainerType = kTLVType_NotSpecified; tlvWriter.Init(tbsData, tbsDataLen); - ReturnErrorOnFailure(tlvWriter.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainerType)); + ReturnErrorOnFailure(tlvWriter.StartContainer(AnonymousTag(), kTLVType_Structure, outerContainerType)); ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(TBSDataTags::kSenderNOC), senderNOC)); if (!senderICAC.empty()) { @@ -2353,7 +2403,7 @@ CHIP_ERROR CASESession::ParseSigma1(TLV::ContiguousBufferTLVReader & tlvReader, { ReturnErrorOnFailure(DecodeMRPParametersIfPresent(AsTlvContextTag(Sigma1Tags::kInitiatorSessionParams), tlvReader, outParsedSigma1.initiatorSessionParams)); - outParsedSigma1.initiatorMrpParamsPresent = true; + outParsedSigma1.initiatorSessionParamsPresent = true; err = tlvReader.Next(); } @@ -2404,73 +2454,6 @@ CHIP_ERROR CASESession::ParseSigma1(TLV::ContiguousBufferTLVReader & tlvReader, return CHIP_NO_ERROR; } -CHIP_ERROR CASESession::ParseSigma2(ContiguousBufferTLVReader & tlvReader, ParsedSigma2 & outParsedSigma2) -{ - CHIP_ERROR err = CHIP_NO_ERROR; - TLVType containerType = kTLVType_Structure; - - // FIND A SOLUTION TO THIS: - P256ECDSASignature tbsData2Signature; - - ReturnErrorOnFailure(err = tlvReader.Next(containerType, AnonymousTag())); - ReturnErrorOnFailure(err = tlvReader.EnterContainer(containerType)); - - // Retrieve Responder's Random value - ReturnErrorOnFailure(err = tlvReader.Next(kTLVType_ByteString, AsTlvContextTag(Sigma2Tags::kResponderRandom))); - ReturnErrorOnFailure(err = tlvReader.GetByteView(outParsedSigma2.responderRandom)); - VerifyOrReturnError(outParsedSigma2.responderRandom.size() == kSigmaParamRandomNumberSize, CHIP_ERROR_INVALID_CASE_PARAMETER); - - // Assign Session ID - ReturnErrorOnFailure(err = tlvReader.Next(kTLVType_UnsignedInteger, AsTlvContextTag(Sigma2Tags::kResponderSessionId))); - ReturnErrorOnFailure(err = tlvReader.Get(outParsedSigma2.responderSessionId)); - - // Retrieve Responder's Ephemeral Pubkey - ReturnErrorOnFailure(err = tlvReader.Next(kTLVType_ByteString, AsTlvContextTag(Sigma2Tags::kResponderEphPubKey))); - ReturnErrorOnFailure(tlvReader.GetByteView(outParsedSigma2.responderEphPubKey)); - VerifyOrReturnError(outParsedSigma2.responderEphPubKey.size() == kP256_PublicKey_Length, CHIP_ERROR_INVALID_CASE_PARAMETER); - - // ReturnErrorOnFailure(err = tlvReader.GetBytes(mRemotePubKey, static_cast(mRemotePubKey.Length()))); - - // Generate decrypted data - ReturnErrorOnFailure(err = tlvReader.Next(kTLVType_ByteString, AsTlvContextTag(Sigma2Tags::kEncrypted2))); - - // TODO find a solution to this - // size_t msgR2EncryptedLen = 0; - - size_t msgR2EncryptedLenWithTag = 0; - - constexpr size_t kCaseOverheadForFutureTbeData = 128; - - size_t maxMsgR2SignedEncLen; - - maxMsgR2SignedEncLen = EstimateStructOverhead(kMaxCHIPCertLength, kMaxCHIPCertLength, tbsData2Signature.Length(), - SessionResumptionStorage::kResumptionIdSize, kCaseOverheadForFutureTbeData); - msgR2EncryptedLenWithTag = tlvReader.GetLength(); - - // Validate we did not receive a buffer larger than legal - // TODO why are comparing this to the "signed one" - // Maybe it is signed encrypted, but is the estimation calculated well? document it - VerifyOrReturnError(msgR2EncryptedLenWithTag <= maxMsgR2SignedEncLen, err = CHIP_ERROR_INVALID_TLV_ELEMENT); - VerifyOrReturnError(msgR2EncryptedLenWithTag > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, err = CHIP_ERROR_INVALID_TLV_ELEMENT); - // Allocate after making sure that msgR2EncryptedLenWithTag is within bounds - VerifyOrReturnError(outParsedSigma2.msgR2Encrypted.Alloc(msgR2EncryptedLenWithTag), err = CHIP_ERROR_NO_MEMORY); - - // TODO, should I keep this as GetBytes? or should I use GetByteView for consistency and do something else? - ReturnErrorOnFailure( - err = tlvReader.GetBytes(outParsedSigma2.msgR2Encrypted.Get(), outParsedSigma2.msgR2Encrypted.AllocatedSize())); - // msgR2EncryptedLen = msgR2EncryptedLenWithTag - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; - - // Retrieve responderMRPParams if present - if (tlvReader.Next() != CHIP_END_OF_TLV) - { - ReturnErrorOnFailure(err = DecodeMRPParametersIfPresent(AsTlvContextTag(Sigma2Tags::kResponderSessionParams), tlvReader, - outParsedSigma2.responderSessionParams)); - outParsedSigma2.responderMrpParamsPresent = true; - } - - return CHIP_NO_ERROR; -} - CHIP_ERROR CASESession::ValidateReceivedMessage(ExchangeContext * ec, const PayloadHeader & payloadHeader, const System::PacketBufferHandle & msg) { diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h index 51499f0a2afd5d..82e7ec1bdc6cfc 100644 --- a/src/protocols/secure_channel/CASESession.h +++ b/src/protocols/secure_channel/CASESession.h @@ -222,7 +222,7 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, struct ParsedSigma1 : Sigma1Param { ByteSpan initiatorEphPubKey; - bool initiatorMrpParamsPresent = false; + bool initiatorSessionParamsPresent = false; SessionParameters initiatorSessionParams; }; @@ -242,7 +242,7 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, { ByteSpan resumptionId; uint8_t sigma2ResumeMICBuffer[Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES]; - MutableByteSpan resumeMIC{ sigma2ResumeMICBuffer }; + MutableByteSpan sigma2ResumeMIC{ sigma2ResumeMICBuffer }; uint16_t responderSessionId; const ReliableMessageProtocolConfig * responderMrpConfig; }; @@ -256,9 +256,8 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, ByteSpan responderEphPubKey; Platform::ScopedMemoryBufferWithSize msgR2Encrypted; - size_t encrypted2Length = 0; - const ReliableMessageProtocolConfig * responderMrpConfig; - bool responderMrpParamsPresent = false; + size_t encrypted2Length = 0; + bool responderSessionParamStructPresent = false; SessionParameters responderSessionParams; }; @@ -275,10 +274,9 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, ByteSpan resumptionId; ByteSpan sigma2ResumeMIC; uint16_t responderSessionId; - const ReliableMessageProtocolConfig * responderMrpConfig; SessionParameters responderSessionParams; // TODO consider removing this? - bool responderMrpParamsPresent = false; + bool responderSessionParamStructPresent = false; }; /** @@ -327,6 +325,7 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, * * @param inParam a struct containing all the values that will be encoded into TLV format * + * @note The inParam member msgR2Encrypted will be freed after encoding it. **/ static CHIP_ERROR EncodeSigma2(System::PacketBufferHandle & outMsg, EncodeSigma2Inputs & inParam); diff --git a/src/protocols/secure_channel/tests/TestCASESession.cpp b/src/protocols/secure_channel/tests/TestCASESession.cpp index 902b7c2af4f5f0..58141c40de1709 100644 --- a/src/protocols/secure_channel/tests/TestCASESession.cpp +++ b/src/protocols/secure_channel/tests/TestCASESession.cpp @@ -1,3 +1,4 @@ + /* * * Copyright (c) 2021 Project CHIP Authors @@ -637,12 +638,13 @@ struct Sigma1Params { // Purposefully not using constants like kSigmaParamRandomNumberSize that // the code uses, so we have a cross-check. - static constexpr size_t kInitiatorRandomLen = 32; - static constexpr uint16_t kInitiatorSessionId = 0; - static constexpr size_t kDestinationIdLen = 32; - static constexpr size_t kInitiatorEphPubKeyLen = 65; - static constexpr size_t kResumptionIdLen = 0; // Nonzero means include it. - static constexpr size_t kInitiatorResumeMICLen = 0; // Nonzero means include it. + static constexpr size_t kInitiatorRandomLen = 32; + static constexpr uint16_t kInitiatorSessionId = 0; + static constexpr size_t kDestinationIdLen = 32; + static constexpr size_t kInitiatorEphPubKeyLen = 65; + static constexpr size_t kResumptionIdLen = 0; // Nonzero means include it. + static constexpr size_t kInitiatorResumeMICLen = 0; // Nonzero means include it. + static constexpr uint16_t kFutureProofTlvElement = 77; static constexpr uint8_t kInitiatorRandomTag = 1; static constexpr uint8_t kInitiatorSessionIdTag = 2; @@ -650,8 +652,12 @@ struct Sigma1Params static constexpr uint8_t kInitiatorEphPubKeyTag = 4; static constexpr uint8_t kResumptionIdTag = 6; static constexpr uint8_t kInitiatorResumeMICTag = 7; + // Choosing a tag that is higher than the current highest tag value in the Specification + static constexpr uint8_t kFutureProofTlvElementTag = 11; static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } + static constexpr bool kIncludeFutureProofTlvElement = false; + static constexpr bool kIncludeStructEnd = true; static constexpr bool kExpectSuccess = true; @@ -748,6 +754,12 @@ static CHIP_ERROR EncodeSigma1Helper(MutableByteSpan & buf) ByteSpan(initiatorResumeMIC, Params::kInitiatorResumeMICLen))); } + // Future-proofing: Ensure that TLV elements being added to the specification in the future are properly handled. + if constexpr (Params::kIncludeFutureProofTlvElement) + { + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kFutureProofTlvElementTag), Params::kFutureProofTlvElement)); + } + if constexpr (Params::kIncludeStructEnd) { ReturnErrorOnFailure(writer.EndContainer(containerType)); @@ -862,6 +874,17 @@ struct Sigma1SessionIdTooBig : public BadSigma1ParamsBase static constexpr uint32_t kInitiatorSessionId = UINT16_MAX + 1; }; +struct Sigma1FutureProofTlvElement : public Sigma1Params +{ + static constexpr bool kIncludeFutureProofTlvElement = true; +}; + +struct Sigma1FutureProofTlvElementNoStructEnd : public BadSigma1ParamsBase +{ + static constexpr bool kIncludeFutureProofTlvElement = true; + static constexpr bool kIncludeStructEnd = false; +}; + TEST_F(TestCASESession, Sigma1ParsingTest) { // 1280 bytes must be enough by definition. @@ -885,6 +908,8 @@ TEST_F(TestCASESession, Sigma1ParsingTest) TestSigma1Parsing(mem, bufferSize, Sigma1TooShortResumeMIC); TestSigma1Parsing(mem, bufferSize, Sigma1SessionIdMax); TestSigma1Parsing(mem, bufferSize, Sigma1SessionIdTooBig); + TestSigma1Parsing(mem, bufferSize, Sigma1FutureProofTlvElement); + TestSigma1Parsing(mem, bufferSize, Sigma1FutureProofTlvElementNoStructEnd); } TEST_F(TestCASESession, EncodeSigma1Test) @@ -1003,14 +1028,19 @@ struct Sigma2Params static constexpr size_t kEncrypted2Len = CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES + 1; // Needs to be at least CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES static constexpr size_t kResponderSessionParamsLen = 0; // Nonzero means include it. + static constexpr uint16_t kFutureProofTlvElement = 77; static constexpr uint8_t kResponderRandomTag = 1; static constexpr uint8_t kResponderSessionIdTag = 2; static constexpr uint8_t kResponderEphPubKeyTag = 3; static constexpr uint8_t kEncrypted2Tag = 4; static constexpr uint8_t kResponderSessionParamsTag = 5; // TODO SessionParams are not tested in non of ParseSigmaTests + static constexpr uint8_t kFutureProofTlvElementTag = 11; + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } + static constexpr bool kIncludeFutureProofTlvElement = false; + static constexpr bool kIncludeStructEnd = true; static constexpr bool kExpectSuccess = true; @@ -1037,6 +1067,12 @@ static CHIP_ERROR EncodeSigma2Helper(MutableByteSpan & buf) uint8_t encrypted2[Params::kEncrypted2Len] = { 3 }; ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kEncrypted2Tag), ByteSpan(encrypted2))); + // Future-proofing: Ensure that TLV elements being added to the specification in the future are properly handled. + if constexpr (Params::kIncludeFutureProofTlvElement) + { + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kFutureProofTlvElementTag), Params::kFutureProofTlvElement)); + } + if constexpr (Params::kIncludeStructEnd) { ReturnErrorOnFailure(writer.EndContainer(containerType)); @@ -1108,6 +1144,18 @@ struct Sigma2SessionIdTooBig : public BadSigma2ParamsBase { static constexpr uint32_t kResponderSessionId = UINT16_MAX + 1; }; + +struct Sigma2FutureProofTlvElement : public Sigma2Params +{ + static constexpr bool kIncludeFutureProofTlvElement = true; +}; + +struct Sigma2FutureProofTlvElementNoStructEnd : public BadSigma2ParamsBase +{ + static constexpr bool kIncludeFutureProofTlvElement = true; + static constexpr bool kIncludeStructEnd = false; +}; + // TODO: Consider making this test (and similar ones) to Value-Parameterized tests once pw_unit_test:light starts supporting them TEST_F(TestCASESession, Sigma2ParsingTest) { @@ -1125,6 +1173,8 @@ TEST_F(TestCASESession, Sigma2ParsingTest) TestSigma2Parsing(mem, bufferSize, Sigma2TooShortPubkey); TestSigma2Parsing(mem, bufferSize, Sigma2SessionIdMax); TestSigma2Parsing(mem, bufferSize, Sigma2SessionIdTooBig); + TestSigma2Parsing(mem, bufferSize, Sigma2FutureProofTlvElement); + TestSigma2Parsing(mem, bufferSize, Sigma2FutureProofTlvElementNoStructEnd); } TEST_F(TestCASESession, EncodeSigma2Test) @@ -1249,13 +1299,18 @@ struct Sigma2ResumeParams static constexpr size_t kSigma2ResumeMICLen = 16; static constexpr uint16_t kResponderSessionId = 0; static constexpr size_t kResponderSessionParamsLen = 0; // Nonzero means include it. TODO should I remove this + static constexpr uint16_t kFutureProofTlvElement = 77; static constexpr uint8_t kResumptionIdTag = 1; static constexpr uint8_t kSigma2ResumeMICTag = 2; static constexpr uint8_t kResponderSessionIdTag = 3; static constexpr uint8_t kResponderSessionParamsTag = 4; // TODO SessionParams are not tested in non of ParseSigmaTests + static constexpr uint8_t kFutureProofTlvElementTag = 11; + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } + static constexpr bool kIncludeFutureProofTlvElement = false; + static constexpr bool kIncludeStructEnd = true; static constexpr bool kExpectSuccess = true; @@ -1280,6 +1335,12 @@ static CHIP_ERROR EncodeSigma2ResumeHelper(MutableByteSpan & buf) ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResponderSessionIdTag), Params::kResponderSessionId)); + // Future-proofing: Ensure that TLV elements being added to the specification in the future are properly handled. + if constexpr (Params::kIncludeFutureProofTlvElement) + { + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kFutureProofTlvElementTag), Params::kFutureProofTlvElement)); + } + if constexpr (Params::kIncludeStructEnd) { ReturnErrorOnFailure(writer.EndContainer(containerType)); @@ -1352,6 +1413,17 @@ struct Sigma2ResumeSessionIdTooBig : public BadSigma2ResumeParamsBase static constexpr uint32_t kResponderSessionId = UINT16_MAX + 1; }; +struct Sigma2ResumeFutureProofTlvElement : public Sigma2ResumeParams +{ + static constexpr bool kIncludeFutureProofTlvElement = true; +}; + +struct Sigma2ResumeFutureProofTlvElementNoStructEnd : public BadSigma2ResumeParamsBase +{ + static constexpr bool kIncludeFutureProofTlvElement = true; + static constexpr bool kIncludeStructEnd = false; +}; + TEST_F(TestCASESession, ParseSigma2Resume) { // 1280 bytes must be enough by definition. @@ -1368,6 +1440,8 @@ TEST_F(TestCASESession, ParseSigma2Resume) TestSigma2ResumeParsing(mem, bufferSize, Sigma2ResumeTooShortResumeMIC); TestSigma2ResumeParsing(mem, bufferSize, Sigma2ResumeSessionIdMax); TestSigma2ResumeParsing(mem, bufferSize, Sigma2ResumeSessionIdTooBig); + TestSigma2ResumeParsing(mem, bufferSize, Sigma2ResumeFutureProofTlvElement); + TestSigma2ResumeParsing(mem, bufferSize, Sigma2ResumeFutureProofTlvElementNoStructEnd); } TEST_F(TestCASESession, EncodeSigma2ResumeTest) @@ -1435,8 +1509,8 @@ struct SessionResumptionTestStorage : SessionResumptionStorage { SessionResumptionTestStorage(CHIP_ERROR findMethodReturnCode, ScopedNodeId peerNodeId, ResumptionIdStorage * resumptionId, Crypto::P256ECDHDerivedSecret * sharedSecret) : - mFindMethodReturnCode(findMethodReturnCode), mPeerNodeId(peerNodeId), mResumptionId(resumptionId), - mSharedSecret(sharedSecret) + mFindMethodReturnCode(findMethodReturnCode), + mPeerNodeId(peerNodeId), mResumptionId(resumptionId), mSharedSecret(sharedSecret) {} SessionResumptionTestStorage(CHIP_ERROR findMethodReturnCode) : mFindMethodReturnCode(findMethodReturnCode) {} CHIP_ERROR FindByScopedNodeId(const ScopedNodeId & node, ResumptionIdStorage & resumptionId, @@ -1717,17 +1791,22 @@ struct Sigma2TBEDataParams { // Purposefully not using constants like kSigmaParamRandomNumberSize that // the code uses, so we have a cross-check. - static constexpr size_t kResponderNOCLen = 400; - static constexpr size_t kResponderICACLen = 400; - static constexpr size_t kSignatureLen = 64; - static constexpr size_t kResumptionIdLen = 16; - - static constexpr uint8_t kResponderNOCTag = 1; - static constexpr uint8_t kResponderICACTag = 2; - static constexpr uint8_t kSignatureTag = 3; - static constexpr uint8_t kResumptionIdTag = 4; + static constexpr size_t kResponderNOCLen = 400; + static constexpr size_t kResponderICACLen = 400; + static constexpr size_t kSignatureLen = 64; + static constexpr size_t kResumptionIdLen = 16; + static constexpr uint16_t kFutureProofTlvElement = 77; + + static constexpr uint8_t kResponderNOCTag = 1; + static constexpr uint8_t kResponderICACTag = 2; + static constexpr uint8_t kSignatureTag = 3; + static constexpr uint8_t kResumptionIdTag = 4; + static constexpr uint8_t kFutureProofTlvElementTag = 11; + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } + static constexpr bool kIncludeFutureProofTlvElement = false; + static constexpr bool kIncludeStructEnd = true; static constexpr bool kExpectSuccess = true; @@ -1755,8 +1834,11 @@ static CHIP_ERROR EncodeSigma2TBEDataHelper(MutableByteSpan & buf) uint8_t resumptionId[Params::kResumptionIdLen] = { 22 }; ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResumptionIdTag), ByteSpan(resumptionId))); - uint8_t resumptionIssd[20] = { 22 }; - ReturnErrorOnFailure(writer.Put(TLV::ContextTag(5), ByteSpan(resumptionIssd))); + // Future-proofing: Ensure that TLV elements being added to the specification in the future are properly handled. + if constexpr (Params::kIncludeFutureProofTlvElement) + { + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kFutureProofTlvElementTag), Params::kFutureProofTlvElement)); + } if constexpr (Params::kIncludeStructEnd) { @@ -1829,6 +1911,17 @@ struct Sigma2TBETooShortResumptionID : public BadSigma2TBEParamsBase static constexpr size_t kResumptionIdLen = 15; }; +struct Sigma2TBEFutureProofTlvElement : public Sigma2TBEDataParams +{ + static constexpr bool kIncludeFutureProofTlvElement = true; +}; + +struct Sigma2TBEFutureProofTlvElementNoStructEnd : public BadSigma2TBEParamsBase +{ + static constexpr bool kIncludeFutureProofTlvElement = true; + static constexpr bool kIncludeStructEnd = false; +}; + TEST_F(TestCASESession, ParseSigma2TBEData) { // 1280 bytes must be enough by definition. @@ -1845,6 +1938,8 @@ TEST_F(TestCASESession, ParseSigma2TBEData) TestSigma2TBEParsing(mem, bufferSize, Sigma2TBETooShortSignature); TestSigma2TBEParsing(mem, bufferSize, Sigma2TBETooLongResumptionID); TestSigma2TBEParsing(mem, bufferSize, Sigma2TBETooShortResumptionID); + TestSigma2TBEParsing(mem, bufferSize, Sigma2TBEFutureProofTlvElement); + TestSigma2TBEParsing(mem, bufferSize, Sigma2TBEFutureProofTlvElementNoStructEnd); } } // namespace chip From 5c9281bb1f18a6fd39bc0c6d19d95803aeaa4f4e Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Sat, 11 Jan 2025 19:40:22 +0100 Subject: [PATCH 09/15] Factor out ParseSigma3 and ParseSigma3TBEData --- src/protocols/secure_channel/CASESession.cpp | 141 ++++++++++--------- src/protocols/secure_channel/CASESession.h | 26 +++- 2 files changed, 100 insertions(+), 67 deletions(-) diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index a612f336459c44..a8d70eaee00e8a 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -372,25 +372,6 @@ struct CASESession::SendSigma3Data P256ECDSASignature tbsData3Signature; }; -struct CASESession::HandleSigma3Data -{ - chip::Platform::ScopedMemoryBuffer msg_R3_Signed; - size_t msg_r3_signed_len; - - ByteSpan initiatorNOC; - ByteSpan initiatorICAC; - - uint8_t rootCertBuf[kMaxCHIPCertLength]; - ByteSpan fabricRCAC; - - P256ECDSASignature tbsData3Signature; - - FabricId fabricId; - NodeId initiatorNodeId; - - ValidationContext validContext; -}; - CASESession::~CASESession() { // Let's clear out any security state stored in the object, before destroying it. @@ -1947,18 +1928,14 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) MATTER_TRACE_SCOPE("HandleSigma3", "CASESession"); CHIP_ERROR err = CHIP_NO_ERROR; System::PacketBufferTLVReader tlvReader; - TLV::TLVReader decryptedDataTlvReader; + TLV::ContiguousBufferTLVReader decryptedDataTlvReader; TLV::TLVType containerType = TLV::kTLVType_Structure; const uint8_t * buf = msg->Start(); const size_t bufLen = msg->DataLength(); - constexpr size_t kCaseOverheadForFutureTbeData = 128; - - chip::Platform::ScopedMemoryBuffer msg_R3_Encrypted; - size_t msg_r3_encrypted_len = 0; - size_t msg_r3_encrypted_len_with_tag = 0; - size_t max_msg_r3_signed_enc_len; + Platform::ScopedMemoryBufferWithSize msgR3Encrypted; + size_t msgR3EncryptedLen = 0; AutoReleaseSessionKey sr3k(*mSessionManager->GetSessionKeystore()); @@ -1983,24 +1960,8 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) VerifyOrExit(mEphemeralKey != nullptr, err = CHIP_ERROR_INTERNAL); tlvReader.Init(std::move(msg)); - SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag())); - SuccessOrExit(err = tlvReader.EnterContainer(containerType)); - - // Fetch encrypted data - max_msg_r3_signed_enc_len = TLV::EstimateStructOverhead(Credentials::kMaxCHIPCertLength, Credentials::kMaxCHIPCertLength, - data.tbsData3Signature.Length(), kCaseOverheadForFutureTbeData); - SuccessOrExit(err = tlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(Sigma3Tags::kEncrypted3))); - - msg_r3_encrypted_len_with_tag = tlvReader.GetLength(); - - // Validate we did not receive a buffer larger than legal - VerifyOrExit(msg_r3_encrypted_len_with_tag <= max_msg_r3_signed_enc_len, err = CHIP_ERROR_INVALID_TLV_ELEMENT); - VerifyOrExit(msg_r3_encrypted_len_with_tag > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, err = CHIP_ERROR_INVALID_TLV_ELEMENT); - - VerifyOrExit(msg_R3_Encrypted.Alloc(msg_r3_encrypted_len_with_tag), err = CHIP_ERROR_NO_MEMORY); - SuccessOrExit(err = tlvReader.GetBytes(msg_R3_Encrypted.Get(), static_cast(msg_r3_encrypted_len_with_tag))); - msg_r3_encrypted_len = msg_r3_encrypted_len_with_tag - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; + SuccessOrExit(err = ParseSigma3(tlvReader, msgR3Encrypted)); // Step 1 { @@ -2012,27 +1973,17 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ buf, bufLen })); // Step 2 - Decrypt data blob - SuccessOrExit(err = AES_CCM_decrypt(msg_R3_Encrypted.Get(), msg_r3_encrypted_len, nullptr, 0, - msg_R3_Encrypted.Get() + msg_r3_encrypted_len, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, - sr3k.KeyHandle(), kTBEData3_Nonce, kTBEDataNonceLength, msg_R3_Encrypted.Get())); + msgR3EncryptedLen = msgR3Encrypted.AllocatedSize() - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; - decryptedDataTlvReader.Init(msg_R3_Encrypted.Get(), msg_r3_encrypted_len); - containerType = TLV::kTLVType_Structure; - SuccessOrExit(err = decryptedDataTlvReader.Next(containerType, TLV::AnonymousTag())); - SuccessOrExit(err = decryptedDataTlvReader.EnterContainer(containerType)); + SuccessOrExit(err = AES_CCM_decrypt(msgR3Encrypted.Get(), msgR3EncryptedLen, nullptr, 0, + msgR3Encrypted.Get() + msgR3EncryptedLen, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, + sr3k.KeyHandle(), kTBEData3_Nonce, kTBEDataNonceLength, msgR3Encrypted.Get())); - SuccessOrExit(err = decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSenderNOC))); - SuccessOrExit(err = decryptedDataTlvReader.Get(data.initiatorNOC)); + decryptedDataTlvReader.Init(msgR3Encrypted.Get(), msgR3EncryptedLen); - SuccessOrExit(err = decryptedDataTlvReader.Next()); - if (decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSenderICAC)) - { - VerifyOrExit(decryptedDataTlvReader.GetType() == TLV::kTLVType_ByteString, err = CHIP_ERROR_WRONG_TLV_TYPE); - SuccessOrExit(err = decryptedDataTlvReader.Get(data.initiatorICAC)); - SuccessOrExit(err = decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSignature))); - } + SuccessOrExit(err = ParseSigma3TBEData(decryptedDataTlvReader, data)); - // Step 4 - Construct Sigma3 TBS Data + // Step 3 - Construct Sigma3 TBS Data data.msg_r3_signed_len = TLV::EstimateStructOverhead(data.initiatorNOC.size(), data.initiatorICAC.size(), kP256_PublicKey_Length, kP256_PublicKey_Length); @@ -2042,12 +1993,7 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) ByteSpan(mEphemeralKey->Pubkey(), mEphemeralKey->Pubkey().Length()), data.msg_R3_Signed.Get(), data.msg_r3_signed_len)); - VerifyOrExit(decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSignature), err = CHIP_ERROR_INVALID_TLV_TAG); - VerifyOrExit(data.tbsData3Signature.Capacity() >= decryptedDataTlvReader.GetLength(), err = CHIP_ERROR_INVALID_TLV_ELEMENT); - data.tbsData3Signature.SetLength(decryptedDataTlvReader.GetLength()); - SuccessOrExit(err = decryptedDataTlvReader.GetBytes(data.tbsData3Signature.Bytes(), data.tbsData3Signature.Length())); - - // Prepare for Step 5/6 + // Prepare for Step 4/5 { MutableByteSpan fabricRCAC{ data.rootCertBuf }; SuccessOrExit(err = mFabricsTable->FetchRootCert(mFabricIndex, fabricRCAC)); @@ -2093,6 +2039,69 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) return err; } +CHIP_ERROR CASESession::ParseSigma3(ContiguousBufferTLVReader & tlvReader, + Platform::ScopedMemoryBufferWithSize & msgR3Encrypted) +{ + TLVType containerType = kTLVType_Structure; + + ReturnErrorOnFailure(tlvReader.Next(containerType, AnonymousTag())); + ReturnErrorOnFailure(tlvReader.EnterContainer(containerType)); + + // Fetch encrypted data + size_t maxMsgR3SignedEncLen; + constexpr size_t kCaseOverheadForFutureTbeData = 128; + + maxMsgR3SignedEncLen = EstimateStructOverhead(Credentials::kMaxCHIPCertLength, // initiatorNOC + Credentials::kMaxCHIPCertLength, // initiatorICAC + kMax_ECDSA_Signature_Length, // signature + kCaseOverheadForFutureTbeData // extra bytes for future-proofing + ); + + ReturnErrorOnFailure(tlvReader.Next(kTLVType_ByteString, AsTlvContextTag(Sigma3Tags::kEncrypted3))); + + size_t msgR3EncryptedLenWithTag = 0; + msgR3EncryptedLenWithTag = tlvReader.GetLength(); + + // Validate we did not receive a buffer larger than legal + VerifyOrReturnError(msgR3EncryptedLenWithTag <= maxMsgR3SignedEncLen, CHIP_ERROR_INVALID_TLV_ELEMENT); + VerifyOrReturnError(msgR3EncryptedLenWithTag > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, CHIP_ERROR_INVALID_TLV_ELEMENT); + + VerifyOrReturnError(msgR3Encrypted.Alloc(msgR3EncryptedLenWithTag), CHIP_ERROR_NO_MEMORY); + ReturnErrorOnFailure(tlvReader.GetBytes(msgR3Encrypted.Get(), static_cast(msgR3EncryptedLenWithTag))); + + ReturnErrorOnFailure(tlvReader.ExitContainer(containerType)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR CASESession::ParseSigma3TBEData(ContiguousBufferTLVReader & decryptedDataTlvReader, HandleSigma3Data & data) +{ + + TLVType containerType = kTLVType_Structure; + ReturnErrorOnFailure(decryptedDataTlvReader.Next(containerType, TLV::AnonymousTag())); + ReturnErrorOnFailure(decryptedDataTlvReader.EnterContainer(containerType)); + + ReturnErrorOnFailure(decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSenderNOC))); + ReturnErrorOnFailure(decryptedDataTlvReader.Get(data.initiatorNOC)); + + ReturnErrorOnFailure(decryptedDataTlvReader.Next()); + if (decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSenderICAC)) + { + VerifyOrReturnError(decryptedDataTlvReader.GetType() == TLV::kTLVType_ByteString, CHIP_ERROR_WRONG_TLV_TYPE); + ReturnErrorOnFailure(decryptedDataTlvReader.Get(data.initiatorICAC)); + ReturnErrorOnFailure(decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSignature))); + } + + VerifyOrReturnError(decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSignature), CHIP_ERROR_INVALID_TLV_TAG); + VerifyOrReturnError(data.tbsData3Signature.Capacity() >= decryptedDataTlvReader.GetLength(), CHIP_ERROR_INVALID_TLV_ELEMENT); + data.tbsData3Signature.SetLength(decryptedDataTlvReader.GetLength()); + ReturnErrorOnFailure(decryptedDataTlvReader.GetBytes(data.tbsData3Signature.Bytes(), data.tbsData3Signature.Length())); + + ReturnErrorOnFailure(decryptedDataTlvReader.ExitContainer(containerType)); + + return CHIP_NO_ERROR; +} + CHIP_ERROR CASESession::HandleSigma3b(HandleSigma3Data & data, bool & cancel) { // Step 5/6 diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h index 82e7ec1bdc6cfc..082ba7224a94dd 100644 --- a/src/protocols/secure_channel/CASESession.h +++ b/src/protocols/secure_channel/CASESession.h @@ -279,6 +279,25 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, bool responderSessionParamStructPresent = false; }; + struct HandleSigma3Data + { + chip::Platform::ScopedMemoryBuffer msg_R3_Signed; + size_t msg_r3_signed_len; + + ByteSpan initiatorNOC; + ByteSpan initiatorICAC; + + uint8_t rootCertBuf[Credentials::kMaxCHIPCertLength]; + ByteSpan fabricRCAC; + + Crypto::P256ECDSASignature tbsData3Signature; + + FabricId fabricId; + NodeId initiatorNodeId; + + Credentials::ValidationContext validContext; + }; + /** * @brief Encodes a Sigma1 message into TLV format and allocates a buffer for it, which is owned by the PacketBufferHandle * outparam. @@ -342,6 +361,11 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, **/ static CHIP_ERROR EncodeSigma2Resume(System::PacketBufferHandle & outMsg, EncodeSigma2ResumeInputs & inParam); + static CHIP_ERROR ParseSigma3(TLV::ContiguousBufferTLVReader & tlvReader, + Platform::ScopedMemoryBufferWithSize & msgR3Encrypted); + + CHIP_ERROR ParseSigma3TBEData(TLV::ContiguousBufferTLVReader & tlvReader, HandleSigma3Data & data); + private: friend class TestCASESession; @@ -385,7 +409,7 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, static CHIP_ERROR SendSigma3b(SendSigma3Data & data, bool & cancel); CHIP_ERROR SendSigma3c(SendSigma3Data & data, CHIP_ERROR status); - struct HandleSigma3Data; + // struct HandleSigma3Data; CHIP_ERROR HandleSigma3a(System::PacketBufferHandle && msg); static CHIP_ERROR HandleSigma3b(HandleSigma3Data & data, bool & cancel); CHIP_ERROR HandleSigma3c(HandleSigma3Data & data, CHIP_ERROR status); From ac790e012ed4104db7a2259f62bbdfb134711ca7 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Sun, 12 Jan 2025 16:06:12 +0100 Subject: [PATCH 10/15] Unit Tests for ParseSigma3 and ParseSigma3TBEData --- .../secure_channel/tests/TestCASESession.cpp | 306 +++++++++++++++++- 1 file changed, 296 insertions(+), 10 deletions(-) diff --git a/src/protocols/secure_channel/tests/TestCASESession.cpp b/src/protocols/secure_channel/tests/TestCASESession.cpp index 58141c40de1709..9e0795fc52686c 100644 --- a/src/protocols/secure_channel/tests/TestCASESession.cpp +++ b/src/protocols/secure_channel/tests/TestCASESession.cpp @@ -69,6 +69,7 @@ class CASESessionAccess : public CASESession using CASESession::EncodeSigma1Inputs; using CASESession::EncodeSigma2Inputs; using CASESession::EncodeSigma2ResumeInputs; + using CASESession::HandleSigma3Data; using CASESession::ParsedSigma1; using CASESession::ParsedSigma2; using CASESession::ParsedSigma2Resume; @@ -81,6 +82,8 @@ class CASESessionAccess : public CASESession using CASESession::ParseSigma2; using CASESession::ParseSigma2Resume; using CASESession::ParseSigma2TBEData; + using CASESession::ParseSigma3; + using CASESession::ParseSigma3TBEData; }; class TestCASESession : public Test::LoopbackMessagingContext @@ -1018,6 +1021,16 @@ TEST_F(TestCASESession, EncodeSigma1Test) gDeviceOperationalKeystore.ReleaseEphemeralKeypair(ephemeralKey); } +constexpr size_t kCaseOverheadForFutureTbeData = 128; + +constexpr size_t kMaxMsgR2SignedEncLen = + TLV::EstimateStructOverhead(kMaxCHIPCertLength, // responderNOC + kMaxCHIPCertLength, // responderICAC + kMax_ECDSA_Signature_Length, // signature + SessionResumptionStorage::kResumptionIdSize, // resumptionID + kCaseOverheadForFutureTbeData // extra bytes for future-proofing + ); + struct Sigma2Params { // Purposefully not using constants like kSigmaParamRandomNumberSize that @@ -1026,7 +1039,7 @@ struct Sigma2Params static constexpr uint16_t kResponderSessionId = 0; static constexpr size_t kResponderEphPubKeyLen = 65; static constexpr size_t kEncrypted2Len = - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES + 1; // Needs to be at least CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES + CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES + 1; // Needs to be bigger than CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES static constexpr size_t kResponderSessionParamsLen = 0; // Nonzero means include it. static constexpr uint16_t kFutureProofTlvElement = 77; @@ -1125,6 +1138,15 @@ struct Sigma2TooShortRandom : public BadSigma2ParamsBase static constexpr size_t kResponderRandomLen = 31; }; +struct Sigma2SessionIdMax : public Sigma2Params +{ + static constexpr uint32_t kResponderSessionId = UINT16_MAX; +}; + +struct Sigma2SessionIdTooBig : public BadSigma2ParamsBase +{ + static constexpr uint32_t kResponderSessionId = UINT16_MAX + 1; +}; struct Sigma2TooLongPubkey : public BadSigma2ParamsBase { static constexpr size_t kResponderEphPubKeyLen = 66; @@ -1135,14 +1157,13 @@ struct Sigma2TooShortPubkey : public BadSigma2ParamsBase static constexpr size_t kResponderEphPubKeyLen = 64; }; -struct Sigma2SessionIdMax : public Sigma2Params +struct Sigma2TooLongEncrypted2 : public BadSigma2ParamsBase { - static constexpr uint32_t kResponderSessionId = UINT16_MAX; + static constexpr size_t kEncrypted2Len = kMaxMsgR2SignedEncLen + 1; }; - -struct Sigma2SessionIdTooBig : public BadSigma2ParamsBase +struct Sigma2TooShortEncrypted2 : public BadSigma2ParamsBase { - static constexpr uint32_t kResponderSessionId = UINT16_MAX + 1; + static constexpr size_t kEncrypted2Len = CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; }; struct Sigma2FutureProofTlvElement : public Sigma2Params @@ -1169,10 +1190,12 @@ TEST_F(TestCASESession, Sigma2ParsingTest) TestSigma2Parsing(mem, bufferSize, Sigma2WrongTags); TestSigma2Parsing(mem, bufferSize, Sigma2TooLongRandom); TestSigma2Parsing(mem, bufferSize, Sigma2TooShortRandom); - TestSigma2Parsing(mem, bufferSize, Sigma2TooLongPubkey); - TestSigma2Parsing(mem, bufferSize, Sigma2TooShortPubkey); TestSigma2Parsing(mem, bufferSize, Sigma2SessionIdMax); TestSigma2Parsing(mem, bufferSize, Sigma2SessionIdTooBig); + TestSigma2Parsing(mem, bufferSize, Sigma2TooLongPubkey); + TestSigma2Parsing(mem, bufferSize, Sigma2TooShortPubkey); + TestSigma2Parsing(mem, bufferSize, Sigma2TooLongEncrypted2); + TestSigma2Parsing(mem, bufferSize, Sigma2TooShortEncrypted2); TestSigma2Parsing(mem, bufferSize, Sigma2FutureProofTlvElement); TestSigma2Parsing(mem, bufferSize, Sigma2FutureProofTlvElementNoStructEnd); } @@ -1509,8 +1532,8 @@ struct SessionResumptionTestStorage : SessionResumptionStorage { SessionResumptionTestStorage(CHIP_ERROR findMethodReturnCode, ScopedNodeId peerNodeId, ResumptionIdStorage * resumptionId, Crypto::P256ECDHDerivedSecret * sharedSecret) : - mFindMethodReturnCode(findMethodReturnCode), - mPeerNodeId(peerNodeId), mResumptionId(resumptionId), mSharedSecret(sharedSecret) + mFindMethodReturnCode(findMethodReturnCode), mPeerNodeId(peerNodeId), mResumptionId(resumptionId), + mSharedSecret(sharedSecret) {} SessionResumptionTestStorage(CHIP_ERROR findMethodReturnCode) : mFindMethodReturnCode(findMethodReturnCode) {} CHIP_ERROR FindByScopedNodeId(const ScopedNodeId & node, ResumptionIdStorage & resumptionId, @@ -1942,4 +1965,267 @@ TEST_F(TestCASESession, ParseSigma2TBEData) TestSigma2TBEParsing(mem, bufferSize, Sigma2TBEFutureProofTlvElementNoStructEnd); } +constexpr size_t kMaxMsgR3SignedEncLen = + TLV::EstimateStructOverhead(kMaxCHIPCertLength, // responderNOC + kMaxCHIPCertLength, // responderICAC + kMax_ECDSA_Signature_Length, // signature + kCaseOverheadForFutureTbeData // extra bytes for future-proofing + ); + +struct Sigma3Params +{ + // Purposefully not using constants like kSigmaParamRandomNumberSize that + // the code uses, so we have a cross-check. + + static constexpr size_t kEncrypted3Len = + CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES + 1; // Needs to be bigger than CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES + static constexpr uint16_t kFutureProofTlvElement = 77; + + static constexpr uint8_t kEncrypted3Tag = 1; + static constexpr uint8_t kFutureProofTlvElementTag = 7; + + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } + + static constexpr bool kIncludeFutureProofTlvElement = false; + static constexpr bool kIncludeStructEnd = true; + + static constexpr bool kExpectSuccess = true; +}; + +template +static CHIP_ERROR EncodeSigma3Helper(MutableByteSpan & buf) +{ + using namespace TLV; + + TLVWriter writer; + writer.Init(buf); + + TLVType containerType; + ReturnErrorOnFailure(writer.StartContainer(AnonymousTag(), kTLVType_Structure, containerType)); + + uint8_t encrypted3[Params::kEncrypted3Len] = { 3 }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kEncrypted3Tag), ByteSpan(encrypted3))); + + // Future-proofing: Ensure that TLV elements being added to the specification in the future are properly handled. + if constexpr (Params::kIncludeFutureProofTlvElement) + { + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kFutureProofTlvElementTag), Params::kFutureProofTlvElement)); + } + + if constexpr (Params::kIncludeStructEnd) + { + ReturnErrorOnFailure(writer.EndContainer(containerType)); + } + + buf.reduce_size(writer.GetLengthWritten()); + return CHIP_NO_ERROR; +} + +// A macro, so we can tell which test failed based on line number. +#define TestSigma3Parsing(mem, bufferSize, params) \ + do \ + { \ + MutableByteSpan buf(mem.Get(), bufferSize); \ + EXPECT_EQ(EncodeSigma3Helper(buf), CHIP_NO_ERROR); \ + \ + TLV::ContiguousBufferTLVReader reader; \ + reader.Init(buf); \ + Platform::ScopedMemoryBufferWithSize msgR3Encrypted; \ + \ + EXPECT_EQ(CASESessionAccess::ParseSigma3(reader, msgR3Encrypted) == CHIP_NO_ERROR, params::kExpectSuccess); \ + if (params::kExpectSuccess) \ + { \ + /* Add other verification tests here as desired */ \ + } \ + } while (0) + +struct BadSigma3ParamsBase : public Sigma3Params +{ + static constexpr bool kExpectSuccess = false; +}; + +struct Sigma3NoStructEnd : public BadSigma3ParamsBase +{ + static constexpr bool kIncludeStructEnd = false; +}; + +struct Sigma3WrongTags : public BadSigma3ParamsBase +{ + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ProfileTag(0, num); } +}; + +struct Sigma3TooLongEncrypted3 : public BadSigma3ParamsBase +{ + static constexpr size_t kEncrypted3Len = kMaxMsgR3SignedEncLen + 1; +}; +struct Sigma3TooShortEncrypted3 : public BadSigma3ParamsBase +{ + static constexpr size_t kEncrypted3Len = CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; +}; + +struct Sigma3FutureProofTlvElement : public Sigma3Params +{ + static constexpr bool kIncludeFutureProofTlvElement = true; +}; + +struct Sigma3FutureProofTlvElementNoStructEnd : public BadSigma3ParamsBase +{ + static constexpr bool kIncludeFutureProofTlvElement = true; + static constexpr bool kIncludeStructEnd = false; +}; + +// TODO: Consider making this test (and similar ones) to Value-Parameterized tests once pw_unit_test:light starts supporting them +TEST_F(TestCASESession, Sigma3ParsingTest) +{ + // 1280 bytes must be enough by definition. + constexpr size_t bufferSize = 1280; + chip::Platform::ScopedMemoryBuffer mem; + EXPECT_TRUE(mem.Calloc(bufferSize)); + + TestSigma3Parsing(mem, bufferSize, Sigma3Params); + TestSigma3Parsing(mem, bufferSize, Sigma3NoStructEnd); + TestSigma3Parsing(mem, bufferSize, Sigma3WrongTags); + TestSigma3Parsing(mem, bufferSize, Sigma3TooLongEncrypted3); + TestSigma3Parsing(mem, bufferSize, Sigma3TooShortEncrypted3); + TestSigma3Parsing(mem, bufferSize, Sigma3FutureProofTlvElement); + TestSigma3Parsing(mem, bufferSize, Sigma3FutureProofTlvElementNoStructEnd); +} + +struct Sigma3TBEDataParams +{ + // Purposefully not using constants like kSigmaParamRandomNumberSize that + // the code uses, so we have a cross-check. + static constexpr size_t kInitiatorNOCLen = 400; + static constexpr size_t kInitiatorICACLen = 400; + static constexpr size_t kSignatureLen = 64; + static constexpr uint16_t kFutureProofTlvElement = 77; + + static constexpr uint8_t kInitiatorNOCTag = 1; + static constexpr uint8_t kInitiatorICACTag = 2; + static constexpr uint8_t kSignatureTag = 3; + static constexpr uint8_t kFutureProofTlvElementTag = 11; + + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } + + static constexpr bool kIncludeFutureProofTlvElement = false; + static constexpr bool kIncludeStructEnd = true; + + static constexpr bool kExpectSuccess = true; +}; + +template +static CHIP_ERROR EncodeSigma3TBEDataHelper(MutableByteSpan & buf) +{ + using namespace TLV; + + TLVWriter writer; + writer.Init(buf); + + TLVType containerType; + ReturnErrorOnFailure(writer.StartContainer(AnonymousTag(), kTLVType_Structure, containerType)); + uint8_t initiatorNOC[Params::kInitiatorNOCLen] = { 1 }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kInitiatorNOCTag), ByteSpan(initiatorNOC))); + + uint8_t initiatorICAC[Params::kInitiatorICACLen] = { 13 }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kInitiatorICACTag), ByteSpan(initiatorICAC))); + + uint8_t signature[Params::kSignatureLen] = { 17 }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kSignatureTag), ByteSpan(signature))); + + // Future-proofing: Ensure that TLV elements being added to the specification in the future are properly handled. + if constexpr (Params::kIncludeFutureProofTlvElement) + { + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kFutureProofTlvElementTag), Params::kFutureProofTlvElement)); + } + + if constexpr (Params::kIncludeStructEnd) + { + ReturnErrorOnFailure(writer.EndContainer(containerType)); + } + + buf.reduce_size(writer.GetLengthWritten()); + return CHIP_NO_ERROR; +} + +// A macro, so we can tell which test failed based on line number. +#define TestSigma3TBEParsing(mem, bufferSize, params) \ + do \ + { \ + MutableByteSpan buf(mem.Get(), bufferSize); \ + EXPECT_EQ(EncodeSigma3TBEDataHelper(buf), CHIP_NO_ERROR); \ + \ + TLV::ContiguousBufferTLVReader reader; \ + reader.Init(buf); \ + CASESessionAccess::HandleSigma3Data handleSigma3Data; \ + \ + EXPECT_EQ(CASESessionAccess::ParseSigma3TBEData(reader, handleSigma3Data) == CHIP_NO_ERROR, params::kExpectSuccess); \ + if (params::kExpectSuccess) \ + { \ + /* Add other verification tests here as desired */ \ + } \ + } while (0) + +struct BadSigma3TBEParamsBase : public Sigma3TBEDataParams +{ + static constexpr bool kExpectSuccess = false; +}; + +struct Sigma3TBENoStructEnd : public BadSigma3TBEParamsBase +{ + static constexpr bool kIncludeStructEnd = false; +}; + +struct Sigma3TBEWrongTags : public BadSigma3TBEParamsBase +{ + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ProfileTag(0, num); } +}; + +struct Sigma3TBETooLongNOC : public BadSigma3TBEParamsBase +{ + static constexpr size_t kInitiatorNOCLen = 401; +}; + +struct Sigma3TBETooLongICAC : public BadSigma3TBEParamsBase +{ + static constexpr size_t kInitiatorICACLen = 401; +}; + +struct Sigma3TBETooLongSignature : public BadSigma3TBEParamsBase +{ + static constexpr size_t kSignatureLen = 65; +}; +struct Sigma3TBETooShortSignature : public BadSigma3TBEParamsBase +{ + static constexpr size_t kSignatureLen = 63; +}; + +struct Sigma3TBEFutureProofTlvElement : public Sigma3TBEDataParams +{ + static constexpr bool kIncludeFutureProofTlvElement = true; +}; + +struct Sigma3TBEFutureProofTlvElementNoStructEnd : public BadSigma3TBEParamsBase +{ + static constexpr bool kIncludeFutureProofTlvElement = true; + static constexpr bool kIncludeStructEnd = false; +}; + +TEST_F(TestCASESession, ParseSigma3TBEData) +{ + // 1280 bytes must be enough by definition. + constexpr size_t bufferSize = 1280; + chip::Platform::ScopedMemoryBuffer mem; + EXPECT_TRUE(mem.Calloc(bufferSize)); + + TestSigma3TBEParsing(mem, bufferSize, Sigma3TBEDataParams); + TestSigma3TBEParsing(mem, bufferSize, Sigma3TBENoStructEnd); + TestSigma3TBEParsing(mem, bufferSize, Sigma3TBEWrongTags); + TestSigma3TBEParsing(mem, bufferSize, Sigma3TBETooLongNOC); + TestSigma3TBEParsing(mem, bufferSize, Sigma3TBETooLongICAC); + TestSigma3TBEParsing(mem, bufferSize, Sigma3TBETooLongSignature); + TestSigma3TBEParsing(mem, bufferSize, Sigma3TBETooShortSignature); + TestSigma3TBEParsing(mem, bufferSize, Sigma3TBEFutureProofTlvElement); + TestSigma3TBEParsing(mem, bufferSize, Sigma3TBEFutureProofTlvElementNoStructEnd); +} + } // namespace chip From 6723bd3989314bc580fd27e211020aa69fda6b56 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Sun, 12 Jan 2025 16:06:36 +0100 Subject: [PATCH 11/15] Code Cleanup --- src/protocols/secure_channel/CASESession.cpp | 179 ++++++++---------- src/protocols/secure_channel/CASESession.h | 53 ++++-- .../secure_channel/tests/TestCASESession.cpp | 40 ++-- 3 files changed, 126 insertions(+), 146 deletions(-) diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index 196c19312092c2..55dc4224589086 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -349,29 +349,6 @@ class CASESession::WorkHelper DATA mData; }; -struct CASESession::SendSigma3Data -{ - FabricIndex fabricIndex; - - // Use one or the other - const FabricTable * fabricTable; - const Crypto::OperationalKeystore * keystore; - - chip::Platform::ScopedMemoryBuffer msg_R3_Signed; - size_t msg_r3_signed_len; - - chip::Platform::ScopedMemoryBuffer msg_R3_Encrypted; - size_t msg_r3_encrypted_len; - - chip::Platform::ScopedMemoryBuffer icacBuf; - MutableByteSpan icaCert; - - chip::Platform::ScopedMemoryBuffer nocBuf; - MutableByteSpan nocCert; - - P256ECDSASignature tbsData3Signature; -}; - CASESession::~CASESession() { // Let's clear out any security state stored in the object, before destroying it. @@ -1243,10 +1220,10 @@ CHIP_ERROR CASESession::PrepareSigma2(EncodeSigma2Inputs & outSigma2Data) ReturnErrorOnFailure(DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR2Info), sr2k)); // Construct Sigma2 TBS Data - size_t msgR2SignedLen = EstimateStructOverhead(kMaxCHIPCertLength, // responderNOC + size_t msgR2SignedLen = EstimateStructOverhead(kMaxCHIPCertLength, // responderNoc kMaxCHIPCertLength, // responderICAC kP256_PublicKey_Length, // responderEphPubKey - kP256_PublicKey_Length // initiatorEphPubKey + kP256_PublicKey_Length // InitiatorEphPubKey ); P256ECDSASignature tbsData2Signature; @@ -1262,7 +1239,7 @@ CHIP_ERROR CASESession::PrepareSigma2(EncodeSigma2Inputs & outSigma2Data) mFabricsTable->SignWithOpKeypair(mFabricIndex, ByteSpan{ msgR2Signed.Get(), msgR2SignedLen }, tbsData2Signature)); } // Construct Sigma2 TBE Data - size_t msgR2SignedEncLen = EstimateStructOverhead(nocCert.size(), // responderNOC + size_t msgR2SignedEncLen = EstimateStructOverhead(nocCert.size(), // responderNoc icaCert.size(), // responderICAC tbsData2Signature.Length(), // signature SessionResumptionStorage::kResumptionIdSize // resumptionID @@ -1381,18 +1358,14 @@ CHIP_ERROR CASESession::HandleSigma2Resume(System::PacketBufferHandle && msg) { MATTER_TRACE_SCOPE("HandleSigma2Resume", "CASESession"); CHIP_ERROR err = CHIP_NO_ERROR; - System::PacketBufferTLVReader tlvReader; ChipLogDetail(SecureChannel, "Received Sigma2Resume msg"); MATTER_TRACE_COUNTER("Sigma2Resume"); MATTER_LOG_METRIC_END(kMetricDeviceCASESessionSigma1, err); - // uint8_t sigma2ResumeMIC[CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES]; - - ParsedSigma2Resume parsedSigma2Resume; - + System::PacketBufferTLVReader tlvReader; tlvReader.Init(std::move(msg)); - + ParsedSigma2Resume parsedSigma2Resume; SuccessOrExit(err = ParseSigma2Resume(tlvReader, parsedSigma2Resume)); if (parsedSigma2Resume.responderSessionParamStructPresent) @@ -1406,7 +1379,7 @@ CHIP_ERROR CASESession::HandleSigma2Resume(System::PacketBufferHandle && msg) parsedSigma2Resume.resumptionId, ByteSpan(kKDFS2RKeyInfo), ByteSpan(kResume2MIC_Nonce))); - ChipLogDetail(SecureChannel, "Peer assigned session key ID %d", parsedSigma2Resume.responderSessionId); + ChipLogDetail(SecureChannel, "Peer assigned session ID %d", parsedSigma2Resume.responderSessionId); SetPeerSessionId(parsedSigma2Resume.responderSessionId); if (mSessionResumptionStorage != nullptr) @@ -1457,14 +1430,16 @@ CHIP_ERROR CASESession::ParseSigma2Resume(ContiguousBufferTLVReader & tlvReader, { ReturnErrorOnFailure(DecodeSessionParametersIfPresent(AsTlvContextTag(Sigma2ResumeTags::kResponderSessionParams), tlvReader, outParsedSigma2Resume.responderSessionParams)); + outParsedSigma2Resume.responderSessionParamStructPresent = true; + err = tlvReader.Next(); } // Future-proofing: CHIP_NO_ERROR will be returned by Next() if we have additional non-parsed TLV Elements, which could - // happen in the future if the spec changes and we have additional elements. + // happen in the future if additional elements are added to the specification. VerifyOrReturnError(err == CHIP_END_OF_TLV || err == CHIP_NO_ERROR, err); - // Exit Container will fail (return CHIP_END_OF_TLV) if the received encoded message does not call EndContainer in the end of - // the TLV message. + // Exit Container will fail (return CHIP_END_OF_TLV) if the received encoded message is not properly terminated with an + // EndOfContainer TLV Element. ReturnErrorOnFailure(tlvReader.ExitContainer(containerType)); return CHIP_NO_ERROR; @@ -1515,10 +1490,9 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) System::PacketBufferTLVReader tlvReader; tlvReader.Init(std::move(msg)); ParsedSigma2 parsedSigma2; - ReturnErrorOnFailure(ParseSigma2(tlvReader, parsedSigma2)); - ChipLogDetail(SecureChannel, "Peer assigned session key ID %d", parsedSigma2.responderSessionId); + ChipLogDetail(SecureChannel, "Peer assigned session ID %d", parsedSigma2.responderSessionId); SetPeerSessionId(parsedSigma2.responderSessionId); // ParseSigma2 ensures that: @@ -1545,11 +1519,8 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters( GetRemoteSessionParameters()); } - /********************************************************************************************************************************** - */ - size_t msgR2EncryptedLen = 0; - msgR2EncryptedLen = parsedSigma2.msgR2Encrypted.AllocatedSize() - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; + size_t msgR2EncryptedLen = parsedSigma2.msgR2Encrypted.AllocatedSize() - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; ReturnErrorOnFailure(AES_CCM_decrypt(parsedSigma2.msgR2Encrypted.Get(), msgR2EncryptedLen, nullptr, 0, parsedSigma2.msgR2Encrypted.Get() + msgR2EncryptedLen, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, @@ -1559,7 +1530,6 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) ContiguousBufferTLVReader decryptedDataTlvReader; decryptedDataTlvReader.Init(parsedSigma2.msgR2Encrypted.Get(), msgR2EncryptedLen); ParsedSigma2TBEData parsedSigma2TBEData; - ReturnErrorOnFailure(ParseSigma2TBEData(decryptedDataTlvReader, parsedSigma2TBEData)); std::copy(parsedSigma2TBEData.resumptionId.begin(), parsedSigma2TBEData.resumptionId.end(), mNewResumptionId.begin()); @@ -1583,11 +1553,10 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) } // Construct msgR2Signed and validate the signature in msgR2Encrypted. - size_t msgR2SignedLen; - msgR2SignedLen = EstimateStructOverhead(parsedSigma2TBEData.responderNOC.size(), // resonderNOC - parsedSigma2TBEData.responderICAC.size(), // responderICAC - kP256_PublicKey_Length, // responderEphPubKey - kP256_PublicKey_Length // initiatorEphPubKey + size_t msgR2SignedLen = EstimateStructOverhead(parsedSigma2TBEData.responderNOC.size(), // resonderNOC + parsedSigma2TBEData.responderICAC.size(), // responderICAC + kP256_PublicKey_Length, // responderEphPubKey + kP256_PublicKey_Length // initiatorEphPubKey ); chip::Platform::ScopedMemoryBuffer msgR2Signed; @@ -1632,32 +1601,24 @@ CHIP_ERROR CASESession::ParseSigma2(ContiguousBufferTLVReader & tlvReader, Parse ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2Tags::kEncrypted2))); constexpr size_t kCaseOverheadForFutureTbeData = 128; - size_t maxMsgR2SignedEncLen; - maxMsgR2SignedEncLen = EstimateStructOverhead(kMaxCHIPCertLength, // responderNOC - kMaxCHIPCertLength, // responderICAC - kMax_ECDSA_Signature_Length, // signature - SessionResumptionStorage::kResumptionIdSize, // resumptionID - kCaseOverheadForFutureTbeData // extra bytes for future-proofing + size_t maxMsgR2SignedEncLen = EstimateStructOverhead(kMaxCHIPCertLength, // responderNOC + kMaxCHIPCertLength, // responderICAC + kMax_ECDSA_Signature_Length, // signature + SessionResumptionStorage::kResumptionIdSize, // resumptionID + kCaseOverheadForFutureTbeData // extra bytes for future-proofing ); - size_t msgR2EncryptedLenWithTag = 0; - msgR2EncryptedLenWithTag = tlvReader.GetLength(); + size_t msgR2EncryptedLenWithTag = tlvReader.GetLength(); // Validate we did not receive a buffer larger than legal - // TODO why are comparing this to the "signed one" - // Maybe it is signed encrypted, but is the estimation calculated well? document it VerifyOrReturnError(msgR2EncryptedLenWithTag <= maxMsgR2SignedEncLen, CHIP_ERROR_INVALID_TLV_ELEMENT); VerifyOrReturnError(msgR2EncryptedLenWithTag > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, CHIP_ERROR_INVALID_TLV_ELEMENT); - // Allocate after making sure that msgR2EncryptedLenWithTag is within bounds VerifyOrReturnError(outParsedSigma2.msgR2Encrypted.Alloc(msgR2EncryptedLenWithTag), CHIP_ERROR_NO_MEMORY); - // TODO, should I keep this as GetBytes? or should I use GetByteView for consistency and do something else? ReturnErrorOnFailure(tlvReader.GetBytes(outParsedSigma2.msgR2Encrypted.Get(), outParsedSigma2.msgR2Encrypted.AllocatedSize())); // Retrieve responderSessionParams if present - // TODO the below check is dangerous, it would allow us to ignore all other errors and go into DecodeMRPParameters even if we - // dont have CHIP_NO_ERROR CHIP_ERROR err = tlvReader.Next(); if (err == CHIP_NO_ERROR && tlvReader.GetTag() == AsTlvContextTag(Sigma2Tags::kResponderSessionParams)) { @@ -1667,10 +1628,13 @@ CHIP_ERROR CASESession::ParseSigma2(ContiguousBufferTLVReader & tlvReader, Parse err = tlvReader.Next(); } - // Future-proofing: CHIP_NO_ERROR will be returned by Next() if we have additional non-parsed TLV Elements received, which could - // happen in the future if the spec changes and we have additional elements. + + // Future-proofing: CHIP_NO_ERROR will be returned by Next() if we have additional non-parsed TLV Elements, which could + // happen in the future if additional elements are added to the specification. VerifyOrReturnError(err == CHIP_END_OF_TLV || err == CHIP_NO_ERROR, err); - // Exit Container will fail if the received encoded message does not call EndContainer in the end of the TLV message. + + // Exit Container will fail (return CHIP_END_OF_TLV) if the received encoded message is not properly terminated with an + // EndOfContainer TLV Element. ReturnErrorOnFailure(tlvReader.ExitContainer(containerType)); return CHIP_NO_ERROR; @@ -1698,6 +1662,7 @@ CHIP_ERROR CASESession::ParseSigma2TBEData(ContiguousBufferTLVReader & decrypted } VerifyOrReturnError(decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSignature), CHIP_ERROR_INVALID_TLV_TAG); + // TODO verify if the below modification in the check is correct (also for Sigma 3) // tbsData2Signature's length should equal kMax_ECDSA_Signature_Length as per the Specification VerifyOrReturnError(outParsedSigma2TBE.tbsData2Signature.Capacity() == decryptedDataTlvReader.GetLength(), CHIP_ERROR_INVALID_TLV_ELEMENT); @@ -1711,7 +1676,8 @@ CHIP_ERROR CASESession::ParseSigma2TBEData(ContiguousBufferTLVReader & decrypted VerifyOrReturnError(outParsedSigma2TBE.resumptionId.size() == SessionResumptionStorage::kResumptionIdSize, CHIP_ERROR_INVALID_CASE_PARAMETER); - // Exit Container will fail if the received encoded message did not add EndOfContainer at the end of the TLV message. + // Exit Container will fail (return CHIP_END_OF_TLV) if the received encoded message is not properly terminated with an + // EndOfContainer TLV Element. ReturnErrorOnFailure(decryptedDataTlvReader.ExitContainer(containerType)); return CHIP_NO_ERROR; @@ -1762,7 +1728,7 @@ CHIP_ERROR CASESession::SendSigma3a() // Prepare Sigma3 TBS Data Blob data.msg_r3_signed_len = - TLV::EstimateStructOverhead(data.icaCert.size(), data.nocCert.size(), kP256_PublicKey_Length, kP256_PublicKey_Length); + EstimateStructOverhead(data.icaCert.size(), data.nocCert.size(), kP256_PublicKey_Length, kP256_PublicKey_Length); VerifyOrReturnError(data.msg_R3_Signed.Alloc(data.msg_r3_signed_len), CHIP_ERROR_NO_MEMORY); @@ -1810,11 +1776,11 @@ CHIP_ERROR CASESession::SendSigma3b(SendSigma3Data & data, bool & cancel) CHIP_ERROR_NO_MEMORY); { - TLV::TLVWriter tlvWriter; - TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified; + TLVWriter tlvWriter; + TLVType outerContainerType = kTLVType_NotSpecified; tlvWriter.Init(data.msg_R3_Encrypted.Get(), data.msg_r3_encrypted_len); - ReturnErrorOnFailure(tlvWriter.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainerType)); + ReturnErrorOnFailure(tlvWriter.StartContainer(AnonymousTag(), kTLVType_Structure, outerContainerType)); ReturnErrorOnFailure(tlvWriter.Put(AsTlvContextTag(TBEDataTags::kSenderNOC), data.nocCert)); if (!data.icaCert.empty()) { @@ -1928,8 +1894,8 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) MATTER_TRACE_SCOPE("HandleSigma3", "CASESession"); CHIP_ERROR err = CHIP_NO_ERROR; System::PacketBufferTLVReader tlvReader; - TLV::ContiguousBufferTLVReader decryptedDataTlvReader; - TLV::TLVType containerType = TLV::kTLVType_Structure; + ContiguousBufferTLVReader decryptedDataTlvReader; + TLVType containerType = kTLVType_Structure; const uint8_t * buf = msg->Start(); const size_t bufLen = msg->DataLength(); @@ -1980,18 +1946,17 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) sr3k.KeyHandle(), kTBEData3_Nonce, kTBEDataNonceLength, msgR3Encrypted.Get())); decryptedDataTlvReader.Init(msgR3Encrypted.Get(), msgR3EncryptedLen); - SuccessOrExit(err = ParseSigma3TBEData(decryptedDataTlvReader, data)); // Step 3 - Construct Sigma3 TBS Data - data.msg_r3_signed_len = TLV::EstimateStructOverhead(data.initiatorNOC.size(), data.initiatorICAC.size(), - kP256_PublicKey_Length, kP256_PublicKey_Length); + data.msgR3SignedLen = TLV::EstimateStructOverhead(data.initiatorNOC.size(), data.initiatorICAC.size(), + kP256_PublicKey_Length, kP256_PublicKey_Length); - VerifyOrExit(data.msg_R3_Signed.Alloc(data.msg_r3_signed_len), err = CHIP_ERROR_NO_MEMORY); + VerifyOrExit(data.msgR3Signed.Alloc(data.msgR3SignedLen), err = CHIP_ERROR_NO_MEMORY); SuccessOrExit(err = ConstructTBSData(data.initiatorNOC, data.initiatorICAC, ByteSpan(mRemotePubKey, mRemotePubKey.Length()), ByteSpan(mEphemeralKey->Pubkey(), mEphemeralKey->Pubkey().Length()), - data.msg_R3_Signed.Get(), data.msg_r3_signed_len)); + data.msgR3Signed.Get(), data.msgR3SignedLen)); // Prepare for Step 4/5 { @@ -2006,22 +1971,24 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) { data.validContext = mValidContext; - // initiatorNOC and initiatorICAC are spans into msg_R3_Encrypted + // initiatorNOC and initiatorICAC are spans into msgR3Encrypted // which is going away, so to save memory, redirect them to their // copies in msg_R3_signed, which is staying around - TLV::TLVReader signedDataTlvReader; - signedDataTlvReader.Init(data.msg_R3_Signed.Get(), data.msg_r3_signed_len); - SuccessOrExit(err = signedDataTlvReader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())); + TLV::ContiguousBufferTLVReader signedDataTlvReader; + signedDataTlvReader.Init(data.msgR3Signed.Get(), data.msgR3SignedLen); + SuccessOrExit(err = signedDataTlvReader.Next(containerType, AnonymousTag())); SuccessOrExit(err = signedDataTlvReader.EnterContainer(containerType)); - SuccessOrExit(err = signedDataTlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(TBSDataTags::kSenderNOC))); - SuccessOrExit(err = signedDataTlvReader.Get(data.initiatorNOC)); + SuccessOrExit(err = signedDataTlvReader.Next(AsTlvContextTag(TBSDataTags::kSenderNOC))); + SuccessOrExit(err = signedDataTlvReader.GetByteView(data.initiatorNOC)); if (!data.initiatorICAC.empty()) { - SuccessOrExit(err = signedDataTlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(TBSDataTags::kSenderICAC))); - SuccessOrExit(err = signedDataTlvReader.Get(data.initiatorICAC)); + SuccessOrExit(err = signedDataTlvReader.Next(AsTlvContextTag(TBSDataTags::kSenderICAC))); + SuccessOrExit(err = signedDataTlvReader.GetByteView(data.initiatorICAC)); } + + SuccessOrExit(err = signedDataTlvReader.ExitContainer(containerType)); } SuccessOrExit(err = helper->ScheduleWork()); @@ -2048,54 +2015,56 @@ CHIP_ERROR CASESession::ParseSigma3(ContiguousBufferTLVReader & tlvReader, ReturnErrorOnFailure(tlvReader.EnterContainer(containerType)); // Fetch encrypted data - size_t maxMsgR3SignedEncLen; + ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma3Tags::kEncrypted3))); + constexpr size_t kCaseOverheadForFutureTbeData = 128; - maxMsgR3SignedEncLen = EstimateStructOverhead(Credentials::kMaxCHIPCertLength, // initiatorNOC - Credentials::kMaxCHIPCertLength, // initiatorICAC - kMax_ECDSA_Signature_Length, // signature - kCaseOverheadForFutureTbeData // extra bytes for future-proofing + size_t maxMsgR3SignedEncLen = EstimateStructOverhead(kMaxCHIPCertLength, // initiatorNOC + kMaxCHIPCertLength, // initiatorICAC + kMax_ECDSA_Signature_Length, // signature + kCaseOverheadForFutureTbeData // extra bytes for future-proofing ); - ReturnErrorOnFailure(tlvReader.Next(kTLVType_ByteString, AsTlvContextTag(Sigma3Tags::kEncrypted3))); - - size_t msgR3EncryptedLenWithTag = 0; - msgR3EncryptedLenWithTag = tlvReader.GetLength(); + size_t msgR3EncryptedLenWithTag = tlvReader.GetLength(); // Validate we did not receive a buffer larger than legal VerifyOrReturnError(msgR3EncryptedLenWithTag <= maxMsgR3SignedEncLen, CHIP_ERROR_INVALID_TLV_ELEMENT); VerifyOrReturnError(msgR3EncryptedLenWithTag > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, CHIP_ERROR_INVALID_TLV_ELEMENT); - VerifyOrReturnError(msgR3Encrypted.Alloc(msgR3EncryptedLenWithTag), CHIP_ERROR_NO_MEMORY); - ReturnErrorOnFailure(tlvReader.GetBytes(msgR3Encrypted.Get(), static_cast(msgR3EncryptedLenWithTag))); + + ReturnErrorOnFailure(tlvReader.GetBytes(msgR3Encrypted.Get(), msgR3Encrypted.AllocatedSize())); ReturnErrorOnFailure(tlvReader.ExitContainer(containerType)); return CHIP_NO_ERROR; } -CHIP_ERROR CASESession::ParseSigma3TBEData(ContiguousBufferTLVReader & decryptedDataTlvReader, HandleSigma3Data & data) +CHIP_ERROR CASESession::ParseSigma3TBEData(ContiguousBufferTLVReader & decryptedDataTlvReader, + HandleSigma3Data & outHandleSigma3TBEData) { TLVType containerType = kTLVType_Structure; ReturnErrorOnFailure(decryptedDataTlvReader.Next(containerType, TLV::AnonymousTag())); ReturnErrorOnFailure(decryptedDataTlvReader.EnterContainer(containerType)); - ReturnErrorOnFailure(decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSenderNOC))); - ReturnErrorOnFailure(decryptedDataTlvReader.Get(data.initiatorNOC)); + ReturnErrorOnFailure(decryptedDataTlvReader.Next(AsTlvContextTag(TBEDataTags::kSenderNOC))); + ReturnErrorOnFailure(decryptedDataTlvReader.GetByteView(outHandleSigma3TBEData.initiatorNOC)); + VerifyOrReturnError(outHandleSigma3TBEData.initiatorNOC.size() <= kMaxCHIPCertLength, CHIP_ERROR_INVALID_CASE_PARAMETER); ReturnErrorOnFailure(decryptedDataTlvReader.Next()); if (decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSenderICAC)) { - VerifyOrReturnError(decryptedDataTlvReader.GetType() == TLV::kTLVType_ByteString, CHIP_ERROR_WRONG_TLV_TYPE); - ReturnErrorOnFailure(decryptedDataTlvReader.Get(data.initiatorICAC)); + ReturnErrorOnFailure(decryptedDataTlvReader.GetByteView(outHandleSigma3TBEData.initiatorICAC)); + VerifyOrReturnError(outHandleSigma3TBEData.initiatorICAC.size() <= kMaxCHIPCertLength, CHIP_ERROR_INVALID_CASE_PARAMETER); ReturnErrorOnFailure(decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSignature))); } VerifyOrReturnError(decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSignature), CHIP_ERROR_INVALID_TLV_TAG); - VerifyOrReturnError(data.tbsData3Signature.Capacity() >= decryptedDataTlvReader.GetLength(), CHIP_ERROR_INVALID_TLV_ELEMENT); - data.tbsData3Signature.SetLength(decryptedDataTlvReader.GetLength()); - ReturnErrorOnFailure(decryptedDataTlvReader.GetBytes(data.tbsData3Signature.Bytes(), data.tbsData3Signature.Length())); + VerifyOrReturnError(outHandleSigma3TBEData.tbsData3Signature.Capacity() == decryptedDataTlvReader.GetLength(), + CHIP_ERROR_INVALID_TLV_ELEMENT); + outHandleSigma3TBEData.tbsData3Signature.SetLength(decryptedDataTlvReader.GetLength()); + ReturnErrorOnFailure(decryptedDataTlvReader.GetBytes(outHandleSigma3TBEData.tbsData3Signature.Bytes(), + outHandleSigma3TBEData.tbsData3Signature.Length())); ReturnErrorOnFailure(decryptedDataTlvReader.ExitContainer(containerType)); @@ -2121,7 +2090,7 @@ CHIP_ERROR CASESession::HandleSigma3b(HandleSigma3Data & data, bool & cancel) // The same change should be made in Sigma2 processing. // Step 7 - Validate Signature ReturnErrorOnFailure( - initiatorPublicKey.ECDSA_validate_msg_signature(data.msg_R3_Signed.Get(), data.msg_r3_signed_len, data.tbsData3Signature)); + initiatorPublicKey.ECDSA_validate_msg_signature(data.msgR3Signed.Get(), data.msgR3SignedLen, data.tbsData3Signature)); return CHIP_NO_ERROR; } diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h index 4266e72b1f1537..f514d0ca8f554b 100644 --- a/src/protocols/secure_channel/CASESession.h +++ b/src/protocols/secure_channel/CASESession.h @@ -237,26 +237,12 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, size_t encrypted2Length = 0; const ReliableMessageProtocolConfig * responderMrpConfig; }; - - struct EncodeSigma2ResumeInputs - { - ByteSpan resumptionId; - uint8_t sigma2ResumeMICBuffer[Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES]; - MutableByteSpan sigma2ResumeMIC{ sigma2ResumeMICBuffer }; - uint16_t responderSessionId; - const ReliableMessageProtocolConfig * responderMrpConfig; - }; - struct ParsedSigma2 { - // TODO responderRandom is ByteSpan in ParsedSigma2 but is an array in EncodeSigma2Inputs ByteSpan responderRandom; uint16_t responderSessionId; - // TODO, If I will inherit, remember that this different from EncodeSigma2Inputs ByteSpan responderEphPubKey; - Platform::ScopedMemoryBufferWithSize msgR2Encrypted; - size_t encrypted2Length = 0; bool responderSessionParamStructPresent = false; SessionParameters responderSessionParams; }; @@ -269,21 +255,52 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, ByteSpan resumptionId; }; + struct EncodeSigma2ResumeInputs + { + ByteSpan resumptionId; + uint8_t sigma2ResumeMICBuffer[Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES]; + MutableByteSpan sigma2ResumeMIC{ sigma2ResumeMICBuffer }; + uint16_t responderSessionId; + const ReliableMessageProtocolConfig * responderMrpConfig; + }; + struct ParsedSigma2Resume { ByteSpan resumptionId; ByteSpan sigma2ResumeMIC; uint16_t responderSessionId; SessionParameters responderSessionParams; - // TODO consider removing this? bool responderSessionParamStructPresent = false; }; - struct HandleSigma3Data + struct SendSigma3Data { + FabricIndex fabricIndex; + + // Use one or the other + const FabricTable * fabricTable; + const Crypto::OperationalKeystore * keystore; + chip::Platform::ScopedMemoryBuffer msg_R3_Signed; size_t msg_r3_signed_len; + chip::Platform::ScopedMemoryBuffer msg_R3_Encrypted; + size_t msg_r3_encrypted_len; + + chip::Platform::ScopedMemoryBuffer icacBuf; + MutableByteSpan icaCert; + + chip::Platform::ScopedMemoryBuffer nocBuf; + MutableByteSpan nocCert; + + Crypto::P256ECDSASignature tbsData3Signature; + }; + + struct HandleSigma3Data + { + chip::Platform::ScopedMemoryBuffer msgR3Signed; + size_t msgR3SignedLen; + ByteSpan initiatorNOC; ByteSpan initiatorICAC; @@ -366,7 +383,7 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, static CHIP_ERROR ParseSigma3(TLV::ContiguousBufferTLVReader & tlvReader, Platform::ScopedMemoryBufferWithSize & msgR3Encrypted); - CHIP_ERROR ParseSigma3TBEData(TLV::ContiguousBufferTLVReader & tlvReader, HandleSigma3Data & data); + static CHIP_ERROR ParseSigma3TBEData(TLV::ContiguousBufferTLVReader & tlvReader, HandleSigma3Data & data); private: friend class TestCASESession; @@ -406,12 +423,10 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, CHIP_ERROR HandleSigma2(System::PacketBufferHandle && msg); CHIP_ERROR HandleSigma2Resume(System::PacketBufferHandle && msg); - struct SendSigma3Data; CHIP_ERROR SendSigma3a(); static CHIP_ERROR SendSigma3b(SendSigma3Data & data, bool & cancel); CHIP_ERROR SendSigma3c(SendSigma3Data & data, CHIP_ERROR status); - // struct HandleSigma3Data; CHIP_ERROR HandleSigma3a(System::PacketBufferHandle && msg); static CHIP_ERROR HandleSigma3b(HandleSigma3Data & data, bool & cancel); CHIP_ERROR HandleSigma3c(HandleSigma3Data & data, CHIP_ERROR status); diff --git a/src/protocols/secure_channel/tests/TestCASESession.cpp b/src/protocols/secure_channel/tests/TestCASESession.cpp index 9e0795fc52686c..1acaaf422f8c16 100644 --- a/src/protocols/secure_channel/tests/TestCASESession.cpp +++ b/src/protocols/secure_channel/tests/TestCASESession.cpp @@ -1039,16 +1039,14 @@ struct Sigma2Params static constexpr uint16_t kResponderSessionId = 0; static constexpr size_t kResponderEphPubKeyLen = 65; static constexpr size_t kEncrypted2Len = - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES + 1; // Needs to be bigger than CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES - static constexpr size_t kResponderSessionParamsLen = 0; // Nonzero means include it. - static constexpr uint16_t kFutureProofTlvElement = 77; + CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES + 1; // Needs to be bigger than CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES + static constexpr uint16_t kFutureProofTlvElement = 77; - static constexpr uint8_t kResponderRandomTag = 1; - static constexpr uint8_t kResponderSessionIdTag = 2; - static constexpr uint8_t kResponderEphPubKeyTag = 3; - static constexpr uint8_t kEncrypted2Tag = 4; - static constexpr uint8_t kResponderSessionParamsTag = 5; // TODO SessionParams are not tested in non of ParseSigmaTests - static constexpr uint8_t kFutureProofTlvElementTag = 11; + static constexpr uint8_t kResponderRandomTag = 1; + static constexpr uint8_t kResponderSessionIdTag = 2; + static constexpr uint8_t kResponderEphPubKeyTag = 3; + static constexpr uint8_t kEncrypted2Tag = 4; + static constexpr uint8_t kFutureProofTlvElementTag = 11; static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } @@ -1318,17 +1316,15 @@ struct Sigma2ResumeParams { // Purposefully not using constants like kSigmaParamRandomNumberSize that // the code uses, so we have a cross-check. - static constexpr size_t kResumptionIdLen = 16; - static constexpr size_t kSigma2ResumeMICLen = 16; - static constexpr uint16_t kResponderSessionId = 0; - static constexpr size_t kResponderSessionParamsLen = 0; // Nonzero means include it. TODO should I remove this - static constexpr uint16_t kFutureProofTlvElement = 77; - - static constexpr uint8_t kResumptionIdTag = 1; - static constexpr uint8_t kSigma2ResumeMICTag = 2; - static constexpr uint8_t kResponderSessionIdTag = 3; - static constexpr uint8_t kResponderSessionParamsTag = 4; // TODO SessionParams are not tested in non of ParseSigmaTests - static constexpr uint8_t kFutureProofTlvElementTag = 11; + static constexpr size_t kResumptionIdLen = 16; + static constexpr size_t kSigma2ResumeMICLen = 16; + static constexpr uint16_t kResponderSessionId = 0; + static constexpr uint16_t kFutureProofTlvElement = 77; + + static constexpr uint8_t kResumptionIdTag = 1; + static constexpr uint8_t kSigma2ResumeMICTag = 2; + static constexpr uint8_t kResponderSessionIdTag = 3; + static constexpr uint8_t kFutureProofTlvElementTag = 11; static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } @@ -1532,8 +1528,8 @@ struct SessionResumptionTestStorage : SessionResumptionStorage { SessionResumptionTestStorage(CHIP_ERROR findMethodReturnCode, ScopedNodeId peerNodeId, ResumptionIdStorage * resumptionId, Crypto::P256ECDHDerivedSecret * sharedSecret) : - mFindMethodReturnCode(findMethodReturnCode), mPeerNodeId(peerNodeId), mResumptionId(resumptionId), - mSharedSecret(sharedSecret) + mFindMethodReturnCode(findMethodReturnCode), + mPeerNodeId(peerNodeId), mResumptionId(resumptionId), mSharedSecret(sharedSecret) {} SessionResumptionTestStorage(CHIP_ERROR findMethodReturnCode) : mFindMethodReturnCode(findMethodReturnCode) {} CHIP_ERROR FindByScopedNodeId(const ScopedNodeId & node, ResumptionIdStorage & resumptionId, From f7b81663385314c8f7f422e2069a9f832ec0dc9f Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Tue, 14 Jan 2025 19:40:53 +0100 Subject: [PATCH 12/15] Integrating Comments and Adding comments --- src/protocols/secure_channel/CASESession.cpp | 87 +++++++------- src/protocols/secure_channel/CASESession.h | 90 ++++++++++++-- .../secure_channel/tests/TestCASESession.cpp | 111 +++++++++++++----- 3 files changed, 209 insertions(+), 79 deletions(-) diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index 55dc4224589086..aa5de2498f488a 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -110,6 +110,8 @@ constexpr chip::TLV::Tag AsTlvContextTag(Enum e) return chip::TLV::ContextTag(chip::to_underlying(e)); } +constexpr size_t kCaseOverheadForFutureTbeData = 128; + } // namespace namespace chip { @@ -1520,15 +1522,12 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) GetRemoteSessionParameters()); } - size_t msgR2EncryptedLen = parsedSigma2.msgR2Encrypted.AllocatedSize() - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; - - ReturnErrorOnFailure(AES_CCM_decrypt(parsedSigma2.msgR2Encrypted.Get(), msgR2EncryptedLen, nullptr, 0, - parsedSigma2.msgR2Encrypted.Get() + msgR2EncryptedLen, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, - sr2k.KeyHandle(), kTBEData2_Nonce, kTBEDataNonceLength, - parsedSigma2.msgR2Encrypted.Get())); + ReturnErrorOnFailure(AES_CCM_decrypt(parsedSigma2.msgR2EncryptedPayload.data(), parsedSigma2.msgR2EncryptedPayload.size(), + nullptr, 0, parsedSigma2.msgR2Mic.data(), parsedSigma2.msgR2Mic.size(), sr2k.KeyHandle(), + kTBEData2_Nonce, kTBEDataNonceLength, parsedSigma2.msgR2EncryptedPayload.data())); ContiguousBufferTLVReader decryptedDataTlvReader; - decryptedDataTlvReader.Init(parsedSigma2.msgR2Encrypted.Get(), msgR2EncryptedLen); + decryptedDataTlvReader.Init(parsedSigma2.msgR2EncryptedPayload.data(), parsedSigma2.msgR2EncryptedPayload.size()); ParsedSigma2TBEData parsedSigma2TBEData; ReturnErrorOnFailure(ParseSigma2TBEData(decryptedDataTlvReader, parsedSigma2TBEData)); @@ -1600,8 +1599,6 @@ CHIP_ERROR CASESession::ParseSigma2(ContiguousBufferTLVReader & tlvReader, Parse // Generate decrypted data ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2Tags::kEncrypted2))); - constexpr size_t kCaseOverheadForFutureTbeData = 128; - size_t maxMsgR2SignedEncLen = EstimateStructOverhead(kMaxCHIPCertLength, // responderNOC kMaxCHIPCertLength, // responderICAC kMax_ECDSA_Signature_Length, // signature @@ -1615,9 +1612,13 @@ CHIP_ERROR CASESession::ParseSigma2(ContiguousBufferTLVReader & tlvReader, Parse VerifyOrReturnError(msgR2EncryptedLenWithTag <= maxMsgR2SignedEncLen, CHIP_ERROR_INVALID_TLV_ELEMENT); VerifyOrReturnError(msgR2EncryptedLenWithTag > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, CHIP_ERROR_INVALID_TLV_ELEMENT); VerifyOrReturnError(outParsedSigma2.msgR2Encrypted.Alloc(msgR2EncryptedLenWithTag), CHIP_ERROR_NO_MEMORY); - ReturnErrorOnFailure(tlvReader.GetBytes(outParsedSigma2.msgR2Encrypted.Get(), outParsedSigma2.msgR2Encrypted.AllocatedSize())); + size_t msgR2EncryptedPayloadLen = msgR2EncryptedLenWithTag - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; + outParsedSigma2.msgR2EncryptedPayload = MutableByteSpan(outParsedSigma2.msgR2Encrypted.Get(), msgR2EncryptedPayloadLen); + outParsedSigma2.msgR2Mic = + ByteSpan(outParsedSigma2.msgR2Encrypted.Get() + msgR2EncryptedPayloadLen, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES); + // Retrieve responderSessionParams if present CHIP_ERROR err = tlvReader.Next(); if (err == CHIP_NO_ERROR && tlvReader.GetTag() == AsTlvContextTag(Sigma2Tags::kResponderSessionParams)) @@ -1662,11 +1663,10 @@ CHIP_ERROR CASESession::ParseSigma2TBEData(ContiguousBufferTLVReader & decrypted } VerifyOrReturnError(decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSignature), CHIP_ERROR_INVALID_TLV_TAG); - // TODO verify if the below modification in the check is correct (also for Sigma 3) // tbsData2Signature's length should equal kMax_ECDSA_Signature_Length as per the Specification - VerifyOrReturnError(outParsedSigma2TBE.tbsData2Signature.Capacity() == decryptedDataTlvReader.GetLength(), - CHIP_ERROR_INVALID_TLV_ELEMENT); - outParsedSigma2TBE.tbsData2Signature.SetLength(decryptedDataTlvReader.GetLength()); + size_t signatureLen = decryptedDataTlvReader.GetLength(); + VerifyOrReturnError(outParsedSigma2TBE.tbsData2Signature.Capacity() == signatureLen, CHIP_ERROR_INVALID_TLV_ELEMENT); + outParsedSigma2TBE.tbsData2Signature.SetLength(signatureLen); ReturnErrorOnFailure(decryptedDataTlvReader.GetBytes(outParsedSigma2TBE.tbsData2Signature.Bytes(), outParsedSigma2TBE.tbsData2Signature.Length())); @@ -1893,16 +1893,12 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) { MATTER_TRACE_SCOPE("HandleSigma3", "CASESession"); CHIP_ERROR err = CHIP_NO_ERROR; - System::PacketBufferTLVReader tlvReader; ContiguousBufferTLVReader decryptedDataTlvReader; TLVType containerType = kTLVType_Structure; const uint8_t * buf = msg->Start(); const size_t bufLen = msg->DataLength(); - Platform::ScopedMemoryBufferWithSize msgR3Encrypted; - size_t msgR3EncryptedLen = 0; - AutoReleaseSessionKey sr3k(*mSessionManager->GetSessionKeystore()); uint8_t msg_salt[kIPKSize + kSHA256_Hash_Length]; @@ -1925,32 +1921,39 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) VerifyOrExit(mEphemeralKey != nullptr, err = CHIP_ERROR_INTERNAL); - tlvReader.Init(std::move(msg)); - - SuccessOrExit(err = ParseSigma3(tlvReader, msgR3Encrypted)); - // Step 1 + // msgR3Encrypted will be allocated and initialised within ParseSigma3() + Platform::ScopedMemoryBufferWithSize msgR3Encrypted; + // both msgR3EncryptedPayload and msgR3Mic will become backed by msgR3Encrypted in ParseSigma3() + MutableByteSpan msgR3EncryptedPayload; + ByteSpan msgR3Mic; { + System::PacketBufferTLVReader tlvReader; + tlvReader.Init(std::move(msg)); + SuccessOrExit(err = ParseSigma3(tlvReader, msgR3Encrypted, msgR3EncryptedPayload, msgR3Mic)); + + // Generate the S3K key MutableByteSpan saltSpan(msg_salt); SuccessOrExit(err = ConstructSaltSigma3(ByteSpan(mIPK), saltSpan)); SuccessOrExit(err = DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR3Info), sr3k)); - } - - SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ buf, bufLen })); + // Add Sigma3 to the TranscriptHash which will be used to generate the Session Encryption Keys + SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ buf, bufLen })); + } // Step 2 - Decrypt data blob - msgR3EncryptedLen = msgR3Encrypted.AllocatedSize() - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; - - SuccessOrExit(err = AES_CCM_decrypt(msgR3Encrypted.Get(), msgR3EncryptedLen, nullptr, 0, - msgR3Encrypted.Get() + msgR3EncryptedLen, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, - sr3k.KeyHandle(), kTBEData3_Nonce, kTBEDataNonceLength, msgR3Encrypted.Get())); + SuccessOrExit(err = AES_CCM_decrypt(msgR3EncryptedPayload.data(), msgR3EncryptedPayload.size(), nullptr, 0, msgR3Mic.data(), + msgR3Mic.size(), sr3k.KeyHandle(), kTBEData3_Nonce, kTBEDataNonceLength, + msgR3EncryptedPayload.data())); - decryptedDataTlvReader.Init(msgR3Encrypted.Get(), msgR3EncryptedLen); + decryptedDataTlvReader.Init(msgR3EncryptedPayload.data(), msgR3EncryptedPayload.size()); SuccessOrExit(err = ParseSigma3TBEData(decryptedDataTlvReader, data)); // Step 3 - Construct Sigma3 TBS Data - data.msgR3SignedLen = TLV::EstimateStructOverhead(data.initiatorNOC.size(), data.initiatorICAC.size(), - kP256_PublicKey_Length, kP256_PublicKey_Length); + data.msgR3SignedLen = TLV::EstimateStructOverhead(data.initiatorNOC.size(), // initiatorNOC + data.initiatorICAC.size(), // initiatorICAC + kP256_PublicKey_Length, // initiatorEphPubKey + kP256_PublicKey_Length // responderEphPubKey + ); VerifyOrExit(data.msgR3Signed.Alloc(data.msgR3SignedLen), err = CHIP_ERROR_NO_MEMORY); @@ -2007,7 +2010,8 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) } CHIP_ERROR CASESession::ParseSigma3(ContiguousBufferTLVReader & tlvReader, - Platform::ScopedMemoryBufferWithSize & msgR3Encrypted) + Platform::ScopedMemoryBufferWithSize & outMsgR3Encrypted, + MutableByteSpan & outMsgR3EncryptedPayload, ByteSpan & outMsgR3Mic) { TLVType containerType = kTLVType_Structure; @@ -2017,8 +2021,6 @@ CHIP_ERROR CASESession::ParseSigma3(ContiguousBufferTLVReader & tlvReader, // Fetch encrypted data ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma3Tags::kEncrypted3))); - constexpr size_t kCaseOverheadForFutureTbeData = 128; - size_t maxMsgR3SignedEncLen = EstimateStructOverhead(kMaxCHIPCertLength, // initiatorNOC kMaxCHIPCertLength, // initiatorICAC kMax_ECDSA_Signature_Length, // signature @@ -2030,9 +2032,12 @@ CHIP_ERROR CASESession::ParseSigma3(ContiguousBufferTLVReader & tlvReader, // Validate we did not receive a buffer larger than legal VerifyOrReturnError(msgR3EncryptedLenWithTag <= maxMsgR3SignedEncLen, CHIP_ERROR_INVALID_TLV_ELEMENT); VerifyOrReturnError(msgR3EncryptedLenWithTag > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, CHIP_ERROR_INVALID_TLV_ELEMENT); - VerifyOrReturnError(msgR3Encrypted.Alloc(msgR3EncryptedLenWithTag), CHIP_ERROR_NO_MEMORY); + VerifyOrReturnError(outMsgR3Encrypted.Alloc(msgR3EncryptedLenWithTag), CHIP_ERROR_NO_MEMORY); + ReturnErrorOnFailure(tlvReader.GetBytes(outMsgR3Encrypted.Get(), outMsgR3Encrypted.AllocatedSize())); - ReturnErrorOnFailure(tlvReader.GetBytes(msgR3Encrypted.Get(), msgR3Encrypted.AllocatedSize())); + size_t msgR3EncryptedPayloadLen = msgR3EncryptedLenWithTag - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; + outMsgR3EncryptedPayload = MutableByteSpan(outMsgR3Encrypted.Get(), msgR3EncryptedPayloadLen); + outMsgR3Mic = ByteSpan(outMsgR3Encrypted.Get() + msgR3EncryptedPayloadLen, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES); ReturnErrorOnFailure(tlvReader.ExitContainer(containerType)); @@ -2060,9 +2065,9 @@ CHIP_ERROR CASESession::ParseSigma3TBEData(ContiguousBufferTLVReader & decrypted } VerifyOrReturnError(decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSignature), CHIP_ERROR_INVALID_TLV_TAG); - VerifyOrReturnError(outHandleSigma3TBEData.tbsData3Signature.Capacity() == decryptedDataTlvReader.GetLength(), - CHIP_ERROR_INVALID_TLV_ELEMENT); - outHandleSigma3TBEData.tbsData3Signature.SetLength(decryptedDataTlvReader.GetLength()); + size_t signatureLen = decryptedDataTlvReader.GetLength(); + VerifyOrReturnError(outHandleSigma3TBEData.tbsData3Signature.Capacity() == signatureLen, CHIP_ERROR_INVALID_TLV_ELEMENT); + outHandleSigma3TBEData.tbsData3Signature.SetLength(signatureLen); ReturnErrorOnFailure(decryptedDataTlvReader.GetBytes(outHandleSigma3TBEData.tbsData3Signature.Bytes(), outHandleSigma3TBEData.tbsData3Signature.Length())); diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h index f514d0ca8f554b..ed300dbd03a02c 100644 --- a/src/protocols/secure_channel/CASESession.h +++ b/src/protocols/secure_channel/CASESession.h @@ -221,6 +221,9 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, struct ParsedSigma1 : Sigma1Param { + // Backed by: Sigma1 PacketBuffer passed to the method HandleSigma1() + // Lifetime: Valid for the lifetime of the tlvReader, which takes ownership of the Sigma1 PacketBuffer in the HandleSigma1() + // method. ByteSpan initiatorEphPubKey; bool initiatorSessionParamStructPresent = false; SessionParameters initiatorSessionParams; @@ -239,20 +242,30 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, }; struct ParsedSigma2 { + // Below ByteSpans are Backed by: Sigma2 PacketBuffer passed to the method HandleSigma2() + // Lifetime: Valid for the lifetime of the tlvReader, which takes ownership of the Sigma2 PacketBuffer in the HandleSigma2() + // method. ByteSpan responderRandom; - uint16_t responderSessionId; ByteSpan responderEphPubKey; + Platform::ScopedMemoryBufferWithSize msgR2Encrypted; - bool responderSessionParamStructPresent = false; + // Below ByteSpans are Backed by: msgR2Encrypted buffer + // Lifetime: Valid as long as msgR2Encrypted is not released + MutableByteSpan msgR2EncryptedPayload; + ByteSpan msgR2Mic; SessionParameters responderSessionParams; + uint16_t responderSessionId; + bool responderSessionParamStructPresent = false; }; struct ParsedSigma2TBEData { + // Below ByteSpans are Backed by: msgR2Encrypted Buffer, member of ParsedSigma2 struct + // Lifetime: Valid for the lifetime of the instance of ParsedSigma2 that contains the msgR2Encrypted Buffer. ByteSpan responderNOC; ByteSpan responderICAC; - Crypto::P256ECDSASignature tbsData2Signature; ByteSpan resumptionId; + Crypto::P256ECDSASignature tbsData2Signature; }; struct EncodeSigma2ResumeInputs @@ -266,10 +279,13 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, struct ParsedSigma2Resume { + // Below ByteSpans are Backed by: Sigma2Resume PacketBuffer passed to the method HandleSigma2Resume() + // Lifetime: Valid for the lifetime of the tlvReader, which takes ownership of the Sigma2Resume PacketBuffer in the + // HandleSigma2Resume() method. ByteSpan resumptionId; ByteSpan sigma2ResumeMIC; - uint16_t responderSessionId; SessionParameters responderSessionParams; + uint16_t responderSessionId; bool responderSessionParamStructPresent = false; }; @@ -301,6 +317,8 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, chip::Platform::ScopedMemoryBuffer msgR3Signed; size_t msgR3SignedLen; + // Below ByteSpans are Backed by: msgR3Encrypted Buffer, local to the HandleSigma3a() method, + // The Spans are later modified to point to the msgR3Signed member of this struct. ByteSpan initiatorNOC; ByteSpan initiatorICAC; @@ -348,10 +366,40 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, */ static CHIP_ERROR ParseSigma1(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma1 & parsedMessage); - static CHIP_ERROR ParseSigma2(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma2 & parsedMessage); + /** + * Parse a Sigma2 message. This function will return success only if the + * message passes schema checks. + * + * @param tlvReader a reference to the TLVReader that has ownership of the Sigma2 PacketBuffer. + * @param outParsedSigma2 a reference to ParsedSigma2. All members of parsedMessage will stay valid as long as tlvReader is + * valid. + * + * @note Calls to this function must always be made with a newly created and fresh ParsedSigma2 parameter. + **/ + static CHIP_ERROR ParseSigma2(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma2 & outParsedSigma2); - static CHIP_ERROR ParseSigma2TBEData(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma2TBEData & parsedMessage); + /** + * Parse a decrypted TBEData2Encrypted message. This function will return success only if the message passes schema checks. + * + * @param tlvReader a reference to the TLVReader that points to the decrypted TBEData2Encrypted buffer (i.e. + * msgR2Encrypted member of ParsedSigma2 struct) + * @param outParsedSigma2TBEData a reference to ParsedSigma2TBEData. All members of parsedMessage will stay valid as long + * as the msgR2Encrypted member of ParsedSigma2 is valid + * + * @note Calls to this function must always be made with a newly created and fresh ParsedSigma2TBEData parameter. + **/ + static CHIP_ERROR ParseSigma2TBEData(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma2TBEData & outParsedSigma2TBEData); + /** + * Parse a Sigma2Resume message. This function will return success only if the + * message passes schema checks. + * + * @param tlvReader a reference to the TLVReader that has ownership of the Sigma2Resume PacketBuffer. + * @param outParsedSigma2Resume a reference to ParsedSigma2Resume. All members of parsedMessage will stay valid as long + * as tlvReader is valid. + * + * @note Calls to this function must always be made with a newly created and fresh ParsedSigma2Resume parameter. + **/ static CHIP_ERROR ParseSigma2Resume(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma2Resume & outParsedSigma2Resume); /** @@ -380,9 +428,37 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, **/ static CHIP_ERROR EncodeSigma2Resume(System::PacketBufferHandle & outMsg, EncodeSigma2ResumeInputs & inParam); + /** + * Parse a Sigma3 message. This function will return success only if the + * message passes schema checks. + * + * @param tlvReader a reference to the TLVReader that has ownership of the Sigma3 PacketBuffer. + * + * @param outMsgR3Encrypted The encrypted3 (TBEData3Encrypted) TLV element. This will be a buffer that is owned by the caller + * but is allocated and initialised within ParseSigma3. Calls to this function must always be made with + * a newly created and fresh outMsgR3Encrypted + * + * @param outMsgR3EncryptedPayload reference to a span that will be set to point to the payload of outMsgR3Encrypted within + * ParseSigma3. Calls to this function must always be made with a newly created and fresh + * outMsgR3Mic + * + * @param outMsgR3Mic reference to a span that will be set to point to the MIC of outMsgR3Encrypted within ParseSigma3. + * Calls to this function must always be made with a newly created and fresh outMsgR3Mic + * + * @note all out parameters will be valid as long the Buffer outMsgR3Encrypted is valid. + **/ static CHIP_ERROR ParseSigma3(TLV::ContiguousBufferTLVReader & tlvReader, - Platform::ScopedMemoryBufferWithSize & msgR3Encrypted); + Platform::ScopedMemoryBufferWithSize & outMsgR3Encrypted, + MutableByteSpan & outMsgR3EncryptedPayload, ByteSpan & outMsgR3Mic); + /** + * Parse a decrypted TBEData3Encrypted message. This function will return success only if the + * message passes schema checks. + * + * @param tlvReader a reference to the TLVReader that points to the decrypted TBEData3Encrypted buffer. + * @param data a reference to HandleSigma3Data. + * + **/ static CHIP_ERROR ParseSigma3TBEData(TLV::ContiguousBufferTLVReader & tlvReader, HandleSigma3Data & data); private: diff --git a/src/protocols/secure_channel/tests/TestCASESession.cpp b/src/protocols/secure_channel/tests/TestCASESession.cpp index 1acaaf422f8c16..d88f3dda6174d6 100644 --- a/src/protocols/secure_channel/tests/TestCASESession.cpp +++ b/src/protocols/secure_channel/tests/TestCASESession.cpp @@ -776,7 +776,7 @@ static CHIP_ERROR EncodeSigma1Helper(MutableByteSpan & buf) #define TestSigma1Parsing(mem, bufferSize, params) \ do \ { \ - MutableByteSpan buf(mem.Get(), bufferSize); \ + MutableByteSpan buf((mem).Get(), (bufferSize)); \ EXPECT_EQ(EncodeSigma1Helper(buf), CHIP_NO_ERROR); \ \ TLV::ContiguousBufferTLVReader reader; \ @@ -1048,6 +1048,10 @@ struct Sigma2Params static constexpr uint8_t kEncrypted2Tag = 4; static constexpr uint8_t kFutureProofTlvElementTag = 11; + static constexpr uint8_t kTestValueResponderRandom = 1; + static constexpr uint8_t kTestValueResponderEphPubKey = 2; + static constexpr uint8_t kTestValueEncrypted2 = 3; + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } static constexpr bool kIncludeFutureProofTlvElement = false; @@ -1067,17 +1071,17 @@ static CHIP_ERROR EncodeSigma2Helper(MutableByteSpan & buf) TLVType containerType; ReturnErrorOnFailure(writer.StartContainer(AnonymousTag(), kTLVType_Structure, containerType)); - uint8_t responderRandom[Params::kResponderRandomLen] = { 1 }; + + uint8_t responderRandom[Params::kResponderRandomLen] = { Params::kTestValueResponderRandom }; ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResponderRandomTag), ByteSpan(responderRandom))); ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResponderSessionIdTag), Params::kResponderSessionId)); - uint8_t responderEphPubKey[Params::kResponderEphPubKeyLen] = { 2 }; + uint8_t responderEphPubKey[Params::kResponderEphPubKeyLen] = { Params::kTestValueResponderEphPubKey }; ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResponderEphPubKeyTag), ByteSpan(responderEphPubKey))); - uint8_t encrypted2[Params::kEncrypted2Len] = { 3 }; + uint8_t encrypted2[Params::kEncrypted2Len] = { Params::kTestValueEncrypted2 }; ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kEncrypted2Tag), ByteSpan(encrypted2))); - // Future-proofing: Ensure that TLV elements being added to the specification in the future are properly handled. if constexpr (Params::kIncludeFutureProofTlvElement) { @@ -1097,7 +1101,7 @@ static CHIP_ERROR EncodeSigma2Helper(MutableByteSpan & buf) #define TestSigma2Parsing(mem, bufferSize, params) \ do \ { \ - MutableByteSpan buf(mem.Get(), bufferSize); \ + MutableByteSpan buf((mem).Get(), (bufferSize)); \ EXPECT_EQ(EncodeSigma2Helper(buf), CHIP_NO_ERROR); \ \ TLV::ContiguousBufferTLVReader reader; \ @@ -1107,7 +1111,16 @@ static CHIP_ERROR EncodeSigma2Helper(MutableByteSpan & buf) EXPECT_EQ(CASESessionAccess::ParseSigma2(reader, parsedSigma2) == CHIP_NO_ERROR, params::kExpectSuccess); \ if (params::kExpectSuccess) \ { \ - /* Add other verification tests here as desired */ \ + \ + uint8_t expectedRandom[params::kResponderRandomLen] = { params::kTestValueResponderRandom }; \ + uint8_t expectedEphKey[params::kResponderEphPubKeyLen] = { params::kTestValueResponderEphPubKey }; \ + uint8_t expectedEncrypted2[params::kEncrypted2Len] = { params::kTestValueEncrypted2 }; \ + \ + EXPECT_TRUE(parsedSigma2.responderRandom.data_equal(ByteSpan(expectedRandom))); \ + EXPECT_EQ(parsedSigma2.responderSessionId, params::kResponderSessionId); \ + EXPECT_TRUE(parsedSigma2.responderEphPubKey.data_equal(ByteSpan(expectedEphKey))); \ + EXPECT_TRUE(ByteSpan(parsedSigma2.msgR2Encrypted.Get(), parsedSigma2.msgR2Encrypted.AllocatedSize()) \ + .data_equal(ByteSpan(expectedEncrypted2))); \ } \ } while (0) @@ -1175,7 +1188,6 @@ struct Sigma2FutureProofTlvElementNoStructEnd : public BadSigma2ParamsBase static constexpr bool kIncludeStructEnd = false; }; -// TODO: Consider making this test (and similar ones) to Value-Parameterized tests once pw_unit_test:light starts supporting them TEST_F(TestCASESession, Sigma2ParsingTest) { // 1280 bytes must be enough by definition. @@ -1326,6 +1338,9 @@ struct Sigma2ResumeParams static constexpr uint8_t kResponderSessionIdTag = 3; static constexpr uint8_t kFutureProofTlvElementTag = 11; + static constexpr uint8_t kTestValueResumptionId = 1; + static constexpr uint8_t kTestValueSigma2ResumeMIC = 2; + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } static constexpr bool kIncludeFutureProofTlvElement = false; @@ -1346,10 +1361,10 @@ static CHIP_ERROR EncodeSigma2ResumeHelper(MutableByteSpan & buf) TLVType containerType; ReturnErrorOnFailure(writer.StartContainer(AnonymousTag(), kTLVType_Structure, containerType)); - uint8_t resumptionID[Params::kResumptionIdLen] = { 1 }; - ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResumptionIdTag), ByteSpan(resumptionID))); + uint8_t resumptionId[Params::kResumptionIdLen] = { Params::kTestValueResumptionId }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResumptionIdTag), ByteSpan(resumptionId))); - uint8_t sigma2ResumeMIC[Params::kSigma2ResumeMICLen] = { 2 }; + uint8_t sigma2ResumeMIC[Params::kSigma2ResumeMICLen] = { Params::kTestValueSigma2ResumeMIC }; ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kSigma2ResumeMICTag), ByteSpan(sigma2ResumeMIC))); ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResponderSessionIdTag), Params::kResponderSessionId)); @@ -1369,11 +1384,10 @@ static CHIP_ERROR EncodeSigma2ResumeHelper(MutableByteSpan & buf) return CHIP_NO_ERROR; } -// A macro, so we can tell which test failed based on line number. #define TestSigma2ResumeParsing(mem, bufferSize, params) \ do \ { \ - MutableByteSpan buf(mem.Get(), bufferSize); \ + MutableByteSpan buf((mem).Get(), (bufferSize)); \ EXPECT_EQ(EncodeSigma2ResumeHelper(buf), CHIP_NO_ERROR); \ \ TLV::ContiguousBufferTLVReader reader; \ @@ -1383,7 +1397,12 @@ static CHIP_ERROR EncodeSigma2ResumeHelper(MutableByteSpan & buf) EXPECT_EQ(CASESessionAccess::ParseSigma2Resume(reader, parsedSigma2Resume) == CHIP_NO_ERROR, params::kExpectSuccess); \ if (params::kExpectSuccess) \ { \ - /* Add other verification tests here as desired */ \ + uint8_t expectedResumptionId[params::kResumptionIdLen] = { params::kTestValueResumptionId }; \ + uint8_t expectedSigma2ResumeMIC[params::kSigma2ResumeMICLen] = { params::kTestValueSigma2ResumeMIC }; \ + \ + EXPECT_TRUE(parsedSigma2Resume.resumptionId.data_equal(ByteSpan(expectedResumptionId))); \ + EXPECT_TRUE(parsedSigma2Resume.sigma2ResumeMIC.data_equal(ByteSpan(expectedSigma2ResumeMIC))); \ + EXPECT_EQ(parsedSigma2Resume.responderSessionId, params::kResponderSessionId); \ } \ } while (0) @@ -1822,6 +1841,11 @@ struct Sigma2TBEDataParams static constexpr uint8_t kResumptionIdTag = 4; static constexpr uint8_t kFutureProofTlvElementTag = 11; + static constexpr uint8_t kTestValueResponderNOC = 1; + static constexpr uint8_t kTestValueResponderICAC = 2; + static constexpr uint8_t kTestValueSignature = 3; + static constexpr uint8_t kTestValueResumptionId = 4; + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } static constexpr bool kIncludeFutureProofTlvElement = false; @@ -1841,16 +1865,16 @@ static CHIP_ERROR EncodeSigma2TBEDataHelper(MutableByteSpan & buf) TLVType containerType; ReturnErrorOnFailure(writer.StartContainer(AnonymousTag(), kTLVType_Structure, containerType)); - uint8_t responderNOC[Params::kResponderNOCLen] = { 1 }; + uint8_t responderNOC[Params::kResponderNOCLen] = { Params::kTestValueResponderNOC }; ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResponderNOCTag), ByteSpan(responderNOC))); - uint8_t responderICAC[Params::kResponderICACLen] = { 13 }; + uint8_t responderICAC[Params::kResponderICACLen] = { Params::kTestValueResponderICAC }; ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResponderICACTag), ByteSpan(responderICAC))); - uint8_t signature[Params::kSignatureLen] = { 17 }; + uint8_t signature[Params::kSignatureLen] = { Params::kTestValueSignature }; ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kSignatureTag), ByteSpan(signature))); - uint8_t resumptionId[Params::kResumptionIdLen] = { 22 }; + uint8_t resumptionId[Params::kResumptionIdLen] = { Params::kTestValueResumptionId }; ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResumptionIdTag), ByteSpan(resumptionId))); // Future-proofing: Ensure that TLV elements being added to the specification in the future are properly handled. @@ -1872,7 +1896,7 @@ static CHIP_ERROR EncodeSigma2TBEDataHelper(MutableByteSpan & buf) #define TestSigma2TBEParsing(mem, bufferSize, params) \ do \ { \ - MutableByteSpan buf(mem.Get(), bufferSize); \ + MutableByteSpan buf((mem).Get(), (bufferSize)); \ EXPECT_EQ(EncodeSigma2TBEDataHelper(buf), CHIP_NO_ERROR); \ \ TLV::ContiguousBufferTLVReader reader; \ @@ -1882,7 +1906,16 @@ static CHIP_ERROR EncodeSigma2TBEDataHelper(MutableByteSpan & buf) EXPECT_EQ(CASESessionAccess::ParseSigma2TBEData(reader, parsedSigma2TBEData) == CHIP_NO_ERROR, params::kExpectSuccess); \ if (params::kExpectSuccess) \ { \ - /* Add other verification tests here as desired */ \ + uint8_t expectedNOC[params::kResponderNOCLen] = { params::kTestValueResponderNOC }; \ + uint8_t expectedICAC[params::kResponderICACLen] = { params::kTestValueResponderICAC }; \ + uint8_t expectedSignature[params::kSignatureLen] = { params::kTestValueSignature }; \ + uint8_t expectedResumptionId[params::kResumptionIdLen] = { params::kTestValueResumptionId }; \ + \ + EXPECT_TRUE(parsedSigma2TBEData.responderNOC.data_equal(ByteSpan(expectedNOC))); \ + EXPECT_TRUE(parsedSigma2TBEData.responderICAC.data_equal(ByteSpan(expectedICAC))); \ + EXPECT_TRUE(ByteSpan(parsedSigma2TBEData.tbsData2Signature.Bytes(), parsedSigma2TBEData.tbsData2Signature.Length()) \ + .data_equal(ByteSpan(expectedSignature))); \ + EXPECT_TRUE(parsedSigma2TBEData.resumptionId.data_equal(ByteSpan(expectedResumptionId))); \ } \ } while (0) @@ -1980,6 +2013,8 @@ struct Sigma3Params static constexpr uint8_t kEncrypted3Tag = 1; static constexpr uint8_t kFutureProofTlvElementTag = 7; + static constexpr uint8_t kTestValueEncrypted3 = { 1 }; + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } static constexpr bool kIncludeFutureProofTlvElement = false; @@ -1999,7 +2034,7 @@ static CHIP_ERROR EncodeSigma3Helper(MutableByteSpan & buf) TLVType containerType; ReturnErrorOnFailure(writer.StartContainer(AnonymousTag(), kTLVType_Structure, containerType)); - uint8_t encrypted3[Params::kEncrypted3Len] = { 3 }; + uint8_t encrypted3[Params::kEncrypted3Len] = { Params::kTestValueEncrypted3 }; ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kEncrypted3Tag), ByteSpan(encrypted3))); // Future-proofing: Ensure that TLV elements being added to the specification in the future are properly handled. @@ -2021,17 +2056,21 @@ static CHIP_ERROR EncodeSigma3Helper(MutableByteSpan & buf) #define TestSigma3Parsing(mem, bufferSize, params) \ do \ { \ - MutableByteSpan buf(mem.Get(), bufferSize); \ + MutableByteSpan buf((mem).Get(), (bufferSize)); \ EXPECT_EQ(EncodeSigma3Helper(buf), CHIP_NO_ERROR); \ \ TLV::ContiguousBufferTLVReader reader; \ reader.Init(buf); \ Platform::ScopedMemoryBufferWithSize msgR3Encrypted; \ + MutableByteSpan msgR3EncryptedPayload; \ + ByteSpan msgR3Mic; \ \ - EXPECT_EQ(CASESessionAccess::ParseSigma3(reader, msgR3Encrypted) == CHIP_NO_ERROR, params::kExpectSuccess); \ + EXPECT_EQ(CASESessionAccess::ParseSigma3(reader, msgR3Encrypted, msgR3EncryptedPayload, msgR3Mic) == CHIP_NO_ERROR, \ + params::kExpectSuccess); \ if (params::kExpectSuccess) \ { \ - /* Add other verification tests here as desired */ \ + uint8_t expectedEncrypted3[params::kEncrypted3Len] = { params::kTestValueEncrypted3 }; \ + EXPECT_TRUE(ByteSpan(msgR3Encrypted.Get(), msgR3Encrypted.AllocatedSize()).data_equal(ByteSpan(expectedEncrypted3))); \ } \ } while (0) @@ -2070,7 +2109,6 @@ struct Sigma3FutureProofTlvElementNoStructEnd : public BadSigma3ParamsBase static constexpr bool kIncludeStructEnd = false; }; -// TODO: Consider making this test (and similar ones) to Value-Parameterized tests once pw_unit_test:light starts supporting them TEST_F(TestCASESession, Sigma3ParsingTest) { // 1280 bytes must be enough by definition. @@ -2101,6 +2139,10 @@ struct Sigma3TBEDataParams static constexpr uint8_t kSignatureTag = 3; static constexpr uint8_t kFutureProofTlvElementTag = 11; + static constexpr uint8_t kTestValueInitiatorNOC = 1; + static constexpr uint8_t kTestValueInitiatorICAC = 2; + static constexpr uint8_t kTestValueSignature = 3; + static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } static constexpr bool kIncludeFutureProofTlvElement = false; @@ -2119,13 +2161,14 @@ static CHIP_ERROR EncodeSigma3TBEDataHelper(MutableByteSpan & buf) TLVType containerType; ReturnErrorOnFailure(writer.StartContainer(AnonymousTag(), kTLVType_Structure, containerType)); - uint8_t initiatorNOC[Params::kInitiatorNOCLen] = { 1 }; + + uint8_t initiatorNOC[Params::kInitiatorNOCLen] = { Params::kTestValueInitiatorNOC }; ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kInitiatorNOCTag), ByteSpan(initiatorNOC))); - uint8_t initiatorICAC[Params::kInitiatorICACLen] = { 13 }; + uint8_t initiatorICAC[Params::kInitiatorICACLen] = { Params::kTestValueInitiatorICAC }; ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kInitiatorICACTag), ByteSpan(initiatorICAC))); - uint8_t signature[Params::kSignatureLen] = { 17 }; + uint8_t signature[Params::kSignatureLen] = { Params::kTestValueSignature }; ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kSignatureTag), ByteSpan(signature))); // Future-proofing: Ensure that TLV elements being added to the specification in the future are properly handled. @@ -2143,11 +2186,10 @@ static CHIP_ERROR EncodeSigma3TBEDataHelper(MutableByteSpan & buf) return CHIP_NO_ERROR; } -// A macro, so we can tell which test failed based on line number. #define TestSigma3TBEParsing(mem, bufferSize, params) \ do \ { \ - MutableByteSpan buf(mem.Get(), bufferSize); \ + MutableByteSpan buf((mem).Get(), (bufferSize)); \ EXPECT_EQ(EncodeSigma3TBEDataHelper(buf), CHIP_NO_ERROR); \ \ TLV::ContiguousBufferTLVReader reader; \ @@ -2157,7 +2199,14 @@ static CHIP_ERROR EncodeSigma3TBEDataHelper(MutableByteSpan & buf) EXPECT_EQ(CASESessionAccess::ParseSigma3TBEData(reader, handleSigma3Data) == CHIP_NO_ERROR, params::kExpectSuccess); \ if (params::kExpectSuccess) \ { \ - /* Add other verification tests here as desired */ \ + uint8_t expectedNOC[params::kInitiatorNOCLen] = { params::kTestValueInitiatorNOC }; \ + uint8_t expectedICAC[params::kInitiatorICACLen] = { params::kTestValueInitiatorICAC }; \ + uint8_t expectedSignature[params::kSignatureLen] = { params::kTestValueSignature }; \ + \ + EXPECT_TRUE(handleSigma3Data.initiatorNOC.data_equal(ByteSpan(expectedNOC))); \ + EXPECT_TRUE(handleSigma3Data.initiatorICAC.data_equal(ByteSpan(expectedICAC))); \ + EXPECT_TRUE(ByteSpan(handleSigma3Data.tbsData3Signature.Bytes(), handleSigma3Data.tbsData3Signature.Length()) \ + .data_equal(ByteSpan(expectedSignature))); \ } \ } while (0) From 1849c1ec69df7bdd9b331d6e2fb3b5341c12f37e Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Thu, 23 Jan 2025 12:35:56 +0100 Subject: [PATCH 13/15] Increase Coverage of TestSigma1Parsing --- .../secure_channel/tests/TestCASESession.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/protocols/secure_channel/tests/TestCASESession.cpp b/src/protocols/secure_channel/tests/TestCASESession.cpp index d88f3dda6174d6..d62c0a3d463eb3 100644 --- a/src/protocols/secure_channel/tests/TestCASESession.cpp +++ b/src/protocols/secure_channel/tests/TestCASESession.cpp @@ -849,7 +849,7 @@ struct Sigma1TooLongResumptionId : public Sigma1WithResumption static constexpr bool kExpectSuccess = false; }; -struct Sigma1TooShortResumptionId : public BadSigma1ParamsBase +struct Sigma1TooShortResumptionId : public Sigma1WithResumption { static constexpr size_t kResumptionIdLen = 15; static constexpr bool kExpectSuccess = false; @@ -867,6 +867,18 @@ struct Sigma1TooShortResumeMIC : public Sigma1WithResumption static constexpr bool kExpectSuccess = false; }; +struct Sigma1WithResumptionIdNoResumeMIC : public Sigma1WithResumption +{ + static constexpr size_t kInitiatorResumeMICLen = 0; + static constexpr bool kExpectSuccess = false; +}; + +struct Sigma1WithResumeMICNoResumptionId : public Sigma1WithResumption +{ + static constexpr size_t kResumptionIdLen = 0; + static constexpr bool kExpectSuccess = false; +}; + struct Sigma1SessionIdMax : public Sigma1Params { static constexpr uint32_t kInitiatorSessionId = UINT16_MAX; @@ -909,6 +921,8 @@ TEST_F(TestCASESession, Sigma1ParsingTest) TestSigma1Parsing(mem, bufferSize, Sigma1TooShortResumptionId); TestSigma1Parsing(mem, bufferSize, Sigma1TooLongResumeMIC); TestSigma1Parsing(mem, bufferSize, Sigma1TooShortResumeMIC); + TestSigma1Parsing(mem, bufferSize, Sigma1WithResumptionIdNoResumeMIC); + TestSigma1Parsing(mem, bufferSize, Sigma1WithResumeMICNoResumptionId); TestSigma1Parsing(mem, bufferSize, Sigma1SessionIdMax); TestSigma1Parsing(mem, bufferSize, Sigma1SessionIdTooBig); TestSigma1Parsing(mem, bufferSize, Sigma1FutureProofTlvElement); From 291a38f6a9a657a92444aef8b16507098b736af5 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Fri, 24 Jan 2025 17:01:50 +0100 Subject: [PATCH 14/15] Integrating Comments --- src/protocols/secure_channel/CASESession.cpp | 58 ++++++++++---------- src/protocols/secure_channel/CASESession.h | 16 +++--- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index aa5de2498f488a..e6fe3d3dd96db2 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -110,7 +110,7 @@ constexpr chip::TLV::Tag AsTlvContextTag(Enum e) return chip::TLV::ContextTag(chip::to_underlying(e)); } -constexpr size_t kCaseOverheadForFutureTbeData = 128; +constexpr size_t kCaseOverheadForFutureTBEData = 128; } // namespace @@ -1370,6 +1370,10 @@ CHIP_ERROR CASESession::HandleSigma2Resume(System::PacketBufferHandle && msg) ParsedSigma2Resume parsedSigma2Resume; SuccessOrExit(err = ParseSigma2Resume(tlvReader, parsedSigma2Resume)); + SuccessOrExit(err = ValidateSigmaResumeMIC(parsedSigma2Resume.sigma2ResumeMIC, ByteSpan(mInitiatorRandom), + parsedSigma2Resume.resumptionId, ByteSpan(kKDFS2RKeyInfo), + ByteSpan(kResume2MIC_Nonce))); + if (parsedSigma2Resume.responderSessionParamStructPresent) { SetRemoteSessionParameters(parsedSigma2Resume.responderSessionParams); @@ -1377,10 +1381,6 @@ CHIP_ERROR CASESession::HandleSigma2Resume(System::PacketBufferHandle && msg) GetRemoteSessionParameters()); } - SuccessOrExit(err = ValidateSigmaResumeMIC(parsedSigma2Resume.sigma2ResumeMIC, ByteSpan(mInitiatorRandom), - parsedSigma2Resume.resumptionId, ByteSpan(kKDFS2RKeyInfo), - ByteSpan(kResume2MIC_Nonce))); - ChipLogDetail(SecureChannel, "Peer assigned session ID %d", parsedSigma2Resume.responderSessionId); SetPeerSessionId(parsedSigma2Resume.responderSessionId); @@ -1494,9 +1494,6 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) ParsedSigma2 parsedSigma2; ReturnErrorOnFailure(ParseSigma2(tlvReader, parsedSigma2)); - ChipLogDetail(SecureChannel, "Peer assigned session ID %d", parsedSigma2.responderSessionId); - SetPeerSessionId(parsedSigma2.responderSessionId); - // ParseSigma2 ensures that: // mRemotePubKey.Length() == responderEphPubKey.size() == kP256_PublicKey_Length. memcpy(mRemotePubKey.Bytes(), parsedSigma2.responderEphPubKey.data(), mRemotePubKey.Length()); @@ -1509,21 +1506,14 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) { uint8_t msg_salt[kIPKSize + kSigmaParamRandomNumberSize + kP256_PublicKey_Length + kSHA256_Hash_Length]; MutableByteSpan saltSpan(msg_salt); - ReturnErrorOnFailure(ConstructSaltSigma2(ByteSpan(parsedSigma2.responderRandom), mRemotePubKey, ByteSpan(mIPK), saltSpan)); + ReturnErrorOnFailure(ConstructSaltSigma2(parsedSigma2.responderRandom, mRemotePubKey, ByteSpan(mIPK), saltSpan)); ReturnErrorOnFailure(DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR2Info), sr2k)); } // Msg2 should only be added to MessageDigest after we construct SaltSigma2 that is used to derive S2K ReturnErrorOnFailure(mCommissioningHash.AddData(ByteSpan{ buf, buflen })); - if (parsedSigma2.responderSessionParamStructPresent) - { - SetRemoteSessionParameters(parsedSigma2.responderSessionParams); - mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters( - GetRemoteSessionParameters()); - } - ReturnErrorOnFailure(AES_CCM_decrypt(parsedSigma2.msgR2EncryptedPayload.data(), parsedSigma2.msgR2EncryptedPayload.size(), - nullptr, 0, parsedSigma2.msgR2Mic.data(), parsedSigma2.msgR2Mic.size(), sr2k.KeyHandle(), + nullptr, 0, parsedSigma2.msgR2MIC.data(), parsedSigma2.msgR2MIC.size(), sr2k.KeyHandle(), kTBEData2_Nonce, kTBEDataNonceLength, parsedSigma2.msgR2EncryptedPayload.data())); ContiguousBufferTLVReader decryptedDataTlvReader; @@ -1531,8 +1521,6 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) ParsedSigma2TBEData parsedSigma2TBEData; ReturnErrorOnFailure(ParseSigma2TBEData(decryptedDataTlvReader, parsedSigma2TBEData)); - std::copy(parsedSigma2TBEData.resumptionId.begin(), parsedSigma2TBEData.resumptionId.end(), mNewResumptionId.begin()); - // Validate responder identity located in msgR2Encrypted // Constructing responder identity P256PublicKey responderPublicKey; @@ -1569,9 +1557,21 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) ReturnErrorOnFailure( responderPublicKey.ECDSA_validate_msg_signature(msgR2Signed.Get(), msgR2SignedLen, parsedSigma2TBEData.tbsData2Signature)); + ChipLogDetail(SecureChannel, "Peer assigned session ID %d", parsedSigma2.responderSessionId); + SetPeerSessionId(parsedSigma2.responderSessionId); + + std::copy(parsedSigma2TBEData.resumptionId.begin(), parsedSigma2TBEData.resumptionId.end(), mNewResumptionId.begin()); + // Retrieve peer CASE Authenticated Tags (CATs) from peer's NOC. ReturnErrorOnFailure(ExtractCATsFromOpCert(parsedSigma2TBEData.responderNOC, mPeerCATs)); + if (parsedSigma2.responderSessionParamStructPresent) + { + SetRemoteSessionParameters(parsedSigma2.responderSessionParams); + mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters( + GetRemoteSessionParameters()); + } + return CHIP_NO_ERROR; } @@ -1603,7 +1603,7 @@ CHIP_ERROR CASESession::ParseSigma2(ContiguousBufferTLVReader & tlvReader, Parse kMaxCHIPCertLength, // responderICAC kMax_ECDSA_Signature_Length, // signature SessionResumptionStorage::kResumptionIdSize, // resumptionID - kCaseOverheadForFutureTbeData // extra bytes for future-proofing + kCaseOverheadForFutureTBEData // extra bytes for future-proofing ); size_t msgR2EncryptedLenWithTag = tlvReader.GetLength(); @@ -1616,7 +1616,7 @@ CHIP_ERROR CASESession::ParseSigma2(ContiguousBufferTLVReader & tlvReader, Parse size_t msgR2EncryptedPayloadLen = msgR2EncryptedLenWithTag - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; outParsedSigma2.msgR2EncryptedPayload = MutableByteSpan(outParsedSigma2.msgR2Encrypted.Get(), msgR2EncryptedPayloadLen); - outParsedSigma2.msgR2Mic = + outParsedSigma2.msgR2MIC = ByteSpan(outParsedSigma2.msgR2Encrypted.Get() + msgR2EncryptedPayloadLen, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES); // Retrieve responderSessionParams if present @@ -1924,13 +1924,13 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) // Step 1 // msgR3Encrypted will be allocated and initialised within ParseSigma3() Platform::ScopedMemoryBufferWithSize msgR3Encrypted; - // both msgR3EncryptedPayload and msgR3Mic will become backed by msgR3Encrypted in ParseSigma3() + // both msgR3EncryptedPayload and msgR3MIC will become backed by msgR3Encrypted in ParseSigma3() MutableByteSpan msgR3EncryptedPayload; - ByteSpan msgR3Mic; + ByteSpan msgR3MIC; { System::PacketBufferTLVReader tlvReader; tlvReader.Init(std::move(msg)); - SuccessOrExit(err = ParseSigma3(tlvReader, msgR3Encrypted, msgR3EncryptedPayload, msgR3Mic)); + SuccessOrExit(err = ParseSigma3(tlvReader, msgR3Encrypted, msgR3EncryptedPayload, msgR3MIC)); // Generate the S3K key MutableByteSpan saltSpan(msg_salt); @@ -1941,8 +1941,8 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ buf, bufLen })); } // Step 2 - Decrypt data blob - SuccessOrExit(err = AES_CCM_decrypt(msgR3EncryptedPayload.data(), msgR3EncryptedPayload.size(), nullptr, 0, msgR3Mic.data(), - msgR3Mic.size(), sr3k.KeyHandle(), kTBEData3_Nonce, kTBEDataNonceLength, + SuccessOrExit(err = AES_CCM_decrypt(msgR3EncryptedPayload.data(), msgR3EncryptedPayload.size(), nullptr, 0, msgR3MIC.data(), + msgR3MIC.size(), sr3k.KeyHandle(), kTBEData3_Nonce, kTBEDataNonceLength, msgR3EncryptedPayload.data())); decryptedDataTlvReader.Init(msgR3EncryptedPayload.data(), msgR3EncryptedPayload.size()); @@ -2011,7 +2011,7 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) CHIP_ERROR CASESession::ParseSigma3(ContiguousBufferTLVReader & tlvReader, Platform::ScopedMemoryBufferWithSize & outMsgR3Encrypted, - MutableByteSpan & outMsgR3EncryptedPayload, ByteSpan & outMsgR3Mic) + MutableByteSpan & outMsgR3EncryptedPayload, ByteSpan & outMsgR3MIC) { TLVType containerType = kTLVType_Structure; @@ -2024,7 +2024,7 @@ CHIP_ERROR CASESession::ParseSigma3(ContiguousBufferTLVReader & tlvReader, size_t maxMsgR3SignedEncLen = EstimateStructOverhead(kMaxCHIPCertLength, // initiatorNOC kMaxCHIPCertLength, // initiatorICAC kMax_ECDSA_Signature_Length, // signature - kCaseOverheadForFutureTbeData // extra bytes for future-proofing + kCaseOverheadForFutureTBEData // extra bytes for future-proofing ); size_t msgR3EncryptedLenWithTag = tlvReader.GetLength(); @@ -2037,7 +2037,7 @@ CHIP_ERROR CASESession::ParseSigma3(ContiguousBufferTLVReader & tlvReader, size_t msgR3EncryptedPayloadLen = msgR3EncryptedLenWithTag - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; outMsgR3EncryptedPayload = MutableByteSpan(outMsgR3Encrypted.Get(), msgR3EncryptedPayloadLen); - outMsgR3Mic = ByteSpan(outMsgR3Encrypted.Get() + msgR3EncryptedPayloadLen, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES); + outMsgR3MIC = ByteSpan(outMsgR3Encrypted.Get() + msgR3EncryptedPayloadLen, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES); ReturnErrorOnFailure(tlvReader.ExitContainer(containerType)); diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h index ed300dbd03a02c..d232e42d22c26e 100644 --- a/src/protocols/secure_channel/CASESession.h +++ b/src/protocols/secure_channel/CASESession.h @@ -222,7 +222,7 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, struct ParsedSigma1 : Sigma1Param { // Backed by: Sigma1 PacketBuffer passed to the method HandleSigma1() - // Lifetime: Valid for the lifetime of the tlvReader, which takes ownership of the Sigma1 PacketBuffer in the HandleSigma1() + // Lifetime: Valid for the lifetime of the TLVReader, which takes ownership of the Sigma1 PacketBuffer in the HandleSigma1() // method. ByteSpan initiatorEphPubKey; bool initiatorSessionParamStructPresent = false; @@ -243,7 +243,7 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, struct ParsedSigma2 { // Below ByteSpans are Backed by: Sigma2 PacketBuffer passed to the method HandleSigma2() - // Lifetime: Valid for the lifetime of the tlvReader, which takes ownership of the Sigma2 PacketBuffer in the HandleSigma2() + // Lifetime: Valid for the lifetime of the TLVReader, which takes ownership of the Sigma2 PacketBuffer in the HandleSigma2() // method. ByteSpan responderRandom; ByteSpan responderEphPubKey; @@ -252,7 +252,7 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, // Below ByteSpans are Backed by: msgR2Encrypted buffer // Lifetime: Valid as long as msgR2Encrypted is not released MutableByteSpan msgR2EncryptedPayload; - ByteSpan msgR2Mic; + ByteSpan msgR2MIC; SessionParameters responderSessionParams; uint16_t responderSessionId; bool responderSessionParamStructPresent = false; @@ -280,7 +280,7 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, struct ParsedSigma2Resume { // Below ByteSpans are Backed by: Sigma2Resume PacketBuffer passed to the method HandleSigma2Resume() - // Lifetime: Valid for the lifetime of the tlvReader, which takes ownership of the Sigma2Resume PacketBuffer in the + // Lifetime: Valid for the lifetime of the TLVReader, which takes ownership of the Sigma2Resume PacketBuffer in the // HandleSigma2Resume() method. ByteSpan resumptionId; ByteSpan sigma2ResumeMIC; @@ -440,16 +440,16 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, * * @param outMsgR3EncryptedPayload reference to a span that will be set to point to the payload of outMsgR3Encrypted within * ParseSigma3. Calls to this function must always be made with a newly created and fresh - * outMsgR3Mic + * outMsgR3MIC * - * @param outMsgR3Mic reference to a span that will be set to point to the MIC of outMsgR3Encrypted within ParseSigma3. - * Calls to this function must always be made with a newly created and fresh outMsgR3Mic + * @param outMsgR3MIC reference to a span that will be set to point to the MIC of outMsgR3Encrypted within ParseSigma3. + * Calls to this function must always be made with a newly created and fresh outMsgR3MIC * * @note all out parameters will be valid as long the Buffer outMsgR3Encrypted is valid. **/ static CHIP_ERROR ParseSigma3(TLV::ContiguousBufferTLVReader & tlvReader, Platform::ScopedMemoryBufferWithSize & outMsgR3Encrypted, - MutableByteSpan & outMsgR3EncryptedPayload, ByteSpan & outMsgR3Mic); + MutableByteSpan & outMsgR3EncryptedPayload, ByteSpan & outMsgR3MIC); /** * Parse a decrypted TBEData3Encrypted message. This function will return success only if the From 33b08d1ec401a315ba00a8414e87a4dc55737b19 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Tue, 4 Feb 2025 09:55:00 +0100 Subject: [PATCH 15/15] Integrating comments --- src/protocols/secure_channel/CASESession.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index e6fe3d3dd96db2..c608c5b1ca4be9 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -1509,7 +1509,8 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) ReturnErrorOnFailure(ConstructSaltSigma2(parsedSigma2.responderRandom, mRemotePubKey, ByteSpan(mIPK), saltSpan)); ReturnErrorOnFailure(DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR2Info), sr2k)); } - // Msg2 should only be added to MessageDigest after we construct SaltSigma2 that is used to derive S2K + // Msg2 should only be added to MessageDigest after we construct SaltSigma2 that is used to derive S2K, + // Because constructing SaltSigma2 uses the MessageDigest at a point when it should only include Msg1. ReturnErrorOnFailure(mCommissioningHash.AddData(ByteSpan{ buf, buflen })); ReturnErrorOnFailure(AES_CCM_decrypt(parsedSigma2.msgR2EncryptedPayload.data(), parsedSigma2.msgR2EncryptedPayload.size(),