Matter SDK Coverage Report
Current view: top level - app/server - JointFabricDatastore.h (source / functions) Coverage Total Hit
Test: SHA:6c8f029dd2432dc900f1c9245c324e69bd79e40a Lines: 73.3 % 116 85
Test Date: 2026-08-08 07:40:09 Functions: 55.8 % 52 29

            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 <algorithm>
      21              : #include <app-common/zap-generated/cluster-objects.h>
      22              : #include <app/data-model-provider/MetadataTypes.h>
      23              : #include <credentials/CHIPCert.h>
      24              : #include <crypto/CHIPCryptoPAL.h>
      25              : #include <functional>
      26              : #include <lib/core/CHIPPersistentStorageDelegate.h>
      27              : #include <lib/core/CHIPVendorIdentifiers.hpp>
      28              : #include <lib/core/DataModelTypes.h>
      29              : #include <lib/core/NodeId.h>
      30              : #include <lib/support/ReadOnlyBuffer.h>
      31              : #include <map>
      32              : #include <type_traits>
      33              : #include <unordered_map>
      34              : #include <utility>
      35              : #include <vector>
      36              : 
      37              : namespace chip {
      38              : 
      39              : // Forward declaration for friend class
      40              : class JFAManager;
      41              : 
      42              : namespace app {
      43              : 
      44              : namespace datastore {
      45              : 
      46              : struct AccessControlEntryStruct
      47              : {
      48              :     Clusters::JointFabricDatastore::DatastoreAccessControlEntryPrivilegeEnum privilege =
      49              :         static_cast<Clusters::JointFabricDatastore::DatastoreAccessControlEntryPrivilegeEnum>(0);
      50              :     Clusters::JointFabricDatastore::DatastoreAccessControlEntryAuthModeEnum authMode =
      51              :         static_cast<Clusters::JointFabricDatastore::DatastoreAccessControlEntryAuthModeEnum>(0);
      52              :     std::vector<uint64_t> subjects;
      53              :     std::vector<Clusters::JointFabricDatastore::Structs::DatastoreAccessControlTargetStruct::Type> targets;
      54              : };
      55              : 
      56              : struct ACLEntryStruct
      57              : {
      58              :     chip::NodeId nodeID = static_cast<chip::NodeId>(0);
      59              :     uint16_t listID     = static_cast<uint16_t>(0);
      60              :     AccessControlEntryStruct ACLEntry;
      61              :     Clusters::JointFabricDatastore::Structs::DatastoreStatusEntryStruct::Type statusEntry;
      62              : };
      63              : 
      64              : } // namespace datastore
      65              : 
      66              : namespace detail {
      67              : 
      68              : /**
      69              :  * Searches `vec` for the first entry that returns true when passed to `pred`, then marks that entry as committed.
      70              :  *
      71              :  * @param [in,out] vec   The vector to search; the matching entry is mutated in place to mark it committed.
      72              :  * @param [in] pred      Predicate invoked as `bool(const T &)`; the first entry for which it returns true is marked.
      73              :  */
      74              : template <typename T, typename Pred>
      75           36 : void MarkEntryCommittedIfFound(std::vector<T> & vec, Pred pred)
      76              : {
      77              :     static_assert(std::is_invocable_r_v<bool, Pred, const T &>,
      78              :                   "MarkEntryCommittedIfFound predicate must accept a const T & and return bool");
      79           36 :     auto it = std::find_if(vec.begin(), vec.end(), pred);
      80           36 :     if (it != vec.end())
      81              :     {
      82           34 :         it->statusEntry.state = Clusters::JointFabricDatastore::DatastoreStateEnum::kCommitted;
      83              :     }
      84           36 : }
      85              : 
      86              : } // namespace detail
      87              : 
      88              : enum RefreshState
      89              : {
      90              :     kIdle,
      91              :     kRefreshingEndpoints,
      92              :     kRefreshingGroups,
      93              :     kRefreshingBindings,
      94              :     kFetchingGroupKeySetList,
      95              :     kFetchingGroupKeySets,
      96              :     kRefreshingGroupKeySets,
      97              :     kRefreshingACLs,
      98              : };
      99              : 
     100              : /**
     101              :  * A struct which extends the DatastoreNodeInformationEntry type with FriendlyName buffer reservation.
     102              :  */
     103              : struct GenericDatastoreNodeInformationEntry
     104              :     : public Clusters::JointFabricDatastore::Structs::DatastoreNodeInformationEntryStruct::Type
     105              : {
     106           24 :     GenericDatastoreNodeInformationEntry(NodeId nodeId = 0,
     107              :                                          Clusters::JointFabricDatastore::DatastoreStateEnum state =
     108              :                                              Clusters::JointFabricDatastore::DatastoreStateEnum::kUnknownEnumValue,
     109              :                                          Optional<CharSpan> label = NullOptional)
     110           24 :     {
     111           24 :         Set(nodeId, state, label);
     112           24 :     }
     113              : 
     114           39 :     GenericDatastoreNodeInformationEntry(const GenericDatastoreNodeInformationEntry & op) { *this = op; }
     115              : 
     116           39 :     GenericDatastoreNodeInformationEntry & operator=(const GenericDatastoreNodeInformationEntry & op)
     117              :     {
     118           39 :         Set(op.nodeID, op.commissioningStatusEntry.state, MakeOptional(op.friendlyName));
     119           39 :         return *this;
     120              :     }
     121              : 
     122           63 :     void Set(NodeId nodeId, Clusters::JointFabricDatastore::DatastoreStateEnum state, Optional<CharSpan> label = NullOptional)
     123              :     {
     124           63 :         this->nodeID                         = nodeId;
     125           63 :         this->commissioningStatusEntry.state = state;
     126           63 :         Set(label);
     127           63 :     }
     128              : 
     129           64 :     void Set(Optional<CharSpan> label = NullOptional)
     130              :     {
     131           64 :         if (label.HasValue())
     132              :         {
     133           64 :             memset(mFriendlyNameBuffer, 0, sizeof(mFriendlyNameBuffer));
     134           64 :             if (label.Value().size() > sizeof(mFriendlyNameBuffer))
     135              :             {
     136            0 :                 memcpy(mFriendlyNameBuffer, label.Value().data(), sizeof(mFriendlyNameBuffer));
     137            0 :                 this->friendlyName = CharSpan(mFriendlyNameBuffer, sizeof(mFriendlyNameBuffer));
     138              :             }
     139              :             else
     140              :             {
     141           64 :                 memcpy(mFriendlyNameBuffer, label.Value().data(), label.Value().size());
     142           64 :                 this->friendlyName = CharSpan(mFriendlyNameBuffer, label.Value().size());
     143              :             }
     144              :         }
     145              :         else
     146              :         {
     147            0 :             this->friendlyName = CharSpan();
     148              :         }
     149           64 :     }
     150              : 
     151              : private:
     152              :     static constexpr size_t kFriendlyNameMaxSize = 32u;
     153              : 
     154              :     char mFriendlyNameBuffer[kFriendlyNameMaxSize];
     155              : };
     156              : 
     157              : class JointFabricDatastore
     158              : {
     159              : public:
     160              :     static JointFabricDatastore & GetInstance()
     161              :     {
     162              :         static JointFabricDatastore sInstance;
     163              :         return sInstance;
     164              :     }
     165              : 
     166              :     class Delegate
     167              :     {
     168              :     public:
     169           16 :         Delegate() {}
     170           16 :         virtual ~Delegate() {}
     171              : 
     172              :         virtual CHIP_ERROR
     173            0 :         SyncNode(NodeId nodeId,
     174              :                  const Clusters::JointFabricDatastore::Structs::DatastoreEndpointGroupIDEntryStruct::Type & endpointGroupIDEntry,
     175              :                  std::function<void()> onSuccess)
     176              :         {
     177            0 :             return CHIP_ERROR_NOT_IMPLEMENTED;
     178              :         }
     179              : 
     180              :         virtual CHIP_ERROR
     181            0 :         SyncNode(NodeId nodeId,
     182              :                  const Clusters::JointFabricDatastore::Structs::DatastoreNodeKeySetEntryStruct::Type & nodeKeySetEntry,
     183              :                  std::function<void()> onSuccess)
     184              :         {
     185            0 :             return CHIP_ERROR_NOT_IMPLEMENTED;
     186              :         }
     187              : 
     188              :         virtual CHIP_ERROR
     189            0 :         SyncNode(NodeId nodeId,
     190              :                  const Clusters::JointFabricDatastore::Structs::DatastoreEndpointBindingEntryStruct::Type & bindingEntry,
     191              :                  std::function<void()> onSuccess)
     192              :         {
     193            0 :             return CHIP_ERROR_NOT_IMPLEMENTED;
     194              :         }
     195              : 
     196              :         virtual CHIP_ERROR
     197            0 :         SyncNode(NodeId nodeId,
     198              :                  std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointBindingEntryStruct::Type> & bindingEntries,
     199              :                  std::function<void()> onSuccess)
     200              :         {
     201            0 :             return CHIP_ERROR_NOT_IMPLEMENTED;
     202              :         }
     203              : 
     204            0 :         virtual CHIP_ERROR SyncNode(NodeId nodeId,
     205              :                                     const Clusters::JointFabricDatastore::Structs::DatastoreACLEntryStruct::Type & aclEntry,
     206              :                                     std::function<void()> onSuccess)
     207              :         {
     208            0 :             return CHIP_ERROR_NOT_IMPLEMENTED;
     209              :         }
     210              : 
     211              :         virtual CHIP_ERROR
     212            0 :         SyncNode(NodeId nodeId,
     213              :                  const std::vector<app::Clusters::JointFabricDatastore::Structs::DatastoreACLEntryStruct::Type> & aclEntries,
     214              :                  std::function<void()> onSuccess)
     215              :         {
     216            0 :             return CHIP_ERROR_NOT_IMPLEMENTED;
     217              :         }
     218              : 
     219            0 :         virtual CHIP_ERROR SyncNode(NodeId nodeId,
     220              :                                     const Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type & groupKeySet,
     221              :                                     std::function<void()> onSuccess)
     222              :         {
     223            0 :             return CHIP_ERROR_NOT_IMPLEMENTED;
     224              :         }
     225              : 
     226            0 :         virtual CHIP_ERROR FetchEndpointList(
     227              :             NodeId nodeId,
     228              :             std::function<void(CHIP_ERROR,
     229              :                                const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointEntryStruct::Type> &)>
     230              :                 onSuccess)
     231              :         {
     232            0 :             return CHIP_ERROR_NOT_IMPLEMENTED;
     233              :         }
     234              : 
     235            0 :         virtual CHIP_ERROR FetchEndpointGroupList(
     236              :             NodeId nodeId, EndpointId endpointId,
     237              :             std::function<
     238              :                 void(CHIP_ERROR,
     239              :                      const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreGroupInformationEntryStruct::Type> &)>
     240              :                 onSuccess)
     241              :         {
     242            0 :             return CHIP_ERROR_NOT_IMPLEMENTED;
     243              :         }
     244              : 
     245            0 :         virtual CHIP_ERROR FetchEndpointBindingList(
     246              :             NodeId nodeId, EndpointId endpointId,
     247              :             std::function<
     248              :                 void(CHIP_ERROR,
     249              :                      const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointBindingEntryStruct::Type> &)>
     250              :                 onSuccess)
     251              :         {
     252            0 :             return CHIP_ERROR_NOT_IMPLEMENTED;
     253              :         }
     254              : 
     255            0 :         virtual CHIP_ERROR FetchGroupKeySetList(NodeId nodeId,
     256              :                                                 std::function<void(CHIP_ERROR, const std::vector<uint16_t> &)> onSuccess)
     257              :         {
     258            0 :             return CHIP_ERROR_NOT_IMPLEMENTED;
     259              :         }
     260              : 
     261            0 :         virtual CHIP_ERROR FetchGroupKeySet(
     262              :             NodeId nodeId, uint16_t groupKeySetID,
     263              :             std::function<void(CHIP_ERROR, const app::Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type &)>
     264              :                 onSuccess)
     265              :         {
     266            0 :             return CHIP_ERROR_NOT_IMPLEMENTED;
     267              :         }
     268              : 
     269            0 :         virtual CHIP_ERROR FetchACLList(
     270              :             NodeId nodeId,
     271              :             std::function<void(CHIP_ERROR,
     272              :                                const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreACLEntryStruct::Type> &)>
     273              :                 onSuccess)
     274              :         {
     275            0 :             return CHIP_ERROR_NOT_IMPLEMENTED;
     276              :         }
     277              :     };
     278              : 
     279              :     CHIP_ERROR SetAnchorRootCA(const ByteSpan & anchorRootCA)
     280              :     {
     281              :         if (anchorRootCA.size() >= sizeof(mAnchorRootCA))
     282              :         {
     283              :             return CHIP_ERROR_INVALID_ARGUMENT;
     284              :         }
     285              :         mAnchorRootCALength = anchorRootCA.size();
     286              :         memcpy(mAnchorRootCA, anchorRootCA.data(), mAnchorRootCALength);
     287              :         return CHIP_NO_ERROR;
     288              :     }
     289              :     ByteSpan GetAnchorRootCA() const { return ByteSpan(mAnchorRootCA, mAnchorRootCALength); }
     290              : 
     291            1 :     CHIP_ERROR SetAnchorNodeId(NodeId anchorNodeId)
     292              :     {
     293            1 :         mAnchorNodeId = anchorNodeId;
     294            1 :         return CHIP_NO_ERROR;
     295              :     }
     296            1 :     NodeId GetAnchorNodeId() { return mAnchorNodeId; }
     297              : 
     298              :     CHIP_ERROR SetAnchorVendorId(VendorId anchorVendorId)
     299              :     {
     300              :         mAnchorVendorId = anchorVendorId;
     301              :         return CHIP_NO_ERROR;
     302              :     }
     303              :     VendorId GetAnchorVendorId() { return mAnchorVendorId; }
     304              : 
     305              :     /**
     306              :      * We track the FabricIndex of the Joint Fabric so that the datastore can be wiped if the Joint Fabric is removed.
     307              :      */
     308            1 :     void SetAnchorFabricIndex(FabricIndex anchorFabricIndex) { mAnchorFabricIndex = anchorFabricIndex; }
     309            2 :     FabricIndex GetAnchorFabricIndex() const { return mAnchorFabricIndex; }
     310              : 
     311              :     /**
     312              :      * Runs when a fabric is removed from the node's FabricTable. If the removed fabric is the anchor fabric, the entire datastore
     313              :      * should be cleared. A no-op for any other fabric.
     314              :      */
     315            2 :     void OnFabricRemoved(FabricIndex fabricIndex)
     316              :     {
     317              :         // The datastore is the anchor's registry for a single joint fabric. Only act when that fabric is
     318              :         // removed; records for any other fabric are not stored here.
     319            2 :         if (mAnchorFabricIndex == kUndefinedFabricIndex || fabricIndex != mAnchorFabricIndex)
     320              :         {
     321            1 :             return;
     322              :         }
     323              : 
     324            1 :         ClearAllRecords();
     325              : 
     326            1 :         for (Listener * listener = mListeners; listener != nullptr; listener = listener->mNext)
     327              :         {
     328            0 :             listener->MarkNodeListChanged();
     329              :         }
     330              :     }
     331              : 
     332              :     CHIP_ERROR SetFriendlyName(const CharSpan & friendlyName)
     333              :     {
     334              :         if (friendlyName.size() >= sizeof(mFriendlyNameBuffer))
     335              :         {
     336              :             return CHIP_ERROR_INVALID_ARGUMENT;
     337              :         }
     338              :         mFriendlyNameBufferLength = friendlyName.size();
     339              :         memcpy(mFriendlyNameBuffer, friendlyName.data(), mFriendlyNameBufferLength);
     340              :         mFriendlyNameBuffer[mFriendlyNameBufferLength] = '\0'; // Ensure null-termination
     341              :         return CHIP_NO_ERROR;
     342              :     }
     343              :     CharSpan GetFriendlyName() const { return CharSpan(mFriendlyNameBuffer, mFriendlyNameBufferLength); }
     344              : 
     345           17 :     const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreGroupInformationEntryStruct::Type> & GetGroupEntries()
     346              :     {
     347           17 :         return mGroupInformationEntries;
     348              :     }
     349              : 
     350              :     void SetStatus(Clusters::JointFabricDatastore::DatastoreStateEnum state, uint32_t updateTimestamp, uint8_t failureCode)
     351              :     {
     352              :         mDatastoreStatusEntry.state           = state;
     353              :         mDatastoreStatusEntry.updateTimestamp = updateTimestamp;
     354              :         mDatastoreStatusEntry.failureCode     = failureCode;
     355              :     }
     356              :     Clusters::JointFabricDatastore::Structs::DatastoreStatusEntryStruct::Type & GetStatus() { return mDatastoreStatusEntry; }
     357              : 
     358            2 :     std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointGroupIDEntryStruct::Type> & GetEndpointGroupIDList()
     359              :     {
     360            2 :         return mEndpointGroupIDEntries;
     361              :     }
     362              : 
     363           12 :     std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointBindingEntryStruct::Type> & GetEndpointBindingList()
     364              :     {
     365           12 :         return mEndpointBindingEntries;
     366              :     }
     367              : 
     368            2 :     std::vector<Clusters::JointFabricDatastore::Structs::DatastoreNodeKeySetEntryStruct::Type> & GetNodeKeySetList()
     369              :     {
     370            2 :         return mNodeKeySetEntries;
     371              :     }
     372              : 
     373           10 :     std::vector<datastore::ACLEntryStruct> & GetNodeACLList() { return mACLEntries; }
     374              : 
     375            5 :     std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointEntryStruct::Type> & GetNodeEndpointList()
     376              :     {
     377            5 :         return mEndpointEntries;
     378              :     }
     379              : 
     380              :     CHIP_ERROR AddPendingNode(NodeId nodeId, const CharSpan & friendlyName);
     381              :     CHIP_ERROR UpdateNode(NodeId nodeId, const CharSpan & friendlyName);
     382              :     CHIP_ERROR RemoveNode(NodeId nodeId);
     383              :     CHIP_ERROR RefreshNode(NodeId nodeId);
     384              :     CHIP_ERROR ContinueRefresh();
     385              : 
     386              :     CHIP_ERROR SetNode(NodeId nodeId, Clusters::JointFabricDatastore::DatastoreStateEnum state);
     387              : 
     388              :     CHIP_ERROR AddGroupKeySetEntry(const Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type & groupKeySet);
     389              :     bool IsGroupKeySetEntryPresent(uint16_t groupKeySetId);
     390              :     CHIP_ERROR RemoveGroupKeySetEntry(uint16_t groupKeySetId);
     391              :     CHIP_ERROR UpdateGroupKeySetEntry(Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type & groupKeySet);
     392              : 
     393              :     CHIP_ERROR
     394              :     AddAdmin(const Clusters::JointFabricDatastore::Structs::DatastoreAdministratorInformationEntryStruct::Type & adminId);
     395              :     bool IsAdminEntryPresent(NodeId nodeId);
     396              :     CHIP_ERROR UpdateAdmin(NodeId nodeId, Optional<CharSpan> friendlyName, Optional<ByteSpan> icac);
     397              :     CHIP_ERROR RemoveAdmin(NodeId nodeId);
     398              : 
     399              :     CHIP_ERROR AddGroup(const Clusters::JointFabricDatastore::Commands::AddGroup::DecodableType & commandData);
     400              :     CHIP_ERROR UpdateGroup(const Clusters::JointFabricDatastore::Commands::UpdateGroup::DecodableType & commandData);
     401              :     CHIP_ERROR RemoveGroup(const Clusters::JointFabricDatastore::Commands::RemoveGroup::DecodableType & commandData);
     402              : 
     403              :     CHIP_ERROR UpdateEndpointForNode(NodeId nodeId, EndpointId endpointId, CharSpan friendlyName);
     404              : 
     405              :     CHIP_ERROR AddGroupIDToEndpointForNode(NodeId nodeId, EndpointId endpointId, GroupId groupId);
     406              :     CHIP_ERROR RemoveGroupIDFromEndpointForNode(NodeId nodeId, EndpointId endpointId, GroupId groupId);
     407              : 
     408              :     CHIP_ERROR
     409              :     AddBindingToEndpointForNode(NodeId nodeId, EndpointId endpointId,
     410              :                                 const Clusters::JointFabricDatastore::Structs::DatastoreBindingTargetStruct::Type & binding);
     411              :     CHIP_ERROR
     412              :     RemoveBindingFromEndpointForNode(uint16_t listId, NodeId nodeId, EndpointId endpointId);
     413              : 
     414              :     CHIP_ERROR
     415              :     AddACLToNode(NodeId nodeId,
     416              :                  const Clusters::JointFabricDatastore::Structs::DatastoreAccessControlEntryStruct::DecodableType & aclEntry);
     417              :     CHIP_ERROR RemoveACLFromNode(uint16_t listId, NodeId nodeId);
     418              : 
     419              :     CHIP_ERROR TestAddNodeKeySetEntry(GroupId groupId, uint16_t groupKeySetId, NodeId nodeId);
     420              :     CHIP_ERROR TestAddEndpointEntry(EndpointId endpointId, NodeId nodeId, CharSpan friendlyName);
     421              : 
     422              :     CHIP_ERROR ForceAddNodeKeySetEntry(uint16_t groupKeySetId, NodeId nodeId);
     423              : 
     424            6 :     const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type> & GetGroupKeySetList()
     425              :     {
     426            6 :         return mGroupKeySetList;
     427              :     }
     428            6 :     const std::vector<GenericDatastoreNodeInformationEntry> & GetNodeInformationEntries() { return mNodeInformationEntries; }
     429              :     const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreAdministratorInformationEntryStruct::Type> &
     430            6 :     GetAdminEntries()
     431              :     {
     432            6 :         return mAdminEntries;
     433              :     }
     434              : 
     435              :     /**
     436              :      * Used to notify of changes in the node list and more TODO.
     437              :      */
     438              :     class Listener
     439              :     {
     440              :     public:
     441            3 :         virtual ~Listener() = default;
     442              : 
     443              :         /**
     444              :          * Notifies of a change in the node list.
     445              :          */
     446              :         virtual void MarkNodeListChanged() = 0;
     447              : 
     448              :     private:
     449              :         Listener * mNext = nullptr;
     450              : 
     451              :         friend class JointFabricDatastore;
     452              :     };
     453              : 
     454              :     /**
     455              :      * Add a listener to be notified of changes in the Joint Fabric Datastore.
     456              :      *
     457              :      * @param [in] listener  The listener to add.
     458              :      */
     459              :     void AddListener(Listener & listener);
     460              : 
     461              :     /**
     462              :      * Remove a listener from being notified of changes in the Joint Fabric Datastore.
     463              :      *
     464              :      * @param [in] listener  The listener to remove.
     465              :      */
     466              :     void RemoveListener(Listener & listener);
     467              : 
     468           16 :     CHIP_ERROR SetDelegate(Delegate * delegate)
     469              :     {
     470           16 :         VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     471           16 :         mDelegate = delegate;
     472              : 
     473           16 :         return CHIP_NO_ERROR;
     474              :     }
     475              : 
     476              : private:
     477              :     // Epoch keys are raw group-key secret material. Hold them in a self-zeroizing buffer so they are
     478              :     // wiped on every deallocation path (per-record erase, overwrite, and whole-map/object destruction),
     479              :     // not just the explicit removal helpers.
     480              :     using EpochKeyStorage = Crypto::SensitiveDataBuffer<Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES>;
     481              : 
     482              :     struct GroupKeySetStorage
     483              :     {
     484              :         EpochKeyStorage epochKey0;
     485              :         EpochKeyStorage epochKey1;
     486              :         EpochKeyStorage epochKey2;
     487              :     };
     488              : 
     489              :     struct GroupInformationStorage
     490              :     {
     491              :         // Friendly names are surfaced as CharSpan and may come from non-null-terminated buffers.
     492              :         // Keep raw owned bytes in vector<char> to preserve exact length semantics for Span use.
     493              :         std::vector<char> friendlyName;
     494              :     };
     495              : 
     496              :     struct AdminEntryStorage
     497              :     {
     498              :         // Friendly names are stored as owned raw bytes for CharSpan (not C-string) semantics.
     499              :         std::vector<char> friendlyName;
     500              :         // ICAC DER is sensitive credential material; hold it in a self-zeroizing buffer (see EpochKeyStorage).
     501              :         Crypto::SensitiveDataBuffer<Credentials::kMaxDERCertLength> icac;
     502              :     };
     503              : 
     504              :     static constexpr size_t kMaxNodes            = 256;
     505              :     static constexpr size_t kMaxAdminNodes       = 32;
     506              :     static constexpr size_t kMaxGroups           = kMaxNodes / 16;
     507              :     static constexpr size_t kMaxGroupKeySet      = kMaxGroups * 16;
     508              :     static constexpr size_t kMaxFriendlyNameSize = 32;
     509              :     static constexpr size_t kMaxACLs             = 64;
     510              : 
     511              :     uint8_t mAnchorRootCA[Credentials::kMaxDERCertLength] = { 0 };
     512              :     size_t mAnchorRootCALength                            = 0;
     513              :     char mFriendlyNameBuffer[kMaxFriendlyNameSize]        = { 0 };
     514              :     size_t mFriendlyNameBufferLength                      = 0;
     515              :     NodeId mAnchorNodeId                                  = kUndefinedNodeId;
     516              :     VendorId mAnchorVendorId                              = VendorId::NotSpecified;
     517              :     FabricIndex mAnchorFabricIndex                        = kUndefinedFabricIndex;
     518              :     Clusters::JointFabricDatastore::Structs::DatastoreStatusEntryStruct::Type mDatastoreStatusEntry;
     519              : 
     520              :     /**
     521              :      * Clears all stored records and resets anchor identity. Used by OnFabricRemoved() when the joint fabric is removed.
     522              :      * Self-zeroizing buffers wipe their secrets as the containers are cleared.
     523              :      */
     524            1 :     void ClearAllRecords()
     525              :     {
     526              :         // Erasing the secret-bearing maps destroys the self-zeroizing EpochKeyStorage / ICAC buffers,
     527              :         // wiping their contents.
     528            1 :         mGroupKeySetStorage.clear();
     529            1 :         mAdminEntryStorage.clear();
     530            1 :         mGroupInformationStorage.clear();
     531            1 :         mEndpointFriendlyNameStorage.clear();
     532              : 
     533            1 :         mNodeInformationEntries.clear();
     534            1 :         mGroupKeySetList.clear();
     535            1 :         mAdminEntries.clear();
     536            1 :         mGroupInformationEntries.clear();
     537            1 :         mEndpointGroupIDEntries.clear();
     538            1 :         mEndpointBindingEntries.clear();
     539            1 :         mNodeKeySetEntries.clear();
     540            1 :         mACLEntries.clear();
     541            1 :         mEndpointEntries.clear();
     542              : 
     543              :         // Reset anchor identity: with the joint fabric gone, the datastore no longer describes a fabric.
     544            1 :         memset(mAnchorRootCA, 0, sizeof(mAnchorRootCA));
     545            1 :         mAnchorRootCALength       = 0;
     546            1 :         mFriendlyNameBuffer[0]    = '\0';
     547            1 :         mFriendlyNameBufferLength = 0;
     548            1 :         mAnchorNodeId             = kUndefinedNodeId;
     549            1 :         mAnchorVendorId           = VendorId::NotSpecified;
     550            1 :         mAnchorFabricIndex        = kUndefinedFabricIndex;
     551            1 :         mDatastoreStatusEntry     = Clusters::JointFabricDatastore::Structs::DatastoreStatusEntryStruct::Type{};
     552            1 :     }
     553              : 
     554              :     // TODO: Persist these members to local storage
     555              :     std::vector<GenericDatastoreNodeInformationEntry> mNodeInformationEntries;
     556              :     std::vector<Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type> mGroupKeySetList;
     557              :     std::unordered_map<uint16_t, GroupKeySetStorage> mGroupKeySetStorage;
     558              :     std::vector<Clusters::JointFabricDatastore::Structs::DatastoreAdministratorInformationEntryStruct::Type> mAdminEntries;
     559              :     std::unordered_map<NodeId, AdminEntryStorage> mAdminEntryStorage;
     560              :     std::vector<Clusters::JointFabricDatastore::Structs::DatastoreGroupInformationEntryStruct::Type> mGroupInformationEntries;
     561              :     std::unordered_map<GroupId, GroupInformationStorage> mGroupInformationStorage;
     562              :     std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointGroupIDEntryStruct::Type> mEndpointGroupIDEntries;
     563              :     std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointBindingEntryStruct::Type> mEndpointBindingEntries;
     564              :     std::vector<Clusters::JointFabricDatastore::Structs::DatastoreNodeKeySetEntryStruct::Type> mNodeKeySetEntries;
     565              :     std::vector<datastore::ACLEntryStruct> mACLEntries;
     566              :     std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointEntryStruct::Type> mEndpointEntries;
     567              :     std::map<std::pair<NodeId, EndpointId>, std::vector<char>> mEndpointFriendlyNameStorage;
     568              : 
     569              :     Listener * mListeners = nullptr;
     570              : 
     571              :     friend class chip::JFAManager;
     572              : 
     573              :     CHIP_ERROR
     574              :     ForceAddGroup(const Clusters::JointFabricDatastore::Commands::AddGroup::DecodableType & commandData);
     575              : 
     576              :     CHIP_ERROR IsNodeIDInDatastore(NodeId nodeId, size_t & index);
     577              : 
     578              :     CHIP_ERROR UpdateNodeKeySetList(Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type & groupKeySet);
     579              :     CHIP_ERROR RemoveKeySet(uint16_t groupKeySetId);
     580              : 
     581              :     CHIP_ERROR IsGroupIDInDatastore(GroupId groupId, size_t & index);
     582              :     CHIP_ERROR IsNodeIdInNodeInformationEntries(NodeId nodeId, size_t & index);
     583              :     CHIP_ERROR IsNodeIdAndEndpointInEndpointInformationEntries(NodeId nodeId, EndpointId endpointId, size_t & index);
     584              : 
     585              :     CHIP_ERROR GenerateAndAssignAUniqueListID(uint16_t & listId);
     586              :     bool BindingMatches(const Clusters::JointFabricDatastore::Structs::DatastoreBindingTargetStruct::Type & binding1,
     587              :                         const Clusters::JointFabricDatastore::Structs::DatastoreBindingTargetStruct::Type & binding2);
     588              :     bool ACLMatches(const datastore::AccessControlEntryStruct & acl1,
     589              :                     const Clusters::JointFabricDatastore::Structs::DatastoreAccessControlEntryStruct::DecodableType & acl2);
     590              :     bool ACLTargetMatches(const Clusters::JointFabricDatastore::Structs::DatastoreAccessControlTargetStruct::Type & target1,
     591              :                           const Clusters::JointFabricDatastore::Structs::DatastoreAccessControlTargetStruct::Type & target2);
     592              : 
     593              :     CHIP_ERROR AddNodeKeySetEntry(GroupId groupId, uint16_t groupKeySetId);
     594              :     CHIP_ERROR RemoveNodeKeySetEntry(GroupId groupId, uint16_t groupKeySetId);
     595              : 
     596              :     CHIP_ERROR
     597              :     CopyGroupKeySetWithOwnedSpans(const Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type & source,
     598              :                                   Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type & destination);
     599              :     void RemoveGroupKeySetStorage(uint16_t groupKeySetId);
     600              : 
     601              :     void SetGroupInformationFriendlyNameWithOwnedStorage(
     602              :         GroupId groupId, const CharSpan & friendlyName,
     603              :         Clusters::JointFabricDatastore::Structs::DatastoreGroupInformationEntryStruct::Type & destination);
     604              :     void RemoveGroupInformationStorage(GroupId groupId);
     605              : 
     606              :     CHIP_ERROR SetAdminEntryWithOwnedStorage(
     607              :         NodeId nodeId, const CharSpan & friendlyName, const ByteSpan & icac,
     608              :         Clusters::JointFabricDatastore::Structs::DatastoreAdministratorInformationEntryStruct::Type & destination);
     609              :     void RemoveAdminEntryStorage(NodeId nodeId);
     610              : 
     611              :     void SetEndpointFriendlyNameWithOwnedStorage(
     612              :         NodeId nodeId, EndpointId endpointId, const CharSpan & friendlyName,
     613              :         Clusters::JointFabricDatastore::Structs::DatastoreEndpointEntryStruct::Type & destination);
     614              :     void RemoveEndpointFriendlyNameStorage(NodeId nodeId, EndpointId endpointId);
     615              : 
     616              :     // Helper methods for copying optional ByteSpan and simple nullable values
     617              :     void CopyByteSpanWithOwnedStorage(const DataModel::Nullable<ByteSpan> & source, EpochKeyStorage & storage,
     618              :                                       DataModel::Nullable<ByteSpan> & destination);
     619              : 
     620              :     template <typename T>
     621            9 :     void CopyNullableValue(const DataModel::Nullable<T> & source, DataModel::Nullable<T> & destination)
     622              :     {
     623              :         // Only update destination if source has a value; leave destination unchanged if source is null.
     624            9 :         if (!source.IsNull())
     625              :         {
     626            0 :             static_cast<void>(destination.Update(source));
     627              :         }
     628            9 :     }
     629              : 
     630              :     Delegate * mDelegate = nullptr;
     631              : 
     632              :     NodeId mRefreshingNodeId           = kUndefinedNodeId;
     633              :     RefreshState mRefreshState         = kIdle;
     634              :     size_t mRefreshingEndpointIndex    = 0;
     635              :     size_t mRefreshingGroupKeySetIndex = 0;
     636              : 
     637              :     std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointEntryStruct::Type> mRefreshingEndpointsList;
     638              :     std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointBindingEntryStruct::Type> mRefreshingBindingEntries;
     639              :     std::vector<Clusters::JointFabricDatastore::Structs::DatastoreACLEntryStruct::Type> mRefreshingACLEntries;
     640              :     std::vector<uint16_t> mRefreshingGroupKeySetIDs;
     641              : };
     642              : 
     643              : } // namespace app
     644              : } // namespace chip
        

Generated by: LCOV version 2.0-1