1139 lines
53 KiB
Markdown
1139 lines
53 KiB
Markdown
# Android Verified Boot 2.0
|
|
---
|
|
|
|
This repository contains tools and libraries for working with Android
|
|
Verified Boot 2.0. Usually AVB is used to refer to this codebase.
|
|
|
|
# Table of Contents
|
|
|
|
* [What is it?](#What-is-it)
|
|
+ [The VBMeta struct](#The-VBMeta-struct)
|
|
+ [Rollback Protection](#Rollback-Protection)
|
|
+ [A/B Support](#A_B-Support)
|
|
+ [The VBMeta Digest](#The-VBMeta-Digest)
|
|
* [Tools and Libraries](#Tools-and-Libraries)
|
|
+ [avbtool and libavb](#avbtool-and-libavb)
|
|
+ [Files and Directories](#Files-and-Directories)
|
|
+ [Portability](#Portability)
|
|
+ [Versioning and Compatibility](#Versioning-and-Compatibility)
|
|
+ [Adding New Features](#Adding-New-Features)
|
|
+ [Using avbtool](#Using-avbtool)
|
|
+ [Build System Integration](#Build-System-Integration)
|
|
* [Device Integration](#Device-Integration)
|
|
+ [System Dependencies](#System-Dependencies)
|
|
+ [Locked and Unlocked mode](#Locked-and-Unlocked-mode)
|
|
+ [Tamper-evident Storage](#Tamper_evident-Storage)
|
|
+ [Named Persistent Values](#Named-Persistent-Values)
|
|
+ [Persistent Digests](#Persistent-Digests)
|
|
+ [Updating Stored Rollback Indexes](#Updating-Stored-Rollback-Indexes)
|
|
+ [Recommended Bootflow](#Recommended-Bootflow)
|
|
+ [Booting Into Recovery](#Booting-Into-Recovery)
|
|
+ [Handling dm-verity Errors](#Handling-dm_verity-Errors)
|
|
+ [Android Specific Integration](#Android-Specific-Integration)
|
|
+ [Device Specific Notes](#Device-Specific-Notes)
|
|
* [Version History](#Version-History)
|
|
|
|
# What is it?
|
|
|
|
Verified boot is the process of assuring the end user of the integrity
|
|
of the software running on a device. It typically starts with a
|
|
read-only portion of the device firmware which loads code and executes
|
|
it only after cryptographically verifying that the code is authentic
|
|
and doesn't have any known security flaws. AVB is one implementation
|
|
of verified boot.
|
|
|
|
## The VBMeta struct
|
|
|
|
The central data structure used in AVB is the VBMeta struct. This data
|
|
structure contains a number of descriptors (and other metadata) and
|
|
all of this data is cryptographically signed. Descriptors are used for
|
|
image hashes, image hashtree metadata, and so-called *chained
|
|
partitions*. A simple example is the following:
|
|
|
|

|
|
|
|
where the `vbmeta` partition holds the hash for the `boot` partition
|
|
in a hash descriptor. For the `system` and `vendor` partitions a
|
|
hashtree follows the filesystem data and the `vbmeta` partition holds
|
|
the root hash, salt, and offset of the hashtree in hashtree
|
|
descriptors. Because the VBMeta struct in the `vbmeta` partition is
|
|
cryptographically signed, the boot loader can check the signature and
|
|
verify it was made by the owner of `key0` (by e.g. embedding the
|
|
public part of `key0`) and thereby trust the hashes used for `boot`,
|
|
`system`, and `vendor`.
|
|
|
|
A chained partition descriptor is used to delegate authority - it
|
|
contains the name of the partition where authority is delegated as
|
|
well as the public key that is trusted for signatures on this
|
|
particular partition. As an example, consider the following setup:
|
|
|
|

|
|
|
|
In this setup the `xyz` partition has a hashtree for
|
|
integrity-checking. Following the hashtree is a VBMeta struct which
|
|
contains the hashtree descriptor with hashtree metadata (root hash,
|
|
salt, offset, etc.) and this struct is signed with `key1`. Finally, at
|
|
the end of the partition is a footer which has the offset of the
|
|
VBMeta struct.
|
|
|
|
This setup allows the bootloader to use the chain partition descriptor
|
|
to find the footer at the end of the partition (using the name in the
|
|
chain partition descriptor) which in turns helps locate the VBMeta
|
|
struct and verify that it was signed by `key1` (using `key1_pub` stored in the
|
|
chain partition descriptor). Crucially, because there's a footer with
|
|
the offset, the `xyz` partition can be updated without the `vbmeta`
|
|
partition needing any changes.
|
|
|
|
The VBMeta struct is flexible enough to allow hash descriptors and hashtree
|
|
descriptors for any partition to live in the `vbmeta` partition, the partition
|
|
that they are used to integrity check (via a chain partition descriptor), or any
|
|
other partition (via a chain partition descriptor). This allows for a wide range
|
|
of organizational and trust relationships.
|
|
|
|
Chained partitions need not use a footer - it is permissible to have a chained
|
|
partition point to a partition where the VBMeta struct is at the beginning
|
|
(e.g. just like the `vbmeta` partition). This is useful for use-cases where all
|
|
hash- and hashtree-descriptors for the partitions owned by an entire
|
|
organization are stored in a dedicated partition, for example `vbmeta_google`.
|
|
In this example the hashtree descriptor for `system` is in the `vbmeta_google`
|
|
partition meaning that the bootloader doesn't need to access the `system`
|
|
partition at all which is helpful if the `system` partition is managed as a
|
|
logical partition (via e.g. [LVM
|
|
techniques](https://en.wikipedia.org/wiki/Logical_volume_management) or
|
|
similar).
|
|
|
|
## Rollback Protection
|
|
|
|
AVB includes Rollback Protection which is used to protect against
|
|
known security flaws. Each VBMeta struct has a *rollback index* baked
|
|
into it like the following:
|
|
|
|

|
|
|
|
These numbers are referred to as `rollback_index[n]` and are increased
|
|
for each image as security flaws are discovered and
|
|
fixed. Additionally the device stores the last seen rollback index in
|
|
tamper-evident storage:
|
|
|
|

|
|
|
|
and these are referred to as `stored_rollback_index[n]`.
|
|
|
|
Rollback protection is having the device reject an image unless
|
|
`rollback_index[n]` >= `stored_rollback_index[n]` for all `n`, and
|
|
having the device increase `stored_rollback_index[n]` over
|
|
time. Exactly how this is done is discussed in
|
|
the
|
|
[Updating Stored Rollback Indexes](#Updating-Stored-Rollback-Indexes)
|
|
section.
|
|
|
|
## A/B Support
|
|
|
|
AVB has been designed to work with A/B by requiring that the A/B
|
|
suffix is never used in any partition names stored in
|
|
descriptors. Here's an example with two slots:
|
|
|
|

|
|
|
|
Note how the rollback indexes differ between slots - for slot A the
|
|
rollback indexes are `[42, 101]` and for slot B they are `[43, 103]`.
|
|
|
|
In version 1.1 or later, avbtool supports `--do_not_use_ab` for
|
|
`add_hash_footer` and `add_hashtree_footer` operations. This makes it
|
|
possible to work with a partition that does not use A/B and should
|
|
never have the prefix. This corresponds to the
|
|
`AVB_HASH[TREE]_DESCRIPTOR_FLAGS_DO_NOT_USE_AB` flags.
|
|
|
|
## The VBMeta Digest
|
|
|
|
The VBMeta digest is a digest over all VBMeta structs including the root struct
|
|
(e.g. in the `vbmeta` partition) and all VBMeta structs in chained
|
|
partitions. This digest can be calculated at build time using `avbtool
|
|
calculate_vbmeta_digest` and also at runtime using the
|
|
`avb_slot_verify_data_calculate_vbmeta_digest()` function. It is also set on the
|
|
kernel command-line as `androidboot.vbmeta.digest`, see the `avb_slot_verify()`
|
|
documentation for exact details.
|
|
|
|
This digest can be used together with `libavb` in userspace inside the loaded
|
|
operating system to verify authenticity of the loaded vbmeta structs. This is
|
|
useful if the root-of-trust and/or stored rollback indexes are only available
|
|
while running in the boot loader.
|
|
|
|
Additionally, if the VBMeta digest is included in [hardware-backed attestation
|
|
data](https://developer.android.com/training/articles/security-key-attestation)
|
|
a relying party can extract the digest and compare it with list of digests for
|
|
known good operating systems which, if found, provides additional assurance
|
|
about the device the application is running on.
|
|
|
|
For [factory images of Pixel 3 and later
|
|
devices](https://developers.google.com/android/images), the
|
|
`pixel_factory_image_verify.py` located in `tools/transparency` is a convenience
|
|
tool for downloading, verifying and calcuating VBMeta Digests.
|
|
|
|
$ pixel_factory_image_verify.py https://dl.google.com/dl/android/aosp/image.zip
|
|
Fetching file from: https://dl.google.com/dl/android/aosp/image.zip
|
|
Successfully downloaded file.
|
|
Successfully unpacked factory image.
|
|
Successfully unpacked factory image partitions.
|
|
Successfully verified VBmeta.
|
|
Successfully calculated VBMeta Digest.
|
|
The VBMeta Digest for factory image is: 1f329b20a2dd69425e7a29566ca870dad51d2c579311992d41c9ba9ba05e170e
|
|
|
|
If the given argument is not an URL it considered to be a local file:
|
|
|
|
$ pixel_factory_image_verify.py image.zip
|
|
|
|
# Tools and Libraries
|
|
|
|
This section contains information about the tools and libraries
|
|
included in AVB.
|
|
|
|
## avbtool and libavb
|
|
|
|
The main job of `avbtool` is to create `vbmeta.img` which is the
|
|
top-level object for verified boot. This image is designed to go into
|
|
the `vbmeta` partition (or, if using A/B, the slot in question
|
|
e.g. `vbmeta_a` or `vbmeta_b`) and be of minimal size (for out-of-band
|
|
updates). The vbmeta image is cryptographically signed and contains
|
|
verification data (e.g. cryptographic digests) for verifying
|
|
`boot.img`, `system.img`, and other partitions/images.
|
|
|
|
The vbmeta image can also contain references to other partitions where
|
|
verification data is stored as well as a public key indicating who
|
|
should sign the verification data. This indirection provides
|
|
delegation, that is, it allows a 3rd party to control content on a
|
|
given partition by including their public key in `vbmeta.img`. By
|
|
design, this authority can be easily revoked by simply updating
|
|
`vbmeta.img` with new descriptors for the partition in question.
|
|
|
|
Storing signed verification data on other images - for example
|
|
`boot.img` and `system.img` - is also done with `avbtool`.
|
|
|
|
The minimum requirement for running `avbtool` is to either have
|
|
Python 3.5 installed or build the avbtool with the embedded launcher
|
|
using `m avbtool` and then run it out of the build artifact directory:
|
|
`out/soong/host/linux-x86/bin/avbtool`
|
|
|
|
In addition to `avbtool`, a library - `libavb` - is provided. This
|
|
library performs all verification on the device side e.g. it starts by
|
|
loading the `vbmeta` partition, checks the signature, and then goes on
|
|
to load the `boot` partition for verification. This library is
|
|
intended to be used in both boot loaders and inside Android. It has a
|
|
simple abstraction for system dependencies (see `avb_sysdeps.h`) as
|
|
well as operations that the boot loader or OS is expected to implement
|
|
(see `avb_ops.h`). The main entry point for verification is
|
|
`avb_slot_verify()`.
|
|
|
|
Android Things has specific requirements and validation logic for the
|
|
vbmeta public key. An extension is provided in `libavb_atx` which
|
|
performs this validation as an implementation of `libavb`'s public key
|
|
validation operation (see `avb_validate_vbmeta_public_key()` in
|
|
`avb_ops.h`).
|
|
|
|
## Files and Directories
|
|
|
|
* `libavb/`
|
|
+ An implementation of image verification. This code is designed
|
|
to be highly portable so it can be used in as many contexts as
|
|
possible. This code requires a C99-compliant C compiler. Part of
|
|
this code is considered internal to the implementation and
|
|
should not be used outside it. For example, this applies to the
|
|
`avb_rsa.[ch]` and `avb_sha.[ch]` files. System dependencies
|
|
expected to be provided by the platform is defined in
|
|
`avb_sysdeps.h`. If the platform provides the standard C runtime
|
|
`avb_sysdeps_posix.c` can be used.
|
|
* `libavb_atx/`
|
|
+ An Android Things Extension for validating public key metadata.
|
|
* `libavb_user/`
|
|
+ Contains an `AvbOps` implementation suitable for use in Android
|
|
userspace. This is used in `boot_control.avb` and `avbctl`.
|
|
* `libavb_ab/`
|
|
+ An experimental A/B implementation for use in boot loaders and
|
|
AVB examples. **NOTE**: This code is *DEPRECATED* and you must
|
|
define `AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED` to use
|
|
it. The code will be removed Jun 1 2018.
|
|
* `boot_control/`
|
|
+ An implementation of the Android `boot_control` HAL for use with
|
|
boot loaders using the experimental `libavb_ab` A/B stack.
|
|
**NOTE**: This code is *DEPRECATED* and will be removed Jun 1
|
|
2018.
|
|
* `Android.bp`
|
|
+ Build instructions for building `libavb` (a static library for use
|
|
on the device), host-side libraries (for unit tests), and unit
|
|
tests.
|
|
* `avbtool`
|
|
+ A tool written in Python for working with images related to
|
|
verified boot.
|
|
* `test/`
|
|
+ Unit tests for `abvtool`, `libavb`, `libavb_ab`, and
|
|
`libavb_atx`.
|
|
* `tools/avbctl/`
|
|
+ Contains the source-code for a tool that can be used to control
|
|
AVB at runtime in Android.
|
|
* `examples/uefi/`
|
|
+ Contains the source-code for a UEFI-based boot-loader utilizing
|
|
`libavb/` and `libavb_ab/`.
|
|
* `examples/things/`
|
|
+ Contains the source-code for a slot verification suitable for Android
|
|
Things.
|
|
* `README.md`
|
|
+ This file.
|
|
* `docs/`
|
|
+ Contains documentation files.
|
|
|
|
## Portability
|
|
|
|
The `libavb` code is intended to be used in bootloaders in devices
|
|
that will load Android or other operating systems. The suggested
|
|
approach is to copy the appropriate header and C files mentioned in
|
|
the previous section into the boot loader and integrate as
|
|
appropriate.
|
|
|
|
As the `libavb/` codebase will evolve over time integration should be
|
|
as non-invasive as possible. The intention is to keep the API of the
|
|
library stable however it will be broken if necessary. As for
|
|
portability, the library is intended to be highly portable, work on
|
|
both little- and big-endian architectures and 32- and 64-bit. It's
|
|
also intended to work in non-standard environments without the
|
|
standard C library and runtime.
|
|
|
|
If the `AVB_ENABLE_DEBUG` preprocessor symbol is set, the code will
|
|
include useful debug information and run-time checks. Production
|
|
builds should not use this. The preprocessor symbol `AVB_COMPILATION`
|
|
should be set only when compiling the libraries. The code must be
|
|
compiled into a separate library.
|
|
|
|
Applications using the compiled `libavb` library must only include the
|
|
`libavb/libavb.h` file (which will include all public interfaces) and
|
|
must not have the `AVB_COMPILATION` preprocessor symbol set. This is
|
|
to ensure that internal code that may be change in the future (for
|
|
example `avb_sha.[ch]` and `avb_rsa.[ch]`) will not be visible to
|
|
application code.
|
|
|
|
## Versioning and Compatibility
|
|
|
|
AVB uses a version number with three fields - the major, minor, and
|
|
sub version. Here's an example version number
|
|
|
|
1.4.3
|
|
^ ^ ^
|
|
| | |
|
|
the major version ---+ | |
|
|
the minor version -----+ |
|
|
the sub version -------+
|
|
|
|
The major version number is bumped only if compatibility is broken,
|
|
e.g. a struct field has been removed or changed. The minor version
|
|
number is bumped only if a new feature is introduced, for example a
|
|
new algorithm or descriptor has been added. The sub version number is
|
|
bumped when bugs are fixed or other changes not affecting
|
|
compatibility are made.
|
|
|
|
The `AvbVBMetaImageHeader` struct (as defined in the
|
|
`avb_vbmeta_image.h`) carries the major and minor version number of
|
|
`libavb` required to verify the struct in question. This is stored in
|
|
the `required_libavb_version_major` and
|
|
`required_libavb_version_minor` fields. Additionally this struct
|
|
contains a textual field with the version of `avbtool` used to create
|
|
the struct, for example "avbtool 1.4.3" or "avbtool 1.4.3 some_board
|
|
Git-4589fbec".
|
|
|
|
Note that it's entirely possible to have a `AvbVBMetaImageHeader`
|
|
struct with
|
|
|
|
required_libavb_version_major = 1
|
|
required_libavb_version_minor = 0
|
|
avbtool_release_string = "avbtool 1.4.3"
|
|
|
|
if, for example, creating an image that does not use any features
|
|
added after AVB version 1.0.
|
|
|
|
## Adding New Features
|
|
|
|
If adding a new feature for example a new algorithm or a new
|
|
descriptor then `AVB_VERSION_MINOR` in `avb_version.h` and `avbtool`
|
|
must be bumped and `AVB_VERSION_SUB` should be set to zero.
|
|
|
|
Unit tests **MUST** be added to check that
|
|
|
|
* The feature is used if - and only if - suitable commands/options are
|
|
passed to `avbtool`.
|
|
* The `required_version_minor` field is set to the bumped value if -
|
|
and only if - the feature is used. Also add tests to check that the
|
|
correct value is output when `--print_required_libavb_version` is
|
|
used.
|
|
|
|
If `AVB_VERSION_MINOR` has already been bumped since the last release
|
|
there is obviously no need to bump it again.
|
|
|
|
## Using avbtool
|
|
|
|
The content for the vbmeta partition can be generated as follows:
|
|
|
|
$ avbtool make_vbmeta_image \
|
|
[--output OUTPUT] \
|
|
[--algorithm ALGORITHM] [--key /path/to/key_used_for_signing_or_pub_key] \
|
|
[--public_key_metadata /path/to/pkmd.bin] \
|
|
[--rollback_index NUMBER] [--rollback_index_location NUMBER] \
|
|
[--include_descriptors_from_image /path/to/image.bin] \
|
|
[--setup_rootfs_from_kernel /path/to/image.bin] \
|
|
[--chain_partition part_name:rollback_index_location:/path/to/key1.bin] \
|
|
[--signing_helper /path/to/external/signer] \
|
|
[--signing_helper_with_files /path/to/external/signer_with_files] \
|
|
[--print_required_libavb_version] \
|
|
[--append_to_release_string STR]
|
|
|
|
An integrity footer containing the hash for an entire partition can be
|
|
added to an existing image as follows:
|
|
|
|
$ avbtool add_hash_footer \
|
|
--partition_name PARTNAME --partition_size SIZE \
|
|
[--image IMAGE] \
|
|
[--algorithm ALGORITHM] [--key /path/to/key_used_for_signing_or_pub_key] \
|
|
[--public_key_metadata /path/to/pkmd.bin] \
|
|
[--rollback_index NUMBER] [--rollback_index_location NUMBER] \
|
|
[--hash_algorithm HASH_ALG] [--salt HEX] \
|
|
[--include_descriptors_from_image /path/to/image.bin] \
|
|
[--setup_rootfs_from_kernel /path/to/image.bin] \
|
|
[--output_vbmeta_image OUTPUT_IMAGE] [--do_not_append_vbmeta_image] \
|
|
[--signing_helper /path/to/external/signer] \
|
|
[--signing_helper_with_files /path/to/external/signer_with_files] \
|
|
[--print_required_libavb_version] \
|
|
[--append_to_release_string STR] \
|
|
[--calc_max_image_size] \
|
|
[--do_not_use_ab] \
|
|
[--use_persistent_digest]
|
|
|
|
Valid values for `HASH_ALG` above include `sha1` and `sha256`.
|
|
|
|
An integrity footer containing the root digest and salt for a hashtree
|
|
for a partition can be added to an existing image as follows. The
|
|
hashtree is also appended to the image.
|
|
|
|
$ avbtool add_hashtree_footer \
|
|
--partition_name PARTNAME --partition_size SIZE \
|
|
[--image IMAGE] \
|
|
[--algorithm ALGORITHM] [--key /path/to/key_used_for_signing_or_pub_key] \
|
|
[--public_key_metadata /path/to/pkmd.bin] \
|
|
[--rollback_index NUMBER] [--rollback_index_location NUMBER] \
|
|
[--hash_algorithm HASH_ALG] [--salt HEX] [--block_size SIZE] \
|
|
[--include_descriptors_from_image /path/to/image.bin] \
|
|
[--setup_rootfs_from_kernel /path/to/image.bin] \
|
|
[--setup_as_rootfs_from_kernel] \
|
|
[--output_vbmeta_image OUTPUT_IMAGE] [--do_not_append_vbmeta_image] \
|
|
[--do_not_generate_fec] [--fec_num_roots FEC_NUM_ROOTS] \
|
|
[--signing_helper /path/to/external/signer] \
|
|
[--signing_helper_with_files /path/to/external/signer_with_files] \
|
|
[--print_required_libavb_version] \
|
|
[--append_to_release_string STR] \
|
|
[--calc_max_image_size] \
|
|
[--do_not_use_ab] \
|
|
[--no_hashtree] \
|
|
[--use_persistent_digest] \
|
|
[--check_at_most_once]
|
|
|
|
Valid values for `HASH_ALG` above include `sha1`, `sha256`, and `blake2b-256`.
|
|
|
|
The size of an image with integrity footers can be changed using the
|
|
`resize_image` command:
|
|
|
|
$ avbtool resize_image \
|
|
--image IMAGE \
|
|
--partition_size SIZE
|
|
|
|
The integrity footer on an image can be removed from an image. The
|
|
hashtree can optionally be kept in place.
|
|
|
|
$ avbtool erase_footer --image IMAGE [--keep_hashtree]
|
|
|
|
For hash- and hashtree-images the vbmeta struct can also be written to
|
|
an external file via the `--output_vbmeta_image` option and one can
|
|
also specify that the vbmeta struct and footer not be added to the
|
|
image being operated on.
|
|
|
|
The hashtree and FEC data in an image can be zeroed out with the following
|
|
command:
|
|
|
|
$ avbtool zero_hashtree --image IMAGE
|
|
|
|
This is useful for trading compressed image size for having to reculculate the
|
|
hashtree and FEC at runtime. If this is done the hashtree and FEC data is set
|
|
to zero except for the first eight bytes which are set to the magic
|
|
`ZeRoHaSH`. Either the hashtree or FEC data or both may be zeroed this way
|
|
so applications should check for the magic both places. Applications can
|
|
use the magic to detect if recalculation is needed.
|
|
|
|
To calculate the maximum size of an image that will fit in a partition
|
|
of a given size after having used the `avbtool add_hash_footer` or
|
|
`avbtool add_hashtree_footer` commands on it, use the
|
|
`--calc_max_image_size` option:
|
|
|
|
$ avbtool add_hash_footer --partition_size $((10*1024*1024)) \
|
|
--calc_max_image_size
|
|
10416128
|
|
|
|
$ avbtool add_hashtree_footer --partition_size $((10*1024*1024)) \
|
|
--calc_max_image_size
|
|
10330112
|
|
|
|
To calculate the required libavb version that would be put in the
|
|
vbmeta struct when using `make_vbmeta_image`, `add_hash_footer`, and
|
|
`add_hashtree_footer` commands use the
|
|
`--print_required_libavb_version` option:
|
|
|
|
$ avbtool make_vbmeta_image \
|
|
--algorithm SHA256_RSA2048 --key /path/to/key.pem \
|
|
--include_descriptors_from_image /path/to/boot.img \
|
|
--include_descriptors_from_image /path/to/system.img \
|
|
--print_required_libavb_version
|
|
1.0
|
|
|
|
Alternatively, `--no_hashtree` can be used with `avbtool add_hashtree_footer`
|
|
command. If `--no_hashtree` is given, the hashtree blob is omitted and only
|
|
its descriptor is added to the vbmeta struct. The descriptor says the size
|
|
of hashtree is 0, which tells an application the need to recalculate
|
|
hashtree.
|
|
|
|
The `--signing_helper` option can be used in `make_vbmeta_image`,
|
|
`add_hash_footer` and `add_hashtree_footer` commands to specify any
|
|
external program for signing hashes. The data to sign (including
|
|
padding e.g. PKCS1-v1.5) is fed via `STDIN` and the signed data is
|
|
returned via `STDOUT`. If `--signing_helper` is present in a command
|
|
line, the `--key` option need only contain a public key. Arguments for
|
|
a signing helper are `algorithm` and `public key`. If the signing
|
|
helper exits with a non-zero exit code, it means failure.
|
|
|
|
Here's an example invocation:
|
|
|
|
/path/to/my_signing_program SHA256_RSA2048 /path/to/publickey.pem
|
|
|
|
The `--signing_helper_with_files` is similar to `--signing_helper`
|
|
except that a temporary file is used to communicate with the helper
|
|
instead of `STDIN` and `STDOUT`. This is useful in situations where
|
|
the signing helper is using code which is outputting diagnostics on
|
|
`STDOUT` instead of `STDERR`. Here's an example invocation
|
|
|
|
/path/to/my_signing_program_with_files SHA256_RSA2048 \
|
|
/path/to/publickey.pem /tmp/path/to/communication_file
|
|
|
|
where the last positional argument is a file that contains the data to
|
|
sign. The helper should write the signature in this file.
|
|
|
|
The `append_vbmeta_image` command can be used to append an entire
|
|
vbmeta blob to the end of another image. This is useful for cases when
|
|
not using any vbmeta partitions, for example:
|
|
|
|
$ cp boot.img boot-with-vbmeta-appended.img
|
|
$ avbtool append_vbmeta_image \
|
|
--image boot-with-vbmeta-appended.img \
|
|
--partition_size SIZE_OF_BOOT_PARTITION \
|
|
--vbmeta_image vbmeta.img
|
|
$ fastboot flash boot boot-with-vbmeta-appended.img
|
|
|
|
Information about an image can be obtained using the `info_image` command. The
|
|
output of this command should not be relied on and the way information is
|
|
structured may change.
|
|
|
|
The `verify_image` command can be used to verify the contents of
|
|
several image files at the same time. When invoked on an image the
|
|
following checks are performed:
|
|
|
|
* If the image has a VBMeta struct the signature is checked against
|
|
the embedded public key. If the image doesn't look like `vbmeta.img`
|
|
then a footer is looked for and used if present.
|
|
|
|
* If the option `--key` is passed then a `.pem` file is expected and
|
|
it's checked that the embedded public key in said VBMeta struct
|
|
matches the given key.
|
|
|
|
* All descriptors in the VBMeta struct are checked in the following
|
|
way:
|
|
+ For a hash descriptor the image file corresponding to the
|
|
partition name is loaded and its digest is checked against that
|
|
in the descriptor.
|
|
+ For a hashtree descriptor the image file corresponding to the
|
|
partition name is loaded and the hashtree is calculated and its
|
|
root digest compared to that in the descriptor.
|
|
+ For a chained partition descriptor its contents is compared
|
|
against content that needs to be passed in via the
|
|
`--expected_chain_partition` options. The format for this option
|
|
is similar to that of the `--chain_partition` option. If there
|
|
is no `--expected_chain_partition` descriptor for the chain
|
|
partition descriptor the check fails.
|
|
|
|
Here's an example for a setup where the digests for `boot.img` and
|
|
`system.img` are stored in `vbmeta.img` which is signed with
|
|
`my_key.pem`. It also checks that the chain partition for partition
|
|
`foobar` uses rollback index 8 and that the public key in AVB format
|
|
matches that of the file `foobar_vendor_key.avbpubkey`:
|
|
|
|
$ avbtool verify_image \
|
|
--image /path/to/vbmeta.img \
|
|
--key my_key.pem \
|
|
--expect_chained_partition foobar:8:foobar_vendor_key.avbpubkey
|
|
|
|
Verifying image /path/to/vbmeta.img using key at my_key.pem
|
|
vbmeta: Successfully verified SHA256_RSA4096 vbmeta struct in /path_to/vbmeta.img
|
|
boot: Successfully verified sha256 hash of /path/to/boot.img for image of 10543104 bytes
|
|
system: Successfully verified sha1 hashtree of /path/to/system.img for image of 1065213952 bytes
|
|
foobar: Successfully verified chain partition descriptor matches expected data
|
|
|
|
In this example the `verify_image` command verifies the files
|
|
`vbmeta.img`, `boot.img`, and `system.img` in the directory
|
|
`/path/to`. The directory and file extension of the given image
|
|
(e.g. `/path/to/vbmeta.img`) is used together with the partition name
|
|
in the descriptor to calculate the filenames of the images holding
|
|
hash and hashtree images.
|
|
|
|
The `verify_image` command can also be used to check that a custom
|
|
signing helper works as intended.
|
|
|
|
The `calculate_vbmeta_digest` command can be used to calculate the vbmeta digest
|
|
of several image files at the same time. The result is printed as a hexadecimal
|
|
string either on `STDOUT` or a supplied path (using the `--output` option).
|
|
|
|
$ avbtool calculate_vbmeta_digest \
|
|
--hash_algorithm sha256 \
|
|
--image /path/to/vbmeta.img
|
|
a20fdd01a6638c55065fe08497186acde350d6797d59a55d70ffbcf41e95c2f5
|
|
|
|
In this example the `calculate_vbmeta_digest` command loads the `vbmeta.img`
|
|
file. If this image has one or more chain partition descriptors, the same logic
|
|
as the `verify_image` command is used to load files for these (e.g. it assumes
|
|
the same directory and file extension as the given image). Once all vbmeta
|
|
structs have been loaded, the digest is calculated (using the hash algorithm
|
|
given by the `--hash_algorithm` option) and printed out.
|
|
|
|
To print hash and hashtree digests embedded in the verified metadata, use the
|
|
`print_partition_digests` command like this:
|
|
|
|
$ avbtool print_partition_digests --image /path/to/vbmeta.img
|
|
system: ddaa513715fd2e22f3c1cea3c1a1f98ccb515fc6
|
|
boot: 5cba9a418e04b5f9e29ee6a250f6cdbe30c6cec867c59d388f141c3fedcb28c1
|
|
vendor: 06993a9e85e46e53d3892881bb75eff48ecadaa8
|
|
|
|
For partitions with hash descriptors, this prints out the digest and for
|
|
partitions with hashtree descriptors the root digest is printed out. Like the
|
|
`calculate_vbmeta_digest` and `verify_image` commands, chain partitions are
|
|
followed. To use JSON for the output, use the `--json` option.
|
|
|
|
In case you would like to log all command lines for all avbtool invocations for
|
|
debugging integrations with other tooling, you can configure the envirionment
|
|
variable AVB_INVOCATION_LOGFILE with the name of the log file:
|
|
|
|
$ export AVB_INVOCATION_LOGFILE='/tmp/avb_invocation.log'
|
|
$ ./avbtool version
|
|
$ ./avbtool version
|
|
$ cat /tmp/avb_invocation.log
|
|
./avbtool version
|
|
./avbtool version
|
|
|
|
|
|
## Build System Integration
|
|
|
|
In Android, AVB is enabled by the `BOARD_AVB_ENABLE` variable
|
|
|
|
BOARD_AVB_ENABLE := true
|
|
|
|
This will make the build system create `vbmeta.img` which will contain
|
|
a hash descriptor for `boot.img`, a hashtree descriptor for
|
|
`system.img`, a kernel-cmdline descriptor for setting up `dm-verity`
|
|
for `system.img` and append a hash-tree to `system.img`. If the build
|
|
system is set up such that one or many of `vendor.img` / `product.img`
|
|
/ `system_ext.img` / `odm.img` are being built, the hash-tree for each
|
|
of them will also be appended to the image respectively, and their
|
|
hash-tree descriptors will be included into `vbmeta.img` accordingly.
|
|
|
|
By default, the algorithm `SHA256_RSA4096` is used with a test key
|
|
from the `external/avb/test/data` directory. This can be overriden by
|
|
the `BOARD_AVB_ALGORITHM` and `BOARD_AVB_KEY_PATH` variables to use
|
|
e.g. a 4096-bit RSA key and SHA-512:
|
|
|
|
BOARD_AVB_ALGORITHM := SHA512_RSA4096
|
|
BOARD_AVB_KEY_PATH := /path/to/rsa_key_4096bits.pem
|
|
|
|
Remember that the public part of this key needs to be available to the
|
|
bootloader of the device expected to verify resulting images. Use
|
|
`avbtool extract_public_key` to extract the key in the expected format
|
|
(`AVB_pk` in the following). If the device is using a different root
|
|
of trust than `AVB_pk` the `--public_key_metadata` option can be used
|
|
to embed a blob (`AVB_pkmd` in the following) that can be used to
|
|
e.g. derive `AVB_pk`. Both `AVB_pk` and `AVB_pkmd` are passed to the
|
|
`validate_vbmeta_public_key()` operation when verifying a slot.
|
|
|
|
Some devices may support the end-user configuring the root of trust to use, see
|
|
the [Device Specific Notes](#Device-Specific-Notes) section for details.
|
|
|
|
Devices can be configured to create additional `vbmeta` partitions as
|
|
[chained partitions](#The-VBMeta-struct) in order to update a subset of
|
|
partitions without changing the top-level `vbmeta` partition. For example,
|
|
the following variables create `vbmeta_system.img` as a chained `vbmeta`
|
|
image that contains the hash-tree descriptors for `system.img`, `system_ext.img`
|
|
and `product.img`. `vbmeta_system.img` itself will be signed by the specified
|
|
key and algorithm.
|
|
|
|
BOARD_AVB_VBMETA_SYSTEM := system system_ext product
|
|
BOARD_AVB_VBMETA_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
|
|
BOARD_AVB_VBMETA_SYSTEM_ALGORITHM := SHA256_RSA2048
|
|
BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX_LOCATION := 1
|
|
|
|
Note that the hash-tree descriptors for `system.img`, `system_ext.img` and
|
|
`product.img` will be included only in `vbmeta_system.img`, but not
|
|
`vbmeta.img`. With the above setup, partitions `system.img`, `system_ext.img`,
|
|
`product.img` and `vbmeta_system.img` can be updated independently - but as a
|
|
group - of the rest of the partitions, *or* as part of the traditional updates
|
|
that update all the partitions.
|
|
|
|
Currently build system supports building chained `vbmeta` images of
|
|
`vbmeta_system.img` (`BOARD_AVB_VBMETA_SYSTEM`) and `vbmeta_vendor.img`
|
|
(`BOARD_AVB_VBMETA_VENDOR`).
|
|
|
|
To prevent rollback attacks, the rollback index should be increased on
|
|
a regular basis. The rollback index can be set with the
|
|
`BOARD_AVB_ROLLBACK_INDEX` variable:
|
|
|
|
BOARD_AVB_ROLLBACK_INDEX := 5
|
|
|
|
If this is not set, the rollback index defaults to 0.
|
|
|
|
The variable `BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS` can be used to specify
|
|
additional options passed to `avbtool make_vbmeta_image`. Typical
|
|
options to be used here include `--prop`, `--prop_from_file`,
|
|
`--chain_partition`, `--public_key_metadata`, and `--signing_helper`.
|
|
|
|
The variable `BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS` can be used to
|
|
specify additional options passed to `avbtool add_hash_footer` for
|
|
`boot.img`. Typical options to be used here include `--hash_algorithm`
|
|
and `--salt`.
|
|
|
|
The variable `BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS` can be used
|
|
to specify additional options passed to `avbtool add_hashtree_footer`
|
|
for `system.img`. Typical options to be used here include
|
|
`--hash_algorithm`, `--salt`, `--block_size`, and
|
|
`--do_not_generate_fec`.
|
|
|
|
The variable `BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS` can be used
|
|
to specify additional options passed to `avbtool add_hashtree_footer`
|
|
for `vendor.img`. Typical options to be used here include
|
|
`--hash_algorithm`, `--salt`, `--block_size`, and
|
|
`--do_not_generate_fec`.
|
|
|
|
The variable `BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS` can be used to
|
|
specify additional options passed to `avbtool add_hash_footer` for
|
|
`dtbo.img`. Typical options to be used here include `--hash_algorithm`
|
|
and `--salt`.
|
|
|
|
Build system variables (such as `PRODUCT_SUPPORTS_VERITY_FEC`) used
|
|
for previous version of Verified Boot in Android are not used in AVB.
|
|
|
|
A/B related build system variables can be found [here](https://source.android.com/devices/tech/ota/ab_updates#build-variables).
|
|
|
|
# Device Integration
|
|
|
|
This section discusses recommendations and best practices for
|
|
integrating `libavb` with a device boot loader. It's important to
|
|
emphasize that these are just recommendations so the use of the word
|
|
`must` should be taken lightly.
|
|
|
|
Additionally term *HLOS* is used in this chapter to refer to the *High
|
|
Level Operating System*. This obviously includes Android (including
|
|
other form-factors than phones) but could also be other operating
|
|
systems.
|
|
|
|
## System Dependencies
|
|
|
|
The `libavb` library is written in a way so it's portable to any
|
|
system with a C99 compiler. It does not require the standard C library
|
|
however the boot loader must implement a simple set of system
|
|
primitives required by `libavb` such as `avb_malloc()`, `avb_free()`,
|
|
and `avb_print()`.
|
|
|
|
In addition to the system primitives, `libavb` interfaces with the boot
|
|
loader through the supplied `AvbOps` struct. This includes operations
|
|
to read and write data from partitions, read and write rollback
|
|
indexes, check if the public key used to make a signature should be
|
|
accepted, and so on.
|
|
|
|
## Locked and Unlocked mode
|
|
|
|
AVB has been designed to support the notion of the device being either
|
|
LOCKED state or UNLOCKED state as used in Android.
|
|
|
|
In the context of AVB, the LOCKED state means that verification errors
|
|
are fatal whereas in UNLOCKED state they are not. If the device is
|
|
UNLOCKED pass `AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR` flag in
|
|
the `flags` parameter of `avb_slot_verify()` and treat verification
|
|
errors including
|
|
|
|
* `AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED`
|
|
* `AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION`
|
|
* `AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX`
|
|
|
|
as non-fatal. If the device is in the LOCKED state, don't pass the
|
|
`AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR` flag in the `flags`
|
|
parameter of `avb_slot_verify()` and only treat
|
|
`AVB_SLOT_VERIFY_RESULT_OK` as non-fatal.
|
|
|
|
On Android, device state may be altered through the fastboot interface
|
|
using, e.g. `fastboot flashing lock` (to transition to the LOCKED
|
|
state) and `fastboot flashing unlock` (to transition to the UNLOCKED
|
|
state).
|
|
|
|
The device must only allow state transitions (e.g. from LOCKED to
|
|
UNLOCKED or UNLOCKED to LOCKED) after asserting physical presence of
|
|
the user. If the device has a display and buttons this is typically
|
|
done by showing a dialog and requiring the user to confirm or cancel
|
|
using physical buttons.
|
|
|
|
All user data must be cleared when transitioning from the LOCKED to
|
|
the UNLOCKED state (including the `userdata` partition and any NVRAM
|
|
spaces). Additionally all `stored_rollback_index[n]` locations must be
|
|
cleared (all elements must be set to zero). Similar action (erasing
|
|
`userdata`, NVRAM spaces, and `stored_rollback_index[n]` locations)
|
|
shall also happening when transitioning from UNLOCKED to LOCKED. If
|
|
the device is required to use full disk encryption, then a less
|
|
intensive wipe is required for UNLOCKED to LOCKED. Depending on the
|
|
device form-factor and intended use, the user should be prompted to
|
|
confirm before any data is erased.
|
|
|
|
## Tamper-evident Storage
|
|
|
|
In this document, *tamper-evident* means that it's possible to detect
|
|
if the HLOS has tampered with the data, e.g. if it has been
|
|
overwritten.
|
|
|
|
Tamper-evident storage must be used for stored rollback indexes, keys
|
|
used for verification, device state (whether the device is LOCKED or
|
|
UNLOCKED), and named persistent values. If tampering has been detected
|
|
the corresponding `AvbOps` operation should fail by e.g. returning
|
|
`AVB_IO_RESULT_ERROR_IO`. It is especially important that verification
|
|
keys cannot be tampered with since they represent the root-of-trust.
|
|
|
|
If verification keys are mutable they must only be set by the end
|
|
user, e.g. it must never be set at the factory or store or any
|
|
intermediate point before the end user. Additionally, it must only be
|
|
possible to set or clear a key while the device is in the UNLOCKED
|
|
state.
|
|
|
|
## Named Persistent Values
|
|
|
|
AVB 1.1 introduces support for named persistent values which must be
|
|
tamper evident and allows AVB to store arbitrary key-value pairs.
|
|
Integrators may limit support for these values to a set of fixed
|
|
well-known names, a maximum value size, and / or a maximum number of
|
|
values.
|
|
|
|
## Persistent Digests
|
|
|
|
Using a persistent digest for a partition means the digest (or root
|
|
digest in the case of a hashtree) is not stored in the descriptor but
|
|
is stored in a named persistent value. This allows configuration data
|
|
which may differ from device to device to be verified by AVB. It must
|
|
not be possible to modify the persistent digest when the device is in
|
|
the LOCKED state, except if a digest does not exist it may be initialized.
|
|
|
|
To specify that a descriptor should use a persistent digest, use the
|
|
`--use_persistent_digest` option for the `add_hash_footer` or
|
|
`add_hashtree_footer` avbtool operations. Then, during verification of
|
|
the descriptor, AVB will look for the digest in the named persistent
|
|
value `avb.persistent_digest.$(partition_name)` instead of in the
|
|
descriptor itself.
|
|
|
|
For hashtree descriptors using a persistent digest, the digest value
|
|
will be available for substitution into kernel command line descriptors
|
|
using a token of the form `$(AVB_FOO_ROOT_DIGEST)` where 'FOO' is the
|
|
uppercase partition name, in this case for the partition named 'foo'.
|
|
The token will be replaced by the digest in hexadecimal form.
|
|
|
|
By default, when the `--use_persistent_digest` option is used with
|
|
`add_hash_footer` or `add_hashtree_footer`, avbtool will generate a
|
|
descriptor with no salt rather than the typical default of generating a
|
|
random salt equal to the digest length. This is because the digest
|
|
value is stored in persistent storage and thus cannot change over time.
|
|
An alternative option would be to manually provide a random salt using
|
|
`--salt`, but this salt would need to remain unchanged for the life
|
|
of the device once the persistent digest value was written.
|
|
|
|
## Updating Stored Rollback Indexes
|
|
|
|
In order for Rollback Protection to work the bootloader will need to
|
|
update the `stored_rollback_indexes[n]` array on the device prior to
|
|
transferring control to the HLOS. If not using A/B this is
|
|
straightforward - just update it to what's in the AVB metadata for the
|
|
slot before booting. In pseudo-code it would look like this:
|
|
|
|
```c++
|
|
// The |slot_data| parameter should be the AvbSlotVerifyData returned
|
|
// by avb_slot_verify() for the slot we're about to boot.
|
|
//
|
|
bool update_stored_rollback_indexes_for_slot(AvbOps* ops,
|
|
AvbSlotVerifyData* slot_data) {
|
|
for (int n = 0; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; n++) {
|
|
uint64_t rollback_index = slot_data->rollback_indexes[n];
|
|
if (rollback_index > 0) {
|
|
AvbIOResult io_ret;
|
|
uint64_t current_stored_rollback_index;
|
|
|
|
io_ret = ops->read_rollback_index(ops, n, ¤t_stored_rollback_index);
|
|
if (io_ret != AVB_IO_RESULT_OK) {
|
|
return false;
|
|
}
|
|
|
|
if (rollback_index > current_stored_rollback_index) {
|
|
io_ret = ops->write_rollback_index(ops, n, rollback_index);
|
|
if (io_ret != AVB_IO_RESULT_OK) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
```
|
|
|
|
However if using A/B more care must be taken to still allow the device
|
|
to fall back to the old slot if the update didn't work.
|
|
|
|
For an HLOS like Android where rollback is only supported if the
|
|
updated OS version is found to not work, `stored_rollback_index[n]`
|
|
should only be updated from slots that are marked as SUCCESSFUL in the
|
|
A/B metadata. The pseudo-code for that is as follows where
|
|
`is_slot_is_marked_as_successful()` comes from the A/B stack in use:
|
|
|
|
```c++
|
|
if (is_slot_is_marked_as_successful(slot->ab_suffix)) {
|
|
if (!update_stored_rollback_indexes_for_slot(ops, slot)) {
|
|
// TODO: handle error.
|
|
}
|
|
}
|
|
```
|
|
|
|
This logic should ideally be implemented outside of the HLOS. One
|
|
possible implementation is to update rollback indices in the
|
|
bootloader when booting into a successful slot. This means that
|
|
when booting into a new OS not yet marked as successful, the
|
|
rollback indices would not be updated. The first reboot after the
|
|
slot succeeded would trigger an update of the rollback indices.
|
|
|
|
For an HLOS where it's possible to roll back to a previous version,
|
|
`stored_rollback_index[n]` should be set to the largest possible value
|
|
allowing all bootable slots to boot. This approach is implemented in
|
|
AVB's experimental (and now deprecated) A/B stack `libavb_ab`, see the
|
|
`avb_ab_flow()` implementation. Note that this requires verifying
|
|
*all* bootable slots at every boot and this may impact boot time.
|
|
|
|
## Recommended Bootflow
|
|
|
|
The recommended boot flow for a device using AVB is as follows:
|
|
|
|

|
|
|
|
Notes:
|
|
|
|
* The device is expected to search through all A/B slots until it
|
|
finds a valid OS to boot. Slots that are rejected in the LOCKED
|
|
state might not be rejected in the UNLOCKED state, (e.g. when
|
|
UNLOCKED any key can be used and rollback index failures are
|
|
allowed), so the algorithm used for selecting a slot varies
|
|
depending on what state the device is in.
|
|
|
|
* If no valid OS (that is, no bootable A/B slot) can be found, the
|
|
device cannot boot and has to enter repair mode. It is
|
|
device-dependent what this looks like. If the device has a screen
|
|
it must convey this state to the user.
|
|
|
|
* If the device is LOCKED, only an OS signed by an embedded
|
|
verification key (see the previous section) shall be
|
|
accepted. Additionally, `rollback_index[n]` as stored in the
|
|
verified image must be greater or equal than what's in
|
|
`stored_rollback_index[n]` on the device (for all `n`) and the
|
|
`stored_rollback_index[n]` array is expected to be updated as
|
|
specified in the previous section.
|
|
+ If the key used for verification was set by the end user, and
|
|
the device has a screen, it must show a warning with the key
|
|
fingerprint to convey that the device is booting a custom
|
|
OS. The warning must be shown for at least 10 seconds before the
|
|
boot process continues. If the device does not have a screen,
|
|
other ways must be used to convey that the device is booting a
|
|
custom OS (lightbars, LEDs, etc.).
|
|
|
|
* If the device is UNLOCKED, there is no requirement to check the key
|
|
used to sign the OS nor is there any requirement to check or update
|
|
rollback `stored_rollback_index[n]` on the device. Because of this
|
|
the user must always be shown a warning about verification not
|
|
occurring.
|
|
+ It is device-dependent how this is implemented since it depends
|
|
on the device form-factor and intended usage. If the device has
|
|
a screen and buttons (for example if it's a phone) the warning
|
|
is to be shown for at least 10 seconds before the boot process
|
|
continues. If the device does not have a screen, other ways must
|
|
be used to convey that the device is UNLOCKED (lightbars, LEDs,
|
|
etc.).
|
|
|
|
### Booting Into Recovery
|
|
|
|
On Android devices not using A/B, the `recovery` partition usually isn't
|
|
updated along with other partitions and therefore can't be referenced
|
|
from the main `vbmeta` partition.
|
|
|
|
It's still possible to use AVB to protect this partition (and others)
|
|
by signing these partitions and passing the
|
|
`AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION` flag to `avb_slot_verify()`.
|
|
In this mode, the key used to sign each requested partition is verified
|
|
by the `validate_public_key_for_partition()` operation which is also
|
|
used to return the rollback index location to be used.
|
|
|
|
## Handling dm-verity Errors
|
|
|
|
By design, hashtree verification errors are detected by the HLOS and
|
|
not the bootloader. AVB provides a way to specify how the error should
|
|
be handled through the `hashtree_error_mode` parameter in the
|
|
`avb_slot_verify()` function. Possible values include
|
|
|
|
* `AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE` means that the HLOS
|
|
will invalidate the current slot and restart. On devices with A/B
|
|
this would lead to attempting to boot the other slot (if it's marked
|
|
as bootable) or it could lead to a mode where no OS can be booted
|
|
(e.g. some form of repair mode). In Linux this requires a kernel
|
|
built with `CONFIG_DM_VERITY_AVB`.
|
|
|
|
* `AVB_HASHTREE_ERROR_MODE_RESTART` means that the OS will restart
|
|
without the current slot being invalidated. Be careful using this
|
|
mode unconditionally as it may introduce boot loops if the same
|
|
hashtree verification error is hit on every boot.
|
|
|
|
* `AVB_HASHTREE_ERROR_MODE_EIO` means that an `EIO` error will be
|
|
returned to the application.
|
|
|
|
* `AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO` means that either the **RESTART**
|
|
or **EIO** mode is used, depending on state. This mode implements a state
|
|
machine whereby **RESTART** is used by default and when the
|
|
`AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION` is passed to
|
|
`avb_slot_verify()` the mode transitions to **EIO**. When a new OS has been
|
|
detected the device transitions back to the **RESTART** mode.
|
|
+ To do this persistent storage is needed - specifically this means that the
|
|
passed in `AvbOps` will need to have the `read_persistent_value()` and
|
|
`write_persistent_value()` operations implemented. The name of the
|
|
persistent value used is **avb.managed_verity_mode** and 32 bytes of storage
|
|
is needed.
|
|
|
|
* `AVB_HASHTREE_ERROR_MODE_LOGGING` means that errors will be logged
|
|
and corrupt data may be returned to applications. This mode should
|
|
be used for **ONLY** diagnostics and debugging. It cannot be used
|
|
unless verification errors are allowed.
|
|
|
|
* `AVB_HASHTREE_ERROR_MODE_PANIC` means that the OS will **panic** without
|
|
the current slot being invalidated. Be careful using this mode as it may
|
|
introduce boot panic if the same hashtree verification error is hit on
|
|
every boot. This mode is available since: 1.7.0 (kernel 5.9)
|
|
|
|
The value passed in `hashtree_error_mode` is essentially just passed on through
|
|
to the HLOS through the the `androidboot.veritymode`,
|
|
`androidboot.veritymode.managed`, and `androidboot.vbmeta.invalidate_on_error`
|
|
kernel command-line parameters in the following way:
|
|
|
|
| | `androidboot.veritymode` | `androidboot.veritymode.managed` | `androidboot.vbmeta.invalidate_on_error` |
|
|
|------|:------------------------:|:--------------------------------:|:----------------------------------------:|
|
|
| `AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE` | **enforcing** | (unset) | **yes** |
|
|
| `AVB_HASHTREE_ERROR_MODE_RESTART` | **enforcing** | (unset) | (unset) |
|
|
| `AVB_HASHTREE_ERROR_MODE_EIO` | **eio** | (unset) | (unset) |
|
|
| `AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO` | **eio** or **enforcing** | **yes** | (unset) |
|
|
| `AVB_HASHTREE_ERROR_MODE_LOGGING` | **ignore_corruption** | (unset) | (unset) |
|
|
| `AVB_HASHTREE_ERROR_MODE_PANIC` | **panicking** | (unset) | (unset) |
|
|
|
|
The only exception to this table is that if the
|
|
`AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED` flag is set in the top-level vbmeta,
|
|
then `androidboot.veritymode` is set to **disabled** and
|
|
`androidboot.veritymode.managed` and `androidboot.vbmeta.invalidate_on_error`
|
|
are unset.
|
|
|
|
The different values of `hashtree_error_mode` parameter in the `avb_slot_verify()`
|
|
function can be categorized into three groups:
|
|
|
|
* `AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE`, which needs `CONFIG_DM_VERITY_AVB`
|
|
in the kernel config for the kernel to invalidate the current slot and
|
|
restart. This is kept here for legacy Android Things devices and is not
|
|
recommended for other device form factors.
|
|
|
|
* The bootloader handles the switch between `AVB_HASHTREE_ERROR_MODE_RESTART`
|
|
and `AVB_HASHTREE_ERROR_MODE_EIO`. This would need a persistent storage on the
|
|
device to store the vbmeta digest, so the bootloader can detect if a device
|
|
ever gets an update or not. Once the new OS is installed and if the device is
|
|
in **EIO** mode, the bootloader should switch back to **RESTART** mode.
|
|
|
|
* `AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO`: `libavb` helps the
|
|
bootloader manage **EIO**/**RESTART** state transition. The bootloader needs
|
|
to implement the callbacks of `AvbOps->read_persistent_value()` and
|
|
`AvbOps->write_persistent_value()` for `libavb` to store the vbmeta digest to
|
|
detect whether a new OS is installed.
|
|
|
|
### Which mode should I use for my device?
|
|
|
|
This depends entirely on the device, how the device is intended to be
|
|
used, and the desired user experience.
|
|
|
|
For Android devices the `AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO` mode
|
|
should be used. Also see the [Boot Flow section on source.android.com](https://source.android.com/security/verifiedboot/boot-flow) for the kind of UX and UI the boot loader should implement.
|
|
|
|
If the device doesn't have a screen or if the HLOS supports multiple bootable
|
|
slots simultaneously it may make more sense to just use
|
|
`AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE`.
|
|
|
|
## Android Specific Integration
|
|
|
|
On Android, the boot loader must set the
|
|
`androidboot.verifiedbootstate` parameter on the kernel command-line
|
|
to indicate the boot state. It shall use the following values:
|
|
|
|
* **green**: If in LOCKED state and the key used for verification was not set by the end user.
|
|
* **yellow**: If in LOCKED state and the key used for verification was set by the end user.
|
|
* **orange**: If in the UNLOCKED state.
|
|
|
|
## Device Specific Notes
|
|
|
|
This section contains information about how AVB is integrated into specific
|
|
devices. This is not an exhaustive list.
|
|
|
|
### Pixel 2 and later
|
|
|
|
On the Pixel 2, Pixel 2 XL and later Pixel models, the boot loader supports a
|
|
virtual partition with the name `avb_custom_key`. Flashing and erasing this
|
|
partition only works in the UNLOCKED state. Setting the custom key is done like
|
|
this:
|
|
|
|
avbtool extract_public_key --key key.pem --output pkmd.bin
|
|
fastboot flash avb_custom_key pkmd.bin
|
|
|
|
Erasing the key is done by erasing the virtual partition:
|
|
|
|
fastboot erase avb_custom_key
|
|
|
|
When the custom key is set and the device is in the LOCKED state it will boot
|
|
images signed with both the built-in key as well as the custom key. All other
|
|
security features (including rollback-protection) are in effect, e.g. the
|
|
**only** difference is the root of trust to use.
|
|
|
|
When booting an image signed with a custom key, a yellow screen will be shown as
|
|
part of the boot process to remind the user that the custom key is in use.
|
|
|
|
# Version History
|
|
|
|
### Version 1.2
|
|
|
|
Version 1.2 adds support for the following:
|
|
* `rollback_index_location` field of the main vbmeta header.
|
|
* `check_at_most_once` parameter of dm-verity in a hashtree descriptor.
|
|
|
|
### Version 1.1
|
|
|
|
Version 1.1 adds support for the following:
|
|
|
|
* A 32-bit `flags` element is added to hash and hashtree descriptors.
|
|
* Support for partitions which don't use [A/B](#A_B-Support).
|
|
* Tamper-evident [named persistent values](#Named-Persistent-Values).
|
|
* [Persistent digests](#Persistent-Digests) for hash or hashtree descriptors.
|
|
|
|
### Version 1.0
|
|
|
|
All features not explicitly listed under a later version are supported by 1.0.
|