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 42 : CHIP_ERROR ICDMonitoringEntry::UpdateKey(StorageKeyName & skey) const
34 : {
35 42 : VerifyOrReturnError(kUndefinedFabricIndex != this->fabricIndex, CHIP_ERROR_INVALID_FABRIC_INDEX);
36 42 : skey = DefaultStorageKeyAllocator::ICDManagementTableEntry(this->fabricIndex, index);
37 42 : 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 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Fields::kAesKeyHandle), ByteSpan(aesKeyHandle.OpaqueBytes())));
48 12 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Fields::kHmacKeyHandle), ByteSpan(hmacKeyHandle.OpaqueBytes())));
49 :
50 12 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Fields::kClientType), clientType));
51 :
52 12 : ReturnErrorOnFailure(writer.EndContainer(outer));
53 12 : ReturnErrorOnFailure(writer.Finalize());
54 12 : return CHIP_NO_ERROR;
55 : }
56 :
57 18 : CHIP_ERROR ICDMonitoringEntry::Deserialize(TLV::TLVReader & reader)
58 : {
59 :
60 18 : CHIP_ERROR err = CHIP_NO_ERROR;
61 : TLV::TLVType outer;
62 :
63 18 : ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag()));
64 18 : VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_WRONG_TLV_TYPE);
65 18 : ReturnErrorOnFailure(reader.EnterContainer(outer));
66 216 : while ((err = reader.Next()) == CHIP_NO_ERROR)
67 : {
68 90 : if (TLV::IsContextTag(reader.GetTag()))
69 : {
70 90 : switch (TLV::TagNumFromTag(reader.GetTag()))
71 : {
72 18 : case to_underlying(Fields::kCheckInNodeID):
73 18 : ReturnErrorOnFailure(reader.Get(checkInNodeID));
74 18 : break;
75 18 : case to_underlying(Fields::kMonitoredSubject):
76 18 : ReturnErrorOnFailure(reader.Get(monitoredSubject));
77 18 : break;
78 18 : case to_underlying(Fields::kAesKeyHandle): {
79 18 : ByteSpan buf;
80 18 : ReturnErrorOnFailure(reader.Get(buf));
81 18 : VerifyOrReturnError(buf.size() == Crypto::Aes128KeyHandle::Size(), CHIP_ERROR_UNEXPECTED_TLV_ELEMENT);
82 : // Since we are storing either the raw key or a key ID, we must
83 : // simply copy the data as is in the keyHandle.
84 : // Calling SetKey here would create another keyHandle in storage and will cause
85 : // key leaks in some implementations.
86 18 : memcpy(aesKeyHandle.OpaqueBytes().data(), buf.data(), Crypto::Aes128KeyHandle::Size());
87 18 : keyHandleValid = true;
88 : }
89 18 : break;
90 18 : case to_underlying(Fields::kHmacKeyHandle): {
91 18 : ByteSpan buf;
92 18 : CHIP_ERROR error = reader.Get(buf);
93 36 : if (error == CHIP_NO_ERROR && buf.size() != Crypto::Hmac128KeyHandle::Size())
94 : {
95 0 : error = CHIP_ERROR_UNEXPECTED_TLV_ELEMENT;
96 : }
97 :
98 36 : if (error != CHIP_NO_ERROR)
99 : {
100 : // If retreiving the hmac key handle failed, we need to set an invalid key handle
101 : // even if the AesKeyHandle is valid.
102 0 : keyHandleValid = false;
103 0 : return error;
104 : }
105 :
106 : // Since we are storing either the raw key or a key ID, we must
107 : // simply copy the data as is in the keyHandle.
108 : // Calling SetKey here would create another keyHandle in storage and will cause
109 : // key leaks in some implementations.
110 18 : memcpy(hmacKeyHandle.OpaqueBytes().data(), buf.data(), Crypto::Hmac128KeyHandle::Size());
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 36 : VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
123 18 : ReturnErrorOnFailure(reader.ExitContainer(outer));
124 18 : return CHIP_NO_ERROR;
125 : }
126 :
127 26 : void ICDMonitoringEntry::Clear()
128 : {
129 26 : this->checkInNodeID = kUndefinedNodeId;
130 26 : this->monitoredSubject = kUndefinedNodeId;
131 26 : this->keyHandleValid = false;
132 26 : this->clientType = app::Clusters::IcdManagement::ClientTypeEnum::kPermanent;
133 26 : }
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 64 : 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 21 : CHIP_ERROR ICDMonitoringEntry::DeleteKey()
162 : {
163 21 : VerifyOrReturnError(symmetricKeystore != nullptr, CHIP_ERROR_INTERNAL);
164 21 : symmetricKeystore->DestroyKey(this->aesKeyHandle);
165 21 : symmetricKeystore->DestroyKey(this->hmacKeyHandle);
166 21 : keyHandleValid = false;
167 21 : 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 30 : 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 nonce[Crypto::CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES] = { 0 };
183 30 : VerifyOrReturnValue(Crypto::DRBG_get_bytes(nonce, sizeof(nonce)) == CHIP_NO_ERROR, false);
184 :
185 : CHIP_ERROR err;
186 :
187 15 : uint64_t data = Crypto::GetRandU64(), validation, encrypted;
188 15 : validation = data;
189 :
190 15 : err = Crypto::AES_CCM_encrypt(reinterpret_cast<uint8_t *>(&data), sizeof(data), nullptr, 0, tempEntry.aesKeyHandle, nonce,
191 : Crypto::CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES, reinterpret_cast<uint8_t *>(&encrypted), mic,
192 : Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES);
193 :
194 15 : data = 0;
195 30 : if (err == CHIP_NO_ERROR)
196 : {
197 15 : err = Crypto::AES_CCM_decrypt(reinterpret_cast<uint8_t *>(&encrypted), sizeof(encrypted), nullptr, 0, mic,
198 15 : Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, aesKeyHandle, nonce,
199 : Crypto::CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES, reinterpret_cast<uint8_t *>(&data));
200 : }
201 15 : TEMPORARY_RETURN_IGNORED tempEntry.DeleteKey();
202 :
203 30 : if (err != CHIP_NO_ERROR)
204 : {
205 1 : return false;
206 : }
207 :
208 14 : return (data == validation) ? true : false;
209 15 : }
210 :
211 1 : ICDMonitoringEntry & ICDMonitoringEntry::operator=(const ICDMonitoringEntry & icdMonitoringEntry)
212 : {
213 1 : if (this == &icdMonitoringEntry)
214 : {
215 0 : return *this;
216 : }
217 :
218 1 : fabricIndex = icdMonitoringEntry.fabricIndex;
219 1 : checkInNodeID = icdMonitoringEntry.checkInNodeID;
220 1 : monitoredSubject = icdMonitoringEntry.monitoredSubject;
221 1 : clientType = icdMonitoringEntry.clientType;
222 1 : index = icdMonitoringEntry.index;
223 1 : keyHandleValid = icdMonitoringEntry.keyHandleValid;
224 1 : symmetricKeystore = icdMonitoringEntry.symmetricKeystore;
225 1 : memcpy(aesKeyHandle.OpaqueBytes().data(), icdMonitoringEntry.aesKeyHandle.OpaqueBytes().data(),
226 : Crypto::Aes128KeyHandle::Size());
227 1 : memcpy(hmacKeyHandle.OpaqueBytes().data(), icdMonitoringEntry.hmacKeyHandle.OpaqueBytes().data(),
228 : Crypto::Hmac128KeyHandle::Size());
229 :
230 1 : return *this;
231 : }
232 :
233 26 : CHIP_ERROR ICDMonitoringTable::Get(uint16_t index, ICDMonitoringEntry & entry) const
234 : {
235 26 : entry.fabricIndex = this->mFabric;
236 26 : entry.index = index;
237 26 : return entry.Load(this->mStorage);
238 : }
239 :
240 0 : CHIP_ERROR ICDMonitoringTable::Find(NodeId id, ICDMonitoringEntry & entry)
241 : {
242 : uint16_t index;
243 0 : ICDMonitoringEntry tempEntry(mSymmetricKeystore);
244 :
245 0 : for (index = 0; index < this->Limit(); index++)
246 : {
247 0 : if (this->Get(index, tempEntry) != CHIP_NO_ERROR)
248 : {
249 0 : break;
250 : }
251 0 : if (id == tempEntry.checkInNodeID)
252 : {
253 0 : entry = tempEntry;
254 0 : return CHIP_NO_ERROR;
255 : }
256 : }
257 :
258 0 : entry.index = index;
259 0 : return CHIP_ERROR_NOT_FOUND;
260 0 : }
261 :
262 19 : CHIP_ERROR ICDMonitoringTable::Set(uint16_t index, const ICDMonitoringEntry & entry)
263 : {
264 19 : VerifyOrReturnError(index < this->Limit(), CHIP_ERROR_INVALID_ARGUMENT);
265 17 : VerifyOrReturnError(kUndefinedNodeId != entry.checkInNodeID, CHIP_ERROR_INVALID_ARGUMENT);
266 16 : VerifyOrReturnError(kUndefinedNodeId != entry.monitoredSubject, CHIP_ERROR_INVALID_ARGUMENT);
267 15 : VerifyOrReturnError(entry.keyHandleValid, CHIP_ERROR_INVALID_ARGUMENT);
268 :
269 12 : ICDMonitoringEntry e(this->mFabric, index);
270 12 : e.checkInNodeID = entry.checkInNodeID;
271 12 : e.monitoredSubject = entry.monitoredSubject;
272 12 : e.clientType = entry.clientType;
273 12 : e.index = index;
274 12 : e.symmetricKeystore = entry.symmetricKeystore;
275 :
276 12 : memcpy(e.aesKeyHandle.OpaqueBytes().data(), entry.aesKeyHandle.OpaqueBytes().data(), Crypto::Aes128KeyHandle::Size());
277 12 : memcpy(e.hmacKeyHandle.OpaqueBytes().data(), entry.hmacKeyHandle.OpaqueBytes().data(), Crypto::Hmac128KeyHandle::Size());
278 :
279 12 : ReturnErrorOnFailure(e.symmetricKeystore->PersistICDKey(e.aesKeyHandle));
280 12 : CHIP_ERROR error = e.symmetricKeystore->PersistICDKey(e.hmacKeyHandle);
281 24 : if (error != CHIP_NO_ERROR)
282 : {
283 : // If setting the persistence of the HmacKeyHandle failed, we need to delete the AesKeyHandle to avoid a key leak
284 0 : e.symmetricKeystore->DestroyKey(e.aesKeyHandle);
285 0 : return error;
286 : }
287 :
288 12 : return e.Save(this->mStorage);
289 12 : }
290 :
291 3 : CHIP_ERROR ICDMonitoringTable::Remove(uint16_t index)
292 : {
293 3 : ICDMonitoringEntry entry(mSymmetricKeystore, this->mFabric);
294 :
295 : // Retrieve entry and delete the keyHandle first as to not
296 : // cause any key leaks.
297 3 : ReturnErrorOnFailure(this->Get(index, entry));
298 2 : ReturnErrorOnFailure(entry.DeleteKey());
299 :
300 : // Shift remaining entries down one position
301 8 : while (CHIP_NO_ERROR == this->Get(static_cast<uint16_t>(index + 1), entry))
302 : {
303 2 : ReturnErrorOnFailure(this->Set(index++, entry));
304 : }
305 :
306 : // Remove last entry
307 2 : entry.fabricIndex = this->mFabric;
308 2 : entry.index = index;
309 :
310 : // entry.Delete() doesn't delete the key from the AES128KeyHandle
311 2 : return entry.Delete(this->mStorage);
312 3 : }
313 :
314 2 : CHIP_ERROR ICDMonitoringTable::RemoveAll()
315 : {
316 2 : ICDMonitoringEntry entry(mSymmetricKeystore, this->mFabric);
317 2 : uint16_t index = 0;
318 4 : while (index < this->Limit())
319 : {
320 3 : CHIP_ERROR err = this->Get(index++, entry);
321 6 : if (CHIP_ERROR_NOT_FOUND == err)
322 : {
323 1 : break;
324 : }
325 2 : ReturnErrorOnFailure(err);
326 2 : entry.fabricIndex = this->mFabric;
327 2 : ReturnErrorOnFailure(entry.DeleteKey());
328 2 : ReturnErrorOnFailure(entry.Delete(this->mStorage));
329 : }
330 2 : return CHIP_NO_ERROR;
331 2 : }
332 :
333 0 : bool ICDMonitoringTable::IsEmpty()
334 : {
335 0 : ICDMonitoringEntry entry(mSymmetricKeystore, this->mFabric);
336 0 : return (this->Get(0, entry) == CHIP_ERROR_NOT_FOUND);
337 0 : }
338 :
339 24 : uint16_t ICDMonitoringTable::Limit() const
340 : {
341 24 : return mLimit;
342 : }
343 :
344 : } // namespace chip
|