Binary Transparency for Pixel Factory Images

You can learn more about Binary Transparency on the Binary Transparency main page and for specific definitions and context of the technical terms see the Glossary.

Overview

Applying Binary Transparency to Pixel factory images enables the verify in trust but verify, enabling users to better assure themselves by technical means of the integrity and provenance of Pixel factory images.

The Pixel verifiable log contains all Pixel factory images published by Google since the availability of the Pixel 6 generation of phones. The goal is to enable device owners to verify that factory images on their phones are the same ones published by Google and enable discoverability and authenticity for all published Pixel factory images. Google updates the factory images for a device throughout the device lifecycle.

All factory images that are publicly downloadable are listed on the Factory Images for Pixel Devices Google developer website page. Every one of these, beginning from Pixel 6, must also appear in the transparency log.

Metadata about factory images (in the form of build_fingerprint and VBMeta digest) is logged to this tamper-evident, verifiable log. Information from this log is publicly accessible, and we publish open-source tools that enable verification by obtaining proofs from the log.

Binary Transparency for Pixel 6 enables a user to verify the integrity and provenance of the factory images they download by ensuring that there is an associated entry in the transparency log.

Claimant Model

The Claimant Model is a framework used to define the roles and artifacts in your verifiable system. In the case of Binary Transparency, the claim we make is that the factory images downloaded from our official website are official Google/Pixel factory images.

  • ClaimFactoryImage: (I, Google, claim that $factoryImage_instance is for $deviceNameOrSku), where:
    • $factoryImage_instance are all factory images, beginning from Pixel 6, available for download from the official website.
    • $deviceNameOrSku can be determined by navigating to "Settings->About phone->Device name" from the user's device. The corresponding $deviceNameOrSku can be used to find the correct $factoryImage_instance from the website mentioned earlier.

Threat Model

Transparency systems are designed and deployed to enable detection—and thus deter—supply chain attacks.

For example, let's suppose a malicious image is distributed, and that the attacker has even managed to cause the image to appear to be correctly signed. Binary Transparency enables the detection of such an attack because the manifest of this malicious image was not added to the transparency log by the legitimate owner of the signing key. If the manifest were added to the log, the public nature of the log would allow the easy discovery of this malicious image.

Pixel Factory Images Transparency Log

Whenever a new release is available for a Pixel 6 (and beyond) device, we will be adding a manifest for that build as a new entry into the transparency log. This log is publicly accessible and thus can be used to verify that the factory image that you possess is included in the Merkle tree.

Log Content

As the log payload, each entry in the log conforms to the following format:

first_value\nsecond_value\n

The first value is the build fingerprint of a corresponding factory image. For Pixel devices, this is a unique human-readable string that describes the build. However, this value is not cryptographically bound. This allows for a quick eyeball verification of all the entries in the log for a verifier. For example, it allows for a verifier to quickly detect if a build fingerprint appears twice on the log. For more information about build fingerprint, please refer to Android Build documentation site.

The second value is a hex-encoded string representing the VBMeta digest for the build whose fingerprint is recorded as the first value. In a nutshell, this digest is composed from all the other VBMeta structs for a factory image. This value is used during the Android Verified Boot process to determine the integrity of the various binaries loaded. Therefore, the ability to reliably extract the VBMeta digest (e.g. via attestation channels) of a live device and check whether that digest is included in the transparency log gives you the confidence that your device is running a legitimate version of firmware. This helps to mitigate the insider attack we described in the Threat Model section.

Verification

Claim Verification

Since we do not yet have any enforcing components built into the early boot phase (e.g. bootloader) of Pixel 6, our current verification strategy relies on manual and offline operations that can be performed by a technically proficient verifier.

Verifiable Log System Diagram

Methods of Verification

In this release, we emphasize one main workflow of verification to obtain the highest degree of confidence in the verification result. This involves downloading a factory image onto your computer, and performing a log inclusion proof on the downloaded artifact before flashing them onto your device.

How to Verify Your Pixel Factory Image

Prerequisites to Verifying

Before proceeding to verify that the factory image that you have just downloaded is in accordance with our claim, you will need to do the following from a network-connected computer.

Golang

The verification tool is written in Go. To build it, install Go 1.17 or later from the Go site.

ADB

Android Debug Bridge (adb) is a tool that is used to communicate with an Android device. You can download it via the Android SDK Platform Tools website.

wget or curl

wget and curl are tools that can be used to retrieve or download files from the Internet. Be sure to have either of these tools installed on your system, as the rest of this guide and the script we provide relies one of them being present.

Avbtool

avbtool is used to compute the VBMeta digest of the factory images and verification of binaries.

You can download it from the AOSP repository using the command below and find more details in the linked documentation.

computer:~$ git clone https://android.googlesource.com/platform/external/avb

After cloning that repository, avbtool, which is actually a symlink to avbtool.py, can be found within the cloned avb repo directory. Additionally, you should add this directory to your $PATH environment variable.

computer:~$ PATH=$PATH:DIRECTORY_CONTAINING_AVBTOOL

Inclusion Proof verifier

The inclusion proof verifier is a Go module which queries the Pixel Factory images transparency log and outputs whether an image is included in the log.

After cloning the AVB repository above, the source code of the verifier is found in tools/transparency/verify/

Fbpacktool

fbpacktool is a python script that is used to unpack individual components in Pixel's bootloader.img. This tool is important to allow avbtool to discover the required images for verification.

To make use of this tool, you should download the following three python scripts into the directory where your avbtool lives, make fbpacktool.py executable, and create a symlink named fbpacktool to fbpacktool.py for convenience.

If you have wget, you can issue the following command to download the three scripts:

computer:dir_to_avbtool$ wget https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/fbpacktool.py \
https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/fbpack.py \
https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/packedstruct.py
computer:dir_to_avbtool$ chmod +x fbpacktool.py
computer:dir_to_avbtool$ ln -s fbpacktool.py fbpacktool

If you have curl, you can issue the following commands to download the three scripts:

computer:dir_to_avbtool$ curl -O https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/fbpacktool.py
computer:dir_to_avbtool$ curl -O https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/fbpack.py
computer:dir_to_avbtool$ curl -O https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/packedstruct.py
computer:dir_to_avbtool$ chmod +x fbpacktool.py
computer:dir_to_avbtool$ ln -s fbpacktool.py fbpacktool

OpenSSL

You also need a tool named openssl to help perform various cryptographic operations via the terminal in the verification workflow. Make sure that it is installed on your system. You can do a check on your terminal by issuing the following command and make sure that the returned result is not empty. For example:

computer:~$ which openssl
/usr/bin/openssl

Construct the Payload for Verification

To verify the image you have on your device or a download from the Pixel Factory Image website, you must first construct the payload corresponding to that image from the Build Fingerprint and VBMeta Digest. There are two options, manual or automatic, as described below.

  • Build Fingerprint: A string identifier of the factory image
    • Example: google/oriole/oriole:12/SD1A.210817.015.A4/7697517:user/release-keys
  • VBMeta Digest: A SHA-256 cryptographic digest that ties the different pieces of the factory images together
    • Example: 4d5e41b7c675a821de81f7d2c744623fe808c8c2d3e19a83e894dab5d0829dbe

Manual Payload Construction

To manually generate the payload for verification you need to create a text file, payload.txt that contains the build fingerprint on one line and then the VBMeta digest on the next line, with an empty new line (please refer to the Log Content section for exact format).

Following either steps below (from Android Device or from Factory Image website), the content of the payload.txt file should look like this:

google/oriole/oriole:12/SD1A.210817.015.A4/7697517:user/release-keys
4d5e41b7c675a821de81f7d2c744623fe808c8c2d3e19a83e894dab5d0829dbe
Extracting the VBMeta Digest and Build Fingerprint from an Android Device

For the first time on Android, a user has the ability to access what their device thinks it is running and compare this information with a transparent log which represents what should be running. The following steps allow the user to get what the device thinks it is running. As we continue on our journey to bring more transparency capabilities to the platform, these trust guarantees will be strengthened over time.

To get the build fingerprint of the firmware your Pixel device is running, you can issue the following command from your terminal:

computer:~$ adb shell getprop ro.build.fingerprint
google/oriole/oriole:12/SD1A.210817.015.A4/7697517:user/release-keys

To extract the VBMeta digest from a supported Pixel device, you can issue the following command from your terminal:

computer:~$ adb shell getprop ro.boot.vbmeta.digest
4d5e41b7c675a821de81f7d2c744623fe808c8c2d3e19a83e894dab5d0829dbe
Extracting the VBMeta Digest and Build Fingerprint from Pixel Factory Image website

When you download a factory image from Pixel Factory Image website, you will need to unzip the downloaded file, which will create another folder in place. Navigate to the newly created folder, and unzip the associated image-*.zip file to obtain individual *.img files. These binaries are partition images. Make sure that a vbmeta.img file exists among the img files. You should see something like this:

computer:oriole-sd1a.210817.015.a4$ unzip image-oriole-sd1a.210817.015.a4.zip
Archive:  image-oriole-sd1a.210817.015.a4.zip
  inflating: android-info.txt
  inflating: boot.img
  inflating: vendor_boot.img
  inflating: system.img
  inflating: vendor.img
  inflating: product.img
  inflating: system_ext.img
  inflating: system_other.img
  inflating: dtbo.img
  inflating: vbmeta_system.img
  inflating: vbmeta.img
  inflating: super_empty.img

You can extract the build fingerprint and compute the VBMeta digest from all the downloaded files.

After avbtool has been made available to your $PATH, you can then issue the following commands from your terminal to extract the build fingerprint and compute the VBMeta digest of the factory image that you have just downloaded:

computer:dir_containing_unzipped_factory_image~$ grep -a 'ro\..*build\.fingerprint=google/.*/release-keys' system.img | cut -f2 -d'='
google/oriole/oriole:12/SD1A.210817.015.A4/7697517:user/release-keys
computer:dir_containing_unzipped_factory_image$ avbtool calculate_vbmeta_digest --image vbmeta.img
4d5e41b7c675a821de81f7d2c744623fe808c8c2d3e19a83e894dab5d0829dbe

Automatic Payload Construction

For your convenience, we have also provided a python script you can use. With this tool, rather than manually fetching and unzipping files, you only need to specify the URL to the desired factory image.

computer:dir_to_avb_repo/tools/transparency$ python3 ./pixel_factory_image_verify.py https://dl.google.com/dl/android/aosp/oriole-sd1a.210817.015.a4-factory-074b7f51.zip
Fetching file from: https://dl.google.com/dl/android/aosp/oriole-sd1a.210817.015.a4-factory-074b7f51.zip
Successfully downloaded file.
Successfully unpacked factory image.
Successfully unpacked factory image partitions.
Successfully unpacked bootloader image.
Successfully verified VBmeta.
Successfully extracted build fingerprint.
Successfully calculated VBMeta Digest.
The build fingerprint for factory image is: google/oriole/oriole:12/SD1A.210817.015.A4/7697517:user/release-keys
The VBMeta Digest for factory image is: 4d5e41b7c675a821de81f7d2c744623fe808c8c2d3e19a83e894dab5d0829dbe

A corresponding "payload.txt" file has been created.

Executing the command above will download the factory image bundle, unzip the bundle, and verify the signatures of the signed partitions. Upon successful verification, it will create a new file named payload.txt, and its content should look similar to below:

google/oriole/oriole:12/SD1A.210817.015.A4/7697517:user/release-keys
4d5e41b7c675a821de81f7d2c744623fe808c8c2d3e19a83e894dab5d0829dbe

After your payload.txt is successfully created you can start the [verification of image inclusion]{:#verifying-image-inclusion-inclusion-proof} steps.

Manual Verification of Downloaded Factory Image

In order to correctly verify the downloaded factory image, you will need to download fbpacktool.py and two other python files it depends on (fbpack.py and packedstruct.py) to unpack the components in bootloader.img, as they are individually described in the VBMeta struct. Once you have the unpacker, you can make use of the fbpacktool.py to unpack the bootloader image. For example:

computer:dir_containing_unzipped_images$ python3 fbpacktool.py unpack bootloader-oriole-slider-1.0-7683913.img

You should be able to see additional .img files appearing in that directory. Now, you can proceed to use avbtool to manually verify that the signatures of the various partitions match.

computer:dir_containing_unzipped_images$ avbtool verify_image --image vbmeta.img --follow_chain_partitions
Verifying image vbmeta.img using embedded public key
vbmeta: Successfully verified SHA256_RSA4096 vbmeta struct in vbmeta.img
vbmeta_system: Chained but ROLLBACK_SLOT (which is 1) and KEY (which has sha1 df529646b7225015196a714006346f0768b87fcf) not specified
--
Verifying image vbmeta_system.img using embedded public key
vbmeta: Successfully verified SHA256_RSA4096 vbmeta struct in vbmeta_system.img
product: Successfully verified sha256 hashtree of product.img for image of 2700468224 bytes
system: Successfully verified sha256 hashtree of system.img for image of 878493696 bytes
system_ext: Successfully verified sha256 hashtree of system_ext.img for image of 251334656 bytes
vbmeta_vendor: Chained but ROLLBACK_SLOT (which is 3) and KEY (which has sha1 85322346680a860c091fa14a64cef1fe4a3ffe31) not specified
--
Verifying image vbmeta_vendor.img using embedded public key
vbmeta: Successfully verified SHA256_RSA4096 vbmeta struct in vbmeta_vendor.img
vendor: Successfully verified sha256 hashtree of vendor.img for image of 472940544 bytes
boot: Chained but ROLLBACK_SLOT (which is 2) and KEY (which has sha1 85322346680a860c091fa14a64cef1fe4a3ffe31) not specified
--
Verifying image boot.img using embedded public key
vbmeta: Successfully verified footer and SHA256_RSA4096 vbmeta struct in boot.img
boot: Successfully verified sha256 hash of boot.img for image of 24481792 bytes
abl: Successfully verified sha256 hash of abl.img for image of 1744896 bytes
bl1: Successfully verified sha256 hash of bl1.img for image of 12288 bytes
bl2: Successfully verified sha256 hash of bl2.img for image of 544768 bytes
bl31: Successfully verified sha256 hash of bl31.img for image of 86016 bytes
dtbo: Successfully verified sha256 hash of dtbo.img for image of 2152234 bytes
gsa: Successfully verified sha256 hash of gsa.img for image of 262144 bytes
ldfw: Successfully verified sha256 hash of ldfw.img for image of 4096000 bytes
pbl: Successfully verified sha256 hash of pbl.img for image of 49152 bytes
tzsw: Successfully verified sha256 hash of tzsw.img for image of 4304896 bytes
vendor_boot: Successfully verified sha256 hash of vendor_boot.img for image of 25718784 bytes
vendor_dlkm: Successfully verified sha256 hashtree of vendor_dlkm.img for image of 54505472 bytes

If you do not see any error or failure messages, this means that the VBMeta of the downloaded image has now been successfully verified.

Verifying Image Inclusion (Inclusion Proof)

Once you've constructed your payload, set a shell variable (e.g. $PAYLOAD_PATH) to point to the path to the payload.

An inclusion proof tool has been published in the avb repository within the Android Open Source Project. To run it:

computer:external/avb/tools/transparency/verify$ PAYLOAD_PATH=../payload.txt
computer:external/avb/tools/transparency/verify$ go build cmd/verifier/verifier.go
computer:external/avb/tools/transparency/verify$ ./verifier --payload_path=${PAYLOAD_PATH}

The verifier uses the checkpoint and the log contents (found at the tile directory) to check that your image payload is in the transparency log, i.e. that it is published by Google.

The output of the command is written to stdout:

  • OK if the image is included in the log,
  • FAILURE if it isn't.

Log Checkpoint Verification

The log expresses its latest state in a log checkpoint file. This file is constructed like so:

  • The 1st line is the ecosystem, this is DEFAULT.
  • The 2nd line is the log size. It is a number indicating the number of images published.
  • The 3rd line is the tree Root Hash.
  • The 4th line is blank.
  • The 5th line is the signature of the first three lines in the Golang sumdb note format: — [keyname] [signature]. Note that the first character is an em dash (U+2014), the keyname is a human-readable representation of the signing ID, and the signature is a base64 encoded blob consisting of the first four bytes of the SHA256 hash of the public key and signature bytes. This follows the Golang sumdb note format.

The published log checkpoint will be cryptographically signed and you can verify the signature using this key. The certificate that contains the key used to sign the log checkpoint is below:

-----BEGIN CERTIFICATE-----
MIICPDCCAeOgAwIBAgIVAPooxISw/nFF/dPwmCUaV36Z4s3hMAoGCCqGSM49BAMCMHQxCzAJBgNV
BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYD
VQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAgFw0y
MTA3MTkyMjQxNDFaGA8yMDUxMDcxOTIyNDE0MVowdDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNh
bGlmb3JuaWExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC0dvb2dsZSBJbmMuMRAw
DgYDVQQLEwdBbmRyb2lkMRAwDgYDVQQDEwdBbmRyb2lkMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
QgAEU83uXNUiTYE53c2TfdWmqpW20bBXy4KEf5Ff8dV8GLKlVAXKHyjw3Lp9J3E0yCRJ/39XKeuA
AMF7KzSvhD248KNQME4wDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQUHRpvuNkzjtBY213BWgUyWWHm
3VYwHwYDVR0jBBgwFoAUHRpvuNkzjtBY213BWgUyWWHm3VYwCgYIKoZIzj0EAwIDRwAwRAIgZsZb
CNBXRkCKLS+LG/41VWj1cTszt9QCdJQNuy7aT94CIDPgn7v5b1ykBVUTuLgRSofxAzHg9R4dg1oA
7tTFAuDg
-----END CERTIFICATE-----

To perform manual verification, you should be able to extract the public key from the certificate above using the following openssl commands after pasting it into a new file (e.g. pixel_log_cert.crt):

computer:dir_containing_cert_file$ openssl x509 -pubkey -noout -in pixel_log_cert.crt > log_pub_key.pem && cat log_pub_key.pem

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEU83uXNUiTYE53c2TfdWmqpW20bBX
y4KEf5Ff8dV8GLKlVAXKHyjw3Lp9J3E0yCRJ/39XKeuAAMF7KzSvhD248A==
-----END PUBLIC KEY-----

You should now be able to use this public key to verify the signed content of the log checkpoint. Having the public key stored in a file called log_pub_key.pem (after running the command above), and the latest checkpoint.txt file downloaded, you should be able to follow the example below, again using openssl command to check that the checkpoint's signature indeed matches the public key you have:

computer:dir_containing_cert_file$ head -3 checkpoint.txt > checkpoint_head.txt
computer:dir_containing_cert_file$ tail -1 checkpoint.txt | awk '{print $NF}' > signature.b64
computer:dir_containing_cert_file$ base64 -d signature.b64 | tail -c +5 > signature.bin
computer:dir_containing_cert_file$ openssl dgst -sha256 -verify log_pub_key.pem -signature signature.bin checkpoint_head.txt
Verified OK

If you see a Verified OK result (like above), that means the signatures are correctly verified. The log has signed and committed to the corresponding checkpoint.