Verified boot requires cryptographically verifying all executable code and data
that is part of the Android version being booted before it is used. This includes
the kernel (loaded from the boot
partition), the device tree (loaded
from the dtbo
partition), system
partition,
vendor
partition, and so on.
Small partitions, such as boot
and dtbo
, that are read
only once are typically verified by loading the entire contents into memory and
then calculating its hash. This calculated hash value is then compared to the
expected hash value. If the value doesn't match, Android won't load.
For more details, see Boot Flow.
Larger partitions that won't fit into memory (such as, file systems) may use a hash tree where verification is a continuous process happening as data is loaded into memory. In this case, the root hash of the hash tree is calculated during run time and is checked against the expected root hash value. Android includes the dm-verity driver to verify larger partitions. If at some point the calculated root hash doesn't match the expected root hash value, the data isn't used and Android enters an error state. For more details, see dm-verity corruption.
The expected hashes are typically stored at either the end or beginning of each verified partition, in a dedicated partition, or both. Crucially, these hashes are signed (either directly or indirectly) by the root of trust. As an example, the AVB implementation supports both approaches, see Android Verified Boot for details.
Rollback protection
Even with a completely secure update process, it's possible for a non-persistent Android kernel exploit to manually install an older, more vulnerable version of Android, reboot into the vulnerable version, and then use that Android version to install a persistent exploit. From there the attacker permanently owns the device and can do anything, including disabling updates.
The protection against this class of attacks is called Rollback Protection. Rollback protection is typically implemented by using tamper-evident storage to record the most recent version of the Android and refusing to boot Android if it's lower than the recorded version. Versions are typically tracked on a per-partition basis.
For more details on how AVB handles rollback protections, see the AVB README.
Handle verification errors
Verification can fail either at boot time (such as, if the calculated hash on
boot
partition doesn't match the expected hash) or at run time
(such as, if dm-verity encounters a verification error on the
system
partition). If verification fails at boot time, the device
can't boot and the end user needs to go through steps to recover the device.
If verification fails at run-time the flow is a bit more complicated. If the
device uses dm-verity, it should be configured in restart
mode. In
restart
mode, if a verification error is encountered, the device is
immediately restarted with a specific flag set to indicate the reason. The boot
loader should notice this flag and switch dm-verity over to use I/O Error
(eio
) mode and stay in this mode until a new update has been
installed.
When booting in eio
mode, the device shows an error screen
informing the user that corruption has been detected and the device might not
function correctly. The screen shows until the user dismisses it. In
eio
mode the dm-verity driver won't restart the device if a
verification error is encountered, instead an EIO error is returned and the
app needs to deal with the error.
The intent is that either the system updater will run (so a new OS without
corruption errors can be installed) or the user can get as much of their data
off the device as possible. Once the new OS has been installed, the boot loader
notices the newly installed OS and switches back to restart
mode.