From b0e065535f17cfaa821fab13d47d808156ad115a Mon Sep 17 00:00:00 2001 From: Jens Utbult Date: Tue, 12 Dec 2023 09:59:18 +0100 Subject: [PATCH 1/5] Better handling of YubiOTP when using the SmartCard extension together with a USB-C key. --- Authenticator.xcodeproj/project.pbxproj | 4 + .../Model/TokenRequestViewModel.swift | 71 ++++++++++++ Authenticator/UI/Base.lproj/Main.storyboard | 105 +++++++++++++++++- .../TokenRequestViewController.swift | 44 +++++--- .../TokenRequestYubiOTPViewController.swift | 42 +++++++ 5 files changed, 246 insertions(+), 20 deletions(-) create mode 100644 Authenticator/UI/TokenSession/TokenRequestYubiOTPViewController.swift diff --git a/Authenticator.xcodeproj/project.pbxproj b/Authenticator.xcodeproj/project.pbxproj index ad03ca84..6f37c280 100644 --- a/Authenticator.xcodeproj/project.pbxproj +++ b/Authenticator.xcodeproj/project.pbxproj @@ -81,6 +81,7 @@ B40327762847AE0A00DF4DB0 /* Licensing.md in Resources */ = {isa = PBXBuildFile; fileRef = B40327752847AE0A00DF4DB0 /* Licensing.md */; }; B40D61A02AE7F37900467AE9 /* DisableOTPView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B40D619F2AE7F37900467AE9 /* DisableOTPView.swift */; }; B40D61A22AE7F89500467AE9 /* DisableOTPModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B40D61A12AE7F89500467AE9 /* DisableOTPModel.swift */; }; + B40F44452B27033A000D5E02 /* TokenRequestYubiOTPViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B40F44442B27033A000D5E02 /* TokenRequestYubiOTPViewController.swift */; }; B411242F29D423A300D58001 /* ListStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B411242E29D423A300D58001 /* ListStatusView.swift */; }; B432B1BF28B65B8600A7182F /* YubiKit in Frameworks */ = {isa = PBXBuildFile; productRef = B432B1BE28B65B8600A7182F /* YubiKit */; }; B452EC1F2A1E4F460045E5D9 /* YubiOtpRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B452EC1E2A1E4F460045E5D9 /* YubiOtpRowView.swift */; }; @@ -224,6 +225,7 @@ B40327752847AE0A00DF4DB0 /* Licensing.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Licensing.md; sourceTree = ""; }; B40D619F2AE7F37900467AE9 /* DisableOTPView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableOTPView.swift; sourceTree = ""; }; B40D61A12AE7F89500467AE9 /* DisableOTPModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableOTPModel.swift; sourceTree = ""; }; + B40F44442B27033A000D5E02 /* TokenRequestYubiOTPViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenRequestYubiOTPViewController.swift; sourceTree = ""; }; B411242E29D423A300D58001 /* ListStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListStatusView.swift; sourceTree = ""; }; B452EC1E2A1E4F460045E5D9 /* YubiOtpRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YubiOtpRowView.swift; sourceTree = ""; }; B452EC3C2A264A620045E5D9 /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = ""; }; @@ -307,6 +309,7 @@ children = ( 5156D05C265D2602007A94F8 /* TokenRequestViewController.swift */, B4FE90D32A443D8400B59170 /* TokenRequestWrapper.swift */, + B40F44442B27033A000D5E02 /* TokenRequestYubiOTPViewController.swift */, ); path = TokenSession; sourceTree = ""; @@ -720,6 +723,7 @@ A525965B23A45501006AA3C0 /* UIImageAdditions.swift in Sources */, 51A162862678A1F100C3FA1E /* OATHConfigurationController.swift in Sources */, 515542622649C88900B19C59 /* PasswordConfigurationViewModel.swift in Sources */, + B40F44452B27033A000D5E02 /* TokenRequestYubiOTPViewController.swift in Sources */, B4C93E60299D156C00C2A8B8 /* ErrorAlertView.swift in Sources */, A591411D23830EB800CCCF67 /* UIApplicationExtension.swift in Sources */, 81FA3C34231AF2D8009C22AB /* AdvancedSettingsViewController.swift in Sources */, diff --git a/Authenticator/Model/TokenRequestViewModel.swift b/Authenticator/Model/TokenRequestViewModel.swift index 9881280d..a86e3031 100644 --- a/Authenticator/Model/TokenRequestViewModel.swift +++ b/Authenticator/Model/TokenRequestViewModel.swift @@ -178,6 +178,77 @@ class TokenRequestViewModel: NSObject { } } + +extension TokenRequestViewModel { + + func isYubiOTPEnabledOverUSBC(completion: @escaping (Bool) -> Void) { + print(#function) + connection.smartCardConnection { connection in + print("got \(connection)") + connection?.managementSession { session, error in + print("got \(session)") + guard let session else { return } + session.getDeviceInfo { deviceInfo, error in + print("got \(deviceInfo)") + guard let deviceInfo, let configuration = deviceInfo.configuration else { return } + guard !configuration.isEnabled(.OTP, overTransport: .USB) || SettingsConfig.isOTPOverUSBIgnored(deviceId: deviceInfo.serialNumber + 1) else { + print("yubiotp enabled") + completion(true) + return + } + print("yubiotp disabled") + completion(false) + } + } + } + } + + func disableOTP(completion: @escaping (Error?) -> Void) { + print(#function) + connection.smartCardConnection { connection in + connection?.managementSession { session, error in + print(session) + guard let session else { return } + session.getDeviceInfo { deviceInfo, error in + print(deviceInfo) + guard let deviceInfo, let configuration = deviceInfo.configuration else { return } + configuration.setEnabled(false, application: .OTP, overTransport: .USB) + session.write(configuration, reboot: true) { error in + print(error) + completion(error) + } + } + } + } + } + + func waitForKeyRemoval(completion: @escaping () -> Void) { + print(#function) + connection.didDisconnect { _, _ in + print("") + completion() + } + } + + func ignoreThisKey(handler: @escaping (Error?) -> Void) { + print(#function) + connection.smartCardConnection { connection in + print(connection) + connection?.managementSession { session, error in + print(session) + guard let session else { handler(error); return } + session.getDeviceInfo { deviceInfo, error in + print(deviceInfo) + guard let deviceInfo else { handler(error); return } + SettingsConfig.registerUSBCDeviceToIgnore(deviceId: deviceInfo.serialNumber) + handler(nil) + } + } + } + } + +} + @available(iOS 14.0, *) private extension YKFPIVSession { func slotForObjectId(_ objectId: String, completion: @escaping (YKFPIVSlot?, TokenRequestViewModel.TokenError?) -> Void) { diff --git a/Authenticator/UI/Base.lproj/Main.storyboard b/Authenticator/UI/Base.lproj/Main.storyboard index df39c8db..36a8ec8e 100644 --- a/Authenticator/UI/Base.lproj/Main.storyboard +++ b/Authenticator/UI/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -1197,7 +1197,7 @@ All rights reserved. - + @@ -1535,6 +1535,103 @@ All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1558,7 +1655,7 @@ All rights reserved. - + diff --git a/Authenticator/UI/TokenSession/TokenRequestViewController.swift b/Authenticator/UI/TokenSession/TokenRequestViewController.swift index 715e821d..e220edb3 100644 --- a/Authenticator/UI/TokenSession/TokenRequestViewController.swift +++ b/Authenticator/UI/TokenSession/TokenRequestViewController.swift @@ -68,22 +68,35 @@ class TokenRequestViewController: UIViewController, UITextFieldDelegate { viewModel = TokenRequestViewModel() passwordTextField.becomeFirstResponder() passwordTextField.delegate = self - viewModel?.isWiredKeyConnected { [weak self] connected in - if !connected && self?.orView.alpha == 1 { return } - UIView.animate(withDuration: 0.2) { - self?.orView.alpha = 0 - self?.nfcView.alpha = 0 - self?.accessoryLabel.alpha = 0 - } completion: { _ in + + viewModel?.isYubiOTPEnabledOverUSBC { yubiOTPEnabled in + if yubiOTPEnabled { + DispatchQueue.main.async { + let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) + let vc: TokenRequestYubiOTPViewController = storyboard.instantiateViewController(withIdentifier: "TokenRequestYubiOTPViewController") as! TokenRequestYubiOTPViewController + vc.viewModel = self.viewModel + vc.modalPresentationStyle = .fullScreen + self.present(vc, animated: true) + } + } + + self.viewModel?.isWiredKeyConnected { [weak self] connected in + if !connected && self?.orView.alpha == 1 { return } UIView.animate(withDuration: 0.2) { - self?.accessoryLabel.alpha = 1 - if connected { - self?.accessoryLabel.text = "Enter the PIN to access the certificate." - } else { - self?.accessoryLabel.text = self?.defaultAccessoryTest - if YubiKitDeviceCapabilities.supportsISO7816NFCTags { - self?.orView.alpha = 1 - self?.nfcView.alpha = 1 + self?.orView.alpha = 0 + self?.nfcView.alpha = 0 + self?.accessoryLabel.alpha = 0 + } completion: { _ in + UIView.animate(withDuration: 0.2) { + self?.accessoryLabel.alpha = 1 + if connected { + self?.accessoryLabel.text = "Enter the PIN to access the certificate." + } else { + self?.accessoryLabel.text = self?.defaultAccessoryTest + if YubiKitDeviceCapabilities.supportsISO7816NFCTags { + self?.orView.alpha = 1 + self?.nfcView.alpha = 1 + } } } } @@ -128,7 +141,6 @@ class TokenRequestViewController: UIViewController, UITextFieldDelegate { deinit { NotificationCenter.default.removeObserver(self) - print("Deinit TokenRequestViewController") } func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { diff --git a/Authenticator/UI/TokenSession/TokenRequestYubiOTPViewController.swift b/Authenticator/UI/TokenSession/TokenRequestYubiOTPViewController.swift new file mode 100644 index 00000000..095f3c44 --- /dev/null +++ b/Authenticator/UI/TokenSession/TokenRequestYubiOTPViewController.swift @@ -0,0 +1,42 @@ +// +// TokenRequestYubiOTP.swift +// Authenticator +// +// Created by Jens Utbult on 2023-12-11. +// Copyright © 2023 Yubico. All rights reserved. +// + +import Foundation + +@available(iOS 14.0, *) +class TokenRequestYubiOTPViewController: UIViewController { + + var viewModel: TokenRequestViewModel? + + @IBOutlet weak var optionsView: UIStackView! + @IBOutlet weak var completedView: UIStackView! + + @IBAction func disableOTP() { + viewModel?.disableOTP { error in + guard error == nil else { return } + UIView.animate(withDuration: 0.5) { + DispatchQueue.main.async { + self.optionsView.alpha = 0 + self.completedView.alpha = 1 + self.viewModel?.waitForKeyRemoval { + self.dismiss(animated: true) + } + } + } + } + } + + @IBAction func ignoreThisKey() { + viewModel?.ignoreThisKey { error in + guard error == nil else { return } + DispatchQueue.main.async { + self.dismiss(animated: true) + } + } + } +} From b05eb8bd8fa48474a0d886168e482850181811a7 Mon Sep 17 00:00:00 2001 From: Jens Utbult Date: Tue, 12 Dec 2023 15:15:07 +0100 Subject: [PATCH 2/5] Error handling and removed debug print. --- .../Model/TokenRequestViewModel.swift | 35 +++++-------------- .../TokenRequestYubiOTPViewController.swift | 20 ++++++++--- 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/Authenticator/Model/TokenRequestViewModel.swift b/Authenticator/Model/TokenRequestViewModel.swift index a86e3031..d411e292 100644 --- a/Authenticator/Model/TokenRequestViewModel.swift +++ b/Authenticator/Model/TokenRequestViewModel.swift @@ -182,21 +182,15 @@ class TokenRequestViewModel: NSObject { extension TokenRequestViewModel { func isYubiOTPEnabledOverUSBC(completion: @escaping (Bool) -> Void) { - print(#function) connection.smartCardConnection { connection in - print("got \(connection)") connection?.managementSession { session, error in - print("got \(session)") - guard let session else { return } + guard let session else { completion(false); return } session.getDeviceInfo { deviceInfo, error in - print("got \(deviceInfo)") - guard let deviceInfo, let configuration = deviceInfo.configuration else { return } - guard !configuration.isEnabled(.OTP, overTransport: .USB) || SettingsConfig.isOTPOverUSBIgnored(deviceId: deviceInfo.serialNumber + 1) else { - print("yubiotp enabled") + guard let deviceInfo, let configuration = deviceInfo.configuration else { completion(false); return } + guard !configuration.isEnabled(.OTP, overTransport: .USB) || SettingsConfig.isOTPOverUSBIgnored(deviceId: deviceInfo.serialNumber) else { completion(true) return } - print("yubiotp disabled") completion(false) } } @@ -204,17 +198,13 @@ extension TokenRequestViewModel { } func disableOTP(completion: @escaping (Error?) -> Void) { - print(#function) connection.smartCardConnection { connection in connection?.managementSession { session, error in - print(session) - guard let session else { return } + guard let session else { completion(error); return } session.getDeviceInfo { deviceInfo, error in - print(deviceInfo) - guard let deviceInfo, let configuration = deviceInfo.configuration else { return } + guard let deviceInfo, let configuration = deviceInfo.configuration else { completion(error); return } configuration.setEnabled(false, application: .OTP, overTransport: .USB) session.write(configuration, reboot: true) { error in - print(error) completion(error) } } @@ -223,30 +213,23 @@ extension TokenRequestViewModel { } func waitForKeyRemoval(completion: @escaping () -> Void) { - print(#function) connection.didDisconnect { _, _ in - print("") completion() } } - func ignoreThisKey(handler: @escaping (Error?) -> Void) { - print(#function) + func ignoreThisKey(completion: @escaping (Error?) -> Void) { connection.smartCardConnection { connection in - print(connection) connection?.managementSession { session, error in - print(session) - guard let session else { handler(error); return } + guard let session else { completion(error); return } session.getDeviceInfo { deviceInfo, error in - print(deviceInfo) - guard let deviceInfo else { handler(error); return } + guard let deviceInfo else { completion(error); return } SettingsConfig.registerUSBCDeviceToIgnore(deviceId: deviceInfo.serialNumber) - handler(nil) + completion(nil) } } } } - } @available(iOS 14.0, *) diff --git a/Authenticator/UI/TokenSession/TokenRequestYubiOTPViewController.swift b/Authenticator/UI/TokenSession/TokenRequestYubiOTPViewController.swift index 095f3c44..ba2021f3 100644 --- a/Authenticator/UI/TokenSession/TokenRequestYubiOTPViewController.swift +++ b/Authenticator/UI/TokenSession/TokenRequestYubiOTPViewController.swift @@ -18,9 +18,12 @@ class TokenRequestYubiOTPViewController: UIViewController { @IBAction func disableOTP() { viewModel?.disableOTP { error in - guard error == nil else { return } - UIView.animate(withDuration: 0.5) { - DispatchQueue.main.async { + guard error == nil else { + self.presentError(error) + return + } + DispatchQueue.main.async { + UIView.animate(withDuration: 0.5) { self.optionsView.alpha = 0 self.completedView.alpha = 1 self.viewModel?.waitForKeyRemoval { @@ -33,10 +36,19 @@ class TokenRequestYubiOTPViewController: UIViewController { @IBAction func ignoreThisKey() { viewModel?.ignoreThisKey { error in - guard error == nil else { return } + guard error == nil else { + self.presentError(error) + return + } DispatchQueue.main.async { self.dismiss(animated: true) } } } + + private func presentError(_ error: Error?) { + guard let error else { return } + let alert = UIAlertController(title: "Error reading YubiKey", message: "\(error.localizedDescription)\n\nRemove and reinsert your YubiKey.") { self.dismiss(animated: true) } + self.present(alert, animated: true, completion: nil) + } } From b628052ee9ae2d0452743507d47937750719f5f6 Mon Sep 17 00:00:00 2001 From: Jens Utbult Date: Wed, 13 Dec 2023 15:47:59 +0100 Subject: [PATCH 3/5] Updated release notes. --- Authenticator/VersionHistory.plist | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Authenticator/VersionHistory.plist b/Authenticator/VersionHistory.plist index 39bae137..356e22bb 100644 --- a/Authenticator/VersionHistory.plist +++ b/Authenticator/VersionHistory.plist @@ -2,7 +2,18 @@ - + + version + 1.7.9 + date + 2023-12-20T09:41:00Z + shouldPromptUser + + changes + + Improved handling on iPhone 15 with YubiKeys that have Yubico OTP enabled when using the SmartCard extension. + + version 1.7.8 From b9d80e07e98095761f2d3cfef9df9680cdaaa50b Mon Sep 17 00:00:00 2001 From: Jens Utbult Date: Thu, 14 Dec 2023 16:14:26 +0100 Subject: [PATCH 4/5] Fixes issues with removing and reinserting yubikeys in the disable yubiotp dialog. --- Authenticator.xcodeproj/project.pbxproj | 35 ++++++++----------- .../Model/TokenRequestViewModel.swift | 30 ++++++++++------ Authenticator/UI/Base.lproj/Main.storyboard | 18 +++++----- .../TokenRequestViewController.swift | 14 +++++--- .../TokenRequestYubiOTPViewController.swift | 10 ++++-- 5 files changed, 60 insertions(+), 47 deletions(-) diff --git a/Authenticator.xcodeproj/project.pbxproj b/Authenticator.xcodeproj/project.pbxproj index 6f37c280..fb588860 100644 --- a/Authenticator.xcodeproj/project.pbxproj +++ b/Authenticator.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 60; objects = { /* Begin PBXBuildFile section */ @@ -83,7 +83,7 @@ B40D61A22AE7F89500467AE9 /* DisableOTPModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B40D61A12AE7F89500467AE9 /* DisableOTPModel.swift */; }; B40F44452B27033A000D5E02 /* TokenRequestYubiOTPViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B40F44442B27033A000D5E02 /* TokenRequestYubiOTPViewController.swift */; }; B411242F29D423A300D58001 /* ListStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B411242E29D423A300D58001 /* ListStatusView.swift */; }; - B432B1BF28B65B8600A7182F /* YubiKit in Frameworks */ = {isa = PBXBuildFile; productRef = B432B1BE28B65B8600A7182F /* YubiKit */; }; + B42A39332B2A03D20039DB26 /* YubiKit in Frameworks */ = {isa = PBXBuildFile; productRef = B42A39322B2A03D20039DB26 /* YubiKit */; }; B452EC1F2A1E4F460045E5D9 /* YubiOtpRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B452EC1E2A1E4F460045E5D9 /* YubiOtpRowView.swift */; }; B452EC3D2A264A620045E5D9 /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B452EC3C2A264A620045E5D9 /* ToastView.swift */; }; B452EC442A2A06940045E5D9 /* ToastPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B452EC432A2A06940045E5D9 /* ToastPresenter.swift */; }; @@ -265,7 +265,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B432B1BF28B65B8600A7182F /* YubiKit in Frameworks */, + B42A39332B2A03D20039DB26 /* YubiKit in Frameworks */, B9F0FF11F842A39183974083 /* (null) in Frameworks */, 51AFD4DA271D4278008F2630 /* QuartzCore.framework in Frameworks */, ); @@ -584,7 +584,7 @@ ); name = Authenticator; packageProductDependencies = ( - B432B1BE28B65B8600A7182F /* YubiKit */, + B42A39322B2A03D20039DB26 /* YubiKit */, ); productName = Authenticator; productReference = 818866B322DFD729006BC0A8 /* Authenticator.app */; @@ -645,7 +645,7 @@ ); mainGroup = 818866AA22DFD729006BC0A8; packageReferences = ( - B432B1BD28B65B8600A7182F /* XCRemoteSwiftPackageReference "yubikit-ios" */, + B42A39312B2A03D20039DB26 /* XCLocalSwiftPackageReference "../yubikit-ios" */, ); productRefGroup = 818866B422DFD729006BC0A8 /* Products */; projectDirPath = ""; @@ -1006,7 +1006,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = Authenticator/Authenticator.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 121; + CURRENT_PROJECT_VERSION = 126; DEVELOPMENT_TEAM = LQA3CS5MM7; HEADER_SEARCH_PATHS = "../Submodules/YubiKit/**"; INFOPLIST_FILE = Authenticator/Info.plist; @@ -1016,7 +1016,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = ""; - MARKETING_VERSION = 1.7.8; + MARKETING_VERSION = 1.7.9; OTHER_LDFLAGS = "-ObjC"; PRODUCT_BUNDLE_IDENTIFIER = com.yubico.Authenticator; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1034,7 +1034,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = Authenticator/Authenticator.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 121; + CURRENT_PROJECT_VERSION = 126; DEVELOPMENT_TEAM = LQA3CS5MM7; HEADER_SEARCH_PATHS = "../Submodules/YubiKit/**"; INFOPLIST_FILE = Authenticator/Info.plist; @@ -1044,7 +1044,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = ""; - MARKETING_VERSION = 1.7.8; + MARKETING_VERSION = 1.7.9; OTHER_LDFLAGS = "-ObjC"; PRODUCT_BUNDLE_IDENTIFIER = com.yubico.Authenticator; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1140,21 +1140,16 @@ }; /* End XCConfigurationList section */ -/* Begin XCRemoteSwiftPackageReference section */ - B432B1BD28B65B8600A7182F /* XCRemoteSwiftPackageReference "yubikit-ios" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/Yubico/yubikit-ios"; - requirement = { - branch = main; - kind = branch; - }; +/* Begin XCLocalSwiftPackageReference section */ + B42A39312B2A03D20039DB26 /* XCLocalSwiftPackageReference "../yubikit-ios" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = "../yubikit-ios"; }; -/* End XCRemoteSwiftPackageReference section */ +/* End XCLocalSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - B432B1BE28B65B8600A7182F /* YubiKit */ = { + B42A39322B2A03D20039DB26 /* YubiKit */ = { isa = XCSwiftPackageProductDependency; - package = B432B1BD28B65B8600A7182F /* XCRemoteSwiftPackageReference "yubikit-ios" */; productName = YubiKit; }; /* End XCSwiftPackageProductDependency section */ diff --git a/Authenticator/Model/TokenRequestViewModel.swift b/Authenticator/Model/TokenRequestViewModel.swift index d411e292..4d0aa867 100644 --- a/Authenticator/Model/TokenRequestViewModel.swift +++ b/Authenticator/Model/TokenRequestViewModel.swift @@ -74,7 +74,8 @@ class TokenRequestViewModel: NSObject { } var isWiredKeyConnectedHandler: ((Bool) -> Void)? - + var isYubiOTPEnabledHandler: ((Bool) -> Void)? + func isWiredKeyConnected(handler: @escaping (Bool) -> Void) { isWiredKeyConnectedHandler = handler connection.smartCardConnection { [weak self] connection in @@ -82,9 +83,11 @@ class TokenRequestViewModel: NSObject { self?.isWiredKeyConnectedHandler?(connection != nil) } } - connection.accessoryConnection { [weak self] connection in - DispatchQueue.main.async { - self?.isWiredKeyConnectedHandler?(connection != nil) + if YubiKitDeviceCapabilities.supportsMFIAccessoryKey { + connection.accessoryConnection { [weak self] connection in + DispatchQueue.main.async { + self?.isWiredKeyConnectedHandler?(connection != nil) + } } } } @@ -181,17 +184,24 @@ class TokenRequestViewModel: NSObject { extension TokenRequestViewModel { - func isYubiOTPEnabledOverUSBC(completion: @escaping (Bool) -> Void) { - connection.smartCardConnection { connection in + func isYubiOTPEnabledOverUSBC(completion: @escaping (Bool?) -> Void) { + isYubiOTPEnabledHandler = completion + + // If this device does not have a lightning port return nil + if YubiKitDeviceCapabilities.supportsMFIAccessoryKey { + completion(nil) + return + } + connection.smartCardConnection { [weak self] connection in connection?.managementSession { session, error in - guard let session else { completion(false); return } + guard let session else { self?.isYubiOTPEnabledHandler?(false); return } session.getDeviceInfo { deviceInfo, error in - guard let deviceInfo, let configuration = deviceInfo.configuration else { completion(false); return } + guard let deviceInfo, let configuration = deviceInfo.configuration else { self?.isYubiOTPEnabledHandler?(false); return } guard !configuration.isEnabled(.OTP, overTransport: .USB) || SettingsConfig.isOTPOverUSBIgnored(deviceId: deviceInfo.serialNumber) else { - completion(true) + self?.isYubiOTPEnabledHandler?(true) return } - completion(false) + self?.isYubiOTPEnabledHandler?(false) } } } diff --git a/Authenticator/UI/Base.lproj/Main.storyboard b/Authenticator/UI/Base.lproj/Main.storyboard index 36a8ec8e..72bd3467 100644 --- a/Authenticator/UI/Base.lproj/Main.storyboard +++ b/Authenticator/UI/Base.lproj/Main.storyboard @@ -1543,24 +1543,24 @@ All rights reserved. - - + + - - +