conceal icon indicating copy to clipboard operation
conceal copied to clipboard

NativeGCMCipherException on Android 8.1

Open alessandrojp opened this issue 8 years ago • 13 comments

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.

alessandrojp avatar Oct 30 '17 11:10 alessandrojp

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 avatar Oct 31 '17 10:10 memichel

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 ?

memichel avatar Nov 28 '17 13:11 memichel

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

alessandrojp avatar Nov 28 '17 13:11 alessandrojp

I'm already using 1.1.3...

memichel avatar Nov 28 '17 13:11 memichel

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.

alessandrojp avatar Nov 29 '17 04:11 alessandrojp

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)

psycholic4 avatar Dec 26 '17 06:12 psycholic4

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?!

cre8ivejp avatar Dec 26 '17 07:12 cre8ivejp

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

psycholic4 avatar Dec 26 '17 07:12 psycholic4

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.

cre8ivejp avatar Dec 26 '17 08:12 cre8ivejp

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")

helios175 avatar Jan 16 '18 06:01 helios175

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};
}

bhargman avatar Jun 15 '18 21:06 bhargman

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.

browep avatar Aug 13 '18 19:08 browep

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();
}

}`

vandac avatar Feb 22 '19 12:02 vandac