A week ago, I looked at the contents of the JWT file
created by the code transparency process. Today, let’s peek at how that gets
Last week, when I showed you a
bundletool command to add code transparency, I used
a command that used a Java keystore directly. That does not seem to be an option for
the verification step. For that (or for adding code transparency), you need an
actual certificate file. You can obtain one from your keystore using
keytool -export \
-alias WhateverAliasYouUsed \
-keystore /path/to/your/keystore.jks \
You can then use the
check-transparency command to verify the contents of… something.
--mode option indicates what the “something” is.
--mode=bundle says that you
are verifying an App Bundle, such as one created by you or your CI server:
bundletool check-transparency \
If you leave off the
print the SHA-256 fingerprint of the certificate:
No APK present. APK signature was not checked.
Code transparency signature is valid. SHA-256 fingerprint of the code transparency key certificate (must be compared with the developer's public key manually): 25 98 AA 59 62 BA 4C C0 7B 40 74 F4 19 09 02 A0 2A CD F1 1B 1F 42 84 92 93 23 8B 6F 87 E5 42 B4
Code transparency verified: code related file contents match the code transparency file.
This should match the one you get from
keytool -list \
-alias WhateverAliasYouUsed \
Alternatively, you can have
bundletool verify the code transparency for an installed app,
bundletool check-transparency \
As before, if you include
bundletool will check
against it; otherwise it will print the SHA-256 fingerprint.
Much of the code for code transparency support in
bundletool resides in
The core “driver” of the verification resides in a set of
ApkTransparencyCheckUtils. This code works off of a list of filesystem paths
to the APKs to check. Where those APKs come from depends on your
particular note, for
adb shell commands
to copy the APKs to a temporary directory for analysis – the verification is
not performed in situ on the device.
The code uses this JSON Web Toolkit library,
which seems to be actively maintained, which is nice.
Unfortunately, the code for
bundletool seems to be fairly monolithic. It does not appear to be organized
as a library with a first-class API that also happens to have a CLI — it looks like it is
just a CLI. And, since
bundletool historically has only been needed for development
machines and CI servers, in many places it seems to assume that environment. Getting verification logic
that can run on-device will require reverse-engineering a spec from the implementation and creating a separate
library, unless Google has interest in a significant reworking of
—Jul 18, 2021
Android 12 Beta 3 is out! And, as one would expect from a late beta, not much has changed.
The good news is that we can use
targetSdkVersion. That means
that we should have reached API stability.
Of the stuff that was announced:
App Search: you saw it here first!
The permission group lookup APIs
are nice, but I seem to recall Google getting rather testy about apps trying to determine the relationship
between permissions and groups. I wonder what changed… 🤔
TranslationManager and the rest of
remain unannounced. They showed up in Beta 1,
and Google hasn’t said anything about them AFAICT. More stuff to 🤔
This should wrap up the 2021 edition of the “Random Musings” posts — if we have
reached API stability, there should be nothing more for me to muse about.
—Jul 15, 2021
Subscribers now have
access to an update to Elements of Android Jetpack,
known as Version 2.1, in PDF, EPUB, and MOBI/Kindle formats, in addition to the
online reader. Just log into
your Warescription page and download
away, or set up an account and subscribe!
OK, this took a lot longer to be released than I had expected, in part because Android Studio 4.2
took a lot longer to be released than I had expected.
There are a lot of changes in this update:
There is a new chapter, focusing on app widgets, those interactive
home screen elements that apps can contribute
There is also a new chapter on
using library modules,
plus a new section on creating library modules
The chapter on dependency inversion was moved up one in the chapter sequence,
and the chapter on Room was updated to use Koin’s DI implementation
for the Kotlin sample
The chapter on Jetpack Navigation now also covers the Kotlin DSL
The various uses of
startActivityForResult() were replaced by
Everything was updated for Android Studio 4.2.2, the current shipping version
And, in addition to all of that, there are the usual suite of bug fixes, to the prose and
to the sample code.
There should be one more update in 2021, after Android Studio 2020.3.1 Arctic Fox
ships in stable form. That is in a beta right now, so it is likely to be at least
a month or two before the stable release.
—Jul 12, 2021
I am starting to spend a bit of time poking around the implementation of
with an eye towards filling in some of the gaps that I wrote about
in my initial thoughts post.
This time, let’s add code transparency to an App Bundle and see what that really means.
Adding Code Transparency
The first thing that you will need is a suitable Java keystore. This should not
be the one that you use for any purpose other than code transparency. It also
needs to have a 3072-bit key size (or higher, presumably). This means that
the Android Studio keystore UI will not work,
and you will need to create the keystore the old-fashioned way:
You will also need an up-to-date copy of
And, of course, you will need an App Bundle for your app.
From there, you can use the
add-transparency command to
add code transparency to a copy of the App Bundle:
bundletool add-transparency \
In 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 are
ways 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 Bundle
.aab files are really ZIP archives, so you can examine them using your
favorite ZIP utility. In an App Bundle with code transparency, you will find a
BUNDLE-METADATA/ directory “is what it says on the tin”: it is metadata about
the contents of the App Bundle. Akin to JAR metadata contents, the contents of
BUNDLE-METADATA/ appear to be namespaced by use, so
will contain metadata related to
is the actual code transparency file.
Decoding the JWT
That file is a JSON Web Token (JWT).
It will be a very long encoded string. For example, a new project
from Android Studio 4.2.2 resulted in a 3460-character code transparency JWT,
looking a bit like:
(with a few thousand additional characters in place of the
So, we need to decode it
by one means or another. JWT is used by lots of systems, and so you may already
have some tools for decoding its contents. Otherwise,
this Web site
offers online decoding, and Linux developers can add a bash function
to 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 inside
of them. They do not validate that the JWT has not been modified — that
is a separate step.
Examining the JWT
That scrap project — based on the “Empty Activity” template FWIW —
gives us the following JWT payload, after decoding:
There is no specification for this payload, something that we will need to rectify
at some point. But, inside the
codeRelatedFile array, we have individual
JSON 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 of
As it turns out, there is a protobuf
.proto file in the
that appears to describe this JSON structure.
NATIVE_LIBRARY is an
DEX is the other value (and presumably is the default value if nothing
apkPath is documented as “Path to file in the APK”; it is not
quite 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 verification
process probably is something like:
Confirm that the JWT is signed by the expected signing key (with that chore largely
being up to us)
Iterate over the JWT payload entries, find the corresponding DEX or
.so file in
the 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 confirm
that 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 using
this to ensure that our apps are not being manipulated.
—Jul 11, 2021
Over nine months since I broached the uncomfortable questions about app signing,
we have the official response. It is simultaneously more than I would have expected
and less than what we need.
These materials have only been in my hand for a few hours, and I expect I’ll write more
about the situation next week. But, let’s see what we got.
First, there is this FAQ entry from “The future of Android App Bundles is here”,
the post where Google firmly declared that the App Bundle requirement is coming in a month:
When distributing apps on Google Play, how do I ensure my app is delivered to users the way I intend?
At any time, you can download and inspect artifacts from the Play Store, from the app bundle explorer in the Play Console, and via the Play Developer API to verify your app. In addition, code transparency for app bundles is a new, optional feature that can be used to inspect that code running on a device matches the code that was originally built and signed by the developer.
The first sentence is typical subterfuge. At best, all downloading those artifacts do
is tell us the state of those specific artifacts.
Google is perfectly capable of delivering different artifacts to different people. It
is not significantly different than is serving different Web pages to different people,
which Google has been doing since its inception. So, they can give unmodified artifacts to
the developer and give tampered artifacts to other parties.
In terms of the second sentence, the passive tense in “can be used” is doing a lot of heavy lifting.
As it turns out, “can” is somewhat theoretical at this time.
More of the details are in the “Code transparency for app bundles”
page in the developer documentation.
(Google seems indecisive over whether or not App Bundle is a proper noun — I will give
it The Capital Treatment here for consistency with past posts on this subject)
Code transparency creates a roster of SHA256 hashes for each DEX file and each
file that is part of the App Bundle. If that file ships to a user as part of an app,
somebody could use it to confirm that the DEX files and
.so files in the app match their
hashes. And, the code transparency file itself is signed, using a signing key private to the developer,
so in principle we can determine if the code transparency file itself has been modified
(e.g., to reflect hashes of tampered files rather than the original files).
That sounds good, and to an extent, it is.
However, all Google needs to do is remove the code transparency file from the apps that they deliver
to users. After all:
They have full signing authority, so they can remove whatever they want
There is no legal or contractual requirement for them to ship this file
There is nothing in the operating system that is looking for this file, as they readily admit:
Important: The Android OS does not verify code transparency files at install time, and continues to rely on the APK signing schemes for verification of any installed APKs.
If there is no code transparency file, there is no code transparency.
Also, verifying that the code transparency file itself is the original implies that we
can validate that its signature is intact and was from the developer’s signing key.
There is no current infrastructure for this, as Google also admits:
Important: To verify that the signature comes from the original developer, the printed fingerprint from one of the following methods must be compared with the public key communicated by the developer through a trusted channel. For example, the developer can host their public certificate on a secure website that is known to belong to them. To ensure no other changes have been made to the app, checking the APK signature is also necessary and recommended.
In addition, the code transparency file conveniently omits some things:
(UPDATE 2021-07-02: The documentation has since been revised to clarify that
the code transparency file is meant to be a complete snapshot of the DEX and
files — if an app contains code that is not in the code transparency file, that
should fail the transparency check)
There has been an app-resigning attack available since Android 8.0 that neatly fits those
The upshot is that we, the developer ecosystem, will need to build a secure, reliable
way for developers to advertise, for any given
versionCode of an
that a code transparency file was included in the App Bundle, along with the certificate (or
certificate hash) that was used to sign that code transparency file. Google, at least
at present, is washing their hands of that mess. Without this, we have no way of knowing,
for any given app, whether a missing code transparency file is a problem and whether
an existing code transparency file is valid.
Then, we, the developer ecosystem, will need to create practical tooling around
this stuff. What Google has given us is new commands for
As a reference implementation, that’s great. As a practical matter, that is very limited.
For example, an anti-malware app running on Android itself is not going to be using
directly via a shell command, if that’s even realistic. We are going to need a specification
for the code transparency file, along with implementations that can be used in more
flexible ways (e.g., a library). It is unclear to what extent Google will be interested in
any of that.
In addition, we, the developer ecosystem, will need to experiment with extending this
system to include other developer-chosen content, such as the manifest, and deal with the
“what if there’s other stuff here than what’s listed?” issue.
Plus, we, the developer ecosystem, will need to get tools in the hands of users to
actually validate the code transparency. It does us no good to have code transparency
files if nothing checks for them and verifies that the app was not modified by Google.
This will likely need to be some mix of existing security-related
apps (e.g., the aforementioned anti-malware apps) and dedicated code transparency
audit apps. Google is unlikely to help here, and frankly, we should view any such help
as being suspect (“the fox guarding the henhouse”).
None of this affects APK-based uses of Play App Signing — official code transparency is
only for App Bundles
None of this affects other distribution channels that employ equivalents to Play
App Signing… such as the Amazon AppStore for Android,
coming to a Windows 11 installation near you soon
We, the developer ecosystem, are going to need to figure out how to address those
scenarios as well.
Worst of all, these are just the things that I have thought of. I am certain to be missing
some problems or skipping over some scenarios, and those might add to the pile of work to be done.
Google’s implementation of code transparency is more than I expected,
insofar as I really did not expect Google to do much of
anything. I have no idea how robust the core elements of their solution are, and we will
only find that out over time. But, the fact that this even exists is a positive step, incomplete
as it may be.
That incompleteness is why this is less than what we need.
And all of this is just to avoid allowing developers to continue distributing APKs
the way that they have for over a decade, if they so choose.
In essence, Google has slashed our car tires, and then has generously offered to pay for a
lift home. While that is a nice gesture, it does not address the problem with the car, and it
would have been nicer if Google had not slashed the tires in the first place.
Again, I expect to write more about this in the coming weeks and months. Suffice it to say:
there’s a lot of work ahead for those of us concerned about the problem. If you are
concerned about the problem, enough to perhaps help with that work,
please reach out!
—Jun 29, 2021