1#![allow(unsafe_code)]
23
24mod aes;
25mod fork;
26mod ghash;
27mod nonce;
28
29use core::{
30 marker::PhantomData,
31 mem::MaybeUninit,
32 ptr::{self, NonNull},
33};
34use subtle::ConstantTimeEq as _;
35use zeroize::Zeroize as _;
36
37pub const KEY_SIZE: usize = 32;
39pub const NONCE_SIZE: usize = 12;
41pub const TAG_SIZE: usize = 16;
43const AES_BLOCK_SIZE: usize = 16;
44const PAR_BLOCKS: usize = aes::PAR_BLOCKS;
45const PAR_BYTES: usize = PAR_BLOCKS * AES_BLOCK_SIZE;
46const MAX_GCM_DATA_LEN: u64 = ((u32::MAX as u64) - 1) * AES_BLOCK_SIZE as u64;
47const MAX_GHASH_INPUT_LEN: u64 = u64::MAX / 8;
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub struct KeyStateLayout {
52 pub size: usize,
54 pub align: usize,
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60#[non_exhaustive]
61pub enum Error {
62 InvalidKeyLength,
64 InvalidNonceLength,
66 Encrypt,
68 Decrypt,
70 CiphertextTooShort,
72 UnsupportedCpu,
74 KeyStateStorageTooSmall,
76 KeyStateStorageMisaligned,
78 InputTooLarge,
80 OutputTooSmall,
82 OsEntropy,
84}
85
86impl std::fmt::Display for Error {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 match self {
89 Self::InvalidKeyLength => f.write_str("invalid AES-256-GCM key length"),
90 Self::InvalidNonceLength => f.write_str("invalid AES-GCM nonce length"),
91 Self::Encrypt => f.write_str("AES-256-GCM encryption failed"),
92 Self::Decrypt => f.write_str("AES-256-GCM authentication failed"),
93 Self::CiphertextTooShort => f.write_str("ciphertext too short"),
94 Self::UnsupportedCpu => {
95 f.write_str("required AES-256-GCM hardware support is unavailable")
96 }
97 Self::KeyStateStorageTooSmall => f.write_str("key-state storage too small"),
98 Self::KeyStateStorageMisaligned => f.write_str("key-state storage misaligned"),
99 Self::InputTooLarge => f.write_str("AES-256-GCM input too large"),
100 Self::OutputTooSmall => f.write_str("plaintext output buffer too small"),
101 Self::OsEntropy => f.write_str("OS entropy source failed during nonce generation"),
102 }
103 }
104}
105
106impl std::error::Error for Error {}
107
108struct KeyState {
109 aes: aes::Aes256,
110 ghash: ghash::GHashKey,
111}
112
113impl KeyState {
114 fn init_in_place(dst: NonNull<Self>, key: &[u8; KEY_SIZE]) -> Result<(), Error> {
115 if !HardwareAes256Gcm::hardware_available() {
116 return Err(Error::UnsupportedCpu);
117 }
118
119 let dst = dst.as_ptr();
120 let aes_ptr = unsafe { ptr::addr_of_mut!((*dst).aes) };
123 aes::Aes256::init_in_place(aes_ptr, key).ok_or(Error::UnsupportedCpu)?;
124
125 let mut hash_subkey = [0_u8; TAG_SIZE];
126 unsafe { (&*aes_ptr).encrypt_block(&mut hash_subkey) };
128 let ghash_ptr = unsafe { ptr::addr_of_mut!((*dst).ghash) };
131 if ghash::GHashKey::init_in_place(ghash_ptr, &mut hash_subkey).is_none() {
132 hash_subkey.zeroize();
136 unsafe { ptr::drop_in_place(aes_ptr) };
138 return Err(Error::UnsupportedCpu);
139 }
140 Ok(())
141 }
142
143 fn encrypt(&self, nonce: &[u8], aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
144 let total = plaintext
145 .len()
146 .checked_add(TAG_SIZE)
147 .ok_or(Error::InputTooLarge)?;
148 let mut out = vec![0_u8; total];
149 self.encrypt_to(nonce, aad, plaintext, &mut out)?;
150 Ok(out)
151 }
152
153 fn encrypt_nonce_appended(&self, nonce: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
154 let nonce = nonce_from_slice(nonce)?;
155 validate_gcm_lengths(0, plaintext.len())?;
156 let total = plaintext
157 .len()
158 .checked_add(TAG_SIZE + NONCE_SIZE)
159 .ok_or(Error::InputTooLarge)?;
160 let mut out = Vec::with_capacity(total);
161 out.extend_from_slice(plaintext);
162 let Some(tag) = self.seal_in_place(&nonce, &[], &mut out) else {
163 out.zeroize();
164 return Err(Error::Encrypt);
165 };
166 append_tag_nonce(&mut out, &tag, &nonce);
167 Ok(out)
168 }
169
170 fn encrypt_nonce_appended_in_place(
171 &self,
172 nonce: &[u8],
173 in_out: &mut Vec<u8>,
174 ) -> Result<(), Error> {
175 let nonce = nonce_from_slice(nonce)?;
176 validate_gcm_lengths(0, in_out.len())?;
177 let total = in_out
178 .len()
179 .checked_add(TAG_SIZE + NONCE_SIZE)
180 .ok_or(Error::InputTooLarge)?;
181 if in_out.capacity() < total {
182 in_out.reserve_exact(total - in_out.len());
183 }
184 let Some(tag) = self.seal_in_place(&nonce, &[], in_out.as_mut_slice()) else {
185 in_out.zeroize();
186 return Err(Error::Encrypt);
187 };
188 append_tag_nonce(in_out, &tag, &nonce);
189 Ok(())
190 }
191
192 fn encrypt_to(
193 &self,
194 nonce: &[u8],
195 aad: &[u8],
196 plaintext: &[u8],
197 out: &mut [u8],
198 ) -> Result<usize, Error> {
199 let nonce = nonce_from_slice(nonce)?;
200 validate_gcm_lengths(aad.len(), plaintext.len())?;
201 let total = plaintext
202 .len()
203 .checked_add(TAG_SIZE)
204 .ok_or(Error::InputTooLarge)?;
205 if out.len() < total {
206 return Err(Error::OutputTooSmall);
207 }
208
209 let (ciphertext, rest) = out.split_at_mut(plaintext.len());
210 let tag = self
211 .seal(&nonce, aad, plaintext, ciphertext)
212 .ok_or(Error::Encrypt)?;
213 rest[..TAG_SIZE].copy_from_slice(&tag);
214 Ok(total)
215 }
216
217 fn seal(
224 &self,
225 nonce: &[u8; NONCE_SIZE],
226 aad: &[u8],
227 plaintext: &[u8],
228 ciphertext: &mut [u8],
229 ) -> Option<[u8; TAG_SIZE]> {
230 debug_assert_eq!(plaintext.len(), ciphertext.len());
231 let mut ghasher = ghash::Ghasher::new(&self.ghash)?;
232 ghasher.absorb_padded(aad);
233
234 let mut counter = j0(nonce);
235 increment_counter(&mut counter);
236
237 let bulk = (plaintext.len() / PAR_BYTES) * PAR_BYTES;
241 let (plaintext_bulk, plaintext_tail) = plaintext.split_at(bulk);
242 let (ciphertext_bulk, ciphertext_tail) = ciphertext.split_at_mut(bulk);
243 if !plaintext_bulk.is_empty() {
244 ghasher.seal_bulk(
245 self.aes.round_keys(),
246 &mut counter,
247 plaintext_bulk,
248 ciphertext_bulk,
249 );
250 }
251 if !plaintext_tail.is_empty() {
252 ciphertext_tail.copy_from_slice(plaintext_tail);
253 self.apply_ctr_serial(&mut counter, ciphertext_tail);
254 ghasher.absorb_padded(ciphertext_tail);
255 }
256
257 let mut tag = ghasher.finalize(aad.len(), plaintext.len())?;
258 let mut mask = j0(nonce);
259 self.aes.encrypt_block(&mut mask);
260 for (tag_byte, mask_byte) in tag.iter_mut().zip(mask.iter()) {
261 *tag_byte ^= mask_byte;
262 }
263 Some(tag)
264 }
265
266 fn seal_in_place(
270 &self,
271 nonce: &[u8; NONCE_SIZE],
272 aad: &[u8],
273 data: &mut [u8],
274 ) -> Option<[u8; TAG_SIZE]> {
275 let mut ghasher = ghash::Ghasher::new(&self.ghash)?;
276 ghasher.absorb_padded(aad);
277
278 let mut counter = j0(nonce);
279 increment_counter(&mut counter);
280
281 let bulk = (data.len() / PAR_BYTES) * PAR_BYTES;
282 let (data_bulk, data_tail) = data.split_at_mut(bulk);
283 if !data_bulk.is_empty() {
284 ghasher.seal_in_place_bulk(self.aes.round_keys(), &mut counter, data_bulk);
285 }
286 if !data_tail.is_empty() {
287 let full = (data_tail.len() / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
288 let (data_tail_blocks, data_partial) = data_tail.split_at_mut(full);
289 if !data_tail_blocks.is_empty() {
290 ghasher.seal_in_place_tail_blocks(
291 self.aes.round_keys(),
292 &mut counter,
293 data_tail_blocks,
294 );
295 }
296 if !data_partial.is_empty() {
297 self.apply_ctr_serial(&mut counter, data_partial);
298 ghasher.absorb_padded(data_partial);
299 }
300 }
301
302 let mut tag = ghasher.finalize(aad.len(), data.len())?;
303 let mut mask = j0(nonce);
304 self.aes.encrypt_block(&mut mask);
305 for (tag_byte, mask_byte) in tag.iter_mut().zip(mask.iter()) {
306 *tag_byte ^= mask_byte;
307 }
308 Some(tag)
309 }
310
311 fn apply_ctr_serial(&self, counter: &mut [u8; AES_BLOCK_SIZE], data: &mut [u8]) {
313 let mut keystream = [0_u8; AES_BLOCK_SIZE];
314 for block in data.chunks_mut(AES_BLOCK_SIZE) {
315 keystream.copy_from_slice(counter);
316 self.aes.encrypt_block(&mut keystream);
317 for (byte, key_byte) in block.iter_mut().zip(keystream.iter()) {
318 *byte ^= key_byte;
319 }
320 increment_counter(counter);
321 }
322 }
323
324 fn decrypt(
325 &self,
326 nonce: &[u8],
327 aad: &[u8],
328 ciphertext_and_tag: &[u8],
329 ) -> Result<Vec<u8>, Error> {
330 let nonce = nonce_from_slice(nonce)?;
331 if ciphertext_and_tag.len() < TAG_SIZE {
332 return Err(Error::Decrypt);
333 }
334
335 let tag_pos = ciphertext_and_tag.len() - TAG_SIZE;
336 let (ciphertext, tag) = ciphertext_and_tag.split_at(tag_pos);
337 validate_gcm_lengths(aad.len(), ciphertext.len())?;
338 let mut out = Vec::with_capacity(ciphertext.len());
339 out.extend_from_slice(ciphertext);
340 let Some(expected) = self.open_in_place(&nonce, aad, &mut out) else {
341 out.zeroize();
342 return Err(Error::Decrypt);
343 };
344 if !constant_time_eq(&expected, tag) {
345 out.zeroize();
346 return Err(Error::Decrypt);
347 }
348 Ok(out)
349 }
350
351 fn decrypt_to(
352 &self,
353 nonce: &[u8],
354 aad: &[u8],
355 ciphertext_and_tag: &[u8],
356 out: &mut [u8],
357 ) -> Result<usize, Error> {
358 let nonce = nonce_from_slice(nonce)?;
359 if ciphertext_and_tag.len() < TAG_SIZE {
360 return Err(Error::Decrypt);
361 }
362
363 let tag_pos = ciphertext_and_tag.len() - TAG_SIZE;
364 let (ciphertext, tag) = ciphertext_and_tag.split_at(tag_pos);
365 validate_gcm_lengths(aad.len(), ciphertext.len())?;
366 if out.len() < ciphertext.len() {
367 return Err(Error::OutputTooSmall);
368 }
369 let out = &mut out[..ciphertext.len()];
370 let Some(expected) = self.open(&nonce, aad, ciphertext, out) else {
371 out.zeroize();
372 return Err(Error::Decrypt);
373 };
374 if !constant_time_eq(&expected, tag) {
379 out.zeroize();
380 return Err(Error::Decrypt);
381 }
382
383 Ok(ciphertext.len())
384 }
385
386 fn open(
390 &self,
391 nonce: &[u8; NONCE_SIZE],
392 aad: &[u8],
393 ciphertext: &[u8],
394 plaintext: &mut [u8],
395 ) -> Option<[u8; TAG_SIZE]> {
396 debug_assert_eq!(ciphertext.len(), plaintext.len());
397 let mut ghasher = ghash::Ghasher::new(&self.ghash)?;
398 ghasher.absorb_padded(aad);
399
400 let mut counter = j0(nonce);
401 increment_counter(&mut counter);
402
403 let bulk = (ciphertext.len() / PAR_BYTES) * PAR_BYTES;
407 let (ciphertext_bulk, ciphertext_tail) = ciphertext.split_at(bulk);
408 let (plaintext_bulk, plaintext_tail) = plaintext.split_at_mut(bulk);
409 if !ciphertext_bulk.is_empty() {
410 ghasher.open_bulk(
411 self.aes.round_keys(),
412 &mut counter,
413 ciphertext_bulk,
414 plaintext_bulk,
415 );
416 }
417 if !ciphertext_tail.is_empty() {
418 ghasher.absorb_padded(ciphertext_tail);
419 plaintext_tail.copy_from_slice(ciphertext_tail);
420 self.apply_ctr_serial(&mut counter, plaintext_tail);
421 }
422
423 let mut tag = ghasher.finalize(aad.len(), ciphertext.len())?;
424 let mut mask = j0(nonce);
425 self.aes.encrypt_block(&mut mask);
426 for (tag_byte, mask_byte) in tag.iter_mut().zip(mask.iter()) {
427 *tag_byte ^= mask_byte;
428 }
429 Some(tag)
430 }
431
432 fn open_in_place(
436 &self,
437 nonce: &[u8; NONCE_SIZE],
438 aad: &[u8],
439 data: &mut [u8],
440 ) -> Option<[u8; TAG_SIZE]> {
441 let mut ghasher = ghash::Ghasher::new(&self.ghash)?;
442 ghasher.absorb_padded(aad);
443
444 let mut counter = j0(nonce);
445 increment_counter(&mut counter);
446
447 let bulk = (data.len() / PAR_BYTES) * PAR_BYTES;
448 let (data_bulk, data_tail) = data.split_at_mut(bulk);
449 if !data_bulk.is_empty() {
450 ghasher.open_in_place_bulk(self.aes.round_keys(), &mut counter, data_bulk);
451 }
452 if !data_tail.is_empty() {
453 ghasher.absorb_padded(data_tail);
454 self.apply_ctr_serial(&mut counter, data_tail);
455 }
456
457 let mut tag = ghasher.finalize(aad.len(), data.len())?;
458 let mut mask = j0(nonce);
459 self.aes.encrypt_block(&mut mask);
460 for (tag_byte, mask_byte) in tag.iter_mut().zip(mask.iter()) {
461 *tag_byte ^= mask_byte;
462 }
463 Some(tag)
464 }
465}
466
467pub struct UninitKeyStateSlot<'a> {
469 storage: &'a mut [u8],
470}
471
472impl<'a> UninitKeyStateSlot<'a> {
473 pub fn new(storage: &'a mut [u8]) -> Result<Self, Error> {
480 let layout = HardwareAes256Gcm::key_state_layout();
481 if storage.len() < layout.size {
482 return Err(Error::KeyStateStorageTooSmall);
483 }
484 if !storage.as_ptr().addr().is_multiple_of(layout.align) {
485 return Err(Error::KeyStateStorageMisaligned);
486 }
487 Ok(Self { storage })
488 }
489}
490
491impl std::fmt::Debug for UninitKeyStateSlot<'_> {
492 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
493 f.debug_struct("UninitKeyStateSlot")
494 .field("len", &self.storage.len())
495 .finish_non_exhaustive()
496 }
497}
498
499#[inline]
500fn init_key_state_at(key: &[u8], state: NonNull<KeyState>) -> Result<(), Error> {
501 if key.len() != KEY_SIZE {
502 return Err(Error::InvalidKeyLength);
503 }
504 let key: &[u8; KEY_SIZE] = key.try_into().map_err(|_| Error::InvalidKeyLength)?;
505 KeyState::init_in_place(state, key)
506}
507
508fn init_key_state_in_slot(key: &[u8], slot: UninitKeyStateSlot<'_>) -> Result<NonNull<u8>, Error> {
509 let UninitKeyStateSlot { storage } = slot;
510 let storage = unsafe { NonNull::new_unchecked(storage.as_mut_ptr()) };
515 #[allow(clippy::cast_ptr_alignment)]
516 let state = storage.cast::<KeyState>();
517 init_key_state_at(key, state)?;
518 Ok(storage)
519}
520
521pub struct OpaqueKeyState<'a> {
527 storage: NonNull<u8>,
528 _marker: PhantomData<&'a mut [u8]>,
529}
530
531const _: () = {
532 const fn assert_send_sync<T: Send + Sync>() {}
533 assert_send_sync::<KeyState>();
536};
537
538unsafe impl Send for OpaqueKeyState<'_> {}
544unsafe impl Sync for OpaqueKeyState<'_> {}
547
548impl OpaqueKeyState<'_> {
549 fn state_ptr(&self) -> *mut KeyState {
550 #[allow(clippy::cast_ptr_alignment)]
553 self.storage.as_ptr().cast::<KeyState>()
554 }
555}
556
557impl Drop for OpaqueKeyState<'_> {
558 fn drop(&mut self) {
559 let size = HardwareAes256Gcm::key_state_layout().size;
560 unsafe { volatile_wipe(self.storage.as_ptr(), size) };
570 }
571}
572
573impl std::fmt::Debug for OpaqueKeyState<'_> {
574 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
575 f.debug_struct("OpaqueKeyState").finish_non_exhaustive()
576 }
577}
578
579pub struct HardwareAes256GcmIn<'a> {
581 state: OpaqueKeyState<'a>,
582 nonce_gen: Option<nonce::NonceGen>,
586}
587
588impl<'a> HardwareAes256GcmIn<'a> {
589 pub fn new_in(key: &[u8], slot: UninitKeyStateSlot<'a>) -> Result<Self, Error> {
596 let storage = init_key_state_in_slot(key, slot)?;
597 Ok(Self {
598 state: OpaqueKeyState {
599 storage,
600 _marker: PhantomData,
601 },
602 nonce_gen: None,
603 })
604 }
605
606 pub fn encrypt(&self, nonce: &[u8], aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
613 self.state_ref().encrypt(nonce, aad, plaintext)
614 }
615
616 pub fn encrypt_to(
626 &self,
627 nonce: &[u8],
628 aad: &[u8],
629 plaintext: &[u8],
630 out: &mut [u8],
631 ) -> Result<usize, Error> {
632 self.state_ref().encrypt_to(nonce, aad, plaintext, out)
633 }
634
635 pub fn decrypt(
642 &self,
643 nonce: &[u8],
644 aad: &[u8],
645 ciphertext_and_tag: &[u8],
646 ) -> Result<Vec<u8>, Error> {
647 self.state_ref().decrypt(nonce, aad, ciphertext_and_tag)
648 }
649
650 pub fn decrypt_to(
662 &self,
663 nonce: &[u8],
664 aad: &[u8],
665 ciphertext_and_tag: &[u8],
666 out: &mut [u8],
667 ) -> Result<usize, Error> {
668 self.state_ref()
669 .decrypt_to(nonce, aad, ciphertext_and_tag, out)
670 }
671
672 pub fn encrypt_nonce_appended(&self, nonce: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
678 self.state_ref().encrypt_nonce_appended(nonce, plaintext)
679 }
680
681 pub fn encrypt_nonce_appended_in_place(
692 &self,
693 nonce: &[u8],
694 in_out: &mut Vec<u8>,
695 ) -> Result<(), Error> {
696 self.state_ref()
697 .encrypt_nonce_appended_in_place(nonce, in_out)
698 }
699
700 pub fn encrypt_nonce_appended_to(
709 &self,
710 nonce: &[u8],
711 plaintext: &[u8],
712 out: &mut [u8],
713 ) -> Result<usize, Error> {
714 encrypt_nonce_appended_to(self.state_ref(), nonce, plaintext, out)
715 }
716
717 pub fn decrypt_nonce_appended(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
725 if data.len() < TAG_SIZE + NONCE_SIZE {
726 return Err(Error::CiphertextTooShort);
727 }
728 let nonce_pos = data.len() - NONCE_SIZE;
729 let (ciphertext_and_tag, nonce) = data.split_at(nonce_pos);
730 self.decrypt(nonce, &[], ciphertext_and_tag)
731 }
732
733 pub fn decrypt_nonce_appended_to(&self, data: &[u8], out: &mut [u8]) -> Result<usize, Error> {
741 if data.len() < TAG_SIZE + NONCE_SIZE {
742 return Err(Error::CiphertextTooShort);
743 }
744 let nonce_pos = data.len() - NONCE_SIZE;
745 let (ciphertext_and_tag, nonce) = data.split_at(nonce_pos);
746 self.decrypt_to(nonce, &[], ciphertext_and_tag, out)
747 }
748
749 pub fn encrypt_with_generated_nonce(
762 &mut self,
763 aad: &[u8],
764 plaintext: &[u8],
765 ) -> Result<([u8; NONCE_SIZE], Vec<u8>), Error> {
766 let nonce = self.next_nonce()?;
767 let ciphertext = self.state_ref().encrypt(&nonce, aad, plaintext)?;
768 Ok((nonce, ciphertext))
769 }
770
771 pub fn encrypt_nonce_appended_generated(&mut self, plaintext: &[u8]) -> Result<Vec<u8>, Error> {
778 let nonce = self.next_nonce()?;
779 self.state_ref().encrypt_nonce_appended(&nonce, plaintext)
780 }
781
782 fn next_nonce(&mut self) -> Result<[u8; NONCE_SIZE], Error> {
783 match self.nonce_gen {
784 Some(ref mut g) => g.next(),
785 None => self.nonce_gen.insert(nonce::NonceGen::new()?).next(),
786 }
787 }
788
789 fn state_ref(&self) -> &KeyState {
790 unsafe { &*self.state.state_ptr() }
792 }
793}
794
795impl std::fmt::Debug for HardwareAes256GcmIn<'_> {
796 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
797 f.debug_struct("HardwareAes256GcmIn")
798 .finish_non_exhaustive()
799 }
800}
801
802#[repr(transparent)]
803struct AlignedKeyStateStorage(MaybeUninit<KeyState>);
804
805impl AlignedKeyStateStorage {
806 #[inline]
807 const fn uninit() -> Self {
808 Self(MaybeUninit::uninit())
809 }
810
811 #[inline]
812 fn state_ptr(&self) -> *const KeyState {
813 self.0.as_ptr()
814 }
815
816 #[inline]
817 fn state_ptr_mut(&mut self) -> NonNull<KeyState> {
818 unsafe { NonNull::new_unchecked(self.0.as_mut_ptr()) }
820 }
821
822 #[inline]
823 fn bytes_ptr_mut(&mut self) -> *mut u8 {
824 self.0.as_mut_ptr().cast::<u8>()
825 }
826}
827
828#[repr(transparent)]
835pub struct HardwareAes256GcmKeyState {
836 storage: AlignedKeyStateStorage,
837}
838
839impl HardwareAes256GcmKeyState {
840 #[inline]
848 pub fn new(key: &[u8]) -> Result<Self, Error> {
849 let mut storage = AlignedKeyStateStorage::uninit();
850 init_key_state_at(key, storage.state_ptr_mut())?;
851 Ok(Self { storage })
852 }
853
854 #[must_use]
856 pub const fn state_size() -> usize {
857 std::mem::size_of::<KeyState>()
858 }
859
860 #[inline]
867 pub fn encrypt_nonce_appended(&self, nonce: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
868 self.state_ref().encrypt_nonce_appended(nonce, plaintext)
869 }
870
871 #[inline]
882 pub fn encrypt_nonce_appended_in_place(
883 &self,
884 nonce: &[u8],
885 in_out: &mut Vec<u8>,
886 ) -> Result<(), Error> {
887 self.state_ref()
888 .encrypt_nonce_appended_in_place(nonce, in_out)
889 }
890
891 #[inline]
899 pub fn decrypt_nonce_appended(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
900 if data.len() < TAG_SIZE + NONCE_SIZE {
901 return Err(Error::CiphertextTooShort);
902 }
903 let nonce_pos = data.len() - NONCE_SIZE;
904 let (ciphertext_and_tag, nonce) = data.split_at(nonce_pos);
905 self.state_ref().decrypt(nonce, &[], ciphertext_and_tag)
906 }
907
908 fn state_ref(&self) -> &KeyState {
909 unsafe { &*self.storage.state_ptr() }
913 }
914}
915
916impl Drop for HardwareAes256GcmKeyState {
917 fn drop(&mut self) {
918 let size = Self::state_size();
919 unsafe { volatile_wipe(self.storage.bytes_ptr_mut(), size) };
926 }
927}
928
929impl std::fmt::Debug for HardwareAes256GcmKeyState {
930 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
931 f.debug_struct("HardwareAes256GcmKeyState")
932 .finish_non_exhaustive()
933 }
934}
935
936pub struct HardwareAes256Gcm {
938 state: Box<KeyState>,
939 nonce_gen: Option<nonce::NonceGen>,
943}
944
945impl HardwareAes256Gcm {
946 pub fn new(key: &[u8]) -> Result<Self, Error> {
952 if key.len() != KEY_SIZE {
953 return Err(Error::InvalidKeyLength);
954 }
955 let mut state = Box::<KeyState>::new_uninit();
956 let ptr = unsafe { NonNull::new_unchecked(state.as_mut_ptr()) };
958 init_key_state_at(key, ptr)?;
959 let state = unsafe { state.assume_init() };
961 Ok(Self {
962 state,
963 nonce_gen: None,
964 })
965 }
966
967 #[must_use]
969 pub fn hardware_available() -> bool {
970 aes::hardware_available() && ghash::hardware_available()
971 }
972
973 #[must_use]
977 pub const fn state_size() -> usize {
978 std::mem::size_of::<KeyState>()
979 }
980
981 #[must_use]
985 pub const fn key_state_layout() -> KeyStateLayout {
986 KeyStateLayout {
987 size: std::mem::size_of::<KeyState>(),
988 align: std::mem::align_of::<KeyState>(),
989 }
990 }
991
992 pub fn encrypt(&self, nonce: &[u8], aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
1001 self.state.encrypt(nonce, aad, plaintext)
1002 }
1003
1004 pub fn encrypt_to(
1014 &self,
1015 nonce: &[u8],
1016 aad: &[u8],
1017 plaintext: &[u8],
1018 out: &mut [u8],
1019 ) -> Result<usize, Error> {
1020 self.state.encrypt_to(nonce, aad, plaintext, out)
1021 }
1022
1023 pub fn decrypt(
1031 &self,
1032 nonce: &[u8],
1033 aad: &[u8],
1034 ciphertext_and_tag: &[u8],
1035 ) -> Result<Vec<u8>, Error> {
1036 self.state.decrypt(nonce, aad, ciphertext_and_tag)
1037 }
1038
1039 pub fn decrypt_to(
1051 &self,
1052 nonce: &[u8],
1053 aad: &[u8],
1054 ciphertext_and_tag: &[u8],
1055 out: &mut [u8],
1056 ) -> Result<usize, Error> {
1057 self.state.decrypt_to(nonce, aad, ciphertext_and_tag, out)
1058 }
1059
1060 pub fn encrypt_nonce_appended(&self, nonce: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
1066 self.state.encrypt_nonce_appended(nonce, plaintext)
1067 }
1068
1069 pub fn encrypt_nonce_appended_in_place(
1080 &self,
1081 nonce: &[u8],
1082 in_out: &mut Vec<u8>,
1083 ) -> Result<(), Error> {
1084 self.state.encrypt_nonce_appended_in_place(nonce, in_out)
1085 }
1086
1087 pub fn encrypt_nonce_appended_to(
1096 &self,
1097 nonce: &[u8],
1098 plaintext: &[u8],
1099 out: &mut [u8],
1100 ) -> Result<usize, Error> {
1101 encrypt_nonce_appended_to(&self.state, nonce, plaintext, out)
1102 }
1103
1104 pub fn decrypt_nonce_appended(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
1113 if data.len() < TAG_SIZE + NONCE_SIZE {
1114 return Err(Error::CiphertextTooShort);
1115 }
1116 let nonce_pos = data.len() - NONCE_SIZE;
1117 let (ciphertext_and_tag, nonce) = data.split_at(nonce_pos);
1118 self.decrypt(nonce, &[], ciphertext_and_tag)
1119 }
1120
1121 pub fn decrypt_nonce_appended_to(&self, data: &[u8], out: &mut [u8]) -> Result<usize, Error> {
1129 if data.len() < TAG_SIZE + NONCE_SIZE {
1130 return Err(Error::CiphertextTooShort);
1131 }
1132 let nonce_pos = data.len() - NONCE_SIZE;
1133 let (ciphertext_and_tag, nonce) = data.split_at(nonce_pos);
1134 self.decrypt_to(nonce, &[], ciphertext_and_tag, out)
1135 }
1136
1137 pub fn encrypt_with_generated_nonce(
1150 &mut self,
1151 aad: &[u8],
1152 plaintext: &[u8],
1153 ) -> Result<([u8; NONCE_SIZE], Vec<u8>), Error> {
1154 let nonce = self.next_nonce()?;
1155 let ciphertext = self.state.encrypt(&nonce, aad, plaintext)?;
1156 Ok((nonce, ciphertext))
1157 }
1158
1159 pub fn encrypt_nonce_appended_generated(&mut self, plaintext: &[u8]) -> Result<Vec<u8>, Error> {
1166 let nonce = self.next_nonce()?;
1167 self.state.encrypt_nonce_appended(&nonce, plaintext)
1168 }
1169
1170 fn next_nonce(&mut self) -> Result<[u8; NONCE_SIZE], Error> {
1171 match self.nonce_gen {
1172 Some(ref mut g) => g.next(),
1173 None => self.nonce_gen.insert(nonce::NonceGen::new()?).next(),
1174 }
1175 }
1176}
1177
1178impl std::fmt::Debug for HardwareAes256Gcm {
1179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1180 f.debug_struct("HardwareAes256Gcm").finish_non_exhaustive()
1181 }
1182}
1183
1184fn nonce_from_slice(nonce: &[u8]) -> Result<[u8; NONCE_SIZE], Error> {
1185 nonce.try_into().map_err(|_| Error::InvalidNonceLength)
1186}
1187
1188fn append_tag_nonce(out: &mut Vec<u8>, tag: &[u8; TAG_SIZE], nonce: &[u8; NONCE_SIZE]) {
1189 debug_assert!(out.capacity() >= out.len() + TAG_SIZE + NONCE_SIZE);
1190
1191 let start = out.len();
1192 let trailer = &mut out.spare_capacity_mut()[..TAG_SIZE + NONCE_SIZE];
1193 for (slot, byte) in trailer[..TAG_SIZE].iter_mut().zip(tag) {
1194 slot.write(*byte);
1195 }
1196 for (slot, byte) in trailer[TAG_SIZE..].iter_mut().zip(nonce) {
1197 slot.write(*byte);
1198 }
1199
1200 unsafe { out.set_len(start + TAG_SIZE + NONCE_SIZE) };
1204}
1205
1206#[allow(clippy::cast_ptr_alignment)] unsafe fn volatile_wipe(bytes: *mut u8, len: usize) {
1214 let mut offset = 0_usize;
1215 if bytes.addr().is_multiple_of(core::mem::align_of::<u128>()) {
1216 while offset + core::mem::size_of::<u128>() <= len {
1217 unsafe { ptr::write_volatile(bytes.add(offset).cast::<u128>(), 0) };
1220 offset += core::mem::size_of::<u128>();
1221 }
1222 }
1223 for offset in offset..len {
1224 unsafe { ptr::write_volatile(bytes.add(offset), 0) };
1226 }
1227 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
1228}
1229
1230fn encrypt_nonce_appended_to(
1232 state: &KeyState,
1233 nonce: &[u8],
1234 plaintext: &[u8],
1235 out: &mut [u8],
1236) -> Result<usize, Error> {
1237 let total = plaintext
1238 .len()
1239 .checked_add(TAG_SIZE + NONCE_SIZE)
1240 .ok_or(Error::InputTooLarge)?;
1241 if out.len() < total {
1242 return Err(Error::OutputTooSmall);
1243 }
1244 let written = state.encrypt_to(nonce, &[], plaintext, out)?;
1245 out[written..total].copy_from_slice(nonce);
1248 Ok(total)
1249}
1250
1251fn validate_gcm_lengths(aad_len: usize, data_len: usize) -> Result<(), Error> {
1252 if len_exceeds_u64_limit(data_len, MAX_GCM_DATA_LEN)
1253 || len_exceeds_u64_limit(aad_len, MAX_GHASH_INPUT_LEN)
1254 || len_exceeds_u64_limit(data_len, MAX_GHASH_INPUT_LEN)
1255 {
1256 return Err(Error::InputTooLarge);
1257 }
1258 Ok(())
1259}
1260
1261fn len_exceeds_u64_limit(len: usize, limit: u64) -> bool {
1262 u64::try_from(len).map_or(true, |len| len > limit)
1263}
1264
1265fn j0(nonce: &[u8; NONCE_SIZE]) -> [u8; 16] {
1266 let mut out = [0_u8; 16];
1267 out[..NONCE_SIZE].copy_from_slice(nonce);
1268 out[15] = 1;
1269 out
1270}
1271
1272pub(crate) fn increment_counter(counter: &mut [u8; 16]) {
1275 let mut low_bytes = [0_u8; 4];
1276 low_bytes.copy_from_slice(&counter[12..]);
1277 let low = u32::from_be_bytes(low_bytes).wrapping_add(1);
1278 counter[12..].copy_from_slice(&low.to_be_bytes());
1279}
1280
1281fn constant_time_eq(expected: &[u8; TAG_SIZE], actual: &[u8]) -> bool {
1282 expected.as_slice().ct_eq(actual).into()
1287}
1288
1289#[cfg(test)]
1290mod tests {
1291 #![allow(clippy::expect_used, clippy::panic)]
1292
1293 use super::{
1294 Error, HardwareAes256Gcm, HardwareAes256GcmIn, HardwareAes256GcmKeyState,
1295 UninitKeyStateSlot, NONCE_SIZE,
1296 };
1297 use core::mem::ManuallyDrop;
1298
1299 #[repr(align(64))]
1300 struct AlignedStorage<const N: usize>([u8; N]);
1301
1302 #[test]
1303 fn rejects_wrong_key_length() {
1304 let Err(err) = HardwareAes256Gcm::new(&[0_u8; 31]) else {
1305 panic!("31-byte key should be rejected");
1306 };
1307 assert_eq!(err, Error::InvalidKeyLength);
1308 }
1309
1310 #[test]
1311 fn caller_placed_key_state_rejects_wrong_key_length() {
1312 let layout = HardwareAes256Gcm::key_state_layout();
1313 let mut storage = AlignedStorage::<512>([0_u8; 512]);
1314 let slot = UninitKeyStateSlot::new(&mut storage.0[..layout.size]).expect("valid slot");
1315 let Err(err) = HardwareAes256GcmIn::new_in(&[0_u8; 31], slot) else {
1316 panic!("31-byte key should be rejected");
1317 };
1318 assert_eq!(err, Error::InvalidKeyLength);
1319 }
1320
1321 #[test]
1322 fn inline_owned_key_state_rejects_wrong_key_length() {
1323 let Err(err) = HardwareAes256GcmKeyState::new(&[0_u8; 31]) else {
1324 panic!("31-byte key should be rejected");
1325 };
1326 assert_eq!(err, Error::InvalidKeyLength);
1327 }
1328
1329 #[test]
1330 fn rejects_ciphertext_shorter_than_tag() {
1331 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1332 let Err(err) = key.decrypt(&[0_u8; NONCE_SIZE], &[], &[0_u8; super::TAG_SIZE - 1]) else {
1333 panic!("input shorter than the tag should be rejected");
1334 };
1335 assert_eq!(err, Error::Decrypt);
1336 }
1337
1338 #[test]
1339 fn rejects_nonce_appended_shorter_than_tag_and_nonce() {
1340 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1341 let short = [0_u8; super::TAG_SIZE + NONCE_SIZE - 1];
1342 let Err(err) = key.decrypt_nonce_appended(&short) else {
1343 panic!("input shorter than tag plus nonce should be rejected");
1344 };
1345 assert_eq!(err, Error::CiphertextTooShort);
1346
1347 let mut out = [0_u8; 64];
1348 let Err(err) = key.decrypt_nonce_appended_to(&short, &mut out) else {
1349 panic!("input shorter than tag plus nonce should be rejected");
1350 };
1351 assert_eq!(err, Error::CiphertextTooShort);
1352 }
1353
1354 #[test]
1355 fn decrypt_to_writes_plaintext_into_caller_buffer() {
1356 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1357 let nonce = [9_u8; NONCE_SIZE];
1358 let ciphertext = key
1359 .encrypt(&nonce, b"aad", b"plaintext")
1360 .expect("encryption should succeed");
1361
1362 let mut exact = [0_u8; 9];
1363 let written = key
1364 .decrypt_to(&nonce, b"aad", &ciphertext, &mut exact)
1365 .expect("decryption into an exact-size buffer should succeed");
1366 assert_eq!(written, 9);
1367 assert_eq!(&exact, b"plaintext");
1368
1369 let mut oversized = [0xff_u8; 32];
1370 let written = key
1371 .decrypt_to(&nonce, b"aad", &ciphertext, &mut oversized)
1372 .expect("decryption into a larger buffer should succeed");
1373 assert_eq!(&oversized[..written], b"plaintext");
1374
1375 let mut short = [0_u8; 8];
1376 let Err(err) = key.decrypt_to(&nonce, b"aad", &ciphertext, &mut short) else {
1377 panic!("short output buffer should be rejected");
1378 };
1379 assert_eq!(err, Error::OutputTooSmall);
1380 assert_eq!(short, [0_u8; 8], "no plaintext may leak into short buffers");
1381
1382 let mut tampered = ciphertext;
1383 *tampered.last_mut().expect("tag byte") ^= 1;
1384 let mut invalid_out = [0xff_u8; 9];
1385 let Err(err) = key.decrypt_to(&nonce, b"aad", &tampered, &mut invalid_out) else {
1386 panic!("invalid authentication tag should be rejected");
1387 };
1388 assert_eq!(err, Error::Decrypt);
1389 assert_eq!(
1390 invalid_out, [0_u8; 9],
1391 "transient plaintext must be wiped on authentication failure"
1392 );
1393 }
1394
1395 #[test]
1396 fn encrypt_to_matches_allocating_encrypt() {
1397 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1398 let nonce = [9_u8; NONCE_SIZE];
1399 let plaintext = [0x5a_u8; 200];
1400 let expected = key
1401 .encrypt(&nonce, b"aad", &plaintext)
1402 .expect("encryption should succeed");
1403
1404 let mut exact = [0_u8; 200 + super::TAG_SIZE];
1405 let written = key
1406 .encrypt_to(&nonce, b"aad", &plaintext, &mut exact)
1407 .expect("encryption into an exact-size buffer should succeed");
1408 assert_eq!(written, exact.len());
1409 assert_eq!(exact.as_slice(), expected.as_slice());
1410
1411 let mut oversized = [0_u8; 256];
1412 let written = key
1413 .encrypt_to(&nonce, b"aad", &plaintext, &mut oversized)
1414 .expect("encryption into a larger buffer should succeed");
1415 assert_eq!(&oversized[..written], expected.as_slice());
1416
1417 let mut short = [0_u8; 200 + super::TAG_SIZE - 1];
1418 let Err(err) = key.encrypt_to(&nonce, b"aad", &plaintext, &mut short) else {
1419 panic!("short output buffer should be rejected");
1420 };
1421 assert_eq!(err, Error::OutputTooSmall);
1422 assert!(
1423 short.iter().all(|byte| *byte == 0),
1424 "no ciphertext may be written into short buffers"
1425 );
1426 }
1427
1428 #[test]
1429 fn encrypt_nonce_appended_to_matches_allocating_variant() {
1430 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1431 let nonce = [9_u8; NONCE_SIZE];
1432 let expected = key
1433 .encrypt_nonce_appended(&nonce, b"plaintext")
1434 .expect("encryption should succeed");
1435
1436 let mut out = [0_u8; 9 + super::TAG_SIZE + NONCE_SIZE];
1437 let written = key
1438 .encrypt_nonce_appended_to(&nonce, b"plaintext", &mut out)
1439 .expect("encryption should succeed");
1440 assert_eq!(written, out.len());
1441 assert_eq!(out.as_slice(), expected.as_slice());
1442
1443 let mut short = [0_u8; 9 + super::TAG_SIZE + NONCE_SIZE - 1];
1444 let Err(err) = key.encrypt_nonce_appended_to(&nonce, b"plaintext", &mut short) else {
1445 panic!("short output buffer should be rejected");
1446 };
1447 assert_eq!(err, Error::OutputTooSmall);
1448 }
1449
1450 #[test]
1451 fn encrypt_nonce_appended_in_place_uses_existing_vec_capacity() {
1452 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1453 let nonce = [9_u8; NONCE_SIZE];
1454 let expected = key
1455 .encrypt_nonce_appended(&nonce, b"plaintext")
1456 .expect("encryption should succeed");
1457
1458 let mut in_out = Vec::with_capacity(b"plaintext".len() + super::TAG_SIZE + NONCE_SIZE);
1459 in_out.extend_from_slice(b"plaintext");
1460 let ptr_before = in_out.as_ptr();
1461 key.encrypt_nonce_appended_in_place(&nonce, &mut in_out)
1462 .expect("in-place encryption should succeed");
1463
1464 assert_eq!(in_out, expected);
1465 assert_eq!(
1466 in_out.as_ptr(),
1467 ptr_before,
1468 "pre-reserved in-place encryption should not reallocate"
1469 );
1470 assert_eq!(
1471 key.decrypt_nonce_appended(&in_out)
1472 .expect("decryption should succeed"),
1473 b"plaintext"
1474 );
1475 }
1476
1477 #[test]
1478 fn decrypt_nonce_appended_to_round_trips() {
1479 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1480 let nonce = [9_u8; NONCE_SIZE];
1481 let data = key
1482 .encrypt_nonce_appended(&nonce, b"plaintext")
1483 .expect("encryption should succeed");
1484
1485 let mut out = [0_u8; 32];
1486 let written = key
1487 .decrypt_nonce_appended_to(&data, &mut out)
1488 .expect("decryption should succeed");
1489 assert_eq!(&out[..written], b"plaintext");
1490 }
1491
1492 #[test]
1493 fn generated_nonce_round_trips_and_is_unique() {
1494 let mut key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1495
1496 let (nonce_a, ct_a) = key
1497 .encrypt_with_generated_nonce(b"aad", b"plaintext")
1498 .expect("encryption should succeed");
1499 let (nonce_b, _ct_b) = key
1500 .encrypt_with_generated_nonce(b"aad", b"plaintext")
1501 .expect("encryption should succeed");
1502
1503 assert_ne!(nonce_a, nonce_b, "generated nonces must be unique");
1505 assert_eq!(
1507 key.decrypt(&nonce_a, b"aad", &ct_a)
1508 .expect("decryption should succeed"),
1509 b"plaintext"
1510 );
1511 }
1512
1513 #[test]
1514 fn generated_nonce_appended_layout_round_trips() {
1515 let mut key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1516 let blob = key
1517 .encrypt_nonce_appended_generated(b"plaintext")
1518 .expect("encryption should succeed");
1519 assert_eq!(
1520 blob.len(),
1521 b"plaintext".len() + super::TAG_SIZE + NONCE_SIZE
1522 );
1523 assert_eq!(
1524 key.decrypt_nonce_appended(&blob)
1525 .expect("decryption should succeed"),
1526 b"plaintext"
1527 );
1528 }
1529
1530 #[test]
1531 fn caller_placed_generated_nonce_round_trips() {
1532 let layout = HardwareAes256Gcm::key_state_layout();
1533 let mut storage = AlignedStorage::<512>([0_u8; 512]);
1534 let slot = UninitKeyStateSlot::new(&mut storage.0[..layout.size]).expect("valid slot");
1535 let mut key = HardwareAes256GcmIn::new_in(&[7_u8; 32], slot).expect("valid test key");
1536 let (nonce, ct) = key
1537 .encrypt_with_generated_nonce(&[], b"plaintext")
1538 .expect("encryption should succeed");
1539 assert_eq!(
1540 key.decrypt(&nonce, &[], &ct)
1541 .expect("decryption should succeed"),
1542 b"plaintext"
1543 );
1544 }
1545
1546 #[test]
1547 fn generated_nonce_does_not_change_key_state_size() {
1548 assert_eq!(HardwareAes256Gcm::key_state_layout().size, 368);
1550 assert_eq!(HardwareAes256Gcm::state_size(), 368);
1551 }
1552
1553 #[test]
1554 fn inline_owned_key_state_layout_matches_reported_layout() {
1555 let layout = HardwareAes256Gcm::key_state_layout();
1556 assert_eq!(
1557 std::mem::size_of::<HardwareAes256GcmKeyState>(),
1558 layout.size
1559 );
1560 assert_eq!(
1561 std::mem::align_of::<HardwareAes256GcmKeyState>(),
1562 layout.align
1563 );
1564 assert_eq!(HardwareAes256GcmKeyState::state_size(), layout.size);
1565 }
1566
1567 #[test]
1568 fn inline_owned_key_state_round_trips_and_uses_existing_vec_capacity() {
1569 let key = HardwareAes256GcmKeyState::new(&[7_u8; 32]).expect("valid test key");
1570 let nonce = [9_u8; NONCE_SIZE];
1571 let expected = key
1572 .encrypt_nonce_appended(&nonce, b"plaintext")
1573 .expect("encryption should succeed");
1574 assert_eq!(
1575 key.decrypt_nonce_appended(&expected)
1576 .expect("decryption should succeed"),
1577 b"plaintext"
1578 );
1579
1580 let mut in_out = Vec::with_capacity(b"plaintext".len() + super::TAG_SIZE + NONCE_SIZE);
1581 in_out.extend_from_slice(b"plaintext");
1582 let ptr_before = in_out.as_ptr();
1583 key.encrypt_nonce_appended_in_place(&nonce, &mut in_out)
1584 .expect("in-place encryption should succeed");
1585 assert_eq!(in_out, expected);
1586 assert_eq!(
1587 in_out.as_ptr(),
1588 ptr_before,
1589 "pre-reserved in-place encryption should not reallocate"
1590 );
1591 }
1592
1593 #[test]
1594 fn inline_owned_key_state_wipes_storage_on_drop() {
1595 let layout = HardwareAes256Gcm::key_state_layout();
1596 let mut key =
1597 ManuallyDrop::new(HardwareAes256GcmKeyState::new(&[7_u8; 32]).expect("valid test key"));
1598 let storage = key.storage.bytes_ptr_mut();
1599
1600 unsafe {
1604 ManuallyDrop::drop(&mut key);
1605 let bytes = std::slice::from_raw_parts(storage.cast_const(), layout.size);
1606 assert!(bytes.iter().all(|byte| *byte == 0));
1607 }
1608 }
1609
1610 #[test]
1611 fn caller_placed_key_state_is_usable_across_threads() {
1612 let layout = HardwareAes256Gcm::key_state_layout();
1613 let mut storage = AlignedStorage::<512>([0_u8; 512]);
1614 let slot = UninitKeyStateSlot::new(&mut storage.0[..layout.size]).expect("valid slot");
1615 let key = HardwareAes256GcmIn::new_in(&[7_u8; 32], slot).expect("valid test key");
1616 let nonce = [9_u8; NONCE_SIZE];
1617 let ciphertext = key
1618 .encrypt(&nonce, &[], b"plaintext")
1619 .expect("encryption should succeed");
1620
1621 std::thread::scope(|scope| {
1622 for _ in 0..4 {
1623 scope.spawn(|| {
1624 assert_eq!(
1625 key.decrypt(&nonce, &[], &ciphertext)
1626 .expect("shared decryption should succeed"),
1627 b"plaintext"
1628 );
1629 });
1630 }
1631 });
1632 }
1633
1634 #[test]
1635 fn rejects_wrong_nonce_length() {
1636 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1637 let Err(err) = key.encrypt(&[0_u8; NONCE_SIZE - 1], &[], b"data") else {
1638 panic!("short nonce should be rejected");
1639 };
1640 assert_eq!(err, Error::InvalidNonceLength);
1641 }
1642
1643 #[test]
1644 #[cfg(target_pointer_width = "64")]
1645 fn rejects_inputs_that_exceed_gcm_counter_limit() {
1646 let max_data_len =
1647 usize::try_from(super::MAX_GCM_DATA_LEN).expect("64-bit usize holds GCM data limit");
1648 assert_eq!(
1649 super::validate_gcm_lengths(0, max_data_len + 1),
1650 Err(Error::InputTooLarge)
1651 );
1652 }
1653
1654 #[test]
1655 #[cfg(target_pointer_width = "64")]
1656 fn accepts_inputs_at_gcm_counter_limit() {
1657 let max_data_len =
1658 usize::try_from(super::MAX_GCM_DATA_LEN).expect("64-bit usize holds GCM data limit");
1659 assert_eq!(super::validate_gcm_lengths(0, max_data_len), Ok(()));
1660 }
1661
1662 #[test]
1663 fn nonce_appended_round_trips() {
1664 let key = HardwareAes256Gcm::new(&[7_u8; 32]).expect("valid test key");
1665 let nonce = [9_u8; NONCE_SIZE];
1666 let ciphertext = key
1667 .encrypt_nonce_appended(&nonce, b"plaintext")
1668 .expect("encryption should succeed");
1669 assert_eq!(
1670 key.decrypt_nonce_appended(&ciphertext)
1671 .expect("decryption should succeed"),
1672 b"plaintext"
1673 );
1674 }
1675
1676 #[test]
1677 fn reports_nonzero_key_state_layout() {
1678 let layout = HardwareAes256Gcm::key_state_layout();
1679 assert!(layout.size >= super::KEY_SIZE);
1680 assert!(layout.align.is_power_of_two());
1681 assert!(layout.size <= 384);
1682 assert_eq!(layout.size, HardwareAes256Gcm::state_size());
1683 }
1684
1685 #[test]
1686 fn caller_placed_key_state_round_trips_and_wipes_storage() {
1687 let layout = HardwareAes256Gcm::key_state_layout();
1688 let mut storage = AlignedStorage::<512>([0xa5; 512]);
1689 let nonce = [9_u8; NONCE_SIZE];
1690
1691 {
1692 let slot = UninitKeyStateSlot::new(&mut storage.0[..layout.size]).expect("valid slot");
1693 let key = HardwareAes256GcmIn::new_in(&[7_u8; 32], slot).expect("valid test key");
1694 let ciphertext = key
1695 .encrypt_nonce_appended(&nonce, b"plaintext")
1696 .expect("encryption should succeed");
1697 assert_eq!(
1698 key.decrypt_nonce_appended(&ciphertext)
1699 .expect("decryption should succeed"),
1700 b"plaintext"
1701 );
1702 }
1703
1704 assert!(storage.0[..layout.size].iter().all(|byte| *byte == 0));
1705 }
1706
1707 #[test]
1708 fn caller_placed_key_state_validates_storage_before_key() {
1709 let layout = HardwareAes256Gcm::key_state_layout();
1710 let mut short = AlignedStorage::<512>([0_u8; 512]);
1711 let err = UninitKeyStateSlot::new(&mut short.0[..layout.size - 1])
1712 .expect_err("short storage should fail");
1713 assert_eq!(err, Error::KeyStateStorageTooSmall);
1714
1715 let mut misaligned = AlignedStorage::<512>([0_u8; 512]);
1716 let start = 1;
1717 let end = start + layout.size;
1718 let err = UninitKeyStateSlot::new(&mut misaligned.0[start..end])
1719 .expect_err("misaligned storage should fail");
1720 assert_eq!(err, Error::KeyStateStorageMisaligned);
1721 }
1722}