Skip to content
This repository has been archived by the owner on Mar 3, 2020. It is now read-only.

NativeGCMCipherException on Android 8.1 #184

Open
alessandrojp opened this issue Oct 30, 2017 · 13 comments
Open

NativeGCMCipherException on Android 8.1 #184

alessandrojp opened this issue Oct 30, 2017 · 13 comments

Comments

@alessandrojp
Copy link

Hi,

I have installed the preview of Android 8.1 api 27 and it is failing to decrypt getting the error below:
The same process doesn't happen on on 8.0 and prior.

Crypto#decrypt
com.facebook.crypto.cipher.NativeGCMCipherException: The message could not be decrypted successfully.It has either been tampered with or the wrong resource is being decrypted.

I know that is still in a preview stage, but maybe you could take a look to check what is the reason.

Thank you.

@memichel
Copy link

memichel commented Oct 31, 2017

I can reproduce this exception too but only after save some input and update from Android 8.0 to 8.1 (I don't test the other cases, previous of 8.0).
Then, in Android 8.1, cleaning application data allow to use again conceal. Is there something change to cipher / decipher data on new Android release ?

@memichel
Copy link

No news on this crash ? Because it's still present with the latest Android DP. Below, you can see my latest tests:

Android 7.1

  • Install & first launch of the application
    => Key not present so we create it (random byte used to cipher/decipher with Conceal)
    key=ekc3J6bRHzeWnPqN3jvH4PyscqN0hyKFzHAz5U1Ja3o=

  • Cipher and store a String with Conceal

[putString] Key: "TEST_KEY", value: "2.1.2.5"
[putPlaintextDataWithHashedAlias] keyHashed=9YH6e0rsN3XEhI+gQikTjALlzjxM9AeeyddK1BbdIIY= 
=> cipheredValue_b64=AQIhu+pBG74skIeV2oTUe8Ta743F0v8qeOtkO64CbPQ4sRQrhRqK
  • Try to decipher:
[getString] Key: "TEST_KEY", value: "2.1.2.5"
[decryptAliasHashed] keyHashed=9YH6e0rsN3XEhI+gQikTjALlzjxM9AeeyddK1BbdIIY= 
=> cipheredValue_b64=AQIhu+pBG74skIeV2oTUe8Ta743F0v8qeOtkO64CbPQ4sRQrhRqK
Decipher value [TEST_KEY, 2.1.2.5]

Update to 8.1 via OTA (build OOP6.171019.012)

  • Launch the application
    => Key is present
    retrieved key=ekc3J6bRHzeWnPqN3jvH4PyscqN0hyKFzHAz5U1Ja3o=
  • Try to decipher:
[getString] Key: "TEST_KEY", value: "2.1.2.5"
[getString] key=TEST_KEY;aliasHashed=9YH6e0rsN3XEhI+gQikTjALlzjxM9AeeyddK1BbdIIY=
  • Crash:
[decrypt] Error while decrypting
     com.facebook.crypto.cipher.NativeGCMCipherException: The message could not be decrypted successfully.It has either been tampered with or the wrong resource is being decrypted.
         at com.facebook.crypto.cipher.NativeGCMCipher.decryptFinal(NativeGCMCipher.java:105)
         at com.facebook.crypto.streams.NativeGCMCipherInputStream.ensureTagValid(NativeGCMCipherInputStream.java:109)
         at com.facebook.crypto.streams.NativeGCMCipherInputStream.read(NativeGCMCipherInputStream.java:90)
         at com.facebook.crypto.streams.NativeGCMCipherInputStream.read(NativeGCMCipherInputStream.java:75)
         at com.facebook.crypto.Crypto.decrypt(Crypto.java:146)
         at com.mylib.SSPClass.SSPClass.a(Unknown Source:7)
         at com.mylib.SSPClass.SSPClass.b(Unknown Source:34)
         at com.mylib.SSPClass.SSPClass.getString(Unknown Source:25)
         at my.app.package.SecuredSharedPrefWL.getFromSecuredSharedPref(SecuredSharedPrefWL.java:52)
         at my.app.package.MainActivity$2.onClick(MainActivity.java:46)
         at android.view.View.performClick(View.java:6294)
         at android.view.View$PerformClick.run(View.java:24770)
         at android.os.Handler.handleCallback(Handler.java:790)
         at android.os.Handler.dispatchMessage(Handler.java:99)
         at android.os.Looper.loop(Looper.java:164)
         at android.app.ActivityThread.main(ActivityThread.java:6494)
         at java.lang.reflect.Method.invoke(Native Method)
         at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

     --------- beginning of crash
11-28 09:32:28.280 5444-5444/my.app.package E/AndroidRuntime: FATAL EXCEPTION: main
     Process: my.app.package, PID: 5444
     java.lang.RuntimeException: com.facebook.crypto.cipher.NativeGCMCipherException: The message could not be decrypted successfully.It has either been tampered with or the wrong resource is being decrypted.
         at com.mylib.SSPClass.SSPClass.a(Unknown Source:23)
         at com.mylib.SSPClass.SSPClass.b(Unknown Source:34)
         at com.mylib.SSPClass.SSPClass.getString(Unknown Source:25)
         at my.app.package.SecuredSharedPrefWL.getFromSecuredSharedPref(SecuredSharedPrefWL.java:52)
         at my.app.package.MainActivity$2.onClick(MainActivity.java:46)
         at android.view.View.performClick(View.java:6294)
         at android.view.View$PerformClick.run(View.java:24770)
         at android.os.Handler.handleCallback(Handler.java:790)
         at android.os.Handler.dispatchMessage(Handler.java:99)
         at android.os.Looper.loop(Looper.java:164)
         at android.app.ActivityThread.main(ActivityThread.java:6494)
         at java.lang.reflect.Method.invoke(Native Method)
         at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
      Caused by: com.facebook.crypto.cipher.NativeGCMCipherException: The message could not be decrypted successfully.It has either been tampered with or the wrong resource is being decrypted.
         at com.facebook.crypto.cipher.NativeGCMCipher.decryptFinal(NativeGCMCipher.java:105)
         at com.facebook.crypto.streams.NativeGCMCipherInputStream.ensureTagValid(NativeGCMCipherInputStream.java:109)
         at com.facebook.crypto.streams.NativeGCMCipherInputStream.read(NativeGCMCipherInputStream.java:90)
         at com.facebook.crypto.streams.NativeGCMCipherInputStream.read(NativeGCMCipherInputStream.java:75)
         at com.facebook.crypto.Crypto.decrypt(Crypto.java:146)
         at com.mylib.SSPClass.SSPClass.a(Unknown Source:7)
         at com.mylib.SSPClass.SSPClass.b(Unknown Source:34) 
         at com.mylib.SSPClass.SSPClass.getString(Unknown Source:25) 
         at my.app.package.SecuredSharedPrefWL.getFromSecuredSharedPref(SecuredSharedPrefWL.java:52) 
         at my.app.package.MainActivity$2.onClick(MainActivity.java:46) 
         at android.view.View.performClick(View.java:6294) 
         at android.view.View$PerformClick.run(View.java:24770) 
         at android.os.Handler.handleCallback(Handler.java:790) 
         at android.os.Handler.dispatchMessage(Handler.java:99) 
         at android.os.Looper.loop(Looper.java:164) 
         at android.app.ActivityThread.main(ActivityThread.java:6494) 
         at java.lang.reflect.Method.invoke(Native Method) 
         at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 
         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 

Is somebody observe the same behaviour ?

@alessandrojp
Copy link
Author

What is the library version that you are using?
I have fixed this issue by updating from 1.0.7 to 1.1.3

@memichel
Copy link

I'm already using 1.1.3...

@alessandrojp
Copy link
Author

alessandrojp commented Nov 29, 2017

Are you using 128 or 256 bits to decrypt?
After updating it to 1.1.3 and changing the implementation to encrypt it using 256 bits, I could decrypt it without any problem.

It seems to be an issue in the library and not in the OS.

@psycholic4
Copy link

psycholic4 commented Dec 26, 2017

I have a same problem.
Updating library cannot resolve this problem because I need to decrypt a encrypted text (already made from 7 or 8.0) in 8.1.
(as mentioned above by @memichel)

com.facebook.crypto.cipher.NativeGCMCipherException: The message could not be decrypted successfully.It has either been tampered with or the wrong resource is being decrypted.
     at com.facebook.crypto.cipher.NativeGCMCipher.decryptFinal(NativeGCMCipher.java:105)
     at com.facebook.crypto.streams.NativeGCMCipherInputStream.ensureTagValid(NativeGCMCipherInputStream.java:109)
     at com.facebook.crypto.streams.NativeGCMCipherInputStream.read(NativeGCMCipherInputStream.java:90)
     at com.facebook.crypto.streams.NativeGCMCipherInputStream.read(NativeGCMCipherInputStream.java:75)
     at com.facebook.crypto.Crypto.decrypt(Crypto.java:146)

@cre8ivejp
Copy link

In my case I encrypted the file again using the version 1.1.3 to solve this.
But for you seems to not be an option.
What was the version used to encrypt in 7.1 and 8?!

@psycholic4
Copy link

psycholic4 commented Dec 26, 2017

Our product used 1.0.7 before.
Re-encrypt is not an option because we don't know original text without decrypting it.

@cre8ivejp
Copy link

I am afraid this will not be fixed anytime soon.
One thing you could do is upload the encrypted file to your server, so you could decrypt it using bouncy castle (https://www.bouncycastle.org) and encrypt it again to make it work with the version 1.1.3
So the client side would be able to decrypt it without any problem.

I know this because I encrypt the log file on the client side and I decrypt it using bouncy castle on server side to see the logs.

Either way you will have to do some work to make this work.

@helios175
Copy link
Contributor

helios175 commented Jan 16, 2018

You can try a newer version and still use the same format.
If you call AndroidConceal.get().createCrypto128Bits(keyChain) it will use the same format as the default one from 1.0.7. In fact there are only two:

0x01 0x01 -> 128 bits key encrypted (IV + cipher data + tag)
0x01 0x02 -> 256 bits key encrypted (IV + cipher data + tag)

Then you can call decrypt(...) with the complete byte[] or an input stream. I will check the log of commits to see if there was some bug that could be caused by differences in how OS behave (like always retrieving data in certain block sizes or not).

UPDATE: If the data was encrypted with 1.0.x then you need to pass Entities created with Entity.utf16(...). The entity is simply some 'id' for the data, and it's part of the integrity verification (so a bad entity might make it fail). In Conceal 1.0.x the entity used UTF-16 to transform the text argument into bytes. Problem is UTF-16 can be little endian (LE) or big endian depending on platform. So for 1.1.x and on we use now UTF-8.

In short:

  • if encrypted with 1.0.x use Entities.utf16(...)
  • if 1.1.x use Entities.create(...)

Just guessing:

what happens if UTF-16 encoding changed from 8.0 to 8.1? (at least for the method Entity used). In that case you could try forcing the conversion. Inherit Entity and override getBytes method to return exactly what you want: "yourentitystring".getBytes("UTF-16BE") or "yourentitystring".getBytes("UTF-16LE")

@bhargman
Copy link

what happens if UTF-16 encoding changed from 8.0 to 8.1?

This seems to be the key. Here's what new Entity(String) would return pre-8.1:
image

And here's 8.1 and up:
image

So, it looks like the endianness got switched (perhaps only on some devices, but I just used AVDs).
However, "hi".getBytes("UTF-16BE") or "hi".getBytes("UTF-16LE") will not work, because those methods wipe out the BOM:
image

It's better to return whatever the pre-8.1 device was doing as a byte array if overriding Entity.getBytes(). For example:

@Override
public byte[] getBytes() {
    return new byte[] {-1, -2, 104, 0, 105, 0};
}

@browep
Copy link

browep commented Aug 13, 2018

any update to this? Still trying to find a smooth solution for users who upgraded from 8.0-8.1 to be able to read their old data. Don't have original file to re-encrypt.

@vandac
Copy link

vandac commented Feb 22, 2019

I experienced this issue after upgrading to Android 9.

I fixed this by overriding the Entity class with two strategies.
The first one uses UTF-16LE )little endian which was used up till Android 8) to read old data and UTF-8 to encode into UTF-8.
The backwards compatible utf16 method does not work as on Android 9 the default byte order is big endian and not little endian (tested on Pixel2 and Samsung Galaxy S9)

This way I could decrypt old data and encrypt using the "safer" UTF-8encoding, which does not mess up byte order.

`public class Utf8Entity extends com.facebook.crypto.Entity {

private String mName;

Utf8Entity(String name) {
    super(name);
    this.mName = name;
}

@Override
public byte[] getBytes() {
    return this.mName.getBytes(StandardCharsets.UTF_8);
}

}`

`public class Utf16Entity extends com.facebook.crypto.Entity {

private static final byte[] byteArrayLE = new byte[]{-1, -2};

private String mName;

Utf16Entity(String name) {
    super(name);
    this.mName = name;
}

@Override
public byte[] getBytes() {

    byte[] bytes = this.mName.getBytes(StandardCharsets.UTF_16LE);
    ByteBuffer utf16LittleEndianBytes = ByteBuffer.allocate(bytes.length + byteArrayLE.length)
            // when String is encoded using UTF-16 without specifying the endianness, the byte array is prepended
            // with {-1,-2} bytes defining the endianness.
            .put(byteArrayLE)
            .put(bytes);
    return utf16LittleEndianBytes.array();
}

}`

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

No branches or pull requests

8 participants