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 ensure!(
88 key.len() == 32,
89 InvalidLength,
90 "chacha20 key must be 32 bytes"
91 );
92 ensure!(
93 nonce.len() == 12,
94 InvalidLength,
95 "chacha20 nonce must be 12 bytes"
96 );
97 let mut k = [0u8; 32];
98 k.copy_from_slice(key);
99 let mut n = [0u8; 12];
100 n.copy_from_slice(nonce);
101
102 #[cfg(all(target_arch = "x86_64", feature = "std"))]
105 let mut done = 0usize;
106 #[cfg(all(target_arch = "x86_64", feature = "std"))]
107 if avx2() {
108 let groups = data.len() / avx2::STRIDE;
109 let needed = (groups as u64) * (avx2::LANES as u64);
114 if (counter as u64).checked_add(needed).is_some() {
115 for g in 0..groups {
116 let ctr = counter.wrapping_add((g * avx2::LANES) as u32);
117 let at = g * avx2::STRIDE;
118 unsafe { avx2::eight_blocks(&k, &n, ctr, &mut data[at..at + avx2::STRIDE]) };
121 }
122 done = groups * avx2::STRIDE;
123 }
124 }
125 #[cfg(all(target_arch = "x86_64", feature = "std"))]
126 let (data, counter) = {
127 let advanced = counter.wrapping_add((done / 64) as u32);
128 (&mut data[done..], advanced)
129 };
130
131 let mut block = [0u8; 64];
132 for (i, chunk) in data.chunks_mut(64).enumerate() {
133 let ctr = counter
134 .checked_add(i as u32)
135 .ok_or(ic_core::err!(CounterExhausted, "chacha20 block counter"))?;
136 chacha20_block(&k, ctr, &n, &mut block);
137 for (d, b) in chunk.iter_mut().zip(block.iter()) {
138 *d ^= b;
139 }
140 }
141 block.zeroize();
142 k.zeroize();
143 Ok(())
144}
145
146#[derive(Clone)]
150pub struct Poly1305 {
151 r: [u32; 5],
152 s: [u32; 4],
153 powers: [[u32; 5]; 3],
168 acc: [u32; 5],
169 buf: [u8; 16],
170 buffered: usize,
171}
172
173impl Drop for Poly1305 {
174 fn drop(&mut self) {
175 self.r.zeroize();
176 self.s.zeroize();
177 for p in &mut self.powers {
178 p.zeroize();
179 }
180 self.acc.zeroize();
181 self.buf.zeroize();
182 }
183}
184
185impl Algorithm for Poly1305 {
186 const ID: &'static str = "poly1305";
187 const NAME: &'static str = "Poly1305";
188}
189
190impl Poly1305 {
191 fn absorb_block(&mut self, block: &[u8]) {
194 let mut b = [0u8; 16];
195 b[..block.len()].copy_from_slice(block);
196 let pad = if block.len() < 16 {
197 b[block.len()] = 1;
198 0
199 } else {
200 1 << 24
201 };
202
203 let t0 = u32::from_le_bytes([b[0], b[1], b[2], b[3]]);
204 let t1 = u32::from_le_bytes([b[4], b[5], b[6], b[7]]);
205 let t2 = u32::from_le_bytes([b[8], b[9], b[10], b[11]]);
206 let t3 = u32::from_le_bytes([b[12], b[13], b[14], b[15]]);
207
208 self.acc[0] += t0 & 0x3ff_ffff;
209 self.acc[1] += ((t0 >> 26) | (t1 << 6)) & 0x3ff_ffff;
210 self.acc[2] += ((t1 >> 20) | (t2 << 12)) & 0x3ff_ffff;
211 self.acc[3] += ((t2 >> 14) | (t3 << 18)) & 0x3ff_ffff;
212 self.acc[4] += (t3 >> 8) | pad;
213
214 self.multiply_by_r();
215 }
216
217 fn multiply_by_r(&mut self) {
218 self.acc = reduce(mul_unreduced(self.acc, self.r, five_times(self.r)));
219 }
220
221 fn absorb4(&mut self, blocks: &[u8]) {
225 debug_assert_eq!(blocks.len(), 64);
226 let mut m = [[0u32; 5]; 4];
227 for (i, slot) in m.iter_mut().enumerate() {
228 let b = &blocks[i * 16..(i + 1) * 16];
229 let t0 = u32::from_le_bytes([b[0], b[1], b[2], b[3]]);
230 let t1 = u32::from_le_bytes([b[4], b[5], b[6], b[7]]);
231 let t2 = u32::from_le_bytes([b[8], b[9], b[10], b[11]]);
232 let t3 = u32::from_le_bytes([b[12], b[13], b[14], b[15]]);
233 *slot = [
235 t0 & 0x3ff_ffff,
236 ((t0 >> 26) | (t1 << 6)) & 0x3ff_ffff,
237 ((t1 >> 20) | (t2 << 12)) & 0x3ff_ffff,
238 ((t2 >> 14) | (t3 << 18)) & 0x3ff_ffff,
239 (t3 >> 8) | (1 << 24),
240 ];
241 }
242
243 for (limb, a) in m[0].iter_mut().zip(self.acc) {
245 *limb += a;
246 }
247
248 let multipliers = [self.powers[2], self.powers[1], self.powers[0], self.r];
249 let mut d = [0u64; 5];
250 for (block, r) in m.iter().zip(multipliers) {
251 let part = mul_unreduced(*block, r, five_times(r));
252 for (acc, term) in d.iter_mut().zip(part) {
253 *acc += term;
254 }
255 }
256 self.acc = reduce(d);
257 }
258}
259
260fn five_times(r: [u32; 5]) -> [u32; 4] {
262 [r[1] * 5, r[2] * 5, r[3] * 5, r[4] * 5]
263}
264
265fn mul_unreduced(h: [u32; 5], r: [u32; 5], s: [u32; 4]) -> [u64; 5] {
271 let m = |a: u32, b: u32| a as u64 * b as u64;
272 [
273 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]),
274 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]),
275 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]),
276 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]),
277 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]),
278 ]
279}
280
281fn reduce(d: [u64; 5]) -> [u32; 5] {
283 let mut acc = [0u32; 5];
284 let mut c = (d[0] >> 26) as u32;
285 acc[0] = d[0] as u32 & 0x3ff_ffff;
286 let d1 = d[1] + c as u64;
287 c = (d1 >> 26) as u32;
288 acc[1] = d1 as u32 & 0x3ff_ffff;
289 let d2 = d[2] + c as u64;
290 c = (d2 >> 26) as u32;
291 acc[2] = d2 as u32 & 0x3ff_ffff;
292 let d3 = d[3] + c as u64;
293 c = (d3 >> 26) as u32;
294 acc[3] = d3 as u32 & 0x3ff_ffff;
295 let d4 = d[4] + c as u64;
296 c = (d4 >> 26) as u32;
297 acc[4] = d4 as u32 & 0x3ff_ffff;
298 acc[0] += c * 5;
299 c = acc[0] >> 26;
300 acc[0] &= 0x3ff_ffff;
301 acc[1] += c;
302 acc
303}
304
305impl Mac for Poly1305 {
306 type Tag = [u8; 16];
307 const TAG_LEN: usize = 16;
308
309 fn new(key: &[u8]) -> Result<Self> {
310 ensure!(
311 key.len() == 32,
312 InvalidLength,
313 "poly1305 key must be 32 bytes"
314 );
315 let t0 = u32::from_le_bytes([key[0], key[1], key[2], key[3]]);
316 let t1 = u32::from_le_bytes([key[4], key[5], key[6], key[7]]);
317 let t2 = u32::from_le_bytes([key[8], key[9], key[10], key[11]]);
318 let t3 = u32::from_le_bytes([key[12], key[13], key[14], key[15]]);
319 let r = [
321 t0 & 0x3ff_ffff,
322 ((t0 >> 26) | (t1 << 6)) & 0x3ff_ff03,
323 ((t1 >> 20) | (t2 << 12)) & 0x3ff_c0ff,
324 ((t2 >> 14) | (t3 << 18)) & 0x3f0_3fff,
325 (t3 >> 8) & 0x000_fffff,
326 ];
327 let s = [
328 u32::from_le_bytes([key[16], key[17], key[18], key[19]]),
329 u32::from_le_bytes([key[20], key[21], key[22], key[23]]),
330 u32::from_le_bytes([key[24], key[25], key[26], key[27]]),
331 u32::from_le_bytes([key[28], key[29], key[30], key[31]]),
332 ];
333 let rr = five_times(r);
336 let mut powers = [[0u32; 5]; 3];
337 let mut p = r;
338 for slot in powers.iter_mut() {
339 p = reduce(mul_unreduced(p, r, rr));
340 *slot = p;
341 }
342
343 Ok(Self {
344 r,
345 s,
346 powers,
347 acc: [0u32; 5],
348 buf: [0u8; 16],
349 buffered: 0,
350 })
351 }
352
353 fn update(&mut self, mut data: &[u8]) {
354 if self.buffered > 0 {
355 let take = core::cmp::min(16 - self.buffered, data.len());
356 self.buf[self.buffered..self.buffered + take].copy_from_slice(&data[..take]);
357 self.buffered += take;
358 data = &data[take..];
359 if self.buffered < 16 {
360 return;
361 }
362 let block = self.buf;
363 self.absorb_block(&block);
364 self.buffered = 0;
365 }
366 while data.len() >= 64 {
367 self.absorb4(&data[..64]);
368 data = &data[64..];
369 }
370 let mut chunks = data.chunks_exact(16);
371 for block in &mut chunks {
372 self.absorb_block(block);
373 }
374 let rest = chunks.remainder();
375 self.buf[..rest.len()].copy_from_slice(rest);
376 self.buffered = rest.len();
377 }
378
379 fn finalize(mut self) -> [u8; 16] {
380 if self.buffered > 0 {
381 let n = self.buffered;
382 let block = self.buf;
383 self.absorb_block(&block[..n]);
384 self.buffered = 0;
385 }
386
387 let mut h = self.acc;
389 let mut c = h[1] >> 26;
390 h[1] &= 0x3ff_ffff;
391 h[2] += c;
392 c = h[2] >> 26;
393 h[2] &= 0x3ff_ffff;
394 h[3] += c;
395 c = h[3] >> 26;
396 h[3] &= 0x3ff_ffff;
397 h[4] += c;
398 c = h[4] >> 26;
399 h[4] &= 0x3ff_ffff;
400 h[0] += c * 5;
401 c = h[0] >> 26;
402 h[0] &= 0x3ff_ffff;
403 h[1] += c;
404
405 let mut g = [0u32; 5];
407 let mut carry = 5u32;
408 for i in 0..4 {
409 let v = h[i] + carry;
410 g[i] = v & 0x3ff_ffff;
411 carry = v >> 26;
412 }
413 g[4] = h[4].wrapping_add(carry).wrapping_sub(1 << 26);
416 let mask = ((g[4] >> 31) ^ 1).wrapping_neg();
417 for i in 0..5 {
418 h[i] = (h[i] & !mask) | (g[i] & mask);
419 }
420
421 let h0 = h[0] | (h[1] << 26);
423 let h1 = (h[1] >> 6) | (h[2] << 20);
424 let h2 = (h[2] >> 12) | (h[3] << 14);
425 let h3 = (h[3] >> 18) | (h[4] << 8);
426
427 let mut f = h0 as u64 + self.s[0] as u64;
428 let r0 = f as u32;
429 f = h1 as u64 + self.s[1] as u64 + (f >> 32);
430 let r1 = f as u32;
431 f = h2 as u64 + self.s[2] as u64 + (f >> 32);
432 let r2 = f as u32;
433 f = h3 as u64 + self.s[3] as u64 + (f >> 32);
434 let r3 = f as u32;
435
436 let mut tag = [0u8; 16];
437 tag[0..4].copy_from_slice(&r0.to_le_bytes());
438 tag[4..8].copy_from_slice(&r1.to_le_bytes());
439 tag[8..12].copy_from_slice(&r2.to_le_bytes());
440 tag[12..16].copy_from_slice(&r3.to_le_bytes());
441 tag
442 }
443}
444
445impl SelfTest for Poly1305 {
446 fn self_test() -> Result<()> {
447 let mut key = [0u8; 32];
449 ic_core::codec::hex_decode(
450 b"85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b",
451 &mut key,
452 )?;
453 let tag = <Self as Mac>::mac(&key, b"Cryptographic Forum Research Group")?;
454 let mut want = [0u8; 16];
455 ic_core::codec::hex_decode(b"a8061dc1305136c6c22b8baf0c0127a9", &mut want)?;
456 ensure!(ic_core::ct::verify(&want, &tag), SelfTestFailed, "poly1305");
457 Ok(())
458 }
459}
460
461pub struct ChaCha20Poly1305 {
463 key: [u8; 32],
464}
465
466impl Drop for ChaCha20Poly1305 {
467 fn drop(&mut self) {
468 self.key.zeroize();
469 }
470}
471
472impl Algorithm for ChaCha20Poly1305 {
473 const ID: &'static str = "chacha20-poly1305";
474 const NAME: &'static str = "ChaCha20-Poly1305";
475}
476
477impl ChaCha20Poly1305 {
478 fn poly_key(&self, nonce: &[u8]) -> Result<[u8; 32]> {
480 let mut block = [0u8; 64];
481 let mut n = [0u8; 12];
482 ensure!(nonce.len() == 12, InvalidLength, "chacha20-poly1305 nonce");
483 n.copy_from_slice(nonce);
484 chacha20_block(&self.key, 0, &n, &mut block);
485 let mut k = [0u8; 32];
486 k.copy_from_slice(&block[..32]);
487 block.zeroize();
488 Ok(k)
489 }
490
491 fn tag(&self, nonce: &[u8], aad: &[u8], ciphertext: &[u8]) -> Result<[u8; 16]> {
493 let mut poly_key = self.poly_key(nonce)?;
494 let mut m = Poly1305::new(&poly_key)?;
495 poly_key.zeroize();
496
497 m.update(aad);
498 m.update(&[0u8; 16][..(16 - aad.len() % 16) % 16]);
499 m.update(ciphertext);
500 m.update(&[0u8; 16][..(16 - ciphertext.len() % 16) % 16]);
501
502 let mut lens = [0u8; 16];
503 lens[..8].copy_from_slice(&(aad.len() as u64).to_le_bytes());
504 lens[8..].copy_from_slice(&(ciphertext.len() as u64).to_le_bytes());
505 m.update(&lens);
506 Ok(m.finalize())
507 }
508}
509
510impl Aead for ChaCha20Poly1305 {
511 const KEY_LEN: usize = 32;
512 const NONCE_LEN: usize = 12;
513 const TAG_LEN: usize = 16;
514
515 fn new(key: &[u8]) -> Result<Self> {
516 ensure!(key.len() == 32, InvalidLength, "chacha20-poly1305 key");
517 let mut k = [0u8; 32];
518 k.copy_from_slice(key);
519 Ok(Self { key: k })
520 }
521
522 fn seal_detached(
523 &self,
524 nonce: &[u8],
525 aad: &[u8],
526 in_out: &mut [u8],
527 tag: &mut [u8],
528 ) -> Result<()> {
529 ensure!(
530 tag.len() == 16,
531 InvalidLength,
532 "chacha20-poly1305 tag buffer"
533 );
534 chacha20_xor(&self.key, nonce, 1, in_out)?;
536 let t = self.tag(nonce, aad, in_out)?;
537 tag.copy_from_slice(&t);
538 Ok(())
539 }
540
541 fn open_detached(&self, nonce: &[u8], aad: &[u8], in_out: &mut [u8], tag: &[u8]) -> Result<()> {
542 ensure!(tag.len() == 16, InvalidLength, "chacha20-poly1305 tag");
543 let expected = self.tag(nonce, aad, in_out)?;
544 if !ic_core::ct::verify(&expected, tag) {
545 in_out.zeroize();
546 return Err(ic_core::err!(AuthenticationFailed, "chacha20-poly1305"));
547 }
548 chacha20_xor(&self.key, nonce, 1, in_out)
549 }
550}
551
552impl SelfTest for ChaCha20Poly1305 {
553 fn self_test() -> Result<()> {
554 let key = [0x42u8; 32];
555 let nonce = [0x24u8; 12];
556 let c = <Self as Aead>::new(&key)?;
557 let mut buf = *b"self-test payload";
558 let original = buf;
559 let mut tag = [0u8; 16];
560 c.seal_detached(&nonce, b"aad", &mut buf, &mut tag)?;
561 ensure!(buf != original, SelfTestFailed, "chacha20-poly1305");
562 c.open_detached(&nonce, b"aad", &mut buf, &tag)?;
563 ensure!(buf == original, SelfTestFailed, "chacha20-poly1305");
564 Ok(())
565 }
566}
567
568#[cfg(test)]
569mod tests {
570 use super::*;
571
572 #[cfg(all(target_arch = "x86_64", feature = "std"))]
584 #[test]
585 fn the_avx2_keystream_matches_the_block_function() {
586 if !avx2() {
587 println!("no AVX2 on this CPU; the backend was not exercised");
588 return;
589 }
590
591 let key = [0x5au8; 32];
592 let nonce = [0x21u8; 12];
593
594 let mut checked = 0;
595 for len in [
596 0usize, 1, 63, 64, 65, 127, 511, 512, 513, 575, 576, 1023, 1024, 1025, 4096, 4097,
597 ] {
598 for counter in [0u32, 1, 7, 8, 9, 1000] {
599 let mut actual = std::vec![0u8; len];
600 chacha20_xor(&key, &nonce, counter, &mut actual).unwrap();
601
602 let mut expect = std::vec![0u8; len];
604 let mut block = [0u8; 64];
605 for (i, chunk) in expect.chunks_mut(64).enumerate() {
606 chacha20_block(&key, counter + i as u32, &nonce, &mut block);
607 for (d, b) in chunk.iter_mut().zip(block.iter()) {
608 *d ^= b;
609 }
610 }
611
612 assert_eq!(
613 actual, expect,
614 "AVX2 and scalar keystreams differ at {len} bytes, counter {counter}"
615 );
616 checked += 1;
617 }
618 }
619 assert_eq!(checked, 16 * 6, "the comparison did not run");
620 }
621
622 #[cfg(all(target_arch = "x86_64", feature = "std"))]
624 #[test]
625 fn the_active_chacha_path_is_reported() {
626 println!(
627 "chacha20 backend: {}",
628 if avx2() { "AVX2 (8 blocks)" } else { "scalar" }
629 );
630 }
631 use ic_core::codec::{hex, unhex};
632
633 #[test]
634 fn rfc8439_chacha20_keystream_vector() {
635 let key: Vec<u8> = (0..32u8).collect();
636 let mut nonce = [0u8; 12];
637 nonce[3] = 0x09;
638 nonce[7] = 0x4a;
639 let mut data = [0u8; 64];
640 chacha20_xor(&key, &nonce, 1, &mut data).unwrap();
641 assert_eq!(
642 hex(&data),
643 "10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4ed2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e"
644 );
645 }
646
647 #[test]
648 fn rfc8439_chacha20_encryption_vector() {
649 let key: Vec<u8> = (0..32u8).collect();
650 let mut nonce = [0u8; 12];
651 nonce[7] = 0x4a;
652 let mut data =
653 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();
654 chacha20_xor(&key, &nonce, 1, &mut data).unwrap();
655 assert_eq!(
656 hex(&data),
657 "6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d624e65152ab8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74a35be6b40b8eedf2785e42874d"
658 );
659 }
660
661 #[test]
662 fn rfc8439_poly1305_vector() {
663 let key =
664 unhex("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b").unwrap();
665 let tag = Poly1305::mac(&key, b"Cryptographic Forum Research Group").unwrap();
666 assert_eq!(hex(&tag), "a8061dc1305136c6c22b8baf0c0127a9");
667 }
668
669 #[test]
670 fn poly1305_streaming_matches_one_shot() {
671 let key = [0x11u8; 32];
672 let data: Vec<u8> = (0..100u8).collect();
673 for split in [0usize, 1, 15, 16, 17, 50, 100] {
674 let mut m = Poly1305::new(&key).unwrap();
675 m.update(&data[..split]);
676 m.update(&data[split..]);
677 assert_eq!(
678 m.finalize(),
679 Poly1305::mac(&key, &data).unwrap(),
680 "split at {split}"
681 );
682 }
683 }
684
685 #[test]
686 fn rfc8439_aead_vector() {
687 let key =
688 unhex("808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f").unwrap();
689 let nonce = unhex("070000004041424344454647").unwrap();
690 let aad = unhex("50515253c0c1c2c3c4c5c6c7").unwrap();
691 let mut buf =
692 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();
693
694 let c = ChaCha20Poly1305::new(&key).unwrap();
695 let mut tag = [0u8; 16];
696 c.seal_detached(&nonce, &aad, &mut buf, &mut tag).unwrap();
697 assert_eq!(
698 hex(&buf),
699 "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116"
700 );
701 assert_eq!(hex(&tag), "1ae10b594f09e26a7e902ecbd0600691");
702
703 c.open_detached(&nonce, &aad, &mut buf, &tag).unwrap();
704 assert_eq!(
705 &buf[..],
706 &b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."[..]
707 );
708 }
709
710 #[test]
711 fn aead_rejects_tampering_and_wipes() {
712 let c = ChaCha20Poly1305::new(&[1u8; 32]).unwrap();
713 let mut buf = b"secret".to_vec();
714 let mut tag = [0u8; 16];
715 c.seal_detached(&[2u8; 12], b"", &mut buf, &mut tag)
716 .unwrap();
717 tag[15] ^= 0x80;
718 assert!(c.open_detached(&[2u8; 12], b"", &mut buf, &tag).is_err());
719 assert_eq!(buf, vec![0u8; 6]);
720 }
721
722 #[test]
734 fn the_grouped_poly1305_agrees_with_the_serial_one() {
735 let key = [0x8eu8; 32];
736
737 let mut checked = 0;
738 for len in [
739 0usize, 1, 15, 16, 17, 31, 32, 63, 64, 65, 79, 80, 127, 128, 129, 255, 256, 1023, 1024,
740 1025,
741 ] {
742 let data: Vec<u8> = (0..len)
743 .map(|i| ((i as u64).wrapping_mul(0x9e37_79b9) >> 5) as u8)
744 .collect();
745
746 let grouped = Poly1305::mac(&key, &data).unwrap();
747
748 let mut serial = Poly1305::new(&key).unwrap();
750 for chunk in data.chunks(16) {
751 serial.absorb_block(chunk);
752 }
753 let serial = serial.finalize();
754
755 assert_eq!(
756 grouped, serial,
757 "grouped and serial Poly1305 differ at {len} bytes"
758 );
759 checked += 1;
760 }
761 assert_eq!(checked, 20, "the comparison did not run");
762 }
763
764 #[test]
770 fn the_poly1305_powers_are_powers_of_r() {
771 let m = Poly1305::new(&[0x3bu8; 32]).unwrap();
772 let mut expect = m.r;
773 for (i, stored) in m.powers.iter().enumerate() {
774 expect = reduce(mul_unreduced(expect, m.r, five_times(m.r)));
775 assert_eq!(*stored, expect, "power {} is not r^{}", i, i + 2);
776 }
777 assert_ne!(m.powers[0], m.powers[1]);
778 assert_ne!(m.powers[0], m.r);
779 }
780
781 #[test]
782 fn self_tests_pass() {
783 Poly1305::self_test().unwrap();
784 ChaCha20Poly1305::self_test().unwrap();
785 }
786}