From b06bd8c6b627c2864c250dacd0e3d2e2e60c0dbc Mon Sep 17 00:00:00 2001 From: Anush Nadathur Date: Wed, 6 Apr 2022 17:29:16 -0700 Subject: [PATCH] Added mechanism to override device attestation failure based on client/user (#17028) * Added mechanism to override device attestation failure based on client/user input Fixes #16681 Change overview Added delegates that can be optionally set by the client of the SDK. When the device attestation delegate is set and when a DA failure is encountered during commissioning, the delegate is invoked letting the client decide on the behavior to either ignore the error and continue commissioning or fail the commissioning. The arm failsafe timer can also be adjusted by the client to handle any delays due to user input. Testing Flashed an M5 with the changes. Used the iOS CHIPTool with the changes and verified no regression in current commissioning process when the delegate is either setup or not. Modified the kTestPaaRoots array in DefaultDeviceAttestationVerifier.cpp to be empty causing DA failure. Verified the dialog is presented to the user and that commissioning succeeds with error is ignored and fails when the user declines to proceed. * Restyled by whitespace * Restyled by clang-format * Restyled by gn * Fixed linker error for chip-tool-darwin * Restyled by gn * Changed CHIPDeviceAttestationDelegate delegate call to match Obj-C API conventions * Restyled by clang-format * Switched to passing DeviceProxy* in DA delegate methods * Restyled by clang-format * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Restyled by whitespace * Restyled by clang-format Co-authored-by: Restyled.io Co-authored-by: Boris Zbarsky --- src/controller/AutoCommissioner.cpp | 5 + src/controller/AutoCommissioner.h | 1 + src/controller/CHIPDeviceController.cpp | 134 +++++++++++++++++- src/controller/CHIPDeviceController.h | 19 +++ src/controller/CommissioningDelegate.h | 12 ++ src/credentials/BUILD.gn | 2 + .../DeviceAttestationDelegate.h | 62 ++++++++ .../QRCode/QRCodeViewController.m | 55 +++++++ .../Framework/CHIP.xcodeproj/project.pbxproj | 12 ++ src/darwin/Framework/CHIP/BUILD.gn | 2 + src/darwin/Framework/CHIP/CHIP.h | 1 + .../CHIP/CHIPCommissioningParameters.h | 10 ++ .../CHIP/CHIPDeviceAttestationDelegate.h | 41 ++++++ .../CHIPDeviceAttestationDelegateBridge.h | 58 ++++++++ .../CHIPDeviceAttestationDelegateBridge.mm | 39 +++++ .../Framework/CHIP/CHIPDeviceController.h | 4 + .../Framework/CHIP/CHIPDeviceController.mm | 49 +++++++ 17 files changed, 503 insertions(+), 3 deletions(-) create mode 100644 src/credentials/attestation_verifier/DeviceAttestationDelegate.h create mode 100644 src/darwin/Framework/CHIP/CHIPDeviceAttestationDelegate.h create mode 100644 src/darwin/Framework/CHIP/CHIPDeviceAttestationDelegateBridge.h create mode 100644 src/darwin/Framework/CHIP/CHIPDeviceAttestationDelegateBridge.mm diff --git a/src/controller/AutoCommissioner.cpp b/src/controller/AutoCommissioner.cpp index fddd448dc2ab46..5629750df31624 100644 --- a/src/controller/AutoCommissioner.cpp +++ b/src/controller/AutoCommissioner.cpp @@ -101,6 +101,11 @@ CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParam return CHIP_NO_ERROR; } +const CommissioningParameters & AutoCommissioner::GetCommissioningParameters() const +{ + return mParams; +} + CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStage currentStage, CHIP_ERROR & lastErr) { auto nextStage = GetNextCommissioningStageInternal(currentStage, lastErr); diff --git a/src/controller/AutoCommissioner.h b/src/controller/AutoCommissioner.h index b15dc89dcf9dd6..2ec23ab30f0bdb 100644 --- a/src/controller/AutoCommissioner.h +++ b/src/controller/AutoCommissioner.h @@ -31,6 +31,7 @@ class AutoCommissioner : public CommissioningDelegate AutoCommissioner(); ~AutoCommissioner() override; CHIP_ERROR SetCommissioningParameters(const CommissioningParameters & params) override; + const CommissioningParameters & GetCommissioningParameters() const override; void SetOperationalCredentialsDelegate(OperationalCredentialsDelegate * operationalCredentialsDelegate) override; CHIP_ERROR StartCommissioning(DeviceCommissioner * commissioner, CommissioneeDeviceProxy * proxy) override; diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index 1d0191d0787adf..0818741b11c7af 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -804,6 +804,56 @@ CHIP_ERROR DeviceCommissioner::Commission(NodeId remoteDeviceId) return CHIP_NO_ERROR; } +CHIP_ERROR +DeviceCommissioner::ContinueCommissioningAfterDeviceAttestationFailure(DeviceProxy * device, + Credentials::AttestationVerificationResult attestationResult) +{ + MATTER_TRACE_EVENT_SCOPE("continueCommissioningDevice", "DeviceCommissioner"); + if (device == nullptr || device != mDeviceBeingCommissioned) + { + ChipLogError(Controller, "Invalid device for commissioning %p", device); + return CHIP_ERROR_INCORRECT_STATE; + } + CommissioneeDeviceProxy * commissioneeDevice = FindCommissioneeDevice(device->GetDeviceId()); + if (commissioneeDevice == nullptr || !commissioneeDevice->IsSecureConnected() || commissioneeDevice != mDeviceBeingCommissioned) + { + ChipLogError(Controller, "Invalid device for commissioning after attestation failure: 0x" ChipLogFormatX64, + ChipLogValueX64(commissioneeDevice->GetDeviceId())); + return CHIP_ERROR_INCORRECT_STATE; + } + + if (mCommissioningStage != CommissioningStage::kAttestationVerification) + { + ChipLogError(Controller, "Commissioning is not attestation verification phase"); + return CHIP_ERROR_INCORRECT_STATE; + } + + if (mDefaultCommissioner == nullptr) + { + ChipLogError(Controller, "No default commissioner is specified"); + return CHIP_ERROR_INCORRECT_STATE; + } + + ChipLogProgress(Controller, "Continuing commissioning after attestation failure for device ID 0x" ChipLogFormatX64, + ChipLogValueX64(commissioneeDevice->GetDeviceId())); + + if (attestationResult != AttestationVerificationResult::kSuccess) + { + ChipLogError(Controller, "Client selected error: %u for failed 'Attestation Information' for device", + to_underlying(attestationResult)); + + CommissioningDelegate::CommissioningReport report; + report.Set(attestationResult); + CommissioningStageComplete(CHIP_ERROR_INTERNAL, report); + } + else + { + ChipLogProgress(Controller, "Overriding attestation failure per client and continuing commissioning"); + CommissioningStageComplete(CHIP_NO_ERROR); + } + return CHIP_NO_ERROR; +} + CHIP_ERROR DeviceCommissioner::GetAttestationChallenge(ByteSpan & attestationChallenge) { Optional secureSessionHandle; @@ -992,12 +1042,89 @@ void DeviceCommissioner::OnDeviceAttestationInformationVerification(void * conte static_cast(result)); // Go look at AttestationVerificationResult enum in src/credentials/attestation_verifier/DeviceAttestationVerifier.h to // understand the errors. - commissioner->CommissioningStageComplete(CHIP_ERROR_INTERNAL, report); + + auto & params = commissioner->mDefaultCommissioner->GetCommissioningParameters(); + Credentials::DeviceAttestationDelegate * deviceAttestationDelegate = params.GetDeviceAttestationDelegate(); + + // If a device attestation status delegate is installed, delegate handling of failure to the client and let them + // decide on whether to proceed further or not. + if (deviceAttestationDelegate) + { + commissioner->ExtendArmFailSafeForFailedDeviceAttestation(result); + } + else + { + commissioner->CommissioningStageComplete(CHIP_ERROR_INTERNAL, report); + } + } + else + { + ChipLogProgress(Controller, "Successfully validated 'Attestation Information' command received from the device."); + commissioner->CommissioningStageComplete(CHIP_NO_ERROR); + } +} + +void DeviceCommissioner::OnArmFailSafeExtendedForFailedDeviceAttestation( + void * context, const GeneralCommissioning::Commands::ArmFailSafeResponse::DecodableType & data) +{ + // If this function starts using "data", need to fix ExtendArmFailSafeForFailedDeviceAttestation accordingly. + DeviceCommissioner * commissioner = static_cast(context); + + if (!commissioner->mDeviceBeingCommissioned) + { return; } - ChipLogProgress(Controller, "Successfully validated 'Attestation Information' command received from the device."); - commissioner->CommissioningStageComplete(CHIP_NO_ERROR); + auto & params = commissioner->mDefaultCommissioner->GetCommissioningParameters(); + Credentials::DeviceAttestationDelegate * deviceAttestationDelegate = params.GetDeviceAttestationDelegate(); + if (deviceAttestationDelegate) + { + ChipLogProgress(Controller, "Device attestation failed, delegating error handling to client"); + deviceAttestationDelegate->OnDeviceAttestationFailed(commissioner, commissioner->mDeviceBeingCommissioned, + commissioner->mAttestationResult); + } + else + { + ChipLogProgress(Controller, "Device attestation failed and no delegate set, failing commissioning"); + CommissioningDelegate::CommissioningReport report; + report.Set(commissioner->mAttestationResult); + commissioner->CommissioningStageComplete(CHIP_ERROR_INTERNAL, report); + } +} + +void DeviceCommissioner::OnFailedToExtendedArmFailSafeFailedDeviceAttestation(void * context, CHIP_ERROR error) +{ + ChipLogProgress(Controller, "Failed to extend fail-safe timer to handle attestation failure %s", chip::ErrorStr(error)); + DeviceCommissioner * commissioner = static_cast(context); + + CommissioningDelegate::CommissioningReport report; + report.Set(commissioner->mAttestationResult); + commissioner->CommissioningStageComplete(CHIP_ERROR_INTERNAL, report); +} + +void DeviceCommissioner::ExtendArmFailSafeForFailedDeviceAttestation(AttestationVerificationResult result) +{ + mAttestationResult = result; + + auto & params = mDefaultCommissioner->GetCommissioningParameters(); + Credentials::DeviceAttestationDelegate * deviceAttestationDelegate = params.GetDeviceAttestationDelegate(); + auto expiryLengthSeconds = deviceAttestationDelegate->FailSafeExpiryTimeoutSecs(); + if (expiryLengthSeconds.HasValue()) + { + GeneralCommissioning::Commands::ArmFailSafe::Type request; + request.expiryLengthSeconds = expiryLengthSeconds.Value(); + request.breadcrumb = mCommissioningStage; + ChipLogProgress(Controller, "Changing fail-safe timer to %u seconds to handle DA failure", request.expiryLengthSeconds); + SendCommand(mDeviceBeingCommissioned, request, OnArmFailSafeExtendedForFailedDeviceAttestation, + OnFailedToExtendedArmFailSafeFailedDeviceAttestation); + } + else + { + ChipLogProgress(Controller, "Proceeding without changing fail-safe timer value as delegate has not set it"); + // Callee does not use data argument. + const GeneralCommissioning::Commands::ArmFailSafeResponse::DecodableType data; + OnArmFailSafeExtendedForFailedDeviceAttestation(this, data); + } } CHIP_ERROR DeviceCommissioner::ValidateAttestationInfo(const Credentials::DeviceAttestationVerifier::AttestationInfo & info) @@ -1684,6 +1811,7 @@ void DeviceCommissioner::OnDone() report.Set(info); CommissioningStageComplete(return_err, report); } + void DeviceCommissioner::OnArmFailSafe(void * context, const GeneralCommissioning::Commands::ArmFailSafeResponse::DecodableType & data) { diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h index 4d163501718ae5..306dc050998ae2 100644 --- a/src/controller/CHIPDeviceController.h +++ b/src/controller/CHIPDeviceController.h @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -414,6 +415,19 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, CHIP_ERROR Commission(NodeId remoteDeviceId, CommissioningParameters & params); CHIP_ERROR Commission(NodeId remoteDeviceId); + /** + * @brief + * This function instructs the commissioner to proceed to the next stage of commissioning after + * attestation failure is reported to an installed attestation delegate. + * + * @param[in] device The device being commissioned. + * @param[in] attestationResult The attestation result to use instead of whatever the device + * attestation verifier came up with. May be a success or an error result. + */ + CHIP_ERROR + ContinueCommissioningAfterDeviceAttestationFailure(DeviceProxy * device, + Credentials::AttestationVerificationResult attestationResult); + CHIP_ERROR GetDeviceBeingCommissioned(NodeId deviceId, CommissioneeDeviceProxy ** device); /** @@ -615,6 +629,7 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, /* Callback when the previously sent CSR request results in failure */ static void OnCSRFailureResponse(void * context, CHIP_ERROR error); + void ExtendArmFailSafeForFailedDeviceAttestation(Credentials::AttestationVerificationResult result); static void OnCertificateChainFailureResponse(void * context, CHIP_ERROR error); static void OnCertificateChainResponse( void * context, const app::Clusters::OperationalCredentials::Commands::CertificateChainResponse::DecodableType & response); @@ -674,6 +689,9 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, const app::Clusters::GeneralCommissioning::Commands::ArmFailSafeResponse::DecodableType & data); static void OnDisarmFailsafeFailure(void * context, CHIP_ERROR error); void DisarmDone(); + static void OnArmFailSafeExtendedForFailedDeviceAttestation( + void * context, const chip::app::Clusters::GeneralCommissioning::Commands::ArmFailSafeResponse::DecodableType & data); + static void OnFailedToExtendedArmFailSafeFailedDeviceAttestation(void * context, CHIP_ERROR error); /** * @brief @@ -767,6 +785,7 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, Platform::UniquePtr mAttributeCache; Platform::UniquePtr mReadClient; + Credentials::AttestationVerificationResult mAttestationResult; }; } // namespace Controller diff --git a/src/controller/CommissioningDelegate.h b/src/controller/CommissioningDelegate.h index f9180976da885a..e15daaf28e5322 100644 --- a/src/controller/CommissioningDelegate.h +++ b/src/controller/CommissioningDelegate.h @@ -19,6 +19,7 @@ #pragma once #include #include +#include #include #include @@ -323,6 +324,14 @@ class CommissioningParameters } void SetCompletionStatus(const CompletionStatus & status) { completionStatus = status; } + CommissioningParameters & SetDeviceAttestationDelegate(Credentials::DeviceAttestationDelegate * deviceAttestationDelegate) + { + mDeviceAttestationDelegate = deviceAttestationDelegate; + return *this; + } + + Credentials::DeviceAttestationDelegate * GetDeviceAttestationDelegate() const { return mDeviceAttestationDelegate; } + private: // Items that can be set by the commissioner Optional mFailsafeTimerSeconds; @@ -347,6 +356,8 @@ class CommissioningParameters Optional mDefaultRegulatoryLocation; Optional mLocationCapability; CompletionStatus completionStatus; + Credentials::DeviceAttestationDelegate * mDeviceAttestationDelegate = + nullptr; // Delegate to handle device attestation failures during commissioning }; struct RequestedCertificate @@ -460,6 +471,7 @@ class CommissioningDelegate CommissioningStage stageCompleted; }; virtual CHIP_ERROR SetCommissioningParameters(const CommissioningParameters & params) = 0; + virtual const CommissioningParameters & GetCommissioningParameters() const = 0; virtual void SetOperationalCredentialsDelegate(OperationalCredentialsDelegate * operationalCredentialsDelegate) = 0; virtual CHIP_ERROR StartCommissioning(DeviceCommissioner * commissioner, CommissioneeDeviceProxy * proxy) = 0; virtual CHIP_ERROR CommissioningStepFinished(CHIP_ERROR err, CommissioningReport report) = 0; diff --git a/src/credentials/BUILD.gn b/src/credentials/BUILD.gn index 0d74130a061ec4..da4b584b3b2f15 100644 --- a/src/credentials/BUILD.gn +++ b/src/credentials/BUILD.gn @@ -37,6 +37,7 @@ static_library("credentials") { "GenerateChipX509Cert.cpp", "GroupDataProvider.h", "GroupDataProviderImpl.cpp", + "attestation_verifier/DeviceAttestationDelegate.h", "attestation_verifier/DeviceAttestationVerifier.cpp", "attestation_verifier/DeviceAttestationVerifier.h", "examples/DeviceAttestationCredsExample.cpp", @@ -85,6 +86,7 @@ static_library("default_attestation_verifier") { sources = [ "attestation_verifier/DefaultDeviceAttestationVerifier.cpp", "attestation_verifier/DefaultDeviceAttestationVerifier.h", + "attestation_verifier/DeviceAttestationDelegate.h", ] if (chip_device_platform == "esp32" || chip_device_platform == "nrfconnect") { diff --git a/src/credentials/attestation_verifier/DeviceAttestationDelegate.h b/src/credentials/attestation_verifier/DeviceAttestationDelegate.h new file mode 100644 index 00000000000000..e79024f89eeaa4 --- /dev/null +++ b/src/credentials/attestation_verifier/DeviceAttestationDelegate.h @@ -0,0 +1,62 @@ +/* + * + * Copyright (c) 2021 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. + */ + +#pragma once + +#include +#include + +namespace chip { + +class DeviceProxy; + +namespace Controller { +class DeviceCommissioner; +} // namespace Controller + +namespace Credentials { + +/// Callbacks for CHIP device attestation status +class DeviceAttestationDelegate +{ +public: + virtual ~DeviceAttestationDelegate() {} + + /** + * @brief + * If valid, value to set for the fail-safe timer before the delegate's OnDeviceAttestionFailed is invoked. + * + * @return Optional value for the fail-safe timer in seconds. + */ + virtual Optional FailSafeExpiryTimeoutSecs() const = 0; + + /** + * @brief + * This method is invoked when device attestation fails for a device that is being commissioned. The client + * handling the failure has the option to continue commissionning or fail the operation. + * + * @param deviceCommissioner The commissioner object that is commissioning the device + * @param device The proxy represent the device being commissioned + * @param attestationResult The failure code for the device attestation validation operation + */ + virtual void OnDeviceAttestationFailed(Controller::DeviceCommissioner * deviceCommissioner, DeviceProxy * device, + AttestationVerificationResult attestationResult) = 0; +}; + +} // namespace Credentials +} // namespace chip diff --git a/src/darwin/CHIPTool/CHIPTool/View Controllers/QRCode/QRCodeViewController.m b/src/darwin/CHIPTool/CHIPTool/View Controllers/QRCode/QRCodeViewController.m index ad87d2d4ce8fbd..2d7f0f8d074050 100644 --- a/src/darwin/CHIPTool/CHIPTool/View Controllers/QRCode/QRCodeViewController.m +++ b/src/darwin/CHIPTool/CHIPTool/View Controllers/QRCode/QRCodeViewController.m @@ -22,6 +22,7 @@ #import "DefaultsUtils.h" #import "DeviceSelector.h" #import +#import #import // system imports @@ -84,6 +85,14 @@ @interface QRCodeViewController () @property (strong, nonatomic) DeviceSelector * deviceList; @end +@interface CHIPToolDeviceAttestationDelegate : NSObject + +@property (weak, nonatomic) QRCodeViewController * viewController; + +- (instancetype)initWithViewController:(QRCodeViewController *)viewController; + +@end + @implementation QRCodeViewController { dispatch_queue_t _captureSessionQueue; } @@ -488,6 +497,8 @@ - (void)onPairingComplete:(NSError * _Nullable)error }); } else { CHIPCommissioningParameters * params = [[CHIPCommissioningParameters alloc] init]; + params.deviceAttestationDelegate = [[CHIPToolDeviceAttestationDelegate alloc] initWithViewController:self]; + params.failSafeExpiryTimeoutSecs = @600; NSError * error; if (![controller commissionDevice:deviceId commissioningParams:params error:&error]) { NSLog(@"Failed to commission Device %llu, with error %@", deviceId, error); @@ -654,6 +665,8 @@ - (void)commissionWithSSID:(NSString *)ssid password:(NSString *)password CHIPCommissioningParameters * params = [[CHIPCommissioningParameters alloc] init]; params.wifiSSID = [ssid dataUsingEncoding:NSUTF8StringEncoding]; params.wifiCredentials = [password dataUsingEncoding:NSUTF8StringEncoding]; + params.deviceAttestationDelegate = [[CHIPToolDeviceAttestationDelegate alloc] initWithViewController:self]; + params.failSafeExpiryTimeoutSecs = @600; uint64_t deviceId = CHIPGetNextAvailableDeviceID() - 1; @@ -1085,3 +1098,45 @@ - (NSString *)encodeStringTo64:(NSArray *)fromArray @synthesize description; @end + +@implementation CHIPToolDeviceAttestationDelegate + +- (instancetype)initWithViewController:(QRCodeViewController *)viewController +{ + if (self = [super init]) { + _viewController = viewController; + } + return self; +} + +- (void)deviceAttestation:(CHIPDeviceController *)controller failedForDevice:(void *)device error:(NSError * _Nonnull)error +{ + dispatch_async(dispatch_get_main_queue(), ^{ + UIAlertController * alertController = [UIAlertController + alertControllerWithTitle:@"Device Attestation" + message:@"Device Attestion failed for device under commissioning. Do you wish to continue pairing?" + preferredStyle:UIAlertControllerStyleAlert]; + + [alertController addAction:[UIAlertAction actionWithTitle:@"No" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + NSError * err; + [controller continueCommissioningDevice:device + ignoreAttestationFailure:NO + error:&err]; + }]]; + + [alertController addAction:[UIAlertAction actionWithTitle:@"Continue" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + NSError * err; + [controller continueCommissioningDevice:device + ignoreAttestationFailure:YES + error:&err]; + }]]; + + [self.viewController presentViewController:alertController animated:YES completion:nil]; + }); +} + +@end diff --git a/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj b/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj index 82b01f1596d9e2..0d0d40c36db939 100644 --- a/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj @@ -62,6 +62,9 @@ 5ACDDD7D27CD16D200EFD68A /* CHIPAttributeCacheContainer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5ACDDD7C27CD16D200EFD68A /* CHIPAttributeCacheContainer.mm */; }; 5ACDDD7E27CD3F3A00EFD68A /* CHIPAttributeCacheContainer_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5ACDDD7B27CD14AF00EFD68A /* CHIPAttributeCacheContainer_Internal.h */; }; 5AE6D4E427A99041001F2493 /* CHIPDeviceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5AE6D4E327A99041001F2493 /* CHIPDeviceTests.m */; }; + 88EBF8CE27FABDD500686BC1 /* CHIPDeviceAttestationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 88EBF8CB27FABDD500686BC1 /* CHIPDeviceAttestationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 88EBF8CF27FABDD500686BC1 /* CHIPDeviceAttestationDelegateBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 88EBF8CC27FABDD500686BC1 /* CHIPDeviceAttestationDelegateBridge.mm */; }; + 88EBF8D027FABDD500686BC1 /* CHIPDeviceAttestationDelegateBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = 88EBF8CD27FABDD500686BC1 /* CHIPDeviceAttestationDelegateBridge.h */; }; 991DC0842475F45400C13860 /* CHIPDeviceController.h in Headers */ = {isa = PBXBuildFile; fileRef = 991DC0822475F45400C13860 /* CHIPDeviceController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 991DC0892475F47D00C13860 /* CHIPDeviceController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 991DC0872475F47D00C13860 /* CHIPDeviceController.mm */; }; 991DC08B247704DC00C13860 /* CHIPLogging.h in Headers */ = {isa = PBXBuildFile; fileRef = 991DC08A247704DC00C13860 /* CHIPLogging.h */; }; @@ -157,6 +160,9 @@ 5ACDDD7B27CD14AF00EFD68A /* CHIPAttributeCacheContainer_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CHIPAttributeCacheContainer_Internal.h; sourceTree = ""; }; 5ACDDD7C27CD16D200EFD68A /* CHIPAttributeCacheContainer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CHIPAttributeCacheContainer.mm; sourceTree = ""; }; 5AE6D4E327A99041001F2493 /* CHIPDeviceTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CHIPDeviceTests.m; sourceTree = ""; }; + 88EBF8CB27FABDD500686BC1 /* CHIPDeviceAttestationDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHIPDeviceAttestationDelegate.h; sourceTree = ""; }; + 88EBF8CC27FABDD500686BC1 /* CHIPDeviceAttestationDelegateBridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CHIPDeviceAttestationDelegateBridge.mm; sourceTree = ""; }; + 88EBF8CD27FABDD500686BC1 /* CHIPDeviceAttestationDelegateBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHIPDeviceAttestationDelegateBridge.h; sourceTree = ""; }; 991DC0822475F45400C13860 /* CHIPDeviceController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CHIPDeviceController.h; sourceTree = ""; }; 991DC0872475F47D00C13860 /* CHIPDeviceController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CHIPDeviceController.mm; sourceTree = ""; }; 991DC08A247704DC00C13860 /* CHIPLogging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CHIPLogging.h; sourceTree = ""; }; @@ -265,6 +271,9 @@ children = ( 27A53C1527FBC6920053F131 /* CHIPAttestationTrustStoreBridge.h */, 27A53C1627FBC6920053F131 /* CHIPAttestationTrustStoreBridge.mm */, + 88EBF8CB27FABDD500686BC1 /* CHIPDeviceAttestationDelegate.h */, + 88EBF8CD27FABDD500686BC1 /* CHIPDeviceAttestationDelegateBridge.h */, + 88EBF8CC27FABDD500686BC1 /* CHIPDeviceAttestationDelegateBridge.mm */, 513DDB852761F69300DAA01A /* CHIPAttributeTLVValueDecoder_Internal.h */, 1ED276E326C5832500547A89 /* CHIPCluster.h */, 1ED276E126C5812A00547A89 /* CHIPCluster.mm */, @@ -370,6 +379,7 @@ B289D4212639C0D300D4E314 /* CHIPOnboardingPayloadParser.h in Headers */, 513DDB862761F69300DAA01A /* CHIPAttributeTLVValueDecoder_Internal.h in Headers */, 2CB7163F252F731E0026E2BB /* CHIPDevicePairingDelegate.h in Headers */, + 88EBF8CE27FABDD500686BC1 /* CHIPDeviceAttestationDelegate.h in Headers */, 2C222AD0255C620600E446B9 /* CHIPDevice.h in Headers */, 991DC0842475F45400C13860 /* CHIPDeviceController.h in Headers */, B2E0D7B2245B0B5C003C5B48 /* CHIPManualSetupPayloadParser.h in Headers */, @@ -379,6 +389,7 @@ 9956064426420367000C28DE /* CHIPSetupPayload_Internal.h in Headers */, 27A53C1727FBC6920053F131 /* CHIPAttestationTrustStoreBridge.h in Headers */, 5A830D6C27CFCF590053B85D /* CHIPDeviceControllerOverXPC_Internal.h in Headers */, + 88EBF8D027FABDD500686BC1 /* CHIPDeviceAttestationDelegateBridge.h in Headers */, 5A60370827EA1FF60020DB79 /* CHIPAttributeCacheContainer+XPC.h in Headers */, 5ACDDD7E27CD3F3A00EFD68A /* CHIPAttributeCacheContainer_Internal.h in Headers */, 998F286D26D55E10001846C6 /* CHIPKeypair.h in Headers */, @@ -549,6 +560,7 @@ 2C1B027A2641DB4E00780EF1 /* CHIPOperationalCredentialsDelegate.mm in Sources */, B2E0D7B9245B0B5C003C5B48 /* CHIPSetupPayload.mm in Sources */, B2E0D7B6245B0B5C003C5B48 /* CHIPManualSetupPayloadParser.mm in Sources */, + 88EBF8CF27FABDD500686BC1 /* CHIPDeviceAttestationDelegateBridge.mm in Sources */, 5A6FEC9827B5C6AF00F25F42 /* CHIPDeviceOverXPC.m in Sources */, 51431AF927D2973E008A7943 /* CHIPIMDispatch.mm in Sources */, 51431AFB27D29CA4008A7943 /* ota-provider.cpp in Sources */, diff --git a/src/darwin/Framework/CHIP/BUILD.gn b/src/darwin/Framework/CHIP/BUILD.gn index 733d219fa2a2e2..12fde696913906 100644 --- a/src/darwin/Framework/CHIP/BUILD.gn +++ b/src/darwin/Framework/CHIP/BUILD.gn @@ -36,6 +36,8 @@ static_library("framework") { "CHIPControllerAccessControl.mm", "CHIPDevice.h", "CHIPDevice.mm", + "CHIPDeviceAttestationDelegateBridge.h", + "CHIPDeviceAttestationDelegateBridge.mm", "CHIPDeviceConnectionBridge.mm", "CHIPDeviceController.h", "CHIPDeviceController.mm", diff --git a/src/darwin/Framework/CHIP/CHIP.h b/src/darwin/Framework/CHIP/CHIP.h index 684334c86cc2a9..cacaa53d59cf9f 100644 --- a/src/darwin/Framework/CHIP/CHIP.h +++ b/src/darwin/Framework/CHIP/CHIP.h @@ -22,6 +22,7 @@ #import #import #import +#import #import #import #import diff --git a/src/darwin/Framework/CHIP/CHIPCommissioningParameters.h b/src/darwin/Framework/CHIP/CHIPCommissioningParameters.h index 5a2e65da00d13b..d1181ba9f6b3e2 100644 --- a/src/darwin/Framework/CHIP/CHIPCommissioningParameters.h +++ b/src/darwin/Framework/CHIP/CHIPCommissioningParameters.h @@ -19,6 +19,8 @@ NS_ASSUME_NONNULL_BEGIN +@protocol CHIPDeviceAttestationDelegate; + /** * The class definition for the CHIPCommissioningParameters * @@ -45,6 +47,14 @@ NS_ASSUME_NONNULL_BEGIN * The Thread operational dataset */ @property (nonatomic, nullable, copy, readwrite) NSData * threadOperationalDataset; +/** + * The Device Attestation status delegate + */ +@property (nonatomic, nullable, strong, readwrite) id deviceAttestationDelegate; +/** + * The timeout in secs to set for fail-safe when attestation fails + */ +@property (nonatomic, nullable, copy, readwrite) NSNumber * failSafeExpiryTimeoutSecs; @end diff --git a/src/darwin/Framework/CHIP/CHIPDeviceAttestationDelegate.h b/src/darwin/Framework/CHIP/CHIPDeviceAttestationDelegate.h new file mode 100644 index 00000000000000..279e0dc6de035d --- /dev/null +++ b/src/darwin/Framework/CHIP/CHIPDeviceAttestationDelegate.h @@ -0,0 +1,41 @@ +/** + * + * Copyright (c) 2020 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. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class CHIPDeviceController; + +/** + * The protocol definition for the CHIPDeviceAttestationDelegate + * + * All delegate methods will be called on the callers queue. + */ +@protocol CHIPDeviceAttestationDelegate +/** + * Notify the delegate when device attestation fails + * + * @param controller Controller corresponding to the commissioning process + * @param device Handle of device being commissioned + * @param error NSError representing the error code for the failure + */ +- (void)deviceAttestation:(CHIPDeviceController *)controller failedForDevice:(void *)device error:(NSError * _Nonnull)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/CHIPDeviceAttestationDelegateBridge.h b/src/darwin/Framework/CHIP/CHIPDeviceAttestationDelegateBridge.h new file mode 100644 index 00000000000000..b7712ed25949d3 --- /dev/null +++ b/src/darwin/Framework/CHIP/CHIPDeviceAttestationDelegateBridge.h @@ -0,0 +1,58 @@ +/** + * + * Copyright (c) 2020 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 +#include + +#include +#include + +@class CHIPDeviceController; + +NS_ASSUME_NONNULL_BEGIN + +class CHIPDeviceAttestationDelegateBridge : public chip::Credentials::DeviceAttestationDelegate { +public: + CHIPDeviceAttestationDelegateBridge(CHIPDeviceController * deviceController, + id deviceAttestationDelegate, dispatch_queue_t queue, + chip::Optional expiryTimeoutSecs) + : mResult(chip::Credentials::AttestationVerificationResult::kSuccess) + , mDeviceController(deviceController) + , mDeviceAttestationDelegate(deviceAttestationDelegate) + , mQueue(queue) + , mExpiryTimeoutSecs(expiryTimeoutSecs) + { + } + + ~CHIPDeviceAttestationDelegateBridge() {} + + chip::Optional FailSafeExpiryTimeoutSecs() const override { return mExpiryTimeoutSecs; } + + void OnDeviceAttestationFailed(chip::Controller::DeviceCommissioner * deviceCommissioner, chip::DeviceProxy * device, + chip::Credentials::AttestationVerificationResult attestationResult) override; + + chip::Credentials::AttestationVerificationResult attestationVerificationResult() const { return mResult; } + +private: + chip::Credentials::AttestationVerificationResult mResult; + CHIPDeviceController * __weak mDeviceController; + id mDeviceAttestationDelegate; + dispatch_queue_t mQueue; + chip::Optional mExpiryTimeoutSecs; +}; + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/CHIPDeviceAttestationDelegateBridge.mm b/src/darwin/Framework/CHIP/CHIPDeviceAttestationDelegateBridge.mm new file mode 100644 index 00000000000000..5a4bb0c65f4954 --- /dev/null +++ b/src/darwin/Framework/CHIP/CHIPDeviceAttestationDelegateBridge.mm @@ -0,0 +1,39 @@ +/** + * + * Copyright (c) 2020 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. + */ + +#import "CHIPDeviceAttestationDelegateBridge.h" +#import "CHIPError_Internal.h" + +void CHIPDeviceAttestationDelegateBridge::OnDeviceAttestationFailed(chip::Controller::DeviceCommissioner * deviceCommissioner, + chip::DeviceProxy * device, chip::Credentials::AttestationVerificationResult attestationResult) +{ + dispatch_async(mQueue, ^{ + NSLog(@"CHIPDeviceAttestationDelegateBridge::OnDeviceAttestionFailed failed with result: %hu", attestationResult); + + mResult = attestationResult; + + id strongDelegate = mDeviceAttestationDelegate; + if ([strongDelegate respondsToSelector:@selector(deviceAttestation:failedForDevice:error:)]) { + + CHIPDeviceController * strongController = mDeviceController; + if (strongController) { + NSError * error = [CHIPError errorForCHIPErrorCode:CHIP_ERROR_INTEGRITY_CHECK_FAILED]; + [strongDelegate deviceAttestation:mDeviceController failedForDevice:device error:error]; + } + } + }); +} diff --git a/src/darwin/Framework/CHIP/CHIPDeviceController.h b/src/darwin/Framework/CHIP/CHIPDeviceController.h index cbd9fb95a71f82..724db8569d44ee 100644 --- a/src/darwin/Framework/CHIP/CHIPDeviceController.h +++ b/src/darwin/Framework/CHIP/CHIPDeviceController.h @@ -54,6 +54,10 @@ typedef void (^CHIPDeviceConnectionCallback)(CHIPDevice * _Nullable device, NSEr commissioningParams:(CHIPCommissioningParameters *)commissioningParams error:(NSError * __autoreleasing *)error; +- (BOOL)continueCommissioningDevice:(void *)device + ignoreAttestationFailure:(BOOL)ignoreAttestationFailure + error:(NSError * __autoreleasing *)error; + - (void)setListenPort:(uint16_t)port; - (BOOL)stopDevicePairing:(uint64_t)deviceID error:(NSError * __autoreleasing *)error; - (void)updateDevice:(uint64_t)deviceID fabricId:(uint64_t)fabricId; diff --git a/src/darwin/Framework/CHIP/CHIPDeviceController.mm b/src/darwin/Framework/CHIP/CHIPDeviceController.mm index e9f6f410c37b80..d801ae1c41da1b 100644 --- a/src/darwin/Framework/CHIP/CHIPDeviceController.mm +++ b/src/darwin/Framework/CHIP/CHIPDeviceController.mm @@ -32,6 +32,7 @@ #import #import +#include "CHIPDeviceAttestationDelegateBridge.h" #import "CHIPDeviceConnectionBridge.h" #include @@ -84,6 +85,7 @@ @interface CHIPDeviceController () @property (readonly) CHIPAttestationTrustStoreBridge * attestationTrustStoreBridge; @property (readonly) CHIPOperationalCredentialsDelegate * operationalCredentialsDelegate; @property (readonly) CHIPP256KeypairBridge keypairBridge; +@property (readonly) CHIPDeviceAttestationDelegateBridge * deviceAttestationDelegateBridge; @property (readonly) chip::NodeId localDeviceId; @property (readonly) uint16_t listenPort; @property (readonly) const char * kvsPath; @@ -477,6 +479,18 @@ - (BOOL)commissionDevice:(uint64_t)deviceId chip::Controller::WiFiCredentials wifiCreds(ssid, credentials); params.SetWiFiCredentials(wifiCreds); } + if (commissioningParams.deviceAttestationDelegate) { + [self clearDeviceAttestationDelegateBridge]; + + chip::Optional timeoutSecs; + if (commissioningParams.failSafeExpiryTimeoutSecs) { + timeoutSecs = chip::MakeOptional( + static_cast([commissioningParams.failSafeExpiryTimeoutSecs unsignedIntValue])); + } + _deviceAttestationDelegateBridge = new CHIPDeviceAttestationDelegateBridge( + self, commissioningParams.deviceAttestationDelegate, _chipWorkQueue, timeoutSecs); + params.SetDeviceAttestationDelegate(_deviceAttestationDelegateBridge); + } _operationalCredentialsDelegate->SetDeviceID(deviceId); errorCode = self.cppCommissioner->Commission(deviceId, params); @@ -486,6 +500,31 @@ - (BOOL)commissionDevice:(uint64_t)deviceId return success; } +- (BOOL)continueCommissioningDevice:(void *)device + ignoreAttestationFailure:(BOOL)ignoreAttestationFailure + error:(NSError * __autoreleasing *)error +{ + __block CHIP_ERROR errorCode = CHIP_ERROR_INCORRECT_STATE; + __block BOOL success = NO; + if (![self isRunning]) { + success = ![self checkForError:errorCode logMsg:kErrorNotRunning error:error]; + return success; + } + dispatch_sync(_chipWorkQueue, ^{ + if ([self isRunning]) { + auto lastAttestationResult = _deviceAttestationDelegateBridge + ? _deviceAttestationDelegateBridge->attestationVerificationResult() + : chip::Credentials::AttestationVerificationResult::kSuccess; + + chip::DeviceProxy * deviceProxy = static_cast(device); + errorCode = self.cppCommissioner->ContinueCommissioningAfterDeviceAttestationFailure(deviceProxy, + ignoreAttestationFailure ? chip::Credentials::AttestationVerificationResult::kSuccess : lastAttestationResult); + } + success = ![self checkForError:errorCode logMsg:kErrorPairDevice error:error]; + }); + return success; +} + - (BOOL)stopDevicePairing:(uint64_t)deviceID error:(NSError * __autoreleasing *)error { __block CHIP_ERROR errorCode = CHIP_ERROR_INCORRECT_STATE; @@ -705,9 +744,19 @@ - (BOOL)checkForInitError:(BOOL)condition logMsg:(NSString *)logMsg _groupStorageDelegate = nullptr; } + [self clearDeviceAttestationDelegateBridge]; + return YES; } +- (void)clearDeviceAttestationDelegateBridge +{ + if (_deviceAttestationDelegateBridge) { + delete _deviceAttestationDelegateBridge; + _deviceAttestationDelegateBridge = nullptr; + } +} + - (BOOL)checkForStartError:(BOOL)condition logMsg:(NSString *)logMsg { if (condition) {