1use crate::entry::MerkleEntry;
9use serde::{Deserialize, Serialize};
10use sha2::{Digest, Sha256};
11
12pub type Hash = [u8; 32];
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(rename_all = "snake_case")]
18pub enum Side {
19 Left,
21 Right,
23}
24
25#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
27pub struct ProofStep {
28 pub sibling: Hash,
30 pub side: Side,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct InclusionProof {
37 pub sequence: u64,
39 pub steps: Vec<ProofStep>,
41}
42
43#[derive(Debug, Default, Clone)]
45pub struct MerkleTree {
46 entries: Vec<MerkleEntry>,
48 leaf_hashes: Vec<Hash>,
50 levels: Vec<Vec<Hash>>,
56 cached_root: Hash,
59}
60
61#[derive(Debug, thiserror::Error)]
63pub enum MerkleError {
64 #[error("sequence {0} out of range (have {1} entries)")]
66 OutOfRange(u64, usize),
67 #[error("consistency proof failed: expected {expected:?}, got {actual:?}")]
69 ConsistencyFailed {
70 expected: Hash,
72 actual: Hash,
74 },
75 #[error("inclusion proof failed for sequence {0}")]
77 InclusionFailed(u64),
78}
79
80fn hash_leaf(entry_hash: Hash) -> Hash {
81 let mut h = Sha256::new();
82 h.update([0x01]);
83 h.update(entry_hash);
84 let r = h.finalize();
85 let mut out = [0u8; 32];
86 out.copy_from_slice(&r);
87 out
88}
89
90fn hash_internal(left: Hash, right: Hash) -> Hash {
91 let mut h = Sha256::new();
92 h.update([0x02]);
93 h.update(left);
94 h.update(right);
95 let r = h.finalize();
96 let mut out = [0u8; 32];
97 out.copy_from_slice(&r);
98 out
99}
100
101fn largest_pow2_strictly_less_than(n: usize) -> usize {
106 if n <= 1 {
107 return 0;
108 }
109 let mut k = 1usize;
110 while k * 2 < n {
111 k *= 2;
112 }
113 k
114}
115
116impl MerkleTree {
117 pub fn new() -> Self {
119 Self::default()
120 }
121
122 pub fn append(&mut self, mut entry: MerkleEntry) -> u64 {
128 if entry.sequence == 0 && !self.entries.is_empty() {
129 entry.sequence = self.entries.len() as u64;
130 }
131 let hash = entry.entry_hash();
132 let leaf = hash_leaf(hash);
133 self.leaf_hashes.push(leaf);
134 self.entries.push(entry);
135 self.append_level(leaf);
136 (self.entries.len() - 1) as u64
137 }
138
139 fn append_level(&mut self, leaf: Hash) {
147 if self.levels.is_empty() {
148 self.levels.push(vec![leaf]);
150 self.cached_root = leaf;
151 return;
152 }
153 self.levels[0].push(leaf);
154 let mut j = 0usize;
155 loop {
156 let n = self.levels[j].len();
157 let up = if n % 2 == 0 {
159 let lvl = &self.levels[j];
160 hash_internal(lvl[n - 2], lvl[n - 1])
161 } else {
162 self.levels[j][n - 1]
163 };
164 let target = n.div_ceil(2);
165 j += 1;
166 if j == self.levels.len() {
167 self.levels.push(vec![up]);
168 } else {
169 let above = &mut self.levels[j];
170 if above.len() == target {
171 *above.last_mut().expect("non-empty level") = up;
173 } else {
174 above.push(up);
177 }
178 }
179 if self.levels[j].len() == 1 {
180 self.cached_root = self.levels[j][0];
182 return;
183 }
184 }
185 }
186
187 #[cfg(test)]
193 fn rebuild_levels(&mut self) {
194 if self.leaf_hashes.is_empty() {
195 self.levels.clear();
196 self.cached_root = [0u8; 32];
197 return;
198 }
199 self.levels.clear();
200 self.levels.push(self.leaf_hashes.clone());
201 while self.levels.last().map_or(0, |l| l.len()) > 1 {
202 let current = self.levels.last().unwrap();
203 let mut next = Vec::with_capacity(current.len() / 2 + 1);
204 let mut iter = current.iter();
205 loop {
206 match (iter.next(), iter.next()) {
207 (Some(l), Some(r)) => next.push(hash_internal(*l, *r)),
208 (Some(l), None) => next.push(*l),
209 _ => break,
210 }
211 }
212 self.levels.push(next);
213 }
214 self.cached_root = self.levels.last().unwrap()[0];
215 }
216
217 pub fn len(&self) -> usize {
219 self.entries.len()
220 }
221
222 pub fn is_empty(&self) -> bool {
224 self.entries.is_empty()
225 }
226
227 pub fn root(&self) -> Hash {
231 self.cached_root
232 }
233
234 pub fn entry(&self, sequence: u64) -> Result<&MerkleEntry, MerkleError> {
236 self.entries
237 .get(sequence as usize)
238 .ok_or(MerkleError::OutOfRange(sequence, self.entries.len()))
239 }
240
241 pub fn inclusion_proof(&self, sequence: u64) -> Result<InclusionProof, MerkleError> {
248 if sequence as usize >= self.leaf_hashes.len() {
249 return Err(MerkleError::OutOfRange(sequence, self.entries.len()));
250 }
251 if self.levels.is_empty() {
256 return Err(MerkleError::OutOfRange(sequence, self.entries.len()));
257 }
258 let mut steps = Vec::new();
259 let mut idx = sequence as usize;
260 for level in 0..self.levels.len().saturating_sub(1) {
261 let current = &self.levels[level];
262 if idx % 2 == 0 {
263 let sibling_idx = idx + 1;
265 if sibling_idx < current.len() {
266 steps.push(ProofStep {
267 sibling: current[sibling_idx],
268 side: Side::Right,
269 });
270 }
271 } else {
272 let sibling_idx = idx - 1;
274 steps.push(ProofStep {
275 sibling: current[sibling_idx],
276 side: Side::Left,
277 });
278 }
279 idx /= 2;
280 }
281 Ok(InclusionProof { sequence, steps })
282 }
283
284 pub fn verify_inclusion(
286 entry: &MerkleEntry,
287 proof: &InclusionProof,
288 root: Hash,
289 ) -> Result<(), MerkleError> {
290 let mut current = hash_leaf(entry.entry_hash());
291 for step in &proof.steps {
292 current = match step.side {
293 Side::Left => hash_internal(step.sibling, current),
294 Side::Right => hash_internal(current, step.sibling),
295 };
296 }
297 use subtle::ConstantTimeEq;
298 if current.ct_eq(&root).into() {
299 Ok(())
300 } else {
301 Err(MerkleError::InclusionFailed(entry.sequence))
302 }
303 }
304
305 pub fn consistency_proof(&self, old_size: usize) -> Result<Vec<Hash>, MerkleError> {
313 let new_size = self.leaf_hashes.len();
314 if old_size > new_size {
315 return Err(MerkleError::OutOfRange(old_size as u64, new_size));
316 }
317 if old_size == 0 || old_size == new_size {
318 return Ok(Vec::new());
319 }
320 Ok(self.consistency_rec(0, old_size, new_size))
321 }
322
323 fn consistency_rec(&self, start: usize, old_size: usize, new_size: usize) -> Vec<Hash> {
339 if old_size == new_size {
340 return Vec::new();
341 }
342 let k = largest_pow2_strictly_less_than(new_size);
343 if old_size <= k {
344 let mut sub = self.consistency_rec(start, old_size, k);
345 sub.push(self.subtree_root(start + k, new_size - k));
346 sub
347 } else {
348 let mut sub = self.consistency_rec(start + k, old_size - k, new_size - k);
349 let mut result = vec![self.subtree_root(start, k)];
350 result.append(&mut sub);
351 result
352 }
353 }
354
355 fn subtree_root(&self, start: usize, size: usize) -> Hash {
362 debug_assert!(
363 start + size <= self.leaf_hashes.len(),
364 "subtree_root: out of range"
365 );
366 if size == 0 {
367 return [0u8; 32];
368 }
369
370 let mut frontier: Vec<(usize, Hash)> = Vec::new();
374 let mut offset = start;
375 let mut remaining = size;
376 let mut k = 1usize;
377 while k * 2 <= remaining {
379 k *= 2;
380 }
381 while remaining > 0 {
382 if remaining >= k {
383 let hash = self.perfect_subtree_root(offset, k);
384 frontier.push((k, hash));
385 offset += k;
386 remaining -= k;
387 }
388 k /= 2;
389 }
390
391 let mut acc = frontier
394 .last()
395 .expect("non-empty size yields non-empty frontier")
396 .1;
397 for &(_, h) in frontier.iter().rev().skip(1) {
398 acc = hash_internal(h, acc);
399 }
400 acc
401 }
402
403 fn perfect_subtree_root(&self, start: usize, size: usize) -> Hash {
415 debug_assert!(
416 size.is_power_of_two(),
417 "perfect_subtree_root: size must be pow2"
418 );
419 debug_assert!(
420 start % size == 0,
421 "perfect_subtree_root: start must be size-aligned"
422 );
423 if size == 1 {
424 return self.leaf_hashes[start];
425 }
426 let j = size.trailing_zeros() as usize; if let Some(level) = self.levels.get(j) {
428 let idx = start / size;
429 if let Some(&h) = level.get(idx) {
430 return h;
431 }
432 }
433 let mut level: Vec<Hash> = self.leaf_hashes[start..start + size].to_vec();
435 while level.len() > 1 {
436 let mut next = Vec::with_capacity(level.len() / 2);
437 for chunk in level.chunks(2) {
438 next.push(hash_internal(chunk[0], chunk[1]));
439 }
440 level = next;
441 }
442 level[0]
443 }
444
445 fn root_at_size(&self, size: usize) -> Hash {
453 if size == 0 || size > self.leaf_hashes.len() {
454 return [0u8; 32];
455 }
456 self.subtree_root(0, size)
457 }
458
459 pub fn verify_consistency(
476 &self,
477 old_root: Hash,
478 new_root: Hash,
479 old_size: usize,
480 new_size: usize,
481 _proof: &[Hash],
482 ) -> Result<(), MerkleError> {
483 if old_size == 0 {
484 return Ok(());
485 }
486 let current_size = self.leaf_hashes.len();
487 if new_size != current_size {
488 return Err(MerkleError::ConsistencyFailed {
489 expected: new_root,
490 actual: self.root(),
491 });
492 }
493 if old_size > current_size {
494 return Err(MerkleError::OutOfRange(old_size as u64, current_size));
495 }
496
497 let computed_old_root = self.root_at_size(old_size);
498 let computed_new_root = self.root();
499
500 use subtle::ConstantTimeEq;
501 let old_ok: bool = computed_old_root.ct_eq(&old_root).into();
502 let new_ok: bool = computed_new_root.ct_eq(&new_root).into();
503 if old_ok && new_ok {
504 Ok(())
505 } else {
506 Err(MerkleError::ConsistencyFailed {
507 expected: old_root,
508 actual: computed_old_root,
509 })
510 }
511 }
512}
513
514#[cfg(test)]
515mod consistency_tests {
516 use super::*;
517 use crate::entry::{ArtifactType, MerkleEntry};
518
519 fn build_tree(n: usize) -> MerkleTree {
520 let mut tree = MerkleTree::new();
521 for i in 0..n {
522 let entry =
523 MerkleEntry::new(i as u64, ArtifactType::CertificateIssuance, [i as u8; 32]);
524 tree.append(entry);
525 }
526 tree
527 }
528
529 #[test]
530 fn consistency_proof_empty_for_same_size() {
531 let tree = build_tree(8);
532 let proof = tree.consistency_proof(8).unwrap();
533 assert!(proof.is_empty());
534 }
535
536 #[test]
537 fn consistency_proof_empty_for_zero() {
538 let tree = build_tree(8);
539 let proof = tree.consistency_proof(0).unwrap();
540 assert!(proof.is_empty());
541 }
542
543 #[test]
544 fn consistency_proof_rejects_old_larger_than_current() {
545 let tree = build_tree(4);
546 assert!(tree.consistency_proof(8).is_err());
547 }
548
549 #[test]
550 fn consistency_proof_returns_subtree_hashes_for_pow2_old_size() {
551 let tree = build_tree(8);
553 let proof = tree.consistency_proof(4).unwrap();
554 assert_eq!(proof.len(), 1, "expected single entry for (4, 8)");
555 }
556
557 #[test]
558 fn consistency_proof_returns_multiple_entries_for_non_pow2() {
559 let tree = build_tree(5);
561 let proof = tree.consistency_proof(3).unwrap();
562 assert_eq!(proof.len(), 3, "expected 3 entries for (3, 5)");
563 }
564
565 #[test]
566 fn verify_consistency_accepts_valid_pow2_old_size() {
567 let mut tree = build_tree(8);
568 let old_root = tree.root_at_size(4);
569 for i in 8..12 {
571 let entry =
572 MerkleEntry::new(i as u64, ArtifactType::CertificateIssuance, [i as u8; 32]);
573 tree.append(entry);
574 }
575 let new_root = tree.root();
576 let proof = tree.consistency_proof(4).unwrap();
577 tree.verify_consistency(old_root, new_root, 4, 12, &proof)
578 .expect("must verify for valid pow2 old_size");
579 }
580
581 #[test]
582 fn verify_consistency_accepts_valid_non_pow2_old_size() {
583 let mut tree = build_tree(5);
584 let old_root = tree.root_at_size(3);
585 for i in 5..11 {
586 let entry =
587 MerkleEntry::new(i as u64, ArtifactType::CertificateIssuance, [i as u8; 32]);
588 tree.append(entry);
589 }
590 let new_root = tree.root();
591 let proof = tree.consistency_proof(3).unwrap();
592 tree.verify_consistency(old_root, new_root, 3, 11, &proof)
593 .expect("must verify for valid non-pow2 old_size");
594 }
595
596 #[test]
597 fn verify_consistency_detects_tampered_old_root() {
598 let mut tree = build_tree(8);
599 for i in 8..12 {
600 let entry =
601 MerkleEntry::new(i as u64, ArtifactType::CertificateIssuance, [i as u8; 32]);
602 tree.append(entry);
603 }
604 let new_root = tree.root();
605 let proof = tree.consistency_proof(4).unwrap();
606 let bogus_old_root = [0xffu8; 32];
607 let result = tree.verify_consistency(bogus_old_root, new_root, 4, 12, &proof);
608 assert!(matches!(result, Err(MerkleError::ConsistencyFailed { .. })));
609 }
610
611 #[test]
612 fn verify_consistency_detects_tampered_new_root() {
613 let mut tree = build_tree(8);
614 let old_root = tree.root_at_size(4);
615 for i in 8..12 {
616 let entry =
617 MerkleEntry::new(i as u64, ArtifactType::CertificateIssuance, [i as u8; 32]);
618 tree.append(entry);
619 }
620 let proof = tree.consistency_proof(4).unwrap();
621 let bogus_new_root = [0xffu8; 32];
622 let result = tree.verify_consistency(old_root, bogus_new_root, 4, 12, &proof);
623 assert!(matches!(result, Err(MerkleError::ConsistencyFailed { .. })));
624 }
625
626 #[test]
627 fn verify_consistency_accepts_all_sizes_1_to_16() {
628 let mut tree = MerkleTree::new();
631 let mut roots: Vec<Hash> = Vec::new();
632 for i in 0..16u64 {
633 let entry = MerkleEntry::new(i, ArtifactType::CertificateIssuance, [i as u8; 32]);
634 tree.append(entry);
635 roots.push(tree.root());
636 }
637 let final_size = tree.leaf_hashes.len();
638 for old_size in 1..=final_size {
639 let old_root = roots[old_size - 1];
640 let new_root = roots[final_size - 1];
641 let proof = tree.consistency_proof(old_size).unwrap();
642 tree.verify_consistency(old_root, new_root, old_size, final_size, &proof)
643 .unwrap_or_else(|e| {
644 panic!("verify_consistency failed for old_size={old_size}: {e:?}")
645 });
646 }
647 }
648}
649
650#[cfg(test)]
651mod tests {
652 use super::*;
653 use crate::entry::ArtifactType;
654
655 #[test]
656 fn incremental_levels_match_rebuild_at_every_size() {
657 let mut tree = MerkleTree::new();
660 for i in 0..64u64 {
661 tree.append(MerkleEntry::new(
662 i,
663 ArtifactType::CertificateIssuance,
664 [(i as u8).wrapping_mul(7); 32],
665 ));
666 let incremental_levels = tree.levels.clone();
667 let incremental_root = tree.cached_root;
668 tree.rebuild_levels();
669 assert_eq!(
670 incremental_levels, tree.levels,
671 "levels diverge after append {i}"
672 );
673 assert_eq!(incremental_root, tree.cached_root, "root diverges at {i}");
674 }
675 }
676
677 #[test]
678 fn empty_tree_has_zero_root() {
679 let tree = MerkleTree::new();
680 assert_eq!(tree.root(), [0u8; 32]);
681 }
682
683 #[test]
684 fn single_entry_tree() {
685 let mut tree = MerkleTree::new();
686 let entry = MerkleEntry::new(0, ArtifactType::CertificateIssuance, [1u8; 32]);
687 tree.append(entry);
688 let root = tree.root();
689 assert_ne!(root, [0u8; 32]);
690 }
691
692 #[test]
693 fn multiple_entries_produce_different_root() {
694 let mut tree1 = MerkleTree::new();
695 let mut tree2 = MerkleTree::new();
696
697 tree1.append(MerkleEntry::new(
698 0,
699 ArtifactType::CertificateIssuance,
700 [1u8; 32],
701 ));
702 tree1.append(MerkleEntry::new(
703 1,
704 ArtifactType::CertificateIssuance,
705 [2u8; 32],
706 ));
707
708 tree2.append(MerkleEntry::new(
709 0,
710 ArtifactType::CertificateIssuance,
711 [1u8; 32],
712 ));
713 tree2.append(MerkleEntry::new(
714 1,
715 ArtifactType::CertificateIssuance,
716 [3u8; 32],
717 ));
718
719 assert_ne!(tree1.root(), tree2.root());
720 }
721
722 #[test]
723 fn inclusion_proof_round_trip() {
724 let mut tree = MerkleTree::new();
725 let mut entries = Vec::new();
726 for i in 0..5u64 {
727 let e = MerkleEntry::new(i, ArtifactType::CertificateIssuance, [i as u8; 32]);
728 entries.push(e.clone());
729 tree.append(e);
730 }
731 let root = tree.root();
732 for i in 0..5 {
734 let proof = tree.inclusion_proof(i).unwrap();
735 MerkleTree::verify_inclusion(&entries[i as usize], &proof, root)
736 .expect("inclusion proof must verify");
737 }
738 }
739
740 #[test]
741 fn inclusion_proof_negative_case() {
742 let mut tree = MerkleTree::new();
743 let entries: Vec<MerkleEntry> = (0..5u64)
744 .map(|i| MerkleEntry::new(i, ArtifactType::CertificateIssuance, [i as u8; 32]))
745 .collect();
746 for e in &entries {
747 tree.append(e.clone());
748 }
749 let root = tree.root();
750 let wrong_proof = tree.inclusion_proof(2).unwrap();
752 let result = MerkleTree::verify_inclusion(&entries[3], &wrong_proof, root);
753 assert!(matches!(result, Err(MerkleError::InclusionFailed(_))));
754 }
755
756 #[test]
757 fn inclusion_proof_power_of_two_tree() {
758 let mut tree = MerkleTree::new();
760 let entries: Vec<MerkleEntry> = (0..8u64)
761 .map(|i| MerkleEntry::new(i, ArtifactType::CertificateIssuance, [i as u8; 32]))
762 .collect();
763 for e in &entries {
764 tree.append(e.clone());
765 }
766 let root = tree.root();
767 for i in 0..8 {
768 let proof = tree.inclusion_proof(i).unwrap();
769 MerkleTree::verify_inclusion(&entries[i as usize], &proof, root).expect("must verify");
770 }
771 }
772
773 #[test]
774 fn out_of_range_returns_error() {
775 let tree = MerkleTree::new();
776 let result = tree.entry(0);
777 assert!(matches!(result, Err(MerkleError::OutOfRange(_, _))));
778 }
779}
780
781#[cfg(test)]
782mod proptests {
783 use super::*;
784 use crate::entry::ArtifactType;
785 use proptest::prelude::*;
786
787 fn arb_entry(seq: u64) -> MerkleEntry {
788 let mut hash = [0u8; 32];
789 hash[0..8].copy_from_slice(&seq.to_le_bytes());
791 MerkleEntry::new(seq, ArtifactType::CertificateIssuance, hash)
792 }
793
794 proptest! {
797 #[test]
798 fn every_leaf_inclusion_proof_verifies(n in 1u64..100) {
799 let mut tree = MerkleTree::new();
800 let entries: Vec<MerkleEntry> = (0..n).map(arb_entry).collect();
801 for e in &entries {
802 tree.append(e.clone());
803 }
804 let root = tree.root();
805 for seq in 0..n {
806 let proof = tree.inclusion_proof(seq)?;
807 MerkleTree::verify_inclusion(&entries[seq as usize], &proof, root)?;
808 }
809 }
810 }
811
812 proptest! {
814 #[test]
815 fn inclusion_proof_rejects_wrong_entry(n in 2u64..50, i in 0u64..50, j in 0u64..50) {
816 prop_assume!(n > 1 && i < n && j < n && i != j);
817 let mut tree = MerkleTree::new();
818 let entries: Vec<MerkleEntry> = (0..n).map(arb_entry).collect();
819 for e in &entries {
820 tree.append(e.clone());
821 }
822 let root = tree.root();
823 let proof = tree.inclusion_proof(i)?;
824 let result = MerkleTree::verify_inclusion(&entries[j as usize], &proof, root);
825 prop_assert!(matches!(result, Err(MerkleError::InclusionFailed(_))));
826 }
827 }
828
829 proptest! {
832 #[test]
833 fn append_changes_root(n in 0u64..50) {
834 let mut tree = MerkleTree::new();
835 for seq in 0..n {
836 tree.append(arb_entry(seq));
837 }
838 let root_before = tree.root();
839 tree.append(arb_entry(n));
840 let root_after = tree.root();
841 prop_assert_ne!(root_before, root_after);
842 }
843 }
844
845 #[test]
847 fn empty_tree_root_is_zero() {
848 let tree = MerkleTree::new();
849 assert_eq!(tree.root(), [0u8; 32]);
850 }
851}