oblador / react-native-keychain

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

Keystore operation failed; java.security.InvalidKeyException: Keystore operation failed #430

Open ifero opened 3 years ago

ifero commented 3 years ago

Hello everyone,

we are facing an unexpected issue. Suddenly, and fortunately, one of my colleagues wasn't able to login to our app in our production environment. After some investigation, we were able to catch some exceptions from logcat. It looks like the app is not able to access the phone's cypher storage.

here you can find the logs:

      CipherStorageBase  E  Keystore operation failed
                         E  java.security.InvalidKeyException: Keystore operatio
                            n failed
                         E      at android.security.KeyStore.getInvalidKeyExcept
                            ion(KeyStore.java:1378)
                         E      at android.security.KeyStore.getInvalidKeyExcept
                            ion(KeyStore.java:1388)
                         E      at android.security.keystore.KeyStoreCryptoOpera
                            tionUtils.getInvalidKeyExceptionForInit(KeyStoreCryp
                            toOperationUtils.java:54)
                         E      at android.security.keystore.KeyStoreCryptoOpera
                            tionUtils.getExceptionForCipherInit(KeyStoreCryptoOp
                            erationUtils.java:89)
                         E      at android.security.keystore.AndroidKeyStoreCiph
                            erSpiBase.ensureKeystoreOperationInitialized(Android
                            KeyStoreCipherSpiBase.java:265)
                         E      at android.security.keystore.AndroidKeyStoreCiph
                            erSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.ja
                            va:109)
                         E      at javax.crypto.Cipher.tryTransformWithProvider(
                            Cipher.java:2984)
                         E      at javax.crypto.Cipher.tryCombinations(Cipher.ja
                            va:2891)
                         E      at javax.crypto.Cipher$SpiAndProviderUpdater.upd
                            ateAndGetSpiAndProvider(Cipher.java:2796)
                         E      at javax.crypto.Cipher.chooseProvider(Cipher.jav
                            a:773)
                         E      at javax.crypto.Cipher.init(Cipher.java:1143)
                         E      at javax.crypto.Cipher.init(Cipher.java:1084)
                         E      at com.oblador.keychain.cipherStorage.CipherStor
                            ageBase$IV.lambda$static$0(CipherStorageBase.java:51
                            3)
                         E      at com.oblador.keychain.cipherStorage.-$$Lambda$
                            CipherStorageBase$IV$1HE0kLYU0fqFndTy2zL8dv3X1vI.ini
                            tialize(Unknown Source:0)
                         E      at com.oblador.keychain.cipherStorage.CipherStor
                            ageBase.encryptString(CipherStorageBase.java:331)
                         E      at com.oblador.keychain.cipherStorage.CipherStor
                            ageKeystoreAesCbc.encryptString(CipherStorageKeystor
                            eAesCbc.java:255)
                         E      at com.oblador.keychain.cipherStorage.CipherStor
                            ageKeystoreAesCbc.encrypt(CipherStorageKeystoreAesCb
                            c.java:117)
                         E      at com.oblador.keychain.KeychainModule.setGeneri
                            cPassword(KeychainModule.java:222)
                         E      at com.oblador.keychain.KeychainModule.setIntern
                            etCredentialsForServer(KeychainModule.java:387)
                         E      at java.lang.reflect.Method.invoke(Native Method
                            )
                         E      at com.facebook.react.bridge.JavaMethodWrapper.i
                            nvoke(JavaMethodWrapper.java:372)
                         E      at com.facebook.react.bridge.JavaModuleWrapper.i
                            nvoke(JavaModuleWrapper.java:151)
                         E      at com.facebook.react.bridge.queue.NativeRunnabl
                            e.run(Native Method)
                         E      at android.os.Handler.handleCallback(Handler.jav
                            a:938)
                         E      at android.os.Handler.dispatchMessage(Handler.ja
                            va:99)
                         E      at com.facebook.react.bridge.queue.MessageQueueT
                            hreadHandler.dispatchMessage(MessageQueueThreadHandl
                            er.java:27)
                         E      at android.os.Looper.loop(Looper.java:223)
                         E      at com.facebook.react.bridge.queue.MessageQueueT
                            hreadImpl$4.run(MessageQueueThreadImpl.java:226)
                         E      at java.lang.Thread.run(Thread.java:923)
                         E  Caused by: android.security.KeyStoreException: Too m
                            any operations
                         E      at android.security.KeyStore.getKeyStoreExceptio
                            n(KeyStore.java:1301)
                         E      ... 28 more
      RNKeychainManager  E  Could not encrypt data with alias: PASSWORD_KEY
                         E  com.oblador.keychain.exceptions.CryptoFailedExceptio
                            n: Could not encrypt data with alias: PASSWORD_KEY
                         E      at com.oblador.keychain.cipherStorage.CipherStor
                            ageKeystoreAesCbc.encrypt(CipherStorageKeystoreAesCb
                            c.java:121)
                         E      at com.oblador.keychain.KeychainModule.setGeneri
                            cPassword(KeychainModule.java:222)
                         E      at com.oblador.keychain.KeychainModule.setIntern
                            etCredentialsForServer(KeychainModule.java:387)
                         E      at java.lang.reflect.Method.invoke(Native Method
                            )
                         E      at com.facebook.react.bridge.JavaMethodWrapper.i
                            nvoke(JavaMethodWrapper.java:372)
                         E      at com.facebook.react.bridge.JavaModuleWrapper.i
                            nvoke(JavaModuleWrapper.java:151)
                         E      at com.facebook.react.bridge.queue.NativeRunnabl
                            e.run(Native Method)
                         E      at android.os.Handler.handleCallback(Handler.jav
                            a:938)
                         E      at android.os.Handler.dispatchMessage(Handler.ja
                            va:99)
                         E      at com.facebook.react.bridge.queue.MessageQueueT
                            hreadHandler.dispatchMessage(MessageQueueThreadHandl
                            er.java:27)
                         E      at android.os.Looper.loop(Looper.java:223)
                         E      at com.facebook.react.bridge.queue.MessageQueueT
                            hreadImpl$4.run(MessageQueueThreadImpl.java:226)
                         E      at java.lang.Thread.run(Thread.java:923)
                         E  Caused by: java.security.InvalidKeyException: Keysto
                            re operation failed
                         E      at android.security.KeyStore.getInvalidKeyExcept
                            ion(KeyStore.java:1378)
                         E      at android.security.KeyStore.getInvalidKeyExcept
                            ion(KeyStore.java:1388)
                         E      at android.security.keystore.KeyStoreCryptoOpera
                            tionUtils.getInvalidKeyExceptionForInit(KeyStoreCryp
                            toOperationUtils.java:54)
                         E      at android.security.keystore.KeyStoreCryptoOpera
                            tionUtils.getExceptionForCipherInit(KeyStoreCryptoOp
                            erationUtils.java:89)
                         E      at android.security.keystore.AndroidKeyStoreCiph
                            erSpiBase.ensureKeystoreOperationInitialized(Android
                            KeyStoreCipherSpiBase.java:265)
                         E      at android.security.keystore.AndroidKeyStoreCiph
                            erSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.ja
                            va:109)
                         E      at javax.crypto.Cipher.tryTransformWithProvider(
                            Cipher.java:2984)
                         E      at javax.crypto.Cipher.tryCombinations(Cipher.ja
                            va:2891)
                         E      at javax.crypto.Cipher$SpiAndProviderUpdater.upd
                            ateAndGetSpiAndProvider(Cipher.java:2796)
                         E      at javax.crypto.Cipher.chooseProvider(Cipher.jav
                            a:773)
                         E      at javax.crypto.Cipher.init(Cipher.java:1143)
                         E      at javax.crypto.Cipher.init(Cipher.java:1084)
                         E      at com.oblador.keychain.cipherStorage.CipherStor
                            ageBase$IV.lambda$static$0(CipherStorageBase.java:51
                            3)
                         E      at com.oblador.keychain.cipherStorage.-$$Lambda$
                            CipherStorageBase$IV$1HE0kLYU0fqFndTy2zL8dv3X1vI.ini
                            tialize(Unknown Source:0)
                         E      at com.oblador.keychain.cipherStorage.CipherStor
                            ageBase.encryptString(CipherStorageBase.java:331)
                         E      at com.oblador.keychain.cipherStorage.CipherStor
                            ageKeystoreAesCbc.encryptString(CipherStorageKeystor
                            eAesCbc.java:255)
                         E      at com.oblador.keychain.cipherStorage.CipherStor
                            ageKeystoreAesCbc.encrypt(CipherStorageKeystoreAesCb
                            c.java:117)
                         E      ... 12 more
                         E  Caused by: android.security.KeyStoreException: Too m
                            any operations
                         E      at android.security.KeyStore.getKeyStoreExceptio
                            n(KeyStore.java:1301)
                         E      ... 28 more

Did anyone had the same issue?

CWolfs commented 3 years ago

What phone was your colleague using? Which biometric method?

ifero commented 3 years ago

My colleague is using a Pixel 3 XL, with fingerprint. But we are not using biometrics to retrieve/store data.

It also seems like that rebooting the phone fixed the issue, but that I suppose is not really a "fix".

enahum commented 3 years ago

We started to see this behavior in our Mattermost app since the last Android 11 update (specially on Pixel 5 devices, but reported with other android devices as well).

We were using version 4.0.5 but the issue also remains with 6.2.0 using SECURE_SOFTWARE and no biometrics

svelle commented 3 years ago

We started to see this behavior in our Mattermost app since the last Android 11 update (specially on Pixel 5 devices, but reported with other android devices as well).

We were using version 4.0.5 but the issue also remains with 6.2.0 using SECURE_SOFTWARE and no biometrics

To add on to this, we've got several user reporting this with both the Pixel 4 and Pixel 5 on the January Patch for Android 11.

ifero commented 3 years ago

The patch was installed on January 5th, and the app has been used without issues until today :/

NeilHanlon commented 3 years ago

I can replicate this pretty much consistently. I login to MM on pixel 5, latest patches... A few hours later.... Logged out again.

Happy to grab logs, just need to know how.

ifero commented 3 years ago

I can replicate this pretty much consistently. I login to MM on pixel 5, latest patches... A few hours later.... Logged out again.

Happy to grab logs, just need to know how.

https://github.com/ecgreb/pidlog You could try this one 🙂

NeilHanlon commented 3 years ago
02-01 15:31:15.546   549  9783 D libnos_transport: Calling app 2 with params 0x0000
02-01 15:31:15.547   549  9783 E libnos_datagram: can't send spi message: Try again
02-01 15:31:15.563   549  9783 I chatty  : uid=1064(hsm) Binder:549_2 identical 3 lines
02-01 15:31:15.569   549  9783 E libnos_datagram: can't send spi message: Try again
02-01 15:31:15.576   549  9783 D libnos_transport: App 2 inspection status=0x00000000 reply_len=0 protocol=1 flags=0x0000
02-01 15:31:15.576   549  9783 D libnos_transport: Send app 2 command data (18 bytes)
02-01 15:31:15.577   549  9783 D libnos_transport: Send app 2 go command 0x00020000
02-01 15:31:15.577   549  9783 D libnos_transport: Polling app 2
02-01 15:31:15.612   549  9783 D libnos_transport: App 2 polled=14 status=0x80000000 reply_len=0 flags=0x0000
02-01 15:31:15.613   549  9783 D libnos_transport: App 2 returning 0x0
02-01 15:31:15.622   549  9783 D libnos_transport: Calling app 2 with params 0x000a
02-01 15:31:15.625   549  9783 D libnos_transport: App 2 inspection status=0x00000000 reply_len=0 protocol=1 flags=0x0000
02-01 15:31:15.625   549  9783 D libnos_transport: Send app 2 command data (1570 bytes)
02-01 15:31:15.636   549  9783 D libnos_transport: Send app 2 go command 0x0002000a
02-01 15:31:15.637   549  9783 D libnos_transport: Polling app 2
02-01 15:31:15.649   549  9783 D libnos_transport: App 2 polled=6 status=0x80000000 reply_len=2 flags=0x0000
02-01 15:31:15.649   549  9783 D libnos_transport: Read app 2 reply data (2 bytes)
02-01 15:31:15.650   549  9783 D libnos_transport: App 2 returning 0x0
02-01 15:31:15.657   550   550 E android.hardware.keymaster@4.1-service.citadel: BeginOperation : device response error code: TOO_MANY_OPERATIONS
02-01 15:31:15.658  1178 14870 D keymaster_worker: Trying to prune operation 0x0
02-01 15:31:15.658  1178 14870 E keymaster_worker: Failed to abort pruneable operation 0x0, error: -28
02-01 15:31:15.663 14619 14661 E RNKeychainManager: Unknown error: Could not encrypt value for service https://chat.rockylinux.org, message: Keystore operation failed
02-01 15:31:15.668 14619 14661 I ReactNativeNotifs: Cancelling all scheduled notifications
02-01 15:31:15.677 14619 14619 E unknown:ReactNative: ReactInstanceManager.attachRootViewToInstance()
02-01 15:31:15.678 14619 14619 E unknown:ReactRootView: runApplication: call AppRegistry.runApplication
02-01 15:31:15.678 14619 14619 E unknown:ReactNative: ReactInstanceManager.attachRootViewToInstance()
02-01 15:31:15.678 14619 14619 E unknown:ReactRootView: runApplication: call AppRegistry.runApplication
02-01 15:31:15.678 14619 14619 E unknown:ReactNative: ReactInstanceManager.attachRootViewToInstance()

This seemed relevant, but I'm not 100% sure. Specifically... android.hardware.keymaster@4.1-service.citadel: BeginOperation : device response error code: TOO_MANY_OPERATIONS

https://source.android.com/security/keystore/tags ?

Tag::MAX_USES_PER_BOOT Specifies the maximum number of times that a key may be used between system reboots. This is another mechanism to rate-limit key use.

The value is a 32-bit integer representing uses per boot.

When a key with this tag is used in an operation, a key-associated counter should be incremented during the begin call. After the key counter has exceeded this value, all subsequent attempts to use the key fail with ErrorCode::MAX_OPS_EXCEEDED, until the device is restarted. This implies that a trustlet keeps a table of use counters for keys with this tag. Because Keymaster memory is often limited, this table can have a fixed maximum size and Keymaster can fail operations that attempt to use keys with this tag when the table is full. The table needs to acommodate at least 16 keys. If an operation fails because the table is full, Keymaster returns ErrorCode::TOO_MANY_OPERATIONS.

svelle commented 3 years ago

FWIW after seeing the data @NeilHanlon posted I ran into this issue again and just restarted my phone, the app worked again without issue and I didn't even have to reenter my credentials so it definitely seems to be related to the MAX_USES_PER_BOOT tag.

Sandi89 commented 3 years ago

Getting this issue on variety of Android devices. Samsung S6 being one of the more prevalent.

Aggie123 commented 3 years ago

Same issue here, can't access keychain store consistently on android, does the latest patches work? where to get the latest patches?

svelle commented 3 years ago

@oblador I hope it's okay if I ping you directly. Is there anyone on the maintainer team of this project that can have a look at this? We have more and more users of our app running into this issue on pixel devices running android 11 and would greatly appreciate any help here.

enahum commented 3 years ago

@oblador does 7.0 resolve this issue?

oblador commented 3 years ago

It should fix some Pixel 4 issues, would you mind trying it out and report back?

johnbonds commented 3 years ago

I'm seeing this issue on 7.0.0. It seems to appear after a certain amount of time. After a reboot of the device, the problem goes away but always seems to come back after a while. I store the token that's used for API calls in the keychain and access it each time a call is made. If there is in fact some kind of MAX_USES_PER_BOOT limit, should I not be doing this and just accessing the token once in the keychain and storing it in memory or something?

enahum commented 3 years ago

We ended up patching the library that adds an internal cache, see if this works for you https://github.com/mattermost/mattermost-mobile/blob/master/patches/react-native-keychain+7.0.0.patch

johnbonds commented 3 years ago

Thanks @enahum. I will take a look at your impl. Seems like a pretty glaring issue unless I'm missing something.