diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index 9ae96c88179483..c608c5b1ca4be9 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 { @@ -349,48 +351,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; -}; - -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. @@ -1163,7 +1123,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 +1152,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( @@ -1400,54 +1360,35 @@ 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]; - + System::PacketBufferTLVReader tlvReader; 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())); + ParsedSigma2Resume parsedSigma2Resume; + SuccessOrExit(err = ParseSigma2Resume(tlvReader, parsedSigma2Resume)); - 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)); + SuccessOrExit(err = ValidateSigmaResumeMIC(parsedSigma2Resume.sigma2ResumeMIC, ByteSpan(mInitiatorRandom), + parsedSigma2Resume.resumptionId, ByteSpan(kKDFS2RKeyInfo), + ByteSpan(kResume2MIC_Nonce))); - SuccessOrExit(err = ValidateSigmaResumeMIC(ByteSpan(sigma2ResumeMIC), ByteSpan(mInitiatorRandom), resumptionId, - ByteSpan(kKDFS2RKeyInfo), ByteSpan(kResume2MIC_Nonce))); - - SuccessOrExit(err = tlvReader.Next()); - VerifyOrExit(TLV::TagNumFromTag(tlvReader.GetTag()) == ++decodeTagIdSeq, err = CHIP_ERROR_INVALID_TLV_TAG); - SuccessOrExit(err = tlvReader.Get(responderSessionId)); - - if (tlvReader.Next() != CHIP_END_OF_TLV) + if (parsedSigma2Resume.responderSessionParamStructPresent) { - SuccessOrExit(err = DecodeSessionParametersIfPresent(TLV::ContextTag(4), tlvReader, mRemoteSessionParams)); + SetRemoteSessionParameters(parsedSigma2Resume.responderSessionParams); mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters( GetRemoteSessionParameters()); } - ChipLogDetail(SecureChannel, "Peer assigned session session ID %d", responderSessionId); - SetPeerSessionId(responderSessionId); + ChipLogDetail(SecureChannel, "Peer assigned session 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,12 +1407,52 @@ CHIP_ERROR CASESession::HandleSigma2Resume(System::PacketBufferHandle && msg) return err; } +CHIP_ERROR CASESession::ParseSigma2Resume(ContiguousBufferTLVReader & tlvReader, ParsedSigma2Resume & outParsedSigma2Resume) +{ + TLVType containerType = kTLVType_Structure; + + ReturnErrorOnFailure(tlvReader.Next(containerType, AnonymousTag())); + ReturnErrorOnFailure(tlvReader.EnterContainer(containerType)); + + ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma2ResumeTags::kResumptionID))); + ReturnErrorOnFailure(tlvReader.GetByteView(outParsedSigma2Resume.resumptionId)); + VerifyOrReturnError(outParsedSigma2Resume.resumptionId.size() == SessionResumptionStorage::kResumptionIdSize, + CHIP_ERROR_INVALID_CASE_PARAMETER); + + 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(tlvReader.Next(AsTlvContextTag(Sigma2ResumeTags::kResponderSessionID))); + ReturnErrorOnFailure(tlvReader.Get(outParsedSigma2Resume.responderSessionId)); + + CHIP_ERROR err = tlvReader.Next(); + if (err == CHIP_NO_ERROR && tlvReader.GetTag() == AsTlvContextTag(Sigma2ResumeTags::kResponderSessionParams)) + { + 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 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 is not properly terminated with an + // EndOfContainer TLV Element. + ReturnErrorOnFailure(tlvReader.ExitContainer(containerType)); + + return CHIP_NO_ERROR; +} + CHIP_ERROR CASESession::HandleSigma2_and_SendSigma3(System::PacketBufferHandle && msg) { 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(); @@ -1479,200 +1460,249 @@ 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"); - CHIP_ERROR err = CHIP_NO_ERROR; - System::PacketBufferTLVReader tlvReader; - TLV::TLVReader decryptedDataTlvReader; - TLV::TLVType containerType = TLV::kTLVType_Structure; + ChipLogProgress(SecureChannel, "Received Sigma2 msg"); + + VerifyOrReturnError(mEphemeralKey != nullptr, CHIP_ERROR_INTERNAL); 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; - 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()); - - P256ECDSASignature tbsData2Signature; - - NodeId responderNodeId; - P256PublicKey responderPublicKey; - - uint8_t responderRandom[kSigmaParamRandomNumberSize]; - ByteSpan responderNOC; - ByteSpan responderICAC; - - uint16_t responderSessionId; - - ChipLogProgress(SecureChannel, "Received Sigma2 msg"); + 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)); - 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))); - - // Assign Session ID - SuccessOrExit(err = tlvReader.Next(TLV::kTLVType_UnsignedInteger, AsTlvContextTag(Sigma2Tags::kResponderSessionId))); - SuccessOrExit(err = tlvReader.Get(responderSessionId)); + ParsedSigma2 parsedSigma2; + ReturnErrorOnFailure(ParseSigma2(tlvReader, parsedSigma2)); - ChipLogDetail(SecureChannel, "Peer assigned session session ID %d", responderSessionId); - SetPeerSessionId(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)); + 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); - SuccessOrExit(err = ConstructSaltSigma2(ByteSpan(responderRandom), mRemotePubKey, ByteSpan(mIPK), saltSpan)); - SuccessOrExit(err = DeriveSigmaKey(saltSpan, ByteSpan(kKDFSR2Info), sr2k)); + 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, + // Because constructing SaltSigma2 uses the MessageDigest at a point when it should only include Msg1. + ReturnErrorOnFailure(mCommissioningHash.AddData(ByteSpan{ buf, buflen })); - SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ buf, buflen })); + 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())); - // 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); - - 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; - - 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())); - - decryptedDataTlvReader.Init(msg_R2_Encrypted.Get(), msg_r2_encrypted_len); - containerType = TLV::kTLVType_Structure; - SuccessOrExit(err = decryptedDataTlvReader.Next(containerType, TLV::AnonymousTag())); - SuccessOrExit(err = decryptedDataTlvReader.EnterContainer(containerType)); + ContiguousBufferTLVReader decryptedDataTlvReader; + decryptedDataTlvReader.Init(parsedSigma2.msgR2EncryptedPayload.data(), parsedSigma2.msgR2EncryptedPayload.size()); + ParsedSigma2TBEData parsedSigma2TBEData; + ReturnErrorOnFailure(ParseSigma2TBEData(decryptedDataTlvReader, parsedSigma2TBEData)); - SuccessOrExit(err = decryptedDataTlvReader.Next(TLV::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); - SuccessOrExit(err = decryptedDataTlvReader.Get(responderICAC)); - SuccessOrExit(err = decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSignature))); - } - - // Validate responder identity located in msg_r2_encrypted + // 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, responderNOC, 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 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); - - VerifyOrExit(msg_R2_Signed.Alloc(msg_r2_signed_len), err = CHIP_ERROR_NO_MEMORY); + // Construct msgR2Signed and validate the signature in msgR2Encrypted. + size_t msgR2SignedLen = EstimateStructOverhead(parsedSigma2TBEData.responderNOC.size(), // resonderNOC + parsedSigma2TBEData.responderICAC.size(), // responderICAC + kP256_PublicKey_Length, // responderEphPubKey + kP256_PublicKey_Length // initiatorEphPubKey + ); - SuccessOrExit(err = ConstructTBSData(responderNOC, responderICAC, ByteSpan(mRemotePubKey, mRemotePubKey.Length()), - ByteSpan(mEphemeralKey->Pubkey(), mEphemeralKey->Pubkey().Length()), msg_R2_Signed.Get(), - msg_r2_signed_len)); + chip::Platform::ScopedMemoryBuffer msgR2Signed; + VerifyOrReturnError(msgR2Signed.Alloc(msgR2SignedLen), CHIP_ERROR_NO_MEMORY); - 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())); + 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(msg_R2_Signed.Get(), msg_r2_signed_len, tbsData2Signature)); + ReturnErrorOnFailure( + responderPublicKey.ECDSA_validate_msg_signature(msgR2Signed.Get(), msgR2SignedLen, parsedSigma2TBEData.tbsData2Signature)); - // Retrieve session resumption ID - SuccessOrExit(err = decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kResumptionID))); - SuccessOrExit(err = decryptedDataTlvReader.GetBytes(mNewResumptionId.data(), mNewResumptionId.size())); + 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. - SuccessOrExit(err = ExtractCATsFromOpCert(responderNOC, mPeerCATs)); + ReturnErrorOnFailure(ExtractCATsFromOpCert(parsedSigma2TBEData.responderNOC, mPeerCATs)); - // Retrieve responderMRPParams if present - if (tlvReader.Next() != CHIP_END_OF_TLV) + if (parsedSigma2.responderSessionParamStructPresent) { - SuccessOrExit(err = DecodeSessionParametersIfPresent(AsTlvContextTag(Sigma2Tags::kResponderSessionParams), tlvReader, - mRemoteSessionParams)); + SetRemoteSessionParameters(parsedSigma2.responderSessionParams); mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters( GetRemoteSessionParameters()); } -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))); + + 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 = tlvReader.GetLength(); + + // Validate we did not receive a buffer larger than legal + 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)) { - SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam); + ReturnErrorOnFailure(DecodeSessionParametersIfPresent(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, 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 (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; +} + +CHIP_ERROR CASESession::ParseSigma2TBEData(ContiguousBufferTLVReader & decryptedDataTlvReader, + ParsedSigma2TBEData & outParsedSigma2TBE) +{ + TLVType containerType = kTLVType_Structure; + + ReturnErrorOnFailure(decryptedDataTlvReader.Next(containerType, AnonymousTag())); + ReturnErrorOnFailure(decryptedDataTlvReader.EnterContainer(containerType)); + + ReturnErrorOnFailure(decryptedDataTlvReader.Next(AsTlvContextTag(TBEDataTags::kSenderNOC))); + ReturnErrorOnFailure(decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.responderNOC)); + VerifyOrReturnError(outParsedSigma2TBE.responderNOC.size() <= kMaxCHIPCertLength, CHIP_ERROR_INVALID_CASE_PARAMETER); + + ReturnErrorOnFailure(decryptedDataTlvReader.Next()); + if (decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSenderICAC)) + { + ReturnErrorOnFailure(decryptedDataTlvReader.GetByteView(outParsedSigma2TBE.responderICAC)); + VerifyOrReturnError(outParsedSigma2TBE.responderICAC.size() <= kMaxCHIPCertLength, CHIP_ERROR_INVALID_CASE_PARAMETER); + + ReturnErrorOnFailure(decryptedDataTlvReader.Next(kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSignature))); + } + + VerifyOrReturnError(decryptedDataTlvReader.GetTag() == AsTlvContextTag(TBEDataTags::kSignature), CHIP_ERROR_INVALID_TLV_TAG); + // tbsData2Signature's length should equal kMax_ECDSA_Signature_Length as per the Specification + 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())); + + // Retrieve session resumption ID + 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 (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; } 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()) { @@ -1686,48 +1716,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); + 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) @@ -1754,11 +1777,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()) { @@ -1823,9 +1846,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); @@ -1871,20 +1894,12 @@ 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::TLVType containerType = TLV::kTLVType_Structure; + ContiguousBufferTLVReader decryptedDataTlvReader; + TLVType containerType = 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; - AutoReleaseSessionKey sr3k(*mSessionManager->GetSessionKeystore()); uint8_t msg_salt[kIPKSize + kSHA256_Hash_Length]; @@ -1907,72 +1922,47 @@ 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; - // 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 - 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())); - - 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(msgR3EncryptedPayload.data(), msgR3EncryptedPayload.size(), nullptr, 0, msgR3MIC.data(), + msgR3MIC.size(), sr3k.KeyHandle(), kTBEData3_Nonce, kTBEDataNonceLength, + msgR3EncryptedPayload.data())); - SuccessOrExit(err = decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, AsTlvContextTag(TBEDataTags::kSenderNOC))); - SuccessOrExit(err = decryptedDataTlvReader.Get(data.initiatorNOC)); + decryptedDataTlvReader.Init(msgR3EncryptedPayload.data(), msgR3EncryptedPayload.size()); + SuccessOrExit(err = ParseSigma3TBEData(decryptedDataTlvReader, data)); - 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))); - } + // Step 3 - Construct Sigma3 TBS Data + data.msgR3SignedLen = TLV::EstimateStructOverhead(data.initiatorNOC.size(), // initiatorNOC + data.initiatorICAC.size(), // initiatorICAC + kP256_PublicKey_Length, // initiatorEphPubKey + kP256_PublicKey_Length // responderEphPubKey + ); - // Step 4 - Construct Sigma3 TBS Data - data.msg_r3_signed_len = TLV::EstimateStructOverhead(sizeof(uint16_t), 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)); - - 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())); + data.msgR3Signed.Get(), data.msgR3SignedLen)); - // Prepare for Step 5/6 + // Prepare for Step 4/5 { MutableByteSpan fabricRCAC{ data.rootCertBuf }; SuccessOrExit(err = mFabricsTable->FetchRootCert(mFabricIndex, fabricRCAC)); @@ -1985,22 +1975,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()); @@ -2018,6 +2010,73 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) return err; } +CHIP_ERROR CASESession::ParseSigma3(ContiguousBufferTLVReader & tlvReader, + Platform::ScopedMemoryBufferWithSize & outMsgR3Encrypted, + MutableByteSpan & outMsgR3EncryptedPayload, ByteSpan & outMsgR3MIC) +{ + TLVType containerType = kTLVType_Structure; + + ReturnErrorOnFailure(tlvReader.Next(containerType, AnonymousTag())); + ReturnErrorOnFailure(tlvReader.EnterContainer(containerType)); + + // Fetch encrypted data + ReturnErrorOnFailure(tlvReader.Next(AsTlvContextTag(Sigma3Tags::kEncrypted3))); + + size_t maxMsgR3SignedEncLen = EstimateStructOverhead(kMaxCHIPCertLength, // initiatorNOC + kMaxCHIPCertLength, // initiatorICAC + kMax_ECDSA_Signature_Length, // signature + kCaseOverheadForFutureTBEData // extra bytes for future-proofing + ); + + 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(outMsgR3Encrypted.Alloc(msgR3EncryptedLenWithTag), CHIP_ERROR_NO_MEMORY); + ReturnErrorOnFailure(tlvReader.GetBytes(outMsgR3Encrypted.Get(), outMsgR3Encrypted.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)); + + return CHIP_NO_ERROR; +} + +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(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)) + { + 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); + 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())); + + ReturnErrorOnFailure(decryptedDataTlvReader.ExitContainer(containerType)); + + return CHIP_NO_ERROR; +} + CHIP_ERROR CASESession::HandleSigma3b(HandleSigma3Data & data, bool & cancel) { // Step 5/6 @@ -2037,7 +2096,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; } @@ -2184,11 +2243,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()) { @@ -2514,7 +2573,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 4928cb633dc77c..d232e42d22c26e 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; @@ -237,16 +240,99 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, size_t encrypted2Length = 0; const ReliableMessageProtocolConfig * responderMrpConfig; }; + 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; + ByteSpan responderEphPubKey; + + Platform::ScopedMemoryBufferWithSize msgR2Encrypted; + // 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; + ByteSpan resumptionId; + Crypto::P256ECDSASignature tbsData2Signature; + }; struct EncodeSigma2ResumeInputs { ByteSpan resumptionId; uint8_t sigma2ResumeMICBuffer[Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES]; - MutableByteSpan resumeMIC{ sigma2ResumeMICBuffer }; + MutableByteSpan sigma2ResumeMIC{ sigma2ResumeMICBuffer }; uint16_t responderSessionId; const ReliableMessageProtocolConfig * responderMrpConfig; }; + 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; + SessionParameters responderSessionParams; + uint16_t responderSessionId; + bool responderSessionParamStructPresent = false; + }; + + 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; + + // 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; + + 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. @@ -280,6 +366,42 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, */ static CHIP_ERROR ParseSigma1(TLV::ContiguousBufferTLVReader & tlvReader, ParsedSigma1 & 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); + + /** + * 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); + /** * @brief Encodes a Sigma2 message into TLV format and allocates a buffer for it, which is owned by the PacketBufferHandle * outparam. @@ -289,6 +411,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); @@ -305,6 +428,39 @@ 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 & 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: friend class TestCASESession; @@ -343,12 +499,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 e44c85a3480595..d62c0a3d463eb3 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 @@ -68,12 +69,21 @@ 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; + using CASESession::ParsedSigma2TBEData; using CASESession::EncodeSigma1; using CASESession::EncodeSigma2; using CASESession::EncodeSigma2Resume; using CASESession::ParseSigma1; + using CASESession::ParseSigma2; + using CASESession::ParseSigma2Resume; + using CASESession::ParseSigma2TBEData; + using CASESession::ParseSigma3; + using CASESession::ParseSigma3TBEData; }; class TestCASESession : public Test::LoopbackMessagingContext @@ -631,12 +641,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; @@ -644,8 +655,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; @@ -742,6 +757,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)); @@ -755,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; \ @@ -828,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; @@ -846,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; @@ -856,6 +889,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. @@ -877,8 +921,12 @@ 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); + TestSigma1Parsing(mem, bufferSize, Sigma1FutureProofTlvElementNoStructEnd); } TEST_F(TestCASESession, EncodeSigma1Test) @@ -987,6 +1035,195 @@ 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 + // 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 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 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; + + 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] = { 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] = { Params::kTestValueResponderEphPubKey }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResponderEphPubKeyTag), ByteSpan(responderEphPubKey))); + + 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) + { + 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 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) \ + { \ + \ + 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) + +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 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; +}; + +struct Sigma2TooShortPubkey : public BadSigma2ParamsBase +{ + static constexpr size_t kResponderEphPubKeyLen = 64; +}; + +struct Sigma2TooLongEncrypted2 : public BadSigma2ParamsBase +{ + static constexpr size_t kEncrypted2Len = kMaxMsgR2SignedEncLen + 1; +}; +struct Sigma2TooShortEncrypted2 : public BadSigma2ParamsBase +{ + static constexpr size_t kEncrypted2Len = CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; +}; + +struct Sigma2FutureProofTlvElement : public Sigma2Params +{ + static constexpr bool kIncludeFutureProofTlvElement = true; +}; + +struct Sigma2FutureProofTlvElementNoStructEnd : public BadSigma2ParamsBase +{ + static constexpr bool kIncludeFutureProofTlvElement = true; + static constexpr bool kIncludeStructEnd = false; +}; + +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, 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); +} + TEST_F(TestCASESession, EncodeSigma2Test) { CASESessionAccess::EncodeSigma2Inputs encodeParams; @@ -1006,8 +1243,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 +1308,214 @@ 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 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 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; + + 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] = { Params::kTestValueResumptionId }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResumptionIdTag), ByteSpan(resumptionId))); + + 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)); + + // 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; +} + +#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) \ + { \ + 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) + +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; +}; + +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. + 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); + TestSigma2ResumeParsing(mem, bufferSize, Sigma2ResumeFutureProofTlvElement); + TestSigma2ResumeParsing(mem, bufferSize, Sigma2ResumeFutureProofTlvElementNoStructEnd); +} + 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,6 +1535,26 @@ 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 @@ -1386,4 +1839,452 @@ 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 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 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; + + 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] = { Params::kTestValueResponderNOC }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResponderNOCTag), ByteSpan(responderNOC))); + + uint8_t responderICAC[Params::kResponderICACLen] = { Params::kTestValueResponderICAC }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kResponderICACTag), ByteSpan(responderICAC))); + + uint8_t signature[Params::kSignatureLen] = { Params::kTestValueSignature }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kSignatureTag), ByteSpan(signature))); + + 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. + 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 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) \ + { \ + 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) + +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; +}; + +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. + 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); + TestSigma2TBEParsing(mem, bufferSize, Sigma2TBEFutureProofTlvElement); + 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 uint8_t kTestValueEncrypted3 = { 1 }; + + 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] = { 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. + 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; \ + MutableByteSpan msgR3EncryptedPayload; \ + ByteSpan msgR3Mic; \ + \ + EXPECT_EQ(CASESessionAccess::ParseSigma3(reader, msgR3Encrypted, msgR3EncryptedPayload, msgR3Mic) == CHIP_NO_ERROR, \ + params::kExpectSuccess); \ + if (params::kExpectSuccess) \ + { \ + uint8_t expectedEncrypted3[params::kEncrypted3Len] = { params::kTestValueEncrypted3 }; \ + EXPECT_TRUE(ByteSpan(msgR3Encrypted.Get(), msgR3Encrypted.AllocatedSize()).data_equal(ByteSpan(expectedEncrypted3))); \ + } \ + } 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; +}; + +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 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; + 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] = { Params::kTestValueInitiatorNOC }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kInitiatorNOCTag), ByteSpan(initiatorNOC))); + + uint8_t initiatorICAC[Params::kInitiatorICACLen] = { Params::kTestValueInitiatorICAC }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::kInitiatorICACTag), ByteSpan(initiatorICAC))); + + 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. + 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; +} + +#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) \ + { \ + 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) + +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