1use argon2::{Algorithm, Argon2, Params, Version};
9use base64::{Engine, engine::general_purpose::STANDARD};
10use ring::aead::{AES_256_GCM, Aad, LessSafeKey, Nonce, UnboundKey};
11use ring::digest::{SHA256, digest};
12use ring::rand::{SecureRandom, SystemRandom};
13
14use crate::error::{PromocryptError, Result};
15
16pub const ARGON2_MEMORY_COST: u32 = 65536;
18
19pub const ARGON2_TIME_COST: u32 = 3;
21
22pub const ARGON2_PARALLELISM: u32 = 1;
24
25pub const SALT_SIZE: usize = 16;
27
28pub const NONCE_SIZE: usize = 12;
30
31pub const TAG_SIZE: usize = 16;
33
34pub const ENCRYPTED_KEY_SIZE: usize = 48;
36
37pub fn derive_key(input: &[u8], salt: &[u8; SALT_SIZE]) -> Result<[u8; 32]> {
46 let params = Params::new(
47 ARGON2_MEMORY_COST,
48 ARGON2_TIME_COST,
49 ARGON2_PARALLELISM,
50 Some(32),
51 )
52 .map_err(|e| PromocryptError::EncryptionFailed(format!("Argon2 params error: {}", e)))?;
53
54 let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
55
56 let mut output = [0u8; 32];
57 argon2
58 .hash_password_into(input, salt, &mut output)
59 .map_err(|e| PromocryptError::EncryptionFailed(format!("Argon2 hash error: {}", e)))?;
60
61 Ok(output)
62}
63
64pub fn generate_random_key() -> Result<[u8; 32]> {
66 let rng = SystemRandom::new();
67 let mut key = [0u8; 32];
68 rng.fill(&mut key).map_err(|_| {
69 PromocryptError::EncryptionFailed("Failed to generate random key".to_string())
70 })?;
71 Ok(key)
72}
73
74pub fn generate_salt() -> Result<[u8; SALT_SIZE]> {
76 let rng = SystemRandom::new();
77 let mut salt = [0u8; SALT_SIZE];
78 rng.fill(&mut salt)
79 .map_err(|_| PromocryptError::EncryptionFailed("Failed to generate salt".to_string()))?;
80 Ok(salt)
81}
82
83pub fn generate_nonce() -> Result<[u8; NONCE_SIZE]> {
85 let rng = SystemRandom::new();
86 let mut nonce = [0u8; NONCE_SIZE];
87 rng.fill(&mut nonce)
88 .map_err(|_| PromocryptError::EncryptionFailed("Failed to generate nonce".to_string()))?;
89 Ok(nonce)
90}
91
92pub fn encrypt(key: &[u8; 32], plaintext: &[u8], nonce: &[u8; NONCE_SIZE]) -> Result<Vec<u8>> {
102 let unbound_key = UnboundKey::new(&AES_256_GCM, key)
103 .map_err(|_| PromocryptError::EncryptionFailed("Invalid key".to_string()))?;
104 let less_safe_key = LessSafeKey::new(unbound_key);
105
106 let nonce = Nonce::assume_unique_for_key(*nonce);
107
108 let mut in_out = plaintext.to_vec();
110 in_out.reserve(TAG_SIZE);
111
112 less_safe_key
113 .seal_in_place_append_tag(nonce, Aad::empty(), &mut in_out)
114 .map_err(|_| PromocryptError::EncryptionFailed("Encryption failed".to_string()))?;
115
116 Ok(in_out)
117}
118
119pub fn decrypt(key: &[u8; 32], ciphertext: &[u8], nonce: &[u8; NONCE_SIZE]) -> Result<Vec<u8>> {
129 if ciphertext.len() < TAG_SIZE {
130 return Err(PromocryptError::DecryptionFailed);
131 }
132
133 let unbound_key =
134 UnboundKey::new(&AES_256_GCM, key).map_err(|_| PromocryptError::DecryptionFailed)?;
135 let less_safe_key = LessSafeKey::new(unbound_key);
136
137 let nonce = Nonce::assume_unique_for_key(*nonce);
138
139 let mut in_out = ciphertext.to_vec();
140 let plaintext = less_safe_key
141 .open_in_place(nonce, Aad::empty(), &mut in_out)
142 .map_err(|_| PromocryptError::DecryptionFailed)?;
143
144 Ok(plaintext.to_vec())
145}
146
147pub fn encrypt_data_key(
160 data_key: &[u8; 32],
161 password_or_machine_id: &[u8],
162 salt: &[u8; SALT_SIZE],
163 nonce: &[u8; NONCE_SIZE],
164) -> Result<[u8; ENCRYPTED_KEY_SIZE]> {
165 let derived_key = derive_key(password_or_machine_id, salt)?;
166 let ciphertext = encrypt(&derived_key, data_key, nonce)?;
167
168 if ciphertext.len() != ENCRYPTED_KEY_SIZE {
169 return Err(PromocryptError::EncryptionFailed(format!(
170 "Unexpected ciphertext length: {}",
171 ciphertext.len()
172 )));
173 }
174
175 let mut result = [0u8; ENCRYPTED_KEY_SIZE];
176 result.copy_from_slice(&ciphertext);
177 Ok(result)
178}
179
180pub fn decrypt_data_key(
191 encrypted: &[u8; ENCRYPTED_KEY_SIZE],
192 password_or_machine_id: &[u8],
193 salt: &[u8; SALT_SIZE],
194 nonce: &[u8; NONCE_SIZE],
195) -> Result<[u8; 32]> {
196 let derived_key = derive_key(password_or_machine_id, salt)?;
197 let plaintext = decrypt(&derived_key, encrypted, nonce)?;
198
199 if plaintext.len() != 32 {
200 return Err(PromocryptError::DecryptionFailed);
201 }
202
203 let mut result = [0u8; 32];
204 result.copy_from_slice(&plaintext);
205 Ok(result)
206}
207
208pub fn encrypt_data(
218 data_key: &[u8; 32],
219 plaintext: &[u8],
220 nonce: &[u8; NONCE_SIZE],
221) -> Result<Vec<u8>> {
222 encrypt(data_key, plaintext, nonce)
223}
224
225pub fn decrypt_data(
235 data_key: &[u8; 32],
236 ciphertext: &[u8],
237 nonce: &[u8; NONCE_SIZE],
238) -> Result<Vec<u8>> {
239 decrypt(data_key, ciphertext, nonce)
240}
241
242fn derive_deterministic_nonce(key: &[u8; 32], plaintext: &[u8]) -> [u8; NONCE_SIZE] {
255 let mut input = Vec::with_capacity(32 + plaintext.len());
257 input.extend_from_slice(key);
258 input.extend_from_slice(plaintext);
259
260 let hash = digest(&SHA256, &input);
261 let mut nonce = [0u8; NONCE_SIZE];
262 nonce.copy_from_slice(&hash.as_ref()[..NONCE_SIZE]);
263 nonce
264}
265
266pub fn encrypt_code_for_storage(storage_key: &[u8; 32], code: &str) -> Result<String> {
278 let plaintext = code.as_bytes();
279 let nonce = derive_deterministic_nonce(storage_key, plaintext);
280 let ciphertext = encrypt(storage_key, plaintext, &nonce)?;
281
282 let mut output = Vec::with_capacity(NONCE_SIZE + ciphertext.len());
285 output.extend_from_slice(&nonce);
286 output.extend_from_slice(&ciphertext);
287
288 Ok(STANDARD.encode(&output))
289}
290
291pub fn decrypt_code_from_storage(storage_key: &[u8; 32], encrypted: &str) -> Result<String> {
300 let data = STANDARD
301 .decode(encrypted)
302 .map_err(|_| PromocryptError::DecryptionFailed)?;
303
304 if data.len() < NONCE_SIZE + TAG_SIZE {
305 return Err(PromocryptError::DecryptionFailed);
306 }
307
308 let mut nonce = [0u8; NONCE_SIZE];
309 nonce.copy_from_slice(&data[..NONCE_SIZE]);
310
311 let ciphertext = &data[NONCE_SIZE..];
312 let plaintext = decrypt(storage_key, ciphertext, &nonce)?;
313
314 String::from_utf8(plaintext).map_err(|_| PromocryptError::DecryptionFailed)
315}
316
317pub fn encrypt_codes_for_storage(storage_key: &[u8; 32], codes: &[&str]) -> Result<Vec<String>> {
319 codes
320 .iter()
321 .map(|code| encrypt_code_for_storage(storage_key, code))
322 .collect()
323}
324
325pub fn decrypt_codes_from_storage(
327 storage_key: &[u8; 32],
328 encrypted: &[&str],
329) -> Result<Vec<String>> {
330 encrypted
331 .iter()
332 .map(|enc| decrypt_code_from_storage(storage_key, enc))
333 .collect()
334}
335
336pub fn hash_secret(secret: &str) -> String {
338 let hash = digest(&SHA256, secret.as_bytes());
339 hex::encode(hash.as_ref())
340}
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345
346 #[test]
347 fn test_derive_key() {
348 let password = b"test-password";
349 let salt = [0u8; SALT_SIZE];
350
351 let key1 = derive_key(password, &salt).unwrap();
352 let key2 = derive_key(password, &salt).unwrap();
353
354 assert_eq!(key1, key2);
355 assert_eq!(key1.len(), 32);
356 }
357
358 #[test]
359 fn test_derive_key_different_inputs() {
360 let salt = [0u8; SALT_SIZE];
361
362 let key1 = derive_key(b"password1", &salt).unwrap();
363 let key2 = derive_key(b"password2", &salt).unwrap();
364
365 assert_ne!(key1, key2);
366 }
367
368 #[test]
369 fn test_derive_key_different_salts() {
370 let password = b"test-password";
371
372 let key1 = derive_key(password, &[0u8; SALT_SIZE]).unwrap();
373 let key2 = derive_key(password, &[1u8; SALT_SIZE]).unwrap();
374
375 assert_ne!(key1, key2);
376 }
377
378 #[test]
379 fn test_encrypt_decrypt_roundtrip() {
380 let key = generate_random_key().unwrap();
381 let nonce = generate_nonce().unwrap();
382 let plaintext = b"Hello, World!";
383
384 let ciphertext = encrypt(&key, plaintext, &nonce).unwrap();
385 let decrypted = decrypt(&key, &ciphertext, &nonce).unwrap();
386
387 assert_eq!(decrypted, plaintext);
388 }
389
390 #[test]
391 fn test_encrypt_decrypt_empty() {
392 let key = generate_random_key().unwrap();
393 let nonce = generate_nonce().unwrap();
394 let plaintext = b"";
395
396 let ciphertext = encrypt(&key, plaintext, &nonce).unwrap();
397 let decrypted = decrypt(&key, &ciphertext, &nonce).unwrap();
398
399 assert_eq!(decrypted, plaintext);
400 }
401
402 #[test]
403 fn test_decrypt_wrong_key() {
404 let key1 = generate_random_key().unwrap();
405 let key2 = generate_random_key().unwrap();
406 let nonce = generate_nonce().unwrap();
407 let plaintext = b"Secret data";
408
409 let ciphertext = encrypt(&key1, plaintext, &nonce).unwrap();
410 let result = decrypt(&key2, &ciphertext, &nonce);
411
412 assert!(result.is_err());
413 }
414
415 #[test]
416 fn test_decrypt_wrong_nonce() {
417 let key = generate_random_key().unwrap();
418 let nonce1 = generate_nonce().unwrap();
419 let nonce2 = generate_nonce().unwrap();
420 let plaintext = b"Secret data";
421
422 let ciphertext = encrypt(&key, plaintext, &nonce1).unwrap();
423 let result = decrypt(&key, &ciphertext, &nonce2);
424
425 assert!(result.is_err());
426 }
427
428 #[test]
429 fn test_data_key_encrypt_decrypt() {
430 let data_key = generate_random_key().unwrap();
431 let password = b"my-secret-password";
432 let salt = generate_salt().unwrap();
433 let nonce = generate_nonce().unwrap();
434
435 let encrypted = encrypt_data_key(&data_key, password, &salt, &nonce).unwrap();
436 let decrypted = decrypt_data_key(&encrypted, password, &salt, &nonce).unwrap();
437
438 assert_eq!(data_key, decrypted);
439 }
440
441 #[test]
442 fn test_data_key_wrong_password() {
443 let data_key = generate_random_key().unwrap();
444 let salt = generate_salt().unwrap();
445 let nonce = generate_nonce().unwrap();
446
447 let encrypted = encrypt_data_key(&data_key, b"correct-password", &salt, &nonce).unwrap();
448 let result = decrypt_data_key(&encrypted, b"wrong-password", &salt, &nonce);
449
450 assert!(result.is_err());
451 }
452
453 #[test]
454 fn test_generate_salt_unique() {
455 let salt1 = generate_salt().unwrap();
456 let salt2 = generate_salt().unwrap();
457
458 assert_ne!(salt1, salt2);
460 }
461
462 #[test]
463 fn test_generate_nonce_unique() {
464 let nonce1 = generate_nonce().unwrap();
465 let nonce2 = generate_nonce().unwrap();
466
467 assert_ne!(nonce1, nonce2);
468 }
469
470 #[test]
471 fn test_ciphertext_size() {
472 let key = generate_random_key().unwrap();
473 let nonce = generate_nonce().unwrap();
474
475 for size in [0, 1, 16, 32, 100, 1000] {
477 let plaintext = vec![0u8; size];
478 let ciphertext = encrypt(&key, &plaintext, &nonce).unwrap();
479 assert_eq!(ciphertext.len(), size + TAG_SIZE);
480 }
481 }
482}