Unlock GPG key material through an optional biometric-protected iPhone Keychain item and the shared authentication lease.
Scope
On the first decrypting action, request the GPG secret-key passphrase when no usable secure item exists, load the key through crates/storage, and establish the shared configurable authentication lease.
When the user enables biometric unlock in Preferences, store the passphrase through the Apple secure-storage adapter with device-only, passcode-required accessibility and current-biometric-set access control.
Retrieve the passphrase only through Keychain/LocalAuthentication so Face ID, Touch ID, or the device's supported biometric method gates access; Swift must not keep a second persistent copy.
When biometric unlock is disabled, remove the protected item and fall back to explicit passphrase entry.
Handle unavailable/unregistered biometrics, changed enrollment, cancellation, lockout, device-passcode removal, and Keychain errors without weakening policy; require manual passphrase re-entry and re-enrollment when needed.
Apply the shared inactivity timeout and manual lock behavior; expiry immediately removes decrypted presentation/edit state.
Keep platform FFI minimal, isolated, documented, and free of unsafe in project crates.
Acceptance criteria
Folder browsing does not authenticate; the first decrypt does.
Opted-in users can reestablish a lease using supported biometrics without the passphrase appearing in config or Swift persistence.
Biometric enrollment changes invalidate the stored credential and require manual recovery.
Disabling the preference deletes the protected passphrase, and expiry/lock masks all decrypted UI.
Deterministic tests cover lease timing while Apple integration tests cover success, cancellation, invalidation, and fallback.
## Goal
Unlock GPG key material through an optional biometric-protected iPhone Keychain item and the shared authentication lease.
## Scope
- On the first decrypting action, request the GPG secret-key passphrase when no usable secure item exists, load the key through `crates/storage`, and establish the shared configurable authentication lease.
- When the user enables biometric unlock in Preferences, store the passphrase through the Apple secure-storage adapter with device-only, passcode-required accessibility and current-biometric-set access control.
- Retrieve the passphrase only through Keychain/LocalAuthentication so Face ID, Touch ID, or the device's supported biometric method gates access; Swift must not keep a second persistent copy.
- When biometric unlock is disabled, remove the protected item and fall back to explicit passphrase entry.
- Handle unavailable/unregistered biometrics, changed enrollment, cancellation, lockout, device-passcode removal, and Keychain errors without weakening policy; require manual passphrase re-entry and re-enrollment when needed.
- Apply the shared inactivity timeout and manual lock behavior; expiry immediately removes decrypted presentation/edit state.
- Keep platform FFI minimal, isolated, documented, and free of `unsafe` in project crates.
## Acceptance criteria
- Folder browsing does not authenticate; the first decrypt does.
- Opted-in users can reestablish a lease using supported biometrics without the passphrase appearing in config or Swift persistence.
- Biometric enrollment changes invalidate the stored credential and require manual recovery.
- Disabling the preference deletes the protected passphrase, and expiry/lock masks all decrypted UI.
- Deterministic tests cover lease timing while Apple integration tests cover success, cancellation, invalidation, and fallback.
hugo
added this to the 04 - iPhone and Apple Watch apps milestone 2026-08-09 20:00:30 +00:00
Added a storage-owned mobile authentication service over the shared configurable lease. The first real entry decrypt verifies a manually entered GPG passphrase; only verified material can be enrolled.
Added Apple secure storage using AccessibleWhenPasscodeSetThisDeviceOnly plus BIOMETRY_CURRENT_SET, with device-only persistence, typed cancellation/unavailable/error handling, enrollment-change recovery, explicit deletion on opt-out, manual lock, and inactivity expiry. Project crates remain safe Rust and launch no helper processes.
Added the UniFFI boundary and native UIKit locked-entry and Preferences presentation. Swift keeps no persistent passphrase copy and folder browsing remains unauthenticated.
Verification: all required cargo fmt, warning-denied check, clippy, and workspace test gates passed; xcodegen generate and the iPhone Simulator build passed; simulator visual checks covered browsing, Preferences, locked/unlocked presentation, manual entry, and fail-closed passcode absence. A development-signed build installed on the paired iPhone and verified manual decrypt, Face ID lease restoration after manual lock, and opt-out deletion restoring the GPG passphrase prompt. The app uses the existing AppIcon.
Implemented in `873db91` and pushed to `main`.
- Added a storage-owned mobile authentication service over the shared configurable lease. The first real entry decrypt verifies a manually entered GPG passphrase; only verified material can be enrolled.
- Added Apple secure storage using `AccessibleWhenPasscodeSetThisDeviceOnly` plus `BIOMETRY_CURRENT_SET`, with device-only persistence, typed cancellation/unavailable/error handling, enrollment-change recovery, explicit deletion on opt-out, manual lock, and inactivity expiry. Project crates remain safe Rust and launch no helper processes.
- Added the UniFFI boundary and native UIKit locked-entry and Preferences presentation. Swift keeps no persistent passphrase copy and folder browsing remains unauthenticated.
- Added deterministic fixture-backed tests for manual recovery, biometric reuse, exact lease expiry, enrollment invalidation, re-enrollment, deletion fallback, and secret-free preference persistence.
Verification: all required `cargo fmt`, warning-denied check, clippy, and workspace test gates passed; `xcodegen generate` and the iPhone Simulator build passed; simulator visual checks covered browsing, Preferences, locked/unlocked presentation, manual entry, and fail-closed passcode absence. A development-signed build installed on the paired iPhone and verified manual decrypt, Face ID lease restoration after manual lock, and opt-out deletion restoring the GPG passphrase prompt. The app uses the existing AppIcon.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Goal
Unlock GPG key material through an optional biometric-protected iPhone Keychain item and the shared authentication lease.
Scope
crates/storage, and establish the shared configurable authentication lease.unsafein project crates.Acceptance criteria
Implemented in
873db91and pushed tomain.AccessibleWhenPasscodeSetThisDeviceOnlyplusBIOMETRY_CURRENT_SET, with device-only persistence, typed cancellation/unavailable/error handling, enrollment-change recovery, explicit deletion on opt-out, manual lock, and inactivity expiry. Project crates remain safe Rust and launch no helper processes.Verification: all required
cargo fmt, warning-denied check, clippy, and workspace test gates passed;xcodegen generateand the iPhone Simulator build passed; simulator visual checks covered browsing, Preferences, locked/unlocked presentation, manual entry, and fail-closed passcode absence. A development-signed build installed on the paired iPhone and verified manual decrypt, Face ID lease restoration after manual lock, and opt-out deletion restoring the GPG passphrase prompt. The app uses the existing AppIcon.