Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2025 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 <app-common/zap-generated/cluster-objects.h>
21 : #include <app/data-model-provider/MetadataTypes.h>
22 : #include <credentials/CHIPCert.h>
23 : #include <functional>
24 : #include <lib/core/CHIPPersistentStorageDelegate.h>
25 : #include <lib/core/CHIPVendorIdentifiers.hpp>
26 : #include <lib/core/NodeId.h>
27 : #include <lib/support/ReadOnlyBuffer.h>
28 : #include <vector>
29 :
30 : namespace chip {
31 : namespace app {
32 :
33 : namespace datastore {
34 :
35 : struct AccessControlEntryStruct
36 : {
37 : Clusters::JointFabricDatastore::DatastoreAccessControlEntryPrivilegeEnum privilege =
38 : static_cast<Clusters::JointFabricDatastore::DatastoreAccessControlEntryPrivilegeEnum>(0);
39 : Clusters::JointFabricDatastore::DatastoreAccessControlEntryAuthModeEnum authMode =
40 : static_cast<Clusters::JointFabricDatastore::DatastoreAccessControlEntryAuthModeEnum>(0);
41 : std::vector<uint64_t> subjects;
42 : std::vector<Clusters::JointFabricDatastore::Structs::DatastoreAccessControlTargetStruct::Type> targets;
43 : };
44 :
45 : struct ACLEntryStruct
46 : {
47 : chip::NodeId nodeID = static_cast<chip::NodeId>(0);
48 : uint16_t listID = static_cast<uint16_t>(0);
49 : AccessControlEntryStruct ACLEntry;
50 : Clusters::JointFabricDatastore::Structs::DatastoreStatusEntryStruct::Type statusEntry;
51 : };
52 :
53 : } // namespace datastore
54 :
55 : enum RefreshState
56 : {
57 : kIdle,
58 : kRefreshingEndpoints,
59 : kRefreshingGroups,
60 : kRefreshingBindings,
61 : kFetchingGroupKeySets,
62 : kRefreshingGroupKeySets,
63 : kRefreshingACLs,
64 : };
65 :
66 : /**
67 : * A struct which extends the DatastoreNodeInformationEntry type with FriendlyName buffer reservation.
68 : */
69 : struct GenericDatastoreNodeInformationEntry
70 : : public Clusters::JointFabricDatastore::Structs::DatastoreNodeInformationEntryStruct::Type
71 : {
72 11 : GenericDatastoreNodeInformationEntry(NodeId nodeId = 0,
73 : Clusters::JointFabricDatastore::DatastoreStateEnum state =
74 : Clusters::JointFabricDatastore::DatastoreStateEnum::kUnknownEnumValue,
75 : Optional<CharSpan> label = NullOptional)
76 11 : {
77 11 : Set(nodeId, state, label);
78 11 : }
79 :
80 11 : GenericDatastoreNodeInformationEntry(const GenericDatastoreNodeInformationEntry & op) { *this = op; }
81 :
82 11 : GenericDatastoreNodeInformationEntry & operator=(const GenericDatastoreNodeInformationEntry & op)
83 : {
84 11 : Set(op.nodeID, op.commissioningStatusEntry.state, MakeOptional(op.friendlyName));
85 11 : return *this;
86 : }
87 :
88 22 : void Set(NodeId nodeId, Clusters::JointFabricDatastore::DatastoreStateEnum state, Optional<CharSpan> label = NullOptional)
89 : {
90 22 : this->nodeID = nodeId;
91 22 : this->commissioningStatusEntry.state = state;
92 22 : Set(label);
93 22 : }
94 :
95 23 : void Set(Optional<CharSpan> label = NullOptional)
96 : {
97 23 : if (label.HasValue())
98 : {
99 23 : memset(mFriendlyNameBuffer, 0, sizeof(mFriendlyNameBuffer));
100 23 : if (label.Value().size() > sizeof(mFriendlyNameBuffer))
101 : {
102 0 : memcpy(mFriendlyNameBuffer, label.Value().data(), sizeof(mFriendlyNameBuffer));
103 0 : this->friendlyName = CharSpan(mFriendlyNameBuffer, sizeof(mFriendlyNameBuffer));
104 : }
105 : else
106 : {
107 23 : memcpy(mFriendlyNameBuffer, label.Value().data(), label.Value().size());
108 23 : this->friendlyName = CharSpan(mFriendlyNameBuffer, label.Value().size());
109 : }
110 : }
111 : else
112 : {
113 0 : this->friendlyName = CharSpan();
114 : }
115 23 : }
116 :
117 : private:
118 : static constexpr size_t kFriendlyNameMaxSize = 32u;
119 :
120 : char mFriendlyNameBuffer[kFriendlyNameMaxSize];
121 : };
122 :
123 : class JointFabricDatastore
124 : {
125 : public:
126 : static JointFabricDatastore & GetInstance()
127 : {
128 : static JointFabricDatastore sInstance;
129 : return sInstance;
130 : }
131 :
132 : class Delegate
133 : {
134 : public:
135 22 : Delegate() {}
136 22 : virtual ~Delegate() {}
137 :
138 : virtual CHIP_ERROR
139 0 : SyncNode(NodeId nodeId,
140 : const Clusters::JointFabricDatastore::Structs::DatastoreEndpointGroupIDEntryStruct::Type & endpointGroupIDEntry,
141 : std::function<void()> onSuccess)
142 : {
143 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
144 : }
145 :
146 : virtual CHIP_ERROR
147 0 : SyncNode(NodeId nodeId,
148 : const Clusters::JointFabricDatastore::Structs::DatastoreNodeKeySetEntryStruct::Type & nodeKeySetEntry,
149 : std::function<void()> onSuccess)
150 : {
151 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
152 : }
153 :
154 : virtual CHIP_ERROR
155 0 : SyncNode(NodeId nodeId,
156 : const Clusters::JointFabricDatastore::Structs::DatastoreEndpointBindingEntryStruct::Type & bindingEntry,
157 : std::function<void()> onSuccess)
158 : {
159 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
160 : }
161 :
162 : virtual CHIP_ERROR
163 0 : SyncNode(NodeId nodeId,
164 : std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointBindingEntryStruct::Type> & bindingEntries,
165 : std::function<void()> onSuccess)
166 : {
167 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
168 : }
169 :
170 0 : virtual CHIP_ERROR SyncNode(NodeId nodeId,
171 : const Clusters::JointFabricDatastore::Structs::DatastoreACLEntryStruct::Type & aclEntry,
172 : std::function<void()> onSuccess)
173 : {
174 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
175 : }
176 :
177 : virtual CHIP_ERROR
178 0 : SyncNode(NodeId nodeId,
179 : const std::vector<app::Clusters::JointFabricDatastore::Structs::DatastoreACLEntryStruct::Type> & aclEntries,
180 : std::function<void()> onSuccess)
181 : {
182 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
183 : }
184 :
185 0 : virtual CHIP_ERROR SyncNode(NodeId nodeId,
186 : const Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type & groupKeySet,
187 : std::function<void()> onSuccess)
188 : {
189 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
190 : }
191 :
192 0 : virtual CHIP_ERROR FetchEndpointList(
193 : NodeId nodeId,
194 : std::function<void(CHIP_ERROR,
195 : const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointEntryStruct::Type> &)>
196 : onSuccess)
197 : {
198 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
199 : }
200 :
201 0 : virtual CHIP_ERROR FetchEndpointGroupList(
202 : NodeId nodeId, EndpointId endpointId,
203 : std::function<
204 : void(CHIP_ERROR,
205 : const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreGroupInformationEntryStruct::Type> &)>
206 : onSuccess)
207 : {
208 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
209 : }
210 :
211 0 : virtual CHIP_ERROR FetchEndpointBindingList(
212 : NodeId nodeId, EndpointId endpointId,
213 : std::function<
214 : void(CHIP_ERROR,
215 : const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointBindingEntryStruct::Type> &)>
216 : onSuccess)
217 : {
218 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
219 : }
220 :
221 0 : virtual CHIP_ERROR FetchGroupKeySetList(
222 : NodeId nodeId,
223 : std::function<void(CHIP_ERROR,
224 : const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type> &)>
225 : onSuccess)
226 : {
227 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
228 : }
229 :
230 0 : virtual CHIP_ERROR FetchACLList(
231 : NodeId nodeId,
232 : std::function<void(CHIP_ERROR,
233 : const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreACLEntryStruct::Type> &)>
234 : onSuccess)
235 : {
236 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
237 : }
238 : };
239 :
240 : ByteSpan GetAnchorRootCA() const { return ByteSpan(mAnchorRootCA, mAnchorRootCALength); }
241 :
242 : CHIP_ERROR SetAnchorNodeId(NodeId anchorNodeId)
243 : {
244 : mAnchorNodeId = anchorNodeId;
245 : return CHIP_NO_ERROR;
246 : }
247 : NodeId GetAnchorNodeId() { return mAnchorNodeId; }
248 :
249 : CHIP_ERROR SetAnchorVendorId(VendorId anchorVendorId)
250 : {
251 : mAnchorVendorId = anchorVendorId;
252 : return CHIP_NO_ERROR;
253 : }
254 : VendorId GetAnchorVendorId() { return mAnchorVendorId; }
255 :
256 : CHIP_ERROR SetFriendlyName(const CharSpan & friendlyName)
257 : {
258 : if (friendlyName.size() >= sizeof(mFriendlyNameBuffer))
259 : {
260 : return CHIP_ERROR_INVALID_ARGUMENT;
261 : }
262 : mFriendlyNameBufferLength = friendlyName.size();
263 : memcpy(mFriendlyNameBuffer, friendlyName.data(), mFriendlyNameBufferLength);
264 : mFriendlyNameBuffer[mFriendlyNameBufferLength] = '\0'; // Ensure null-termination
265 : return CHIP_NO_ERROR;
266 : }
267 : CharSpan GetFriendlyName() const { return CharSpan(mFriendlyNameBuffer, mFriendlyNameBufferLength); }
268 :
269 : const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreGroupInformationEntryStruct::Type> & GetGroupEntries()
270 : {
271 : return mGroupInformationEntries;
272 : }
273 :
274 : Clusters::JointFabricDatastore::Structs::DatastoreStatusEntryStruct::Type & GetStatus() { return mDatastoreStatusEntry; }
275 :
276 : std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointGroupIDEntryStruct::Type> & GetEndpointGroupIDList()
277 : {
278 : return mEndpointGroupIDEntries;
279 : }
280 :
281 : std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointBindingEntryStruct::Type> & GetEndpointBindingList()
282 : {
283 : return mEndpointBindingEntries;
284 : }
285 :
286 : std::vector<Clusters::JointFabricDatastore::Structs::DatastoreNodeKeySetEntryStruct::Type> & GetNodeKeySetList()
287 : {
288 : return mNodeKeySetEntries;
289 : }
290 :
291 : std::vector<datastore::ACLEntryStruct> & GetNodeACLList() { return mACLEntries; }
292 :
293 : std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointEntryStruct::Type> & GetNodeEndpointList()
294 : {
295 : return mEndpointEntries;
296 : }
297 :
298 : CHIP_ERROR AddPendingNode(NodeId nodeId, const CharSpan & friendlyName);
299 : CHIP_ERROR UpdateNode(NodeId nodeId, const CharSpan & friendlyName);
300 : CHIP_ERROR RemoveNode(NodeId nodeId);
301 : CHIP_ERROR RefreshNode(NodeId nodeId);
302 : CHIP_ERROR ContinueRefresh();
303 :
304 : CHIP_ERROR SetNode(NodeId nodeId, Clusters::JointFabricDatastore::DatastoreStateEnum state);
305 :
306 : CHIP_ERROR AddGroupKeySetEntry(Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type & groupKeySet);
307 : bool IsGroupKeySetEntryPresent(uint16_t groupKeySetId);
308 : CHIP_ERROR RemoveGroupKeySetEntry(uint16_t groupKeySetId);
309 : CHIP_ERROR UpdateGroupKeySetEntry(Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type & groupKeySet);
310 :
311 : CHIP_ERROR AddAdmin(Clusters::JointFabricDatastore::Structs::DatastoreAdministratorInformationEntryStruct::Type & adminId);
312 : bool IsAdminEntryPresent(NodeId nodeId);
313 : CHIP_ERROR UpdateAdmin(NodeId nodeId, CharSpan friendlyName, ByteSpan icac);
314 : CHIP_ERROR RemoveAdmin(NodeId nodeId);
315 :
316 : CHIP_ERROR AddGroup(const Clusters::JointFabricDatastore::Commands::AddGroup::DecodableType & commandData);
317 : CHIP_ERROR UpdateGroup(const Clusters::JointFabricDatastore::Commands::UpdateGroup::DecodableType & commandData);
318 : CHIP_ERROR RemoveGroup(const Clusters::JointFabricDatastore::Commands::RemoveGroup::DecodableType & commandData);
319 :
320 : CHIP_ERROR UpdateEndpointForNode(NodeId nodeId, EndpointId endpointId, CharSpan friendlyName);
321 :
322 : CHIP_ERROR AddGroupIDToEndpointForNode(NodeId nodeId, EndpointId endpointId, GroupId groupId);
323 : CHIP_ERROR RemoveGroupIDFromEndpointForNode(NodeId nodeId, EndpointId endpointId, GroupId groupId);
324 :
325 : CHIP_ERROR
326 : AddBindingToEndpointForNode(NodeId nodeId, EndpointId endpointId,
327 : const Clusters::JointFabricDatastore::Structs::DatastoreBindingTargetStruct::Type & binding);
328 : CHIP_ERROR
329 : RemoveBindingFromEndpointForNode(uint16_t listId, NodeId nodeId, EndpointId endpointId);
330 :
331 : CHIP_ERROR
332 : AddACLToNode(NodeId nodeId,
333 : const Clusters::JointFabricDatastore::Structs::DatastoreAccessControlEntryStruct::DecodableType & aclEntry);
334 : CHIP_ERROR RemoveACLFromNode(uint16_t listId, NodeId nodeId);
335 :
336 : CHIP_ERROR TestAddNodeKeySetEntry(GroupId groupId, uint16_t groupKeySetId, NodeId nodeId);
337 : CHIP_ERROR TestAddEndpointEntry(EndpointId endpointId, NodeId nodeId, CharSpan friendlyName);
338 :
339 : const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type> & GetGroupKeySetList()
340 : {
341 : return mGroupKeySetList;
342 : }
343 : const std::vector<GenericDatastoreNodeInformationEntry> & GetNodeInformationEntries() { return mNodeInformationEntries; }
344 : const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreAdministratorInformationEntryStruct::Type> &
345 : GetAdminEntries()
346 : {
347 : return mAdminEntries;
348 : }
349 :
350 : /**
351 : * Used to notify of changes in the node list and more TODO.
352 : */
353 : class Listener
354 : {
355 : public:
356 6 : virtual ~Listener() = default;
357 :
358 : /**
359 : * Notifies of a change in the node list.
360 : */
361 : virtual void MarkNodeListChanged() = 0;
362 :
363 : private:
364 : Listener * mNext = nullptr;
365 :
366 : friend class JointFabricDatastore;
367 : };
368 :
369 : /**
370 : * Add a listener to be notified of changes in the Joint Fabric Datastore.
371 : *
372 : * @param [in] listener The listener to add.
373 : */
374 : void AddListener(Listener & listener);
375 :
376 : /**
377 : * Remove a listener from being notified of changes in the Joint Fabric Datastore.
378 : *
379 : * @param [in] listener The listener to remove.
380 : */
381 : void RemoveListener(Listener & listener);
382 :
383 22 : CHIP_ERROR SetDelegate(Delegate * delegate)
384 : {
385 22 : VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
386 22 : mDelegate = delegate;
387 :
388 22 : return CHIP_NO_ERROR;
389 : }
390 :
391 : private:
392 : static constexpr size_t kMaxNodes = 256;
393 : static constexpr size_t kMaxAdminNodes = 32;
394 : static constexpr size_t kMaxGroups = kMaxNodes / 16;
395 : static constexpr size_t kMaxGroupKeySet = kMaxGroups * 16;
396 : static constexpr size_t kMaxFriendlyNameSize = 32;
397 : static constexpr size_t kMaxACLs = 64;
398 :
399 : uint8_t mAnchorRootCA[Credentials::kMaxDERCertLength] = { 0 };
400 : size_t mAnchorRootCALength = 0;
401 : char mFriendlyNameBuffer[kMaxFriendlyNameSize] = { 0 };
402 : size_t mFriendlyNameBufferLength = 0;
403 : NodeId mAnchorNodeId = kUndefinedNodeId;
404 : VendorId mAnchorVendorId = VendorId::NotSpecified;
405 : Clusters::JointFabricDatastore::Structs::DatastoreStatusEntryStruct::Type mDatastoreStatusEntry;
406 :
407 : // TODO: Persist these members to local storage
408 : std::vector<GenericDatastoreNodeInformationEntry> mNodeInformationEntries;
409 : std::vector<Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type> mGroupKeySetList;
410 : std::vector<Clusters::JointFabricDatastore::Structs::DatastoreAdministratorInformationEntryStruct::Type> mAdminEntries;
411 : std::vector<Clusters::JointFabricDatastore::Structs::DatastoreGroupInformationEntryStruct::Type> mGroupInformationEntries;
412 : std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointGroupIDEntryStruct::Type> mEndpointGroupIDEntries;
413 : std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointBindingEntryStruct::Type> mEndpointBindingEntries;
414 : std::vector<Clusters::JointFabricDatastore::Structs::DatastoreNodeKeySetEntryStruct::Type> mNodeKeySetEntries;
415 : std::vector<datastore::ACLEntryStruct> mACLEntries;
416 : std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointEntryStruct::Type> mEndpointEntries;
417 :
418 : Listener * mListeners = nullptr;
419 :
420 : CHIP_ERROR IsNodeIDInDatastore(NodeId nodeId, size_t & index);
421 :
422 : CHIP_ERROR UpdateNodeKeySetList(Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type & groupKeySet);
423 : CHIP_ERROR RemoveKeySet(uint16_t groupKeySetId);
424 :
425 : CHIP_ERROR IsGroupIDInDatastore(GroupId groupId, size_t & index);
426 : CHIP_ERROR IsNodeIdInNodeInformationEntries(NodeId nodeId, size_t & index);
427 : CHIP_ERROR IsNodeIdAndEndpointInEndpointInformationEntries(NodeId nodeId, EndpointId endpointId, size_t & index);
428 :
429 : CHIP_ERROR GenerateAndAssignAUniqueListID(uint16_t & listId);
430 : bool BindingMatches(const Clusters::JointFabricDatastore::Structs::DatastoreBindingTargetStruct::Type & binding1,
431 : const Clusters::JointFabricDatastore::Structs::DatastoreBindingTargetStruct::Type & binding2);
432 : bool ACLMatches(const datastore::AccessControlEntryStruct & acl1,
433 : const Clusters::JointFabricDatastore::Structs::DatastoreAccessControlEntryStruct::DecodableType & acl2);
434 : bool ACLTargetMatches(const Clusters::JointFabricDatastore::Structs::DatastoreAccessControlTargetStruct::Type & target1,
435 : const Clusters::JointFabricDatastore::Structs::DatastoreAccessControlTargetStruct::Type & target2);
436 :
437 : CHIP_ERROR AddNodeKeySetEntry(GroupId groupId, uint16_t groupKeySetId);
438 : CHIP_ERROR RemoveNodeKeySetEntry(GroupId groupId, uint16_t groupKeySetId);
439 :
440 : Delegate * mDelegate = nullptr;
441 :
442 : NodeId mRefreshingNodeId = kUndefinedNodeId;
443 : RefreshState mRefreshState = kIdle;
444 : size_t mRefreshingEndpointIndex = 0;
445 :
446 : std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointEntryStruct::Type> mRefreshingEndpointsList;
447 : std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointBindingEntryStruct::Type> mRefreshingBindingEntries;
448 : std::vector<Clusters::JointFabricDatastore::Structs::DatastoreACLEntryStruct::Type> mRefreshingACLEntries;
449 : };
450 :
451 : } // namespace app
452 : } // namespace chip
|