1#![allow(clippy::needless_range_loop)]
20
21use crate::aes::{Aes128, Aes192, Aes256, BLOCK_LEN};
22use crate::modes::increment_be32;
23use ic_core::traits::{Aead, Algorithm, BlockCipher, SelfTest};
24use ic_core::{ensure, Result, Zeroize};
25
26const R: u8 = 0xe1;
28
29pub struct GcmLimits;
33
34impl GcmLimits {
35 pub const MAX_PLAINTEXT_BYTES: u64 = (1 << 36) - 32;
37 pub const MAX_RANDOM_NONCE_INVOCATIONS: u64 = 1 << 32;
39 pub const RECOMMENDED_NONCE_LEN: usize = 12;
42}
43
44#[inline]
50#[must_use]
51pub fn ghash_accelerated() -> bool {
52 ic_core::cpu::has_ghash_clmul()
53}
54
55pub(crate) fn portable_ghash_mul(x: &mut [u8; BLOCK_LEN], h: &[u8; BLOCK_LEN]) {
60 let mut z = [0u8; BLOCK_LEN];
61 let mut v = *h;
62 for i in 0..128 {
63 let bit = (x[i / 8] >> (7 - (i % 8))) & 1;
64 let m = bit.wrapping_neg();
65 for j in 0..BLOCK_LEN {
66 z[j] ^= v[j] & m;
67 }
68 let lsb = v[BLOCK_LEN - 1] & 1;
70 let mut carry = 0u8;
71 for byte in v.iter_mut() {
72 let next = *byte & 1;
73 *byte = (*byte >> 1) | (carry << 7);
74 carry = next;
75 }
76 v[0] ^= R & lsb.wrapping_neg();
77 }
78 *x = z;
79 z.zeroize();
80 v.zeroize();
81}
82
83struct Ghash {
85 h: [u8; BLOCK_LEN],
86 powers: [[u8; BLOCK_LEN]; 8],
104 #[cfg(all(target_arch = "x86_64", feature = "std"))]
106 high_powers: bool,
107 acc: [u8; BLOCK_LEN],
108 #[cfg(all(target_arch = "x86_64", feature = "std"))]
114 accelerated: bool,
115}
116
117impl Ghash {
118 fn new(h: [u8; BLOCK_LEN]) -> Self {
119 let mut me = Self {
120 h,
121 powers: [[0u8; BLOCK_LEN]; 8],
122 #[cfg(all(target_arch = "x86_64", feature = "std"))]
123 high_powers: false,
124 acc: [0u8; BLOCK_LEN],
125 #[cfg(all(target_arch = "x86_64", feature = "std"))]
126 accelerated: ghash_accelerated(),
127 };
128 me.powers[0] = h;
131 for slot in 1..4 {
132 let mut p = me.powers[slot - 1];
133 me.mul_by(&mut p, &h);
134 me.powers[slot] = p;
135 }
136 me
137 }
138
139 #[inline]
141 fn mul_by(&self, x: &mut [u8; BLOCK_LEN], y: &[u8; BLOCK_LEN]) {
142 #[cfg(all(target_arch = "x86_64", feature = "std"))]
143 if self.accelerated {
144 unsafe { crate::clmul::mul(x, y) };
147 return;
148 }
149 portable_ghash_mul(x, y);
150 }
151
152 #[inline]
156 fn absorb4(&mut self, blocks: &[u8]) {
157 debug_assert_eq!(blocks.len(), BLOCK_LEN * 4);
158 let mut terms = [[0u8; BLOCK_LEN]; 4];
159 for (i, t) in terms.iter_mut().enumerate() {
160 t.copy_from_slice(&blocks[i * BLOCK_LEN..(i + 1) * BLOCK_LEN]);
161 }
162 for j in 0..BLOCK_LEN {
165 terms[0][j] ^= self.acc[j];
166 }
167 let multipliers = [
168 &self.powers[3],
169 &self.powers[2],
170 &self.powers[1],
171 &self.powers[0],
172 ];
173 for (t, m) in terms.iter_mut().zip(multipliers) {
174 self.mul_by(t, m);
175 }
176 self.acc = terms[0];
177 for t in &terms[1..] {
178 for j in 0..BLOCK_LEN {
179 self.acc[j] ^= t[j];
180 }
181 }
182 }
183
184 #[inline]
186 fn mul_acc(&mut self) {
187 #[cfg(all(target_arch = "x86_64", feature = "std"))]
188 if self.accelerated {
189 unsafe { crate::clmul::mul(&mut self.acc, &self.h) };
192 return;
193 }
194 portable_ghash_mul(&mut self.acc, &self.h);
195 }
196
197 fn update_padded(&mut self, mut data: &[u8]) {
199 #[cfg(all(target_arch = "x86_64", feature = "std"))]
202 if self.accelerated && data.len() >= BLOCK_LEN * 8 {
203 if !self.high_powers {
204 for slot in 4..8 {
205 let mut p = self.powers[slot - 1];
206 self.mul_by(&mut p, &self.h);
207 self.powers[slot] = p;
208 }
209 self.high_powers = true;
210 }
211 let whole = data.len() - data.len() % (BLOCK_LEN * 8);
212 unsafe { crate::clmul::absorb8(&mut self.acc, &self.powers, &data[..whole]) };
216 data = &data[whole..];
217 }
218 while data.len() >= BLOCK_LEN * 4 {
221 self.absorb4(&data[..BLOCK_LEN * 4]);
222 data = &data[BLOCK_LEN * 4..];
223 }
224 for chunk in data.chunks(BLOCK_LEN) {
225 let mut block = [0u8; BLOCK_LEN];
226 block[..chunk.len()].copy_from_slice(chunk);
227 for j in 0..BLOCK_LEN {
228 self.acc[j] ^= block[j];
229 }
230 self.mul_acc();
231 }
232 }
233
234 fn finalize(self) -> [u8; BLOCK_LEN] {
235 self.acc
236 }
237}
238
239impl Drop for Ghash {
240 fn drop(&mut self) {
241 self.h.zeroize();
242 self.acc.zeroize();
243 for p in self.powers.iter_mut() {
246 p.zeroize();
247 }
248 }
249}
250
251pub(crate) trait Ctr32: BlockCipher {
258 fn ctr32_xor(&self, counter: &mut [u8; BLOCK_LEN], data: &mut [u8]) -> Result<()> {
259 ctr32_xor_generic(self, counter, data)
260 }
261}
262
263pub(crate) fn ctr32_xor_generic<C: BlockCipher + ?Sized>(
265 cipher: &C,
266 counter: &mut [u8; BLOCK_LEN],
267 data: &mut [u8],
268) -> Result<()> {
269 const CTR_BATCH: usize = 8;
270 let mut keystream = [0u8; BLOCK_LEN * CTR_BATCH];
271 for chunk in data.chunks_mut(BLOCK_LEN * CTR_BATCH) {
272 let blocks = chunk.len().div_ceil(BLOCK_LEN);
273 for i in 0..blocks {
274 keystream[i * BLOCK_LEN..(i + 1) * BLOCK_LEN].copy_from_slice(counter);
275 increment_be32(counter);
276 }
277 cipher.encrypt_blocks(&mut keystream[..blocks * BLOCK_LEN])?;
278 for (d, k) in chunk.iter_mut().zip(keystream.iter()) {
279 *d ^= k;
280 }
281 }
282 keystream.zeroize();
283 Ok(())
284}
285
286fn derive_j0(nonce: &[u8], h: &[u8; BLOCK_LEN]) -> [u8; BLOCK_LEN] {
288 if nonce.len() == 12 {
289 let mut j0 = [0u8; BLOCK_LEN];
290 j0[..12].copy_from_slice(nonce);
291 j0[15] = 1;
292 j0
293 } else {
294 let mut g = Ghash::new(*h);
295 g.update_padded(nonce);
296 let mut len_block = [0u8; BLOCK_LEN];
297 len_block[8..].copy_from_slice(&((nonce.len() as u64) * 8).to_be_bytes());
298 g.update_padded(&len_block);
299 g.finalize()
300 }
301}
302
303fn gcm_core<C: Ctr32>(
305 cipher: &C,
306 nonce: &[u8],
307 aad: &[u8],
308 in_out: &mut [u8],
309 encrypting: bool,
310) -> Result<[u8; BLOCK_LEN]> {
311 ensure!(
312 !nonce.is_empty(),
313 InvalidParameter,
314 "gcm nonce must be non-empty"
315 );
316 ensure!(
317 in_out.len() as u64 <= GcmLimits::MAX_PLAINTEXT_BYTES,
318 CounterExhausted,
319 "gcm plaintext exceeds 2^39-256 bits"
320 );
321
322 let mut h = [0u8; BLOCK_LEN];
324 cipher.encrypt_block(&mut h)?;
325
326 let j0 = derive_j0(nonce, &h);
327
328 let mut g = Ghash::new(h);
331 g.update_padded(aad);
332 if !encrypting {
333 g.update_padded(in_out);
334 }
335
336 let mut counter = j0;
338 increment_be32(&mut counter);
339 cipher.ctr32_xor(&mut counter, in_out)?;
340
341 if encrypting {
342 g.update_padded(in_out);
343 }
344
345 let mut len_block = [0u8; BLOCK_LEN];
346 len_block[..8].copy_from_slice(&((aad.len() as u64) * 8).to_be_bytes());
347 len_block[8..].copy_from_slice(&((in_out.len() as u64) * 8).to_be_bytes());
348 g.update_padded(&len_block);
349
350 let mut tag = g.finalize();
351 let mut ek_j0 = j0;
352 cipher.encrypt_block(&mut ek_j0)?;
353 for j in 0..BLOCK_LEN {
354 tag[j] ^= ek_j0[j];
355 }
356 ek_j0.zeroize();
357 h.zeroize();
358 Ok(tag)
359}
360
361macro_rules! aes_gcm {
362 ($name:ident, $inner:ty, $id:literal, $disp:literal, $keylen:literal) => {
363 #[doc = concat!("SP 800-38D ", $disp, ".")]
364 pub struct $name($inner);
365
366 impl Algorithm for $name {
367 const ID: &'static str = $id;
368 const NAME: &'static str = $disp;
369 }
370
371 impl Aead for $name {
372 const KEY_LEN: usize = $keylen;
373 const NONCE_LEN: usize = 12;
374 const TAG_LEN: usize = 16;
375
376 fn new(key: &[u8]) -> Result<Self> {
377 Ok(Self(<$inner as BlockCipher>::new(key)?))
378 }
379
380 fn seal_detached(
381 &self,
382 nonce: &[u8],
383 aad: &[u8],
384 in_out: &mut [u8],
385 tag: &mut [u8],
386 ) -> Result<()> {
387 ensure!(tag.len() == 16, InvalidLength, "gcm tag buffer");
388 let t = gcm_core(&self.0, nonce, aad, in_out, true)?;
389 tag.copy_from_slice(&t);
390 Ok(())
391 }
392
393 fn open_detached(
394 &self,
395 nonce: &[u8],
396 aad: &[u8],
397 in_out: &mut [u8],
398 tag: &[u8],
399 ) -> Result<()> {
400 ensure!(tag.len() == 16, InvalidLength, "gcm tag");
401 let expected = gcm_core(&self.0, nonce, aad, in_out, false)?;
402 if ic_core::ct::verify(&expected, tag) {
403 Ok(())
404 } else {
405 in_out.zeroize();
407 Err(ic_core::err!(AuthenticationFailed, $id))
408 }
409 }
410 }
411
412 impl SelfTest for $name {
413 fn self_test() -> Result<()> {
414 let key = [0u8; $keylen];
415 let nonce = [0u8; 12];
416 let c = <Self as Aead>::new(&key)?;
417 let mut buf = [0u8; 16];
418 let mut tag = [0u8; 16];
419 c.seal_detached(&nonce, &[], &mut buf, &mut tag)?;
420 c.open_detached(&nonce, &[], &mut buf, &tag)?;
421 ensure!(buf == [0u8; 16], SelfTestFailed, $id);
422 tag[0] ^= 1;
424 ensure!(
425 c.open_detached(&nonce, &[], &mut buf, &tag).is_err(),
426 SelfTestFailed,
427 $id
428 );
429 Ok(())
430 }
431 }
432 };
433}
434
435aes_gcm!(Aes128Gcm, Aes128, "aes-128-gcm", "AES-128-GCM", 16);
436aes_gcm!(Aes192Gcm, Aes192, "aes-192-gcm", "AES-192-GCM", 24);
437aes_gcm!(Aes256Gcm, Aes256, "aes-256-gcm", "AES-256-GCM", 32);
438
439#[cfg(test)]
440mod tests {
441 use super::*;
442
443 #[test]
452 fn the_backend_counter_mode_agrees_with_the_generic_one() {
453 use crate::aes::{Aes128, Aes256, Backend};
454 type CtrFn = std::boxed::Box<dyn Fn(&mut [u8; 16], &mut [u8])>;
455 let mut checked = 0;
456 for key_len in [16usize, 32] {
457 let key: std::vec::Vec<u8> = (0..key_len).map(|i| (i * 7 + 3) as u8).collect();
458 let (fast, slow): (CtrFn, CtrFn) = if key_len == 16 {
459 let f = Aes128::new(&key).unwrap();
460 let s = Aes128::new_portable(&key).unwrap();
461 if crate::aes::aesni_available() {
462 assert_eq!(f.backend(), Backend::Aesni);
463 }
464 (
465 std::boxed::Box::new(move |c, d| f.ctr32_xor(c, d).unwrap()),
466 std::boxed::Box::new(move |c, d| ctr32_xor_generic(&s, c, d).unwrap()),
467 )
468 } else {
469 let f = Aes256::new(&key).unwrap();
470 let s = Aes256::new_portable(&key).unwrap();
471 (
472 std::boxed::Box::new(move |c, d| f.ctr32_xor(c, d).unwrap()),
473 std::boxed::Box::new(move |c, d| ctr32_xor_generic(&s, c, d).unwrap()),
474 )
475 };
476 for tail in [0x0000_0001u32, 0xffff_fffd, 0xffff_fff9, 0xffff_ffff] {
479 for len in 0..=300usize {
480 let mut counter = [0x5au8; 16];
481 counter[12..].copy_from_slice(&tail.to_be_bytes());
482 let data: std::vec::Vec<u8> = (0..len).map(|i| (i * 13) as u8).collect();
483 let (mut a, mut b) = (data.clone(), data.clone());
484 let (mut ca, mut cb) = (counter, counter);
485 fast(&mut ca, &mut a);
486 slow(&mut cb, &mut b);
487 assert_eq!(a, b, "key {key_len}, counter tail {tail:#x}, {len} bytes");
488 assert_eq!(
489 ca, cb,
490 "final counter, key {key_len}, tail {tail:#x}, {len} bytes"
491 );
492 checked += 1;
493 }
494 }
495 }
496 assert_eq!(checked, 2 * 4 * 301);
497 }
498
499 #[test]
508 fn the_batched_ghash_agrees_with_the_serial_one() {
509 let h = [
510 0x66, 0xe9, 0x4b, 0xd4, 0xef, 0x8a, 0x2c, 0x3b, 0x88, 0x4c, 0xfa, 0x59, 0xca, 0x34,
511 0x2b, 0x2e,
512 ];
513
514 let mut checked = 0;
515 for len in [
517 0usize, 1, 15, 16, 17, 31, 63, 64, 65, 79, 80, 127, 128, 129, 255, 256, 1024, 1025,
518 ] {
519 let data: std::vec::Vec<u8> = (0..len)
520 .map(|i| ((i as u64).wrapping_mul(0x9e37_79b9) >> 3) as u8)
521 .collect();
522
523 let mut batched = Ghash::new(h);
524 batched.update_padded(&data);
525
526 let mut serial = Ghash::new(h);
528 for chunk in data.chunks(BLOCK_LEN) {
529 let mut block = [0u8; BLOCK_LEN];
530 block[..chunk.len()].copy_from_slice(chunk);
531 for j in 0..BLOCK_LEN {
532 serial.acc[j] ^= block[j];
533 }
534 serial.mul_acc();
535 }
536
537 assert_eq!(
538 batched.acc, serial.acc,
539 "batched and serial GHASH disagree at {len} bytes"
540 );
541 checked += 1;
542 }
543 assert_eq!(checked, 18, "the comparison did not run");
544
545 for (prefix, len) in [(17usize, 128usize), (16, 2048), (5, 2048 + 48), (33, 1023)] {
549 let data: std::vec::Vec<u8> = (0..prefix + len)
550 .map(|i| ((i as u64).wrapping_mul(0x2545_f491_4f6c_dd1d) >> 11) as u8)
551 .collect();
552 let (head, tail) = data.split_at(prefix);
553 let mut batched = Ghash::new(h);
554 batched.update_padded(head);
555 batched.update_padded(tail);
556
557 let mut serial = Ghash::new(h);
558 for part in [head, tail] {
559 for chunk in part.chunks(BLOCK_LEN) {
560 let mut block = [0u8; BLOCK_LEN];
561 block[..chunk.len()].copy_from_slice(chunk);
562 for j in 0..BLOCK_LEN {
563 serial.acc[j] ^= block[j];
564 }
565 serial.mul_acc();
566 }
567 }
568 assert_eq!(batched.acc, serial.acc, "prefix {prefix}, then {len} bytes");
569
570 #[cfg(all(target_arch = "x86_64", feature = "std"))]
573 assert_eq!(batched.high_powers, batched.accelerated);
574 }
575
576 let long = std::vec![0xa5u8; BLOCK_LEN * 4];
579 let mut g = Ghash::new(h);
580 g.absorb4(&long);
581 let mut serial = Ghash::new(h);
582 for chunk in long.chunks(BLOCK_LEN) {
583 for j in 0..BLOCK_LEN {
584 serial.acc[j] ^= chunk[j];
585 }
586 serial.mul_acc();
587 }
588 assert_eq!(g.acc, serial.acc, "absorb4 alone disagrees with four steps");
589 }
590
591 #[test]
593 fn the_precomputed_powers_are_powers_of_h() {
594 let h = [0x3cu8; BLOCK_LEN];
595 let mut g = Ghash::new(h);
596 g.update_padded(&[0u8; BLOCK_LEN * 8]);
599 #[cfg(all(target_arch = "x86_64", feature = "std"))]
600 let built = if g.high_powers { 8 } else { 4 };
601 #[cfg(not(all(target_arch = "x86_64", feature = "std")))]
602 let built = 4;
603
604 let mut expect = h;
605 for (i, stored) in g.powers[..built].iter().enumerate() {
606 if i > 0 {
607 g.mul_by(&mut expect, &h);
608 }
609 assert_eq!(*stored, expect, "powers[{i}] is not H^{}", i + 1);
610 }
611 for i in 1..built {
613 assert_ne!(g.powers[i - 1], g.powers[i]);
614 }
615 }
616 use ic_core::codec::{hex, unhex};
617
618 fn check(key: &str, nonce: &str, pt: &str, aad: &str, ct: &str, tag: &str) {
620 let k = unhex(key).unwrap();
621 let mut buf = unhex(pt).unwrap();
622 let mut got_tag = [0u8; 16];
623 let n = unhex(nonce).unwrap();
624 let a = unhex(aad).unwrap();
625
626 match k.len() {
627 16 => {
628 let c = Aes128Gcm::new(&k).unwrap();
629 c.seal_detached(&n, &a, &mut buf, &mut got_tag).unwrap();
630 }
631 24 => {
632 let c = Aes192Gcm::new(&k).unwrap();
633 c.seal_detached(&n, &a, &mut buf, &mut got_tag).unwrap();
634 }
635 _ => {
636 let c = Aes256Gcm::new(&k).unwrap();
637 c.seal_detached(&n, &a, &mut buf, &mut got_tag).unwrap();
638 }
639 }
640 assert_eq!(hex(&buf), ct, "ciphertext");
641 assert_eq!(hex(&got_tag), tag, "tag");
642 }
643
644 #[test]
645 fn gcm_spec_case_1_empty() {
646 check(
647 "00000000000000000000000000000000",
648 "000000000000000000000000",
649 "",
650 "",
651 "",
652 "58e2fccefa7e3061367f1d57a4e7455a",
653 );
654 }
655
656 #[test]
657 fn gcm_spec_case_2_single_block() {
658 check(
659 "00000000000000000000000000000000",
660 "000000000000000000000000",
661 "00000000000000000000000000000000",
662 "",
663 "0388dace60b6a392f328c2b971b2fe78",
664 "ab6e47d42cec13bdf53a67b21257bddf",
665 );
666 }
667
668 #[test]
672 fn gcm_spec_case_3_multi_block() {
673 check(
674 "feffe9928665731c6d6a8f9467308308",
675 "cafebabefacedbaddecaf888",
676 "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
677 "",
678 "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985",
679 "4d5c2af327cd64a62cf35abd2ba6fab4",
680 );
681 }
682
683 #[test]
684 fn gcm_spec_case_4_with_aad() {
685 check(
686 "feffe9928665731c6d6a8f9467308308",
687 "cafebabefacedbaddecaf888",
688 "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
689 "feedfacedeadbeeffeedfacedeadbeefabaddad2",
690 "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091",
691 "5bc94fbc3221a5db94fae95ae7121a47",
692 );
693 }
694
695 #[test]
697 fn gcm_short_nonce_uses_ghash_j0() {
698 check(
699 "feffe9928665731c6d6a8f9467308308",
700 "cafebabefacedbad",
701 "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
702 "feedfacedeadbeeffeedfacedeadbeefabaddad2",
703 "61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598",
704 "3612d2e79e3b0785561be14aaca2fccb",
705 );
706 }
707
708 #[test]
709 fn aes256_gcm_vector() {
710 check(
711 "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
712 "cafebabefacedbaddecaf888",
713 "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
714 "feedfacedeadbeeffeedfacedeadbeefabaddad2",
715 "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662",
716 "76fc6ece0f4e1768cddf8853bb2d551b",
717 );
718 }
719
720 #[test]
721 fn roundtrip_and_tamper_detection() {
722 let c = Aes256Gcm::new(&[7u8; 32]).unwrap();
723 let nonce = [9u8; 12];
724 let aad = b"header";
725 let plaintext = b"attack at dawn, bring the ontology";
726
727 let mut buf = plaintext.to_vec();
728 let mut tag = [0u8; 16];
729 c.seal_detached(&nonce, aad, &mut buf, &mut tag).unwrap();
730 assert_ne!(&buf[..], &plaintext[..]);
731
732 let mut ok = buf.clone();
733 c.open_detached(&nonce, aad, &mut ok, &tag).unwrap();
734 assert_eq!(&ok[..], &plaintext[..]);
735
736 let mut bad = buf.clone();
738 bad[0] ^= 1;
739 assert!(c.open_detached(&nonce, aad, &mut bad, &tag).is_err());
740 assert_eq!(
741 bad,
742 vec![0u8; bad.len()],
743 "plaintext must be wiped on failure"
744 );
745
746 let mut wrong_aad = buf.clone();
748 assert!(c
749 .open_detached(&nonce, b"other", &mut wrong_aad, &tag)
750 .is_err());
751
752 let mut wrong_nonce = buf.clone();
754 assert!(c
755 .open_detached(&[0u8; 12], aad, &mut wrong_nonce, &tag)
756 .is_err());
757 }
758
759 #[test]
760 fn self_tests_pass() {
761 Aes128Gcm::self_test().unwrap();
762 Aes192Gcm::self_test().unwrap();
763 Aes256Gcm::self_test().unwrap();
764 }
765}