diff --git a/src/access/AccessControl.cpp b/src/access/AccessControl.cpp index 6c98504cb59eee..257d866d150914 100644 --- a/src/access/AccessControl.cpp +++ b/src/access/AccessControl.cpp @@ -20,7 +20,9 @@ namespace { +using chip::CATValues; using chip::FabricIndex; +using chip::NodeId; using namespace chip::Access; AccessControl defaultAccessControl; @@ -115,12 +117,46 @@ CHIP_ERROR AccessControl::Check(const SubjectDescriptor & subjectDescriptor, con { NodeId subject = kUndefinedNodeId; ReturnErrorOnFailure(entry.GetSubject(i, subject)); - if (subject == subjectDescriptor.subjects[0]) + if (IsOperationalNodeId(subject)) { - subjectMatched = true; - break; + if (subject == subjectDescriptor.subject) + { + subjectMatched = true; + break; + } + } + else if (IsGroupId(subject)) + { + VerifyOrReturnError(authMode == AuthMode::kGroup, CHIP_ERROR_INVALID_ARGUMENT); + if (subject == subjectDescriptor.subject) + { + subjectMatched = true; + break; + } + } + // TODO: Add the implicit admit for PASE after the spec is updated. + else if (IsPAKEKeyId(subject)) + { + VerifyOrReturnError(authMode == AuthMode::kPase, CHIP_ERROR_INVALID_ARGUMENT); + if (subject == subjectDescriptor.subject) + { + subjectMatched = true; + break; + } + } + else if (IsCASEAuthTag(subject)) + { + VerifyOrReturnError(authMode == AuthMode::kCase, CHIP_ERROR_INVALID_ARGUMENT); + if (subjectDescriptor.cats.CheckSubjectAgainstCATs(subject)) + { + subjectMatched = true; + break; + } + } + else + { + return CHIP_ERROR_INVALID_ARGUMENT; } - // TODO: check against CATs in subject descriptor } if (!subjectMatched) { diff --git a/src/access/SubjectDescriptor.h b/src/access/SubjectDescriptor.h index d9f7664c94aafe..ec6abec0b38a30 100644 --- a/src/access/SubjectDescriptor.h +++ b/src/access/SubjectDescriptor.h @@ -20,6 +20,7 @@ #include "AuthMode.h" +#include #include #include @@ -36,9 +37,11 @@ struct SubjectDescriptor // NOTE: due to packing there should be free bytes here - // Holds subjects according to auth mode, and the latter two are only valid - // if auth mode is CASE. - NodeId subjects[3] = { kUndefinedNodeId, kUndefinedNodeId, kUndefinedNodeId }; + // Holds subject according to auth mode. + NodeId subject = kUndefinedNodeId; + + // CASE Authenticated Tags (CATs) only valid if auth mode is CASE. + CATValues cats; }; } // namespace Access diff --git a/src/access/tests/TestAccessControl.cpp b/src/access/tests/TestAccessControl.cpp index f1eace61c9f085..19c55f282eb790 100644 --- a/src/access/tests/TestAccessControl.cpp +++ b/src/access/tests/TestAccessControl.cpp @@ -47,6 +47,24 @@ constexpr NodeId kPaseVerifier1 = 0xFFFFFFFB0000'0001; constexpr NodeId kPaseVerifier3 = 0xFFFFFFFB0000'0003; constexpr NodeId kPaseVerifier5 = 0xFFFFFFFB0000'0005; +constexpr NodeId kOperationalNodeId0 = 0x0123456789ABCDEF; +constexpr NodeId kOperationalNodeId1 = 0x1234567812345678; +constexpr NodeId kOperationalNodeId2 = 0x1122334455667788; +constexpr NodeId kOperationalNodeId3 = 0x1111111111111111; +constexpr NodeId kOperationalNodeId4 = 0x2222222222222222; + +constexpr CASEAuthTag kCASEAuthTag0 = 0x0001'0001; +constexpr CASEAuthTag kCASEAuthTag1 = 0x0002'0001; +constexpr CASEAuthTag kCASEAuthTag2 = 0xABCD'0002; +constexpr CASEAuthTag kCASEAuthTag3 = 0xABCD'0008; +constexpr CASEAuthTag kCASEAuthTag4 = 0xABCD'ABCD; + +constexpr NodeId kCASEAuthTagAsNodeId0 = kMinCASEAuthTag | kCASEAuthTag0; +constexpr NodeId kCASEAuthTagAsNodeId1 = kMinCASEAuthTag | kCASEAuthTag1; +constexpr NodeId kCASEAuthTagAsNodeId2 = kMinCASEAuthTag | kCASEAuthTag2; +constexpr NodeId kCASEAuthTagAsNodeId3 = kMinCASEAuthTag | kCASEAuthTag3; +constexpr NodeId kCASEAuthTagAsNodeId4 = kMinCASEAuthTag | kCASEAuthTag4; + constexpr NodeId kGroup2 = 0xFFFFFFFFFFFF'0002; constexpr NodeId kGroup4 = 0xFFFFFFFFFFFF'0004; constexpr NodeId kGroup6 = 0xFFFFFFFFFFFF'0006; @@ -65,9 +83,9 @@ constexpr NodeId subjects[][3] = { { kPaseVerifier5, }, { - 0x0123456789ABCDEF, // CASE node - 0xFFFFFFFD'00000001, // CAT1 - 0xFFFFFFFC'00000002, // CAT2 + kOperationalNodeId0, + kCASEAuthTagAsNodeId1, + kCASEAuthTagAsNodeId2, }, { kGroup4, @@ -282,7 +300,7 @@ constexpr EntryData entryData1[] = { .fabricIndex = 1, .privilege = Privilege::kAdminister, .authMode = AuthMode::kCase, - .subjects = { 0x1111111111111111 }, + .subjects = { kOperationalNodeId3 }, }, { .fabricIndex = 1, @@ -293,7 +311,7 @@ constexpr EntryData entryData1[] = { .fabricIndex = 2, .privilege = Privilege::kAdminister, .authMode = AuthMode::kCase, - .subjects = { 0x2222222222222222 }, + .subjects = { kOperationalNodeId4 }, }, { .fabricIndex = 1, @@ -317,8 +335,33 @@ constexpr EntryData entryData1[] = { { .flags = Target::kCluster, .cluster = kOnOffCluster }, { .flags = Target::kEndpoint, .endpoint = 2 } }, }, + // entry 6 + { + .fabricIndex = 1, + .privilege = Privilege::kAdminister, + .authMode = AuthMode::kCase, + .subjects = { kCASEAuthTagAsNodeId0 }, + }, + // entry 7 + { + .fabricIndex = 2, + .privilege = Privilege::kManage, + .authMode = AuthMode::kCase, + .subjects = { kCASEAuthTagAsNodeId3, kCASEAuthTagAsNodeId1 }, + .targets = { { .flags = Target::kCluster, .cluster = kOnOffCluster } }, + }, + // entry 8 + { + .fabricIndex = 2, + .privilege = Privilege::kOperate, + .authMode = AuthMode::kCase, + .subjects = { kCASEAuthTagAsNodeId4, kCASEAuthTagAsNodeId1 }, + .targets = { { .flags = Target::kCluster, .cluster = kLevelControlCluster } }, + }, }; +constexpr size_t entryData1Count = ArraySize(entryData1); + struct CheckData { SubjectDescriptor subjectDescriptor; @@ -327,321 +370,285 @@ struct CheckData bool allow; }; -constexpr CheckData checkData1[] = -{ +constexpr CheckData checkData1[] = { // Checks for entry 0 - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subjects = { 0x1111111111111111 }, }, - .requestPath = { .cluster = kAccessControlCluster, .endpoint = 0 }, - .privilege = Privilege::kAdminister, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subjects = { 0x1111111111111111 }, }, - .requestPath = { .cluster = 1, .endpoint = 2 }, - .privilege = Privilege::kManage, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subjects = { 0x1111111111111111 }, }, - .requestPath = { .cluster = 3, .endpoint = 4 }, - .privilege = Privilege::kOperate, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subjects = { 0x1111111111111111 }, }, - .requestPath = { .cluster = 5, .endpoint = 6 }, - .privilege = Privilege::kView, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subjects = { 0x1111111111111111 }, }, - .requestPath = { .cluster = 7, .endpoint = 8 }, - .privilege = Privilege::kProxyView, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subjects = { 0x1111111111111111 }, }, - .requestPath = { .cluster = 1, .endpoint = 2 }, - .privilege = Privilege::kAdminister, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kPase, .subjects = { 0x1111111111111111 }, }, - .requestPath = { .cluster = 1, .endpoint = 2 }, - .privilege = Privilege::kAdminister, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kGroup, .subjects = { 0x1111111111111111 }, }, - .requestPath = { .cluster = 1, .endpoint = 2 }, - .privilege = Privilege::kAdminister, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subjects = { 0x2222222222222222 }, }, - .requestPath = { .cluster = 1, .endpoint = 2 }, - .privilege = Privilege::kAdminister, - .allow = false - }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId3 }, + .requestPath = { .cluster = kAccessControlCluster, .endpoint = 0 }, + .privilege = Privilege::kAdminister, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId3 }, + .requestPath = { .cluster = 1, .endpoint = 2 }, + .privilege = Privilege::kManage, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId3 }, + .requestPath = { .cluster = 3, .endpoint = 4 }, + .privilege = Privilege::kOperate, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId3 }, + .requestPath = { .cluster = 5, .endpoint = 6 }, + .privilege = Privilege::kView, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId3 }, + .requestPath = { .cluster = 7, .endpoint = 8 }, + .privilege = Privilege::kProxyView, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kOperationalNodeId3 }, + .requestPath = { .cluster = 1, .endpoint = 2 }, + .privilege = Privilege::kAdminister, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kPase, .subject = kOperationalNodeId3 }, + .requestPath = { .cluster = 1, .endpoint = 2 }, + .privilege = Privilege::kAdminister, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kGroup, .subject = kOperationalNodeId3 }, + .requestPath = { .cluster = 1, .endpoint = 2 }, + .privilege = Privilege::kAdminister, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId4 }, + .requestPath = { .cluster = 1, .endpoint = 2 }, + .privilege = Privilege::kAdminister, + .allow = false }, // Checks for entry 1 - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subjects = { 0x1234567812345678 }, }, - .requestPath = { .cluster = 11, .endpoint = 13 }, - .privilege = Privilege::kView, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subjects = { 0x1234567812345678 }, }, - .requestPath = { .cluster = 11, .endpoint = 13 }, - .privilege = Privilege::kOperate, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subjects = { 0x1234567812345678 }, }, - .requestPath = { .cluster = 11, .endpoint = 13 }, - .privilege = Privilege::kView, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kPase, .subjects = { 0x1234567812345678 }, }, - .requestPath = { .cluster = 11, .endpoint = 13 }, - .privilege = Privilege::kView, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kGroup, .subjects = { 0x1234567812345678 }, }, - .requestPath = { .cluster = 11, .endpoint = 13 }, - .privilege = Privilege::kView, - .allow = false - }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, + .requestPath = { .cluster = 11, .endpoint = 13 }, + .privilege = Privilege::kView, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, + .requestPath = { .cluster = 11, .endpoint = 13 }, + .privilege = Privilege::kOperate, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, + .requestPath = { .cluster = 11, .endpoint = 13 }, + .privilege = Privilege::kView, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kPase, .subject = kOperationalNodeId1 }, + .requestPath = { .cluster = 11, .endpoint = 13 }, + .privilege = Privilege::kView, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kGroup, .subject = kOperationalNodeId1 }, + .requestPath = { .cluster = 11, .endpoint = 13 }, + .privilege = Privilege::kView, + .allow = false }, // Checks for entry 2 - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subjects = { 0x2222222222222222 }, }, - .requestPath = { .cluster = kAccessControlCluster, .endpoint = 0 }, - .privilege = Privilege::kAdminister, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subjects = { 0x2222222222222222 }, }, - .requestPath = { .cluster = 1, .endpoint = 2 }, - .privilege = Privilege::kManage, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subjects = { 0x2222222222222222 }, }, - .requestPath = { .cluster = 3, .endpoint = 4 }, - .privilege = Privilege::kOperate, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subjects = { 0x2222222222222222 }, }, - .requestPath = { .cluster = 5, .endpoint = 6 }, - .privilege = Privilege::kView, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subjects = { 0x2222222222222222 }, }, - .requestPath = { .cluster = 7, .endpoint = 8 }, - .privilege = Privilege::kProxyView, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subjects = { 0x2222222222222222 }, }, - .requestPath = { .cluster = 1, .endpoint = 2 }, - .privilege = Privilege::kAdminister, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subjects = { 0x2222222222222222 }, }, - .requestPath = { .cluster = 1, .endpoint = 2 }, - .privilege = Privilege::kAdminister, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subjects = { 0x2222222222222222 }, }, - .requestPath = { .cluster = 1, .endpoint = 2 }, - .privilege = Privilege::kAdminister, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subjects = { 0x1111111111111111 }, }, - .requestPath = { .cluster = 1, .endpoint = 2 }, - .privilege = Privilege::kAdminister, - .allow = false - }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kOperationalNodeId4 }, + .requestPath = { .cluster = kAccessControlCluster, .endpoint = 0 }, + .privilege = Privilege::kAdminister, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kOperationalNodeId4 }, + .requestPath = { .cluster = 1, .endpoint = 2 }, + .privilege = Privilege::kManage, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kOperationalNodeId4 }, + .requestPath = { .cluster = 3, .endpoint = 4 }, + .privilege = Privilege::kOperate, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kOperationalNodeId4 }, + .requestPath = { .cluster = 5, .endpoint = 6 }, + .privilege = Privilege::kView, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kOperationalNodeId4 }, + .requestPath = { .cluster = 7, .endpoint = 8 }, + .privilege = Privilege::kProxyView, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId4 }, + .requestPath = { .cluster = 1, .endpoint = 2 }, + .privilege = Privilege::kAdminister, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subject = kOperationalNodeId4 }, + .requestPath = { .cluster = 1, .endpoint = 2 }, + .privilege = Privilege::kAdminister, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subject = kOperationalNodeId4 }, + .requestPath = { .cluster = 1, .endpoint = 2 }, + .privilege = Privilege::kAdminister, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kOperationalNodeId3 }, + .requestPath = { .cluster = 1, .endpoint = 2 }, + .privilege = Privilege::kAdminister, + .allow = false }, // Checks for entry 3 - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subjects = { 0x1234567812345678 }, }, - .requestPath = { .cluster = kOnOffCluster, .endpoint = 11 }, - .privilege = Privilege::kOperate, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subjects = { 0x1122334455667788 }, }, - .requestPath = { .cluster = kOnOffCluster, .endpoint = 13 }, - .privilege = Privilege::kOperate, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subjects = { 0x1234567812345678 }, }, - .requestPath = { .cluster = kOnOffCluster, .endpoint = 11 }, - .privilege = Privilege::kOperate, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kPase, .subjects = { 0x1234567812345678 }, }, - .requestPath = { .cluster = kOnOffCluster, .endpoint = 11 }, - .privilege = Privilege::kOperate, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subjects = { 0x1234567812345678 }, }, - .requestPath = { .cluster = 123, .endpoint = 11 }, - .privilege = Privilege::kOperate, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subjects = { 0x1234567812345678 }, }, - .requestPath = { .cluster = kOnOffCluster, .endpoint = 11 }, - .privilege = Privilege::kManage, - .allow = false - }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 11 }, + .privilege = Privilege::kOperate, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId2 }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 13 }, + .privilege = Privilege::kOperate, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 11 }, + .privilege = Privilege::kOperate, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kPase, .subject = kOperationalNodeId1 }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 11 }, + .privilege = Privilege::kOperate, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, + .requestPath = { .cluster = 123, .endpoint = 11 }, + .privilege = Privilege::kOperate, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 11 }, + .privilege = Privilege::kManage, + .allow = false }, // Checks for entry 4 - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subjects = { kPaseVerifier1 }, }, - .requestPath = { .cluster = kOnOffCluster, .endpoint = 2 }, - .privilege = Privilege::kManage, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kPase, .subjects = { kPaseVerifier1 }, }, - .requestPath = { .cluster = kOnOffCluster, .endpoint = 2 }, - .privilege = Privilege::kManage, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subjects = { kPaseVerifier1 }, }, - .requestPath = { .cluster = kOnOffCluster, .endpoint = 2 }, - .privilege = Privilege::kManage, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subjects = { kPaseVerifier1 }, }, - .requestPath = { .cluster = kOnOffCluster, .endpoint = 2 }, - .privilege = Privilege::kManage, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subjects = { kPaseVerifier0 }, }, - .requestPath = { .cluster = kOnOffCluster, .endpoint = 2 }, - .privilege = Privilege::kManage, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subjects = { kPaseVerifier3 }, }, - .requestPath = { .cluster = kOnOffCluster, .endpoint = 2 }, - .privilege = Privilege::kManage, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subjects = { kPaseVerifier1 }, }, - .requestPath = { .cluster = kLevelControlCluster, .endpoint = 2 }, - .privilege = Privilege::kManage, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subjects = { kPaseVerifier1 }, }, - .requestPath = { .cluster = kOnOffCluster, .endpoint = 1 }, - .privilege = Privilege::kManage, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subjects = { kPaseVerifier1 }, }, - .requestPath = { .cluster = kOnOffCluster, .endpoint = 2 }, - .privilege = Privilege::kAdminister, - .allow = false - }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subject = kPaseVerifier1 }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 2 }, + .privilege = Privilege::kManage, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kPase, .subject = kPaseVerifier1 }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 2 }, + .privilege = Privilege::kManage, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kPaseVerifier1 }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 2 }, + .privilege = Privilege::kManage, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subject = kPaseVerifier1 }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 2 }, + .privilege = Privilege::kManage, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subject = kPaseVerifier0 }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 2 }, + .privilege = Privilege::kManage, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subject = kPaseVerifier3 }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 2 }, + .privilege = Privilege::kManage, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subject = kPaseVerifier1 }, + .requestPath = { .cluster = kLevelControlCluster, .endpoint = 2 }, + .privilege = Privilege::kManage, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subject = kPaseVerifier1 }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 1 }, + .privilege = Privilege::kManage, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subject = kPaseVerifier1 }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 2 }, + .privilege = Privilege::kAdminister, + .allow = false }, // Checks for entry 5 - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subjects = { kGroup2 }, }, - .requestPath = { .cluster = kLevelControlCluster, .endpoint = 1 }, - .privilege = Privilege::kProxyView, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subjects = { kGroup2 }, }, - .requestPath = { .cluster = kOnOffCluster, .endpoint = 3 }, - .privilege = Privilege::kProxyView, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subjects = { kGroup2 }, }, - .requestPath = { .cluster = kColorControlCluster, .endpoint = 2 }, - .privilege = Privilege::kProxyView, - .allow = true - }, - { - .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kGroup, .subjects = { kGroup2 }, }, - .requestPath = { .cluster = kLevelControlCluster, .endpoint = 1 }, - .privilege = Privilege::kProxyView, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subjects = { kGroup2 }, }, - .requestPath = { .cluster = kLevelControlCluster, .endpoint = 1 }, - .privilege = Privilege::kProxyView, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subjects = { kGroup2 }, }, - .requestPath = { .cluster = kLevelControlCluster, .endpoint = 1 }, - .privilege = Privilege::kProxyView, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subjects = { kGroup4 }, }, - .requestPath = { .cluster = kLevelControlCluster, .endpoint = 1 }, - .privilege = Privilege::kProxyView, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subjects = { kGroup2 }, }, - .requestPath = { .cluster = kColorControlCluster, .endpoint = 1 }, - .privilege = Privilege::kProxyView, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subjects = { kGroup2 }, }, - .requestPath = { .cluster = kColorControlCluster, .endpoint = 1 }, - .privilege = Privilege::kProxyView, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subjects = { kGroup2 }, }, - .requestPath = { .cluster = kLevelControlCluster, .endpoint = 3 }, - .privilege = Privilege::kProxyView, - .allow = false - }, - { - .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subjects = { kGroup2 }, }, - .requestPath = { .cluster = kLevelControlCluster, .endpoint = 1 }, - .privilege = Privilege::kOperate, - .allow = false - }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subject = kGroup2 }, + .requestPath = { .cluster = kLevelControlCluster, .endpoint = 1 }, + .privilege = Privilege::kProxyView, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subject = kGroup2 }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 3 }, + .privilege = Privilege::kProxyView, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subject = kGroup2 }, + .requestPath = { .cluster = kColorControlCluster, .endpoint = 2 }, + .privilege = Privilege::kProxyView, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kGroup, .subject = kGroup2 }, + .requestPath = { .cluster = kLevelControlCluster, .endpoint = 1 }, + .privilege = Privilege::kProxyView, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subject = kGroup2 }, + .requestPath = { .cluster = kLevelControlCluster, .endpoint = 1 }, + .privilege = Privilege::kProxyView, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kGroup2 }, + .requestPath = { .cluster = kLevelControlCluster, .endpoint = 1 }, + .privilege = Privilege::kProxyView, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subject = kGroup4 }, + .requestPath = { .cluster = kLevelControlCluster, .endpoint = 1 }, + .privilege = Privilege::kProxyView, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subject = kGroup2 }, + .requestPath = { .cluster = kColorControlCluster, .endpoint = 1 }, + .privilege = Privilege::kProxyView, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subject = kGroup2 }, + .requestPath = { .cluster = kColorControlCluster, .endpoint = 1 }, + .privilege = Privilege::kProxyView, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subject = kGroup2 }, + .requestPath = { .cluster = kLevelControlCluster, .endpoint = 3 }, + .privilege = Privilege::kProxyView, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kGroup, .subject = kGroup2 }, + .requestPath = { .cluster = kLevelControlCluster, .endpoint = 1 }, + .privilege = Privilege::kOperate, + .allow = false }, + // Checks for entry 6 + { .subjectDescriptor = { .fabricIndex = 2, + .authMode = AuthMode::kCase, + .cats = { kCASEAuthTag0, kUndefinedCAT, kUndefinedCAT } }, + .requestPath = { .cluster = kLevelControlCluster, .endpoint = 1 }, + .privilege = Privilege::kOperate, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 1, + .authMode = AuthMode::kCase, + .cats = { kCASEAuthTag0, kUndefinedCAT, kUndefinedCAT } }, + .requestPath = { .cluster = kLevelControlCluster, .endpoint = 1 }, + .privilege = Privilege::kOperate, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 1, + .authMode = AuthMode::kCase, + .cats = { kCASEAuthTag1, kUndefinedCAT, kUndefinedCAT } }, + .requestPath = { .cluster = kLevelControlCluster, .endpoint = 1 }, + .privilege = Privilege::kOperate, + .allow = false }, + // Checks for entry 7 + { .subjectDescriptor = { .fabricIndex = 2, + .authMode = AuthMode::kCase, + .cats = { kCASEAuthTag0, kUndefinedCAT, kUndefinedCAT } }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 1 }, + .privilege = Privilege::kOperate, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, + .authMode = AuthMode::kCase, + .cats = { kCASEAuthTag0, kCASEAuthTag2, kUndefinedCAT } }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 1 }, + .privilege = Privilege::kOperate, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, + .authMode = AuthMode::kCase, + .cats = { kCASEAuthTag0, kCASEAuthTag3, kUndefinedCAT } }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 1 }, + .privilege = Privilege::kOperate, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 2, + .authMode = AuthMode::kCase, + .cats = { kCASEAuthTag0, kCASEAuthTag4, kUndefinedCAT } }, + .requestPath = { .cluster = kOnOffCluster, .endpoint = 1 }, + .privilege = Privilege::kManage, + .allow = true }, + // Checks for entry 8 + { .subjectDescriptor = { .fabricIndex = 2, + .authMode = AuthMode::kCase, + .cats = { kCASEAuthTag0, kCASEAuthTag3, kUndefinedCAT } }, + .requestPath = { .cluster = kLevelControlCluster, .endpoint = 1 }, + .privilege = Privilege::kOperate, + .allow = false }, + { .subjectDescriptor = { .fabricIndex = 2, + .authMode = AuthMode::kCase, + .cats = { kCASEAuthTag0, kCASEAuthTag4, kUndefinedCAT } }, + .requestPath = { .cluster = kLevelControlCluster, .endpoint = 2 }, + .privilege = Privilege::kOperate, + .allow = true }, + { .subjectDescriptor = { .fabricIndex = 2, + .authMode = AuthMode::kCase, + .cats = { kCASEAuthTag1, kUndefinedCAT, kUndefinedCAT } }, + .requestPath = { .cluster = kLevelControlCluster, .endpoint = 2 }, + .privilege = Privilege::kOperate, + .allow = true }, }; void MetaTest(nlTestSuite * inSuite, void * inContext) { - NL_TEST_ASSERT(inSuite, LoadAccessControl(accessControl, entryData1, ArraySize(entryData1)) == CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, CompareAccessControl(accessControl, entryData1, ArraySize(entryData1)) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, LoadAccessControl(accessControl, entryData1, entryData1Count) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, CompareAccessControl(accessControl, entryData1, entryData1Count) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, accessControl.DeleteEntry(3) == CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, CompareAccessControl(accessControl, entryData1, ArraySize(entryData1)) != CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, CompareAccessControl(accessControl, entryData1, entryData1Count) != CHIP_NO_ERROR); } void TestCheck(nlTestSuite * inSuite, void * inContext) { - LoadAccessControl(accessControl, entryData1, ArraySize(entryData1)); + LoadAccessControl(accessControl, entryData1, entryData1Count); for (const auto & checkData : checkData1) { CHIP_ERROR expectedResult = checkData.allow ? CHIP_NO_ERROR : CHIP_ERROR_ACCESS_DENIED; @@ -653,7 +660,7 @@ void TestCheck(nlTestSuite * inSuite, void * inContext) void TestCreateReadEntry(nlTestSuite * inSuite, void * inContext) { - for (size_t i = 0; i < ArraySize(entryData1); ++i) + for (size_t i = 0; i < entryData1Count; ++i) { NL_TEST_ASSERT(inSuite, LoadAccessControl(accessControl, entryData1 + i, 1) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, CompareAccessControl(accessControl, entryData1, i + 1) == CHIP_NO_ERROR); @@ -662,7 +669,7 @@ void TestCreateReadEntry(nlTestSuite * inSuite, void * inContext) void TestDeleteEntry(nlTestSuite * inSuite, void * inContext) { - EntryData data[ArraySize(entryData1)]; + EntryData data[entryData1Count]; for (size_t pos = 0; pos < ArraySize(data); ++pos) { for (size_t count = ArraySize(data) - pos; count > 0; --count) @@ -678,7 +685,7 @@ void TestDeleteEntry(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, accessControl.DeleteEntry(pos) == CHIP_NO_ERROR); } - NL_TEST_ASSERT(inSuite, LoadAccessControl(accessControl, data, ArraySize(data) - count) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, CompareAccessControl(accessControl, data, ArraySize(data) - count) == CHIP_NO_ERROR); } } } @@ -687,15 +694,15 @@ void TestFabricFilteredCreateEntry(nlTestSuite * inSuite, void * inContext) { for (auto & fabricIndex : fabricIndexes) { - for (size_t count = 0; count < ArraySize(entryData1); ++count) + for (size_t count = 0; count < entryData1Count; ++count) { NL_TEST_ASSERT(inSuite, ClearAccessControl(accessControl) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, LoadAccessControl(accessControl, entryData1, count) == CHIP_NO_ERROR); - constexpr size_t expectedIndexes[][ArraySize(entryData1)] = { - { 0, 1, 2, 2, 3, 3 }, - { 0, 0, 0, 1, 1, 2 }, - { 0, 0, 0, 0, 0, 0 }, + constexpr size_t expectedIndexes[][entryData1Count] = { + { 0, 1, 2, 2, 3, 3, 3, 4, 4 }, + { 0, 0, 0, 1, 1, 2, 3, 3, 4 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, }; const size_t expectedIndex = expectedIndexes[&fabricIndex - fabricIndexes][count]; @@ -715,18 +722,18 @@ void TestFabricFilteredCreateEntry(nlTestSuite * inSuite, void * inContext) void TestFabricFilteredReadEntry(nlTestSuite * inSuite, void * inContext) { - NL_TEST_ASSERT(inSuite, LoadAccessControl(accessControl, entryData1, ArraySize(entryData1)) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, LoadAccessControl(accessControl, entryData1, entryData1Count) == CHIP_NO_ERROR); for (auto & fabricIndex : fabricIndexes) { - constexpr size_t indexes[] = { 0, 1, 2, 3 }; + constexpr size_t indexes[] = { 0, 1, 2, 3, 4, 5 }; for (auto & index : indexes) { - constexpr size_t illegalIndex = ArraySize(entryData1); + constexpr size_t illegalIndex = entryData1Count; constexpr size_t expectedIndexes[][ArraySize(indexes)] = { - { 0, 1, 3, illegalIndex }, - { 2, 4, 5, illegalIndex }, - { illegalIndex, illegalIndex, illegalIndex, illegalIndex }, + { 0, 1, 3, 6, illegalIndex, illegalIndex }, + { 2, 4, 5, 7, 8, illegalIndex }, + { illegalIndex, illegalIndex, illegalIndex, illegalIndex, illegalIndex, illegalIndex }, }; const size_t expectedIndex = expectedIndexes[&fabricIndex - fabricIndexes][&index - indexes]; @@ -748,7 +755,7 @@ void TestFabricFilteredReadEntry(nlTestSuite * inSuite, void * inContext) void TestIterator(nlTestSuite * inSuite, void * inContext) { - LoadAccessControl(accessControl, entryData1, ArraySize(entryData1)); + LoadAccessControl(accessControl, entryData1, entryData1Count); FabricIndex fabricIndex; EntryIterator iterator; @@ -762,7 +769,7 @@ void TestIterator(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, CompareEntry(entry, entryData1[count]) == CHIP_NO_ERROR); count++; } - NL_TEST_ASSERT(inSuite, count == ArraySize(entryData1)); + NL_TEST_ASSERT(inSuite, count == entryData1Count); fabricIndex = kUndefinedFabricIndex; NL_TEST_ASSERT(inSuite, accessControl.Entries(iterator, &fabricIndex) == CHIP_NO_ERROR); @@ -770,7 +777,7 @@ void TestIterator(nlTestSuite * inSuite, void * inContext) fabricIndex = 1; NL_TEST_ASSERT(inSuite, accessControl.Entries(iterator, &fabricIndex) == CHIP_NO_ERROR); - size_t fabric1[] = { 0, 1, 3 }; + size_t fabric1[] = { 0, 1, 3, 6 }; count = 0; while (iterator.Next(entry) == CHIP_NO_ERROR) { @@ -781,14 +788,14 @@ void TestIterator(nlTestSuite * inSuite, void * inContext) fabricIndex = 2; NL_TEST_ASSERT(inSuite, accessControl.Entries(iterator, &fabricIndex) == CHIP_NO_ERROR); - size_t fabric2[] = { 2, 4, 5 }; + size_t fabric2[] = { 2, 4, 5, 7, 8 }; count = 0; while (iterator.Next(entry) == CHIP_NO_ERROR) { NL_TEST_ASSERT(inSuite, CompareEntry(entry, entryData1[fabric2[count]]) == CHIP_NO_ERROR); count++; } - NL_TEST_ASSERT(inSuite, count == ArraySize(fabric1)); + NL_TEST_ASSERT(inSuite, count == ArraySize(fabric2)); } void TestPrepareEntry(nlTestSuite * inSuite, void * inContext) @@ -1043,12 +1050,12 @@ void TestSubjectsTargets(nlTestSuite * inSuite, void * inContext) void TestUpdateEntry(nlTestSuite * inSuite, void * inContext) { - EntryData data[6]; + EntryData data[entryData1Count]; memcpy(data, entryData1, sizeof(data)); - NL_TEST_ASSERT(inSuite, LoadAccessControl(accessControl, data, 6) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, LoadAccessControl(accessControl, data, ArraySize(data)) == CHIP_NO_ERROR); EntryData updateData; - for (size_t i = 0; i < 6; ++i) + for (size_t i = 0; i < ArraySize(data); ++i) { updateData.authMode = authModes[i % 3]; updateData.fabricIndex = fabricIndexes[i % 3]; @@ -1072,7 +1079,7 @@ void TestUpdateEntry(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, accessControl.UpdateEntry(i, entry) == CHIP_NO_ERROR); } - NL_TEST_ASSERT(inSuite, CompareAccessControl(accessControl, data, 6) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, CompareAccessControl(accessControl, data, ArraySize(data)) == CHIP_NO_ERROR); } } diff --git a/src/credentials/CHIPCert.cpp b/src/credentials/CHIPCert.cpp index 6a37e9b46c500d..aea781de5dbe18 100644 --- a/src/credentials/CHIPCert.cpp +++ b/src/credentials/CHIPCert.cpp @@ -914,12 +914,12 @@ CHIP_ERROR ExtractCATsFromOpCert(const ChipCertificateData & opcert, CATValues & // 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); + cats.values[catCount++] = static_cast(rdn.mChipVal); } } for (uint8_t i = catCount; i < cats.size(); ++i) { - cats.val[i] = kUndefinedCAT; + cats.values[i] = kUndefinedCAT; } return CHIP_NO_ERROR; diff --git a/src/credentials/CHIPCert.h b/src/credentials/CHIPCert.h index 6a5ed262a8a1a4..258cecbc5d85b9 100644 --- a/src/credentials/CHIPCert.h +++ b/src/credentials/CHIPCert.h @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -55,18 +56,6 @@ static constexpr uint32_t kMaxDERCertLength = 600; // The decode buffer is used to reconstruct TBS section of X.509 certificate, which doesn't include signature. static constexpr uint32_t kMaxCHIPCertDecodeBufLength = kMaxDERCertLength - Crypto::kMax_ECDSA_Signature_Length_Der; -// 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 */ enum diff --git a/src/lib/core/CASEAuthTag.h b/src/lib/core/CASEAuthTag.h new file mode 100644 index 00000000000000..8365b8fd904069 --- /dev/null +++ b/src/lib/core/CASEAuthTag.h @@ -0,0 +1,63 @@ +/* + * + * Copyright (c) 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +namespace chip { + +typedef uint32_t CASEAuthTag; + +static constexpr CASEAuthTag kUndefinedCAT = 0; +static constexpr NodeId kTagIdentifierMask = 0x0000'0000'FFFF'0000ULL; +static constexpr NodeId kTagVersionMask = 0x0000'0000'0000'FFFFULL; + +// Maximum number of CASE Authenticated Tags (CAT) in the CHIP certificate subject. +static constexpr size_t kMaxSubjectCATAttributeCount = CHIP_CONFIG_CERT_MAX_RDN_ATTRIBUTES - 2; + +struct CATValues +{ + CASEAuthTag values[kMaxSubjectCATAttributeCount] = { kUndefinedCAT }; + + /* @brief Returns size of the CAT values array. + */ + static constexpr size_t size() { return ArraySize(values); } + + /* @brief Returns true if subject input checks against one of the CATs in the values array. + */ + bool CheckSubjectAgainstCATs(NodeId subject) const + { + for (auto cat : values) + { + // All valid CAT values are always in the beginning of the array followed by kUndefinedCAT values. + ReturnErrorCodeIf(cat == kUndefinedCAT, false); + if (((cat & kTagIdentifierMask) == (subject & kTagIdentifierMask)) && + ((cat & kTagVersionMask) >= (subject & kTagVersionMask))) + { + return true; + } + } + return false; + } +}; + +static constexpr CATValues kUndefinedCATs = { { kUndefinedCAT } }; + +} // namespace chip diff --git a/src/lib/core/DataModelTypes.h b/src/lib/core/DataModelTypes.h index 1f007b0e38d666..b36ad1ce179975 100644 --- a/src/lib/core/DataModelTypes.h +++ b/src/lib/core/DataModelTypes.h @@ -28,7 +28,6 @@ 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 f6f62b2b1fa01f..e2ce4eb6db21d1 100644 --- a/src/lib/core/NodeId.h +++ b/src/lib/core/NodeId.h @@ -56,4 +56,19 @@ constexpr bool IsOperationalNodeId(NodeId aNodeId) return (aNodeId != kUndefinedNodeId) && (aNodeId <= kMaxOperationalNodeId); } +constexpr bool IsGroupId(NodeId aNodeId) +{ + return (aNodeId >= kMinGroupNodeId); +} + +constexpr bool IsCASEAuthTag(NodeId aNodeId) +{ + return (aNodeId >= kMinCASEAuthTag) && (aNodeId <= kMaxCASEAuthTag); +} + +constexpr bool IsPAKEKeyId(NodeId aNodeId) +{ + return (aNodeId >= kMinPAKEKeyId) && (aNodeId <= kMaxPAKEKeyId); +} + } // namespace chip diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index 581c14bd517153..db782eac4b4322 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -1,4 +1,3 @@ - /* * * Copyright (c) 2021 Project CHIP Authors @@ -129,12 +128,11 @@ CHIP_ERROR CASESession::ToCachable(CASESessionCachable & cachableSession) VerifyOrReturnError(CanCastTo(mSharedSecret.Length()), CHIP_ERROR_INTERNAL); VerifyOrReturnError(CanCastTo(peerNodeId), CHIP_ERROR_INTERNAL); - memset(&cachableSession, 0, sizeof(cachableSession)); cachableSession.mSharedSecretLen = LittleEndian::HostSwap16(static_cast(mSharedSecret.Length())); cachableSession.mPeerNodeId = LittleEndian::HostSwap64(peerNodeId); for (size_t i = 0; i < cachableSession.mPeerCATs.size(); i++) { - cachableSession.mPeerCATs.val[i] = LittleEndian::HostSwap32(GetPeerCATs().val[i]); + cachableSession.mPeerCATs.values[i] = LittleEndian::HostSwap32(GetPeerCATs().values[i]); } // TODO: Get the fabric index cachableSession.mLocalFabricIndex = 0; @@ -154,10 +152,10 @@ CHIP_ERROR CASESession::FromCachable(const CASESessionCachable & cachableSession memcpy(mSharedSecret, cachableSession.mSharedSecret, length); SetPeerNodeId(LittleEndian::HostSwap64(cachableSession.mPeerNodeId)); - Credentials::CATValues peerCATs; + CATValues peerCATs; for (size_t i = 0; i < cachableSession.mPeerCATs.size(); i++) { - peerCATs.val[i] = LittleEndian::HostSwap32(cachableSession.mPeerCATs.val[i]); + peerCATs.values[i] = LittleEndian::HostSwap32(cachableSession.mPeerCATs.values[i]); } SetPeerCATs(peerCATs); SetSessionTimeStamp(LittleEndian::HostSwap64(cachableSession.mSessionSetupTimeStamp)); @@ -852,9 +850,11 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) 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); + { + CATValues peerCATs; + SuccessOrExit(err = ExtractCATsFromOpCert(responderNOC, peerCATs)); + SetPeerCATs(peerCATs); + } // Retrieve responderMRPParams if present if (tlvReader.Next() != CHIP_END_OF_TLV) @@ -1120,9 +1120,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); + { + CATValues peerCATs; + SuccessOrExit(err = ExtractCATsFromOpCert(initiatorNOC, peerCATs)); + SetPeerCATs(peerCATs); + } SendStatusReport(mExchangeCtxt, kProtocolCodeSuccess); diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h index 1bc451f2a48ed5..9d967ddf7074fe 100644 --- a/src/protocols/secure_channel/CASESession.h +++ b/src/protocols/secure_channel/CASESession.h @@ -60,13 +60,13 @@ constexpr size_t kCASEResumptionIDSize = 16; struct CASESessionCachable { - uint16_t mSharedSecretLen; - uint8_t mSharedSecret[Crypto::kMax_ECDH_Secret_Length]; - FabricIndex mLocalFabricIndex; - NodeId mPeerNodeId; - Credentials::CATValues mPeerCATs; - uint8_t mResumptionId[kCASEResumptionIDSize]; - uint64_t mSessionSetupTimeStamp; + uint16_t mSharedSecretLen = 0; + uint8_t mSharedSecret[Crypto::kMax_ECDH_Secret_Length] = { 0 }; + FabricIndex mLocalFabricIndex = 0; + NodeId mPeerNodeId = kUndefinedNodeId; + CATValues mPeerCATs; + uint8_t mResumptionId[kCASEResumptionIDSize] = { 0 }; + uint64_t mSessionSetupTimeStamp = 0; }; class DLL_EXPORT CASESession : public Messaging::ExchangeDelegate, public PairingSession diff --git a/src/protocols/secure_channel/tests/TestCASESession.cpp b/src/protocols/secure_channel/tests/TestCASESession.cpp index 5a090485976840..aaaaf52b1a8a8e 100644 --- a/src/protocols/secure_channel/tests/TestCASESession.cpp +++ b/src/protocols/secure_channel/tests/TestCASESession.cpp @@ -154,9 +154,9 @@ void CASE_SecurePairingWaitTest(nlTestSuite * inSuite, void * inContext) FabricTable fabrics; NL_TEST_ASSERT(inSuite, pairing.GetSecureSessionType() == SecureSession::Type::kCASE); - Credentials::CATValues peerCATs; + CATValues peerCATs; peerCATs = pairing.GetPeerCATs(); - NL_TEST_ASSERT(inSuite, memcmp(&peerCATs, &kUndefinedCATs, sizeof(Credentials::CATValues)) == 0); + NL_TEST_ASSERT(inSuite, memcmp(&peerCATs, &kUndefinedCATs, sizeof(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); diff --git a/src/protocols/secure_channel/tests/TestCASESessionCache.cpp b/src/protocols/secure_channel/tests/TestCASESessionCache.cpp index 42f7c5782afdd1..87def7e6243a26 100644 --- a/src/protocols/secure_channel/tests/TestCASESessionCache.cpp +++ b/src/protocols/secure_channel/tests/TestCASESessionCache.cpp @@ -63,8 +63,8 @@ class CASESessionTest : public CASESession mCASESessionCachableArray[i].mSharedSecretLen = sharedSecretLen; memcpy(mCASESessionCachableArray[i].mSharedSecret, sTest_SharedSecret, sharedSecretLen); - mCASESessionCachableArray[i].mPeerNodeId = static_cast(sTest_PeerId + i); - mCASESessionCachableArray[i].mPeerCATs.val[0] = (uint32_t) i; + mCASESessionCachableArray[i].mPeerNodeId = static_cast(sTest_PeerId + i); + mCASESessionCachableArray[i].mPeerCATs.values[0] = (uint32_t) i; memcpy(mCASESessionCachableArray[i].mResumptionId, sTest_ResumptionId, kCASEResumptionIDSize); mCASESessionCachableArray[i].mLocalFabricIndex = 0; mCASESessionCachableArray[i].mSessionSetupTimeStamp = timestamp; @@ -75,7 +75,7 @@ class CASESessionTest : public CASESession return (cachableSession.mSharedSecretLen == mCASESessionCachableArray[index].mSharedSecretLen) && ((ByteSpan(cachableSession.mSharedSecret)).data_equal(ByteSpan(mCASESessionCachableArray[index].mSharedSecret))) && (cachableSession.mPeerNodeId == mCASESessionCachableArray[index].mPeerNodeId) && - cachableSession.mPeerCATs.val[0] == mCASESessionCachableArray[index].mPeerCATs.val[0] && + cachableSession.mPeerCATs.values[0] == mCASESessionCachableArray[index].mPeerCATs.values[0] && ((ResumptionID(cachableSession.mResumptionId)) .data_equal(ResumptionID(mCASESessionCachableArray[index].mResumptionId))) && (cachableSession.mLocalFabricIndex == mCASESessionCachableArray[index].mLocalFabricIndex) && diff --git a/src/protocols/secure_channel/tests/TestPASESession.cpp b/src/protocols/secure_channel/tests/TestPASESession.cpp index 800afa97e879cf..553fa514340d3e 100644 --- a/src/protocols/secure_channel/tests/TestPASESession.cpp +++ b/src/protocols/secure_channel/tests/TestPASESession.cpp @@ -92,9 +92,9 @@ void SecurePairingWaitTest(nlTestSuite * inSuite, void * inContext) PASESession pairing; NL_TEST_ASSERT(inSuite, pairing.GetSecureSessionType() == SecureSession::Type::kPASE); - Credentials::CATValues peerCATs; + CATValues peerCATs; peerCATs = pairing.GetPeerCATs(); - NL_TEST_ASSERT(inSuite, memcmp(&peerCATs, &Credentials::kUndefinedCATs, sizeof(Credentials::CATValues)) == 0); + NL_TEST_ASSERT(inSuite, memcmp(&peerCATs, &kUndefinedCATs, sizeof(CATValues)) == 0); gLoopback.Reset(); diff --git a/src/transport/PairingSession.h b/src/transport/PairingSession.h index 27e69a1c6a6542..d86b388e71b204 100644 --- a/src/transport/PairingSession.h +++ b/src/transport/PairingSession.h @@ -48,7 +48,7 @@ class DLL_EXPORT PairingSession // SetPeerNodeId is not necessary. NodeId GetPeerNodeId() const { return mPeerNodeId; } - Credentials::CATValues GetPeerCATs() const { return mPeerCATs; } + 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 @@ -106,7 +106,7 @@ class DLL_EXPORT PairingSession protected: void SetSecureSessionType(Transport::SecureSession::Type secureSessionType) { mSecureSessionType = secureSessionType; } void SetPeerNodeId(NodeId peerNodeId) { mPeerNodeId = peerNodeId; } - void SetPeerCATs(Credentials::CATValues peerCATs) { mPeerCATs = peerCATs; } + void SetPeerCATs(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; } @@ -179,7 +179,7 @@ class DLL_EXPORT PairingSession { mSecureSessionType = Transport::SecureSession::Type::kUndefined; mPeerNodeId = kUndefinedNodeId; - mPeerCATs = Credentials::kUndefinedCATs; + mPeerCATs = kUndefinedCATs; mPeerAddress = Transport::PeerAddress::Uninitialized(); mPeerSessionId.ClearValue(); mLocalSessionId = kInvalidKeyId; @@ -188,7 +188,7 @@ class DLL_EXPORT PairingSession private: Transport::SecureSession::Type mSecureSessionType = Transport::SecureSession::Type::kUndefined; NodeId mPeerNodeId = kUndefinedNodeId; - Credentials::CATValues mPeerCATs = Credentials::kUndefinedCATs; + CATValues mPeerCATs; // 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 3627d4da7d81a5..9c9cbc627bdd91 100644 --- a/src/transport/SecureSession.h +++ b/src/transport/SecureSession.h @@ -64,9 +64,8 @@ class SecureSession 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) : + SecureSession(Type secureSessionType, uint16_t localSessionId, NodeId peerNodeId, 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) @@ -85,7 +84,7 @@ class SecureSession Type GetSecureSessionType() const { return mSecureSessionType; } NodeId GetPeerNodeId() const { return mPeerNodeId; } - Credentials::CATValues GetPeerCATs() const { return mPeerCATs; } + CATValues GetPeerCATs() const { return mPeerCATs; } void SetMRPConfig(const ReliableMessageProtocolConfig & config) { mMRPConfig = config; } @@ -117,7 +116,7 @@ class SecureSession private: const Type mSecureSessionType; const NodeId mPeerNodeId; - const Credentials::CATValues mPeerCATs; + const 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 8a44c52d951222..00392e70d9a479 100644 --- a/src/transport/SecureSessionTable.h +++ b/src/transport/SecureSessionTable.h @@ -61,7 +61,7 @@ class SecureSessionTable */ CHECK_RETURN_VALUE SecureSession * CreateNewSecureSession(SecureSession::Type secureSessionType, uint16_t localSessionId, NodeId peerNodeId, - Credentials::CATValues peerCATs, uint16_t peerSessionId, FabricIndex fabric, + CATValues peerCATs, uint16_t peerSessionId, FabricIndex fabric, const ReliableMessageProtocolConfig & config) { return mEntries.CreateObject(secureSessionType, localSessionId, peerNodeId, peerCATs, peerSessionId, fabric, config, diff --git a/src/transport/tests/TestPeerConnections.cpp b/src/transport/tests/TestPeerConnections.cpp index bee5e9cdfb7497..ce603e28a86592 100644 --- a/src/transport/tests/TestPeerConnections.cpp +++ b/src/transport/tests/TestPeerConnections.cpp @@ -56,16 +56,16 @@ 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; +const CATValues kPeer1CATs = { { 0xABCD0001, 0xABCE0100, 0xABCD0020 } }; +const CATValues kPeer2CATs = { { 0xABCD0012, kUndefinedCAT, kUndefinedCAT } }; +const CATValues kPeer3CATs; void TestBasicFunctionality(nlTestSuite * inSuite, void * inContext) { SecureSession * statePtr; SecureSessionTable<2, Time::Source::kTest> connections; connections.GetTimeSource().SetMonotonicTimestamp(100_ms64); - Credentials::CATValues peerCATs; + CATValues peerCATs; // Node ID 1, peer key 1, local key 2 statePtr = connections.CreateNewSecureSession(kPeer1SessionType, 2, kPeer1NodeId, kPeer1CATs, 1, 0 /* fabricIndex */, @@ -74,7 +74,7 @@ void TestBasicFunctionality(nlTestSuite * inSuite, void * inContext) 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); + NL_TEST_ASSERT(inSuite, memcmp(&peerCATs, &kPeer1CATs, sizeof(CATValues)) == 0); // Node ID 2, peer key 3, local key 4 statePtr = connections.CreateNewSecureSession(kPeer2SessionType, 4, kPeer2NodeId, kPeer2CATs, 3, 0 /* fabricIndex */, @@ -84,7 +84,7 @@ void TestBasicFunctionality(nlTestSuite * inSuite, void * inContext) 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); + NL_TEST_ASSERT(inSuite, memcmp(&peerCATs, &kPeer2CATs, sizeof(CATValues)) == 0); // Insufficient space for new connections. Object is max size 2 statePtr = connections.CreateNewSecureSession(kPeer3SessionType, 6, kPeer3NodeId, kPeer3CATs, 5, 0 /* fabricIndex */,