Matter SDK Coverage Report
Current view: top level - credentials - GroupDataProvider.h (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 81.4 % 43 35
Test Date: 2025-01-17 19:00:11 Functions: 76.2 % 21 16

            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              : #pragma once
      18              : 
      19              : #include <optional>
      20              : #include <stdint.h>
      21              : #include <sys/types.h>
      22              : 
      23              : #include <app/util/basic-types.h>
      24              : #include <crypto/CHIPCryptoPAL.h>
      25              : #include <lib/core/CHIPError.h>
      26              : #include <lib/core/ClusterEnums.h>
      27              : #include <lib/support/CHIPMemString.h>
      28              : #include <lib/support/CommonIterator.h>
      29              : 
      30              : namespace chip {
      31              : namespace Credentials {
      32              : 
      33              : class GroupDataProvider
      34              : {
      35              : public:
      36              :     using SecurityPolicy                                  = app::Clusters::GroupKeyManagement::GroupKeySecurityPolicyEnum;
      37              :     static constexpr KeysetId kIdentityProtectionKeySetId = 0;
      38              : 
      39              :     struct GroupInfo
      40              :     {
      41              :         static constexpr size_t kGroupNameMax = CHIP_CONFIG_MAX_GROUP_NAME_LENGTH;
      42              : 
      43              :         // Identifies group within the scope of the given Fabric
      44              :         GroupId group_id = kUndefinedGroupId;
      45              :         // Lastest group name written for a given GroupId on any Endpoint via the Groups cluster
      46              :         char name[kGroupNameMax + 1] = { 0 };
      47              : 
      48            0 :         GroupInfo() { SetName(nullptr); }
      49          340 :         GroupInfo(const char * groupName) { SetName(groupName); }
      50              :         GroupInfo(const CharSpan & groupName) { SetName(groupName); }
      51            0 :         GroupInfo(GroupId id, const char * groupName) : group_id(id) { SetName(groupName); }
      52              :         GroupInfo(GroupId id, const CharSpan & groupName) : group_id(id) { SetName(groupName); }
      53            0 :         void SetName(const char * groupName)
      54              :         {
      55            0 :             if (nullptr == groupName)
      56              :             {
      57            0 :                 name[0] = 0;
      58              :             }
      59              :             else
      60              :             {
      61            0 :                 Platform::CopyString(name, groupName);
      62              :             }
      63            0 :         }
      64          621 :         void SetName(const CharSpan & groupName)
      65              :         {
      66          621 :             if (nullptr == groupName.data())
      67              :             {
      68          621 :                 name[0] = 0;
      69              :             }
      70              :             else
      71              :             {
      72            0 :                 Platform::CopyString(name, groupName);
      73              :             }
      74          621 :         }
      75              :         bool operator==(const GroupInfo & other) const
      76              :         {
      77              :             return (this->group_id == other.group_id) && !strncmp(this->name, other.name, kGroupNameMax);
      78              :         }
      79              :     };
      80              : 
      81              :     struct GroupKey
      82              :     {
      83           66 :         GroupKey() = default;
      84           21 :         GroupKey(GroupId group, KeysetId keyset) : group_id(group), keyset_id(keyset) {}
      85              :         // Identifies group within the scope of the given Fabric
      86              :         GroupId group_id = kUndefinedGroupId;
      87              :         // Set of group keys that generate operational group keys for use with this group
      88              :         KeysetId keyset_id = 0;
      89              :         bool operator==(const GroupKey & other) const
      90              :         {
      91              :             return this->group_id == other.group_id && this->keyset_id == other.keyset_id;
      92              :         }
      93              :     };
      94              : 
      95              :     struct GroupEndpoint
      96              :     {
      97          145 :         GroupEndpoint() = default;
      98           88 :         GroupEndpoint(GroupId group, EndpointId endpoint) : group_id(group), endpoint_id(endpoint) {}
      99              :         // Identifies group within the scope of the given Fabric
     100              :         GroupId group_id = kUndefinedGroupId;
     101              :         // Endpoint on the Node to which messages to this group may be forwarded
     102              :         EndpointId endpoint_id = kInvalidEndpointId;
     103              : 
     104              :         bool operator==(const GroupEndpoint & other) const
     105              :         {
     106              :             return this->group_id == other.group_id && this->endpoint_id == other.endpoint_id;
     107              :         }
     108              :     };
     109              : 
     110              :     struct GroupSession
     111              :     {
     112            6 :         GroupSession()   = default;
     113              :         GroupId group_id = kUndefinedGroupId;
     114              :         FabricIndex fabric_index;
     115              :         SecurityPolicy security_policy;
     116              :         Crypto::SymmetricKeyContext * keyContext = nullptr;
     117              :     };
     118              : 
     119              :     // An EpochKey is a single key usable to determine an operational group key
     120              :     struct EpochKey
     121              :     {
     122              :         static constexpr size_t kLengthBytes = Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES;
     123              :         // Validity start time in microseconds since 2000-01-01T00:00:00 UTC ("the Epoch")
     124              :         uint64_t start_time;
     125              :         // Actual key bits. Depending on context, it may be a raw epoch key (as seen within `SetKeySet` calls)
     126              :         // or it may be the derived operational group key (as seen in any other usage).
     127              :         uint8_t key[kLengthBytes];
     128              : 
     129          153 :         void Clear()
     130              :         {
     131          153 :             start_time = 0;
     132          153 :             Crypto::ClearSecretData(&key[0], sizeof(key));
     133          153 :         }
     134              :     };
     135              : 
     136              :     // A operational group key set, usable by many GroupState mappings
     137              :     struct KeySet
     138              :     {
     139              :         static constexpr size_t kEpochKeysMax = 3;
     140              : 
     141           21 :         KeySet() = default;
     142              :         KeySet(uint16_t id, SecurityPolicy policy_id, uint8_t num_keys) : keyset_id(id), policy(policy_id), num_keys_used(num_keys)
     143              :         {}
     144              : 
     145              :         // The actual keys for the group key set
     146              :         EpochKey epoch_keys[kEpochKeysMax];
     147              :         // Logical id provided by the Administrator that configured the entry
     148              :         uint16_t keyset_id = 0;
     149              :         // Security policy to use for groups that use this keyset
     150              :         SecurityPolicy policy = SecurityPolicy::kCacheAndSync;
     151              :         // Number of keys present
     152              :         uint8_t num_keys_used = 0;
     153              : 
     154              :         bool operator==(const KeySet & other) const
     155              :         {
     156              :             VerifyOrReturnError(this->policy == other.policy && this->num_keys_used == other.num_keys_used, false);
     157              :             return !memcmp(this->epoch_keys, other.epoch_keys, this->num_keys_used * sizeof(EpochKey));
     158              :         }
     159              : 
     160           28 :         void ClearKeys()
     161              :         {
     162          112 :             for (size_t key_idx = 0; key_idx < kEpochKeysMax; ++key_idx)
     163              :             {
     164           84 :                 epoch_keys[key_idx].Clear();
     165              :             }
     166           28 :         }
     167              :     };
     168              : 
     169              :     /**
     170              :      *  Interface to listen for changes in the Group info.
     171              :      */
     172              :     class GroupListener
     173              :     {
     174              :     public:
     175            1 :         virtual ~GroupListener() = default;
     176              :         /**
     177              :          *  Callback invoked when a new group is added.
     178              :          *
     179              :          *  @param[in] new_group  GroupInfo structure of the new group.
     180              :          */
     181              :         virtual void OnGroupAdded(FabricIndex fabric_index, const GroupInfo & new_group) = 0;
     182              :         /**
     183              :          *  Callback invoked when an existing group is removed.
     184              :          *
     185              :          *  @param[in] old_group  GroupInfo structure of the removed group.
     186              :          */
     187              :         virtual void OnGroupRemoved(FabricIndex fabric_index, const GroupInfo & old_group) = 0;
     188              :     };
     189              : 
     190              :     using GroupInfoIterator    = CommonIterator<GroupInfo>;
     191              :     using GroupKeyIterator     = CommonIterator<GroupKey>;
     192              :     using EndpointIterator     = CommonIterator<GroupEndpoint>;
     193              :     using KeySetIterator       = CommonIterator<KeySet>;
     194              :     using GroupSessionIterator = CommonIterator<GroupSession>;
     195              : 
     196            1 :     GroupDataProvider(uint16_t maxGroupsPerFabric    = CHIP_CONFIG_MAX_GROUPS_PER_FABRIC,
     197            1 :                       uint16_t maxGroupKeysPerFabric = CHIP_CONFIG_MAX_GROUP_KEYS_PER_FABRIC) :
     198            1 :         mMaxGroupsPerFabric(maxGroupsPerFabric),
     199            1 :         mMaxGroupKeysPerFabric(maxGroupKeysPerFabric)
     200            1 :     {}
     201              : 
     202            1 :     virtual ~GroupDataProvider() = default;
     203              : 
     204              :     // Not copyable
     205              :     GroupDataProvider(const GroupDataProvider &)             = delete;
     206              :     GroupDataProvider & operator=(const GroupDataProvider &) = delete;
     207              : 
     208              :     uint16_t GetMaxGroupsPerFabric() const { return mMaxGroupsPerFabric; }
     209              :     uint16_t GetMaxGroupKeysPerFabric() const { return mMaxGroupKeysPerFabric; }
     210              : 
     211              :     /**
     212              :      *  Initialize the GroupDataProvider, including possibly any persistent
     213              :      *  data store initialization done by the implementation. Must be called once
     214              :      *  before any other API succeeds.
     215              :      *
     216              :      *  @retval #CHIP_ERROR_INCORRECT_STATE if called when already initialized.
     217              :      *  @retval #CHIP_NO_ERROR on success
     218              :      */
     219              :     virtual CHIP_ERROR Init() = 0;
     220              :     virtual void Finish()     = 0;
     221              : 
     222              :     //
     223              :     // Group Table
     224              :     //
     225              : 
     226              :     // By id
     227              :     virtual CHIP_ERROR SetGroupInfo(FabricIndex fabric_index, const GroupInfo & info)             = 0;
     228              :     virtual CHIP_ERROR GetGroupInfo(FabricIndex fabric_index, GroupId group_id, GroupInfo & info) = 0;
     229              :     virtual CHIP_ERROR RemoveGroupInfo(FabricIndex fabric_index, GroupId group_id)                = 0;
     230              :     // By index
     231              :     virtual CHIP_ERROR SetGroupInfoAt(FabricIndex fabric_index, size_t index, const GroupInfo & info) = 0;
     232              :     virtual CHIP_ERROR GetGroupInfoAt(FabricIndex fabric_index, size_t index, GroupInfo & info)       = 0;
     233              :     virtual CHIP_ERROR RemoveGroupInfoAt(FabricIndex fabric_index, size_t index)                      = 0;
     234              :     // Endpoints
     235              :     virtual bool HasEndpoint(FabricIndex fabric_index, GroupId group_id, EndpointId endpoint_id)          = 0;
     236              :     virtual CHIP_ERROR AddEndpoint(FabricIndex fabric_index, GroupId group_id, EndpointId endpoint_id)    = 0;
     237              :     virtual CHIP_ERROR RemoveEndpoint(FabricIndex fabric_index, GroupId group_id, EndpointId endpoint_id) = 0;
     238              :     virtual CHIP_ERROR RemoveEndpoint(FabricIndex fabric_index, EndpointId endpoint_id)                   = 0;
     239              :     // Iterators
     240              :     /**
     241              :      *  Creates an iterator that may be used to obtain the list of groups associated with the given fabric.
     242              :      *  In order to release the allocated memory, the Release() method must be called after the iteration is finished.
     243              :      *  Modifying the group table during the iteration is currently not supported, and may yield unexpected behaviour.
     244              :      *  @retval An instance of EndpointIterator on success
     245              :      *  @retval nullptr if no iterator instances are available.
     246              :      */
     247              :     virtual GroupInfoIterator * IterateGroupInfo(FabricIndex fabric_index) = 0;
     248              :     /**
     249              :      *  Creates an iterator that may be used to obtain the list of (group, endpoint) pairs associated with the given fabric.
     250              :      *  In order to release the allocated memory, the Release() method must be called after the iteration is finished.
     251              :      *  Modifying the group table during the iteration is currently not supported, and may yield unexpected behaviour.
     252              :      *  If you wish to iterate only the endpoints of a particular group id you can provide the optional `group_id` to do so.
     253              :      *  @retval An instance of EndpointIterator on success
     254              :      *  @retval nullptr if no iterator instances are available.
     255              :      */
     256              :     virtual EndpointIterator * IterateEndpoints(FabricIndex fabric_index, std::optional<GroupId> group_id = std::nullopt) = 0;
     257              : 
     258              :     //
     259              :     // Group-Key map
     260              :     //
     261              : 
     262              :     virtual CHIP_ERROR SetGroupKeyAt(FabricIndex fabric_index, size_t index, const GroupKey & info) = 0;
     263              :     virtual CHIP_ERROR GetGroupKeyAt(FabricIndex fabric_index, size_t index, GroupKey & info)       = 0;
     264              :     virtual CHIP_ERROR RemoveGroupKeyAt(FabricIndex fabric_index, size_t index)                     = 0;
     265              :     virtual CHIP_ERROR RemoveGroupKeys(FabricIndex fabric_index)                                    = 0;
     266              : 
     267              :     /**
     268              :      *  Creates an iterator that may be used to obtain the list of (group, keyset) pairs associated with the given fabric.
     269              :      *  In order to release the allocated memory, the Release() method must be called after the iteration is finished.
     270              :      *  Modifying the keyset mappings during the iteration is currently not supported, and may yield unexpected behaviour.
     271              :      *  @retval An instance of GroupKeyIterator on success
     272              :      *  @retval nullptr if no iterator instances are available.
     273              :      */
     274              :     virtual GroupKeyIterator * IterateGroupKeys(FabricIndex fabric_index) = 0;
     275              : 
     276              :     //
     277              :     // Key Sets
     278              :     //
     279              : 
     280              :     virtual CHIP_ERROR SetKeySet(FabricIndex fabric_index, const ByteSpan & compressed_fabric_id, const KeySet & keys) = 0;
     281              :     virtual CHIP_ERROR GetKeySet(FabricIndex fabric_index, KeysetId keyset_id, KeySet & keys)                          = 0;
     282              :     virtual CHIP_ERROR RemoveKeySet(FabricIndex fabric_index, KeysetId keyset_id)                                      = 0;
     283              : 
     284              :     /**
     285              :      * @brief Obtain the actual operational Identity Protection Key (IPK) keyset for a given
     286              :      *        fabric. These keys are used by the CASE protocol, and do not participate in
     287              :      *        any direct traffic encryption. Since the identity protection operational keyset
     288              :      *        is used in multiple key derivations and procedures, it cannot be hidden behind a
     289              :      *        SymmetricKeyContext, and must be obtainable by value.
     290              :      *
     291              :      * @param fabric_index - Fabric index for which to get the IPK operational keyset
     292              :      * @param out_keyset - Reference to a KeySet where the IPK keys will be stored on success
     293              :      * @return CHIP_NO_ERROR on success, CHIP_ERROR_NOT_FOUND if the IPK keyset is somehow unavailable
     294              :      *         or another CHIP_ERROR value if an internal storage error occurs.
     295              :      */
     296              :     virtual CHIP_ERROR GetIpkKeySet(FabricIndex fabric_index, KeySet & out_keyset) = 0;
     297              : 
     298              :     /**
     299              :      *  Creates an iterator that may be used to obtain the list of key sets associated with the given fabric.
     300              :      *  In order to release the allocated memory, the Release() method must be called after the iteration is finished.
     301              :      *  Modifying the key sets table during the iteration is currently not supported, and may yield unexpected behaviour.
     302              :      *
     303              :      *  @retval An instance of KeySetIterator on success
     304              :      *  @retval nullptr if no iterator instances are available.
     305              :      */
     306              :     virtual KeySetIterator * IterateKeySets(FabricIndex fabric_index) = 0;
     307              : 
     308              :     // Fabrics
     309              :     virtual CHIP_ERROR RemoveFabric(FabricIndex fabric_index) = 0;
     310              : 
     311              :     // Decryption
     312              :     virtual GroupSessionIterator * IterateGroupSessions(uint16_t session_id)                        = 0;
     313              :     virtual Crypto::SymmetricKeyContext * GetKeyContext(FabricIndex fabric_index, GroupId group_id) = 0;
     314              : 
     315              :     // Listener
     316            1 :     void SetListener(GroupListener * listener) { mListener = listener; };
     317              :     void RemoveListener() { mListener = nullptr; };
     318              : 
     319              : protected:
     320           78 :     void GroupAdded(FabricIndex fabric_index, const GroupInfo & new_group)
     321              :     {
     322           78 :         if (mListener)
     323              :         {
     324           38 :             mListener->OnGroupAdded(fabric_index, new_group);
     325              :         }
     326           78 :     }
     327           32 :     void GroupRemoved(FabricIndex fabric_index, const GroupInfo & old_group)
     328              :     {
     329           32 :         if (mListener)
     330              :         {
     331           32 :             mListener->OnGroupRemoved(fabric_index, old_group);
     332              :         }
     333           32 :     }
     334              :     const uint16_t mMaxGroupsPerFabric;
     335              :     const uint16_t mMaxGroupKeysPerFabric;
     336              :     GroupListener * mListener = nullptr;
     337              : };
     338              : 
     339              : /**
     340              :  * @brief Utility Set the IPK Epoch key on a GroupDataProvider assuming a single IPK
     341              :  *
     342              :  * This utility replaces having to call `GroupDataProvider::SetKeySet` for the simple situation of a
     343              :  * single IPK for a fabric, if a single epoch key is used. Start time will be set to 0 ("was always valid")
     344              :  *
     345              :  * @param provider - pointer to GroupDataProvider on which to set the IPK
     346              :  * @param fabric_index - fabric index within the GroupDataProvider for which to set the IPK
     347              :  * @param ipk_epoch_span - Span containing the IPK epoch key
     348              :  * @param compressed_fabric_id - Compressed fabric ID associated with the fabric, for key derivation
     349              :  * @return CHIP_NO_ERROR on success, CHIP_ERROR_INVALID_ARGUMENT on any bad argument, other CHIP_ERROR values
     350              :  *         from implementation on other errors
     351              :  */
     352              : inline CHIP_ERROR SetSingleIpkEpochKey(GroupDataProvider * provider, FabricIndex fabric_index, const ByteSpan & ipk_epoch_span,
     353              :                                        const ByteSpan & compressed_fabric_id)
     354              : {
     355              :     GroupDataProvider::KeySet ipkKeySet(GroupDataProvider::kIdentityProtectionKeySetId,
     356              :                                         GroupDataProvider::SecurityPolicy::kTrustFirst, 1);
     357              : 
     358              :     VerifyOrReturnError(provider != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     359              :     VerifyOrReturnError(ipk_epoch_span.size() == sizeof(ipkKeySet.epoch_keys[0].key), CHIP_ERROR_INVALID_ARGUMENT);
     360              :     VerifyOrReturnError(compressed_fabric_id.size() == sizeof(uint64_t), CHIP_ERROR_INVALID_ARGUMENT);
     361              : 
     362              :     ipkKeySet.epoch_keys[0].start_time = 0;
     363              :     memcpy(&ipkKeySet.epoch_keys[0].key, ipk_epoch_span.data(), ipk_epoch_span.size());
     364              : 
     365              :     // Set a single IPK, validate key derivation follows spec
     366              :     return provider->SetKeySet(fabric_index, compressed_fabric_id, ipkKeySet);
     367              : }
     368              : 
     369              : /**
     370              :  * Instance getter for the global GroupDataProvider.
     371              :  *
     372              :  * Callers have to externally synchronize usage of this function.
     373              :  *
     374              :  * @return The global Group Data Provider
     375              :  */
     376              : GroupDataProvider * GetGroupDataProvider();
     377              : 
     378              : /**
     379              :  * Instance setter for the global GroupDataProvider.
     380              :  *
     381              :  * Callers have to externally synchronize usage of this function.
     382              :  *
     383              :  * The `provider` can be set to nullptr if the owner is done with it fully.
     384              :  *
     385              :  * @param[in] provider pointer to the Group Data Provider global isntance to use
     386              :  */
     387              : void SetGroupDataProvider(GroupDataProvider * provider);
     388              : 
     389              : } // namespace Credentials
     390              : } // namespace chip
        

Generated by: LCOV version 2.0-1