Line data Source code
1 : /**
2 : *
3 : * Copyright (c) 2023 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 <crypto/CHIPCryptoPAL.h>
20 : #include <crypto/SessionKeystore.h>
21 : #include <lib/core/CHIPConfig.h>
22 : #include <lib/core/CHIPPersistentStorageDelegate.h>
23 : #include <lib/core/ClusterEnums.h>
24 : #include <lib/core/DataModelTypes.h>
25 : #include <lib/support/CodeUtils.h>
26 : #include <lib/support/PersistentData.h>
27 : #include <stddef.h>
28 :
29 : namespace chip {
30 : namespace Crypto {
31 : using SymmetricKeystore = SessionKeystore;
32 : }
33 : } // namespace chip
34 :
35 : namespace chip {
36 :
37 : static constexpr size_t MaxICDMonitoringEntrySize()
38 : {
39 : // All the fields added together
40 : return TLV::EstimateStructOverhead(sizeof(NodeId) /*checkInNodeID*/, sizeof(uint64_t) /*monitoredSubject*/,
41 : sizeof(Crypto::Symmetric128BitsKeyByteArray) /*aes_key_handle*/,
42 : sizeof(Crypto::Symmetric128BitsKeyByteArray) /*hmac_key_handle*/,
43 : sizeof(uint8_t) /*client_type*/) *
44 : // Provide 50% extra space to make a firmware upgrade that starts storing
45 : // more data followed by a downgrade work easily and reliably.
46 : // The 50% number is chosen fairly randomly; storage increases larger than that are
47 : // possible but need to be staged carefully.
48 : 3 / 2;
49 : }
50 :
51 : inline constexpr size_t kICDMonitoringBufferSize = MaxICDMonitoringEntrySize();
52 :
53 : struct ICDMonitoringEntry : public PersistentData<kICDMonitoringBufferSize>
54 : {
55 13 : ICDMonitoringEntry(FabricIndex fabric = kUndefinedFabricIndex, NodeId nodeId = kUndefinedNodeId)
56 13 : {
57 13 : this->fabricIndex = fabric;
58 13 : this->checkInNodeID = nodeId;
59 13 : this->monitoredSubject = nodeId;
60 13 : }
61 :
62 44 : ICDMonitoringEntry(Crypto::SymmetricKeystore * keyStore, FabricIndex fabric = kUndefinedFabricIndex,
63 : NodeId nodeId = kUndefinedNodeId)
64 44 : {
65 44 : this->fabricIndex = fabric;
66 44 : this->checkInNodeID = nodeId;
67 44 : this->monitoredSubject = nodeId;
68 44 : this->symmetricKeystore = keyStore;
69 44 : }
70 :
71 : CHIP_ERROR UpdateKey(StorageKeyName & key) override;
72 : CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override;
73 : CHIP_ERROR Deserialize(TLV::TLVReader & reader) override;
74 : void Clear() override;
75 : /**
76 : * @brief Set the Key object
77 : * This method will create a new keyHandle. The key handle might contain either
78 : * the raw key or a keyID depending on which Crypto implementation is used.
79 : * To avoid leaking keys, API consumers must either call the DeleteKey method
80 : * or save the entry within the ICDMonitoring Table before this object goes out of scope.
81 : *
82 : * A new entry object should be used for each key when adding entries to the ICDMonitoring
83 : * table.
84 : *
85 : * @param keyData A byte span containing the raw key
86 : * @return CHIP_ERROR CHIP_NO_ERROR success
87 : * CHIP_ERROR_INVALID_ARGUMENT wrong size of the raw key
88 : * CHIP_ERROR_INTERNAL No KeyStore for the entry or Key Handle already present
89 : * CHIP_ERROR_XXX Crypto API related failure
90 : */
91 : CHIP_ERROR SetKey(ByteSpan keyData);
92 : CHIP_ERROR DeleteKey(void);
93 : inline bool IsValid()
94 : {
95 : return (symmetricKeystore != nullptr && keyHandleValid && fabricIndex != kUndefinedFabricIndex &&
96 : checkInNodeID != kUndefinedNodeId);
97 : }
98 :
99 : ICDMonitoringEntry & operator=(const ICDMonitoringEntry & icdMonitoringEntry);
100 :
101 : /**
102 : * @brief Implement the key verification needed by the ICDManagement Server.
103 : * Since for some key implementations we cannot retrieve the key from the AES128KeyHandle
104 : * we must implement a way to deduce whether the verification key
105 : * received is the same or at least works as the same way as the one stored.
106 : *
107 : * This method will produce a random number and then encrypt it with the keyData.
108 : * It will then decrypt it with the key stored in the entry. If the resulting decrypted
109 : * challenge matches the randomly generated number, then we can safely assume that both key are interchangeable.
110 : * This method cannot guarantee a perfect match since the probability of two keys generating the same output in AES128 is
111 : * not 0 but 1/2^128 which is small enough for our purposes.
112 : *
113 : * @param keyData
114 : * @return bool True if the key is equivalent to the one stored, otherwise false
115 : */
116 : bool IsKeyEquivalent(ByteSpan keyData);
117 :
118 : chip::FabricIndex fabricIndex = kUndefinedFabricIndex;
119 : chip::NodeId checkInNodeID = kUndefinedNodeId;
120 : uint64_t monitoredSubject = static_cast<uint64_t>(0);
121 : app::Clusters::IcdManagement::ClientTypeEnum clientType = app::Clusters::IcdManagement::ClientTypeEnum::kPermanent;
122 : Crypto::Aes128KeyHandle aesKeyHandle = Crypto::Aes128KeyHandle();
123 : Crypto::Hmac128KeyHandle hmacKeyHandle = Crypto::Hmac128KeyHandle();
124 : bool keyHandleValid = false;
125 : uint16_t index = 0;
126 : Crypto::SymmetricKeystore * symmetricKeystore = nullptr;
127 : };
128 :
129 : /**
130 : * @brief ICDMonitoringTable exists to manage the persistence of entries in the IcdManagement Cluster.
131 : * To access persisted data with the ICDMonitoringTable class, instantiate an instance of this class
132 : * and call the LoadFromStorage function.
133 : *
134 : * This class can only manage one fabric at a time. The flow is load a fabric, execute necessary operations,
135 : * save it if there are any changes and load another fabric.
136 : *
137 : * Issue to refactor the class to use one entry for the entire table
138 : * https://github.com/project-chip/connectedhomeip/issues/24288
139 : */
140 :
141 : struct ICDMonitoringTable
142 : {
143 : ICDMonitoringTable(PersistentStorageDelegate & storage, FabricIndex fabric, uint16_t limit,
144 : Crypto::SymmetricKeystore * symmetricKeystore) :
145 : mStorage(&storage),
146 : mFabric(fabric), mLimit(limit), mSymmetricKeystore(symmetricKeystore)
147 : {}
148 :
149 : /**
150 : * @brief Returns the MonitoringRegistrationStruct entry at the given position.
151 : * @param index Zero-based position within the RegisteredClients table.
152 : * @param entry On success, contains the MonitoringRegistrationStruct matching the given index.
153 : * @return CHIP_NO_ERROR on success,
154 : * CHIP_ERROR_NOT_FOUND if index is greater than the index of the last entry on the table.
155 : */
156 : CHIP_ERROR Get(uint16_t index, ICDMonitoringEntry & entry) const;
157 :
158 : /**
159 : * @brief Stores the MonitoringRegistrationStruct entry at the given position,
160 : * overwriting any existing entry.
161 : * @param index Zero-based position within the RegisteredClients table.
162 : * @param entry On success, contains the MonitoringRegistrationStruct matching the given index.
163 : * @return CHIP_NO_ERROR on success
164 : */
165 : CHIP_ERROR Set(uint16_t index, const ICDMonitoringEntry & entry);
166 :
167 : /**
168 : * @brief Search the registered clients for an entry on the fabric whose checkInNodeID matches the given id.
169 : * @param id NodeId to match.
170 : * @param entry On success, contains the MonitoringRegistrationStruct matching the given node ID.
171 : * If found, entry.index contains the position of the entry in the table.
172 : * If CHIP_ERROR_NOT_FOUND is returned, entry.index contains the total number of entries in the table.
173 : * @return CHIP_NO_ERROR if found, CHIP_ERROR_NOT_FOUND if no checkInNodeID matches the provided id.
174 : */
175 : CHIP_ERROR Find(NodeId id, ICDMonitoringEntry & entry);
176 :
177 : /**
178 : * @brief Removes the MonitoringRegistrationStruct entry at the given position,
179 : * shifting down the upper entries.
180 : * @param index Zero-based position within the RegisteredClients table.
181 : * @return CHIP_NO_ERROR on success
182 : */
183 : CHIP_ERROR Remove(uint16_t index);
184 :
185 : /**
186 : * @brief Removes all the entries for the current fabricIndex.
187 : * @return CHIP_NO_ERROR on success
188 : */
189 : CHIP_ERROR RemoveAll();
190 :
191 : /**
192 : * @brief Check if the table is empty
193 : * @return True when there is no entry in the table. False if there is at least one
194 : */
195 : bool IsEmpty();
196 :
197 : /**
198 : * @return Maximum number of entries allowed in the RegisteredClients table.
199 : */
200 : uint16_t Limit() const;
201 :
202 : private:
203 : PersistentStorageDelegate * mStorage;
204 : FabricIndex mFabric;
205 : uint16_t mLimit = 0;
206 : Crypto::SymmetricKeystore * mSymmetricKeystore = nullptr;
207 : };
208 :
209 : } // namespace chip
|