kruton / android-keyczar-demo

Demo application using Keyczar on Android
28 stars 2 forks source link

java.lang.AssertionError using Proguard #1

Closed EdmondChuiHW closed 11 years ago

EdmondChuiHW commented 11 years ago

Hi Kenny,

This Fix works great in debug mode, but as I sign and export the app with Proguard, following your rules, a AssertionError arises whenever Keyczar is used. I have cloned your project and it behaves the same (works in debug mode, not in release).

Proguard did not report any errors or warnings, and all the jar libraries have the same hash as listed in this repo, I have also included the static class as appropriate.

I believe you can reproduce this problem by signing this project with Proguard, but here is the stack trace below.

Thank you for your kind attention.

Edmond

-----Stack Trace----- threadid=1: thread exiting with uncaught exception (group=0x40015560) FATAL EXCEPTION: main java.lang.AssertionError at com.google.gson.internal.bind.TypeAdapters$EnumTypeAdapter.(Unknown Source) at com.google.gson.internal.bind.TypeAdapters$26.com.google.gson.TypeAdapter create(com.google.gson.Gson,com.google.gson.reflect.TypeToken)(Unknown Source) at com.google.gson.Gson.com.google.gson.TypeAdapter doubleAdapter(boolean)(Unknown Source) void checkValidFloatingPoint(double) com.google.gson.TypeAdapter longAdapter(com.google.gson.LongSerializationPolicy) com.google.gson.TypeAdapter getAdapter(com.google.gson.reflect.TypeToken) com.google.gson.TypeAdapter getDelegateAdapter(com.google.gson.TypeAdapterFactory,com.google.gson.reflect.TypeToken) com.google.gson.TypeAdapter getAdapter(java.lang.Class) java.lang.String toJson(java.lang.Object) java.lang.String toJson(java.lang.Object,java.lang.reflect.Type) void toJson(java.lang.Object,java.lang.reflect.Type,java.lang.Appendable) void toJson(java.lang.Object,java.lang.reflect.Type,com.google.gson.stream.JsonWriter) java.lang.String toJson(com.google.gson.JsonElement) void toJson(com.google.gson.JsonElement,java.lang.Appendable) com.google.gson.stream.JsonWriter newJsonWriter(java.io.Writer) void toJson(com.google.gson.JsonElement,com.google.gson.stream.JsonWriter) java.lang.Object fromJson(java.lang.String,java.lang.Class) java.lang.Object fromJson(java.lang.String,java.lang.reflect.Type) java.lang.Object fromJson(java.io.Reader,java.lang.reflect.Type) void assertFullConsumption(java.lang.Object,com.google.gson.stream.JsonReader) java.lang.Object fromJson(com.google.gson.stream.JsonReader,java.lang.reflect.Type) void access$000(com.google.gson.Gson,double) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.(Unknown Source) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.boolean excludeField(java.lang.reflect.Field,boolean)(Unknown Source) java.lang.String getFieldName(java.lang.reflect.Field) com.google.gson.TypeAdapter create(com.google.gson.Gson,com.google.gson.reflect.TypeToken) com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$BoundField createBoundField(com.google.gson.Gson,java.lang.reflect.Field,java.lang.String,com.google.gson.reflect.TypeToken,boolean,boolean) java.util.Map getBoundFields(com.google.gson.Gson,com.google.gson.reflect.TypeToken,java.lang.Class) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.boolean excludeField(java.lang.reflect.Field,boolean)(Unknown Source) java.lang.String getFieldName(java.lang.reflect.Field) com.google.gson.TypeAdapter create(com.google.gson.Gson,com.google.gson.reflect.TypeToken) com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$BoundField createBoundField(com.google.gson.Gson,java.lang.reflect.Field,java.lang.String,com.google.gson.reflect.TypeToken,boolean,boolean) java.util.Map getBoundFields(com.google.gson.Gson,com.google.gson.reflect.TypeToken,java.lang.Class) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.boolean excludeField(java.lang.reflect.Field,boolean)(Unknown Source) java.lang.String getFieldName(java.lang.reflect.Field) com.google.gson.TypeAdapter create(com.google.gson.Gson,com.google.gson.reflect.TypeToken) com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$BoundField createBoundField(com.google.gson.Gson,java.lang.reflect.Field,java.lang.String,com.google.gson.reflect.TypeToken,boolean,boolean) java.util.Map getBoundFields(com.google.gson.Gson,com.google.gson.reflect.TypeToken,java.lang.Class) at com.google.gson.Gson.com.google.gson.TypeAdapter doubleAdapter(boolean)(Unknown Source) void checkValidFloatingPoint(double) com.google.gson.TypeAdapter longAdapter(com.google.gson.LongSerializationPolicy) com.google.gson.TypeAdapter getAdapter(com.google.gson.reflect.TypeToken) com.google.gson.TypeAdapter getDelegateAdapter(com.google.gson.TypeAdapterFactory,com.google.gson.reflect.TypeToken) com.google.gson.TypeAdapter getAdapter(java.lang.Class) java.lang.String toJson(java.lang.Object) java.lang.String toJson(java.lang.Object,java.lang.reflect.Type) void toJson(java.lang.Object,java.lang.reflect.Type,java.lang.Appendable) void toJson(java.lang.Object,java.lang.reflect.Type,com.google.gson.stream.JsonWriter) java.lang.String toJson(com.google.gson.JsonElement) void toJson(com.google.gson.JsonElement,java.lang.Appendable) com.google.gson.stream.JsonWriter newJsonWriter(java.io.Writer) void toJson(com.google.gson.JsonElement,com.google.gson.stream.JsonWriter) java.lang.Object fromJson(java.lang.String,java.lang.Class) java.lang.Object fromJson(java.lang.String,java.lang.reflect.Type) java.lang.Object fromJson(java.io.Reader,java.lang.reflect.Type) void assertFullConsumption(java.lang.Object,com.google.gson.stream.JsonReader) java.lang.Object fromJson(com.google.gson.stream.JsonReader,java.lang.reflect.Type) void access$000(com.google.gson.Gson,double) at com.google.gson.Gson.com.google.gson.TypeAdapter doubleAdapter(boolean)(Unknown Source) void checkValidFloatingPoint(double) com.google.gson.TypeAdapter longAdapter(com.google.gson.LongSerializationPolicy) com.google.gson.TypeAdapter getAdapter(com.google.gson.reflect.TypeToken) com.google.gson.TypeAdapter getDelegateAdapter(com.google.gson.TypeAdapterFactory,com.google.gson.reflect.TypeToken) com.google.gson.TypeAdapter getAdapter(java.lang.Class) java.lang.String toJson(java.lang.Object) java.lang.String toJson(java.lang.Object,java.lang.reflect.Type) void toJson(java.lang.Object,java.lang.reflect.Type,java.lang.Appendable) void toJson(java.lang.Object,java.lang.reflect.Type,com.google.gson.stream.JsonWriter) java.lang.String toJson(com.google.gson.JsonElement) void toJson(com.google.gson.JsonElement,java.lang.Appendable) com.google.gson.stream.JsonWriter newJsonWriter(java.io.Writer) void toJson(com.google.gson.JsonElement,com.google.gson.stream.JsonWriter) java.lang.Object fromJson(java.lang.String,java.lang.Class) java.lang.Object fromJson(java.lang.String,java.lang.reflect.Type) java.lang.Object fromJson(java.io.Reader,java.lang.reflect.Type) void assertFullConsumption(java.lang.Object,com.google.gson.stream.JsonReader) java.lang.Object fromJson(com.google.gson.stream.JsonReader,java.lang.reflect.Type) void access$000(com.google.gson.Gson,double) at com.google.gson.Gson.com.google.gson.TypeAdapter doubleAdapter(boolean)(Unknown Source) void checkValidFloatingPoint(double) com.google.gson.TypeAdapter longAdapter(com.google.gson.LongSerializationPolicy) com.google.gson.TypeAdapter getAdapter(com.google.gson.reflect.TypeToken) com.google.gson.TypeAdapter getDelegateAdapter(com.google.gson.TypeAdapterFactory,com.google.gson.reflect.TypeToken) com.google.gson.TypeAdapter getAdapter(java.lang.Class) java.lang.String toJson(java.lang.Object) java.lang.String toJson(java.lang.Object,java.lang.reflect.Type) void toJson(java.lang.Object,java.lang.reflect.Type,java.lang.Appendable) void toJson(java.lang.Object,java.lang.reflect.Type,com.google.gson.stream.JsonWriter) java.lang.String toJson(com.google.gson.JsonElement) void toJson(com.google.gson.JsonElement,java.lang.Appendable) com.google.gson.stream.JsonWriter newJsonWriter(java.io.Writer) void toJson(com.google.gson.JsonElement,com.google.gson.stream.JsonWriter) java.lang.Object fromJson(java.lang.String,java.lang.Class) java.lang.Object fromJson(java.lang.String,java.lang.reflect.Type) java.lang.Object fromJson(java.io.Reader,java.lang.reflect.Type) void assertFullConsumption(java.lang.Object,com.google.gson.stream.JsonReader) java.lang.Object fromJson(com.google.gson.stream.JsonReader,java.lang.reflect.Type) void access$000(com.google.gson.Gson,double) at com.google.gson.Gson.com.google.gson.TypeAdapter doubleAdapter(boolean)(Unknown Source) void checkValidFloatingPoint(double) com.google.gson.TypeAdapter longAdapter(com.google.gson.LongSerializationPolicy) com.google.gson.TypeAdapter getAdapter(com.google.gson.reflect.TypeToken) com.google.gson.TypeAdapter getDelegateAdapter(com.google.gson.TypeAdapterFactory,com.google.gson.reflect.TypeToken) com.google.gson.TypeAdapter getAdapter(java.lang.Class) java.lang.String toJson(java.lang.Object) java.lang.String toJson(java.lang.Object,java.lang.reflect.Type) void toJson(java.lang.Object,java.lang.reflect.Type,java.lang.Appendable) void toJson(java.lang.Object,java.lang.reflect.Type,com.google.gson.stream.JsonWriter) java.lang.String toJson(com.google.gson.JsonElement) void toJson(com.google.gson.JsonElement,java.lang.Appendable) com.google.gson.stream.JsonWriter newJsonWriter(java.io.Writer) void toJson(com.google.gson.JsonElement,com.google.gson.stream.JsonWriter) java.lang.Object fromJson(java.lang.String,java.lang.Class) java.lang.Object fromJson(java.lang.String,java.lang.reflect.Type) java.lang.Object fromJson(java.io.Reader,java.lang.reflect.Type) void assertFullConsumption(java.lang.Object,com.google.gson.stream.JsonReader) java.lang.Object fromJson(com.google.gson.stream.JsonReader,java.lang.reflect.Type) void access$000(com.google.gson.Gson,double) at com.google.gson.Gson.com.google.gson.TypeAdapter doubleAdapter(boolean)(Unknown Source) void checkValidFloatingPoint(double) com.google.gson.TypeAdapter longAdapter(com.google.gson.LongSerializationPolicy) com.google.gson.TypeAdapter getAdapter(com.google.gson.reflect.TypeToken) com.google.gson.TypeAdapter getDelegateAdapter(com.google.gson.TypeAdapterFactory,com.google.gson.reflect.TypeToken) com.google.gson.TypeAdapter getAdapter(java.lang.Class) java.lang.String toJson(java.lang.Object) java.lang.String toJson(java.lang.Object,java.lang.reflect.Type) void toJson(java.lang.Object,java.lang.reflect.Type,java.lang.Appendable) void toJson(java.lang.Object,java.lang.reflect.Type,com.google.gson.stream.JsonWriter) java.lang.String toJson(com.google.gson.JsonElement) void toJson(com.google.gson.JsonElement,java.lang.Appendable) com.google.gson.stream.JsonWriter newJsonWriter(java.io.Writer) void toJson(com.google.gson.JsonElement,com.google.gson.stream.JsonWriter) java.lang.Object fromJson(java.lang.String,java.lang.Class) java.lang.Object fromJson(java.lang.String,java.lang.reflect.Type) java.lang.Object fromJson(java.io.Reader,java.lang.reflect.Type) void assertFullConsumption(java.lang.Object,com.google.gson.stream.JsonReader) java.lang.Object fromJson(com.google.gson.stream.JsonReader,java.lang.reflect.Type) void access$000(com.google.gson.Gson,double) at org.keyczar.KeyMetadata.org.keyczar.enums.KeyPurpose getPurpose()(Unknown Source) org.keyczar.KeyMetadata read(java.lang.String) at org.keyczar.Keyczar.(Unknown Source) at org.keyczar.Encrypter.(Unknown Source) at org.keyczar.Crypter.(Unknown Source) at com.example.android.keyczardemo.KeyczarDemoActivity.void onCreate(android.os.Bundle)(Unknown Source) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663) at android.app.ActivityThread.access$1500(ActivityThread.java:117) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:130) at android.app.ActivityThread.main(ActivityThread.java:3683) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:507) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597) at dalvik.system.NativeStart.main(Native Method) Force finishing activity com.example.android.keyczardemo/.KeyczarDemoActivity

-----Stack Trace-----

kruton commented 11 years ago

It probably uses reflection. Try adding this to the proguard.cfg file:

-keepattributes Signature
-keepattributes *Annotation*

and perhaps something else from http://code.google.com/p/google-gson/source/browse/trunk/examples/android-proguard-example/proguard.cfg

# Preserve the special static methods that are required in all enumeration classes.
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
EdmondChuiHW commented 11 years ago

Hi Kenny,

I've added the Proguard rules from Google Gson, but instead of AssetionError, it now throws KeyczarException whenever I try to instantiate Crypter. The key files were exactly the same and they work on debug version as well.

Thanks again!

-----Stack Trace----- org.keyczar.exceptions.KeyczarException: Unacceptable purpose: TEST W/System.err(14354): org.keyczar.exceptions.KeyczarException: Unacceptable purpose: TEST W/System.err(14354): at org.keyczar.Keyczar.(SourceFile:83) W/System.err(14354): at org.keyczar.Encrypter.(SourceFile:61) W/System.err(14354): at org.keyczar.Crypter.(SourceFile:56) -----Stack Trace-----

kruton commented 11 years ago

You can probably add this to get going, but it's a big hammer:

-keep class org.keyczar.** { *; }
EdmondChuiHW commented 11 years ago

Working now.

So I guess somewhere in those libraries uses reflection to do their work properly, and Proguard trimmed them out because they are not "called" directly. By keeping the whole Keyczar library untouched with the trade off of larger apk size seems to be the only solution.

Thanks again! Have a nice day. :D