Skip to content

Commit

Permalink
ACLs: Added SessionType and CATs Parameters to SecureSession and Pair…
Browse files Browse the repository at this point in the history
…ingSession Classes (#12027)
  • Loading branch information
emargolis authored and pull[bot] committed Jan 24, 2022
1 parent b1ad93c commit e3bf329
Show file tree
Hide file tree
Showing 15 changed files with 201 additions and 72 deletions.
26 changes: 21 additions & 5 deletions src/credentials/CHIPCert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<CertDecodeFlags>()));

return ExtractCATsFromOpCert(certData, cats);
}

CHIP_ERROR ExtractCATsFromOpCert(const ChipCertificateData & opcert, CATValues & cats)
{
uint8_t catCount = 0;
uint8_t certType;
Expand All @@ -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<uint32_t>(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<CASEAuthTag>(rdn.mChipVal), CHIP_ERROR_INVALID_ARGUMENT);
cats.val[catCount++] = static_cast<CASEAuthTag>(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;
Expand Down
24 changes: 22 additions & 2 deletions src/credentials/CHIPCert.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <lib/asn1/ASN1.h>
#include <lib/core/CHIPConfig.h>
#include <lib/core/CHIPTLV.h>
#include <lib/core/DataModelTypes.h>
#include <lib/core/PeerId.h>
#include <lib/support/BitFlags.h>
#include <lib/support/DLLUtil.h>
Expand All @@ -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
*/
Expand Down Expand Up @@ -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
Expand Down
64 changes: 31 additions & 33 deletions src/credentials/tests/TestChipCert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down Expand Up @@ -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()
};
Expand Down
1 change: 1 addition & 0 deletions src/lib/core/DataModelTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 2 additions & 5 deletions src/lib/core/NodeId.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
26 changes: 24 additions & 2 deletions src/protocols/secure_channel/CASESession.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

/*
*
* Copyright (c) 2021 Project CHIP Authors
Expand Down Expand Up @@ -91,6 +92,7 @@ static constexpr ExchangeContext::Timeout kSigma_Response_Timeout = System::Cloc

CASESession::CASESession()
{
SetSecureSessionType(Transport::SecureSession::Type::kCASE);
mTrustedRootId = CertificateKeyId();
}

Expand Down Expand Up @@ -174,8 +176,12 @@ CHIP_ERROR CASESession::ToSerializable(CASESessionSerializable & serializable)
serializable.mMessageDigestLen = LittleEndian::HostSwap16(static_cast<uint16_t>(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());
Expand All @@ -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));

Expand Down Expand Up @@ -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<uint32_t>(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)
{
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/protocols/secure_channel/CASESession.h
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
5 changes: 4 additions & 1 deletion src/protocols/secure_channel/PASESession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
5 changes: 5 additions & 0 deletions src/protocols/secure_channel/tests/TestCASESession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 6 additions & 1 deletion src/protocols/secure_channel/tests/TestPASESession.cpp
Original file line number Diff line number Diff line change
@@ -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");
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit e3bf329

Please sign in to comment.