Matter SDK Coverage Report
Current view: top level - app/icd/server - ICDMonitoringTable.cpp (source / functions) Coverage Total Hit
Test: SHA:3f9cd168e84cd831b7699126f5296f5c5498690f Lines: 86.2 % 174 150
Test Date: 2026-04-27 19:52:19 Functions: 86.7 % 15 13

            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
        

Generated by: LCOV version 2.0-1