Auth contexts

This commit is contained in:
Max Goedjen 2024-01-12 12:46:44 -08:00
parent 53c4eea96c
commit 9957ab3e40
No known key found for this signature in database
3 changed files with 132 additions and 15 deletions

View File

@ -107,10 +107,10 @@ extension SecureEnclave {
context = existing.context
} else {
let newContext = LAContext()
newContext.localizedCancelTitle = "Deny"
newContext.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
context = newContext
}
context.localizedReason = "sign a request from \"\(provenance.origin.displayName)\" using secret \"\(secret.name)\""
context.localizedReason = String(localized: "auth_context_request_signature_description_\(provenance.origin.displayName)_\(secret.name)")
let attributes = KeychainDictionary([
kSecClass: kSecClassKey,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
@ -140,8 +140,8 @@ extension SecureEnclave {
public func verify(signature: Data, for data: Data, with secret: Secret) throws -> Bool {
let context = LAContext()
context.localizedReason = "verify a signature using secret \"\(secret.name)\""
context.localizedCancelTitle = "Deny"
context.localizedReason = String(localized: "auth_context_request_verify_description_\(secret.name)")
context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
let attributes = KeychainDictionary([
kSecClass: kSecClassKey,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
@ -181,16 +181,16 @@ extension SecureEnclave {
public func persistAuthentication(secret: Secret, forDuration duration: TimeInterval) throws {
let newContext = LAContext()
newContext.touchIDAuthenticationAllowableReuseDuration = duration
newContext.localizedCancelTitle = "Deny"
newContext.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .spellOut
formatter.allowedUnits = [.hour, .minute, .day]
if let durationString = formatter.string(from: duration) {
newContext.localizedReason = "unlock secret \"\(secret.name)\" for \(durationString)"
newContext.localizedReason = String(localized: "auth_context_persist_for_duration_\(secret.name)_\(durationString)")
} else {
newContext.localizedReason = "unlock secret \"\(secret.name)\""
newContext.localizedReason = String(localized: "auth_context_persist_for_duration_unknown_\(secret.name)")
}
newContext.evaluatePolicy(LAPolicy.deviceOwnerAuthentication, localizedReason: newContext.localizedReason) { [weak self] success, _ in
guard success else { return }
@ -260,7 +260,7 @@ extension SecureEnclave.Store {
nil)!
let wrapped: [SecureEnclave.Secret] = publicTyped.map {
let name = $0[kSecAttrLabel] as? String ?? "Unnamed"
let name = $0[kSecAttrLabel] as? String ?? String(localized: "unnamed_secret")
let id = $0[kSecAttrApplicationLabel] as! Data
let publicKeyRef = $0[kSecValueRef] as! SecKey
let publicKeyAttributes = SecKeyCopyAttributes(publicKeyRef) as! [CFString: Any]

View File

@ -50,8 +50,8 @@ extension SmartCard {
public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) throws -> Data {
guard let tokenID = tokenID else { fatalError() }
let context = LAContext()
context.localizedReason = "sign a request from \"\(provenance.origin.displayName)\" using secret \"\(secret.name)\""
context.localizedCancelTitle = "Deny"
context.localizedReason = String(localized: "auth_context_request_signature_description_\(provenance.origin.displayName)_\(secret.name)")
context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
let attributes = KeychainDictionary([
kSecClass: kSecClassKey,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
@ -156,7 +156,7 @@ extension SmartCard.Store {
SecItemCopyMatching(attributes, &untyped)
guard let typed = untyped as? [[CFString: Any]] else { return }
let wrapped = typed.map {
let name = $0[kSecAttrLabel] as? String ?? "Unnamed"
let name = $0[kSecAttrLabel] as? String ?? String(localized: "unnamed_secret")
let tokenID = $0[kSecAttrApplicationLabel] as! Data
let algorithm = Algorithm(secAttr: $0[kSecAttrKeyType] as! NSNumber)
let keySize = $0[kSecAttrKeySizeInBits] as! Int
@ -183,8 +183,8 @@ extension SmartCard.Store {
/// - Warning: Encryption functions are deliberately only exposed on a library level, and are not exposed in Secretive itself to prevent users from data loss. Any pull requests which expose this functionality in the app will not be merged.
public func encrypt(data: Data, with secret: SecretType) throws -> Data {
let context = LAContext()
context.localizedReason = "encrypt data using secret \"\(secret.name)\""
context.localizedCancelTitle = "Deny"
context.localizedReason = String(localized: "auth_context_request_encrypt_description_\(secret.name)")
context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
let attributes = KeychainDictionary([
kSecAttrKeyType: secret.algorithm.secAttrKeyType,
kSecAttrKeySizeInBits: secret.keySize,
@ -212,8 +212,8 @@ extension SmartCard.Store {
public func decrypt(data: Data, with secret: SecretType) throws -> Data {
guard let tokenID = tokenID else { fatalError() }
let context = LAContext()
context.localizedReason = "decrypt data using secret \"\(secret.name)\""
context.localizedCancelTitle = "Deny"
context.localizedReason = String(localized: "auth_context_request_decrypt_description_\(secret.name)")
context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
let attributes = KeychainDictionary([
kSecClass: kSecClassKey,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,

View File

@ -167,6 +167,90 @@
}
}
},
"auth_context_persist_for_duration_%@_%@" : {
"comment" : "When the user clicks the notification to leave a secret unlocked, they are shown a prompt to approve the action. This is the description, showing which secret will used. The first placeholder is the name of the secret. The second placeholder is a localized description of the time period it will remain unlocked for (eg: \"five minutes\")",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "unlock secret \"%1$@\" for %2$@"
}
}
}
},
"auth_context_persist_for_duration_unknown_%@" : {
"comment" : "When the user clicks the notification to leave a secret unlocked, they are shown a prompt to approve the action. This is the description, showing which secret will used. The placeholder is the name of the secret. This is a fallback used when a duration is unable to be specified.",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "unlock secret \"%1$@\""
}
}
}
},
"auth_context_request_decrypt_description_%@" : {
"comment" : "When the user performs a decryption action using a secret, they are shown a prompt to approve the action. This is the description, showing which secret will be used. The placeholder is the name of the secret. NOTE: This is currently not exposed in UI.",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "decrypt data using secret \"%1$@\""
}
}
}
},
"auth_context_request_deny_button" : {
"comment" : "When the user chooses to perform an action that requires Touch ID/password authentication, they are shown a prompt to approve the action. This is the deny button for that prompt.",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Deny"
}
}
}
},
"auth_context_request_encrypt_description_%@" : {
"comment" : "When the user performs an encryption action using a secret, they are shown a prompt to approve the action. This is the description, showing which secret will be used. The placeholder is the name of the secret. NOTE: This is currently not exposed in UI.",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "encrypt data using secret \"%1$@\""
}
}
}
},
"auth_context_request_signature_description_%@_%@" : {
"comment" : "When the user performs a signature action using a secret, they are shown a prompt to approve the action. This is the description, showing which secret will be used, and where the request is coming from. The first placeholder is the name of the app requesting the operation. The second placeholder is the name of the secret.",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "sign a request from \"%1$@\" using secret \"%2$@\""
}
}
}
},
"auth_context_request_verify_description_%@" : {
"comment" : "When the user performs a signature verification action using a secret, they are shown a prompt to approve the action. This is the description, showing which secret will be used. The placeholder is the name of the secret. NOTE: This is currently not exposed in UI.",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "verify a signature using secret \"%1$@\""
}
}
}
},
"copyable_click_to_copy_button" : {
"localizations" : {
"en" : {
@ -747,6 +831,17 @@
}
}
},
"secure_enclave" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Secure Enclave"
}
}
}
},
"setup_agent_activity_monitor_description" : {
"localizations" : {
"en" : {
@ -1023,6 +1118,28 @@
}
}
},
"smart_card" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Smart Card"
}
}
}
},
"unnamed_secret" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Unnamed"
}
}
}
},
"update_critical_notice_title" : {
"localizations" : {
"en" : {