Smart card working

This commit is contained in:
Max Goedjen 2020-03-06 00:52:44 -08:00
parent 9ebb14d1cf
commit 610bcfe023
No known key found for this signature in database
GPG Key ID: E58C21DD77B9B8E8
3 changed files with 92 additions and 11 deletions

View File

@ -5,7 +5,7 @@ import OSLog
@NSApplicationMain @NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate { class AppDelegate: NSObject, NSApplicationDelegate {
let store = SecureEnclave.Store() let store = SmartCard.Store()
let notifier = Notifier() let notifier = Notifier()
lazy var agent: Agent = { lazy var agent: Agent = {
Agent(store: store, notifier: notifier) Agent(store: store, notifier: notifier)

View File

@ -4,6 +4,10 @@
<dict> <dict>
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>
<key>com.apple.security.temporary-exception.mach-lookup.global-name</key>
<array>
<string>com.apple.ctkd.watcher-client</string>
</array>
<key>keychain-access-groups</key> <key>keychain-access-groups</key>
<array> <array>
<string>$(AppIdentifierPrefix)com.maxgoedjen.Secretive</string> <string>$(AppIdentifierPrefix)com.maxgoedjen.Secretive</string>

View File

@ -12,25 +12,102 @@ extension SmartCard {
public let name = NSLocalizedString("Smart Card", comment: "Smart Card") public let name = NSLocalizedString("Smart Card", comment: "Smart Card")
@Published public fileprivate(set) var secrets: [Secret] = [] @Published public fileprivate(set) var secrets: [Secret] = []
fileprivate let watcher = TKTokenWatcher() fileprivate let watcher = TKTokenWatcher()
fileprivate var id: String?
public init() { 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 } guard !string.contains("setoken") else { return }
let driver = TKSmartCardTokenDriver() self.id = string
let token = TKToken(tokenDriver: driver, instanceID: string) self.secrets.removeAll()
let session = TKSmartCardTokenSession(token: token) self.loadSecrets()
print(session)
} }
print(watcher.tokenIDs) loadSecrets()
} }
public func sign(data: Data, with secret: SmartCard.Secret) throws -> Data { // MARK: Public API
fatalError()
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<CFError>
} }