Matter SDK Coverage Report
Current view: top level - crypto - PersistentStorageOperationalKeystore.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 97.1 % 140 136
Test Date: 2025-01-17 19:00:11 Functions: 100.0 % 14 14

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

Generated by: LCOV version 2.0-1