crypto/ascon/
ascon_cxof128.rs1use super::*;
2use crate::Xof;
3
4const IV: u64 = 0x0000_0800_00cc_0004;
6
7#[derive(Clone)]
30#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
31pub struct AsconCxof128 {
32 state: State,
33 buf: [u8; 8],
34 buf_len: usize,
35 squeezing: bool,
36 squeeze_pos: usize,
37 current_block: [u8; 8],
38}
39
40impl AsconCxof128 {
41 #[inline]
43 pub fn new() -> Self {
44 Self::new_with_customization(&[])
45 }
46
47 pub fn new_with_customization(z: &[u8]) -> Self {
53 assert!(z.len() <= 256, "CXOF customization string must be at most 256 bytes");
54 let mut state = State::init_hash(IV);
55 p12(&mut state);
56
57 let z_bits = (z.len() as u64).wrapping_mul(8);
59 state.absorb_block(&z_bits.to_le_bytes());
60 p12(&mut state);
61
62 if !z.is_empty() {
64 let (chunks, remainder) = z.as_chunks::<8>();
65 for chunk in chunks {
66 state.absorb_block(chunk);
67 p12(&mut state);
68 }
69 if !remainder.is_empty() {
70 let mut padded = [0u8; 8];
71 padded[..remainder.len()].copy_from_slice(remainder);
72 padded[remainder.len()] = 0x01;
73 state.absorb_block(&padded);
74 p12(&mut state);
75 } else {
76 let mut padded = [0u8; 8];
78 padded[0] = 0x01;
79 state.absorb_block(&padded);
80 p12(&mut state);
81 }
82 } else {
83 let mut padded = [0u8; 8];
85 padded[0] = 0x01;
86 state.absorb_block(&padded);
87 p12(&mut state);
88 }
89
90 AsconCxof128 {
91 state,
92 buf: [0u8; 8],
93 buf_len: 0,
94 squeezing: false,
95 squeeze_pos: 0,
96 current_block: [0u8; 8],
97 }
98 }
99}
100
101impl Xof for AsconCxof128 {
102 fn absorb(&mut self, mut data: &[u8]) {
103 assert!(!self.squeezing, "absorb cannot be called after squeeze");
104
105 if self.buf_len > 0 {
106 let to_fill = (8 - self.buf_len).min(data.len());
107 self.buf[self.buf_len..self.buf_len + to_fill].copy_from_slice(&data[..to_fill]);
108 self.buf_len += to_fill;
109 data = &data[to_fill..];
110
111 if self.buf_len == 8 {
112 self.state.absorb_block(&self.buf);
113 p12(&mut self.state);
114 self.buf_len = 0;
115 }
116 }
117
118 let (chunks, remainder) = data.as_chunks::<8>();
119 for chunk in chunks {
120 self.state.absorb_block(chunk);
121 p12(&mut self.state);
122 }
123
124 if !remainder.is_empty() {
125 self.buf[..remainder.len()].copy_from_slice(remainder);
126 self.buf_len = remainder.len();
127 }
128 }
129
130 fn squeeze(&mut self, out: &mut [u8]) {
131 if !self.squeezing {
132 let mut padded = [0u8; 8];
133 padded[..self.buf_len].copy_from_slice(&self.buf[..self.buf_len]);
134 padded[self.buf_len] = 0x01;
135 self.state.absorb_block(&padded);
136 p12(&mut self.state);
137 self.squeezing = true;
138 }
139
140 let mut remaining = out;
141 while !remaining.is_empty() {
142 if self.squeeze_pos == 0 {
143 self.current_block = self.state.squeeze_byte();
144 }
145 let n = remaining.len().min(8 - self.squeeze_pos);
146 remaining[..n].copy_from_slice(&self.current_block[self.squeeze_pos..self.squeeze_pos + n]);
147 self.squeeze_pos += n;
148 remaining = &mut remaining[n..];
149
150 if self.squeeze_pos == 8 && !remaining.is_empty() {
151 self.squeeze_pos = 0;
152 p12(&mut self.state);
153 }
154 }
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use crate::Xof;
162
163 #[test]
164 fn empty_cxof() {
165 let mut out = [0u8; 64];
166 let mut cxof = AsconCxof128::new();
167 cxof.absorb(b"");
168 cxof.squeeze(&mut out);
169 let expected = hex::decode("4F50159EF70BB3DAD8807E034EAEBD44C4FA2CBBC8CF1F05511AB66CDCC529905CA12083FC186AD899B270B1473DC5F7EC88D1052082DCDFE69FB75D269E7B74").unwrap();
170 assert_eq!(out.as_slice(), expected.as_slice());
171 }
172
173 #[test]
174 fn cxof_with_customization() {
175 let mut out = [0u8; 64];
176 let mut cxof = AsconCxof128::new_with_customization(b"\x10");
177 cxof.absorb(b"");
178 cxof.squeeze(&mut out);
179 let expected = hex::decode("0C93A483E7D574D49FE52CCE03EE646117977D57A8AA57704AB4DAF44B501430FF6AC11A5D1FD6F2154B5C65728268270C8BB578508487B8965718ADA6272FD6").unwrap();
180 assert_eq!(out.as_slice(), expected.as_slice());
181 }
182
183 #[test]
184 fn cxof_differs_from_xof() {
185 let mut xof_out = [0u8; 32];
186 {
187 let mut xof = crate::ascon::AsconXof128::new();
188 xof.absorb(b"test");
189 xof.squeeze(&mut xof_out);
190 }
191
192 let mut cxof_out = [0u8; 32];
193 let mut cxof = AsconCxof128::new_with_customization(b"test");
194 cxof.absorb(b"");
195 cxof.squeeze(&mut cxof_out);
196
197 assert_ne!(xof_out, cxof_out, "CXOF with non-empty customization should differ from XOF");
198 }
199
200 #[test]
201 #[should_panic(expected = "CXOF customization string must be at most 256 bytes")]
202 fn cxof_customization_too_long() {
203 AsconCxof128::new_with_customization(&[0u8; 257]);
204 }
205
206 #[test]
207 fn kat_vectors() {
208 let data = include_str!("../../testdata/ascon/LWC_CXOF_KAT_128_512.txt");
209 let mut count = 0u64;
210 let mut msg_hex = String::new();
211 let mut z_hex = String::new();
212
213 for line in data.lines() {
214 let line = line.trim();
215 if line.is_empty() {
216 continue;
217 }
218 if line.starts_with("Count = ") {
219 count = line["Count = ".len()..].parse().unwrap();
220 msg_hex.clear();
221 z_hex.clear();
222 continue;
223 }
224 if line.starts_with("Msg = ") {
225 msg_hex = line[6..].to_string();
226 continue;
227 }
228 if line.starts_with("Z = ") {
229 z_hex = line[4..].to_string();
230 continue;
231 }
232 if line.starts_with("MD = ") {
233 let expected_md: &str = &line[5..];
234 let msg = hex::decode(&msg_hex).unwrap();
235 let z = hex::decode(&z_hex).unwrap();
236 let expected = hex::decode(expected_md).unwrap();
237 let mut cxof = AsconCxof128::new_with_customization(&z);
238 cxof.absorb(&msg);
239 let mut out = vec![0u8; expected.len()];
240 cxof.squeeze(&mut out);
241 assert_eq!(out.as_slice(), expected.as_slice(), "KAT CXOF Count={count} mismatch");
242 msg_hex.clear();
243 z_hex.clear();
244 continue;
245 }
246 }
247 }
248}