kimberlite_crypto/encryption.rs
1//! AES-256-GCM authenticated encryption for tenant data isolation.
2//!
3//! Provides envelope encryption where each tenant's data is encrypted with
4//! a unique key. AES-256-GCM is a FIPS 197 approved AEAD cipher that provides
5//! both confidentiality and integrity.
6//!
7//! # Example
8//!
9//! ```
10//! use kimberlite_crypto::encryption::{EncryptionKey, Nonce, encrypt, decrypt};
11//!
12//! let key = EncryptionKey::generate();
13//! let position: u64 = 42; // Log position of the record
14//! let nonce = Nonce::from_position(position);
15//!
16//! let plaintext = b"sensitive tenant data";
17//! let ciphertext = encrypt(&key, &nonce, plaintext);
18//!
19//! let decrypted = decrypt(&key, &nonce, &ciphertext).unwrap();
20//! assert_eq!(decrypted, plaintext.as_slice());
21//! ```
22//!
23//! # Security
24//!
25//! - **Never reuse a nonce** with the same key. For `Kimberlite`, nonces are
26//! derived from log position to guarantee uniqueness.
27//! - Store keys encrypted at rest using [`WrappedKey`] for the key hierarchy.
28//! - The authentication tag prevents tampering — decryption fails if the
29//! ciphertext or tag is modified.
30
31use aes_gcm::{Aes256Gcm, KeyInit, aead::Aead};
32use zeroize::{Zeroize, ZeroizeOnDrop};
33
34use crate::CryptoError;
35
36// ============================================================================
37// Constants
38// ============================================================================
39
40/// Length of an AES-256-GCM encryption key in bytes (256 bits).
41///
42/// AES-256-GCM is chosen as a FIPS 197 approved algorithm, providing
43/// compliance for regulated industries (healthcare, finance, government).
44pub const KEY_LENGTH: usize = 32;
45
46/// Length of an AES-256-GCM nonce in bytes (96 bits).
47///
48/// In `Kimberlite`, nonces are derived from the log position to guarantee
49/// uniqueness without requiring random generation.
50pub const NONCE_LENGTH: usize = 12;
51
52/// Length of the AES-GCM authentication tag in bytes (128 bits).
53///
54/// The authentication tag ensures integrity — decryption fails if the
55/// ciphertext or tag has been tampered with.
56pub const TAG_LENGTH: usize = 16;
57
58pub const WRAPPED_KEY_LENGTH: usize = NONCE_LENGTH + KEY_LENGTH + TAG_LENGTH;
59
60// ============================================================================
61// EncryptionKey
62// ============================================================================
63
64/// An AES-256-GCM encryption key (256 bits).
65///
66/// This is secret key material that must be protected. Use [`EncryptionKey::generate`]
67/// to create a new random key, or [`EncryptionKey::from_bytes`] to restore from
68/// secure storage.
69///
70/// Key material is securely zeroed from memory when dropped via [`ZeroizeOnDrop`].
71///
72/// # Security
73///
74/// - Never log or expose the key bytes
75/// - Store encrypted at rest (wrap with a KEK from the key hierarchy)
76/// - Use one key per tenant/segment for isolation
77#[derive(Zeroize, ZeroizeOnDrop)]
78pub struct EncryptionKey([u8; KEY_LENGTH]);
79
80impl EncryptionKey {
81 // ========================================================================
82 // Functional Core (pure, testable)
83 // ========================================================================
84
85 /// Creates an encryption key from random bytes (pure, no IO).
86 ///
87 /// This is the functional core - it performs no IO and is fully testable.
88 /// Use [`Self::generate`] for the public API that handles randomness.
89 ///
90 /// # Security
91 ///
92 /// Only use bytes from a CSPRNG. This is `pub(crate)` to prevent misuse.
93 pub(crate) fn from_random_bytes(bytes: [u8; KEY_LENGTH]) -> Self {
94 // Precondition: caller provided non-degenerate random bytes
95 assert!(
96 bytes.iter().any(|&b| b != 0),
97 "EncryptionKey random bytes are all zeros - RNG failure or bug"
98 );
99
100 Self(bytes)
101 }
102
103 /// Restores an encryption key from its 32-byte representation.
104 ///
105 /// # Security
106 ///
107 /// Only use bytes from a previously generated key or a secure KDF.
108 pub fn from_bytes(bytes: &[u8; KEY_LENGTH]) -> Self {
109 // Precondition: caller didn't pass degenerate key material
110 assert!(
111 bytes.iter().any(|&b| b != 0),
112 "EncryptionKey bytes are all zeros - corrupted or uninitialized key material"
113 );
114
115 Self(*bytes)
116 }
117
118 /// Returns the raw 32-byte key material.
119 ///
120 /// # Security
121 ///
122 /// Handle with care — this is secret key material.
123 pub fn to_bytes(&self) -> [u8; KEY_LENGTH] {
124 self.0
125 }
126
127 // ========================================================================
128 // Imperative Shell (IO boundary)
129 // ========================================================================
130
131 /// Generates a new random encryption key using the OS CSPRNG.
132 ///
133 /// This is the imperative shell - it handles IO (randomness) and delegates
134 /// to a pure internal constructor for the actual construction.
135 ///
136 /// # Panics
137 ///
138 /// Panics if the OS CSPRNG fails (catastrophic system error).
139 pub fn generate() -> Self {
140 let random_bytes: [u8; KEY_LENGTH] = generate_random();
141 Self::from_random_bytes(random_bytes)
142 }
143}
144
145// ============================================================================
146// Nonce
147// ============================================================================
148
149/// An AES-256-GCM nonce (96 bits) derived from log position.
150///
151/// Nonces in `Kimberlite` are deterministically derived from the record's log
152/// position, guaranteeing uniqueness without requiring random generation.
153/// The 8-byte position is placed in the first 8 bytes; the remaining 4 bytes
154/// are reserved (currently zero).
155///
156/// ```text
157/// Nonce layout (12 bytes):
158/// ┌────────────────────────────┬──────────────┐
159/// │ position (8 bytes, LE) │ reserved │
160/// │ [0..8] │ [8..12] │
161/// └────────────────────────────┴──────────────┘
162/// ```
163///
164/// # Security
165///
166/// **Never reuse a nonce with the same key.** Nonce reuse completely breaks
167/// the confidentiality of AES-GCM. Position-derived nonces guarantee uniqueness
168/// as long as each position is encrypted at most once per key.
169#[derive(Clone, Copy, PartialEq, Eq)]
170pub struct Nonce([u8; NONCE_LENGTH]);
171
172impl Nonce {
173 // ========================================================================
174 // Functional Core (pure, testable)
175 // ========================================================================
176
177 /// Creates a nonce from random bytes (pure, no IO).
178 ///
179 /// This is the functional core - it performs no IO and is fully testable.
180 /// Use [`Self::generate_random`] for the public API that handles randomness.
181 ///
182 /// # Security
183 ///
184 /// Only use bytes from a CSPRNG. This is `pub(crate)` to prevent misuse.
185 pub(crate) fn from_random_bytes(bytes: [u8; NONCE_LENGTH]) -> Self {
186 // Precondition: caller provided non-degenerate random bytes
187 assert!(
188 bytes.iter().any(|&b| b != 0),
189 "Nonce random bytes are all zeros - RNG failure or bug"
190 );
191
192 Self(bytes)
193 }
194
195 /// Creates a nonce from a log position (pure, deterministic).
196 ///
197 /// The position's little-endian bytes occupy the first 8 bytes of the
198 /// nonce. The remaining 4 bytes are reserved for future use (e.g., key
199 /// rotation counter) and are currently set to zero.
200 ///
201 /// # Arguments
202 ///
203 /// * `position` - The log position of the record being encrypted
204 pub fn from_position(position: u64) -> Self {
205 let mut nonce = [0u8; NONCE_LENGTH];
206 nonce[..8].copy_from_slice(&position.to_le_bytes());
207
208 Self(nonce)
209 }
210
211 /// Creates a nonce from raw bytes.
212 ///
213 /// Used when deserializing a nonce from storage (e.g., from a [`WrappedKey`]).
214 ///
215 /// # Arguments
216 ///
217 /// * `bytes` - The 12-byte nonce
218 pub fn from_bytes(bytes: [u8; NONCE_LENGTH]) -> Self {
219 Self(bytes)
220 }
221
222 /// Returns the raw 12-byte nonce.
223 ///
224 /// Use this for serialization or when interfacing with external systems.
225 pub fn to_bytes(&self) -> [u8; NONCE_LENGTH] {
226 self.0
227 }
228
229 // ========================================================================
230 // Imperative Shell (IO boundary)
231 // ========================================================================
232
233 /// Generates a random nonce using the OS CSPRNG.
234 ///
235 /// Used for key wrapping where there's no log position to derive from.
236 /// The random nonce must be stored alongside the ciphertext.
237 ///
238 /// This is the imperative shell - it handles IO (randomness) and delegates
239 /// to a pure internal constructor for the actual construction.
240 ///
241 /// # Panics
242 ///
243 /// Panics if the OS CSPRNG fails (catastrophic system error).
244 pub fn generate_random() -> Self {
245 let random_bytes: [u8; NONCE_LENGTH] = generate_random();
246 Self::from_random_bytes(random_bytes)
247 }
248}
249
250// ============================================================================
251// Ciphertext
252// ============================================================================
253
254/// Encrypted data with its authentication tag.
255///
256/// Contains the ciphertext followed by a 16-byte AES-GCM authentication tag.
257/// The total length is `plaintext.len() + TAG_LENGTH`. Decryption will fail
258/// if the ciphertext or tag has been modified.
259///
260/// ```text
261/// Ciphertext layout:
262/// ┌────────────────────────────┬──────────────────┐
263/// │ encrypted data │ auth tag │
264/// │ [0..plaintext.len()] │ [last 16 bytes] │
265/// └────────────────────────────┴──────────────────┘
266/// ```
267#[derive(Clone, PartialEq, Eq)]
268pub struct Ciphertext(Vec<u8>);
269
270impl Ciphertext {
271 /// Creates a ciphertext from raw bytes.
272 ///
273 /// # Arguments
274 ///
275 /// * `bytes` - The encrypted data with authentication tag appended
276 ///
277 /// # Panics
278 ///
279 /// Debug builds panic if `bytes.len() < TAG_LENGTH` (no room for auth tag).
280 pub fn from_bytes(bytes: Vec<u8>) -> Self {
281 assert!(
282 bytes.len() >= TAG_LENGTH,
283 "ciphertext too short: must be at least {} bytes for auth tag, got {}",
284 TAG_LENGTH,
285 bytes.len()
286 );
287
288 Self(bytes)
289 }
290
291 /// Returns the raw ciphertext bytes (including authentication tag).
292 ///
293 /// Use this for serialization or storage.
294 pub fn to_bytes(&self) -> &[u8] {
295 &self.0
296 }
297
298 /// Returns the length of the ciphertext (including authentication tag).
299 pub fn len(&self) -> usize {
300 self.0.len()
301 }
302
303 /// Returns true if the ciphertext is empty (which would be invalid).
304 pub fn is_empty(&self) -> bool {
305 self.0.is_empty()
306 }
307}
308
309// ============================================================================
310// WrappedKey
311// ============================================================================
312
313/// An encryption key wrapped (encrypted) by another key.
314///
315/// Used in the key hierarchy to protect DEKs with KEKs, and KEKs with the
316/// master key. The wrapped key can be safely stored on disk — it cannot be
317/// unwrapped without the wrapping key.
318///
319/// ```text
320/// WrappedKey layout (60 bytes):
321/// ┌────────────────────────────┬────────────────────────────┬──────────────┐
322/// │ nonce (12 bytes) │ encrypted key (32 bytes) │ tag (16) │
323/// └────────────────────────────┴────────────────────────────┴──────────────┘
324/// ```
325///
326/// # Security
327///
328/// - The wrapped ciphertext is encrypted, not raw key material
329/// - The nonce is not secret (it's stored alongside the ciphertext)
330/// - `ZeroizeOnDrop` is not needed since this contains no plaintext secrets
331/// - The wrapping key (KEK/MK) is what must be protected
332///
333/// # Example
334///
335/// ```
336/// use kimberlite_crypto::encryption::{EncryptionKey, WrappedKey, KEY_LENGTH};
337///
338/// // KEK wraps a DEK
339/// let kek = EncryptionKey::generate();
340/// let dek_bytes: [u8; KEY_LENGTH] = [0x42; KEY_LENGTH]; // In practice, use generate()
341///
342/// let wrapped = WrappedKey::new(&kek, &dek_bytes);
343///
344/// // Store wrapped.to_bytes() on disk...
345///
346/// // Later, unwrap with the same KEK
347/// let unwrapped = wrapped.unwrap_key(&kek).unwrap();
348/// assert_eq!(dek_bytes, unwrapped);
349/// ```
350pub struct WrappedKey {
351 nonce: Nonce,
352 ciphertext: Ciphertext,
353}
354
355impl WrappedKey {
356 /// Wraps (encrypts) a key using the provided wrapping key.
357 ///
358 /// Generates a random nonce and encrypts the key material using AES-256-GCM.
359 /// The nonce is stored alongside the ciphertext for later unwrapping.
360 ///
361 /// # Arguments
362 ///
363 /// * `wrapping_key` - The key used to encrypt (KEK or master key)
364 /// * `key_to_wrap` - The 32-byte key material to protect (DEK or KEK)
365 ///
366 /// # Returns
367 ///
368 /// A [`WrappedKey`] that can be serialized and stored safely.
369 pub fn new(wrapping_key: &EncryptionKey, key_to_wrap: &[u8; KEY_LENGTH]) -> Self {
370 // Precondition: key material isn't degenerate
371 assert!(
372 key_to_wrap.iter().any(|&b| b != 0),
373 "key_to_wrap is all zeros - corrupted or uninitialized key material"
374 );
375
376 let nonce = Nonce::generate_random();
377 let ciphertext = encrypt(wrapping_key, &nonce, key_to_wrap);
378
379 // Postcondition: ciphertext is correct size (key + tag)
380 assert_eq!(
381 ciphertext.len(),
382 KEY_LENGTH + TAG_LENGTH,
383 "wrapped ciphertext has unexpected length: expected {}, got {}",
384 KEY_LENGTH + TAG_LENGTH,
385 ciphertext.len()
386 );
387
388 Self { nonce, ciphertext }
389 }
390
391 /// Unwraps (decrypts) the key using the provided wrapping key.
392 ///
393 /// Verifies the authentication tag and returns the original key material
394 /// if the wrapping key is correct.
395 ///
396 /// # Arguments
397 ///
398 /// * `wrapping_key` - The key used during wrapping (must match)
399 ///
400 /// # Errors
401 ///
402 /// Returns [`CryptoError::DecryptionError`] if:
403 /// - The wrapping key is incorrect
404 /// - The wrapped data has been tampered with
405 ///
406 /// # Returns
407 ///
408 /// The original 32-byte key material.
409 pub fn unwrap_key(
410 &self,
411 wrapping_key: &EncryptionKey,
412 ) -> Result<[u8; KEY_LENGTH], CryptoError> {
413 let decrypted = decrypt(wrapping_key, &self.nonce, &self.ciphertext)?;
414
415 // Postcondition: decrypted key has correct length
416 assert_eq!(
417 decrypted.len(),
418 KEY_LENGTH,
419 "unwrapped key has unexpected length: expected {}, got {}",
420 KEY_LENGTH,
421 decrypted.len()
422 );
423
424 decrypted
425 .try_into()
426 .map_err(|_| CryptoError::DecryptionError)
427 }
428
429 /// Serializes the wrapped key to bytes for storage.
430 ///
431 /// The format is: `nonce (12 bytes) || ciphertext (48 bytes)`.
432 ///
433 /// # Returns
434 ///
435 /// A 60-byte array suitable for storing on disk or in a database.
436 pub fn to_bytes(&self) -> [u8; WRAPPED_KEY_LENGTH] {
437 let mut bytes = [0u8; WRAPPED_KEY_LENGTH];
438 bytes[..NONCE_LENGTH].copy_from_slice(&self.nonce.to_bytes());
439 bytes[NONCE_LENGTH..].copy_from_slice(self.ciphertext.to_bytes());
440
441 // Postcondition: we produced non-degenerate output
442 assert!(
443 bytes.iter().any(|&b| b != 0),
444 "serialized wrapped key is all zeros - encryption bug"
445 );
446
447 bytes
448 }
449
450 /// Deserializes a wrapped key from bytes.
451 ///
452 /// # Arguments
453 ///
454 /// * `bytes` - A 60-byte array from [`WrappedKey::to_bytes`]
455 ///
456 /// # Returns
457 ///
458 /// A [`WrappedKey`] that can be unwrapped with the original wrapping key.
459 pub fn from_bytes(bytes: &[u8; WRAPPED_KEY_LENGTH]) -> Self {
460 // Precondition: bytes aren't all zeros (likely corrupted or uninitialized)
461 assert!(
462 bytes.iter().any(|&b| b != 0),
463 "wrapped key bytes are all zeros - corrupted or uninitialized storage"
464 );
465
466 let mut nonce_bytes = [0u8; NONCE_LENGTH];
467 nonce_bytes.copy_from_slice(&bytes[..NONCE_LENGTH]);
468 let ciphertext = Ciphertext::from_bytes(bytes[NONCE_LENGTH..].to_vec());
469
470 Self {
471 nonce: Nonce::from_bytes(nonce_bytes),
472 ciphertext,
473 }
474 }
475}
476
477// ============================================================================
478// Key Hierarchy
479// ============================================================================
480
481/// Provider for master key operations.
482///
483/// The master key is the root of the key hierarchy. It wraps Key Encryption
484/// Keys (KEKs), which in turn wrap Data Encryption Keys (DEKs).
485///
486/// ```text
487/// MasterKeyProvider
488/// │
489/// └── wraps ──► KeyEncryptionKey (per tenant)
490/// │
491/// └── wraps ──► DataEncryptionKey (per segment)
492/// │
493/// └── encrypts ──► Record data
494/// ```
495///
496/// This trait abstracts the master key storage, enabling:
497/// - [`InMemoryMasterKey`]: Development, testing, single-node deployments
498/// - Future: HSM-backed implementation where the key never leaves the hardware
499///
500/// # Security
501///
502/// The master key is the most sensitive secret in the system. If compromised,
503/// all tenant data can be decrypted. In production, use an HSM.
504pub trait MasterKeyProvider {
505 /// Wraps a Key Encryption Key for secure storage.
506 ///
507 /// The wrapped KEK can be stored on disk and later unwrapped with
508 /// [`MasterKeyProvider::unwrap_kek`].
509 fn wrap_kek(&self, kek_bytes: &[u8; KEY_LENGTH]) -> WrappedKey;
510
511 /// Unwraps a Key Encryption Key from storage.
512 ///
513 /// # Errors
514 ///
515 /// Returns [`CryptoError::DecryptionError`] if the wrapped key is
516 /// corrupted or was wrapped by a different master key.
517 fn unwrap_kek(&self, wrapped: &WrappedKey) -> Result<[u8; KEY_LENGTH], CryptoError>;
518}
519
520/// In-memory master key for development and testing.
521///
522/// Stores the master key material directly in memory. Suitable for:
523/// - Development and testing
524/// - Single-node deployments with disk encryption
525/// - Environments without HSM access
526///
527/// # Security
528///
529/// The key material is zeroed on drop via [`ZeroizeOnDrop`]. However, for
530/// production deployments handling sensitive data, prefer an HSM-backed
531/// implementation.
532///
533/// # Example
534///
535/// ```
536/// use kimberlite_crypto::encryption::{InMemoryMasterKey, MasterKeyProvider, KeyEncryptionKey};
537///
538/// let master = InMemoryMasterKey::generate();
539/// let (kek, wrapped_kek) = KeyEncryptionKey::generate_and_wrap(&master);
540///
541/// // Store wrapped_kek.to_bytes() on disk...
542/// ```
543#[derive(Zeroize, ZeroizeOnDrop)]
544pub struct InMemoryMasterKey(EncryptionKey);
545
546impl InMemoryMasterKey {
547 // ========================================================================
548 // Functional Core (pure, testable)
549 // ========================================================================
550
551 /// Creates a master key from random bytes (pure, no IO).
552 ///
553 /// This is the functional core - it performs no IO and is fully testable.
554 /// Use [`Self::generate`] for the public API that handles randomness.
555 ///
556 /// # Security
557 ///
558 /// Only use bytes from a CSPRNG. This is `pub(crate)` to prevent misuse.
559 pub(crate) fn from_random_bytes(bytes: [u8; KEY_LENGTH]) -> Self {
560 Self(EncryptionKey::from_random_bytes(bytes))
561 }
562
563 /// Restores a master key from its 32-byte representation.
564 ///
565 /// # Security
566 ///
567 /// Only use bytes from a previously generated key or secure backup.
568 /// The bytes should come from encrypted-at-rest storage.
569 pub fn from_bytes(bytes: &[u8; KEY_LENGTH]) -> Self {
570 // Precondition: caller didn't pass degenerate key material
571 assert!(
572 bytes.iter().any(|&b| b != 0),
573 "master key bytes are all zeros - corrupted or uninitialized key material"
574 );
575
576 Self(EncryptionKey::from_bytes(bytes))
577 }
578
579 /// Returns the raw 32-byte key material for backup.
580 ///
581 /// # Security
582 ///
583 /// **Handle with extreme care.** This is the root secret of the entire
584 /// key hierarchy. Only use this for:
585 /// - Secure backup to encrypted storage
586 /// - Key escrow with proper controls
587 ///
588 /// Never log, transmit unencrypted, or store in plaintext.
589 pub fn to_bytes(&self) -> [u8; KEY_LENGTH] {
590 self.0.to_bytes()
591 }
592
593 // ========================================================================
594 // Imperative Shell (IO boundary)
595 // ========================================================================
596
597 /// Generates a new random master key using the OS CSPRNG.
598 ///
599 /// This is the imperative shell - it handles IO (randomness) and delegates
600 /// to a pure internal constructor for the actual construction.
601 ///
602 /// # Panics
603 ///
604 /// Panics if the OS CSPRNG fails (catastrophic system error).
605 pub fn generate() -> Self {
606 let random_bytes: [u8; KEY_LENGTH] = generate_random();
607 Self::from_random_bytes(random_bytes)
608 }
609}
610
611impl MasterKeyProvider for InMemoryMasterKey {
612 fn wrap_kek(&self, kek_bytes: &[u8; KEY_LENGTH]) -> WrappedKey {
613 // Precondition: KEK bytes aren't degenerate
614 assert!(
615 kek_bytes.iter().any(|&b| b != 0),
616 "KEK bytes are all zeros - corrupted or uninitialized key material"
617 );
618
619 WrappedKey::new(&self.0, kek_bytes)
620 }
621
622 fn unwrap_kek(&self, wrapped: &WrappedKey) -> Result<[u8; KEY_LENGTH], CryptoError> {
623 let kek_bytes = wrapped.unwrap_key(&self.0)?;
624
625 // Postcondition: unwrapped KEK isn't degenerate
626 assert!(
627 kek_bytes.iter().any(|&b| b != 0),
628 "unwrapped KEK is all zeros - decryption produced invalid key"
629 );
630
631 Ok(kek_bytes)
632 }
633}
634
635/// Key Encryption Key (KEK) for wrapping Data Encryption Keys.
636///
637/// Each tenant has one KEK. The KEK is wrapped by the master key and stored
638/// alongside tenant metadata. Deleting a tenant's wrapped KEK renders all
639/// their data cryptographically inaccessible (GDPR "right to erasure").
640///
641/// # Key Hierarchy Position
642///
643/// ```text
644/// MasterKeyProvider
645/// │
646/// └── wraps ──► KeyEncryptionKey (this type)
647/// │
648/// └── wraps ──► DataEncryptionKey
649/// ```
650///
651/// # Example
652///
653/// ```
654/// use kimberlite_crypto::encryption::{
655/// InMemoryMasterKey, MasterKeyProvider, KeyEncryptionKey, DataEncryptionKey,
656/// };
657///
658/// let master = InMemoryMasterKey::generate();
659///
660/// // Create KEK for a new tenant
661/// let (kek, wrapped_kek) = KeyEncryptionKey::generate_and_wrap(&master);
662///
663/// // Store wrapped_kek.to_bytes() in tenant metadata...
664///
665/// // Later: restore KEK when tenant accesses data
666/// let kek = KeyEncryptionKey::restore(&master, &wrapped_kek).unwrap();
667/// ```
668#[derive(Zeroize, ZeroizeOnDrop)]
669pub struct KeyEncryptionKey(EncryptionKey);
670
671impl KeyEncryptionKey {
672 // ========================================================================
673 // Functional Core (pure, testable)
674 // ========================================================================
675
676 /// Creates a KEK from random bytes and wraps it (pure, no IO).
677 ///
678 /// This is the functional core - it performs no IO and is fully testable.
679 /// Use [`Self::generate_and_wrap`] for the public API that handles randomness.
680 ///
681 /// # Security
682 ///
683 /// Only use bytes from a CSPRNG. This is `pub(crate)` to prevent misuse.
684 pub(crate) fn from_random_bytes_and_wrap(
685 random_bytes: [u8; KEY_LENGTH],
686 master: &impl MasterKeyProvider,
687 ) -> (Self, WrappedKey) {
688 let key = EncryptionKey::from_random_bytes(random_bytes);
689 let wrapped = master.wrap_kek(&key.to_bytes());
690
691 (Self(key), wrapped)
692 }
693
694 /// Restores a KEK from its wrapped form (pure, no IO).
695 ///
696 /// Use this when loading a tenant's KEK from storage.
697 ///
698 /// # Arguments
699 ///
700 /// * `master` - The master key provider that originally wrapped this KEK
701 /// * `wrapped` - The wrapped KEK from storage
702 ///
703 /// # Errors
704 ///
705 /// Returns [`CryptoError::DecryptionError`] if:
706 /// - The wrapped key is corrupted
707 /// - The wrong master key is used
708 pub fn restore(
709 master: &impl MasterKeyProvider,
710 wrapped: &WrappedKey,
711 ) -> Result<Self, CryptoError> {
712 let key_bytes = master.unwrap_kek(wrapped)?;
713
714 // Postcondition: restored key isn't degenerate
715 assert!(
716 key_bytes.iter().any(|&b| b != 0),
717 "restored KEK is all zeros - decryption produced invalid key"
718 );
719
720 Ok(Self(EncryptionKey::from_bytes(&key_bytes)))
721 }
722
723 /// Wraps a Data Encryption Key for secure storage.
724 ///
725 /// The wrapped DEK should be stored in the segment header.
726 pub fn wrap_dek(&self, dek_bytes: &[u8; KEY_LENGTH]) -> WrappedKey {
727 // Precondition: DEK bytes aren't degenerate
728 assert!(
729 dek_bytes.iter().any(|&b| b != 0),
730 "DEK bytes are all zeros - corrupted or uninitialized key material"
731 );
732
733 WrappedKey::new(&self.0, dek_bytes)
734 }
735
736 /// Unwraps a Data Encryption Key from storage.
737 ///
738 /// # Errors
739 ///
740 /// Returns [`CryptoError::DecryptionError`] if:
741 /// - The wrapped key is corrupted
742 /// - The wrong KEK is used
743 pub fn unwrap_dek(&self, wrapped: &WrappedKey) -> Result<[u8; KEY_LENGTH], CryptoError> {
744 let dek_bytes = wrapped.unwrap_key(&self.0)?;
745
746 // Postcondition: unwrapped DEK isn't degenerate
747 assert!(
748 dek_bytes.iter().any(|&b| b != 0),
749 "unwrapped DEK is all zeros - decryption produced invalid key"
750 );
751
752 Ok(dek_bytes)
753 }
754
755 // ========================================================================
756 // Imperative Shell (IO boundary)
757 // ========================================================================
758
759 /// Generates a new KEK and wraps it with the master key.
760 ///
761 /// Returns both the usable KEK and its wrapped form for storage.
762 /// The wrapped form should be persisted alongside tenant metadata.
763 ///
764 /// This is the imperative shell - it handles IO (randomness) and delegates
765 /// to a pure internal constructor for the actual construction.
766 ///
767 /// # Arguments
768 ///
769 /// * `master` - The master key provider to wrap the KEK
770 ///
771 /// # Returns
772 ///
773 /// A tuple of `(usable_kek, wrapped_kek_for_storage)`.
774 ///
775 /// # Panics
776 ///
777 /// Panics if the OS CSPRNG fails (catastrophic system error).
778 pub fn generate_and_wrap(master: &impl MasterKeyProvider) -> (Self, WrappedKey) {
779 let random_bytes: [u8; KEY_LENGTH] = generate_random();
780 Self::from_random_bytes_and_wrap(random_bytes, master)
781 }
782}
783
784/// Data Encryption Key (DEK) for encrypting actual record data.
785///
786/// Each segment (chunk of the log) has its own DEK. The DEK is wrapped by
787/// the tenant's KEK and stored in the segment header.
788///
789/// # Key Hierarchy Position
790///
791/// ```text
792/// MasterKeyProvider
793/// │
794/// └── wraps ──► KeyEncryptionKey
795/// │
796/// └── wraps ──► DataEncryptionKey (this type)
797/// │
798/// └── encrypts ──► Record data
799/// ```
800///
801/// # Example
802///
803/// ```
804/// use kimberlite_crypto::encryption::{
805/// InMemoryMasterKey, KeyEncryptionKey, DataEncryptionKey,
806/// Nonce, encrypt, decrypt,
807/// };
808///
809/// let master = InMemoryMasterKey::generate();
810/// let (kek, _) = KeyEncryptionKey::generate_and_wrap(&master);
811///
812/// // Create DEK for a new segment
813/// let (dek, wrapped_dek) = DataEncryptionKey::generate_and_wrap(&kek);
814///
815/// // Encrypt data
816/// let nonce = Nonce::from_position(0);
817/// let ciphertext = encrypt(dek.encryption_key(), &nonce, b"secret data");
818///
819/// // Decrypt data
820/// let plaintext = decrypt(dek.encryption_key(), &nonce, &ciphertext).unwrap();
821/// assert_eq!(plaintext, b"secret data");
822/// ```
823#[derive(Zeroize, ZeroizeOnDrop)]
824pub struct DataEncryptionKey(EncryptionKey);
825
826impl DataEncryptionKey {
827 // ========================================================================
828 // Functional Core (pure, testable)
829 // ========================================================================
830
831 /// Creates a DEK from random bytes and wraps it (pure, no IO).
832 ///
833 /// This is the functional core - it performs no IO and is fully testable.
834 /// Use [`Self::generate_and_wrap`] for the public API that handles randomness.
835 ///
836 /// # Security
837 ///
838 /// Only use bytes from a CSPRNG. This is `pub(crate)` to prevent misuse.
839 pub(crate) fn from_random_bytes_and_wrap(
840 random_bytes: [u8; KEY_LENGTH],
841 kek: &KeyEncryptionKey,
842 ) -> (Self, WrappedKey) {
843 let encryption_key = EncryptionKey::from_random_bytes(random_bytes);
844 let wrapped = kek.wrap_dek(&encryption_key.to_bytes());
845
846 (Self(encryption_key), wrapped)
847 }
848
849 /// Restores a DEK from its wrapped form (pure, no IO).
850 ///
851 /// Use this when loading a segment's DEK from its header.
852 ///
853 /// # Arguments
854 ///
855 /// * `kek` - The KEK that originally wrapped this DEK
856 /// * `wrapped` - The wrapped DEK from the segment header
857 ///
858 /// # Errors
859 ///
860 /// Returns [`CryptoError::DecryptionError`] if:
861 /// - The wrapped key is corrupted
862 /// - The wrong KEK is used
863 pub fn restore(kek: &KeyEncryptionKey, wrapped: &WrappedKey) -> Result<Self, CryptoError> {
864 let key_bytes = kek.unwrap_dek(wrapped)?;
865
866 // Postcondition: restored key isn't degenerate
867 assert!(
868 key_bytes.iter().any(|&b| b != 0),
869 "restored DEK is all zeros - decryption produced invalid key"
870 );
871
872 Ok(Self(EncryptionKey::from_bytes(&key_bytes)))
873 }
874
875 /// Returns a reference to the underlying encryption key.
876 ///
877 /// Use this with [`encrypt`] and [`decrypt`] to encrypt/decrypt record data.
878 ///
879 /// # Example
880 ///
881 /// ```
882 /// # use kimberlite_crypto::encryption::{InMemoryMasterKey, KeyEncryptionKey, DataEncryptionKey, Nonce, encrypt};
883 /// # let master = InMemoryMasterKey::generate();
884 /// # let (kek, _) = KeyEncryptionKey::generate_and_wrap(&master);
885 /// # let (dek, _) = DataEncryptionKey::generate_and_wrap(&kek);
886 /// let nonce = Nonce::from_position(42);
887 /// let ciphertext = encrypt(dek.encryption_key(), &nonce, b"data");
888 /// ```
889 pub fn encryption_key(&self) -> &EncryptionKey {
890 &self.0
891 }
892
893 // ========================================================================
894 // Imperative Shell (IO boundary)
895 // ========================================================================
896
897 /// Generates a new DEK and wraps it with the KEK.
898 ///
899 /// Returns both the usable DEK and its wrapped form for storage.
900 /// The wrapped form should be stored in the segment header.
901 ///
902 /// This is the imperative shell - it handles IO (randomness) and delegates
903 /// to a pure internal constructor for the actual construction.
904 ///
905 /// # Arguments
906 ///
907 /// * `kek` - The Key Encryption Key to wrap this DEK
908 ///
909 /// # Returns
910 ///
911 /// A tuple of `(usable_dek, wrapped_dek_for_storage)`.
912 ///
913 /// # Panics
914 ///
915 /// Panics if the OS CSPRNG fails (catastrophic system error).
916 pub fn generate_and_wrap(kek: &KeyEncryptionKey) -> (Self, WrappedKey) {
917 let random_bytes: [u8; KEY_LENGTH] = generate_random();
918 Self::from_random_bytes_and_wrap(random_bytes, kek)
919 }
920}
921
922// ============================================================================
923// Encrypt / Decrypt
924// ============================================================================
925
926/// Maximum plaintext size for encryption (64 MiB).
927///
928/// This is a sanity limit to catch accidental misuse. AES-GCM can encrypt
929/// larger messages, but individual records should never approach this size.
930#[allow(dead_code)]
931const MAX_PLAINTEXT_LENGTH: usize = 64 * 1024 * 1024;
932
933/// Encrypts plaintext using AES-256-GCM.
934///
935/// Returns a [`Ciphertext`] containing the encrypted data with a 16-byte
936/// authentication tag appended. The ciphertext length is `plaintext.len() + 16`.
937///
938/// # Arguments
939///
940/// * `key` - The encryption key
941/// * `nonce` - A unique nonce derived from log position (never reuse with the same key!)
942/// * `plaintext` - The data to encrypt
943///
944/// # Returns
945///
946/// A [`Ciphertext`] containing the encrypted data and authentication tag.
947///
948/// # Panics
949///
950/// Debug builds panic if `plaintext` exceeds 64 MiB (sanity limit).
951pub fn encrypt(key: &EncryptionKey, nonce: &Nonce, plaintext: &[u8]) -> Ciphertext {
952 // Precondition: plaintext length is reasonable
953 assert!(
954 plaintext.len() <= MAX_PLAINTEXT_LENGTH,
955 "plaintext exceeds {} byte sanity limit, got {} bytes",
956 MAX_PLAINTEXT_LENGTH,
957 plaintext.len()
958 );
959
960 let cipher = Aes256Gcm::new_from_slice(&key.0).expect("KEY_LENGTH is always valid");
961 let nonce_array = nonce.0.into();
962
963 let data = cipher
964 .encrypt(&nonce_array, plaintext)
965 .expect("AES-GCM encryption cannot fail with valid inputs");
966
967 // Postcondition: ciphertext is plaintext + tag
968 assert_eq!(
969 data.len(),
970 plaintext.len() + TAG_LENGTH,
971 "ciphertext length mismatch: expected {}, got {}",
972 plaintext.len() + TAG_LENGTH,
973 data.len()
974 );
975
976 Ciphertext(data)
977}
978
979/// Decrypts ciphertext using AES-256-GCM.
980///
981/// Verifies the authentication tag and returns the original plaintext if valid.
982///
983/// # Arguments
984///
985/// * `key` - The encryption key (must match the key used for encryption)
986/// * `nonce` - The nonce used during encryption (same log position)
987/// * `ciphertext` - The encrypted data with authentication tag
988///
989/// # Errors
990///
991/// Returns [`CryptoError::DecryptionError`] if:
992/// - The key is incorrect
993/// - The nonce is incorrect
994/// - The ciphertext has been tampered with
995/// - The authentication tag is invalid
996///
997/// # Panics
998///
999/// Debug builds panic if `ciphertext` is shorter than [`TAG_LENGTH`] bytes.
1000pub fn decrypt(
1001 key: &EncryptionKey,
1002 nonce: &Nonce,
1003 ciphertext: &Ciphertext,
1004) -> Result<Vec<u8>, CryptoError> {
1005 // Precondition: ciphertext has at least the auth tag
1006 let ciphertext_len = ciphertext.0.len();
1007 assert!(
1008 ciphertext_len >= TAG_LENGTH,
1009 "ciphertext too short: {ciphertext_len} bytes, need at least {TAG_LENGTH}"
1010 );
1011
1012 let cipher = Aes256Gcm::new_from_slice(&key.0).expect("KEY_LENGTH is always valid");
1013 let nonce_array = nonce.0.into();
1014
1015 let plaintext = cipher
1016 .decrypt(&nonce_array, ciphertext.0.as_slice())
1017 .map_err(|_| CryptoError::DecryptionError)?;
1018
1019 // Postcondition: plaintext is ciphertext minus tag
1020 assert_eq!(
1021 plaintext.len(),
1022 ciphertext.0.len() - TAG_LENGTH,
1023 "plaintext length mismatch: expected {}, got {}",
1024 ciphertext.0.len() - TAG_LENGTH,
1025 plaintext.len()
1026 );
1027
1028 Ok(plaintext)
1029}
1030
1031// ============================================================================
1032// Internal Helpers
1033// ============================================================================
1034
1035/// Fills a buffer with cryptographically secure random bytes.
1036///
1037/// # Panics
1038///
1039/// Panics if the OS CSPRNG fails. This indicates a catastrophic system error
1040/// (e.g., /dev/urandom unavailable) and cannot be meaningfully recovered from.
1041fn generate_random<const N: usize>() -> [u8; N] {
1042 let mut bytes = [0u8; N];
1043 getrandom::fill(&mut bytes).expect("CSPRNG failure");
1044 bytes
1045}
1046
1047// ============================================================================
1048// Tests
1049// ============================================================================
1050
1051#[cfg(test)]
1052mod tests {
1053 use super::*;
1054
1055 #[test]
1056 fn encrypt_decrypt_roundtrip() {
1057 let key = EncryptionKey::generate();
1058 let nonce = Nonce::from_position(42);
1059 let plaintext = b"sensitive tenant data";
1060
1061 let ciphertext = encrypt(&key, &nonce, plaintext);
1062 let decrypted = decrypt(&key, &nonce, &ciphertext).unwrap();
1063
1064 assert_eq!(decrypted, plaintext);
1065 }
1066
1067 #[test]
1068 fn encrypt_decrypt_empty_plaintext() {
1069 let key = EncryptionKey::generate();
1070 let nonce = Nonce::from_position(0);
1071 let plaintext = b"";
1072
1073 let ciphertext = encrypt(&key, &nonce, plaintext);
1074 let decrypted = decrypt(&key, &nonce, &ciphertext).unwrap();
1075
1076 assert_eq!(decrypted, plaintext);
1077 assert_eq!(ciphertext.len(), TAG_LENGTH); // Just the tag
1078 }
1079
1080 #[test]
1081 fn ciphertext_length_is_plaintext_plus_tag() {
1082 let key = EncryptionKey::generate();
1083 let nonce = Nonce::from_position(100);
1084 let plaintext = b"hello world";
1085
1086 let ciphertext = encrypt(&key, &nonce, plaintext);
1087
1088 assert_eq!(ciphertext.len(), plaintext.len() + TAG_LENGTH);
1089 }
1090
1091 #[test]
1092 fn wrong_key_fails_decryption() {
1093 let key1 = EncryptionKey::generate();
1094 let key2 = EncryptionKey::generate();
1095 let nonce = Nonce::from_position(42);
1096 let plaintext = b"secret message";
1097
1098 let ciphertext = encrypt(&key1, &nonce, plaintext);
1099 let result = decrypt(&key2, &nonce, &ciphertext);
1100
1101 assert!(result.is_err());
1102 }
1103
1104 #[test]
1105 fn wrong_nonce_fails_decryption() {
1106 let key = EncryptionKey::generate();
1107 let nonce1 = Nonce::from_position(42);
1108 let nonce2 = Nonce::from_position(43);
1109 let plaintext = b"secret message";
1110
1111 let ciphertext = encrypt(&key, &nonce1, plaintext);
1112 let result = decrypt(&key, &nonce2, &ciphertext);
1113
1114 assert!(result.is_err());
1115 }
1116
1117 #[test]
1118 fn tampered_ciphertext_fails_decryption() {
1119 let key = EncryptionKey::generate();
1120 let nonce = Nonce::from_position(42);
1121 let plaintext = b"secret message";
1122
1123 let ciphertext = encrypt(&key, &nonce, plaintext);
1124
1125 // Tamper with the ciphertext
1126 let mut tampered_bytes = ciphertext.to_bytes().to_vec();
1127 tampered_bytes[0] ^= 0x01; // Flip a bit
1128 let tampered = Ciphertext::from_bytes(tampered_bytes);
1129
1130 let result = decrypt(&key, &nonce, &tampered);
1131
1132 assert!(result.is_err());
1133 }
1134
1135 #[test]
1136 fn tampered_tag_fails_decryption() {
1137 let key = EncryptionKey::generate();
1138 let nonce = Nonce::from_position(42);
1139 let plaintext = b"secret message";
1140
1141 let ciphertext = encrypt(&key, &nonce, plaintext);
1142
1143 // Tamper with the auth tag (last 16 bytes)
1144 let mut tampered_bytes = ciphertext.to_bytes().to_vec();
1145 let len = tampered_bytes.len();
1146 tampered_bytes[len - 1] ^= 0x01; // Flip a bit in the tag
1147 let tampered = Ciphertext::from_bytes(tampered_bytes);
1148
1149 let result = decrypt(&key, &nonce, &tampered);
1150
1151 assert!(result.is_err());
1152 }
1153
1154 #[test]
1155 fn nonce_from_position_layout() {
1156 let nonce = Nonce::from_position(0x0102_0304_0506_0708);
1157 let bytes = nonce.to_bytes();
1158
1159 // Little-endian: least significant byte first
1160 assert_eq!(bytes[0], 0x08);
1161 assert_eq!(bytes[1], 0x07);
1162 assert_eq!(bytes[2], 0x06);
1163 assert_eq!(bytes[3], 0x05);
1164 assert_eq!(bytes[4], 0x04);
1165 assert_eq!(bytes[5], 0x03);
1166 assert_eq!(bytes[6], 0x02);
1167 assert_eq!(bytes[7], 0x01);
1168
1169 // Reserved bytes are zero
1170 assert_eq!(bytes[8], 0x00);
1171 assert_eq!(bytes[9], 0x00);
1172 assert_eq!(bytes[10], 0x00);
1173 assert_eq!(bytes[11], 0x00);
1174 }
1175
1176 #[test]
1177 fn nonce_position_zero_is_valid() {
1178 let key = EncryptionKey::generate();
1179 let nonce = Nonce::from_position(0);
1180 let plaintext = b"first record";
1181
1182 let ciphertext = encrypt(&key, &nonce, plaintext);
1183 let decrypted = decrypt(&key, &nonce, &ciphertext).unwrap();
1184
1185 assert_eq!(decrypted, plaintext);
1186 }
1187
1188 #[test]
1189 fn encryption_key_roundtrip() {
1190 let original = EncryptionKey::generate();
1191 let bytes = original.to_bytes();
1192 let restored = EncryptionKey::from_bytes(&bytes);
1193
1194 // Same key should produce same ciphertext
1195 let nonce = Nonce::from_position(1);
1196 let plaintext = b"test";
1197
1198 let ct1 = encrypt(&original, &nonce, plaintext);
1199 let ct2 = encrypt(&restored, &nonce, plaintext);
1200
1201 assert_eq!(ct1.to_bytes(), ct2.to_bytes());
1202 }
1203
1204 #[test]
1205 fn ciphertext_roundtrip() {
1206 let key = EncryptionKey::generate();
1207 let nonce = Nonce::from_position(999);
1208 let plaintext = b"data to serialize";
1209
1210 let ciphertext = encrypt(&key, &nonce, plaintext);
1211
1212 // Simulate serialization/deserialization
1213 let bytes = ciphertext.to_bytes().to_vec();
1214 let restored = Ciphertext::from_bytes(bytes);
1215
1216 let decrypted = decrypt(&key, &nonce, &restored).unwrap();
1217 assert_eq!(decrypted, plaintext);
1218 }
1219
1220 #[test]
1221 fn different_positions_produce_different_ciphertexts() {
1222 let key = EncryptionKey::generate();
1223 let plaintext = b"same plaintext";
1224
1225 let ct1 = encrypt(&key, &Nonce::from_position(1), plaintext);
1226 let ct2 = encrypt(&key, &Nonce::from_position(2), plaintext);
1227
1228 // Same plaintext, different nonces = different ciphertexts
1229 assert_ne!(ct1.to_bytes(), ct2.to_bytes());
1230 }
1231
1232 #[test]
1233 fn encryption_is_deterministic() {
1234 let key = EncryptionKey::generate();
1235 let nonce = Nonce::from_position(42);
1236 let plaintext = b"deterministic test";
1237
1238 let ct1 = encrypt(&key, &nonce, plaintext);
1239 let ct2 = encrypt(&key, &nonce, plaintext);
1240
1241 // Same key + nonce + plaintext = same ciphertext
1242 assert_eq!(ct1.to_bytes(), ct2.to_bytes());
1243 }
1244
1245 // ========================================================================
1246 // WrappedKey Tests
1247 // ========================================================================
1248
1249 #[test]
1250 fn wrap_unwrap_roundtrip() {
1251 let wrapping_key = EncryptionKey::generate();
1252 let original_key: [u8; KEY_LENGTH] = generate_random();
1253
1254 let wrapped = WrappedKey::new(&wrapping_key, &original_key);
1255 let unwrapped = wrapped.unwrap_key(&wrapping_key).unwrap();
1256
1257 assert_eq!(original_key, unwrapped);
1258 }
1259
1260 #[test]
1261 fn wrapped_key_serialization_roundtrip() {
1262 let wrapping_key = EncryptionKey::generate();
1263 let original_key: [u8; KEY_LENGTH] = generate_random();
1264
1265 let wrapped = WrappedKey::new(&wrapping_key, &original_key);
1266 let bytes = wrapped.to_bytes();
1267
1268 // Simulate storing to disk and loading back
1269 let restored = WrappedKey::from_bytes(&bytes);
1270 let unwrapped = restored.unwrap_key(&wrapping_key).unwrap();
1271
1272 assert_eq!(original_key, unwrapped);
1273 }
1274
1275 #[test]
1276 fn wrapped_key_has_correct_length() {
1277 let wrapping_key = EncryptionKey::generate();
1278 let key_to_wrap: [u8; KEY_LENGTH] = generate_random();
1279
1280 let wrapped = WrappedKey::new(&wrapping_key, &key_to_wrap);
1281 let bytes = wrapped.to_bytes();
1282
1283 assert_eq!(bytes.len(), WRAPPED_KEY_LENGTH);
1284 assert_eq!(bytes.len(), NONCE_LENGTH + KEY_LENGTH + TAG_LENGTH);
1285 }
1286
1287 #[test]
1288 fn wrong_wrapping_key_fails_unwrap() {
1289 let key1 = EncryptionKey::generate();
1290 let key2 = EncryptionKey::generate();
1291 let original: [u8; KEY_LENGTH] = generate_random();
1292
1293 let wrapped = WrappedKey::new(&key1, &original);
1294 let result = wrapped.unwrap_key(&key2);
1295
1296 assert!(result.is_err());
1297 }
1298
1299 #[test]
1300 fn tampered_wrapped_key_fails_unwrap() {
1301 let wrapping_key = EncryptionKey::generate();
1302 let original: [u8; KEY_LENGTH] = generate_random();
1303
1304 let wrapped = WrappedKey::new(&wrapping_key, &original);
1305 let mut bytes = wrapped.to_bytes();
1306
1307 // Tamper with the ciphertext portion
1308 bytes[NONCE_LENGTH] ^= 0x01;
1309
1310 let tampered = WrappedKey::from_bytes(&bytes);
1311 let result = tampered.unwrap_key(&wrapping_key);
1312
1313 assert!(result.is_err());
1314 }
1315
1316 #[test]
1317 fn different_keys_produce_different_wrapped_output() {
1318 let wrapping_key = EncryptionKey::generate();
1319 let key1: [u8; KEY_LENGTH] = generate_random();
1320 let key2: [u8; KEY_LENGTH] = generate_random();
1321
1322 let wrapped1 = WrappedKey::new(&wrapping_key, &key1);
1323 let wrapped2 = WrappedKey::new(&wrapping_key, &key2);
1324
1325 // Different plaintext keys = different ciphertexts
1326 assert_ne!(wrapped1.to_bytes(), wrapped2.to_bytes());
1327 }
1328
1329 #[test]
1330 fn same_key_wrapped_twice_differs_due_to_random_nonce() {
1331 let wrapping_key = EncryptionKey::generate();
1332 let key_to_wrap: [u8; KEY_LENGTH] = generate_random();
1333
1334 let wrapped1 = WrappedKey::new(&wrapping_key, &key_to_wrap);
1335 let wrapped2 = WrappedKey::new(&wrapping_key, &key_to_wrap);
1336
1337 // Same key wrapped twice uses different random nonces
1338 assert_ne!(wrapped1.to_bytes(), wrapped2.to_bytes());
1339
1340 // But both unwrap to the same key
1341 let unwrapped1 = wrapped1.unwrap_key(&wrapping_key).unwrap();
1342 let unwrapped2 = wrapped2.unwrap_key(&wrapping_key).unwrap();
1343 assert_eq!(unwrapped1, unwrapped2);
1344 assert_eq!(unwrapped1, key_to_wrap);
1345 }
1346
1347 // ========================================================================
1348 // Key Hierarchy Tests
1349 // ========================================================================
1350
1351 #[test]
1352 fn master_key_generate_and_restore() {
1353 let master = InMemoryMasterKey::generate();
1354 let bytes = master.to_bytes();
1355
1356 let restored = InMemoryMasterKey::from_bytes(&bytes);
1357
1358 // Both should wrap the same KEK identically (modulo random nonce)
1359 let kek_bytes: [u8; KEY_LENGTH] = generate_random();
1360 let wrapped1 = master.wrap_kek(&kek_bytes);
1361 let wrapped2 = restored.wrap_kek(&kek_bytes);
1362
1363 // Different nonces, but both unwrap to same key
1364 let unwrapped1 = master.unwrap_kek(&wrapped1).unwrap();
1365 let unwrapped2 = restored.unwrap_kek(&wrapped2).unwrap();
1366
1367 assert_eq!(unwrapped1, kek_bytes);
1368 assert_eq!(unwrapped2, kek_bytes);
1369 }
1370
1371 #[test]
1372 fn kek_generate_and_restore() {
1373 let master = InMemoryMasterKey::generate();
1374
1375 // Generate a new KEK
1376 let (kek, wrapped_kek) = KeyEncryptionKey::generate_and_wrap(&master);
1377
1378 // Restore it from the wrapped form
1379 let restored_kek = KeyEncryptionKey::restore(&master, &wrapped_kek).unwrap();
1380
1381 // Both should wrap the same DEK bytes
1382 let dek_bytes: [u8; KEY_LENGTH] = generate_random();
1383 let wrapped1 = kek.wrap_dek(&dek_bytes);
1384 let wrapped2 = restored_kek.wrap_dek(&dek_bytes);
1385
1386 // Verify both can unwrap each other's wrapped keys
1387 let unwrapped1 = kek.unwrap_dek(&wrapped2).unwrap();
1388 let unwrapped2 = restored_kek.unwrap_dek(&wrapped1).unwrap();
1389
1390 assert_eq!(unwrapped1, dek_bytes);
1391 assert_eq!(unwrapped2, dek_bytes);
1392 }
1393
1394 #[test]
1395 fn dek_generate_and_restore() {
1396 let master = InMemoryMasterKey::generate();
1397 let (kek, _) = KeyEncryptionKey::generate_and_wrap(&master);
1398
1399 // Generate a new DEK
1400 let (dek, wrapped_dek) = DataEncryptionKey::generate_and_wrap(&kek);
1401
1402 // Restore it from the wrapped form
1403 let restored_dek = DataEncryptionKey::restore(&kek, &wrapped_dek).unwrap();
1404
1405 // Both should encrypt/decrypt identically
1406 let nonce = Nonce::from_position(42);
1407 let plaintext = b"secret tenant data";
1408
1409 let ciphertext = encrypt(dek.encryption_key(), &nonce, plaintext);
1410 let decrypted = decrypt(restored_dek.encryption_key(), &nonce, &ciphertext).unwrap();
1411
1412 assert_eq!(decrypted, plaintext);
1413 }
1414
1415 #[test]
1416 fn full_key_hierarchy_roundtrip() {
1417 // Master key (root of trust)
1418 let master = InMemoryMasterKey::generate();
1419
1420 // KEK for tenant "acme"
1421 let (kek_acme, wrapped_kek_acme) = KeyEncryptionKey::generate_and_wrap(&master);
1422
1423 // DEK for segment 0 of tenant "acme"
1424 let (dek_seg0, wrapped_dek_seg0) = DataEncryptionKey::generate_and_wrap(&kek_acme);
1425
1426 // Encrypt some data
1427 let nonce = Nonce::from_position(0);
1428 let plaintext = b"acme's sensitive record";
1429 let ciphertext = encrypt(dek_seg0.encryption_key(), &nonce, plaintext);
1430
1431 // --- Simulate restart: reload everything from wrapped forms ---
1432
1433 // Restore KEK from wrapped form
1434 let kek = KeyEncryptionKey::restore(&master, &wrapped_kek_acme).unwrap();
1435
1436 // Restore DEK from wrapped form
1437 let dek = DataEncryptionKey::restore(&kek, &wrapped_dek_seg0).unwrap();
1438
1439 // Decrypt the data
1440 let decrypted = decrypt(dek.encryption_key(), &nonce, &ciphertext).unwrap();
1441
1442 assert_eq!(decrypted, plaintext);
1443 }
1444
1445 #[test]
1446 fn wrong_master_key_fails_kek_restore() {
1447 let master1 = InMemoryMasterKey::generate();
1448 let master2 = InMemoryMasterKey::generate();
1449
1450 let (_, wrapped_kek) = KeyEncryptionKey::generate_and_wrap(&master1);
1451
1452 // Try to restore with wrong master key
1453 let result = KeyEncryptionKey::restore(&master2, &wrapped_kek);
1454
1455 assert!(result.is_err());
1456 }
1457
1458 #[test]
1459 fn wrong_kek_fails_dek_restore() {
1460 let master = InMemoryMasterKey::generate();
1461 let (kek1, _) = KeyEncryptionKey::generate_and_wrap(&master);
1462 let (kek2, _) = KeyEncryptionKey::generate_and_wrap(&master);
1463
1464 let (_, wrapped_dek) = DataEncryptionKey::generate_and_wrap(&kek1);
1465
1466 // Try to restore with wrong KEK
1467 let result = DataEncryptionKey::restore(&kek2, &wrapped_dek);
1468
1469 assert!(result.is_err());
1470 }
1471
1472 #[test]
1473 fn tenant_isolation_via_kek() {
1474 let master = InMemoryMasterKey::generate();
1475
1476 // Two tenants with different KEKs
1477 let (kek_tenant_a, _) = KeyEncryptionKey::generate_and_wrap(&master);
1478 let (kek_tenant_b, _) = KeyEncryptionKey::generate_and_wrap(&master);
1479
1480 // Tenant A encrypts data
1481 let (dek_a, wrapped_dek_a) = DataEncryptionKey::generate_and_wrap(&kek_tenant_a);
1482 let nonce = Nonce::from_position(0);
1483 let _ciphertext_a = encrypt(dek_a.encryption_key(), &nonce, b"tenant A secret");
1484
1485 // Tenant B cannot restore tenant A's DEK
1486 let result = DataEncryptionKey::restore(&kek_tenant_b, &wrapped_dek_a);
1487 assert!(result.is_err());
1488
1489 // Even if somehow they got the wrapped DEK bytes, they can't decrypt
1490 // (This is guaranteed by the above test, but demonstrates the isolation)
1491 }
1492
1493 #[test]
1494 fn wrapped_kek_serialization_roundtrip() {
1495 let master = InMemoryMasterKey::generate();
1496 let (_, wrapped_kek) = KeyEncryptionKey::generate_and_wrap(&master);
1497
1498 // Serialize to bytes (for storage)
1499 let bytes = wrapped_kek.to_bytes();
1500
1501 // Deserialize back
1502 let restored_wrapped = WrappedKey::from_bytes(&bytes);
1503
1504 // Should still be unwrappable
1505 let kek = KeyEncryptionKey::restore(&master, &restored_wrapped).unwrap();
1506
1507 // And should work for wrapping DEKs
1508 let dek_bytes: [u8; KEY_LENGTH] = generate_random();
1509 let dek_wrapped = kek.wrap_dek(&dek_bytes);
1510 let unwrapped = kek.unwrap_dek(&dek_wrapped).unwrap();
1511
1512 assert_eq!(unwrapped, dek_bytes);
1513 }
1514
1515 #[test]
1516 fn dek_encryption_key_reference() {
1517 let master = InMemoryMasterKey::generate();
1518 let (kek, _) = KeyEncryptionKey::generate_and_wrap(&master);
1519 let (dek, _) = DataEncryptionKey::generate_and_wrap(&kek);
1520
1521 // Get reference to inner key
1522 let key_ref = dek.encryption_key();
1523
1524 // Use it for encryption
1525 let nonce = Nonce::from_position(1);
1526 let ciphertext = encrypt(key_ref, &nonce, b"test");
1527
1528 // And decryption
1529 let plaintext = decrypt(key_ref, &nonce, &ciphertext).unwrap();
1530
1531 assert_eq!(plaintext, b"test");
1532 }
1533
1534 // ========================================================================
1535 // Property-Based Tests (for comprehensive coverage)
1536 // ========================================================================
1537
1538 use proptest::prelude::*;
1539
1540 proptest! {
1541 /// Property: encrypt + decrypt is the identity for any plaintext
1542 #[test]
1543 fn prop_encrypt_decrypt_roundtrip(plaintext in prop::collection::vec(any::<u8>(), 0..10000)) {
1544 let key = EncryptionKey::generate();
1545 let nonce = Nonce::from_position(42);
1546
1547 let ciphertext = encrypt(&key, &nonce, &plaintext);
1548 let decrypted = decrypt(&key, &nonce, &ciphertext)
1549 .expect("decryption should succeed for valid ciphertext");
1550
1551 prop_assert_eq!(decrypted, plaintext);
1552 }
1553
1554 /// Property: different nonces produce different ciphertexts for same plaintext
1555 #[test]
1556 fn prop_different_nonces_different_ciphertext(
1557 positions in (any::<u64>(), any::<u64>()).prop_filter("positions must differ", |(p1, p2)| p1 != p2),
1558 plaintext in prop::collection::vec(any::<u8>(), 1..1000),
1559 ) {
1560 let (position1, position2) = positions;
1561 let key = EncryptionKey::generate();
1562 let nonce1 = Nonce::from_position(position1);
1563 let nonce2 = Nonce::from_position(position2);
1564
1565 let ct1 = encrypt(&key, &nonce1, &plaintext);
1566 let ct2 = encrypt(&key, &nonce2, &plaintext);
1567
1568 // Different nonces must produce different ciphertexts
1569 prop_assert_ne!(ct1.to_bytes(), ct2.to_bytes());
1570 }
1571
1572 /// Property: ciphertext is always plaintext length + TAG_LENGTH
1573 #[test]
1574 fn prop_ciphertext_length(plaintext in prop::collection::vec(any::<u8>(), 0..10000)) {
1575 let key = EncryptionKey::generate();
1576 let nonce = Nonce::from_position(100);
1577
1578 let ciphertext = encrypt(&key, &nonce, &plaintext);
1579
1580 prop_assert_eq!(ciphertext.to_bytes().len(), plaintext.len() + TAG_LENGTH);
1581 }
1582
1583 /// Property: decryption with wrong key always fails
1584 #[test]
1585 fn prop_wrong_key_fails(plaintext in prop::collection::vec(any::<u8>(), 1..1000)) {
1586 let key1 = EncryptionKey::generate();
1587 let key2 = EncryptionKey::generate();
1588 let nonce = Nonce::from_position(42);
1589
1590 let ciphertext = encrypt(&key1, &nonce, &plaintext);
1591 let result = decrypt(&key2, &nonce, &ciphertext);
1592
1593 prop_assert!(result.is_err(), "decryption with wrong key must fail");
1594 }
1595
1596 /// Property: decryption with wrong nonce always fails
1597 #[test]
1598 fn prop_wrong_nonce_fails(
1599 positions in (any::<u64>(), any::<u64>()).prop_filter("positions must differ", |(p1, p2)| p1 != p2),
1600 plaintext in prop::collection::vec(any::<u8>(), 1..1000),
1601 ) {
1602 let (position1, position2) = positions;
1603 let key = EncryptionKey::generate();
1604 let nonce1 = Nonce::from_position(position1);
1605 let nonce2 = Nonce::from_position(position2);
1606
1607 let ciphertext = encrypt(&key, &nonce1, &plaintext);
1608 let result = decrypt(&key, &nonce2, &ciphertext);
1609
1610 prop_assert!(result.is_err(), "decryption with wrong nonce must fail");
1611 }
1612
1613 /// Property: any bit flip in ciphertext causes decryption failure
1614 #[test]
1615 fn prop_tampered_ciphertext_fails(
1616 plaintext in prop::collection::vec(any::<u8>(), 1..1000),
1617 bit_position in 0usize..8000, // Up to 1000 bytes * 8 bits
1618 ) {
1619 let key = EncryptionKey::generate();
1620 let nonce = Nonce::from_position(42);
1621
1622 let ciphertext = encrypt(&key, &nonce, &plaintext);
1623 let ct_bytes = ciphertext.to_bytes();
1624
1625 // Only tamper if bit_position is within range
1626 if bit_position / 8 < ct_bytes.len() {
1627 let mut tampered = ct_bytes.to_vec();
1628 let byte_idx = bit_position / 8;
1629 let bit_idx = bit_position % 8;
1630 tampered[byte_idx] ^= 1 << bit_idx;
1631
1632 let tampered_ct = Ciphertext::from_bytes(tampered);
1633 let result = decrypt(&key, &nonce, &tampered_ct);
1634
1635 prop_assert!(result.is_err(), "tampered ciphertext must fail authentication");
1636 }
1637 }
1638
1639 /// Property: wrapped key unwraps to original key
1640 #[test]
1641 fn prop_wrap_unwrap_roundtrip(key_bytes in prop::array::uniform32(any::<u8>())) {
1642 let wrapping_key = EncryptionKey::generate();
1643
1644 let wrapped = WrappedKey::new(&wrapping_key, &key_bytes);
1645 let unwrapped = wrapped.unwrap_key(&wrapping_key)
1646 .expect("unwrapping with correct key should succeed");
1647
1648 prop_assert_eq!(unwrapped, key_bytes);
1649 }
1650
1651 /// Property: encryption is deterministic (same key+nonce+plaintext = same ciphertext)
1652 #[test]
1653 fn prop_encryption_deterministic(
1654 position in any::<u64>(),
1655 plaintext in prop::collection::vec(any::<u8>(), 0..1000),
1656 ) {
1657 let key = EncryptionKey::generate();
1658 let nonce = Nonce::from_position(position);
1659
1660 let ct1 = encrypt(&key, &nonce, &plaintext);
1661 let ct2 = encrypt(&key, &nonce, &plaintext);
1662
1663 prop_assert_eq!(ct1.to_bytes(), ct2.to_bytes());
1664 }
1665
1666 /// Property: key serialization roundtrip preserves functionality
1667 #[test]
1668 fn prop_key_serialization_roundtrip(plaintext in prop::collection::vec(any::<u8>(), 1..1000)) {
1669 let original = EncryptionKey::generate();
1670 let bytes = original.to_bytes();
1671 let restored = EncryptionKey::from_bytes(&bytes);
1672
1673 let nonce = Nonce::from_position(1);
1674 let ct1 = encrypt(&original, &nonce, &plaintext);
1675 let ct2 = encrypt(&restored, &nonce, &plaintext);
1676
1677 // Same key produces same ciphertext
1678 prop_assert_eq!(ct1.to_bytes(), ct2.to_bytes());
1679
1680 // Both can decrypt
1681 let decrypted1 = decrypt(&original, &nonce, &ct1).unwrap();
1682 let decrypted2 = decrypt(&restored, &nonce, &ct2).unwrap();
1683 prop_assert_eq!(&decrypted1[..], &plaintext[..]);
1684 prop_assert_eq!(&decrypted2[..], &plaintext[..]);
1685 }
1686
1687 /// Property: nonce from position is injective (different positions = different nonces)
1688 #[test]
1689 fn prop_nonce_position_injective(
1690 positions in (any::<u64>(), any::<u64>()).prop_filter("positions must differ", |(p1, p2)| p1 != p2),
1691 ) {
1692 let (pos1, pos2) = positions;
1693 let nonce1 = Nonce::from_position(pos1);
1694 let nonce2 = Nonce::from_position(pos2);
1695
1696 prop_assert_ne!(nonce1.to_bytes(), nonce2.to_bytes());
1697 }
1698 }
1699
1700 // ========================================================================
1701 // Additional Edge Case Tests
1702 // ========================================================================
1703
1704 #[test]
1705 fn truncated_ciphertext_fails() {
1706 let key = EncryptionKey::generate();
1707 let nonce = Nonce::from_position(42);
1708 let plaintext = b"hello world";
1709
1710 let ciphertext = encrypt(&key, &nonce, plaintext);
1711 let ct_bytes = ciphertext.to_bytes();
1712
1713 // Truncate to less than TAG_LENGTH
1714 if ct_bytes.len() > TAG_LENGTH {
1715 let truncated = Ciphertext::from_bytes(ct_bytes[..TAG_LENGTH].to_vec());
1716 let result = decrypt(&key, &nonce, &truncated);
1717 assert!(result.is_err(), "truncated ciphertext must fail");
1718 }
1719 }
1720
1721 #[test]
1722 fn corrupted_tag_fails() {
1723 let key = EncryptionKey::generate();
1724 let nonce = Nonce::from_position(42);
1725 let plaintext = b"authenticated encryption test";
1726
1727 let ciphertext = encrypt(&key, &nonce, plaintext);
1728 let mut ct_bytes = ciphertext.to_bytes().to_vec();
1729
1730 // Corrupt the last byte of the tag
1731 let last_idx = ct_bytes.len() - 1;
1732 ct_bytes[last_idx] = ct_bytes[last_idx].wrapping_add(1);
1733
1734 let corrupted = Ciphertext::from_bytes(ct_bytes);
1735 let result = decrypt(&key, &nonce, &corrupted);
1736
1737 assert!(
1738 result.is_err(),
1739 "corrupted tag must cause authentication failure"
1740 );
1741 }
1742
1743 #[test]
1744 fn maximum_position_nonce() {
1745 let key = EncryptionKey::generate();
1746 let max_position = u64::MAX;
1747 let nonce = Nonce::from_position(max_position);
1748 let plaintext = b"test at max position";
1749
1750 let ciphertext = encrypt(&key, &nonce, plaintext);
1751 let decrypted = decrypt(&key, &nonce, &ciphertext).unwrap();
1752
1753 assert_eq!(decrypted, plaintext);
1754 }
1755
1756 #[test]
1757 fn large_plaintext_encryption() {
1758 let key = EncryptionKey::generate();
1759 let nonce = Nonce::from_position(1);
1760 // 1MB plaintext
1761 let plaintext = vec![0xAB; 1_024 * 1_024];
1762
1763 let ciphertext = encrypt(&key, &nonce, &plaintext);
1764 let decrypted = decrypt(&key, &nonce, &ciphertext).unwrap();
1765
1766 assert_eq!(decrypted, plaintext);
1767 assert_eq!(ciphertext.to_bytes().len(), plaintext.len() + TAG_LENGTH);
1768 }
1769
1770 #[test]
1771 fn wrapped_key_wrong_length_from_bytes() {
1772 // Test that from_bytes handles wrong length gracefully
1773 let mut too_short = [0u8; WRAPPED_KEY_LENGTH];
1774 too_short[WRAPPED_KEY_LENGTH - 1] = 0xFF; // Make it non-zero to ensure it's different
1775
1776 let wrapped = WrappedKey::from_bytes(&too_short);
1777 let wrapping_key = EncryptionKey::generate();
1778 let result = wrapped.unwrap_key(&wrapping_key);
1779
1780 // Should fail to unwrap (authentication will fail)
1781 assert!(result.is_err(), "corrupted wrapped key should fail unwrap");
1782 }
1783
1784 #[test]
1785 #[should_panic(expected = "EncryptionKey bytes are all zeros")]
1786 fn encryption_key_all_zeros_panics() {
1787 // Production assertion rejects all-zero keys
1788 let _key = EncryptionKey::from_bytes(&[0u8; KEY_LENGTH]);
1789 }
1790
1791 #[test]
1792 #[should_panic(expected = "EncryptionKey random bytes are all zeros")]
1793 fn encryption_key_from_random_bytes_all_zeros_panics() {
1794 // Production assertion checks for non-degenerate keys
1795 let key_bytes = [0u8; KEY_LENGTH];
1796 let key = EncryptionKey::from_random_bytes(key_bytes);
1797 drop(key);
1798 }
1799
1800 #[test]
1801 fn ciphertext_serialization_preserves_authentication() {
1802 let key = EncryptionKey::generate();
1803 let nonce = Nonce::from_position(123);
1804 let plaintext = b"serialization test";
1805
1806 let ciphertext = encrypt(&key, &nonce, plaintext);
1807
1808 // Serialize and deserialize
1809 let bytes = ciphertext.to_bytes().to_vec();
1810 let restored = Ciphertext::from_bytes(bytes);
1811
1812 // Should still authenticate correctly
1813 let decrypted = decrypt(&key, &nonce, &restored).unwrap();
1814 assert_eq!(decrypted, plaintext);
1815
1816 // Tamper after deserialization
1817 let mut tampered_bytes = restored.to_bytes().to_vec();
1818 tampered_bytes[0] ^= 0x01;
1819 let tampered = Ciphertext::from_bytes(tampered_bytes);
1820
1821 let result = decrypt(&key, &nonce, &tampered);
1822 assert!(
1823 result.is_err(),
1824 "tampered deserialized ciphertext must fail"
1825 );
1826 }
1827
1828 #[test]
1829 fn multiple_wrapped_keys_independent() {
1830 let wrapping_key = EncryptionKey::generate();
1831 let key1: [u8; KEY_LENGTH] = generate_random();
1832 let key2: [u8; KEY_LENGTH] = generate_random();
1833
1834 let wrapped1 = WrappedKey::new(&wrapping_key, &key1);
1835 let wrapped2 = WrappedKey::new(&wrapping_key, &key2);
1836
1837 // Each wrapped key unwraps to its original
1838 assert_eq!(wrapped1.unwrap_key(&wrapping_key).unwrap(), key1);
1839 assert_eq!(wrapped2.unwrap_key(&wrapping_key).unwrap(), key2);
1840
1841 // Wrapped keys are different (due to random nonces)
1842 assert_ne!(wrapped1.to_bytes(), wrapped2.to_bytes());
1843 }
1844
1845 #[test]
1846 fn nonce_reserves_upper_bytes() {
1847 // Verify that upper 4 bytes are always zero (reserved for future use)
1848 for position in [0u64, 1, 42, u64::MAX / 2, u64::MAX] {
1849 let nonce = Nonce::from_position(position);
1850 let bytes = nonce.to_bytes();
1851
1852 // Bytes 8-11 must be zero (reserved)
1853 assert_eq!(bytes[8], 0, "byte 8 must be reserved (zero)");
1854 assert_eq!(bytes[9], 0, "byte 9 must be reserved (zero)");
1855 assert_eq!(bytes[10], 0, "byte 10 must be reserved (zero)");
1856 assert_eq!(bytes[11], 0, "byte 11 must be reserved (zero)");
1857 }
1858 }
1859
1860 #[test]
1861 fn kek_dek_hierarchy_isolation() {
1862 let master = InMemoryMasterKey::generate();
1863
1864 // Create two separate KEK/DEK hierarchies
1865 let (kek1, _) = KeyEncryptionKey::generate_and_wrap(&master);
1866 let (kek2, _) = KeyEncryptionKey::generate_and_wrap(&master);
1867
1868 let (_dek1, wrapped_dek1) = DataEncryptionKey::generate_and_wrap(&kek1);
1869 let (_dek2, wrapped_dek2) = DataEncryptionKey::generate_and_wrap(&kek2);
1870
1871 // kek1 can unwrap dek1 but not dek2
1872 assert!(DataEncryptionKey::restore(&kek1, &wrapped_dek1).is_ok());
1873 assert!(DataEncryptionKey::restore(&kek1, &wrapped_dek2).is_err());
1874
1875 // kek2 can unwrap dek2 but not dek1
1876 assert!(DataEncryptionKey::restore(&kek2, &wrapped_dek2).is_ok());
1877 assert!(DataEncryptionKey::restore(&kek2, &wrapped_dek1).is_err());
1878 }
1879
1880 #[test]
1881 fn encryption_key_zeroize_on_drop() {
1882 // This test verifies that ZeroizeOnDrop is working
1883 // We can't directly observe the memory being zeroed, but we can verify
1884 // the trait is properly applied (compilation would fail otherwise)
1885 let key = EncryptionKey::generate();
1886 let _bytes = key.to_bytes();
1887 // When key goes out of scope, ZeroizeOnDrop should zero the memory
1888 drop(key);
1889 // If ZeroizeOnDrop wasn't working, this would be a security issue
1890 }
1891}