Skip to main content

belt_hash/
block_api.rs

1use belt_block::belt_block_raw;
2use core::fmt;
3use digest::{
4    HashMarker, Output,
5    block_api::{
6        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, Eager, FixedOutputCore,
7        OutputSizeUser, Reset, UpdateCore,
8    },
9    common::hazmat::{DeserializeStateError, SerializableState, SerializedState},
10    typenum::{U32, U64, Unsigned},
11};
12
13const H0: [u32; 8] = [
14    0xC8BA94B1, 0x3BF5080A, 0x8E006D36, 0xE45D4A58, 0x9DFA0485, 0xACC7B61B, 0xC2722E25, 0x0DCEFD02,
15];
16
17/// Core BelT hasher state.
18#[derive(Clone)]
19pub struct BeltHashCore {
20    r: u128,
21    s: [u32; 4],
22    h: [u32; 8],
23}
24
25impl BeltHashCore {
26    fn compress_block(&mut self, block: &Block<Self>) {
27        let x1 = read_u32s(&block[..16]);
28        let x2 = read_u32s(&block[16..]);
29        let (t, h) = belt_compress(x1, x2, self.h);
30        self.h = h;
31        self.s = xor(self.s, t);
32    }
33}
34
35impl HashMarker for BeltHashCore {}
36
37impl BlockSizeUser for BeltHashCore {
38    type BlockSize = U32;
39}
40
41impl BufferKindUser for BeltHashCore {
42    type BufferKind = Eager;
43}
44
45impl OutputSizeUser for BeltHashCore {
46    type OutputSize = U32;
47}
48
49impl UpdateCore for BeltHashCore {
50    #[inline]
51    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
52        self.r = self.r.wrapping_add(blocks.len() as u128);
53        for block in blocks {
54            self.compress_block(block);
55        }
56    }
57}
58
59impl FixedOutputCore for BeltHashCore {
60    #[inline]
61    fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
62        let pos = buffer.get_pos();
63        if pos != 0 {
64            let block = buffer.pad_with_zeros();
65            self.compress_block(&block);
66        }
67        let bs = Self::BlockSize::USIZE as u128;
68        let r = encode_r(8 * ((bs * self.r) + pos as u128));
69        let (_, y) = belt_compress(r, self.s, self.h);
70        write_u32s(&y, out);
71    }
72}
73
74impl Default for BeltHashCore {
75    #[inline]
76    fn default() -> Self {
77        Self {
78            r: 0,
79            s: [0; 4],
80            h: H0,
81        }
82    }
83}
84
85impl Reset for BeltHashCore {
86    #[inline]
87    fn reset(&mut self) {
88        *self = Default::default();
89    }
90}
91
92impl AlgorithmName for BeltHashCore {
93    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        f.write_str("BeltHash")
95    }
96}
97
98impl fmt::Debug for BeltHashCore {
99    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100        f.write_str("BeltHashCore { ... }")
101    }
102}
103
104impl Drop for BeltHashCore {
105    fn drop(&mut self) {
106        #[cfg(feature = "zeroize")]
107        {
108            use digest::zeroize::Zeroize;
109            self.r.zeroize();
110            self.s.zeroize();
111            self.h.zeroize();
112        }
113    }
114}
115
116#[cfg(feature = "zeroize")]
117impl digest::zeroize::ZeroizeOnDrop for BeltHashCore {}
118
119impl SerializableState for BeltHashCore {
120    type SerializedStateSize = U64;
121
122    fn serialize(&self) -> SerializedState<Self> {
123        let mut dst = SerializedState::<Self>::default();
124
125        let (r_dst, tail) = dst.split_at_mut(16);
126        let (s_dst, h_dst) = tail.split_at_mut(16);
127
128        r_dst.copy_from_slice(&self.r.to_le_bytes());
129        write_u32s(&self.s, s_dst);
130        write_u32s(&self.h, h_dst);
131
132        dst
133    }
134
135    fn deserialize(
136        serialized_state: &SerializedState<Self>,
137    ) -> Result<Self, DeserializeStateError> {
138        let (r_src, tail) = serialized_state.split_at(16);
139        let (s_src, h_src) = tail.split_at(16);
140
141        Ok(Self {
142            r: u128::from_le_bytes(r_src.try_into().unwrap()),
143            s: read_u32s(s_src),
144            h: read_u32s(h_src),
145        })
146    }
147}
148
149/// Compression function described in the section 6.3.2
150#[inline(always)]
151pub fn belt_compress(x1: [u32; 4], x2: [u32; 4], x34: [u32; 8]) -> ([u32; 4], [u32; 8]) {
152    let x3 = [x34[0], x34[1], x34[2], x34[3]];
153    let x4 = [x34[4], x34[5], x34[6], x34[7]];
154
155    // Step 2
156    let t1 = belt_block_raw(xor(x3, x4), &concat(x1, x2));
157    let s = xor(xor(t1, x3), x4);
158    // Step 3
159    let t2 = belt_block_raw(x1, &concat(s, x4));
160    let y1 = xor(t2, x1);
161    // Step 4
162    let t3 = belt_block_raw(x2, &concat(s.map(|v| !v), x3));
163    let y2 = xor(t3, x2);
164    // Step 5
165    (s, concat(y1, y2))
166}
167
168#[inline(always)]
169fn xor(a: [u32; 4], b: [u32; 4]) -> [u32; 4] {
170    [a[0] ^ b[0], a[1] ^ b[1], a[2] ^ b[2], a[3] ^ b[3]]
171}
172
173#[inline(always)]
174fn concat(a: [u32; 4], b: [u32; 4]) -> [u32; 8] {
175    [a[0], a[1], a[2], a[3], b[0], b[1], b[2], b[3]]
176}
177
178#[inline(always)]
179fn read_u32s<const N: usize>(src: &[u8]) -> [u32; N] {
180    assert_eq!(src.len(), 4 * N);
181
182    let mut dst = [0u32; N];
183    for (dst, src) in dst.iter_mut().zip(src.chunks_exact(4)) {
184        *dst = u32::from_le_bytes(src.try_into().unwrap());
185    }
186    dst
187}
188
189#[inline(always)]
190fn write_u32s(src: &[u32], dst: &mut [u8]) {
191    assert_eq!(4 * src.len(), dst.len());
192    for (src, dst) in src.iter().zip(dst.chunks_exact_mut(4)) {
193        dst.copy_from_slice(&src.to_le_bytes());
194    }
195}
196
197#[inline(always)]
198fn encode_r(r: u128) -> [u32; 4] {
199    core::array::from_fn(|i| (r >> (32 * i)) as u32)
200}
201
202#[cfg(test)]
203mod tests {
204    use super::{belt_compress, read_u32s};
205    use hex_literal::hex;
206
207    /// Test vectors for the `belt-compress` functions from the
208    /// specification (Table A.8).
209    #[test]
210    fn compress() {
211        let x = &hex!(
212            "B194BAC8 0A08F53B 366D008E 584A5DE4"
213            "8504FA9D 1BB6C7AC 252E72C2 02FDCE0D"
214            "5BE3D612 17B96181 FE6786AD 716B890B"
215            "5CB0C0FF 33C356B8 35C405AE D8E07F99"
216        );
217        let expected_s = &hex!("46FE7425 C9B181EB 41DFEE3E 72163D5A");
218        let expected_y = &hex!(
219            "ED2F5481 D593F40D 87FCE37D 6BC1A2E1"
220            "B7D1A2CC 975C82D3 C0497488 C90D99D8"
221        );
222        let x1 = read_u32s(&x[..16]);
223        let x2 = read_u32s(&x[16..32]);
224        let x34 = read_u32s(&x[32..]);
225
226        let (s, y) = belt_compress(x1, x2, x34);
227
228        assert_eq!(s, read_u32s(expected_s));
229        assert_eq!(y, read_u32s(expected_y));
230    }
231}