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