This commit is contained in:
Max Goedjen 2022-01-01 18:45:03 -08:00 committed by GitHub
parent fb6cebe92f
commit eb282b4116
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 18 additions and 2 deletions

View File

@ -0,0 +1,3 @@
# ````SmartCardSecretKit````
SmartCardSecretKit contains implementations of SecretKit protocols backed by a Smart Card.

View File

@ -1 +1,2 @@
/// Namespace for the Smart Card implementations.
public enum SmartCard {} public enum SmartCard {}

View File

@ -4,6 +4,7 @@ import SecretKit
extension SmartCard { extension SmartCard {
/// An implementation of Secret backed by a Smart Card.
public struct Secret: SecretKit.Secret { public struct Secret: SecretKit.Secret {
public let id: Data public let id: Data

View File

@ -8,6 +8,7 @@ import SecretKit
// ie, each token has its own Store. // ie, each token has its own Store.
extension SmartCard { extension SmartCard {
/// An implementation of Store backed by a Smart Card.
public class Store: SecretStore { public class Store: SecretStore {
@Published public var isAvailable: Bool = false @Published public var isAvailable: Bool = false
@ -17,6 +18,7 @@ extension SmartCard {
private let watcher = TKTokenWatcher() private let watcher = TKTokenWatcher()
private var tokenID: String? private var tokenID: String?
/// Initializes a Store.
public init() { public init() {
tokenID = watcher.nonSecureEnclaveTokens.first tokenID = watcher.nonSecureEnclaveTokens.first
watcher.setInsertionHandler { string in watcher.setInsertionHandler { string in
@ -56,7 +58,7 @@ extension SmartCard {
kSecAttrTokenID: tokenID, kSecAttrTokenID: tokenID,
kSecUseAuthenticationContext: context, kSecUseAuthenticationContext: context,
kSecReturnRef: true kSecReturnRef: true
] as CFDictionary ] as CFDictionary
var untyped: CFTypeRef? var untyped: CFTypeRef?
let status = SecItemCopyMatching(attributes, &untyped) let status = SecItemCopyMatching(attributes, &untyped)
if status != errSecSuccess { if status != errSecSuccess {
@ -91,11 +93,14 @@ extension SmartCard {
extension SmartCard.Store { extension SmartCard.Store {
/// Resets the token ID and reloads secrets.
/// - Parameter tokenID: The ID of the token that was removed.
private func smartcardRemoved(for tokenID: String? = nil) { private func smartcardRemoved(for tokenID: String? = nil) {
self.tokenID = nil self.tokenID = nil
reloadSecrets() reloadSecrets()
} }
/// Reloads all secrets from the store.
private func reloadSecrets() { private func reloadSecrets() {
DispatchQueue.main.async { DispatchQueue.main.async {
self.isAvailable = self.tokenID != nil self.isAvailable = self.tokenID != nil
@ -104,6 +109,7 @@ extension SmartCard.Store {
} }
} }
/// Loads all secrets from the store.
private func loadSecrets() { private func loadSecrets() {
guard let tokenID = tokenID else { return } guard let tokenID = tokenID else { return }
@ -131,7 +137,7 @@ extension SmartCard.Store {
kSecReturnRef: true, kSecReturnRef: true,
kSecMatchLimit: kSecMatchLimitAll, kSecMatchLimit: kSecMatchLimitAll,
kSecReturnAttributes: true kSecReturnAttributes: true
] as CFDictionary ] as CFDictionary
var untyped: CFTypeRef? var untyped: CFTypeRef?
SecItemCopyMatching(attributes, &untyped) SecItemCopyMatching(attributes, &untyped)
guard let typed = untyped as? [[CFString: Any]] else { return } guard let typed = untyped as? [[CFString: Any]] else { return }
@ -153,6 +159,7 @@ extension SmartCard.Store {
extension TKTokenWatcher { extension TKTokenWatcher {
/// All available tokens, excluding the Secure Enclave.
fileprivate var nonSecureEnclaveTokens: [String] { fileprivate var nonSecureEnclaveTokens: [String] {
tokenIDs.filter { !$0.contains("setoken") } tokenIDs.filter { !$0.contains("setoken") }
} }
@ -161,11 +168,15 @@ extension TKTokenWatcher {
extension SmartCard { extension SmartCard {
/// A wrapper around an error code reported by a Keychain API.
public struct KeychainError: Error { public struct KeychainError: Error {
/// The status code involved.
public let statusCode: OSStatus public let statusCode: OSStatus
} }
/// A signing-related error.
public struct SigningError: Error { public struct SigningError: Error {
/// The underlying error reported by the API, if one was returned.
public let error: SecurityError? public let error: SecurityError?
} }