bitwarden_wasm_internal/
pure_crypto.rs

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    AsymmetricCryptoKey, AsymmetricPublicCryptoKey, BitwardenLegacyKeyBytes, CoseKeyBytes,
8    CoseSerializable, CoseSign1Bytes, CryptoError, Decryptable, EncString, Kdf, KeyDecryptable,
9    KeyEncryptable, KeyStore, MasterKey, OctetStreamBytes, Pkcs8PrivateKeyBytes,
10    PrimitiveEncryptable, PublicKeyEncryptionAlgorithm, SignatureAlgorithm, SignedPublicKey,
11    SigningKey, 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/// This module represents a stopgap solution to provide access to primitive crypto functions for JS
21/// clients. It is not intended to be used outside of the JS clients and this pattern should not be
22/// proliferated. It is necessary because we want to use SDK crypto prior to the SDK being fully
23/// responsible for state and keys.
24#[wasm_bindgen]
25pub struct PureCrypto {}
26
27// Encryption
28#[wasm_bindgen]
29impl PureCrypto {
30    /// DEPRECATED: Use `symmetric_decrypt_string` instead.
31    /// Cleanup ticket: <https://bitwarden.atlassian.net/browse/PM-21247>
32    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 key = &BitwardenLegacyKeyBytes::from(key);
41        EncString::from_str(&enc_string)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?)
42    }
43
44    pub fn symmetric_decrypt_bytes(
45        enc_string: String,
46        key: Vec<u8>,
47    ) -> Result<Vec<u8>, CryptoError> {
48        let key = &BitwardenLegacyKeyBytes::from(key);
49        EncString::from_str(&enc_string)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?)
50    }
51
52    /// DEPRECATED: Use `symmetric_decrypt_filedata` instead.
53    /// Cleanup ticket: <https://bitwarden.atlassian.net/browse/PM-21247>
54    pub fn symmetric_decrypt_array_buffer(
55        enc_bytes: Vec<u8>,
56        key: Vec<u8>,
57    ) -> Result<Vec<u8>, CryptoError> {
58        Self::symmetric_decrypt_filedata(enc_bytes, key)
59    }
60
61    pub fn symmetric_decrypt_filedata(
62        enc_bytes: Vec<u8>,
63        key: Vec<u8>,
64    ) -> Result<Vec<u8>, CryptoError> {
65        let key = &BitwardenLegacyKeyBytes::from(key);
66        EncString::from_buffer(&enc_bytes)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?)
67    }
68
69    pub fn symmetric_encrypt_string(plain: String, key: Vec<u8>) -> Result<String, CryptoError> {
70        let key = &BitwardenLegacyKeyBytes::from(key);
71        plain
72            .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?)
73            .map(|enc| enc.to_string())
74    }
75
76    /// DEPRECATED: Only used by send keys
77    pub fn symmetric_encrypt_bytes(plain: Vec<u8>, key: Vec<u8>) -> Result<String, CryptoError> {
78        let key = &BitwardenLegacyKeyBytes::from(key);
79        OctetStreamBytes::from(plain)
80            .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?)
81            .map(|enc| enc.to_string())
82    }
83
84    pub fn symmetric_encrypt_filedata(
85        plain: Vec<u8>,
86        key: Vec<u8>,
87    ) -> Result<Vec<u8>, CryptoError> {
88        let key = &BitwardenLegacyKeyBytes::from(key);
89        OctetStreamBytes::from(plain)
90            .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?)?
91            .to_buffer()
92    }
93
94    pub fn decrypt_user_key_with_master_password(
95        encrypted_user_key: String,
96        master_password: String,
97        email: String,
98        kdf: Kdf,
99    ) -> Result<Vec<u8>, CryptoError> {
100        let master_key = MasterKey::derive(master_password.as_str(), email.as_str(), &kdf)?;
101        let encrypted_user_key = EncString::from_str(&encrypted_user_key)?;
102        let result = master_key
103            .decrypt_user_key(encrypted_user_key)
104            .map_err(|_| CryptoError::InvalidKey)?;
105        Ok(result.to_encoded().to_vec())
106    }
107
108    pub fn encrypt_user_key_with_master_password(
109        user_key: Vec<u8>,
110        master_password: String,
111        email: String,
112        kdf: Kdf,
113    ) -> Result<String, CryptoError> {
114        let master_key = MasterKey::derive(master_password.as_str(), email.as_str(), &kdf)?;
115        let user_key = &BitwardenLegacyKeyBytes::from(user_key);
116        let user_key = SymmetricCryptoKey::try_from(user_key)?;
117        let result = master_key.encrypt_user_key(&user_key)?;
118        Ok(result.to_string())
119    }
120
121    pub fn make_user_key_aes256_cbc_hmac() -> Vec<u8> {
122        SymmetricCryptoKey::make_aes256_cbc_hmac_key()
123            .to_encoded()
124            .to_vec()
125    }
126
127    pub fn make_user_key_xchacha20_poly1305() -> Vec<u8> {
128        SymmetricCryptoKey::make_xchacha20_poly1305_key()
129            .to_encoded()
130            .to_vec()
131    }
132
133    /// Wraps (encrypts) a symmetric key using a symmetric wrapping key, returning the wrapped key
134    /// as an EncString.
135    pub fn wrap_symmetric_key(
136        key_to_be_wrapped: Vec<u8>,
137        wrapping_key: Vec<u8>,
138    ) -> Result<String, CryptoError> {
139        let tmp_store: KeyStore<KeyIds> = KeyStore::default();
140        let mut context = tmp_store.context();
141        let wrapping_key =
142            SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(wrapping_key))?;
143        let wrapping_key = context.add_local_symmetric_key(wrapping_key);
144        let key_to_be_wrapped =
145            SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(key_to_be_wrapped))?;
146        let key_to_wrap = context.add_local_symmetric_key(key_to_be_wrapped);
147        // Note: The order of arguments is different here, and should probably be refactored
148        Ok(context
149            .wrap_symmetric_key(wrapping_key, key_to_wrap)?
150            .to_string())
151    }
152
153    /// Unwraps (decrypts) a wrapped symmetric key using a symmetric wrapping key, returning the
154    /// unwrapped key as a serialized byte array.
155    pub fn unwrap_symmetric_key(
156        wrapped_key: String,
157        wrapping_key: Vec<u8>,
158    ) -> Result<Vec<u8>, CryptoError> {
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        // Note: The order of arguments is different here, and should probably be refactored
165        let unwrapped = context
166            .unwrap_symmetric_key(wrapping_key, &EncString::from_str(wrapped_key.as_str())?)?;
167        #[allow(deprecated)]
168        let key = context.dangerous_get_symmetric_key(unwrapped)?;
169        Ok(key.to_encoded().to_vec())
170    }
171
172    /// Wraps (encrypts) an SPKI DER encoded encapsulation (public) key using a symmetric wrapping
173    /// key. Note: Usually, a public key is - by definition - public, so this should not be
174    /// used. The specific use-case for this function is to enable rotateable key sets, where
175    /// the "public key" is not public, with the intent of preventing the server from being able
176    /// to overwrite the user key unlocked by the rotateable keyset.
177    pub fn wrap_encapsulation_key(
178        encapsulation_key: Vec<u8>,
179        wrapping_key: Vec<u8>,
180    ) -> Result<String, CryptoError> {
181        let tmp_store: KeyStore<KeyIds> = KeyStore::default();
182        let mut context = tmp_store.context();
183        let wrapping_key = context.add_local_symmetric_key(SymmetricCryptoKey::try_from(
184            &BitwardenLegacyKeyBytes::from(wrapping_key),
185        )?);
186        Ok(SpkiPublicKeyBytes::from(encapsulation_key)
187            .encrypt(&mut context, wrapping_key)?
188            .to_string())
189    }
190
191    /// Unwraps (decrypts) a wrapped SPKI DER encoded encapsulation (public) key using a symmetric
192    /// wrapping key.
193    pub fn unwrap_encapsulation_key(
194        wrapped_key: String,
195        wrapping_key: Vec<u8>,
196    ) -> Result<Vec<u8>, CryptoError> {
197        let tmp_store: KeyStore<KeyIds> = KeyStore::default();
198        let mut context = tmp_store.context();
199        let wrapping_key = context.add_local_symmetric_key(SymmetricCryptoKey::try_from(
200            &BitwardenLegacyKeyBytes::from(wrapping_key),
201        )?);
202        EncString::from_str(wrapped_key.as_str())?.decrypt(&mut context, wrapping_key)
203    }
204
205    /// Wraps (encrypts) a PKCS8 DER encoded decapsulation (private) key using a symmetric wrapping
206    /// key,
207    pub fn wrap_decapsulation_key(
208        decapsulation_key: Vec<u8>,
209        wrapping_key: Vec<u8>,
210    ) -> Result<String, CryptoError> {
211        let tmp_store: KeyStore<KeyIds> = KeyStore::default();
212        let mut context = tmp_store.context();
213        let wrapping_key = context.add_local_symmetric_key(SymmetricCryptoKey::try_from(
214            &BitwardenLegacyKeyBytes::from(wrapping_key),
215        )?);
216        Ok(Pkcs8PrivateKeyBytes::from(decapsulation_key)
217            .encrypt(&mut context, wrapping_key)?
218            .to_string())
219    }
220
221    /// Unwraps (decrypts) a wrapped PKCS8 DER encoded decapsulation (private) key using a symmetric
222    /// wrapping key.
223    pub fn unwrap_decapsulation_key(
224        wrapped_key: String,
225        wrapping_key: Vec<u8>,
226    ) -> Result<Vec<u8>, CryptoError> {
227        let tmp_store: KeyStore<KeyIds> = KeyStore::default();
228        let mut context = tmp_store.context();
229        let wrapping_key = context.add_local_symmetric_key(SymmetricCryptoKey::try_from(
230            &BitwardenLegacyKeyBytes::from(wrapping_key),
231        )?);
232        EncString::from_str(wrapped_key.as_str())?.decrypt(&mut context, wrapping_key)
233    }
234
235    /// Encapsulates (encrypts) a symmetric key using an asymmetric encapsulation key (public key)
236    /// in SPKI format, returning the encapsulated key as a string. Note: This is unsigned, so
237    /// the sender's authenticity cannot be verified by the recipient.
238    pub fn encapsulate_key_unsigned(
239        shared_key: Vec<u8>,
240        encapsulation_key: Vec<u8>,
241    ) -> Result<String, CryptoError> {
242        let encapsulation_key =
243            AsymmetricPublicCryptoKey::from_der(&SpkiPublicKeyBytes::from(encapsulation_key))?;
244        Ok(UnsignedSharedKey::encapsulate_key_unsigned(
245            &SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(shared_key))?,
246            &encapsulation_key,
247        )?
248        .to_string())
249    }
250
251    /// Decapsulates (decrypts) a symmetric key using an decapsulation key (private key) in PKCS8
252    /// DER format. Note: This is unsigned, so the sender's authenticity cannot be verified by the
253    /// recipient.
254    pub fn decapsulate_key_unsigned(
255        encapsulated_key: String,
256        decapsulation_key: Vec<u8>,
257    ) -> Result<Vec<u8>, CryptoError> {
258        Ok(UnsignedSharedKey::from_str(encapsulated_key.as_str())?
259            .decapsulate_key_unsigned(&AsymmetricCryptoKey::from_der(
260                &Pkcs8PrivateKeyBytes::from(decapsulation_key),
261            )?)?
262            .to_encoded()
263            .to_vec())
264    }
265
266    /// Given a wrapped signing key and the symmetric key it is wrapped with, this returns
267    /// the corresponding verifying key.
268    pub fn verifying_key_for_signing_key(
269        signing_key: String,
270        wrapping_key: Vec<u8>,
271    ) -> Result<Vec<u8>, CryptoError> {
272        let bytes = Self::symmetric_decrypt_bytes(signing_key, wrapping_key)?;
273        let signing_key = SigningKey::from_cose(&CoseKeyBytes::from(bytes))?;
274        let verifying_key = signing_key.to_verifying_key();
275        Ok(verifying_key.to_cose().to_vec())
276    }
277
278    /// Returns the algorithm used for the given verifying key.
279    pub fn key_algorithm_for_verifying_key(
280        verifying_key: Vec<u8>,
281    ) -> Result<SignatureAlgorithm, CryptoError> {
282        let verifying_key = VerifyingKey::from_cose(&CoseKeyBytes::from(verifying_key))?;
283        let algorithm = verifying_key.algorithm();
284        Ok(algorithm)
285    }
286
287    /// For a given signing identity (verifying key), this function verifies that the signing
288    /// identity claimed ownership of the public key. This is a one-sided claim and merely shows
289    /// that the signing identity has the intent to receive messages encrypted to the public
290    /// key.
291    pub fn verify_and_unwrap_signed_public_key(
292        signed_public_key: Vec<u8>,
293        verifying_key: Vec<u8>,
294    ) -> Result<Vec<u8>, CryptoError> {
295        let signed_public_key = SignedPublicKey::try_from(CoseSign1Bytes::from(signed_public_key))?;
296        let verifying_key = VerifyingKey::from_cose(&CoseKeyBytes::from(verifying_key))?;
297        signed_public_key
298            .verify_and_unwrap(&verifying_key)
299            .map(|public_key| public_key.to_der())?
300            .map(|pk| pk.to_vec())
301    }
302
303    /// Derive output of the KDF for a [bitwarden_crypto::Kdf] configuration.
304    pub fn derive_kdf_material(
305        password: &[u8],
306        salt: &[u8],
307        kdf: Kdf,
308    ) -> Result<Vec<u8>, CryptoError> {
309        #[allow(deprecated)]
310        dangerous_derive_kdf_material(password, salt, &kdf)
311    }
312
313    pub fn decrypt_user_key_with_master_key(
314        encrypted_user_key: String,
315        master_key: Vec<u8>,
316    ) -> Result<Vec<u8>, CryptoError> {
317        let master_key = &BitwardenLegacyKeyBytes::from(master_key);
318        let master_key = &SymmetricCryptoKey::try_from(master_key)?;
319        let master_key = MasterKey::try_from(master_key)?;
320        let encrypted_user_key = EncString::from_str(&encrypted_user_key)?;
321        let result = master_key
322            .decrypt_user_key(encrypted_user_key)
323            .map_err(|_| CryptoError::InvalidKey)?;
324        Ok(result.to_encoded().to_vec())
325    }
326
327    /// Given a decrypted private RSA key PKCS8 DER this
328    /// returns the corresponding public RSA key in DER format.
329    /// HAZMAT WARNING: Do not use outside of implementing cryptofunctionservice
330    pub fn rsa_extract_public_key(private_key: Vec<u8>) -> Result<Vec<u8>, RsaError> {
331        let private_key = AsymmetricCryptoKey::from_der(&Pkcs8PrivateKeyBytes::from(private_key))
332            .map_err(|_| RsaError::KeyParse)?;
333        let public_key = private_key.to_public_key();
334        Ok(public_key
335            .to_der()
336            .map_err(|_| RsaError::KeySerialize)?
337            .to_vec())
338    }
339
340    /// Generates a new RSA key pair and returns the private key
341    /// HAZMAT WARNING: Do not use outside of implementing cryptofunctionservice
342    pub fn rsa_generate_keypair() -> Result<Vec<u8>, RsaError> {
343        let private_key = AsymmetricCryptoKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
344        Ok(private_key
345            .to_der()
346            .map_err(|_| RsaError::KeySerialize)?
347            .to_vec())
348    }
349
350    /// Decrypts data using RSAES-OAEP with SHA-1
351    /// HAZMAT WARNING: Do not use outside of implementing cryptofunctionservice
352    pub fn rsa_decrypt_data(
353        encrypted_data: Vec<u8>,
354        private_key: Vec<u8>,
355    ) -> Result<Vec<u8>, RsaError> {
356        let private_key = RsaPrivateKey::from_pkcs8_der(private_key.as_slice())
357            .map_err(|_| RsaError::KeyParse)?;
358        let padding = Oaep::new::<Sha1>();
359        private_key
360            .decrypt(padding, &encrypted_data)
361            .map_err(|_| RsaError::Decryption)
362    }
363
364    /// Encrypts data using RSAES-OAEP with SHA-1
365    /// HAZMAT WARNING: Do not use outside of implementing cryptofunctionservice
366    pub fn rsa_encrypt_data(plain_data: Vec<u8>, public_key: Vec<u8>) -> Result<Vec<u8>, RsaError> {
367        let public_key = RsaPublicKey::from_public_key_der(public_key.as_slice())
368            .map_err(|_| RsaError::KeyParse)?;
369        let padding = Oaep::new::<Sha1>();
370        let mut rng = rand::thread_rng();
371        public_key
372            .encrypt(&mut rng, padding, &plain_data)
373            .map_err(|_| RsaError::Encryption)
374    }
375}
376
377#[wasm_bindgen]
378#[derive(Debug)]
379pub enum RsaError {
380    Decryption,
381    Encryption,
382    KeyParse,
383    KeySerialize,
384}
385
386#[cfg(test)]
387mod tests {
388    use std::{num::NonZero, str::FromStr};
389
390    use bitwarden_crypto::EncString;
391
392    use super::*;
393
394    const KEY: &[u8] = &[
395        81, 142, 1, 228, 222, 3, 3, 133, 34, 176, 35, 66, 150, 6, 109, 70, 190, 149, 47, 47, 89,
396        23, 144, 87, 92, 46, 220, 13, 148, 106, 162, 234, 202, 139, 136, 33, 16, 200, 8, 73, 176,
397        172, 185, 187, 224, 10, 65, 223, 228, 54, 92, 181, 8, 213, 162, 221, 117, 254, 245, 111,
398        55, 211, 77, 29,
399    ];
400
401    const ENCRYPTED: &str = "2.Dh7AFLXR+LXcxUaO5cRjpg==|uXyhubjAoNH8lTdy/zgJDQ==|cHEMboj0MYsU5yDRQ1rLCgxcjNbKRc1PWKuv8bpU5pM=";
402    const DECRYPTED: &str = "test";
403    const DECRYPTED_BYTES: &[u8] = b"test";
404    const ENCRYPTED_BYTES: &[u8] = &[
405        2, 209, 195, 115, 49, 205, 253, 128, 162, 169, 246, 175, 217, 144, 73, 108, 191, 27, 113,
406        69, 55, 94, 142, 62, 129, 204, 173, 130, 37, 42, 97, 209, 25, 192, 64, 126, 112, 139, 248,
407        2, 89, 112, 178, 83, 25, 77, 130, 187, 127, 85, 179, 211, 159, 186, 111, 44, 109, 211, 18,
408        120, 104, 144, 4, 76, 3,
409    ];
410
411    const PEM_KEY: &str = "-----BEGIN PRIVATE KEY-----
412MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDiTQVuzhdygFz5
413qv14i+XFDGTnDravzUQT1hPKPGUZOUSZ1gwdNgkWqOIaOnR65BHEnL0sp4bnuiYc
414afeK2JAW5Sc8Z7IxBNSuAwhQmuKx3RochMIiuCkI2/p+JvUQoJu6FBNm8OoJ4Cwm
415qqHGZESMfnpQDCuDrB3JdJEdXhtmnl0C48sGjOk3WaBMcgGqn8LbJDUlyu1zdqyv
416b0waJf0iV4PJm2fkUl7+57D/2TkpbCqURVnZK1FFIEg8mr6FzSN1F2pOfktkNYZw
417P7MSNR7o81CkRSCMr7EkIVa+MZYMBx106BMK7FXgWB7nbSpsWKxBk7ZDHkID2fam
418rEcVtrzDAgMBAAECggEBAKwq9OssGGKgjhvUnyrLJHAZ0dqIMyzk+dotkLjX4gKi
419szJmyqiep6N5sStLNbsZMPtoU/RZMCW0VbJgXFhiEp2YkZU/Py5UAoqw++53J+kx
4200d/IkPphKbb3xUec0+1mg5O6GljDCQuiZXS1dIa/WfeZcezclW6Dz9WovY6ePjJ+
4218vEBR1icbNKzyeINd6MtPtpcgQPHtDwHvhPyUDbKDYGbLvjh9nui8h4+ZUlXKuVR
422jB0ChxiKV1xJRjkrEVoulOOicd5r597WfB2ghax3pvRZ4MdXemCXm3gQYqPVKach
423vGU+1cPQR/MBJZpxT+EZA97xwtFS3gqwbxJaNFcoE8ECgYEA9OaeYZhQPDo485tI
4241u/Z7L/3PNape9hBQIXoW7+MgcQ5NiWqYh8Jnj43EIYa0wM/ECQINr1Za8Q5e6KR
425J30FcU+kfyjuQ0jeXdNELGU/fx5XXNg/vV8GevHwxRlwzqZTCg6UExUZzbYEQqd7
426l+wPyETGeua5xCEywA1nX/D101kCgYEA7I6aMFjhEjO71RmzNhqjKJt6DOghoOfQ
427TjhaaanNEhLYSbenFz1mlb21mW67ulmz162saKdIYLxQNJIP8ZPmxh4ummOJI8w9
428ClHfo8WuCI2hCjJ19xbQJocSbTA5aJg6lA1IDVZMDbQwsnAByPRGpaLHBT/Q9Bye
429KvCMB+9amXsCgYEAx65yXSkP4sumPBrVHUub6MntERIGRxBgw/drKcPZEMWp0FiN
430wEuGUBxyUWrG3F69QK/gcqGZE6F/LSu0JvptQaKqgXQiMYJsrRvhbkFvsHpQyUcZ
431UZL1ebFjm5HOxPAgrQaN/bEqxOwwNRjSUWEMzUImg3c06JIZCzbinvudtKECgYEA
432kY3JF/iIPI/yglP27lKDlCfeeHSYxI3+oTKRhzSAxx8rUGidenJAXeDGDauR/T7W
433pt3pGNfddBBK9Z3uC4Iq3DqUCFE4f/taj7ADAJ1Q0Vh7/28/IJM77ojr8J1cpZwN
434Zy2o6PPxhfkagaDjqEeN9Lrs5LD4nEvDkr5CG1vOjmMCgYEAvIBFKRm31NyF8jLi
435CVuPwC5PzrW5iThDmsWTaXFpB3esUsbICO2pEz872oeQS+Em4GO5vXUlpbbFPzup
436PFhA8iMJ8TAvemhvc7oM0OZqpU6p3K4seHf6BkwLxumoA3vDJfovu9RuXVcJVOnf
437DnqOsltgPomWZ7xVfMkm9niL2OA=
438-----END PRIVATE KEY-----";
439
440    const SIGNING_KEY_WRAPPING_KEY: &[u8] = &[
441        40, 215, 110, 199, 183, 4, 182, 78, 213, 123, 251, 113, 72, 223, 57, 2, 3, 81, 136, 19, 88,
442        78, 206, 176, 158, 251, 211, 84, 1, 199, 203, 142, 176, 227, 187, 136, 209, 79, 23, 13, 44,
443        224, 90, 10, 191, 72, 22, 227, 171, 105, 107, 139, 24, 49, 9, 150, 103, 139, 151, 204, 165,
444        121, 165, 71,
445    ];
446    const SIGNING_KEY: &[u8] = &[
447        166, 1, 1, 2, 80, 123, 226, 102, 228, 194, 232, 71, 30, 183, 42, 219, 193, 50, 30, 21, 43,
448        3, 39, 4, 130, 1, 2, 35, 88, 32, 148, 2, 66, 69, 169, 57, 129, 240, 37, 18, 225, 211, 207,
449        133, 66, 143, 204, 238, 113, 152, 43, 112, 133, 173, 179, 17, 202, 135, 175, 237, 1, 59,
450        32, 6,
451    ];
452    const VERIFYING_KEY: &[u8] = &[
453        166, 1, 1, 2, 80, 123, 226, 102, 228, 194, 232, 71, 30, 183, 42, 219, 193, 50, 30, 21, 43,
454        3, 39, 4, 129, 2, 32, 6, 33, 88, 32, 63, 70, 49, 37, 246, 232, 146, 144, 83, 224, 0, 17,
455        111, 248, 16, 242, 69, 195, 84, 46, 39, 218, 55, 63, 90, 112, 148, 91, 224, 186, 122, 4,
456    ];
457    const PUBLIC_KEY: &[u8] = &[
458        48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0,
459        48, 130, 1, 10, 2, 130, 1, 1, 0, 173, 4, 54, 63, 125, 12, 254, 38, 115, 34, 95, 164, 148,
460        115, 86, 140, 129, 74, 19, 70, 212, 212, 130, 163, 105, 249, 101, 120, 154, 46, 194, 250,
461        229, 242, 156, 67, 109, 179, 187, 134, 59, 235, 60, 107, 144, 163, 35, 22, 109, 230, 134,
462        243, 44, 243, 79, 84, 76, 11, 64, 56, 236, 167, 98, 26, 30, 213, 143, 105, 52, 92, 129, 92,
463        88, 22, 115, 135, 63, 215, 79, 8, 11, 183, 124, 10, 73, 231, 170, 110, 210, 178, 22, 100,
464        76, 75, 118, 202, 252, 204, 67, 204, 152, 6, 244, 208, 161, 146, 103, 225, 233, 239, 88,
465        195, 88, 150, 230, 111, 62, 142, 12, 157, 184, 155, 34, 84, 237, 111, 11, 97, 56, 152, 130,
466        14, 72, 123, 140, 47, 137, 5, 97, 166, 4, 147, 111, 23, 65, 78, 63, 208, 198, 50, 161, 39,
467        80, 143, 100, 194, 37, 252, 194, 53, 207, 166, 168, 250, 165, 121, 9, 207, 90, 36, 213,
468        211, 84, 255, 14, 205, 114, 135, 217, 137, 105, 232, 58, 169, 222, 10, 13, 138, 203, 16,
469        12, 122, 72, 227, 95, 160, 111, 54, 200, 198, 143, 156, 15, 143, 196, 50, 150, 204, 144,
470        255, 162, 248, 50, 28, 47, 66, 9, 83, 158, 67, 9, 50, 147, 174, 147, 200, 199, 238, 190,
471        248, 60, 114, 218, 32, 209, 120, 218, 17, 234, 14, 128, 192, 166, 33, 60, 73, 227, 108,
472        201, 41, 160, 81, 133, 171, 205, 221, 2, 3, 1, 0, 1,
473    ];
474
475    const SIGNED_PUBLIC_KEY: &[u8] = &[
476        132, 88, 30, 164, 1, 39, 3, 24, 60, 4, 80, 123, 226, 102, 228, 194, 232, 71, 30, 183, 42,
477        219, 193, 50, 30, 21, 43, 58, 0, 1, 56, 127, 1, 160, 89, 1, 78, 163, 105, 97, 108, 103,
478        111, 114, 105, 116, 104, 109, 0, 109, 99, 111, 110, 116, 101, 110, 116, 70, 111, 114, 109,
479        97, 116, 0, 105, 112, 117, 98, 108, 105, 99, 75, 101, 121, 89, 1, 38, 48, 130, 1, 34, 48,
480        13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 10, 2,
481        130, 1, 1, 0, 173, 4, 54, 63, 125, 12, 254, 38, 115, 34, 95, 164, 148, 115, 86, 140, 129,
482        74, 19, 70, 212, 212, 130, 163, 105, 249, 101, 120, 154, 46, 194, 250, 229, 242, 156, 67,
483        109, 179, 187, 134, 59, 235, 60, 107, 144, 163, 35, 22, 109, 230, 134, 243, 44, 243, 79,
484        84, 76, 11, 64, 56, 236, 167, 98, 26, 30, 213, 143, 105, 52, 92, 129, 92, 88, 22, 115, 135,
485        63, 215, 79, 8, 11, 183, 124, 10, 73, 231, 170, 110, 210, 178, 22, 100, 76, 75, 118, 202,
486        252, 204, 67, 204, 152, 6, 244, 208, 161, 146, 103, 225, 233, 239, 88, 195, 88, 150, 230,
487        111, 62, 142, 12, 157, 184, 155, 34, 84, 237, 111, 11, 97, 56, 152, 130, 14, 72, 123, 140,
488        47, 137, 5, 97, 166, 4, 147, 111, 23, 65, 78, 63, 208, 198, 50, 161, 39, 80, 143, 100, 194,
489        37, 252, 194, 53, 207, 166, 168, 250, 165, 121, 9, 207, 90, 36, 213, 211, 84, 255, 14, 205,
490        114, 135, 217, 137, 105, 232, 58, 169, 222, 10, 13, 138, 203, 16, 12, 122, 72, 227, 95,
491        160, 111, 54, 200, 198, 143, 156, 15, 143, 196, 50, 150, 204, 144, 255, 162, 248, 50, 28,
492        47, 66, 9, 83, 158, 67, 9, 50, 147, 174, 147, 200, 199, 238, 190, 248, 60, 114, 218, 32,
493        209, 120, 218, 17, 234, 14, 128, 192, 166, 33, 60, 73, 227, 108, 201, 41, 160, 81, 133,
494        171, 205, 221, 2, 3, 1, 0, 1, 88, 64, 207, 18, 4, 242, 149, 31, 37, 255, 243, 62, 78, 46,
495        12, 150, 134, 159, 69, 89, 62, 222, 132, 12, 177, 74, 155, 80, 154, 37, 77, 176, 19, 142,
496        73, 4, 134, 242, 24, 56, 54, 38, 178, 59, 11, 118, 230, 159, 87, 91, 20, 237, 188, 186,
497        216, 86, 189, 50, 46, 173, 117, 36, 54, 105, 216, 9,
498    ];
499
500    const DERIVED_KDF_MATERIAL_PBKDF2: &[u8] = &[
501        129, 57, 137, 140, 156, 220, 110, 212, 201, 255, 52, 182, 22, 206, 221, 66, 136, 199, 181,
502        89, 252, 175, 82, 168, 79, 204, 88, 174, 166, 60, 52, 79,
503    ];
504    const DERIVED_KDF_MATERIAL_ARGON2ID: &[u8] = &[
505        221, 57, 158, 206, 27, 154, 188, 170, 33, 198, 250, 144, 191, 231, 29, 74, 201, 102, 253,
506        77, 8, 128, 173, 111, 217, 41, 125, 9, 156, 52, 112, 140,
507    ];
508
509    #[test]
510    fn test_symmetric_decrypt() {
511        let enc_string = EncString::from_str(ENCRYPTED).unwrap();
512
513        let result = PureCrypto::symmetric_decrypt_string(enc_string.to_string(), KEY.to_vec());
514        assert!(result.is_ok());
515        assert_eq!(result.unwrap(), DECRYPTED);
516    }
517
518    #[test]
519    fn test_symmetric_encrypt() {
520        let result = PureCrypto::symmetric_encrypt_string(DECRYPTED.to_string(), KEY.to_vec());
521        assert!(result.is_ok());
522        // Cannot test encrypted string content because IV is unique per encryption
523    }
524
525    #[test]
526    fn test_symmetric_string_round_trip() {
527        let encrypted =
528            PureCrypto::symmetric_encrypt_string(DECRYPTED.to_string(), KEY.to_vec()).unwrap();
529        let decrypted =
530            PureCrypto::symmetric_decrypt_string(encrypted.clone(), KEY.to_vec()).unwrap();
531        assert_eq!(decrypted, DECRYPTED);
532    }
533
534    #[test]
535    fn test_symmetric_bytes_round_trip() {
536        let encrypted =
537            PureCrypto::symmetric_encrypt_bytes(DECRYPTED.as_bytes().to_vec(), KEY.to_vec())
538                .unwrap();
539        let decrypted =
540            PureCrypto::symmetric_decrypt_bytes(encrypted.clone(), KEY.to_vec()).unwrap();
541        assert_eq!(decrypted, DECRYPTED.as_bytes().to_vec());
542    }
543
544    #[test]
545    fn test_symmetric_decrypt_array_buffer() {
546        let result = PureCrypto::symmetric_decrypt_filedata(ENCRYPTED_BYTES.to_vec(), KEY.to_vec());
547        assert!(result.is_ok());
548        assert_eq!(result.unwrap(), DECRYPTED_BYTES);
549    }
550
551    #[test]
552    fn test_symmetric_encrypt_to_array_buffer() {
553        let result = PureCrypto::symmetric_encrypt_filedata(DECRYPTED_BYTES.to_vec(), KEY.to_vec());
554        assert!(result.is_ok());
555        // Cannot test encrypted string content because IV is unique per encryption
556    }
557
558    #[test]
559    fn test_symmetric_filedata_round_trip() {
560        let encrypted =
561            PureCrypto::symmetric_encrypt_filedata(DECRYPTED_BYTES.to_vec(), KEY.to_vec()).unwrap();
562        let decrypted =
563            PureCrypto::symmetric_decrypt_filedata(encrypted.clone(), KEY.to_vec()).unwrap();
564        assert_eq!(decrypted, DECRYPTED_BYTES);
565    }
566
567    #[test]
568    fn test_make_aes256_cbc_hmac_key() {
569        let key = PureCrypto::make_user_key_aes256_cbc_hmac();
570        assert_eq!(key.len(), 64);
571    }
572
573    #[test]
574    fn test_make_xchacha20_poly1305_key() {
575        let key = PureCrypto::make_user_key_xchacha20_poly1305();
576        assert!(key.len() > 64);
577    }
578
579    #[test]
580    fn roundtrip_encrypt_user_key_with_master_password() {
581        let master_password = "test";
582        let email = "[email protected]";
583        let kdf = Kdf::PBKDF2 {
584            iterations: NonZero::try_from(600000).unwrap(),
585        };
586        let user_key = PureCrypto::make_user_key_aes256_cbc_hmac();
587        let encrypted_user_key = PureCrypto::encrypt_user_key_with_master_password(
588            user_key.clone(),
589            master_password.to_string(),
590            email.to_string(),
591            kdf.clone(),
592        )
593        .unwrap();
594        let decrypted_user_key = PureCrypto::decrypt_user_key_with_master_password(
595            encrypted_user_key,
596            master_password.to_string(),
597            email.to_string(),
598            kdf,
599        )
600        .unwrap();
601        assert_eq!(user_key, decrypted_user_key);
602    }
603
604    #[test]
605    fn test_wrap_unwrap_symmetric_key() {
606        let key_to_be_wrapped = PureCrypto::make_user_key_aes256_cbc_hmac();
607        let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac();
608        let wrapped_key =
609            PureCrypto::wrap_symmetric_key(key_to_be_wrapped.clone(), wrapping_key.clone())
610                .unwrap();
611        let unwrapped_key = PureCrypto::unwrap_symmetric_key(wrapped_key, wrapping_key).unwrap();
612        assert_eq!(key_to_be_wrapped, unwrapped_key);
613    }
614
615    #[test]
616    fn test_wrap_encapsulation_key() {
617        let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap();
618        let encapsulation_key = decapsulation_key
619            .to_public_key()
620            .to_der()
621            .unwrap()
622            .as_ref()
623            .to_vec();
624        let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac();
625        let wrapped_key =
626            PureCrypto::wrap_encapsulation_key(encapsulation_key.clone(), wrapping_key.clone())
627                .unwrap();
628        let unwrapped_key =
629            PureCrypto::unwrap_encapsulation_key(wrapped_key, wrapping_key).unwrap();
630        assert_eq!(encapsulation_key, unwrapped_key);
631    }
632
633    #[test]
634    fn test_wrap_decapsulation_key() {
635        let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap();
636        let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac();
637        let wrapped_key = PureCrypto::wrap_decapsulation_key(
638            decapsulation_key.to_der().unwrap().to_vec(),
639            wrapping_key.clone(),
640        )
641        .unwrap();
642        let unwrapped_key =
643            PureCrypto::unwrap_decapsulation_key(wrapped_key, wrapping_key).unwrap();
644        assert_eq!(decapsulation_key.to_der().unwrap().to_vec(), unwrapped_key);
645    }
646
647    #[test]
648    fn test_encapsulate_key_unsigned() {
649        let shared_key = PureCrypto::make_user_key_aes256_cbc_hmac();
650        let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap();
651        let encapsulation_key = decapsulation_key.to_public_key().to_der().unwrap();
652        let encapsulated_key = PureCrypto::encapsulate_key_unsigned(
653            shared_key.clone(),
654            encapsulation_key.clone().to_vec(),
655        )
656        .unwrap();
657        let unwrapped_key = PureCrypto::decapsulate_key_unsigned(
658            encapsulated_key,
659            decapsulation_key.to_der().unwrap().to_vec(),
660        )
661        .unwrap();
662        assert_eq!(shared_key, unwrapped_key);
663    }
664
665    #[test]
666    fn test_key_algorithm_for_verifying_key() {
667        let verifying_key =
668            VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY.to_vec())).unwrap();
669        let algorithm =
670            PureCrypto::key_algorithm_for_verifying_key(verifying_key.to_cose().to_vec()).unwrap();
671        assert_eq!(algorithm, SignatureAlgorithm::Ed25519);
672    }
673
674    #[test]
675    fn test_verifying_key_for_signing_key() {
676        let wrapped_signing_key = PureCrypto::symmetric_encrypt_bytes(
677            SIGNING_KEY.to_vec(),
678            SIGNING_KEY_WRAPPING_KEY.to_vec(),
679        )
680        .unwrap();
681        let verifying_key =
682            VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY.to_vec())).unwrap();
683        let verifying_key_derived = PureCrypto::verifying_key_for_signing_key(
684            wrapped_signing_key.to_string(),
685            SIGNING_KEY_WRAPPING_KEY.to_vec(),
686        )
687        .unwrap();
688        let verifying_key_derived =
689            VerifyingKey::from_cose(&CoseKeyBytes::from(verifying_key_derived)).unwrap();
690        assert_eq!(verifying_key.to_cose(), verifying_key_derived.to_cose());
691    }
692
693    #[test]
694    fn test_verify_and_unwrap_signed_public_key() {
695        let public_key = PureCrypto::verify_and_unwrap_signed_public_key(
696            SIGNED_PUBLIC_KEY.to_vec(),
697            VERIFYING_KEY.to_vec(),
698        )
699        .unwrap();
700        assert_eq!(public_key, PUBLIC_KEY);
701    }
702
703    #[test]
704    fn test_derive_pbkdf2_output() {
705        let password = "test_password".as_bytes();
706        let email = "[email protected]".as_bytes();
707        let kdf = Kdf::PBKDF2 {
708            iterations: NonZero::try_from(600000).unwrap(),
709        };
710        let derived_key = PureCrypto::derive_kdf_material(password, email, kdf).unwrap();
711        assert_eq!(derived_key, DERIVED_KDF_MATERIAL_PBKDF2);
712    }
713
714    #[test]
715    fn test_derived_argon2_output() {
716        let password = "test_password".as_bytes();
717        let email = "[email protected]".as_bytes();
718        let kdf = Kdf::Argon2id {
719            iterations: NonZero::try_from(3).unwrap(),
720            memory: NonZero::try_from(64).unwrap(),
721            parallelism: NonZero::try_from(4).unwrap(),
722        };
723        let derived_key = PureCrypto::derive_kdf_material(password, email, kdf).unwrap();
724        assert_eq!(derived_key, DERIVED_KDF_MATERIAL_ARGON2ID);
725    }
726
727    #[test]
728    fn test_decrypt_user_key_with_master_key() {
729        let password = "test_password";
730        let email = "[email protected]";
731        let kdf = &Kdf::Argon2id {
732            iterations: NonZero::try_from(3).unwrap(),
733            memory: NonZero::try_from(64).unwrap(),
734            parallelism: NonZero::try_from(4).unwrap(),
735        };
736        let master_key = MasterKey::derive(password, email, kdf).unwrap();
737        let (user_key, encrypted_user_key) = master_key.make_user_key().unwrap();
738        let master_key_bytes = master_key.to_base64().into_bytes();
739
740        let decrypted_user_key = PureCrypto::decrypt_user_key_with_master_key(
741            encrypted_user_key.to_string(),
742            master_key_bytes,
743        )
744        .unwrap();
745        assert_eq!(user_key.0.to_encoded().to_vec(), decrypted_user_key);
746    }
747
748    #[test]
749    fn test_rsa_round_trip() {
750        let private_key = PureCrypto::rsa_generate_keypair().unwrap();
751        let public_key = PureCrypto::rsa_extract_public_key(private_key.clone()).unwrap();
752        let plain_data = b"Test RSA encryption data".to_vec();
753        let encrypted_data = PureCrypto::rsa_encrypt_data(plain_data.clone(), public_key).unwrap();
754        let decrypted_data = PureCrypto::rsa_decrypt_data(encrypted_data, private_key).unwrap();
755        assert_eq!(plain_data, decrypted_data);
756    }
757}