From e3bf3295645df408af8a812096e4fa6642c346b9 Mon Sep 17 00:00:00 2001 From: Evgeny Margolis Date: Mon, 29 Nov 2021 22:08:00 -0800 Subject: [PATCH] ACLs: Added SessionType and CATs Parameters to SecureSession and PairingSession Classes (#12027) --- src/credentials/CHIPCert.cpp | 26 ++++++-- src/credentials/CHIPCert.h | 24 ++++++- src/credentials/tests/TestChipCert.cpp | 64 +++++++++---------- src/lib/core/DataModelTypes.h | 1 + src/lib/core/NodeId.h | 7 +- src/protocols/secure_channel/CASESession.cpp | 26 +++++++- src/protocols/secure_channel/CASESession.h | 1 + src/protocols/secure_channel/PASESession.cpp | 5 +- .../secure_channel/tests/TestCASESession.cpp | 5 ++ .../secure_channel/tests/TestPASESession.cpp | 7 +- src/transport/PairingSession.h | 17 ++++- src/transport/SecureSession.h | 30 +++++++-- src/transport/SecureSessionTable.h | 9 ++- src/transport/SessionManager.cpp | 3 +- src/transport/tests/TestPeerConnections.cpp | 48 ++++++++++---- 15 files changed, 201 insertions(+), 72 deletions(-) diff --git a/src/credentials/CHIPCert.cpp b/src/credentials/CHIPCert.cpp index 5bb47aea3aba84..c6acf9d1e9f49f 100644 --- a/src/credentials/CHIPCert.cpp +++ b/src/credentials/CHIPCert.cpp @@ -883,7 +883,19 @@ CHIP_ERROR ExtractFabricIdFromCert(const ChipCertificateData & cert, FabricId * return CHIP_ERROR_INVALID_ARGUMENT; } -CHIP_ERROR ExtractCATsFromOpCert(const ChipCertificateData & opcert, uint32_t * cats, uint8_t catsSize) +CHIP_ERROR ExtractCATsFromOpCert(const ByteSpan & opcert, CATValues & cats) +{ + ChipCertificateSet certSet; + ChipCertificateData certData; + + ReturnErrorOnFailure(certSet.Init(&certData, 1)); + + ReturnErrorOnFailure(certSet.LoadCert(opcert, BitFlags())); + + return ExtractCATsFromOpCert(certData, cats); +} + +CHIP_ERROR ExtractCATsFromOpCert(const ChipCertificateData & opcert, CATValues & cats) { uint8_t catCount = 0; uint8_t certType; @@ -897,13 +909,17 @@ CHIP_ERROR ExtractCATsFromOpCert(const ChipCertificateData & opcert, uint32_t * const auto & rdn = subjectDN.rdn[i]; if (rdn.mAttrOID == ASN1::kOID_AttributeType_ChipCASEAuthenticatedTag) { - ReturnErrorCodeIf(catCount == catsSize, CHIP_ERROR_BUFFER_TOO_SMALL); - cats[catCount++] = static_cast(rdn.mChipVal); + // This error should never happen in practice because valid NOC cannot have more + // than kMaxSubjectCATAttributeCount CATs in its subject. The check that it is + // valid NOC was done above. + ReturnErrorCodeIf(catCount == cats.size(), CHIP_ERROR_BUFFER_TOO_SMALL); + VerifyOrReturnError(CanCastTo(rdn.mChipVal), CHIP_ERROR_INVALID_ARGUMENT); + cats.val[catCount++] = static_cast(rdn.mChipVal); } } - for (uint8_t i = catCount; i < catsSize; ++i) + for (uint8_t i = catCount; i < cats.size(); ++i) { - cats[i] = 0; + cats.val[i] = kUndefinedCAT; } return CHIP_NO_ERROR; diff --git a/src/credentials/CHIPCert.h b/src/credentials/CHIPCert.h index b957e60283b638..6a5ed262a8a1a4 100644 --- a/src/credentials/CHIPCert.h +++ b/src/credentials/CHIPCert.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,15 @@ static constexpr uint32_t kMaxCHIPCertDecodeBufLength = kMaxDERCertLength - Cryp // Muximum number of CASE Authenticated Tags (CAT) in the CHIP certificate subject. static constexpr size_t kMaxSubjectCATAttributeCount = CHIP_CONFIG_CERT_MAX_RDN_ATTRIBUTES - 2; +static constexpr CASEAuthTag kUndefinedCAT = 0; + +struct CATValues +{ + CASEAuthTag val[kMaxSubjectCATAttributeCount]; + + size_t size() const { return ArraySize(val); } +}; +static constexpr CATValues kUndefinedCATs = { { Credentials::kUndefinedCAT } }; /** Data Element Tags for the CHIP Certificate */ @@ -799,16 +809,26 @@ CHIP_ERROR ExtractFabricIdFromCert(const ChipCertificateData & cert, FabricId * */ CHIP_ERROR ExtractNodeIdFabricIdFromOpCert(const ChipCertificateData & opcert, NodeId * nodeId, FabricId * fabricId); +/** + * Extract CASE Authenticated Tags from an operational certificate in ByteSpan TLV-encoded form. + * + * All values in the 'cats' struct will be set either to a valid CAT value or zero (undefined) value. + * + * @return CHIP_ERROR_INVALID_ARGUMENT if the passed-in cert is not NOC. + * @return CHIP_ERROR_BUFFER_TOO_SMALL if there are too many CATs in the NOC + */ +CHIP_ERROR ExtractCATsFromOpCert(const ByteSpan & opcert, CATValues & cats); + /** * Extract CASE Authenticated Tags from an operational certificate that has already been * parsed. * - * All values in the 'cats' array will be set either to a valid CAT value or zero (undefined) value. + * All values in the 'cats' struct will be set either to a valid CAT value or to the kUndefinedCAT value. * * @return CHIP_ERROR_INVALID_ARGUMENT if the passed-in cert is not NOC. * @return CHIP_ERROR_BUFFER_TOO_SMALL if the passed-in CATs array is too small. */ -CHIP_ERROR ExtractCATsFromOpCert(const ChipCertificateData & opcert, uint32_t * cats, uint8_t catsSize); +CHIP_ERROR ExtractCATsFromOpCert(const ChipCertificateData & opcert, CATValues & cats); /** * Extract Node ID and Fabric ID from an operational certificate in ByteSpan TLV-encoded diff --git a/src/credentials/tests/TestChipCert.cpp b/src/credentials/tests/TestChipCert.cpp index 034db14064336c..5a2fd8009a3b73 100644 --- a/src/credentials/tests/TestChipCert.cpp +++ b/src/credentials/tests/TestChipCert.cpp @@ -1113,70 +1113,68 @@ static void TestChipCert_ExtractCATsFromOpCert(nlTestSuite * inSuite, void * inC struct TestCase { uint8_t Cert; - uint32_t ExpectedCAT[kMaxSubjectCATAttributeCount]; + CATValues ExpectedCATs; }; // clang-format off static constexpr TestCase sTestCases[] = { // Cert CATs - // ============================================================= - { TestCert::kNode01_01, { 0, 0, 0 } }, - { TestCert::kNode01_02, { 0, 0, 0 } }, - { TestCert::kNode02_01, { 0, 0, 0 } }, - { TestCert::kNode02_02, { 0, 0, 0 } }, - { TestCert::kNode02_03, { 0xABCD0001, 0, 0 } }, - { TestCert::kNode02_04, { 0xABCE1002, 0xABCD0003, 0 } }, - { TestCert::kNode02_05, { 0xABCD0010, 0xABCE1008, 0 } }, - { TestCert::kNode02_06, { 0, 0, 0 } }, - { TestCert::kNode02_07, { 0, 0, 0 } }, - { TestCert::kNode02_08, { 0xABCF00A0, 0xABCD0020, 0xABCE0100 } }, + // ============================================================================ + { TestCert::kNode01_01, { { kUndefinedCAT, kUndefinedCAT, kUndefinedCAT } } }, + { TestCert::kNode01_02, { { kUndefinedCAT, kUndefinedCAT, kUndefinedCAT } } }, + { TestCert::kNode02_01, { { kUndefinedCAT, kUndefinedCAT, kUndefinedCAT } } }, + { TestCert::kNode02_02, { { kUndefinedCAT, kUndefinedCAT, kUndefinedCAT } } }, + { TestCert::kNode02_03, { { 0xABCD0001, kUndefinedCAT, kUndefinedCAT } } }, + { TestCert::kNode02_04, { { 0xABCE1002, 0xABCD0003, kUndefinedCAT } } }, + { TestCert::kNode02_05, { { 0xABCD0010, 0xABCE1008, kUndefinedCAT } } }, + { TestCert::kNode02_06, { { kUndefinedCAT, kUndefinedCAT, kUndefinedCAT } } }, + { TestCert::kNode02_07, { { kUndefinedCAT, kUndefinedCAT, kUndefinedCAT } } }, + { TestCert::kNode02_08, { { 0xABCF00A0, 0xABCD0020, 0xABCE0100 } } }, }; // clang-format on // Test extraction from the raw ByteSpan form. - ChipCertificateSet certSet; for (auto & testCase : sTestCases) { - CHIP_ERROR err = certSet.Init(1); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - - err = LoadTestCert(certSet, testCase.Cert, sNullLoadFlag, sNullDecodeFlag); + ByteSpan cert; + CHIP_ERROR err = GetTestCert(testCase.Cert, sNullLoadFlag, cert); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - uint32_t cats[kMaxSubjectCATAttributeCount]; - err = ExtractCATsFromOpCert(certSet.GetCertSet()[0], cats, ArraySize(cats)); + CATValues cats; + err = ExtractCATsFromOpCert(cert, cats); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, memcmp(cats, testCase.ExpectedCAT, sizeof(cats)) == 0); - - certSet.Release(); + NL_TEST_ASSERT(inSuite, memcmp(&cats, &testCase.ExpectedCATs, sizeof(cats)) == 0); } - // Error case: trying to extract CAT from Root Cert. + // Test extraction from the parsed form. + ChipCertificateSet certSet; + for (auto & testCase : sTestCases) { CHIP_ERROR err = certSet.Init(1); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - err = LoadTestCert(certSet, TestCert::kRoot01, sNullLoadFlag, sNullDecodeFlag); + err = LoadTestCert(certSet, testCase.Cert, sNullLoadFlag, sNullDecodeFlag); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - uint32_t cats[kMaxSubjectCATAttributeCount]; - err = ExtractCATsFromOpCert(certSet.GetCertSet()[0], cats, ArraySize(cats)); - NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_INVALID_ARGUMENT); + CATValues cats; + err = ExtractCATsFromOpCert(certSet.GetCertSet()[0], cats); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, memcmp(&cats, &testCase.ExpectedCATs, sizeof(cats)) == 0); certSet.Release(); } - // Error case: CAT array is too small. + // Error case: trying to extract CAT from Root Cert. { CHIP_ERROR err = certSet.Init(1); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - err = LoadTestCert(certSet, TestCert::kNode02_08, sNullLoadFlag, sNullDecodeFlag); + err = LoadTestCert(certSet, TestCert::kRoot01, sNullLoadFlag, sNullDecodeFlag); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - uint32_t cats[kMaxSubjectCATAttributeCount - 1]; - err = ExtractCATsFromOpCert(certSet.GetCertSet()[0], cats, ArraySize(cats)); - NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_BUFFER_TOO_SMALL); + CATValues cats; + err = ExtractCATsFromOpCert(certSet.GetCertSet()[0], cats); + NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_INVALID_ARGUMENT); certSet.Release(); } @@ -1276,7 +1274,7 @@ static const nlTest sTests[] = { NL_TEST_DEF("Test CHIP Verify Generated Cert Chain", TestChipCert_VerifyGeneratedCerts), NL_TEST_DEF("Test CHIP Verify Generated Cert Chain No ICA", TestChipCert_VerifyGeneratedCertsNoICA), NL_TEST_DEF("Test extracting PeerId from node certificate", TestChipCert_ExtractPeerId), - NL_TEST_DEF("Test extracting CAST Authenticated Tags from node certificate", TestChipCert_ExtractCATsFromOpCert), + NL_TEST_DEF("Test extracting CASE Authenticated Tags from node certificate", TestChipCert_ExtractCATsFromOpCert), NL_TEST_DEF("Test extracting PublicKey and SKID from chip certificate", TestChipCert_ExtractPublicKeyAndSKID), NL_TEST_SENTINEL() }; diff --git a/src/lib/core/DataModelTypes.h b/src/lib/core/DataModelTypes.h index ef5f98d742f77e..7b4b8e3e2f6e2a 100644 --- a/src/lib/core/DataModelTypes.h +++ b/src/lib/core/DataModelTypes.h @@ -28,6 +28,7 @@ namespace chip { typedef uint8_t ActionId; typedef uint32_t AttributeId; +typedef uint32_t CASEAuthTag; typedef uint32_t ClusterId; typedef uint8_t ClusterStatus; typedef uint32_t CommandId; diff --git a/src/lib/core/NodeId.h b/src/lib/core/NodeId.h index 07e79b01378fb0..f6f62b2b1fa01f 100644 --- a/src/lib/core/NodeId.h +++ b/src/lib/core/NodeId.h @@ -40,11 +40,8 @@ constexpr NodeId kMinTemporaryLocalId = 0xFFFF'FFFE'0000'0000ULL; constexpr NodeId kMaxTemporaryLocalId = 0xFFFF'FFFE'FFFF'FFFEULL; constexpr NodeId kPlaceholderNodeId = 0xFFFF'FFFE'FFFF'FFFFULL; -constexpr NodeId kMinCASEAuthTag1 = 0xFFFF'FFFD'0000'0000ULL; -constexpr NodeId kMaxCASEAuthTag1 = 0xFFFF'FFFD'FFFF'FFFFULL; - -constexpr NodeId kMinCASEAuthTag2 = 0xFFFF'FFFC'0000'0000ULL; -constexpr NodeId kMaxCASEAuthTag2 = 0xFFFF'FFFC'FFFF'FFFFULL; +constexpr NodeId kMinCASEAuthTag = 0xFFFF'FFFD'0000'0000ULL; +constexpr NodeId kMaxCASEAuthTag = 0xFFFF'FFFD'FFFF'FFFFULL; constexpr NodeId kMinPAKEKeyId = 0xFFFF'FFFB'0000'0000ULL; constexpr NodeId kMaxPAKEKeyId = 0xFFFF'FFFB'FFFF'FFFFULL; diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index 14be8805152995..e4722ea380ada6 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -1,3 +1,4 @@ + /* * * Copyright (c) 2021 Project CHIP Authors @@ -91,6 +92,7 @@ static constexpr ExchangeContext::Timeout kSigma_Response_Timeout = System::Cloc CASESession::CASESession() { + SetSecureSessionType(Transport::SecureSession::Type::kCASE); mTrustedRootId = CertificateKeyId(); } @@ -174,8 +176,12 @@ CHIP_ERROR CASESession::ToSerializable(CASESessionSerializable & serializable) serializable.mMessageDigestLen = LittleEndian::HostSwap16(static_cast(sizeof(mMessageDigest))); serializable.mVersion = kCASESessionVersion; serializable.mPeerNodeId = LittleEndian::HostSwap64(peerNodeId); - serializable.mLocalSessionId = LittleEndian::HostSwap16(GetLocalSessionId()); - serializable.mPeerSessionId = LittleEndian::HostSwap16(GetPeerSessionId()); + for (size_t i = 0; i < serializable.mPeerCATs.size(); i++) + { + serializable.mPeerCATs.val[i] = LittleEndian::HostSwap32(GetPeerCATs().val[i]); + } + serializable.mLocalSessionId = LittleEndian::HostSwap16(GetLocalSessionId()); + serializable.mPeerSessionId = LittleEndian::HostSwap16(GetPeerSessionId()); memcpy(serializable.mResumptionId, mResumptionId, sizeof(mResumptionId)); memcpy(serializable.mSharedSecret, mSharedSecret, mSharedSecret.Length()); @@ -198,6 +204,12 @@ CHIP_ERROR CASESession::FromSerializable(const CASESessionSerializable & seriali memcpy(mMessageDigest, serializable.mMessageDigest, length); SetPeerNodeId(LittleEndian::HostSwap64(serializable.mPeerNodeId)); + Credentials::CATValues peerCATs; + for (size_t i = 0; i < serializable.mPeerCATs.size(); i++) + { + peerCATs.val[i] = LittleEndian::HostSwap32(serializable.mPeerCATs.val[i]); + } + SetPeerCATs(peerCATs); SetLocalSessionId(LittleEndian::HostSwap16(serializable.mLocalSessionId)); SetPeerSessionId(LittleEndian::HostSwap16(serializable.mPeerSessionId)); @@ -863,6 +875,11 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) SuccessOrExit(err = decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, TLV::ContextTag(kTag_TBEData_ResumptionID))); SuccessOrExit(err = decryptedDataTlvReader.GetBytes(mResumptionId, static_cast(sizeof(mResumptionId)))); + // Retrieve peer CASE Authenticated Tags (CATs) from peer's NOC. + Credentials::CATValues peerCATs; + SuccessOrExit(err = ExtractCATsFromOpCert(responderNOC, peerCATs)); + SetPeerCATs(peerCATs); + exit: if (err != CHIP_NO_ERROR) { @@ -1120,6 +1137,11 @@ CHIP_ERROR CASESession::HandleSigma3(System::PacketBufferHandle && msg) SuccessOrExit(err = mCommissioningHash.Finish(messageDigestSpan)); + // Retrieve peer CASE Authenticated Tags (CATs) from peer's NOC. + Credentials::CATValues peerCATs; + SuccessOrExit(err = ExtractCATsFromOpCert(initiatorNOC, peerCATs)); + SetPeerCATs(peerCATs); + SendStatusReport(mExchangeCtxt, kProtocolCodeSuccess); // TODO: Set timestamp on the new session, to allow selecting a least-recently-used session for eviction diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h index 71cda76e5eabd7..860f28b9d28a51 100644 --- a/src/protocols/secure_channel/CASESession.h +++ b/src/protocols/secure_channel/CASESession.h @@ -68,6 +68,7 @@ struct CASESessionSerializable uint16_t mMessageDigestLen; uint8_t mMessageDigest[Crypto::kSHA256_Hash_Length]; NodeId mPeerNodeId; + Credentials::CATValues mPeerCATs; uint16_t mLocalSessionId; uint16_t mPeerSessionId; uint8_t mResumptionId[kCASEResumptionIDSize]; diff --git a/src/protocols/secure_channel/PASESession.cpp b/src/protocols/secure_channel/PASESession.cpp index bf777b51ad4711..10340d0ad29ed6 100644 --- a/src/protocols/secure_channel/PASESession.cpp +++ b/src/protocols/secure_channel/PASESession.cpp @@ -70,7 +70,10 @@ using PBKDF2_sha256_crypto = PBKDF2_sha256HSM; using PBKDF2_sha256_crypto = PBKDF2_sha256; #endif -PASESession::PASESession() {} +PASESession::PASESession() +{ + SetSecureSessionType(Transport::SecureSession::Type::kPASE); +} PASESession::~PASESession() { diff --git a/src/protocols/secure_channel/tests/TestCASESession.cpp b/src/protocols/secure_channel/tests/TestCASESession.cpp index 8de873cc2340b8..c925a3ebf64491 100644 --- a/src/protocols/secure_channel/tests/TestCASESession.cpp +++ b/src/protocols/secure_channel/tests/TestCASESession.cpp @@ -153,6 +153,11 @@ void CASE_SecurePairingWaitTest(nlTestSuite * inSuite, void * inContext) TestCASESessionIPK pairing; FabricTable fabrics; + NL_TEST_ASSERT(inSuite, pairing.GetSecureSessionType() == SecureSession::Type::kCASE); + Credentials::CATValues peerCATs; + peerCATs = pairing.GetPeerCATs(); + NL_TEST_ASSERT(inSuite, memcmp(&peerCATs, &kUndefinedCATs, sizeof(Credentials::CATValues)) == 0); + NL_TEST_ASSERT(inSuite, pairing.ListenForSessionEstablishment(0, nullptr, nullptr) == CHIP_ERROR_INVALID_ARGUMENT); NL_TEST_ASSERT(inSuite, pairing.ListenForSessionEstablishment(0, nullptr, &delegate) == CHIP_ERROR_INVALID_ARGUMENT); NL_TEST_ASSERT(inSuite, pairing.ListenForSessionEstablishment(0, &fabrics, &delegate) == CHIP_NO_ERROR); diff --git a/src/protocols/secure_channel/tests/TestPASESession.cpp b/src/protocols/secure_channel/tests/TestPASESession.cpp index 3d21abefeaaf40..ea1409047d6387 100644 --- a/src/protocols/secure_channel/tests/TestPASESession.cpp +++ b/src/protocols/secure_channel/tests/TestPASESession.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -101,6 +101,11 @@ void SecurePairingWaitTest(nlTestSuite * inSuite, void * inContext) TestSecurePairingDelegate delegate; PASESession pairing; + NL_TEST_ASSERT(inSuite, pairing.GetSecureSessionType() == SecureSession::Type::kPASE); + Credentials::CATValues peerCATs; + peerCATs = pairing.GetPeerCATs(); + NL_TEST_ASSERT(inSuite, memcmp(&peerCATs, &Credentials::kUndefinedCATs, sizeof(Credentials::CATValues)) == 0); + gLoopback.Reset(); NL_TEST_ASSERT(inSuite, pairing.WaitForPairing(1234, 500, ByteSpan(nullptr, 0), 0, &delegate) == CHIP_ERROR_INVALID_ARGUMENT); diff --git a/src/transport/PairingSession.h b/src/transport/PairingSession.h index c6cce25ddc720f..fa47f093313454 100644 --- a/src/transport/PairingSession.h +++ b/src/transport/PairingSession.h @@ -30,6 +30,7 @@ #include #include #include +#include namespace chip { @@ -39,11 +40,15 @@ class DLL_EXPORT PairingSession PairingSession() {} virtual ~PairingSession() {} + Transport::SecureSession::Type GetSecureSessionType() const { return mSecureSessionType; } + // TODO: the session should know which peer we are trying to connect to at start // mPeerNodeId should be const and assigned at the construction, such that GetPeerNodeId will never return kUndefinedNodeId, and // SetPeerNodeId is not necessary. NodeId GetPeerNodeId() const { return mPeerNodeId; } + Credentials::CATValues GetPeerCATs() const { return mPeerCATs; } + // TODO: the local key id should be allocateed at start // mLocalSessionId should be const and assigned at the construction, such that GetLocalSessionId will always return a valid key // id , and SetLocalSessionId is not necessary. @@ -98,7 +103,9 @@ class DLL_EXPORT PairingSession virtual const char * GetR2ISessionInfo() const = 0; protected: + void SetSecureSessionType(Transport::SecureSession::Type secureSessionType) { mSecureSessionType = secureSessionType; } void SetPeerNodeId(NodeId peerNodeId) { mPeerNodeId = peerNodeId; } + void SetPeerCATs(Credentials::CATValues peerCATs) { mPeerCATs = peerCATs; } void SetPeerSessionId(uint16_t id) { mPeerSessionId.SetValue(id); } void SetLocalSessionId(uint16_t id) { mLocalSessionId = id; } void SetPeerAddress(const Transport::PeerAddress & address) { mPeerAddress = address; } @@ -157,14 +164,18 @@ class DLL_EXPORT PairingSession // TODO: remove Clear, we should create a new instance instead reset the old instance. void Clear() { - mPeerNodeId = kUndefinedNodeId; - mPeerAddress = Transport::PeerAddress::Uninitialized(); + mSecureSessionType = Transport::SecureSession::Type::kUndefined; + mPeerNodeId = kUndefinedNodeId; + mPeerCATs = Credentials::kUndefinedCATs; + mPeerAddress = Transport::PeerAddress::Uninitialized(); mPeerSessionId.ClearValue(); mLocalSessionId = kInvalidKeyId; } private: - NodeId mPeerNodeId = kUndefinedNodeId; + Transport::SecureSession::Type mSecureSessionType = Transport::SecureSession::Type::kUndefined; + NodeId mPeerNodeId = kUndefinedNodeId; + Credentials::CATValues mPeerCATs = Credentials::kUndefinedCATs; // TODO: the local key id should be allocateed at start // then we can remove kInvalidKeyId diff --git a/src/transport/SecureSession.h b/src/transport/SecureSession.h index 733f1a9ded6bcd..3627d4da7d81a5 100644 --- a/src/transport/SecureSession.h +++ b/src/transport/SecureSession.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 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. @@ -22,6 +22,7 @@ #pragma once #include +#include #include #include #include @@ -38,8 +39,10 @@ static constexpr uint32_t kUndefinedMessageIndex = UINT32_MAX; * Defines state of a peer connection at a transport layer. * * Information contained within the state: + * - SecureSessionType represents CASE or PASE session * - PeerAddress represents how to talk to the peer * - PeerNodeId is the unique ID of the peer + * - PeerCATs represents CASE Authenticated Tags * - SendMessageIndex is an ever increasing index for sending messages * - LastActivityTime is a monotonic timestamp of when this connection was * last used. Inactive connections can expire. @@ -50,10 +53,23 @@ static constexpr uint32_t kUndefinedMessageIndex = UINT32_MAX; class SecureSession { public: - SecureSession(uint16_t localSessionId, NodeId peerNodeId, uint16_t peerSessionId, FabricIndex fabric, - const ReliableMessageProtocolConfig & config, System::Clock::Timestamp currentTime) : - mPeerNodeId(peerNodeId), - mLocalSessionId(localSessionId), mPeerSessionId(peerSessionId), mFabric(fabric), mMRPConfig(config) + /** + * @brief + * Defines SecureSession Type. Currently supported types are PASE and CASE. + */ + enum class Type : uint8_t + { + kUndefined = 0, + kPASE = 1, + kCASE = 2, + }; + + SecureSession(Type secureSessionType, uint16_t localSessionId, NodeId peerNodeId, Credentials::CATValues peerCATs, + uint16_t peerSessionId, FabricIndex fabric, const ReliableMessageProtocolConfig & config, + System::Clock::Timestamp currentTime) : + mSecureSessionType(secureSessionType), + mPeerNodeId(peerNodeId), mPeerCATs(peerCATs), mLocalSessionId(localSessionId), mPeerSessionId(peerSessionId), + mFabric(fabric), mMRPConfig(config) { SetLastActivityTime(currentTime); } @@ -67,7 +83,9 @@ class SecureSession PeerAddress & GetPeerAddress() { return mPeerAddress; } void SetPeerAddress(const PeerAddress & address) { mPeerAddress = address; } + Type GetSecureSessionType() const { return mSecureSessionType; } NodeId GetPeerNodeId() const { return mPeerNodeId; } + Credentials::CATValues GetPeerCATs() const { return mPeerCATs; } void SetMRPConfig(const ReliableMessageProtocolConfig & config) { mMRPConfig = config; } @@ -97,7 +115,9 @@ class SecureSession SessionMessageCounter & GetSessionMessageCounter() { return mSessionMessageCounter; } private: + const Type mSecureSessionType; const NodeId mPeerNodeId; + const Credentials::CATValues mPeerCATs; const uint16_t mLocalSessionId; const uint16_t mPeerSessionId; const FabricIndex mFabric; diff --git a/src/transport/SecureSessionTable.h b/src/transport/SecureSessionTable.h index 85a709bc81b027..27b3ad673dbeb9 100644 --- a/src/transport/SecureSessionTable.h +++ b/src/transport/SecureSessionTable.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 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. @@ -44,8 +44,10 @@ class SecureSessionTable /** * Allocates a new secure session out of the internal resource pool. * + * @param sessionType secure session type * @param localSessionId represents the encryption key ID assigned by local node * @param peerNodeId represents peer Node's ID + * @param peerCATs represents peer CASE Authenticated Tags * @param peerSessionId represents the encryption key ID assigned by peer node * @param fabric represents fabric ID for the session * @@ -55,10 +57,11 @@ class SecureSessionTable * has been reached (with CHIP_ERROR_NO_MEMORY). */ CHECK_RETURN_VALUE - SecureSession * CreateNewSecureSession(uint16_t localSessionId, NodeId peerNodeId, uint16_t peerSessionId, FabricIndex fabric, + SecureSession * CreateNewSecureSession(SecureSession::Type secureSessionType, uint16_t localSessionId, NodeId peerNodeId, + Credentials::CATValues peerCATs, uint16_t peerSessionId, FabricIndex fabric, const ReliableMessageProtocolConfig & config) { - return mEntries.CreateObject(localSessionId, peerNodeId, peerSessionId, fabric, config, + return mEntries.CreateObject(secureSessionType, localSessionId, peerNodeId, peerCATs, peerSessionId, fabric, config, mTimeSource.GetMonotonicTimestamp()); } diff --git a/src/transport/SessionManager.cpp b/src/transport/SessionManager.cpp index 65d3545bb7e7c5..549214193861a7 100644 --- a/src/transport/SessionManager.cpp +++ b/src/transport/SessionManager.cpp @@ -294,7 +294,8 @@ CHIP_ERROR SessionManager::NewPairing(const Optional & p ChipLogDetail(Inet, "New secure session created for device 0x" ChipLogFormatX64 ", key %d!!", ChipLogValueX64(peerNodeId), peerSessionId); - session = mSecureSessions.CreateNewSecureSession(localSessionId, peerNodeId, peerSessionId, fabric, pairing->GetMRPConfig()); + session = mSecureSessions.CreateNewSecureSession(pairing->GetSecureSessionType(), localSessionId, peerNodeId, + pairing->GetPeerCATs(), peerSessionId, fabric, pairing->GetMRPConfig()); ReturnErrorCodeIf(session == nullptr, CHIP_ERROR_NO_MEMORY); if (peerAddr.HasValue() && peerAddr.Value().GetIPAddress() != Inet::IPAddress::Any) diff --git a/src/transport/tests/TestPeerConnections.cpp b/src/transport/tests/TestPeerConnections.cpp index d8e3203be482e4..bee5e9cdfb7497 100644 --- a/src/transport/tests/TestPeerConnections.cpp +++ b/src/transport/tests/TestPeerConnections.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -52,24 +52,43 @@ const NodeId kPeer1NodeId = 123; const NodeId kPeer2NodeId = 6; const NodeId kPeer3NodeId = 81; +const SecureSession::Type kPeer1SessionType = SecureSession::Type::kCASE; +const SecureSession::Type kPeer2SessionType = SecureSession::Type::kCASE; +const SecureSession::Type kPeer3SessionType = SecureSession::Type::kPASE; + +const Credentials::CATValues kPeer1CATs = { { 0xABCD0001, 0xABCE0100, 0xABCD0020 } }; +const Credentials::CATValues kPeer2CATs = { { 0xABCD0012, Credentials::kUndefinedCAT, Credentials::kUndefinedCAT } }; +const Credentials::CATValues kPeer3CATs = Credentials::kUndefinedCATs; + void TestBasicFunctionality(nlTestSuite * inSuite, void * inContext) { SecureSession * statePtr; SecureSessionTable<2, Time::Source::kTest> connections; connections.GetTimeSource().SetMonotonicTimestamp(100_ms64); + Credentials::CATValues peerCATs; // Node ID 1, peer key 1, local key 2 - statePtr = connections.CreateNewSecureSession(2, kPeer1NodeId, 1, 0 /* fabricIndex */, gDefaultMRPConfig); + statePtr = connections.CreateNewSecureSession(kPeer1SessionType, 2, kPeer1NodeId, kPeer1CATs, 1, 0 /* fabricIndex */, + gDefaultMRPConfig); NL_TEST_ASSERT(inSuite, statePtr != nullptr); + NL_TEST_ASSERT(inSuite, statePtr->GetSecureSessionType() == kPeer1SessionType); + NL_TEST_ASSERT(inSuite, statePtr->GetPeerNodeId() == kPeer1NodeId); + peerCATs = statePtr->GetPeerCATs(); + NL_TEST_ASSERT(inSuite, memcmp(&peerCATs, &kPeer1CATs, sizeof(Credentials::CATValues)) == 0); // Node ID 2, peer key 3, local key 4 - statePtr = connections.CreateNewSecureSession(4, kPeer2NodeId, 3, 0 /* fabricIndex */, gDefaultMRPConfig); + statePtr = connections.CreateNewSecureSession(kPeer2SessionType, 4, kPeer2NodeId, kPeer2CATs, 3, 0 /* fabricIndex */, + gDefaultMRPConfig); NL_TEST_ASSERT(inSuite, statePtr != nullptr); + NL_TEST_ASSERT(inSuite, statePtr->GetSecureSessionType() == kPeer2SessionType); NL_TEST_ASSERT(inSuite, statePtr->GetPeerNodeId() == kPeer2NodeId); NL_TEST_ASSERT(inSuite, statePtr->GetLastActivityTime() == 100_ms64); + peerCATs = statePtr->GetPeerCATs(); + NL_TEST_ASSERT(inSuite, memcmp(&peerCATs, &kPeer2CATs, sizeof(Credentials::CATValues)) == 0); // Insufficient space for new connections. Object is max size 2 - statePtr = connections.CreateNewSecureSession(6, kPeer3NodeId, 5, 0 /* fabricIndex */, gDefaultMRPConfig); + statePtr = connections.CreateNewSecureSession(kPeer3SessionType, 6, kPeer3NodeId, kPeer3CATs, 5, 0 /* fabricIndex */, + gDefaultMRPConfig); NL_TEST_ASSERT(inSuite, statePtr == nullptr); } @@ -79,14 +98,16 @@ void TestFindByKeyId(nlTestSuite * inSuite, void * inContext) SecureSessionTable<2, Time::Source::kTest> connections; // Node ID 1, peer key 1, local key 2 - statePtr = connections.CreateNewSecureSession(2, kPeer1NodeId, 1, 0 /* fabricIndex */, gDefaultMRPConfig); + statePtr = connections.CreateNewSecureSession(kPeer1SessionType, 2, kPeer1NodeId, kPeer1CATs, 1, 0 /* fabricIndex */, + gDefaultMRPConfig); NL_TEST_ASSERT(inSuite, statePtr != nullptr); NL_TEST_ASSERT(inSuite, !connections.FindSecureSessionByLocalKey(1)); NL_TEST_ASSERT(inSuite, connections.FindSecureSessionByLocalKey(2)); // Node ID 2, peer key 3, local key 4 - statePtr = connections.CreateNewSecureSession(4, kPeer2NodeId, 3, 0 /* fabricIndex */, gDefaultMRPConfig); + statePtr = connections.CreateNewSecureSession(kPeer2SessionType, 4, kPeer2NodeId, kPeer2CATs, 3, 0 /* fabricIndex */, + gDefaultMRPConfig); NL_TEST_ASSERT(inSuite, statePtr != nullptr); NL_TEST_ASSERT(inSuite, !connections.FindSecureSessionByLocalKey(3)); @@ -109,19 +130,22 @@ void TestExpireConnections(nlTestSuite * inSuite, void * inContext) connections.GetTimeSource().SetMonotonicTimestamp(100_ms64); // Node ID 1, peer key 1, local key 2 - statePtr = connections.CreateNewSecureSession(2, kPeer1NodeId, 1, 0 /* fabricIndex */, gDefaultMRPConfig); + statePtr = connections.CreateNewSecureSession(kPeer1SessionType, 2, kPeer1NodeId, kPeer1CATs, 1, 0 /* fabricIndex */, + gDefaultMRPConfig); NL_TEST_ASSERT(inSuite, statePtr != nullptr); statePtr->SetPeerAddress(kPeer1Addr); connections.GetTimeSource().SetMonotonicTimestamp(200_ms64); // Node ID 2, peer key 3, local key 4 - statePtr = connections.CreateNewSecureSession(4, kPeer2NodeId, 3, 0 /* fabricIndex */, gDefaultMRPConfig); + statePtr = connections.CreateNewSecureSession(kPeer2SessionType, 4, kPeer2NodeId, kPeer2CATs, 3, 0 /* fabricIndex */, + gDefaultMRPConfig); NL_TEST_ASSERT(inSuite, statePtr != nullptr); statePtr->SetPeerAddress(kPeer2Addr); // cannot add before expiry connections.GetTimeSource().SetMonotonicTimestamp(300_ms64); - statePtr = connections.CreateNewSecureSession(6, kPeer3NodeId, 5, 0 /* fabricIndex */, gDefaultMRPConfig); + statePtr = connections.CreateNewSecureSession(kPeer3SessionType, 6, kPeer3NodeId, kPeer3CATs, 5, 0 /* fabricIndex */, + gDefaultMRPConfig); NL_TEST_ASSERT(inSuite, statePtr == nullptr); // at time 300, this expires ip addr 1 @@ -138,7 +162,8 @@ void TestExpireConnections(nlTestSuite * inSuite, void * inContext) // now that the connections were expired, we can add peer3 connections.GetTimeSource().SetMonotonicTimestamp(300_ms64); // Node ID 3, peer key 5, local key 6 - statePtr = connections.CreateNewSecureSession(6, kPeer3NodeId, 5, 0 /* fabricIndex */, gDefaultMRPConfig); + statePtr = connections.CreateNewSecureSession(kPeer3SessionType, 6, kPeer3NodeId, kPeer3CATs, 5, 0 /* fabricIndex */, + gDefaultMRPConfig); NL_TEST_ASSERT(inSuite, statePtr != nullptr); statePtr->SetPeerAddress(kPeer3Addr); @@ -169,7 +194,8 @@ void TestExpireConnections(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, !connections.FindSecureSessionByLocalKey(6)); // Node ID 1, peer key 1, local key 2 - statePtr = connections.CreateNewSecureSession(2, kPeer1NodeId, 1, 0 /* fabricIndex */, gDefaultMRPConfig); + statePtr = connections.CreateNewSecureSession(kPeer1SessionType, 2, kPeer1NodeId, kPeer1CATs, 1, 0 /* fabricIndex */, + gDefaultMRPConfig); NL_TEST_ASSERT(inSuite, statePtr != nullptr); NL_TEST_ASSERT(inSuite, connections.FindSecureSessionByLocalKey(2)); NL_TEST_ASSERT(inSuite, connections.FindSecureSessionByLocalKey(4));