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