1extern crate rand;
2
3use core::str;
4use std::{
5 fs::{self},
6 io::{self, Read},
7};
8
9use aes::{cipher::KeyIvInit, Aes128};
10use anyhow::Result;
11use bip39::{Language, Mnemonic};
12use ctr::{cipher::StreamCipher, Ctr128BE};
13use hmac::{Hmac, Mac};
14use multiversx_chain_core::{std::Bech32Address, types::Address};
15use pbkdf2::pbkdf2;
16use rand::RngCore;
17use scrypt::{scrypt, Params};
18use serde_json::json;
19use sha2::{Digest, Sha256, Sha512};
20use sha3::Keccak256;
21use zeroize::Zeroize;
22
23use crate::{
24 crypto::{
25 private_key::{PrivateKey, PRIVATE_KEY_LENGTH},
26 public_key::PublicKey,
27 },
28 data::{keystore::*, transaction::Transaction},
29 utils::*,
30};
31
32use uuid::Uuid;
33
34const EGLD_COIN_TYPE: u32 = 508;
35const HARDENED: u32 = 0x80000000;
36const CIPHER_ALGORITHM_AES_128_CTR: &str = "aes-128-ctr";
37const KDF_SCRYPT: &str = "scrypt";
38
39type HmacSha512 = Hmac<Sha512>;
40type HmacSha256 = Hmac<Sha256>;
41
42#[derive(Copy, Clone, Debug)]
43pub struct Wallet {
44 priv_key: PrivateKey,
45}
46
47impl Wallet {
48 pub fn generate_mnemonic() -> Mnemonic {
50 Mnemonic::generate_in(Language::English, 24).unwrap()
51 }
52
53 fn seed_from_mnemonic(mnemonic: Mnemonic, password: &str) -> [u8; 64] {
54 let mut salt = String::with_capacity(8 + password.len());
55 salt.push_str("mnemonic");
56 salt.push_str(password);
57
58 let mut seed = [0u8; 64];
59
60 let _ = pbkdf2::<Hmac<Sha512>>(
61 mnemonic.to_string().as_bytes(),
62 salt.as_bytes(),
63 2048,
64 &mut seed,
65 );
66
67 salt.zeroize();
68
69 seed
70 }
71
72 pub fn get_private_key_from_mnemonic(
73 mnemonic: Mnemonic,
74 account: u32,
75 address_index: u32,
76 ) -> PrivateKey {
77 let seed = Self::seed_from_mnemonic(mnemonic, "");
78
79 let serialized_key_len = 32;
80 let hardened_child_padding: u8 = 0;
81
82 let mut digest =
83 HmacSha512::new_from_slice(b"ed25519 seed").expect("HMAC can take key of any size");
84 HmacSha512::new_from_slice(b"ed25519 seed").expect("HMAC can take key of any size");
85 digest.update(&seed);
86 let intermediary: Vec<u8> = digest.finalize().into_bytes().into_iter().collect();
87 let mut key = intermediary[..serialized_key_len].to_vec();
88 let mut chain_code = intermediary[serialized_key_len..].to_vec();
89
90 for child_idx in [
91 44 | HARDENED,
92 EGLD_COIN_TYPE | HARDENED,
93 account | HARDENED, HARDENED,
95 address_index | HARDENED, ] {
97 let mut buff = [vec![hardened_child_padding], key.clone()].concat();
98 buff.push((child_idx >> 24) as u8);
99 buff.push((child_idx >> 16) as u8);
100 buff.push((child_idx >> 8) as u8);
101 buff.push(child_idx as u8);
102
103 digest =
104 HmacSha512::new_from_slice(&chain_code).expect("HMAC can take key of any size");
105 HmacSha512::new_from_slice(&chain_code).expect("HMAC can take key of any size");
106 digest.update(&buff);
107 let intermediary: Vec<u8> = digest.finalize().into_bytes().into_iter().collect();
108 key = intermediary[..serialized_key_len].to_vec();
109 chain_code = intermediary[serialized_key_len..].to_vec();
110 }
111
112 PrivateKey::from_bytes(key.as_slice()).unwrap()
113 }
114
115 pub fn get_wallet_keys_mnemonic(mnemonic_str: String) -> (String, String) {
116 let mnemonic = Mnemonic::parse(mnemonic_str.replace('\n', "")).unwrap();
117 let private_key = Self::get_private_key_from_mnemonic(mnemonic, 0u32, 0u32);
118 let public_key = PublicKey::from(&private_key);
119
120 let public_key_str: &str = &public_key.to_string();
121 let private_key_str: &str = &private_key.to_string();
122
123 (private_key_str.to_string(), public_key_str.to_string())
124 }
125
126 pub fn from_private_key(priv_key: &str) -> Result<Self> {
127 let priv_key = PrivateKey::from_hex_str(priv_key)?;
128 Ok(Self { priv_key })
129 }
130
131 pub fn from_pem_file(file_path: &str) -> Result<Self> {
132 let contents = std::fs::read_to_string(file_path).unwrap();
133 Self::from_pem_file_contents(contents)
134 }
135
136 pub fn from_pem_file_contents(contents: String) -> Result<Self> {
137 let x = pem::parse(contents)?;
138 let x = x.contents()[..PRIVATE_KEY_LENGTH].to_vec();
139 let priv_key_str = std::str::from_utf8(x.as_slice())?;
140 let pri_key = PrivateKey::from_hex_str(priv_key_str)?;
141 Ok(Self { priv_key: pri_key })
142 }
143
144 pub fn get_shard(&self) -> u8 {
145 let address = self.to_address();
146 let address_bytes = address.as_bytes();
147 address_bytes[address_bytes.len() - 1] % 3
148 }
149
150 pub fn get_pem_decoded_content(file: &str) -> Vec<u8> {
151 let pem_content = fs::read_to_string(file).unwrap();
152 let lines: Vec<&str> = pem_content.split('\n').collect();
153 let pem_encoded_keys = format!("{}{}{}", lines[1], lines[2], lines[3]);
154 base64_decode(pem_encoded_keys)
155 }
156
157 pub fn get_wallet_keys_pem(file: &str) -> (String, String) {
158 let pem_decoded_keys = Self::get_pem_decoded_content(file);
159 let (private_key, public_key) = pem_decoded_keys.split_at(pem_decoded_keys.len() / 2);
160 let private_key_str = String::from_utf8(private_key.to_vec()).unwrap();
161 let public_key_str = String::from_utf8(public_key.to_vec()).unwrap();
162
163 (private_key_str, public_key_str)
164 }
165
166 pub fn from_keystore_secret(file_path: &str, insert_password: InsertPassword) -> Result<Self> {
167 let decryption_params = match insert_password {
168 InsertPassword::Plaintext(password) => {
169 Self::validate_keystore_password(file_path, password.to_string()).unwrap_or_else(
170 |e| {
171 panic!("Error: {:?}", e);
172 },
173 )
174 }
175 InsertPassword::StandardInput => {
176 Self::validate_keystore_password(file_path, Self::get_keystore_password())
177 .unwrap_or_else(|e| {
178 panic!("Error: {:?}", e);
179 })
180 }
181 };
182 let priv_key = PrivateKey::from_hex_str(
183 hex::encode(Self::decrypt_secret_key(decryption_params)).as_str(),
184 )?;
185 Ok(Self { priv_key })
186 }
187
188 pub fn get_private_key_from_keystore_secret(
189 file_path: &str,
190 password: &str,
191 ) -> Result<PrivateKey> {
192 let decyption_params = Self::validate_keystore_password(file_path, password.to_string())
193 .unwrap_or_else(|e| {
194 panic!("Error: {:?}", e);
195 });
196 let priv_key = PrivateKey::from_hex_str(
197 hex::encode(Self::decrypt_secret_key(decyption_params)).as_str(),
198 )?;
199 Ok(priv_key)
200 }
201
202 #[deprecated(
203 since = "0.54.0",
204 note = "Renamed to `to_address`, type changed to multiversx_chain_core::types::Address"
205 )]
206 pub fn address(&self) -> Bech32Address {
207 self.to_address().to_bech32_default()
208 }
209
210 pub fn to_address(&self) -> Address {
211 PublicKey::from(&self.priv_key).to_address()
212 }
213
214 pub fn sign_tx(&self, unsign_tx: &Transaction) -> [u8; 64] {
215 let mut unsign_tx = unsign_tx.clone();
216 unsign_tx.signature = None;
217
218 let mut tx_bytes = json!(unsign_tx).to_string().as_bytes().to_vec();
219
220 let should_sign_on_tx_hash = unsign_tx.version >= 2 && unsign_tx.options & 1 > 0;
221 if should_sign_on_tx_hash {
222 let mut h = Keccak256::new();
223 h.update(tx_bytes);
224 tx_bytes = h.finalize().to_vec();
225 }
226
227 self.priv_key.sign(tx_bytes)
228 }
229
230 pub fn get_keystore_password() -> String {
231 println!(
232 "Insert password. Press 'Ctrl-D' (Linux / MacOS) or 'Ctrl-Z' (Windows) when done."
233 );
234 let mut password = String::new();
235 io::stdin().read_to_string(&mut password).unwrap();
236 password = password.trim().to_string();
237 password
238 }
239
240 pub fn validate_keystore_password(
241 path: &str,
242 password: String,
243 ) -> Result<DecryptionParams, WalletError> {
244 let json_body = fs::read_to_string(path).unwrap();
245 let keystore: Keystore = serde_json::from_str(&json_body).unwrap();
246 let ciphertext = hex::decode(&keystore.crypto.ciphertext).unwrap();
247
248 let cipher = &keystore.crypto.cipher;
249 if cipher != CIPHER_ALGORITHM_AES_128_CTR {
250 return Err(WalletError::InvalidCipher);
251 }
252
253 let iv = hex::decode(&keystore.crypto.cipherparams.iv).unwrap();
254 let salt = hex::decode(&keystore.crypto.kdfparams.salt).unwrap();
255 let json_mac = hex::decode(&keystore.crypto.mac).unwrap();
256
257 let kdf = &keystore.crypto.kdf;
258 if kdf != KDF_SCRYPT {
259 return Err(WalletError::InvalidKdf);
260 }
261 let n = keystore.crypto.kdfparams.n as f64;
262 let r = keystore.crypto.kdfparams.r as u64;
263 let p = keystore.crypto.kdfparams.p as u64;
264 let dklen = keystore.crypto.kdfparams.dklen as usize;
265
266 let params = Params::new(n.log2() as u8, r as u32, p as u32, dklen).unwrap();
267
268 let mut derived_key = vec![0u8; 32];
269 scrypt(password.as_bytes(), &salt, ¶ms, &mut derived_key).unwrap();
270
271 let derived_key_first_half = derived_key[0..16].to_vec();
272 let derived_key_second_half = derived_key[16..32].to_vec();
273
274 let mut input_mac = HmacSha256::new_from_slice(&derived_key_second_half).unwrap();
275 input_mac.update(&ciphertext);
276 let computed_mac = input_mac.finalize().into_bytes();
277
278 if computed_mac.to_vec() == json_mac {
279 println!("Password is correct");
280 Ok(DecryptionParams {
281 derived_key_first_half,
282 iv,
283 data: ciphertext,
284 })
285 } else {
286 println!("Password is incorrect");
287 Err(WalletError::InvalidPassword)
288 }
289 }
290
291 pub fn decrypt_secret_key(decryption_params: DecryptionParams) -> Vec<u8> {
292 let mut cipher = Ctr128BE::<Aes128>::new(
293 decryption_params.derived_key_first_half.as_slice().into(),
294 decryption_params.iv.as_slice().into(),
295 );
296 let mut decrypted = decryption_params.data.to_vec();
297 cipher.apply_keystream(&mut decrypted);
298
299 decrypted
300 }
301
302 pub fn encrypt_keystore(
303 data: &[u8],
304 hrp: &str,
305 address: &Address,
306 public_key: &str,
307 password: &str,
308 ) -> String {
309 let params = Params::new((KDF_N as f64).log2() as u8, KDF_R, KDF_P, KDF_DKLEN).unwrap();
310 let mut rand_salt: [u8; 32] = [0u8; 32];
311 rand::thread_rng().fill_bytes(&mut rand_salt);
312 let salt_hex = hex::encode(rand_salt);
313
314 let mut rand_iv: [u8; 16] = [0u8; 16];
315 rand::thread_rng().fill_bytes(&mut rand_iv);
316 let iv_hex = hex::encode(rand_iv);
317
318 let mut derived_key = vec![0u8; 32];
319 scrypt(password.as_bytes(), &rand_salt, ¶ms, &mut derived_key).unwrap();
320
321 let derived_key_first_half = derived_key[0..16].to_vec();
322 let derived_key_second_half = derived_key[16..32].to_vec();
323
324 let decryption_params = DecryptionParams {
325 derived_key_first_half,
326 iv: rand_iv.to_vec(),
327 data: data.to_vec(),
328 };
329
330 let ciphertext = Self::decrypt_secret_key(decryption_params);
331
332 let mut h = HmacSha256::new_from_slice(&derived_key_second_half).unwrap();
333 h.update(&ciphertext);
334 let mac = h.finalize().into_bytes();
335 let keystore = Keystore {
336 crypto: Crypto {
337 cipher: CIPHER_ALGORITHM_AES_128_CTR.to_string(),
338 cipherparams: CryptoParams { iv: iv_hex },
339 ciphertext: hex::encode(&ciphertext),
340 kdf: KDF_SCRYPT.to_string(),
341 kdfparams: KdfParams {
342 salt: salt_hex,
343 n: KDF_N,
344 r: KDF_R,
345 p: KDF_P,
346 dklen: KDF_DKLEN as u32,
347 },
348 mac: hex::encode(mac),
349 },
350 id: Uuid::new_v4().to_string(),
351 version: KEYSTORE_VERSION,
352 kind: "secretKey".to_string(),
353 address: public_key.to_string(),
354 bech32: address.to_bech32(hrp).bech32,
355 };
356
357 let mut keystore_json: String = serde_json::to_string_pretty(&keystore).unwrap();
358 keystore_json.push('\n');
359 keystore_json
360 }
361
362 pub fn generate_pem_content(
363 hrp: &str,
364 address: &Address,
365 private_key: &str,
366 public_key: &str,
367 ) -> String {
368 let concat_keys = format!("{}{}", private_key, public_key);
369 let concat_keys_b64 = base64_encode(concat_keys);
370
371 let formatted_key = concat_keys_b64
373 .as_bytes()
374 .chunks(64)
375 .map(|chunk| std::str::from_utf8(chunk).unwrap())
376 .collect::<Vec<&str>>()
377 .join("\n");
378
379 let address_bech32 = Bech32Address::encode_address(hrp, address.clone());
380 let pem_content = format!(
381 "-----BEGIN PRIVATE KEY for {address_bech32}-----\n{formatted_key}\n-----END PRIVATE KEY for {address_bech32}-----\n"
382 );
383
384 pem_content
385 }
386}