mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-04-18 05:22:11 +00:00
Infrastructure.
This commit is contained in:
parent
33ecabef20
commit
29be661b46
@ -21,6 +21,12 @@ public protocol SecretStoreModifiable: SecretStore {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public protocol SecretStoreAuthenticationPersistable: SecretStore {
|
||||||
|
|
||||||
|
func persistAuthentication(secret: SecretType, forDuration: TimeInterval) throws
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
extension NSNotification.Name {
|
extension NSNotification.Name {
|
||||||
|
|
||||||
static let secretStoreUpdated = NSNotification.Name("com.maxgoedjen.Secretive.secretStore.updated")
|
static let secretStoreUpdated = NSNotification.Name("com.maxgoedjen.Secretive.secretStore.updated")
|
||||||
|
@ -10,6 +10,7 @@ extension SecureEnclave {
|
|||||||
public let algorithm = Algorithm.ellipticCurve
|
public let algorithm = Algorithm.ellipticCurve
|
||||||
public let keySize = 256
|
public let keySize = 256
|
||||||
public let publicKey: Data
|
public let publicKey: Data
|
||||||
|
public let requiresAuthentication: Bool
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import LocalAuthentication
|
|||||||
|
|
||||||
extension SecureEnclave {
|
extension SecureEnclave {
|
||||||
|
|
||||||
public class Store: SecretStoreModifiable {
|
public class Store: SecretStoreModifiable, SecretStoreAuthenticationPersistable {
|
||||||
|
|
||||||
public var isAvailable: Bool {
|
public var isAvailable: Bool {
|
||||||
// For some reason, as of build time, CryptoKit.SecureEnclave.isAvailable always returns false
|
// For some reason, as of build time, CryptoKit.SecureEnclave.isAvailable always returns false
|
||||||
@ -16,7 +16,9 @@ extension SecureEnclave {
|
|||||||
public let id = UUID()
|
public let id = UUID()
|
||||||
public let name = NSLocalizedString("Secure Enclave", comment: "Secure Enclave")
|
public let name = NSLocalizedString("Secure Enclave", comment: "Secure Enclave")
|
||||||
@Published public private(set) var secrets: [Secret] = []
|
@Published public private(set) var secrets: [Secret] = []
|
||||||
private var existingLAContexts: [Secret: LAContext] = [:]
|
|
||||||
|
private var pendingAuthenticationContext: PersistentAuthenticationContext? = nil
|
||||||
|
private var persistedAuthenticationContexts: [Secret: PersistentAuthenticationContext] = [:]
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { _ in
|
DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { _ in
|
||||||
@ -96,16 +98,15 @@ extension SecureEnclave {
|
|||||||
|
|
||||||
public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> Data {
|
public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> Data {
|
||||||
let context: LAContext
|
let context: LAContext
|
||||||
if let existing = existingLAContexts[secret] {
|
if let existing = persistedAuthenticationContexts[secret], existing.valid {
|
||||||
context = existing
|
context = existing.context
|
||||||
} else {
|
} else {
|
||||||
let newContext = LAContext()
|
let newContext = LAContext()
|
||||||
newContext.localizedCancelTitle = "Deny"
|
newContext.localizedCancelTitle = "Deny"
|
||||||
newContext.touchIDAuthenticationAllowableReuseDuration = 60 * 5
|
pendingAuthenticationContext = PersistentAuthenticationContext(secret: secret, context: newContext, expiration: Date(timeIntervalSinceNow: Constants.durationOneMinute))
|
||||||
existingLAContexts[secret] = newContext
|
|
||||||
context = newContext
|
context = newContext
|
||||||
}
|
}
|
||||||
context.localizedReason = "sign a request from \"\(provenance.origin.displayName)\" using secret \"\(secret.name)\""
|
context.localizedReason = "saign a request from \"\(provenance.origin.displayName)\" using secret \"\(secret.name)\""
|
||||||
let attributes = [
|
let attributes = [
|
||||||
kSecClass: kSecClassKey,
|
kSecClass: kSecClassKey,
|
||||||
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
||||||
@ -132,6 +133,12 @@ extension SecureEnclave {
|
|||||||
return signature as Data
|
return signature as Data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func persistAuthentication(secret: Secret, forDuration: TimeInterval) throws {
|
||||||
|
guard secret == pendingAuthenticationContext?.secret else { throw AuthenticationPersistenceError() }
|
||||||
|
persistedAuthenticationContexts[secret] = pendingAuthenticationContext
|
||||||
|
pendingAuthenticationContext = nil
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -165,7 +172,8 @@ extension SecureEnclave.Store {
|
|||||||
let publicKeyRef = $0[kSecValueRef] as! SecKey
|
let publicKeyRef = $0[kSecValueRef] as! SecKey
|
||||||
let publicKeyAttributes = SecKeyCopyAttributes(publicKeyRef) as! [CFString: Any]
|
let publicKeyAttributes = SecKeyCopyAttributes(publicKeyRef) as! [CFString: Any]
|
||||||
let publicKey = publicKeyAttributes[kSecValueData] as! Data
|
let publicKey = publicKeyAttributes[kSecValueData] as! Data
|
||||||
return SecureEnclave.Secret(id: id, name: name, publicKey: publicKey)
|
// TODO: FIX
|
||||||
|
return SecureEnclave.Secret(id: id, name: name, publicKey: publicKey, requiresAuthentication: false)
|
||||||
}
|
}
|
||||||
secrets.append(contentsOf: wrapped)
|
secrets.append(contentsOf: wrapped)
|
||||||
}
|
}
|
||||||
@ -199,6 +207,9 @@ extension SecureEnclave {
|
|||||||
public let error: SecurityError?
|
public let error: SecurityError?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct AuthenticationPersistenceError: Error {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SecureEnclave {
|
extension SecureEnclave {
|
||||||
@ -212,6 +223,25 @@ extension SecureEnclave {
|
|||||||
enum Constants {
|
enum Constants {
|
||||||
static let keyTag = "com.maxgoedjen.secretive.secureenclave.key".data(using: .utf8)! as CFData
|
static let keyTag = "com.maxgoedjen.secretive.secureenclave.key".data(using: .utf8)! as CFData
|
||||||
static let keyType = kSecAttrKeyTypeECSECPrimeRandom
|
static let keyType = kSecAttrKeyTypeECSECPrimeRandom
|
||||||
|
static let durationOneMinute: TimeInterval = 60
|
||||||
|
static let durationFiveMinutes = durationOneMinute * 5
|
||||||
|
static let durationSixtyMinutes = durationOneMinute * 60
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SecureEnclave {
|
||||||
|
|
||||||
|
private struct PersistentAuthenticationContext {
|
||||||
|
|
||||||
|
let secret: Secret
|
||||||
|
let context: LAContext
|
||||||
|
// TODO: monotonic time instead of Date() to prevent people setting the clock back.
|
||||||
|
let expiration: Date
|
||||||
|
|
||||||
|
var valid: Bool {
|
||||||
|
Date() < expiration
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user