LCOV - code coverage report
Current view: top level - credentials - GroupDataProvider.h (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 35 43 81.4 %
Date: 2024-02-15 08:20:41 Functions: 16 21 76.2 %

          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 <algorithm>
      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)
      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)
     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           2 :     GroupDataProvider(uint16_t maxGroupsPerFabric    = CHIP_CONFIG_MAX_GROUPS_PER_FABRIC,
     197           2 :                       uint16_t maxGroupKeysPerFabric = CHIP_CONFIG_MAX_GROUP_KEYS_PER_FABRIC) :
     198           2 :         mMaxGroupsPerFabric(maxGroupsPerFabric),
     199           2 :         mMaxGroupKeysPerFabric(maxGroupKeysPerFabric)
     200           2 :     {}
     201             : 
     202          16 :     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, Optional<GroupId> group_id = NullOptional) = 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 1.14