blob: 5340412fa6d4b2b5a46cb798e8456459cdda74e7 [file]
<html devsite>
<head>
<title>Verifying Boot</title>
<meta name="project_path" value="/_project.yaml" />
<meta name="book_path" value="/_book.yaml" />
</head>
<body>
<!--
Copyright 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
//www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<p>
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 <code>boot</code> partition), the device tree (loaded
from the <code>dtbo</code> partition), <code>system</code> partition,
<code>vendor</code> partition, and so on.
</p>
<p>
Small partitions, such as <code>boot</code> and <code>dtbo</code>, 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
<em>expected hash value</em>. If the value doesn't match, Android won't load.
For more details, see <a href="/security/verifiedboot/boot-flow">Boot Flow</a>.
</p>
<p>
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 <em>expected root hash value</em>.
Android includes the <a href="/security/verifiedboot/dm-verity">dm-verity
driver</a> to verify larger partitions. If at some point the calculated root
hash doesn't match the <em>expected root hash value</em>, the data is not used
and Android enters an error state. For more details, see
<a href="/security/verifiedboot/boot-flow#dm-verity-corruption">dm-verity
corruption</a>.
</p>
<p>
The <em>expected hashes</em> 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
<a href="/security/verifiedboot/avb">Android Verified Boot</a> for details.
</p>
<h2 id="rollback-protection">Rollback protection</h2>
<p>
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.
</p>
<p>
The protection against this class of attacks is called <em>Rollback
Protection</em>. 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.
</p>
<p>
For more details on how AVB handles rollback protections, see the AVB
<a href="https://android.googlesource.com/platform/external/avb/+/master/README.md#Rollback-Protection"
class="external">README</a>.
</p>
<h2 id="handling-verification-errors">Handling verification errors</h2>
<p>
Verification can fail either at boot time (such as, if the calculated hash on
<code>boot</code> partition doesn't match the expected hash) or at run time
(such as, if dm-verity encounters a verification error on the
<code>system</code> partition). If verification fails at boot time, the device
cannot boot and the end user needs to go through steps to recover the device.
</p>
<p>
If verification fails at run-time the flow is a bit more complicated. If the
device uses dm-verity, it should be configured in <code>restart</code> mode. In
<code>restart</code> 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
(<code>eio</code>) mode and stay in this mode until a new update has been
installed.
</p>
<p>
When booting in <code>eio</code> mode, the device shows an error screen
informing the user that corruption has been detected and the device may not
function correctly. The screen shows until the user dismisses it. In
<code>eio</code> mode the dm-verity driver will not restart the device if a
verification error is encountered, instead an EIO error is returned and the
application needs to deal with the error.
</p>
<p>
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 <code>restart</code> mode.
</p>
</body>
</html>