1use std::str::FromStr;
2
3use bitwarden_core::key_management::KeySlotIds;
4#[allow(deprecated)]
5use bitwarden_crypto::dangerous_derive_kdf_material;
6use bitwarden_crypto::{
7 BitwardenLegacyKeyBytes, CoseKeyBytes, CoseSerializable, CoseSign1Bytes, CryptoError,
8 Decryptable, EncString, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey,
9 OctetStreamBytes, Pkcs8PrivateKeyBytes, PrimitiveEncryptable, PrivateKey, PublicKey,
10 PublicKeyEncryptionAlgorithm, SignatureAlgorithm, SignedPublicKey, SigningKey,
11 SpkiPublicKeyBytes, SymmetricCryptoKey, UnsignedSharedKey, VerifyingKey,
12};
13use rand::RngExt;
14use rsa::{
15 Oaep, RsaPrivateKey, RsaPublicKey,
16 pkcs8::{DecodePrivateKey, DecodePublicKey},
17};
18use sha1::Sha1;
19use wasm_bindgen::prelude::*;
20
21#[wasm_bindgen]
26pub struct PureCrypto {}
27
28#[wasm_bindgen]
30impl PureCrypto {
31 pub fn symmetric_decrypt(enc_string: String, key: Vec<u8>) -> Result<String, CryptoError> {
34 Self::symmetric_decrypt_string(enc_string, key)
35 }
36
37 pub fn symmetric_decrypt_string(
38 enc_string: String,
39 key: Vec<u8>,
40 ) -> Result<String, CryptoError> {
41 let _span = tracing::info_span!("PureCrypto::symmetric_decrypt_string").entered();
42 let key = &BitwardenLegacyKeyBytes::from(key);
43 EncString::from_str(&enc_string)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?)
44 }
45
46 pub fn symmetric_decrypt_bytes(
47 enc_string: String,
48 key: Vec<u8>,
49 ) -> Result<Vec<u8>, CryptoError> {
50 let _span = tracing::info_span!("PureCrypto::symmetric_decrypt_bytes").entered();
51 let key = &BitwardenLegacyKeyBytes::from(key);
52 EncString::from_str(&enc_string)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?)
53 }
54
55 pub fn symmetric_decrypt_array_buffer(
58 enc_bytes: Vec<u8>,
59 key: Vec<u8>,
60 ) -> Result<Vec<u8>, CryptoError> {
61 Self::symmetric_decrypt_filedata(enc_bytes, key)
62 }
63
64 pub fn symmetric_decrypt_filedata(
65 enc_bytes: Vec<u8>,
66 key: Vec<u8>,
67 ) -> Result<Vec<u8>, CryptoError> {
68 let _span = tracing::info_span!("PureCrypto::symmetric_decrypt_filedata").entered();
69 let key = &BitwardenLegacyKeyBytes::from(key);
70 EncString::from_buffer(&enc_bytes)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?)
71 }
72
73 pub fn symmetric_encrypt_string(plain: String, key: Vec<u8>) -> Result<String, CryptoError> {
74 let _span = tracing::info_span!("PureCrypto::symmetric_encrypt_string").entered();
75 let key = &BitwardenLegacyKeyBytes::from(key);
76 plain
77 .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?)
78 .map(|enc| enc.to_string())
79 }
80
81 pub fn symmetric_encrypt_bytes(plain: Vec<u8>, key: Vec<u8>) -> Result<String, CryptoError> {
83 let _span = tracing::info_span!("PureCrypto::symmetric_encrypt_bytes").entered();
84 let key = &BitwardenLegacyKeyBytes::from(key);
85 OctetStreamBytes::from(plain)
86 .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?)
87 .map(|enc| enc.to_string())
88 }
89
90 pub fn symmetric_encrypt_filedata(
91 plain: Vec<u8>,
92 key: Vec<u8>,
93 ) -> Result<Vec<u8>, CryptoError> {
94 let _span = tracing::info_span!("PureCrypto::symmetric_encrypt_filedata").entered();
95 let key = &BitwardenLegacyKeyBytes::from(key);
96 OctetStreamBytes::from(plain)
97 .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?)?
98 .to_buffer()
99 }
100
101 pub fn decrypt_user_key_with_master_password(
102 encrypted_user_key: String,
103 master_password: String,
104 email: String,
105 kdf: Kdf,
106 ) -> Result<Vec<u8>, CryptoError> {
107 let _span = tracing::info_span!(
108 "PureCrypto::decrypt_user_key_with_master_password",
109 email = %email,
110 kdf = ?kdf
111 )
112 .entered();
113
114 let master_key = MasterKey::derive(master_password.as_str(), email.as_str(), &kdf)?;
115 let encrypted_user_key = EncString::from_str(&encrypted_user_key)?;
116 let result = master_key
117 .decrypt_user_key(encrypted_user_key)
118 .map_err(|_| CryptoError::InvalidKey)?;
119 Ok(result.to_encoded().to_vec())
120 }
121
122 pub fn encrypt_user_key_with_master_password(
123 user_key: Vec<u8>,
124 master_password: String,
125 email: String,
126 kdf: Kdf,
127 ) -> Result<String, CryptoError> {
128 let _span = tracing::info_span!(
129 "PureCrypto::encrypt_user_key_with_master_password",
130 email = %email,
131 kdf = ?kdf
132 )
133 .entered();
134 let master_key = MasterKey::derive(master_password.as_str(), email.as_str(), &kdf)?;
135 let user_key = &BitwardenLegacyKeyBytes::from(user_key);
136 let user_key = SymmetricCryptoKey::try_from(user_key)?;
137 let result = master_key.encrypt_user_key(&user_key)?;
138 Ok(result.to_string())
139 }
140
141 pub fn make_user_key_aes256_cbc_hmac() -> Vec<u8> {
142 SymmetricCryptoKey::make_aes256_cbc_hmac_key()
143 .to_encoded()
144 .to_vec()
145 }
146
147 pub fn make_user_key_xchacha20_poly1305() -> Vec<u8> {
148 SymmetricCryptoKey::make_xchacha20_poly1305_key()
149 .to_encoded()
150 .to_vec()
151 }
152
153 pub fn wrap_symmetric_key(
156 key_to_be_wrapped: Vec<u8>,
157 wrapping_key: Vec<u8>,
158 ) -> Result<String, CryptoError> {
159 let _span = tracing::info_span!("PureCrypto::wrap_symmetric_key").entered();
160 let tmp_store: KeyStore<KeySlotIds> = KeyStore::default();
161 let mut context = tmp_store.context();
162 let wrapping_key =
163 SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(wrapping_key))?;
164 let wrapping_key = context.add_local_symmetric_key(wrapping_key);
165 let key_to_be_wrapped =
166 SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(key_to_be_wrapped))?;
167 let key_to_wrap = context.add_local_symmetric_key(key_to_be_wrapped);
168 Ok(context
170 .wrap_symmetric_key(wrapping_key, key_to_wrap)?
171 .to_string())
172 }
173
174 pub fn unwrap_symmetric_key(
177 wrapped_key: String,
178 wrapping_key: Vec<u8>,
179 ) -> Result<Vec<u8>, CryptoError> {
180 let _span = tracing::info_span!("PureCrypto::unwrap_symmetric_key").entered();
181 let tmp_store: KeyStore<KeySlotIds> = KeyStore::default();
182 let mut context = tmp_store.context();
183 let wrapping_key =
184 SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(wrapping_key))?;
185 let wrapping_key = context.add_local_symmetric_key(wrapping_key);
186 let unwrapped = context
188 .unwrap_symmetric_key(wrapping_key, &EncString::from_str(wrapped_key.as_str())?)?;
189 #[allow(deprecated)]
190 let key = context.dangerous_get_symmetric_key(unwrapped)?;
191 Ok(key.to_encoded().to_vec())
192 }
193
194 pub fn wrap_encapsulation_key(
200 encapsulation_key: Vec<u8>,
201 wrapping_key: Vec<u8>,
202 ) -> Result<String, CryptoError> {
203 let _span = tracing::info_span!("PureCrypto::wrap_encapsulation_key").entered();
204 let tmp_store: KeyStore<KeySlotIds> = KeyStore::default();
205 let mut context = tmp_store.context();
206 let wrapping_key = context.add_local_symmetric_key(SymmetricCryptoKey::try_from(
207 &BitwardenLegacyKeyBytes::from(wrapping_key),
208 )?);
209 Ok(SpkiPublicKeyBytes::from(encapsulation_key)
210 .encrypt(&mut context, wrapping_key)?
211 .to_string())
212 }
213
214 pub fn unwrap_encapsulation_key(
217 wrapped_key: String,
218 wrapping_key: Vec<u8>,
219 ) -> Result<Vec<u8>, CryptoError> {
220 let _span = tracing::info_span!("PureCrypto::unwrap_encapsulation_key").entered();
221 let tmp_store: KeyStore<KeySlotIds> = KeyStore::default();
222 let mut context = tmp_store.context();
223 let wrapping_key = context.add_local_symmetric_key(SymmetricCryptoKey::try_from(
224 &BitwardenLegacyKeyBytes::from(wrapping_key),
225 )?);
226 EncString::from_str(wrapped_key.as_str())?.decrypt(&mut context, wrapping_key)
227 }
228
229 pub fn wrap_decapsulation_key(
232 decapsulation_key: Vec<u8>,
233 wrapping_key: Vec<u8>,
234 ) -> Result<String, CryptoError> {
235 let _span = tracing::info_span!("PureCrypto::wrap_decapsulation_key").entered();
236 let tmp_store: KeyStore<KeySlotIds> = KeyStore::default();
237 let mut context = tmp_store.context();
238 let wrapping_key = context.add_local_symmetric_key(SymmetricCryptoKey::try_from(
239 &BitwardenLegacyKeyBytes::from(wrapping_key),
240 )?);
241 Ok(Pkcs8PrivateKeyBytes::from(decapsulation_key)
242 .encrypt(&mut context, wrapping_key)?
243 .to_string())
244 }
245
246 pub fn unwrap_decapsulation_key(
249 wrapped_key: String,
250 wrapping_key: Vec<u8>,
251 ) -> Result<Vec<u8>, CryptoError> {
252 let _span = tracing::info_span!("PureCrypto::unwrap_decapsulation_key").entered();
253 let tmp_store: KeyStore<KeySlotIds> = KeyStore::default();
254 let mut context = tmp_store.context();
255 let wrapping_key = context.add_local_symmetric_key(SymmetricCryptoKey::try_from(
256 &BitwardenLegacyKeyBytes::from(wrapping_key),
257 )?);
258 EncString::from_str(wrapped_key.as_str())?.decrypt(&mut context, wrapping_key)
259 }
260
261 pub fn encapsulate_key_unsigned(
265 shared_key: Vec<u8>,
266 encapsulation_key: Vec<u8>,
267 ) -> Result<String, CryptoError> {
268 let _span = tracing::info_span!("PureCrypto::encapsulate_key_unsigned").entered();
269 let encapsulation_key = PublicKey::from_der(&SpkiPublicKeyBytes::from(encapsulation_key))?;
270 #[expect(deprecated)]
271 Ok(UnsignedSharedKey::encapsulate_key_unsigned(
272 &SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(shared_key))?,
273 &encapsulation_key,
274 )?
275 .to_string())
276 }
277
278 pub fn decapsulate_key_unsigned(
282 encapsulated_key: String,
283 decapsulation_key: Vec<u8>,
284 ) -> Result<Vec<u8>, CryptoError> {
285 let _span = tracing::info_span!("PureCrypto::decapsulate_key_unsigned").entered();
286 #[expect(deprecated)]
287 Ok(UnsignedSharedKey::from_str(encapsulated_key.as_str())?
288 .decapsulate_key_unsigned(&PrivateKey::from_der(&Pkcs8PrivateKeyBytes::from(
289 decapsulation_key,
290 ))?)?
291 .to_encoded()
292 .to_vec())
293 }
294
295 pub fn verifying_key_for_signing_key(
298 signing_key: String,
299 wrapping_key: Vec<u8>,
300 ) -> Result<Vec<u8>, CryptoError> {
301 let _span = tracing::info_span!("PureCrypto::verifying_key_for_signing_key").entered();
302 let bytes = Self::symmetric_decrypt_bytes(signing_key, wrapping_key)?;
303 let signing_key = SigningKey::from_cose(&CoseKeyBytes::from(bytes))?;
304 let verifying_key = signing_key.to_verifying_key();
305 Ok(verifying_key.to_cose().to_vec())
306 }
307
308 pub fn key_algorithm_for_verifying_key(
310 verifying_key: Vec<u8>,
311 ) -> Result<SignatureAlgorithm, CryptoError> {
312 let _span = tracing::info_span!("PureCrypto::key_algorithm_for_verifying_key").entered();
313 let verifying_key = VerifyingKey::from_cose(&CoseKeyBytes::from(verifying_key))?;
314 let algorithm = verifying_key.algorithm();
315 Ok(algorithm)
316 }
317
318 pub fn verify_and_unwrap_signed_public_key(
323 signed_public_key: Vec<u8>,
324 verifying_key: Vec<u8>,
325 ) -> Result<Vec<u8>, CryptoError> {
326 let _span =
327 tracing::info_span!("PureCrypto::verify_and_unwrap_signed_public_key").entered();
328 let signed_public_key = SignedPublicKey::try_from(CoseSign1Bytes::from(signed_public_key))?;
329 let verifying_key = VerifyingKey::from_cose(&CoseKeyBytes::from(verifying_key))?;
330 signed_public_key
331 .verify_and_unwrap(&verifying_key)
332 .map(|public_key| public_key.to_der())?
333 .map(|pk| pk.to_vec())
334 }
335
336 pub fn derive_kdf_material(
338 password: &[u8],
339 salt: &[u8],
340 kdf: Kdf,
341 ) -> Result<Vec<u8>, CryptoError> {
342 let _span = tracing::info_span!("PureCrypto::derive_kdf_material", kdf = ?kdf).entered();
343 #[allow(deprecated)]
344 dangerous_derive_kdf_material(password, salt, &kdf)
345 }
346
347 pub fn decrypt_user_key_with_master_key(
348 encrypted_user_key: String,
349 master_key: Vec<u8>,
350 ) -> Result<Vec<u8>, CryptoError> {
351 let _span = tracing::info_span!("PureCrypto::decrypt_user_key_with_master_key").entered();
352 let master_key = &BitwardenLegacyKeyBytes::from(master_key);
353 let master_key = &SymmetricCryptoKey::try_from(master_key)?;
354 let master_key = MasterKey::try_from(master_key)?;
355 let encrypted_user_key = EncString::from_str(&encrypted_user_key)?;
356 let result = master_key
357 .decrypt_user_key(encrypted_user_key)
358 .map_err(|_| CryptoError::InvalidKey)?;
359 Ok(result.to_encoded().to_vec())
360 }
361
362 pub fn rsa_extract_public_key(private_key: Vec<u8>) -> Result<Vec<u8>, RsaError> {
366 let _span = tracing::info_span!("PureCrypto::rsa_extract_public_key").entered();
367 let private_key = PrivateKey::from_der(&Pkcs8PrivateKeyBytes::from(private_key))
368 .map_err(|_| RsaError::KeyParse)?;
369 let public_key = private_key.to_public_key();
370 Ok(public_key
371 .to_der()
372 .map_err(|_| RsaError::KeySerialize)?
373 .to_vec())
374 }
375
376 pub fn rsa_generate_keypair() -> Result<Vec<u8>, RsaError> {
379 let _span = tracing::info_span!("PureCrypto::rsa_generate_keypair").entered();
380 let private_key = PrivateKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
381 Ok(private_key
382 .to_der()
383 .map_err(|_| RsaError::KeySerialize)?
384 .to_vec())
385 }
386
387 pub fn rsa_decrypt_data(
390 encrypted_data: Vec<u8>,
391 private_key: Vec<u8>,
392 ) -> Result<Vec<u8>, RsaError> {
393 let _span = tracing::info_span!("PureCrypto::rsa_decrypt_data").entered();
394 let private_key = RsaPrivateKey::from_pkcs8_der(private_key.as_slice())
395 .map_err(|_| RsaError::KeyParse)?;
396 let padding = Oaep::<Sha1>::new();
397 private_key
398 .decrypt(padding, &encrypted_data)
399 .map_err(|_| RsaError::Decryption)
400 }
401
402 pub fn rsa_encrypt_data(plain_data: Vec<u8>, public_key: Vec<u8>) -> Result<Vec<u8>, RsaError> {
405 let _span = tracing::info_span!("PureCrypto::rsa_encrypt_data").entered();
406 let public_key = RsaPublicKey::from_public_key_der(public_key.as_slice())
407 .map_err(|_| RsaError::KeyParse)?;
408 let padding = Oaep::<Sha1>::new();
409 let mut rng = rand::rng();
410 public_key
411 .encrypt(&mut rng, padding, &plain_data)
412 .map_err(|_| RsaError::Encryption)
413 }
414
415 pub fn random_number(min: u32, max: u32) -> u32 {
418 let _span = tracing::info_span!("PureCrypto::random_number").entered();
419 let mut rng = rand::rng();
420 rng.random_range(min..=max)
421 }
422
423 pub fn new_guid() -> String {
425 let _span = tracing::info_span!("PureCrypto::new_guid").entered();
426 uuid::Uuid::new_v4().to_string()
427 }
428}
429
430#[wasm_bindgen]
431#[derive(Debug)]
432pub enum RsaError {
433 Decryption,
434 Encryption,
435 KeyParse,
436 KeySerialize,
437}
438
439#[cfg(test)]
440mod tests {
441 use std::{num::NonZero, str::FromStr};
442
443 use bitwarden_crypto::EncString;
444
445 use super::*;
446
447 const KEY: &[u8] = &[
448 81, 142, 1, 228, 222, 3, 3, 133, 34, 176, 35, 66, 150, 6, 109, 70, 190, 149, 47, 47, 89,
449 23, 144, 87, 92, 46, 220, 13, 148, 106, 162, 234, 202, 139, 136, 33, 16, 200, 8, 73, 176,
450 172, 185, 187, 224, 10, 65, 223, 228, 54, 92, 181, 8, 213, 162, 221, 117, 254, 245, 111,
451 55, 211, 77, 29,
452 ];
453
454 const ENCRYPTED: &str = "2.Dh7AFLXR+LXcxUaO5cRjpg==|uXyhubjAoNH8lTdy/zgJDQ==|cHEMboj0MYsU5yDRQ1rLCgxcjNbKRc1PWKuv8bpU5pM=";
455 const DECRYPTED: &str = "test";
456 const DECRYPTED_BYTES: &[u8] = b"test";
457 const ENCRYPTED_BYTES: &[u8] = &[
458 2, 209, 195, 115, 49, 205, 253, 128, 162, 169, 246, 175, 217, 144, 73, 108, 191, 27, 113,
459 69, 55, 94, 142, 62, 129, 204, 173, 130, 37, 42, 97, 209, 25, 192, 64, 126, 112, 139, 248,
460 2, 89, 112, 178, 83, 25, 77, 130, 187, 127, 85, 179, 211, 159, 186, 111, 44, 109, 211, 18,
461 120, 104, 144, 4, 76, 3,
462 ];
463
464 const PEM_KEY: &str = "-----BEGIN PRIVATE KEY-----
465MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDiTQVuzhdygFz5
466qv14i+XFDGTnDravzUQT1hPKPGUZOUSZ1gwdNgkWqOIaOnR65BHEnL0sp4bnuiYc
467afeK2JAW5Sc8Z7IxBNSuAwhQmuKx3RochMIiuCkI2/p+JvUQoJu6FBNm8OoJ4Cwm
468qqHGZESMfnpQDCuDrB3JdJEdXhtmnl0C48sGjOk3WaBMcgGqn8LbJDUlyu1zdqyv
469b0waJf0iV4PJm2fkUl7+57D/2TkpbCqURVnZK1FFIEg8mr6FzSN1F2pOfktkNYZw
470P7MSNR7o81CkRSCMr7EkIVa+MZYMBx106BMK7FXgWB7nbSpsWKxBk7ZDHkID2fam
471rEcVtrzDAgMBAAECggEBAKwq9OssGGKgjhvUnyrLJHAZ0dqIMyzk+dotkLjX4gKi
472szJmyqiep6N5sStLNbsZMPtoU/RZMCW0VbJgXFhiEp2YkZU/Py5UAoqw++53J+kx
4730d/IkPphKbb3xUec0+1mg5O6GljDCQuiZXS1dIa/WfeZcezclW6Dz9WovY6ePjJ+
4748vEBR1icbNKzyeINd6MtPtpcgQPHtDwHvhPyUDbKDYGbLvjh9nui8h4+ZUlXKuVR
475jB0ChxiKV1xJRjkrEVoulOOicd5r597WfB2ghax3pvRZ4MdXemCXm3gQYqPVKach
476vGU+1cPQR/MBJZpxT+EZA97xwtFS3gqwbxJaNFcoE8ECgYEA9OaeYZhQPDo485tI
4771u/Z7L/3PNape9hBQIXoW7+MgcQ5NiWqYh8Jnj43EIYa0wM/ECQINr1Za8Q5e6KR
478J30FcU+kfyjuQ0jeXdNELGU/fx5XXNg/vV8GevHwxRlwzqZTCg6UExUZzbYEQqd7
479l+wPyETGeua5xCEywA1nX/D101kCgYEA7I6aMFjhEjO71RmzNhqjKJt6DOghoOfQ
480TjhaaanNEhLYSbenFz1mlb21mW67ulmz162saKdIYLxQNJIP8ZPmxh4ummOJI8w9
481ClHfo8WuCI2hCjJ19xbQJocSbTA5aJg6lA1IDVZMDbQwsnAByPRGpaLHBT/Q9Bye
482KvCMB+9amXsCgYEAx65yXSkP4sumPBrVHUub6MntERIGRxBgw/drKcPZEMWp0FiN
483wEuGUBxyUWrG3F69QK/gcqGZE6F/LSu0JvptQaKqgXQiMYJsrRvhbkFvsHpQyUcZ
484UZL1ebFjm5HOxPAgrQaN/bEqxOwwNRjSUWEMzUImg3c06JIZCzbinvudtKECgYEA
485kY3JF/iIPI/yglP27lKDlCfeeHSYxI3+oTKRhzSAxx8rUGidenJAXeDGDauR/T7W
486pt3pGNfddBBK9Z3uC4Iq3DqUCFE4f/taj7ADAJ1Q0Vh7/28/IJM77ojr8J1cpZwN
487Zy2o6PPxhfkagaDjqEeN9Lrs5LD4nEvDkr5CG1vOjmMCgYEAvIBFKRm31NyF8jLi
488CVuPwC5PzrW5iThDmsWTaXFpB3esUsbICO2pEz872oeQS+Em4GO5vXUlpbbFPzup
489PFhA8iMJ8TAvemhvc7oM0OZqpU6p3K4seHf6BkwLxumoA3vDJfovu9RuXVcJVOnf
490DnqOsltgPomWZ7xVfMkm9niL2OA=
491-----END PRIVATE KEY-----";
492
493 const SIGNING_KEY_WRAPPING_KEY: &[u8] = &[
494 40, 215, 110, 199, 183, 4, 182, 78, 213, 123, 251, 113, 72, 223, 57, 2, 3, 81, 136, 19, 88,
495 78, 206, 176, 158, 251, 211, 84, 1, 199, 203, 142, 176, 227, 187, 136, 209, 79, 23, 13, 44,
496 224, 90, 10, 191, 72, 22, 227, 171, 105, 107, 139, 24, 49, 9, 150, 103, 139, 151, 204, 165,
497 121, 165, 71,
498 ];
499 const SIGNING_KEY: &[u8] = &[
500 166, 1, 1, 2, 80, 123, 226, 102, 228, 194, 232, 71, 30, 183, 42, 219, 193, 50, 30, 21, 43,
501 3, 39, 4, 130, 1, 2, 35, 88, 32, 148, 2, 66, 69, 169, 57, 129, 240, 37, 18, 225, 211, 207,
502 133, 66, 143, 204, 238, 113, 152, 43, 112, 133, 173, 179, 17, 202, 135, 175, 237, 1, 59,
503 32, 6,
504 ];
505 const VERIFYING_KEY: &[u8] = &[
506 166, 1, 1, 2, 80, 123, 226, 102, 228, 194, 232, 71, 30, 183, 42, 219, 193, 50, 30, 21, 43,
507 3, 39, 4, 129, 2, 32, 6, 33, 88, 32, 63, 70, 49, 37, 246, 232, 146, 144, 83, 224, 0, 17,
508 111, 248, 16, 242, 69, 195, 84, 46, 39, 218, 55, 63, 90, 112, 148, 91, 224, 186, 122, 4,
509 ];
510 const PUBLIC_KEY: &[u8] = &[
511 48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0,
512 48, 130, 1, 10, 2, 130, 1, 1, 0, 173, 4, 54, 63, 125, 12, 254, 38, 115, 34, 95, 164, 148,
513 115, 86, 140, 129, 74, 19, 70, 212, 212, 130, 163, 105, 249, 101, 120, 154, 46, 194, 250,
514 229, 242, 156, 67, 109, 179, 187, 134, 59, 235, 60, 107, 144, 163, 35, 22, 109, 230, 134,
515 243, 44, 243, 79, 84, 76, 11, 64, 56, 236, 167, 98, 26, 30, 213, 143, 105, 52, 92, 129, 92,
516 88, 22, 115, 135, 63, 215, 79, 8, 11, 183, 124, 10, 73, 231, 170, 110, 210, 178, 22, 100,
517 76, 75, 118, 202, 252, 204, 67, 204, 152, 6, 244, 208, 161, 146, 103, 225, 233, 239, 88,
518 195, 88, 150, 230, 111, 62, 142, 12, 157, 184, 155, 34, 84, 237, 111, 11, 97, 56, 152, 130,
519 14, 72, 123, 140, 47, 137, 5, 97, 166, 4, 147, 111, 23, 65, 78, 63, 208, 198, 50, 161, 39,
520 80, 143, 100, 194, 37, 252, 194, 53, 207, 166, 168, 250, 165, 121, 9, 207, 90, 36, 213,
521 211, 84, 255, 14, 205, 114, 135, 217, 137, 105, 232, 58, 169, 222, 10, 13, 138, 203, 16,
522 12, 122, 72, 227, 95, 160, 111, 54, 200, 198, 143, 156, 15, 143, 196, 50, 150, 204, 144,
523 255, 162, 248, 50, 28, 47, 66, 9, 83, 158, 67, 9, 50, 147, 174, 147, 200, 199, 238, 190,
524 248, 60, 114, 218, 32, 209, 120, 218, 17, 234, 14, 128, 192, 166, 33, 60, 73, 227, 108,
525 201, 41, 160, 81, 133, 171, 205, 221, 2, 3, 1, 0, 1,
526 ];
527
528 const SIGNED_PUBLIC_KEY: &[u8] = &[
529 132, 88, 30, 164, 1, 39, 3, 24, 60, 4, 80, 123, 226, 102, 228, 194, 232, 71, 30, 183, 42,
530 219, 193, 50, 30, 21, 43, 58, 0, 1, 56, 127, 1, 160, 89, 1, 78, 163, 105, 97, 108, 103,
531 111, 114, 105, 116, 104, 109, 0, 109, 99, 111, 110, 116, 101, 110, 116, 70, 111, 114, 109,
532 97, 116, 0, 105, 112, 117, 98, 108, 105, 99, 75, 101, 121, 89, 1, 38, 48, 130, 1, 34, 48,
533 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 10, 2,
534 130, 1, 1, 0, 173, 4, 54, 63, 125, 12, 254, 38, 115, 34, 95, 164, 148, 115, 86, 140, 129,
535 74, 19, 70, 212, 212, 130, 163, 105, 249, 101, 120, 154, 46, 194, 250, 229, 242, 156, 67,
536 109, 179, 187, 134, 59, 235, 60, 107, 144, 163, 35, 22, 109, 230, 134, 243, 44, 243, 79,
537 84, 76, 11, 64, 56, 236, 167, 98, 26, 30, 213, 143, 105, 52, 92, 129, 92, 88, 22, 115, 135,
538 63, 215, 79, 8, 11, 183, 124, 10, 73, 231, 170, 110, 210, 178, 22, 100, 76, 75, 118, 202,
539 252, 204, 67, 204, 152, 6, 244, 208, 161, 146, 103, 225, 233, 239, 88, 195, 88, 150, 230,
540 111, 62, 142, 12, 157, 184, 155, 34, 84, 237, 111, 11, 97, 56, 152, 130, 14, 72, 123, 140,
541 47, 137, 5, 97, 166, 4, 147, 111, 23, 65, 78, 63, 208, 198, 50, 161, 39, 80, 143, 100, 194,
542 37, 252, 194, 53, 207, 166, 168, 250, 165, 121, 9, 207, 90, 36, 213, 211, 84, 255, 14, 205,
543 114, 135, 217, 137, 105, 232, 58, 169, 222, 10, 13, 138, 203, 16, 12, 122, 72, 227, 95,
544 160, 111, 54, 200, 198, 143, 156, 15, 143, 196, 50, 150, 204, 144, 255, 162, 248, 50, 28,
545 47, 66, 9, 83, 158, 67, 9, 50, 147, 174, 147, 200, 199, 238, 190, 248, 60, 114, 218, 32,
546 209, 120, 218, 17, 234, 14, 128, 192, 166, 33, 60, 73, 227, 108, 201, 41, 160, 81, 133,
547 171, 205, 221, 2, 3, 1, 0, 1, 88, 64, 207, 18, 4, 242, 149, 31, 37, 255, 243, 62, 78, 46,
548 12, 150, 134, 159, 69, 89, 62, 222, 132, 12, 177, 74, 155, 80, 154, 37, 77, 176, 19, 142,
549 73, 4, 134, 242, 24, 56, 54, 38, 178, 59, 11, 118, 230, 159, 87, 91, 20, 237, 188, 186,
550 216, 86, 189, 50, 46, 173, 117, 36, 54, 105, 216, 9,
551 ];
552
553 const DERIVED_KDF_MATERIAL_PBKDF2: &[u8] = &[
554 129, 57, 137, 140, 156, 220, 110, 212, 201, 255, 52, 182, 22, 206, 221, 66, 136, 199, 181,
555 89, 252, 175, 82, 168, 79, 204, 88, 174, 166, 60, 52, 79,
556 ];
557 const DERIVED_KDF_MATERIAL_ARGON2ID: &[u8] = &[
558 221, 57, 158, 206, 27, 154, 188, 170, 33, 198, 250, 144, 191, 231, 29, 74, 201, 102, 253,
559 77, 8, 128, 173, 111, 217, 41, 125, 9, 156, 52, 112, 140,
560 ];
561
562 #[test]
563 fn test_symmetric_decrypt() {
564 let enc_string = EncString::from_str(ENCRYPTED).unwrap();
565
566 let result = PureCrypto::symmetric_decrypt_string(enc_string.to_string(), KEY.to_vec());
567 assert!(result.is_ok());
568 assert_eq!(result.unwrap(), DECRYPTED);
569 }
570
571 #[test]
572 fn test_symmetric_encrypt() {
573 let result = PureCrypto::symmetric_encrypt_string(DECRYPTED.to_string(), KEY.to_vec());
574 assert!(result.is_ok());
575 }
577
578 #[test]
579 fn test_symmetric_string_round_trip() {
580 let encrypted =
581 PureCrypto::symmetric_encrypt_string(DECRYPTED.to_string(), KEY.to_vec()).unwrap();
582 let decrypted =
583 PureCrypto::symmetric_decrypt_string(encrypted.clone(), KEY.to_vec()).unwrap();
584 assert_eq!(decrypted, DECRYPTED);
585 }
586
587 #[test]
588 fn test_symmetric_bytes_round_trip() {
589 let encrypted =
590 PureCrypto::symmetric_encrypt_bytes(DECRYPTED.as_bytes().to_vec(), KEY.to_vec())
591 .unwrap();
592 let decrypted =
593 PureCrypto::symmetric_decrypt_bytes(encrypted.clone(), KEY.to_vec()).unwrap();
594 assert_eq!(decrypted, DECRYPTED.as_bytes().to_vec());
595 }
596
597 #[test]
598 fn test_symmetric_decrypt_array_buffer() {
599 let result = PureCrypto::symmetric_decrypt_filedata(ENCRYPTED_BYTES.to_vec(), KEY.to_vec());
600 assert!(result.is_ok());
601 assert_eq!(result.unwrap(), DECRYPTED_BYTES);
602 }
603
604 #[test]
605 fn test_symmetric_encrypt_to_array_buffer() {
606 let result = PureCrypto::symmetric_encrypt_filedata(DECRYPTED_BYTES.to_vec(), KEY.to_vec());
607 assert!(result.is_ok());
608 }
610
611 #[test]
612 fn test_symmetric_filedata_round_trip() {
613 let encrypted =
614 PureCrypto::symmetric_encrypt_filedata(DECRYPTED_BYTES.to_vec(), KEY.to_vec()).unwrap();
615 let decrypted =
616 PureCrypto::symmetric_decrypt_filedata(encrypted.clone(), KEY.to_vec()).unwrap();
617 assert_eq!(decrypted, DECRYPTED_BYTES);
618 }
619
620 #[test]
621 fn test_make_aes256_cbc_hmac_key() {
622 let key = PureCrypto::make_user_key_aes256_cbc_hmac();
623 assert_eq!(key.len(), 64);
624 }
625
626 #[test]
627 fn test_make_xchacha20_poly1305_key() {
628 let key = PureCrypto::make_user_key_xchacha20_poly1305();
629 assert!(key.len() > 64);
630 }
631
632 #[test]
633 fn roundtrip_encrypt_user_key_with_master_password() {
634 let master_password = "test";
635 let email = "[email protected]";
636 let kdf = Kdf::PBKDF2 {
637 iterations: NonZero::try_from(600000).unwrap(),
638 };
639 let user_key = PureCrypto::make_user_key_aes256_cbc_hmac();
640 let encrypted_user_key = PureCrypto::encrypt_user_key_with_master_password(
641 user_key.clone(),
642 master_password.to_string(),
643 email.to_string(),
644 kdf.clone(),
645 )
646 .unwrap();
647 let decrypted_user_key = PureCrypto::decrypt_user_key_with_master_password(
648 encrypted_user_key,
649 master_password.to_string(),
650 email.to_string(),
651 kdf,
652 )
653 .unwrap();
654 assert_eq!(user_key, decrypted_user_key);
655 }
656
657 #[test]
658 fn test_wrap_unwrap_symmetric_key() {
659 let key_to_be_wrapped = PureCrypto::make_user_key_aes256_cbc_hmac();
660 let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac();
661 let wrapped_key =
662 PureCrypto::wrap_symmetric_key(key_to_be_wrapped.clone(), wrapping_key.clone())
663 .unwrap();
664 let unwrapped_key = PureCrypto::unwrap_symmetric_key(wrapped_key, wrapping_key).unwrap();
665 assert_eq!(key_to_be_wrapped, unwrapped_key);
666 }
667
668 #[test]
669 fn test_wrap_encapsulation_key() {
670 let decapsulation_key = PrivateKey::from_pem(PEM_KEY).unwrap();
671 let encapsulation_key = decapsulation_key
672 .to_public_key()
673 .to_der()
674 .unwrap()
675 .as_ref()
676 .to_vec();
677 let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac();
678 let wrapped_key =
679 PureCrypto::wrap_encapsulation_key(encapsulation_key.clone(), wrapping_key.clone())
680 .unwrap();
681 let unwrapped_key =
682 PureCrypto::unwrap_encapsulation_key(wrapped_key, wrapping_key).unwrap();
683 assert_eq!(encapsulation_key, unwrapped_key);
684 }
685
686 #[test]
687 fn test_wrap_decapsulation_key() {
688 let decapsulation_key = PrivateKey::from_pem(PEM_KEY).unwrap();
689 let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac();
690 let wrapped_key = PureCrypto::wrap_decapsulation_key(
691 decapsulation_key.to_der().unwrap().to_vec(),
692 wrapping_key.clone(),
693 )
694 .unwrap();
695 let unwrapped_key =
696 PureCrypto::unwrap_decapsulation_key(wrapped_key, wrapping_key).unwrap();
697 assert_eq!(decapsulation_key.to_der().unwrap().to_vec(), unwrapped_key);
698 }
699
700 #[test]
701 fn test_encapsulate_key_unsigned() {
702 let shared_key = PureCrypto::make_user_key_aes256_cbc_hmac();
703 let decapsulation_key = PrivateKey::from_pem(PEM_KEY).unwrap();
704 let encapsulation_key = decapsulation_key.to_public_key().to_der().unwrap();
705 let encapsulated_key = PureCrypto::encapsulate_key_unsigned(
706 shared_key.clone(),
707 encapsulation_key.clone().to_vec(),
708 )
709 .unwrap();
710 let unwrapped_key = PureCrypto::decapsulate_key_unsigned(
711 encapsulated_key,
712 decapsulation_key.to_der().unwrap().to_vec(),
713 )
714 .unwrap();
715 assert_eq!(shared_key, unwrapped_key);
716 }
717
718 #[test]
719 fn test_key_algorithm_for_verifying_key() {
720 let verifying_key =
721 VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY.to_vec())).unwrap();
722 let algorithm =
723 PureCrypto::key_algorithm_for_verifying_key(verifying_key.to_cose().to_vec()).unwrap();
724 assert_eq!(algorithm, SignatureAlgorithm::Ed25519);
725 }
726
727 #[test]
728 fn test_verifying_key_for_signing_key() {
729 let wrapped_signing_key = PureCrypto::symmetric_encrypt_bytes(
730 SIGNING_KEY.to_vec(),
731 SIGNING_KEY_WRAPPING_KEY.to_vec(),
732 )
733 .unwrap();
734 let verifying_key =
735 VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY.to_vec())).unwrap();
736 let verifying_key_derived = PureCrypto::verifying_key_for_signing_key(
737 wrapped_signing_key.to_string(),
738 SIGNING_KEY_WRAPPING_KEY.to_vec(),
739 )
740 .unwrap();
741 let verifying_key_derived =
742 VerifyingKey::from_cose(&CoseKeyBytes::from(verifying_key_derived)).unwrap();
743 assert_eq!(verifying_key.to_cose(), verifying_key_derived.to_cose());
744 }
745
746 #[test]
747 fn test_verify_and_unwrap_signed_public_key() {
748 let public_key = PureCrypto::verify_and_unwrap_signed_public_key(
749 SIGNED_PUBLIC_KEY.to_vec(),
750 VERIFYING_KEY.to_vec(),
751 )
752 .unwrap();
753 assert_eq!(public_key, PUBLIC_KEY);
754 }
755
756 #[test]
757 fn test_derive_pbkdf2_output() {
758 let password = "test_password".as_bytes();
759 let email = "[email protected]".as_bytes();
760 let kdf = Kdf::PBKDF2 {
761 iterations: NonZero::try_from(600000).unwrap(),
762 };
763 let derived_key = PureCrypto::derive_kdf_material(password, email, kdf).unwrap();
764 assert_eq!(derived_key, DERIVED_KDF_MATERIAL_PBKDF2);
765 }
766
767 #[test]
768 fn test_derived_argon2_output() {
769 let password = "test_password".as_bytes();
770 let email = "[email protected]".as_bytes();
771 let kdf = Kdf::Argon2id {
772 iterations: NonZero::try_from(3).unwrap(),
773 memory: NonZero::try_from(64).unwrap(),
774 parallelism: NonZero::try_from(4).unwrap(),
775 };
776 let derived_key = PureCrypto::derive_kdf_material(password, email, kdf).unwrap();
777 assert_eq!(derived_key, DERIVED_KDF_MATERIAL_ARGON2ID);
778 }
779
780 #[test]
781 fn test_decrypt_user_key_with_master_key() {
782 let password = "test_password";
783 let email = "[email protected]";
784 let kdf = &Kdf::Argon2id {
785 iterations: NonZero::try_from(3).unwrap(),
786 memory: NonZero::try_from(64).unwrap(),
787 parallelism: NonZero::try_from(4).unwrap(),
788 };
789 let master_key = MasterKey::derive(password, email, kdf).unwrap();
790 let (user_key, encrypted_user_key) = master_key.make_user_key().unwrap();
791 let master_key_bytes = master_key.to_base64().into_bytes();
792
793 let decrypted_user_key = PureCrypto::decrypt_user_key_with_master_key(
794 encrypted_user_key.to_string(),
795 master_key_bytes,
796 )
797 .unwrap();
798 assert_eq!(user_key.0.to_encoded().to_vec(), decrypted_user_key);
799 }
800
801 #[test]
802 fn test_rsa_round_trip() {
803 let private_key = PureCrypto::rsa_generate_keypair().unwrap();
804 let public_key = PureCrypto::rsa_extract_public_key(private_key.clone()).unwrap();
805 let plain_data = b"Test RSA encryption data".to_vec();
806 let encrypted_data = PureCrypto::rsa_encrypt_data(plain_data.clone(), public_key).unwrap();
807 let decrypted_data = PureCrypto::rsa_decrypt_data(encrypted_data, private_key).unwrap();
808 assert_eq!(plain_data, decrypted_data);
809 }
810
811 #[test]
812 fn test_new_guid_is_v4_and_unique() {
813 let first = PureCrypto::new_guid();
814 let second = PureCrypto::new_guid();
815
816 let parsed = uuid::Uuid::parse_str(&first).expect("new_guid output must parse as a UUID");
817 assert_eq!(parsed.get_version_num(), 4);
818 assert_ne!(first, second);
819 }
820}