LCOV - code coverage report
Current view: top level - crypto - PersistentStorageOperationalKeystore.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 136 140 97.1 %
Date: 2024-02-15 08:20:41 Functions: 14 14 100.0 %

          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         682 : bool PersistentStorageOperationalKeystore::HasOpKeypairForFabric(FabricIndex fabricIndex) const
     173             : {
     174         682 :     VerifyOrReturnError(mStorage != nullptr, false);
     175         682 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndex), false);
     176             : 
     177             :     // If there was a pending keypair, then there's really a usable key
     178         682 :     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         646 :     Crypto::SensitiveDataBuffer<OpKeyTLVMaxSize()> buf;
     189             : 
     190         646 :     uint16_t keySize = static_cast<uint16_t>(buf.Capacity());
     191             :     CHIP_ERROR err =
     192         646 :         mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName(), buf.Bytes(), keySize);
     193             : 
     194         646 :     return (err == CHIP_NO_ERROR);
     195         646 : }
     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         387 : void PersistentStorageOperationalKeystore::RevertPendingKeypair()
     289             : {
     290         387 :     VerifyOrReturn(mStorage != nullptr);
     291             : 
     292             :     // Just reset the pending key, we never stored anything
     293         387 :     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 1.14