oblador / react-native-keychain

:key: Keychain Access for React Native
MIT License
3.2k stars 520 forks source link

getGenericPassword is too slow on android 10 #337

Closed sm2017 closed 3 weeks ago

sm2017 commented 4 years ago

I have slow startup on android 10 , After checking , I understand that getGenericPassword methods takes 7-8 seconds on android 10 I run on android 7 and 6 too , It takes about 600-700ms

Yoon-SeungHwan commented 3 years ago

@nicolaslazzos @aranda-adapptor

You guys are stars! 👍🏻 - It worked, many thanks!

For other new users like me that have the same problem: Apart from changing MainApplication.java as the @nicolaslazzos mentioned above and all the other file changes he posted. You will also need to add compile project(':react-native-keychain') inside your dependencies { } of /android/app/build.gradle

And my JS setGenericPassword for passing AES looks like: await Keychain.setGenericPassword("some_user", "123456", {storage: Keychain.STORAGE_TYPE.AES })

Good luck

Don't forget put this to project/settings.gradle include ':react-native-keychain' project(':react-native-keychain').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keychain/android')

Sandi89 commented 3 years ago

@sm2017 I tried dropping to 4.0.5, started getting an issue where storage: Keychain.STORAGE_TYPE.RSA doesnt exist. Removing it basically makes Android to read from secured keychain without prompting for Biometric credentials. Any ideas?

nicolaslazzos commented 3 years ago

@Sandi89 That's because unlocking the keychain with biometry was introduced in the v5.0.0, according to the releases.

Bardiamist commented 3 years ago

Ok, I also will revert to 4.0.5 because:

6.2.0 extracting one key on Android 8.1: In debug: 8 sec In release APK: 15 sec

4.0.5 extracting one key on Android 8.1: In debug: 3 sec

tranquan commented 3 years ago

On 6.2.0, it said: "Optionally skip biometric warmup on Android" Is that about this issue?

siddharth-kt commented 3 years ago

Is the issue sorted in version 7 ?

nicolaslazzos commented 3 years ago

No

MFrat commented 3 years ago

Same issue here.

If you just want to access keystore/keychain without biometric and etc, a good alternative is https://github.com/emeraldsanto/react-native-encrypted-storage.

rares-lupascu commented 3 years ago

+1

HLeshan commented 3 years ago

any updates ?

genie09 commented 3 years ago

I also downgraded to 4.0.5.

haryelramalho commented 3 years ago

react-native-keychain: 6.0.0 react-native: 0.62.2

Same issue for me. In Simulator (API28) and on a Huawei P30 Pro (API29) it works super fast, on a Samsung SM-J730F (API28) it takes way beyond 10 seconds sometimes and fails with following exception. I experience it only happening the very first time after installing the app or wiping app data.

2020-05-04 15:31:18.866 30385-30539/? W/CipherStorageBase: StrongBox security storage is not available.
    android.security.keystore.StrongBoxUnavailableException: Failed to generate key pair
        at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeystoreKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:554)
        at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:499)
        at java.security.KeyPairGenerator$Delegate.generateKeyPair(KeyPairGenerator.java:727)
        at com.oblador.keychain.cipherStorage.CipherStorageKeystoreRsaEcb.generateKey(CipherStorageKeystoreRsaEcb.java:256)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.tryGenerateStrongBoxSecurityKey(CipherStorageBase.java:444)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.generateKeyAndStoreUnderAlias(CipherStorageBase.java:391)
        at com.oblador.keychain.KeychainModule.internalWarmingBestCipher(KeychainModule.java:171)
        at com.oblador.keychain.KeychainModule.lambda$NuQDyTTfZc67dTNiVeEDbYNRCJw(Unknown Source:0)
        at com.oblador.keychain.-$$Lambda$KeychainModule$NuQDyTTfZc67dTNiVeEDbYNRCJw.run(Unknown Source:2)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: android.security.KeyStoreException: No StrongBox available
        at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeystoreKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:554) 
        at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:499) 
        at java.security.KeyPairGenerator$Delegate.generateKeyPair(KeyPairGenerator.java:727) 
        at com.oblador.keychain.cipherStorage.CipherStorageKeystoreRsaEcb.generateKey(CipherStorageKeystoreRsaEcb.java:256) 
        at com.oblador.keychain.cipherStorage.CipherStorageBase.tryGenerateStrongBoxSecurityKey(CipherStorageBase.java:444) 
        at com.oblador.keychain.cipherStorage.CipherStorageBase.generateKeyAndStoreUnderAlias(CipherStorageBase.java:391) 
        at com.oblador.keychain.KeychainModule.internalWarmingBestCipher(KeychainModule.java:171) 
        at com.oblador.keychain.KeychainModule.lambda$NuQDyTTfZc67dTNiVeEDbYNRCJw(Unknown Source:0) 
        at com.oblador.keychain.-$$Lambda$KeychainModule$NuQDyTTfZc67dTNiVeEDbYNRCJw.run(Unknown Source:2) 
        at java.lang.Thread.run(Thread.java:764) 

Same error, how did you solve it? My app crashes in 4-5 seconds after opening and always on white screen. I caught this error in a logcat

ludovic-noirault commented 3 years ago

react-native-keychain: 6.0.0 react-native: 0.62.2 Same issue for me. In Simulator (API28) and on a Huawei P30 Pro (API29) it works super fast, on a Samsung SM-J730F (API28) it takes way beyond 10 seconds sometimes and fails with following exception. I experience it only happening the very first time after installing the app or wiping app data.

2020-05-04 15:31:18.866 30385-30539/? W/CipherStorageBase: StrongBox security storage is not available.
    android.security.keystore.StrongBoxUnavailableException: Failed to generate key pair
        at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeystoreKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:554)
        at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:499)
        at java.security.KeyPairGenerator$Delegate.generateKeyPair(KeyPairGenerator.java:727)
        at com.oblador.keychain.cipherStorage.CipherStorageKeystoreRsaEcb.generateKey(CipherStorageKeystoreRsaEcb.java:256)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.tryGenerateStrongBoxSecurityKey(CipherStorageBase.java:444)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.generateKeyAndStoreUnderAlias(CipherStorageBase.java:391)
        at com.oblador.keychain.KeychainModule.internalWarmingBestCipher(KeychainModule.java:171)
        at com.oblador.keychain.KeychainModule.lambda$NuQDyTTfZc67dTNiVeEDbYNRCJw(Unknown Source:0)
        at com.oblador.keychain.-$$Lambda$KeychainModule$NuQDyTTfZc67dTNiVeEDbYNRCJw.run(Unknown Source:2)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: android.security.KeyStoreException: No StrongBox available
        at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeystoreKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:554) 
        at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:499) 
        at java.security.KeyPairGenerator$Delegate.generateKeyPair(KeyPairGenerator.java:727) 
        at com.oblador.keychain.cipherStorage.CipherStorageKeystoreRsaEcb.generateKey(CipherStorageKeystoreRsaEcb.java:256) 
        at com.oblador.keychain.cipherStorage.CipherStorageBase.tryGenerateStrongBoxSecurityKey(CipherStorageBase.java:444) 
        at com.oblador.keychain.cipherStorage.CipherStorageBase.generateKeyAndStoreUnderAlias(CipherStorageBase.java:391) 
        at com.oblador.keychain.KeychainModule.internalWarmingBestCipher(KeychainModule.java:171) 
        at com.oblador.keychain.KeychainModule.lambda$NuQDyTTfZc67dTNiVeEDbYNRCJw(Unknown Source:0) 
        at com.oblador.keychain.-$$Lambda$KeychainModule$NuQDyTTfZc67dTNiVeEDbYNRCJw.run(Unknown Source:2) 
        at java.lang.Thread.run(Thread.java:764) 

Same error, how did you solve it? My app crashes in 4-5 seconds after opening and always on white screen. I caught this error in a logcat

Hello, We're experiencing the same issue on physical android Tv's on app initialization. Trying on emulated android tv's doesn't generate this error. On TvOS emulators, the functionality works as intended.

Reverting to 4.0.5 ends up not solving the issue as others have mentioned above in the thread, the whole build of the app not compiling.

Any idea on how to fix this issue ?

Ludovic - Codekraft

ruairioliverwv commented 3 years ago

I ended up forking this repo, using v4.0.5 on android and v7 on iOS. v4.0.5 performance is dramatically better. I was testing on a Galaxy J7. On v7 I was getting times ranging from 400ms to 18 seconds for getGenericPassword. Consistently < 40ms on v4.0.5

nicolaslazzos commented 3 years ago

Thats because the v4.0.5 version does not have biometry to unlock the keychain. My fork is working ok with the latest version by making the changes i mentioned in the other comments.

musti-91 commented 3 years ago

For me is getGenericPassword creates a different password for the second time user opens the app and only in IOS.

oblador commented 3 years ago

Please try out 8.0.0 which has performance improvements

HamidOsouli-zz commented 3 years ago

@nicolaslazzos @aranda-adapptor I am trying on a Samsung Galaxy S7 your method as:

...

      // get the best storage
      // final String accessControl = getAccessControlOrDefault(options);
      // final boolean useBiometry = getUseBiometry(accessControl);
      // final CipherStorage current = getCipherStorageForCurrentAPILevel(useBiometry);
      final CipherStorage current = getSelectedStorage(options);
      final String rules = getSecurityRulesOrDefault(options);

...

And disabled the warmup as the docs suggest here Link, but the code changed and I call .withoutWarmUp() in this file https://github.com/oblador/react-native-keychain/blob/master/android/src/main/java/com/oblador/keychain/KeychainPackage.java as:

  public KeychainPackage() {
    this(new KeychainModuleBuilder().withoutWarmUp());
  }

I delete the build folder of the node package and run: react-native run-android, but it still takes about 7 seconds while in iPhone, it is instantly. Any advice on this? - Sorry if I specify too much, I am new to react native. Thanks

I dont know if i understood correctly, but you dont have to call .withoutWarmUp() in this file: https://github.com/oblador/react-native-keychain/blob/master/android/src/main/java/com/oblador/keychain/KeychainPackage.java, you have to do it in your MainApplication.java. So for that you have to import:

import com.oblador.keychain.KeychainPackage;
import com.oblador.keychain.KeychainModuleBuilder;

And then, in the getPackages() method, yo have to add the package manually like that:

packages.add(new KeychainPackage(new KeychainModuleBuilder().withoutWarmUp()));

And because react-native-keychain has autolinking, i had to disable it in order to add the package manually. For that, i created a react-native.config.js file in the root of my project with the following code:

module.exports = {
  dependencies: {
    'react-native-keychain': {
      platforms: {
        android: null,
      },
    },
  },
};

I hope it works for you!

Moving to version 8.0.0 didn't really help with biometric performance, but this solution finally worked ✅🎉 Thank you

MaTToX3 commented 1 year ago

Still an issue in 8.1.1.

tomerh2001 commented 8 months ago

Still an issue

tomerh2001 commented 8 months ago

@nicolaslazzos @aranda-adapptor I am trying on a Samsung Galaxy S7 your method as:

...

      // get the best storage
      // final String accessControl = getAccessControlOrDefault(options);
      // final boolean useBiometry = getUseBiometry(accessControl);
      // final CipherStorage current = getCipherStorageForCurrentAPILevel(useBiometry);
      final CipherStorage current = getSelectedStorage(options);
      final String rules = getSecurityRulesOrDefault(options);

...

And disabled the warmup as the docs suggest here Link, but the code changed and I call .withoutWarmUp() in this file https://github.com/oblador/react-native-keychain/blob/master/android/src/main/java/com/oblador/keychain/KeychainPackage.java as:

  public KeychainPackage() {
    this(new KeychainModuleBuilder().withoutWarmUp());
  }

I delete the build folder of the node package and run: react-native run-android, but it still takes about 7 seconds while in iPhone, it is instantly. Any advice on this? - Sorry if I specify too much, I am new to react native. Thanks

I dont know if i understood correctly, but you dont have to call .withoutWarmUp() in this file: https://github.com/oblador/react-native-keychain/blob/master/android/src/main/java/com/oblador/keychain/KeychainPackage.java, you have to do it in your MainApplication.java. So for that you have to import:

import com.oblador.keychain.KeychainPackage;
import com.oblador.keychain.KeychainModuleBuilder;

And then, in the getPackages() method, yo have to add the package manually like that:

packages.add(new KeychainPackage(new KeychainModuleBuilder().withoutWarmUp()));

And because react-native-keychain has autolinking, i had to disable it in order to add the package manually. For that, i created a react-native.config.js file in the root of my project with the following code:

module.exports = {
  dependencies: {
    'react-native-keychain': {
      platforms: {
        android: null,
      },
    },
  },
};

I hope it works for you!

I'm getting unresolved reference 'keychain': image

This is my keychain version: image

And I also added this file as specified: image

farwayer commented 8 months ago

@tomerh2001 check this https://github.com/oblador/react-native-keychain?tab=readme-ov-file#option-manually-1

chizhkov422 commented 7 months ago

Hi, same problem with samsung s10e (android 12). I tried to downgrade to 4.0.5 version and manual integration with packages.add(new KeychainPackage(new KeychainModuleBuilder().withoutWarmUp())); But it didn't help me.

vafada commented 7 months ago

does this PR fixes this issue?

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

owav commented 6 months ago

does this PR fixes this issue?

577

I still have the issue with v8.2.0 (which has this PR). getGenericPassword takes ~10s on my pixel 6a.

With v4.0.5, it takes less than 100 milliseconds.

humaidk2 commented 5 months ago

Still having this issue on 8.2.0, but the fix with removing warm up fixes it.

Would be nice if someone added a small gradle variable and enabled warm up only need it.

Then all the manual install steps for android wouldn't be necessary.

c-goettert commented 4 months ago

does this PR fixes this issue?

577

In fact, for us this PR seems to introduce performance issues on android. We are facing the same performance drop as described in #630. If I revert this change locally, the app starts faster again.

DorianMazur commented 2 months ago

Hi @c-goettert @humaidk2 @owav @vafada

I’ve recently migrated the project to use Android DataStore Preferences in the latest release 1.2.0. Could you please test and confirm if the bug is still present?

owav commented 1 month ago

Hey @DorianMazur

I tried your version as a drop-in replacement but I get the following error when trying to getGenericPassword (setGenericPassword seems to work fine) : [Error: There are multiple DataStores active for the same file: /data/user/0/com.myapp/files/datastore/RN_KEYCHAIN.preferences_pb. You should either maintain your DataStore as a singleton or confirm that there is no two DataStore's active on the same file (by confirming that the scope is cancelled).]

Edit : it seems this error has something to do with reloading the app without killing it. Anyway, with a clean cold start getGenericPassword takes approx 12sec, same as with v8.2.0 (as for v4.0.5, less than 100ms).

Edit 2 : if it is of any help, here is my quick test code :

test code ``` import React, { useEffect } from 'react'; import { SafeAreaView, Text, View, } from 'react-native'; //import * as Keychain from 'react-native-keychain'; import * as Keychain from 'react-native-keychain-manager'; function App(): React.JSX.Element { useEffect(() => { const testKeychain = async () => { try{ console.debug("testKeychain") const perf1 = Date.now(); const credentials = await Keychain.getGenericPassword(); if(credentials){ console.debug("credentials = ", credentials); const perf2 = Date.now(); const perfDuration = (perf2-perf1)/1000; // r-n-keychain performance issue // https://github.com/oblador/react-native-keychain/issues/337 // versions [5.0.0, 8.2.0] is very slow (~10s on my pixel 6a) // stick with version 4.0.5 for the time being (<100ms) console.debug("Loaded keychain data in %d seconds", perfDuration); } else{ console.debug("setGenericPassword") await Keychain.setGenericPassword("myUsername", "myPassword"); } } catch(err){ console.warn(err); await Keychain.resetGenericPassword(); } } testKeychain(); }, []); return ( Hi ! ); } export default App; ```
DorianMazur commented 3 weeks ago

I tested react-native-keychain 9.0.0 on Android 10 (API 29):

Loaded keychain data in 0.017 seconds

Since the issue no longer occurs, I’m closing this. Feel free to reopen if needed.

DorianMazur commented 2 weeks ago

After looking into it more, this happens because of StrongBox on some devices. It's not a bug, but I'll add an option to turn off StrongBox for those who don't need the extra security.

ovitrif commented 6 days ago

[Error: There are multiple DataStores active for the same file: /data/user/0/com.myapp/files/datastore/RN_KEYCHAIN.preferences_pb. You should either maintain your DataStore as a singleton or confirm that there is no two DataStore's active on the same file (by confirming that the scope is cancelled).] Edit : it seems this error has something to do with reloading the app without killing it. Anyway, with a clean cold start getGenericPassword takes approx 12sec, same as with v8.2.0 (as for v4.0.5, less than 100ms).

Indeed hot reloading was broken because of this issue, needed a CoroutineScope to cancel it on each stop.

Should be fixed on the latest updates in PR #629