gimli_hash/
lib.rs

1#![no_std]
2
3use core::cmp;
4use gimli_permutation::{ SIZE, gimli, state_with as with };
5
6
7const RATE: usize = 16;
8
9#[derive(Clone)]
10pub struct GimliHash {
11    state: [u32; SIZE],
12    pos: usize
13}
14
15impl Default for GimliHash {
16    fn default() -> Self {
17        GimliHash { state: [0; SIZE], pos: 0 }
18    }
19}
20
21impl GimliHash {
22    #[inline]
23    pub fn update(&mut self, buf: &[u8]) {
24        self.absorb(buf);
25    }
26
27    #[inline]
28    pub fn finalize(self, buf: &mut [u8]) {
29        self.xof().squeeze(buf);
30    }
31
32    #[inline]
33    pub fn xof(mut self) -> XofReader {
34        self.pad();
35        XofReader { state: self.state, pos: 0 }
36    }
37
38    pub fn fill_block(&mut self) {
39        self.pos = 0;
40        gimli(&mut self.state);
41    }
42
43    fn absorb(&mut self, buf: &[u8]) {
44        let take = cmp::min(RATE - self.pos, buf.len());
45        let (prefix, buf) = buf.split_at(take);
46
47        if !prefix.is_empty() {
48            let pos = self.pos;
49            with(&mut self.state, |state| {
50                for (dest, src) in state.iter_mut()
51                    .skip(pos)
52                    .zip(prefix)
53                {
54                    *dest ^= *src;
55                }
56            });
57
58            self.pos += prefix.len();
59
60            if self.pos == RATE {
61                gimli(&mut self.state);
62                self.pos = 0;
63            }
64        }
65
66        let mut iter = buf.chunks_exact(RATE);
67        for chunk in &mut iter {
68            with(&mut self.state, |state| {
69                for (dest, src) in state.iter_mut().zip(chunk) {
70                    *dest ^= *src;
71                }
72            });
73            gimli(&mut self.state);
74        }
75
76        let chunk = iter.remainder();
77        if !chunk.is_empty() {
78            with(&mut self.state, |state| {
79                for (dest, src) in state.iter_mut().zip(chunk) {
80                    *dest ^= *src;
81                }
82            });
83            self.pos += chunk.len();
84        }
85    }
86
87    fn pad(&mut self) {
88        let pos = self.pos;
89        with(&mut self.state, |state| {
90            state[pos] ^= 0x1f;
91            state[RATE - 1] ^= 0x80;
92        });
93        gimli(&mut self.state);
94    }
95}
96
97
98pub struct XofReader {
99    state: [u32; SIZE],
100    pos: usize
101}
102
103impl XofReader {
104    pub fn squeeze(&mut self, buf: &mut [u8]) {
105        let take = cmp::min(RATE - self.pos, buf.len());
106        let (prefix, buf) = buf.split_at_mut(take);
107
108        if !prefix.is_empty() {
109            let pos = self.pos;
110            with(&mut self.state, |state| {
111                prefix.copy_from_slice(&state[pos..][..prefix.len()]);
112            });
113
114            self.pos += prefix.len();
115
116            if self.pos == RATE {
117                gimli(&mut self.state);
118                self.pos = 0;
119            }
120        }
121
122        let mut iter = buf.chunks_exact_mut(RATE);
123        for chunk in &mut iter {
124            with(&mut self.state, |state| chunk.copy_from_slice(&state[..RATE]));
125            gimli(&mut self.state);
126        }
127
128        let chunk = iter.into_remainder();
129        if !chunk.is_empty() {
130            with(&mut self.state, |state| {
131                chunk.copy_from_slice(&state[..chunk.len()]);
132            });
133            self.pos += take;
134        }
135    }
136}