binius_hash/vision_6/
digest.rs1use binius_field::{BinaryField128bGhash as Ghash, Field};
4use binius_utils::{DeserializeBytes, SerializeBytes};
5use digest::{
6 FixedOutput, FixedOutputReset, HashMarker, OutputSizeUser, Reset, Update, consts::U32,
7 core_api::BlockSizeUser,
8};
9
10use super::{constants::M, permutation::permutation};
11
12pub const RATE_AS_U128: usize = 4;
13pub const RATE_AS_U8: usize = RATE_AS_U128 * std::mem::size_of::<u128>();
14
15const PADDING_START: u8 = 0x80;
16const PADDING_END: u8 = 0x01;
17
18pub const PADDING_BLOCK: [u8; RATE_AS_U8] = {
19 let mut block = [0; RATE_AS_U8];
20 block[0] = PADDING_START;
21 block[RATE_AS_U8 - 1] |= PADDING_END;
22 block
23};
24
25#[inline(always)]
27pub fn fill_padding(data: &mut [u8]) {
28 debug_assert!(!data.is_empty() && data.len() <= RATE_AS_U8);
29
30 data.fill(0);
31 data[0] |= PADDING_START;
32 data[data.len() - 1] |= PADDING_END;
33}
34
35#[derive(Clone)]
37pub struct VisionHasherDigest {
38 state: [Ghash; M],
39 buffer: [u8; RATE_AS_U8],
40 filled_bytes: usize,
41}
42
43impl Default for VisionHasherDigest {
44 fn default() -> Self {
45 Self {
46 state: [Ghash::ZERO; M],
47 buffer: [0; RATE_AS_U8],
48 filled_bytes: 0,
49 }
50 }
51}
52
53impl VisionHasherDigest {
54 pub fn permute(state: &mut [Ghash; M], data: &[u8]) {
55 debug_assert_eq!(data.len(), RATE_AS_U8);
56
57 for i in 0..RATE_AS_U128 {
59 state[i] = Ghash::deserialize(&data[i * 16..]).expect("data len checked");
60 }
61
62 permutation(state);
63 }
64
65 fn finalize(&mut self, out: &mut digest::Output<Self>) {
66 if self.filled_bytes != 0 {
67 fill_padding(&mut self.buffer[self.filled_bytes..]);
68 Self::permute(&mut self.state, &self.buffer);
69 } else {
70 Self::permute(&mut self.state, &PADDING_BLOCK);
71 }
72
73 let (state0, state1) = out.as_mut_slice().split_at_mut(16);
75 self.state[0].serialize(state0).expect("fits in 16 bytes");
76 self.state[1].serialize(state1).expect("fits in 16 bytes");
77 }
78}
79
80impl HashMarker for VisionHasherDigest {}
81
82impl Update for VisionHasherDigest {
83 fn update(&mut self, mut data: &[u8]) {
84 if self.filled_bytes != 0 {
85 let to_copy = std::cmp::min(data.len(), RATE_AS_U8 - self.filled_bytes);
86 self.buffer[self.filled_bytes..self.filled_bytes + to_copy]
87 .copy_from_slice(&data[..to_copy]);
88 data = &data[to_copy..];
89 self.filled_bytes += to_copy;
90
91 if self.filled_bytes == RATE_AS_U8 {
92 Self::permute(&mut self.state, &self.buffer);
93 self.filled_bytes = 0;
94 }
95 }
96
97 let mut chunks = data.chunks_exact(RATE_AS_U8);
98 for chunk in &mut chunks {
99 Self::permute(&mut self.state, chunk);
100 }
101
102 let remaining = chunks.remainder();
103 if !remaining.is_empty() {
104 self.buffer[..remaining.len()].copy_from_slice(remaining);
105 self.filled_bytes = remaining.len();
106 }
107 }
108}
109
110impl OutputSizeUser for VisionHasherDigest {
111 type OutputSize = U32;
112}
113
114impl BlockSizeUser for VisionHasherDigest {
115 type BlockSize = U32;
116}
117
118impl FixedOutput for VisionHasherDigest {
119 fn finalize_into(mut self, out: &mut digest::Output<Self>) {
120 Self::finalize(&mut self, out);
121 }
122}
123
124impl FixedOutputReset for VisionHasherDigest {
125 fn finalize_into_reset(&mut self, out: &mut digest::Output<Self>) {
126 Self::finalize(self, out);
127 Reset::reset(self);
128 }
129}
130
131impl Reset for VisionHasherDigest {
132 fn reset(&mut self) {
133 self.state = [Ghash::ZERO; M];
134 self.buffer = [0; RATE_AS_U8];
135 self.filled_bytes = 0;
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use digest::Digest;
142
143 use super::VisionHasherDigest;
144
145 const INPUT: &[u8] = "One part of the mysterious existence of Captain Nemo had been unveiled and, if his identity had not been recognised, at least, the nations united against him were no longer hunting a chimerical creature, but a man who had vowed a deadly hatred against them".as_bytes();
146
147 #[test]
148 fn test_multi_block_aligned() {
149 let mut hasher = VisionHasherDigest::default();
150
151 hasher.update(INPUT);
152 let out = hasher.finalize();
153
154 let mut hasher = VisionHasherDigest::default();
155 let input_as_b = INPUT;
156 hasher.update(&input_as_b[0..63]);
157 hasher.update(&input_as_b[63..128]);
158 hasher.update(&input_as_b[128..163]);
159 hasher.update(&input_as_b[163..]);
160
161 assert_eq!(out, hasher.finalize());
162 }
163
164 #[test]
165 fn test_multi_block_unaligned() {
166 let mut hasher = VisionHasherDigest::default();
167 hasher.update(INPUT);
168 let out = hasher.finalize();
169
170 let mut hasher = VisionHasherDigest::default();
171 let input_as_b = INPUT;
172 hasher.update(&input_as_b[0..1]);
173 hasher.update(&input_as_b[1..120]);
174 hasher.update(&input_as_b[120..120]);
175 hasher.update(&input_as_b[120..]);
176
177 assert_eq!(out, hasher.finalize());
178 }
179}