1use aes_gcm::{
2 Aes256Gcm, Nonce,
3 aead::{Aead, AeadCore, KeyInit, OsRng},
4};
5use clap::Parser;
6use eyre::{Result, eyre};
7use pbkdf2::pbkdf2_hmac;
8use rand::{Rng, rng};
9use sha2::Sha256;
10
11const SALT_SIZE: usize = 32;
13const NONCE_SIZE: usize = 12;
15const KEY_SIZE: usize = 32;
17const PBKDF2_ITERATIONS: u32 = 100_000;
19
20#[derive(Parser, Debug)]
22enum Commands {
23 GeneratePassword,
25 Encrypt {
27 #[arg(short, long)]
29 key: String,
30 },
31}
32
33fn encrypt_key(private_key: Vec<u8>, pwd: Vec<u8>) -> Result<Vec<u8>> {
53 let mut salt = [0u8; SALT_SIZE];
55 rng().fill(&mut salt);
56
57 let mut derived_key = [0u8; KEY_SIZE];
59 pbkdf2_hmac::<Sha256>(&pwd, &salt, PBKDF2_ITERATIONS, &mut derived_key);
60
61 let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
63
64 let cipher = Aes256Gcm::new_from_slice(&derived_key).map_err(|e| eyre!("Failed to create cipher: {}", e))?;
66
67 let ciphertext = cipher.encrypt(&nonce, private_key.as_ref()).map_err(|e| eyre!("Encryption failed: {}", e))?;
68
69 let mut result = Vec::with_capacity(SALT_SIZE + NONCE_SIZE + ciphertext.len());
71 result.extend_from_slice(&salt);
72 result.extend_from_slice(&nonce);
73 result.extend_from_slice(&ciphertext);
74
75 Ok(result)
76}
77
78fn decrypt_key(encrypted_data: &[u8], pwd: &[u8]) -> Result<Vec<u8>> {
91 if encrypted_data.len() < SALT_SIZE + NONCE_SIZE {
92 return Err(eyre!("Invalid encrypted data: too short"));
93 }
94
95 let salt = &encrypted_data[..SALT_SIZE];
97 let nonce = &encrypted_data[SALT_SIZE..SALT_SIZE + NONCE_SIZE];
98 let ciphertext = &encrypted_data[SALT_SIZE + NONCE_SIZE..];
99
100 let mut derived_key = [0u8; KEY_SIZE];
102 pbkdf2_hmac::<Sha256>(pwd, salt, PBKDF2_ITERATIONS, &mut derived_key);
103
104 let cipher = Aes256Gcm::new_from_slice(&derived_key).map_err(|e| eyre!("Failed to create cipher: {}", e))?;
106
107 let nonce = Nonce::from_slice(nonce);
108 let plaintext = cipher.decrypt(nonce, ciphertext).map_err(|e| eyre!("Decryption failed: {}", e))?;
109
110 Ok(plaintext)
111}
112
113fn main() -> Result<()> {
114 let args = Commands::parse();
115 match args {
116 Commands::GeneratePassword => {
117 let mut random = rng();
119 let pwd: Vec<u8> = (0..16).map(|_| random.random::<u8>()).collect();
120 println!("{pwd:?}");
121 }
122 Commands::Encrypt { key } => {
123 let pwd = kabu_types_entities::private::KEY_ENCRYPTION_PWD.to_vec();
124
125 let private_key = hex::decode(key.strip_prefix("0x").unwrap_or(key.clone().as_str()))?;
126 let encrypted_key = encrypt_key(private_key.clone(), pwd.clone())?;
127
128 let decrypted_key = decrypt_key(&encrypted_key, &pwd)?;
130 if decrypted_key == private_key {
131 println!("Encrypted private key : {}", hex::encode(encrypted_key))
132 } else {
133 println!("Error encrypting private key");
134 }
135 }
136 }
137
138 Ok(())
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn test_encrypt_decrypt_roundtrip() {
147 let private_key = vec![0x42; 32];
148 let password = b"test_password_123!";
149
150 let encrypted = encrypt_key(private_key.clone(), password.to_vec()).unwrap();
151 let decrypted = decrypt_key(&encrypted, password).unwrap();
152
153 assert_eq!(decrypted, private_key);
154 }
155
156 #[test]
157 fn test_wrong_password_fails() {
158 let private_key = vec![0x42; 32];
159 let password = b"correct_password";
160 let wrong_password = b"wrong_password";
161
162 let encrypted = encrypt_key(private_key.clone(), password.to_vec()).unwrap();
163 let result = decrypt_key(&encrypted, wrong_password);
164
165 assert!(result.is_err());
166 }
167
168 #[test]
169 fn test_tampered_data_fails() {
170 let private_key = vec![0x42; 32];
171 let password = b"test_password";
172
173 let mut encrypted = encrypt_key(private_key, password.to_vec()).unwrap();
174 let last_idx = encrypted.len() - 1;
176 encrypted[last_idx] ^= 0xFF;
177
178 let result = decrypt_key(&encrypted, password);
179 assert!(result.is_err());
180 }
181
182 #[test]
183 fn test_encryption_randomness() {
184 let private_key = vec![0xff; 32];
185 let password = b"same_password";
186
187 let encrypted1 = encrypt_key(private_key.clone(), password.to_vec()).unwrap();
188 let encrypted2 = encrypt_key(private_key.clone(), password.to_vec()).unwrap();
189
190 assert_ne!(encrypted1, encrypted2);
192
193 let decrypted1 = decrypt_key(&encrypted1, password).unwrap();
195 let decrypted2 = decrypt_key(&encrypted2, password).unwrap();
196 assert_eq!(decrypted1, private_key);
197 assert_eq!(decrypted2, private_key);
198 }
199}