blob: 93a649ba1c794811fe837897fde7961b11375e22 [file] [log] [blame]
/*
* Copyright (c) 2025 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VendorIdVerificationClient.h"
#include <controller/InvokeInteraction.h>
#include <credentials/CHIPCert.h>
#include <credentials/FabricTable.h>
namespace chip {
namespace Credentials {
namespace JCM {
CHIP_ERROR VendorIdVerificationClient::VerifyNOCCertificateChain(const ByteSpan & nocSpan, const ByteSpan & icacSpan,
const ByteSpan & rcacSpan)
{
ValidationContext validContext;
validContext.Reset();
validContext.mRequiredKeyUsages.Set(KeyUsageFlags::kDigitalSignature);
validContext.mRequiredKeyPurposes.Set(KeyPurposeFlags::kClientAuth);
ChipCertificateSet certificates;
constexpr uint8_t kMaxNumCertsInOpCreds = 3;
ReturnLogErrorOnFailure(certificates.Init(kMaxNumCertsInOpCreds));
ReturnLogErrorOnFailure(certificates.LoadCert(rcacSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)));
if (!icacSpan.empty())
{
ReturnLogErrorOnFailure(certificates.LoadCert(icacSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)));
}
ReturnLogErrorOnFailure(certificates.LoadCert(nocSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)));
ReturnLogErrorOnFailure(certificates.ValidateCert(certificates.GetLastCert(), validContext));
return CHIP_NO_ERROR;
}
CHIP_ERROR VendorIdVerificationClient::Verify(
TrustVerificationInfo * info, const ByteSpan clientChallengeSpan, const ByteSpan attestationChallengeSpan,
const app::Clusters::OperationalCredentials::Commands::SignVIDVerificationResponse::DecodableType responseData)
{
// Steps 1-9 have already been completed prior to the response callback
const FabricIndex fabricIndex = info->adminFabricIndex;
const VendorId vendorID = info->adminVendorId;
const FabricId fabricId = info->adminFabricId;
auto rcacSpan = info->adminRCAC.Span();
auto icacSpan = info->adminICAC.Span();
auto nocSpan = info->adminNOC.Span();
// Extract the root public key
P256PublicKeySpan rcacPublicKeySpan;
ReturnLogErrorOnFailure(ExtractPublicKeyFromChipCert(rcacSpan, rcacPublicKeySpan));
Crypto::P256PublicKey rcacPublicKey(rcacPublicKeySpan);
// Locally generate the vendor_fabric_binding_message
uint8_t vendorFabricBindingMessageBuffer[Crypto::kVendorFabricBindingMessageV1Size];
MutableByteSpan vendorFabricBindingMessageSpan{ vendorFabricBindingMessageBuffer };
ReturnLogErrorOnFailure(Crypto::GenerateVendorFabricBindingMessage(Crypto::FabricBindingVersion::kVersion1, rcacPublicKey,
fabricId, vendorID, vendorFabricBindingMessageSpan));
// Locally generate the vendor_id_verification_tbs message
ByteSpan vidVerificationStatementSpan;
uint8_t vidVerificationStatementBuffer[Crypto::kVendorIdVerificationTbsV1MaxSize];
MutableByteSpan vidVerificationTbsSpan{ vidVerificationStatementBuffer };
// Generate the ToBeSigned portion of the VID verification
ReturnLogErrorOnFailure(Crypto::GenerateVendorIdVerificationToBeSigned(fabricIndex, clientChallengeSpan,
attestationChallengeSpan, vendorFabricBindingMessageSpan,
vidVerificationStatementSpan, vidVerificationTbsSpan));
// Extract the operational public key
P256PublicKeySpan nocPublicKeySpan;
ReturnLogErrorOnFailure(ExtractPublicKeyFromChipCert(nocSpan, nocPublicKeySpan));
Crypto::P256PublicKey nocPublicKey(nocPublicKeySpan);
// 10. Given the subject public key associated with the fabric being verified, validate that Crypto_Verify(noc_public_key,
// vendor_id_verification_tbs, signature) succeeds, otherwise the procedure terminates as failed.
Crypto::P256ECDSASignature signature;
ReturnValueOnFailure(signature.SetLength(responseData.signature.size()), CHIP_ERROR_BUFFER_TOO_SMALL);
memcpy(signature.Bytes(), responseData.signature.data(), responseData.signature.size());
ReturnLogErrorOnFailure(
nocPublicKey.ECDSA_validate_msg_signature(vidVerificationTbsSpan.data(), vidVerificationTbsSpan.size(), signature));
// 11. Verify that the NOC chain is valid using Crypto_VerifyChain() against the entire chain reconstructed from both the
// NOCs and TrustedRootCertificates attribute entries whose FabricIndex field matches the fabric being verified.
// If the chain is not valid, the procedure terminates as failed.
ReturnLogErrorOnFailure(VerifyNOCCertificateChain(nocSpan, icacSpan, rcacSpan));
// 12a If the VIDVerificationStatement field was present in the entry in Fabrics whose VendorID is being verified
// (Not currently supported)
// 12b. Otherwise (i.e VIDVerificationStatement not used):
// 12bi. Look-up the entry in the Operational Trust Anchors Schema under the expected VendorID (VID field)
// being verified whose IsRoot field is true and whose SubjectKeyID matches the SubjectKeyID field value of
// the TrustedRootCertificates attribute entry whose FabricIndex field matches the fabric being verified
// (i.e. the RCAC of the candidate fabric)
ChipCertificateData certData;
// Decode root cert with default (null) options
ReturnLogErrorOnFailure(DecodeChipCert(rcacSpan, certData));
ByteSpan globallyTrustedRootSpan;
ReturnLogErrorOnFailure(OnLookupOperationalTrustAnchor(vendorID, certData.mSubjectKeyId, globallyTrustedRootSpan));
// 12bii. Verify that the NOC chain is valid using Crypto_VerifyChain() against the entire chain reconstructed
// from the NOCs attribute entry whose FabricIndex field matches the fabric being verified, but populating
// the trusted root with the GloballyTrustedRoot certificate rather than the value in TrustedRootCertificates
// associated with the candidate fabric. If the chain is not valid, the procedure terminates as failed.
ReturnLogErrorOnFailure(VerifyNOCCertificateChain(nocSpan, icacSpan, globallyTrustedRootSpan));
// 13. Given that all prior steps succeeded, the candidate fabric’s VendorID SHALL be deemed authentic and err should be
// CHIP_NO_ERROR.
return CHIP_NO_ERROR;
}
CHIP_ERROR VendorIdVerificationClient::VerifyVendorId(Messaging::ExchangeManager * exchangeMgr, const SessionGetterFunc getSession,
TrustVerificationInfo * info)
{
ChipLogProgress(Controller, "Performing vendor ID verification for vendor ID: %u", info->adminVendorId);
// Generate a 32-octet random challenge
uint8_t kClientChallenge[32];
TEMPORARY_RETURN_IGNORED Crypto::DRBG_get_bytes(kClientChallenge, sizeof(kClientChallenge));
ByteSpan clientChallengeSpan{ kClientChallenge };
chip::app::Clusters::OperationalCredentials::Commands::SignVIDVerificationRequest::Type request;
// The selected fabric information that matches the Fabrics attribute whose VendorID field to be verified
// is in the JCMTrustVerificationInfo info
request.fabricIndex = info->adminFabricIndex;
request.clientChallenge = clientChallengeSpan;
auto onSuccessCb = [&, getSession, info, kClientChallenge](const app::ConcreteCommandPath & aPath,
const app::StatusIB & aStatus,
const decltype(request)::ResponseType & responseData) {
ChipLogProgress(Controller, "Successfully received SignVIDVerificationResponse");
ByteSpan clientChallenge{ kClientChallenge };
auto session = getSession();
if (!session.HasValue())
{
ChipLogError(Controller, "Session is missing");
OnVendorIdVerificationComplete(CHIP_ERROR_INCORRECT_STATE);
return;
}
ByteSpan attestationChallenge = session.Value()->AsSecureSession()->GetCryptoContext().GetAttestationChallenge();
CHIP_ERROR err = Verify(info, clientChallenge, attestationChallenge, responseData);
ChipLogProgress(Controller, "Vendor ID verification completed with result: %s", ErrorStr(err));
OnVendorIdVerificationComplete(err);
};
auto onFailureCb = [&](CHIP_ERROR err) {
ChipLogError(Controller, "Failed to receive SignVIDVerificationResponse: %s", ErrorStr(err));
OnVendorIdVerificationComplete(err);
};
Optional<SessionHandle> session = getSession();
if (!session.HasValue())
{
ChipLogError(Controller, "Session is missing");
CHIP_ERROR err = CHIP_ERROR_INCORRECT_STATE;
OnVendorIdVerificationComplete(err);
return err;
}
CHIP_ERROR err =
Controller::InvokeCommandRequest(exchangeMgr, session.Value(), kRootEndpointId, request, onSuccessCb, onFailureCb);
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Failed to send SignVIDVerificationRequest: %s", ErrorStr(err));
this->OnVendorIdVerificationComplete(err);
}
return CHIP_NO_ERROR;
}
} // namespace JCM
} // namespace Credentials
} // namespace chip