From fc2d740680e136cfe9f66fe704c8cd2d2fc0ef5b Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Thu, 5 Mar 2020 22:47:13 -0800 Subject: [PATCH 1/3] Dump --- SecretKit/SmartCard/SmartCardStore.swift | 9 ++++++++- Secretive/AppDelegate.swift | 2 +- Secretive/Secretive.entitlements | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/SecretKit/SmartCard/SmartCardStore.swift b/SecretKit/SmartCard/SmartCardStore.swift index da243c6..7e65c5f 100644 --- a/SecretKit/SmartCard/SmartCardStore.swift +++ b/SecretKit/SmartCard/SmartCardStore.swift @@ -2,6 +2,8 @@ import Foundation import Security import CryptoTokenKit +// TODO: Might need to split this up into "sub-stores?" +// ie, each token has its own Store. extension SmartCard { public class Store: SecretStore { @@ -13,7 +15,12 @@ extension SmartCard { public init() { watcher.setInsertionHandler { (string) in - print(string) + guard !string.contains("setoken") else { return } + let driver = TKSmartCardTokenDriver() + let token = TKToken(tokenDriver: driver, instanceID: string) + let session = TKSmartCardTo kenSession(token: token) + print(session) + } print(watcher.tokenIDs) } diff --git a/Secretive/AppDelegate.swift b/Secretive/AppDelegate.swift index ee03f86..420f4ae 100644 --- a/Secretive/AppDelegate.swift +++ b/Secretive/AppDelegate.swift @@ -12,7 +12,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ aNotification: Notification) { - let contentView = ContentView(store: secureEnclave) + let contentView = ContentView(store: yubikey) // Create the window and set the content view. window = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 480, height: 300), diff --git a/Secretive/Secretive.entitlements b/Secretive/Secretive.entitlements index 7f8f6ca..56b1906 100644 --- a/Secretive/Secretive.entitlements +++ b/Secretive/Secretive.entitlements @@ -3,7 +3,7 @@ com.apple.security.app-sandbox - + keychain-access-groups $(AppIdentifierPrefix)com.maxgoedjen.Secretive From 9ebb14d1cf4e3bc9f1d8f8516e879f6d7debbc53 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Thu, 5 Mar 2020 23:57:31 -0800 Subject: [PATCH 2/3] Merge --- SecretKit/SmartCard/SmartCardStore.swift | 2 +- Secretive/Secretive.entitlements | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/SecretKit/SmartCard/SmartCardStore.swift b/SecretKit/SmartCard/SmartCardStore.swift index 7e65c5f..e75ffda 100644 --- a/SecretKit/SmartCard/SmartCardStore.swift +++ b/SecretKit/SmartCard/SmartCardStore.swift @@ -18,7 +18,7 @@ extension SmartCard { guard !string.contains("setoken") else { return } let driver = TKSmartCardTokenDriver() let token = TKToken(tokenDriver: driver, instanceID: string) - let session = TKSmartCardTo kenSession(token: token) + let session = TKSmartCardTokenSession(token: token) print(session) } diff --git a/Secretive/Secretive.entitlements b/Secretive/Secretive.entitlements index 56b1906..6d010b7 100644 --- a/Secretive/Secretive.entitlements +++ b/Secretive/Secretive.entitlements @@ -3,7 +3,11 @@ com.apple.security.app-sandbox - + + com.apple.security.temporary-exception.mach-lookup.global-name + + com.apple.ctkd.watcher-client + keychain-access-groups $(AppIdentifierPrefix)com.maxgoedjen.Secretive From 610bcfe02392424a489d08fab3931b9c5444e24d Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Fri, 6 Mar 2020 00:52:44 -0800 Subject: [PATCH 3/3] Smart card working --- SecretAgent/AppDelegate.swift | 2 +- SecretAgent/SecretAgent.entitlements | 4 + SecretKit/SmartCard/SmartCardStore.swift | 97 +++++++++++++++++++++--- 3 files changed, 92 insertions(+), 11 deletions(-) diff --git a/SecretAgent/AppDelegate.swift b/SecretAgent/AppDelegate.swift index 5421581..1791fa9 100644 --- a/SecretAgent/AppDelegate.swift +++ b/SecretAgent/AppDelegate.swift @@ -5,7 +5,7 @@ import OSLog @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { - let store = SecureEnclave.Store() + let store = SmartCard.Store() let notifier = Notifier() lazy var agent: Agent = { Agent(store: store, notifier: notifier) diff --git a/SecretAgent/SecretAgent.entitlements b/SecretAgent/SecretAgent.entitlements index 7f8f6ca..6d010b7 100644 --- a/SecretAgent/SecretAgent.entitlements +++ b/SecretAgent/SecretAgent.entitlements @@ -4,6 +4,10 @@ com.apple.security.app-sandbox + com.apple.security.temporary-exception.mach-lookup.global-name + + com.apple.ctkd.watcher-client + keychain-access-groups $(AppIdentifierPrefix)com.maxgoedjen.Secretive diff --git a/SecretKit/SmartCard/SmartCardStore.swift b/SecretKit/SmartCard/SmartCardStore.swift index e75ffda..c5e7f02 100644 --- a/SecretKit/SmartCard/SmartCardStore.swift +++ b/SecretKit/SmartCard/SmartCardStore.swift @@ -12,25 +12,102 @@ extension SmartCard { public let name = NSLocalizedString("Smart Card", comment: "Smart Card") @Published public fileprivate(set) var secrets: [Secret] = [] fileprivate let watcher = TKTokenWatcher() + fileprivate var id: String? public init() { - watcher.setInsertionHandler { (string) in + id = watcher.tokenIDs.filter { !$0.contains("setoken") }.first + watcher.setInsertionHandler { string in + guard self.id == nil else { return } guard !string.contains("setoken") else { return } - let driver = TKSmartCardTokenDriver() - let token = TKToken(tokenDriver: driver, instanceID: string) - let session = TKSmartCardTokenSession(token: token) - print(session) - + self.id = string + self.secrets.removeAll() + self.loadSecrets() } - print(watcher.tokenIDs) + loadSecrets() } - public func sign(data: Data, with secret: SmartCard.Secret) throws -> Data { - fatalError() + // MARK: Public API + + public func create(name: String) throws { + fatalError("Keys must be created on the smart card.") } - public func delete(secret: SmartCard.Secret) throws { + public func delete(secret: Secret) throws { + fatalError("Keys must be deleted on the smart card.") + } + + public func sign(data: Data, with secret: SecretType) throws -> Data { + guard let id = id else { fatalError() } + let attributes = [ + kSecClass: kSecClassKey, + kSecAttrKeyClass: kSecAttrKeyClassPrivate, + kSecAttrApplicationLabel: secret.id as CFData, + kSecAttrTokenID: id, + kSecReturnRef: true + ] as CFDictionary + var untyped: CFTypeRef? + let status = SecItemCopyMatching(attributes, &untyped) + if status != errSecSuccess { + throw KeychainError(statusCode: status) + } + guard let untypedSafe = untyped else { + throw KeychainError(statusCode: errSecSuccess) + } + let key = untypedSafe as! SecKey + var signError: SecurityError? + guard let signature = SecKeyCreateSignature(key, .ecdsaSignatureMessageX962SHA256, data as CFData, &signError) else { + throw SigningError(error: signError) + } + return signature as Data } } + +} + +extension SmartCard.Store { + + fileprivate func loadSecrets() { + guard let id = id else { return } + let attributes = [ + kSecClass: kSecClassKey, + kSecAttrTokenID: id, + kSecReturnRef: true, + kSecMatchLimit: kSecMatchLimitAll, + kSecReturnAttributes: true + ] as CFDictionary + var untyped: CFTypeRef? + let status = SecItemCopyMatching(attributes, &untyped) + print(status) + guard let typed = untyped as? [[CFString: Any]] else { return } + let wrapped: [SmartCard.Secret] = typed.map { + let name = $0[kSecAttrLabel] as? String ?? "Unnamed" + let id = $0[kSecAttrApplicationLabel] as! Data + let publicKeyRef = $0[kSecValueRef] as! SecKey + let publicKeySecRef = SecKeyCopyPublicKey(publicKeyRef)! + let publicKeyAttributes = SecKeyCopyAttributes(publicKeySecRef) as! [CFString: Any] + let publicKey = publicKeyAttributes[kSecValueData] as! Data + return SmartCard.Secret(id: id, name: name, publicKey: publicKey) + } + secrets.append(contentsOf: wrapped) + } + +} + +extension SmartCard { + + public struct KeychainError: Error { + public let statusCode: OSStatus + } + + public struct SigningError: Error { + public let error: SecurityError? + } + +} + +extension SmartCard { + + public typealias SecurityError = Unmanaged + }