fix(android): Use CryptoObject to prevent UserNotAuthenticatedException#788
Open
grndcherokee wants to merge 3 commits intooblador:masterfrom
Open
fix(android): Use CryptoObject to prevent UserNotAuthenticatedException#788grndcherokee wants to merge 3 commits intooblador:masterfrom
grndcherokee wants to merge 3 commits intooblador:masterfrom
Conversation
Fix race condition between BiometricPrompt.onAuthenticationSucceeded and subsequent crypto operations that causes "User not authenticated" errors on some Android devices (particularly Samsung S24/S25). When setUserAuthenticationRequired(true) is set on a KeyStore key with a validity duration, there's a timing window between when the callback fires and when the crypto operation executes. On some devices, this window can expire before the crypto runs. The fix uses BiometricPrompt.CryptoObject to atomically bind the biometric authentication to the crypto operation: 1. Pre-initialize Cipher before calling BiometricPrompt.authenticate() 2. Pass it as BiometricPrompt.CryptoObject(cipher) 3. Use authenticated cipher from AuthenticationResult.cryptoObject This ensures the crypto operation is cryptographically bound to the biometric authentication, eliminating timing-related failures. Changes: - ResultHandler.kt: Add optional cipher field to CryptoContext - CipherStorageKeystoreRsaEcb.kt: Init cipher for decrypt - CipherStorageKeystoreAesGcm.kt: Init cipher for encrypt/decrypt - ResultHandlerInteractiveBiometric.kt: Use CryptoObject for auth
This commit adds patches previously maintained separately by Klarna: 1. Skip StrongBox on slow OEM implementations (Motorola, Xiaomi) - Add SLOW_STRONGBOX_MANUFACTURERS set - Add isSlowStrongBoxManufacturer() function - Skip StrongBox key generation for these manufacturers 2. Enhanced error reporting with stack traces - Add generateUserInfo() helper to attach stack traces - Include stack traces in all promise.reject calls - Helps with debugging keychain errors in production These changes are additive and don't affect the CryptoObject fix.
When installing from GitHub (not npm), the prepare script doesn't run automatically, causing TypeScript type definitions to be missing. This commit includes the pre-built lib folder to ensure the package works correctly when installed directly from GitHub.
|
Hi @grndcherokee. I'm using your version on my project. On my package.json i'm using "react-native-keychain": "git+https://github.com/grndcherokee/react-native-keychain.git#fix/crypto-object-binding", instead of "react-native-keychain": "10.0.0". But, now, on my android device I'm only able to log in using fingerprint biometrics. The error message "authenticate to retrieve secret" appear and only fingerprint biometrics is acessible for log in. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fix race condition between
BiometricPrompt.onAuthenticationSucceededand subsequent crypto operations that causesCryptoFailedException: User not authenticatedon some Android devices (particularly Samsung S24/S25).Problem
When
setUserAuthenticationRequired(true)is set on a KeyStore key with a validity duration (e.g., 5 seconds), there's a timing window between:BiometricPrompt.onAuthenticationSucceededcallback firingcipher.doFinal()) executingOn some devices, particularly Samsung Galaxy S24/S25 with fingerprint authentication, this window can expire before the crypto operation runs, causing
UserNotAuthenticatedExceptionwrapped inCryptoFailedException.The root cause is that the current implementation calls
prompt.authenticate(promptInfo)without aCryptoObject, then attempts crypto operations in the success callback. This decouples the authentication from the crypto operation, creating a race condition.Solution
Use
BiometricPrompt.CryptoObjectto atomically bind the biometric authentication to the crypto operation:Cipherbefore callingBiometricPrompt.authenticate()BiometricPrompt.CryptoObject(cipher)toauthenticate()AuthenticationResult.cryptoObjectinonAuthenticationSucceededThis ensures the crypto operation is cryptographically bound to the biometric authentication, eliminating timing-related failures.
Changes
ResultHandler.kt: Add optionalcipherfield toCryptoContextfor pre-initialized ciphersCipherStorageKeystoreRsaEcb.kt: Initialize cipher before biometric prompt for decrypt operationsCipherStorageKeystoreAesGcm.kt: Initialize cipher before biometric prompt for encrypt/decrypt operationsResultHandlerInteractiveBiometric.kt:CryptoObjectwhen callingBiometricPrompt.authenticate()AuthenticationResult.cryptoObjectin success callbackBackwards Compatibility
Testing
accessControlTest.spec.jswhich test biometric save/load flowsRelated Issues
Fixes timing-related
UserNotAuthenticatedExceptionerrors reported on Samsung devices.