From a9d7e7644ebc3e3be1b694c78b4c290983b2a3f3 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Sat, 7 Mar 2020 15:42:40 -0800 Subject: [PATCH] Availibilty --- SecretKit/SecretStore.swift | 7 +++++ .../SecureEnclave/SecureEnclaveStore.swift | 7 +++++ SecretKit/SmartCard/SmartCardStore.swift | 14 +++++++--- Secretive/AppDelegate.swift | 10 ++++--- Secretive/ContentView.swift | 26 +++++++++++-------- Secretive/Preview Content/PreviewStore.swift | 1 + 6 files changed, 47 insertions(+), 18 deletions(-) diff --git a/SecretKit/SecretStore.swift b/SecretKit/SecretStore.swift index 8dd3129..a2f6615 100644 --- a/SecretKit/SecretStore.swift +++ b/SecretKit/SecretStore.swift @@ -3,6 +3,7 @@ import Combine public protocol SecretStore: ObservableObject { associatedtype SecretType: Secret + var isAvailable: Bool { get } var name: String { get } var secrets: [SecretType] { get } @@ -20,6 +21,7 @@ extension NSNotification.Name { public class AnySecretStore: SecretStore { fileprivate let base: Any + fileprivate let _isAvailable: () -> Bool fileprivate let _name: () -> String fileprivate let _secrets: () -> [AnySecret] fileprivate let _sign: (Data, AnySecret) throws -> Data @@ -27,11 +29,16 @@ public class AnySecretStore: SecretStore { public init(_ secretStore: T) where T: SecretStore { base = secretStore + _isAvailable = { secretStore.isAvailable } _name = { secretStore.name } _secrets = { secretStore.secrets.map { AnySecret($0) } } _sign = { try secretStore.sign(data: $0, with: $1 as! T.SecretType) } _delete = { try secretStore.delete(secret: $0 as! T.SecretType) } } + + public var isAvailable: Bool { + return _isAvailable() + } public var name: String { return _name() diff --git a/SecretKit/SecureEnclave/SecureEnclaveStore.swift b/SecretKit/SecureEnclave/SecureEnclaveStore.swift index 8e9ac2e..4eb3bbd 100644 --- a/SecretKit/SecureEnclave/SecureEnclaveStore.swift +++ b/SecretKit/SecureEnclave/SecureEnclaveStore.swift @@ -1,10 +1,17 @@ import Foundation import Security +import CryptoTokenKit extension SecureEnclave { public class Store: SecretStore { + public var isAvailable: Bool { + // For some reason, as of build time, CryptoKit.SecureEnclave.isAvailable always returns false + // error msg "Received error sending GET UNIQUE DEVICE command" + // Verify it with TKTokenWatcher manually. + return TKTokenWatcher().tokenIDs.contains("com.apple.setoken") + } public let name = NSLocalizedString("Secure Enclave", comment: "Secure Enclave") @Published public fileprivate(set) var secrets: [Secret] = [] diff --git a/SecretKit/SmartCard/SmartCardStore.swift b/SecretKit/SmartCard/SmartCardStore.swift index 9707614..c95ea8a 100644 --- a/SecretKit/SmartCard/SmartCardStore.swift +++ b/SecretKit/SmartCard/SmartCardStore.swift @@ -9,6 +9,7 @@ extension SmartCard { public class Store: SecretStore { // TODO: Read actual smart card name, eg "YubiKey 5c" + @Published public var isAvailable: Bool = false public let name = NSLocalizedString("Smart Card", comment: "Smart Card") @Published public fileprivate(set) var secrets: [Secret] = [] fileprivate let watcher = TKTokenWatcher() @@ -21,10 +22,11 @@ extension SmartCard { guard !string.contains("setoken") else { return } self.id = string self.reloadSecrets() - self.watcher.addRemovalHandler(self.reloadSecrets, forTokenID: string) + self.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: string) } if let id = id { - self.watcher.addRemovalHandler(self.reloadSecrets, forTokenID: id) + self.isAvailable = true + self.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: id) } loadSecrets() } @@ -70,8 +72,14 @@ extension SmartCard { extension SmartCard.Store { - fileprivate func reloadSecrets(for tokenID: String? = nil) { + fileprivate func smartcardRemoved(for tokenID: String? = nil) { + id = nil + reloadSecrets() + } + + fileprivate func reloadSecrets() { DispatchQueue.main.async { + self.isAvailable = self.id != nil self.secrets.removeAll() self.loadSecrets() } diff --git a/Secretive/AppDelegate.swift b/Secretive/AppDelegate.swift index 84003e1..9caaae9 100644 --- a/Secretive/AppDelegate.swift +++ b/Secretive/AppDelegate.swift @@ -27,10 +27,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { window.makeKeyAndOrderFront(nil) window.titleVisibility = .hidden window.toolbar = toolbar - let plus = NSTitlebarAccessoryViewController() - plus.view = NSButton(image: NSImage(named: NSImage.addTemplateName)!, target: self, action: #selector(add(sender:))) - plus.layoutAttribute = .right - window.addTitlebarAccessoryViewController(plus) + if secureEnclave.isAvailable { + let plus = NSTitlebarAccessoryViewController() + plus.view = NSButton(image: NSImage(named: NSImage.addTemplateName)!, target: self, action: #selector(add(sender:))) + plus.layoutAttribute = .right + window.addTitlebarAccessoryViewController(plus) + } runSetupIfNeeded() } diff --git a/Secretive/ContentView.swift b/Secretive/ContentView.swift index 8bc2e40..0eb07cd 100644 --- a/Secretive/ContentView.swift +++ b/Secretive/ContentView.swift @@ -13,21 +13,25 @@ struct ContentView: View { var body: some View { NavigationView { List(selection: $active) { - Section(header: Text(secureEnclave.name)) { - ForEach(secureEnclave.secrets) { secret in - NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: self.$active) { - Text(secret.name) - }.contextMenu { - Button(action: { self.delete(secret: secret) }) { - Text("Delete") + if secureEnclave.isAvailable { + Section(header: Text(secureEnclave.name)) { + ForEach(secureEnclave.secrets) { secret in + NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: self.$active) { + Text(secret.name) + }.contextMenu { + Button(action: { self.delete(secret: secret) }) { + Text("Delete") + } } } } } - Section(header: Text(smartCard.name)) { - ForEach(smartCard.secrets) { secret in - NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: self.$active) { - Text(secret.name) + if smartCard.isAvailable { + Section(header: Text(smartCard.name)) { + ForEach(smartCard.secrets) { secret in + NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: self.$active) { + Text(secret.name) + } } } } diff --git a/Secretive/Preview Content/PreviewStore.swift b/Secretive/Preview Content/PreviewStore.swift index cfc41d0..16f52a0 100644 --- a/Secretive/Preview Content/PreviewStore.swift +++ b/Secretive/Preview Content/PreviewStore.swift @@ -19,6 +19,7 @@ extension Preview { class Store: SecretStore, ObservableObject { + let isAvailable = true let name = "Preview Store" @Published var secrets: [Secret] = []