1use crate::error::{Error, Result};
8
9pub const SALT_SIZE: usize = 16;
11
12pub const NONCE_SIZE: usize = 12;
14
15pub type PasswordEncryptResult = (u8, [u8; 16], [u8; 12], Vec<u8>);
17
18pub type RecipientEncryptResult = (u8, [u8; 32], [u8; 12], Vec<u8>);
21
22pub const TAG_SIZE: usize = 16;
24
25pub const X25519_PUBLIC_KEY_SIZE: usize = 32;
27
28pub mod mode {
30 pub const PASSWORD: u8 = 0x00;
32 pub const RECIPIENT: u8 = 0x01;
34}
35
36#[derive(Debug, Clone)]
38pub enum EncryptionMode {
39 Password(String),
41 Recipient {
43 recipient_public_key: [u8; 32],
45 },
46}
47
48#[derive(Debug, Clone)]
50pub struct EncryptionParams {
51 pub mode: EncryptionMode,
53}
54
55impl EncryptionParams {
56 #[must_use]
58 pub fn password(password: impl Into<String>) -> Self {
59 Self {
60 mode: EncryptionMode::Password(password.into()),
61 }
62 }
63
64 #[must_use]
66 pub fn recipient(public_key: [u8; 32]) -> Self {
67 Self {
68 mode: EncryptionMode::Recipient {
69 recipient_public_key: public_key,
70 },
71 }
72 }
73}
74
75#[derive(Debug, Clone)]
77pub enum DecryptionParams {
78 Password(String),
80 PrivateKey([u8; 32]),
82}
83
84impl DecryptionParams {
85 #[must_use]
87 pub fn password(password: impl Into<String>) -> Self {
88 Self::Password(password.into())
89 }
90
91 #[must_use]
93 pub fn private_key(key: [u8; 32]) -> Self {
94 Self::PrivateKey(key)
95 }
96}
97
98#[cfg(feature = "format-encryption")]
102pub fn encrypt_password(plaintext: &[u8], password: &str) -> Result<PasswordEncryptResult> {
103 use aes_gcm::{
104 aead::{Aead, KeyInit},
105 Aes256Gcm, Nonce,
106 };
107 use argon2::Argon2;
108
109 let mut salt = [0u8; SALT_SIZE];
111 let mut nonce = [0u8; NONCE_SIZE];
112 getrandom::getrandom(&mut salt).map_err(|e| Error::Format(format!("RNG error: {e}")))?;
113 getrandom::getrandom(&mut nonce).map_err(|e| Error::Format(format!("RNG error: {e}")))?;
114
115 let mut key = [0u8; 32];
117 Argon2::default()
118 .hash_password_into(password.as_bytes(), &salt, &mut key)
119 .map_err(|e| Error::Format(format!("Argon2 error: {e}")))?;
120
121 let cipher = Aes256Gcm::new_from_slice(&key)
123 .map_err(|e| Error::Format(format!("AES init error: {e}")))?;
124 let nonce_obj = Nonce::from_slice(&nonce);
125 let ciphertext = cipher
126 .encrypt(nonce_obj, plaintext)
127 .map_err(|e| Error::Format(format!("Encryption error: {e}")))?;
128
129 Ok((mode::PASSWORD, salt, nonce, ciphertext))
130}
131
132#[cfg(feature = "format-encryption")]
134pub fn decrypt_password(
135 ciphertext: &[u8],
136 password: &str,
137 salt: &[u8; 16],
138 nonce: &[u8; 12],
139) -> Result<Vec<u8>> {
140 use aes_gcm::{
141 aead::{Aead, KeyInit},
142 Aes256Gcm, Nonce,
143 };
144 use argon2::Argon2;
145
146 let mut key = [0u8; 32];
148 Argon2::default()
149 .hash_password_into(password.as_bytes(), salt, &mut key)
150 .map_err(|e| Error::Format(format!("Argon2 error: {e}")))?;
151
152 let cipher = Aes256Gcm::new_from_slice(&key)
154 .map_err(|e| Error::Format(format!("AES init error: {e}")))?;
155 let nonce_obj = Nonce::from_slice(nonce);
156 cipher.decrypt(nonce_obj, ciphertext).map_err(|_| {
157 Error::Format("Decryption failed: wrong password or corrupted data".to_string())
158 })
159}
160
161#[cfg(feature = "format-encryption")]
165pub fn encrypt_recipient(
166 plaintext: &[u8],
167 recipient_public_key: &[u8; 32],
168) -> Result<RecipientEncryptResult> {
169 use aes_gcm::{
170 aead::{Aead, KeyInit},
171 Aes256Gcm, Nonce,
172 };
173 use hkdf::Hkdf;
174 use sha2::Sha256;
175 use x25519_dalek::{EphemeralSecret, PublicKey};
176
177 let mut rng_bytes = [0u8; 32];
179 getrandom::getrandom(&mut rng_bytes).map_err(|e| Error::Format(format!("RNG error: {e}")))?;
180 let ephemeral_secret = EphemeralSecret::random_from_rng(RngWrapper(rng_bytes));
181 let ephemeral_public = PublicKey::from(&ephemeral_secret);
182
183 let recipient_pk = PublicKey::from(*recipient_public_key);
185 let shared_secret = ephemeral_secret.diffie_hellman(&recipient_pk);
186
187 let hkdf = Hkdf::<Sha256>::new(None, shared_secret.as_bytes());
189 let mut key = [0u8; 32];
190 hkdf.expand(b"ald-v1-encrypt", &mut key)
191 .map_err(|e| Error::Format(format!("HKDF error: {e}")))?;
192
193 let mut nonce = [0u8; NONCE_SIZE];
195 getrandom::getrandom(&mut nonce).map_err(|e| Error::Format(format!("RNG error: {e}")))?;
196
197 let cipher = Aes256Gcm::new_from_slice(&key)
199 .map_err(|e| Error::Format(format!("AES init error: {e}")))?;
200 let nonce_obj = Nonce::from_slice(&nonce);
201 let ciphertext = cipher
202 .encrypt(nonce_obj, plaintext)
203 .map_err(|e| Error::Format(format!("Encryption error: {e}")))?;
204
205 Ok((
206 mode::RECIPIENT,
207 ephemeral_public.to_bytes(),
208 nonce,
209 ciphertext,
210 ))
211}
212
213#[cfg(feature = "format-encryption")]
215pub fn decrypt_recipient(
216 ciphertext: &[u8],
217 recipient_private_key: &[u8; 32],
218 ephemeral_public_key: &[u8; 32],
219 nonce: &[u8; 12],
220) -> Result<Vec<u8>> {
221 use aes_gcm::{
222 aead::{Aead, KeyInit},
223 Aes256Gcm, Nonce,
224 };
225 use hkdf::Hkdf;
226 use sha2::Sha256;
227 use x25519_dalek::{PublicKey, StaticSecret};
228
229 let recipient_secret = StaticSecret::from(*recipient_private_key);
231 let ephemeral_pk = PublicKey::from(*ephemeral_public_key);
232 let shared_secret = recipient_secret.diffie_hellman(&ephemeral_pk);
233
234 let hkdf = Hkdf::<Sha256>::new(None, shared_secret.as_bytes());
236 let mut key = [0u8; 32];
237 hkdf.expand(b"ald-v1-encrypt", &mut key)
238 .map_err(|e| Error::Format(format!("HKDF error: {e}")))?;
239
240 let cipher = Aes256Gcm::new_from_slice(&key)
242 .map_err(|e| Error::Format(format!("AES init error: {e}")))?;
243 let nonce_obj = Nonce::from_slice(nonce);
244 cipher
245 .decrypt(nonce_obj, ciphertext)
246 .map_err(|_| Error::Format("Decryption failed: wrong key or corrupted data".to_string()))
247}
248
249#[cfg(feature = "format-encryption")]
251struct RngWrapper([u8; 32]);
252
253#[cfg(feature = "format-encryption")]
254impl rand_core::RngCore for RngWrapper {
255 fn next_u32(&mut self) -> u32 {
256 let mut buf = [0u8; 4];
257 self.fill_bytes(&mut buf);
258 u32::from_le_bytes(buf)
259 }
260
261 fn next_u64(&mut self) -> u64 {
262 let mut buf = [0u8; 8];
263 self.fill_bytes(&mut buf);
264 u64::from_le_bytes(buf)
265 }
266
267 fn fill_bytes(&mut self, dest: &mut [u8]) {
268 dest.copy_from_slice(&self.0[..dest.len()]);
269 }
270
271 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> std::result::Result<(), rand_core::Error> {
272 self.fill_bytes(dest);
273 Ok(())
274 }
275}
276
277#[cfg(feature = "format-encryption")]
278impl rand_core::CryptoRng for RngWrapper {}
279
280#[cfg(all(test, feature = "format-encryption"))]
281mod tests {
282 use super::*;
283
284 #[test]
285 fn test_password_encrypt_decrypt_roundtrip() {
286 let plaintext = b"Hello, World! This is a test message for encryption.";
287 let password = "my_secure_password_123";
288
289 let (mode_byte, salt, nonce, ciphertext) =
290 encrypt_password(plaintext, password).expect("encrypt failed");
291
292 assert_eq!(mode_byte, mode::PASSWORD);
293 assert_ne!(ciphertext, plaintext);
294
295 let decrypted =
296 decrypt_password(&ciphertext, password, &salt, &nonce).expect("decrypt failed");
297
298 assert_eq!(decrypted, plaintext);
299 }
300
301 #[test]
302 fn test_password_wrong_password_fails() {
303 let plaintext = b"Secret data";
304 let password = "correct_password";
305 let wrong_password = "wrong_password";
306
307 let (_, salt, nonce, ciphertext) =
308 encrypt_password(plaintext, password).expect("encrypt failed");
309
310 let result = decrypt_password(&ciphertext, wrong_password, &salt, &nonce);
311 assert!(result.is_err());
312 }
313
314 #[test]
315 fn test_recipient_encrypt_decrypt_roundtrip() {
316 use x25519_dalek::{PublicKey, StaticSecret};
317
318 let plaintext = b"Hello, recipient! This is a secure message.";
319
320 let mut key_bytes = [0u8; 32];
322 getrandom::getrandom(&mut key_bytes).expect("rng failed");
323 let recipient_secret = StaticSecret::from(key_bytes);
324 let recipient_public = PublicKey::from(&recipient_secret);
325
326 let (mode_byte, ephemeral_pub, nonce, ciphertext) =
327 encrypt_recipient(plaintext, recipient_public.as_bytes()).expect("encrypt failed");
328
329 assert_eq!(mode_byte, mode::RECIPIENT);
330 assert_ne!(ciphertext, plaintext);
331
332 let decrypted = decrypt_recipient(&ciphertext, &key_bytes, &ephemeral_pub, &nonce)
333 .expect("decrypt failed");
334
335 assert_eq!(decrypted, plaintext);
336 }
337
338 #[test]
339 fn test_recipient_wrong_key_fails() {
340 use x25519_dalek::{PublicKey, StaticSecret};
341
342 let plaintext = b"Secret for specific recipient";
343
344 let mut key_bytes = [0u8; 32];
346 getrandom::getrandom(&mut key_bytes).expect("rng failed");
347 let recipient_secret = StaticSecret::from(key_bytes);
348 let recipient_public = PublicKey::from(&recipient_secret);
349
350 let (_, ephemeral_pub, nonce, ciphertext) =
351 encrypt_recipient(plaintext, recipient_public.as_bytes()).expect("encrypt failed");
352
353 let mut wrong_key_bytes = [0u8; 32];
355 getrandom::getrandom(&mut wrong_key_bytes).expect("rng failed");
356
357 let result = decrypt_recipient(&ciphertext, &wrong_key_bytes, &ephemeral_pub, &nonce);
358 assert!(result.is_err());
359 }
360
361 #[test]
362 fn test_encryption_produces_different_ciphertexts() {
363 let plaintext = b"Same message";
364 let password = "same_password";
365
366 let (_, _, _, ct1) = encrypt_password(plaintext, password).expect("encrypt 1 failed");
367 let (_, _, _, ct2) = encrypt_password(plaintext, password).expect("encrypt 2 failed");
368
369 assert_ne!(ct1, ct2);
371 }
372
373 #[test]
374 fn test_empty_plaintext_encryption() {
375 let plaintext = b"";
376 let password = "password";
377
378 let (mode_byte, salt, nonce, ciphertext) =
379 encrypt_password(plaintext, password).expect("encrypt failed");
380
381 assert_eq!(mode_byte, mode::PASSWORD);
382 assert!(!ciphertext.is_empty());
384
385 let decrypted =
386 decrypt_password(&ciphertext, password, &salt, &nonce).expect("decrypt failed");
387 assert_eq!(decrypted.as_slice(), plaintext);
388 }
389
390 #[test]
391 fn test_large_plaintext_encryption() {
392 let plaintext: Vec<u8> = (0u32..1_000_000).map(|i| (i % 256) as u8).collect();
394 let password = "large_data_password";
395
396 let (mode_byte, salt, nonce, ciphertext) =
397 encrypt_password(&plaintext, password).expect("encrypt failed");
398
399 assert_eq!(mode_byte, mode::PASSWORD);
400 assert!(ciphertext.len() >= plaintext.len());
401
402 let decrypted =
403 decrypt_password(&ciphertext, password, &salt, &nonce).expect("decrypt failed");
404 assert_eq!(decrypted, plaintext);
405 }
406
407 #[test]
408 fn test_special_characters_in_password() {
409 let plaintext = b"Test data";
410 let password = "p@$$w0rd!#%^&*()_+-=[]{}|;':\",./<>?`~";
411
412 let (_, salt, nonce, ciphertext) =
413 encrypt_password(plaintext, password).expect("encrypt failed");
414
415 let decrypted =
416 decrypt_password(&ciphertext, password, &salt, &nonce).expect("decrypt failed");
417 assert_eq!(decrypted.as_slice(), plaintext);
418 }
419
420 #[test]
421 fn test_unicode_password() {
422 let plaintext = b"Data with unicode password";
423 let password = "密码🔐пароль";
424
425 let (_, salt, nonce, ciphertext) =
426 encrypt_password(plaintext, password).expect("encrypt failed");
427
428 let decrypted =
429 decrypt_password(&ciphertext, password, &salt, &nonce).expect("decrypt failed");
430 assert_eq!(decrypted.as_slice(), plaintext);
431 }
432
433 #[test]
434 fn test_corrupted_ciphertext_fails() {
435 let plaintext = b"Original data";
436 let password = "password";
437
438 let (_, salt, nonce, mut ciphertext) =
439 encrypt_password(plaintext, password).expect("encrypt failed");
440
441 if !ciphertext.is_empty() {
443 ciphertext[0] ^= 0xFF;
444 }
445
446 let result = decrypt_password(&ciphertext, password, &salt, &nonce);
447 assert!(result.is_err());
448 }
449
450 #[test]
451 fn test_corrupted_nonce_fails() {
452 let plaintext = b"Original data";
453 let password = "password";
454
455 let (_, salt, mut nonce, ciphertext) =
456 encrypt_password(plaintext, password).expect("encrypt failed");
457
458 nonce[0] ^= 0xFF;
460
461 let result = decrypt_password(&ciphertext, password, &salt, &nonce);
462 assert!(result.is_err());
463 }
464
465 #[test]
466 fn test_corrupted_salt_fails() {
467 let plaintext = b"Original data";
468 let password = "password";
469
470 let (_, mut salt, nonce, ciphertext) =
471 encrypt_password(plaintext, password).expect("encrypt failed");
472
473 salt[0] ^= 0xFF;
475
476 let result = decrypt_password(&ciphertext, password, &salt, &nonce);
477 assert!(result.is_err());
478 }
479
480 #[test]
481 fn test_rng_wrapper() {
482 use rand_core::RngCore;
483
484 let mut rng = RngWrapper([0x42; 32]);
485
486 let val = rng.next_u32();
488 assert_eq!(val, 0x42424242);
489
490 let val64 = rng.next_u64();
492 assert_eq!(val64, 0x4242424242424242);
493
494 let mut buf = [0u8; 8];
496 rng.fill_bytes(&mut buf);
497 assert_eq!(buf, [0x42; 8]);
498
499 let mut buf2 = [0u8; 4];
501 rng.try_fill_bytes(&mut buf2).expect("should succeed");
502 assert_eq!(buf2, [0x42; 4]);
503 }
504
505 #[test]
506 fn test_mode_constants() {
507 assert_ne!(mode::PASSWORD, mode::RECIPIENT);
509 assert_eq!(mode::PASSWORD, 0x00);
510 assert_eq!(mode::RECIPIENT, 0x01);
511 }
512}