More progress.

This commit is contained in:
Max Goedjen
2020-03-08 20:03:40 -07:00
parent a9d7e7644e
commit 376f26ef38
11 changed files with 130 additions and 65 deletions

View File

@@ -10,23 +10,24 @@ extension SmartCard {
// TODO: Read actual smart card name, eg "YubiKey 5c"
@Published public var isAvailable: Bool = false
public let id = UUID()
public let name = NSLocalizedString("Smart Card", comment: "Smart Card")
@Published public fileprivate(set) var secrets: [Secret] = []
fileprivate let watcher = TKTokenWatcher()
fileprivate var id: String?
fileprivate var tokenID: String?
public init() {
id = watcher.tokenIDs.filter { !$0.contains("setoken") }.first
tokenID = watcher.tokenIDs.filter { !$0.contains("setoken") }.first
watcher.setInsertionHandler { string in
guard self.id == nil else { return }
guard self.tokenID == nil else { return }
guard !string.contains("setoken") else { return }
self.id = string
self.tokenID = string
self.reloadSecrets()
self.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: string)
}
if let id = id {
if let tokenID = tokenID {
self.isAvailable = true
self.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: id)
self.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: tokenID)
}
loadSecrets()
}
@@ -42,12 +43,12 @@ extension SmartCard {
}
public func sign(data: Data, with secret: SecretType) throws -> Data {
guard let id = id else { fatalError() }
guard let tokenID = tokenID else { fatalError() }
let attributes = [
kSecClass: kSecClassKey,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
kSecAttrApplicationLabel: secret.id as CFData,
kSecAttrTokenID: id,
kSecAttrTokenID: tokenID,
kSecReturnRef: true
] as CFDictionary
var untyped: CFTypeRef?
@@ -73,23 +74,23 @@ extension SmartCard {
extension SmartCard.Store {
fileprivate func smartcardRemoved(for tokenID: String? = nil) {
id = nil
self.tokenID = nil
reloadSecrets()
}
fileprivate func reloadSecrets() {
DispatchQueue.main.async {
self.isAvailable = self.id != nil
self.isAvailable = self.tokenID != nil
self.secrets.removeAll()
self.loadSecrets()
}
}
fileprivate func loadSecrets() {
guard let id = id else { return }
guard let tokenID = tokenID else { return }
let attributes = [
kSecClass: kSecClassKey,
kSecAttrTokenID: id,
kSecAttrTokenID: tokenID,
kSecReturnRef: true,
kSecMatchLimit: kSecMatchLimitAll,
kSecReturnAttributes: true
@@ -99,12 +100,12 @@ extension SmartCard.Store {
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 tokenID = $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)
return SmartCard.Secret(id: tokenID, name: name, publicKey: publicKey)
}
secrets.append(contentsOf: wrapped)
}