Skip to content

Commit

Permalink
Allow partial validation of DAC and CD (for external cloud or custom …
Browse files Browse the repository at this point in the history
…app based validation) (#21725)

* Draft: Allow partial validation of DAC and CD (when PAA list is not local)

* Draft: Allow partial validation of DAC and CD (when PAA list is not local)

* Move cloud attestation to a separate class, re-structure default attestation verification logic to allow cloud verifier to leverage it

* straggler

* address feedback

* address feedback

* straggler

* fix builds, integration tests
  • Loading branch information
chrisdecenzo authored and pull[bot] committed Nov 8, 2023
1 parent e780e78 commit e3a401d
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 4 deletions.
5 changes: 5 additions & 0 deletions src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,11 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController,
CHIP_ERROR IssueNOCChain(const ByteSpan & NOCSRElements, NodeId nodeId,
chip::Callback::Callback<OnNOCChainGeneration> * callback);

void SetDeviceAttestationVerifier(Credentials::DeviceAttestationVerifier * deviceAttestationVerifier)
{
mDeviceAttestationVerifier = deviceAttestationVerifier;
}

private:
DevicePairingDelegate * mPairingDelegate;

Expand Down
5 changes: 5 additions & 0 deletions src/controller/java/AndroidDeviceControllerWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <controller/CHIPDeviceController.h>
#include <credentials/GroupDataProviderImpl.h>
#include <credentials/PersistentStorageOpCertStore.h>
#include <credentials/attestation_verifier/DacOnlyPartialAttestationVerifier.h>
#include <lib/support/TimeUtils.h>
#include <platform/android/CHIPP256KeypairBridge.h>
#include <platform/internal/DeviceNetworkInfo.h>
Expand Down Expand Up @@ -94,6 +95,8 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel

chip::Controller::AutoCommissioner * GetAutoCommissioner() { return &mAutoCommissioner; }

chip::Credentials::PartialDACVerifier * GetPartialDACVerifier() { return &mPartialDACVerifier; }

const chip::Controller::CommissioningParameters & GetCommissioningParameters() const
{
return mAutoCommissioner.GetCommissioningParameters();
Expand Down Expand Up @@ -175,6 +178,8 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel

chip::Controller::AutoCommissioner mAutoCommissioner;

chip::Credentials::PartialDACVerifier mPartialDACVerifier;

AndroidDeviceControllerWrapper(ChipDeviceControllerPtr controller, AndroidOperationalCredentialsIssuerPtr opCredsIssuer) :
mController(std::move(controller)), mOpCredsIssuer(std::move(opCredsIssuer))
{}
Expand Down
14 changes: 10 additions & 4 deletions src/controller/java/AndroidOperationalCredentialsIssuer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ CHIP_ERROR AndroidOperationalCredentialsIssuer::GenerateNOCChain(const ByteSpan
}

CHIP_ERROR AndroidOperationalCredentialsIssuer::CallbackGenerateNOCChain(const ByteSpan & csrElements, const ByteSpan & csrNonce,
const ByteSpan & csrSignature,
const ByteSpan & csrElementsSignature,
const ByteSpan & attestationChallenge,
const ByteSpan & DAC, const ByteSpan & PAI,
Callback::Callback<OnNOCChainGeneration> * onCompletion)
Expand All @@ -177,8 +177,9 @@ CHIP_ERROR AndroidOperationalCredentialsIssuer::CallbackGenerateNOCChain(const B
jbyteArray javaCsrNonce;
JniReferences::GetInstance().N2J_ByteArray(env, csrNonce.data(), csrNonce.size(), javaCsrNonce);

jbyteArray javaCsrSignature;
JniReferences::GetInstance().N2J_ByteArray(env, csrSignature.data(), csrSignature.size(), javaCsrSignature);
jbyteArray javaCsrElementsSignature;
JniReferences::GetInstance().N2J_ByteArray(env, csrElementsSignature.data(), csrElementsSignature.size(),
javaCsrElementsSignature);

ChipLogProgress(Controller, "Parsing Certificate Signing Request");
TLVReader reader;
Expand All @@ -202,8 +203,13 @@ CHIP_ERROR AndroidOperationalCredentialsIssuer::CallbackGenerateNOCChain(const B
jbyteArray javaCsr;
JniReferences::GetInstance().N2J_ByteArray(env, csr.data(), csr.size(), javaCsr);

P256PublicKey pubkey;
ReturnErrorOnFailure(VerifyCertificateSigningRequest(csr.data(), csr.size(), pubkey));
// TODO: verify signed by DAC creds?
ChipLogProgress(chipTool, "VerifyCertificateSigningRequest");

jobject csrInfo;
err = N2J_CSRInfo(env, javaCsrNonce, javaCsrElements, javaCsrSignature, javaCsr, csrInfo);
err = N2J_CSRInfo(env, javaCsrNonce, javaCsrElements, javaCsrElementsSignature, javaCsr, csrInfo);
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Failed to create CSRInfo");
Expand Down
11 changes: 11 additions & 0 deletions src/controller/java/CHIPDeviceController-JNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,17 @@ JNI_METHOD(void, setUseJavaCallbackForNOCRequest)
AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);

wrapper->GetAndroidOperationalCredentialsIssuer()->SetUseJavaCallbackForNOCRequest(useCallback);

if (useCallback)
{
// if we are assigning a callback, then make the device commissioner delegate verification to the cloud
wrapper->Controller()->SetDeviceAttestationVerifier(wrapper->GetPartialDACVerifier());
}
else
{
// if we are setting callback to null, then make the device commissioner use the default verifier
wrapper->Controller()->SetDeviceAttestationVerifier(GetDeviceAttestationVerifier());
}
}

JNI_METHOD(void, updateCommissioningNetworkCredentials)
Expand Down
2 changes: 2 additions & 0 deletions src/credentials/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ static_library("default_attestation_verifier") {
output_name = "libDefaultAttestationVerifier"

sources = [
"attestation_verifier/DacOnlyPartialAttestationVerifier.cpp",
"attestation_verifier/DacOnlyPartialAttestationVerifier.h",
"attestation_verifier/DefaultDeviceAttestationVerifier.cpp",
"attestation_verifier/DefaultDeviceAttestationVerifier.h",
"attestation_verifier/DeviceAttestationDelegate.h",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
*
* Copyright (c) 2021-2022 Project CHIP Authors
*
* 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 "DacOnlyPartialAttestationVerifier.h"

#include <controller/OperationalCredentialsDelegate.h>
#include <credentials/CHIPCert.h>
#include <credentials/CertificationDeclaration.h>
#include <credentials/DeviceAttestationConstructor.h>
#include <credentials/DeviceAttestationVendorReserved.h>
#include <crypto/CHIPCryptoPAL.h>

#include <lib/core/CHIPError.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/ScopedBuffer.h>
#include <lib/support/Span.h>

using namespace chip::Crypto;

namespace chip {
namespace Credentials {

// As per specifications section 11.22.5.1. Constant RESP_MAX
constexpr size_t kMaxResponseLength = 900;

void PartialDACVerifier::VerifyAttestationInformation(const DeviceAttestationVerifier::AttestationInfo & info,
Callback::Callback<OnAttestationInformationVerification> * onCompletion)
{
AttestationVerificationResult attestationError = AttestationVerificationResult::kSuccess;

AttestationCertVidPid dacVidPid;
AttestationCertVidPid paiVidPid;
AttestationCertVidPid paaVidPid;

DeviceInfoForAttestation deviceInfo{
.vendorId = info.vendorId,
.productId = info.productId,
};

VerifyOrExit(!info.attestationElementsBuffer.empty() && !info.attestationChallengeBuffer.empty() &&
!info.attestationSignatureBuffer.empty() && !info.paiDerBuffer.empty() && !info.dacDerBuffer.empty() &&
!info.attestationNonceBuffer.empty() && onCompletion != nullptr,
attestationError = AttestationVerificationResult::kInvalidArgument);

VerifyOrExit(info.attestationElementsBuffer.size() <= kMaxResponseLength,
attestationError = AttestationVerificationResult::kInvalidArgument);

// match DAC and PAI VIDs
{
VerifyOrExit(ExtractVIDPIDFromX509Cert(info.dacDerBuffer, dacVidPid) == CHIP_NO_ERROR,
attestationError = AttestationVerificationResult::kDacFormatInvalid);
VerifyOrExit(ExtractVIDPIDFromX509Cert(info.paiDerBuffer, paiVidPid) == CHIP_NO_ERROR,
attestationError = AttestationVerificationResult::kPaiFormatInvalid);
VerifyOrExit(paiVidPid.mVendorId.HasValue() && paiVidPid.mVendorId == dacVidPid.mVendorId,
attestationError = AttestationVerificationResult::kDacVendorIdMismatch);
VerifyOrExit(dacVidPid.mProductId.HasValue(), attestationError = AttestationVerificationResult::kDacProductIdMismatch);
if (paiVidPid.mProductId.HasValue())
{
VerifyOrExit(paiVidPid.mProductId == dacVidPid.mProductId,
attestationError = AttestationVerificationResult::kDacProductIdMismatch);
}
}

{
P256PublicKey remoteManufacturerPubkey;
P256ECDSASignature deviceSignature;

VerifyOrExit(ExtractPubkeyFromX509Cert(info.dacDerBuffer, remoteManufacturerPubkey) == CHIP_NO_ERROR,
attestationError = AttestationVerificationResult::kDacFormatInvalid);

// Validate overall attestation signature on attestation information
// SetLength will fail if signature doesn't fit
VerifyOrExit(deviceSignature.SetLength(info.attestationSignatureBuffer.size()) == CHIP_NO_ERROR,
attestationError = AttestationVerificationResult::kAttestationSignatureInvalidFormat);
memcpy(deviceSignature.Bytes(), info.attestationSignatureBuffer.data(), info.attestationSignatureBuffer.size());
VerifyOrExit(ValidateAttestationSignature(remoteManufacturerPubkey, info.attestationElementsBuffer,
info.attestationChallengeBuffer, deviceSignature) == CHIP_NO_ERROR,
attestationError = AttestationVerificationResult::kAttestationSignatureInvalid);
}

{
MutableByteSpan akid(deviceInfo.paaSKID);

VerifyOrExit(ExtractAKIDFromX509Cert(info.paiDerBuffer, akid) == CHIP_NO_ERROR,
attestationError = AttestationVerificationResult::kPaiFormatInvalid);

ChipLogProgress(Support, "PartialDACVerifier::CheckPAA skipping vid-scoped PAA check - PAARootStore disabled");
}

#if !defined(CURRENT_TIME_NOT_IMPLEMENTED)
VerifyOrExit(IsCertificateValidAtCurrentTime(info.dacDerBuffer) == CHIP_NO_ERROR,
attestationError = AttestationVerificationResult::kDacExpired);
#endif

ChipLogProgress(Support, "PartialDACVerifier::CheckCertChain skipping cert chain check - PAARootStore disabled");

{
ByteSpan certificationDeclarationSpan;
ByteSpan attestationNonceSpan;
uint32_t timestampDeconstructed;
ByteSpan firmwareInfoSpan;
DeviceAttestationVendorReservedDeconstructor vendorReserved;
ByteSpan certificationDeclarationPayload;

deviceInfo.dacVendorId = dacVidPid.mVendorId.Value();
deviceInfo.dacProductId = dacVidPid.mProductId.Value();
deviceInfo.paiVendorId = paiVidPid.mVendorId.Value();
deviceInfo.paiProductId = paiVidPid.mProductId.ValueOr(0);
deviceInfo.paaVendorId = paaVidPid.mVendorId.ValueOr(VendorId::NotSpecified);

ChipLogProgress(
Support,
"PartialDACVerifier::VerifyAttestationInformation skipping PAA subject key id extraction - PAARootStore disabled");

VerifyOrExit(DeconstructAttestationElements(info.attestationElementsBuffer, certificationDeclarationSpan,
attestationNonceSpan, timestampDeconstructed, firmwareInfoSpan,
vendorReserved) == CHIP_NO_ERROR,
attestationError = AttestationVerificationResult::kAttestationElementsMalformed);

// Verify that Nonce matches with what we sent
VerifyOrExit(attestationNonceSpan.data_equal(info.attestationNonceBuffer),
attestationError = AttestationVerificationResult::kAttestationNonceMismatch);

ChipLogProgress(Support,
"PartialDACVerifier::VerifyAttestationInformation skipping CD signature check - LocalCSAStore disabled");
VerifyOrExit(CMS_ExtractCDContent(certificationDeclarationSpan, certificationDeclarationPayload) == CHIP_NO_ERROR,
attestationError = AttestationVerificationResult::kPaaFormatInvalid);

attestationError = ValidateCertificateDeclarationPayload(certificationDeclarationPayload, firmwareInfoSpan, deviceInfo);
VerifyOrExit(attestationError == AttestationVerificationResult::kSuccess, attestationError = attestationError);
}

exit:
onCompletion->mCall(onCompletion->mContext, attestationError); // TODO: is this check getting done?
}

} // namespace Credentials
} // namespace chip
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
*
* Copyright (c) 2021 Project CHIP Authors
*
* 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.
*/
#pragma once

#include <credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h>

namespace chip {
namespace Credentials {

class PartialDACVerifier : public DefaultDACVerifier
{
public:
PartialDACVerifier() {}

void VerifyAttestationInformation(const DeviceAttestationVerifier::AttestationInfo & info,
Callback::Callback<OnAttestationInformationVerification> * onCompletion) override;

protected:
};

} // namespace Credentials
} // namespace chip

0 comments on commit e3a401d

Please sign in to comment.