1#![allow(unsafe_code)]
23
24mod aes;
25mod fork;
26mod ghash;
27mod nonce;
28mod siv;
29
30#[cfg(feature = "ct-verify")]
34pub use ghash::{ct_verify_leaky_control, ct_verify_mulx};
35
36#[cfg(feature = "saw-verify")]
39pub use ghash::{
40 saw_field_mul, saw_field_mul_commutes, saw_field_mul_left_linear, saw_field_mul_right_linear,
41};
42
43pub use siv::{
44 HardwareAes256GcmSiv, HardwareAes256GcmSivIn, HardwareAes256GcmSivKeyState,
45 SivUninitKeyStateSlot,
46};
47
48use core::{
49 marker::PhantomData,
50 mem::MaybeUninit,
51 ptr::{self, NonNull},
52};
53use subtle::ConstantTimeEq as _;
54use zeroize::Zeroize as _;
55
56pub const KEY_SIZE: usize = 32;
58pub const NONCE_SIZE: usize = 12;
60pub const TAG_SIZE: usize = 16;
62const AES_BLOCK_SIZE: usize = 16;
63const PAR_BLOCKS: usize = aes::PAR_BLOCKS;
64const PAR_BYTES: usize = PAR_BLOCKS * AES_BLOCK_SIZE;
65const MAX_GCM_DATA_LEN: u64 = ((u32::MAX as u64) - 1) * AES_BLOCK_SIZE as u64;
66const MAX_GHASH_INPUT_LEN: u64 = u64::MAX / 8;
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub struct KeyStateLayout {
71 pub size: usize,
73 pub align: usize,
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79#[non_exhaustive]
80pub enum Error {
81 InvalidKeyLength,
83 InvalidNonceLength,
85 Encrypt,
87 Decrypt,
89 CiphertextTooShort,
91 UnsupportedCpu,
93 KeyStateStorageTooSmall,
95 KeyStateStorageMisaligned,
97 InputTooLarge,
99 OutputTooSmall,
101 OsEntropy,
103}
104
105impl std::fmt::Display for Error {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 match self {
108 Self::InvalidKeyLength => f.write_str("invalid AES-256-GCM key length"),
109 Self::InvalidNonceLength => f.write_str("invalid AES-GCM nonce length"),
110 Self::Encrypt => f.write_str("AES-256-GCM encryption failed"),
111 Self::Decrypt => f.write_str("AES-256-GCM authentication failed"),
112 Self::CiphertextTooShort => f.write_str("ciphertext too short"),
113 Self::UnsupportedCpu => {
114 f.write_str("required AES-256-GCM hardware support is unavailable")
115 }
116 Self::KeyStateStorageTooSmall => f.write_str("key-state storage too small"),
117 Self::KeyStateStorageMisaligned => f.write_str("key-state storage misaligned"),
118 Self::InputTooLarge => f.write_str("AES-256-GCM input too large"),
119 Self::OutputTooSmall => f.write_str("plaintext output buffer too small"),
120 Self::OsEntropy => f.write_str("OS entropy source failed during nonce generation"),
121 }
122 }
123}
124
125impl std::error::Error for Error {}
126
127struct KeyState {
128 aes: aes::Aes256,
129 ghash: ghash::GHashKey,
130}
131
132impl KeyState {
133 fn init_in_place(dst: NonNull<Self>, key: &[u8; KEY_SIZE]) -> Result<(), Error> {
134 if !HardwareAes256Gcm::hardware_available() {
135 return Err(Error::UnsupportedCpu);
136 }
137
138 let dst = dst.as_ptr();
139 let aes_ptr = unsafe { ptr::addr_of_mut!((*dst).aes) };
142 aes::Aes256::init_in_place(aes_ptr, key).ok_or(Error::UnsupportedCpu)?;
143
144 let mut hash_subkey = [0_u8; TAG_SIZE];
145 unsafe { (&*aes_ptr).encrypt_block(&mut hash_subkey) };
147 let ghash_ptr = unsafe { ptr::addr_of_mut!((*dst).ghash) };
150 if ghash::GHashKey::init_in_place(ghash_ptr, &mut hash_subkey).is_none() {
151 hash_subkey.zeroize();
155 unsafe { ptr::drop_in_place(aes_ptr) };
157 return Err(Error::UnsupportedCpu);
158 }
159 Ok(())
160 }
161
162 fn encrypt(&self, nonce: &[u8], aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
163 let total = plaintext
164 .len()
165 .checked_add(TAG_SIZE)
166 .ok_or(Error::InputTooLarge)?;
167 let mut out = vec![0_u8; total];
168 self.encrypt_to(nonce, aad, plaintext, &mut out)?;
169 Ok(out)
170 }
171
172 fn encrypt_nonce_appended(&self, nonce: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
173 self.encrypt_envelope(nonce, &[], plaintext)
174 }
175
176 fn encrypt_envelope(
177 &self,
178 nonce: &[u8],
179 aad: &[u8],
180 plaintext: &[u8],
181 ) -> Result<Vec<u8>, Error> {
182 let nonce = nonce_from_slice(nonce)?;
183 validate_gcm_lengths(aad.len(), plaintext.len())?;
184 let total = plaintext
185 .len()
186 .checked_add(TAG_SIZE + NONCE_SIZE)
187 .ok_or(Error::InputTooLarge)?;
188 let mut out = Vec::with_capacity(total);
189 out.extend_from_slice(plaintext);
190 let Some(tag) = self.seal_in_place(&nonce, aad, &mut out) else {
191 out.zeroize();
192 return Err(Error::Encrypt);
193 };
194 append_tag_nonce(&mut out, &tag, &nonce);
195 Ok(out)
196 }
197
198 fn encrypt_envelope_to(
199 &self,
200 nonce: &[u8],
201 aad: &[u8],
202 plaintext: &[u8],
203 out: &mut [u8],
204 ) -> Result<usize, Error> {
205 let nonce = nonce_from_slice(nonce)?;
206 validate_gcm_lengths(aad.len(), plaintext.len())?;
207 let total = plaintext
208 .len()
209 .checked_add(TAG_SIZE + NONCE_SIZE)
210 .ok_or(Error::InputTooLarge)?;
211 if out.len() < total {
212 return Err(Error::OutputTooSmall);
213 }
214
215 let ciphertext_tag_len = plaintext.len() + TAG_SIZE;
216 self.encrypt_to(&nonce, aad, plaintext, &mut out[..ciphertext_tag_len])?;
217 out[ciphertext_tag_len..total].copy_from_slice(&nonce);
218 Ok(total)
219 }
220
221 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
222 fn encrypt_nonce_appended_in_place(
223 &self,
224 nonce: &[u8],
225 in_out: &mut Vec<u8>,
226 ) -> Result<(), Error> {
227 let nonce = nonce_from_slice(nonce)?;
228 validate_gcm_lengths(0, in_out.len())?;
229 let total = in_out
230 .len()
231 .checked_add(TAG_SIZE + NONCE_SIZE)
232 .ok_or(Error::InputTooLarge)?;
233 if in_out.capacity() < total {
234 in_out.reserve_exact(total - in_out.len());
235 }
236 let Some(tag) = self.seal_in_place(&nonce, &[], in_out.as_mut_slice()) else {
237 in_out.zeroize();
238 return Err(Error::Encrypt);
239 };
240 append_tag_nonce(in_out, &tag, &nonce);
241 Ok(())
242 }
243
244 fn decrypt_envelope(&self, aad: &[u8], data: &[u8]) -> Result<Vec<u8>, Error> {
245 if data.len() < TAG_SIZE + NONCE_SIZE {
246 return Err(Error::CiphertextTooShort);
247 }
248 let nonce_pos = data.len() - NONCE_SIZE;
249 let (ciphertext_and_tag, nonce) = data.split_at(nonce_pos);
250 self.decrypt(nonce, aad, ciphertext_and_tag)
251 }
252
253 fn decrypt_envelope_to(&self, aad: &[u8], data: &[u8], out: &mut [u8]) -> Result<usize, Error> {
254 if data.len() < TAG_SIZE + NONCE_SIZE {
255 return Err(Error::CiphertextTooShort);
256 }
257 let nonce_pos = data.len() - NONCE_SIZE;
258 let (ciphertext_and_tag, nonce) = data.split_at(nonce_pos);
259 self.decrypt_to(nonce, aad, ciphertext_and_tag, out)
260 }
261
262 fn encrypt_to(
263 &self,
264 nonce: &[u8],
265 aad: &[u8],
266 plaintext: &[u8],
267 out: &mut [u8],
268 ) -> Result<usize, Error> {
269 let nonce = nonce_from_slice(nonce)?;
270 validate_gcm_lengths(aad.len(), plaintext.len())?;
271 let total = plaintext
272 .len()
273 .checked_add(TAG_SIZE)
274 .ok_or(Error::InputTooLarge)?;
275 if out.len() < total {
276 return Err(Error::OutputTooSmall);
277 }
278
279 let (ciphertext, rest) = out.split_at_mut(plaintext.len());
280 let tag = self
281 .seal(&nonce, aad, plaintext, ciphertext)
282 .ok_or(Error::Encrypt)?;
283 rest[..TAG_SIZE].copy_from_slice(&tag);
284 Ok(total)
285 }
286
287 fn seal(
294 &self,
295 nonce: &[u8; NONCE_SIZE],
296 aad: &[u8],
297 plaintext: &[u8],
298 ciphertext: &mut [u8],
299 ) -> Option<[u8; TAG_SIZE]> {
300 debug_assert_eq!(plaintext.len(), ciphertext.len());
301 let mut ghasher = ghash::Ghasher::new(&self.ghash)?;
302 ghasher.absorb_padded(aad);
303
304 let mut counter = j0(nonce);
305 increment_counter(&mut counter);
306
307 let bulk = (plaintext.len() / PAR_BYTES) * PAR_BYTES;
311 let (plaintext_bulk, plaintext_tail) = plaintext.split_at(bulk);
312 let (ciphertext_bulk, ciphertext_tail) = ciphertext.split_at_mut(bulk);
313 if !plaintext_bulk.is_empty() {
314 ghasher.seal_bulk(
315 self.aes.round_keys(),
316 &mut counter,
317 plaintext_bulk,
318 ciphertext_bulk,
319 );
320 }
321 if !plaintext_tail.is_empty() {
322 ciphertext_tail.copy_from_slice(plaintext_tail);
323 self.apply_ctr_serial(&mut counter, ciphertext_tail);
324 ghasher.absorb_padded(ciphertext_tail);
325 }
326
327 let mut tag = ghasher.finalize(aad.len(), plaintext.len())?;
328 let mut mask = j0(nonce);
329 self.aes.encrypt_block(&mut mask);
330 for (tag_byte, mask_byte) in tag.iter_mut().zip(mask.iter()) {
331 *tag_byte ^= mask_byte;
332 }
333 Some(tag)
334 }
335
336 fn seal_in_place(
340 &self,
341 nonce: &[u8; NONCE_SIZE],
342 aad: &[u8],
343 data: &mut [u8],
344 ) -> Option<[u8; TAG_SIZE]> {
345 let mut ghasher = ghash::Ghasher::new(&self.ghash)?;
346 ghasher.absorb_padded(aad);
347
348 let mut counter = j0(nonce);
349 increment_counter(&mut counter);
350
351 let bulk = (data.len() / PAR_BYTES) * PAR_BYTES;
352 let (data_bulk, data_tail) = data.split_at_mut(bulk);
353 if !data_bulk.is_empty() {
354 ghasher.seal_in_place_bulk(self.aes.round_keys(), &mut counter, data_bulk);
355 }
356 if !data_tail.is_empty() {
357 let full = (data_tail.len() / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
358 let (data_tail_blocks, data_partial) = data_tail.split_at_mut(full);
359 if !data_tail_blocks.is_empty() {
360 ghasher.seal_in_place_tail_blocks(
361 self.aes.round_keys(),
362 &mut counter,
363 data_tail_blocks,
364 );
365 }
366 if !data_partial.is_empty() {
367 self.apply_ctr_serial(&mut counter, data_partial);
368 ghasher.absorb_padded(data_partial);
369 }
370 }
371
372 let mut tag = ghasher.finalize(aad.len(), data.len())?;
373 let mut mask = j0(nonce);
374 self.aes.encrypt_block(&mut mask);
375 for (tag_byte, mask_byte) in tag.iter_mut().zip(mask.iter()) {
376 *tag_byte ^= mask_byte;
377 }
378 Some(tag)
379 }
380
381 fn apply_ctr_serial(&self, counter: &mut [u8; AES_BLOCK_SIZE], data: &mut [u8]) {
383 let mut keystream = [0_u8; AES_BLOCK_SIZE];
384 for block in data.chunks_mut(AES_BLOCK_SIZE) {
385 keystream.copy_from_slice(counter);
386 self.aes.encrypt_block(&mut keystream);
387 for (byte, key_byte) in block.iter_mut().zip(keystream.iter()) {
388 *byte ^= key_byte;
389 }
390 increment_counter(counter);
391 }
392 }
393
394 fn decrypt(
395 &self,
396 nonce: &[u8],
397 aad: &[u8],
398 ciphertext_and_tag: &[u8],
399 ) -> Result<Vec<u8>, Error> {
400 let nonce = nonce_from_slice(nonce)?;
401 if ciphertext_and_tag.len() < TAG_SIZE {
402 return Err(Error::Decrypt);
403 }
404
405 let tag_pos = ciphertext_and_tag.len() - TAG_SIZE;
406 let (ciphertext, tag) = ciphertext_and_tag.split_at(tag_pos);
407 validate_gcm_lengths(aad.len(), ciphertext.len())?;
408 let mut out = Vec::with_capacity(ciphertext.len());
409 out.extend_from_slice(ciphertext);
410 let Some(expected) = self.open_in_place(&nonce, aad, &mut out) else {
411 out.zeroize();
412 return Err(Error::Decrypt);
413 };
414 if !constant_time_eq(&expected, tag) {
415 out.zeroize();
416 return Err(Error::Decrypt);
417 }
418 Ok(out)
419 }
420
421 fn decrypt_to(
422 &self,
423 nonce: &[u8],
424 aad: &[u8],
425 ciphertext_and_tag: &[u8],
426 out: &mut [u8],
427 ) -> Result<usize, Error> {
428 let nonce = nonce_from_slice(nonce)?;
429 if ciphertext_and_tag.len() < TAG_SIZE {
430 return Err(Error::Decrypt);
431 }
432
433 let tag_pos = ciphertext_and_tag.len() - TAG_SIZE;
434 let (ciphertext, tag) = ciphertext_and_tag.split_at(tag_pos);
435 validate_gcm_lengths(aad.len(), ciphertext.len())?;
436 if out.len() < ciphertext.len() {
437 return Err(Error::OutputTooSmall);
438 }
439 let out = &mut out[..ciphertext.len()];
440 let Some(expected) = self.open(&nonce, aad, ciphertext, out) else {
441 out.zeroize();
442 return Err(Error::Decrypt);
443 };
444 if !constant_time_eq(&expected, tag) {
449 out.zeroize();
450 return Err(Error::Decrypt);
451 }
452
453 Ok(ciphertext.len())
454 }
455
456 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
459 fn seal_detached(
460 &self,
461 nonce: &[u8],
462 aad: &[u8],
463 plaintext: &[u8],
464 ) -> Result<(Vec<u8>, [u8; TAG_SIZE]), Error> {
465 let nonce = nonce_from_slice(nonce)?;
466 validate_gcm_lengths(aad.len(), plaintext.len())?;
467 let mut ciphertext = vec![0_u8; plaintext.len()];
468 let Some(tag) = self.seal(&nonce, aad, plaintext, &mut ciphertext) else {
469 ciphertext.zeroize();
470 return Err(Error::Encrypt);
471 };
472 Ok((ciphertext, tag))
473 }
474
475 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
478 fn seal_in_place_detached(
479 &self,
480 nonce: &[u8],
481 aad: &[u8],
482 data: &mut [u8],
483 ) -> Result<[u8; TAG_SIZE], Error> {
484 let nonce = nonce_from_slice(nonce)?;
485 validate_gcm_lengths(aad.len(), data.len())?;
486 let Some(tag) = self.seal_in_place(&nonce, aad, data) else {
487 data.zeroize();
488 return Err(Error::Encrypt);
489 };
490 Ok(tag)
491 }
492
493 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
497 fn open_detached(
498 &self,
499 nonce: &[u8],
500 aad: &[u8],
501 ciphertext: &[u8],
502 tag: &[u8],
503 ) -> Result<Vec<u8>, Error> {
504 let nonce = nonce_from_slice(nonce)?;
505 if tag.len() != TAG_SIZE {
506 return Err(Error::Decrypt);
507 }
508 validate_gcm_lengths(aad.len(), ciphertext.len())?;
509 let mut out = vec![0_u8; ciphertext.len()];
510 let Some(expected) = self.open(&nonce, aad, ciphertext, &mut out) else {
511 out.zeroize();
512 return Err(Error::Decrypt);
513 };
514 if !constant_time_eq(&expected, tag) {
515 out.zeroize();
516 return Err(Error::Decrypt);
517 }
518 Ok(out)
519 }
520
521 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
525 fn open_in_place_detached(
526 &self,
527 nonce: &[u8],
528 aad: &[u8],
529 data: &mut [u8],
530 tag: &[u8],
531 ) -> Result<(), Error> {
532 let nonce = nonce_from_slice(nonce)?;
533 if tag.len() != TAG_SIZE {
534 return Err(Error::Decrypt);
535 }
536 validate_gcm_lengths(aad.len(), data.len())?;
537 let Some(expected) = self.open_in_place(&nonce, aad, data) else {
538 data.zeroize();
539 return Err(Error::Decrypt);
540 };
541 if !constant_time_eq(&expected, tag) {
542 data.zeroize();
543 return Err(Error::Decrypt);
544 }
545 Ok(())
546 }
547
548 fn open(
552 &self,
553 nonce: &[u8; NONCE_SIZE],
554 aad: &[u8],
555 ciphertext: &[u8],
556 plaintext: &mut [u8],
557 ) -> Option<[u8; TAG_SIZE]> {
558 debug_assert_eq!(ciphertext.len(), plaintext.len());
559 let mut ghasher = ghash::Ghasher::new(&self.ghash)?;
560 ghasher.absorb_padded(aad);
561
562 let mut counter = j0(nonce);
563 increment_counter(&mut counter);
564
565 let bulk = (ciphertext.len() / PAR_BYTES) * PAR_BYTES;
569 let (ciphertext_bulk, ciphertext_tail) = ciphertext.split_at(bulk);
570 let (plaintext_bulk, plaintext_tail) = plaintext.split_at_mut(bulk);
571 if !ciphertext_bulk.is_empty() {
572 ghasher.open_bulk(
573 self.aes.round_keys(),
574 &mut counter,
575 ciphertext_bulk,
576 plaintext_bulk,
577 );
578 }
579 if !ciphertext_tail.is_empty() {
580 ghasher.absorb_padded(ciphertext_tail);
581 plaintext_tail.copy_from_slice(ciphertext_tail);
582 self.apply_ctr_serial(&mut counter, plaintext_tail);
583 }
584
585 let mut tag = ghasher.finalize(aad.len(), ciphertext.len())?;
586 let mut mask = j0(nonce);
587 self.aes.encrypt_block(&mut mask);
588 for (tag_byte, mask_byte) in tag.iter_mut().zip(mask.iter()) {
589 *tag_byte ^= mask_byte;
590 }
591 Some(tag)
592 }
593
594 fn open_in_place(
598 &self,
599 nonce: &[u8; NONCE_SIZE],
600 aad: &[u8],
601 data: &mut [u8],
602 ) -> Option<[u8; TAG_SIZE]> {
603 let mut ghasher = ghash::Ghasher::new(&self.ghash)?;
604 ghasher.absorb_padded(aad);
605
606 let mut counter = j0(nonce);
607 increment_counter(&mut counter);
608
609 let bulk = (data.len() / PAR_BYTES) * PAR_BYTES;
610 let (data_bulk, data_tail) = data.split_at_mut(bulk);
611 if !data_bulk.is_empty() {
612 ghasher.open_in_place_bulk(self.aes.round_keys(), &mut counter, data_bulk);
613 }
614 if !data_tail.is_empty() {
615 ghasher.absorb_padded(data_tail);
616 self.apply_ctr_serial(&mut counter, data_tail);
617 }
618
619 let mut tag = ghasher.finalize(aad.len(), data.len())?;
620 let mut mask = j0(nonce);
621 self.aes.encrypt_block(&mut mask);
622 for (tag_byte, mask_byte) in tag.iter_mut().zip(mask.iter()) {
623 *tag_byte ^= mask_byte;
624 }
625 Some(tag)
626 }
627}
628
629pub struct UninitKeyStateSlot<'a> {
631 storage: &'a mut [u8],
632}
633
634impl<'a> UninitKeyStateSlot<'a> {
635 pub fn new(storage: &'a mut [u8]) -> Result<Self, Error> {
642 let layout = HardwareAes256Gcm::key_state_layout();
643 if storage.len() < layout.size {
644 return Err(Error::KeyStateStorageTooSmall);
645 }
646 if !storage.as_ptr().addr().is_multiple_of(layout.align) {
647 return Err(Error::KeyStateStorageMisaligned);
648 }
649 Ok(Self { storage })
650 }
651}
652
653impl std::fmt::Debug for UninitKeyStateSlot<'_> {
654 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
655 f.debug_struct("UninitKeyStateSlot")
656 .field("len", &self.storage.len())
657 .finish_non_exhaustive()
658 }
659}
660
661#[inline]
662fn init_key_state_at(key: &[u8], state: NonNull<KeyState>) -> Result<(), Error> {
663 if key.len() != KEY_SIZE {
664 return Err(Error::InvalidKeyLength);
665 }
666 let key: &[u8; KEY_SIZE] = key.try_into().map_err(|_| Error::InvalidKeyLength)?;
667 KeyState::init_in_place(state, key)
668}
669
670fn init_key_state_in_slot(key: &[u8], slot: UninitKeyStateSlot<'_>) -> Result<NonNull<u8>, Error> {
671 let UninitKeyStateSlot { storage } = slot;
672 let storage = unsafe { NonNull::new_unchecked(storage.as_mut_ptr()) };
677 #[allow(clippy::cast_ptr_alignment)]
678 let state = storage.cast::<KeyState>();
679 init_key_state_at(key, state)?;
680 Ok(storage)
681}
682
683pub struct OpaqueKeyState<'a> {
689 storage: NonNull<u8>,
690 _marker: PhantomData<&'a mut [u8]>,
691}
692
693const _: () = {
694 const fn assert_send_sync<T: Send + Sync>() {}
695 assert_send_sync::<KeyState>();
698};
699
700unsafe impl Send for OpaqueKeyState<'_> {}
706unsafe impl Sync for OpaqueKeyState<'_> {}
709
710impl OpaqueKeyState<'_> {
711 fn state_ptr(&self) -> *mut KeyState {
712 #[allow(clippy::cast_ptr_alignment)]
715 self.storage.as_ptr().cast::<KeyState>()
716 }
717}
718
719impl Drop for OpaqueKeyState<'_> {
720 fn drop(&mut self) {
721 let size = HardwareAes256Gcm::key_state_layout().size;
722 unsafe { volatile_wipe(self.storage.as_ptr(), size) };
732 }
733}
734
735impl std::fmt::Debug for OpaqueKeyState<'_> {
736 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
737 f.debug_struct("OpaqueKeyState").finish_non_exhaustive()
738 }
739}
740
741pub struct HardwareAes256GcmIn<'a> {
743 state: OpaqueKeyState<'a>,
744 nonce_gen: Option<nonce::NonceGen>,
748}
749
750impl<'a> HardwareAes256GcmIn<'a> {
751 pub fn new_in(key: &[u8], slot: UninitKeyStateSlot<'a>) -> Result<Self, Error> {
758 let storage = init_key_state_in_slot(key, slot)?;
759 Ok(Self {
760 state: OpaqueKeyState {
761 storage,
762 _marker: PhantomData,
763 },
764 nonce_gen: None,
765 })
766 }
767
768 pub fn encrypt(&mut self, aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
775 let nonce = self.next_nonce()?;
776 self.state_ref().encrypt_envelope(&nonce, aad, plaintext)
777 }
778
779 pub fn encrypt_to(
791 &mut self,
792 aad: &[u8],
793 plaintext: &[u8],
794 out: &mut [u8],
795 ) -> Result<usize, Error> {
796 let nonce = self.next_nonce()?;
797 self.state_ref()
798 .encrypt_envelope_to(&nonce, aad, plaintext, out)
799 }
800
801 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
802 #[doc(hidden)]
803 pub fn encrypt_with_nonce(
804 &self,
805 nonce: &[u8],
806 aad: &[u8],
807 plaintext: &[u8],
808 ) -> Result<Vec<u8>, Error> {
809 self.state_ref().encrypt(nonce, aad, plaintext)
810 }
811
812 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
823 #[doc(hidden)]
824 pub fn encrypt_with_nonce_to(
825 &self,
826 nonce: &[u8],
827 aad: &[u8],
828 plaintext: &[u8],
829 out: &mut [u8],
830 ) -> Result<usize, Error> {
831 self.state_ref().encrypt_to(nonce, aad, plaintext, out)
832 }
833
834 pub fn decrypt(&self, aad: &[u8], ciphertext_tag_nonce: &[u8]) -> Result<Vec<u8>, Error> {
841 self.state_ref().decrypt_envelope(aad, ciphertext_tag_nonce)
842 }
843
844 pub fn decrypt_to(
856 &self,
857 aad: &[u8],
858 ciphertext_tag_nonce: &[u8],
859 out: &mut [u8],
860 ) -> Result<usize, Error> {
861 self.state_ref()
862 .decrypt_envelope_to(aad, ciphertext_tag_nonce, out)
863 }
864
865 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
866 #[doc(hidden)]
867 pub fn decrypt_with_nonce(
868 &self,
869 nonce: &[u8],
870 aad: &[u8],
871 ciphertext_and_tag: &[u8],
872 ) -> Result<Vec<u8>, Error> {
873 self.state_ref().decrypt(nonce, aad, ciphertext_and_tag)
874 }
875
876 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
888 #[doc(hidden)]
889 pub fn decrypt_with_nonce_to(
890 &self,
891 nonce: &[u8],
892 aad: &[u8],
893 ciphertext_and_tag: &[u8],
894 out: &mut [u8],
895 ) -> Result<usize, Error> {
896 self.state_ref()
897 .decrypt_to(nonce, aad, ciphertext_and_tag, out)
898 }
899
900 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
906 #[doc(hidden)]
907 pub fn encrypt_nonce_appended(&self, nonce: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
908 self.state_ref().encrypt_nonce_appended(nonce, plaintext)
909 }
910
911 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
922 #[doc(hidden)]
923 pub fn encrypt_nonce_appended_in_place(
924 &self,
925 nonce: &[u8],
926 in_out: &mut Vec<u8>,
927 ) -> Result<(), Error> {
928 self.state_ref()
929 .encrypt_nonce_appended_in_place(nonce, in_out)
930 }
931
932 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
941 #[doc(hidden)]
942 pub fn encrypt_nonce_appended_to(
943 &self,
944 nonce: &[u8],
945 plaintext: &[u8],
946 out: &mut [u8],
947 ) -> Result<usize, Error> {
948 encrypt_nonce_appended_to(self.state_ref(), nonce, plaintext, out)
949 }
950
951 #[doc(hidden)]
959 pub fn decrypt_nonce_appended(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
960 self.state_ref().decrypt_envelope(&[], data)
961 }
962
963 #[doc(hidden)]
971 pub fn decrypt_nonce_appended_to(&self, data: &[u8], out: &mut [u8]) -> Result<usize, Error> {
972 self.state_ref().decrypt_envelope_to(&[], data, out)
973 }
974
975 #[doc(hidden)]
988 pub fn encrypt_with_generated_nonce(
989 &mut self,
990 aad: &[u8],
991 plaintext: &[u8],
992 ) -> Result<([u8; NONCE_SIZE], Vec<u8>), Error> {
993 let nonce = self.next_nonce()?;
994 let ciphertext = self.state_ref().encrypt(&nonce, aad, plaintext)?;
995 Ok((nonce, ciphertext))
996 }
997
998 #[doc(hidden)]
1005 pub fn encrypt_nonce_appended_generated(&mut self, plaintext: &[u8]) -> Result<Vec<u8>, Error> {
1006 let nonce = self.next_nonce()?;
1007 self.state_ref().encrypt_nonce_appended(&nonce, plaintext)
1008 }
1009
1010 fn next_nonce(&mut self) -> Result<[u8; NONCE_SIZE], Error> {
1011 match self.nonce_gen {
1012 Some(ref mut g) => g.next(),
1013 None => self.nonce_gen.insert(nonce::NonceGen::new()?).next(),
1014 }
1015 }
1016
1017 fn state_ref(&self) -> &KeyState {
1018 unsafe { &*self.state.state_ptr() }
1020 }
1021}
1022
1023impl std::fmt::Debug for HardwareAes256GcmIn<'_> {
1024 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1025 f.debug_struct("HardwareAes256GcmIn")
1026 .finish_non_exhaustive()
1027 }
1028}
1029
1030#[repr(transparent)]
1031struct AlignedKeyStateStorage(MaybeUninit<KeyState>);
1032
1033impl AlignedKeyStateStorage {
1034 #[inline]
1035 const fn uninit() -> Self {
1036 Self(MaybeUninit::uninit())
1037 }
1038
1039 #[inline]
1040 fn state_ptr(&self) -> *const KeyState {
1041 self.0.as_ptr()
1042 }
1043
1044 #[inline]
1045 fn state_ptr_mut(&mut self) -> NonNull<KeyState> {
1046 unsafe { NonNull::new_unchecked(self.0.as_mut_ptr()) }
1048 }
1049
1050 #[inline]
1051 fn bytes_ptr_mut(&mut self) -> *mut u8 {
1052 self.0.as_mut_ptr().cast::<u8>()
1053 }
1054}
1055
1056pub struct HardwareAes256GcmKeyState {
1063 storage: AlignedKeyStateStorage,
1064 nonce_gen: Option<nonce::NonceGen>,
1065}
1066
1067impl HardwareAes256GcmKeyState {
1068 #[inline]
1076 pub fn new(key: &[u8]) -> Result<Self, Error> {
1077 let mut storage = AlignedKeyStateStorage::uninit();
1078 init_key_state_at(key, storage.state_ptr_mut())?;
1079 Ok(Self {
1080 storage,
1081 nonce_gen: None,
1082 })
1083 }
1084
1085 #[must_use]
1087 pub const fn state_size() -> usize {
1088 std::mem::size_of::<KeyState>()
1089 }
1090
1091 pub fn encrypt(&mut self, aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
1099 let nonce = self.next_nonce()?;
1100 self.state_ref().encrypt_envelope(&nonce, aad, plaintext)
1101 }
1102
1103 pub fn encrypt_to(
1116 &mut self,
1117 aad: &[u8],
1118 plaintext: &[u8],
1119 out: &mut [u8],
1120 ) -> Result<usize, Error> {
1121 let nonce = self.next_nonce()?;
1122 self.state_ref()
1123 .encrypt_envelope_to(&nonce, aad, plaintext, out)
1124 }
1125
1126 pub fn decrypt(&self, aad: &[u8], ciphertext_tag_nonce: &[u8]) -> Result<Vec<u8>, Error> {
1133 self.state_ref().decrypt_envelope(aad, ciphertext_tag_nonce)
1134 }
1135
1136 pub fn decrypt_to(
1148 &self,
1149 aad: &[u8],
1150 ciphertext_tag_nonce: &[u8],
1151 out: &mut [u8],
1152 ) -> Result<usize, Error> {
1153 self.state_ref()
1154 .decrypt_envelope_to(aad, ciphertext_tag_nonce, out)
1155 }
1156
1157 #[inline]
1164 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
1165 #[doc(hidden)]
1166 pub fn encrypt_nonce_appended(&self, nonce: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
1167 self.state_ref().encrypt_nonce_appended(nonce, plaintext)
1168 }
1169
1170 #[inline]
1181 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
1182 #[doc(hidden)]
1183 pub fn encrypt_nonce_appended_in_place(
1184 &self,
1185 nonce: &[u8],
1186 in_out: &mut Vec<u8>,
1187 ) -> Result<(), Error> {
1188 self.state_ref()
1189 .encrypt_nonce_appended_in_place(nonce, in_out)
1190 }
1191
1192 #[inline]
1200 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
1201 #[doc(hidden)]
1202 pub fn decrypt_nonce_appended(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
1203 self.state_ref().decrypt_envelope(&[], data)
1204 }
1205
1206 #[inline]
1222 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
1223 #[doc(hidden)]
1224 pub fn seal_detached(
1225 &self,
1226 nonce: &[u8],
1227 aad: &[u8],
1228 plaintext: &[u8],
1229 ) -> Result<(Vec<u8>, [u8; TAG_SIZE]), Error> {
1230 self.state_ref().seal_detached(nonce, aad, plaintext)
1231 }
1232
1233 #[inline]
1242 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
1243 #[doc(hidden)]
1244 pub fn seal_in_place_detached(
1245 &self,
1246 nonce: &[u8],
1247 aad: &[u8],
1248 data: &mut [u8],
1249 ) -> Result<[u8; TAG_SIZE], Error> {
1250 self.state_ref().seal_in_place_detached(nonce, aad, data)
1251 }
1252
1253 #[inline]
1262 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
1263 #[doc(hidden)]
1264 pub fn open_detached(
1265 &self,
1266 nonce: &[u8],
1267 aad: &[u8],
1268 ciphertext: &[u8],
1269 tag: &[u8],
1270 ) -> Result<Vec<u8>, Error> {
1271 self.state_ref().open_detached(nonce, aad, ciphertext, tag)
1272 }
1273
1274 #[inline]
1283 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
1284 #[doc(hidden)]
1285 pub fn open_in_place_detached(
1286 &self,
1287 nonce: &[u8],
1288 aad: &[u8],
1289 data: &mut [u8],
1290 tag: &[u8],
1291 ) -> Result<(), Error> {
1292 self.state_ref()
1293 .open_in_place_detached(nonce, aad, data, tag)
1294 }
1295
1296 fn next_nonce(&mut self) -> Result<[u8; NONCE_SIZE], Error> {
1297 match self.nonce_gen {
1298 Some(ref mut g) => g.next(),
1299 None => self.nonce_gen.insert(nonce::NonceGen::new()?).next(),
1300 }
1301 }
1302
1303 fn state_ref(&self) -> &KeyState {
1304 unsafe { &*self.storage.state_ptr() }
1308 }
1309}
1310
1311impl Drop for HardwareAes256GcmKeyState {
1312 fn drop(&mut self) {
1313 let size = Self::state_size();
1314 unsafe { volatile_wipe(self.storage.bytes_ptr_mut(), size) };
1321 }
1322}
1323
1324impl std::fmt::Debug for HardwareAes256GcmKeyState {
1325 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1326 f.debug_struct("HardwareAes256GcmKeyState")
1327 .finish_non_exhaustive()
1328 }
1329}
1330
1331pub struct HardwareAes256Gcm {
1333 state: Box<KeyState>,
1334 nonce_gen: Option<nonce::NonceGen>,
1338}
1339
1340impl HardwareAes256Gcm {
1341 pub fn new(key: &[u8]) -> Result<Self, Error> {
1347 if key.len() != KEY_SIZE {
1348 return Err(Error::InvalidKeyLength);
1349 }
1350 let mut state = Box::<KeyState>::new_uninit();
1351 let ptr = unsafe { NonNull::new_unchecked(state.as_mut_ptr()) };
1353 init_key_state_at(key, ptr)?;
1354 let state = unsafe { state.assume_init() };
1356 Ok(Self {
1357 state,
1358 nonce_gen: None,
1359 })
1360 }
1361
1362 #[must_use]
1364 pub fn hardware_available() -> bool {
1365 aes::hardware_available() && ghash::hardware_available()
1366 }
1367
1368 #[must_use]
1372 pub const fn state_size() -> usize {
1373 std::mem::size_of::<KeyState>()
1374 }
1375
1376 #[must_use]
1380 pub const fn key_state_layout() -> KeyStateLayout {
1381 KeyStateLayout {
1382 size: std::mem::size_of::<KeyState>(),
1383 align: std::mem::align_of::<KeyState>(),
1384 }
1385 }
1386
1387 pub fn encrypt(&mut self, aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
1395 let nonce = self.next_nonce()?;
1396 self.state.encrypt_envelope(&nonce, aad, plaintext)
1397 }
1398
1399 pub fn encrypt_to(
1412 &mut self,
1413 aad: &[u8],
1414 plaintext: &[u8],
1415 out: &mut [u8],
1416 ) -> Result<usize, Error> {
1417 let nonce = self.next_nonce()?;
1418 self.state.encrypt_envelope_to(&nonce, aad, plaintext, out)
1419 }
1420
1421 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
1422 #[doc(hidden)]
1423 pub fn encrypt_with_nonce(
1424 &self,
1425 nonce: &[u8],
1426 aad: &[u8],
1427 plaintext: &[u8],
1428 ) -> Result<Vec<u8>, Error> {
1429 self.state.encrypt(nonce, aad, plaintext)
1430 }
1431
1432 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
1443 #[doc(hidden)]
1444 pub fn encrypt_with_nonce_to(
1445 &self,
1446 nonce: &[u8],
1447 aad: &[u8],
1448 plaintext: &[u8],
1449 out: &mut [u8],
1450 ) -> Result<usize, Error> {
1451 self.state.encrypt_to(nonce, aad, plaintext, out)
1452 }
1453
1454 pub fn decrypt(&self, aad: &[u8], ciphertext_tag_nonce: &[u8]) -> Result<Vec<u8>, Error> {
1461 self.state.decrypt_envelope(aad, ciphertext_tag_nonce)
1462 }
1463
1464 pub fn decrypt_to(
1476 &self,
1477 aad: &[u8],
1478 ciphertext_tag_nonce: &[u8],
1479 out: &mut [u8],
1480 ) -> Result<usize, Error> {
1481 self.state
1482 .decrypt_envelope_to(aad, ciphertext_tag_nonce, out)
1483 }
1484
1485 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
1486 #[doc(hidden)]
1487 pub fn decrypt_with_nonce(
1488 &self,
1489 nonce: &[u8],
1490 aad: &[u8],
1491 ciphertext_and_tag: &[u8],
1492 ) -> Result<Vec<u8>, Error> {
1493 self.state.decrypt(nonce, aad, ciphertext_and_tag)
1494 }
1495
1496 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
1508 #[doc(hidden)]
1509 pub fn decrypt_with_nonce_to(
1510 &self,
1511 nonce: &[u8],
1512 aad: &[u8],
1513 ciphertext_and_tag: &[u8],
1514 out: &mut [u8],
1515 ) -> Result<usize, Error> {
1516 self.state.decrypt_to(nonce, aad, ciphertext_and_tag, out)
1517 }
1518
1519 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
1525 #[doc(hidden)]
1526 pub fn encrypt_nonce_appended(&self, nonce: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
1527 self.state.encrypt_nonce_appended(nonce, plaintext)
1528 }
1529
1530 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
1541 #[doc(hidden)]
1542 pub fn encrypt_nonce_appended_in_place(
1543 &self,
1544 nonce: &[u8],
1545 in_out: &mut Vec<u8>,
1546 ) -> Result<(), Error> {
1547 self.state.encrypt_nonce_appended_in_place(nonce, in_out)
1548 }
1549
1550 #[cfg(any(test, feature = "hazmat-explicit-nonce"))]
1559 #[doc(hidden)]
1560 pub fn encrypt_nonce_appended_to(
1561 &self,
1562 nonce: &[u8],
1563 plaintext: &[u8],
1564 out: &mut [u8],
1565 ) -> Result<usize, Error> {
1566 encrypt_nonce_appended_to(&self.state, nonce, plaintext, out)
1567 }
1568
1569 #[doc(hidden)]
1578 pub fn decrypt_nonce_appended(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
1579 self.state.decrypt_envelope(&[], data)
1580 }
1581
1582 #[doc(hidden)]
1590 pub fn decrypt_nonce_appended_to(&self, data: &[u8], out: &mut [u8]) -> Result<usize, Error> {
1591 self.state.decrypt_envelope_to(&[], data, out)
1592 }
1593
1594 #[doc(hidden)]
1607 pub fn encrypt_with_generated_nonce(
1608 &mut self,
1609 aad: &[u8],
1610 plaintext: &[u8],
1611 ) -> Result<([u8; NONCE_SIZE], Vec<u8>), Error> {
1612 let nonce = self.next_nonce()?;
1613 let ciphertext = self.state.encrypt(&nonce, aad, plaintext)?;
1614 Ok((nonce, ciphertext))
1615 }
1616
1617 #[doc(hidden)]
1624 pub fn encrypt_nonce_appended_generated(&mut self, plaintext: &[u8]) -> Result<Vec<u8>, Error> {
1625 let nonce = self.next_nonce()?;
1626 self.state.encrypt_nonce_appended(&nonce, plaintext)
1627 }
1628
1629 fn next_nonce(&mut self) -> Result<[u8; NONCE_SIZE], Error> {
1630 match self.nonce_gen {
1631 Some(ref mut g) => g.next(),
1632 None => self.nonce_gen.insert(nonce::NonceGen::new()?).next(),
1633 }
1634 }
1635}
1636
1637impl std::fmt::Debug for HardwareAes256Gcm {
1638 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1639 f.debug_struct("HardwareAes256Gcm").finish_non_exhaustive()
1640 }
1641}
1642
1643fn nonce_from_slice(nonce: &[u8]) -> Result<[u8; NONCE_SIZE], Error> {
1644 nonce.try_into().map_err(|_| Error::InvalidNonceLength)
1645}
1646
1647fn append_tag_nonce(out: &mut Vec<u8>, tag: &[u8; TAG_SIZE], nonce: &[u8; NONCE_SIZE]) {
1648 debug_assert!(out.capacity() >= out.len() + TAG_SIZE + NONCE_SIZE);
1649
1650 let start = out.len();
1651 let trailer = &mut out.spare_capacity_mut()[..TAG_SIZE + NONCE_SIZE];
1652 for (slot, byte) in trailer[..TAG_SIZE].iter_mut().zip(tag) {
1653 slot.write(*byte);
1654 }
1655 for (slot, byte) in trailer[TAG_SIZE..].iter_mut().zip(nonce) {
1656 slot.write(*byte);
1657 }
1658
1659 unsafe { out.set_len(start + TAG_SIZE + NONCE_SIZE) };
1663}
1664
1665#[allow(clippy::cast_ptr_alignment)] unsafe fn volatile_wipe(bytes: *mut u8, len: usize) {
1673 let mut offset = 0_usize;
1674 if bytes.addr().is_multiple_of(core::mem::align_of::<u128>()) {
1675 while offset + core::mem::size_of::<u128>() <= len {
1676 unsafe { ptr::write_volatile(bytes.add(offset).cast::<u128>(), 0) };
1679 offset += core::mem::size_of::<u128>();
1680 }
1681 }
1682 for offset in offset..len {
1683 unsafe { ptr::write_volatile(bytes.add(offset), 0) };
1685 }
1686 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
1687}
1688
1689#[cfg(any(test, feature = "hazmat-explicit-nonce"))]
1691fn encrypt_nonce_appended_to(
1692 state: &KeyState,
1693 nonce: &[u8],
1694 plaintext: &[u8],
1695 out: &mut [u8],
1696) -> Result<usize, Error> {
1697 let total = plaintext
1698 .len()
1699 .checked_add(TAG_SIZE + NONCE_SIZE)
1700 .ok_or(Error::InputTooLarge)?;
1701 if out.len() < total {
1702 return Err(Error::OutputTooSmall);
1703 }
1704 let written = state.encrypt_to(nonce, &[], plaintext, out)?;
1705 out[written..total].copy_from_slice(nonce);
1708 Ok(total)
1709}
1710
1711fn validate_gcm_lengths(aad_len: usize, data_len: usize) -> Result<(), Error> {
1712 if len_exceeds_u64_limit(data_len, MAX_GCM_DATA_LEN)
1713 || len_exceeds_u64_limit(aad_len, MAX_GHASH_INPUT_LEN)
1714 || len_exceeds_u64_limit(data_len, MAX_GHASH_INPUT_LEN)
1715 {
1716 return Err(Error::InputTooLarge);
1717 }
1718 Ok(())
1719}
1720
1721fn len_exceeds_u64_limit(len: usize, limit: u64) -> bool {
1722 u64::try_from(len).map_or(true, |len| len > limit)
1723}
1724
1725fn j0(nonce: &[u8; NONCE_SIZE]) -> [u8; 16] {
1726 let mut out = [0_u8; 16];
1727 out[..NONCE_SIZE].copy_from_slice(nonce);
1728 out[15] = 1;
1729 out
1730}
1731
1732pub(crate) fn increment_counter(counter: &mut [u8; 16]) {
1735 let mut low_bytes = [0_u8; 4];
1736 low_bytes.copy_from_slice(&counter[12..]);
1737 let low = u32::from_be_bytes(low_bytes).wrapping_add(1);
1738 counter[12..].copy_from_slice(&low.to_be_bytes());
1739}
1740
1741#[cfg(feature = "saw-verify")]
1745#[no_mangle]
1746#[allow(clippy::not_unsafe_ptr_arg_deref)]
1747pub extern "C" fn saw_increment_counter(counter: *mut u8) {
1748 let c = unsafe { &mut *(counter.cast::<[u8; 16]>()) };
1750 increment_counter(c);
1751}
1752
1753#[cfg(feature = "saw-verify")]
1756#[no_mangle]
1757#[allow(clippy::not_unsafe_ptr_arg_deref)]
1758pub extern "C" fn saw_j0(nonce: *const u8, out: *mut u8) {
1759 let n = unsafe { &*(nonce.cast::<[u8; NONCE_SIZE]>()) };
1761 let block = j0(n);
1762 unsafe { core::ptr::copy_nonoverlapping(block.as_ptr(), out, 16) };
1764}
1765
1766fn constant_time_eq(expected: &[u8; TAG_SIZE], actual: &[u8]) -> bool {
1767 expected.as_slice().ct_eq(actual).into()
1772}
1773
1774#[cfg(feature = "ct-verify")]
1779#[inline(never)]
1780#[must_use]
1781pub fn ct_verify_constant_time_eq(expected: &[u8; TAG_SIZE], actual: &[u8; TAG_SIZE]) -> bool {
1782 constant_time_eq(
1783 core::hint::black_box(expected),
1784 core::hint::black_box(actual.as_slice()),
1785 )
1786}
1787
1788#[cfg(test)]
1789mod tests {
1790 #![allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
1791
1792 use super::{
1793 increment_counter, j0, Error, HardwareAes256Gcm, HardwareAes256GcmIn,
1794 HardwareAes256GcmKeyState, UninitKeyStateSlot, NONCE_SIZE, TAG_SIZE,
1795 };
1796 use core::mem::ManuallyDrop;
1797
1798 #[repr(align(64))]
1799 struct AlignedStorage<const N: usize>([u8; N]);
1800
1801 #[test]
1805 fn increment_counter_is_be32_inc_leaving_high_96_bits() {
1806 let mut counter = [0_u8; 16];
1808 counter[..12].copy_from_slice(&[0xAA; 12]);
1809 counter[12..].copy_from_slice(&u32::MAX.to_be_bytes());
1810 increment_counter(&mut counter);
1811 assert_eq!(
1812 &counter[..12],
1813 &[0xAA; 12],
1814 "leading 96 bits must be preserved"
1815 );
1816 assert_eq!(
1817 &counter[12..],
1818 &0_u32.to_be_bytes(),
1819 "low 32 bits must wrap to 0"
1820 );
1821
1822 let mut counter = [0_u8; 16];
1824 counter[12..].copy_from_slice(&0x0000_00FF_u32.to_be_bytes());
1825 increment_counter(&mut counter);
1826 assert_eq!(&counter[12..], &0x0000_0100_u32.to_be_bytes());
1827
1828 let nonce = [0x11_u8; NONCE_SIZE];
1830 let block = j0(&nonce);
1831 assert_eq!(&block[..NONCE_SIZE], &nonce);
1832 assert_eq!(&block[NONCE_SIZE..], &[0, 0, 0, 1]);
1833 }
1834
1835 #[test]
1836 fn rejects_wrong_key_length() {
1837 let Err(err) = HardwareAes256Gcm::new(&[0_u8; 31]) else {
1838 panic!("31-byte key should be rejected");
1839 };
1840 assert_eq!(err, Error::InvalidKeyLength);
1841 }
1842
1843 #[test]
1844 fn caller_placed_key_state_rejects_wrong_key_length() {
1845 let layout = HardwareAes256Gcm::key_state_layout();
1846 let mut storage = AlignedStorage::<512>([0_u8; 512]);
1847 let slot = UninitKeyStateSlot::new(&mut storage.0[..layout.size]).expect("valid slot");
1848 let Err(err) = HardwareAes256GcmIn::new_in(&[0_u8; 31], slot) else {
1849 panic!("31-byte key should be rejected");
1850 };
1851 assert_eq!(err, Error::InvalidKeyLength);
1852 }
1853
1854 #[test]
1855 fn inline_owned_key_state_rejects_wrong_key_length() {
1856 let Err(err) = HardwareAes256GcmKeyState::new(&[0_u8; 31]) else {
1857 panic!("31-byte key should be rejected");
1858 };
1859 assert_eq!(err, Error::InvalidKeyLength);
1860 }
1861
1862 #[test]
1863 fn rejects_ciphertext_shorter_than_tag() {
1864 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1865 let Err(err) =
1866 key.decrypt_with_nonce(&[0_u8; NONCE_SIZE], &[], &[0_u8; super::TAG_SIZE - 1])
1867 else {
1868 panic!("input shorter than the tag should be rejected");
1869 };
1870 assert_eq!(err, Error::Decrypt);
1871 }
1872
1873 #[test]
1874 fn rejects_nonce_appended_shorter_than_tag_and_nonce() {
1875 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1876 let short = [0_u8; super::TAG_SIZE + NONCE_SIZE - 1];
1877 let Err(err) = key.decrypt_nonce_appended(&short) else {
1878 panic!("input shorter than tag plus nonce should be rejected");
1879 };
1880 assert_eq!(err, Error::CiphertextTooShort);
1881
1882 let mut out = [0_u8; 64];
1883 let Err(err) = key.decrypt_nonce_appended_to(&short, &mut out) else {
1884 panic!("input shorter than tag plus nonce should be rejected");
1885 };
1886 assert_eq!(err, Error::CiphertextTooShort);
1887 }
1888
1889 #[test]
1890 fn decrypt_to_writes_plaintext_into_caller_buffer() {
1891 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1892 let nonce = [9_u8; NONCE_SIZE];
1893 let ciphertext = key
1894 .encrypt_with_nonce(&nonce, b"aad", b"plaintext")
1895 .expect("encryption should succeed");
1896
1897 let mut exact = [0_u8; 9];
1898 let written = key
1899 .decrypt_with_nonce_to(&nonce, b"aad", &ciphertext, &mut exact)
1900 .expect("decryption into an exact-size buffer should succeed");
1901 assert_eq!(written, 9);
1902 assert_eq!(&exact, b"plaintext");
1903
1904 let mut oversized = [0xff_u8; 32];
1905 let written = key
1906 .decrypt_with_nonce_to(&nonce, b"aad", &ciphertext, &mut oversized)
1907 .expect("decryption into a larger buffer should succeed");
1908 assert_eq!(&oversized[..written], b"plaintext");
1909
1910 let mut short = [0_u8; 8];
1911 let Err(err) = key.decrypt_with_nonce_to(&nonce, b"aad", &ciphertext, &mut short) else {
1912 panic!("short output buffer should be rejected");
1913 };
1914 assert_eq!(err, Error::OutputTooSmall);
1915 assert_eq!(short, [0_u8; 8], "no plaintext may leak into short buffers");
1916
1917 let mut tampered = ciphertext;
1918 *tampered.last_mut().expect("tag byte") ^= 1;
1919 let mut invalid_out = [0xff_u8; 9];
1920 let Err(err) = key.decrypt_with_nonce_to(&nonce, b"aad", &tampered, &mut invalid_out)
1921 else {
1922 panic!("invalid authentication tag should be rejected");
1923 };
1924 assert_eq!(err, Error::Decrypt);
1925 assert_eq!(
1926 invalid_out, [0_u8; 9],
1927 "transient plaintext must be wiped on authentication failure"
1928 );
1929 }
1930
1931 #[test]
1932 fn encrypt_to_matches_allocating_encrypt() {
1933 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1934 let nonce = [9_u8; NONCE_SIZE];
1935 let plaintext = [0x5a_u8; 200];
1936 let expected = key
1937 .encrypt_with_nonce(&nonce, b"aad", &plaintext)
1938 .expect("encryption should succeed");
1939
1940 let mut exact = [0_u8; 200 + super::TAG_SIZE];
1941 let written = key
1942 .encrypt_with_nonce_to(&nonce, b"aad", &plaintext, &mut exact)
1943 .expect("encryption into an exact-size buffer should succeed");
1944 assert_eq!(written, exact.len());
1945 assert_eq!(exact.as_slice(), expected.as_slice());
1946
1947 let mut oversized = [0_u8; 256];
1948 let written = key
1949 .encrypt_with_nonce_to(&nonce, b"aad", &plaintext, &mut oversized)
1950 .expect("encryption into a larger buffer should succeed");
1951 assert_eq!(&oversized[..written], expected.as_slice());
1952
1953 let mut short = [0_u8; 200 + super::TAG_SIZE - 1];
1954 let Err(err) = key.encrypt_with_nonce_to(&nonce, b"aad", &plaintext, &mut short) else {
1955 panic!("short output buffer should be rejected");
1956 };
1957 assert_eq!(err, Error::OutputTooSmall);
1958 assert!(
1959 short.iter().all(|byte| *byte == 0),
1960 "no ciphertext may be written into short buffers"
1961 );
1962 }
1963
1964 #[test]
1965 fn encrypt_nonce_appended_to_matches_allocating_variant() {
1966 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1967 let nonce = [9_u8; NONCE_SIZE];
1968 let expected = key
1969 .encrypt_nonce_appended(&nonce, b"plaintext")
1970 .expect("encryption should succeed");
1971
1972 let mut out = [0_u8; 9 + super::TAG_SIZE + NONCE_SIZE];
1973 let written = key
1974 .encrypt_nonce_appended_to(&nonce, b"plaintext", &mut out)
1975 .expect("encryption should succeed");
1976 assert_eq!(written, out.len());
1977 assert_eq!(out.as_slice(), expected.as_slice());
1978
1979 let mut short = [0_u8; 9 + super::TAG_SIZE + NONCE_SIZE - 1];
1980 let Err(err) = key.encrypt_nonce_appended_to(&nonce, b"plaintext", &mut short) else {
1981 panic!("short output buffer should be rejected");
1982 };
1983 assert_eq!(err, Error::OutputTooSmall);
1984 }
1985
1986 #[test]
1987 fn encrypt_nonce_appended_in_place_uses_existing_vec_capacity() {
1988 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1989 let nonce = [9_u8; NONCE_SIZE];
1990 let expected = key
1991 .encrypt_nonce_appended(&nonce, b"plaintext")
1992 .expect("encryption should succeed");
1993
1994 let mut in_out = Vec::with_capacity(b"plaintext".len() + super::TAG_SIZE + NONCE_SIZE);
1995 in_out.extend_from_slice(b"plaintext");
1996 let ptr_before = in_out.as_ptr();
1997 key.encrypt_nonce_appended_in_place(&nonce, &mut in_out)
1998 .expect("in-place encryption should succeed");
1999
2000 assert_eq!(in_out, expected);
2001 assert_eq!(
2002 in_out.as_ptr(),
2003 ptr_before,
2004 "pre-reserved in-place encryption should not reallocate"
2005 );
2006 assert_eq!(
2007 key.decrypt_nonce_appended(&in_out)
2008 .expect("decryption should succeed"),
2009 b"plaintext"
2010 );
2011 }
2012
2013 #[test]
2014 fn decrypt_nonce_appended_to_round_trips() {
2015 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
2016 let nonce = [9_u8; NONCE_SIZE];
2017 let data = key
2018 .encrypt_nonce_appended(&nonce, b"plaintext")
2019 .expect("encryption should succeed");
2020
2021 let mut out = [0_u8; 32];
2022 let written = key
2023 .decrypt_nonce_appended_to(&data, &mut out)
2024 .expect("decryption should succeed");
2025 assert_eq!(&out[..written], b"plaintext");
2026 }
2027
2028 #[test]
2029 fn generated_nonce_round_trips_and_is_unique() {
2030 let mut key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
2031
2032 let (nonce_a, ct_a) = key
2033 .encrypt_with_generated_nonce(b"aad", b"plaintext")
2034 .expect("encryption should succeed");
2035 let (nonce_b, _ct_b) = key
2036 .encrypt_with_generated_nonce(b"aad", b"plaintext")
2037 .expect("encryption should succeed");
2038
2039 assert_ne!(nonce_a, nonce_b, "generated nonces must be unique");
2041 assert_eq!(
2043 key.decrypt_with_nonce(&nonce_a, b"aad", &ct_a)
2044 .expect("decryption should succeed"),
2045 b"plaintext"
2046 );
2047 }
2048
2049 #[test]
2050 fn generated_nonce_appended_layout_round_trips() {
2051 let mut key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
2052 let blob = key
2053 .encrypt_nonce_appended_generated(b"plaintext")
2054 .expect("encryption should succeed");
2055 assert_eq!(
2056 blob.len(),
2057 b"plaintext".len() + super::TAG_SIZE + NONCE_SIZE
2058 );
2059 assert_eq!(
2060 key.decrypt_nonce_appended(&blob)
2061 .expect("decryption should succeed"),
2062 b"plaintext"
2063 );
2064 }
2065
2066 #[test]
2067 fn caller_placed_generated_nonce_round_trips() {
2068 let layout = HardwareAes256Gcm::key_state_layout();
2069 let mut storage = AlignedStorage::<512>([0_u8; 512]);
2070 let slot = UninitKeyStateSlot::new(&mut storage.0[..layout.size]).expect("valid slot");
2071 let mut key = HardwareAes256GcmIn::new_in(&[7_u8; 32], slot).expect("valid test key");
2072 let (nonce, ct) = key
2073 .encrypt_with_generated_nonce(&[], b"plaintext")
2074 .expect("encryption should succeed");
2075 assert_eq!(
2076 key.decrypt_with_nonce(&nonce, &[], &ct)
2077 .expect("decryption should succeed"),
2078 b"plaintext"
2079 );
2080 }
2081
2082 #[test]
2089 fn caller_placed_in_buffer_methods_round_trip() {
2090 const KEY: [u8; 32] = [7_u8; 32];
2091 const NONCE: [u8; NONCE_SIZE] = [0x24; NONCE_SIZE];
2092 let pt = b"caller-placed in-buffer methods".to_vec();
2093 let aad = b"header".as_slice();
2094
2095 let layout = HardwareAes256Gcm::key_state_layout();
2096 let mut storage = AlignedStorage::<512>([0_u8; 512]);
2097 let slot = UninitKeyStateSlot::new(&mut storage.0[..layout.size]).expect("valid slot");
2098 let mut key = HardwareAes256GcmIn::new_in(&KEY, slot).expect("valid test key");
2099 let reference = HardwareAes256Gcm::new(&KEY).expect("reference key");
2101
2102 let ct = key
2104 .encrypt_with_nonce(&NONCE, aad, &pt)
2105 .expect("encrypt_with_nonce");
2106 assert_eq!(
2107 ct,
2108 reference.encrypt_with_nonce(&NONCE, aad, &pt).unwrap(),
2109 "encrypt_with_nonce diverged from the reference cipher"
2110 );
2111 assert_eq!(key.decrypt_with_nonce(&NONCE, aad, &ct).unwrap(), pt);
2112
2113 let mut buf = vec![0_u8; pt.len() + TAG_SIZE];
2114 let n = key
2115 .encrypt_with_nonce_to(&NONCE, aad, &pt, &mut buf)
2116 .expect("encrypt_with_nonce_to");
2117 assert_eq!(n, pt.len() + TAG_SIZE);
2118 assert_eq!(&buf[..n], &ct[..]);
2119 let mut out = vec![0_u8; pt.len()];
2120 let m = key
2121 .decrypt_with_nonce_to(&NONCE, aad, &buf[..n], &mut out)
2122 .expect("decrypt_with_nonce_to");
2123 assert_eq!(&out[..m], &pt[..]);
2124
2125 let env = key
2127 .encrypt_nonce_appended(&NONCE, &pt)
2128 .expect("encrypt_nonce_appended");
2129 assert_eq!(key.decrypt_nonce_appended(&env).unwrap(), pt);
2130
2131 let mut env_buf = vec![0_u8; pt.len() + TAG_SIZE + NONCE_SIZE];
2132 let n = key
2133 .encrypt_nonce_appended_to(&NONCE, &pt, &mut env_buf)
2134 .expect("encrypt_nonce_appended_to");
2135 assert_eq!(n, env_buf.len());
2136 assert_eq!(env_buf, env);
2137 let mut out = vec![0_u8; pt.len()];
2138 let m = key
2139 .decrypt_nonce_appended_to(&env_buf, &mut out)
2140 .expect("decrypt_nonce_appended_to");
2141 assert_eq!(&out[..m], &pt[..]);
2142
2143 for prealloc in [0_usize, pt.len() + TAG_SIZE + NONCE_SIZE] {
2146 let mut in_out = Vec::with_capacity(prealloc);
2147 in_out.extend_from_slice(&pt);
2148 key.encrypt_nonce_appended_in_place(&NONCE, &mut in_out)
2149 .expect("encrypt_nonce_appended_in_place");
2150 assert_eq!(in_out, env, "in-place layout must match");
2151 assert_eq!(key.decrypt_nonce_appended(&in_out).unwrap(), pt);
2152 }
2153
2154 let mut out = vec![0_u8; pt.len() + TAG_SIZE + NONCE_SIZE];
2156 let n = key.encrypt_to(aad, &pt, &mut out).expect("encrypt_to");
2157 assert_eq!(n, out.len());
2158 let mut dec = vec![0_u8; pt.len()];
2159 let m = key.decrypt_to(aad, &out, &mut dec).expect("decrypt_to");
2160 assert_eq!(&dec[..m], &pt[..]);
2161
2162 let g1 = key.encrypt_nonce_appended_generated(&pt).unwrap();
2163 let g2 = key.encrypt_nonce_appended_generated(&pt).unwrap();
2164 assert_ne!(g1, g2, "generated nonces must differ between calls");
2165 assert_eq!(key.decrypt_nonce_appended(&g1).unwrap(), pt);
2166 assert_eq!(key.decrypt_nonce_appended(&g2).unwrap(), pt);
2167
2168 drop(reference);
2169 }
2170
2171 #[test]
2174 fn owned_key_state_encrypt_to_round_trips() {
2175 let mut key = HardwareAes256GcmKeyState::new(&[9_u8; 32]).expect("valid key");
2176 let pt = b"owned key state encrypt_to path";
2177 let mut out = vec![0_u8; pt.len() + TAG_SIZE + NONCE_SIZE];
2178 let n = key.encrypt_to(b"aad", pt, &mut out).expect("encrypt_to");
2179 assert_eq!(n, out.len());
2180 assert_eq!(key.decrypt(b"aad", &out).expect("decrypt"), pt);
2181 }
2182
2183 #[test]
2189 fn gcm_length_validation_rejects_each_over_limit() {
2190 use super::{validate_gcm_lengths, MAX_GCM_DATA_LEN, MAX_GHASH_INPUT_LEN};
2191 assert!(validate_gcm_lengths(16, 16).is_ok());
2192 if let Ok(over) = usize::try_from(MAX_GCM_DATA_LEN + 1) {
2194 assert_eq!(validate_gcm_lengths(16, over), Err(Error::InputTooLarge));
2195 }
2196 if let Ok(over) = usize::try_from(MAX_GHASH_INPUT_LEN + 1) {
2198 assert_eq!(validate_gcm_lengths(over, 16), Err(Error::InputTooLarge));
2199 }
2200 if let Ok(lim) = usize::try_from(MAX_GCM_DATA_LEN) {
2202 assert!(validate_gcm_lengths(0, lim).is_ok());
2203 }
2204 }
2205
2206 #[test]
2207 fn generated_nonce_does_not_change_key_state_size() {
2208 assert_eq!(HardwareAes256Gcm::key_state_layout().size, 368);
2210 assert_eq!(HardwareAes256Gcm::state_size(), 368);
2211 }
2212
2213 #[test]
2214 fn inline_owned_key_state_layout_matches_reported_layout() {
2215 let layout = HardwareAes256Gcm::key_state_layout();
2216 assert!(std::mem::size_of::<HardwareAes256GcmKeyState>() >= layout.size);
2217 assert!(std::mem::align_of::<HardwareAes256GcmKeyState>() >= layout.align);
2218 assert_eq!(HardwareAes256GcmKeyState::state_size(), layout.size);
2219 }
2220
2221 #[test]
2222 fn inline_owned_key_state_default_envelope_round_trips() {
2223 let mut key = HardwareAes256GcmKeyState::new(&[7_u8; 32]).expect("valid test key");
2224 let first = key
2225 .encrypt(b"aad", b"plaintext")
2226 .expect("encryption should succeed");
2227 let second = key
2228 .encrypt(b"aad", b"plaintext")
2229 .expect("encryption should succeed");
2230
2231 assert_eq!(
2232 key.decrypt(b"aad", &first)
2233 .expect("decryption should succeed"),
2234 b"plaintext"
2235 );
2236 assert_eq!(
2237 key.decrypt(b"aad", &second)
2238 .expect("decryption should succeed"),
2239 b"plaintext"
2240 );
2241 assert_ne!(
2242 &first[first.len() - NONCE_SIZE..],
2243 &second[second.len() - NONCE_SIZE..],
2244 "generated nonces must be unique"
2245 );
2246
2247 let mut out = [0_u8; 9];
2248 let written = key
2249 .decrypt_to(b"aad", &first, &mut out)
2250 .expect("decryption into caller buffer should succeed");
2251 assert_eq!(written, out.len());
2252 assert_eq!(&out, b"plaintext");
2253 }
2254
2255 #[test]
2256 fn inline_owned_key_state_round_trips_and_uses_existing_vec_capacity() {
2257 let key = HardwareAes256GcmKeyState::new(&[7_u8; 32]).expect("valid test key");
2258 let nonce = [9_u8; NONCE_SIZE];
2259 let expected = key
2260 .encrypt_nonce_appended(&nonce, b"plaintext")
2261 .expect("encryption should succeed");
2262 assert_eq!(
2263 key.decrypt_nonce_appended(&expected)
2264 .expect("decryption should succeed"),
2265 b"plaintext"
2266 );
2267
2268 let mut in_out = Vec::with_capacity(b"plaintext".len() + super::TAG_SIZE + NONCE_SIZE);
2269 in_out.extend_from_slice(b"plaintext");
2270 let ptr_before = in_out.as_ptr();
2271 key.encrypt_nonce_appended_in_place(&nonce, &mut in_out)
2272 .expect("in-place encryption should succeed");
2273 assert_eq!(in_out, expected);
2274 assert_eq!(
2275 in_out.as_ptr(),
2276 ptr_before,
2277 "pre-reserved in-place encryption should not reallocate"
2278 );
2279 }
2280
2281 #[test]
2282 fn inline_owned_key_state_wipes_storage_on_drop() {
2283 let layout = HardwareAes256Gcm::key_state_layout();
2284 let mut key =
2285 ManuallyDrop::new(HardwareAes256GcmKeyState::new(&[7_u8; 32]).expect("valid test key"));
2286
2287 unsafe {
2293 ManuallyDrop::drop(&mut key);
2294 let storage = key.storage.bytes_ptr_mut();
2295 let bytes = std::slice::from_raw_parts(storage.cast_const(), layout.size);
2296 assert!(bytes.iter().all(|byte| *byte == 0));
2297 }
2298 }
2299
2300 #[test]
2301 fn caller_placed_key_state_is_usable_across_threads() {
2302 let layout = HardwareAes256Gcm::key_state_layout();
2303 let mut storage = AlignedStorage::<512>([0_u8; 512]);
2304 let slot = UninitKeyStateSlot::new(&mut storage.0[..layout.size]).expect("valid slot");
2305 let mut key = HardwareAes256GcmIn::new_in(&[7_u8; 32], slot).expect("valid test key");
2306 let ciphertext = key
2307 .encrypt(&[], b"plaintext")
2308 .expect("encryption should succeed");
2309
2310 std::thread::scope(|scope| {
2311 for _ in 0..4 {
2312 scope.spawn(|| {
2313 assert_eq!(
2314 key.decrypt(&[], &ciphertext)
2315 .expect("shared decryption should succeed"),
2316 b"plaintext"
2317 );
2318 });
2319 }
2320 });
2321 }
2322
2323 #[test]
2324 fn rejects_wrong_nonce_length() {
2325 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
2326 let Err(err) = key.encrypt_with_nonce(&[0_u8; NONCE_SIZE - 1], &[], b"data") else {
2327 panic!("short nonce should be rejected");
2328 };
2329 assert_eq!(err, Error::InvalidNonceLength);
2330 }
2331
2332 #[test]
2333 #[cfg(target_pointer_width = "64")]
2334 fn rejects_inputs_that_exceed_gcm_counter_limit() {
2335 let max_data_len =
2336 usize::try_from(super::MAX_GCM_DATA_LEN).expect("64-bit usize holds GCM data limit");
2337 assert_eq!(
2338 super::validate_gcm_lengths(0, max_data_len + 1),
2339 Err(Error::InputTooLarge)
2340 );
2341 }
2342
2343 #[test]
2344 #[cfg(target_pointer_width = "64")]
2345 fn accepts_inputs_at_gcm_counter_limit() {
2346 let max_data_len =
2347 usize::try_from(super::MAX_GCM_DATA_LEN).expect("64-bit usize holds GCM data limit");
2348 assert_eq!(super::validate_gcm_lengths(0, max_data_len), Ok(()));
2349 }
2350
2351 #[test]
2352 fn nonce_appended_round_trips() {
2353 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
2354 let nonce = [9_u8; NONCE_SIZE];
2355 let ciphertext = key
2356 .encrypt_nonce_appended(&nonce, b"plaintext")
2357 .expect("encryption should succeed");
2358 assert_eq!(
2359 key.decrypt_nonce_appended(&ciphertext)
2360 .expect("decryption should succeed"),
2361 b"plaintext"
2362 );
2363 }
2364
2365 #[test]
2366 fn reports_nonzero_key_state_layout() {
2367 let layout = HardwareAes256Gcm::key_state_layout();
2368 assert!(layout.size >= super::KEY_SIZE);
2369 assert!(layout.align.is_power_of_two());
2370 assert!(layout.size <= 384);
2371 assert_eq!(layout.size, HardwareAes256Gcm::state_size());
2372 }
2373
2374 #[test]
2375 fn caller_placed_key_state_round_trips_and_wipes_storage() {
2376 let layout = HardwareAes256Gcm::key_state_layout();
2377 let mut storage = AlignedStorage::<512>([0xa5; 512]);
2378
2379 {
2380 let slot = UninitKeyStateSlot::new(&mut storage.0[..layout.size]).expect("valid slot");
2381 let mut key = HardwareAes256GcmIn::new_in(&[7_u8; 32], slot).expect("valid test key");
2382 let ciphertext = key
2383 .encrypt(&[], b"plaintext")
2384 .expect("encryption should succeed");
2385 assert_eq!(
2386 key.decrypt(&[], &ciphertext)
2387 .expect("decryption should succeed"),
2388 b"plaintext"
2389 );
2390 }
2391
2392 assert!(storage.0[..layout.size].iter().all(|byte| *byte == 0));
2393 }
2394
2395 #[test]
2396 fn caller_placed_key_state_validates_storage_before_key() {
2397 let layout = HardwareAes256Gcm::key_state_layout();
2398 let mut short = AlignedStorage::<512>([0_u8; 512]);
2399 let err = UninitKeyStateSlot::new(&mut short.0[..layout.size - 1])
2400 .expect_err("short storage should fail");
2401 assert_eq!(err, Error::KeyStateStorageTooSmall);
2402
2403 let mut misaligned = AlignedStorage::<512>([0_u8; 512]);
2404 let start = 1;
2405 let end = start + layout.size;
2406 let err = UninitKeyStateSlot::new(&mut misaligned.0[start..end])
2407 .expect_err("misaligned storage should fail");
2408 assert_eq!(err, Error::KeyStateStorageMisaligned);
2409 }
2410
2411 fn hx(s: &str) -> Vec<u8> {
2412 (0..s.len())
2413 .step_by(2)
2414 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
2415 .collect()
2416 }
2417
2418 #[test]
2419 fn detached_round_trips_and_matches_nonce_appended_path() {
2420 const KEY: [u8; 32] = [0x5b; 32];
2421 const NONCE: [u8; NONCE_SIZE] = [0x42; NONCE_SIZE];
2422 let key = HardwareAes256GcmKeyState::new(&KEY).expect("valid key");
2423 let pt = b"detached seal/open round-trip payload".to_vec();
2424 let aad = b"associated-data".as_slice();
2425
2426 let (ct, tag) = key.seal_detached(&NONCE, aad, &pt).expect("seal_detached");
2428 assert_eq!(ct.len(), pt.len(), "ciphertext is plaintext-length");
2429 assert_eq!(key.open_detached(&NONCE, aad, &ct, &tag).unwrap(), pt);
2430
2431 let mut buf = pt.clone();
2433 let tag2 = key
2434 .seal_in_place_detached(&NONCE, aad, &mut buf)
2435 .expect("seal_in_place_detached");
2436 assert_eq!(buf, ct, "in-place ciphertext matches allocating");
2437 assert_eq!(tag2, tag, "in-place tag matches allocating");
2438 key.open_in_place_detached(&NONCE, aad, &mut buf, &tag2)
2439 .expect("open_in_place_detached");
2440 assert_eq!(buf, pt, "in-place open recovers plaintext");
2441
2442 let env = key.encrypt_nonce_appended(&NONCE, &pt).expect("appended");
2446 let (ct0, tag0) = key.seal_detached(&NONCE, &[], &pt).expect("seal_detached");
2447 let mut joined = ct0;
2448 joined.extend_from_slice(&tag0);
2449 assert_eq!(joined, env[..env.len() - NONCE_SIZE]);
2450 }
2451
2452 #[test]
2453 fn detached_seal_reproduces_nist_vector() {
2454 let key = HardwareAes256GcmKeyState::new(&hx(
2458 "31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22",
2459 ))
2460 .expect("valid key");
2461 let nonce = hx("0d18e06c7c725ac9e362e1ce");
2462 let pt = hx("2db5168e932556f8089a0622981d017d");
2463 let (ct, tag) = key.seal_detached(&nonce, &[], &pt).expect("seal_detached");
2464 assert_eq!(ct, hx("fa4362189661d163fcd6a56d8bf0405a"));
2465 assert_eq!(tag.as_slice(), hx("d636ac1bbedd5cc3ee727dc2ab4a9489"));
2466 assert_eq!(key.open_detached(&nonce, &[], &ct, &tag).unwrap(), pt);
2468 }
2469
2470 #[test]
2471 fn detached_open_rejects_wrong_tag_and_aad() {
2472 const KEY: [u8; 32] = [0x11; 32];
2473 const NONCE: [u8; NONCE_SIZE] = [0x22; NONCE_SIZE];
2474 let key = HardwareAes256GcmKeyState::new(&KEY).expect("valid key");
2475 let pt = b"authenticate me".to_vec();
2476 let (ct, mut tag) = key.seal_detached(&NONCE, b"aad", &pt).expect("seal");
2477
2478 assert_eq!(
2480 key.open_detached(&NONCE, b"other", &ct, &tag),
2481 Err(Error::Decrypt)
2482 );
2483 tag[0] ^= 0xff;
2485 assert_eq!(
2486 key.open_detached(&NONCE, b"aad", &ct, &tag),
2487 Err(Error::Decrypt)
2488 );
2489 assert_eq!(
2491 key.open_detached(&NONCE, b"aad", &ct, &tag[..15]),
2492 Err(Error::Decrypt)
2493 );
2494 }
2495
2496 #[test]
2497 fn detached_rejects_bad_nonce_length() {
2498 let key = HardwareAes256GcmKeyState::new(&[3_u8; 32]).expect("valid key");
2499 assert_eq!(
2500 key.seal_detached(&[0_u8; 11], &[], b"x"),
2501 Err(Error::InvalidNonceLength)
2502 );
2503 }
2504}
2505
2506#[cfg(kani)]
2514mod kani_proofs {
2515 use super::{
2516 constant_time_eq, increment_counter, j0, nonce_from_slice, validate_gcm_lengths, Error,
2517 MAX_GCM_DATA_LEN, MAX_GHASH_INPUT_LEN, NONCE_SIZE, TAG_SIZE,
2518 };
2519
2520 #[kani::proof]
2524 fn increment_counter_is_be32_inc() {
2525 let mut counter: [u8; 16] = kani::any();
2526 let original = counter;
2527 increment_counter(&mut counter);
2528 assert!(counter[..12] == original[..12]);
2529 let low = u32::from_be_bytes([original[12], original[13], original[14], original[15]]);
2530 assert!(counter[12..] == low.wrapping_add(1).to_be_bytes());
2531 }
2532
2533 #[kani::proof]
2535 fn j0_layout() {
2536 let nonce: [u8; NONCE_SIZE] = kani::any();
2537 let block = j0(&nonce);
2538 assert!(block[..NONCE_SIZE] == nonce);
2539 assert!(block[NONCE_SIZE..] == [0, 0, 0, 1]);
2540 }
2541
2542 #[kani::proof]
2545 fn validate_gcm_lengths_matches_limits() {
2546 let aad_len: usize = kani::any();
2547 let data_len: usize = kani::any();
2548 let result = validate_gcm_lengths(aad_len, data_len);
2549 let aad = aad_len as u64;
2550 let data = data_len as u64;
2551 let in_range =
2552 data <= MAX_GCM_DATA_LEN && aad <= MAX_GHASH_INPUT_LEN && data <= MAX_GHASH_INPUT_LEN;
2553 assert!(result.is_ok() == in_range);
2554 }
2555
2556 #[kani::proof]
2559 fn nonce_from_slice_accepts_only_correct_length() {
2560 let len: usize = kani::any();
2561 kani::assume(len <= 24);
2562 let buf = [0_u8; 24];
2563 let result = nonce_from_slice(&buf[..len]);
2564 match result {
2565 Ok(n) => assert!(len == NONCE_SIZE && n.len() == NONCE_SIZE),
2566 Err(e) => assert!(len != NONCE_SIZE && matches!(e, Error::InvalidNonceLength)),
2567 }
2568 }
2569
2570 #[kani::proof]
2581 fn constant_time_eq_equals_bytewise_equality() {
2582 let expected: [u8; TAG_SIZE] = kani::any();
2583 let actual: [u8; TAG_SIZE] = kani::any();
2584 assert!(constant_time_eq(&expected, &actual) == (expected == actual));
2585 }
2586}