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