blob: d20a10fdf63d9f952932448ec08a92f566f345c6 [file] [log] [blame] [view]
# Fuchsia Merkle Roots
[Merkle Trees][merkletree] are used in various places in the Fuchsia ecosystem,
including the [FAR Archive Format][far], the Blob Storage Filesystem, and the
[Package Manager][pm].
In [Zircon][zircon] `zx-verity` provides an API for application components to
read data from local storage. When retrieving data the integrity of the data is
verified and causing reads to fail when the data has been modified or corrupted.
zx-verity is based on a [Merkle Tree][merkletree], and is derived from a similar
system in [Chrome OS][dmverity].
All of these implementations share the algorithm documented herein.
## Parameters of the Merkle Tree
* Block size: 8kb, 0 padded.
* Root digest size: 32 bytes.
* Hash algorithm: SHA-256.
* Block digest computation: SHA-256(u64(offset | level) + u32(length) + data)
## Definitions
The merkle tree contains levels. A level is a row of the tree, starting at 0 and
counting upward. Level 0 represents the level that contains hashes of chunks of
the input stream.
Each level contains a number of hashes of the previous level. The hashes within
a level are computed from 8kb blocks from the previous level (or data, if level
0), prepended with a block identity.
A block identity is the binary OR of the starting byte index of the block within
the current level and the current level index, followed by the length of the
block. For level 0, the length of the block is 8kb, except for the last block,
which may be less than 8kb. All other levels use a length of 8kb, even when the
last block is 0 padded.
## Computation of a level
1. Initialize the level with an index, an offset starting at 0, and an empty
list of hashes.
2. For each 8kb (or remainder of) of input, compute the next block identity by
taking the binary OR of the level index and the current offset, followed
by the length of the input.
3. Init a SHA-256 digest, append to it the identity, the input, and if the
input is shorter than 8kb, a pad of 0 up to 8kb.
4. Append the output of the digest to the level's list of hashes. Increment
the offset by the input length.
5. Repeat 1-4 until all input is consumed.
6. If the length of hashes is 32, finish.
7. If the length of hashes is not 8kb aligned, 0 fill up to an 8kb
alignment and compute more levels until there is a root level containing a
single 32 byte digest.
## Computation of a root digest
Compute level 0 with the input data. Construct and compute subsequent levels
using the previous level hashes as input data, until a level hashes contains
exactly 32 bytes. This last level contains the root digest of the merkle tree.
## A note about the empty digest
As a special case, when there is no input data, implementations may need to
handle the calculation independently. The digest of the empty input is simply
the SHA-256 of 12 0 bytes, the block identity of a single 0 length block.
## Example values
* The empty digest:
* 8192 bytes of `0xff` - "oneblock"
* 65536 bytes of `0xff` - "small"
* 2105344 bytes of `0xff` - "large"
* 2109440 bytes of `0xff` - "unaligned"
* `0xff0080` bytes filled with repetitions of `0xff0080` - "fuchsia"
[merkletree]: "Merkle Tree"
[dmverity]: "Chrome OS Verified Boot"
[far]: /docs/concepts/source_code/ "Archive Format"
[pm]: /src/sys/pkg/bin/pm/ "Package Manager"
[zircon]: /zircon/ "Zircon"