From ded653d2a7bd6ce573f9b091760e3e9c1c343532 Mon Sep 17 00:00:00 2001 From: neithanmo Date: Sun, 7 Jul 2024 23:14:48 -0600 Subject: [PATCH] Implement the reconstruct function and enhance a bit the lookup function which needs to be rewritten --- app/rust/Cargo.lock | 49 +++++++++++++++++++-- app/rust/Cargo.toml | 1 + app/rust/src/parser/hash_tree.rs | 75 +++++++++++++++++++++++++++++++- app/rust/src/parser/label.rs | 5 ++- 4 files changed, 124 insertions(+), 6 deletions(-) diff --git a/app/rust/Cargo.lock b/app/rust/Cargo.lock index 0a60a093..ba42f681 100644 --- a/app/rust/Cargo.lock +++ b/app/rust/Cargo.lock @@ -11,6 +11,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -26,6 +35,16 @@ dependencies = [ "libc", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "digest" version = "0.9.0" @@ -35,6 +54,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", +] + [[package]] name = "ff" version = "0.13.0" @@ -81,7 +110,7 @@ dependencies = [ "ic_bls12_381", "pairing", "rand", - "sha2", + "sha2 0.9.9", ] [[package]] @@ -90,7 +119,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "314f73709ab4ba7eb5f75d3446a08c4bb4db10240ea2d0bc113d17d70290a43e" dependencies = [ - "digest", + "digest 0.9.0", "ff", "group", "pairing", @@ -154,6 +183,7 @@ dependencies = [ "ic-verify-bls-signature", "minicbor", "no-std-compat", + "sha2 0.10.8", ] [[package]] @@ -162,13 +192,24 @@ version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest", + "digest 0.9.0", "opaque-debug", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + [[package]] name = "subtle" version = "2.5.0" diff --git a/app/rust/Cargo.toml b/app/rust/Cargo.toml index 2d2f79df..07367828 100644 --- a/app/rust/Cargo.toml +++ b/app/rust/Cargo.toml @@ -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"] } diff --git a/app/rust/src/parser/hash_tree.rs b/app/rust/src/parser/hash_tree.rs index 77b772d6..107bb092 100644 --- a/app/rust/src/parser/hash_tree.rs +++ b/app/rust/src/parser/hash_tree.rs @@ -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; @@ -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; @@ -132,8 +137,62 @@ pub fn lookup_path<'a>(label: &Label<'a>, tree: RawValue<'a>) -> Result 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::*; @@ -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); + } } diff --git a/app/rust/src/parser/label.rs b/app/rust/src/parser/label.rs index dbef2585..127d728d 100644 --- a/app/rust/src/parser/label.rs +++ b/app/rust/src/parser/label.rs @@ -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(),