mirror of
https://github.com/maxgoedjen/secretive.git
synced 2024-11-25 06:57:07 +00:00
refactoring KeychainDictionary
from CFDictionary to NSDictionary high-level API, supports ARC, which simplifies development and reduces the likelihood of memory leaks.
This commit is contained in:
parent
ad56019901
commit
fd7a9c2f7a
@ -2,12 +2,6 @@ import Foundation
|
|||||||
|
|
||||||
public typealias SecurityError = Unmanaged<CFError>
|
public typealias SecurityError = Unmanaged<CFError>
|
||||||
|
|
||||||
/// Wraps a Swift dictionary in a CFDictionary.
|
|
||||||
/// - Parameter dictionary: The Swift dictionary to wrap.
|
|
||||||
/// - Returns: A CFDictionary containing the keys and values.
|
|
||||||
public func KeychainDictionary(_ dictionary: [CFString: Any]) -> CFDictionary {
|
|
||||||
dictionary as CFDictionary
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension CFError {
|
public extension CFError {
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ extension SecureEnclave {
|
|||||||
throw error.takeRetainedValue() as Error
|
throw error.takeRetainedValue() as Error
|
||||||
}
|
}
|
||||||
|
|
||||||
let attributes = KeychainDictionary([
|
let attributes : NSDictionary = [
|
||||||
kSecAttrLabel: name,
|
kSecAttrLabel: name,
|
||||||
kSecAttrKeyType: Constants.keyType,
|
kSecAttrKeyType: Constants.keyType,
|
||||||
kSecAttrTokenID: kSecAttrTokenIDSecureEnclave,
|
kSecAttrTokenID: kSecAttrTokenIDSecureEnclave,
|
||||||
@ -58,7 +58,7 @@ extension SecureEnclave {
|
|||||||
kSecAttrIsPermanent: true,
|
kSecAttrIsPermanent: true,
|
||||||
kSecAttrAccessControl: access
|
kSecAttrAccessControl: access
|
||||||
]
|
]
|
||||||
])
|
]
|
||||||
|
|
||||||
var createKeyError: SecurityError?
|
var createKeyError: SecurityError?
|
||||||
let keypair = SecKeyCreateRandomKey(attributes, &createKeyError)
|
let keypair = SecKeyCreateRandomKey(attributes, &createKeyError)
|
||||||
@ -73,10 +73,10 @@ extension SecureEnclave {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func delete(secret: Secret) throws {
|
public func delete(secret: Secret) throws {
|
||||||
let deleteAttributes = KeychainDictionary([
|
let deleteAttributes : NSDictionary = [
|
||||||
kSecClass: kSecClassKey,
|
kSecClass: kSecClassKey,
|
||||||
kSecAttrApplicationLabel: secret.id as CFData
|
kSecAttrApplicationLabel: secret.id
|
||||||
])
|
]
|
||||||
let status = SecItemDelete(deleteAttributes)
|
let status = SecItemDelete(deleteAttributes)
|
||||||
if status != errSecSuccess {
|
if status != errSecSuccess {
|
||||||
throw KeychainError(statusCode: status)
|
throw KeychainError(statusCode: status)
|
||||||
@ -85,14 +85,14 @@ extension SecureEnclave {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func update(secret: Secret, name: String) throws {
|
public func update(secret: Secret, name: String) throws {
|
||||||
let updateQuery = KeychainDictionary([
|
let updateQuery : NSDictionary = [
|
||||||
kSecClass: kSecClassKey,
|
kSecClass: kSecClassKey,
|
||||||
kSecAttrApplicationLabel: secret.id as CFData
|
kSecAttrApplicationLabel: secret.id
|
||||||
])
|
]
|
||||||
|
|
||||||
let updatedAttributes = KeychainDictionary([
|
let updatedAttributes : NSDictionary = [
|
||||||
kSecAttrLabel: name,
|
kSecAttrLabel: name,
|
||||||
])
|
]
|
||||||
|
|
||||||
let status = SecItemUpdate(updateQuery, updatedAttributes)
|
let status = SecItemUpdate(updateQuery, updatedAttributes)
|
||||||
if status != errSecSuccess {
|
if status != errSecSuccess {
|
||||||
@ -111,16 +111,16 @@ extension SecureEnclave {
|
|||||||
context = newContext
|
context = newContext
|
||||||
}
|
}
|
||||||
context.localizedReason = String(localized: "auth_context_request_signature_description_\(provenance.origin.displayName)_\(secret.name)")
|
context.localizedReason = String(localized: "auth_context_request_signature_description_\(provenance.origin.displayName)_\(secret.name)")
|
||||||
let attributes = KeychainDictionary([
|
let attributes : NSDictionary = [
|
||||||
kSecClass: kSecClassKey,
|
kSecClass: kSecClassKey,
|
||||||
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
||||||
kSecAttrApplicationLabel: secret.id as CFData,
|
kSecAttrApplicationLabel: secret.id,
|
||||||
kSecAttrKeyType: Constants.keyType,
|
kSecAttrKeyType: Constants.keyType,
|
||||||
kSecAttrTokenID: kSecAttrTokenIDSecureEnclave,
|
kSecAttrTokenID: kSecAttrTokenIDSecureEnclave,
|
||||||
kSecAttrApplicationTag: Constants.keyTag,
|
kSecAttrApplicationTag: Constants.keyTag,
|
||||||
kSecUseAuthenticationContext: context,
|
kSecUseAuthenticationContext: context,
|
||||||
kSecReturnRef: true
|
kSecReturnRef: true
|
||||||
])
|
]
|
||||||
var untyped: CFTypeRef?
|
var untyped: CFTypeRef?
|
||||||
let status = SecItemCopyMatching(attributes, &untyped)
|
let status = SecItemCopyMatching(attributes, &untyped)
|
||||||
if status != errSecSuccess {
|
if status != errSecSuccess {
|
||||||
@ -142,16 +142,16 @@ extension SecureEnclave {
|
|||||||
let context = LAContext()
|
let context = LAContext()
|
||||||
context.localizedReason = String(localized: "auth_context_request_verify_description_\(secret.name)")
|
context.localizedReason = String(localized: "auth_context_request_verify_description_\(secret.name)")
|
||||||
context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
|
context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
|
||||||
let attributes = KeychainDictionary([
|
let attributes : NSDictionary = [
|
||||||
kSecClass: kSecClassKey,
|
kSecClass: kSecClassKey,
|
||||||
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
||||||
kSecAttrApplicationLabel: secret.id as CFData,
|
kSecAttrApplicationLabel: secret.id,
|
||||||
kSecAttrKeyType: Constants.keyType,
|
kSecAttrKeyType: Constants.keyType,
|
||||||
kSecAttrTokenID: kSecAttrTokenIDSecureEnclave,
|
kSecAttrTokenID: kSecAttrTokenIDSecureEnclave,
|
||||||
kSecAttrApplicationTag: Constants.keyTag,
|
kSecAttrApplicationTag: Constants.keyTag,
|
||||||
kSecUseAuthenticationContext: context,
|
kSecUseAuthenticationContext: context,
|
||||||
kSecReturnRef: true
|
kSecReturnRef: true
|
||||||
])
|
]
|
||||||
var verifyError: SecurityError?
|
var verifyError: SecurityError?
|
||||||
var untyped: CFTypeRef?
|
var untyped: CFTypeRef?
|
||||||
let status = SecItemCopyMatching(attributes, &untyped)
|
let status = SecItemCopyMatching(attributes, &untyped)
|
||||||
@ -225,7 +225,7 @@ extension SecureEnclave.Store {
|
|||||||
|
|
||||||
/// Loads all secrets from the store.
|
/// Loads all secrets from the store.
|
||||||
private func loadSecrets() {
|
private func loadSecrets() {
|
||||||
let publicAttributes = KeychainDictionary([
|
let publicAttributes : NSDictionary = [
|
||||||
kSecClass: kSecClassKey,
|
kSecClass: kSecClassKey,
|
||||||
kSecAttrKeyType: SecureEnclave.Constants.keyType,
|
kSecAttrKeyType: SecureEnclave.Constants.keyType,
|
||||||
kSecAttrApplicationTag: SecureEnclave.Constants.keyTag,
|
kSecAttrApplicationTag: SecureEnclave.Constants.keyTag,
|
||||||
@ -233,11 +233,11 @@ extension SecureEnclave.Store {
|
|||||||
kSecReturnRef: true,
|
kSecReturnRef: true,
|
||||||
kSecMatchLimit: kSecMatchLimitAll,
|
kSecMatchLimit: kSecMatchLimitAll,
|
||||||
kSecReturnAttributes: true
|
kSecReturnAttributes: true
|
||||||
])
|
]
|
||||||
var publicUntyped: CFTypeRef?
|
var publicUntyped: CFTypeRef?
|
||||||
SecItemCopyMatching(publicAttributes, &publicUntyped)
|
SecItemCopyMatching(publicAttributes, &publicUntyped)
|
||||||
guard let publicTyped = publicUntyped as? [[CFString: Any]] else { return }
|
guard let publicTyped = publicUntyped as? [[CFString: Any]] else { return }
|
||||||
let privateAttributes = KeychainDictionary([
|
let privateAttributes : NSDictionary = [
|
||||||
kSecClass: kSecClassKey,
|
kSecClass: kSecClassKey,
|
||||||
kSecAttrKeyType: SecureEnclave.Constants.keyType,
|
kSecAttrKeyType: SecureEnclave.Constants.keyType,
|
||||||
kSecAttrApplicationTag: SecureEnclave.Constants.keyTag,
|
kSecAttrApplicationTag: SecureEnclave.Constants.keyTag,
|
||||||
@ -245,7 +245,7 @@ extension SecureEnclave.Store {
|
|||||||
kSecReturnRef: true,
|
kSecReturnRef: true,
|
||||||
kSecMatchLimit: kSecMatchLimitAll,
|
kSecMatchLimit: kSecMatchLimitAll,
|
||||||
kSecReturnAttributes: true
|
kSecReturnAttributes: true
|
||||||
])
|
]
|
||||||
var privateUntyped: CFTypeRef?
|
var privateUntyped: CFTypeRef?
|
||||||
SecItemCopyMatching(privateAttributes, &privateUntyped)
|
SecItemCopyMatching(privateAttributes, &privateUntyped)
|
||||||
guard let privateTyped = privateUntyped as? [[CFString: Any]] else { return }
|
guard let privateTyped = privateUntyped as? [[CFString: Any]] else { return }
|
||||||
@ -283,7 +283,7 @@ extension SecureEnclave.Store {
|
|||||||
/// - publicKey: The public key to save.
|
/// - publicKey: The public key to save.
|
||||||
/// - name: A user-facing name for the key.
|
/// - name: A user-facing name for the key.
|
||||||
private func savePublicKey(_ publicKey: SecKey, name: String) throws {
|
private func savePublicKey(_ publicKey: SecKey, name: String) throws {
|
||||||
let attributes = KeychainDictionary([
|
let attributes : NSDictionary = [
|
||||||
kSecClass: kSecClassKey,
|
kSecClass: kSecClassKey,
|
||||||
kSecAttrKeyType: SecureEnclave.Constants.keyType,
|
kSecAttrKeyType: SecureEnclave.Constants.keyType,
|
||||||
kSecAttrKeyClass: kSecAttrKeyClassPublic,
|
kSecAttrKeyClass: kSecAttrKeyClassPublic,
|
||||||
@ -292,7 +292,7 @@ extension SecureEnclave.Store {
|
|||||||
kSecAttrIsPermanent: true,
|
kSecAttrIsPermanent: true,
|
||||||
kSecReturnData: true,
|
kSecReturnData: true,
|
||||||
kSecAttrLabel: name
|
kSecAttrLabel: name
|
||||||
])
|
]
|
||||||
let status = SecItemAdd(attributes, nil)
|
let status = SecItemAdd(attributes, nil)
|
||||||
if status != errSecSuccess {
|
if status != errSecSuccess {
|
||||||
throw KeychainError(statusCode: status)
|
throw KeychainError(statusCode: status)
|
||||||
|
@ -52,14 +52,15 @@ extension SmartCard {
|
|||||||
let context = LAContext()
|
let context = LAContext()
|
||||||
context.localizedReason = String(localized: "auth_context_request_signature_description_\(provenance.origin.displayName)_\(secret.name)")
|
context.localizedReason = String(localized: "auth_context_request_signature_description_\(provenance.origin.displayName)_\(secret.name)")
|
||||||
context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
|
context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
|
||||||
let attributes = KeychainDictionary([
|
let attributes : NSDictionary = [
|
||||||
kSecClass: kSecClassKey,
|
kSecClass: kSecClassKey,
|
||||||
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
||||||
kSecAttrApplicationLabel: secret.id as CFData,
|
kSecAttrApplicationLabel: secret.id,
|
||||||
kSecAttrTokenID: tokenID,
|
kSecAttrTokenID: tokenID,
|
||||||
kSecUseAuthenticationContext: context,
|
kSecUseAuthenticationContext: context,
|
||||||
kSecReturnRef: true
|
kSecReturnRef: true
|
||||||
])
|
]
|
||||||
|
|
||||||
var untyped: CFTypeRef?
|
var untyped: CFTypeRef?
|
||||||
let status = SecItemCopyMatching(attributes, &untyped)
|
let status = SecItemCopyMatching(attributes, &untyped)
|
||||||
if status != errSecSuccess {
|
if status != errSecSuccess {
|
||||||
@ -77,11 +78,12 @@ extension SmartCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func verify(signature: Data, for data: Data, with secret: Secret) throws -> Bool {
|
public func verify(signature: Data, for data: Data, with secret: Secret) throws -> Bool {
|
||||||
let attributes = KeychainDictionary([
|
let attributes : NSDictionary = [
|
||||||
kSecAttrKeyType: secret.algorithm.secAttrKeyType,
|
kSecAttrKeyType: secret.algorithm.secAttrKeyType,
|
||||||
kSecAttrKeySizeInBits: secret.keySize,
|
kSecAttrKeySizeInBits: secret.keySize,
|
||||||
kSecAttrKeyClass: kSecAttrKeyClassPublic
|
kSecAttrKeyClass: kSecAttrKeyClassPublic
|
||||||
])
|
]
|
||||||
|
|
||||||
var verifyError: SecurityError?
|
var verifyError: SecurityError?
|
||||||
let untyped: CFTypeRef? = SecKeyCreateWithData(secret.publicKey as CFData, attributes, &verifyError)
|
let untyped: CFTypeRef? = SecKeyCreateWithData(secret.publicKey as CFData, attributes, &verifyError)
|
||||||
guard let untypedSafe = untyped else {
|
guard let untypedSafe = untyped else {
|
||||||
@ -145,13 +147,13 @@ extension SmartCard.Store {
|
|||||||
name = fallbackName
|
name = fallbackName
|
||||||
}
|
}
|
||||||
|
|
||||||
let attributes = KeychainDictionary([
|
let attributes : NSDictionary = [
|
||||||
kSecClass: kSecClassKey,
|
kSecClass: kSecClassKey,
|
||||||
kSecAttrTokenID: tokenID,
|
kSecAttrTokenID: tokenID,
|
||||||
kSecReturnRef: true,
|
kSecReturnRef: true,
|
||||||
kSecMatchLimit: kSecMatchLimitAll,
|
kSecMatchLimit: kSecMatchLimitAll,
|
||||||
kSecReturnAttributes: true
|
kSecReturnAttributes: true
|
||||||
])
|
]
|
||||||
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 }
|
||||||
@ -185,12 +187,12 @@ extension SmartCard.Store {
|
|||||||
let context = LAContext()
|
let context = LAContext()
|
||||||
context.localizedReason = String(localized: "auth_context_request_encrypt_description_\(secret.name)")
|
context.localizedReason = String(localized: "auth_context_request_encrypt_description_\(secret.name)")
|
||||||
context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
|
context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
|
||||||
let attributes = KeychainDictionary([
|
let attributes : NSDictionary = [
|
||||||
kSecAttrKeyType: secret.algorithm.secAttrKeyType,
|
kSecAttrKeyType: secret.algorithm.secAttrKeyType,
|
||||||
kSecAttrKeySizeInBits: secret.keySize,
|
kSecAttrKeySizeInBits: secret.keySize,
|
||||||
kSecAttrKeyClass: kSecAttrKeyClassPublic,
|
kSecAttrKeyClass: kSecAttrKeyClassPublic,
|
||||||
kSecUseAuthenticationContext: context
|
kSecUseAuthenticationContext: context
|
||||||
])
|
]
|
||||||
var encryptError: SecurityError?
|
var encryptError: SecurityError?
|
||||||
let untyped: CFTypeRef? = SecKeyCreateWithData(secret.publicKey as CFData, attributes, &encryptError)
|
let untyped: CFTypeRef? = SecKeyCreateWithData(secret.publicKey as CFData, attributes, &encryptError)
|
||||||
guard let untypedSafe = untyped else {
|
guard let untypedSafe = untyped else {
|
||||||
@ -214,14 +216,14 @@ extension SmartCard.Store {
|
|||||||
let context = LAContext()
|
let context = LAContext()
|
||||||
context.localizedReason = String(localized: "auth_context_request_decrypt_description_\(secret.name)")
|
context.localizedReason = String(localized: "auth_context_request_decrypt_description_\(secret.name)")
|
||||||
context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
|
context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
|
||||||
let attributes = KeychainDictionary([
|
let attributes : NSDictionary = [
|
||||||
kSecClass: kSecClassKey,
|
kSecClass: kSecClassKey,
|
||||||
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
||||||
kSecAttrApplicationLabel: secret.id as CFData,
|
kSecAttrApplicationLabel: secret.id,
|
||||||
kSecAttrTokenID: tokenID,
|
kSecAttrTokenID: tokenID,
|
||||||
kSecUseAuthenticationContext: context,
|
kSecUseAuthenticationContext: context,
|
||||||
kSecReturnRef: true
|
kSecReturnRef: true
|
||||||
])
|
]
|
||||||
var untyped: CFTypeRef?
|
var untyped: CFTypeRef?
|
||||||
let status = SecItemCopyMatching(attributes, &untyped)
|
let status = SecItemCopyMatching(attributes, &untyped)
|
||||||
if status != errSecSuccess {
|
if status != errSecSuccess {
|
||||||
|
@ -27,7 +27,7 @@ extension Stub {
|
|||||||
flags,
|
flags,
|
||||||
nil) as Any
|
nil) as Any
|
||||||
|
|
||||||
let attributes = KeychainDictionary([
|
let attributes : NSDictionary = [
|
||||||
kSecAttrLabel: name,
|
kSecAttrLabel: name,
|
||||||
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
|
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
|
||||||
kSecAttrKeySizeInBits: size,
|
kSecAttrKeySizeInBits: size,
|
||||||
@ -35,7 +35,7 @@ extension Stub {
|
|||||||
kSecAttrIsPermanent: true,
|
kSecAttrIsPermanent: true,
|
||||||
kSecAttrAccessControl: access
|
kSecAttrAccessControl: access
|
||||||
]
|
]
|
||||||
])
|
]
|
||||||
|
|
||||||
let privateKey = SecKeyCreateRandomKey(attributes, nil)!
|
let privateKey = SecKeyCreateRandomKey(attributes, nil)!
|
||||||
let publicKey = SecKeyCopyPublicKey(privateKey)!
|
let publicKey = SecKeyCopyPublicKey(privateKey)!
|
||||||
@ -52,21 +52,21 @@ extension Stub {
|
|||||||
guard !shouldThrow else {
|
guard !shouldThrow else {
|
||||||
throw NSError(domain: "test", code: 0, userInfo: nil)
|
throw NSError(domain: "test", code: 0, userInfo: nil)
|
||||||
}
|
}
|
||||||
let privateKey = SecKeyCreateWithData(secret.privateKey as CFData, KeychainDictionary([
|
let privateKey = SecKeyCreateWithData(secret.privateKey as CFData, [
|
||||||
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
|
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
|
||||||
kSecAttrKeySizeInBits: secret.keySize,
|
kSecAttrKeySizeInBits: secret.keySize,
|
||||||
kSecAttrKeyClass: kSecAttrKeyClassPrivate
|
kSecAttrKeyClass: kSecAttrKeyClassPrivate
|
||||||
])
|
] as CFDictionary
|
||||||
, nil)!
|
, nil)!
|
||||||
return SecKeyCreateSignature(privateKey, signatureAlgorithm(for: secret), data as CFData, nil)! as Data
|
return SecKeyCreateSignature(privateKey, signatureAlgorithm(for: secret), data as CFData, nil)! as Data
|
||||||
}
|
}
|
||||||
|
|
||||||
public func verify(signature: Data, for data: Data, with secret: Stub.Secret) throws -> Bool {
|
public func verify(signature: Data, for data: Data, with secret: Stub.Secret) throws -> Bool {
|
||||||
let attributes = KeychainDictionary([
|
let attributes: NSDictionary = [
|
||||||
kSecAttrKeyType: secret.algorithm.secAttrKeyType,
|
kSecAttrKeyType: secret.algorithm.secAttrKeyType,
|
||||||
kSecAttrKeySizeInBits: secret.keySize,
|
kSecAttrKeySizeInBits: secret.keySize,
|
||||||
kSecAttrKeyClass: kSecAttrKeyClassPublic
|
kSecAttrKeyClass: kSecAttrKeyClassPublic
|
||||||
])
|
]
|
||||||
var verifyError: Unmanaged<CFError>?
|
var verifyError: Unmanaged<CFError>?
|
||||||
let untyped: CFTypeRef? = SecKeyCreateWithData(secret.publicKey as CFData, attributes, &verifyError)
|
let untyped: CFTypeRef? = SecKeyCreateWithData(secret.publicKey as CFData, attributes, &verifyError)
|
||||||
guard let untypedSafe = untyped else {
|
guard let untypedSafe = untyped else {
|
||||||
|
Loading…
Reference in New Issue
Block a user