The Merkle–Damgård transform #
Given a keyed 2n → n collision-resistant compression family
h : List Bool → List Bool → List Bool, the Merkle–Damgård transform ([BS23],
Section 8.4, Figure 8.5 and Theorem 8.3) constructs a collision-resistant hash family on
arbitrary-length messages by iterating h k over a sequence of n-bit blocks starting from a
fixed n-bit initialization vector 0^n.
We formalize the transform and its collision-extraction reduction at two levels:
Block-chain level (
merkleDamgardChain,extractChainCollision): Iteratingh kover a list ofn-bit blocks produces ann-bit digest. Whenever two block listsbs₁andbs₂ofn-bit blocks produce the same final digest undermerkleDamgardChainand neither block list is a suffix of the other (¬ (bs₁ <:+ bs₂) ∧ ¬ (bs₂ <:+ bs₁)), scanning the compression inputs backward from the final step (extractChainCollision) deterministically finds a2n-bit collision(x₁, x₂)forh kin polynomial time. In particular, this covers both cases of the classical length-strengthened Merkle–Damgård proof ([BS23], Theorem 8.3):isCollision_extractChainCollision_of_getLast?_ne: the final (length-encoding) blocks ofbs₁andbs₂differ;isCollision_extractChainCollision_of_length_eq:bs₁andbs₂have the same number of blocks andbs₁ ≠ bs₂. Consequently, any polynomial-time suffix-free block encoding turns aCompressionFamilyinto a collision-resistant hash family (CompressionFamily.collisionResistantOn_merkleDamgardChain).
Unrestricted bit-string level (
mdBlocks,merkleDamgard,extractMerkleDamgardCollision): Encoding an arbitrary bit stringmsg : List Boolas the initial marker block0^nfollowed by onen-bit data block1 ‖ b ‖ 0^{n-2}per bitbofmsgproduces a suffix-free block encoding of all bit strings for everyn ≥ 2, with no upper bound on message length. IteratinghovermdBlocks k.length msgyieldsmerkleDamgard h : List Bool → List Bool → List Bool, which is collision-resistant on all arbitrary-length bit strings (CompressionFamily.collisionResistant_merkleDamgard,CompressionFamily.hashFamily_merkleDamgard).
Main definitions #
Cslib.Crypto.padOrTake: fixed-width bit-string normalization to exact lengthn.Cslib.Crypto.merkleDamgardChain: block-level iterated compressionblocks.foldl (fun acc b => h k (acc ++ b)) iv.Cslib.Crypto.chainTraceStep,Cslib.Crypto.chainTrace,Cslib.Crypto.extractChainCollision: polynomial-time backward scan extracting a2n-bit compression collision from colliding block chains.Cslib.Crypto.mdEndBlock,Cslib.Crypto.mdDataBlock,Cslib.Crypto.mdBlocks: suffix-freen-bit block encoding of arbitrary bit strings.Cslib.Crypto.merkleDamgard,Cslib.Crypto.extractMerkleDamgardCollision: domain-extended keyed hash family and its deterministic collision extractor.
Main results #
Cslib.Crypto.length_merkleDamgardChain,Cslib.Crypto.length_merkleDamgard: digest length preservation.Cslib.Crypto.isCollision_extractChainCollision_of_not_suffix,Cslib.Crypto.isCollision_extractChainCollision_of_getLast?_ne,Cslib.Crypto.isCollision_extractChainCollision_of_length_eq: fundamental Merkle–Damgård chain collision extraction lemmas.Cslib.Crypto.CompressionFamily.collisionResistantOn_merkleDamgardChain: collision resistance for any polynomial-time suffix-free block encoding.Cslib.Crypto.mdBlocks_suffix_iff: suffix-freeness ofmdBlocks nforn ≥ 2.Cslib.Crypto.isCollision_extractMerkleDamgardCollision: deterministic collision extraction formerkleDamgard.Cslib.Crypto.CompressionFamily.collisionResistant_merkleDamgard,Cslib.Crypto.CompressionFamily.hashFamily_merkleDamgard: any2n → ncollision-resistant compression family yields an unrestricted collision-resistant hash family onList Bool.
References #
- D. Boneh, V. Shoup, A Graduate Course in Applied Cryptography, Section 8.4 (Figure 8.5 and Theorem 8.3).
- J. Katz, Y. Lindell, Introduction to Modern Cryptography, Section 5.2.
Fixed-width normalization #
Normalizing a word to unary length n is computable in plain polynomial time.
Block-level Merkle–Damgård chain and collision extraction #
Block-level Merkle–Damgård iterated compression ([BS23], Section 8.4, Figure 8.5):
starting from the initial chaining value iv, fold over blocks by feeding acc ++ b to
h k at each step.
Instances For
If the initial vector and every block have length k.length, and h k maps 2 * k.length-bit
inputs to k.length-bit outputs, then merkleDamgardChain produces a k.length-bit digest.
Single step of chainTrace: normalize the current chaining value and block to k.length bits,
compress their concatenation under h k, and prepend the 2 * k.length-bit compression input to
the reverse-chronological trace.
Instances For
Run the Merkle–Damgård block chain while recording, in reverse chronological order, the
2n-bit compression inputs fed to h k at each step. Each step normalizes the chaining value and
block to k.length bits via padOrTake k.length, which is the identity on valid k.length-bit
chains and guarantees polynomial state growth on all inputs.
Instances For
Computing chainTrace is polynomial-time for any polynomial-time compression function h.
Extract a candidate 2n-bit collision (x₁, x₂) for h k from two block chains bs₁ and
bs₂ by scanning their compression inputs backward from the final step and returning the first
differing pair of inputs (or ([], []) if none differ).
Instances For
Extracting a compression collision from two block chains is computable in plain polynomial time.
Fundamental Merkle–Damgård chain collision lemma (suffix-free form):
if bs₁ and bs₂ are lists of k.length-bit blocks such that neither is a suffix of the other
and merkleDamgardChain h k iv bs₁ = merkleDamgardChain h k iv bs₂, then
extractChainCollision h k iv bs₁ bs₂ produces a valid 2 * k.length-bit collision for h k.
Merkle–Damgård chain collision lemma (distinct final blocks, [BS23], Theorem 8.3
Case 1): if two non-empty k.length-bit block chains bs₁ and bs₂ differ in their last block
(bs₁.getLast? ≠ bs₂.getLast?) and produce the same chain digest, then extractChainCollision
returns a 2 * k.length-bit collision for h k.
Merkle–Damgård chain collision lemma (equal block lengths, [BS23], Theorem 8.3
Case 2): if two distinct k.length-bit block chains bs₁ ≠ bs₂ of equal length
(bs₁.length = bs₂.length) produce the same chain digest, then extractChainCollision returns a
2 * k.length-bit collision for h k.
General suffix-free block-encoding Merkle–Damgård reduction:
for any polynomial-time block encoder encode : ℕ → List Bool → List (List Bool) that maps valid
messages at parameter n (for all sufficiently large n) into suffix-free lists of n-bit
blocks, iterating a collision-resistant CompressionFamily over encode k.length msg yields a
collision-resistant hash family on valid.
Prefix-tagged bit-block encoding and unrestricted Merkle–Damgård hash family #
Encode a single message bit b as an n-bit block 1 ‖ b ‖ 0^{n-2} (truncated to n bits
when n < 2). For n ≥ 2, the leading tag bit true distinguishes every data block from
mdEndBlock n, and the second bit b distinguishes the two bit values.
Instances For
Encode an arbitrary bit string msg : List Bool as a non-empty list of n-bit blocks:
the initial marker block mdEndBlock n followed by one mdDataBlock n per bit of msg.
Because mdEndBlock n starts with false and every mdDataBlock n b starts with true, no
distinct message encoding can be a suffix of another whenever n ≥ 2.
Instances For
Encoding a bit string into n-bit Merkle–Damgård blocks is computable in plain polynomial
time.
The Merkle–Damgård hash family (merkleDamgard): hash an arbitrary-length bit string
msg : List Bool under key k : List Bool by iterating the 2n → n compression function h k
over mdBlocks k.length msg from the initial vector 0^{k.length}.
Instances For
For any compression function h mapping 2 * k.length-bit inputs to k.length-bit outputs,
merkleDamgard h k msg always has length k.length.
Evaluating merkleDamgard h is computable in plain polynomial time whenever h is computable
in polynomial time and compresses 2n-bit inputs to n-bit digests.
Extracting a 2n-bit compression collision from a merkleDamgard h collision is computable
in plain polynomial time.
Deterministic Merkle–Damgård collision extraction ([BS23], Theorem 8.3):
for any key k of length ≥ 2 and any two distinct messages m₁ ≠ m₂ with
merkleDamgard h k m₁ = merkleDamgard h k m₂, extractMerkleDamgardCollision h k m₁ m₂ outputs a
valid 2 * k.length-bit collision (x₁, x₂) for h k.
Security of the Merkle–Damgård transform ([BS23], Theorem 8.3):
if h is a 2n → n collision-resistant compression family (CompressionFamily h), then
merkleDamgard h is collision-resistant on all arbitrary-length bit strings (List Bool).
Domain extension from compression families to unrestricted hash families:
every 2n → n collision-resistant CompressionFamily h induces a full-domain collision-resistant
HashFamily (merkleDamgard h) on arbitrary bit strings.