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 mut chunks = data.chunks_exact(8);
86        for chunk in &mut chunks {
87            self.state.absorb_block(chunk);
88            p12(&mut self.state);
89        }
90
91        let remainder = chunks.remainder();
92        if !remainder.is_empty() {
93            self.buf[..remainder.len()].copy_from_slice(remainder);
94            self.buf_len = remainder.len();
95        }
96    }
97
98    fn squeeze(&mut self, out: &mut [u8]) {
99        if !self.squeezing {
100            let mut padded = [0u8; 8];
101            padded[..self.buf_len].copy_from_slice(&self.buf[..self.buf_len]);
102            padded[self.buf_len] = 0x01;
103            self.state.absorb_block(&padded);
104            p12(&mut self.state);
105            self.squeezing = true;
106        }
107
108        let mut remaining = out;
109        while !remaining.is_empty() {
110            if self.squeeze_pos == 0 {
111                self.current_block = self.state.squeeze_byte();
112            }
113            let n = remaining.len().min(8 - self.squeeze_pos);
114            remaining[..n].copy_from_slice(&self.current_block[self.squeeze_pos..self.squeeze_pos + n]);
115            self.squeeze_pos += n;
116            remaining = &mut remaining[n..];
117
118            if self.squeeze_pos == 8 && !remaining.is_empty() {
119                self.squeeze_pos = 0;
120                p12(&mut self.state);
121            }
122        }
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129    use crate::Xof;
130
131    #[test]
132    fn empty_xof() {
133        let mut out = [0u8; 64];
134        AsconXof128::hash(b"", &mut out);
135        let expected = hex::decode("473D5E6164F58B39DFD84AACDB8AE42EC2D91FED33388EE0D960D9B3993295C6AD77855A5D3B13FE6AD9E6098988373AF7D0956D05A8F1665D2C67D1A3AD10FF").unwrap();
136        assert_eq!(out.as_slice(), expected.as_slice());
137    }
138
139    #[test]
140    fn one_byte_xof() {
141        let mut out = [0u8; 64];
142        AsconXof128::hash(b"\x00", &mut out);
143        let expected = hex::decode("51430E0438ECDF642B393630D977625F5F337656BA58AB1E960784AC32A16E0D446405551F5469384F8EA283CF12E64FA72C426BFEBAEA3AA1529E2C4AB23A2F").unwrap();
144        assert_eq!(out.as_slice(), expected.as_slice());
145    }
146
147    #[test]
148    fn incremental_xof() {
149        let mut one_shot = [0u8; 64];
150        AsconXof128::hash(b"", &mut one_shot);
151
152        let mut xof = AsconXof128::new();
153        xof.absorb(b"");
154        let mut first = [0u8; 32];
155        let mut second = [0u8; 32];
156        xof.squeeze(&mut first);
157        xof.squeeze(&mut second);
158
159        assert_eq!(first.as_slice(), &one_shot[..32]);
160        assert_eq!(second.as_slice(), &one_shot[32..]);
161    }
162
163    #[test]
164    fn prefix_property() {
165        let mut out64 = [0u8; 64];
166        AsconXof128::hash(b"data", &mut out64);
167
168        let mut out32 = [0u8; 32];
169        AsconXof128::hash(b"data", &mut out32);
170
171        assert_eq!(out32.as_slice(), &out64[..32]);
172    }
173
174    #[test]
175    fn kat_vectors() {
176        let data = include_str!("../../testdata/ascon/LWC_XOF_KAT_128_512.txt");
177        let mut count = 0u64;
178        let mut msg_hex = String::new();
179
180        for line in data.lines() {
181            let line = line.trim();
182            if line.is_empty() {
183                continue;
184            }
185            if line.starts_with("Count = ") {
186                count = line["Count = ".len()..].parse().unwrap();
187                msg_hex.clear();
188                continue;
189            }
190            if line.starts_with("Msg = ") {
191                msg_hex = line[6..].to_string();
192                continue;
193            }
194            if line.starts_with("MD = ") {
195                let expected_md: &str = &line[5..];
196                let msg = hex::decode(&msg_hex).unwrap();
197                let expected = hex::decode(expected_md).unwrap();
198                let mut out = vec![0u8; expected.len()];
199                AsconXof128::hash(&msg, &mut out);
200                assert_eq!(out.as_slice(), expected.as_slice(), "KAT XOF Count={count} mismatch");
201
202                // Test incremental
203                let mut xof = AsconXof128::new();
204                xof.absorb(&msg);
205                let mut out_inc = vec![0u8; expected.len()];
206                xof.squeeze(&mut out_inc);
207                assert_eq!(
208                    out_inc.as_slice(),
209                    expected.as_slice(),
210                    "KAT XOF Count={count} incremental mismatch"
211                );
212                msg_hex.clear();
213                continue;
214            }
215        }
216    }
217}