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