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 : #include <credentials/GroupDataProviderImpl.h>
18 : #include <crypto/CHIPCryptoPAL.h>
19 : #include <lib/core/TLV.h>
20 : #include <lib/support/CodeUtils.h>
21 : #include <lib/support/CommonPersistentData.h>
22 : #include <lib/support/DefaultStorageKeyAllocator.h>
23 : #include <lib/support/PersistentData.h>
24 : #include <lib/support/Pool.h>
25 : #include <lib/support/logging/CHIPLogging.h>
26 : #include <stdlib.h>
27 :
28 : namespace chip {
29 : namespace Credentials {
30 :
31 : using GroupInfo = GroupDataProvider::GroupInfo;
32 : using GroupKey = GroupDataProvider::GroupKey;
33 : using GroupEndpoint = GroupDataProvider::GroupEndpoint;
34 : using EpochKey = GroupDataProvider::EpochKey;
35 : using KeySet = GroupDataProvider::KeySet;
36 : using GroupSession = GroupDataProvider::GroupSession;
37 :
38 : struct FabricList : public CommonPersistentData::FabricList
39 : {
40 1183 : CHIP_ERROR UpdateKey(StorageKeyName & key) const override
41 : {
42 1183 : key = DefaultStorageKeyAllocator::GroupFabricList();
43 1183 : return CHIP_NO_ERROR;
44 : }
45 : };
46 :
47 : constexpr size_t kPersistentBufferMax = 128;
48 :
49 : struct LinkedData : public PersistableData<kPersistentBufferMax>
50 : {
51 : static constexpr uint16_t kMinLinkId = 1;
52 :
53 70 : LinkedData() = default;
54 769 : LinkedData(uint16_t linked_id) : id(linked_id) {}
55 : uint16_t id = kMinLinkId;
56 : uint16_t index = 0;
57 : uint16_t next = 0;
58 : uint16_t prev = 0;
59 : uint16_t max_id = 0;
60 : bool first = true;
61 : };
62 :
63 : struct FabricData : public PersistableData<kPersistentBufferMax>
64 : {
65 7744 : static constexpr TLV::Tag TagFirstGroup() { return TLV::ContextTag(1); }
66 7744 : static constexpr TLV::Tag TagGroupCount() { return TLV::ContextTag(2); }
67 7744 : static constexpr TLV::Tag TagFirstMap() { return TLV::ContextTag(3); }
68 7744 : static constexpr TLV::Tag TagMapCount() { return TLV::ContextTag(4); }
69 7744 : static constexpr TLV::Tag TagFirstKeyset() { return TLV::ContextTag(5); }
70 7744 : static constexpr TLV::Tag TagKeysetCount() { return TLV::ContextTag(6); }
71 7744 : static constexpr TLV::Tag TagNext() { return TLV::ContextTag(7); }
72 :
73 : chip::FabricIndex fabric_index = kUndefinedFabricIndex;
74 : chip::GroupId first_group = kUndefinedGroupId;
75 : uint16_t group_count = 0;
76 : uint16_t first_map = 0;
77 : uint16_t map_count = 0;
78 : chip::KeysetId first_keyset = kInvalidKeysetId;
79 : uint16_t keyset_count = 0;
80 : chip::FabricIndex next = kUndefinedFabricIndex;
81 :
82 33 : FabricData() = default;
83 7329 : FabricData(chip::FabricIndex fabric) : fabric_index(fabric) {}
84 :
85 8385 : CHIP_ERROR UpdateKey(StorageKeyName & key) const override
86 : {
87 8385 : VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
88 8383 : key = DefaultStorageKeyAllocator::FabricGroups(fabric_index);
89 8383 : return CHIP_NO_ERROR;
90 : }
91 :
92 7374 : void Clear() override
93 : {
94 7374 : first_group = kUndefinedGroupId;
95 7374 : group_count = 0;
96 7374 : first_keyset = kInvalidKeysetId;
97 7374 : keyset_count = 0;
98 7374 : next = kUndefinedFabricIndex;
99 7374 : }
100 :
101 982 : CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
102 : {
103 : TLV::TLVType container;
104 982 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, container));
105 :
106 982 : ReturnErrorOnFailure(writer.Put(TagFirstGroup(), static_cast<uint16_t>(first_group)));
107 982 : ReturnErrorOnFailure(writer.Put(TagGroupCount(), static_cast<uint16_t>(group_count)));
108 982 : ReturnErrorOnFailure(writer.Put(TagFirstMap(), static_cast<uint16_t>(first_map)));
109 982 : ReturnErrorOnFailure(writer.Put(TagMapCount(), static_cast<uint16_t>(map_count)));
110 982 : ReturnErrorOnFailure(writer.Put(TagFirstKeyset(), static_cast<uint16_t>(first_keyset)));
111 982 : ReturnErrorOnFailure(writer.Put(TagKeysetCount(), static_cast<uint16_t>(keyset_count)));
112 982 : ReturnErrorOnFailure(writer.Put(TagNext(), static_cast<uint16_t>(next)));
113 :
114 982 : return writer.EndContainer(container);
115 : }
116 6762 : CHIP_ERROR Deserialize(TLV::TLVReader & reader) override
117 : {
118 6762 : ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag()));
119 6762 : VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_INTERNAL);
120 :
121 : TLV::TLVType container;
122 6762 : ReturnErrorOnFailure(reader.EnterContainer(container));
123 :
124 : // first_group
125 6762 : ReturnErrorOnFailure(reader.Next(TagFirstGroup()));
126 6762 : ReturnErrorOnFailure(reader.Get(first_group));
127 : // group_count
128 6762 : ReturnErrorOnFailure(reader.Next(TagGroupCount()));
129 6762 : ReturnErrorOnFailure(reader.Get(group_count));
130 : // first_map
131 6762 : ReturnErrorOnFailure(reader.Next(TagFirstMap()));
132 6762 : ReturnErrorOnFailure(reader.Get(first_map));
133 : // map_count
134 6762 : ReturnErrorOnFailure(reader.Next(TagMapCount()));
135 6762 : ReturnErrorOnFailure(reader.Get(map_count));
136 : // first_keyset
137 6762 : ReturnErrorOnFailure(reader.Next(TagFirstKeyset()));
138 6762 : ReturnErrorOnFailure(reader.Get(first_keyset));
139 : // keyset_count
140 6762 : ReturnErrorOnFailure(reader.Next(TagKeysetCount()));
141 6762 : ReturnErrorOnFailure(reader.Get(keyset_count));
142 : // next
143 6762 : ReturnErrorOnFailure(reader.Next(TagNext()));
144 6762 : ReturnErrorOnFailure(reader.Get(next));
145 :
146 6762 : return reader.ExitContainer(container);
147 : }
148 :
149 : // Register the fabric in the fabrics' linked-list
150 982 : CHIP_ERROR Register(PersistentStorageDelegate * storage)
151 : {
152 982 : FabricList fabric_list;
153 982 : CHIP_ERROR err = fabric_list.Load(storage);
154 1964 : if (CHIP_ERROR_NOT_FOUND == err)
155 : {
156 : // New fabric list
157 101 : fabric_list.first_entry = fabric_index;
158 101 : fabric_list.entry_count = 1;
159 101 : return fabric_list.Save(storage);
160 : }
161 881 : ReturnErrorOnFailure(err);
162 :
163 : // Existing fabric list, search for existing entry
164 881 : FabricData fabric(fabric_list.first_entry);
165 948 : for (size_t i = 0; i < fabric_list.entry_count; i++)
166 : {
167 918 : err = fabric.Load(storage);
168 1836 : if (CHIP_NO_ERROR != err)
169 : {
170 0 : break;
171 : }
172 918 : if (fabric.fabric_index == this->fabric_index)
173 : {
174 : // Fabric already registered
175 851 : return CHIP_NO_ERROR;
176 : }
177 67 : fabric.fabric_index = fabric.next;
178 : }
179 : // Add this fabric to the fabric list
180 30 : this->next = fabric_list.first_entry;
181 30 : fabric_list.first_entry = this->fabric_index;
182 30 : fabric_list.entry_count++;
183 30 : return fabric_list.Save(storage);
184 982 : }
185 :
186 : // Remove the fabric from the fabrics' linked list
187 33 : CHIP_ERROR Unregister(PersistentStorageDelegate * storage) const
188 : {
189 33 : FabricList fabric_list;
190 33 : CHIP_ERROR err = fabric_list.Load(storage);
191 68 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
192 :
193 : // Existing fabric list, search for existing entry
194 33 : FabricData fabric(fabric_list.first_entry);
195 33 : FabricData prev;
196 :
197 46 : for (size_t i = 0; i < fabric_list.entry_count; i++)
198 : {
199 40 : err = fabric.Load(storage);
200 80 : if (CHIP_NO_ERROR != err)
201 : {
202 0 : break;
203 : }
204 40 : if (fabric.fabric_index == this->fabric_index)
205 : {
206 : // Fabric found
207 27 : if (i == 0)
208 : {
209 : // Remove first fabric
210 15 : fabric_list.first_entry = this->next;
211 : }
212 : else
213 : {
214 : // Remove intermediate fabric
215 12 : prev.next = this->next;
216 12 : ReturnErrorOnFailure(prev.Save(storage));
217 : }
218 27 : VerifyOrReturnError(fabric_list.entry_count > 0, CHIP_ERROR_INTERNAL);
219 27 : fabric_list.entry_count--;
220 27 : return fabric_list.Save(storage);
221 : }
222 13 : prev = fabric;
223 13 : fabric.fabric_index = fabric.next;
224 : }
225 : // Fabric not in the list
226 6 : return CHIP_ERROR_NOT_FOUND;
227 33 : }
228 :
229 : // Check the fabric is registered in the fabrics' linked list
230 : CHIP_ERROR Validate(PersistentStorageDelegate * storage) const
231 : {
232 : FabricList fabric_list;
233 : ReturnErrorOnFailure(fabric_list.Load(storage));
234 :
235 : // Existing fabric list, search for existing entry
236 : FabricData fabric(fabric_list.first_entry);
237 :
238 : for (size_t i = 0; i < fabric_list.entry_count; i++)
239 : {
240 : ReturnErrorOnFailure(fabric.Load(storage));
241 : if (fabric.fabric_index == this->fabric_index)
242 : {
243 : return CHIP_NO_ERROR;
244 : }
245 : fabric.fabric_index = fabric.next;
246 : }
247 : // Fabric not in the list
248 : return CHIP_ERROR_NOT_FOUND;
249 : }
250 :
251 982 : CHIP_ERROR Save(PersistentStorageDelegate * storage) // NOLINT(bugprone-derived-method-shadowing-base-method)
252 : {
253 982 : ReturnErrorOnFailure(Register(storage));
254 982 : return PersistableData::Save(storage);
255 : }
256 :
257 33 : CHIP_ERROR Delete(PersistentStorageDelegate * storage) // NOLINT(bugprone-derived-method-shadowing-base-method)
258 : {
259 33 : ReturnErrorOnFailure(Unregister(storage));
260 27 : return PersistableData::Delete(storage);
261 : }
262 : };
263 :
264 : struct GroupData : public GroupDataProvider::GroupInfo, PersistableData<kPersistentBufferMax>
265 : {
266 10772 : static constexpr TLV::Tag TagName() { return TLV::ContextTag(1); }
267 10772 : static constexpr TLV::Tag TagFirstEndpoint() { return TLV::ContextTag(2); }
268 10772 : static constexpr TLV::Tag TagEndpointCount() { return TLV::ContextTag(3); }
269 10772 : static constexpr TLV::Tag TagNext() { return TLV::ContextTag(4); }
270 10772 : static constexpr TLV::Tag TagFlags() { return TLV::ContextTag(5); }
271 :
272 : chip::FabricIndex fabric_index = kUndefinedFabricIndex;
273 : chip::EndpointId first_endpoint = kInvalidEndpointId;
274 : uint16_t endpoint_count = 0;
275 : uint16_t index = 0;
276 : chip::GroupId next = 0;
277 : chip::GroupId prev = 0;
278 : bool first = true;
279 :
280 2416 : GroupData() : GroupInfo(nullptr){};
281 : GroupData(chip::FabricIndex fabric) : fabric_index(fabric) {}
282 5666 : GroupData(chip::FabricIndex fabric, chip::GroupId group) : GroupInfo(group, nullptr), fabric_index(fabric) {}
283 :
284 10842 : CHIP_ERROR UpdateKey(StorageKeyName & key) const override
285 : {
286 10842 : VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
287 10842 : key = DefaultStorageKeyAllocator::FabricGroup(fabric_index, group_id);
288 10842 : return CHIP_NO_ERROR;
289 : }
290 :
291 8963 : void Clear() override
292 : {
293 8963 : SetName(CharSpan());
294 8963 : first_endpoint = kInvalidEndpointId;
295 8963 : endpoint_count = 0;
296 8963 : next = 0;
297 8963 : flags = 0;
298 8963 : }
299 :
300 1810 : CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
301 : {
302 : TLV::TLVType container;
303 1810 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, container));
304 :
305 1810 : size_t name_size = strnlen(name, GroupDataProvider::GroupInfo::kGroupNameMax);
306 1810 : ReturnErrorOnFailure(writer.PutString(TagName(), name, static_cast<uint32_t>(name_size)));
307 1810 : ReturnErrorOnFailure(writer.Put(TagFirstEndpoint(), static_cast<uint16_t>(first_endpoint)));
308 1810 : ReturnErrorOnFailure(writer.Put(TagEndpointCount(), static_cast<uint16_t>(endpoint_count)));
309 1810 : ReturnErrorOnFailure(writer.Put(TagNext(), static_cast<uint16_t>(next)));
310 1810 : ReturnErrorOnFailure(writer.Put(TagFlags(), static_cast<uint8_t>(flags)));
311 1810 : return writer.EndContainer(container);
312 : }
313 :
314 8962 : CHIP_ERROR Deserialize(TLV::TLVReader & reader) override
315 : {
316 8962 : ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag()));
317 8962 : VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_INTERNAL);
318 :
319 : TLV::TLVType container;
320 8962 : ReturnErrorOnFailure(reader.EnterContainer(container));
321 :
322 : // name
323 8962 : ReturnErrorOnFailure(reader.Next(TagName()));
324 8962 : ReturnErrorOnFailure(reader.GetString(name, sizeof(name)));
325 8962 : size_t size = strnlen(name, kGroupNameMax);
326 8962 : name[size] = 0;
327 : // first_endpoint
328 8962 : ReturnErrorOnFailure(reader.Next(TagFirstEndpoint()));
329 8962 : ReturnErrorOnFailure(reader.Get(first_endpoint));
330 : // endpoint_count
331 8962 : ReturnErrorOnFailure(reader.Next(TagEndpointCount()));
332 8962 : ReturnErrorOnFailure(reader.Get(endpoint_count));
333 : // next
334 8962 : ReturnErrorOnFailure(reader.Next(TagNext()));
335 8962 : ReturnErrorOnFailure(reader.Get(next));
336 :
337 : // Groupcast
338 8962 : CHIP_ERROR err = reader.Next(TagFlags());
339 17924 : if (CHIP_NO_ERROR == err)
340 : {
341 : // flags
342 8962 : uint8_t value = 0;
343 8962 : ReturnErrorOnFailure(reader.Get(value));
344 8962 : flags = value;
345 : }
346 :
347 8962 : return reader.ExitContainer(container);
348 : }
349 :
350 118 : bool Get(PersistentStorageDelegate * storage, const FabricData & fabric, size_t target_index)
351 : {
352 118 : fabric_index = fabric.fabric_index;
353 118 : group_id = fabric.first_group;
354 118 : index = 0;
355 118 : first = true;
356 :
357 183 : while (index < fabric.group_count)
358 : {
359 320 : if (CHIP_NO_ERROR != Load(storage))
360 : {
361 0 : break;
362 : }
363 160 : if (index == target_index)
364 : {
365 : // Target index found
366 95 : return true;
367 : }
368 :
369 65 : first = false;
370 65 : prev = group_id;
371 65 : group_id = next;
372 65 : index++;
373 : }
374 :
375 23 : return false;
376 : }
377 :
378 2263 : bool Find(PersistentStorageDelegate * storage, const FabricData & fabric, chip::GroupId target_group)
379 : {
380 2263 : fabric_index = fabric.fabric_index;
381 2263 : group_id = fabric.first_group;
382 2263 : index = 0;
383 2263 : first = true;
384 :
385 3813 : while (index < fabric.group_count)
386 : {
387 6264 : if (CHIP_NO_ERROR != Load(storage))
388 : {
389 0 : break;
390 : }
391 3132 : if (group_id == target_group)
392 : {
393 : // Target index found
394 1582 : return true;
395 : }
396 1550 : first = false;
397 1550 : prev = group_id;
398 1550 : group_id = next;
399 1550 : index++;
400 : }
401 681 : return false;
402 : }
403 : };
404 :
405 : struct KeyMapData : public GroupDataProvider::GroupKey, LinkedData
406 : {
407 1773 : static constexpr TLV::Tag TagGroupId() { return TLV::ContextTag(1); }
408 1773 : static constexpr TLV::Tag TagKeysetId() { return TLV::ContextTag(2); }
409 1773 : static constexpr TLV::Tag TagNext() { return TLV::ContextTag(3); }
410 :
411 : chip::FabricIndex fabric_index = kUndefinedFabricIndex;
412 : chip::GroupId group_id = kUndefinedGroupId;
413 : chip::KeysetId keyset_id = 0;
414 :
415 70 : KeyMapData(){};
416 769 : KeyMapData(chip::FabricIndex fabric, uint16_t link_id = 0, chip::GroupId group = kUndefinedGroupId, chip::KeysetId keyset = 0) :
417 769 : GroupKey(group, keyset), LinkedData(link_id), fabric_index(fabric)
418 769 : {}
419 :
420 1796 : CHIP_ERROR UpdateKey(StorageKeyName & key) const override
421 : {
422 1796 : VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
423 1796 : key = DefaultStorageKeyAllocator::FabricGroupKey(fabric_index, id);
424 1796 : return CHIP_NO_ERROR;
425 : }
426 :
427 1228 : void Clear() override {}
428 :
429 545 : CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
430 : {
431 : TLV::TLVType container;
432 545 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, container));
433 :
434 545 : ReturnErrorOnFailure(writer.Put(TagGroupId(), static_cast<uint16_t>(group_id)));
435 545 : ReturnErrorOnFailure(writer.Put(TagKeysetId(), static_cast<uint16_t>(keyset_id)));
436 545 : ReturnErrorOnFailure(writer.Put(TagNext(), static_cast<uint16_t>(next)));
437 545 : return writer.EndContainer(container);
438 : }
439 :
440 1228 : CHIP_ERROR Deserialize(TLV::TLVReader & reader) override
441 : {
442 1228 : ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag()));
443 1228 : VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_INTERNAL);
444 :
445 : TLV::TLVType container;
446 1228 : ReturnErrorOnFailure(reader.EnterContainer(container));
447 :
448 : // first_endpoint
449 1228 : ReturnErrorOnFailure(reader.Next(TagGroupId()));
450 1228 : ReturnErrorOnFailure(reader.Get(group_id));
451 : // endpoint_count
452 1228 : ReturnErrorOnFailure(reader.Next(TagKeysetId()));
453 1228 : ReturnErrorOnFailure(reader.Get(keyset_id));
454 : // next
455 1228 : ReturnErrorOnFailure(reader.Next(TagNext()));
456 1228 : ReturnErrorOnFailure(reader.Get(next));
457 :
458 1228 : return reader.ExitContainer(container);
459 : }
460 :
461 340 : bool Get(PersistentStorageDelegate * storage, const FabricData & fabric, size_t target_index)
462 : {
463 340 : fabric_index = fabric.fabric_index;
464 340 : id = fabric.first_map;
465 340 : max_id = 0;
466 340 : index = 0;
467 340 : first = true;
468 :
469 698 : while (index < fabric.map_count)
470 : {
471 802 : if (CHIP_NO_ERROR != Load(storage))
472 : {
473 0 : break;
474 : }
475 401 : if (index == target_index)
476 : {
477 : // Target index found
478 43 : return true;
479 : }
480 358 : max_id = std::max(id, max_id);
481 358 : first = false;
482 358 : prev = id;
483 358 : id = next;
484 358 : index++;
485 : }
486 :
487 297 : id = static_cast<uint16_t>(max_id + 1);
488 297 : return false;
489 : }
490 :
491 304 : bool Find(PersistentStorageDelegate * storage, const FabricData & fabric, const GroupKey & map)
492 : {
493 304 : fabric_index = fabric.fabric_index;
494 304 : id = fabric.first_map;
495 304 : max_id = 0;
496 304 : index = 0;
497 304 : first = true;
498 :
499 618 : while (index < fabric.map_count)
500 : {
501 650 : if (CHIP_NO_ERROR != Load(storage))
502 : {
503 0 : break;
504 : }
505 325 : if ((group_id == map.group_id) && (keyset_id == map.keyset_id))
506 : {
507 : // Match found
508 11 : return true;
509 : }
510 314 : max_id = std::max(id, max_id);
511 314 : first = false;
512 314 : prev = id;
513 314 : id = next;
514 314 : index++;
515 : }
516 :
517 293 : id = static_cast<uint16_t>(max_id + 1);
518 293 : return false;
519 : }
520 :
521 : // returns index if the find_id is found, otherwise std::numeric_limits<size_t>::max
522 2 : size_t Find(PersistentStorageDelegate * storage, const FabricData & fabric, const KeysetId find_id)
523 : {
524 2 : fabric_index = fabric.fabric_index;
525 2 : id = fabric.first_map;
526 2 : max_id = 0;
527 2 : index = 0;
528 2 : first = true;
529 :
530 6 : while (index < fabric.map_count)
531 : {
532 10 : if (CHIP_NO_ERROR != Load(storage))
533 : {
534 0 : break;
535 : }
536 5 : if (keyset_id == find_id)
537 : {
538 : // Match found
539 1 : return index;
540 : }
541 4 : max_id = std::max(id, max_id);
542 4 : first = false;
543 4 : prev = id;
544 4 : id = next;
545 4 : index++;
546 : }
547 :
548 1 : id = static_cast<uint16_t>(max_id + 1);
549 1 : return std::numeric_limits<size_t>::max();
550 : }
551 : };
552 :
553 : struct EndpointData : GroupDataProvider::GroupEndpoint, PersistableData<kPersistentBufferMax>
554 : {
555 67084 : static constexpr TLV::Tag TagEndpoint() { return TLV::ContextTag(1); }
556 67084 : static constexpr TLV::Tag TagNext() { return TLV::ContextTag(2); }
557 :
558 : chip::FabricIndex fabric_index = kUndefinedFabricIndex;
559 : uint16_t index = 0;
560 : chip::EndpointId next = 0;
561 : chip::EndpointId prev = 0;
562 : bool first = true;
563 :
564 1432 : EndpointData() = default;
565 2217 : EndpointData(chip::FabricIndex fabric, chip::GroupId group = kUndefinedGroupId,
566 2217 : chip::EndpointId endpoint = kInvalidEndpointId) :
567 : GroupEndpoint(group, endpoint),
568 2217 : fabric_index(fabric)
569 2217 : {}
570 :
571 67551 : CHIP_ERROR UpdateKey(StorageKeyName & key) const override
572 : {
573 67551 : VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
574 67551 : key = DefaultStorageKeyAllocator::FabricGroupEndpoint(fabric_index, group_id, endpoint_id);
575 67551 : return CHIP_NO_ERROR;
576 : }
577 :
578 64265 : void Clear() override { next = kInvalidEndpointId; }
579 :
580 2842 : CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
581 : {
582 : TLV::TLVType container;
583 2842 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, container));
584 :
585 2842 : ReturnErrorOnFailure(writer.Put(TagEndpoint(), static_cast<uint16_t>(endpoint_id)));
586 2842 : ReturnErrorOnFailure(writer.Put(TagNext(), static_cast<uint16_t>(next)));
587 :
588 2842 : return writer.EndContainer(container);
589 : }
590 64242 : CHIP_ERROR Deserialize(TLV::TLVReader & reader) override
591 : {
592 64242 : ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag()));
593 64242 : VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_INTERNAL);
594 :
595 : TLV::TLVType container;
596 64242 : ReturnErrorOnFailure(reader.EnterContainer(container));
597 :
598 : // endpoint_id
599 64242 : ReturnErrorOnFailure(reader.Next(TagEndpoint()));
600 64242 : ReturnErrorOnFailure(reader.Get(endpoint_id));
601 : // next
602 64242 : ReturnErrorOnFailure(reader.Next(TagNext()));
603 64242 : ReturnErrorOnFailure(reader.Get(next));
604 :
605 64242 : return reader.ExitContainer(container);
606 : }
607 :
608 1417 : bool Find(PersistentStorageDelegate * storage, const FabricData & fabric, const GroupData & group, chip::EndpointId target_id)
609 : {
610 1417 : fabric_index = fabric.fabric_index;
611 1417 : group_id = group.group_id;
612 1417 : endpoint_id = group.first_endpoint;
613 1417 : index = 0;
614 1417 : first = true;
615 :
616 61753 : while (index < group.endpoint_count)
617 : {
618 121238 : if (CHIP_NO_ERROR != Load(storage))
619 : {
620 0 : break;
621 : }
622 60619 : if (this->endpoint_id == target_id)
623 : {
624 : // Match found
625 283 : return true;
626 : }
627 :
628 60336 : first = false;
629 60336 : prev = endpoint_id;
630 60336 : endpoint_id = next;
631 60336 : index++;
632 : }
633 :
634 1134 : return false;
635 : }
636 : };
637 :
638 : struct KeySetData : PersistableData<kPersistentBufferMax>
639 : {
640 859 : static constexpr TLV::Tag TagPolicy() { return TLV::ContextTag(1); }
641 859 : static constexpr TLV::Tag TagNumKeys() { return TLV::ContextTag(2); }
642 859 : static constexpr TLV::Tag TagGroupCredentials() { return TLV::ContextTag(3); }
643 2577 : static constexpr TLV::Tag TagStartTime() { return TLV::ContextTag(4); }
644 2577 : static constexpr TLV::Tag TagKeyHash() { return TLV::ContextTag(5); }
645 2577 : static constexpr TLV::Tag TagKeyValue() { return TLV::ContextTag(6); }
646 859 : static constexpr TLV::Tag TagNext() { return TLV::ContextTag(7); }
647 :
648 : chip::FabricIndex fabric_index = kUndefinedFabricIndex;
649 : chip::KeysetId next = kInvalidKeysetId;
650 : chip::KeysetId prev = kInvalidKeysetId;
651 : bool first = true;
652 :
653 : uint16_t keyset_id = 0;
654 : GroupDataProvider::SecurityPolicy policy = GroupDataProvider::SecurityPolicy::kTrustFirst;
655 : uint8_t keys_count = 0;
656 : Crypto::GroupOperationalCredentials operational_keys[KeySet::kEpochKeysMax];
657 :
658 539 : KeySetData() = default;
659 44 : KeySetData(chip::FabricIndex fabric, chip::KeysetId id) : fabric_index(fabric) { keyset_id = id; }
660 : KeySetData(chip::FabricIndex fabric, chip::KeysetId id, GroupDataProvider::SecurityPolicy policy_id, uint8_t num_keys) :
661 : fabric_index(fabric), keyset_id(id), policy(policy_id), keys_count(num_keys)
662 : {}
663 :
664 886 : CHIP_ERROR UpdateKey(StorageKeyName & key) const override
665 : {
666 886 : VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
667 886 : VerifyOrReturnError(kInvalidKeysetId != keyset_id, CHIP_ERROR_INVALID_KEY_ID);
668 886 : key = DefaultStorageKeyAllocator::FabricKeyset(fabric_index, keyset_id);
669 886 : return CHIP_NO_ERROR;
670 : }
671 :
672 596 : void Clear() override
673 : {
674 596 : policy = GroupDataProvider::SecurityPolicy::kTrustFirst;
675 596 : keys_count = 0;
676 596 : Crypto::ClearSecretData(reinterpret_cast<uint8_t *>(operational_keys), sizeof(operational_keys));
677 596 : next = kInvalidKeysetId;
678 596 : }
679 :
680 4 : Crypto::GroupOperationalCredentials * GetCurrentGroupCredentials()
681 : {
682 : // An epoch key update SHALL order the keys from oldest to newest,
683 : // the current epoch key having the second newest time if time
684 : // synchronization is not achieved or guaranteed.
685 4 : switch (this->keys_count)
686 : {
687 1 : case 1:
688 : case 2:
689 1 : return &operational_keys[0];
690 3 : case 3:
691 3 : return &operational_keys[1];
692 0 : default:
693 0 : return nullptr;
694 : }
695 : }
696 :
697 263 : CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
698 : {
699 : TLV::TLVType container;
700 263 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, container));
701 :
702 : // policy
703 263 : ReturnErrorOnFailure(writer.Put(TagPolicy(), static_cast<uint16_t>(policy)));
704 : // keys_count
705 263 : ReturnErrorOnFailure(writer.Put(TagNumKeys(), static_cast<uint16_t>(keys_count)));
706 : // operational_keys
707 : {
708 : TLV::TLVType array, item;
709 263 : ReturnErrorOnFailure(writer.StartContainer(TagGroupCredentials(), TLV::kTLVType_Array, array));
710 263 : uint8_t keyCount = 0;
711 263 : uint64_t startTime = 0;
712 263 : uint16_t hash = 0;
713 : uint8_t encryptionKey[Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES];
714 1052 : for (auto & key : operational_keys)
715 : {
716 789 : startTime = 0;
717 789 : hash = 0;
718 789 : memset(encryptionKey, 0, sizeof(encryptionKey));
719 789 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, item));
720 :
721 789 : if (keyCount++ < keys_count)
722 : {
723 659 : startTime = key.start_time;
724 659 : hash = key.hash;
725 659 : memcpy(encryptionKey, key.encryption_key, sizeof(encryptionKey));
726 : }
727 789 : ReturnErrorOnFailure(writer.Put(TagStartTime(), static_cast<uint64_t>(startTime)));
728 789 : ReturnErrorOnFailure(writer.Put(TagKeyHash(), hash));
729 789 : ReturnErrorOnFailure(writer.Put(TagKeyValue(), ByteSpan(encryptionKey)));
730 :
731 789 : ReturnErrorOnFailure(writer.EndContainer(item));
732 : }
733 263 : ReturnErrorOnFailure(writer.EndContainer(array));
734 : }
735 : // next keyset
736 263 : ReturnErrorOnFailure(writer.Put(TagNext(), static_cast<uint16_t>(next)));
737 :
738 263 : return writer.EndContainer(container);
739 : }
740 :
741 596 : CHIP_ERROR Deserialize(TLV::TLVReader & reader) override
742 : {
743 596 : ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag()));
744 596 : VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_INTERNAL);
745 :
746 : TLV::TLVType container;
747 596 : ReturnErrorOnFailure(reader.EnterContainer(container));
748 :
749 : // policy
750 596 : ReturnErrorOnFailure(reader.Next(TagPolicy()));
751 596 : ReturnErrorOnFailure(reader.Get(policy));
752 : // CacheAndSync is unimplemented, and should not have been used as the default security policy.
753 : // If a KeySet was saved with this policy, we re-map it here to be TrustFirst.
754 596 : if (policy == GroupDataProvider::SecurityPolicy::kCacheAndSync)
755 : {
756 3 : ChipLogDetail(NotSpecified,
757 : "Re-mapping security policy from CacheAndSync to TrustFirst for keyset %u (fabric index %u)", keyset_id,
758 : fabric_index);
759 3 : policy = GroupDataProvider::SecurityPolicy::kTrustFirst;
760 : }
761 : // keys_count
762 596 : ReturnErrorOnFailure(reader.Next(TagNumKeys()));
763 596 : ReturnErrorOnFailure(reader.Get(keys_count));
764 : // TODO(#21614): Enforce maximum number of 3 keys in a keyset
765 : {
766 : // operational_keys
767 596 : ReturnErrorOnFailure(reader.Next(TagGroupCredentials()));
768 596 : VerifyOrReturnError(TLV::kTLVType_Array == reader.GetType(), CHIP_ERROR_INTERNAL);
769 :
770 : TLV::TLVType array, item;
771 596 : ReturnErrorOnFailure(reader.EnterContainer(array));
772 2384 : for (auto & key : operational_keys)
773 : {
774 1788 : ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag()));
775 1788 : VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_INTERNAL);
776 :
777 1788 : ReturnErrorOnFailure(reader.EnterContainer(item));
778 : // start_time
779 1788 : ReturnErrorOnFailure(reader.Next(TagStartTime()));
780 1788 : ReturnErrorOnFailure(reader.Get(key.start_time));
781 : // key hash
782 1788 : ReturnErrorOnFailure(reader.Next(TagKeyHash()));
783 1788 : ReturnErrorOnFailure(reader.Get(key.hash));
784 : // key value
785 1788 : ByteSpan encryption_key;
786 1788 : ReturnErrorOnFailure(reader.Next(TagKeyValue()));
787 1788 : ReturnErrorOnFailure(reader.Get(encryption_key));
788 1788 : VerifyOrReturnError(Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES == encryption_key.size(), CHIP_ERROR_INTERNAL);
789 1788 : memcpy(key.encryption_key, encryption_key.data(), encryption_key.size());
790 : // Re-derive privacy key from encryption key when loading from storage to save on storage size.
791 1788 : MutableByteSpan privacy_key(key.privacy_key);
792 1788 : ReturnErrorOnFailure(Crypto::DeriveGroupPrivacyKey(encryption_key, privacy_key));
793 1788 : ReturnErrorOnFailure(reader.ExitContainer(item));
794 : }
795 596 : ReturnErrorOnFailure(reader.ExitContainer(array));
796 : }
797 : // next keyset
798 596 : ReturnErrorOnFailure(reader.Next(TagNext()));
799 596 : ReturnErrorOnFailure(reader.Get(next));
800 :
801 596 : return reader.ExitContainer(container);
802 : }
803 :
804 521 : bool Find(PersistentStorageDelegate * storage, const FabricData & fabric, size_t target_id)
805 : {
806 521 : uint16_t count = 0;
807 :
808 521 : fabric_index = fabric.fabric_index;
809 521 : keyset_id = fabric.first_keyset;
810 521 : first = true;
811 :
812 847 : while (count++ < fabric.keyset_count)
813 : {
814 1136 : if (CHIP_NO_ERROR != Load(storage))
815 : {
816 0 : break;
817 : }
818 :
819 568 : if (keyset_id == target_id)
820 : {
821 : // Target id found
822 242 : return true;
823 : }
824 :
825 326 : first = false;
826 326 : prev = keyset_id;
827 326 : keyset_id = next;
828 : }
829 :
830 279 : return false;
831 : }
832 : };
833 :
834 : //
835 : // General
836 : //
837 :
838 : constexpr size_t GroupDataProvider::GroupInfo::kGroupNameMax;
839 : constexpr size_t GroupDataProviderImpl::kIteratorsMax;
840 :
841 118 : CHIP_ERROR GroupDataProviderImpl::Init()
842 : {
843 118 : if (mStorage == nullptr || mSessionKeystore == nullptr)
844 : {
845 0 : return CHIP_ERROR_INCORRECT_STATE;
846 : }
847 118 : return CHIP_NO_ERROR;
848 : }
849 :
850 112 : void GroupDataProviderImpl::Finish()
851 : {
852 112 : mGroupInfoIterators.ReleaseAll();
853 112 : mGroupKeyIterators.ReleaseAll();
854 112 : mEndpointIterators.ReleaseAll();
855 112 : mKeySetIterators.ReleaseAll();
856 112 : mGroupSessionsIterator.ReleaseAll();
857 112 : mGroupKeyContexPool.ReleaseAll();
858 112 : }
859 :
860 118 : void GroupDataProviderImpl::SetStorageDelegate(PersistentStorageDelegate * storage)
861 : {
862 118 : VerifyOrDie(storage != nullptr);
863 118 : mStorage = storage;
864 118 : }
865 :
866 : //
867 : // Group Info
868 : //
869 :
870 327 : CHIP_ERROR GroupDataProviderImpl::SetGroupInfo(chip::FabricIndex fabric_index, const GroupInfo & info)
871 : {
872 327 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
873 :
874 327 : FabricData fabric(fabric_index);
875 327 : GroupData group;
876 :
877 : // Load fabric data (defaults to zero)
878 327 : CHIP_ERROR err = fabric.Load(mStorage);
879 720 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
880 :
881 327 : if (group.Find(mStorage, fabric, info.group_id))
882 : {
883 : // Existing group_id
884 48 : if (IsGroupcastEnabled() && group.endpoint_count > 0 && (group.HasAuxiliaryACL() != info.HasAuxiliaryACL()))
885 : {
886 4 : mAuxAclNotificationNeeded = true;
887 : }
888 :
889 48 : group.Copy(info);
890 48 : ReturnErrorOnFailure(group.Save(mStorage));
891 48 : GroupModified(fabric_index, info.group_id);
892 48 : return CHIP_NO_ERROR;
893 : }
894 :
895 : // New group_id
896 279 : group.Copy(info);
897 279 : return SetGroupInfoAt(fabric_index, fabric.group_count, group);
898 327 : }
899 :
900 192 : CHIP_ERROR GroupDataProviderImpl::GetGroupInfo(chip::FabricIndex fabric_index, chip::GroupId group_id, GroupInfo & info)
901 : {
902 192 : FabricData fabric(fabric_index);
903 192 : GroupData group;
904 :
905 192 : ReturnErrorOnFailure(fabric.Load(mStorage));
906 176 : info.count = fabric.group_count;
907 176 : VerifyOrReturnError(group.Find(mStorage, fabric, group_id), CHIP_ERROR_NOT_FOUND);
908 :
909 102 : info.Copy(group);
910 102 : return CHIP_NO_ERROR;
911 192 : }
912 :
913 9 : CHIP_ERROR GroupDataProviderImpl::RemoveGroupInfo(chip::FabricIndex fabric_index, chip::GroupId group_id)
914 : {
915 9 : FabricData fabric(fabric_index);
916 9 : GroupData group;
917 :
918 9 : ReturnErrorOnFailure(fabric.Load(mStorage));
919 9 : VerifyOrReturnError(group.Find(mStorage, fabric, group_id), CHIP_ERROR_NOT_FOUND);
920 :
921 9 : return RemoveGroupInfoAt(fabric_index, group.index);
922 9 : }
923 :
924 321 : CHIP_ERROR GroupDataProviderImpl::SetGroupInfoAt(chip::FabricIndex fabric_index, size_t index, const GroupInfo & info)
925 : {
926 321 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
927 :
928 321 : FabricData fabric(fabric_index);
929 321 : GroupData group;
930 :
931 : // Load fabric, defaults to zero
932 321 : CHIP_ERROR err = fabric.Load(mStorage);
933 718 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
934 :
935 : // If the group exists, the index must match
936 321 : bool found = group.Find(mStorage, fabric, info.group_id);
937 321 : VerifyOrReturnError(!found || (group.index == index), CHIP_ERROR_DUPLICATE_KEY_ID);
938 :
939 317 : group.Copy(info);
940 317 : group.endpoint_count = 0;
941 :
942 317 : if (found)
943 : {
944 : // Update existing entry
945 7 : if (IsGroupcastEnabled() && group.endpoint_count > 0 && (group.HasAuxiliaryACL() != info.HasAuxiliaryACL()))
946 : {
947 0 : mAuxAclNotificationNeeded = true;
948 : }
949 7 : return group.Save(mStorage);
950 : }
951 310 : if (index < fabric.group_count)
952 : {
953 : // Replace existing entry with a new group
954 1 : GroupData old_group;
955 1 : old_group.Get(mStorage, fabric, index);
956 1 : group.first = old_group.first;
957 1 : group.prev = old_group.prev;
958 1 : group.next = old_group.next;
959 :
960 1 : ReturnErrorOnFailure(RemoveEndpoints(fabric_index, old_group.group_id));
961 1 : ReturnErrorOnFailure(old_group.Delete(mStorage));
962 1 : GroupRemoved(fabric_index, old_group);
963 1 : }
964 : else
965 : {
966 : // Insert last
967 309 : VerifyOrReturnError(fabric.group_count == index, CHIP_ERROR_INVALID_ARGUMENT);
968 308 : VerifyOrReturnError(fabric.group_count < GetMaxGroupsPerFabric(), CHIP_ERROR_INVALID_LIST_LENGTH);
969 306 : fabric.group_count++;
970 : }
971 :
972 307 : ReturnErrorOnFailure(group.Save(mStorage));
973 :
974 307 : if (group.first)
975 : {
976 : // First group, update fabric
977 116 : fabric.first_group = group.group_id;
978 : }
979 : else
980 : {
981 : // Second to last group, update previous
982 191 : GroupData prev(fabric_index, group.prev);
983 191 : ReturnErrorOnFailure(prev.Load(mStorage));
984 191 : prev.next = group.group_id;
985 191 : ReturnErrorOnFailure(prev.Save(mStorage));
986 191 : }
987 : // Update fabric
988 307 : ReturnErrorOnFailure(fabric.Save(mStorage));
989 307 : GroupAdded(fabric_index, group);
990 307 : return CHIP_NO_ERROR;
991 321 : }
992 :
993 57 : CHIP_ERROR GroupDataProviderImpl::GetGroupInfoAt(chip::FabricIndex fabric_index, size_t index, GroupInfo & info)
994 : {
995 57 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
996 :
997 57 : FabricData fabric(fabric_index);
998 57 : GroupData group;
999 :
1000 57 : ReturnErrorOnFailure(fabric.Load(mStorage));
1001 49 : VerifyOrReturnError(group.Get(mStorage, fabric, index), CHIP_ERROR_NOT_FOUND);
1002 :
1003 : // Target group found
1004 26 : info.Copy(group);
1005 26 : return CHIP_NO_ERROR;
1006 57 : }
1007 :
1008 68 : CHIP_ERROR GroupDataProviderImpl::RemoveGroupInfoAt(chip::FabricIndex fabric_index, size_t index)
1009 : {
1010 68 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
1011 :
1012 68 : FabricData fabric(fabric_index);
1013 68 : GroupData group;
1014 :
1015 68 : ReturnErrorOnFailure(fabric.Load(mStorage));
1016 68 : VerifyOrReturnError(group.Get(mStorage, fabric, index), CHIP_ERROR_NOT_FOUND);
1017 :
1018 68 : bool notifyNeeded = (IsGroupcastEnabled() && group.HasAuxiliaryACL() && group.endpoint_count > 0);
1019 :
1020 : // Remove endpoints
1021 68 : EndpointData endpoint(fabric_index, group.group_id, group.first_endpoint);
1022 68 : size_t count = 0;
1023 336 : while (count++ < group.endpoint_count)
1024 : {
1025 582 : if (CHIP_NO_ERROR != endpoint.Load(mStorage))
1026 : {
1027 23 : break;
1028 : }
1029 268 : TEMPORARY_RETURN_IGNORED endpoint.Delete(mStorage);
1030 268 : endpoint.endpoint_id = endpoint.next;
1031 : }
1032 :
1033 68 : ReturnErrorOnFailure(group.Delete(mStorage));
1034 :
1035 68 : if (notifyNeeded)
1036 : {
1037 25 : mAuxAclNotificationNeeded = true;
1038 : }
1039 :
1040 68 : if (group.first)
1041 : {
1042 : // Remove first group
1043 43 : fabric.first_group = group.next;
1044 : }
1045 : else
1046 : {
1047 : // Remove intermediate group, update previous
1048 25 : GroupData prev_data(fabric_index, group.prev);
1049 25 : ReturnErrorOnFailure(prev_data.Load(mStorage));
1050 25 : prev_data.next = group.next;
1051 25 : ReturnErrorOnFailure(prev_data.Save(mStorage));
1052 25 : }
1053 68 : if (fabric.group_count > 0)
1054 : {
1055 68 : fabric.group_count--;
1056 : }
1057 : // Update fabric info
1058 68 : ReturnErrorOnFailure(fabric.Save(mStorage));
1059 68 : GroupRemoved(fabric_index, group);
1060 68 : return CHIP_NO_ERROR;
1061 68 : }
1062 :
1063 176 : bool GroupDataProviderImpl::HasEndpoint(chip::FabricIndex fabric_index, chip::GroupId group_id, chip::EndpointId endpoint_id)
1064 : {
1065 176 : VerifyOrReturnError(IsInitialized(), false);
1066 :
1067 176 : FabricData fabric(fabric_index);
1068 176 : GroupData group;
1069 176 : EndpointData endpoint;
1070 :
1071 352 : VerifyOrReturnError(CHIP_NO_ERROR == fabric.Load(mStorage), false);
1072 165 : VerifyOrReturnError(group.Find(mStorage, fabric, group_id), false);
1073 157 : return endpoint.Find(mStorage, fabric, group, endpoint_id);
1074 176 : }
1075 :
1076 1141 : CHIP_ERROR GroupDataProviderImpl::AddEndpoint(chip::FabricIndex fabric_index, chip::GroupId group_id, chip::EndpointId endpoint_id)
1077 : {
1078 1141 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
1079 :
1080 1141 : FabricData fabric(fabric_index);
1081 1141 : GroupData group;
1082 :
1083 : // Load fabric data (defaults to zero)
1084 1141 : CHIP_ERROR err = fabric.Load(mStorage);
1085 2286 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
1086 :
1087 1141 : if (!group.Find(mStorage, fabric, group_id))
1088 : {
1089 : // New group
1090 9 : VerifyOrReturnError(fabric.group_count < GetMaxGroupsPerFabric(), CHIP_ERROR_INVALID_LIST_LENGTH);
1091 9 : ReturnErrorOnFailure(EndpointData(fabric_index, group_id, endpoint_id).Save(mStorage));
1092 : // Save the new group into the fabric
1093 9 : group.group_id = group_id;
1094 9 : group.name[0] = 0;
1095 9 : group.first_endpoint = endpoint_id;
1096 9 : group.endpoint_count = 1;
1097 9 : group.next = fabric.first_group;
1098 9 : group.prev = kUndefinedGroupId;
1099 9 : ReturnErrorOnFailure(group.Save(mStorage));
1100 :
1101 9 : if (IsGroupcastEnabled() && group.HasAuxiliaryACL())
1102 : {
1103 0 : mAuxAclNotificationNeeded = true;
1104 : }
1105 :
1106 : // Update fabric
1107 9 : fabric.first_group = group.group_id;
1108 9 : fabric.group_count++;
1109 9 : ReturnErrorOnFailure(fabric.Save(mStorage));
1110 9 : GroupAdded(fabric_index, group);
1111 9 : return CHIP_NO_ERROR;
1112 : }
1113 :
1114 : // Existing group
1115 1132 : EndpointData endpoint;
1116 1132 : VerifyOrReturnError(!endpoint.Find(mStorage, fabric, group, endpoint_id), CHIP_NO_ERROR);
1117 :
1118 : // New endpoint, insert last
1119 1124 : endpoint.endpoint_id = endpoint_id;
1120 1124 : ReturnErrorOnFailure(endpoint.Save(mStorage));
1121 :
1122 1124 : if (IsGroupcastEnabled() && group.HasAuxiliaryACL())
1123 : {
1124 722 : mAuxAclNotificationNeeded = true;
1125 : }
1126 :
1127 1124 : if (endpoint.first)
1128 : {
1129 : // First endpoint of group
1130 283 : group.first_endpoint = endpoint.endpoint_id;
1131 : }
1132 : else
1133 : {
1134 : // Previous endpoint(s)
1135 841 : ReturnErrorOnFailure(endpoint.Save(mStorage));
1136 841 : EndpointData prev(fabric_index, group.group_id, endpoint.prev);
1137 841 : ReturnErrorOnFailure(prev.Load(mStorage));
1138 841 : prev.next = endpoint.endpoint_id;
1139 841 : ReturnErrorOnFailure(prev.Save(mStorage));
1140 841 : }
1141 1124 : group.endpoint_count++;
1142 1124 : ReturnErrorOnFailure(group.Save(mStorage));
1143 1124 : GroupModified(fabric_index, group.group_id);
1144 1124 : return CHIP_NO_ERROR;
1145 1141 : }
1146 :
1147 120 : CHIP_ERROR GroupDataProviderImpl::RemoveEndpoint(chip::FabricIndex fabric_index, chip::GroupId group_id,
1148 : chip::EndpointId endpoint_id, GroupCleanupPolicy cleanupPolicy)
1149 : {
1150 120 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
1151 :
1152 120 : FabricData fabric(fabric_index);
1153 120 : GroupData group;
1154 120 : EndpointData endpoint;
1155 :
1156 120 : ReturnErrorOnFailure(fabric.Load(mStorage));
1157 120 : VerifyOrReturnError(group.Find(mStorage, fabric, group_id), CHIP_ERROR_NOT_FOUND);
1158 119 : VerifyOrReturnError(endpoint.Find(mStorage, fabric, group, endpoint_id), CHIP_ERROR_NOT_FOUND);
1159 :
1160 : // Existing endpoint
1161 118 : TEMPORARY_RETURN_IGNORED endpoint.Delete(mStorage);
1162 :
1163 118 : if (IsGroupcastEnabled() && group.HasAuxiliaryACL())
1164 : {
1165 91 : mAuxAclNotificationNeeded = true;
1166 : }
1167 :
1168 118 : if (endpoint.first)
1169 : {
1170 : // Remove first
1171 91 : group.first_endpoint = endpoint.next;
1172 : }
1173 : else
1174 : {
1175 : // Remove middle
1176 27 : EndpointData prev(fabric_index, group.group_id, endpoint.prev);
1177 27 : ReturnErrorOnFailure(prev.Load(mStorage));
1178 27 : prev.next = endpoint.next;
1179 27 : ReturnErrorOnFailure(prev.Save(mStorage));
1180 27 : }
1181 :
1182 : // Check if we should keep the group with no endpoints or not(Groupcast Sender usecase)
1183 118 : uint16_t kGroupEndpointCountMin = (cleanupPolicy == GroupCleanupPolicy::kKeepGroupIfEmpty) ? 0 : 1;
1184 118 : if (group.endpoint_count > kGroupEndpointCountMin)
1185 : {
1186 95 : group.endpoint_count--;
1187 95 : ReturnErrorOnFailure(group.Save(mStorage));
1188 95 : GroupModified(fabric_index, group.group_id);
1189 95 : return CHIP_NO_ERROR;
1190 : }
1191 :
1192 : // No more endpoints and empty groups are not allowed: remove the group.
1193 23 : return RemoveGroupInfoAt(fabric_index, group.index);
1194 120 : }
1195 :
1196 11 : CHIP_ERROR GroupDataProviderImpl::RemoveEndpoint(chip::FabricIndex fabric_index, chip::GroupId group_id,
1197 : chip::EndpointId endpoint_id)
1198 : {
1199 11 : return RemoveEndpoint(fabric_index, group_id, endpoint_id, GroupCleanupPolicy::kDeleteGroupIfEmpty);
1200 : }
1201 :
1202 5 : CHIP_ERROR GroupDataProviderImpl::RemoveEndpointAllGroups(chip::FabricIndex fabric_index, chip::EndpointId endpoint_id,
1203 : GroupCleanupPolicy cleanupPolicy)
1204 : {
1205 5 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
1206 :
1207 5 : FabricData fabric(fabric_index);
1208 :
1209 5 : ReturnErrorOnFailure(fabric.Load(mStorage));
1210 :
1211 4 : GroupData group(fabric_index, fabric.first_group);
1212 4 : size_t group_index = 0;
1213 4 : EndpointData endpoint;
1214 :
1215 : // Loop through all the groups
1216 13 : while (group_index < fabric.group_count)
1217 : {
1218 18 : if (CHIP_NO_ERROR != group.Load(mStorage))
1219 : {
1220 0 : break;
1221 : }
1222 9 : if (endpoint.Find(mStorage, fabric, group, endpoint_id))
1223 : {
1224 : // Endpoint found in group
1225 9 : ReturnErrorOnFailure(RemoveEndpoint(fabric_index, group.group_id, endpoint_id, cleanupPolicy));
1226 : }
1227 :
1228 9 : group.group_id = group.next;
1229 9 : group_index++;
1230 : }
1231 :
1232 4 : return CHIP_NO_ERROR;
1233 5 : }
1234 :
1235 5 : CHIP_ERROR GroupDataProviderImpl::RemoveEndpoint(chip::FabricIndex fabric_index, chip::EndpointId endpoint_id)
1236 : {
1237 5 : return RemoveEndpointAllGroups(fabric_index, endpoint_id, GroupCleanupPolicy::kDeleteGroupIfEmpty);
1238 : }
1239 :
1240 2828 : GroupDataProvider::GroupInfoIterator * GroupDataProviderImpl::IterateGroupInfo(chip::FabricIndex fabric_index)
1241 : {
1242 2828 : VerifyOrReturnError(IsInitialized(), nullptr);
1243 2828 : return mGroupInfoIterators.CreateObject(*this, fabric_index);
1244 : }
1245 :
1246 2828 : GroupDataProviderImpl::GroupInfoIteratorImpl::GroupInfoIteratorImpl(GroupDataProviderImpl & provider,
1247 2828 : chip::FabricIndex fabric_index) :
1248 2828 : mProvider(provider),
1249 2828 : mFabric(fabric_index)
1250 : {
1251 2828 : FabricData fabric(fabric_index);
1252 5656 : if (CHIP_NO_ERROR == fabric.Load(provider.mStorage))
1253 : {
1254 2483 : mNextId = fabric.first_group;
1255 2483 : mTotal = fabric.group_count;
1256 2483 : mCount = 0;
1257 : }
1258 2828 : }
1259 :
1260 87 : size_t GroupDataProviderImpl::GroupInfoIteratorImpl::Count()
1261 : {
1262 87 : return mTotal;
1263 : }
1264 :
1265 6793 : bool GroupDataProviderImpl::GroupInfoIteratorImpl::Next(GroupInfo & output)
1266 : {
1267 6793 : VerifyOrReturnError(mCount < mTotal, false);
1268 :
1269 4047 : GroupData group(mFabric, mNextId);
1270 4047 : CHIP_ERROR err = group.Load(mProvider.mStorage);
1271 8094 : VerifyOrReturnError(CHIP_NO_ERROR == err, false);
1272 :
1273 4047 : mCount++;
1274 4047 : mNextId = group.next;
1275 4047 : output.Copy(group);
1276 4047 : return true;
1277 4047 : }
1278 :
1279 2828 : void GroupDataProviderImpl::GroupInfoIteratorImpl::Release()
1280 : {
1281 2828 : mProvider.mGroupInfoIterators.ReleaseObject(this);
1282 2828 : }
1283 :
1284 61 : GroupDataProvider::EndpointIterator * GroupDataProviderImpl::IterateEndpoints(chip::FabricIndex fabric_index,
1285 : std::optional<GroupId> group_id)
1286 : {
1287 61 : VerifyOrReturnError(IsInitialized(), nullptr);
1288 61 : return mEndpointIterators.CreateObject(*this, fabric_index, group_id);
1289 : }
1290 :
1291 61 : GroupDataProviderImpl::EndpointIteratorImpl::EndpointIteratorImpl(GroupDataProviderImpl & provider, chip::FabricIndex fabric_index,
1292 61 : std::optional<GroupId> group_id) :
1293 61 : mProvider(provider),
1294 61 : mFabric(fabric_index)
1295 : {
1296 61 : FabricData fabric(fabric_index);
1297 122 : VerifyOrReturn(CHIP_NO_ERROR == fabric.Load(provider.mStorage));
1298 :
1299 59 : if (group_id.has_value())
1300 : {
1301 45 : GroupData group(fabric_index, *group_id);
1302 90 : VerifyOrReturn(CHIP_NO_ERROR == group.Load(provider.mStorage));
1303 :
1304 44 : mGroup = *group_id;
1305 44 : mFirstGroup = *group_id;
1306 44 : mGroupCount = 1;
1307 44 : mEndpoint = group.first_endpoint;
1308 44 : mEndpointCount = group.endpoint_count;
1309 45 : }
1310 : else
1311 : {
1312 14 : GroupData group(fabric_index, fabric.first_group);
1313 28 : VerifyOrReturn(CHIP_NO_ERROR == group.Load(provider.mStorage));
1314 :
1315 14 : mGroup = fabric.first_group;
1316 14 : mFirstGroup = fabric.first_group;
1317 14 : mGroupCount = fabric.group_count;
1318 14 : mEndpoint = group.first_endpoint;
1319 14 : mEndpointCount = group.endpoint_count;
1320 14 : }
1321 61 : }
1322 :
1323 43 : size_t GroupDataProviderImpl::EndpointIteratorImpl::Count()
1324 : {
1325 43 : GroupData group(mFabric, mFirstGroup);
1326 43 : size_t group_index = 0;
1327 43 : size_t endpoint_index = 0;
1328 43 : size_t count = 0;
1329 :
1330 86 : while (group_index++ < mGroupCount)
1331 : {
1332 86 : if (CHIP_NO_ERROR != group.Load(mProvider.mStorage))
1333 : {
1334 0 : break;
1335 : }
1336 43 : EndpointData endpoint(mFabric, group.group_id, group.first_endpoint);
1337 1247 : while (endpoint_index++ < group.endpoint_count)
1338 : {
1339 2408 : if (CHIP_NO_ERROR != endpoint.Load(mProvider.mStorage))
1340 : {
1341 0 : break;
1342 : }
1343 1204 : endpoint.endpoint_id = endpoint.next;
1344 1204 : count++;
1345 : }
1346 43 : group.group_id = group.next;
1347 43 : endpoint_index = 0;
1348 43 : }
1349 86 : return count;
1350 43 : }
1351 :
1352 1281 : bool GroupDataProviderImpl::EndpointIteratorImpl::Next(GroupEndpoint & output)
1353 : {
1354 1353 : while (mGroupIndex < mGroupCount)
1355 : {
1356 1297 : GroupData group(mFabric, mGroup);
1357 2594 : if (CHIP_NO_ERROR != group.Load(mProvider.mStorage))
1358 : {
1359 0 : mGroupIndex = mGroupCount;
1360 0 : return false;
1361 : }
1362 1297 : if (mFirstEndpoint)
1363 : {
1364 72 : mEndpoint = group.first_endpoint;
1365 72 : mEndpointIndex = 0;
1366 72 : mEndpointCount = group.endpoint_count;
1367 72 : mFirstEndpoint = false;
1368 : }
1369 1297 : if (mEndpointIndex < mEndpointCount)
1370 : {
1371 1225 : EndpointData endpoint(mFabric, mGroup, mEndpoint);
1372 2450 : if (CHIP_NO_ERROR == endpoint.Load(mProvider.mStorage))
1373 : {
1374 1225 : output.group_id = group.group_id;
1375 1225 : output.endpoint_id = endpoint.endpoint_id;
1376 1225 : mEndpoint = endpoint.next;
1377 1225 : mEndpointIndex++;
1378 1225 : return true;
1379 : }
1380 1225 : }
1381 72 : mGroup = group.next;
1382 72 : mGroupIndex++;
1383 72 : mFirstEndpoint = true;
1384 1297 : }
1385 56 : return false;
1386 : }
1387 :
1388 61 : void GroupDataProviderImpl::EndpointIteratorImpl::Release()
1389 : {
1390 61 : mProvider.mEndpointIterators.ReleaseObject(this);
1391 61 : }
1392 :
1393 4 : CHIP_ERROR GroupDataProviderImpl::RemoveEndpoints(chip::FabricIndex fabric_index, chip::GroupId group_id)
1394 : {
1395 4 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
1396 :
1397 4 : FabricData fabric(fabric_index);
1398 4 : GroupData group;
1399 :
1400 8 : VerifyOrReturnError(CHIP_NO_ERROR == fabric.Load(mStorage), CHIP_ERROR_INVALID_FABRIC_INDEX);
1401 4 : VerifyOrReturnError(group.Find(mStorage, fabric, group_id), CHIP_ERROR_KEY_NOT_FOUND);
1402 :
1403 4 : bool notifyNeeded = (IsGroupcastEnabled() && group.HasAuxiliaryACL() && group.endpoint_count > 0);
1404 :
1405 4 : EndpointData endpoint(fabric_index, group.group_id, group.first_endpoint);
1406 4 : size_t endpoint_index = 0;
1407 62 : while (endpoint_index < group.endpoint_count)
1408 : {
1409 58 : ReturnErrorOnFailure(endpoint.Load(mStorage));
1410 58 : TEMPORARY_RETURN_IGNORED endpoint.Delete(mStorage);
1411 58 : endpoint.endpoint_id = endpoint.next;
1412 58 : endpoint_index++;
1413 : }
1414 4 : group.first_endpoint = kInvalidEndpointId;
1415 4 : group.endpoint_count = 0;
1416 4 : ReturnErrorOnFailure(group.Save(mStorage));
1417 :
1418 4 : if (notifyNeeded)
1419 : {
1420 3 : mAuxAclNotificationNeeded = true;
1421 : }
1422 :
1423 4 : GroupModified(fabric_index, group.group_id);
1424 4 : return CHIP_NO_ERROR;
1425 4 : }
1426 :
1427 : //
1428 : // Group-Key map
1429 : //
1430 :
1431 98 : CHIP_ERROR GroupDataProviderImpl::SetGroupKey(FabricIndex fabric_index, GroupId group_id, KeysetId keyset_id)
1432 : {
1433 98 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
1434 :
1435 98 : FabricData fabric(fabric_index);
1436 98 : ReturnErrorOnFailure(fabric.Load(mStorage));
1437 :
1438 : // Search for an existing mapping
1439 98 : size_t count = 0;
1440 98 : KeyMapData map(fabric_index, fabric.first_map);
1441 180 : while (count++ < fabric.map_count)
1442 : {
1443 139 : ReturnErrorOnFailure(map.Load(mStorage));
1444 139 : if (map.group_id == group_id)
1445 : {
1446 : // Existing group, replace keyset
1447 :
1448 57 : map.keyset_id = keyset_id;
1449 57 : ReturnErrorOnFailure(map.Save(mStorage));
1450 57 : GroupModified(fabric_index, group_id);
1451 57 : return CHIP_NO_ERROR;
1452 : }
1453 82 : map.id = map.next;
1454 : }
1455 :
1456 : // New group, insert last
1457 41 : GroupKey entry(group_id, keyset_id);
1458 41 : return SetGroupKeyAt(fabric_index, fabric.map_count, entry);
1459 98 : }
1460 :
1461 304 : CHIP_ERROR GroupDataProviderImpl::SetGroupKeyAt(chip::FabricIndex fabric_index, size_t index, const GroupKey & in_map)
1462 : {
1463 304 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
1464 :
1465 304 : FabricData fabric(fabric_index);
1466 304 : KeyMapData map(fabric_index);
1467 :
1468 : // Load fabric, defaults to zero
1469 304 : CHIP_ERROR err = fabric.Load(mStorage);
1470 616 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
1471 :
1472 : // If the group exists, the index must match
1473 304 : bool found = map.Find(mStorage, fabric, in_map);
1474 304 : VerifyOrReturnError(!found || (map.index == index), CHIP_ERROR_DUPLICATE_KEY_ID);
1475 :
1476 298 : found = map.Get(mStorage, fabric, index);
1477 298 : map.group_id = in_map.group_id;
1478 298 : map.keyset_id = in_map.keyset_id;
1479 :
1480 298 : if (found)
1481 : {
1482 : // Update existing map
1483 9 : ReturnErrorOnFailure(map.Save(mStorage));
1484 9 : GroupModified(fabric_index, in_map.group_id);
1485 9 : return CHIP_NO_ERROR;
1486 : }
1487 :
1488 : // Insert last
1489 289 : VerifyOrReturnError(fabric.map_count == index, CHIP_ERROR_INVALID_ARGUMENT);
1490 288 : VerifyOrReturnError(fabric.map_count < GetMaxGroupsPerFabric(), CHIP_ERROR_INVALID_LIST_LENGTH);
1491 :
1492 287 : map.next = 0;
1493 287 : ReturnErrorOnFailure(map.Save(mStorage));
1494 :
1495 287 : if (map.first)
1496 : {
1497 : // First map, update fabric
1498 106 : fabric.first_map = map.id;
1499 : }
1500 : else
1501 : {
1502 : // Last map, update previous
1503 181 : KeyMapData prev(fabric_index, map.prev);
1504 181 : ReturnErrorOnFailure(prev.Load(mStorage));
1505 181 : prev.next = map.id;
1506 181 : ReturnErrorOnFailure(prev.Save(mStorage));
1507 181 : }
1508 : // Update fabric
1509 287 : fabric.map_count++;
1510 287 : GroupModified(fabric_index, in_map.group_id);
1511 287 : return fabric.Save(mStorage);
1512 304 : }
1513 :
1514 29 : CHIP_ERROR GroupDataProviderImpl::GetGroupKey(FabricIndex fabric_index, GroupId group_id, KeysetId & keyset_id)
1515 : {
1516 29 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
1517 :
1518 29 : FabricData fabric(fabric_index);
1519 29 : ReturnErrorOnFailure(fabric.Load(mStorage));
1520 :
1521 29 : size_t count = 0;
1522 29 : KeyMapData map(fabric_index, fabric.first_map);
1523 42 : while (count++ < fabric.map_count)
1524 : {
1525 41 : ReturnErrorOnFailure(map.Load(mStorage));
1526 41 : if (map.group_id == group_id)
1527 : {
1528 28 : keyset_id = map.keyset_id;
1529 28 : return CHIP_NO_ERROR;
1530 : }
1531 13 : map.id = map.next;
1532 : }
1533 1 : return CHIP_ERROR_NOT_FOUND;
1534 29 : }
1535 :
1536 28 : CHIP_ERROR GroupDataProviderImpl::GetGroupKeyAt(chip::FabricIndex fabric_index, size_t index, GroupKey & out_map)
1537 : {
1538 28 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
1539 :
1540 28 : FabricData fabric(fabric_index);
1541 28 : KeyMapData map;
1542 :
1543 28 : ReturnErrorOnFailure(fabric.Load(mStorage));
1544 27 : VerifyOrReturnError(map.Get(mStorage, fabric, index), CHIP_ERROR_NOT_FOUND);
1545 :
1546 : // Target map found
1547 19 : out_map.group_id = map.group_id;
1548 19 : out_map.keyset_id = map.keyset_id;
1549 19 : return CHIP_NO_ERROR;
1550 28 : }
1551 :
1552 15 : CHIP_ERROR GroupDataProviderImpl::RemoveGroupKeyAt(chip::FabricIndex fabric_index, size_t index)
1553 : {
1554 15 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
1555 :
1556 15 : FabricData fabric(fabric_index);
1557 15 : KeyMapData map;
1558 :
1559 15 : ReturnErrorOnFailure(fabric.Load(mStorage));
1560 15 : VerifyOrReturnError(map.Get(mStorage, fabric, index), CHIP_ERROR_NOT_FOUND);
1561 :
1562 15 : ReturnErrorOnFailure(map.Delete(mStorage));
1563 15 : if (map.first)
1564 : {
1565 : // Remove first map
1566 4 : fabric.first_map = map.next;
1567 : }
1568 : else
1569 : {
1570 : // Remove intermediate map, update previous
1571 11 : KeyMapData prev_data(fabric_index, map.prev);
1572 11 : ReturnErrorOnFailure(prev_data.Load(mStorage));
1573 11 : prev_data.next = map.next;
1574 11 : ReturnErrorOnFailure(prev_data.Save(mStorage));
1575 11 : }
1576 15 : if (fabric.map_count > 0)
1577 : {
1578 15 : fabric.map_count--;
1579 : }
1580 : // Update fabric
1581 15 : GroupModified(fabric_index, map.group_id);
1582 15 : return fabric.Save(mStorage);
1583 15 : }
1584 :
1585 8 : CHIP_ERROR GroupDataProviderImpl::RemoveGroupKeys(chip::FabricIndex fabric_index)
1586 : {
1587 8 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
1588 :
1589 8 : FabricData fabric(fabric_index);
1590 16 : VerifyOrReturnError(CHIP_NO_ERROR == fabric.Load(mStorage), CHIP_ERROR_INVALID_FABRIC_INDEX);
1591 :
1592 4 : size_t count = 0;
1593 4 : KeyMapData map(fabric_index, fabric.first_map);
1594 12 : while (count++ < fabric.map_count)
1595 : {
1596 16 : if (CHIP_NO_ERROR != map.Load(mStorage))
1597 : {
1598 0 : break;
1599 : }
1600 8 : TEMPORARY_RETURN_IGNORED map.Delete(mStorage);
1601 8 : map.id = map.next;
1602 : }
1603 :
1604 4 : GroupModified(fabric_index, 0 /* all groups affected*/);
1605 : // Update fabric
1606 4 : fabric.first_map = 0;
1607 4 : fabric.map_count = 0;
1608 4 : return fabric.Save(mStorage);
1609 8 : }
1610 :
1611 50 : GroupDataProvider::GroupKeyIterator * GroupDataProviderImpl::IterateGroupKeys(chip::FabricIndex fabric_index)
1612 : {
1613 50 : VerifyOrReturnError(IsInitialized(), nullptr);
1614 50 : return mGroupKeyIterators.CreateObject(*this, fabric_index);
1615 : }
1616 :
1617 50 : GroupDataProviderImpl::GroupKeyIteratorImpl::GroupKeyIteratorImpl(GroupDataProviderImpl & provider,
1618 50 : chip::FabricIndex fabric_index) :
1619 50 : mProvider(provider),
1620 50 : mFabric(fabric_index)
1621 : {
1622 50 : FabricData fabric(fabric_index);
1623 100 : if (CHIP_NO_ERROR == fabric.Load(provider.mStorage))
1624 : {
1625 48 : mNextId = fabric.first_map;
1626 48 : mTotal = fabric.map_count;
1627 48 : mCount = 0;
1628 : }
1629 50 : }
1630 :
1631 16 : size_t GroupDataProviderImpl::GroupKeyIteratorImpl::Count()
1632 : {
1633 16 : return mTotal;
1634 : }
1635 :
1636 84 : bool GroupDataProviderImpl::GroupKeyIteratorImpl::Next(GroupKey & output)
1637 : {
1638 84 : VerifyOrReturnError(mCount < mTotal, false);
1639 :
1640 73 : KeyMapData map(mFabric, mNextId);
1641 146 : VerifyOrReturnError(CHIP_NO_ERROR == map.Load(mProvider.mStorage), false);
1642 :
1643 73 : mCount++;
1644 73 : mNextId = map.next;
1645 73 : output.group_id = map.group_id;
1646 73 : output.keyset_id = map.keyset_id;
1647 73 : return true;
1648 73 : }
1649 :
1650 50 : void GroupDataProviderImpl::GroupKeyIteratorImpl::Release()
1651 : {
1652 50 : mProvider.mGroupKeyIterators.ReleaseObject(this);
1653 50 : }
1654 :
1655 : //
1656 : // Key Sets
1657 : //
1658 :
1659 : constexpr size_t GroupDataProvider::EpochKey::kLengthBytes;
1660 :
1661 262 : CHIP_ERROR GroupDataProviderImpl::SetKeySet(chip::FabricIndex fabric_index, const ByteSpan & compressed_fabric_id,
1662 : const KeySet & in_keyset)
1663 : {
1664 262 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
1665 262 : VerifyOrReturnError(in_keyset.num_keys_used >= 1 && in_keyset.num_keys_used <= KeySet::kEpochKeysMax,
1666 : CHIP_ERROR_INVALID_ARGUMENT);
1667 262 : if (in_keyset.policy != SecurityPolicy::kTrustFirst)
1668 : {
1669 1 : ChipLogError(NotSpecified, "Unsupported group key security policy: %d", static_cast<int>(in_keyset.policy));
1670 1 : return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
1671 : }
1672 261 : FabricData fabric(fabric_index);
1673 261 : KeySetData keyset;
1674 :
1675 : // Load fabric, defaults to zero
1676 261 : CHIP_ERROR err = fabric.Load(mStorage);
1677 567 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
1678 :
1679 : // Search existing keyset
1680 261 : bool found = keyset.Find(mStorage, fabric, in_keyset.keyset_id);
1681 :
1682 261 : keyset.keyset_id = in_keyset.keyset_id;
1683 261 : keyset.policy = in_keyset.policy;
1684 261 : keyset.keys_count = in_keyset.num_keys_used;
1685 261 : memset(keyset.operational_keys, 0x00, sizeof(keyset.operational_keys));
1686 :
1687 : // Store the operational keys and hash instead of the epoch keys
1688 913 : for (size_t i = 0; i < in_keyset.num_keys_used; ++i)
1689 : {
1690 652 : keyset.operational_keys[i].start_time = in_keyset.epoch_keys[i].start_time;
1691 652 : ByteSpan epoch_key(in_keyset.epoch_keys[i].key, Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES);
1692 652 : ReturnErrorOnFailure(
1693 : Crypto::DeriveGroupOperationalCredentials(epoch_key, compressed_fabric_id, keyset.operational_keys[i]));
1694 : }
1695 :
1696 261 : if (found)
1697 : {
1698 : // Update existing keyset info, keep next
1699 6 : return keyset.Save(mStorage);
1700 : }
1701 :
1702 : // New keyset
1703 255 : VerifyOrReturnError(fabric.keyset_count < mMaxGroupKeysPerFabric, CHIP_ERROR_INVALID_LIST_LENGTH);
1704 :
1705 : // Insert first
1706 253 : keyset.next = fabric.first_keyset;
1707 253 : ReturnErrorOnFailure(keyset.Save(mStorage));
1708 : // Update fabric
1709 253 : fabric.keyset_count++;
1710 253 : fabric.first_keyset = in_keyset.keyset_id;
1711 253 : return fabric.Save(mStorage);
1712 261 : }
1713 :
1714 174 : CHIP_ERROR GroupDataProviderImpl::GetKeySet(chip::FabricIndex fabric_index, uint16_t target_id, KeySet & out_keyset)
1715 : {
1716 174 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
1717 :
1718 174 : FabricData fabric(fabric_index);
1719 174 : KeySetData keyset;
1720 :
1721 174 : ReturnErrorOnFailure(fabric.Load(mStorage));
1722 156 : VerifyOrReturnError(keyset.Find(mStorage, fabric, target_id), CHIP_ERROR_NOT_FOUND);
1723 :
1724 : // Target keyset found
1725 138 : out_keyset.ClearKeys();
1726 138 : out_keyset.keyset_id = keyset.keyset_id;
1727 138 : out_keyset.policy = keyset.policy;
1728 138 : out_keyset.num_keys_used = keyset.keys_count;
1729 : // Epoch keys are not read back, only start times
1730 138 : out_keyset.epoch_keys[0].start_time = keyset.operational_keys[0].start_time;
1731 138 : out_keyset.epoch_keys[1].start_time = keyset.operational_keys[1].start_time;
1732 138 : out_keyset.epoch_keys[2].start_time = keyset.operational_keys[2].start_time;
1733 :
1734 138 : return CHIP_NO_ERROR;
1735 174 : }
1736 :
1737 31 : CHIP_ERROR GroupDataProviderImpl::RemoveKeySet(chip::FabricIndex fabric_index, uint16_t target_id)
1738 : {
1739 31 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
1740 :
1741 31 : FabricData fabric(fabric_index);
1742 31 : KeySetData keyset;
1743 :
1744 31 : ReturnErrorOnFailure(fabric.Load(mStorage));
1745 31 : VerifyOrReturnError(keyset.Find(mStorage, fabric, target_id), CHIP_ERROR_NOT_FOUND);
1746 27 : ReturnErrorOnFailure(keyset.Delete(mStorage));
1747 :
1748 27 : if (keyset.first)
1749 : {
1750 : // Remove first keyset
1751 23 : fabric.first_keyset = keyset.next;
1752 : }
1753 : else
1754 : {
1755 : // Remove intermediate keyset, update previous
1756 4 : KeySetData prev_data(fabric_index, keyset.prev);
1757 4 : ReturnErrorOnFailure(prev_data.Load(mStorage));
1758 4 : prev_data.next = keyset.next;
1759 4 : ReturnErrorOnFailure(prev_data.Save(mStorage));
1760 4 : }
1761 27 : if (fabric.keyset_count > 0)
1762 : {
1763 27 : fabric.keyset_count--;
1764 : }
1765 : // Update fabric info
1766 27 : ReturnErrorOnFailure(fabric.Save(mStorage));
1767 :
1768 : // Removing a key set also removes the associated group mappings
1769 27 : KeyMapData map;
1770 27 : uint16_t original_count = fabric.map_count;
1771 28 : for (uint16_t i = 0; i < original_count; ++i)
1772 : {
1773 2 : TEMPORARY_RETURN_IGNORED fabric.Load(mStorage);
1774 2 : size_t idx = map.Find(mStorage, fabric, target_id);
1775 2 : if (idx == std::numeric_limits<size_t>::max())
1776 : {
1777 1 : break;
1778 : }
1779 : // NOTE: It's unclear what should happen here if we have removed the key set
1780 : // and possibly some mappings before failing. For now, ignoring errors, but
1781 : // open to suggestsions for the correct behavior.
1782 1 : TEMPORARY_RETURN_IGNORED RemoveGroupKeyAt(fabric_index, idx);
1783 : }
1784 27 : return CHIP_NO_ERROR;
1785 31 : }
1786 :
1787 2 : GroupDataProvider::KeySetIterator * GroupDataProviderImpl::IterateKeySets(chip::FabricIndex fabric_index)
1788 : {
1789 2 : VerifyOrReturnError(IsInitialized(), nullptr);
1790 2 : return mKeySetIterators.CreateObject(*this, fabric_index);
1791 : }
1792 :
1793 2 : GroupDataProviderImpl::KeySetIteratorImpl::KeySetIteratorImpl(GroupDataProviderImpl & provider, chip::FabricIndex fabric_index) :
1794 2 : mProvider(provider), mFabric(fabric_index)
1795 : {
1796 2 : FabricData fabric(fabric_index);
1797 4 : if (CHIP_NO_ERROR == fabric.Load(provider.mStorage))
1798 : {
1799 2 : mNextId = fabric.first_keyset;
1800 2 : mTotal = fabric.keyset_count;
1801 2 : mCount = 0;
1802 : }
1803 2 : }
1804 :
1805 2 : size_t GroupDataProviderImpl::KeySetIteratorImpl::Count()
1806 : {
1807 2 : return mTotal;
1808 : }
1809 :
1810 9 : bool GroupDataProviderImpl::KeySetIteratorImpl::Next(KeySet & output)
1811 : {
1812 9 : VerifyOrReturnError(mCount < mTotal, false);
1813 :
1814 7 : KeySetData keyset(mFabric, mNextId);
1815 14 : VerifyOrReturnError(CHIP_NO_ERROR == keyset.Load(mProvider.mStorage), false);
1816 :
1817 7 : mCount++;
1818 7 : mNextId = keyset.next;
1819 7 : output.ClearKeys();
1820 7 : output.keyset_id = keyset.keyset_id;
1821 7 : output.policy = keyset.policy;
1822 7 : output.num_keys_used = keyset.keys_count;
1823 : // Epoch keys are not read back, only start times
1824 7 : output.epoch_keys[0].start_time = keyset.operational_keys[0].start_time;
1825 7 : output.epoch_keys[1].start_time = keyset.operational_keys[1].start_time;
1826 7 : output.epoch_keys[2].start_time = keyset.operational_keys[2].start_time;
1827 7 : return true;
1828 7 : }
1829 :
1830 2 : void GroupDataProviderImpl::KeySetIteratorImpl::Release()
1831 : {
1832 2 : mProvider.mKeySetIterators.ReleaseObject(this);
1833 2 : }
1834 :
1835 : //
1836 : // Fabrics
1837 : //
1838 :
1839 33 : CHIP_ERROR GroupDataProviderImpl::RemoveFabric(chip::FabricIndex fabric_index)
1840 : {
1841 33 : FabricData fabric(fabric_index);
1842 :
1843 : // Fabric data defaults to zero, so if not entry is found, no mappings, or keys are removed
1844 : // However, states has a separate list, and needs to be removed regardless
1845 33 : CHIP_ERROR err = fabric.Load(mStorage);
1846 72 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
1847 :
1848 : // Remove Group mappings
1849 :
1850 45 : for (size_t i = 0; i < fabric.map_count; i++)
1851 : {
1852 12 : TEMPORARY_RETURN_IGNORED RemoveGroupKeyAt(fabric_index, fabric.map_count - i - 1);
1853 : }
1854 :
1855 : // Remove group info
1856 :
1857 68 : for (size_t i = 0; i < fabric.group_count; i++)
1858 : {
1859 35 : TEMPORARY_RETURN_IGNORED RemoveGroupInfoAt(fabric_index, fabric.group_count - i - 1);
1860 : }
1861 :
1862 : // Remove Keysets
1863 :
1864 33 : KeySetData keyset(fabric_index, fabric.first_keyset);
1865 33 : size_t keyset_count = 0;
1866 :
1867 : // Loop the keysets associated with the target fabric
1868 50 : while (keyset_count < fabric.keyset_count)
1869 : {
1870 34 : if (CHIP_NO_ERROR != keyset.Load(mStorage))
1871 : {
1872 0 : break;
1873 : }
1874 17 : TEMPORARY_RETURN_IGNORED RemoveKeySet(fabric_index, keyset.keyset_id);
1875 17 : keyset.keyset_id = keyset.next;
1876 17 : keyset_count++;
1877 : }
1878 :
1879 : // Remove fabric, and ensure no auxiliary acl changed
1880 : // event will be emitted from this action
1881 33 : err = fabric.Delete(mStorage);
1882 33 : mAuxAclNotificationNeeded = false;
1883 33 : return err;
1884 33 : }
1885 :
1886 : //
1887 : // Cryptography
1888 : //
1889 :
1890 4 : Crypto::SymmetricKeyContext * GroupDataProviderImpl::GetKeyContext(FabricIndex fabric_index, GroupId group_id)
1891 : {
1892 4 : FabricData fabric(fabric_index);
1893 8 : VerifyOrReturnError(CHIP_NO_ERROR == fabric.Load(mStorage), nullptr);
1894 :
1895 4 : KeyMapData mapping(fabric.fabric_index, fabric.first_map);
1896 :
1897 : // Look for the target group in the fabric's keyset-group pairs
1898 4 : for (uint16_t i = 0; i < fabric.map_count; ++i, mapping.id = mapping.next)
1899 : {
1900 8 : VerifyOrReturnError(CHIP_NO_ERROR == mapping.Load(mStorage), nullptr);
1901 : // GroupKeySetID of 0 is reserved for the Identity Protection Key (IPK),
1902 : // it cannot be used for operational group communication.
1903 4 : if (mapping.keyset_id > 0 && mapping.group_id == group_id)
1904 : {
1905 : // Group found, get the keyset
1906 4 : KeySetData keyset;
1907 4 : VerifyOrReturnError(keyset.Find(mStorage, fabric, mapping.keyset_id), nullptr);
1908 4 : Crypto::GroupOperationalCredentials * creds = keyset.GetCurrentGroupCredentials();
1909 4 : if (nullptr != creds)
1910 : {
1911 4 : return mGroupKeyContexPool.CreateObject(*this, creds->encryption_key, creds->hash, creds->privacy_key);
1912 : }
1913 4 : }
1914 : }
1915 0 : return nullptr;
1916 4 : }
1917 :
1918 30 : CHIP_ERROR GroupDataProviderImpl::GetIpkKeySet(FabricIndex fabric_index, KeySet & out_keyset)
1919 : {
1920 30 : FabricData fabric(fabric_index);
1921 60 : VerifyOrReturnError(CHIP_NO_ERROR == fabric.Load(mStorage), CHIP_ERROR_NOT_FOUND);
1922 :
1923 29 : KeyMapData mapping(fabric.fabric_index, fabric.first_map);
1924 :
1925 : // Fabric found, get the keyset
1926 29 : KeySetData keyset;
1927 29 : VerifyOrReturnError(keyset.Find(mStorage, fabric, kIdentityProtectionKeySetId), CHIP_ERROR_NOT_FOUND);
1928 :
1929 : // If the keyset ID doesn't match, we have a ... problem.
1930 27 : VerifyOrReturnError(keyset.keyset_id == kIdentityProtectionKeySetId, CHIP_ERROR_INTERNAL);
1931 :
1932 27 : out_keyset.keyset_id = keyset.keyset_id;
1933 27 : out_keyset.num_keys_used = keyset.keys_count;
1934 27 : out_keyset.policy = keyset.policy;
1935 :
1936 108 : for (size_t key_idx = 0; key_idx < MATTER_ARRAY_SIZE(out_keyset.epoch_keys); ++key_idx)
1937 : {
1938 81 : out_keyset.epoch_keys[key_idx].Clear();
1939 81 : if (key_idx < keyset.keys_count)
1940 : {
1941 27 : out_keyset.epoch_keys[key_idx].start_time = keyset.operational_keys[key_idx].start_time;
1942 27 : memcpy(&out_keyset.epoch_keys[key_idx].key[0], keyset.operational_keys[key_idx].encryption_key, EpochKey::kLengthBytes);
1943 : }
1944 : }
1945 :
1946 27 : return CHIP_NO_ERROR;
1947 30 : }
1948 :
1949 4 : void GroupDataProviderImpl::GroupKeyContext::Release()
1950 : {
1951 4 : ReleaseKeys();
1952 4 : mProvider.mGroupKeyContexPool.ReleaseObject(this);
1953 4 : }
1954 :
1955 4 : CHIP_ERROR GroupDataProviderImpl::GroupKeyContext::MessageEncrypt(const ByteSpan & plaintext, const ByteSpan & aad,
1956 : const ByteSpan & nonce, MutableByteSpan & mic,
1957 : MutableByteSpan & ciphertext) const
1958 : {
1959 4 : uint8_t * output = ciphertext.data();
1960 4 : return Crypto::AES_CCM_encrypt(plaintext.data(), plaintext.size(), aad.data(), aad.size(), mEncryptionKey, nonce.data(),
1961 4 : nonce.size(), output, mic.data(), mic.size());
1962 : }
1963 :
1964 8 : CHIP_ERROR GroupDataProviderImpl::GroupKeyContext::MessageDecrypt(const ByteSpan & ciphertext, const ByteSpan & aad,
1965 : const ByteSpan & nonce, const ByteSpan & mic,
1966 : MutableByteSpan & plaintext) const
1967 : {
1968 8 : uint8_t * output = plaintext.data();
1969 16 : return Crypto::AES_CCM_decrypt(ciphertext.data(), ciphertext.size(), aad.data(), aad.size(), mic.data(), mic.size(),
1970 16 : mEncryptionKey, nonce.data(), nonce.size(), output);
1971 : }
1972 :
1973 0 : CHIP_ERROR GroupDataProviderImpl::GroupKeyContext::PrivacyEncrypt(const ByteSpan & input, const ByteSpan & nonce,
1974 : MutableByteSpan & output) const
1975 : {
1976 0 : return Crypto::AES_CTR_crypt(input.data(), input.size(), mPrivacyKey, nonce.data(), nonce.size(), output.data());
1977 : }
1978 :
1979 3 : CHIP_ERROR GroupDataProviderImpl::GroupKeyContext::PrivacyDecrypt(const ByteSpan & input, const ByteSpan & nonce,
1980 : MutableByteSpan & output) const
1981 : {
1982 3 : return Crypto::AES_CTR_crypt(input.data(), input.size(), mPrivacyKey, nonce.data(), nonce.size(), output.data());
1983 : }
1984 :
1985 10 : GroupDataProviderImpl::GroupSessionIterator * GroupDataProviderImpl::IterateGroupSessions(uint16_t session_id)
1986 : {
1987 10 : VerifyOrReturnError(IsInitialized(), nullptr);
1988 10 : return mGroupSessionsIterator.CreateObject(*this, session_id);
1989 : }
1990 :
1991 10 : GroupDataProviderImpl::GroupSessionIteratorImpl::GroupSessionIteratorImpl(GroupDataProviderImpl & provider, uint16_t session_id) :
1992 10 : mProvider(provider), mSessionId(session_id), mGroupKeyContext(provider)
1993 : {
1994 10 : FabricList fabric_list;
1995 10 : ReturnOnFailure(fabric_list.Load(provider.mStorage));
1996 10 : mFirstFabric = fabric_list.first_entry;
1997 10 : mFabric = fabric_list.first_entry;
1998 10 : mFabricCount = 0;
1999 10 : mFabricTotal = fabric_list.entry_count;
2000 10 : mMapCount = 0;
2001 10 : mFirstMap = true;
2002 10 : }
2003 :
2004 1 : size_t GroupDataProviderImpl::GroupSessionIteratorImpl::Count()
2005 : {
2006 1 : FabricData fabric(mFirstFabric);
2007 1 : size_t count = 0;
2008 :
2009 3 : for (size_t i = 0; i < mFabricTotal; i++, fabric.fabric_index = fabric.next)
2010 : {
2011 4 : if (CHIP_NO_ERROR != fabric.Load(mProvider.mStorage))
2012 : {
2013 0 : break;
2014 : }
2015 :
2016 : // Iterate key sets
2017 2 : KeyMapData mapping(fabric.fabric_index, fabric.first_map);
2018 :
2019 : // Look for the target group in the fabric's keyset-group pairs
2020 8 : for (uint16_t j = 0; j < fabric.map_count; ++j, mapping.id = mapping.next)
2021 : {
2022 12 : if (CHIP_NO_ERROR != mapping.Load(mProvider.mStorage))
2023 : {
2024 0 : break;
2025 : }
2026 :
2027 : // Group found, get the keyset
2028 6 : KeySetData keyset;
2029 6 : if (!keyset.Find(mProvider.mStorage, fabric, mapping.keyset_id))
2030 : {
2031 0 : break;
2032 : }
2033 20 : for (uint16_t k = 0; k < keyset.keys_count; ++k)
2034 : {
2035 14 : if (keyset.operational_keys[k].hash == mSessionId)
2036 : {
2037 1 : count++;
2038 : }
2039 : }
2040 6 : }
2041 2 : }
2042 2 : return count;
2043 1 : }
2044 :
2045 13 : bool GroupDataProviderImpl::GroupSessionIteratorImpl::Next(GroupSession & output)
2046 : {
2047 41 : while (mFabricCount < mFabricTotal)
2048 : {
2049 38 : FabricData fabric(mFabric);
2050 76 : VerifyOrReturnError(CHIP_NO_ERROR == fabric.Load(mProvider.mStorage), false);
2051 :
2052 38 : if (mMapCount >= fabric.map_count)
2053 : {
2054 : // No more keyset/group mappings on the current fabric, try next fabric
2055 4 : mFabric = fabric.next;
2056 4 : mFabricCount++;
2057 4 : mFirstMap = true;
2058 4 : mMapCount = 0;
2059 4 : continue;
2060 : }
2061 :
2062 34 : if (mFirstMap)
2063 : {
2064 11 : mMapping = fabric.first_map;
2065 11 : mFirstMap = false;
2066 : }
2067 :
2068 34 : KeyMapData mapping(mFabric, mMapping);
2069 68 : VerifyOrReturnError(CHIP_NO_ERROR == mapping.Load(mProvider.mStorage), false);
2070 :
2071 : // Group found, get the keyset
2072 34 : KeySetData keyset;
2073 34 : VerifyOrReturnError(keyset.Find(mProvider.mStorage, fabric, mapping.keyset_id), false);
2074 :
2075 34 : if (mKeyIndex >= keyset.keys_count || (mKeyIndex >= KeySet::kEpochKeysMax))
2076 : {
2077 : // No more keys in current keyset, try next
2078 8 : mMapping = mapping.next;
2079 8 : mMapCount++;
2080 8 : mKeyIndex = 0;
2081 8 : continue;
2082 : }
2083 :
2084 26 : Crypto::GroupOperationalCredentials & creds = keyset.operational_keys[mKeyIndex++];
2085 26 : if (creds.hash == mSessionId)
2086 : {
2087 10 : TEMPORARY_RETURN_IGNORED mGroupKeyContext.Initialize(creds.encryption_key, mSessionId, creds.privacy_key);
2088 10 : output.fabric_index = fabric.fabric_index;
2089 10 : output.group_id = mapping.group_id;
2090 10 : output.security_policy = keyset.policy;
2091 10 : output.keyContext = &mGroupKeyContext;
2092 10 : return true;
2093 : }
2094 74 : }
2095 :
2096 3 : return false;
2097 : }
2098 :
2099 10 : void GroupDataProviderImpl::GroupSessionIteratorImpl::Release()
2100 : {
2101 10 : mGroupKeyContext.ReleaseKeys();
2102 10 : mProvider.mGroupSessionsIterator.ReleaseObject(this);
2103 10 : }
2104 :
2105 : namespace {
2106 :
2107 : GroupDataProvider * gGroupsProvider = nullptr;
2108 :
2109 : } // namespace
2110 :
2111 116 : GroupDataProvider * GetGroupDataProvider()
2112 : {
2113 116 : return gGroupsProvider;
2114 : }
2115 :
2116 164 : void SetGroupDataProvider(GroupDataProvider * provider)
2117 : {
2118 164 : gGroupsProvider = provider;
2119 164 : }
2120 :
2121 : } // namespace Credentials
2122 : } // namespace chip
|