Merkle trees #
A Merkle tree hashes a list of leaves to a single root so that each leaf can later be opened
against the root with a short authentication path ([Mer90], Section 6; [BS23],
Section 8.9). Here the hash is a keyed function H : List Bool → List Bool → List Bool, as in
CollisionResistant. As in both sources, a leaf x becomes the node H k x, and the parent of
nodes l and r is H k (encPair encBits encBits (l, r)). The pairing encPair is injective,
so nodes of any length can be paired without ambiguity.
The tree over leaves has depth Nat.clog 2 leaves.length, the least d with
leaves.length ≤ 2 ^ d. Missing leaves read as the empty word, padding the tree to 2 ^ d leaves
as [BS23] suggests. For 2 ^ d leaves this is Merkle's H(1, 2 ^ d, Y), with his
Y_i as leaves.getD (i - 1) []. For other leaf counts it differs from Merkle's recursion, which
splits at the midpoint without padding.
An authentication path for position i lists the sibling of each node on the way from leaf i
to the root. merkleRootOfPath recomputes a root from a leaf, a position and a path. Its
correctness is merkleRootOfPath_merklePath. Binding is isCollision_extractMerkleCollision: two
openings of the same position to different leaves, along paths of equal length and with the same
recomputed root, contain a collision of H k, found deterministically by
extractMerkleCollision. This is the reduction behind [BS23], Theorem 8.8.
What a verifier must check. Leaves and internal nodes are hashed with the same H k, with no
domain separation, so a recomputed root alone binds nothing. A verifier must fix the path length
to the depth d and check i < 2 ^ d:
- with a shorter path, an internal node's hash input opens as a leaf; for leaves
[a, b], the "leaf"encPair encBits encBits (H k a, H k b)at position0with path[]recomputes the root; merkleRootOfPathreads only the lowpath.lengthbits ofi, so positions wrap modulo2 ^ path.length;- the root does not fix the number of leaves:
[a, b, c]and[a, b, c, []]have the same root.
The root, the path, root recomputation and extraction have plain polynomial-time certificates for hash families whose digests have the length of the key. Positions are unary in these certificates, which suits trees with polynomially many leaves.
Main definitions #
Cslib.Crypto.merkleParents,Cslib.Crypto.merkleLevel: one level of the tree from the one below, and leveljcounted from the hashed leaves.Cslib.Crypto.merkleRoot,Cslib.Crypto.merklePath: the root and the authentication path of a position.Cslib.Crypto.merkleInputs,Cslib.Crypto.merkleRootOfPath: the hash inputs met while recomputing a root from an opening, and the recomputed root.Cslib.Crypto.extractMerkleCollision: the first differing pair of hash inputs of two openings.
Main results #
Cslib.Crypto.length_merkleLevel,Cslib.Crypto.length_merklePath: level sizes and path length.Cslib.Crypto.merkleRootOfPath_merklePath: an honest opening recomputes the root.Cslib.Crypto.isCollision_extractMerkleCollision: a second opening of a position yields a collision.Cslib.Crypto.computableInPolyTime_merkleRoot,Cslib.Crypto.computableInPolyTime_merklePath,Cslib.Crypto.computableInPolyTime_merkleRootOfPath,Cslib.Crypto.computableInPolyTime_extractMerkleCollision: plain polynomial-time certificates.
References #
- R. C. Merkle, A Certified Digital Signature, Section 6.
- D. Boneh, V. Shoup, A Graduate Course in Applied Cryptography, Section 8.9, Figure 8.12 and Theorem 8.8.
The tree #
The position of the sibling of node p within its level.
Instances For
Recomputing a root from an opening #
One step of merkleInputs: hash the current input to a node and pair it with sibling,
putting the node on the left when the current position is even.
Instances For
The root recomputed from leaf at position i along the authentication path path
([BS23], (8.16)). Only the low path.length bits of i are read, and nothing checks
the path length: a verifier must fix path.length to the depth d and check i < 2 ^ d (see
the module documentation).
Instances For
Shape of the tree #
Parent i hashes the pair of nodes 2 * i and 2 * i + 1.
The authentication path of position i lists, for each level t below the root, the sibling
of the ancestor of leaf i at level t.
Hashed leaves have the length of the key when the hash's digests do.
Parents have the length of the key when the hash's digests do.
Correctness of honest openings #
Binding #
Binding, the deterministic reduction behind [BS23], Theorem 8.8: two openings
of position i to different leaves, along paths of equal length and with the same recomputed
root, contain a collision of H k, which extractMerkleCollision finds. Equal path lengths are
essential, since leaves and internal nodes are not domain-separated; a verifier that fixes the
path length to the depth d and checks i < 2 ^ d gives this hypothesis.
Polynomial time #
Computing the root of a Merkle tree is polynomial-time for a polynomial-time keyed hash whose digests have the length of the key.
Computing an authentication path is polynomial-time for a polynomial-time keyed hash whose digests have the length of the key.
Recomputing a root from an opening is polynomial-time for a polynomial-time keyed hash whose digests have the length of the key.
Extracting a collision from two openings is polynomial-time for a polynomial-time keyed hash whose digests have the length of the key.