Feature #3887
Updated by Joshua Zhao 6 days ago
h2. Problem and resulting behavior New Android passkeys need encrypted local software key storage while existing Android Keystore credentials and still-available legacy software keys retain their original credential identity. The earlier retirement policy in #3880 does not cover this behavior. h2. Scope * Generate software P-256 keys for new passkeys. Encrypt each private key with a fresh AES-GCM record key, wrapped by an authentication-bound Android Keystore RSA key, in noBackupFilesDir. * Require the successful BiometricPrompt CryptoObject to decrypt and sign. Preserve the existing Keystore signing path. * Validate and encrypt legacy software keys before removing plaintext metadata. Preserve IDs, keys, account metadata, and counters; make interrupted migration retryable. * Keep registration-time backup eligibility consistent in assertions. New software credentials are eligible for future backup but never claim an existing backup; backup, sync, export, and recovery remain deferred. * Move storage and cryptographic work off the UI thread, serialize credential metadata updates, and show storage failures and unavailable credentials in Passkey Manager. * Include regression tests and storage documentation. * Record the standing Redmine commit requirement and secure credential lookup instructions in the repository-root AGENTS.md. h2. Validation Passed @:app:testDebugUnitTest@ and @:app:compileDebugAndroidTestKotlin@ with Android Studio's bundled JDK and Gradle offline mode. @git diff --check@ passed. Instrumentation tests were compiled, not executed on a device. Physical-device upgrade, biometric/device-credential authentication, cancellation, key invalidation, and backup/transfer acceptance checks remain pending as documented in @docs/android-security-storage.md@. Related prior work: #3879, #3880, #3883, #3884. Repository workflow instructions and independent-commit rules are tracked separately in #3889.