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
328#[cfg(test)]
329mod tests {
330    use std::{num::NonZero, str::FromStr};
331
332    use bitwarden_crypto::EncString;
333
334    use super::*;
335
336    const KEY: &[u8] = &[
337        81, 142, 1, 228, 222, 3, 3, 133, 34, 176, 35, 66, 150, 6, 109, 70, 190, 149, 47, 47, 89,
338        23, 144, 87, 92, 46, 220, 13, 148, 106, 162, 234, 202, 139, 136, 33, 16, 200, 8, 73, 176,
339        172, 185, 187, 224, 10, 65, 223, 228, 54, 92, 181, 8, 213, 162, 221, 117, 254, 245, 111,
340        55, 211, 77, 29,
341    ];
342
343    const ENCRYPTED: &str = "2.Dh7AFLXR+LXcxUaO5cRjpg==|uXyhubjAoNH8lTdy/zgJDQ==|cHEMboj0MYsU5yDRQ1rLCgxcjNbKRc1PWKuv8bpU5pM=";
344    const DECRYPTED: &str = "test";
345    const DECRYPTED_BYTES: &[u8] = b"test";
346    const ENCRYPTED_BYTES: &[u8] = &[
347        2, 209, 195, 115, 49, 205, 253, 128, 162, 169, 246, 175, 217, 144, 73, 108, 191, 27, 113,
348        69, 55, 94, 142, 62, 129, 204, 173, 130, 37, 42, 97, 209, 25, 192, 64, 126, 112, 139, 248,
349        2, 89, 112, 178, 83, 25, 77, 130, 187, 127, 85, 179, 211, 159, 186, 111, 44, 109, 211, 18,
350        120, 104, 144, 4, 76, 3,
351    ];
352
353    const PEM_KEY: &str = "-----BEGIN PRIVATE KEY-----
354MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDiTQVuzhdygFz5
355qv14i+XFDGTnDravzUQT1hPKPGUZOUSZ1gwdNgkWqOIaOnR65BHEnL0sp4bnuiYc
356afeK2JAW5Sc8Z7IxBNSuAwhQmuKx3RochMIiuCkI2/p+JvUQoJu6FBNm8OoJ4Cwm
357qqHGZESMfnpQDCuDrB3JdJEdXhtmnl0C48sGjOk3WaBMcgGqn8LbJDUlyu1zdqyv
358b0waJf0iV4PJm2fkUl7+57D/2TkpbCqURVnZK1FFIEg8mr6FzSN1F2pOfktkNYZw
359P7MSNR7o81CkRSCMr7EkIVa+MZYMBx106BMK7FXgWB7nbSpsWKxBk7ZDHkID2fam
360rEcVtrzDAgMBAAECggEBAKwq9OssGGKgjhvUnyrLJHAZ0dqIMyzk+dotkLjX4gKi
361szJmyqiep6N5sStLNbsZMPtoU/RZMCW0VbJgXFhiEp2YkZU/Py5UAoqw++53J+kx
3620d/IkPphKbb3xUec0+1mg5O6GljDCQuiZXS1dIa/WfeZcezclW6Dz9WovY6ePjJ+
3638vEBR1icbNKzyeINd6MtPtpcgQPHtDwHvhPyUDbKDYGbLvjh9nui8h4+ZUlXKuVR
364jB0ChxiKV1xJRjkrEVoulOOicd5r597WfB2ghax3pvRZ4MdXemCXm3gQYqPVKach
365vGU+1cPQR/MBJZpxT+EZA97xwtFS3gqwbxJaNFcoE8ECgYEA9OaeYZhQPDo485tI
3661u/Z7L/3PNape9hBQIXoW7+MgcQ5NiWqYh8Jnj43EIYa0wM/ECQINr1Za8Q5e6KR
367J30FcU+kfyjuQ0jeXdNELGU/fx5XXNg/vV8GevHwxRlwzqZTCg6UExUZzbYEQqd7
368l+wPyETGeua5xCEywA1nX/D101kCgYEA7I6aMFjhEjO71RmzNhqjKJt6DOghoOfQ
369TjhaaanNEhLYSbenFz1mlb21mW67ulmz162saKdIYLxQNJIP8ZPmxh4ummOJI8w9
370ClHfo8WuCI2hCjJ19xbQJocSbTA5aJg6lA1IDVZMDbQwsnAByPRGpaLHBT/Q9Bye
371KvCMB+9amXsCgYEAx65yXSkP4sumPBrVHUub6MntERIGRxBgw/drKcPZEMWp0FiN
372wEuGUBxyUWrG3F69QK/gcqGZE6F/LSu0JvptQaKqgXQiMYJsrRvhbkFvsHpQyUcZ
373UZL1ebFjm5HOxPAgrQaN/bEqxOwwNRjSUWEMzUImg3c06JIZCzbinvudtKECgYEA
374kY3JF/iIPI/yglP27lKDlCfeeHSYxI3+oTKRhzSAxx8rUGidenJAXeDGDauR/T7W
375pt3pGNfddBBK9Z3uC4Iq3DqUCFE4f/taj7ADAJ1Q0Vh7/28/IJM77ojr8J1cpZwN
376Zy2o6PPxhfkagaDjqEeN9Lrs5LD4nEvDkr5CG1vOjmMCgYEAvIBFKRm31NyF8jLi
377CVuPwC5PzrW5iThDmsWTaXFpB3esUsbICO2pEz872oeQS+Em4GO5vXUlpbbFPzup
378PFhA8iMJ8TAvemhvc7oM0OZqpU6p3K4seHf6BkwLxumoA3vDJfovu9RuXVcJVOnf
379DnqOsltgPomWZ7xVfMkm9niL2OA=
380-----END PRIVATE KEY-----";
381
382    const SIGNING_KEY_WRAPPING_KEY: &[u8] = &[
383        40, 215, 110, 199, 183, 4, 182, 78, 213, 123, 251, 113, 72, 223, 57, 2, 3, 81, 136, 19, 88,
384        78, 206, 176, 158, 251, 211, 84, 1, 199, 203, 142, 176, 227, 187, 136, 209, 79, 23, 13, 44,
385        224, 90, 10, 191, 72, 22, 227, 171, 105, 107, 139, 24, 49, 9, 150, 103, 139, 151, 204, 165,
386        121, 165, 71,
387    ];
388    const SIGNING_KEY: &[u8] = &[
389        166, 1, 1, 2, 80, 123, 226, 102, 228, 194, 232, 71, 30, 183, 42, 219, 193, 50, 30, 21, 43,
390        3, 39, 4, 130, 1, 2, 35, 88, 32, 148, 2, 66, 69, 169, 57, 129, 240, 37, 18, 225, 211, 207,
391        133, 66, 143, 204, 238, 113, 152, 43, 112, 133, 173, 179, 17, 202, 135, 175, 237, 1, 59,
392        32, 6,
393    ];
394    const VERIFYING_KEY: &[u8] = &[
395        166, 1, 1, 2, 80, 123, 226, 102, 228, 194, 232, 71, 30, 183, 42, 219, 193, 50, 30, 21, 43,
396        3, 39, 4, 129, 2, 32, 6, 33, 88, 32, 63, 70, 49, 37, 246, 232, 146, 144, 83, 224, 0, 17,
397        111, 248, 16, 242, 69, 195, 84, 46, 39, 218, 55, 63, 90, 112, 148, 91, 224, 186, 122, 4,
398    ];
399    const PUBLIC_KEY: &[u8] = &[
400        48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0,
401        48, 130, 1, 10, 2, 130, 1, 1, 0, 173, 4, 54, 63, 125, 12, 254, 38, 115, 34, 95, 164, 148,
402        115, 86, 140, 129, 74, 19, 70, 212, 212, 130, 163, 105, 249, 101, 120, 154, 46, 194, 250,
403        229, 242, 156, 67, 109, 179, 187, 134, 59, 235, 60, 107, 144, 163, 35, 22, 109, 230, 134,
404        243, 44, 243, 79, 84, 76, 11, 64, 56, 236, 167, 98, 26, 30, 213, 143, 105, 52, 92, 129, 92,
405        88, 22, 115, 135, 63, 215, 79, 8, 11, 183, 124, 10, 73, 231, 170, 110, 210, 178, 22, 100,
406        76, 75, 118, 202, 252, 204, 67, 204, 152, 6, 244, 208, 161, 146, 103, 225, 233, 239, 88,
407        195, 88, 150, 230, 111, 62, 142, 12, 157, 184, 155, 34, 84, 237, 111, 11, 97, 56, 152, 130,
408        14, 72, 123, 140, 47, 137, 5, 97, 166, 4, 147, 111, 23, 65, 78, 63, 208, 198, 50, 161, 39,
409        80, 143, 100, 194, 37, 252, 194, 53, 207, 166, 168, 250, 165, 121, 9, 207, 90, 36, 213,
410        211, 84, 255, 14, 205, 114, 135, 217, 137, 105, 232, 58, 169, 222, 10, 13, 138, 203, 16,
411        12, 122, 72, 227, 95, 160, 111, 54, 200, 198, 143, 156, 15, 143, 196, 50, 150, 204, 144,
412        255, 162, 248, 50, 28, 47, 66, 9, 83, 158, 67, 9, 50, 147, 174, 147, 200, 199, 238, 190,
413        248, 60, 114, 218, 32, 209, 120, 218, 17, 234, 14, 128, 192, 166, 33, 60, 73, 227, 108,
414        201, 41, 160, 81, 133, 171, 205, 221, 2, 3, 1, 0, 1,
415    ];
416
417    const SIGNED_PUBLIC_KEY: &[u8] = &[
418        132, 88, 30, 164, 1, 39, 3, 24, 60, 4, 80, 123, 226, 102, 228, 194, 232, 71, 30, 183, 42,
419        219, 193, 50, 30, 21, 43, 58, 0, 1, 56, 127, 1, 160, 89, 1, 78, 163, 105, 97, 108, 103,
420        111, 114, 105, 116, 104, 109, 0, 109, 99, 111, 110, 116, 101, 110, 116, 70, 111, 114, 109,
421        97, 116, 0, 105, 112, 117, 98, 108, 105, 99, 75, 101, 121, 89, 1, 38, 48, 130, 1, 34, 48,
422        13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 10, 2,
423        130, 1, 1, 0, 173, 4, 54, 63, 125, 12, 254, 38, 115, 34, 95, 164, 148, 115, 86, 140, 129,
424        74, 19, 70, 212, 212, 130, 163, 105, 249, 101, 120, 154, 46, 194, 250, 229, 242, 156, 67,
425        109, 179, 187, 134, 59, 235, 60, 107, 144, 163, 35, 22, 109, 230, 134, 243, 44, 243, 79,
426        84, 76, 11, 64, 56, 236, 167, 98, 26, 30, 213, 143, 105, 52, 92, 129, 92, 88, 22, 115, 135,
427        63, 215, 79, 8, 11, 183, 124, 10, 73, 231, 170, 110, 210, 178, 22, 100, 76, 75, 118, 202,
428        252, 204, 67, 204, 152, 6, 244, 208, 161, 146, 103, 225, 233, 239, 88, 195, 88, 150, 230,
429        111, 62, 142, 12, 157, 184, 155, 34, 84, 237, 111, 11, 97, 56, 152, 130, 14, 72, 123, 140,
430        47, 137, 5, 97, 166, 4, 147, 111, 23, 65, 78, 63, 208, 198, 50, 161, 39, 80, 143, 100, 194,
431        37, 252, 194, 53, 207, 166, 168, 250, 165, 121, 9, 207, 90, 36, 213, 211, 84, 255, 14, 205,
432        114, 135, 217, 137, 105, 232, 58, 169, 222, 10, 13, 138, 203, 16, 12, 122, 72, 227, 95,
433        160, 111, 54, 200, 198, 143, 156, 15, 143, 196, 50, 150, 204, 144, 255, 162, 248, 50, 28,
434        47, 66, 9, 83, 158, 67, 9, 50, 147, 174, 147, 200, 199, 238, 190, 248, 60, 114, 218, 32,
435        209, 120, 218, 17, 234, 14, 128, 192, 166, 33, 60, 73, 227, 108, 201, 41, 160, 81, 133,
436        171, 205, 221, 2, 3, 1, 0, 1, 88, 64, 207, 18, 4, 242, 149, 31, 37, 255, 243, 62, 78, 46,
437        12, 150, 134, 159, 69, 89, 62, 222, 132, 12, 177, 74, 155, 80, 154, 37, 77, 176, 19, 142,
438        73, 4, 134, 242, 24, 56, 54, 38, 178, 59, 11, 118, 230, 159, 87, 91, 20, 237, 188, 186,
439        216, 86, 189, 50, 46, 173, 117, 36, 54, 105, 216, 9,
440    ];
441
442    const DERIVED_KDF_MATERIAL_PBKDF2: &[u8] = &[
443        129, 57, 137, 140, 156, 220, 110, 212, 201, 255, 52, 182, 22, 206, 221, 66, 136, 199, 181,
444        89, 252, 175, 82, 168, 79, 204, 88, 174, 166, 60, 52, 79,
445    ];
446    const DERIVED_KDF_MATERIAL_ARGON2ID: &[u8] = &[
447        221, 57, 158, 206, 27, 154, 188, 170, 33, 198, 250, 144, 191, 231, 29, 74, 201, 102, 253,
448        77, 8, 128, 173, 111, 217, 41, 125, 9, 156, 52, 112, 140,
449    ];
450
451    #[test]
452    fn test_symmetric_decrypt() {
453        let enc_string = EncString::from_str(ENCRYPTED).unwrap();
454
455        let result = PureCrypto::symmetric_decrypt_string(enc_string.to_string(), KEY.to_vec());
456        assert!(result.is_ok());
457        assert_eq!(result.unwrap(), DECRYPTED);
458    }
459
460    #[test]
461    fn test_symmetric_encrypt() {
462        let result = PureCrypto::symmetric_encrypt_string(DECRYPTED.to_string(), KEY.to_vec());
463        assert!(result.is_ok());
464        // Cannot test encrypted string content because IV is unique per encryption
465    }
466
467    #[test]
468    fn test_symmetric_string_round_trip() {
469        let encrypted =
470            PureCrypto::symmetric_encrypt_string(DECRYPTED.to_string(), KEY.to_vec()).unwrap();
471        let decrypted =
472            PureCrypto::symmetric_decrypt_string(encrypted.clone(), KEY.to_vec()).unwrap();
473        assert_eq!(decrypted, DECRYPTED);
474    }
475
476    #[test]
477    fn test_symmetric_bytes_round_trip() {
478        let encrypted =
479            PureCrypto::symmetric_encrypt_bytes(DECRYPTED.as_bytes().to_vec(), KEY.to_vec())
480                .unwrap();
481        let decrypted =
482            PureCrypto::symmetric_decrypt_bytes(encrypted.clone(), KEY.to_vec()).unwrap();
483        assert_eq!(decrypted, DECRYPTED.as_bytes().to_vec());
484    }
485
486    #[test]
487    fn test_symmetric_decrypt_array_buffer() {
488        let result = PureCrypto::symmetric_decrypt_filedata(ENCRYPTED_BYTES.to_vec(), KEY.to_vec());
489        assert!(result.is_ok());
490        assert_eq!(result.unwrap(), DECRYPTED_BYTES);
491    }
492
493    #[test]
494    fn test_symmetric_encrypt_to_array_buffer() {
495        let result = PureCrypto::symmetric_encrypt_filedata(DECRYPTED_BYTES.to_vec(), KEY.to_vec());
496        assert!(result.is_ok());
497        // Cannot test encrypted string content because IV is unique per encryption
498    }
499
500    #[test]
501    fn test_symmetric_filedata_round_trip() {
502        let encrypted =
503            PureCrypto::symmetric_encrypt_filedata(DECRYPTED_BYTES.to_vec(), KEY.to_vec()).unwrap();
504        let decrypted =
505            PureCrypto::symmetric_decrypt_filedata(encrypted.clone(), KEY.to_vec()).unwrap();
506        assert_eq!(decrypted, DECRYPTED_BYTES);
507    }
508
509    #[test]
510    fn test_make_aes256_cbc_hmac_key() {
511        let key = PureCrypto::make_user_key_aes256_cbc_hmac();
512        assert_eq!(key.len(), 64);
513    }
514
515    #[test]
516    fn test_make_xchacha20_poly1305_key() {
517        let key = PureCrypto::make_user_key_xchacha20_poly1305();
518        assert!(key.len() > 64);
519    }
520
521    #[test]
522    fn roundtrip_encrypt_user_key_with_master_password() {
523        let master_password = "test";
524        let email = "[email protected]";
525        let kdf = Kdf::PBKDF2 {
526            iterations: NonZero::try_from(600000).unwrap(),
527        };
528        let user_key = PureCrypto::make_user_key_aes256_cbc_hmac();
529        let encrypted_user_key = PureCrypto::encrypt_user_key_with_master_password(
530            user_key.clone(),
531            master_password.to_string(),
532            email.to_string(),
533            kdf.clone(),
534        )
535        .unwrap();
536        let decrypted_user_key = PureCrypto::decrypt_user_key_with_master_password(
537            encrypted_user_key,
538            master_password.to_string(),
539            email.to_string(),
540            kdf,
541        )
542        .unwrap();
543        assert_eq!(user_key, decrypted_user_key);
544    }
545
546    #[test]
547    fn test_wrap_unwrap_symmetric_key() {
548        let key_to_be_wrapped = PureCrypto::make_user_key_aes256_cbc_hmac();
549        let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac();
550        let wrapped_key =
551            PureCrypto::wrap_symmetric_key(key_to_be_wrapped.clone(), wrapping_key.clone())
552                .unwrap();
553        let unwrapped_key = PureCrypto::unwrap_symmetric_key(wrapped_key, wrapping_key).unwrap();
554        assert_eq!(key_to_be_wrapped, unwrapped_key);
555    }
556
557    #[test]
558    fn test_wrap_encapsulation_key() {
559        let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap();
560        let encapsulation_key = decapsulation_key
561            .to_public_key()
562            .to_der()
563            .unwrap()
564            .as_ref()
565            .to_vec();
566        let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac();
567        let wrapped_key =
568            PureCrypto::wrap_encapsulation_key(encapsulation_key.clone(), wrapping_key.clone())
569                .unwrap();
570        let unwrapped_key =
571            PureCrypto::unwrap_encapsulation_key(wrapped_key, wrapping_key).unwrap();
572        assert_eq!(encapsulation_key, unwrapped_key);
573    }
574
575    #[test]
576    fn test_wrap_decapsulation_key() {
577        let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap();
578        let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac();
579        let wrapped_key = PureCrypto::wrap_decapsulation_key(
580            decapsulation_key.to_der().unwrap().to_vec(),
581            wrapping_key.clone(),
582        )
583        .unwrap();
584        let unwrapped_key =
585            PureCrypto::unwrap_decapsulation_key(wrapped_key, wrapping_key).unwrap();
586        assert_eq!(decapsulation_key.to_der().unwrap().to_vec(), unwrapped_key);
587    }
588
589    #[test]
590    fn test_encapsulate_key_unsigned() {
591        let shared_key = PureCrypto::make_user_key_aes256_cbc_hmac();
592        let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap();
593        let encapsulation_key = decapsulation_key.to_public_key().to_der().unwrap();
594        let encapsulated_key = PureCrypto::encapsulate_key_unsigned(
595            shared_key.clone(),
596            encapsulation_key.clone().to_vec(),
597        )
598        .unwrap();
599        let unwrapped_key = PureCrypto::decapsulate_key_unsigned(
600            encapsulated_key,
601            decapsulation_key.to_der().unwrap().to_vec(),
602        )
603        .unwrap();
604        assert_eq!(shared_key, unwrapped_key);
605    }
606
607    #[test]
608    fn test_key_algorithm_for_verifying_key() {
609        let verifying_key =
610            VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY.to_vec())).unwrap();
611        let algorithm =
612            PureCrypto::key_algorithm_for_verifying_key(verifying_key.to_cose().to_vec()).unwrap();
613        assert_eq!(algorithm, SignatureAlgorithm::Ed25519);
614    }
615
616    #[test]
617    fn test_verifying_key_for_signing_key() {
618        let wrapped_signing_key = PureCrypto::symmetric_encrypt_bytes(
619            SIGNING_KEY.to_vec(),
620            SIGNING_KEY_WRAPPING_KEY.to_vec(),
621        )
622        .unwrap();
623        let verifying_key =
624            VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY.to_vec())).unwrap();
625        let verifying_key_derived = PureCrypto::verifying_key_for_signing_key(
626            wrapped_signing_key.to_string(),
627            SIGNING_KEY_WRAPPING_KEY.to_vec(),
628        )
629        .unwrap();
630        let verifying_key_derived =
631            VerifyingKey::from_cose(&CoseKeyBytes::from(verifying_key_derived)).unwrap();
632        assert_eq!(verifying_key.to_cose(), verifying_key_derived.to_cose());
633    }
634
635    #[test]
636    fn test_verify_and_unwrap_signed_public_key() {
637        let public_key = PureCrypto::verify_and_unwrap_signed_public_key(
638            SIGNED_PUBLIC_KEY.to_vec(),
639            VERIFYING_KEY.to_vec(),
640        )
641        .unwrap();
642        assert_eq!(public_key, PUBLIC_KEY);
643    }
644
645    #[test]
646    fn test_derive_pbkdf2_output() {
647        let password = "test_password".as_bytes();
648        let email = "[email protected]".as_bytes();
649        let kdf = Kdf::PBKDF2 {
650            iterations: NonZero::try_from(600000).unwrap(),
651        };
652        let derived_key = PureCrypto::derive_kdf_material(password, email, kdf).unwrap();
653        assert_eq!(derived_key, DERIVED_KDF_MATERIAL_PBKDF2);
654    }
655
656    #[test]
657    fn test_derived_argon2_output() {
658        let password = "test_password".as_bytes();
659        let email = "[email protected]".as_bytes();
660        let kdf = Kdf::Argon2id {
661            iterations: NonZero::try_from(3).unwrap(),
662            memory: NonZero::try_from(64).unwrap(),
663            parallelism: NonZero::try_from(4).unwrap(),
664        };
665        let derived_key = PureCrypto::derive_kdf_material(password, email, kdf).unwrap();
666        assert_eq!(derived_key, DERIVED_KDF_MATERIAL_ARGON2ID);
667    }
668}