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 :
18 : #include "ICDMonitoringTable.h"
19 :
20 : #include <crypto/RandUtils.h>
21 :
22 : namespace chip {
23 :
24 : enum class Fields : uint8_t
25 : {
26 : kCheckInNodeID = 1,
27 : kMonitoredSubject = 2,
28 : kAesKeyHandle = 3,
29 : kHmacKeyHandle = 4,
30 : kClientType = 5,
31 : };
32 :
33 44 : CHIP_ERROR ICDMonitoringEntry::UpdateKey(StorageKeyName & skey)
34 : {
35 44 : VerifyOrReturnError(kUndefinedFabricIndex != this->fabricIndex, CHIP_ERROR_INVALID_FABRIC_INDEX);
36 44 : skey = DefaultStorageKeyAllocator::ICDManagementTableEntry(this->fabricIndex, index);
37 44 : return CHIP_NO_ERROR;
38 : }
39 :
40 12 : CHIP_ERROR ICDMonitoringEntry::Serialize(TLV::TLVWriter & writer) const
41 : {
42 : TLV::TLVType outer;
43 12 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outer));
44 12 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Fields::kCheckInNodeID), checkInNodeID));
45 12 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Fields::kMonitoredSubject), monitoredSubject));
46 :
47 12 : ByteSpan aesKeybuf(aesKeyHandle.As<Crypto::Symmetric128BitsKeyByteArray>());
48 12 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Fields::kAesKeyHandle), aesKeybuf));
49 :
50 12 : ByteSpan hmacKeybuf(hmacKeyHandle.As<Crypto::Symmetric128BitsKeyByteArray>());
51 12 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Fields::kHmacKeyHandle), hmacKeybuf));
52 :
53 12 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Fields::kClientType), clientType));
54 :
55 12 : ReturnErrorOnFailure(writer.EndContainer(outer));
56 12 : ReturnErrorOnFailure(writer.Finalize());
57 12 : return CHIP_NO_ERROR;
58 : }
59 :
60 18 : CHIP_ERROR ICDMonitoringEntry::Deserialize(TLV::TLVReader & reader)
61 : {
62 :
63 18 : CHIP_ERROR err = CHIP_NO_ERROR;
64 : TLV::TLVType outer;
65 :
66 18 : ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag()));
67 18 : VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_WRONG_TLV_TYPE);
68 18 : ReturnErrorOnFailure(reader.EnterContainer(outer));
69 108 : while ((err = reader.Next()) == CHIP_NO_ERROR)
70 : {
71 90 : if (TLV::IsContextTag(reader.GetTag()))
72 : {
73 90 : switch (TLV::TagNumFromTag(reader.GetTag()))
74 : {
75 18 : case to_underlying(Fields::kCheckInNodeID):
76 18 : ReturnErrorOnFailure(reader.Get(checkInNodeID));
77 18 : break;
78 18 : case to_underlying(Fields::kMonitoredSubject):
79 18 : ReturnErrorOnFailure(reader.Get(monitoredSubject));
80 18 : break;
81 18 : case to_underlying(Fields::kAesKeyHandle): {
82 18 : ByteSpan buf;
83 18 : ReturnErrorOnFailure(reader.Get(buf));
84 : // Since we are storing either the raw key or a key ID, we must
85 : // simply copy the data as is in the keyHandle.
86 : // Calling SetKey here would create another keyHandle in storage and will cause
87 : // key leaks in some implementations.
88 18 : memcpy(aesKeyHandle.AsMutable<Crypto::Symmetric128BitsKeyByteArray>(), buf.data(),
89 : sizeof(Crypto::Symmetric128BitsKeyByteArray));
90 18 : keyHandleValid = true;
91 : }
92 18 : break;
93 18 : case to_underlying(Fields::kHmacKeyHandle): {
94 18 : ByteSpan buf;
95 18 : CHIP_ERROR error = reader.Get(buf);
96 :
97 18 : if (error != CHIP_NO_ERROR)
98 : {
99 : // If retreiving the hmac key handle failed, we need to set an invalid key handle
100 : // even if the AesKeyHandle is valid.
101 0 : keyHandleValid = false;
102 0 : return error;
103 : }
104 :
105 : // Since we are storing either the raw key or a key ID, we must
106 : // simply copy the data as is in the keyHandle.
107 : // Calling SetKey here would create another keyHandle in storage and will cause
108 : // key leaks in some implementations.
109 18 : memcpy(hmacKeyHandle.AsMutable<Crypto::Symmetric128BitsKeyByteArray>(), buf.data(),
110 : sizeof(Crypto::Symmetric128BitsKeyByteArray));
111 : }
112 18 : break;
113 18 : case to_underlying(Fields::kClientType):
114 18 : ReturnErrorOnFailure(reader.Get(clientType));
115 18 : break;
116 0 : default:
117 0 : break;
118 : }
119 : }
120 : }
121 :
122 18 : VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
123 18 : ReturnErrorOnFailure(reader.ExitContainer(outer));
124 18 : return CHIP_NO_ERROR;
125 : }
126 :
127 27 : void ICDMonitoringEntry::Clear()
128 : {
129 27 : this->checkInNodeID = kUndefinedNodeId;
130 27 : this->monitoredSubject = kUndefinedNodeId;
131 27 : this->keyHandleValid = false;
132 27 : this->clientType = app::Clusters::IcdManagement::ClientTypeEnum::kPermanent;
133 27 : }
134 :
135 36 : CHIP_ERROR ICDMonitoringEntry::SetKey(ByteSpan keyData)
136 : {
137 36 : VerifyOrReturnError(keyData.size() == sizeof(Crypto::Symmetric128BitsKeyByteArray), CHIP_ERROR_INVALID_ARGUMENT);
138 33 : VerifyOrReturnError(symmetricKeystore != nullptr, CHIP_ERROR_INTERNAL);
139 33 : VerifyOrReturnError(!keyHandleValid, CHIP_ERROR_INTERNAL);
140 :
141 : Crypto::Symmetric128BitsKeyByteArray keyMaterial;
142 32 : memcpy(keyMaterial, keyData.data(), sizeof(Crypto::Symmetric128BitsKeyByteArray));
143 :
144 32 : ReturnErrorOnFailure(symmetricKeystore->CreateKey(keyMaterial, aesKeyHandle));
145 32 : CHIP_ERROR error = symmetricKeystore->CreateKey(keyMaterial, hmacKeyHandle);
146 :
147 32 : if (error == CHIP_NO_ERROR)
148 : {
149 : // If the creation of the HmacKeyHandle succeeds, both key handles are valid
150 32 : keyHandleValid = true;
151 : }
152 : else
153 : {
154 : // Creation of the HmacKeyHandle failed, we need to delete the AesKeyHandle to avoid a key leak
155 0 : symmetricKeystore->DestroyKey(this->aesKeyHandle);
156 : }
157 :
158 32 : return error;
159 : }
160 :
161 22 : CHIP_ERROR ICDMonitoringEntry::DeleteKey()
162 : {
163 22 : VerifyOrReturnError(symmetricKeystore != nullptr, CHIP_ERROR_INTERNAL);
164 22 : symmetricKeystore->DestroyKey(this->aesKeyHandle);
165 22 : symmetricKeystore->DestroyKey(this->hmacKeyHandle);
166 22 : keyHandleValid = false;
167 22 : return CHIP_NO_ERROR;
168 : }
169 :
170 15 : bool ICDMonitoringEntry::IsKeyEquivalent(ByteSpan keyData)
171 : {
172 15 : VerifyOrReturnValue(keyData.size() == Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES, false);
173 15 : VerifyOrReturnValue(symmetricKeystore != nullptr, false);
174 15 : VerifyOrReturnValue(keyHandleValid, false);
175 :
176 15 : ICDMonitoringEntry tempEntry(symmetricKeystore);
177 :
178 15 : VerifyOrReturnValue(tempEntry.SetKey(keyData) == CHIP_NO_ERROR, false);
179 :
180 : // Challenge
181 15 : uint8_t mic[Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES] = { 0 };
182 15 : uint8_t aead[Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES] = { 0 };
183 :
184 : CHIP_ERROR err;
185 :
186 15 : uint64_t data = Crypto::GetRandU64(), validation, encrypted;
187 15 : validation = data;
188 :
189 15 : err = Crypto::AES_CCM_encrypt(reinterpret_cast<uint8_t *>(&data), sizeof(data), nullptr, 0, tempEntry.aesKeyHandle, aead,
190 : Crypto::CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES, reinterpret_cast<uint8_t *>(&encrypted), mic,
191 : Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES);
192 :
193 15 : data = 0;
194 15 : if (err == CHIP_NO_ERROR)
195 : {
196 15 : err = Crypto::AES_CCM_decrypt(reinterpret_cast<uint8_t *>(&encrypted), sizeof(encrypted), nullptr, 0, mic,
197 15 : Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, aesKeyHandle, aead,
198 : Crypto::CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES, reinterpret_cast<uint8_t *>(&data));
199 : }
200 15 : tempEntry.DeleteKey();
201 :
202 15 : if (err != CHIP_NO_ERROR)
203 : {
204 1 : return false;
205 : }
206 :
207 14 : return (data == validation) ? true : false;
208 15 : }
209 :
210 1 : ICDMonitoringEntry & ICDMonitoringEntry::operator=(const ICDMonitoringEntry & icdMonitoringEntry)
211 : {
212 1 : if (this == &icdMonitoringEntry)
213 : {
214 0 : return *this;
215 : }
216 :
217 1 : fabricIndex = icdMonitoringEntry.fabricIndex;
218 1 : checkInNodeID = icdMonitoringEntry.checkInNodeID;
219 1 : monitoredSubject = icdMonitoringEntry.monitoredSubject;
220 1 : clientType = icdMonitoringEntry.clientType;
221 1 : index = icdMonitoringEntry.index;
222 1 : keyHandleValid = icdMonitoringEntry.keyHandleValid;
223 1 : symmetricKeystore = icdMonitoringEntry.symmetricKeystore;
224 1 : memcpy(aesKeyHandle.AsMutable<Crypto::Symmetric128BitsKeyByteArray>(),
225 1 : icdMonitoringEntry.aesKeyHandle.As<Crypto::Symmetric128BitsKeyByteArray>(),
226 : sizeof(Crypto::Symmetric128BitsKeyByteArray));
227 1 : memcpy(hmacKeyHandle.AsMutable<Crypto::Symmetric128BitsKeyByteArray>(),
228 1 : icdMonitoringEntry.hmacKeyHandle.As<Crypto::Symmetric128BitsKeyByteArray>(),
229 : sizeof(Crypto::Symmetric128BitsKeyByteArray));
230 :
231 1 : return *this;
232 : }
233 :
234 27 : CHIP_ERROR ICDMonitoringTable::Get(uint16_t index, ICDMonitoringEntry & entry) const
235 : {
236 27 : entry.fabricIndex = this->mFabric;
237 27 : entry.index = index;
238 27 : ReturnErrorOnFailure(entry.Load(this->mStorage));
239 18 : entry.fabricIndex = this->mFabric;
240 18 : return CHIP_NO_ERROR;
241 : }
242 :
243 0 : CHIP_ERROR ICDMonitoringTable::Find(NodeId id, ICDMonitoringEntry & entry)
244 : {
245 : uint16_t index;
246 0 : ICDMonitoringEntry tempEntry(mSymmetricKeystore);
247 :
248 0 : for (index = 0; index < this->Limit(); index++)
249 : {
250 0 : if (this->Get(index, tempEntry) != CHIP_NO_ERROR)
251 : {
252 0 : break;
253 : }
254 0 : if (id == tempEntry.checkInNodeID)
255 : {
256 0 : entry = tempEntry;
257 0 : return CHIP_NO_ERROR;
258 : }
259 : }
260 :
261 0 : entry.index = index;
262 0 : return CHIP_ERROR_NOT_FOUND;
263 0 : }
264 :
265 19 : CHIP_ERROR ICDMonitoringTable::Set(uint16_t index, const ICDMonitoringEntry & entry)
266 : {
267 19 : VerifyOrReturnError(index < this->Limit(), CHIP_ERROR_INVALID_ARGUMENT);
268 17 : VerifyOrReturnError(kUndefinedNodeId != entry.checkInNodeID, CHIP_ERROR_INVALID_ARGUMENT);
269 16 : VerifyOrReturnError(kUndefinedNodeId != entry.monitoredSubject, CHIP_ERROR_INVALID_ARGUMENT);
270 15 : VerifyOrReturnError(entry.keyHandleValid, CHIP_ERROR_INVALID_ARGUMENT);
271 :
272 12 : ICDMonitoringEntry e(this->mFabric, index);
273 12 : e.checkInNodeID = entry.checkInNodeID;
274 12 : e.monitoredSubject = entry.monitoredSubject;
275 12 : e.clientType = entry.clientType;
276 12 : e.index = index;
277 12 : e.symmetricKeystore = entry.symmetricKeystore;
278 :
279 12 : memcpy(e.aesKeyHandle.AsMutable<Crypto::Symmetric128BitsKeyByteArray>(),
280 12 : entry.aesKeyHandle.As<Crypto::Symmetric128BitsKeyByteArray>(), sizeof(Crypto::Symmetric128BitsKeyByteArray));
281 12 : memcpy(e.hmacKeyHandle.AsMutable<Crypto::Symmetric128BitsKeyByteArray>(),
282 12 : entry.hmacKeyHandle.As<Crypto::Symmetric128BitsKeyByteArray>(), sizeof(Crypto::Symmetric128BitsKeyByteArray));
283 :
284 12 : ReturnErrorOnFailure(e.symmetricKeystore->PersistICDKey(e.aesKeyHandle));
285 12 : CHIP_ERROR error = e.symmetricKeystore->PersistICDKey(e.hmacKeyHandle);
286 12 : if (error != CHIP_NO_ERROR)
287 : {
288 : // If setting the persistence of the HmacKeyHandle failed, we need to delete the AesKeyHandle to avoid a key leak
289 0 : e.symmetricKeystore->DestroyKey(e.aesKeyHandle);
290 0 : return error;
291 : }
292 :
293 12 : return e.Save(this->mStorage);
294 12 : }
295 :
296 3 : CHIP_ERROR ICDMonitoringTable::Remove(uint16_t index)
297 : {
298 3 : ICDMonitoringEntry entry(mSymmetricKeystore, this->mFabric);
299 :
300 : // Retrieve entry and delete the keyHandle first as to not
301 : // cause any key leaks.
302 3 : this->Get(index, entry);
303 3 : ReturnErrorOnFailure(entry.DeleteKey());
304 :
305 : // Shift remaining entries down one position
306 5 : while (CHIP_NO_ERROR == this->Get(static_cast<uint16_t>(index + 1), entry))
307 : {
308 2 : ReturnErrorOnFailure(this->Set(index++, entry));
309 : }
310 :
311 : // Remove last entry
312 3 : entry.fabricIndex = this->mFabric;
313 3 : entry.index = index;
314 :
315 : // entry.Delete() doesn't delete the key from the AES128KeyHandle
316 3 : return entry.Delete(this->mStorage);
317 3 : }
318 :
319 2 : CHIP_ERROR ICDMonitoringTable::RemoveAll()
320 : {
321 2 : ICDMonitoringEntry entry(mSymmetricKeystore, this->mFabric);
322 2 : uint16_t index = 0;
323 4 : while (index < this->Limit())
324 : {
325 3 : CHIP_ERROR err = this->Get(index++, entry);
326 3 : if (CHIP_ERROR_NOT_FOUND == err)
327 : {
328 1 : break;
329 : }
330 2 : ReturnErrorOnFailure(err);
331 2 : entry.fabricIndex = this->mFabric;
332 2 : ReturnErrorOnFailure(entry.DeleteKey());
333 2 : ReturnErrorOnFailure(entry.Delete(this->mStorage));
334 : }
335 2 : return CHIP_NO_ERROR;
336 2 : }
337 :
338 0 : bool ICDMonitoringTable::IsEmpty()
339 : {
340 0 : ICDMonitoringEntry entry(mSymmetricKeystore, this->mFabric);
341 0 : return (this->Get(0, entry) == CHIP_ERROR_NOT_FOUND);
342 0 : }
343 :
344 24 : uint16_t ICDMonitoringTable::Limit() const
345 : {
346 24 : return mLimit;
347 : }
348 :
349 : } // namespace chip
|