Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reintroduce a fix for strongbox decryption data corruption (#383) #385

Merged
merged 5 commits into from
Sep 7, 2020

Conversation

cshfang
Copy link

@cshfang cshfang commented Aug 27, 2020

A PR (#288) a while back was merged to fix an issue where decrypting the keychain using a CipherInputStream causes certain Google Pixel devices to throw an exception and hang.

The change seems to have been inadvertently reverted in the large refactoring that took place in #260.

This PR reintroduces that fix.

Copy link
Collaborator

@aeirola aeirola left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, the code looks good! Would still like to clean it up a bit by removing the mostly unused byte array streams. Otherwise I'm worried they might cause some confusion in the future.

Chris Fang added 2 commits August 28, 2020 09:54
* Clean up comments referencing input stream

* Update handler to expect bytes array instead of input stream

* Remove unused resources in try
@@ -505,7 +504,7 @@ public static String getDefaultAliasIfEmpty(@Nullable final String service, @Non
public static IvParameterSpec readIv(@NonNull final byte[] bytes) throws IOException {
final byte[] iv = new byte[IV_LENGTH];

if (IV_LENGTH <= bytes.length)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When switching over to this overloaded method handling byte[], this condition was triggered. Reading through this method, I believe the intent may have been to validate that the input bytes array is actually greater than IV_LENGTH.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoa, strange bug. Wonder if this code was ever used before.

@aeirola
Copy link
Collaborator

aeirola commented Aug 31, 2020

The code looks good, and normal encoding and decoding seems to work OK on Android. But when trying to get stored values which are protected with fingerprint authentication, I get the a javax.crypto.IllegalBlockSizeException error caused by android.security.KeyStoreException: Invalid input length. Apparently the AES and RSA cipher storages use the same decryptBytes method, and the RSA cipher used for fingerprints doesn't allow decyrpting data using doFinal with arbitrary length input.

Might be that we need to move the modified decryptBytes method from CipherStorageBase to the CipherStorageKeystoreAesCbc class, so that we don't interfere with the workings of the other cipher storage implementations.

This issue wasn't present in the previous fix for the same issue, as the Android fingerprints weren't supported yet at the time.

@cshfang
Copy link
Author

cshfang commented Aug 31, 2020

The code looks good, and normal encoding and decoding seems to work OK on Android. But when trying to get stored values which are protected with fingerprint authentication, I get the a javax.crypto.IllegalBlockSizeException error caused by android.security.KeyStoreException: Invalid input length. Apparently the AES and RSA cipher storages use the same decryptBytes method, and the RSA cipher used for fingerprints doesn't allow decyrpting data using doFinal with arbitrary length input.

Might be that we need to move the modified decryptBytes method from CipherStorageBase to the CipherStorageKeystoreAesCbc class, so that we don't interfere with the workings of the other cipher storage implementations.

This issue wasn't present in the previous fix for the same issue, as the Android fingerprints weren't supported yet at the time.

Ah thanks for the good catch. Based on what you're saying, it makes sense to me to try to make the modified decryptBytes method the exception for AES rather than the norm. I'll work on this today.

* Refactored strongbox decryption changes to apply only to CipherStorageKeystoreAesCbc
@cshfang
Copy link
Author

cshfang commented Sep 1, 2020

@aeirola I pushed a refactor to override decryptBytes within the CipherStorageKeystoreAesCbc class. It seems like the changes are still holding up in my testing but I only tested with a fairly contrived RSA set/get. I'm not sure how to replicate the android.security.KeyStoreException: Invalid input length you found above so may have to lean on you for further testing.

Copy link
Collaborator

@aeirola aeirola left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good, and works well in my testing. Might personally prefer to keep the changes even more local to the CipherStorageKeystoreAesCbc class by not using generics for the DecryptBytesHandler interface, but this is more of a matter of taste. Feel free to change it or ignore my comments :)

Before merging I'll try to contact people who've faced the issue and see wether they would be willing to help out testing this PR with actual Titan M devices.

@cshfang
Copy link
Author

cshfang commented Sep 1, 2020

Before merging I'll try to contact people who've faced the issue and see wether they would be willing to help out testing this PR with actual Titan M devices.

FWIW, I have been testing this against my physical Pixel 4 device as well, which I believe utilizes the Titan M.

@aeirola aeirola mentioned this pull request Sep 1, 2020
@aeirola
Copy link
Collaborator

aeirola commented Sep 1, 2020

@Metroidaron @nicolashemonic Hi, I remember that you've previously helped this project by testing issues on Pixel devices in #208 . Now we're in a situation where we have faced a regression in the support for Pixel devices with Titan M security chips. In order to verify that our fix is valid, I'd like to ask wether you would be able to help out once again by checking if the latest code in this PR still works on your devices and use cases. The code from this PR can be installed with npm install github:cshfang/react-native-keychain#fix-strongbox-decryption. Thanks again!

* Move away from generic typing in DecryptBytesHandler interface

* Call readIv directly in AES cipher storage decryptBytes rather than delegating to handler
@aeirola
Copy link
Collaborator

aeirola commented Sep 7, 2020

Unfortunately there wasn't much interest in testing the fix on additional devices. Would have been nice with some extra validations with different devices, but won't let that block the deployment of the fix as the code looks good and you already tested on one Pixel 4 device. I'll merge this and see if we can get it released soon.

@aeirola aeirola merged commit c3ecdda into oblador:master Sep 7, 2020
@aeirola
Copy link
Collaborator

aeirola commented Sep 7, 2020

@oblador Do we have a schedule for the next release? Releaseing this fix would be useful for a certain group of Android users.

@oblador
Copy link
Owner

oblador commented Sep 8, 2020

@aeirola It's released in 6.2.0!

@nicolashemonic
Copy link

Hi @aeirola @oblador

Sorry for the silence because I was in long vacation 🏖 and busy when back to the office.

I upgraded my app to [email protected] and I confirm that the fix works well on Pixel 3a Android 11. The authentication (very long token stored in keychain) is not lost when the app is closed and opened.

Thanks !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

getGenericCredentials never resolves or rejects due to ShortBufferException in Google Pixel 4
4 participants