Matter SDK Coverage Report
Current view: top level - crypto - PersistentStorageOperationalKeystore.cpp (source / functions) Coverage Total Hit
Test: SHA:2a48c1efeab1c0f76f3adb3a0940b0f7de706453 Lines: 97.2 % 141 137
Test Date: 2026-01-31 08:14:20 Functions: 100.0 % 15 15

            Line data    Source code
       1              : /*
       2              :  *    Copyright (c) 2022 Project CHIP Authors
       3              :  *    All rights reserved.
       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 <crypto/OperationalKeystore.h>
      19              : #include <lib/core/CHIPError.h>
      20              : #include <lib/core/CHIPPersistentStorageDelegate.h>
      21              : #include <lib/core/DataModelTypes.h>
      22              : #include <lib/core/TLV.h>
      23              : #include <lib/support/CHIPMem.h>
      24              : #include <lib/support/CodeUtils.h>
      25              : #include <lib/support/DefaultStorageKeyAllocator.h>
      26              : #include <lib/support/SafeInt.h>
      27              : 
      28              : #include "PersistentStorageOperationalKeystore.h"
      29              : 
      30              : namespace chip {
      31              : 
      32              : using namespace chip::Crypto;
      33              : 
      34              : namespace {
      35              : 
      36              : // Tags for our operational keypair storage.
      37              : constexpr TLV::Tag kOpKeyVersionTag = TLV::ContextTag(0);
      38              : constexpr TLV::Tag kOpKeyDataTag    = TLV::ContextTag(1);
      39              : 
      40              : // If this version grows beyond UINT16_MAX, adjust OpKeypairTLVMaxSize
      41              : // accordingly.
      42              : constexpr uint16_t kOpKeyVersion = 1;
      43              : 
      44              : constexpr size_t OpKeyTLVMaxSize()
      45              : {
      46              :     // Version and serialized key
      47              :     return TLV::EstimateStructOverhead(sizeof(uint16_t), Crypto::P256SerializedKeypair::Capacity());
      48              : }
      49              : 
      50              : /** WARNING: This can leave the operational key on the stack somewhere, since many of the platform
      51              :  *           APIs use stack buffers and do not sanitize! This implementation is for example purposes
      52              :  *           only of the API and it is recommended to avoid directly accessing raw private key bits
      53              :  *           in storage.
      54              :  */
      55           26 : CHIP_ERROR StoreOperationalKey(FabricIndex fabricIndex, PersistentStorageDelegate * storage, P256Keypair * keypair)
      56              : {
      57           26 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (storage != nullptr) && (keypair != nullptr),
      58              :                         CHIP_ERROR_INVALID_ARGUMENT);
      59              : 
      60              :     // Use a SensitiveDataBuffer to get RAII secret data clearing on scope exit.
      61           26 :     Crypto::SensitiveDataBuffer<OpKeyTLVMaxSize()> buf;
      62           26 :     TLV::TLVWriter writer;
      63              : 
      64           26 :     writer.Init(buf.Bytes(), buf.Capacity());
      65              : 
      66              :     TLV::TLVType outerType;
      67           26 :     ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerType));
      68              : 
      69           26 :     ReturnErrorOnFailure(writer.Put(kOpKeyVersionTag, kOpKeyVersion));
      70              : 
      71              :     {
      72              :         // P256SerializedKeypair has RAII secret clearing
      73           26 :         Crypto::P256SerializedKeypair serializedOpKey;
      74           26 :         ReturnErrorOnFailure(keypair->Serialize(serializedOpKey));
      75              : 
      76           26 :         ReturnErrorOnFailure(writer.Put(kOpKeyDataTag, ByteSpan(serializedOpKey.Bytes(), serializedOpKey.Length())));
      77           26 :     }
      78              : 
      79           26 :     ReturnErrorOnFailure(writer.EndContainer(outerType));
      80              : 
      81           26 :     const auto opKeyLength = writer.GetLengthWritten();
      82           26 :     VerifyOrReturnError(CanCastTo<uint16_t>(opKeyLength), CHIP_ERROR_BUFFER_TOO_SMALL);
      83           26 :     ReturnErrorOnFailure(storage->SyncSetKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName(), buf.ConstBytes(),
      84              :                                                   static_cast<uint16_t>(opKeyLength)));
      85              : 
      86           26 :     return CHIP_NO_ERROR;
      87           26 : }
      88              : 
      89           21 : CHIP_ERROR ExportStoredOpKey(FabricIndex fabricIndex, PersistentStorageDelegate * storage,
      90              :                              Crypto::P256SerializedKeypair & serializedOpKey)
      91              : {
      92           21 :     VerifyOrReturnError(storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      93           21 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
      94              : 
      95              :     // Use a SensitiveDataBuffer to get RAII secret data clearing on scope exit.
      96           21 :     Crypto::SensitiveDataBuffer<OpKeyTLVMaxSize()> buf;
      97              : 
      98              :     // Load up the operational key structure from storage
      99           21 :     uint16_t size = static_cast<uint16_t>(buf.Capacity());
     100           21 :     ReturnErrorOnFailure(
     101              :         storage->SyncGetKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName(), buf.Bytes(), size));
     102              : 
     103           18 :     TEMPORARY_RETURN_IGNORED buf.SetLength(static_cast<size_t>(size));
     104              : 
     105              :     // Read-out the operational key TLV entry.
     106           18 :     TLV::ContiguousBufferTLVReader reader;
     107           18 :     reader.Init(buf.Bytes(), buf.Length());
     108              : 
     109           18 :     ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
     110              :     TLV::TLVType containerType;
     111           18 :     ReturnErrorOnFailure(reader.EnterContainer(containerType));
     112              : 
     113           18 :     ReturnErrorOnFailure(reader.Next(kOpKeyVersionTag));
     114              :     uint16_t opKeyVersion;
     115           18 :     ReturnErrorOnFailure(reader.Get(opKeyVersion));
     116           18 :     VerifyOrReturnError(opKeyVersion == kOpKeyVersion, CHIP_ERROR_VERSION_MISMATCH);
     117              : 
     118           18 :     ReturnErrorOnFailure(reader.Next(kOpKeyDataTag));
     119              :     {
     120           18 :         ByteSpan keyData;
     121           18 :         ReturnErrorOnFailure(reader.GetByteView(keyData));
     122              : 
     123              :         // Unfortunately, we have to copy the data into a P256SerializedKeypair.
     124           18 :         VerifyOrReturnError(keyData.size() <= serializedOpKey.Capacity(), CHIP_ERROR_BUFFER_TOO_SMALL);
     125              : 
     126           18 :         ReturnErrorOnFailure(reader.ExitContainer(containerType));
     127              : 
     128           18 :         memcpy(serializedOpKey.Bytes(), keyData.data(), keyData.size());
     129           18 :         return serializedOpKey.SetLength(keyData.size());
     130              :     }
     131           21 : }
     132              : 
     133              : /** WARNING: This can leave the operational key on the stack somewhere, since many of the platform
     134              :  *           APIs use stack buffers and do not sanitize! This implementation is for example purposes
     135              :  *           only of the API and it is recommended to avoid directly accessing raw private key bits
     136              :  *           in storage.
     137              :  */
     138           18 : CHIP_ERROR SignWithStoredOpKey(FabricIndex fabricIndex, PersistentStorageDelegate * storage, const ByteSpan & message,
     139              :                                P256ECDSASignature & outSignature)
     140              : {
     141           18 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (storage != nullptr), CHIP_ERROR_INVALID_ARGUMENT);
     142              : 
     143              :     // Use RAII scoping for the transient keypair, to make sure it doesn't get leaked on any error paths.
     144              :     // Key is put in heap since signature is a costly stack operation and P256Keypair is
     145              :     // a costly class depending on the backend.
     146           18 :     auto transientOperationalKeypair = Platform::MakeUnique<P256Keypair>();
     147           18 :     if (!transientOperationalKeypair)
     148              :     {
     149            0 :         return CHIP_ERROR_NO_MEMORY;
     150              :     }
     151              : 
     152              :     // Scope 1: Load up the keypair data from storage
     153           18 :     P256SerializedKeypair serializedOpKey;
     154           18 :     CHIP_ERROR err = ExportStoredOpKey(fabricIndex, storage, serializedOpKey);
     155           36 :     if (CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND == err)
     156              :     {
     157            2 :         return CHIP_ERROR_INVALID_FABRIC_INDEX;
     158              :     }
     159              : 
     160              :     // Load-up key material
     161              :     // WARNING: This makes use of the raw key bits
     162           16 :     ReturnErrorOnFailure(transientOperationalKeypair->Deserialize(serializedOpKey));
     163              : 
     164              :     // Scope 2: Sign message with the keypair
     165           16 :     return transientOperationalKeypair->ECDSA_sign_msg(message.data(), message.size(), outSignature);
     166           18 : }
     167              : 
     168              : } // namespace
     169              : 
     170           90 : bool PersistentStorageOperationalKeystore::HasPendingOpKeypair() const
     171              : {
     172           90 :     return (mPendingKeypair != nullptr);
     173              : }
     174              : 
     175          952 : bool PersistentStorageOperationalKeystore::HasOpKeypairForFabric(FabricIndex fabricIndex) const
     176              : {
     177          952 :     VerifyOrReturnError(mStorage != nullptr, false);
     178          952 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndex), false);
     179              : 
     180              :     // If there was a pending keypair, then there's really a usable key
     181          952 :     if (mIsPendingKeypairActive && (fabricIndex == mPendingFabricIndex) && (mPendingKeypair != nullptr))
     182              :     {
     183           50 :         return true;
     184              :     }
     185              : 
     186              :     // TODO(#16958): need to actually read the key to know if it's there due to platforms not
     187              :     //               properly enforcing CHIP_ERROR_BUFFER_TOO_SMALL behavior needed by
     188              :     //               PersistentStorageDelegate. Very unfortunate, needs fixing ASAP.
     189              : 
     190              :     // Use a SensitiveDataBuffer to get RAII secret data clearing on scope exit.
     191          902 :     Crypto::SensitiveDataBuffer<OpKeyTLVMaxSize()> buf;
     192              : 
     193          902 :     uint16_t keySize = static_cast<uint16_t>(buf.Capacity());
     194              :     CHIP_ERROR err =
     195          902 :         mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName(), buf.Bytes(), keySize);
     196              : 
     197         1804 :     return (err == CHIP_NO_ERROR);
     198          902 : }
     199              : 
     200           39 : CHIP_ERROR PersistentStorageOperationalKeystore::NewOpKeypairForFabric(FabricIndex fabricIndex,
     201              :                                                                        MutableByteSpan & outCertificateSigningRequest)
     202              : {
     203           39 :     VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
     204           38 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
     205              :     // If a key is pending, we cannot generate for a different fabric index until we commit or revert.
     206           36 :     if ((mPendingFabricIndex != kUndefinedFabricIndex) && (fabricIndex != mPendingFabricIndex))
     207              :     {
     208            1 :         return CHIP_ERROR_INVALID_FABRIC_INDEX;
     209              :     }
     210           35 :     VerifyOrReturnError(outCertificateSigningRequest.size() >= Crypto::kMIN_CSR_Buffer_Size, CHIP_ERROR_BUFFER_TOO_SMALL);
     211              : 
     212              :     // Replace previous pending keypair, if any was previously allocated
     213           35 :     ResetPendingKey();
     214              : 
     215           35 :     mPendingKeypair = Platform::New<Crypto::P256Keypair>();
     216           35 :     VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_NO_MEMORY);
     217              : 
     218           35 :     TEMPORARY_RETURN_IGNORED mPendingKeypair->Initialize(Crypto::ECPKeyTarget::ECDSA);
     219           35 :     size_t csrLength = outCertificateSigningRequest.size();
     220           35 :     CHIP_ERROR err   = mPendingKeypair->NewCertificateSigningRequest(outCertificateSigningRequest.data(), csrLength);
     221           70 :     if (err != CHIP_NO_ERROR)
     222              :     {
     223            0 :         ResetPendingKey();
     224            0 :         return err;
     225              :     }
     226              : 
     227           35 :     outCertificateSigningRequest.reduce_size(csrLength);
     228           35 :     mPendingFabricIndex = fabricIndex;
     229              : 
     230           35 :     return CHIP_NO_ERROR;
     231              : }
     232              : 
     233           33 : CHIP_ERROR PersistentStorageOperationalKeystore::ActivateOpKeypairForFabric(FabricIndex fabricIndex,
     234              :                                                                             const Crypto::P256PublicKey & nocPublicKey)
     235              : {
     236           33 :     VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
     237           32 :     VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
     238           32 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (fabricIndex == mPendingFabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
     239              : 
     240              :     // Validate public key being activated matches last generated pending keypair
     241           31 :     VerifyOrReturnError(mPendingKeypair->Pubkey().Matches(nocPublicKey), CHIP_ERROR_INVALID_PUBLIC_KEY);
     242              : 
     243           30 :     mIsPendingKeypairActive = true;
     244              : 
     245           30 :     return CHIP_NO_ERROR;
     246              : }
     247              : 
     248           27 : CHIP_ERROR PersistentStorageOperationalKeystore::CommitOpKeypairForFabric(FabricIndex fabricIndex)
     249              : {
     250           27 :     VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
     251           26 :     VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
     252           26 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (fabricIndex == mPendingFabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
     253           25 :     VerifyOrReturnError(mIsPendingKeypairActive == true, CHIP_ERROR_INCORRECT_STATE);
     254              : 
     255              :     // Try to store persistent key. On failure, leave everything pending as-is
     256           25 :     CHIP_ERROR err = StoreOperationalKey(fabricIndex, mStorage, mPendingKeypair);
     257           25 :     ReturnErrorOnFailure(err);
     258              : 
     259              :     // If we got here, we succeeded and can reset the pending key: next `SignWithOpKeypair` will use the stored key.
     260           25 :     ResetPendingKey();
     261           25 :     return CHIP_NO_ERROR;
     262              : }
     263              : 
     264            3 : CHIP_ERROR PersistentStorageOperationalKeystore::ExportOpKeypairForFabric(FabricIndex fabricIndex,
     265              :                                                                           Crypto::P256SerializedKeypair & outKeypair)
     266              : {
     267            3 :     VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
     268            3 :     return ExportStoredOpKey(fabricIndex, mStorage, outKeypair);
     269              : }
     270              : 
     271           21 : CHIP_ERROR PersistentStorageOperationalKeystore::RemoveOpKeypairForFabric(FabricIndex fabricIndex)
     272              : {
     273           21 :     VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
     274           20 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
     275              : 
     276              :     // Remove pending state if matching
     277           20 :     if ((mPendingKeypair != nullptr) && (fabricIndex == mPendingFabricIndex))
     278              :     {
     279            2 :         RevertPendingKeypair();
     280              :     }
     281              : 
     282           20 :     CHIP_ERROR err = mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName());
     283           40 :     if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
     284              :     {
     285           18 :         err = CHIP_ERROR_INVALID_FABRIC_INDEX;
     286              :     }
     287              : 
     288           20 :     return err;
     289              : }
     290              : 
     291          523 : void PersistentStorageOperationalKeystore::RevertPendingKeypair()
     292              : {
     293          523 :     VerifyOrReturn(mStorage != nullptr);
     294              : 
     295              :     // Just reset the pending key, we never stored anything
     296          523 :     ResetPendingKey();
     297              : }
     298              : 
     299           19 : CHIP_ERROR PersistentStorageOperationalKeystore::SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message,
     300              :                                                                    Crypto::P256ECDSASignature & outSignature) const
     301              : {
     302           19 :     VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
     303           19 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
     304              : 
     305           19 :     if (mIsPendingKeypairActive && (fabricIndex == mPendingFabricIndex))
     306              :     {
     307            1 :         VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_INTERNAL);
     308              :         // We have an override key: sign with it!
     309            1 :         return mPendingKeypair->ECDSA_sign_msg(message.data(), message.size(), outSignature);
     310              :     }
     311              : 
     312           18 :     return SignWithStoredOpKey(fabricIndex, mStorage, message, outSignature);
     313              : }
     314              : 
     315            2 : Crypto::P256Keypair * PersistentStorageOperationalKeystore::AllocateEphemeralKeypairForCASE()
     316              : {
     317              :     // DO NOT CUT AND PASTE without considering the ReleaseEphemeralKeypair().
     318              :     // If allocating a derived class, then `ReleaseEphemeralKeypair` MUST
     319              :     // de-allocate the derived class after up-casting the base class pointer.
     320            2 :     return Platform::New<Crypto::P256Keypair>();
     321              : }
     322              : 
     323            3 : void PersistentStorageOperationalKeystore::ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair)
     324              : {
     325              :     // DO NOT CUT AND PASTE without considering the AllocateEphemeralKeypairForCASE().
     326              :     // This must delete the same concrete class as allocated in `AllocateEphemeralKeypairForCASE`
     327            3 :     Platform::Delete<Crypto::P256Keypair>(keypair);
     328            3 : }
     329              : 
     330            6 : CHIP_ERROR PersistentStorageOperationalKeystore::MigrateOpKeypairForFabric(FabricIndex fabricIndex,
     331              :                                                                            OperationalKeystore & operationalKeystore) const
     332              : {
     333            6 :     VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
     334            6 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
     335              : 
     336            4 :     P256SerializedKeypair serializedKeypair;
     337              : 
     338              :     // Do not allow overwriting the existing key and just remove it from the previous Operational Keystore if needed.
     339            4 :     if (!HasOpKeypairForFabric(fabricIndex))
     340              :     {
     341            2 :         ReturnErrorOnFailure(operationalKeystore.ExportOpKeypairForFabric(fabricIndex, serializedKeypair));
     342              : 
     343            1 :         auto operationalKeypair = Platform::MakeUnique<P256Keypair>();
     344            1 :         if (!operationalKeypair)
     345              :         {
     346            0 :             return CHIP_ERROR_NO_MEMORY;
     347              :         }
     348              : 
     349            1 :         ReturnErrorOnFailure(operationalKeypair->Deserialize(serializedKeypair));
     350            1 :         ReturnErrorOnFailure(StoreOperationalKey(fabricIndex, mStorage, operationalKeypair.get()));
     351              : 
     352            1 :         ReturnErrorOnFailure(operationalKeystore.RemoveOpKeypairForFabric(fabricIndex));
     353            1 :     }
     354            2 :     else if (operationalKeystore.HasOpKeypairForFabric(fabricIndex))
     355              :     {
     356            1 :         ReturnErrorOnFailure(operationalKeystore.RemoveOpKeypairForFabric(fabricIndex));
     357              :     }
     358              : 
     359            3 :     return CHIP_NO_ERROR;
     360            4 : }
     361              : 
     362              : } // namespace chip
        

Generated by: LCOV version 2.0-1