Skip to main content

crypto/ascon/
ascon_xof128.rs

1use super::*;
2use crate::Xof;
3
4/// Ascon-XOF128 initialization vector (NIST SP 800-232).
5const IV: u64 = 0x0000_0800_00cc_0003;
6
7/// Ascon-XOF128 extensible-output function (NIST SP 800-232 ยง5.2).
8///
9/// Produces variable-length output. Rate = 64 bits (8 bytes), capacity = 256 bits.
10/// Shorter outputs are prefixes of longer ones.
11///
12/// Implements the [`Xof`] trait.
13///
14/// # One-shot API
15///
16/// ```ignore
17/// use crypto::ascon::AsconXof128;
18///
19/// let mut output = [0u8; 32];
20/// AsconXof128::hash(b"hello world", &mut output);
21/// ```
22///
23/// # Incremental API
24///
25/// ```ignore
26/// use crypto::{ascon::AsconXof128, Xof};
27///
28/// let mut xof = AsconXof128::new();
29/// xof.absorb(b"hello ");
30/// xof.absorb(b"world");
31/// let mut out = [0u8; 32];
32/// xof.squeeze(&mut out);
33/// ```
34#[derive(Clone)]
35#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
36pub struct AsconXof128 {
37    state: State,
38    buf: [u8; 8],
39    buf_len: usize,
40    squeezing: bool,
41    squeeze_pos: usize,
42    current_block: [u8; 8],
43}
44
45impl AsconXof128 {
46    #[inline]
47    pub fn new() -> Self {
48        let mut state = State::init_hash(IV);
49        p12(&mut state);
50        AsconXof128 {
51            state,
52            buf: [0u8; 8],
53            buf_len: 0,
54            squeezing: false,
55            squeeze_pos: 0,
56            current_block: [0u8; 8],
57        }
58    }
59
60    #[inline]
61    pub fn hash(data: &[u8], output: &mut [u8]) {
62        let mut xof = Self::new();
63        xof.absorb(data);
64        xof.squeeze(output);
65    }
66}
67
68impl Xof for AsconXof128 {
69    fn absorb(&mut self, mut data: &[u8]) {
70        assert!(!self.squeezing, "absorb cannot be called after squeeze");
71
72        if self.buf_len > 0 {
73            let to_fill = (8 - self.buf_len).min(data.len());
74            self.buf[self.buf_len..self.buf_len + to_fill].copy_from_slice(&data[..to_fill]);
75            self.buf_len += to_fill;
76            data = &data[to_fill..];
77
78            if self.buf_len == 8 {
79                self.state.absorb_block(&self.buf);
80                p12(&mut self.state);
81                self.buf_len = 0;
82            }
83        }
84
85        let (chunks, remainder) = data.as_chunks::<8>();
86        for chunk in chunks {
87            self.state.absorb_block(chunk);
88            p12(&mut self.state);
89        }
90
91        if !remainder.is_empty() {
92            self.buf[..remainder.len()].copy_from_slice(remainder);
93            self.buf_len = remainder.len();
94        }
95    }
96
97    fn squeeze(&mut self, out: &mut [u8]) {
98        if !self.squeezing {
99            let mut padded = [0u8; 8];
100            padded[..self.buf_len].copy_from_slice(&self.buf[..self.buf_len]);
101            padded[self.buf_len] = 0x01;
102            self.state.absorb_block(&padded);
103            p12(&mut self.state);
104            self.squeezing = true;
105        }
106
107        let mut remaining = out;
108        while !remaining.is_empty() {
109            if self.squeeze_pos == 0 {
110                self.current_block = self.state.squeeze_byte();
111            }
112            let n = remaining.len().min(8 - self.squeeze_pos);
113            remaining[..n].copy_from_slice(&self.current_block[self.squeeze_pos..self.squeeze_pos + n]);
114            self.squeeze_pos += n;
115            remaining = &mut remaining[n..];
116
117            if self.squeeze_pos == 8 && !remaining.is_empty() {
118                self.squeeze_pos = 0;
119                p12(&mut self.state);
120            }
121        }
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128    use crate::Xof;
129
130    #[test]
131    fn empty_xof() {
132        let mut out = [0u8; 64];
133        AsconXof128::hash(b"", &mut out);
134        let expected = hex::decode("473D5E6164F58B39DFD84AACDB8AE42EC2D91FED33388EE0D960D9B3993295C6AD77855A5D3B13FE6AD9E6098988373AF7D0956D05A8F1665D2C67D1A3AD10FF").unwrap();
135        assert_eq!(out.as_slice(), expected.as_slice());
136    }
137
138    #[test]
139    fn one_byte_xof() {
140        let mut out = [0u8; 64];
141        AsconXof128::hash(b"\x00", &mut out);
142        let expected = hex::decode("51430E0438ECDF642B393630D977625F5F337656BA58AB1E960784AC32A16E0D446405551F5469384F8EA283CF12E64FA72C426BFEBAEA3AA1529E2C4AB23A2F").unwrap();
143        assert_eq!(out.as_slice(), expected.as_slice());
144    }
145
146    #[test]
147    fn incremental_xof() {
148        let mut one_shot = [0u8; 64];
149        AsconXof128::hash(b"", &mut one_shot);
150
151        let mut xof = AsconXof128::new();
152        xof.absorb(b"");
153        let mut first = [0u8; 32];
154        let mut second = [0u8; 32];
155        xof.squeeze(&mut first);
156        xof.squeeze(&mut second);
157
158        assert_eq!(first.as_slice(), &one_shot[..32]);
159        assert_eq!(second.as_slice(), &one_shot[32..]);
160    }
161
162    #[test]
163    fn prefix_property() {
164        let mut out64 = [0u8; 64];
165        AsconXof128::hash(b"data", &mut out64);
166
167        let mut out32 = [0u8; 32];
168        AsconXof128::hash(b"data", &mut out32);
169
170        assert_eq!(out32.as_slice(), &out64[..32]);
171    }
172
173    #[test]
174    fn kat_vectors() {
175        let data = include_str!("../../testdata/ascon/LWC_XOF_KAT_128_512.txt");
176        let mut count = 0u64;
177        let mut msg_hex = String::new();
178
179        for line in data.lines() {
180            let line = line.trim();
181            if line.is_empty() {
182                continue;
183            }
184            if line.starts_with("Count = ") {
185                count = line["Count = ".len()..].parse().unwrap();
186                msg_hex.clear();
187                continue;
188            }
189            if line.starts_with("Msg = ") {
190                msg_hex = line[6..].to_string();
191                continue;
192            }
193            if line.starts_with("MD = ") {
194                let expected_md: &str = &line[5..];
195                let msg = hex::decode(&msg_hex).unwrap();
196                let expected = hex::decode(expected_md).unwrap();
197                let mut out = vec![0u8; expected.len()];
198                AsconXof128::hash(&msg, &mut out);
199                assert_eq!(out.as_slice(), expected.as_slice(), "KAT XOF Count={count} mismatch");
200
201                // Test incremental
202                let mut xof = AsconXof128::new();
203                xof.absorb(&msg);
204                let mut out_inc = vec![0u8; expected.len()];
205                xof.squeeze(&mut out_inc);
206                assert_eq!(
207                    out_inc.as_slice(),
208                    expected.as_slice(),
209                    "KAT XOF Count={count} incremental mismatch"
210                );
211                msg_hex.clear();
212                continue;
213            }
214        }
215    }
216}