1use crate::{Error, key::SecretKey, Algorithm, Result};
4use rand_core::{CryptoRng, RngCore};
5use aead::{Aead, KeyInit, Payload, generic_array::typenum::U12};
6use chacha20poly1305::{ChaCha20Poly1305, Key as ChaChaKey, Nonce as ChaChaNonce};
7use aes_gcm::Aes256Gcm;
8
9type AesKey = aes_gcm::Key<Aes256Gcm>;
11type AesNonce = aes_gcm::Nonce<U12>;
12
13pub trait SecureRandom: RngCore + CryptoRng {
15 fn fill_secure_bytes(&mut self, dest: &mut [u8]) -> Result<()> {
17 self.try_fill_bytes(dest).map_err(|e| Error::crypto(format!("failed to fill secure bytes: {}", {e})))?;
18 Ok(())
19 }
20}
21
22pub trait KeyGenerator {
24 type Key;
26
27 fn generate<R: SecureRandom>(&self, rng: &mut R) -> Result<Self::Key>;
29
30 fn generate_with_params<R: SecureRandom>(
32 &self, rng: &mut R, params: KeyGenParams,
33 ) -> Result<Self::Key>;
34}
35pub struct SimpleSymmetricKeyGenerator;
37
38impl KeyGenerator for SimpleSymmetricKeyGenerator {
39 type Key = SecretKey;
40
41 fn generate<R: SecureRandom>(&self, rng: &mut R) -> Result<Self::Key> {
42 let algorithm = Algorithm::ChaCha20Poly1305; let key_len = algorithm.key_size();
44 let mut buf = vec![0u8; key_len];
45 rng.fill_secure_bytes(&mut buf)?;
46 SecretKey::from_bytes(buf, algorithm)
47 }
48
49 fn generate_with_params<R: SecureRandom>(
50 &self, rng: &mut R, params: KeyGenParams,
51 ) -> Result<Self::Key> {
52 let algorithm = params.algorithm;
53 let key_len = params.key_size.unwrap_or(algorithm.key_size());
54 let mut buf = vec![0u8; key_len];
55 rng.fill_secure_bytes(&mut buf)?;
56 SecretKey::from_bytes(buf, algorithm)
57 }
58}
59
60#[derive(Debug, Clone)]
62pub struct KeyGenParams {
63 pub algorithm: Algorithm,
65 pub seed: Option<Vec<u8>>,
67 pub key_size: Option<usize>,
69}
70
71pub trait AEAD {
73 const NONCE_SIZE: usize;
75 const TAG_SIZE: usize;
77
78 fn encrypt(
80 &self,
81 key: &SecretKey,
82 nonce: &[u8],
83 plaintext: &[u8],
84 associated_data: &[u8],
85 ) -> Result<Vec<u8>>;
86
87 fn decrypt(
89 &self,
90 key: &SecretKey,
91 nonce: &[u8],
92 ciphertext: &[u8],
93 associated_data: &[u8],
94 ) -> Result<Vec<u8>>;
95}
96
97pub struct RuntimeAead;
99
100impl RuntimeAead {
101 fn check_key_len(key: &SecretKey, expected: usize) -> Result<()> {
102 if key.expose_secret().len() != expected {
103 return Err(Error::crypto(format!(
104 "invalid key size: expected {}, got {}",
105 expected,
106 key.expose_secret().len()
107 )));
108 }
109 Ok(())
110 }
111}
112
113impl crate::crypto::AEAD for RuntimeAead {
114 const NONCE_SIZE: usize = 12;
116 const TAG_SIZE: usize = 16;
117
118 fn encrypt(
119 &self,
120 key: &SecretKey,
121 nonce: &[u8],
122 plaintext: &[u8],
123 associated_data: &[u8],
124 ) -> Result<Vec<u8>> {
125 if nonce.len() != Self::NONCE_SIZE {
126 return Err(Error::crypto(format!("invalid nonce length: expected {}", Self::NONCE_SIZE)));
127 }
128
129 match key.algorithm() {
130 Algorithm::ChaCha20Poly1305 => {
131 Self::check_key_len(key, 32)?;
132 let cipher = ChaCha20Poly1305::new(ChaChaKey::from_slice(key.expose_secret()));
133 let n: &ChaChaNonce = ChaChaNonce::from_slice(nonce);
135 cipher
136 .encrypt(n, Payload { msg: plaintext, aad: associated_data })
137 .map_err(|e| Error::crypto(format!("ChaCha20-Poly1305 encryption failed: {}", {e}))) }
139 Algorithm::Aes256Gcm => {
140 Self::check_key_len(key, 32)?;
141 let cipher = Aes256Gcm::new(AesKey::from_slice(key.expose_secret()));
142 let n: &AesNonce = AesNonce::from_slice(nonce);
143 cipher
144 .encrypt(n, Payload { msg: plaintext, aad: associated_data })
145 .map_err(|e| Error::crypto(format!("AES-256-GCM encryption failed: {}", {e})))
146 }
147 alg => Err(Error::crypto(format!("algorithm {alg:?} not supported for AEAD"))),
148 }
149 }
150
151 fn decrypt(
152 &self,
153 key: &SecretKey,
154 nonce: &[u8],
155 ciphertext: &[u8],
156 associated_data: &[u8],
157 ) -> Result<Vec<u8>> {
158 if nonce.len() != Self::NONCE_SIZE {
159 return Err(Error::crypto(format!("invalid nonce length: expected {}", Self::NONCE_SIZE)));
160 }
161
162 match key.algorithm() {
163 Algorithm::ChaCha20Poly1305 => {
164 Self::check_key_len(key, 32)?;
165 let cipher = ChaCha20Poly1305::new(ChaChaKey::from_slice(key.expose_secret()));
166 let n: &ChaChaNonce = ChaChaNonce::from_slice(nonce);
167 cipher
168 .decrypt(n, Payload { msg: ciphertext, aad: associated_data })
169 .map_err(|e| Error::crypto(format!("ChaCha20-Poly1305 decryption failed: {}", {e})))
170 }
171 Algorithm::Aes256Gcm => {
172 Self::check_key_len(key, 32)?;
173 let cipher = Aes256Gcm::new(AesKey::from_slice(key.expose_secret()));
174 let n: &AesNonce = AesNonce::from_slice(nonce);
175 cipher
176 .decrypt(n, Payload { msg: ciphertext, aad: associated_data })
177 .map_err(|e| Error::crypto(format!("AES-256-GCM decryption failed: {}", {e})))
178 }
179 alg => Err(Error::crypto(format!("algorithm {alg:?} not supported for AEAD"))),
180 }
181 }
182}
183
184#[derive(Debug, Clone, Copy)]
186pub enum NonceStrategy {
187 Random,
189 Counter,
191 Synthetic,
193}
194
195pub trait NonceGenerator {
197 fn generate_nonce(&mut self, message_id: &[u8]) -> Result<Vec<u8>>;
201
202 fn strategy(&self) -> NonceStrategy;
204}
205
206pub struct RandomNonceGenerator<R: SecureRandom> {
208 rng: R,
209 nonce_size: usize,
210}
211
212impl<R: SecureRandom> RandomNonceGenerator<R> {
214 pub fn new(rng: R, nonce_size: usize) -> Self {
216 Self { rng, nonce_size }
217 }
218}
219
220impl<R: SecureRandom> NonceGenerator for RandomNonceGenerator<R> {
221 fn generate_nonce(&mut self, _message_id: &[u8]) -> Result<Vec<u8>> {
222 let mut nonce = vec![0u8; self.nonce_size];
223 self.rng.fill_secure_bytes(&mut nonce)?;
224 Ok(nonce)
225 }
226
227 fn strategy(&self) -> NonceStrategy {
228 NonceStrategy::Random
229 }
230}
231
232impl<T> SecureRandom for T where T: RngCore + CryptoRng {}
234
235pub trait ConstantTime {
237 fn ct_eq(&self, other: &[u8]) -> bool;
239
240 fn ct_select(condition: bool, a: Self, b: Self) -> Self where Self: Sized;
242}
243
244#[cfg(test)]
245mod tests {
246 use rand_chacha::ChaCha12Rng;
247 use rand_core::SeedableRng;
248
249 use super::*;
250
251 #[test]
252 fn test_keygen_params() {
253 let params = KeyGenParams {
254 algorithm: Algorithm::Aes256Gcm,
255 seed: None,
256 key_size: Some(32),
257 };
258 assert_eq!(params.algorithm, Algorithm::Aes256Gcm);
259 assert_eq!(params.key_size, Some(32));
260 assert_eq!(params.seed, None);
261 }
262
263 #[test]
264 fn test_end_to_end_aead() {
265 let mut rng = ChaCha12Rng::seed_from_u64(42);
267 let generator = SimpleSymmetricKeyGenerator;
268 let key = generator.generate(&mut rng).unwrap();
269
270 let aead = RuntimeAead;
272 let mut nonce_gen = RandomNonceGenerator::new(
273 ChaCha12Rng::seed_from_u64(123),
274 RuntimeAead::NONCE_SIZE
275 );
276
277 let plaintext = b"Secret message for testing";
279 let associated_data = b"public metadata";
280
281 let nonce = nonce_gen.generate_nonce(b"msg_001").unwrap ();
283 let ciphertext = aead.encrypt(&key, &nonce, plaintext, associated_data).unwrap();
284
285 assert_ne!(ciphertext.as_slice(), plaintext);
287
288 let decrypted = aead.decrypt(&key, &nonce, &ciphertext, associated_data).unwrap();
290 assert_eq!(decrypted.as_slice(), plaintext);
291 }
292}