Repository navigation
fix(storage): keep RSA key for PKCS1 migration on padding mismatch - #1110
Merged
Merged
Conversation
RSADecrypt deleted the RSA/AES keys on InvalidKeyException (and the AES key + legacy IV on ProviderException) before tryRecoverCurrentAESKey could run the PKCS1 migration, logging out users upgrading from SDK <= 3.12.x / 2.x. RSADecrypt now only throws CryptoException; recovery and cleanup stay in tryRecoverCurrentAESKey, so the MGF1 regeneration from #1048 is unchanged. Fixes auth0/react-native-auth0#1696 Co-Authored-By: Claude (cherry picked from commit 15b91f1)
pmathew92
approved these changes
Oct 26, 2026
Merged
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.
Changes
Cherry-pick of #1107 (3.x) / #1108 (
v5_development) tomain(4.x). The code change is identical.Users who saved credentials with Auth0.Android 3.12.1 or older (incl. 2.x) are logged out on the first launch after updating to 4.2.0 (and 3.21.1 / 3.21.2).
Root cause. Those versions created a PKCS1-only RSA key. Since #1092 (4.x port of #1048), when
RSADecryptgetsInvalidKeyException(KeystoreINCOMPATIBLE_PADDING_MODE), it deletes the RSA and AES keys before throwing. Its only caller,tryRecoverCurrentAESKey, then runsattemptPKCS1Migration, which needs that RSA key. The key is already gone (No RSA key found for migration), so new keys are generated and the stored credentials are cleared.The
ProviderExceptionbranch has a smaller version of the same problem:deleteAESKeys()also removes the legacy IV. Credentials saved by SDK 2.x use the legacy format, so on devices that report the padding mismatch asProviderException, the AES key migrates but the credentials can't be decrypted afterwards.Fix (
CryptoUtil.RSADecryptonly):InvalidKeyException | InvalidAlgorithmParameterException: no longer deletes keys; still throwsCryptoException(neverIncompatibleDeviceException, as introduced in fix(storage): authorize MGF1 digest to prevent INCOMPATIBLE_DEVICE on Keystore2 #1092).ProviderException: no longer callsdeleteAESKeys(); still throwsCryptoException.Recovery stays in
tryRecoverCurrentAESKey: it tries the PKCS1 migration first, and if that fails it deletes both keys and a new key pair is generated.ProviderException, 2.x dataNo public API changes.
References
v5_development): same fixisDeviceIncompatiblefires on fully crypto-capable devices (Galaxy S24+, Pixel) #1016Testing
Unit tests (
CryptoUtilTest): same tests as #1108. The new tests run the realRSADecryptbefore the migration, with a KeyStore mock where a deleted key is really gone.shouldKeepKeysAndThrowCryptoExceptionOnInvalidKeyExceptionWhenTryingToRSADecryptCryptoException, no key deletedshouldKeepKeysAndThrowCryptoExceptionOnInvalidAlgorithmParameterExceptionWhenTryingToRSADecryptCryptoException, no key deletedshouldWrapProviderExceptionFromCipherInitInRSADecryptAsCryptoExceptionCryptoException, storage untouchedshouldMigratePKCS1KeyWhenRealRSADecryptThrowsInvalidKeyExceptionshouldMigratePKCS1KeyAndKeepLegacyIVWhenRealRSADecryptThrowsProviderExceptionshouldMigratePKCS1KeyWhenEncryptIsTheFirstOperationAfterUpgradeencrypt()uses the migrated key, no new AES keyshouldRegenerateKeysWithoutIncompatibleDeviceWhenMgf1DigestIsRejectedIncompatibleDeviceException./gradlew testReleaseUnitTest lintReleaseonmain+ this change: all unit tests pass (1,491;CryptoUtilTest87), lint 0 errors.Device (Pixel 10, Android 17 / API 37, TEE-backed Keystore2). Sample app using
SecureCredentialsManagerwith a real login against a test tenant. Every upgrade is an in-place update (adb install -r, app data kept), followed by a cold start that callsgetCredentials().[PKCS1Padding], legacykey_iv), update, launchRSA key rejected … (legacy PKCS1 key or MGF1 digest mismatch)→PKCS1 migration successful→AES key re-encrypted with OAEP and stored[OAEPPadding],key_ivkept; cold start: no migration, successThe full device matrix (3.12.0 → fix, MGF1 mismatch on 3.21.1 vs fix, 3.21.0 OAEP key → fix, credentials already cleared → fix) ran against the 3.x build of the same
CryptoUtilchange; see #1107.Checklist
Summary by CodeRabbit