After KeyPermanentlyInvalidatedException: silent key re-enrollment or forced server re-authentication?
0 reputation · 03 Jan 2020, 10:21 UTC
0 reputation · 03 Jan 2020, 10:21 UTC
I'm designing credential handling for an Android app that binds its refresh-token encryption key to the Android Keystore with setUserAuthenticationRequired(true) and setInvalidatedByBiometricEnrollment(true). The goal is least-privilege access: no usable credential without fresh user authentication.
The documented behavior creates a fork I can't resolve. When the user enrolls a new biometric or removes the lock screen, the key is permanently destroyed and Cipher.init throws KeyPermanentlyInvalidatedException. At that point the app can either silently generate a new key and re-bind the stored refresh token, or wipe local credentials and force full server re-authentication.
The trade-off: silent re-enrollment is seamless, but the platform provides no signal distinguishing a benign enrollment change from a compromised device, so re-binding could hand a fresh key to an attacker. Forced re-authentication is safer but punishes users for routine lock-screen changes, and I can't tell how common this is across OEM implementations given known inconsistencies in invalidation behavior.
Assume target SDK 30+, TEE-backed keys with StrongBox fallback where available.
setUserAuthenticationParameters change the risk calculus here?35775 reputation · 03 Jan 2020, 12:51 UTC
setUserAuthenticationParameters(0, false) (per-use auth) significantly reduces risk. A validity window > 0 allows a regenerated key to be used without fresh user confirmation during that window, undermining the security model.When KeyPermanentlyInvalidatedException occurs, the original key is destroyed. The app must create a new key to continue encrypting or decrypting the refresh token. The critical question is who authorizes that new key.
If the app silently generates a new key and re-wraps the stored token, an attacker who triggered the invalidation (for example, by enrolling their own fingerprint on a stolen device) gains immediate access to a usable encryption key. The platform provides no signal to differentiate this from a legitimate user changing their lock screen.
Requiring step-up authentication—prompting the user for biometric or device credential before creating the replacement key—ensures the person authorizing the new key is the same person who just authenticated. This matches the original threat model: no usable credential without fresh user authentication.
The server does not need to know why the key was invalidated. It only needs to observe that a refresh token is being presented after the client reported an authentication failure, or after an unexpected token rotation. A simple rule works:
KeyPermanentlyInvalidatedException, prompts step-up auth, creates a new key, re-wraps the token, and presents the token with a rotation or re-attestation signal.This adds a defensive layer even if the client is compromised or the exception is suppressed locally.
setUserAuthenticationParameters| Mode | Validity Window | Risk After Invalidation |
|---|---|---|
| Per-use | 0 | Each cryptographic operation, including new key creation and first use, requires fresh user auth. An attacker cannot use a regenerated key without the user present. |
| Time-bounded | >0 (for example, 30s) | After step-up auth creates a new key, that key can be used for the window duration without further auth. If invalidation was attacker-induced, they hold a usable key for that window. |
For mid-sensitivity apps, per-use authentication is the safer default. The usability cost is one additional biometric prompt at the moment of key rotation, which should be rare.
setUserAuthenticationRequired(true), setInvalidatedByBiometricEnrollment(true), and setUserAuthenticationParameters(0, false).KeyPermanentlyInvalidatedException.Marked for review: OEM implementations differ on exactly which events trigger invalidation (lock-screen removal only versus any biometric change), so test on the target device fleet. The step-up recommendation holds regardless, but the frequency of user-facing prompts will vary.
One diagnostic detail changes the server-side recommendation: whether the refresh token is single-use and rotating. If it is, a stale token presented after invalidation can be rejected outright rather than merely flagged, which is stronger than a step-up prompt.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.