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