1use ic_core::traits::{Aead, Algorithm, Mac, SelfTest};
10use ic_core::{ensure, Result, Zeroize};
11
12const SIGMA: [u32; 4] = [0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574];
13
14#[inline(always)]
15fn quarter_round(s: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize) {
16 s[a] = s[a].wrapping_add(s[b]);
17 s[d] = (s[d] ^ s[a]).rotate_left(16);
18 s[c] = s[c].wrapping_add(s[d]);
19 s[b] = (s[b] ^ s[c]).rotate_left(12);
20 s[a] = s[a].wrapping_add(s[b]);
21 s[d] = (s[d] ^ s[a]).rotate_left(8);
22 s[c] = s[c].wrapping_add(s[d]);
23 s[b] = (s[b] ^ s[c]).rotate_left(7);
24}
25
26fn chacha20_block(key: &[u8; 32], counter: u32, nonce: &[u8; 12], out: &mut [u8; 64]) {
28 let mut state = [0u32; 16];
29 state[..4].copy_from_slice(&SIGMA);
30 for i in 0..8 {
31 state[4 + i] =
32 u32::from_le_bytes([key[i * 4], key[i * 4 + 1], key[i * 4 + 2], key[i * 4 + 3]]);
33 }
34 state[12] = counter;
35 for i in 0..3 {
36 state[13 + i] = u32::from_le_bytes([
37 nonce[i * 4],
38 nonce[i * 4 + 1],
39 nonce[i * 4 + 2],
40 nonce[i * 4 + 3],
41 ]);
42 }
43
44 let mut working = state;
45 for _ in 0..10 {
46 quarter_round(&mut working, 0, 4, 8, 12);
48 quarter_round(&mut working, 1, 5, 9, 13);
49 quarter_round(&mut working, 2, 6, 10, 14);
50 quarter_round(&mut working, 3, 7, 11, 15);
51 quarter_round(&mut working, 0, 5, 10, 15);
53 quarter_round(&mut working, 1, 6, 11, 12);
54 quarter_round(&mut working, 2, 7, 8, 13);
55 quarter_round(&mut working, 3, 4, 9, 14);
56 }
57 for i in 0..16 {
58 let v = working[i].wrapping_add(state[i]);
59 out[i * 4..i * 4 + 4].copy_from_slice(&v.to_le_bytes());
60 }
61 working.zeroize();
62 state.zeroize();
63}
64
65#[cfg(all(target_arch = "x86_64", feature = "std"))]
67mod avx2;
68
69#[cfg(all(target_arch = "x86_64", feature = "std"))]
71fn avx2() -> bool {
72 use core::sync::atomic::{AtomicU8, Ordering};
73 static CACHED: AtomicU8 = AtomicU8::new(0);
74 match CACHED.load(Ordering::Relaxed) {
75 1 => true,
76 2 => false,
77 _ => {
78 let have = std::is_x86_feature_detected!("avx2");
79 CACHED.store(u8::from(!have) + 1, Ordering::Relaxed);
80 have
81 }
82 }
83}
84
85pub fn chacha20_xor(key: &[u8], nonce: &[u8], counter: u32, data: &mut [u8]) -> Result<()> {
87 ic_core::module::operational()?;
88 ensure!(
89 key.len() == 32,
90 InvalidLength,
91 "chacha20 key must be 32 bytes"
92 );
93 ensure!(
94 nonce.len() == 12,
95 InvalidLength,
96 "chacha20 nonce must be 12 bytes"
97 );
98 let mut k = [0u8; 32];
99 k.copy_from_slice(key);
100 let mut n = [0u8; 12];
101 n.copy_from_slice(nonce);
102
103 #[cfg(all(target_arch = "x86_64", feature = "std"))]
106 let mut done = 0usize;
107 #[cfg(all(target_arch = "x86_64", feature = "std"))]
108 if avx2() {
109 let groups = data.len() / avx2::STRIDE;
110 let needed = (groups as u64) * (avx2::LANES as u64);
115 if (counter as u64).checked_add(needed).is_some() {
116 for g in 0..groups {
117 let ctr = counter.wrapping_add((g * avx2::LANES) as u32);
118 let at = g * avx2::STRIDE;
119 unsafe { avx2::eight_blocks(&k, &n, ctr, &mut data[at..at + avx2::STRIDE]) };
122 }
123 done = groups * avx2::STRIDE;
124 }
125 }
126 #[cfg(all(target_arch = "x86_64", feature = "std"))]
127 let (data, counter) = {
128 let advanced = counter.wrapping_add((done / 64) as u32);
129 (&mut data[done..], advanced)
130 };
131
132 let mut block = [0u8; 64];
133 for (i, chunk) in data.chunks_mut(64).enumerate() {
134 let ctr = counter
135 .checked_add(i as u32)
136 .ok_or(ic_core::err!(CounterExhausted, "chacha20 block counter"))?;
137 chacha20_block(&k, ctr, &n, &mut block);
138 for (d, b) in chunk.iter_mut().zip(block.iter()) {
139 *d ^= b;
140 }
141 }
142 block.zeroize();
143 k.zeroize();
144 Ok(())
145}
146
147#[derive(Clone)]
151pub struct Poly1305 {
152 r: [u32; 5],
153 s: [u32; 4],
154 powers: [[u32; 5]; 3],
169 acc: [u32; 5],
170 buf: [u8; 16],
171 buffered: usize,
172}
173
174impl Drop for Poly1305 {
175 fn drop(&mut self) {
176 self.r.zeroize();
177 self.s.zeroize();
178 for p in &mut self.powers {
179 p.zeroize();
180 }
181 self.acc.zeroize();
182 self.buf.zeroize();
183 }
184}
185
186impl Algorithm for Poly1305 {
187 const ID: &'static str = "poly1305";
188 const NAME: &'static str = "Poly1305";
189}
190
191impl Poly1305 {
192 fn absorb_block(&mut self, block: &[u8]) {
195 let mut b = [0u8; 16];
196 b[..block.len()].copy_from_slice(block);
197 let pad = if block.len() < 16 {
198 b[block.len()] = 1;
199 0
200 } else {
201 1 << 24
202 };
203
204 let t0 = u32::from_le_bytes([b[0], b[1], b[2], b[3]]);
205 let t1 = u32::from_le_bytes([b[4], b[5], b[6], b[7]]);
206 let t2 = u32::from_le_bytes([b[8], b[9], b[10], b[11]]);
207 let t3 = u32::from_le_bytes([b[12], b[13], b[14], b[15]]);
208
209 self.acc[0] += t0 & 0x3ff_ffff;
210 self.acc[1] += ((t0 >> 26) | (t1 << 6)) & 0x3ff_ffff;
211 self.acc[2] += ((t1 >> 20) | (t2 << 12)) & 0x3ff_ffff;
212 self.acc[3] += ((t2 >> 14) | (t3 << 18)) & 0x3ff_ffff;
213 self.acc[4] += (t3 >> 8) | pad;
214
215 self.multiply_by_r();
216 }
217
218 fn multiply_by_r(&mut self) {
219 self.acc = reduce(mul_unreduced(self.acc, self.r, five_times(self.r)));
220 }
221
222 fn absorb4(&mut self, blocks: &[u8]) {
226 debug_assert_eq!(blocks.len(), 64);
227 let mut m = [[0u32; 5]; 4];
228 for (i, slot) in m.iter_mut().enumerate() {
229 let b = &blocks[i * 16..(i + 1) * 16];
230 let t0 = u32::from_le_bytes([b[0], b[1], b[2], b[3]]);
231 let t1 = u32::from_le_bytes([b[4], b[5], b[6], b[7]]);
232 let t2 = u32::from_le_bytes([b[8], b[9], b[10], b[11]]);
233 let t3 = u32::from_le_bytes([b[12], b[13], b[14], b[15]]);
234 *slot = [
236 t0 & 0x3ff_ffff,
237 ((t0 >> 26) | (t1 << 6)) & 0x3ff_ffff,
238 ((t1 >> 20) | (t2 << 12)) & 0x3ff_ffff,
239 ((t2 >> 14) | (t3 << 18)) & 0x3ff_ffff,
240 (t3 >> 8) | (1 << 24),
241 ];
242 }
243
244 for (limb, a) in m[0].iter_mut().zip(self.acc) {
246 *limb += a;
247 }
248
249 let multipliers = [self.powers[2], self.powers[1], self.powers[0], self.r];
250 let mut d = [0u64; 5];
251 for (block, r) in m.iter().zip(multipliers) {
252 let part = mul_unreduced(*block, r, five_times(r));
253 for (acc, term) in d.iter_mut().zip(part) {
254 *acc += term;
255 }
256 }
257 self.acc = reduce(d);
258 }
259}
260
261fn five_times(r: [u32; 5]) -> [u32; 4] {
263 [r[1] * 5, r[2] * 5, r[3] * 5, r[4] * 5]
264}
265
266fn mul_unreduced(h: [u32; 5], r: [u32; 5], s: [u32; 4]) -> [u64; 5] {
272 let m = |a: u32, b: u32| a as u64 * b as u64;
273 [
274 m(h[0], r[0]) + m(h[1], s[3]) + m(h[2], s[2]) + m(h[3], s[1]) + m(h[4], s[0]),
275 m(h[0], r[1]) + m(h[1], r[0]) + m(h[2], s[3]) + m(h[3], s[2]) + m(h[4], s[1]),
276 m(h[0], r[2]) + m(h[1], r[1]) + m(h[2], r[0]) + m(h[3], s[3]) + m(h[4], s[2]),
277 m(h[0], r[3]) + m(h[1], r[2]) + m(h[2], r[1]) + m(h[3], r[0]) + m(h[4], s[3]),
278 m(h[0], r[4]) + m(h[1], r[3]) + m(h[2], r[2]) + m(h[3], r[1]) + m(h[4], r[0]),
279 ]
280}
281
282fn reduce(d: [u64; 5]) -> [u32; 5] {
296 const MASK: u64 = 0x3ff_ffff;
297 let mut c = d[0] >> 26;
298 let a0 = d[0] & MASK;
299 let d1 = d[1] + c;
300 c = d1 >> 26;
301 let a1 = d1 & MASK;
302 let d2 = d[2] + c;
303 c = d2 >> 26;
304 let a2 = d2 & MASK;
305 let d3 = d[3] + c;
306 c = d3 >> 26;
307 let a3 = d3 & MASK;
308 let d4 = d[4] + c;
309 c = d4 >> 26;
310 let a4 = d4 & MASK;
311 let folded = a0 + c * 5;
314 let a0 = folded & MASK;
315 let a1 = a1 + (folded >> 26);
318 [a0 as u32, a1 as u32, a2 as u32, a3 as u32, a4 as u32]
319}
320
321impl Mac for Poly1305 {
322 type Tag = [u8; 16];
323 const TAG_LEN: usize = 16;
324
325 fn new(key: &[u8]) -> Result<Self> {
326 ic_core::module::operational()?;
327 ensure!(
328 key.len() == 32,
329 InvalidLength,
330 "poly1305 key must be 32 bytes"
331 );
332 let t0 = u32::from_le_bytes([key[0], key[1], key[2], key[3]]);
333 let t1 = u32::from_le_bytes([key[4], key[5], key[6], key[7]]);
334 let t2 = u32::from_le_bytes([key[8], key[9], key[10], key[11]]);
335 let t3 = u32::from_le_bytes([key[12], key[13], key[14], key[15]]);
336 let r = [
338 t0 & 0x3ff_ffff,
339 ((t0 >> 26) | (t1 << 6)) & 0x3ff_ff03,
340 ((t1 >> 20) | (t2 << 12)) & 0x3ff_c0ff,
341 ((t2 >> 14) | (t3 << 18)) & 0x3f0_3fff,
342 (t3 >> 8) & 0x000_fffff,
343 ];
344 let s = [
345 u32::from_le_bytes([key[16], key[17], key[18], key[19]]),
346 u32::from_le_bytes([key[20], key[21], key[22], key[23]]),
347 u32::from_le_bytes([key[24], key[25], key[26], key[27]]),
348 u32::from_le_bytes([key[28], key[29], key[30], key[31]]),
349 ];
350 let rr = five_times(r);
353 let mut powers = [[0u32; 5]; 3];
354 let mut p = r;
355 for slot in powers.iter_mut() {
356 p = reduce(mul_unreduced(p, r, rr));
357 *slot = p;
358 }
359
360 Ok(Self {
361 r,
362 s,
363 powers,
364 acc: [0u32; 5],
365 buf: [0u8; 16],
366 buffered: 0,
367 })
368 }
369
370 fn update(&mut self, mut data: &[u8]) {
371 if self.buffered > 0 {
372 let take = core::cmp::min(16 - self.buffered, data.len());
373 self.buf[self.buffered..self.buffered + take].copy_from_slice(&data[..take]);
374 self.buffered += take;
375 data = &data[take..];
376 if self.buffered < 16 {
377 return;
378 }
379 let block = self.buf;
380 self.absorb_block(&block);
381 self.buffered = 0;
382 }
383 while data.len() >= 64 {
384 self.absorb4(&data[..64]);
385 data = &data[64..];
386 }
387 let mut chunks = data.chunks_exact(16);
388 for block in &mut chunks {
389 self.absorb_block(block);
390 }
391 let rest = chunks.remainder();
392 self.buf[..rest.len()].copy_from_slice(rest);
393 self.buffered = rest.len();
394 }
395
396 fn finalize(mut self) -> [u8; 16] {
397 if self.buffered > 0 {
398 let n = self.buffered;
399 let block = self.buf;
400 self.absorb_block(&block[..n]);
401 self.buffered = 0;
402 }
403
404 let mut h = self.acc;
406 let mut c = h[1] >> 26;
407 h[1] &= 0x3ff_ffff;
408 h[2] += c;
409 c = h[2] >> 26;
410 h[2] &= 0x3ff_ffff;
411 h[3] += c;
412 c = h[3] >> 26;
413 h[3] &= 0x3ff_ffff;
414 h[4] += c;
415 c = h[4] >> 26;
416 h[4] &= 0x3ff_ffff;
417 h[0] += c * 5;
418 c = h[0] >> 26;
419 h[0] &= 0x3ff_ffff;
420 h[1] += c;
421
422 let mut g = [0u32; 5];
424 let mut carry = 5u32;
425 for i in 0..4 {
426 let v = h[i] + carry;
427 g[i] = v & 0x3ff_ffff;
428 carry = v >> 26;
429 }
430 g[4] = h[4].wrapping_add(carry).wrapping_sub(1 << 26);
433 let mask = ((g[4] >> 31) ^ 1).wrapping_neg();
434 for i in 0..5 {
435 h[i] = (h[i] & !mask) | (g[i] & mask);
436 }
437
438 let h0 = h[0] | (h[1] << 26);
440 let h1 = (h[1] >> 6) | (h[2] << 20);
441 let h2 = (h[2] >> 12) | (h[3] << 14);
442 let h3 = (h[3] >> 18) | (h[4] << 8);
443
444 let mut f = h0 as u64 + self.s[0] as u64;
445 let r0 = f as u32;
446 f = h1 as u64 + self.s[1] as u64 + (f >> 32);
447 let r1 = f as u32;
448 f = h2 as u64 + self.s[2] as u64 + (f >> 32);
449 let r2 = f as u32;
450 f = h3 as u64 + self.s[3] as u64 + (f >> 32);
451 let r3 = f as u32;
452
453 let mut tag = [0u8; 16];
454 tag[0..4].copy_from_slice(&r0.to_le_bytes());
455 tag[4..8].copy_from_slice(&r1.to_le_bytes());
456 tag[8..12].copy_from_slice(&r2.to_le_bytes());
457 tag[12..16].copy_from_slice(&r3.to_le_bytes());
458 tag
459 }
460}
461
462impl SelfTest for Poly1305 {
463 fn self_test() -> Result<()> {
464 let mut key = [0u8; 32];
466 ic_core::codec::hex_decode(
467 b"85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b",
468 &mut key,
469 )?;
470 let tag = <Self as Mac>::mac(&key, b"Cryptographic Forum Research Group")?;
471 let mut want = [0u8; 16];
472 ic_core::codec::hex_decode(b"a8061dc1305136c6c22b8baf0c0127a9", &mut want)?;
473 ensure!(ic_core::ct::verify(&want, &tag), SelfTestFailed, "poly1305");
474 Ok(())
475 }
476}
477
478pub struct ChaCha20Poly1305 {
480 key: [u8; 32],
481}
482
483impl Drop for ChaCha20Poly1305 {
484 fn drop(&mut self) {
485 self.key.zeroize();
486 }
487}
488
489impl Algorithm for ChaCha20Poly1305 {
490 const ID: &'static str = "chacha20-poly1305";
491 const NAME: &'static str = "ChaCha20-Poly1305";
492}
493
494impl ChaCha20Poly1305 {
495 fn poly_key(&self, nonce: &[u8]) -> Result<[u8; 32]> {
497 let mut block = [0u8; 64];
498 let mut n = [0u8; 12];
499 ensure!(nonce.len() == 12, InvalidLength, "chacha20-poly1305 nonce");
500 n.copy_from_slice(nonce);
501 chacha20_block(&self.key, 0, &n, &mut block);
502 let mut k = [0u8; 32];
503 k.copy_from_slice(&block[..32]);
504 block.zeroize();
505 Ok(k)
506 }
507
508 fn tag(&self, nonce: &[u8], aad: &[u8], ciphertext: &[u8]) -> Result<[u8; 16]> {
510 let mut poly_key = self.poly_key(nonce)?;
511 let mut m = Poly1305::new(&poly_key)?;
512 poly_key.zeroize();
513
514 m.update(aad);
515 m.update(&[0u8; 16][..(16 - aad.len() % 16) % 16]);
516 m.update(ciphertext);
517 m.update(&[0u8; 16][..(16 - ciphertext.len() % 16) % 16]);
518
519 let mut lens = [0u8; 16];
520 lens[..8].copy_from_slice(&(aad.len() as u64).to_le_bytes());
521 lens[8..].copy_from_slice(&(ciphertext.len() as u64).to_le_bytes());
522 m.update(&lens);
523 Ok(m.finalize())
524 }
525}
526
527impl Aead for ChaCha20Poly1305 {
528 const KEY_LEN: usize = 32;
529 const NONCE_LEN: usize = 12;
530 const TAG_LEN: usize = 16;
531
532 fn new(key: &[u8]) -> Result<Self> {
533 ic_core::module::operational()?;
534 ensure!(key.len() == 32, InvalidLength, "chacha20-poly1305 key");
535 let mut k = [0u8; 32];
536 k.copy_from_slice(key);
537 Ok(Self { key: k })
538 }
539
540 fn seal_detached(
541 &self,
542 nonce: &[u8],
543 aad: &[u8],
544 in_out: &mut [u8],
545 tag: &mut [u8],
546 ) -> Result<()> {
547 ic_core::module::operational()?;
548 ensure!(
549 tag.len() == 16,
550 InvalidLength,
551 "chacha20-poly1305 tag buffer"
552 );
553 chacha20_xor(&self.key, nonce, 1, in_out)?;
555 let t = self.tag(nonce, aad, in_out)?;
556 tag.copy_from_slice(&t);
557 Ok(())
558 }
559
560 fn open_detached(&self, nonce: &[u8], aad: &[u8], in_out: &mut [u8], tag: &[u8]) -> Result<()> {
561 ic_core::module::operational()?;
562 ensure!(tag.len() == 16, InvalidLength, "chacha20-poly1305 tag");
563 let expected = self.tag(nonce, aad, in_out)?;
564 if !ic_core::ct::verify(&expected, tag) {
565 in_out.zeroize();
566 return Err(ic_core::err!(AuthenticationFailed, "chacha20-poly1305"));
567 }
568 chacha20_xor(&self.key, nonce, 1, in_out)
569 }
570}
571
572impl SelfTest for ChaCha20Poly1305 {
573 fn self_test() -> Result<()> {
574 let key = [0x42u8; 32];
575 let nonce = [0x24u8; 12];
576 let c = <Self as Aead>::new(&key)?;
577 let mut buf = *b"self-test payload";
578 let original = buf;
579 let mut tag = [0u8; 16];
580 c.seal_detached(&nonce, b"aad", &mut buf, &mut tag)?;
581 ensure!(buf != original, SelfTestFailed, "chacha20-poly1305");
582 c.open_detached(&nonce, b"aad", &mut buf, &tag)?;
583 ensure!(buf == original, SelfTestFailed, "chacha20-poly1305");
584 Ok(())
585 }
586}
587
588#[cfg(test)]
589mod tests {
590 use super::*;
591
592 #[cfg(all(target_arch = "x86_64", feature = "std"))]
604 #[test]
605 fn the_avx2_keystream_matches_the_block_function() {
606 if !avx2() {
607 println!("no AVX2 on this CPU; the backend was not exercised");
608 return;
609 }
610
611 let key = [0x5au8; 32];
612 let nonce = [0x21u8; 12];
613
614 let mut checked = 0;
615 for len in [
616 0usize, 1, 63, 64, 65, 127, 511, 512, 513, 575, 576, 1023, 1024, 1025, 4096, 4097,
617 ] {
618 for counter in [0u32, 1, 7, 8, 9, 1000] {
619 let mut actual = std::vec![0u8; len];
620 chacha20_xor(&key, &nonce, counter, &mut actual).unwrap();
621
622 let mut expect = std::vec![0u8; len];
624 let mut block = [0u8; 64];
625 for (i, chunk) in expect.chunks_mut(64).enumerate() {
626 chacha20_block(&key, counter + i as u32, &nonce, &mut block);
627 for (d, b) in chunk.iter_mut().zip(block.iter()) {
628 *d ^= b;
629 }
630 }
631
632 assert_eq!(
633 actual, expect,
634 "AVX2 and scalar keystreams differ at {len} bytes, counter {counter}"
635 );
636 checked += 1;
637 }
638 }
639 assert_eq!(checked, 16 * 6, "the comparison did not run");
640 }
641
642 #[cfg(all(target_arch = "x86_64", feature = "std"))]
644 #[test]
645 fn the_active_chacha_path_is_reported() {
646 println!(
647 "chacha20 backend: {}",
648 if avx2() { "AVX2 (8 blocks)" } else { "scalar" }
649 );
650 }
651 use ic_core::codec::{hex, unhex};
652
653 #[test]
654 fn rfc8439_chacha20_keystream_vector() {
655 let key: Vec<u8> = (0..32u8).collect();
656 let mut nonce = [0u8; 12];
657 nonce[3] = 0x09;
658 nonce[7] = 0x4a;
659 let mut data = [0u8; 64];
660 chacha20_xor(&key, &nonce, 1, &mut data).unwrap();
661 assert_eq!(
662 hex(&data),
663 "10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4ed2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e"
664 );
665 }
666
667 #[test]
668 fn rfc8439_chacha20_encryption_vector() {
669 let key: Vec<u8> = (0..32u8).collect();
670 let mut nonce = [0u8; 12];
671 nonce[7] = 0x4a;
672 let mut data =
673 b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.".to_vec();
674 chacha20_xor(&key, &nonce, 1, &mut data).unwrap();
675 assert_eq!(
676 hex(&data),
677 "6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d624e65152ab8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74a35be6b40b8eedf2785e42874d"
678 );
679 }
680
681 #[test]
682 fn rfc8439_poly1305_vector() {
683 let key =
684 unhex("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b").unwrap();
685 let tag = Poly1305::mac(&key, b"Cryptographic Forum Research Group").unwrap();
686 assert_eq!(hex(&tag), "a8061dc1305136c6c22b8baf0c0127a9");
687 }
688
689 #[test]
690 fn poly1305_streaming_matches_one_shot() {
691 let key = [0x11u8; 32];
692 let data: Vec<u8> = (0..100u8).collect();
693 for split in [0usize, 1, 15, 16, 17, 50, 100] {
694 let mut m = Poly1305::new(&key).unwrap();
695 m.update(&data[..split]);
696 m.update(&data[split..]);
697 assert_eq!(
698 m.finalize(),
699 Poly1305::mac(&key, &data).unwrap(),
700 "split at {split}"
701 );
702 }
703 }
704
705 #[test]
706 fn rfc8439_aead_vector() {
707 let key =
708 unhex("808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f").unwrap();
709 let nonce = unhex("070000004041424344454647").unwrap();
710 let aad = unhex("50515253c0c1c2c3c4c5c6c7").unwrap();
711 let mut buf =
712 b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.".to_vec();
713
714 let c = ChaCha20Poly1305::new(&key).unwrap();
715 let mut tag = [0u8; 16];
716 c.seal_detached(&nonce, &aad, &mut buf, &mut tag).unwrap();
717 assert_eq!(
718 hex(&buf),
719 "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116"
720 );
721 assert_eq!(hex(&tag), "1ae10b594f09e26a7e902ecbd0600691");
722
723 c.open_detached(&nonce, &aad, &mut buf, &tag).unwrap();
724 assert_eq!(
725 &buf[..],
726 &b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."[..]
727 );
728 }
729
730 #[test]
731 fn aead_rejects_tampering_and_wipes() {
732 let c = ChaCha20Poly1305::new(&[1u8; 32]).unwrap();
733 let mut buf = b"secret".to_vec();
734 let mut tag = [0u8; 16];
735 c.seal_detached(&[2u8; 12], b"", &mut buf, &mut tag)
736 .unwrap();
737 tag[15] ^= 0x80;
738 assert!(c.open_detached(&[2u8; 12], b"", &mut buf, &tag).is_err());
739 assert_eq!(buf, vec![0u8; 6]);
740 }
741
742 #[test]
769 fn the_grouped_poly1305_agrees_with_the_serial_one_at_its_largest_carries() {
770 let mut keys = Vec::new();
771 let mut largest = [0xffu8; 32];
773 largest[..16].copy_from_slice(&[
774 0xff, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x0f, 0xfc, 0xff,
775 0xff, 0x0f,
776 ]);
777 keys.push(largest);
778 keys.push([0xffu8; 32]);
779 for seed in 0u8..62 {
780 let mut key = [0u8; 32];
781 for (i, b) in key.iter_mut().enumerate() {
782 *b = (i as u8)
783 .wrapping_mul(0x9d)
784 .wrapping_add(seed.wrapping_mul(0x3b))
785 ^ seed;
786 }
787 keys.push(key);
788 }
789
790 let mut checked = 0;
791 for key in &keys {
792 for fill in [0xffu8, 0xfe, 0x80] {
793 for len in [64usize, 128, 129, 192, 256, 1024] {
794 let data = vec![fill; len];
795 let grouped = Poly1305::mac(key, &data).unwrap();
796
797 let mut serial = Poly1305::new(key).unwrap();
798 for chunk in data.chunks(16) {
799 serial.absorb_block(chunk);
800 }
801 assert_eq!(
802 grouped,
803 serial.finalize(),
804 "grouped and serial Poly1305 differ: {len} bytes of {fill:#04x}"
805 );
806 checked += 1;
807 }
808 }
809 }
810 assert_eq!(checked, 64 * 3 * 6, "the comparison did not run");
811 }
812
813 #[test]
814 fn the_grouped_poly1305_agrees_with_the_serial_one() {
815 let key = [0x8eu8; 32];
816
817 let mut checked = 0;
818 for len in [
819 0usize, 1, 15, 16, 17, 31, 32, 63, 64, 65, 79, 80, 127, 128, 129, 255, 256, 1023, 1024,
820 1025,
821 ] {
822 let data: Vec<u8> = (0..len)
823 .map(|i| ((i as u64).wrapping_mul(0x9e37_79b9) >> 5) as u8)
824 .collect();
825
826 let grouped = Poly1305::mac(&key, &data).unwrap();
827
828 let mut serial = Poly1305::new(&key).unwrap();
830 for chunk in data.chunks(16) {
831 serial.absorb_block(chunk);
832 }
833 let serial = serial.finalize();
834
835 assert_eq!(
836 grouped, serial,
837 "grouped and serial Poly1305 differ at {len} bytes"
838 );
839 checked += 1;
840 }
841 assert_eq!(checked, 20, "the comparison did not run");
842 }
843
844 #[test]
850 fn the_poly1305_powers_are_powers_of_r() {
851 let m = Poly1305::new(&[0x3bu8; 32]).unwrap();
852 let mut expect = m.r;
853 for (i, stored) in m.powers.iter().enumerate() {
854 expect = reduce(mul_unreduced(expect, m.r, five_times(m.r)));
855 assert_eq!(*stored, expect, "power {} is not r^{}", i, i + 2);
856 }
857 assert_ne!(m.powers[0], m.powers[1]);
858 assert_ne!(m.powers[0], m.r);
859 }
860
861 #[test]
862 fn self_tests_pass() {
863 Poly1305::self_test().unwrap();
864 ChaCha20Poly1305::self_test().unwrap();
865 }
866}