whisky_wallet/encryption/
cipher.rs1use aes_gcm::{aead::AeadMut, Aes256Gcm, KeyInit, Nonce}; use base64::{engine::general_purpose, Engine as _};
3use pbkdf2::pbkdf2_hmac; use rand::RngCore; use serde_json::json;
6use sha2::Sha256;
7use whisky_common::WError; const IV_LENGTH: usize = 16;
10
11pub fn encrypt_with_cipher(
12 data: &str,
13 key: &str,
14 initialization_vector_size: Option<usize>,
15) -> Result<String, WError> {
16 let initialization_vector_size = initialization_vector_size.unwrap_or(IV_LENGTH);
18
19 let mut salt = vec![0u8; initialization_vector_size];
21 rand::thread_rng().fill_bytes(&mut salt);
22
23 let mut derived_key = vec![0u8; 32]; pbkdf2_hmac::<Sha256>(key.as_bytes(), &salt, 100_000, &mut derived_key);
27
28 let mut cipher = Aes256Gcm::new_from_slice(&derived_key).map_err(WError::from_err(
30 "encrypt_with_cipher - Aes256Gcm::new_from_slice",
31 ))?;
32
33 let mut iv = vec![0u8; initialization_vector_size];
35 rand::thread_rng().fill_bytes(&mut iv);
36 let nonce = Nonce::from_slice(&iv); let ciphertext = cipher
40 .encrypt(nonce, data.as_bytes())
41 .map_err(WError::from_err("encrypt_with_cipher - cipher.encrypt"))?;
42
43 let iv_base64 = general_purpose::STANDARD.encode(&iv);
45 let salt_base64 = general_purpose::STANDARD.encode(&salt);
46 let ciphertext_base64 = general_purpose::STANDARD.encode(&ciphertext);
47
48 let result = json!({
49 "iv": iv_base64,
50 "salt": salt_base64,
51 "ciphertext": ciphertext_base64,
52 });
53
54 Ok(result.to_string())
55}
56
57pub fn decrypt_with_cipher(encrypted_data_json: &str, key: &str) -> Result<String, WError> {
58 let encrypted_data: serde_json::Value = serde_json::from_str(encrypted_data_json).map_err(
60 WError::from_err("decrypt_with_cipher - JSON parsing failed"),
61 )?;
62
63 let iv_base64 = encrypted_data["iv"]
64 .as_str()
65 .ok_or_else(WError::from_opt("decrypt_with_cipher", "Missing IV"))?;
66 let ciphertext_base64 = encrypted_data["ciphertext"]
67 .as_str()
68 .ok_or_else(WError::from_opt(
69 "decrypt_with_cipher",
70 "Missing ciphertext",
71 ))?;
72
73 let iv = general_purpose::STANDARD
75 .decode(iv_base64)
76 .map_err(WError::from_err(
77 "decrypt_with_cipher - Base64 decode of IV failed",
78 ))?;
79 let ciphertext = general_purpose::STANDARD
80 .decode(ciphertext_base64)
81 .map_err(WError::from_err(
82 "decrypt_with_cipher - Base64 decode of ciphertext failed",
83 ))?;
84
85 let salt = if let Some(salt_base64) = encrypted_data["salt"].as_str() {
87 if !salt_base64.is_empty() {
89 general_purpose::STANDARD
90 .decode(salt_base64)
91 .map_err(WError::from_err(
92 "decrypt_with_cipher - Base64 decode of salt failed",
93 ))?
94 } else {
95 vec![0u8; iv.len()]
97 }
98 } else {
99 vec![0u8; iv.len()]
101 };
102
103 let mut derived_key = vec![0u8; 32]; pbkdf2_hmac::<Sha256>(key.as_bytes(), &salt, 100_000, &mut derived_key);
109
110 let mut cipher = Aes256Gcm::new_from_slice(&derived_key).map_err(WError::from_err(
112 "decrypt_with_cipher - Aes256Gcm::new_from_slice",
113 ))?;
114
115 let nonce = Nonce::from_slice(&iv);
117
118 let decrypted_data = cipher
120 .decrypt(nonce, ciphertext.as_ref())
121 .map_err(WError::from_err(
122 "decrypt_with_cipher - Decryption failed (incorrect password or corrupted data)",
123 ))?;
124
125 let decrypted_str = String::from_utf8(decrypted_data).map_err(WError::from_err(
127 "decrypt_with_cipher - Failed to convert decrypted data to UTF-8",
128 ))?;
129
130 Ok(decrypted_str)
131}