Matter SDK Coverage Report
Current view: top level - credentials/jcm - VendorIdVerificationClient.cpp (source / functions) Coverage Total Hit
Test: SHA:6c8f029dd2432dc900f1c9245c324e69bd79e40a Lines: 91.5 % 82 75
Test Date: 2026-08-08 07:40:09 Functions: 100.0 % 5 5

            Line data    Source code
       1              : /*
       2              :  *    Copyright (c) 2025 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 "VendorIdVerificationClient.h"
      19              : 
      20              : #include <controller/InvokeInteraction.h>
      21              : #include <credentials/CHIPCert.h>
      22              : #include <credentials/FabricTable.h>
      23              : 
      24              : namespace chip {
      25              : namespace Credentials {
      26              : namespace JCM {
      27              : 
      28            8 : CHIP_ERROR VendorIdVerificationClient::VerifyNOCCertificateChain(const ByteSpan & nocSpan, const ByteSpan & icacSpan,
      29              :                                                                  const ByteSpan & rcacSpan)
      30              : {
      31            8 :     ValidationContext validContext;
      32              : 
      33            8 :     validContext.Reset();
      34            8 :     validContext.mRequiredKeyUsages.Set(KeyUsageFlags::kDigitalSignature);
      35            8 :     validContext.mRequiredKeyPurposes.Set(KeyPurposeFlags::kClientAuth);
      36              : 
      37            8 :     ChipCertificateSet certificates;
      38            8 :     constexpr uint8_t kMaxNumCertsInOpCreds = 3;
      39            8 :     ReturnLogErrorOnFailure(certificates.Init(kMaxNumCertsInOpCreds));
      40            8 :     ReturnLogErrorOnFailure(certificates.LoadCert(rcacSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)));
      41            7 :     if (!icacSpan.empty())
      42              :     {
      43            7 :         ReturnLogErrorOnFailure(certificates.LoadCert(icacSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)));
      44              :     }
      45            6 :     ReturnLogErrorOnFailure(certificates.LoadCert(nocSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)));
      46            6 :     ReturnLogErrorOnFailure(certificates.ValidateCert(certificates.GetLastCert(), validContext));
      47              : 
      48            6 :     return CHIP_NO_ERROR;
      49            8 : }
      50              : 
      51            9 : CHIP_ERROR VendorIdVerificationClient::Verify(
      52              :     TrustVerificationInfo * info, const ByteSpan clientChallengeSpan, const ByteSpan attestationChallengeSpan,
      53              :     const app::Clusters::OperationalCredentials::Commands::SignVIDVerificationResponse::DecodableType responseData)
      54              : {
      55              :     // Steps 1-9 have already been completed prior to the response callback
      56            9 :     const FabricIndex fabricIndex = info->adminFabricIndex;
      57            9 :     const VendorId vendorID       = info->adminVendorId;
      58            9 :     const FabricId fabricId       = info->adminFabricId;
      59            9 :     auto rcacSpan                 = info->adminRCAC.Span();
      60            9 :     auto icacSpan                 = info->adminICAC.Span();
      61            9 :     auto nocSpan                  = info->adminNOC.Span();
      62              : 
      63              :     // Extract the root public key
      64            9 :     P256PublicKeySpan rcacPublicKeySpan;
      65            9 :     ReturnLogErrorOnFailure(ExtractPublicKeyFromChipCert(rcacSpan, rcacPublicKeySpan));
      66            8 :     Crypto::P256PublicKey rcacPublicKey(rcacPublicKeySpan);
      67              : 
      68              :     // Locally generate the vendor_fabric_binding_message
      69              :     uint8_t vendorFabricBindingMessageBuffer[Crypto::kVendorFabricBindingMessageV1Size];
      70            8 :     MutableByteSpan vendorFabricBindingMessageSpan{ vendorFabricBindingMessageBuffer };
      71            8 :     ReturnLogErrorOnFailure(Crypto::GenerateVendorFabricBindingMessage(Crypto::FabricBindingVersion::kVersion1, rcacPublicKey,
      72              :                                                                        fabricId, vendorID, vendorFabricBindingMessageSpan));
      73              : 
      74              :     // Locally generate the vendor_id_verification_tbs message
      75            8 :     ByteSpan vidVerificationStatementSpan;
      76              :     uint8_t vidVerificationStatementBuffer[Crypto::kVendorIdVerificationTbsV1MaxSize];
      77            8 :     MutableByteSpan vidVerificationTbsSpan{ vidVerificationStatementBuffer };
      78              : 
      79              :     // Generate the ToBeSigned portion of the VID verification
      80            8 :     ReturnLogErrorOnFailure(Crypto::GenerateVendorIdVerificationToBeSigned(fabricIndex, clientChallengeSpan,
      81              :                                                                            attestationChallengeSpan, vendorFabricBindingMessageSpan,
      82              :                                                                            vidVerificationStatementSpan, vidVerificationTbsSpan));
      83              : 
      84              :     // Extract the operational public key
      85            8 :     P256PublicKeySpan nocPublicKeySpan;
      86            8 :     ReturnLogErrorOnFailure(ExtractPublicKeyFromChipCert(nocSpan, nocPublicKeySpan));
      87            7 :     Crypto::P256PublicKey nocPublicKey(nocPublicKeySpan);
      88              : 
      89              :     // 10. Given the subject public key associated with the fabric being verified, validate that Crypto_Verify(noc_public_key,
      90              :     // vendor_id_verification_tbs, signature) succeeds, otherwise the procedure terminates as failed.
      91            7 :     Crypto::P256ECDSASignature signature;
      92            7 :     ReturnValueOnFailure(signature.SetLength(responseData.signature.size()), CHIP_ERROR_BUFFER_TOO_SMALL);
      93            7 :     memcpy(signature.Bytes(), responseData.signature.data(), responseData.signature.size());
      94              : 
      95            7 :     ReturnLogErrorOnFailure(
      96              :         nocPublicKey.ECDSA_validate_msg_signature(vidVerificationTbsSpan.data(), vidVerificationTbsSpan.size(), signature));
      97              : 
      98              :     // 11. Verify that the NOC chain is valid using Crypto_VerifyChain() against the entire chain reconstructed from both the
      99              :     // NOCs and TrustedRootCertificates attribute entries whose FabricIndex field matches the fabric being verified.
     100              :     // If the chain is not valid, the procedure terminates as failed.
     101            5 :     ReturnLogErrorOnFailure(VerifyNOCCertificateChain(nocSpan, icacSpan, rcacSpan));
     102              : 
     103              :     // 12a If the VIDVerificationStatement field was present in the entry in Fabrics whose VendorID is being verified
     104              :     // (Not currently supported)
     105              :     // 12b. Otherwise (i.e VIDVerificationStatement not used):
     106              :     // 12bi. Look-up the entry in the Operational Trust Anchors Schema under the expected VendorID (VID field)
     107              :     // being verified whose IsRoot field is true and whose SubjectKeyID matches the SubjectKeyID field value of
     108              :     // the TrustedRootCertificates attribute entry whose FabricIndex field matches the fabric being verified
     109              :     // (i.e. the RCAC of the candidate fabric)
     110            4 :     ChipCertificateData certData;
     111              :     // Decode root cert with default (null) options
     112            4 :     ReturnLogErrorOnFailure(DecodeChipCert(rcacSpan, certData));
     113            4 :     ByteSpan globallyTrustedRootSpan;
     114            4 :     ReturnLogErrorOnFailure(OnLookupOperationalTrustAnchor(vendorID, certData.mSubjectKeyId, globallyTrustedRootSpan));
     115              : 
     116              :     // 12bii. Verify that the NOC chain is valid using Crypto_VerifyChain() against the entire chain reconstructed
     117              :     // from the NOCs attribute entry whose FabricIndex field matches the fabric being verified, but populating
     118              :     // the trusted root with the GloballyTrustedRoot certificate rather than the value in TrustedRootCertificates
     119              :     // associated with the candidate fabric. If the chain is not valid, the procedure terminates as failed.
     120            3 :     ReturnLogErrorOnFailure(VerifyNOCCertificateChain(nocSpan, icacSpan, globallyTrustedRootSpan));
     121              : 
     122              :     // 13. Given that all prior steps succeeded, the candidate fabric’s VendorID SHALL be deemed authentic and err should be
     123              :     // CHIP_NO_ERROR.
     124            2 :     return CHIP_NO_ERROR;
     125            8 : }
     126              : 
     127           11 : CHIP_ERROR VendorIdVerificationClient::VerifyVendorId(Messaging::ExchangeManager * exchangeMgr, const SessionGetterFunc getSession,
     128              :                                                       TrustVerificationInfo * info)
     129              : {
     130           11 :     ChipLogProgress(Controller, "Performing vendor ID verification for vendor ID: %u", info->adminVendorId);
     131              : 
     132              :     // Generate a 32-octet random challenge. On entropy/DRBG failure the buffer is left untouched, so
     133              :     // propagate the error rather than sending an indeterminate challenge that is folded into the
     134              :     // signed TBS. The caller maps a non-NO_ERROR return to verification failure.
     135           11 :     uint8_t kClientChallenge[32] = {};
     136           11 :     ReturnLogErrorOnFailure(Crypto::DRBG_get_bytes(kClientChallenge, sizeof(kClientChallenge)));
     137           11 :     ByteSpan clientChallengeSpan{ kClientChallenge };
     138           11 :     chip::app::Clusters::OperationalCredentials::Commands::SignVIDVerificationRequest::Type request;
     139              : 
     140              :     // The selected fabric information that matches the Fabrics attribute whose VendorID field to be verified
     141              :     // is in the JCMTrustVerificationInfo info
     142           11 :     request.fabricIndex     = info->adminFabricIndex;
     143           11 :     request.clientChallenge = clientChallengeSpan;
     144              : 
     145            9 :     auto onSuccessCb = [&, getSession, info, kClientChallenge](const app::ConcreteCommandPath & aPath,
     146              :                                                                const app::StatusIB & aStatus,
     147            9 :                                                                const decltype(request)::ResponseType & responseData) {
     148            9 :         ChipLogProgress(Controller, "Successfully received SignVIDVerificationResponse");
     149            9 :         ByteSpan clientChallenge{ kClientChallenge };
     150              : 
     151            9 :         auto session = getSession();
     152            9 :         if (!session.HasValue())
     153              :         {
     154            0 :             ChipLogError(Controller, "Session is missing");
     155            0 :             OnVendorIdVerificationComplete(CHIP_ERROR_INCORRECT_STATE);
     156            0 :             return;
     157              :         }
     158            9 :         ByteSpan attestationChallenge = session.Value()->AsSecureSession()->GetCryptoContext().GetAttestationChallenge();
     159            9 :         CHIP_ERROR err                = Verify(info, clientChallenge, attestationChallenge, responseData);
     160            9 :         ChipLogProgress(Controller, "Vendor ID verification completed with result: %s", ErrorStr(err));
     161            9 :         OnVendorIdVerificationComplete(err);
     162           20 :     };
     163              : 
     164            2 :     auto onFailureCb = [&](CHIP_ERROR err) {
     165            2 :         ChipLogError(Controller, "Failed to receive SignVIDVerificationResponse: %s", ErrorStr(err));
     166            2 :         OnVendorIdVerificationComplete(err);
     167           13 :     };
     168              : 
     169           11 :     Optional<SessionHandle> session = getSession();
     170           11 :     if (!session.HasValue())
     171              :     {
     172            0 :         ChipLogError(Controller, "Session is missing");
     173            0 :         return CHIP_ERROR_INCORRECT_STATE;
     174              :     }
     175              :     // Guard the response callbacks: a FailSafe teardown can destroy this object while the invoke is in
     176              :     // flight, and the CommandSender-owned callback would otherwise fire OnVendorIdVerificationComplete()
     177              :     // through a freed `this`.
     178           33 :     CHIP_ERROR err = Controller::InvokeCommandRequest(exchangeMgr, session.Value(), kRootEndpointId, request,
     179           33 :                                                       GuardWithLiveness(onSuccessCb), GuardWithLiveness(onFailureCb));
     180           22 :     if (err != CHIP_NO_ERROR)
     181              :     {
     182            0 :         ChipLogError(Controller, "Failed to send SignVIDVerificationRequest: %s", ErrorStr(err));
     183            0 :         return err;
     184              :     }
     185              : 
     186           11 :     return CHIP_NO_ERROR;
     187           11 : }
     188              : 
     189              : } // namespace JCM
     190              : } // namespace Credentials
     191              : } // namespace chip
        

Generated by: LCOV version 2.0-1