Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ACLs: Added SessionType and CATs Parameters #12027

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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