Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow partial validation of DAC and CD (for external cloud or custom app based validation) #21725

Merged
merged 9 commits into from
Aug 16, 2022
5 changes: 5 additions & 0 deletions src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,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/CloudDeviceAttestationVerifier.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::CloudDACVerifier * GetCloudDACVerifier() { return &mCloudDACVerifier; }

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::CloudDACVerifier mCloudDACVerifier;

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?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this TODO intended to remain?

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no cloud involved. Suggest removing the word cloud

wrapper->Controller()->SetDeviceAttestationVerifier(wrapper->GetCloudDACVerifier());
woody-apple marked this conversation as resolved.
Show resolved Hide resolved
}
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/CloudDeviceAttestationVerifier.cpp",
"attestation_verifier/CloudDeviceAttestationVerifier.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,169 @@
/*
*
* 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 "CloudDeviceAttestationVerifier.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;

AttestationVerificationResult CloudDACVerifier::CheckPAA(const DeviceAttestationVerifier::AttestationInfo & info,
DeviceInfoForAttestation & deviceInfo,
Platform::ScopedMemoryBuffer<uint8_t> & paaCert,
MutableByteSpan & paaDerBuffer, AttestationCertVidPid & paaVidPid,
AttestationCertVidPid & paiVidPid)
{
AttestationVerificationResult attestationError = AttestationVerificationResult::kSuccess;
MutableByteSpan akid(deviceInfo.paaSKID);

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

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

exit:
return attestationError;
}

AttestationVerificationResult CloudDACVerifier::CheckCertTimes(const DeviceAttestationVerifier::AttestationInfo & info,
MutableByteSpan & paaDerBuffer)
{
AttestationVerificationResult attestationError = AttestationVerificationResult::kSuccess;

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

VerifyOrExit(IsCertificateValidAtIssuance(info.dacDerBuffer, info.paiDerBuffer) == CHIP_NO_ERROR,
attestationError = AttestationVerificationResult::kPaiExpired);

ChipLogProgress(Support, "CloudDACVerifier::CheckCertTimes skipping PAA expiry check - PAARootStore disabled");

exit:
return attestationError;
}

AttestationVerificationResult CloudDACVerifier::CheckCertChain(const DeviceAttestationVerifier::AttestationInfo & info,
MutableByteSpan & paaDerBuffer)
{
ChipLogProgress(Support, "CloudDACVerifier::CheckCertChain skipping cert chain check - PAARootStore disabled");

return AttestationVerificationResult::kSuccess;
}

AttestationVerificationResult
CloudDACVerifier::CheckCertDeclaration(const DeviceAttestationVerifier::AttestationInfo & info, MutableByteSpan & paaDerBuffer,
AttestationCertVidPid & dacVidPid, AttestationCertVidPid & paiVidPid,
AttestationCertVidPid & paaVidPid, DeviceInfoForAttestation & deviceInfo)
{
AttestationVerificationResult attestationError = AttestationVerificationResult::kSuccess;

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);

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, "CloudDACVerifier::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:
return attestationError;
}

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

Platform::ScopedMemoryBuffer<uint8_t> paaCert;
MutableByteSpan paaDerBuffer;
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);

attestationError = CheckDacPaiVidPids(info, dacVidPid, paiVidPid);
VerifyOrExit(attestationError == AttestationVerificationResult::kSuccess, attestationError = attestationError);

attestationError = CheckAttestationSignature(info);
VerifyOrExit(attestationError == AttestationVerificationResult::kSuccess, attestationError = attestationError);

attestationError = CheckPAA(info, deviceInfo, paaCert, paaDerBuffer, paaVidPid, paiVidPid);
VerifyOrExit(attestationError == AttestationVerificationResult::kSuccess, attestationError = attestationError);

attestationError = CheckCertTimes(info, paaDerBuffer);
VerifyOrExit(attestationError == AttestationVerificationResult::kSuccess, attestationError = attestationError);

attestationError = CheckCertChain(info, paaDerBuffer);
VerifyOrExit(attestationError == AttestationVerificationResult::kSuccess, attestationError = attestationError);

attestationError = CheckCertDeclaration(info, paaDerBuffer, dacVidPid, paiVidPid, paaVidPid, 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,52 @@
/*
*
* 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 CloudDACVerifier : public DefaultDACVerifier
{
public:
CloudDACVerifier() {}

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

protected:
AttestationVerificationResult CheckPAA(const DeviceAttestationVerifier::AttestationInfo & info,
DeviceInfoForAttestation & deviceInfo, Platform::ScopedMemoryBuffer<uint8_t> & paaCert,
MutableByteSpan & paaDerBuffer, Crypto::AttestationCertVidPid & paaVidPid,
Crypto::AttestationCertVidPid & paiVidPid);

AttestationVerificationResult CheckCertTimes(const DeviceAttestationVerifier::AttestationInfo & info,
MutableByteSpan & paaDerBuffer);

AttestationVerificationResult CheckCertChain(const DeviceAttestationVerifier::AttestationInfo & info,
MutableByteSpan & paaDerBuffer);

AttestationVerificationResult CheckCertDeclaration(const DeviceAttestationVerifier::AttestationInfo & info,
MutableByteSpan & paaDerBuffer, Crypto::AttestationCertVidPid & dacVidPid,
Crypto::AttestationCertVidPid & paiVidPid,
Crypto::AttestationCertVidPid & paaVidPid,
DeviceInfoForAttestation & deviceInfo);
};

} // namespace Credentials
} // namespace chip
Loading