Skip to main content

lumen/chunks/
lhas.rs

1//! The `LHAS` chunk: per-layer leaf hashes and a Merkle root
2//! ([`spec/09-layer-data.md`] section 4.10).
3//!
4//! The tree is RFC 6962 style and domain separated: leaves hash `0x00 || data`,
5//! internal nodes hash `0x01 || left || right`, and an odd node at any level is
6//! promoted rather than duplicated.
7
8use crate::check::Check;
9use crate::error::{Error, Result};
10use crate::io::{Reader, Writer};
11use sha2::{Digest, Sha256};
12
13/// The `LHAS` header: algorithm, hash size, layer count and root.
14pub const LHAS_HEADER_LEN: usize = 38;
15/// `hash_algorithm` for SHA-256.
16pub const HASH_ALGORITHM_SHA256: u8 = 0x01;
17/// `hash_size` for SHA-256.
18pub const HASH_SIZE_SHA256: u8 = 32;
19
20/// The `LHAS` chunk.
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct LayerHashes {
23    /// Hash algorithm. `0x01` is SHA-256.
24    pub hash_algorithm: u8,
25    /// Digest length. `32` for SHA-256.
26    pub hash_size: u8,
27    /// Layer count, which must equal `HEAD.total_layers`.
28    pub layer_count: u32,
29    /// Root of the Merkle tree over `layer_hashes`.
30    pub merkle_root: [u8; 32],
31    /// Leaf hashes, one per layer.
32    pub layer_hashes: Vec<[u8; 32]>,
33}
34
35impl LayerHashes {
36    /// Parse the chunk payload.
37    pub fn parse(payload: &[u8]) -> Result<LayerHashes> {
38        let mut r = Reader::checked(payload, Check::LhasFrame);
39        let hash_algorithm = r.u8()?;
40        let hash_size = r.u8()?;
41        let layer_count = r.u32()?;
42        let merkle_root = r.array::<32>()?;
43        if hash_algorithm != HASH_ALGORITHM_SHA256 || hash_size != HASH_SIZE_SHA256 {
44            return Err(Error::new(
45                Check::LhasHashAlgorithm,
46                format!(
47                    "hash_algorithm {hash_algorithm:#04x} with hash_size {hash_size} is not SHA-256"
48                ),
49            ));
50        }
51        let table_len = (layer_count as usize)
52            .checked_mul(HASH_SIZE_SHA256 as usize)
53            .ok_or_else(|| Error::new(Check::LhasFrame, "the leaf table length overflows"))?;
54        if table_len > r.remaining() {
55            return Err(Error::new(
56                Check::LhasFrame,
57                format!(
58                    "{layer_count} leaf hashes need {table_len} bytes, {} remain",
59                    r.remaining()
60                ),
61            ));
62        }
63        let mut layer_hashes = Vec::with_capacity(layer_count as usize);
64        for _ in 0..layer_count {
65            layer_hashes.push(r.array::<32>()?);
66        }
67        Ok(LayerHashes {
68            hash_algorithm,
69            hash_size,
70            layer_count,
71            merkle_root,
72            layer_hashes,
73        })
74    }
75
76    /// Serialize the chunk payload.
77    pub fn to_bytes(&self) -> Vec<u8> {
78        let mut w = Writer::with_capacity(LHAS_HEADER_LEN + self.layer_hashes.len() * 32);
79        w.u8(self.hash_algorithm);
80        w.u8(self.hash_size);
81        w.u32(self.layer_count);
82        w.bytes(&self.merkle_root);
83        for leaf in &self.layer_hashes {
84            w.bytes(leaf);
85        }
86        w.into_vec()
87    }
88}
89
90/// The leaf hash of one layer: `SHA-256(0x00 || data)`.
91///
92/// An empty layer hashes the single `0x00` prefix byte.
93pub fn leaf_hash(data: &[u8]) -> [u8; 32] {
94    let mut hasher = Sha256::new();
95    hasher.update([0x00u8]);
96    hasher.update(data);
97    hasher.finalize().into()
98}
99
100/// An internal node: `SHA-256(0x01 || left || right)`.
101fn internal_hash(left: &[u8; 32], right: &[u8; 32]) -> [u8; 32] {
102    let mut hasher = Sha256::new();
103    hasher.update([0x01u8]);
104    hasher.update(left);
105    hasher.update(right);
106    hasher.finalize().into()
107}
108
109/// The empty internal-node hash, `SHA-256(0x01)`.
110///
111/// A conforming file always has `total_layers > 0`, so the zero-leaf root is
112/// never stored; this value is chosen because it cannot be mistaken for the
113/// digest of a real node's contents.
114fn empty_internal_hash() -> [u8; 32] {
115    let mut hasher = Sha256::new();
116    hasher.update([0x01u8]);
117    hasher.finalize().into()
118}
119
120/// The Merkle root over `leaves`, promoting odd nodes at each level.
121pub fn merkle_root(leaves: &[[u8; 32]]) -> [u8; 32] {
122    if leaves.is_empty() {
123        return empty_internal_hash();
124    }
125    let mut level = leaves.to_vec();
126    while level.len() > 1 {
127        let mut next = Vec::with_capacity(level.len().div_ceil(2));
128        let mut i = 0;
129        while i + 1 < level.len() {
130            next.push(internal_hash(&level[i], &level[i + 1]));
131            i += 2;
132        }
133        if i < level.len() {
134            // An odd node is promoted unchanged, never duplicated.
135            next.push(level[i]);
136        }
137        level = next;
138    }
139    level.into_iter().next().unwrap_or_else(empty_internal_hash)
140}
141
142/// Whether level `n` leaves with `index` carries a sibling, mirroring the level
143/// walk of [`merkle_path`].
144fn sibling_flags(mut n: usize, mut index: usize) -> Vec<bool> {
145    let mut flags = Vec::new();
146    while n > 1 {
147        flags.push(index % 2 == 1 || index + 1 < n);
148        index >>= 1;
149        n = n.div_ceil(2);
150    }
151    flags
152}
153
154/// The number of siblings on the path, without materializing the flags.
155fn sibling_count(mut n: usize, mut index: usize) -> usize {
156    let mut count = 0;
157    while n > 1 {
158        if index % 2 == 1 || index + 1 < n {
159            count += 1;
160        }
161        index >>= 1;
162        n = n.div_ceil(2);
163    }
164    count
165}
166
167/// Fold `leaf` along `flags`, consuming one sibling per set flag.
168fn fold_flags(
169    leaf: &[u8; 32],
170    path: &[[u8; 32]],
171    mut index: usize,
172    flags: &[bool],
173) -> Option<[u8; 32]> {
174    let mut acc = *leaf;
175    let mut used = 0;
176    for &has_sibling in flags {
177        if has_sibling {
178            let sibling = path.get(used)?;
179            acc = if index & 1 == 1 {
180                internal_hash(sibling, &acc)
181            } else {
182                internal_hash(&acc, sibling)
183            };
184            used += 1;
185        }
186        index >>= 1;
187    }
188    Some(acc)
189}
190
191/// The sibling hashes on the path from `index` to the root.
192///
193/// At most `ceil(log2(layer_count))` of them, every one recoverable from the
194/// leaf table, which is what makes single-layer verification bounded.
195pub fn merkle_path(leaves: &[[u8; 32]], index: usize) -> Vec<[u8; 32]> {
196    let mut path = Vec::new();
197    if index >= leaves.len() {
198        return path;
199    }
200    let mut level = leaves.to_vec();
201    let mut i = index;
202    while level.len() > 1 {
203        if i % 2 == 1 {
204            if let Some(left) = level.get(i - 1) {
205                path.push(*left);
206            }
207        } else if let Some(right) = level.get(i + 1) {
208            path.push(*right);
209        }
210        let mut next = Vec::with_capacity(level.len().div_ceil(2));
211        let mut k = 0;
212        while k + 1 < level.len() {
213            next.push(internal_hash(&level[k], &level[k + 1]));
214            k += 2;
215        }
216        if k < level.len() {
217            next.push(level[k]);
218        }
219        level = next;
220        i >>= 1;
221    }
222    path
223}
224
225/// Fold `leaf` up `path` and report whether the result is `root`.
226pub fn verify_leaf(leaf: &[u8; 32], path: &[[u8; 32]], index: usize, root: &[u8; 32]) -> bool {
227    // The common case: no level promoted this node, so every path element is a
228    // sibling and the node's side is the running index's low bit.
229    let mut acc = *leaf;
230    let mut i = index;
231    for sibling in path {
232        acc = if i & 1 == 1 {
233            internal_hash(sibling, &acc)
234        } else {
235            internal_hash(&acc, sibling)
236        };
237        i >>= 1;
238    }
239    if &acc == root {
240        return true;
241    }
242
243    // A promotion leaves a hole in the path, so the fast fold cannot align it.
244    // A promoted node was the last of an odd-sized level, which caps the tree at
245    // `2*index + 2` leaves; the path length is monotone in the leaf count, so
246    // find the smallest count that produces this path and fold with its levels.
247    let target = path.len();
248    let mut lo = index.saturating_add(1);
249    let mut hi = index.saturating_mul(2).saturating_add(2);
250    if sibling_count(lo, index) > target {
251        return false;
252    }
253    while lo < hi {
254        let mid = lo + (hi - lo) / 2;
255        if sibling_count(mid, index) < target {
256            lo = mid + 1;
257        } else {
258            hi = mid;
259        }
260    }
261    if sibling_count(lo, index) != target {
262        return false;
263    }
264    fold_flags(leaf, path, index, &sibling_flags(lo, index)) == Some(*root)
265}
266
267#[cfg(test)]
268mod tests {
269    use super::*;
270
271    /// SHA-256 of the concatenation of `parts`, spelled out so the expected
272    /// roots do not go through the code under test.
273    fn sha(parts: &[&[u8]]) -> [u8; 32] {
274        let mut hasher = Sha256::new();
275        for part in parts {
276            hasher.update(part);
277        }
278        hasher.finalize().into()
279    }
280
281    fn leaves(n: usize) -> Vec<[u8; 32]> {
282        (0..n).map(|i| leaf_hash(&[i as u8; 4])).collect()
283    }
284
285    #[test]
286    fn leaf_hash_prefixes_the_data_with_zero() {
287        assert_eq!(leaf_hash(b"abc"), sha(&[&[0x00], b"abc"]));
288        assert_eq!(leaf_hash(b""), sha(&[&[0x00]]));
289        assert_ne!(leaf_hash(b"abc"), sha(&[b"abc"]));
290    }
291
292    #[test]
293    fn merkle_root_handles_one_two_three_and_five_leaves() {
294        let l = leaves(5);
295        let node = |left: &[u8; 32], right: &[u8; 32]| sha(&[&[0x01], left, right]);
296
297        assert_eq!(merkle_root(&l[..1]), l[0]);
298        assert_eq!(merkle_root(&l[..2]), node(&l[0], &l[1]));
299
300        // Three leaves: the third is promoted, never duplicated.
301        let promoted = node(&l[0], &l[1]);
302        assert_eq!(merkle_root(&l[..3]), node(&promoted, &l[2]));
303        let duplicated = node(&promoted, &node(&l[2], &l[2]));
304        assert_ne!(merkle_root(&l[..3]), duplicated);
305
306        // Five leaves: 5 -> 3 -> 2 -> 1, promoting the third then the fifth.
307        let top = node(&node(&l[0], &l[1]), &node(&l[2], &l[3]));
308        assert_eq!(merkle_root(&l[..5]), node(&top, &l[4]));
309    }
310
311    #[test]
312    fn merkle_path_verifies_every_leaf_including_promoted_ones() {
313        let l = leaves(5);
314        let root = merkle_root(&l);
315        for (i, leaf) in l.iter().enumerate() {
316            let path = merkle_path(&l, i);
317            assert!(verify_leaf(leaf, &path, i, &root), "leaf {i} should verify");
318            let mut flipped = *leaf;
319            flipped[0] ^= 0x01;
320            assert!(
321                !verify_leaf(&flipped, &path, i, &root),
322                "flipped leaf {i} must not verify"
323            );
324        }
325
326        // A three-leaf tree promotes index 2.
327        let l3 = leaves(3);
328        let root3 = merkle_root(&l3);
329        let path = merkle_path(&l3, 2);
330        assert!(verify_leaf(&l3[2], &path, 2, &root3));
331        assert!(merkle_path(&l3, 3).is_empty());
332    }
333
334    #[test]
335    fn lhas_round_trips_and_rejects_bad_payloads() {
336        let hashes = LayerHashes {
337            hash_algorithm: HASH_ALGORITHM_SHA256,
338            hash_size: HASH_SIZE_SHA256,
339            layer_count: 3,
340            merkle_root: [7u8; 32],
341            layer_hashes: vec![[1u8; 32], [2u8; 32], [3u8; 32]],
342        };
343        let bytes = hashes.to_bytes();
344        assert_eq!(bytes.len(), LHAS_HEADER_LEN + 3 * 32);
345        assert_eq!(LayerHashes::parse(&bytes).unwrap(), hashes);
346
347        assert_eq!(
348            LayerHashes::parse(&bytes[..LHAS_HEADER_LEN - 1])
349                .unwrap_err()
350                .check(),
351            Check::LhasFrame
352        );
353
354        let mut wrong_algorithm = bytes.clone();
355        wrong_algorithm[0] = 2;
356        assert_eq!(
357            LayerHashes::parse(&wrong_algorithm).unwrap_err().check(),
358            Check::LhasHashAlgorithm
359        );
360
361        let mut short = bytes.clone();
362        short[2..6].copy_from_slice(&9u32.to_le_bytes());
363        assert_eq!(
364            LayerHashes::parse(&short).unwrap_err().check(),
365            Check::LhasFrame
366        );
367    }
368}