crypto/ascon/
ascon_xof128.rs1use super::*;
2use crate::Xof;
3
4const IV: u64 = 0x0000_0800_00cc_0003;
6
7#[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 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}