cardano_serialization_lib/
emip3.rs1use super::error::JsError;
2use super::*;
3
4use cryptoxide::chacha20poly1305::ChaCha20Poly1305;
5use cryptoxide::hmac::Hmac;
6use cryptoxide::pbkdf2::pbkdf2;
7use cryptoxide::sha2::Sha512;
8use hex::ToHex;
9
10use std::iter::repeat;
11
12mod password_encryption_parameter {
15 pub const ITER: u32 = 19_162;
16 pub const SALT_SIZE: usize = 32;
17 pub const NONCE_SIZE: usize = 12;
18 pub const KEY_SIZE: usize = 32;
19 pub const TAG_SIZE: usize = 16;
20
21 pub const METADATA_SIZE: usize = SALT_SIZE + NONCE_SIZE + TAG_SIZE;
22
23 pub const SALT_START: usize = 0;
24 pub const SALT_END: usize = SALT_START + SALT_SIZE;
25 pub const NONCE_START: usize = SALT_END;
26 pub const NONCE_END: usize = NONCE_START + NONCE_SIZE;
27 pub const TAG_START: usize = NONCE_END;
28 pub const TAG_END: usize = TAG_START + TAG_SIZE;
29 pub const ENCRYPTED_START: usize = TAG_END;
30}
31
32#[wasm_bindgen]
33pub fn encrypt_with_password(
34 password: &str,
35 salt: &str,
36 nonce: &str,
37 data: &str,
38) -> Result<String, JsError> {
39 use password_encryption_parameter::*;
40
41 let password = hex::decode(password).map_err(|e| JsError::from_str(&e.to_string()))?;
42 let salt = hex::decode(salt).map_err(|e| JsError::from_str(&e.to_string()))?;
43 let nonce = hex::decode(nonce).map_err(|e| JsError::from_str(&e.to_string()))?;
44 let data = hex::decode(data).map_err(|e| JsError::from_str(&e.to_string()))?;
45
46 if salt.len() != SALT_SIZE {
47 return Err(JsError::from_str(&format!(
48 "salt len must be {}, found {} bytes",
49 SALT_SIZE,
50 salt.len()
51 )));
52 }
53 if nonce.len() != NONCE_SIZE {
54 return Err(JsError::from_str(&format!(
55 "nonce len must be {}, found {} bytes",
56 NONCE_SIZE,
57 nonce.len()
58 )));
59 }
60 if password.len() == 0 {
61 return Err(JsError::from_str("Password len cannot be 0"));
62 }
63
64 let key = {
65 let mut mac = Hmac::new(Sha512::new(), &password);
66 let mut key: Vec<u8> = repeat(0).take(KEY_SIZE).collect();
67 pbkdf2(&mut mac, &salt[..], ITER, &mut key);
68 key
69 };
70
71 let mut tag = [0; TAG_SIZE];
72 let mut encrypted: Vec<u8> = repeat(0).take(data.len()).collect();
73 {
74 ChaCha20Poly1305::new(&key, &nonce, &[]).encrypt(&data, &mut encrypted, &mut tag);
75 }
76
77 let mut output = Vec::with_capacity(data.len() + METADATA_SIZE);
78 output.extend_from_slice(&salt);
79 output.extend_from_slice(&nonce);
80 output.extend_from_slice(&tag);
81 output.extend_from_slice(&encrypted);
82
83 Ok(output.encode_hex::<String>())
84}
85
86#[wasm_bindgen]
87pub fn decrypt_with_password(password: &str, data: &str) -> Result<String, JsError> {
88 use password_encryption_parameter::*;
89 let password = hex::decode(password).map_err(|e| JsError::from_str(&e.to_string()))?;
90 let data = hex::decode(data).map_err(|e| JsError::from_str(&e.to_string()))?;
91
92 if data.len() <= METADATA_SIZE {
93 return Err(JsError::from_str("Missing input data"));
95 }
96
97 let salt = &data[SALT_START..SALT_END];
98 let nonce = &data[NONCE_START..NONCE_END];
99 let tag = &data[TAG_START..TAG_END];
100 let encrypted = &data[ENCRYPTED_START..];
101
102 let key = {
103 let mut mac = Hmac::new(Sha512::new(), &password);
104 let mut key: Vec<u8> = repeat(0).take(KEY_SIZE).collect();
105 pbkdf2(&mut mac, &salt[..], ITER, &mut key);
106 key
107 };
108
109 let mut decrypted: Vec<u8> = repeat(0).take(encrypted.len()).collect();
110 let decryption_succeed =
111 { ChaCha20Poly1305::new(&key, &nonce, &[]).decrypt(&encrypted, &mut decrypted, &tag) };
112
113 if decryption_succeed {
114 Ok(decrypted.encode_hex::<String>())
115 } else {
116 Err(JsError::from_str("Decryption error"))
117 }
118}