Inside Code Transparency: The Verification Process

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 verified.

bundletool Commands

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:

keytool -export \
  -alias WhateverAliasYouUsed \
  -keystore /path/to/your/keystore.jks \
  -rfc \
  -file /path/to/your/exported.cert

You can then use the check-transparency command to verify the contents of… something. The --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 \
  --mode=bundle \
  --bundle=/path/to/your/AppBundleWithCT.aab \
  --transparency-key-certificate=/path/to/your/exported.cert

If you leave off the --transparency-key-certificate option, bundletool will 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:

keytool -list \
  -alias WhateverAliasYouUsed \
  -keystore /path/to/your/keystore.jks

Alternatively, you can have bundletool verify the code transparency for an installed app, via --mode=connected_device:

bundletool check-transparency \
  --mode=connected_device \
  --package-name=com.commonsware.scrap

As before, if you include --transparency-key-certificate, bundletool will check against it; otherwise it will print the SHA-256 fingerprint.

bundletool Implementation

Much of the code for code transparency support in bundletool resides in the com.android.tools.build.bundletool.transparency package.

The core “driver” of the verification resides in a set of static methods on ApkTransparencyCheckUtils. This code works off of a list of filesystem paths to the APKs to check. Where those APKs come from depends on your --mode. Of particular note, for --mode=connected_device, bundletool uses 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 bundletool.