Inside Code Transparency: The JWT File
I am starting to spend a bit of time poking around the implementation ofcode transparency,with an eye towards filling in some of the gaps that I wrote aboutin my initial thoughts post.
This time, let���s add code transparency to an App Bundle and see what that really means.
Adding Code TransparencyThe first thing that you will need is a suitable Java keystore. This should notbe the one that you use for any purpose other than code transparency. It alsoneeds to have a 3072-bit key size (or higher, presumably). This means thatthe Android Studio keystore UI will not work,and you will need to create the keystore the old-fashioned way:using keytool.
You will also need an up-to-date copy of bundletool.
And, of course, you will need an App Bundle for your app.
From there, you can use the add-transparency command toadd code transparency to a copy of the App Bundle:
bundletool add-transparency \ --bundle=/path/to/your/AppBundle.aab \ --output=/path/to/your/AppBundleWithCT.aab \ --ks=/path/to/your/keystore.jks \ --ks-key-alias=WhateverAliasYouUsedIn a nutshell:
--bundle points to the App Bundle that you created (e.g., from Studio)
--output points to where you want bundletool to write the augmented App Bundle
--ks points to your keystore
--ks-key-alias is the alias of the key inside that keystore that you wish to use
You will be prompted for the keystore password at the command line, or there areways to use a --ks-pass command-line option to supply it.
This may take several seconds or longer, depending on the size of your App Bundle,the power of your machine running bundletool, the current phase of the moon, etc.
Examining the Augmented App BundleApp Bundle .aab files are really ZIP archives, so you can examine them using yourfavorite ZIP utility. In an App Bundle with code transparency, you will find afile at:
/BUNDLE-METADATA/com.android.tools.build.bundletool/co...The BUNDLE-METADATA/ directory ���is what it says on the tin���: it is metadata aboutthe contents of the App Bundle. Akin to JAR metadata contents, the contents ofBUNDLE-METADATA/ appear to be namespaced by use, so com.android.tools.build.bundletoolwill contain metadata related to bundletool. code_transparency_signed.jwtis the actual code transparency file.
Decoding the JWTThat file is a JSON Web Token (JWT).It will be a very long encoded string. For example, a new projectfrom Android Studio 4.2.2 resulted in a 3460-character code transparency JWT,looking a bit like:
eyJhbGciOiJSUzI1NiIsIng1YyI6WyJNSUlFMVRDQ0FyMmdBd0lCQWdJRVMwekdQVEFOQ...(with a few thousand additional characters in place of the ...)
So, we need to decode itby one means or another. JWT is used by lots of systems, and so you may alreadyhave some tools for decoding its contents. Otherwise,this Web siteoffers online decoding, and Linux developers can add a bash functionto decode at the command line.
(macOS and Windows developers: I���m sure you have something good to use too!)
Note that these tools merely decode the JWT, allowing us to see what is insideof them. They do not validate that the JWT has not been modified ��� thatis a separate step.
Examining the JWTThat scrap project ��� based on the ���Empty Activity��� template FWIW ���gives us the following JWT payload, after decoding:
{ "codeRelatedFile": [ { "path": "base/dex/classes.dex", "sha256": "c8a57ffe798c896f1b2c5f33862cbde817bb233c291217e3511245e1e9b91c82" }, { "path": "base/dex/classes2.dex", "sha256": "192a3e51f14682fb41b91b99e916b068e818ad598d7d5659ea9c7e26c201de15" }, { "path": "base/dex/classes3.dex", "sha256": "08cae05a2180b2249079bbabedaf3a8ac20bef1e4a3d4f8ea319ea4c2f42a396" } ]}There is no specification for this payload, something that we will need to rectifyat some point. But, inside the codeRelatedFile array, we have individualJSON objects, each having:
path: a relative path, from the base of the .aab ZIP contents, of a ���code-related file���, such as a DEX file
sha256: the SHA-256 hash of the contents of the identified file
DEX files get this shorthand JSON syntax. Native libraries have a couple ofadditional properties:
{ "path": "base/lib/x86_64/libflipper.so", "type": "NATIVE_LIBRARY", "apkPath": "lib/x86_64/libflipper.so", "sha256": "3f4d0a1a03b7825cb5350453c579d39f9f3369f885b8906dfdf1748510186664" },As it turns out, there is a protobuf .proto file in the bundletool projectthat appears to describe this JSON structure. NATIVE_LIBRARY is an enumvalue, where DEX is the other value (and presumably is the default value if nothingis provided). apkPath is documented as ���Path to file in the APK���; it is notquite clear to me why this is needed for native libraries but not DEX files.
I still have not yet torn into the bundletool implementation, but the verificationprocess probably is something like:
Confirm that the JWT is signed by the expected signing key (with that chore largelybeing up to us)
Iterate over the JWT payload entries, find the corresponding DEX or .so file inthe APKs installed for this app, and validate the SHA-256 hashes
Iterate over the DEX and .so files of the APKs installed for this app and confirmthat everything there was represented in the JWT (so there has not been a code insertion attack)
I will continue blogging about code transparency in the coming weeks and months,as I try to make sense of how we can cover what Google has not: actually usingthis to ensure that our apps are not being manipulated.


