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