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
|