1use std::str::FromStr;
2
3use bitwarden_core::key_management::{KeyIds, SymmetricKeyId};
4use bitwarden_crypto::{
5 AsymmetricCryptoKey, AsymmetricPublicCryptoKey, CryptoError, Decryptable, EncString,
6 Encryptable, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, SymmetricCryptoKey,
7 UnsignedSharedKey,
8};
9use wasm_bindgen::prelude::*;
10
11#[wasm_bindgen]
16pub struct PureCrypto {}
17
18#[wasm_bindgen]
20impl PureCrypto {
21 pub fn symmetric_decrypt(enc_string: String, key: Vec<u8>) -> Result<String, CryptoError> {
24 Self::symmetric_decrypt_string(enc_string, key)
25 }
26
27 pub fn symmetric_decrypt_string(
28 enc_string: String,
29 key: Vec<u8>,
30 ) -> Result<String, CryptoError> {
31 EncString::from_str(&enc_string)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?)
32 }
33
34 pub fn symmetric_decrypt_bytes(
35 enc_string: String,
36 key: Vec<u8>,
37 ) -> Result<Vec<u8>, CryptoError> {
38 EncString::from_str(&enc_string)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?)
39 }
40
41 pub fn symmetric_decrypt_array_buffer(
44 enc_bytes: Vec<u8>,
45 key: Vec<u8>,
46 ) -> Result<Vec<u8>, CryptoError> {
47 Self::symmetric_decrypt_filedata(enc_bytes, key)
48 }
49
50 pub fn symmetric_decrypt_filedata(
51 enc_bytes: Vec<u8>,
52 key: Vec<u8>,
53 ) -> Result<Vec<u8>, CryptoError> {
54 EncString::from_buffer(&enc_bytes)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?)
55 }
56
57 pub fn symmetric_encrypt_string(plain: String, key: Vec<u8>) -> Result<String, CryptoError> {
58 plain
59 .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?)
60 .map(|enc| enc.to_string())
61 }
62
63 pub fn symmetric_encrypt_bytes(plain: Vec<u8>, key: Vec<u8>) -> Result<String, CryptoError> {
64 plain
65 .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?)
66 .map(|enc| enc.to_string())
67 }
68
69 pub fn symmetric_encrypt_filedata(
70 plain: Vec<u8>,
71 key: Vec<u8>,
72 ) -> Result<Vec<u8>, CryptoError> {
73 plain
74 .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?)?
75 .to_buffer()
76 }
77
78 pub fn decrypt_user_key_with_master_password(
79 encrypted_user_key: String,
80 master_password: String,
81 email: String,
82 kdf: Kdf,
83 ) -> Result<Vec<u8>, CryptoError> {
84 let master_key = MasterKey::derive(master_password.as_str(), email.as_str(), &kdf)?;
85 let encrypted_user_key = EncString::from_str(&encrypted_user_key)?;
86 let result = master_key
87 .decrypt_user_key(encrypted_user_key)
88 .map_err(|_| CryptoError::InvalidKey)?;
89 Ok(result.to_encoded())
90 }
91
92 pub fn encrypt_user_key_with_master_password(
93 user_key: Vec<u8>,
94 master_password: String,
95 email: String,
96 kdf: Kdf,
97 ) -> Result<String, CryptoError> {
98 let master_key = MasterKey::derive(master_password.as_str(), email.as_str(), &kdf)?;
99 let user_key = SymmetricCryptoKey::try_from(user_key)?;
100 let result = master_key.encrypt_user_key(&user_key)?;
101 Ok(result.to_string())
102 }
103
104 pub fn make_user_key_aes256_cbc_hmac() -> Vec<u8> {
105 SymmetricCryptoKey::make_aes256_cbc_hmac_key().to_encoded()
106 }
107
108 pub fn make_user_key_xchacha20_poly1305() -> Vec<u8> {
109 SymmetricCryptoKey::make_xchacha20_poly1305_key().to_encoded()
110 }
111
112 pub fn wrap_symmetric_key(
115 key_to_be_wrapped: Vec<u8>,
116 wrapping_key: Vec<u8>,
117 ) -> Result<String, CryptoError> {
118 let tmp_store: KeyStore<KeyIds> = KeyStore::default();
119 let mut context = tmp_store.context();
120 #[allow(deprecated)]
121 context.set_symmetric_key(
122 SymmetricKeyId::Local("wrapping_key"),
123 SymmetricCryptoKey::try_from(wrapping_key)?,
124 )?;
125 #[allow(deprecated)]
126 context.set_symmetric_key(
127 SymmetricKeyId::Local("key_to_wrap"),
128 SymmetricCryptoKey::try_from(key_to_be_wrapped)?,
129 )?;
130 Ok(context
132 .wrap_symmetric_key(
133 SymmetricKeyId::Local("wrapping_key"),
134 SymmetricKeyId::Local("key_to_wrap"),
135 )?
136 .to_string())
137 }
138
139 pub fn unwrap_symmetric_key(
142 wrapped_key: String,
143 wrapping_key: Vec<u8>,
144 ) -> Result<Vec<u8>, CryptoError> {
145 let tmp_store: KeyStore<KeyIds> = KeyStore::default();
146 let mut context = tmp_store.context();
147 #[allow(deprecated)]
148 context.set_symmetric_key(
149 SymmetricKeyId::Local("wrapping_key"),
150 SymmetricCryptoKey::try_from(wrapping_key)?,
151 )?;
152 context.unwrap_symmetric_key(
154 SymmetricKeyId::Local("wrapping_key"),
155 SymmetricKeyId::Local("wrapped_key"),
156 &EncString::from_str(wrapped_key.as_str())?,
157 )?;
158 #[allow(deprecated)]
159 let key = context.dangerous_get_symmetric_key(SymmetricKeyId::Local("wrapped_key"))?;
160 Ok(key.to_encoded())
161 }
162
163 pub fn wrap_encapsulation_key(
169 encapsulation_key: Vec<u8>,
170 wrapping_key: Vec<u8>,
171 ) -> Result<String, CryptoError> {
172 let tmp_store: KeyStore<KeyIds> = KeyStore::default();
173 let mut context = tmp_store.context();
174 #[allow(deprecated)]
175 context.set_symmetric_key(
176 SymmetricKeyId::Local("wrapping_key"),
177 SymmetricCryptoKey::try_from(wrapping_key)?,
178 )?;
179 Ok(encapsulation_key
181 .encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))?
182 .to_string())
183 }
184
185 pub fn unwrap_encapsulation_key(
188 wrapped_key: String,
189 wrapping_key: Vec<u8>,
190 ) -> Result<Vec<u8>, CryptoError> {
191 let tmp_store: KeyStore<KeyIds> = KeyStore::default();
192 let mut context = tmp_store.context();
193 #[allow(deprecated)]
194 context.set_symmetric_key(
195 SymmetricKeyId::Local("wrapping_key"),
196 SymmetricCryptoKey::try_from(wrapping_key)?,
197 )?;
198 EncString::from_str(wrapped_key.as_str())?
200 .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))
201 }
202
203 pub fn wrap_decapsulation_key(
206 decapsulation_key: Vec<u8>,
207 wrapping_key: Vec<u8>,
208 ) -> Result<String, CryptoError> {
209 let tmp_store: KeyStore<KeyIds> = KeyStore::default();
210 let mut context = tmp_store.context();
211 #[allow(deprecated)]
212 context.set_symmetric_key(
213 SymmetricKeyId::Local("wrapping_key"),
214 SymmetricCryptoKey::try_from(wrapping_key)?,
215 )?;
216 Ok(decapsulation_key
218 .encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))?
219 .to_string())
220 }
221
222 pub fn unwrap_decapsulation_key(
225 wrapped_key: String,
226 wrapping_key: Vec<u8>,
227 ) -> Result<Vec<u8>, CryptoError> {
228 let tmp_store: KeyStore<KeyIds> = KeyStore::default();
229 let mut context = tmp_store.context();
230 #[allow(deprecated)]
231 context.set_symmetric_key(
232 SymmetricKeyId::Local("wrapping_key"),
233 SymmetricCryptoKey::try_from(wrapping_key)?,
234 )?;
235 EncString::from_str(wrapped_key.as_str())?
237 .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))
238 }
239
240 pub fn encapsulate_key_unsigned(
244 shared_key: Vec<u8>,
245 encapsulation_key: Vec<u8>,
246 ) -> Result<String, CryptoError> {
247 let encapsulation_key = AsymmetricPublicCryptoKey::from_der(encapsulation_key.as_slice())?;
248 Ok(UnsignedSharedKey::encapsulate_key_unsigned(
249 &SymmetricCryptoKey::try_from(shared_key)?,
250 &encapsulation_key,
251 )?
252 .to_string())
253 }
254
255 pub fn decapsulate_key_unsigned(
259 encapsulated_key: String,
260 decapsulation_key: Vec<u8>,
261 ) -> Result<Vec<u8>, CryptoError> {
262 Ok(UnsignedSharedKey::from_str(encapsulated_key.as_str())?
263 .decapsulate_key_unsigned(&AsymmetricCryptoKey::from_der(
264 decapsulation_key.as_slice(),
265 )?)?
266 .to_encoded())
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use std::{num::NonZero, str::FromStr};
273
274 use bitwarden_crypto::EncString;
275
276 use super::*;
277
278 const KEY: &[u8] = &[
279 81, 142, 1, 228, 222, 3, 3, 133, 34, 176, 35, 66, 150, 6, 109, 70, 190, 149, 47, 47, 89,
280 23, 144, 87, 92, 46, 220, 13, 148, 106, 162, 234, 202, 139, 136, 33, 16, 200, 8, 73, 176,
281 172, 185, 187, 224, 10, 65, 223, 228, 54, 92, 181, 8, 213, 162, 221, 117, 254, 245, 111,
282 55, 211, 77, 29,
283 ];
284
285 const ENCRYPTED: &str = "2.Dh7AFLXR+LXcxUaO5cRjpg==|uXyhubjAoNH8lTdy/zgJDQ==|cHEMboj0MYsU5yDRQ1rLCgxcjNbKRc1PWKuv8bpU5pM=";
286 const DECRYPTED: &str = "test";
287 const DECRYPTED_BYTES: &[u8] = b"test";
288 const ENCRYPTED_BYTES: &[u8] = &[
289 2, 209, 195, 115, 49, 205, 253, 128, 162, 169, 246, 175, 217, 144, 73, 108, 191, 27, 113,
290 69, 55, 94, 142, 62, 129, 204, 173, 130, 37, 42, 97, 209, 25, 192, 64, 126, 112, 139, 248,
291 2, 89, 112, 178, 83, 25, 77, 130, 187, 127, 85, 179, 211, 159, 186, 111, 44, 109, 211, 18,
292 120, 104, 144, 4, 76, 3,
293 ];
294
295 const PEM_KEY: &str = "-----BEGIN PRIVATE KEY-----
296MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDiTQVuzhdygFz5
297qv14i+XFDGTnDravzUQT1hPKPGUZOUSZ1gwdNgkWqOIaOnR65BHEnL0sp4bnuiYc
298afeK2JAW5Sc8Z7IxBNSuAwhQmuKx3RochMIiuCkI2/p+JvUQoJu6FBNm8OoJ4Cwm
299qqHGZESMfnpQDCuDrB3JdJEdXhtmnl0C48sGjOk3WaBMcgGqn8LbJDUlyu1zdqyv
300b0waJf0iV4PJm2fkUl7+57D/2TkpbCqURVnZK1FFIEg8mr6FzSN1F2pOfktkNYZw
301P7MSNR7o81CkRSCMr7EkIVa+MZYMBx106BMK7FXgWB7nbSpsWKxBk7ZDHkID2fam
302rEcVtrzDAgMBAAECggEBAKwq9OssGGKgjhvUnyrLJHAZ0dqIMyzk+dotkLjX4gKi
303szJmyqiep6N5sStLNbsZMPtoU/RZMCW0VbJgXFhiEp2YkZU/Py5UAoqw++53J+kx
3040d/IkPphKbb3xUec0+1mg5O6GljDCQuiZXS1dIa/WfeZcezclW6Dz9WovY6ePjJ+
3058vEBR1icbNKzyeINd6MtPtpcgQPHtDwHvhPyUDbKDYGbLvjh9nui8h4+ZUlXKuVR
306jB0ChxiKV1xJRjkrEVoulOOicd5r597WfB2ghax3pvRZ4MdXemCXm3gQYqPVKach
307vGU+1cPQR/MBJZpxT+EZA97xwtFS3gqwbxJaNFcoE8ECgYEA9OaeYZhQPDo485tI
3081u/Z7L/3PNape9hBQIXoW7+MgcQ5NiWqYh8Jnj43EIYa0wM/ECQINr1Za8Q5e6KR
309J30FcU+kfyjuQ0jeXdNELGU/fx5XXNg/vV8GevHwxRlwzqZTCg6UExUZzbYEQqd7
310l+wPyETGeua5xCEywA1nX/D101kCgYEA7I6aMFjhEjO71RmzNhqjKJt6DOghoOfQ
311TjhaaanNEhLYSbenFz1mlb21mW67ulmz162saKdIYLxQNJIP8ZPmxh4ummOJI8w9
312ClHfo8WuCI2hCjJ19xbQJocSbTA5aJg6lA1IDVZMDbQwsnAByPRGpaLHBT/Q9Bye
313KvCMB+9amXsCgYEAx65yXSkP4sumPBrVHUub6MntERIGRxBgw/drKcPZEMWp0FiN
314wEuGUBxyUWrG3F69QK/gcqGZE6F/LSu0JvptQaKqgXQiMYJsrRvhbkFvsHpQyUcZ
315UZL1ebFjm5HOxPAgrQaN/bEqxOwwNRjSUWEMzUImg3c06JIZCzbinvudtKECgYEA
316kY3JF/iIPI/yglP27lKDlCfeeHSYxI3+oTKRhzSAxx8rUGidenJAXeDGDauR/T7W
317pt3pGNfddBBK9Z3uC4Iq3DqUCFE4f/taj7ADAJ1Q0Vh7/28/IJM77ojr8J1cpZwN
318Zy2o6PPxhfkagaDjqEeN9Lrs5LD4nEvDkr5CG1vOjmMCgYEAvIBFKRm31NyF8jLi
319CVuPwC5PzrW5iThDmsWTaXFpB3esUsbICO2pEz872oeQS+Em4GO5vXUlpbbFPzup
320PFhA8iMJ8TAvemhvc7oM0OZqpU6p3K4seHf6BkwLxumoA3vDJfovu9RuXVcJVOnf
321DnqOsltgPomWZ7xVfMkm9niL2OA=
322-----END PRIVATE KEY-----";
323
324 #[test]
325 fn test_symmetric_decrypt() {
326 let enc_string = EncString::from_str(ENCRYPTED).unwrap();
327
328 let result = PureCrypto::symmetric_decrypt_string(enc_string.to_string(), KEY.to_vec());
329 assert!(result.is_ok());
330 assert_eq!(result.unwrap(), DECRYPTED);
331 }
332
333 #[test]
334 fn test_symmetric_encrypt() {
335 let result = PureCrypto::symmetric_encrypt_string(DECRYPTED.to_string(), KEY.to_vec());
336 assert!(result.is_ok());
337 }
339
340 #[test]
341 fn test_symmetric_string_round_trip() {
342 let encrypted =
343 PureCrypto::symmetric_encrypt_string(DECRYPTED.to_string(), KEY.to_vec()).unwrap();
344 let decrypted =
345 PureCrypto::symmetric_decrypt_string(encrypted.clone(), KEY.to_vec()).unwrap();
346 assert_eq!(decrypted, DECRYPTED);
347 }
348
349 #[test]
350 fn test_symmetric_bytes_round_trip() {
351 let encrypted =
352 PureCrypto::symmetric_encrypt_bytes(DECRYPTED.as_bytes().to_vec(), KEY.to_vec())
353 .unwrap();
354 let decrypted =
355 PureCrypto::symmetric_decrypt_bytes(encrypted.clone(), KEY.to_vec()).unwrap();
356 assert_eq!(decrypted, DECRYPTED.as_bytes().to_vec());
357 }
358
359 #[test]
360 fn test_symmetric_decrypt_array_buffer() {
361 let result = PureCrypto::symmetric_decrypt_filedata(ENCRYPTED_BYTES.to_vec(), KEY.to_vec());
362 assert!(result.is_ok());
363 assert_eq!(result.unwrap(), DECRYPTED_BYTES);
364 }
365
366 #[test]
367 fn test_symmetric_encrypt_to_array_buffer() {
368 let result = PureCrypto::symmetric_encrypt_filedata(DECRYPTED_BYTES.to_vec(), KEY.to_vec());
369 assert!(result.is_ok());
370 }
372
373 #[test]
374 fn test_symmetric_filedata_round_trip() {
375 let encrypted =
376 PureCrypto::symmetric_encrypt_filedata(DECRYPTED_BYTES.to_vec(), KEY.to_vec()).unwrap();
377 let decrypted =
378 PureCrypto::symmetric_decrypt_filedata(encrypted.clone(), KEY.to_vec()).unwrap();
379 assert_eq!(decrypted, DECRYPTED_BYTES);
380 }
381
382 #[test]
383 fn test_make_aes256_cbc_hmac_key() {
384 let key = PureCrypto::make_user_key_aes256_cbc_hmac();
385 assert_eq!(key.len(), 64);
386 }
387
388 #[test]
389 fn test_make_xchacha20_poly1305_key() {
390 let key = PureCrypto::make_user_key_xchacha20_poly1305();
391 assert!(key.len() > 64);
392 }
393
394 #[test]
395 fn roundtrip_encrypt_user_key_with_master_password() {
396 let master_password = "test";
397 let email = "[email protected]";
398 let kdf = Kdf::PBKDF2 {
399 iterations: NonZero::try_from(600000).unwrap(),
400 };
401 let user_key = PureCrypto::make_user_key_aes256_cbc_hmac();
402 let encrypted_user_key = PureCrypto::encrypt_user_key_with_master_password(
403 user_key.clone(),
404 master_password.to_string(),
405 email.to_string(),
406 kdf.clone(),
407 )
408 .unwrap();
409 let decrypted_user_key = PureCrypto::decrypt_user_key_with_master_password(
410 encrypted_user_key,
411 master_password.to_string(),
412 email.to_string(),
413 kdf,
414 )
415 .unwrap();
416 assert_eq!(user_key, decrypted_user_key);
417 }
418
419 #[test]
420 fn test_wrap_unwrap_symmetric_key() {
421 let key_to_be_wrapped = PureCrypto::make_user_key_aes256_cbc_hmac();
422 let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac();
423 let wrapped_key =
424 PureCrypto::wrap_symmetric_key(key_to_be_wrapped.clone(), wrapping_key.clone())
425 .unwrap();
426 let unwrapped_key = PureCrypto::unwrap_symmetric_key(wrapped_key, wrapping_key).unwrap();
427 assert_eq!(key_to_be_wrapped, unwrapped_key);
428 }
429
430 #[test]
431 fn test_wrap_encapsulation_key() {
432 let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap();
433 let encapsulation_key = decapsulation_key.to_public_der().unwrap();
434 let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac();
435 let wrapped_key =
436 PureCrypto::wrap_encapsulation_key(encapsulation_key.clone(), wrapping_key.clone())
437 .unwrap();
438 let unwrapped_key =
439 PureCrypto::unwrap_encapsulation_key(wrapped_key, wrapping_key).unwrap();
440 assert_eq!(encapsulation_key, unwrapped_key);
441 }
442
443 #[test]
444 fn test_wrap_decapsulation_key() {
445 let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap();
446 let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac();
447 let wrapped_key = PureCrypto::wrap_decapsulation_key(
448 decapsulation_key.to_der().unwrap(),
449 wrapping_key.clone(),
450 )
451 .unwrap();
452 let unwrapped_key =
453 PureCrypto::unwrap_decapsulation_key(wrapped_key, wrapping_key).unwrap();
454 assert_eq!(decapsulation_key.to_der().unwrap(), unwrapped_key);
455 }
456
457 #[test]
458 fn test_encapsulate_key_unsigned() {
459 let shared_key = PureCrypto::make_user_key_aes256_cbc_hmac();
460 let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap();
461 let encapsulation_key = decapsulation_key.to_public_der().unwrap();
462 let encapsulated_key =
463 PureCrypto::encapsulate_key_unsigned(shared_key.clone(), encapsulation_key.clone())
464 .unwrap();
465 let unwrapped_key = PureCrypto::decapsulate_key_unsigned(
466 encapsulated_key,
467 decapsulation_key.to_der().unwrap(),
468 )
469 .unwrap();
470 assert_eq!(shared_key, unwrapped_key);
471 }
472}