Matter SDK Coverage Report
Current view: top level - credentials - TestOnlyLocalCertificateAuthority.h (source / functions) Coverage Total Hit
Test: SHA:4d2388ac7eed75b2fe5e05e20de377999c632502 Lines: 95.7 % 117 112
Test Date: 2025-07-26 07:12:52 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              : #pragma once
      19              : 
      20              : #include <credentials/CHIPCert.h>
      21              : #include <crypto/CHIPCryptoPAL.h>
      22              : #include <lib/core/CHIPError.h>
      23              : #include <lib/core/DataModelTypes.h>
      24              : #include <lib/core/Optional.h>
      25              : #include <lib/support/CHIPMem.h>
      26              : #include <lib/support/CodeUtils.h>
      27              : 
      28              : namespace chip {
      29              : namespace Credentials {
      30              : 
      31              : class TestOnlyLocalCertificateAuthority
      32              : {
      33              : public:
      34           21 :     TestOnlyLocalCertificateAuthority()
      35           21 :     {
      36              :         // Initializing the default start validity to start of 2021.
      37              :         chip::ASN1::ASN1UniversalTime effectiveTime;
      38           21 :         CHIP_ZERO_AT(effectiveTime);
      39           21 :         effectiveTime.Year  = 2021;
      40           21 :         effectiveTime.Month = 1;
      41           21 :         effectiveTime.Day   = 1;
      42           21 :         VerifyOrDie(ASN1ToChipEpochTime(effectiveTime, mNow) == CHIP_NO_ERROR);
      43           21 :     }
      44              : 
      45           21 :     ~TestOnlyLocalCertificateAuthority()
      46              :     {
      47           21 :         mRootKeypair.reset();
      48           21 :         mLastRcac.Free();
      49           21 :         mLastNoc.Free();
      50           21 :         mLastIcac.Free();
      51           21 :     }
      52              : 
      53              :     // Non-copyable
      54              :     TestOnlyLocalCertificateAuthority(TestOnlyLocalCertificateAuthority const &) = delete;
      55              :     void operator=(TestOnlyLocalCertificateAuthority const &)                    = delete;
      56              : 
      57           18 :     TestOnlyLocalCertificateAuthority & Init()
      58              :     {
      59           18 :         Crypto::P256SerializedKeypair emptyKeypair;
      60           18 :         return Init(emptyKeypair);
      61           18 :     }
      62              : 
      63           21 :     TestOnlyLocalCertificateAuthority & Init(Crypto::P256SerializedKeypair & rootKeyPair)
      64              :     {
      65           21 :         SuccessOrExit(mCurrentStatus);
      66              : 
      67           21 :         mRootKeypair = Platform::MakeUnique<Crypto::P256Keypair>();
      68           21 :         VerifyOrExit(mRootKeypair != nullptr, mCurrentStatus = CHIP_ERROR_NO_MEMORY);
      69              : 
      70           21 :         if (rootKeyPair.Length() != 0)
      71              :         {
      72            3 :             mCurrentStatus = mRootKeypair->Deserialize(rootKeyPair);
      73            3 :             SuccessOrExit(mCurrentStatus);
      74              :         }
      75              :         else
      76              :         {
      77           18 :             mRootKeypair->Initialize(Crypto::ECPKeyTarget::ECDSA);
      78              :         }
      79           21 :         mCurrentStatus = GenerateRootCert(*mRootKeypair.get());
      80           21 :         SuccessOrExit(mCurrentStatus);
      81           21 :     exit:
      82           21 :         return *this;
      83              :     }
      84              : 
      85           35 :     TestOnlyLocalCertificateAuthority & SetIncludeIcac(bool includeIcac)
      86              :     {
      87           35 :         mIncludeIcac   = includeIcac;
      88           35 :         mCurrentStatus = (mCurrentStatus != CHIP_NO_ERROR) ? mCurrentStatus : CHIP_NO_ERROR;
      89           35 :         return *this;
      90              :     }
      91              : 
      92              :     void ResetIssuer()
      93              :     {
      94              :         mCurrentStatus = CHIP_NO_ERROR;
      95              :         mIncludeIcac   = false;
      96              :         mLastNoc.Free();
      97              :         mLastIcac.Free();
      98              :     }
      99              : 
     100           35 :     CHIP_ERROR GetStatus() { return mCurrentStatus; }
     101           21 :     bool IsSuccess() { return mCurrentStatus == CHIP_NO_ERROR; }
     102              : 
     103           35 :     ByteSpan GetNoc() const { return ByteSpan{ mLastNoc.Get(), mLastNoc.AllocatedSize() }; }
     104           22 :     ByteSpan GetIcac() const { return mIncludeIcac ? ByteSpan{ mLastIcac.Get(), mLastIcac.AllocatedSize() } : ByteSpan{}; }
     105           28 :     ByteSpan GetRcac() const { return ByteSpan{ mLastRcac.Get(), mLastRcac.AllocatedSize() }; }
     106              : 
     107           35 :     TestOnlyLocalCertificateAuthority & GenerateNocChain(FabricId fabricId, NodeId nodeId,
     108              :                                                          const Crypto::P256PublicKey & nocPublicKey)
     109              :     {
     110           35 :         if (mCurrentStatus != CHIP_NO_ERROR)
     111              :         {
     112            0 :             return *this;
     113              :         }
     114              : 
     115           35 :         if (mRootKeypair.get() == nullptr)
     116              :         {
     117            0 :             mCurrentStatus = CHIP_ERROR_NO_SHARED_TRUSTED_ROOT;
     118            0 :             return *this;
     119              :         }
     120              : 
     121           35 :         mLastIcac.Free();
     122           35 :         mLastNoc.Free();
     123           35 :         mCurrentStatus = GenerateCertChainInternal(fabricId, nodeId, nocPublicKey);
     124           35 :         return *this;
     125              :     }
     126              : 
     127           34 :     TestOnlyLocalCertificateAuthority & GenerateNocChain(FabricId fabricId, NodeId nodeId, const ByteSpan & csr)
     128              :     {
     129           34 :         if (mCurrentStatus != CHIP_NO_ERROR)
     130              :         {
     131            0 :             return *this;
     132              :         }
     133              : 
     134           34 :         Crypto::P256PublicKey nocPublicKey;
     135           34 :         mCurrentStatus = Crypto::VerifyCertificateSigningRequest(csr.data(), csr.size(), nocPublicKey);
     136           34 :         if (mCurrentStatus != CHIP_NO_ERROR)
     137              :         {
     138            0 :             return *this;
     139              :         }
     140              : 
     141           34 :         return GenerateNocChain(fabricId, nodeId, nocPublicKey);
     142           34 :     }
     143              : 
     144              : protected:
     145           35 :     CHIP_ERROR GenerateCertChainInternal(FabricId fabricId, NodeId nodeId, const Crypto::P256PublicKey & nocPublicKey)
     146              :     {
     147           35 :         ChipDN rcac_dn;
     148           35 :         ChipDN icac_dn;
     149           35 :         ChipDN noc_dn;
     150              : 
     151              :         // Get subject DN of RCAC as our issuer field for ICAC and/or NOC depending on if ICAC is present
     152           35 :         ReturnErrorOnFailure(ExtractSubjectDNFromChipCert(ByteSpan{ mLastRcac.Get(), mLastRcac.AllocatedSize() }, rcac_dn));
     153              : 
     154           35 :         Crypto::P256Keypair icacKeypair;
     155           35 :         ReturnErrorOnFailure(icacKeypair.Initialize(Crypto::ECPKeyTarget::ECDSA)); // Maybe we won't use it, but it's OK
     156              : 
     157           35 :         Crypto::P256Keypair * nocIssuerKeypair = mRootKeypair.get();
     158           35 :         ChipDN * issuer_dn                     = &rcac_dn;
     159              : 
     160              :         // Generate ICAC if needed
     161           35 :         if (mIncludeIcac)
     162              :         {
     163           22 :             Platform::ScopedMemoryBufferWithSize<uint8_t> icacDerBuf;
     164           22 :             VerifyOrReturnError(icacDerBuf.Alloc(Credentials::kMaxDERCertLength), CHIP_ERROR_NO_MEMORY);
     165           22 :             Platform::ScopedMemoryBufferWithSize<uint8_t> icacChipBuf;
     166           22 :             VerifyOrReturnError(icacChipBuf.Alloc(Credentials::kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
     167              : 
     168           22 :             ReturnErrorOnFailure(icac_dn.AddAttribute_MatterFabricId(fabricId));
     169           22 :             ReturnErrorOnFailure(icac_dn.AddAttribute_MatterICACId(1234));
     170              : 
     171           22 :             X509CertRequestParams icac_request = { 0, mNow, mNow + mValidity, icac_dn, rcac_dn };
     172              : 
     173           22 :             MutableByteSpan icacDerSpan{ icacDerBuf.Get(), icacDerBuf.AllocatedSize() };
     174           22 :             ReturnErrorOnFailure(Credentials::NewICAX509Cert(icac_request, icacKeypair.Pubkey(), *mRootKeypair.get(), icacDerSpan));
     175              : 
     176           22 :             MutableByteSpan icacChipSpan{ icacChipBuf.Get(), icacChipBuf.AllocatedSize() };
     177           22 :             ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(icacDerSpan, icacChipSpan));
     178              : 
     179           22 :             VerifyOrReturnError(mLastIcac.Alloc(icacChipSpan.size()), CHIP_ERROR_NO_MEMORY);
     180              : 
     181           22 :             memcpy(mLastIcac.Get(), icacChipSpan.data(), icacChipSpan.size());
     182              : 
     183           22 :             nocIssuerKeypair = &icacKeypair;
     184           22 :             issuer_dn        = &icac_dn;
     185           22 :         }
     186              : 
     187              :         // Generate NOC always, either issued from ICAC if present or from RCAC
     188              :         {
     189           35 :             Platform::ScopedMemoryBufferWithSize<uint8_t> nocDerBuf;
     190           35 :             VerifyOrReturnError(nocDerBuf.Alloc(Credentials::kMaxDERCertLength), CHIP_ERROR_NO_MEMORY);
     191           35 :             Platform::ScopedMemoryBufferWithSize<uint8_t> nocChipBuf;
     192           35 :             VerifyOrReturnError(nocChipBuf.Alloc(Credentials::kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
     193              : 
     194           35 :             ReturnErrorOnFailure(noc_dn.AddAttribute_MatterFabricId(fabricId));
     195           35 :             ReturnErrorOnFailure(noc_dn.AddAttribute_MatterNodeId(nodeId));
     196              : 
     197           35 :             X509CertRequestParams noc_request = { 0, mNow, mNow + mValidity, noc_dn, *issuer_dn };
     198              : 
     199           35 :             MutableByteSpan nocDerSpan{ nocDerBuf.Get(), nocDerBuf.AllocatedSize() };
     200           35 :             ReturnErrorOnFailure(Credentials::NewNodeOperationalX509Cert(noc_request, nocPublicKey, *nocIssuerKeypair, nocDerSpan));
     201              : 
     202           35 :             MutableByteSpan nocChipSpan{ nocChipBuf.Get(), nocChipBuf.AllocatedSize() };
     203           35 :             ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(nocDerSpan, nocChipSpan));
     204              : 
     205           35 :             VerifyOrReturnError(mLastNoc.Alloc(nocChipSpan.size()), CHIP_ERROR_NO_MEMORY);
     206              : 
     207           35 :             memcpy(mLastNoc.Get(), nocChipSpan.data(), nocChipSpan.size());
     208           35 :         }
     209              : 
     210           35 :         return CHIP_NO_ERROR;
     211           35 :     }
     212              : 
     213           21 :     CHIP_ERROR GenerateRootCert(Crypto::P256Keypair & rootKeyPair)
     214              :     {
     215           21 :         ChipDN rcac_dn;
     216           21 :         const uint64_t kIssuerId = 1234567;
     217              : 
     218           21 :         Platform::ScopedMemoryBufferWithSize<uint8_t> rcacDerBuf;
     219           21 :         VerifyOrReturnError(rcacDerBuf.Alloc(Credentials::kMaxDERCertLength), CHIP_ERROR_NO_MEMORY);
     220           21 :         Platform::ScopedMemoryBufferWithSize<uint8_t> rcacChipBuf;
     221           21 :         VerifyOrReturnError(rcacChipBuf.Alloc(Credentials::kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
     222              : 
     223           21 :         ReturnErrorOnFailure(rcac_dn.AddAttribute_MatterRCACId(kIssuerId));
     224              : 
     225           21 :         X509CertRequestParams rcac_request = { 0, mNow, mNow + mValidity, rcac_dn, rcac_dn };
     226              : 
     227           21 :         MutableByteSpan rcacDerSpan{ rcacDerBuf.Get(), rcacDerBuf.AllocatedSize() };
     228           21 :         ReturnErrorOnFailure(Credentials::NewRootX509Cert(rcac_request, rootKeyPair, rcacDerSpan));
     229              : 
     230           21 :         MutableByteSpan rcacChipSpan{ rcacChipBuf.Get(), rcacChipBuf.AllocatedSize() };
     231           21 :         ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(rcacDerSpan, rcacChipSpan));
     232              : 
     233           21 :         VerifyOrReturnError(mLastRcac.Alloc(rcacChipSpan.size()), CHIP_ERROR_NO_MEMORY);
     234           21 :         memcpy(mLastRcac.Get(), rcacChipSpan.data(), rcacChipSpan.size());
     235              : 
     236           21 :         return CHIP_NO_ERROR;
     237           21 :     }
     238              : 
     239              :     uint32_t mNow = 0;
     240              : 
     241              :     // By default, let's set validity to 10 years
     242              :     uint32_t mValidity = 365 * 24 * 60 * 60 * 10;
     243              : 
     244              :     CHIP_ERROR mCurrentStatus = CHIP_NO_ERROR;
     245              :     bool mIncludeIcac         = false;
     246              : 
     247              :     Platform::ScopedMemoryBufferWithSize<uint8_t> mLastNoc;
     248              :     Platform::ScopedMemoryBufferWithSize<uint8_t> mLastIcac;
     249              :     Platform::ScopedMemoryBufferWithSize<uint8_t> mLastRcac;
     250              : 
     251              :     Platform::UniquePtr<Crypto::P256Keypair> mRootKeypair;
     252              : };
     253              : 
     254              : } // namespace Credentials
     255              : } // namespace chip
        

Generated by: LCOV version 2.0-1