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 16 : ReturnLogErrorOnFailure(certificates.Init(kMaxNumCertsInOpCreds));
40 16 : ReturnLogErrorOnFailure(certificates.LoadCert(rcacSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)));
41 7 : if (!icacSpan.empty())
42 : {
43 14 : ReturnLogErrorOnFailure(certificates.LoadCert(icacSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)));
44 : }
45 12 : ReturnLogErrorOnFailure(certificates.LoadCert(nocSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)));
46 12 : 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 18 : 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 16 : 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 16 : ReturnLogErrorOnFailure(Crypto::GenerateVendorIdVerificationToBeSigned(fabricIndex, clientChallengeSpan,
81 : attestationChallengeSpan, vendorFabricBindingMessageSpan,
82 : vidVerificationStatementSpan, vidVerificationTbsSpan));
83 :
84 : // Extract the operational public key
85 8 : P256PublicKeySpan nocPublicKeySpan;
86 16 : 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 14 : 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 10 : 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 8 : ReturnLogErrorOnFailure(DecodeChipCert(rcacSpan, certData));
113 4 : ByteSpan globallyTrustedRootSpan;
114 8 : 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 6 : 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
133 : uint8_t kClientChallenge[32];
134 11 : TEMPORARY_RETURN_IGNORED Crypto::DRBG_get_bytes(kClientChallenge, sizeof(kClientChallenge));
135 11 : ByteSpan clientChallengeSpan{ kClientChallenge };
136 11 : chip::app::Clusters::OperationalCredentials::Commands::SignVIDVerificationRequest::Type request;
137 :
138 : // The selected fabric information that matches the Fabrics attribute whose VendorID field to be verified
139 : // is in the JCMTrustVerificationInfo info
140 11 : request.fabricIndex = info->adminFabricIndex;
141 11 : request.clientChallenge = clientChallengeSpan;
142 :
143 9 : auto onSuccessCb = [&, getSession, info, kClientChallenge](const app::ConcreteCommandPath & aPath,
144 : const app::StatusIB & aStatus,
145 9 : const decltype(request)::ResponseType & responseData) {
146 9 : ChipLogProgress(Controller, "Successfully received SignVIDVerificationResponse");
147 9 : ByteSpan clientChallenge{ kClientChallenge };
148 9 : ByteSpan attestationChallenge = getSession().Value()->AsSecureSession()->GetCryptoContext().GetAttestationChallenge();
149 9 : CHIP_ERROR err = Verify(info, clientChallenge, attestationChallenge, responseData);
150 9 : ChipLogProgress(Controller, "Vendor ID verification completed with result: %s", ErrorStr(err));
151 9 : OnVendorIdVerificationComplete(err);
152 11 : };
153 :
154 2 : auto onFailureCb = [&](CHIP_ERROR err) {
155 2 : ChipLogError(Controller, "Failed to receive SignVIDVerificationResponse: %s", ErrorStr(err));
156 2 : OnVendorIdVerificationComplete(err);
157 13 : };
158 :
159 : CHIP_ERROR err =
160 11 : Controller::InvokeCommandRequest(exchangeMgr, getSession().Value(), kRootEndpointId, request, onSuccessCb, onFailureCb);
161 22 : if (err != CHIP_NO_ERROR)
162 : {
163 0 : ChipLogError(Controller, "Failed to send SignVIDVerificationRequest: %s", ErrorStr(err));
164 0 : this->OnVendorIdVerificationComplete(err);
165 : }
166 :
167 11 : return CHIP_NO_ERROR;
168 11 : }
169 :
170 : } // namespace JCM
171 : } // namespace Credentials
172 : } // namespace chip
|