ic_auth/
chain_key_batch.rs1use ic_auth_protocol_types::{ChainKeyBatchWitnessStepV1, ChainKeyBatchWitnessV1};
8use sha2::{Digest, Sha256};
9use std::ops::Range;
10use thiserror::Error;
11
12#[derive(Debug, Error, Eq, PartialEq)]
14pub enum ChainKeyBatchError {
15 #[error("chain-key batch requires at least one leaf")]
17 EmptyBatch,
18 #[error("chain-key batch has {found} leaves, exceeding the limit {max}")]
20 TooManyLeaves { found: usize, max: usize },
21}
22
23pub fn merkle_root_and_witnesses(
36 leaf_hashes: &[[u8; 32]],
37 max_leaves: usize,
38) -> Result<([u8; 32], Vec<ChainKeyBatchWitnessV1>), ChainKeyBatchError> {
39 if leaf_hashes.is_empty() {
40 return Err(ChainKeyBatchError::EmptyBatch);
41 }
42 if leaf_hashes.len() > max_leaves {
43 return Err(ChainKeyBatchError::TooManyLeaves {
44 found: leaf_hashes.len(),
45 max: max_leaves,
46 });
47 }
48
49 let mut witnesses = vec![ChainKeyBatchWitnessV1 { steps: Vec::new() }; leaf_hashes.len()];
50 let mut level: Vec<_> = leaf_hashes
51 .iter()
52 .enumerate()
53 .map(|(index, hash)| Node {
54 hash: *hash,
55 leaves: index..index + 1,
56 })
57 .collect();
58 let mut next = Vec::with_capacity(level.len().div_ceil(2));
59 while level.len() > 1 {
60 next.clear();
61 for pair in level.chunks(2) {
62 let left = &pair[0];
63 if pair.len() == 1 {
64 next.push(left.clone());
65 continue;
66 }
67 let right = &pair[1];
68 for index in left.leaves.clone() {
69 witnesses[index]
70 .steps
71 .push(ChainKeyBatchWitnessStepV1::RightSibling(right.hash));
72 }
73 for index in right.leaves.clone() {
74 witnesses[index]
75 .steps
76 .push(ChainKeyBatchWitnessStepV1::LeftSibling(left.hash));
77 }
78 next.push(Node {
79 hash: node_hash(left.hash, right.hash),
80 leaves: left.leaves.start..right.leaves.end,
81 });
82 }
83 std::mem::swap(&mut level, &mut next);
84 }
85 Ok((level[0].hash, witnesses))
86}
87
88#[derive(Clone)]
89struct Node {
90 hash: [u8; 32],
91 leaves: Range<usize>,
92}
93
94#[cfg(feature = "chain-key-verification")]
97pub(crate) fn witness_root(leaf: [u8; 32], witness: &ChainKeyBatchWitnessV1) -> [u8; 32] {
98 witness.steps.iter().fold(leaf, |current, step| match step {
99 ChainKeyBatchWitnessStepV1::LeftSibling(hash) => node_hash(*hash, current),
100 ChainKeyBatchWitnessStepV1::RightSibling(hash) => node_hash(current, *hash),
101 })
102}
103
104fn node_hash(left: [u8; 32], right: [u8; 32]) -> [u8; 32] {
105 let mut hasher = Sha256::new();
106 hasher.update([1]);
107 hasher.update(left);
108 hasher.update(right);
109 hasher.finalize().into()
110}