|  | dm-verity | 
|  | ========== | 
|  |  | 
|  | Device-Mapper's "verity" target provides transparent integrity checking of | 
|  | block devices using a cryptographic digest provided by the kernel crypto API. | 
|  | This target is read-only. | 
|  |  | 
|  | Construction Parameters | 
|  | ======================= | 
|  | <version> <dev> <hash_dev> <hash_start> | 
|  | <data_block_size> <hash_block_size> | 
|  | <num_data_blocks> <hash_start_block> | 
|  | <algorithm> <digest> <salt> | 
|  |  | 
|  | <version> | 
|  | This is the version number of the on-disk format. | 
|  |  | 
|  | 0 is the original format used in the Chromium OS. | 
|  | The salt is appended when hashing, digests are stored continuously and | 
|  | the rest of the block is padded with zeros. | 
|  |  | 
|  | 1 is the current format that should be used for new devices. | 
|  | The salt is prepended when hashing and each digest is | 
|  | padded with zeros to the power of two. | 
|  |  | 
|  | <dev> | 
|  | This is the device containing the data the integrity of which needs to be | 
|  | checked.  It may be specified as a path, like /dev/sdaX, or a device number, | 
|  | <major>:<minor>. | 
|  |  | 
|  | <hash_dev> | 
|  | This is the device that that supplies the hash tree data.  It may be | 
|  | specified similarly to the device path and may be the same device.  If the | 
|  | same device is used, the hash_start should be outside of the dm-verity | 
|  | configured device size. | 
|  |  | 
|  | <data_block_size> | 
|  | The block size on a data device.  Each block corresponds to one digest on | 
|  | the hash device. | 
|  |  | 
|  | <hash_block_size> | 
|  | The size of a hash block. | 
|  |  | 
|  | <num_data_blocks> | 
|  | The number of data blocks on the data device.  Additional blocks are | 
|  | inaccessible.  You can place hashes to the same partition as data, in this | 
|  | case hashes are placed after <num_data_blocks>. | 
|  |  | 
|  | <hash_start_block> | 
|  | This is the offset, in <hash_block_size>-blocks, from the start of hash_dev | 
|  | to the root block of the hash tree. | 
|  |  | 
|  | <algorithm> | 
|  | The cryptographic hash algorithm used for this device.  This should | 
|  | be the name of the algorithm, like "sha1". | 
|  |  | 
|  | <digest> | 
|  | The hexadecimal encoding of the cryptographic hash of the root hash block | 
|  | and the salt.  This hash should be trusted as there is no other authenticity | 
|  | beyond this point. | 
|  |  | 
|  | <salt> | 
|  | The hexadecimal encoding of the salt value. | 
|  |  | 
|  | Theory of operation | 
|  | =================== | 
|  |  | 
|  | dm-verity is meant to be setup as part of a verified boot path.  This | 
|  | may be anything ranging from a boot using tboot or trustedgrub to just | 
|  | booting from a known-good device (like a USB drive or CD). | 
|  |  | 
|  | When a dm-verity device is configured, it is expected that the caller | 
|  | has been authenticated in some way (cryptographic signatures, etc). | 
|  | After instantiation, all hashes will be verified on-demand during | 
|  | disk access.  If they cannot be verified up to the root node of the | 
|  | tree, the root hash, then the I/O will fail.  This should identify | 
|  | tampering with any data on the device and the hash data. | 
|  |  | 
|  | Cryptographic hashes are used to assert the integrity of the device on a | 
|  | per-block basis.  This allows for a lightweight hash computation on first read | 
|  | into the page cache.  Block hashes are stored linearly-aligned to the nearest | 
|  | block the size of a page. | 
|  |  | 
|  | Hash Tree | 
|  | --------- | 
|  |  | 
|  | Each node in the tree is a cryptographic hash.  If it is a leaf node, the hash | 
|  | is of some block data on disk.  If it is an intermediary node, then the hash is | 
|  | of a number of child nodes. | 
|  |  | 
|  | Each entry in the tree is a collection of neighboring nodes that fit in one | 
|  | block.  The number is determined based on block_size and the size of the | 
|  | selected cryptographic digest algorithm.  The hashes are linearly-ordered in | 
|  | this entry and any unaligned trailing space is ignored but included when | 
|  | calculating the parent node. | 
|  |  | 
|  | The tree looks something like: | 
|  |  | 
|  | alg = sha256, num_blocks = 32768, block_size = 4096 | 
|  |  | 
|  | [   root    ] | 
|  | /    . . .    \ | 
|  | [entry_0]                 [entry_1] | 
|  | /  . . .  \                 . . .   \ | 
|  | [entry_0_0]   . . .  [entry_0_127]    . . . .  [entry_1_127] | 
|  | / ... \             /   . . .  \             /           \ | 
|  | blk_0 ... blk_127  blk_16256   blk_16383      blk_32640 . . . blk_32767 | 
|  |  | 
|  |  | 
|  | On-disk format | 
|  | ============== | 
|  |  | 
|  | Below is the recommended on-disk format. The verity kernel code does not | 
|  | read the on-disk header. It only reads the hash blocks which directly | 
|  | follow the header. It is expected that a user-space tool will verify the | 
|  | integrity of the verity_header and then call dmsetup with the correct | 
|  | parameters. Alternatively, the header can be omitted and the dmsetup | 
|  | parameters can be passed via the kernel command-line in a rooted chain | 
|  | of trust where the command-line is verified. | 
|  |  | 
|  | The on-disk format is especially useful in cases where the hash blocks | 
|  | are on a separate partition. The magic number allows easy identification | 
|  | of the partition contents. Alternatively, the hash blocks can be stored | 
|  | in the same partition as the data to be verified. In such a configuration | 
|  | the filesystem on the partition would be sized a little smaller than | 
|  | the full-partition, leaving room for the hash blocks. | 
|  |  | 
|  | struct superblock { | 
|  | uint8_t signature[8] | 
|  | "verity\0\0"; | 
|  |  | 
|  | uint8_t version; | 
|  | 1 - current format | 
|  |  | 
|  | uint8_t data_block_bits; | 
|  | log2(data block size) | 
|  |  | 
|  | uint8_t hash_block_bits; | 
|  | log2(hash block size) | 
|  |  | 
|  | uint8_t pad1[1]; | 
|  | zero padding | 
|  |  | 
|  | uint16_t salt_size; | 
|  | big-endian salt size | 
|  |  | 
|  | uint8_t pad2[2]; | 
|  | zero padding | 
|  |  | 
|  | uint32_t data_blocks_hi; | 
|  | big-endian high 32 bits of the 64-bit number of data blocks | 
|  |  | 
|  | uint32_t data_blocks_lo; | 
|  | big-endian low 32 bits of the 64-bit number of data blocks | 
|  |  | 
|  | uint8_t algorithm[16]; | 
|  | cryptographic algorithm | 
|  |  | 
|  | uint8_t salt[384]; | 
|  | salt (the salt size is specified above) | 
|  |  | 
|  | uint8_t pad3[88]; | 
|  | zero padding to 512-byte boundary | 
|  | } | 
|  |  | 
|  | Directly following the header (and with sector number padded to the next hash | 
|  | block boundary) are the hash blocks which are stored a depth at a time | 
|  | (starting from the root), sorted in order of increasing index. | 
|  |  | 
|  | Status | 
|  | ====== | 
|  | V (for Valid) is returned if every check performed so far was valid. | 
|  | If any check failed, C (for Corruption) is returned. | 
|  |  | 
|  | Example | 
|  | ======= | 
|  |  | 
|  | Setup a device: | 
|  | dmsetup create vroot --table \ | 
|  | "0 2097152 "\ | 
|  | "verity 1 /dev/sda1 /dev/sda2 4096 4096 2097152 1 "\ | 
|  | "4392712ba01368efdf14b05c76f9e4df0d53664630b5d48632ed17a137f39076 "\ | 
|  | "1234000000000000000000000000000000000000000000000000000000000000" | 
|  |  | 
|  | A command line tool veritysetup is available to compute or verify | 
|  | the hash tree or activate the kernel driver.  This is available from | 
|  | the LVM2 upstream repository and may be supplied as a package called | 
|  | device-mapper-verity-tools: | 
|  | git://sources.redhat.com/git/lvm2 | 
|  | http://sourceware.org/git/?p=lvm2.git | 
|  | http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/verity?cvsroot=lvm2 | 
|  |  | 
|  | veritysetup -a vroot /dev/sda1 /dev/sda2 \ | 
|  | 4392712ba01368efdf14b05c76f9e4df0d53664630b5d48632ed17a137f39076 |