248 lines
9.6 KiB
HTML
248 lines
9.6 KiB
HTML
<html devsite>
|
|
<head>
|
|
<title>Implementing dm-verity</title>
|
|
<meta name="project_path" value="/_project.yaml" />
|
|
<meta name="book_path" value="/_book.yaml" />
|
|
</head>
|
|
<body>
|
|
<!--
|
|
Copyright 2017 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
|
|
|
|
http://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.
|
|
-->
|
|
|
|
|
|
|
|
<h2 id="operation">Operation</h2>
|
|
|
|
<p>dm-verity protection lives in the kernel. So if rooting software compromises the
|
|
system before the kernel comes up, it will retain that access. To mitigate this
|
|
risk, most manufacturers verify the kernel using a key burned into the device.
|
|
That key is not changeable once the device leaves the factory.</p>
|
|
|
|
<p>Manufacturers use that key to verify the signature on the first-level
|
|
bootloader, which in turn verifies the signature on subsequent levels, the
|
|
application bootloader and eventually the kernel. Each manufacturer wishing to
|
|
take advantage of <a href="verified-boot.html">verified boot</a> should have a
|
|
method for verifying the integrity of the kernel. Assuming the kernel has been
|
|
verified, the kernel can look at a block device and verify it as it is mounted.</p>
|
|
|
|
<p>One way of verifying a block device is to directly hash its contents and compare
|
|
them to a stored value. However, attempting to verify an entire block device can
|
|
take an extended period and consume much of a device's power. Devices would take
|
|
long periods to boot and then be significantly drained prior to use.</p>
|
|
|
|
<p>Instead, dm-verity verifies blocks individually and only when each one is
|
|
accessed. When read into memory, the block is hashed in parallel. The hash is
|
|
then verified up the tree. And since reading the block is such an expensive
|
|
operation, the latency introduced by this block-level verification is
|
|
comparatively nominal.</p>
|
|
|
|
<p>If verification fails, the device generates an I/O error indicating the block
|
|
cannot be read. It will appear as if the filesystem has been corrupted, as is
|
|
expected.</p>
|
|
|
|
<p>Applications may choose to proceed without the resulting data, such as when
|
|
those results are not required to the application's primary function. However,
|
|
if the application cannot continue without the data, it will fail.</p>
|
|
|
|
<h2 id="implementation">Implementation</h2>
|
|
|
|
<h3 id="summary">Summary</h3>
|
|
|
|
<ol>
|
|
<li>Generate an ext4 system image.</li>
|
|
<li><a href="#hash-tree">Generate a hash tree</a> for that image.</li>
|
|
<li><a href="#mapping-table">Build a dm-verity table</a> for that hash tree.</li>
|
|
<li><a href="#signing">Sign that dm-verity table</a> to produce a table
|
|
signature.</li>
|
|
<li><a href="#metadata">Bundle the table signature</a> and dm-verity table
|
|
into verity metadata.</li>
|
|
<li>Concatenate the system image, the verity metadata, and the hash tree.</li>
|
|
</ol>
|
|
|
|
<p>See the <a href="http://www.chromium.org/chromium-os/chromiumos-design-docs/verified-boot">The Chromium Projects - Verified
|
|
Boot</a>
|
|
for a detailed description of the hash tree and dm-verity table.</p>
|
|
|
|
<h3 id="hash-tree">Generating the hash tree</h3>
|
|
|
|
<p>As described in the <a href="#introduction">Introduction</a>, the hash tree is
|
|
integral to dm-verity. The
|
|
<a href="https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity">cryptsetup</a> tool will
|
|
generate a hash tree for you. Alternatively, a compatible one is defined here:</p>
|
|
|
|
<pre class="devsite-click-to-copy">
|
|
<your block device name> <your block device name> <block size> <block size> <image size in blocks> <image size in blocks + 8> <root hash> <salt>
|
|
</pre>
|
|
|
|
<p>To form the hash, the system image is split at layer 0 into 4k blocks, each
|
|
assigned a SHA256 hash. Layer 1 is formed by joining only those SHA256 hashes
|
|
into 4k blocks, resulting in a much smaller image. Layer 2 is formed
|
|
identically, with the SHA256 hashes of Layer 1.</p>
|
|
|
|
<p>This is done until the SHA256 hashes of the previous layer can fit in a single
|
|
block. When get the SHA256 of that block, you have the root hash of the tree. </p>
|
|
|
|
<p>The size of the hash tree (and corresponding disk space usage) varies with the
|
|
size of the verified partition. In practice, the size of hash trees tends to be
|
|
small, often less than 30 MB.</p>
|
|
|
|
<p>If you have a block in a layer that isn't completely filled naturally by the
|
|
hashes of the previous layer, you should pad it with zeroes to achieve the
|
|
expected 4k. This allows you to know the hash tree hasn't been removed and is
|
|
instead completed with blank data.</p>
|
|
|
|
<p>To generate the hash tree, concatenate the layer 2 hashes onto those for layer
|
|
1, the layer 3 the hashes onto those of layer 2, and so on. Write all of this
|
|
out to disk. Note that this doesn't reference layer 0 of the root hash.</p>
|
|
|
|
<p>To recap, the general algorithm to construct the hash tree is as follows:</p>
|
|
|
|
<ol>
|
|
<li>Choose a random salt (hexadecimal encoding).</li>
|
|
<li>Unsparse your system image into 4k blocks.</li>
|
|
<li>For each block, get its (salted) SHA256 hash.</li>
|
|
<li>Concatenate these hashes to form a level</li>
|
|
<li>Pad the level with 0s to a 4k block boundary.</li>
|
|
<li>Concatenate the level to your hash tree.</li>
|
|
<li>Repeat steps 2-6 using the previous level as the source for the next until
|
|
you have only a single hash.</li>
|
|
</ol>
|
|
|
|
<p>The result of this is a single hash, which is your root hash. This and your salt
|
|
are used during the construction of your dm-verity mapping table.</p>
|
|
|
|
<h3 id="mapping-table">Building the dm-verity mapping table</h3>
|
|
|
|
<p>Build the dm-verity mapping table, which identifies the block device (or target)
|
|
for the kernel and the location of the hash tree (which is the same value.) This
|
|
mapping is used for <code>fstab</code> generation and booting. The table also identifies
|
|
the size of the blocks and the hash_start, the start location of the hash tree
|
|
(specifically, its block number from the beginning of the image).</p>
|
|
|
|
<p>See <a href="https://code.google.com/p/cryptsetup/wiki/DMVerity">cryptsetup</a> for a
|
|
detailed description of the verity target mapping table fields.</p>
|
|
|
|
<h3 id="signing">Signing the dm-verity table</h3>
|
|
|
|
<p>Sign the dm-verity table to produce a table signature. When verifying a
|
|
partition, the table signature is validated first. This is done against a key on
|
|
your boot image in a fixed location. Keys are typically included in the
|
|
manufacturers' build systems for automatic inclusion on devices in a fixed
|
|
location.</p>
|
|
|
|
<p>To verify the partition with this signature and key combination:</p>
|
|
|
|
<ol>
|
|
<li>Add an RSA-2048 key in libmincrypt-compatible format to the /boot partition
|
|
at /verity_key. Identify the location of the key used to verify the hash
|
|
tree.</li>
|
|
<li>In the fstab for the relevant entry, add 'verify' to the fs_mgr flags.</li>
|
|
</ol>
|
|
|
|
<h3 id="metadata">Bundling the table signature into metadata</h3>
|
|
|
|
<p>Bundle the table signature and dm-verity table into verity metadata. The entire
|
|
block of metadata is versioned so it may be extended, such as to add a second
|
|
kind of signature or change some ordering.</p>
|
|
|
|
<p>As a sanity check, a magic number is associated with each set of table metadata
|
|
that helps identify the table. Since the length is included in the ext4 system
|
|
image header, this provides a way to search for the metadata without knowing the
|
|
contents of the data itself.</p>
|
|
|
|
<p>This makes sure you haven't elected to verify an unverified partition. If so,
|
|
the absence of this magic number will halt the verification process. This number
|
|
resembles:<br/>
|
|
0xb001b001</p>
|
|
|
|
<p>The byte values in hex are:</p>
|
|
|
|
<ul>
|
|
<li>first byte = b0</li>
|
|
<li>second byte = 01</li>
|
|
<li>third byte = b0</li>
|
|
<li>fourth byte = 01</li>
|
|
</ul>
|
|
|
|
<p>The following diagram depicts the breakdown of the verity metadata:</p>
|
|
|
|
<pre><magic number>|<version>|<signature>|<table length>|<table>|<padding>
|
|
\-------------------------------------------------------------------/
|
|
\----------------------------------------------------------/ |
|
|
| |
|
|
| 32K
|
|
block content
|
|
</pre>
|
|
|
|
<p>And this table describes those metadata fields.</p>
|
|
|
|
<p class="table-caption" id="table1">
|
|
<strong>Table 1.</strong> Verity metadata fields</p>
|
|
|
|
<table>
|
|
<tr>
|
|
<th>Field</th>
|
|
<th>Purpose</th>
|
|
<th>Size</th>
|
|
<th>Value</th>
|
|
</tr>
|
|
<tr>
|
|
<td>magic number</td>
|
|
<td>used by fs_mgr as a sanity check</td>
|
|
<td>4 bytes</td>
|
|
<td>0xb001b001</td>
|
|
</tr>
|
|
<tr>
|
|
<td>version</td>
|
|
<td>used to version the metadata block</td>
|
|
<td>4 bytes</td>
|
|
<td>currently 0</td>
|
|
</tr>
|
|
<tr>
|
|
<td>signature</td>
|
|
<td>the signature of the table in PKCS1.5 padded form</td>
|
|
<td>256 bytes</td>
|
|
<td></td>
|
|
</tr>
|
|
<tr>
|
|
<td>table length</td>
|
|
<td>the length of the dm-verity table in bytes</td>
|
|
<td>4 bytes</td>
|
|
<td></td>
|
|
</tr>
|
|
<tr>
|
|
<td>table</td>
|
|
<td>the dm-verity table described earlier</td>
|
|
<td>`table length` bytes</td>
|
|
<td></td>
|
|
</tr>
|
|
<tr>
|
|
<td>padding</td>
|
|
<td>this structure is 0-padded to 32k in length</td>
|
|
<td></td>
|
|
<td>0</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<h3 id="optimize">Optimizing dm-verity</h3>
|
|
|
|
<p>To get the best performance out of dm-verity, you should:</p>
|
|
<ul>
|
|
<li>In the kernel, turn on NEON SHA-2 for ARMv7 and the SHA-2 extensions for ARMv8.
|
|
<li>Experiment with different read-ahead and prefetch_cluster settings to find the best configuration for your device.
|
|
</ul>
|
|
|
|
</body>
|
|
</html>
|