Skip to content

Commit

Permalink
FIDO reset implemented.
Browse files Browse the repository at this point in the history
  • Loading branch information
jensutbult committed Sep 2, 2024
1 parent c322864 commit 7ebde6c
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 61 deletions.
6 changes: 0 additions & 6 deletions Authenticator/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -889,9 +889,6 @@
},
"FIDO accounts deleted and FIDO application reset to factory defaults." : {
"comment" : "FIDO reset confirmation message"
},
"FIDO has been reset" : {

},
"FIDO PIN" : {

Expand Down Expand Up @@ -1811,9 +1808,6 @@
},
"Reset all accounts stored on YubiKey, make sure they are not in use anywhere before doing this." : {

},
"Reset all FIDO accounts stored on YubiKey, make sure they are not in use anywhere before doing this." : {

},
"Reset complete" : {
"comment" : "Reset YubiKey complete alert title",
Expand Down
72 changes: 47 additions & 25 deletions Authenticator/Model/Connection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,14 @@ class Connection: NSObject {
private var nfcConnection: YKFNFCConnection?
private var smartCardConnection: YKFSmartCardConnection?
private var accessoryConnection: YKFAccessoryConnection?

private var connectionCallback: ((_ connection: YKFConnectionProtocol) -> Void)?
private var disconnectionCallback: ((_ connection: YKFConnectionProtocol, _ error: Error?) -> Void)?

private var accessoryConnectionCallback: ((_ connection: YKFAccessoryConnection?) -> Void)?
private var nfcConnectionCallback: ((_ connection: YKFNFCConnection?) -> Void)?
private var smartCardConnectionCallback: ((_ connection: YKFSmartCardConnection?) -> Void)?

func startConnection(completion: @escaping (_ connection: YKFConnectionProtocol) -> Void) {
if let connection = accessoryConnection {
completion(connection)
Expand All @@ -53,28 +59,40 @@ class Connection: NSObject {
}
}

private var accessoryConnectionCallback: ((_ connection: YKFAccessoryConnection?) -> Void)?
func startWiredConnection(completion: @escaping (_ connection: YKFConnectionProtocol) -> Void) {
if let connection = accessoryConnection {
completion(connection)
} else if let connection = smartCardConnection {
completion(connection)
} else {
connectionCallback = completion
}
}

func accessoryConnection(handler: @escaping (_ connection: YKFAccessoryConnection?) -> Void) {
handler(accessoryConnection)
accessoryConnectionCallback = handler
if let connection = accessoryConnection {
handler(connection)
} else {
accessoryConnectionCallback = handler
}
}

private var smartCardConnectionCallback: ((_ connection: YKFSmartCardConnection?) -> Void)?
func smartCardConnection(handler: @escaping (_ connection: YKFSmartCardConnection?) -> Void) {
handler(smartCardConnection)
smartCardConnectionCallback = handler
if let connection = smartCardConnection {
handler(connection)
} else {
smartCardConnectionCallback = handler
}
}

private var nfcConnectionCallback: ((_ connection: YKFNFCConnection?) -> Void)?

func nfcConnection(handler: @escaping (_ connection: YKFNFCConnection?) -> Void) {
handler(nfcConnection)
nfcConnectionCallback = handler
if let connection = nfcConnection {
handler(connection)
} else {
nfcConnectionCallback = handler
}
}

private var disconnectionCallback: ((_ connection: YKFConnectionProtocol, _ error: Error?) -> Void)?

func didDisconnect(handler: @escaping (_ connection: YKFConnectionProtocol, _ error: Error?) -> Void) {
disconnectionCallback = handler
}
Expand All @@ -85,48 +103,52 @@ extension Connection: YKFManagerDelegate {

func didConnectNFC(_ connection: YKFNFCConnection) {
nfcConnection = connection
if let callback = connectionCallback {
callback(connection)
}
nfcConnectionCallback?(connection)
nfcConnectionCallback = nil
connectionCallback?(connection)
connectionCallback = nil
}

func didDisconnectNFC(_ connection: YKFNFCConnection, error: Error?) {
if let callback = disconnectionCallback {
callback(connection, error)
}
connectionCallback = nil
nfcConnection = nil
nfcConnectionCallback = nil
connectionCallback = nil
disconnectionCallback?(connection, error)
disconnectionCallback = nil
}

func didFailConnectingNFC(_ error: Error) {}

func didConnectAccessory(_ connection: YKFAccessoryConnection) {
accessoryConnection = connection
accessoryConnectionCallback?(connection)
accessoryConnectionCallback = nil
connectionCallback?(connection)
connectionCallback = nil
}

func didDisconnectAccessory(_ connection: YKFAccessoryConnection, error: Error?) {
disconnectionCallback?(connection, error)
accessoryConnectionCallback?(nil)
connectionCallback = nil
accessoryConnection = nil
accessoryConnectionCallback = nil
connectionCallback = nil
disconnectionCallback?(connection, error)
disconnectionCallback = nil
}

func didConnectSmartCard(_ connection: YKFSmartCardConnection) {
smartCardConnection = connection
smartCardConnectionCallback?(connection)
smartCardConnectionCallback = nil
connectionCallback?(connection)
connectionCallback = nil
}

func didDisconnectSmartCard(_ connection: YKFSmartCardConnection, error: Error?) {
disconnectionCallback?(connection, error)
smartCardConnectionCallback?(nil)
connectionCallback = nil
smartCardConnection = nil
smartCardConnectionCallback = nil
connectionCallback = nil
disconnectionCallback?(connection, error)
disconnectionCallback = nil
}

}
4 changes: 3 additions & 1 deletion Authenticator/Model/FIDOPINViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@

enum FidoViewModelError: Error, LocalizedError {

case usbNotSupported
case usbNotSupported, timeout

public var errorDescription: String? {
switch self {
case .usbNotSupported:
return "Fido over USB-C is not supported by iOS. Use NFC or the desktop Yubico Authenticator instead."
case .timeout:
return "Operation timed out."
}
}
}
Expand Down
137 changes: 117 additions & 20 deletions Authenticator/Model/FIDOResetViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,29 @@

import Foundation

extension String: LocalizedError {
public var errorDescription: String? {
return self
}
}

class FIDOResetViewModel: ObservableObject {

@Published var state: ResetState = .ready

enum ResetState: Equatable {
case ready, success, error(Error)
case ready, waitingForKeyRemove, waitingForKeyReinsert, waitingForKeyTouch, success, error(Error)

static func == (lhs: FIDOResetViewModel.ResetState, rhs: FIDOResetViewModel.ResetState) -> Bool {
switch (lhs, rhs) {
case (.ready, .ready):
return true
case (.waitingForKeyRemove, .waitingForKeyRemove):
return true
case (.waitingForKeyReinsert, .waitingForKeyReinsert):
return true
case (.waitingForKeyTouch, .waitingForKeyTouch):
return true
case (.success, .success):
return true
case (.error(_), .error(_)):
Expand All @@ -35,6 +47,15 @@ class FIDOResetViewModel: ObservableObject {
return false
}
}

func isError() -> Bool {
switch self {
case (.error(_)):
return true
default:
return false
}
}
}

private let connection = Connection()
Expand All @@ -47,32 +68,108 @@ class FIDOResetViewModel: ObservableObject {
}
return
}
connection.fido2Session { session, error in
guard let session = session else {
let errorMessage = error?.localizedDescription ?? "Unknown error"
YubiKitManager.shared.stopNFCConnection(withErrorMessage: errorMessage)
DispatchQueue.main.async {
self.state = .error(errorMessage)
if let connection = connection as? YKFNFCConnection {
self.resetNFC(connection: connection)
return
}
if let connection = connection as? YKFAccessoryConnection {
self.resetAccessory(connection: connection)
return
}
self.state = .error("Unknown error")
}
}

deinit {
print("deinit FIDOResetViewModel")
}
}


extension FIDOResetViewModel {
func resetNFC(connection: YKFNFCConnection) {
connection.fido2Session { session, error in
guard let session = session else {
YubiKitManager.shared.stopNFCConnection(withErrorMessage: error!.localizedDescription)
DispatchQueue.main.async {
self.state = .error(error!)
}
return
}
session.reset { error in
DispatchQueue.main.async {
if let error = error {
YubiKitManager.shared.stopNFCConnection(withErrorMessage: error.localizedDescription)
self.state = .error(error)
} else {
let message = String(localized: "FIDO accounts deleted and FIDO application reset to factory defaults.", comment: "FIDO reset confirmation message")
YubiKitManager.shared.stopNFCConnection(withMessage: message)
self.state = .success
}
return
}
session.reset { error in
}
}
}
}


extension FIDOResetViewModel {
func resetAccessory(connection: YKFAccessoryConnection) {
connection.fido2Session { _, error in
if let error {
DispatchQueue.main.async {
self.state = .error(error)
}
return
}

var cancellation = Task {
try await Task.sleep(for: .seconds(10))
if !Task.isCancelled {
DispatchQueue.main.async {
if let error = error {
YubiKitManager.shared.stopNFCConnection(withErrorMessage: error.localizedDescription)
self.state = .error(error.localizedDescription)
} else {
let message = String(localized: "FIDO accounts deleted and FIDO application reset to factory defaults.", comment: "FIDO reset confirmation message")
YubiKitManager.shared.stopNFCConnection(withMessage: message)
self.state = .success
self.state = .error(FidoViewModelError.timeout)
}
}
}

DispatchQueue.main.async {
self.state = .waitingForKeyRemove
}
self.connection.didDisconnect { _, _ in
cancellation.cancel()
cancellation = Task {
try await Task.sleep(for: .seconds(10))
if !Task.isCancelled {
DispatchQueue.main.async {
self.state = .error(FidoViewModelError.timeout)
}
}
}
DispatchQueue.main.async {
self.state = .waitingForKeyReinsert
}
self.connection.startWiredConnection { connection in
connection.fido2Session { session, error in
cancellation.cancel()
cancellation = Task {
try await Task.sleep(for: .seconds(10))
if !Task.isCancelled {
DispatchQueue.main.async {
self.state = .error(FidoViewModelError.timeout)
}
}
}
DispatchQueue.main.async {
self.state = .waitingForKeyTouch
}
session?.reset { error in
DispatchQueue.main.async {
self.state = .success
}
}
}
}
}
}
}

deinit {
print("deinit FIDOResetViewModel")
}
}
Loading

0 comments on commit 7ebde6c

Please sign in to comment.