Home | History | Annotate | Download | only in verifiedboot
      1 <html devsite>
      2   <head>
      3     <title>Implementing dm-verity</title>
      4     <meta name="project_path" value="/_project.yaml" />
      5     <meta name="book_path" value="/_book.yaml" />
      6   </head>
      7   <body>
      8   <!--
      9       Copyright 2017 The Android Open Source Project
     10 
     11       Licensed under the Apache License, Version 2.0 (the "License");
     12       you may not use this file except in compliance with the License.
     13       You may obtain a copy of the License at
     14 
     15           http://www.apache.org/licenses/LICENSE-2.0
     16 
     17       Unless required by applicable law or agreed to in writing, software
     18       distributed under the License is distributed on an "AS IS" BASIS,
     19       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     20       See the License for the specific language governing permissions and
     21       limitations under the License.
     22   -->
     23 
     24 
     25 
     26 <h2 id="operation">Operation</h2>
     27 
     28 <p>dm-verity protection lives in the kernel. So if rooting software compromises the
     29 system before the kernel comes up, it will retain that access. To mitigate this
     30 risk, most manufacturers verify the kernel using a key burned into the device.
     31 That key is not changeable once the device leaves the factory.</p>
     32 
     33 <p>Manufacturers use that key to verify the signature on the first-level
     34 bootloader, which in turn verifies the signature on subsequent levels, the
     35 application bootloader and eventually the kernel. Each manufacturer wishing to
     36 take advantage of <a href="verified-boot.html">verified boot</a> should have a
     37 method for verifying the integrity of the kernel. Assuming the kernel has been
     38 verified, the kernel can look at a block device and verify it as it is mounted.</p>
     39 
     40 <p>One way of verifying a block device is to directly hash its contents and compare
     41 them to a stored value. However, attempting to verify an entire block device can
     42 take an extended period and consume much of a device's power. Devices would take
     43 long periods to boot and then be significantly drained prior to use.</p>
     44 
     45 <p>Instead, dm-verity verifies blocks individually and only when each one is
     46 accessed. When read into memory, the block is hashed in parallel. The hash is
     47 then verified up the tree. And since reading the block is such an expensive
     48 operation, the latency introduced by this block-level verification is
     49 comparatively nominal.</p>
     50 
     51 <p>If verification fails, the device generates an I/O error indicating the block
     52 cannot be read. It will appear as if the filesystem has been corrupted, as is
     53 expected.</p>
     54 
     55 <p>Applications may choose to proceed without the resulting data, such as when
     56 those results are not required to the application's primary function. However,
     57 if the application cannot continue without the data, it will fail.</p>
     58 
     59 <h2 id="implementation">Implementation</h2>
     60 
     61 <h3 id="summary">Summary</h3>
     62 
     63 <ol>
     64 <li>Generate an ext4 system image.</li>
     65 <li><a href="#hash-tree">Generate a hash tree</a> for that image.</li>
     66 <li><a href="#mapping-table">Build a dm-verity table</a> for that hash tree.</li>
     67 <li><a href="#signing">Sign that dm-verity table</a> to produce a table
     68 signature.</li>
     69 <li><a href="#metadata">Bundle the table signature</a> and dm-verity table
     70 into verity metadata.</li>
     71 <li>Concatenate the system image, the verity metadata, and the hash tree.</li>
     72 </ol>
     73 
     74 <p>See the <a href="http://www.chromium.org/chromium-os/chromiumos-design-docs/verified-boot">The Chromium Projects - Verified
     75 Boot</a>
     76 for a detailed description of the hash tree and dm-verity table.</p>
     77 
     78 <h3 id="hash-tree">Generating the hash tree</h3>
     79 
     80 <p>As described in the <a href="#introduction">Introduction</a>, the hash tree is
     81 integral to dm-verity. The
     82 <a href="https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity">cryptsetup</a> tool will
     83 generate a hash tree for you. Alternatively, a compatible one is defined here:</p>
     84 
     85 <pre class="devsite-click-to-copy">
     86 &lt;your block device name&gt; &lt;your block device name&gt; &lt;block size&gt; &lt;block size&gt; &lt;image size in blocks&gt; &lt;image size in blocks + 8&gt; &lt;root hash&gt; &lt;salt&gt;
     87 </pre>
     88 
     89 <p>To form the hash, the system image is split at layer 0 into 4k blocks, each
     90 assigned a SHA256 hash. Layer 1 is formed by joining only those SHA256 hashes
     91 into 4k blocks, resulting in a much smaller image. Layer 2 is formed
     92 identically, with the SHA256 hashes of Layer 1.</p>
     93 
     94 <p>This is done until the SHA256 hashes of the previous layer can fit in a single
     95 block. When get the SHA256 of that block, you have the root hash of the tree. </p>
     96 
     97 <p>The size of the hash tree (and corresponding disk space usage) varies with the
     98 size of the verified partition. In practice, the size of hash trees tends to be
     99 small, often less than 30 MB.</p>
    100 
    101 <p>If you have a block in a layer that isn't completely filled naturally by the
    102 hashes of the previous layer, you should pad it with zeroes to achieve the
    103 expected 4k. This allows you to know the hash tree hasn't been removed and is
    104 instead completed with blank data.</p>
    105 
    106 <p>To generate the hash tree, concatenate the layer 2 hashes onto those for layer
    107 1, the layer 3 the hashes onto those of layer 2, and so on. Write all of this
    108 out to disk. Note that this doesn't reference layer 0 of the root hash.</p>
    109 
    110 <p>To recap, the general algorithm to construct the hash tree is as follows:</p>
    111 
    112 <ol>
    113 <li>Choose a random salt (hexadecimal encoding).</li>
    114 <li>Unsparse your system image into 4k blocks.</li>
    115 <li>For each block, get its (salted) SHA256 hash.</li>
    116 <li>Concatenate these hashes to form a level</li>
    117 <li>Pad the level with 0s to a 4k block boundary.</li>
    118 <li>Concatenate the level to your hash tree.</li>
    119 <li>Repeat steps 2-6 using the previous level as the source for the next until
    120 you have only a single hash.</li>
    121 </ol>
    122 
    123 <p>The result of this is a single hash, which is your root hash. This and your salt
    124 are used during the construction of your dm-verity mapping table.</p>
    125 
    126 <h3 id="mapping-table">Building the dm-verity mapping table</h3>
    127 
    128 <p>Build the dm-verity mapping table, which identifies the block device (or target)
    129 for the kernel and the location of the hash tree (which is the same value.) This
    130 mapping is used for <code>fstab</code> generation and booting. The table also identifies
    131 the size of the blocks and the hash_start, the start location of the hash tree
    132 (specifically, its block number from the beginning of the image).</p>
    133 
    134 <p>See <a href="https://code.google.com/p/cryptsetup/wiki/DMVerity">cryptsetup</a> for a
    135 detailed description of the verity target mapping table fields.</p>
    136 
    137 <h3 id="signing">Signing the dm-verity table</h3>
    138 
    139 <p>Sign the dm-verity table to produce a table signature. When verifying a
    140 partition, the table signature is validated first. This is done against a key on
    141 your boot image in a fixed location. Keys are typically included in the
    142 manufacturers' build systems for automatic inclusion on devices in a fixed
    143 location.</p>
    144 
    145 <p>To verify the partition with this signature and key combination:</p>
    146 
    147 <ol>
    148 <li>Add an RSA-2048 key in libmincrypt-compatible format to the /boot partition
    149 at /verity_key. Identify the location of the key used to verify the hash
    150 tree.</li>
    151 <li>In the fstab for the relevant entry, add 'verify' to the fs_mgr flags.</li>
    152 </ol>
    153 
    154 <h3 id="metadata">Bundling the table signature into metadata</h3>
    155 
    156 <p>Bundle the table signature and dm-verity table into verity metadata. The entire
    157 block of metadata is versioned so it may be extended, such as to add a second
    158 kind of signature or change some ordering.</p>
    159 
    160 <p>As a sanity check, a magic number is associated with each set of table metadata
    161 that helps identify the table. Since the length is included in the ext4 system
    162 image header, this provides a way to search for the metadata without knowing the
    163 contents of the data itself.</p>
    164 
    165 <p>This makes sure you haven't elected to verify an unverified partition. If so,
    166 the absence of this magic number will halt the verification process. This number
    167 resembles:<br/>
    168 0xb001b001</p>
    169 
    170 <p>The byte values in hex are:</p>
    171 
    172 <ul>
    173 <li>first byte = b0</li>
    174 <li>second byte = 01</li>
    175 <li>third byte = b0</li>
    176 <li>fourth byte = 01</li>
    177 </ul>
    178 
    179 <p>The following diagram depicts the breakdown of the verity metadata:</p>
    180 
    181 <pre>&lt;magic number&gt;|&lt;version&gt;|&lt;signature&gt;|&lt;table length&gt;|&lt;table&gt;|&lt;padding&gt;
    182 \-------------------------------------------------------------------/
    183 \----------------------------------------------------------/   |
    184                             |                                  |
    185                             |                                 32K
    186                        block content
    187 </pre>
    188 
    189 <p>And this table describes those metadata fields.</p>
    190 
    191 <p class="table-caption" id="table1">
    192   <strong>Table 1.</strong> Verity metadata fields</p>
    193 
    194 <table>
    195 <tr>
    196 <th>Field</th>
    197 <th>Purpose</th>
    198 <th>Size</th>
    199 <th>Value</th>
    200 </tr>
    201 <tr>
    202 <td>magic number</td>
    203 <td>used by fs_mgr as a sanity check</td>
    204 <td>4 bytes</td>
    205 <td>0xb001b001</td>
    206 </tr>
    207 <tr>
    208 <td>version</td>
    209 <td>used to version the metadata block</td>
    210 <td>4 bytes</td>
    211 <td>currently 0</td>
    212 </tr>
    213 <tr>
    214 <td>signature</td>
    215 <td>the signature of the table in PKCS1.5 padded form</td>
    216 <td>256 bytes</td>
    217 <td></td>
    218 </tr>
    219 <tr>
    220 <td>table length</td>
    221 <td>the length of the dm-verity table in bytes</td>
    222 <td>4 bytes</td>
    223 <td></td>
    224 </tr>
    225 <tr>
    226 <td>table</td>
    227 <td>the dm-verity table described earlier</td>
    228 <td>`table length` bytes</td>
    229 <td></td>
    230 </tr>
    231 <tr>
    232 <td>padding</td>
    233 <td>this structure is 0-padded to 32k in length</td>
    234 <td></td>
    235 <td>0</td>
    236 </tr>
    237 </table>
    238 
    239 <h3 id="optimize">Optimizing dm-verity</h3>
    240 
    241 <p>To get the best performance out of dm-verity, you should:</p>
    242   <ul>
    243     <li>In the kernel, turn on NEON SHA-2 for ARMv7 and the SHA-2 extensions for ARMv8.
    244     <li>Experiment with different read-ahead and prefetch_cluster settings to find the best configuration for your device.
    245   </ul>
    246 
    247   </body>
    248 </html>
    249