oblador / react-native-keychain

:key: Keychain Access for React Native
MIT License
3.12k stars 515 forks source link

Android: Wrapped error: User not authenticated #318

Open alphec opened 4 years ago

alphec commented 4 years ago

I get this error when trying to retrieve credentials stored in the keychain on Android devices. IMHO this is the effect of following exception being thrown: UserNotAuthenticatedException.

This is my code:

  useEffect(() => {
    if (!loginStarted) {
      return;
    }

    Keychain.getSupportedBiometryType()
      .then(data => {
        console.log('Supported biometry: ' + data);
        setBiometry(!!data);
        if (!!data) {
          // Try auto login
          Keychain.getGenericPassword({service: service})
            .then(credentials => {
              if (!credentials) {
                // no credentials stored, continue with normal login
                console.log('No saved credentials.');
                return;
              }

              // auto login with stored credentials
              if (credentials.username && credentials.password) {
                console.log('Trying auto login with saved credentials: ' + JSON.stringify(credentials));
                login(credentials.username, credentials.password);
              }
            })
            .catch(e => console.log('Error getting credentials: ' + e));
        }
      })
      .catch(e => console.log('Error getting biometry: ' + e));
  }, [loginStarted]); 

The error is thrown at Keychain.getGenericPassword. On iOS the code works fine. Any idea or workaround for this?

skicson commented 4 years ago

I noticed similar behavior where the onAuthenticationSucceeded was being called (in KeychainModule.java but I was still getting the UserNotAuthenticatedException. Basically the decryption key's authentication was expiring before the cipher could get initialized. (probably since I was running in a debug environment, but could also happen with slower devices)

I was able to get it to work by extending the UserAuthenticationValidityDuration attribute of the key from 1 second to 5. In my case, it was using the RSA key, so the change was in the CipherStorageKeystoreRsaEcb.java file, around line 228

    return new KeyGenParameterSpec.Builder(alias, purposes)
      .setBlockModes(BLOCK_MODE_ECB)
      .setEncryptionPaddings(PADDING_PKCS1)
      .setRandomizedEncryptionRequired(true)
      .setUserAuthenticationRequired(true)
      .setUserAuthenticationValidityDurationSeconds(5)
      .setKeySize(ENCRYPTION_KEY_SIZE);
hoangcxa commented 4 years ago

I'm having the same problem with android Samsung s9. I'm using this method to save credentials. setInternetCredentials(server, username, password, { accessControl: ACCESS_CONTROL.BIOMETRY_ANY, }); When I'm using this method getInternetCredentials(server) to get credential, and using fingerprint option, I can get my username and password back. But when I use face Id option, even the faceId verified. I still can't get my username and password (undefined) , and it's showing in warn "Wrapped error: User not authenticated " .

lucaashrq commented 4 years ago

Any update for this issue? I'm having the same problem after i updated for RN 0.62.x

hoangcxa commented 4 years ago

@skicson I tried your way but it's still doesn't work. When I'm using fingerprint , it works normal. But when I use Face Recognization of this device, it's still throw that error. 2020-04-10 11:14:43.034 5729-6663/com.awesomeproject V/myLog CipherStorageBase: failed:: User not authenticated android.security.keystore.UserNotAuthenticatedException: User not authenticated at android.security.KeyStore.getInvalidKeyException(KeyStore.java:1584) at android.security.KeyStore.getInvalidKeyException(KeyStore.java:1714) at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:54) at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:89) at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:265) at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:109) at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2984) at javax.crypto.Cipher.tryCombinations(Cipher.java:2891) at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796) at javax.crypto.Cipher.chooseProvider(Cipher.java:773) at javax.crypto.Cipher.init(Cipher.java:1143) at javax.crypto.Cipher.init(Cipher.java:1084) at com.oblador.keychain.cipherStorage.CipherStorageBase$Defaults.lambda$static$1(CipherStorageBase.java:539) at com.oblador.keychain.cipherStorage.-$$Lambda$CipherStorageBase$Defaults$gUPKtklt7huSpCITAtk3nzsSgTY.initialize(Unknown Source:0) at com.oblador.keychain.cipherStorage.CipherStorageBase.decryptBytes(CipherStorageBase.java:393) at com.oblador.keychain.cipherStorage.CipherStorageBase.decryptBytes(CipherStorageBase.java:338) at com.oblador.keychain.KeychainModule$InteractiveBiometric.onAuthenticationSucceeded(KeychainModule.java:856) at androidx.biometric.BiometricFragment$2$2.run(BiometricFragment.java:140) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.lang.Thread.run(Thread.java:919)

hoangcxa commented 4 years ago

@skicson regarding to the stack above, the onAuthenticationSucceeded is being called . But then some how the public static final DecryptBytesHandler decrypt = (cipher, key, input) -> { cipher.init(Cipher.DECRYPT_MODE, key); }; failed with the android.security.KeyStore.getInvalidKeyException

krutkowski86 commented 4 years ago

+1

sh-rp commented 4 years ago

I have not been able to test with a fingerprint enabled device, but I can confirm the same wrapped exception being thrown on a google pixel 4 with face recognition enabled. iOS works fine.

cold-briu commented 4 years ago

same error here! Pixel 3 fingerprint.

I tried...

setUserAuthenticationValidityDurationSeconds

and it didn't worked for me

matthewyeager commented 4 years ago

I'm also noticing this issue with Android. It works correctly until the device is restarted.

The weird thing is, setUserAuthenticationValidityDurationSeconds seems to be related to the time since the user authenticated from the lock screen. If I call getGenericPassword after unlocking the device within setUserAuthenticationValidityDurationSeconds value, it works.

Sandi89 commented 4 years ago

Hi guys, i am also experiencing this issue with Android 8. Fingerprint works fine, so does Iris authentication. Face ID fails every single time. It would be great if there was a setting to force which type of biometric to use to unlock keychain as on Android it falls back to whatever user selected in the preference.

crees1 commented 4 years ago

Has anyone found a workaround to get this to work on devices that have both Fingerprint as well as Face authentication? Currently I just have biometrics disabled on Android until I can figure out a way to either make Face work on Android or force only Fingerprint for the time being.

ericrguimaraes commented 3 years ago

I was getting the same error on a Pixel 3 emulator. So I went to Settings and removed the fingerprint, then set it up back again. After that, I got rid of the error. I know it's maybe too much to ask of your users (to remove then set up again the fingerprint in android settings), but it's a possible workaround.

ericrguimaraes commented 3 years ago

I was getting the same error on a Pixel 3 emulator. So I went to Settings and removed the fingerprint, then set it up back again. After that, I got rid of the error. I know it's maybe too much to ask of your users (to remove then set up again the fingerprint in android settings), but it's a possible workaround.

I just noticed that, after restarting the phone, the error comes back :/

calz10 commented 3 years ago

hi folks, i have updated rn-key-chain package thought that its because im using expo and i have old keychain version, yet im still getting Wrapped error: unknown key type passed to RSA error. FYI, IOS works well, and in android, without prompting any types it will successfully get the secret, so currently, im using rn-touchid for prompting biometrics in android and get data in my rn-keychain.

timxyz commented 3 years ago

Increasing setUserAuthenticationValidityDurationSeconds to a larger value fixes this for me. Note that you have to ensure you have this value set before you store the secret into the keychain.

With that in mind I think #339 is a reasonable fix for this.

whysetiawan commented 3 years ago

any update on this?

skicson commented 3 years ago

I was able to get Face ID working consistently (on a Pixel 4) when I extended the setUserAuthenticationValidityDurationSeconds to 60. Logged out, killed the app, logged back in to set the password (and presumably the duration). For the Face ID, it seems the time between when the "confirm" dialog appears and when the user selects "confirm" has to be less than setUserAuthenticationValidityDurationSeconds for it to work.

vdino96 commented 3 years ago

any update?

Zo2m4bie commented 3 years ago

This PR solves the issue https://github.com/oblador/react-native-keychain/pull/399

ameenmattar commented 3 years ago

Any updates ?

Zo2m4bie commented 3 years ago

@ameenmattar you can use my fork

AdriaRios commented 3 years ago

Any update?

CWolfs commented 3 years ago

So I've been doing a little digging on this issue for the Pixel 4 / Face scan only devices.

I agree with what @skicson said. The setUserAuthenticationValidityDurationSeconds time seems to start after the succesful Face scan. If the user doesn't select 'Confirm' before that countdown is over - they will get the User not authenticated error.

I know some people extended the time in the setUserAuthenticationValidityDurationSeconds call which can work but I wanted to see if there was another solution.

I found this PromptInfo.Builder method call setConfirmationRequired. This call removes the 'Confirm' button on Android 10+ but it's also only classed as a 'hint' to the vendor version of Android OS so I don't know how well it's supported.

E.g. As outlined in this blog post - Android - One Biometric API Over All.

With confirm button

to

Without confirm button

This could be worth investigating more. If this works fine I'll make a PR for the change.

CWolfs commented 3 years ago

Seems to work fine on the Pixel 4 (need to test other devices) and it's actually more inline with the behaviour/experience seen on iOS with FaceID too.

Zo2m4bie commented 3 years ago

@CWolfs could you ping me here in this thread when your PR is ready? I will be able to check it on Samsung with FaceID

CWolfs commented 3 years ago

@Zo2m4bie sure, I'll try to do in the next few days - been bogged down with wrapping up a release but want to PR this soon. I only had a Pixel 4 to test so if you can test this on a Samsung that would be great to see if it works or not. I'll ping you when I'm ready.

CWolfs commented 3 years ago

@Zo2m4bie Here you go.

https://github.com/oblador/react-native-keychain/pull/414

I'd be very interested to see if this works (or doesn't) with the Samsung phone you mentioned. What version of Samsung phone is it that has Face ID that works with app-unlocking? I know the Pixel 4 is meant to be the first device with app-unlocking biometrics on Android. I assumed Samsung had one too but couldn't find info on it yet.

Zo2m4bie commented 3 years ago

Hello @CWolfs Sorry for delay. On Samsung it has the same problem as before. It proposes to use Face id to get data, however it returns "User not authenticated" error, because on Samsung face recognition isn't secure. It happens because you don't pass Cipher when you ask an access permission. The system doesn't understand why you need it. In my solution you can see that I pass Cypher into permission modal that allow the system to recognize why it was asked for biometric. In this case Samsung devices show only fingerpint https://github.com/Zo2m4bie/react-native-keychain/blob/ISSUE-318/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAesCbcBiom.java#L145

poocart commented 3 years ago

@CWolfs thanks for your input! I've tried to apply this to Pixel 4 XL and it still produces same problem as well.

michaelgmcd commented 3 years ago

Hey everyone, what's the status on this?

My team is trying to migrate from an existing in-house solution to react-native-keychain and this is a blocker. As @crees1 mentioned, only allowing Fingerprint on Android would be an ideal workaround for now. Still getting accustomed to the library, but I'm happy to help in any way I can.

Zo2m4bie commented 3 years ago

Hello @michaelgmcd right now the only available solution is using this fork. It has a bit different flow for Android and iOS, however it works for all devices

AlphaJuliettOmega commented 3 years ago

on a Huawei P8 Lite (Android 7, Api Level 24)

Fingerprint recognition fails with the same or other errors. Sometimes this:

2021-02-11 14:05:47.661 29465-29565/? W/CipherStorageBase: User not authenticated
    android.security.keystore.UserNotAuthenticatedException: User not authenticated
        at android.security.KeyStore.getInvalidKeyException(KeyStore.java:718)
        at android.security.KeyStore.getInvalidKeyException(KeyStore.java:754)
        at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:54)
        at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:89)
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:265)
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:109)
        at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2977)
        at javax.crypto.Cipher.tryCombinations(Cipher.java:2884)
        at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2789)
        at javax.crypto.Cipher.chooseProvider(Cipher.java:956)
        at javax.crypto.Cipher.init(Cipher.java:1199)
        at javax.crypto.Cipher.init(Cipher.java:1143)
        at com.oblador.keychain.cipherStorage.CipherStorageBase$Defaults.lambda$static$1(CipherStorageBase.java:502)
        at com.oblador.keychain.cipherStorage.-$$Lambda$CipherStorageBase$Defaults$DeW6NXOzsQTAPQNNW0rqTXPHW4c.initialize(lambda)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.decryptBytes(CipherStorageBase.java:360)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.decryptBytes(CipherStorageBase.java:315)
        at com.oblador.keychain.cipherStorage.CipherStorageKeystoreRsaEcb.decrypt(CipherStorageKeystoreRsaEcb.java:126)
        at com.oblador.keychain.KeychainModule.decryptToResult(KeychainModule.java:623)
        at com.oblador.keychain.KeychainModule.decryptCredentials(KeychainModule.java:590)
        at com.oblador.keychain.KeychainModule.getGenericPassword(KeychainModule.java:295)
        at com.oblador.keychain.KeychainModule.getGenericPasswordForOptions(KeychainModule.java:323)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:372)
        at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:151)
        at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
        at android.os.Handler.handleCallback(Handler.java:761)
        at android.os.Handler.dispatchMessage(Handler.java:98)
        at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
        at android.os.Looper.loop(Looper.java:156)
        at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:226)
        at java.lang.Thread.run(Thread.java:776)
2021-02-11 14:05:47.673 29465-29565/? I/RNKeychainManager: blocking thread. waiting for done UI operation.

sometimes this:

2021-02-11 14:53:43.752 545-545/? I/HisiKeyMaster: hisi_km_get_key_characteristics start
2021-02-11 14:53:43.754 1552-1552/? I/KeyguardUpdateMonitor: Ignore update FP: 0 false
2021-02-11 14:53:43.757 409-420/? I/gralloc: alloc_device_free:586: Free handle(0x7e64a33f00)
2021-02-11 14:53:43.757 409-420/? I/gralloc: alloc_device_free:586: Free handle(0x7e5cc34d00)
2021-02-11 14:53:43.771 2377-2456/? E/LogCollectService: illegal eventid
2021-02-11 14:53:43.771 545-545/? I/HisiKeyMaster: hisi_km_get_key_characteristics success
2021-02-11 14:53:43.771 545-545/? I/HisiKeyMaster: hisi_km_get_key_characteristics start
2021-02-11 14:53:43.775 545-545/? I/HisiKeyMaster: hisi_km_get_key_characteristics success
2021-02-11 14:53:43.775 545-545/? I/HisiKeyMaster: hisi_km_begin start
2021-02-11 14:53:43.784 545-545/? I/HisiKeyMaster: out_params_buffer_len = 4096
2021-02-11 14:53:43.784 545-545/? I/HisiKeyMaster: hisi_km_begin success
2021-02-11 14:53:43.787 545-545/? I/HisiKeyMaster: hisi_km_finish start
2021-02-11 14:53:43.907 545-545/? I/HisiKeyMaster: output data size=12
2021-02-11 14:53:43.907 545-545/? I/HisiKeyMaster: hisi_km_finish success
2021-02-11 14:53:43.908 545-545/? I/HisiKeyMaster: hisi_km_get_key_characteristics start
2021-02-11 14:53:43.911 545-545/? I/HisiKeyMaster: hisi_km_get_key_characteristics success
2021-02-11 14:53:43.911 545-545/? I/HisiKeyMaster: hisi_km_begin start
2021-02-11 14:53:43.915 545-545/? I/HisiKeyMaster: out_params_buffer_len = 4096
2021-02-11 14:53:43.915 545-545/? I/HisiKeyMaster: hisi_km_begin success
2021-02-11 14:53:43.916 545-545/? I/HisiKeyMaster: hisi_km_finish start
2021-02-11 14:53:43.919 545-545/? E/libteec: invoke cmd failed, code=0xfffffc18, origin=3
2021-02-11 14:53:43.919 545-545/? E/HisiKeyMaster: invoke km command failed, cmd = 10, ret = 0xfffffc18, ret_origin = 3
2021-02-11 14:53:43.919 545-545/? E/HisiKeyMaster: TEE_Finish failed, ret=0xfffffc18
2021-02-11 14:53:43.921 545-545/? I/HisiKeyMaster: hisi_km_get_key_characteristics start
2021-02-11 14:53:43.923 545-545/? I/HisiKeyMaster: hisi_km_get_key_characteristics success
2021-02-11 14:53:43.923 545-545/? I/HisiKeyMaster: hisi_km_get_key_characteristics start
2021-02-11 14:53:43.926 545-545/? I/HisiKeyMaster: hisi_km_get_key_characteristics success
2021-02-11 14:53:43.926 545-545/? I/HisiKeyMaster: hisi_km_begin start
2021-02-11 14:53:43.930 545-545/? I/HisiKeyMaster: out_params_buffer_len = 4096
2021-02-11 14:53:43.930 545-545/? I/HisiKeyMaster: hisi_km_begin success
2021-02-11 14:53:43.930 545-545/? I/HisiKeyMaster: hisi_km_finish start
2021-02-11 14:53:43.933 545-545/? E/libteec: invoke cmd failed, code=0xfffffc18, origin=3
2021-02-11 14:53:43.933 545-545/? E/HisiKeyMaster: invoke km command failed, cmd = 10, ret = 0xfffffc18, ret_origin = 3
2021-02-11 14:53:43.933 545-545/? E/HisiKeyMaster: TEE_Finish failed, ret=0xfffffc18
2021-02-11 14:53:43.934 1616-1808/? I/RNKeychainManager: unblocking thread.

This section on Android notes: https://github.com/oblador/react-native-keychain#android-notes

Should mention that Biometrics on Android isn't stable.

fedeerbes commented 3 years ago

Hi folks. Any news related to this issue? Or is there a way to just use Fingerprint in Android?

sgal commented 3 years ago

7.0.0 was just released with the support of strong bio by default. Please try it out. It should remove all User not authenticated errors.

tunm1228 commented 3 years ago

7.0.0 was just released with the support of strong bio by default. Please try it out. It should remove all User not authenticated errors.

i installed but not working android

sgal commented 3 years ago

@tunm1228 What exactly are you doing and what exactly is not working?

tunm1228 commented 3 years ago

@sgal I want biometric authentication when calling the function Keychain.getGenericPassword(). IOS working fine.

sgal commented 3 years ago

@tunm1228 Sorry, I cannot help you if you don't provide the details of your implementation and error you're getting. I would suggest trying the example app and see if that is working.

AlphaJuliettOmega commented 3 years ago

@sgal Android Keychain behaves very differently for Android >8 and Android <8

If you've tested for both, then I might test to see if it works for us too.

At the moment there's a False Acceptance and False Rejection problem for Android <=8

ie. the Keychain responds 'Succesful unlock' but with empty string content. or the Keychain master responds with 'Failed to unlock' for seemingly no reason, in an unreproducible way.

sgal commented 3 years ago

@AlphaJuliettOmega I only tested on Android 9, 10 and 11. Which options do you use?

AlphaJuliettOmega commented 3 years ago

@sgal it's got to do with this blog post: https://security.googleblog.com/2018/06/better-biometrics-in-android-p.html

False Acceptance Rate + False Rejection Rate

I don't know how to deal with this in the library but you might.

Android <9 ie. 8 and older needs this config:

  accessControl: Keychain.ACCESS_CONTROL.DEVICE_PASSCODE,

A bit related: https://github.com/oblador/react-native-keychain/issues/262

Android 9+ can use:

accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY_OR_DEVICE_PASSCODE,

Any mention of biometry in the config causes older phones to basically ask for your fingerprint, and it responds that the fingerprint was successfully entered, but

arpitgarg23 commented 3 years ago

Even with RN 0.64 upgrade getting both errors with 7.0.0 key chain on android

2021-05-21 12:28:22.970 32466-32556/com.xx.xx W/CipherStorageBase: User not authenticated
    android.security.keystore.UserNotAuthenticatedException: User not authenticated
        at android.security.KeyStore.getInvalidKeyException(KeyStore.java:895)
        at android.security.KeyStore.getInvalidKeyException(KeyStore.java:937)
        at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:54)
        at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:89)
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:265)
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:109)
        at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2984)
        at javax.crypto.Cipher.tryCombinations(Cipher.java:2891)
        at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796)
        at javax.crypto.Cipher.chooseProvider(Cipher.java:773)
        at javax.crypto.Cipher.init(Cipher.java:1143)
        at javax.crypto.Cipher.init(Cipher.java:1084)
        at com.oblador.keychain.cipherStorage.CipherStorageBase$Defaults.lambda$static$1(CipherStorageBase.java:519)
        at com.oblador.keychain.cipherStorage.-$$Lambda$CipherStorageBase$Defaults$DeW6NXOzsQTAPQNNW0rqTXPHW4c.initialize(Unknown Source:0)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.decryptBytes(CipherStorageBase.java:377)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.decryptBytes(CipherStorageBase.java:332)
        at com.oblador.keychain.cipherStorage.CipherStorageKeystoreRsaEcb.decrypt(CipherStorageKeystoreRsaEcb.java:127)
        at com.oblador.keychain.KeychainModule.decryptToResult(KeychainModule.java:669)
        at com.oblador.keychain.KeychainModule.decryptCredentials(KeychainModule.java:636)
        at com.oblador.keychain.KeychainModule.getGenericPassword(KeychainModule.java:296)
        at com.oblador.keychain.KeychainModule.getGenericPasswordForOptions(KeychainModule.java:357)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:372)
        at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:151)
        at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
        at android.os.Looper.loop(Looper.java:193)
        at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:226)
        at java.lang.Thread.run(Thread.java:764)
2021-05-21 12:28:22.973 32466-32556/com.xx.xx D/CipherStorageBase: Unlock of keystore is needed. Error: User not authenticated
    android.security.keystore.UserNotAuthenticatedException: User not authenticated
        at android.security.KeyStore.getInvalidKeyException(KeyStore.java:895)
        at android.security.KeyStore.getInvalidKeyException(KeyStore.java:937)
        at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:54)
        at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:89)
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:265)
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:109)
        at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2984)
        at javax.crypto.Cipher.tryCombinations(Cipher.java:2891)
        at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796)
        at javax.crypto.Cipher.chooseProvider(Cipher.java:773)
        at javax.crypto.Cipher.init(Cipher.java:1143)
        at javax.crypto.Cipher.init(Cipher.java:1084)
        at com.oblador.keychain.cipherStorage.CipherStorageBase$Defaults.lambda$static$1(CipherStorageBase.java:519)
        at com.oblador.keychain.cipherStorage.-$$Lambda$CipherStorageBase$Defaults$DeW6NXOzsQTAPQNNW0rqTXPHW4c.initialize(Unknown Source:0)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.decryptBytes(CipherStorageBase.java:377)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.decryptBytes(CipherStorageBase.java:332)
        at com.oblador.keychain.cipherStorage.CipherStorageKeystoreRsaEcb.decrypt(CipherStorageKeystoreRsaEcb.java:127)
        at com.oblador.keychain.KeychainModule.decryptToResult(KeychainModule.java:669)
        at com.oblador.keychain.KeychainModule.decryptCredentials(KeychainModule.java:636)
        at com.oblador.keychain.KeychainModule.getGenericPassword(KeychainModule.java:296)
        at com.oblador.keychain.KeychainModule.getGenericPasswordForOptions(KeychainModule.java:357)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:372)
        at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:151)
        at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
        at android.os.Looper.loop(Looper.java:193)
        at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:226)
        at java.lang.Thread.run(Thread.java:764)
AlphaJuliettOmega commented 3 years ago

@arpitgarg23 yes, exactly!

Did you try the config settings I've suggested in the comment before yours?

Here's a snippet that might help:

let androidConfig: Keychain.Options = {
  accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY_OR_DEVICE_PASSCODE,
  // https://github.com/oblador/react-native-keychain/issues/262
  storage: Keychain.STORAGE_TYPE.AES
};

/**
 * This config is for Android 7 and 8 (Api level 26 and below) devices, which report false authentications and false authentication rejections
 * ie. Biometric auth passes but with an empty result
 * or fails when it should pass
 * so we bypass Biometric for them.
 */
const androidLegacyConfig: Keychain.Options = {
  accessControl: Keychain.ACCESS_CONTROL.DEVICE_PASSCODE,
  // https://github.com/oblador/react-native-keychain/issues/262
  storage: Keychain.STORAGE_TYPE.AES
};

const androidLegacyCheck = () => {
  if (Platform.OS === 'android') {
    let androidVersion = device.getAndroidVersion();
    if (androidVersion !== 0) {
      if (androidVersion <= 26) {
        androidConfig = androidLegacyConfig;
      }
    }
  }
};
...
 await Keychain.setGenericPassword(
      username,
      password,
      Platform.OS === 'ios' ? iosUnlockConfig : androidConfig
    );
...
await Keychain.getGenericPassword(
    Platform.OS === 'ios' ? iosUnlockConfig : androidConfig
  )
arpitgarg23 commented 3 years ago

@AlphaJuliettOmega Thanks for the snippet. I was using accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY irrespective of Platform or android. I tried the code my android api level is 28 so below configuration was applied ``` { accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY_OR_DEVICE_PASSCODE, // https://github.com/oblador/react-native-keychain/issues/262 storage: Keychain.STORAGE_TYPE.AES } removed the warnings.

sgal commented 3 years ago

@arpitgarg23 @AlphaJuliettOmega Biometrics are not used for AES storage, only RSA, so ACCESS_CONTROL value has no difference here.

AlphaJuliettOmega commented 3 years ago

@sgal

Biometrics are not used for AES storage, only RSA

How do you know this / where can I read more?

Access control here, set to any other values for Android <9 causes keychain retrieval failures, regardless of the storage type.

sgal commented 3 years ago

@AlphaJuliettOmega I meant in this library. In Android API you can make AES key that is protected by biometrics or device passcode. This lib though only offers that for RSA keys.

This is done by using setUserAuthenticationRequired(true) on KeyGenParameterSpec object that is used to generate crypto keys.

This is how it is done in this lib for RSA storage - https://github.com/oblador/react-native-keychain/blob/master/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreRsaEcb.java#L228

And this is for AES - https://github.com/oblador/react-native-keychain/blob/master/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAesCbc.java#L187

Regarding your case - let me debug the flow with access controls to see what could be the culprit. Meanwhile, is there any pattern in device models that fail more often?

sgal commented 3 years ago

@AlphaJuliettOmega Ok, I think I found the issue(s). First of all, a bug in getGenericPassword ignores the storage type in options when passed along with access control that implies biometrics. The second is the automatic storage upgrade, which re-stores your values when more secure storage is available (based on the access control value). This can be prevented by not passing the access control to getGenericPassword method.

I suggest you skip all options in the getGenericPassword call, except for the service value (if you use it). Leave setGenericPassword as is.

AlphaJuliettOmega commented 3 years ago

@sgal that's very interesting feedback, unfortunately the settings I documented above isn't working for all Android devices.

React Native Keychain 7.0.0

Analytics reports .setGenericPassword failing for these device models:

As well as on iOs (much more rarely):

sgal commented 3 years ago

@AlphaJuliettOmega I see. Could you try removing all options from getGenericPassword call? Except for service, of course.