Skip to main content

crypto/ascon/
ascon_aead128.rs

1use super::*;
2use crate::{Aead, AeadError, Bytes, Hash};
3
4/// Ascon-AEAD128 authenticated encryption with associated data (NIST SP 800-232).
5///
6/// # Parameters
7///
8/// - Key: 128 bits (16 bytes)
9/// - Nonce: 128 bits (16 bytes)
10/// - Tag: 128 bits (16 bytes)
11/// - Rate: 128 bits, capacity: 192 bits
12/// - Initialization/finalization rounds: 12
13/// - Data processing rounds: 8
14///
15/// # Example
16///
17/// ```ignore
18/// use crypto::{Aead, ascon::AsconAead128};
19///
20/// let key = [0x42u8; 16];
21/// let nonce = [0xABu8; 16];
22/// let aad = b"example metadata";
23/// let plaintext = b"hello, world!";
24///
25/// let cipher = AsconAead128::new(&key);
26///
27/// let mut ct = plaintext.to_vec();
28/// let tag = cipher.encrypt_in_place(&mut ct, &nonce, aad);
29///
30/// let mut pt = ct.clone();
31/// cipher.decrypt_in_place(&mut pt, &nonce, aad, tag.as_ref()).unwrap();
32/// assert_eq!(&pt, plaintext);
33/// ```
34///
35/// # Usage limits (NIST SP 800-232 ยง4.3)
36///
37/// - Max data per key: 2^54 bytes
38/// - Nonces must be distinct per key (up to 2^8 repetitions tolerated)
39/// - Max decryption failures: 2^(tag_len - 32) before key rotation
40#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
41pub struct AsconAead128 {
42    iv: u64,
43    k0: u64,
44    k1: u64,
45    key: [u8; 16],
46}
47
48impl AsconAead128 {
49    /// Creates a new Ascon-AEAD128 instance from a 16-byte key.
50    pub fn new(key: &[u8; 16]) -> Self {
51        let k0 = u64::from_le_bytes(key[0..8].try_into().unwrap());
52        let k1 = u64::from_le_bytes(key[8..16].try_into().unwrap());
53        AsconAead128 {
54            iv: 0x0000_1000_808c_0001,
55            k0,
56            k1,
57            key: *key,
58        }
59    }
60
61    /// Initialize the state with key + nonce + IV.
62    fn init_state(&self, nonce: &[u8; 16]) -> State {
63        let mut state = State::init_aead(&self.key, nonce, self.iv);
64        p12(&mut state);
65        state.xor_word(3, self.k0);
66        state.xor_word(4, self.k1);
67        state
68    }
69
70    /// Process associated data.
71    fn process_ad(state: &mut State, aad: &[u8]) {
72        if aad.is_empty() {
73            return;
74        }
75        let mut chunks = aad.chunks_exact(16);
76        for chunk in &mut chunks {
77            state.xor_rate128_bytes(chunk.try_into().unwrap());
78            p8(state);
79        }
80        let remainder = chunks.remainder();
81        if !remainder.is_empty() {
82            state.xor_partial_rate(remainder);
83        }
84        state.apply_aead_pad(remainder.len());
85        p8(state);
86    }
87
88    /// Compute the authentication tag from the final state.
89    fn compute_tag(&self, state: &State) -> [u8; 16] {
90        let tag = state.tag_bytes();
91        let t0 = u64::from_le_bytes(tag[0..8].try_into().unwrap()) ^ self.k0;
92        let t1 = u64::from_le_bytes(tag[8..16].try_into().unwrap()) ^ self.k1;
93        let mut result = [0u8; 16];
94        result[..8].copy_from_slice(&t0.to_le_bytes());
95        result[8..].copy_from_slice(&t1.to_le_bytes());
96        result
97    }
98}
99
100impl Aead for AsconAead128 {
101    const TAG_SIZE: usize = 16;
102    const NONCE_SIZE: usize = 16;
103
104    fn encrypt_in_place(&self, in_out: &mut [u8], nonce: &[u8], aad: &[u8]) -> Hash {
105        let nonce: &[u8; 16] = nonce.try_into().expect("nonce must be 16 bytes");
106        let mut state = self.init_state(nonce);
107        Self::process_ad(&mut state, aad);
108        state.apply_domain_sep();
109
110        let rem_len = in_out.len() % 16;
111
112        let mut chunks = in_out.chunks_exact_mut(16);
113        for chunk in &mut chunks {
114            state.encrypt_in_place_block(chunk.try_into().unwrap());
115            p8(&mut state);
116        }
117        let remainder = chunks.into_remainder();
118
119        if rem_len > 0 {
120            state.xor_partial_rate(remainder);
121            state.read_rate_bytes(remainder);
122        }
123        state.apply_aead_pad(rem_len);
124
125        state.xor_word(2, self.k0);
126        state.xor_word(3, self.k1);
127        p12(&mut state);
128
129        let tag_bytes = self.compute_tag(&state);
130        let mut tag = Hash(Bytes::<64>::with_length(16));
131        tag.as_mut().copy_from_slice(&tag_bytes);
132        tag
133    }
134
135    fn decrypt_in_place(&self, in_out: &mut [u8], nonce: &[u8], aad: &[u8], tag: &[u8]) -> Result<(), AeadError> {
136        if tag.len() != Self::TAG_SIZE {
137            return Err(AeadError::InvalidCiphertext);
138        }
139        let nonce: &[u8; 16] = nonce.try_into().map_err(|_| AeadError::InvalidNonce)?;
140
141        let mut state = self.init_state(nonce);
142        Self::process_ad(&mut state, aad);
143        state.apply_domain_sep();
144
145        let rem_len = in_out.len() % 16;
146
147        let mut chunks = in_out.chunks_exact_mut(16);
148        for chunk in &mut chunks {
149            state.decrypt_in_place_block(chunk.try_into().unwrap());
150            p8(&mut state);
151        }
152        let remainder = chunks.into_remainder();
153
154        if rem_len > 0 {
155            let mut ct = [0u8; 16];
156            ct[..rem_len].copy_from_slice(remainder);
157            state.read_rate_bytes(remainder);
158            for j in 0..rem_len {
159                remainder[j] ^= ct[j];
160            }
161            state.apply_aead_pad(rem_len);
162            state.write_rate_bytes(&ct[..rem_len]);
163        } else {
164            state.apply_aead_pad(0);
165        }
166
167        state.xor_word(2, self.k0);
168        state.xor_word(3, self.k1);
169        p12(&mut state);
170
171        let computed = self.compute_tag(&state);
172
173        if !constant_time_eq::constant_time_eq(&computed, tag) {
174            in_out.fill(0);
175            return Err(AeadError::InvalidCiphertext);
176        }
177
178        Ok(())
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185    use crate::Aead;
186
187    static KEY: [u8; 16] = [
188        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
189    ];
190    static NONCE: [u8; 16] = [
191        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
192    ];
193
194    #[test]
195    fn empty_pt_empty_ad() {
196        let ae = AsconAead128::new(&KEY);
197        let mut pt = vec![];
198        let tag = ae.encrypt_in_place(&mut pt, &NONCE, &[]);
199        let expected_ct = hex::decode("").unwrap();
200        let expected_tag = hex::decode("4F9C278211BEC9316BF68F46EE8B2EC6").unwrap();
201        assert_eq!(pt, expected_ct);
202        assert_eq!(tag.as_ref(), expected_tag.as_slice());
203    }
204
205    #[test]
206    fn one_byte_pt_empty_ad() {
207        let ae = AsconAead128::new(&KEY);
208        let mut pt = hex::decode("20").unwrap();
209        let tag = ae.encrypt_in_place(&mut pt, &NONCE, &[]);
210        // CT line: E8DD576ABA1CD3E6FC704DE02AEDB79588 (34 hex chars = 17 bytes)
211        // ciphertext = first byte = E8, tag = remaining 16 = DD576ABA1CD3E6FC704DE02AEDB79588
212        let expected_ct = hex::decode("E8").unwrap();
213        let expected_tag = hex::decode("DD576ABA1CD3E6FC704DE02AEDB79588").unwrap();
214        assert_eq!(pt, expected_ct);
215        assert_eq!(tag.as_ref(), expected_tag.as_slice());
216    }
217
218    #[test]
219    fn one_byte_pt_one_byte_ad() {
220        let ae = AsconAead128::new(&KEY);
221        let mut pt = hex::decode("20").unwrap();
222        let aad = hex::decode("30").unwrap();
223        let tag = ae.encrypt_in_place(&mut pt, &NONCE, &aad);
224        // ciphertext=96, tag=2B8016836C75A7D86866588CA245D886
225        let expected_ct = hex::decode("96").unwrap();
226        let expected_tag = hex::decode("2B8016836C75A7D86866588CA245D886").unwrap();
227        assert_eq!(pt, expected_ct);
228        assert_eq!(tag.as_ref(), expected_tag.as_slice());
229    }
230
231    #[test]
232    fn two_byte_pt_six_byte_ad() {
233        let ae = AsconAead128::new(&KEY);
234        let mut pt = hex::decode("2021").unwrap();
235        let aad = hex::decode("303132333435").unwrap();
236        let tag = ae.encrypt_in_place(&mut pt, &NONCE, &aad);
237        // ciphertext=9310, tag=6848C186CA92DCC20741A92F7AAFE673
238        let expected_ct = hex::decode("9310").unwrap();
239        let expected_tag = hex::decode("6848C186CA92DCC20741A92F7AAFE673").unwrap();
240        assert_eq!(pt, expected_ct);
241        assert_eq!(tag.as_ref(), expected_tag.as_slice());
242    }
243
244    #[test]
245    fn thirtytwo_byte_pt_twentysix_byte_ad() {
246        let ae = AsconAead128::new(&KEY);
247        let mut pt = hex::decode("202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F").unwrap();
248        let aad = hex::decode("303132333435363738393A3B3C3D3E3F40414243444546474849").unwrap();
249        let tag = ae.encrypt_in_place(&mut pt, &NONCE, &aad);
250        // ciphertext=32 bytes, tag=16 bytes
251        let expected_ct = hex::decode("A92EF70DF2EF0FAA74A21F9739FB89237DF62F9A2B4080B850046DDD386DED48").unwrap();
252        let expected_tag = hex::decode("E7833CF56F755945AEB70D2BAAAA361C").unwrap();
253        assert_eq!(pt, expected_ct);
254        assert_eq!(tag.as_ref(), expected_tag.as_slice());
255    }
256
257    #[test]
258    fn roundtrip() {
259        let key = [0x55u8; 16];
260        let nonce = [0xAAu8; 16];
261        let aad = b"associated data";
262        let plaintext = b"hello, world!";
263
264        let ae = AsconAead128::new(&key);
265        let mut ct = plaintext.to_vec();
266        let tag = ae.encrypt_in_place(&mut ct, &nonce, aad);
267
268        let mut decrypted = ct.clone();
269        ae.decrypt_in_place(&mut decrypted, &nonce, aad, tag.as_ref()).unwrap();
270        assert_eq!(decrypted, plaintext);
271    }
272
273    #[test]
274    fn roundtrip_empty() {
275        let key = [0x55u8; 16];
276        let nonce = [0xAAu8; 16];
277
278        let ae = AsconAead128::new(&key);
279        let mut ct = vec![];
280        let tag = ae.encrypt_in_place(&mut ct, &nonce, b"ad");
281
282        let mut decrypted = ct.clone();
283        ae.decrypt_in_place(&mut decrypted, &nonce, b"ad", tag.as_ref())
284            .unwrap();
285        assert!(decrypted.is_empty());
286    }
287
288    #[test]
289    fn tampered_tag_fails() {
290        let key = [0x55u8; 16];
291        let nonce = [0xAAu8; 16];
292
293        let ae = AsconAead128::new(&key);
294        let mut ct = b"secret".to_vec();
295        let mut tag = ae.encrypt_in_place(&mut ct, &nonce, b"");
296        tag.as_mut()[0] ^= 1;
297
298        let mut decrypted = ct.clone();
299        let result = ae.decrypt_in_place(&mut decrypted, &nonce, b"", tag.as_ref());
300        assert!(result.is_err());
301        assert!(decrypted.iter().all(|b| *b == 0));
302    }
303
304    #[test]
305    fn tampered_ciphertext_fails() {
306        let key = [0x55u8; 16];
307        let nonce = [0xAAu8; 16];
308
309        let ae = AsconAead128::new(&key);
310        let mut ct = b"secret".to_vec();
311        let tag = ae.encrypt_in_place(&mut ct, &nonce, b"");
312        ct[3] ^= 1;
313
314        let mut decrypted = ct.clone();
315        let result = ae.decrypt_in_place(&mut decrypted, &nonce, b"", tag.as_ref());
316        assert!(result.is_err());
317    }
318
319    #[test]
320    fn wrong_nonce_fails() {
321        let key = [0x55u8; 16];
322
323        let ae = AsconAead128::new(&key);
324        let mut ct = b"secret".to_vec();
325        let tag = ae.encrypt_in_place(&mut ct, &[0u8; 16], b"");
326        let mut decrypted = ct.clone();
327        let result = ae.decrypt_in_place(&mut decrypted, &[1u8; 16], b"", tag.as_ref());
328        assert!(result.is_err());
329    }
330
331    #[test]
332    fn wrong_ad_fails() {
333        let key = [0x55u8; 16];
334        let nonce = [0xAAu8; 16];
335
336        let ae = AsconAead128::new(&key);
337        let mut ct = b"secret".to_vec();
338        let tag = ae.encrypt_in_place(&mut ct, &nonce, b"correct ad");
339        let mut decrypted = ct.clone();
340        let result = ae.decrypt_in_place(&mut decrypted, &nonce, b"wrong ad", tag.as_ref());
341        assert!(result.is_err());
342    }
343
344    #[test]
345    fn wrong_key_fails() {
346        let nonce = [0xAAu8; 16];
347
348        let ae = AsconAead128::new(&[0x55u8; 16]);
349        let mut ct = b"secret".to_vec();
350        let tag = ae.encrypt_in_place(&mut ct, &nonce, b"");
351        let mut decrypted = ct.clone();
352        let ae2 = AsconAead128::new(&[0xAAu8; 16]);
353        let result = ae2.decrypt_in_place(&mut decrypted, &nonce, b"", tag.as_ref());
354        assert!(result.is_err());
355    }
356
357    #[test]
358    fn empty_ad_with_data() {
359        let key = [0x55u8; 16];
360        let nonce = [0xAAu8; 16];
361
362        let ae = AsconAead128::new(&key);
363        let mut ct = b"data with empty AD".to_vec();
364        let tag = ae.encrypt_in_place(&mut ct, &nonce, b"");
365
366        let mut decrypted = ct.clone();
367        ae.decrypt_in_place(&mut decrypted, &nonce, b"", tag.as_ref()).unwrap();
368        assert_eq!(decrypted, b"data with empty AD");
369    }
370
371    #[test]
372    fn kat_vectors() {
373        let data = include_str!("../../testdata/ascon/LWC_AEAD_KAT_128_128.txt");
374        let mut count = 0u64;
375        let mut key = None;
376        let mut nonce = None;
377        let mut pt_hex = String::new();
378        let mut ad_hex = String::new();
379        let mut ct_hex = String::new();
380
381        for line in data.lines() {
382            let line = line.trim();
383            if line.is_empty() {
384                if let (Some(k), Some(n)) = (&key, &nonce) {
385                    let key_bytes: [u8; 16] = hex::decode(k).unwrap().try_into().unwrap();
386                    let nonce_bytes: [u8; 16] = hex::decode(n).unwrap().try_into().unwrap();
387                    let pt = hex::decode(&pt_hex).unwrap();
388                    let ct_bytes = hex::decode(&ct_hex).unwrap();
389                    let aad = hex::decode(&ad_hex).unwrap();
390
391                    let expected_ct = &ct_bytes[..pt.len()];
392                    let expected_tag = &ct_bytes[pt.len()..];
393
394                    let ae = AsconAead128::new(&key_bytes);
395                    let mut enc = pt.clone();
396                    let tag = ae.encrypt_in_place(&mut enc, &nonce_bytes, &aad);
397                    assert_eq!(enc, expected_ct, "KAT AEAD Count={count} ct mismatch");
398                    assert_eq!(tag.as_ref(), expected_tag, "KAT AEAD Count={count} tag mismatch");
399
400                    let mut dec = expected_ct.to_vec();
401                    ae.decrypt_in_place(&mut dec, &nonce_bytes, &aad, expected_tag)
402                        .unwrap_or_else(|e| panic!("KAT AEAD Count={count} decrypt failed: {e:?}"));
403                    assert_eq!(dec, pt, "KAT AEAD Count={count} decrypt pt mismatch");
404                }
405                // Reset
406                key = None;
407                nonce = None;
408                pt_hex.clear();
409                ad_hex.clear();
410                ct_hex.clear();
411                continue;
412            }
413
414            if line.starts_with("Count = ") {
415                count = line["Count = ".len()..].parse().unwrap();
416                continue;
417            }
418            if line.starts_with("Key = ") {
419                key = Some(line[6..].to_string());
420                continue;
421            }
422            if line.starts_with("Nonce = ") {
423                nonce = Some(line[8..].to_string());
424                continue;
425            }
426            if line.starts_with("PT = ") {
427                pt_hex = line[5..].to_string();
428                continue;
429            }
430            if line.starts_with("AD = ") {
431                ad_hex = line[5..].to_string();
432                continue;
433            }
434            if line.starts_with("CT = ") {
435                ct_hex = line[5..].to_string();
436                continue;
437            }
438        }
439
440        // Process last entry
441        if let (Some(k), Some(n)) = (&key, &nonce) {
442            let key_bytes: [u8; 16] = hex::decode(k).unwrap().try_into().unwrap();
443            let nonce_bytes: [u8; 16] = hex::decode(n).unwrap().try_into().unwrap();
444            let pt = hex::decode(&pt_hex).unwrap();
445            let ct_bytes = hex::decode(&ct_hex).unwrap();
446            let aad = hex::decode(&ad_hex).unwrap();
447
448            let expected_ct = &ct_bytes[..pt.len()];
449            let expected_tag = &ct_bytes[pt.len()..];
450
451            let ae = AsconAead128::new(&key_bytes);
452            let mut enc = pt.clone();
453            let tag = ae.encrypt_in_place(&mut enc, &nonce_bytes, &aad);
454            assert_eq!(enc, expected_ct, "KAT AEAD Count={count} ct mismatch");
455            assert_eq!(tag.as_ref(), expected_tag, "KAT AEAD Count={count} tag mismatch");
456
457            let mut dec = expected_ct.to_vec();
458            ae.decrypt_in_place(&mut dec, &nonce_bytes, &aad, expected_tag)
459                .unwrap_or_else(|e| panic!("KAT AEAD Count={count} decrypt failed: {e:?}"));
460            assert_eq!(dec, pt, "KAT AEAD Count={count} decrypt pt mismatch");
461        }
462    }
463}