bitwarden_wasm_internal/
pure_crypto.rs

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