From dbbc6e959edd82a5b4f1c6f018f18be9ecea3704 Mon Sep 17 00:00:00 2001 From: Evgeny Margolis Date: Fri, 4 Mar 2022 16:18:43 -0800 Subject: [PATCH] Fixed/Added CASE Identity Checks. (#15829) Added checks: -- Verify that peer NOC FabricId matches the expected Fabric ID. -- On Processing of Sigma2: Verify that peer NodeId matches one used to generate Destination Identifier in Sigma1. -- On Processing of Sigma3: Assign peer NodeId from peer NOC. -- If root certificate includes Fabric ID verify it matches ICAC and NOC Fabric IDs. --- src/credentials/FabricTable.cpp | 20 +++++++++--- src/protocols/secure_channel/CASESession.cpp | 32 ++++++++++++-------- src/protocols/secure_channel/CASESession.h | 4 +-- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/credentials/FabricTable.cpp b/src/credentials/FabricTable.cpp index aa813dddc641d0..3ade7091661b58 100644 --- a/src/credentials/FabricTable.cpp +++ b/src/credentials/FabricTable.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-2022 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -312,12 +312,24 @@ CHIP_ERROR FabricInfo::VerifyCredentials(const ByteSpan & noc, const ByteSpan & NodeId nodeId; ReturnErrorOnFailure(ExtractNodeIdFabricIdFromOpCert(certificates.GetLastCert()[0], &nodeId, &fabricId)); + FabricId icacFabricId = kUndefinedFabricId; if (!icac.empty()) { - FabricId icacFabric = kUndefinedFabricId; - if (ExtractFabricIdFromCert(certificates.GetCertSet()[1], &icacFabric) == CHIP_NO_ERROR && icacFabric != kUndefinedFabricId) + if (ExtractFabricIdFromCert(certificates.GetCertSet()[1], &icacFabricId) == CHIP_NO_ERROR && + icacFabricId != kUndefinedFabricId) { - ReturnErrorCodeIf(icacFabric != fabricId, CHIP_ERROR_FABRIC_MISMATCH_ON_ICA); + ReturnErrorCodeIf(icacFabricId != fabricId, CHIP_ERROR_FABRIC_MISMATCH_ON_ICA); + } + } + + FabricId rcacFabricId = kUndefinedFabricId; + if (ExtractFabricIdFromCert(certificates.GetCertSet()[0], &rcacFabricId) == CHIP_NO_ERROR && rcacFabricId != kUndefinedFabricId) + { + ReturnErrorCodeIf(rcacFabricId != fabricId, CHIP_ERROR_WRONG_CERT_DN); + if (!icac.empty()) + { + // If FabricId attribute is present in RCAC then it SHOULD be present in ICAC as well. + ReturnErrorCodeIf(icacFabricId == kUndefinedFabricId, CHIP_ERROR_WRONG_CERT_DN); } } diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index bb2161d94dedc2..3c0cd1c012de5c 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -756,7 +756,8 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) P256ECDSASignature tbsData2Signature; - P256PublicKey remoteCredential; + NodeId responderNodeId; + P256PublicKey responderPublicKey; uint8_t responderRandom[kSigmaParamRandomNumberSize]; ByteSpan responderNOC; @@ -841,7 +842,11 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) // Validate responder identity located in msg_r2_encrypted // Constructing responder identity - SuccessOrExit(err = Validate_and_RetrieveResponderID(responderNOC, responderICAC, remoteCredential)); + SuccessOrExit(err = ValidatePeerIdentity(responderNOC, responderICAC, responderNodeId, responderPublicKey)); + + // Verify that responderNodeId (from responderNOC) matches one that was included + // in the computation of the Destination Identifier when generating Sigma1. + VerifyOrReturnError(GetPeerNodeId() == 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(), @@ -859,7 +864,7 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) SuccessOrExit(err = decryptedDataTlvReader.GetBytes(tbsData2Signature, tbsData2Signature.Length())); // Validate signature - SuccessOrExit(err = remoteCredential.ECDSA_validate_msg_signature(msg_R2_Signed.Get(), msg_r2_signed_len, tbsData2Signature)); + SuccessOrExit(err = responderPublicKey.ECDSA_validate_msg_signature(msg_R2_Signed.Get(), msg_r2_signed_len, tbsData2Signature)); // Retrieve session resumption ID SuccessOrExit(err = decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, TLV::ContextTag(kTag_TBEData_ResumptionID))); @@ -1043,7 +1048,8 @@ CHIP_ERROR CASESession::HandleSigma3(System::PacketBufferHandle && msg) P256ECDSASignature tbsData3Signature; - P256PublicKey remoteCredential; + NodeId initiatorNodeId; + P256PublicKey initiatorPublicKey; ByteSpan initiatorNOC; ByteSpan initiatorICAC; @@ -1106,7 +1112,8 @@ CHIP_ERROR CASESession::HandleSigma3(System::PacketBufferHandle && msg) // Step 5/6 // Validate initiator identity located in msg->Start() // Constructing responder identity - SuccessOrExit(err = Validate_and_RetrieveResponderID(initiatorNOC, initiatorICAC, remoteCredential)); + SuccessOrExit(err = ValidatePeerIdentity(initiatorNOC, initiatorICAC, initiatorNodeId, initiatorPublicKey)); + SetPeerNodeId(initiatorNodeId); // Step 4 - Construct Sigma3 TBS Data msg_r3_signed_len = TLV::EstimateStructOverhead(sizeof(uint16_t), initiatorNOC.size(), initiatorICAC.size(), @@ -1129,7 +1136,7 @@ CHIP_ERROR CASESession::HandleSigma3(System::PacketBufferHandle && msg) // current flow of code, a malicious node can trigger a DoS style attack on the device. // The same change should be made in Sigma2 processing. // Step 7 - Validate Signature - SuccessOrExit(err = remoteCredential.ECDSA_validate_msg_signature(msg_R3_Signed.Get(), msg_r3_signed_len, tbsData3Signature)); + SuccessOrExit(err = initiatorPublicKey.ECDSA_validate_msg_signature(msg_R3_Signed.Get(), msg_r3_signed_len, tbsData3Signature)); SuccessOrExit(err = mCommissioningHash.Finish(messageDigestSpan)); @@ -1259,19 +1266,20 @@ CHIP_ERROR CASESession::ValidateSigmaResumeMIC(const ByteSpan & resumeMIC, const return CHIP_NO_ERROR; } -CHIP_ERROR CASESession::Validate_and_RetrieveResponderID(const ByteSpan & responderNOC, const ByteSpan & responderICAC, - Crypto::P256PublicKey & responderID) +CHIP_ERROR CASESession::ValidatePeerIdentity(const ByteSpan & peerNOC, const ByteSpan & peerICAC, NodeId & peerNodeId, + Crypto::P256PublicKey & peerPublicKey) { ReturnErrorCodeIf(mFabricInfo == nullptr, CHIP_ERROR_INCORRECT_STATE); ReturnErrorOnFailure(SetEffectiveTime()); PeerId peerId; - FabricId rawFabricId; - ReturnErrorOnFailure( - mFabricInfo->VerifyCredentials(responderNOC, responderICAC, mValidContext, peerId, rawFabricId, responderID)); + FabricId peerNOCFabricId; + ReturnErrorOnFailure(mFabricInfo->VerifyCredentials(peerNOC, peerICAC, mValidContext, peerId, peerNOCFabricId, peerPublicKey)); + + VerifyOrReturnError(mFabricInfo->GetFabricId() == peerNOCFabricId, CHIP_ERROR_INVALID_CASE_PARAMETER); - SetPeerNodeId(peerId.GetNodeId()); + peerNodeId = peerId.GetNodeId(); return CHIP_NO_ERROR; } diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h index e813eaf6c1cdfa..6c8c35e3ed8d55 100644 --- a/src/protocols/secure_channel/CASESession.h +++ b/src/protocols/secure_channel/CASESession.h @@ -198,8 +198,8 @@ class DLL_EXPORT CASESession : public Messaging::ExchangeDelegate, public Pairin CHIP_ERROR ConstructSaltSigma2(const ByteSpan & rand, const Crypto::P256PublicKey & pubkey, const ByteSpan & ipk, MutableByteSpan & salt); - CHIP_ERROR Validate_and_RetrieveResponderID(const ByteSpan & responderNOC, const ByteSpan & responderICAC, - Crypto::P256PublicKey & responderID); + CHIP_ERROR ValidatePeerIdentity(const ByteSpan & peerNOC, const ByteSpan & peerICAC, NodeId & peerNodeId, + Crypto::P256PublicKey & peerPublicKey); CHIP_ERROR ConstructTBSData(const ByteSpan & senderNOC, const ByteSpan & senderICAC, const ByteSpan & senderPubKey, const ByteSpan & receiverPubKey, uint8_t * tbsData, size_t & tbsDataLen); CHIP_ERROR ConstructSaltSigma3(const ByteSpan & ipk, MutableByteSpan & salt);