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 while level.len() > 1 {
59 let mut next = Vec::with_capacity(level.len().div_ceil(2));
60 for pair in level.chunks(2) {
61 let left = &pair[0];
62 if pair.len() == 1 {
63 next.push(left.clone());
64 continue;
65 }
66 let right = &pair[1];
67 for index in left.leaves.clone() {
68 witnesses[index]
69 .steps
70 .push(ChainKeyBatchWitnessStepV1::RightSibling(right.hash));
71 }
72 for index in right.leaves.clone() {
73 witnesses[index]
74 .steps
75 .push(ChainKeyBatchWitnessStepV1::LeftSibling(left.hash));
76 }
77 next.push(Node {
78 hash: node_hash(left.hash, right.hash),
79 leaves: left.leaves.start..right.leaves.end,
80 });
81 }
82 level = next;
83 }
84 Ok((level[0].hash, witnesses))
85}
86
87#[derive(Clone)]
88struct Node {
89 hash: [u8; 32],
90 leaves: Range<usize>,
91}
92
93#[cfg(feature = "token-verification")]
96pub(crate) fn witness_root(leaf: [u8; 32], witness: &ChainKeyBatchWitnessV1) -> [u8; 32] {
97 witness.steps.iter().fold(leaf, |current, step| match step {
98 ChainKeyBatchWitnessStepV1::LeftSibling(hash) => node_hash(*hash, current),
99 ChainKeyBatchWitnessStepV1::RightSibling(hash) => node_hash(current, *hash),
100 })
101}
102
103fn node_hash(left: [u8; 32], right: [u8; 32]) -> [u8; 32] {
104 let mut hasher = Sha256::new();
105 hasher.update([1]);
106 hasher.update(left);
107 hasher.update(right);
108 hasher.finalize().into()
109}