mirror of
https://github.com/maxgoedjen/secretive.git
synced 2026-01-06 16:42:01 +01:00
Merge 825824d5cb into 9f2c6d9e84
This commit is contained in:
commit
34c4cf0988
@ -18,6 +18,7 @@ extension SecureEnclave {
|
||||
public let id = UUID()
|
||||
public let name = String(localized: .secureEnclave)
|
||||
private let persistentAuthenticationHandler = PersistentAuthenticationHandler<Secret>()
|
||||
private let signingSerializer = SigningSerializer()
|
||||
|
||||
/// Initializes a Store.
|
||||
@MainActor public init() {
|
||||
@ -38,6 +39,16 @@ extension SecureEnclave {
|
||||
// MARK: SecretStore
|
||||
|
||||
public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) async throws -> Data {
|
||||
if secret.attributes.authentication.required {
|
||||
return try await signingSerializer.serialize { [self] in
|
||||
try await self.performSign(data: data, with: secret, for: provenance)
|
||||
}
|
||||
} else {
|
||||
return try await performSign(data: data, with: secret, for: provenance)
|
||||
}
|
||||
}
|
||||
|
||||
private func performSign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) async throws -> Data {
|
||||
var context: LAContext
|
||||
if let existing = await persistentAuthenticationHandler.existingPersistedAuthenticationContext(secret: secret) {
|
||||
context = unsafe existing.context
|
||||
@ -85,7 +96,6 @@ extension SecureEnclave {
|
||||
default:
|
||||
throw UnsupportedAlgorithmError()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public func existingPersistedAuthenticationContext(secret: Secret) async -> PersistedAuthenticationContext? {
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
import Foundation
|
||||
|
||||
/// Serializes signing operations for protected keys to prevent LAContext conflicts.
|
||||
/// macOS only allows one biometric authentication prompt at a time, so concurrent
|
||||
/// requests for protected Secure Enclave keys must be queued.
|
||||
actor SigningSerializer {
|
||||
private var isProcessing = false
|
||||
private var waiters: [CheckedContinuation<Void, Never>] = []
|
||||
|
||||
func serialize<T: Sendable>(operation: @escaping @Sendable () async throws -> T) async throws -> T {
|
||||
// If someone is already processing, wait in line
|
||||
if isProcessing {
|
||||
await withCheckedContinuation { continuation in
|
||||
waiters.append(continuation)
|
||||
}
|
||||
}
|
||||
|
||||
isProcessing = true
|
||||
|
||||
defer {
|
||||
if let next = waiters.first {
|
||||
waiters.removeFirst()
|
||||
next.resume()
|
||||
} else {
|
||||
isProcessing = false
|
||||
}
|
||||
}
|
||||
|
||||
return try await operation()
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user