From ac0a2b938d84e53a6dccbb961c7ce3c84a3a1033 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Tue, 1 Aug 2017 14:43:30 -0600 Subject: [PATCH 1/7] Update issue_template.md --- issue_template.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/issue_template.md b/issue_template.md index 6834946..5a8c44c 100644 --- a/issue_template.md +++ b/issue_template.md @@ -4,6 +4,10 @@ Thanks for using Soft U2F. I'm sorry that you've encountered a bug. To aide in debugging, please provide the output from running the following commands: +#### What browser are you using? + +FIDO U2F only works natively using Chrome and Opera. Browser extensions enabling U2F are available for Safari and Firefox. + #### `sw_vers` This tells us the version of macOS you are running. Soft U2F only works on macOS Sierra (10.12) and newer. From dee71f07892fe03cbd53ba5054b8467b27f750c0 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Tue, 1 Aug 2017 15:57:08 -0600 Subject: [PATCH 2/7] add another step to uninstall --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 0fff8af..744ecb5 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,12 @@ Delete the kernel extension $ sudo rm -rf /Library/Extensions/softu2f.kext ``` +Tell macOS to forget about the installation + +``` +$ sudo pkgutil --forget com.GitHub.SoftU2F +``` + Done ## Security considerations From 01eb13dde9498c6c5f4f7cca95423489e6dd8dc3 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Mon, 7 Aug 2017 16:44:13 -0600 Subject: [PATCH 3/7] make sure LaunchAgents directory exists in postinstall script --- install-scripts/postinstall | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/install-scripts/postinstall b/install-scripts/postinstall index d917164..030340f 100755 --- a/install-scripts/postinstall +++ b/install-scripts/postinstall @@ -2,11 +2,16 @@ set -e KEXT="/Library/Extensions/softu2f.kext" -LAUNCH_AGENT_PLIST="$HOME/Library/LaunchAgents/com.github.SoftU2F.plist" +LAUCNH_AGENTS_DIR="$HOME/Library/LaunchAgents" +LAUNCH_AGENT_PLIST="$LAUCNH_AGENTS_DIR/com.github.SoftU2F.plist" # Make sure the kext is loaded kextutil $KEXT +# This directory should already exist, but some users have had issues with it +# being missing. +mkdir -p $LAUCNH_AGENTS_DIR + # Write a LaunchAgent plist so app starts at login cat > $LAUNCH_AGENT_PLIST << EOT From f33569a245ee93f20967dcbe8681c6dad65f34d6 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Tue, 8 Aug 2017 20:54:25 -0600 Subject: [PATCH 4/7] add duo known facet --- SoftU2FTool/KnownFacets.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SoftU2FTool/KnownFacets.swift b/SoftU2FTool/KnownFacets.swift index c8b11d4..5796f0f 100644 --- a/SoftU2FTool/KnownFacets.swift +++ b/SoftU2FTool/KnownFacets.swift @@ -13,5 +13,6 @@ let KnownFacets: [Data: String] = [ SHA256.digest("https://www.dropbox.com/u2f-app-id.json"): "https://dropbox.com", SHA256.digest("https://www.gstatic.com/securitykey/origins.json"): "https://google.com", SHA256.digest("https://vault.bitwarden.com/app-id.json"): "https://vault.bitwarden.com", - SHA256.digest("https://keepersecurity.com"): "https://keepersecurity.com" + SHA256.digest("https://keepersecurity.com"): "https://keepersecurity.com", + SHA256.digest("https://api-9dcf9b83.duosecurity.com"): "https://api-9dcf9b83.duosecurity.com" ] From 69a07e39518494cbfc1e30becc9bc023a9d19805 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Fri, 11 Aug 2017 16:06:43 -0600 Subject: [PATCH 5/7] Add links to FF/Safari extensions in README fixes #33 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 744ecb5..5022a4a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![](https://user-images.githubusercontent.com/1144197/28190263-470a80d2-67e7-11e7-81e6-17895d70bf75.png) -Soft U2F is a software U2F authenticator for OS X. It emulates a hardware U2F HID device and performs cryptographic operations using the OS X Keychain. This tool works with Google Chrome and Opera's built-in U2F implementations as well as with the U2F extensions for OS X Safari and Firefox. +Soft U2F is a software U2F authenticator for OS X. It emulates a hardware U2F HID device and performs cryptographic operations using the OS X Keychain. This tool works with Google Chrome and Opera's built-in U2F implementations as well as with the U2F extensions for [OS X Safari](https://github.com/Safari-FIDO-U2F/Safari-FIDO-U2F) and [Firefox](https://addons.mozilla.org/en-US/firefox/addon/u2f-support-add-on/). We take the security of this project seriously. Report any security vulnerabilities to the [GitHub Bug Bounty Program](https://hackerone.com/github). From c5de693323782425d22e46e028be6d0296cd31e5 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Fri, 11 Aug 2017 16:40:03 -0600 Subject: [PATCH 6/7] add settings/cli from #29. settings to disable touchid --- SoftU2F.xcodeproj/project.pbxproj | 8 +++ SoftU2FTool/AppDelegate.swift | 12 +++- SoftU2FTool/CLI.swift | 99 ++++++++++++++++++++++++++++++ SoftU2FTool/KeyPair.swift | 64 +++++++++++++++++++ SoftU2FTool/Keychain.swift | 31 +++++++++- SoftU2FTool/Settings.swift | 38 ++++++++++++ SoftU2FTool/U2FAuthenticator.swift | 22 ++++++- SoftU2FTool/U2FRegistration.swift | 37 +++++++++++ SoftU2FTool/UserPresence.swift | 53 ++++++++-------- 9 files changed, 332 insertions(+), 32 deletions(-) create mode 100644 SoftU2FTool/CLI.swift create mode 100644 SoftU2FTool/Settings.swift diff --git a/SoftU2F.xcodeproj/project.pbxproj b/SoftU2F.xcodeproj/project.pbxproj index fc8696b..316d7c6 100644 --- a/SoftU2F.xcodeproj/project.pbxproj +++ b/SoftU2F.xcodeproj/project.pbxproj @@ -85,6 +85,8 @@ F738F5871E4A3C09005680A2 /* DataReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5851E4A3C09005680A2 /* DataReaderTests.swift */; }; F738F5881E4A3C09005680A2 /* DataWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5861E4A3C09005680A2 /* DataWriterTests.swift */; }; F738F58A1E4A3C21005680A2 /* TestUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5891E4A3C21005680A2 /* TestUtil.swift */; }; + F78BEF5F1F3E654A0005B3D5 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78BEF5E1F3E654A0005B3D5 /* Settings.swift */; }; + F78BEF611F3E65510005B3D5 /* CLI.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78BEF601F3E65510005B3D5 /* CLI.swift */; }; F7ABD9BE1E80603D00768FEC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F7ABD9BD1E80603D00768FEC /* Assets.xcassets */; }; F7B5DBAD1E4A5CED00E5ABD4 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBAC1E4A5CED00E5ABD4 /* Command.swift */; }; F7B5DBAF1E4A815700E5ABD4 /* RawConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBAE1E4A815700E5ABD4 /* RawConvertible.swift */; }; @@ -241,6 +243,8 @@ F738F5851E4A3C09005680A2 /* DataReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DataReaderTests.swift; path = DataTests/DataReaderTests.swift; sourceTree = ""; }; F738F5861E4A3C09005680A2 /* DataWriterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DataWriterTests.swift; path = DataTests/DataWriterTests.swift; sourceTree = ""; }; F738F5891E4A3C21005680A2 /* TestUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestUtil.swift; sourceTree = ""; }; + F78BEF5E1F3E654A0005B3D5 /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; + F78BEF601F3E65510005B3D5 /* CLI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CLI.swift; sourceTree = ""; }; F7ABD9BD1E80603D00768FEC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; F7B5DBAC1E4A5CED00E5ABD4 /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = ""; }; F7B5DBAE1E4A815700E5ABD4 /* RawConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawConvertible.swift; sourceTree = ""; }; @@ -374,6 +378,8 @@ 51213EC51E3916EB005454E0 /* U2FHID.swift */, 51B289E41E39903F00AD90CC /* U2FAuthenticator.swift */, 51FE30EC1E403DA000BAE824 /* U2FRegistration.swift */, + F78BEF601F3E65510005B3D5 /* CLI.swift */, + F78BEF5E1F3E654A0005B3D5 /* Settings.swift */, 518537BF1E380E4600600911 /* SoftU2F-Bridging-Header.h */, 51F090181E37E8C600F03AD3 /* Info.plist */, ); @@ -841,12 +847,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F78BEF611F3E65510005B3D5 /* CLI.swift in Sources */, 5190B6121E3BFE3D00E6FE06 /* UserPresence.swift in Sources */, 51FE30F11E410B3D00BAE824 /* Utils.swift in Sources */, 5119862E1E3C1519006A3BBB /* KnownFacets.swift in Sources */, 51F090101E37E8C600F03AD3 /* AppDelegate.swift in Sources */, 51213EC61E3916EB005454E0 /* U2FHID.swift in Sources */, 51E214601E3823E7005B2864 /* SHA256.swift in Sources */, + F78BEF5F1F3E654A0005B3D5 /* Settings.swift in Sources */, 51B289E51E39903F00AD90CC /* U2FAuthenticator.swift in Sources */, 514F3D821E43C833008FA513 /* Keychain.swift in Sources */, 51FE30ED1E403DA000BAE824 /* U2FRegistration.swift in Sources */, diff --git a/SoftU2FTool/AppDelegate.swift b/SoftU2FTool/AppDelegate.swift index 84672f9..728d863 100644 --- a/SoftU2FTool/AppDelegate.swift +++ b/SoftU2FTool/AppDelegate.swift @@ -10,14 +10,16 @@ import Cocoa @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ aNotification: Notification) { - if !U2FAuthenticator.start() { + if CLI(CommandLine.arguments).run() { + quit() + } else if !U2FAuthenticator.start(){ print("Error starting authenticator") + quit() } - } func applicationWillTerminate(_ aNotification: Notification) { - if !U2FAuthenticator.stop() { + if U2FAuthenticator.running && !U2FAuthenticator.stop() { print("Error stopping authenticator") } } @@ -28,4 +30,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { // with our notification. NSApplication.shared().hide(nil) } + + private func quit() { + NSApplication.shared().terminate(self) + } } diff --git a/SoftU2FTool/CLI.swift b/SoftU2FTool/CLI.swift new file mode 100644 index 0000000..fb2b646 --- /dev/null +++ b/SoftU2FTool/CLI.swift @@ -0,0 +1,99 @@ +// +// CLI.swift +// SoftU2F +// +// Created by Ben Toews on 8/1/17. +// + +import Foundation + +// Command line flags +fileprivate let listFlag = "--list" +fileprivate let deleteAllFlag = "--delete-all" +fileprivate let showTouchidFlag = "--show-touchid" +fileprivate let enableTouchidFlag = "--enable-touchid" +fileprivate let disableTouchidFlag = "--disable-touchid" + +class CLI { + private let args: [String] + + init(_ arguments: [String]) { + args = arguments + } + + func run() -> Bool { + if args.contains(listFlag) { + listRegistrations() + return true + } else if args.contains(deleteAllFlag) { + deleteAll() + return true + } else if args.contains(showTouchidFlag) { + showTouchid() + } else if args.contains(enableTouchidFlag) { + enableTouchid() + } else if args.contains(disableTouchidFlag) { + disableTouchid() + } + + return false + } + + private func listRegistrations() { + print("The following is a list of U2F registrations stored in your keychain. Each key contains several fields:") + print(" - Key handle: This is the key handle that we registered with a website. For Soft U2F, the key handle is simply a hash of the public key.") + print(" - Application parameter: This is the sha256 of the app-id of the site.") + print(" - Known facet: For some sites we know the application parameter → site name mapping.") + print(" - Counter: How many times this registration has been used.") + print("") + + U2FRegistration.all.forEach { reg in + print("Key handle: ", reg.keyHandle.base64EncodedString()) + print("Application parameter: ", reg.applicationParameter.base64EncodedString()) + + if let kf = KnownFacets[reg.applicationParameter] { + print("Known facet: ", kf) + } else { + print("Known facet: N/A") + } + + print("Counter: ", reg.counter) + print("") + } + } + + private func deleteAll() { + guard let initialCount = U2FRegistration.count else { + print("Error getting registration count from keychain.") + return + } + + if !U2FRegistration.deleteAll() { + print("Error deleting registrations from keychain.") + return + } + + print("Deleted ", initialCount, " registrations") + } + + private func showTouchid() { + if Settings.touchidDisabled { + print("SEP storage is enabled") + } else { + print("SEP storage is disabled") + } + } + + private func enableTouchid() { + if Settings.enableTouchid() { + print("TouchID is now enabled") + } else { + print("Error enabling TouchID. Does your system support it?") + } + } + + private func disableTouchid() { + Settings.disableTouchid() + print("TouchID is now disabled for") + } +} diff --git a/SoftU2FTool/KeyPair.swift b/SoftU2FTool/KeyPair.swift index 54b4380..6ee0a4f 100644 --- a/SoftU2FTool/KeyPair.swift +++ b/SoftU2FTool/KeyPair.swift @@ -7,7 +7,63 @@ import Foundation +fileprivate class tmpKeyPair { + var label: String + var applicationLabel: Data + var publicKey: SecKey? + var privateKey: SecKey? + + var keyPair: KeyPair? { + guard let pub = publicKey else { return nil } + guard let priv = privateKey else { return nil } + + return KeyPair(label: label, appLabel: applicationLabel, publicKey: pub, privateKey: priv) + } + + init(label l: String, applicationLabel al: Data) { + label = l + applicationLabel = al + } +} + class KeyPair { + /// Get all KeyPairs with the given label. + static func all(label: String) -> [KeyPair] { + let secKeys = Keychain.getSecKeys(attrLabel: label as CFString) + var tmpKeyPairs: [tmpKeyPair] = [] + var keyPairs: [KeyPair] = [] + + secKeys.forEach { secKey in + guard let appLabel: CFData = Keychain.getSecKeyAttr(key: secKey, attr: kSecAttrApplicationLabel) else { return } + + let applicationLabel = appLabel as Data + var tmpKP: tmpKeyPair + + if let kp = tmpKeyPairs.first(where: { $0.applicationLabel == applicationLabel }) { + tmpKP = kp + } else { + tmpKP = tmpKeyPair.init(label: label, applicationLabel: applicationLabel) + tmpKeyPairs.append(tmpKP) + } + + guard let klass: CFString = Keychain.getSecKeyAttr(key: secKey, attr: kSecAttrKeyClass) else { return } + + if klass == kSecAttrKeyClassPublic { + tmpKP.publicKey = secKey + } else { + tmpKP.privateKey = secKey + } + } + + tmpKeyPairs.forEach { tmpKP in + guard let kp = tmpKP.keyPair else { return } + + keyPairs.append(kp) + } + + return keyPairs + } + // The number of key pairs (keys/2) in the keychain. static func count(label: String) -> Int? { guard let c = Keychain.count(attrLabel: label as CFString) else { return nil } @@ -75,6 +131,14 @@ class KeyPair { privateKey = priv } + // Initialize a key pair with all the necessary data. + init(label l: String, appLabel al: Data, publicKey pub: SecKey, privateKey priv: SecKey) { + label = l + applicationLabel = al + publicKey = pub + privateKey = priv + } + // Delete this key pair. func delete() -> Bool { return Keychain.delete( diff --git a/SoftU2FTool/Keychain.swift b/SoftU2FTool/Keychain.swift index f5eab50..9274c45 100644 --- a/SoftU2FTool/Keychain.swift +++ b/SoftU2FTool/Keychain.swift @@ -99,7 +99,6 @@ class Keychain { } guard let opaqueResult = dict[name as String] else { - print("Missing key in dictionary") return nil } @@ -152,6 +151,34 @@ class Keychain { return opaqueResult as! CFData as Data } + // Lookup all keys with the given label. + static func getSecKeys(attrLabel: CFString) -> [SecKey] { + let query = makeCFDictionary( + (kSecClass, kSecClassKey), + (kSecAttrKeyType, kSecAttrKeyTypeEC), + (kSecAttrLabel, attrLabel), + (kSecReturnRef, kCFBooleanTrue), + (kSecMatchLimit, 1000 as CFNumber) + ) + + var optionalOpaqueResult: CFTypeRef? = nil + let err = SecItemCopyMatching(query, &optionalOpaqueResult) + + if err != errSecSuccess { + print("Error from keychain: \(err)") + return [] + } + + guard let opaqueResult = optionalOpaqueResult else { + print("Unexpected nil returned from keychain") + return [] + } + + let result = opaqueResult as! [SecKey] + + return result + } + static func getSecKey(attrAppLabel: CFData, keyClass: CFString) -> SecKey? { // Lookup public key. let query = makeCFDictionary( @@ -223,7 +250,7 @@ class Keychain { var err: Unmanaged? = nil defer { err?.release() } - let sig = SecKeyCreateSignature(key, .ecdsaSignatureMessageX962SHA256, data as CFData, &err) as? Data + let sig = SecKeyCreateSignature(key, .ecdsaSignatureMessageX962SHA256, data as CFData, &err) as Data? if err != nil { print("Error creating signature: \(err!.takeUnretainedValue().localizedDescription)") diff --git a/SoftU2FTool/Settings.swift b/SoftU2FTool/Settings.swift new file mode 100644 index 0000000..f0a24f9 --- /dev/null +++ b/SoftU2FTool/Settings.swift @@ -0,0 +1,38 @@ +// +// Settings.swift +// SoftU2F +// +// Created by Ben Toews on 8/2/17. +// + +import Foundation +import LocalAuthentication + +class Settings { + private static let touchidDisabledKey = "touchidDisabled" + + static var touchidDisabled: Bool { + return touchidAvailable && UserDefaults.standard.bool(forKey: touchidDisabledKey) + } + + static func enableTouchid() -> Bool { + if touchidAvailable { + UserDefaults.standard.set(true, forKey: touchidDisabledKey) + return true + } else { + return false + } + } + + static func disableTouchid() { + UserDefaults.standard.set(false, forKey: touchidDisabledKey) + } + + private static var touchidAvailable: Bool { + if #available(OSX 10.12.2, *) { + return LAContext().canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) + } else { + return false + } + } +} diff --git a/SoftU2FTool/U2FAuthenticator.swift b/SoftU2FTool/U2FAuthenticator.swift index 9a5eace..bd2dabf 100644 --- a/SoftU2FTool/U2FAuthenticator.swift +++ b/SoftU2FTool/U2FAuthenticator.swift @@ -13,8 +13,15 @@ class U2FAuthenticator { static let shared = U2FAuthenticator() private static var hasShared = false + var running: Bool + private let u2fhid: U2FHID + static var running: Bool { + guard let ua: U2FAuthenticator = shared else { return false } + return ua.running + } + static func start() -> Bool { guard let ua: U2FAuthenticator = shared else { return false } return ua.start() @@ -28,16 +35,27 @@ class U2FAuthenticator { init?() { guard let uh: U2FHID = U2FHID.shared else { return nil } + running = false u2fhid = uh installMsgHandler() } func start() -> Bool { - return u2fhid.run() + if u2fhid.run() { + running = true + return true + } + + return false } func stop() -> Bool { - return u2fhid.stop() + if u2fhid.stop() { + running = false + return true + } + + return false } func installMsgHandler() { diff --git a/SoftU2FTool/U2FRegistration.swift b/SoftU2FTool/U2FRegistration.swift index 94872f8..95f35fc 100644 --- a/SoftU2FTool/U2FRegistration.swift +++ b/SoftU2FTool/U2FRegistration.swift @@ -11,6 +11,22 @@ class U2FRegistration { // Allow using separate keychain namespace for tests. static var namespace = "SoftU2F Security Key" + static var all: [U2FRegistration] { + let kps = KeyPair.all(label: namespace) + var regs: [U2FRegistration] = [] + + kps.forEach { kp in + guard let reg = U2FRegistration(keyPair: kp) else { + print("Error initializing U2FRegistration") + return + } + + regs.append(reg) + } + + return regs + } + // The number of key pairs (keys/2) in the keychain. static var count: Int? { return KeyPair.count(label: namespace) @@ -69,6 +85,27 @@ class U2FRegistration { } } + // Initialize a registration with all the necessary data. + init?(keyPair kp: KeyPair) { + keyPair = kp + + // Read our application parameter from the keychain. + guard let appTag = keyPair.applicationTag else { return nil } + + let counterSize = MemoryLayout.size + let appTagSize = Int(U2F_APPID_SIZE) + + if appTag.count != counterSize + appTagSize { + return nil + } + + counter = appTag.withUnsafeBytes { (ptr:UnsafePointer) -> UInt32 in + return ptr.pointee.bigEndian + } + + applicationParameter = appTag.subdata(in: counterSize..<(counterSize + appTagSize)) + } + // Sign some data with the private key and increment our counter. func sign(_ data: Data) -> Data? { guard let sig = keyPair.sign(data) else { return nil } diff --git a/SoftU2FTool/UserPresence.swift b/SoftU2FTool/UserPresence.swift index 1d6b9c5..dd1a24b 100644 --- a/SoftU2FTool/UserPresence.swift +++ b/SoftU2FTool/UserPresence.swift @@ -80,36 +80,39 @@ class UserPresence: NSObject { // Send a notification popup to the user. func test(_ type: Notification) { if #available(OSX 10.12.2, *) { - let ctx = LAContext() - - if ctx.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) { - ctx.localizedCancelTitle = "Reject" - ctx.localizedFallbackTitle = "Skip TouchID" - - var prompt: String - switch type { - case let .Register(facet): - prompt = "register with " + (facet ?? "site") - case let .Authenticate(facet): - prompt = "authenticate with " + (facet ?? "site") - } - - ctx.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: prompt) { (success, err) in - guard let lerr = err as? LAError else { - self.complete(success) - return + if !Settings.touchidDisabled { + let ctx = LAContext() + + if ctx.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) { + ctx.localizedCancelTitle = "Reject" + ctx.localizedFallbackTitle = "Skip TouchID" + + var prompt: String + switch type { + case let .Register(facet): + prompt = "register with " + (facet ?? "site") + case let .Authenticate(facet): + prompt = "authenticate with " + (facet ?? "site") } - - switch lerr.code { - case .userFallback, .touchIDNotAvailable, .touchIDNotEnrolled: - self.sendNotification(type) - default: - self.complete(false) + + ctx.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: prompt) { (success, err) in + guard let lerr = err as? LAError else { + self.complete(success) + return + } + + switch lerr.code { + case .userFallback, .touchIDNotAvailable, .touchIDNotEnrolled: + self.sendNotification(type) + default: + self.complete(false) + } } + return } - return } } + sendNotification(type) } From 4615c8e443e163ada1a6547e37ff75b118b5a4d4 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Fri, 11 Aug 2017 16:49:20 -0600 Subject: [PATCH 7/7] fix up settings/cli --- SoftU2FTool/CLI.swift | 9 ++++++--- SoftU2FTool/Settings.swift | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/SoftU2FTool/CLI.swift b/SoftU2FTool/CLI.swift index fb2b646..9d3cfee 100644 --- a/SoftU2FTool/CLI.swift +++ b/SoftU2FTool/CLI.swift @@ -30,10 +30,13 @@ class CLI { return true } else if args.contains(showTouchidFlag) { showTouchid() + return true } else if args.contains(enableTouchidFlag) { enableTouchid() + return true } else if args.contains(disableTouchidFlag) { disableTouchid() + return true } return false @@ -78,9 +81,9 @@ class CLI { private func showTouchid() { if Settings.touchidDisabled { - print("SEP storage is enabled") + print("TouchID is disabled") } else { - print("SEP storage is disabled") + print("TouchID is enabled") } } @@ -94,6 +97,6 @@ class CLI { private func disableTouchid() { Settings.disableTouchid() - print("TouchID is now disabled for") + print("TouchID is now disabled") } } diff --git a/SoftU2FTool/Settings.swift b/SoftU2FTool/Settings.swift index f0a24f9..64abb82 100644 --- a/SoftU2FTool/Settings.swift +++ b/SoftU2FTool/Settings.swift @@ -17,7 +17,7 @@ class Settings { static func enableTouchid() -> Bool { if touchidAvailable { - UserDefaults.standard.set(true, forKey: touchidDisabledKey) + UserDefaults.standard.set(false, forKey: touchidDisabledKey) return true } else { return false @@ -25,7 +25,7 @@ class Settings { } static func disableTouchid() { - UserDefaults.standard.set(false, forKey: touchidDisabledKey) + UserDefaults.standard.set(true, forKey: touchidDisabledKey) } private static var touchidAvailable: Bool {