jam_std_common/crypto/
hashing.rs

1use codec::{Encode, Output};
2use jam_types::Hash;
3use tiny_keccak::{Hasher, Keccak};
4
5pub fn keccak(data: &[u8]) -> Hash {
6	let mut hasher = Keccak::v256();
7	hasher.update(data);
8	let mut output = [0u8; 32];
9	hasher.finalize(&mut output);
10	output
11}
12
13/// Compute Keccak hash of the concatenation of byte slices.
14pub fn keccak_concat<I, T>(items: I) -> Hash
15where
16	I: IntoIterator<Item = T>,
17	T: AsRef<[u8]>,
18{
19	let mut hasher = Keccak::v256();
20	for item in items.into_iter() {
21		hasher.update(item.as_ref());
22	}
23	let mut output = [0u8; 32];
24	hasher.finalize(&mut output);
25	output
26}
27
28pub fn hash_raw(data: &[u8]) -> Hash {
29	let mut hasher = Blake2bHasher::new();
30	hasher.update(data);
31	hasher.into_hash()
32}
33
34/// Compute the hash of the concatenation of byte slices.
35pub fn hash_raw_concat<I, T>(items: I) -> Hash
36where
37	I: IntoIterator<Item = T>,
38	T: AsRef<[u8]>,
39{
40	let mut hasher = Blake2bHasher::new();
41	for item in items.into_iter() {
42		hasher.update(item.as_ref());
43	}
44	hasher.into_hash()
45}
46
47pub fn hash_encoded(what: &impl Encode) -> Hash {
48	let mut hasher = Blake2bHasher::new();
49	what.encode_to(&mut hasher);
50	hasher.into_hash()
51}
52
53struct Blake2bHasher {
54	state: blake2b_simd::State,
55}
56
57impl Blake2bHasher {
58	fn new() -> Self {
59		let state = blake2b_simd::Params::new().hash_length(32).to_state();
60		Self { state }
61	}
62
63	fn update(&mut self, data: &[u8]) {
64		self.state.update(data);
65	}
66
67	fn into_hash(self) -> Hash {
68		self.state.finalize().as_bytes().try_into().expect("Hash length set to 32")
69	}
70}
71
72impl Output for Blake2bHasher {
73	fn write(&mut self, bytes: &[u8]) {
74		self.update(bytes);
75	}
76}
77
78/// A wrapper for [`Input`](codec::Input) that hashes the bytes as they are read from the
79/// underlying input.
80pub struct HashedInput<'a, I: codec::Input> {
81	inner: &'a mut I,
82	hasher: Blake2bHasher,
83}
84
85impl<'a, I: codec::Input> HashedInput<'a, I> {
86	/// Create new wrapper from the supplied underlying input.
87	pub fn new(inner: &'a mut I) -> Self {
88		Self { inner, hasher: Blake2bHasher::new() }
89	}
90
91	/// Consume the wrapper returning the hash of all successfully read bytes.
92	pub fn into_hash(self) -> Hash {
93		self.hasher.into_hash()
94	}
95}
96
97impl<I: codec::Input> codec::Input for HashedInput<'_, I> {
98	fn remaining_len(&mut self) -> Result<Option<usize>, codec::Error> {
99		self.inner.remaining_len()
100	}
101
102	fn read(&mut self, buf: &mut [u8]) -> Result<(), codec::Error> {
103		self.inner.read(buf)?;
104		self.hasher.update(buf);
105		Ok(())
106	}
107
108	fn read_byte(&mut self) -> Result<u8, codec::Error> {
109		self.inner.read_byte().inspect(|byte| self.hasher.update(&[*byte]))
110	}
111
112	fn ascend_ref(&mut self) {
113		self.inner.ascend_ref()
114	}
115
116	fn descend_ref(&mut self) -> Result<(), codec::Error> {
117		self.inner.descend_ref()
118	}
119
120	fn on_before_alloc_mem(&mut self, size: usize) -> Result<(), codec::Error> {
121		self.inner.on_before_alloc_mem(size)
122	}
123}
124
125#[cfg(test)]
126mod tests {
127	use super::*;
128	use codec::Decode;
129
130	#[derive(Encode, Decode, PartialEq, Eq, Debug)]
131	struct Dummy {
132		x: Vec<Dummy>,
133		y: u8,
134		z: String,
135	}
136
137	#[test]
138	fn hashed_input_works() {
139		let dummy = Dummy {
140			x: vec![Dummy { x: Vec::new(), y: 123, z: "Hello".into() }],
141			y: 133,
142			z: "world".into(),
143		};
144		let bytes = dummy.encode();
145		let expected_hash = hash_raw(&bytes[..]);
146		let mut input = &bytes[..];
147		let mut hashed_input = HashedInput::new(&mut input);
148		let actual = Dummy::decode(&mut hashed_input).unwrap();
149		assert_eq!(dummy, actual);
150		let hash = hashed_input.into_hash();
151		assert_eq!(expected_hash, hash);
152	}
153
154	#[test]
155	fn hash_encoded_works() {
156		let dummy = Dummy {
157			x: vec![Dummy { x: Vec::new(), y: 123, z: "Hello".into() }],
158			y: 133,
159			z: "world".into(),
160		};
161		assert_eq!(hash_raw(&dummy.encode()), hash_encoded(&dummy));
162		assert_eq!(dummy.using_encoded(hash_raw), hash_encoded(&dummy));
163	}
164
165	#[test]
166	fn hash_raw_concat_works() {
167		assert_eq!(
168			hash_raw(&[b"x".as_ref(), b"y", b"z"].concat()),
169			hash_raw_concat([b"x", b"y", b"z"])
170		);
171	}
172
173	#[test]
174	fn keccak_concat_works() {
175		assert_eq!(
176			keccak(&[b"x".as_ref(), b"y", b"z"].concat()),
177			keccak_concat([b"x", b"y", b"z"])
178		);
179	}
180}