bitwarden_crypto/store/
context.rs

1use std::{
2    cell::Cell,
3    sync::{RwLockReadGuard, RwLockWriteGuard},
4};
5
6use serde::Serialize;
7use zeroize::Zeroizing;
8
9use super::KeyStoreInner;
10use crate::{
11    derive_shareable_key, error::UnsupportedOperation, signing, store::backend::StoreBackend,
12    AsymmetricCryptoKey, BitwardenLegacyKeyBytes, ContentFormat, CryptoError, EncString, KeyId,
13    KeyIds, PublicKeyEncryptionAlgorithm, Result, RotatedUserKeys, Signature, SignatureAlgorithm,
14    SignedObject, SignedPublicKey, SignedPublicKeyMessage, SigningKey, SymmetricCryptoKey,
15    UnsignedSharedKey,
16};
17
18/// The context of a crypto operation using [super::KeyStore]
19///
20/// This will usually be accessed from an implementation of [crate::Decryptable] or
21/// [crate::CompositeEncryptable], [crate::PrimitiveEncryptable],
22/// but can also be obtained
23/// through [super::KeyStore::context]
24///
25/// This context contains access to the user keys stored in the [super::KeyStore] (sometimes
26/// referred to as `global keys`) and it also contains it's own individual secure backend for key
27/// storage. Keys stored in this individual backend are usually referred to as `local keys`, they
28/// will be cleared when this context goes out of scope and is dropped and they do not affect either
29/// the global [super::KeyStore] or other instances of contexts.
30///
31/// This context-local storage is recommended for ephemeral and temporary keys that are decrypted
32/// during the course of a decrypt/encrypt operation, but won't be used after the operation itself
33/// is complete.
34///
35/// ```rust
36/// # use bitwarden_crypto::*;
37/// # key_ids! {
38/// #     #[symmetric]
39/// #     pub enum SymmKeyId {
40/// #         User,
41/// #         Local(&'static str),
42/// #     }
43/// #     #[asymmetric]
44/// #     pub enum AsymmKeyId {
45/// #         UserPrivate,
46/// #     }
47/// #     #[signing]
48/// #     pub enum SigningKeyId {
49/// #         UserSigning,
50/// #     }
51/// #     pub Ids => SymmKeyId, AsymmKeyId, SigningKeyId;
52/// # }
53/// struct Data {
54///     key: EncString,
55///     name: String,
56/// }
57/// # impl IdentifyKey<SymmKeyId> for Data {
58/// #    fn key_identifier(&self) -> SymmKeyId {
59/// #        SymmKeyId::User
60/// #    }
61/// # }
62///
63/// const LOCAL_KEY: SymmKeyId = SymmKeyId::Local("local_key_id");
64///
65/// impl CompositeEncryptable<Ids, SymmKeyId, EncString> for Data {
66///     fn encrypt_composite(&self, ctx: &mut KeyStoreContext<Ids>, key: SymmKeyId) -> Result<EncString, CryptoError> {
67///         let local_key_id = ctx.unwrap_symmetric_key(key, LOCAL_KEY, &self.key)?;
68///         self.name.encrypt(ctx, local_key_id)
69///     }
70/// }
71/// ```
72#[must_use]
73pub struct KeyStoreContext<'a, Ids: KeyIds> {
74    pub(super) global_keys: GlobalKeys<'a, Ids>,
75
76    pub(super) local_symmetric_keys: Box<dyn StoreBackend<Ids::Symmetric>>,
77    pub(super) local_asymmetric_keys: Box<dyn StoreBackend<Ids::Asymmetric>>,
78    pub(super) local_signing_keys: Box<dyn StoreBackend<Ids::Signing>>,
79
80    // Make sure the context is !Send & !Sync
81    pub(super) _phantom: std::marker::PhantomData<(Cell<()>, RwLockReadGuard<'static, ()>)>,
82}
83
84/// A KeyStoreContext is usually limited to a read only access to the global keys,
85/// which allows us to have multiple read only contexts at the same time and do multitheaded
86/// encryption/decryption. We also have the option to create a read/write context, which allows us
87/// to modify the global keys, but only allows one context at a time. This is controlled by a
88/// [std::sync::RwLock] on the global keys, and this struct stores both types of guards.
89pub(crate) enum GlobalKeys<'a, Ids: KeyIds> {
90    ReadOnly(RwLockReadGuard<'a, KeyStoreInner<Ids>>),
91    ReadWrite(RwLockWriteGuard<'a, KeyStoreInner<Ids>>),
92}
93
94impl<Ids: KeyIds> GlobalKeys<'_, Ids> {
95    pub fn get(&self) -> &KeyStoreInner<Ids> {
96        match self {
97            GlobalKeys::ReadOnly(keys) => keys,
98            GlobalKeys::ReadWrite(keys) => keys,
99        }
100    }
101
102    pub fn get_mut(&mut self) -> Result<&mut KeyStoreInner<Ids>> {
103        match self {
104            GlobalKeys::ReadOnly(_) => Err(CryptoError::ReadOnlyKeyStore),
105            GlobalKeys::ReadWrite(keys) => Ok(keys),
106        }
107    }
108}
109
110impl<Ids: KeyIds> KeyStoreContext<'_, Ids> {
111    /// Clears all the local keys stored in this context
112    /// This will not affect the global keys even if this context has write access.
113    /// To clear the global keys, you need to use [super::KeyStore::clear] instead.
114    pub fn clear_local(&mut self) {
115        self.local_symmetric_keys.clear();
116        self.local_asymmetric_keys.clear();
117        self.local_signing_keys.clear();
118    }
119
120    /// Remove all symmetric keys from the context for which the predicate returns false
121    /// This will also remove the keys from the global store if this context has write access
122    pub fn retain_symmetric_keys(&mut self, f: fn(Ids::Symmetric) -> bool) {
123        if let Ok(keys) = self.global_keys.get_mut() {
124            keys.symmetric_keys.retain(f);
125        }
126        self.local_symmetric_keys.retain(f);
127    }
128
129    /// Remove all asymmetric keys from the context for which the predicate returns false
130    /// This will also remove the keys from the global store if this context has write access
131    pub fn retain_asymmetric_keys(&mut self, f: fn(Ids::Asymmetric) -> bool) {
132        if let Ok(keys) = self.global_keys.get_mut() {
133            keys.asymmetric_keys.retain(f);
134        }
135        self.local_asymmetric_keys.retain(f);
136    }
137
138    // TODO: All these encrypt x key with x key look like they need to be made generic,
139    // but I haven't found the best way to do that yet.
140
141    /// Decrypt a symmetric key into the context by using an already existing symmetric key
142    ///
143    /// # Arguments
144    ///
145    /// * `wrapping_key` - The key id used to decrypt the `wrapped_key`. It must already exist in
146    ///   the context
147    /// * `new_key_id` - The key id where the decrypted key will be stored. If it already exists, it
148    ///   will be overwritten
149    /// * `wrapped_key` - The key to decrypt
150    pub fn unwrap_symmetric_key(
151        &mut self,
152        wrapping_key: Ids::Symmetric,
153        new_key_id: Ids::Symmetric,
154        wrapped_key: &EncString,
155    ) -> Result<Ids::Symmetric> {
156        let wrapping_key = self.get_symmetric_key(wrapping_key)?;
157
158        let key = match (wrapped_key, wrapping_key) {
159            (EncString::Aes256Cbc_B64 { iv, data }, SymmetricCryptoKey::Aes256CbcKey(key)) => {
160                SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(
161                    crate::aes::decrypt_aes256(iv, data.clone(), &key.enc_key)?,
162                ))?
163            }
164            (
165                EncString::Aes256Cbc_HmacSha256_B64 { iv, mac, data },
166                SymmetricCryptoKey::Aes256CbcHmacKey(key),
167            ) => SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(
168                crate::aes::decrypt_aes256_hmac(iv, mac, data.clone(), &key.mac_key, &key.enc_key)?,
169            ))?,
170            (
171                EncString::Cose_Encrypt0_B64 { data },
172                SymmetricCryptoKey::XChaCha20Poly1305Key(key),
173            ) => {
174                let (content_bytes, content_format) =
175                    crate::cose::decrypt_xchacha20_poly1305(data, key)?;
176                match content_format {
177                    ContentFormat::BitwardenLegacyKey => {
178                        SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(content_bytes))?
179                    }
180                    ContentFormat::CoseKey => SymmetricCryptoKey::try_from_cose(&content_bytes)?,
181                    _ => return Err(CryptoError::InvalidKey),
182                }
183            }
184            _ => return Err(CryptoError::InvalidKey),
185        };
186
187        #[allow(deprecated)]
188        self.set_symmetric_key(new_key_id, key)?;
189
190        // Returning the new key identifier for convenience
191        Ok(new_key_id)
192    }
193
194    /// Encrypt and return a symmetric key from the context by using an already existing symmetric
195    /// key
196    ///
197    /// # Arguments
198    ///
199    /// * `wrapping_key` - The key id used to wrap (encrypt) the `key_to_wrap`. It must already
200    ///   exist in the context
201    /// * `key_to_wrap` - The key id to wrap. It must already exist in the context
202    pub fn wrap_symmetric_key(
203        &self,
204        wrapping_key: Ids::Symmetric,
205        key_to_wrap: Ids::Symmetric,
206    ) -> Result<EncString> {
207        use SymmetricCryptoKey::*;
208
209        let wrapping_key_instance = self.get_symmetric_key(wrapping_key)?;
210        let key_to_wrap_instance = self.get_symmetric_key(key_to_wrap)?;
211        // `Aes256CbcHmacKey` can wrap keys by encrypting their byte serialization obtained using
212        // `SymmetricCryptoKey::to_encoded()`. `XChaCha20Poly1305Key` need to specify the
213        // content format to be either octet stream, in case the wrapped key is a Aes256CbcHmacKey
214        // or `Aes256CbcKey`, or by specifying the content format to be CoseKey, in case the
215        // wrapped key is a `XChaCha20Poly1305Key`.
216        match (wrapping_key_instance, key_to_wrap_instance) {
217            (
218                Aes256CbcHmacKey(_),
219                Aes256CbcHmacKey(_) | Aes256CbcKey(_) | XChaCha20Poly1305Key(_),
220            ) => self.encrypt_data_with_symmetric_key(
221                wrapping_key,
222                key_to_wrap_instance
223                    .to_encoded()
224                    .as_ref()
225                    .to_vec()
226                    .as_slice(),
227                ContentFormat::BitwardenLegacyKey,
228            ),
229            (XChaCha20Poly1305Key(_), _) => {
230                let encoded = key_to_wrap_instance.to_encoded_raw();
231                let content_format = encoded.content_format();
232                self.encrypt_data_with_symmetric_key(
233                    wrapping_key,
234                    Into::<Vec<u8>>::into(encoded).as_slice(),
235                    content_format,
236                )
237            }
238            _ => Err(CryptoError::OperationNotSupported(
239                UnsupportedOperation::EncryptionNotImplementedForKey,
240            )),
241        }
242    }
243
244    /// Decapsulate a symmetric key into the context by using an already existing asymmetric key
245    ///
246    /// # Arguments
247    ///
248    /// * `decapsulation_key` - The key id used to decrypt the `encrypted_key`. It must already
249    ///   exist in the context
250    /// * `new_key_id` - The key id where the decrypted key will be stored. If it already exists, it
251    ///   will be overwritten
252    /// * `encapsulated_shared_key` - The symmetric key to decrypt
253    pub fn decapsulate_key_unsigned(
254        &mut self,
255        decapsulation_key: Ids::Asymmetric,
256        new_key_id: Ids::Symmetric,
257        encapsulated_shared_key: &UnsignedSharedKey,
258    ) -> Result<Ids::Symmetric> {
259        let decapsulation_key = self.get_asymmetric_key(decapsulation_key)?;
260        let decapsulated_key =
261            encapsulated_shared_key.decapsulate_key_unsigned(decapsulation_key)?;
262
263        #[allow(deprecated)]
264        self.set_symmetric_key(new_key_id, decapsulated_key)?;
265
266        // Returning the new key identifier for convenience
267        Ok(new_key_id)
268    }
269
270    /// Encapsulate and return a symmetric key from the context by using an already existing
271    /// asymmetric key
272    ///
273    /// # Arguments
274    ///
275    /// * `encapsulation_key` - The key id used to encrypt the `encapsulated_key`. It must already
276    ///   exist in the context
277    /// * `shared_key` - The key id to encrypt. It must already exist in the context
278    pub fn encapsulate_key_unsigned(
279        &self,
280        encapsulation_key: Ids::Asymmetric,
281        shared_key: Ids::Symmetric,
282    ) -> Result<UnsignedSharedKey> {
283        UnsignedSharedKey::encapsulate_key_unsigned(
284            self.get_symmetric_key(shared_key)?,
285            &self.get_asymmetric_key(encapsulation_key)?.to_public_key(),
286        )
287    }
288
289    /// Returns `true` if the context has a symmetric key with the given identifier
290    pub fn has_symmetric_key(&self, key_id: Ids::Symmetric) -> bool {
291        self.get_symmetric_key(key_id).is_ok()
292    }
293
294    /// Returns `true` if the context has an asymmetric key with the given identifier
295    pub fn has_asymmetric_key(&self, key_id: Ids::Asymmetric) -> bool {
296        self.get_asymmetric_key(key_id).is_ok()
297    }
298
299    /// Returns `true` if the context has a signing key with the given identifier
300    pub fn has_signing_key(&self, key_id: Ids::Signing) -> bool {
301        self.get_signing_key(key_id).is_ok()
302    }
303
304    /// Generate a new random symmetric key and store it in the context
305    pub fn generate_symmetric_key(&mut self, key_id: Ids::Symmetric) -> Result<Ids::Symmetric> {
306        let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
307        #[allow(deprecated)]
308        self.set_symmetric_key(key_id, key)?;
309        Ok(key_id)
310    }
311
312    /// Generate a new random xchacha20-poly1305 symmetric key and store it in the context
313    #[cfg(test)]
314    pub(crate) fn make_cose_symmetric_key(
315        &mut self,
316        key_id: Ids::Symmetric,
317    ) -> Result<Ids::Symmetric> {
318        let key = SymmetricCryptoKey::make_xchacha20_poly1305_key();
319        #[allow(deprecated)]
320        self.set_symmetric_key(key_id, key)?;
321        Ok(key_id)
322    }
323
324    /// Makes a new asymmetric encryption key using the current default algorithm, and stores it in
325    /// the context
326    pub fn make_asymmetric_key(&mut self, key_id: Ids::Asymmetric) -> Result<Ids::Asymmetric> {
327        let key = AsymmetricCryptoKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
328        #[allow(deprecated)]
329        self.set_asymmetric_key(key_id, key)?;
330        Ok(key_id)
331    }
332
333    /// Generate a new signature key using the current default algorithm, and store it in the
334    /// context
335    pub fn make_signing_key(&mut self, key_id: Ids::Signing) -> Result<Ids::Signing> {
336        let key = SigningKey::make(SignatureAlgorithm::default_algorithm());
337        #[allow(deprecated)]
338        self.set_signing_key(key_id, key)?;
339        Ok(key_id)
340    }
341
342    /// Derive a shareable key using hkdf from secret and name and store it in the context.
343    ///
344    /// A specialized variant of this function was called `CryptoService.makeSendKey` in the
345    /// Bitwarden `clients` repository.
346    pub fn derive_shareable_key(
347        &mut self,
348        key_id: Ids::Symmetric,
349        secret: Zeroizing<[u8; 16]>,
350        name: &str,
351        info: Option<&str>,
352    ) -> Result<Ids::Symmetric> {
353        #[allow(deprecated)]
354        self.set_symmetric_key(
355            key_id,
356            SymmetricCryptoKey::Aes256CbcHmacKey(derive_shareable_key(secret, name, info)),
357        )?;
358        Ok(key_id)
359    }
360
361    #[deprecated(note = "This function should ideally never be used outside this crate")]
362    #[allow(missing_docs)]
363    pub fn dangerous_get_symmetric_key(
364        &self,
365        key_id: Ids::Symmetric,
366    ) -> Result<&SymmetricCryptoKey> {
367        self.get_symmetric_key(key_id)
368    }
369
370    #[deprecated(note = "This function should ideally never be used outside this crate")]
371    #[allow(missing_docs)]
372    pub fn dangerous_get_asymmetric_key(
373        &self,
374        key_id: Ids::Asymmetric,
375    ) -> Result<&AsymmetricCryptoKey> {
376        self.get_asymmetric_key(key_id)
377    }
378
379    /// Makes a signed public key from an asymmetric private key and signing key stored in context.
380    /// Signing a public key asserts ownership, and makes the claim to other users that if they want
381    /// to share with you, they can use this public key.
382    pub fn make_signed_public_key(
383        &self,
384        private_key_id: Ids::Asymmetric,
385        signing_key_id: Ids::Signing,
386    ) -> Result<SignedPublicKey> {
387        let public_key = self.get_asymmetric_key(private_key_id)?.to_public_key();
388        let signing_key = self.get_signing_key(signing_key_id)?;
389        let signed_public_key =
390            SignedPublicKeyMessage::from_public_key(&public_key)?.sign(signing_key)?;
391        Ok(signed_public_key)
392    }
393
394    fn get_symmetric_key(&self, key_id: Ids::Symmetric) -> Result<&SymmetricCryptoKey> {
395        if key_id.is_local() {
396            self.local_symmetric_keys.get(key_id)
397        } else {
398            self.global_keys.get().symmetric_keys.get(key_id)
399        }
400        .ok_or_else(|| crate::CryptoError::MissingKeyId(format!("{key_id:?}")))
401    }
402
403    pub(super) fn get_asymmetric_key(
404        &self,
405        key_id: Ids::Asymmetric,
406    ) -> Result<&AsymmetricCryptoKey> {
407        if key_id.is_local() {
408            self.local_asymmetric_keys.get(key_id)
409        } else {
410            self.global_keys.get().asymmetric_keys.get(key_id)
411        }
412        .ok_or_else(|| crate::CryptoError::MissingKeyId(format!("{key_id:?}")))
413    }
414
415    pub(super) fn get_signing_key(&self, key_id: Ids::Signing) -> Result<&SigningKey> {
416        if key_id.is_local() {
417            self.local_signing_keys.get(key_id)
418        } else {
419            self.global_keys.get().signing_keys.get(key_id)
420        }
421        .ok_or_else(|| crate::CryptoError::MissingKeyId(format!("{key_id:?}")))
422    }
423
424    #[deprecated(note = "This function should ideally never be used outside this crate")]
425    #[allow(missing_docs)]
426    pub fn set_symmetric_key(
427        &mut self,
428        key_id: Ids::Symmetric,
429        key: SymmetricCryptoKey,
430    ) -> Result<()> {
431        if key_id.is_local() {
432            self.local_symmetric_keys.upsert(key_id, key);
433        } else {
434            self.global_keys
435                .get_mut()?
436                .symmetric_keys
437                .upsert(key_id, key);
438        }
439        Ok(())
440    }
441
442    #[deprecated(note = "This function should ideally never be used outside this crate")]
443    #[allow(missing_docs)]
444    pub fn set_asymmetric_key(
445        &mut self,
446        key_id: Ids::Asymmetric,
447        key: AsymmetricCryptoKey,
448    ) -> Result<()> {
449        if key_id.is_local() {
450            self.local_asymmetric_keys.upsert(key_id, key);
451        } else {
452            self.global_keys
453                .get_mut()?
454                .asymmetric_keys
455                .upsert(key_id, key);
456        }
457        Ok(())
458    }
459
460    /// Sets a signing key in the context
461    #[deprecated(note = "This function should ideally never be used outside this crate")]
462    pub fn set_signing_key(&mut self, key_id: Ids::Signing, key: SigningKey) -> Result<()> {
463        if key_id.is_local() {
464            self.local_signing_keys.upsert(key_id, key);
465        } else {
466            self.global_keys.get_mut()?.signing_keys.upsert(key_id, key);
467        }
468        Ok(())
469    }
470
471    pub(crate) fn decrypt_data_with_symmetric_key(
472        &self,
473        key: Ids::Symmetric,
474        data: &EncString,
475    ) -> Result<Vec<u8>> {
476        let key = self.get_symmetric_key(key)?;
477
478        match (data, key) {
479            (EncString::Aes256Cbc_B64 { iv, data }, SymmetricCryptoKey::Aes256CbcKey(key)) => {
480                crate::aes::decrypt_aes256(iv, data.clone(), &key.enc_key)
481            }
482            (
483                EncString::Aes256Cbc_HmacSha256_B64 { iv, mac, data },
484                SymmetricCryptoKey::Aes256CbcHmacKey(key),
485            ) => crate::aes::decrypt_aes256_hmac(iv, mac, data.clone(), &key.mac_key, &key.enc_key),
486            (
487                EncString::Cose_Encrypt0_B64 { data },
488                SymmetricCryptoKey::XChaCha20Poly1305Key(key),
489            ) => {
490                let (data, _) = crate::cose::decrypt_xchacha20_poly1305(data, key)?;
491                Ok(data)
492            }
493            _ => Err(CryptoError::InvalidKey),
494        }
495    }
496
497    pub(crate) fn encrypt_data_with_symmetric_key(
498        &self,
499        key: Ids::Symmetric,
500        data: &[u8],
501        content_format: ContentFormat,
502    ) -> Result<EncString> {
503        let key = self.get_symmetric_key(key)?;
504        match key {
505            SymmetricCryptoKey::Aes256CbcKey(_) => Err(CryptoError::OperationNotSupported(
506                UnsupportedOperation::EncryptionNotImplementedForKey,
507            )),
508            SymmetricCryptoKey::Aes256CbcHmacKey(key) => EncString::encrypt_aes256_hmac(data, key),
509            SymmetricCryptoKey::XChaCha20Poly1305Key(key) => {
510                EncString::encrypt_xchacha20_poly1305(data, key, content_format)
511            }
512        }
513    }
514
515    /// Signs the given data using the specified signing key, for the given
516    /// [crate::SigningNamespace] and returns the signature and the serialized message. See
517    /// [crate::SigningKey::sign]
518    pub fn sign<Message: Serialize>(
519        &self,
520        key: Ids::Signing,
521        message: &Message,
522        namespace: &crate::SigningNamespace,
523    ) -> Result<SignedObject> {
524        self.get_signing_key(key)?.sign(message, namespace)
525    }
526
527    /// Signs the given data using the specified signing key, for the given
528    /// [crate::SigningNamespace] and returns the signature and the serialized message. See
529    /// [crate::SigningKey::sign_detached]
530    #[allow(unused)]
531    pub(crate) fn sign_detached<Message: Serialize>(
532        &self,
533        key: Ids::Signing,
534        message: &Message,
535        namespace: &crate::SigningNamespace,
536    ) -> Result<(Signature, signing::SerializedMessage)> {
537        self.get_signing_key(key)?.sign_detached(message, namespace)
538    }
539
540    /// Re-encrypts the user's keys with the provided symmetric key for a v2 user.
541    pub fn dangerous_get_v2_rotated_account_keys(
542        &self,
543        current_user_private_key_id: Ids::Asymmetric,
544        current_user_signing_key_id: Ids::Signing,
545    ) -> Result<RotatedUserKeys> {
546        crate::dangerous_get_v2_rotated_account_keys(
547            current_user_private_key_id,
548            current_user_signing_key_id,
549            self,
550        )
551    }
552}
553
554#[cfg(test)]
555#[allow(deprecated)]
556mod tests {
557    use serde::{Deserialize, Serialize};
558
559    use crate::{
560        store::{
561            tests::{Data, DataView},
562            KeyStore,
563        },
564        traits::tests::{TestAsymmKey, TestIds, TestSigningKey, TestSymmKey},
565        AsymmetricCryptoKey, AsymmetricPublicCryptoKey, CompositeEncryptable, CoseKeyBytes,
566        CoseSerializable, CryptoError, Decryptable, KeyDecryptable, Pkcs8PrivateKeyBytes,
567        PublicKeyEncryptionAlgorithm, SignatureAlgorithm, SigningKey, SigningNamespace,
568        SymmetricCryptoKey,
569    };
570
571    #[test]
572    fn test_set_signing_key() {
573        let store: KeyStore<TestIds> = KeyStore::default();
574
575        // Generate and insert a key
576        let key_a0_id = TestSigningKey::A(0);
577        let key_a0 = SigningKey::make(SignatureAlgorithm::Ed25519);
578        store
579            .context_mut()
580            .set_signing_key(key_a0_id, key_a0)
581            .unwrap();
582    }
583
584    #[test]
585    fn test_set_keys_for_encryption() {
586        let store: KeyStore<TestIds> = KeyStore::default();
587
588        // Generate and insert a key
589        let key_a0_id = TestSymmKey::A(0);
590        let key_a0 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
591
592        store
593            .context_mut()
594            .set_symmetric_key(TestSymmKey::A(0), key_a0.clone())
595            .unwrap();
596
597        assert!(store.context().has_symmetric_key(key_a0_id));
598
599        // Encrypt some data with the key
600        let data = DataView("Hello, World!".to_string(), key_a0_id);
601        let _encrypted: Data = data
602            .encrypt_composite(&mut store.context(), key_a0_id)
603            .unwrap();
604    }
605
606    #[test]
607    fn test_key_encryption() {
608        let store: KeyStore<TestIds> = KeyStore::default();
609
610        let mut ctx = store.context();
611
612        // Generate and insert a key
613        let key_1_id = TestSymmKey::C(1);
614        let key_1 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
615
616        ctx.set_symmetric_key(key_1_id, key_1.clone()).unwrap();
617
618        assert!(ctx.has_symmetric_key(key_1_id));
619
620        // Generate and insert a new key
621        let key_2_id = TestSymmKey::C(2);
622        let key_2 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
623
624        ctx.set_symmetric_key(key_2_id, key_2.clone()).unwrap();
625
626        assert!(ctx.has_symmetric_key(key_2_id));
627
628        // Encrypt the new key with the old key
629        let key_2_enc = ctx.wrap_symmetric_key(key_1_id, key_2_id).unwrap();
630
631        // Decrypt the new key with the old key in a different identifier
632        let new_key_id = TestSymmKey::C(3);
633
634        ctx.unwrap_symmetric_key(key_1_id, new_key_id, &key_2_enc)
635            .unwrap();
636
637        // Now `key_2_id` and `new_key_id` contain the same key, so we should be able to encrypt
638        // with one and decrypt with the other
639
640        let data = DataView("Hello, World!".to_string(), key_2_id);
641        let encrypted = data.encrypt_composite(&mut ctx, key_2_id).unwrap();
642
643        let decrypted1 = encrypted.decrypt(&mut ctx, key_2_id).unwrap();
644        let decrypted2 = encrypted.decrypt(&mut ctx, new_key_id).unwrap();
645
646        // Assert that the decrypted data is the same
647        assert_eq!(decrypted1.0, decrypted2.0);
648    }
649
650    #[test]
651    fn test_wrap_unwrap() {
652        let store: KeyStore<TestIds> = KeyStore::default();
653        let mut ctx = store.context_mut();
654
655        // Aes256 CBC HMAC keys
656        let key_aes_1_id = TestSymmKey::A(1);
657        let key_aes_1 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
658        ctx.set_symmetric_key(key_aes_1_id, key_aes_1.clone())
659            .unwrap();
660        let key_aes_2_id = TestSymmKey::A(2);
661        let key_aes_2 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
662        ctx.set_symmetric_key(key_aes_2_id, key_aes_2.clone())
663            .unwrap();
664
665        // XChaCha20 Poly1305 keys
666        let key_xchacha_3_id = TestSymmKey::A(3);
667        let key_xchacha_3 = SymmetricCryptoKey::make_xchacha20_poly1305_key();
668        ctx.set_symmetric_key(key_xchacha_3_id, key_xchacha_3.clone())
669            .unwrap();
670        let key_xchacha_4_id = TestSymmKey::A(4);
671        let key_xchacha_4 = SymmetricCryptoKey::make_xchacha20_poly1305_key();
672        ctx.set_symmetric_key(key_xchacha_4_id, key_xchacha_4.clone())
673            .unwrap();
674
675        // Wrap and unwrap the keys
676        let wrapped_key_1_2 = ctx.wrap_symmetric_key(key_aes_1_id, key_aes_2_id).unwrap();
677        let wrapped_key_1_3 = ctx
678            .wrap_symmetric_key(key_aes_1_id, key_xchacha_3_id)
679            .unwrap();
680        let wrapped_key_3_1 = ctx
681            .wrap_symmetric_key(key_xchacha_3_id, key_aes_1_id)
682            .unwrap();
683        let wrapped_key_3_4 = ctx
684            .wrap_symmetric_key(key_xchacha_3_id, key_xchacha_4_id)
685            .unwrap();
686
687        // Unwrap the keys
688        let unwrapped_key_2 = ctx
689            .unwrap_symmetric_key(key_aes_1_id, key_aes_2_id, &wrapped_key_1_2)
690            .unwrap();
691        let unwrapped_key_3 = ctx
692            .unwrap_symmetric_key(key_aes_1_id, key_xchacha_3_id, &wrapped_key_1_3)
693            .unwrap();
694        let unwrapped_key_1 = ctx
695            .unwrap_symmetric_key(key_xchacha_3_id, key_aes_1_id, &wrapped_key_3_1)
696            .unwrap();
697        let unwrapped_key_4 = ctx
698            .unwrap_symmetric_key(key_xchacha_3_id, key_xchacha_4_id, &wrapped_key_3_4)
699            .unwrap();
700
701        // Assert that the unwrapped keys are the same as the original keys
702        assert_eq!(unwrapped_key_2, key_aes_2_id);
703        assert_eq!(unwrapped_key_3, key_xchacha_3_id);
704        assert_eq!(unwrapped_key_1, key_aes_1_id);
705        assert_eq!(unwrapped_key_4, key_xchacha_4_id);
706    }
707
708    #[test]
709    fn test_signing() {
710        let store: KeyStore<TestIds> = KeyStore::default();
711
712        // Generate and insert a key
713        let key_a0_id = TestSigningKey::A(0);
714        let key_a0 = SigningKey::make(SignatureAlgorithm::Ed25519);
715        let verifying_key = key_a0.to_verifying_key();
716        store
717            .context_mut()
718            .set_signing_key(key_a0_id, key_a0)
719            .unwrap();
720
721        assert!(store.context().has_signing_key(key_a0_id));
722
723        // Sign some data with the key
724        #[derive(Serialize, Deserialize)]
725        struct TestData {
726            data: String,
727        }
728        let signed_object = store
729            .context()
730            .sign(
731                key_a0_id,
732                &TestData {
733                    data: "Hello".to_string(),
734                },
735                &SigningNamespace::ExampleNamespace,
736            )
737            .unwrap();
738        let payload: Result<TestData, CryptoError> =
739            signed_object.verify_and_unwrap(&verifying_key, &SigningNamespace::ExampleNamespace);
740        assert!(payload.is_ok());
741
742        let (signature, serialized_message) = store
743            .context()
744            .sign_detached(
745                key_a0_id,
746                &TestData {
747                    data: "Hello".to_string(),
748                },
749                &SigningNamespace::ExampleNamespace,
750            )
751            .unwrap();
752        assert!(signature.verify(
753            serialized_message.as_bytes(),
754            &verifying_key,
755            &SigningNamespace::ExampleNamespace
756        ))
757    }
758
759    #[test]
760    fn test_account_key_rotation() {
761        let store: KeyStore<TestIds> = KeyStore::default();
762        let mut ctx = store.context_mut();
763
764        // Generate a new user key
765        let current_user_private_key_id = TestAsymmKey::A(0);
766        let current_user_signing_key_id = TestSigningKey::A(0);
767
768        // Make the keys
769        ctx.generate_symmetric_key(TestSymmKey::A(0)).unwrap();
770        ctx.make_signing_key(current_user_signing_key_id).unwrap();
771        ctx.set_asymmetric_key(
772            current_user_private_key_id,
773            AsymmetricCryptoKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1),
774        )
775        .unwrap();
776
777        // Get the rotated account keys
778        let rotated_keys = ctx
779            .dangerous_get_v2_rotated_account_keys(
780                current_user_private_key_id,
781                current_user_signing_key_id,
782            )
783            .unwrap();
784
785        // Public/Private key
786        assert_eq!(
787            AsymmetricPublicCryptoKey::from_der(&rotated_keys.public_key)
788                .unwrap()
789                .to_der()
790                .unwrap(),
791            ctx.get_asymmetric_key(current_user_private_key_id)
792                .unwrap()
793                .to_public_key()
794                .to_der()
795                .unwrap()
796        );
797        let decrypted_private_key: Vec<u8> = rotated_keys
798            .private_key
799            .decrypt_with_key(&rotated_keys.user_key)
800            .unwrap();
801        let private_key =
802            AsymmetricCryptoKey::from_der(&Pkcs8PrivateKeyBytes::from(decrypted_private_key))
803                .unwrap();
804        assert_eq!(
805            private_key.to_der().unwrap(),
806            ctx.get_asymmetric_key(current_user_private_key_id)
807                .unwrap()
808                .to_der()
809                .unwrap()
810        );
811
812        // Signing Key
813        let decrypted_signing_key: Vec<u8> = rotated_keys
814            .signing_key
815            .decrypt_with_key(&rotated_keys.user_key)
816            .unwrap();
817        let signing_key =
818            SigningKey::from_cose(&CoseKeyBytes::from(decrypted_signing_key)).unwrap();
819        assert_eq!(
820            signing_key.to_cose(),
821            ctx.get_signing_key(current_user_signing_key_id)
822                .unwrap()
823                .to_cose(),
824        );
825
826        // Signed Public Key
827        let signed_public_key = rotated_keys.signed_public_key;
828        let unwrapped_key = signed_public_key
829            .verify_and_unwrap(
830                &ctx.get_signing_key(current_user_signing_key_id)
831                    .unwrap()
832                    .to_verifying_key(),
833            )
834            .unwrap();
835        assert_eq!(
836            unwrapped_key.to_der().unwrap(),
837            ctx.get_asymmetric_key(current_user_private_key_id)
838                .unwrap()
839                .to_public_key()
840                .to_der()
841                .unwrap()
842        );
843    }
844}