Skip to content

Commit

Permalink
Implement the reconstruct function and enhance a bit the lookup funct…
Browse files Browse the repository at this point in the history
…ion which needs to be rewritten
  • Loading branch information
neithanmo committed Jul 8, 2024
1 parent 7a38bc3 commit ded653d
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 6 deletions.
49 changes: 45 additions & 4 deletions app/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions app/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ crate-type = ["staticlib"]
minicbor = { version = "0.24.2", default-features = false }
no-std-compat = { version = "0.4.1" }
bls_signature = { package = "ic-verify-bls-signature", version = "0.3.0", default-features = false }
sha2 = { version = "0.10.8", default-features = false }

[dev-dependencies]
no-std-compat = { version = "0.4.1", features = ["std"] }
Expand Down
75 changes: 74 additions & 1 deletion app/rust/src/parser/hash_tree.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use minicbor::{decode::Error, Decode, Decoder};
use sha2::Digest;

use super::{label::Label, raw_value::RawValue};
const MAX_TREE_DEPTH: usize = 32;
Expand All @@ -18,6 +19,10 @@ impl<'a> HashTree<'a> {
HashTree::decode(&mut d, &mut ())
}

pub fn reconstruct(&self) -> Result<[u8; 32], Error> {
reconstruct(self)
}

#[cfg(test)]
pub fn parse_and_print_hash_tree(raw_tree: &RawValue, indent: usize) -> Result<(), Error> {
use std::println;
Expand Down Expand Up @@ -132,8 +137,62 @@ pub fn lookup_path<'a>(label: &Label<'a>, tree: RawValue<'a>) -> Result<LookupRe
inner_lookup(label, tree, 1)
}

fn reconstruct(tree: &HashTree) -> Result<[u8; 32], Error> {
let hash = match tree {
HashTree::Empty => hash_with_domain_sep("ic-hashtree-empty", &[]),
HashTree::Fork(left, right) => {
let left = HashTree::parse(*left)?;
let right = HashTree::parse(*right)?;

let left_hash = reconstruct(&left)?;
let right_hash = reconstruct(&right)?;
let mut concat = [0; 64];
concat[..32].copy_from_slice(&left_hash);
concat[32..].copy_from_slice(&right_hash);
hash_with_domain_sep("ic-hashtree-fork", &concat)
}
HashTree::Labeled(label, subtree) => {
let subtree = HashTree::parse(*subtree)?;
let subtree_hash = reconstruct(&subtree)?;
// domain.len() + label_max_len + hash_len
let mut concat = [0; Label::MAX_LEN + 32];
let label_len = label.as_bytes().len();
concat[..label_len].copy_from_slice(label.as_bytes());
concat[label_len..label_len + 32].copy_from_slice(&subtree_hash);
hash_with_domain_sep("ic-hashtree-labeled", &concat[..label_len + 32])
}
HashTree::Leaf(v) => hash_with_domain_sep("ic-hashtree-leaf", v.bytes()),
HashTree::Pruned(h) => {
// Skip the CBOR type identifier and length information
let hash_start = if h.len() == 34 && h.bytes()[0] == 0x58 && h.bytes()[1] == 0x20 {
2
} else {
0
};

if h.len() - hash_start != 32 {
// FIXME: Do not panic
panic!("Pruned node hash must be 32 bytes");
}

let mut result = [0; 32];
result.copy_from_slice(&h.bytes()[hash_start..]);
result
}
};
Ok(hash)
}

pub fn hash_with_domain_sep(domain: &str, data: &[u8]) -> [u8; 32] {
let mut hasher = sha2::Sha256::new();
hasher.update([domain.len() as u8]);
hasher.update(domain.as_bytes());
hasher.update(data);
hasher.finalize().into()
}

#[cfg(test)]
mod tests {
mod hash_tree_tests {
use crate::parser::certificate::Certificate;

use super::*;
Expand Down Expand Up @@ -163,13 +222,27 @@ mod tests {
);
std::println!("Successfully found and matched 'time' leaf data");
}
// FIXME: Do not panic
_ => panic!("Expected Leaf, found {:?}", value),
}
}
// FIXME: Do not panic
LookupResult::Absent => panic!("'time' path not found in the tree"),
LookupResult::Unknown => {
// FIXME: Do not panic
panic!("Unable to determine if 'time' path exists due to pruned nodes")
}
}
}

#[test]
fn test_reconstruct() {
let data = hex::decode(DATA).unwrap();
let cert = Certificate::parse(&data).unwrap();

let tree = HashTree::parse(*cert.tree()).unwrap();
let hash = tree.reconstruct().unwrap();
let hash_str = hex::encode(hash);
std::println!("Reconstructed hash: {}", hash_str);
}
}
5 changes: 4 additions & 1 deletion app/rust/src/parser/label.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ pub enum Label<'a> {
}

impl<'a> Label<'a> {
fn as_bytes(&self) -> &'a [u8] {
// TODO: Check if docs tell something about
// max label length
pub const MAX_LEN: usize = 32;
pub fn as_bytes(&self) -> &'a [u8] {
match self {
Label::Blob(b) => b,
Label::String(s) => s.as_bytes(),
Expand Down

0 comments on commit ded653d

Please sign in to comment.