From 488262f6937494f87e400b03307643acc6b827dc Mon Sep 17 00:00:00 2001 From: chrisdecenzo <61757564+chrisdecenzo@users.noreply.github.com> Date: Mon, 15 Aug 2022 19:53:18 -0700 Subject: [PATCH] Allow partial validation of DAC and CD (for external cloud or custom 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 --- src/controller/CHIPDeviceController.h | 5 + .../java/AndroidDeviceControllerWrapper.h | 5 + .../AndroidOperationalCredentialsIssuer.cpp | 14 +- .../java/CHIPDeviceController-JNI.cpp | 11 ++ src/credentials/BUILD.gn | 2 + .../DacOnlyPartialAttestationVerifier.cpp | 151 ++++++++++++++++++ .../DacOnlyPartialAttestationVerifier.h | 36 +++++ 7 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 src/credentials/attestation_verifier/DacOnlyPartialAttestationVerifier.cpp create mode 100644 src/credentials/attestation_verifier/DacOnlyPartialAttestationVerifier.h diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h index 9e3f77cc90bb46..ad357842685c94 100644 --- a/src/controller/CHIPDeviceController.h +++ b/src/controller/CHIPDeviceController.h @@ -631,6 +631,11 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, CHIP_ERROR IssueNOCChain(const ByteSpan & NOCSRElements, NodeId nodeId, chip::Callback::Callback * callback); + void SetDeviceAttestationVerifier(Credentials::DeviceAttestationVerifier * deviceAttestationVerifier) + { + mDeviceAttestationVerifier = deviceAttestationVerifier; + } + private: DevicePairingDelegate * mPairingDelegate; diff --git a/src/controller/java/AndroidDeviceControllerWrapper.h b/src/controller/java/AndroidDeviceControllerWrapper.h index 77f9a6d735cc25..2890d5f6e976ba 100644 --- a/src/controller/java/AndroidDeviceControllerWrapper.h +++ b/src/controller/java/AndroidDeviceControllerWrapper.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -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(); @@ -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)) {} diff --git a/src/controller/java/AndroidOperationalCredentialsIssuer.cpp b/src/controller/java/AndroidOperationalCredentialsIssuer.cpp index aa54ae1d4c1336..249921ae17c923 100644 --- a/src/controller/java/AndroidOperationalCredentialsIssuer.cpp +++ b/src/controller/java/AndroidOperationalCredentialsIssuer.cpp @@ -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 * onCompletion) @@ -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; @@ -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"); diff --git a/src/controller/java/CHIPDeviceController-JNI.cpp b/src/controller/java/CHIPDeviceController-JNI.cpp index 724c22b41a1706..1cd4972db04a37 100644 --- a/src/controller/java/CHIPDeviceController-JNI.cpp +++ b/src/controller/java/CHIPDeviceController-JNI.cpp @@ -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) diff --git a/src/credentials/BUILD.gn b/src/credentials/BUILD.gn index ad809326b892d2..7e33874fe22402 100644 --- a/src/credentials/BUILD.gn +++ b/src/credentials/BUILD.gn @@ -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", diff --git a/src/credentials/attestation_verifier/DacOnlyPartialAttestationVerifier.cpp b/src/credentials/attestation_verifier/DacOnlyPartialAttestationVerifier.cpp new file mode 100644 index 00000000000000..e37155886d61ce --- /dev/null +++ b/src/credentials/attestation_verifier/DacOnlyPartialAttestationVerifier.cpp @@ -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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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 * 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 diff --git a/src/credentials/attestation_verifier/DacOnlyPartialAttestationVerifier.h b/src/credentials/attestation_verifier/DacOnlyPartialAttestationVerifier.h new file mode 100644 index 00000000000000..acd28f66d71054 --- /dev/null +++ b/src/credentials/attestation_verifier/DacOnlyPartialAttestationVerifier.h @@ -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 + +namespace chip { +namespace Credentials { + +class PartialDACVerifier : public DefaultDACVerifier +{ +public: + PartialDACVerifier() {} + + void VerifyAttestationInformation(const DeviceAttestationVerifier::AttestationInfo & info, + Callback::Callback * onCompletion) override; + +protected: +}; + +} // namespace Credentials +} // namespace chip