react-native-keychain icon indicating copy to clipboard operation
react-native-keychain copied to clipboard

cipher.init() is slow on Motorola Edge 50 Ultra.

Open yberstad opened this issue 8 months ago • 6 comments

Description

I'm experiencing slow performance when retrieving values from the secure store using getInternetCredentialsForServer on my Motorola Edge 50 Ultra. It takes arround 1900 ms each time. Some of our customers are also reporting similar issues on other devices as well. The Motorola Edge 50 Ultra is running Android 15.

I have added logging to pinpoint where time is consumed, and it seems that the cipher.initit() in the CipherStorageKeystoreAesGcm class that takes arround 600ms each time. It is very consistent.

    /** Initialization vector support. */
    object IV {
        const val IV_LENGTH = 12
        const val TAG_LENGTH = 128

        val encrypt = EncryptStringHandler { cipher, key, output ->
            cipher.init(Cipher.ENCRYPT_MODE, key)
            val iv = cipher.iv
            output.write(iv, 0, iv.size)
        }

        val decrypt = DecryptBytesHandler { cipher, key, input ->
            val iv = ByteArray(IV_LENGTH)

            timeItNoSuspend(tag = "CustomTimer", name = "IV.read") {
                val result = input.read(iv, 0, IV_LENGTH)
                if (result != IV_LENGTH) throw IOException("Input stream has insufficient data.")
            }

            val spec = GCMParameterSpec(TAG_LENGTH, iv)

            timeItNoSuspend(tag = "CustomTimer", name = "IV.cipherInit") {
                cipher.init(Cipher.DECRYPT_MODE, key, spec)
            }
        }
    }
     12:36:00.688 10072 10394 D CustomTimer: getInternetCredentialsForServer ▶ start
     12:36:00.707 10072 10401 D CustomTimer: extractGeneratedKey ▶ completed in 2ms
     12:36:00.707 10072 10401 D CustomTimer: decryptBytes.getCachedInstance ▶ completed in 0ms
     12:36:00.707 10072 10401 D CustomTimer: IV.read ▶ completed in 0ms
 ->  12:36:01.291 10072 10401 D CustomTimer: IV.cipherInit ▶ completed in 584ms
     12:36:01.291 10072 10401 D CustomTimer: decryptBytes.handlerInitialize ▶ completed in 584ms
     12:36:01.292 10072 10401 D CustomTimer: decryptBytes.readBytes ▶ completed in 0ms
     12:36:01.423 10072 10401 D CustomTimer: decryptBytes.cipherDoFinal ▶ completed in 131ms
     12:36:01.423 10072 10401 D CustomTimer: decryptBytes.total ▶ completed in 716ms
     12:36:01.423 10072 10401 D CustomTimer: decryptBytes.getCachedInstance ▶ completed in 0ms
     12:36:01.423 10072 10401 D CustomTimer: IV.read ▶ completed in 0ms
 ->  12:36:02.011 10072 10401 D CustomTimer: IV.cipherInit ▶ completed in 587ms
     12:36:02.011 10072 10401 D CustomTimer: decryptBytes.handlerInitialize ▶ completed in 588ms
     12:36:02.011 10072 10401 D CustomTimer: decryptBytes.readBytes ▶ completed in 0ms
     12:36:02.538 10072 10401 D CustomTimer: decryptBytes.cipherDoFinal ▶ completed in 527ms
     12:36:02.539 10072 10401 D CustomTimer: decryptBytes.total ▶ completed in 1115ms
     12:36:02.539 10072 10401 D CustomTimer: DecryptionResult ▶ completed in 1831ms
     12:36:02.539 10072 10401 D CustomTimer: decryptToResult ▶ completed in 1834ms
     12:36:02.539 10072 10401 D CustomTimer: getInternetCredentialsForServer ✔ resolved in 1852ms       

I have configured the setInternetCredentials as such:

    await setInternetCredentials(key, key, value, {
      accessible: ACCESSIBLE.AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY,
      storage: STORAGE_TYPE.AES_GCM_NO_AUTH,
    });

Is there anything I can do to make the cipher.initit() run faster, or is there some way to reuse / warm up / cache the the result of the cipher.initit() to improve performance?

Thanks in advance.

Steps to reproduce

Reproducable on some devices, Google Pixel 8a, but especially on Motorola Edge 50 Ultra.

Snack or a link to a repository

https://github.com/yberstad/react-native-keychain/tree/v9.2.2-fork

React Native Keychain version

9.2.2

React Native version

0.77.0

Platforms

Android

Workflow

Expo bare workflow

Architecture

Fabric (New Architecture)

yberstad avatar May 05 '25 11:05 yberstad

I have testet setting the securityLevel to SECURE_SOFTWARE, to use the TEE, instead of the StrongBox, just to see if this would improve the performance in favour of security. I deleted the app, to be sure that the secure storage was recreated, but this did not imporve anything regarding performace. I thought this was a bit strange?

    await setInternetCredentials(key, key, value, {
      accessible: ACCESSIBLE.AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY,
      storage: STORAGE_TYPE.AES_GCM_NO_AUTH,
      securityLevel: SECURITY_LEVEL.SECURE_SOFTWARE,
    });

yberstad avatar May 07 '25 06:05 yberstad

I think it's actually related to strongbox backed keys. I'm doing some analysis of performance and noticed that disabling strongox backed keys improves performance a lot. My test was done on Motorola Razr 50 Ultra. It does improve on top devices as well, like S24, but very little.

Also, following playstore data, devices with particular CPU cores are highly impacted, for example the performance of StrongBox-backed cryptographic operations on devices with Arm Cortex-A53 and Cortex-A55 cores is very bad. I think it would be good to either exclude some devices when deciding if strongbox should be enabled or not on library level or provide a way to disable it from the apps that use rn-keychain.

androideveloper avatar May 13 '25 15:05 androideveloper

As a simple improvement, should we actually increase the sdk level requirement for strongbox usage @DorianMazur ? Currently it's set to Android 9.

I read that there were quite a lot of improvements to strongbox in the recent versions and data also confirms that devices up to Android 14 are more impacted by cold app start issues.

Image

androideveloper avatar May 15 '25 13:05 androideveloper

@androideveloper Yeah, I think we could raise the minimum to Android 12/13. But the Motorola Edge 50 Ultra is a new phone, even though it's low-end and it's having issues

DorianMazur avatar May 15 '25 18:05 DorianMazur

Yes, that won't cover all cases, but will improve a bit performance of apps on pre-android 12/13 devices. I think in case of Motorola it depends on implementation of strongbox on vendor side.

androideveloper avatar May 16 '25 07:05 androideveloper

I've prepared PR for it https://github.com/oblador/react-native-keychain/pull/754

androideveloper avatar May 16 '25 10:05 androideveloper