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 : static constexpr uint16_t kAdminCATIdentifier = 0xFFFF;
36 : static constexpr uint16_t kAnchorCATIdentifier = 0xFFFE;
37 :
38 : // Maximum number of CASE Authenticated Tags (CAT) in the CHIP certificate subject.
39 : static constexpr size_t kMaxSubjectCATAttributeCount = CHIP_CONFIG_CERT_MAX_RDN_ATTRIBUTES - 2;
40 :
41 : constexpr NodeId NodeIdFromCASEAuthTag(CASEAuthTag aCAT)
42 : {
43 : return kMinCASEAuthTag | aCAT;
44 : }
45 :
46 364 : constexpr CASEAuthTag CASEAuthTagFromNodeId(NodeId aNodeId)
47 : {
48 364 : return aNodeId & kMaskCASEAuthTag;
49 : }
50 :
51 196 : constexpr bool IsValidCASEAuthTag(CASEAuthTag aCAT)
52 : {
53 196 : return (aCAT & kTagVersionMask) > 0;
54 : }
55 :
56 122 : constexpr uint16_t GetCASEAuthTagIdentifier(CASEAuthTag aCAT)
57 : {
58 122 : return static_cast<uint16_t>((aCAT & kTagIdentifierMask) >> kTagIdentifierShift);
59 : }
60 :
61 354 : constexpr uint16_t GetCASEAuthTagVersion(CASEAuthTag aCAT)
62 : {
63 354 : return static_cast<uint16_t>(aCAT & kTagVersionMask);
64 : }
65 :
66 : constexpr CASEAuthTag GetAdminCATWithVersion(uint16_t version)
67 : {
68 : return ((static_cast<uint32_t>(kAdminCATIdentifier) << 16) | version);
69 : }
70 :
71 : constexpr CASEAuthTag GetAnchorCATWithVersion(uint16_t version)
72 : {
73 : return ((static_cast<uint32_t>(kAnchorCATIdentifier) << 16) | version);
74 : }
75 :
76 : struct CATValues
77 : {
78 : std::array<CASEAuthTag, kMaxSubjectCATAttributeCount> values = { kUndefinedCAT };
79 :
80 : /* @brief Returns maximum number of CAT values that the array can contain.
81 : */
82 815 : static constexpr size_t size() { return std::tuple_size<decltype(values)>::value; }
83 :
84 : /**
85 : * @return the number of CATs present in the set (values not equal to kUndefinedCAT)
86 : */
87 106 : size_t GetNumTagsPresent() const
88 : {
89 106 : size_t count = 0;
90 424 : for (auto cat : values)
91 : {
92 318 : count += (cat != kUndefinedCAT) ? 1 : 0;
93 : }
94 106 : return count;
95 : }
96 :
97 : /**
98 : * @return true if `tag` is in the set exactly, false otherwise.
99 : */
100 0 : bool Contains(CASEAuthTag tag) const
101 : {
102 0 : for (auto candidate : values)
103 : {
104 0 : if ((candidate != kUndefinedCAT) && (candidate == tag))
105 : {
106 0 : return true;
107 : }
108 : }
109 :
110 0 : return false;
111 : }
112 :
113 148 : bool AreValid() const
114 : {
115 589 : for (size_t idx = 0; idx < size(); ++idx)
116 : {
117 442 : const auto & candidate = values[idx];
118 442 : if (candidate == kUndefinedCAT)
119 : {
120 424 : continue;
121 : }
122 :
123 : // Every entry that is not empty must have version > 0
124 18 : if (!IsValidCASEAuthTag(candidate))
125 : {
126 0 : return false;
127 : }
128 : // Identifiers cannot collide in set (there cannot be more than 1 version of an identifier)
129 70 : for (size_t other_idx = 0; other_idx < size(); ++other_idx)
130 : {
131 53 : if (idx == other_idx)
132 : {
133 18 : continue;
134 : }
135 35 : if (values[other_idx] == kUndefinedCAT)
136 : {
137 14 : continue;
138 : }
139 :
140 21 : uint16_t other_identifier = GetCASEAuthTagIdentifier(values[other_idx]);
141 21 : uint16_t candidate_identifier = GetCASEAuthTagIdentifier(candidate);
142 21 : if (other_identifier == candidate_identifier)
143 : {
144 1 : return false;
145 : }
146 : }
147 : }
148 :
149 147 : return true;
150 : }
151 :
152 : /**
153 : * @brief Returns true if this set contains any version of the `identifier`
154 : *
155 : * @param identifier - CAT identifier to find
156 : * @return true if the identifier is in the set, false otherwise
157 : */
158 : bool ContainsIdentifier(uint16_t identifier) const
159 : {
160 : for (auto candidate : values)
161 : {
162 : uint16_t candidate_identifier = GetCASEAuthTagIdentifier(candidate);
163 : if ((candidate != kUndefinedCAT) && (identifier == candidate_identifier))
164 : {
165 : return true;
166 : }
167 : }
168 :
169 : return false;
170 : }
171 :
172 : /* @brief Returns true if subject input checks against one of the CATs in the values array.
173 : */
174 84 : bool CheckSubjectAgainstCATs(NodeId subject) const
175 : {
176 84 : VerifyOrReturnError(IsCASEAuthTag(subject), false);
177 49 : CASEAuthTag catFromSubject = CASEAuthTagFromNodeId(subject);
178 :
179 171 : for (auto catFromNoc : values)
180 : {
181 40 : if ((catFromNoc != kUndefinedCAT) &&
182 53 : (GetCASEAuthTagIdentifier(catFromNoc) == GetCASEAuthTagIdentifier(catFromSubject)) &&
183 185 : (GetCASEAuthTagVersion(catFromSubject) > 0) &&
184 13 : (GetCASEAuthTagVersion(catFromNoc) >= GetCASEAuthTagVersion(catFromSubject)))
185 : {
186 10 : return true;
187 : }
188 : }
189 39 : return false;
190 : }
191 :
192 53 : bool operator==(const CATValues & other) const
193 : {
194 : // Two sets of CATs confer equal permissions if the sets are exactly equal
195 : // and the sets are valid.
196 : // Ignoring kUndefinedCAT values, evaluate this.
197 53 : if (this->GetNumTagsPresent() != other.GetNumTagsPresent())
198 : {
199 0 : return false;
200 : }
201 53 : if (!this->AreValid() || !other.AreValid())
202 : {
203 0 : return false;
204 : }
205 212 : for (auto cat : this->values)
206 : {
207 159 : if (cat == kUndefinedCAT)
208 : {
209 159 : continue;
210 : }
211 :
212 0 : if (!other.Contains(cat))
213 : {
214 0 : return false;
215 : }
216 : }
217 53 : return true;
218 : }
219 : bool operator!=(const CATValues & other) const { return !(*this == other); }
220 :
221 : static constexpr size_t kSerializedLength = kMaxSubjectCATAttributeCount * sizeof(CASEAuthTag);
222 : typedef uint8_t Serialized[kSerializedLength];
223 :
224 207 : CHIP_ERROR Serialize(Serialized & outSerialized) const
225 : {
226 207 : uint8_t * p = outSerialized;
227 828 : for (size_t i = 0; i < kMaxSubjectCATAttributeCount; i++)
228 : {
229 621 : Encoding::LittleEndian::Write32(p, values[i]);
230 : }
231 207 : return CHIP_NO_ERROR;
232 : }
233 :
234 317 : CHIP_ERROR Deserialize(const Serialized & inSerialized)
235 : {
236 317 : const uint8_t * p = inSerialized;
237 1268 : for (size_t i = 0; i < kMaxSubjectCATAttributeCount; i++)
238 : {
239 951 : values[i] = Encoding::LittleEndian::Read32(p);
240 : }
241 317 : return CHIP_NO_ERROR;
242 : }
243 : };
244 :
245 : static constexpr CATValues kUndefinedCATs = { { kUndefinedCAT } };
246 :
247 : } // namespace chip
|