Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021-2022 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #pragma once
19 :
20 : #include <array>
21 :
22 : #include <lib/core/CHIPConfig.h>
23 : #include <lib/core/CHIPEncoding.h>
24 : #include <lib/core/NodeId.h>
25 : #include <lib/support/CodeUtils.h>
26 :
27 : namespace chip {
28 :
29 : typedef uint32_t CASEAuthTag;
30 :
31 : static constexpr CASEAuthTag kUndefinedCAT = 0;
32 : static constexpr NodeId kTagIdentifierMask = 0x0000'0000'FFFF'0000ULL;
33 : static constexpr uint32_t kTagIdentifierShift = 16;
34 : static constexpr NodeId kTagVersionMask = 0x0000'0000'0000'FFFFULL;
35 :
36 : // Maximum number of CASE Authenticated Tags (CAT) in the CHIP certificate subject.
37 : static constexpr size_t kMaxSubjectCATAttributeCount = CHIP_CONFIG_CERT_MAX_RDN_ATTRIBUTES - 2;
38 :
39 : constexpr NodeId NodeIdFromCASEAuthTag(CASEAuthTag aCAT)
40 : {
41 : return kMinCASEAuthTag | aCAT;
42 : }
43 :
44 364 : constexpr CASEAuthTag CASEAuthTagFromNodeId(NodeId aNodeId)
45 : {
46 364 : return aNodeId & kMaskCASEAuthTag;
47 : }
48 :
49 196 : constexpr bool IsValidCASEAuthTag(CASEAuthTag aCAT)
50 : {
51 196 : return (aCAT & kTagVersionMask) > 0;
52 : }
53 :
54 122 : constexpr uint16_t GetCASEAuthTagIdentifier(CASEAuthTag aCAT)
55 : {
56 122 : return static_cast<uint16_t>((aCAT & kTagIdentifierMask) >> kTagIdentifierShift);
57 : }
58 :
59 354 : constexpr uint16_t GetCASEAuthTagVersion(CASEAuthTag aCAT)
60 : {
61 354 : return static_cast<uint16_t>(aCAT & kTagVersionMask);
62 : }
63 :
64 : struct CATValues
65 : {
66 : std::array<CASEAuthTag, kMaxSubjectCATAttributeCount> values = { kUndefinedCAT };
67 :
68 : /* @brief Returns maximum number of CAT values that the array can contain.
69 : */
70 775 : static constexpr size_t size() { return std::tuple_size<decltype(values)>::value; }
71 :
72 : /**
73 : * @return the number of CATs present in the set (values not equal to kUndefinedCAT)
74 : */
75 106 : size_t GetNumTagsPresent() const
76 : {
77 106 : size_t count = 0;
78 424 : for (auto cat : values)
79 : {
80 318 : count += (cat != kUndefinedCAT) ? 1 : 0;
81 : }
82 106 : return count;
83 : }
84 :
85 : /**
86 : * @return true if `tag` is in the set exactly, false otherwise.
87 : */
88 0 : bool Contains(CASEAuthTag tag) const
89 : {
90 0 : for (auto candidate : values)
91 : {
92 0 : if ((candidate != kUndefinedCAT) && (candidate == tag))
93 : {
94 0 : return true;
95 : }
96 : }
97 :
98 0 : return false;
99 : }
100 :
101 142 : bool AreValid() const
102 : {
103 565 : for (size_t idx = 0; idx < size(); ++idx)
104 : {
105 424 : const auto & candidate = values[idx];
106 424 : if (candidate == kUndefinedCAT)
107 : {
108 406 : continue;
109 : }
110 :
111 : // Every entry that is not empty must have version > 0
112 18 : if (!IsValidCASEAuthTag(candidate))
113 : {
114 0 : return false;
115 : }
116 : // Identifiers cannot collide in set (there cannot be more than 1 version of an identifier)
117 70 : for (size_t other_idx = 0; other_idx < size(); ++other_idx)
118 : {
119 53 : if (idx == other_idx)
120 : {
121 18 : continue;
122 : }
123 35 : if (values[other_idx] == kUndefinedCAT)
124 : {
125 14 : continue;
126 : }
127 :
128 21 : uint16_t other_identifier = GetCASEAuthTagIdentifier(values[other_idx]);
129 21 : uint16_t candidate_identifier = GetCASEAuthTagIdentifier(candidate);
130 21 : if (other_identifier == candidate_identifier)
131 : {
132 1 : return false;
133 : }
134 : }
135 : }
136 :
137 141 : return true;
138 : }
139 :
140 : /**
141 : * @brief Returns true if this set contains any version of the `identifier`
142 : *
143 : * @param identifier - CAT identifier to find
144 : * @return true if the identifier is in the set, false otherwise
145 : */
146 : bool ContainsIdentifier(uint16_t identifier) const
147 : {
148 : for (auto candidate : values)
149 : {
150 : uint16_t candidate_identifier = GetCASEAuthTagIdentifier(candidate);
151 : if ((candidate != kUndefinedCAT) && (identifier == candidate_identifier))
152 : {
153 : return true;
154 : }
155 : }
156 :
157 : return false;
158 : }
159 :
160 : /* @brief Returns true if subject input checks against one of the CATs in the values array.
161 : */
162 80 : bool CheckSubjectAgainstCATs(NodeId subject) const
163 : {
164 80 : VerifyOrReturnError(IsCASEAuthTag(subject), false);
165 49 : CASEAuthTag catFromSubject = CASEAuthTagFromNodeId(subject);
166 :
167 171 : for (auto catFromNoc : values)
168 : {
169 40 : if ((catFromNoc != kUndefinedCAT) &&
170 53 : (GetCASEAuthTagIdentifier(catFromNoc) == GetCASEAuthTagIdentifier(catFromSubject)) &&
171 185 : (GetCASEAuthTagVersion(catFromSubject) > 0) &&
172 13 : (GetCASEAuthTagVersion(catFromNoc) >= GetCASEAuthTagVersion(catFromSubject)))
173 : {
174 10 : return true;
175 : }
176 : }
177 39 : return false;
178 : }
179 :
180 53 : bool operator==(const CATValues & other) const
181 : {
182 : // Two sets of CATs confer equal permissions if the sets are exactly equal
183 : // and the sets are valid.
184 : // Ignoring kUndefinedCAT values, evaluate this.
185 53 : if (this->GetNumTagsPresent() != other.GetNumTagsPresent())
186 : {
187 0 : return false;
188 : }
189 53 : if (!this->AreValid() || !other.AreValid())
190 : {
191 0 : return false;
192 : }
193 212 : for (auto cat : this->values)
194 : {
195 159 : if (cat == kUndefinedCAT)
196 : {
197 159 : continue;
198 : }
199 :
200 0 : if (!other.Contains(cat))
201 : {
202 0 : return false;
203 : }
204 : }
205 53 : return true;
206 : }
207 : bool operator!=(const CATValues & other) const { return !(*this == other); }
208 :
209 : static constexpr size_t kSerializedLength = kMaxSubjectCATAttributeCount * sizeof(CASEAuthTag);
210 : typedef uint8_t Serialized[kSerializedLength];
211 :
212 207 : CHIP_ERROR Serialize(Serialized & outSerialized) const
213 : {
214 207 : uint8_t * p = outSerialized;
215 828 : for (size_t i = 0; i < kMaxSubjectCATAttributeCount; i++)
216 : {
217 621 : Encoding::LittleEndian::Write32(p, values[i]);
218 : }
219 207 : return CHIP_NO_ERROR;
220 : }
221 :
222 317 : CHIP_ERROR Deserialize(const Serialized & inSerialized)
223 : {
224 317 : const uint8_t * p = inSerialized;
225 1268 : for (size_t i = 0; i < kMaxSubjectCATAttributeCount; i++)
226 : {
227 951 : values[i] = Encoding::LittleEndian::Read32(p);
228 : }
229 317 : return CHIP_NO_ERROR;
230 : }
231 : };
232 :
233 : static constexpr CATValues kUndefinedCATs = { { kUndefinedCAT } };
234 :
235 : } // namespace chip
|