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, CryptoError, EncString, KeyId, KeyIds, Result, Signature,
13    SignatureAlgorithm, SignedObject, SignedPublicKey, SignedPublicKeyMessage, SigningKey,
14    SymmetricCryptoKey, UnsignedSharedKey,
15};
16
17/// The context of a crypto operation using [super::KeyStore]
18///
19/// This will usually be accessed from an implementation of [crate::Decryptable] or
20/// [crate::Encryptable], but can also be obtained through [super::KeyStore::context]
21///
22/// This context contains access to the user keys stored in the [super::KeyStore] (sometimes
23/// referred to as `global keys`) and it also contains it's own individual secure backend for key
24/// storage. Keys stored in this individual backend are usually referred to as `local keys`, they
25/// will be cleared when this context goes out of scope and is dropped and they do not affect either
26/// the global [super::KeyStore] or other instances of contexts.
27///
28/// This context-local storage is recommended for ephemeral and temporary keys that are decrypted
29/// during the course of a decrypt/encrypt operation, but won't be used after the operation itself
30/// is complete.
31///
32/// ```rust
33/// # use bitwarden_crypto::*;
34/// # key_ids! {
35/// #     #[symmetric]
36/// #     pub enum SymmKeyId {
37/// #         User,
38/// #         Local(&'static str),
39/// #     }
40/// #     #[asymmetric]
41/// #     pub enum AsymmKeyId {
42/// #         UserPrivate,
43/// #     }
44/// #     #[signing]
45/// #     pub enum SigningKeyId {
46/// #         UserSigning,
47/// #     }
48/// #     pub Ids => SymmKeyId, AsymmKeyId, SigningKeyId;
49/// # }
50/// struct Data {
51///     key: EncString,
52///     name: String,
53/// }
54/// # impl IdentifyKey<SymmKeyId> for Data {
55/// #    fn key_identifier(&self) -> SymmKeyId {
56/// #        SymmKeyId::User
57/// #    }
58/// # }
59///
60/// const LOCAL_KEY: SymmKeyId = SymmKeyId::Local("local_key_id");
61///
62/// impl Encryptable<Ids, SymmKeyId, EncString> for Data {
63///     fn encrypt(&self, ctx: &mut KeyStoreContext<Ids>, key: SymmKeyId) -> Result<EncString, CryptoError> {
64///         let local_key_id = ctx.unwrap_symmetric_key(key, LOCAL_KEY, &self.key)?;
65///         self.name.encrypt(ctx, local_key_id)
66///     }
67/// }
68/// ```
69#[must_use]
70pub struct KeyStoreContext<'a, Ids: KeyIds> {
71    pub(super) global_keys: GlobalKeys<'a, Ids>,
72
73    pub(super) local_symmetric_keys: Box<dyn StoreBackend<Ids::Symmetric>>,
74    pub(super) local_asymmetric_keys: Box<dyn StoreBackend<Ids::Asymmetric>>,
75    pub(super) local_signing_keys: Box<dyn StoreBackend<Ids::Signing>>,
76
77    // Make sure the context is !Send & !Sync
78    pub(super) _phantom: std::marker::PhantomData<(Cell<()>, RwLockReadGuard<'static, ()>)>,
79}
80
81/// A KeyStoreContext is usually limited to a read only access to the global keys,
82/// which allows us to have multiple read only contexts at the same time and do multitheaded
83/// encryption/decryption. We also have the option to create a read/write context, which allows us
84/// to modify the global keys, but only allows one context at a time. This is controlled by a
85/// [std::sync::RwLock] on the global keys, and this struct stores both types of guards.
86pub(crate) enum GlobalKeys<'a, Ids: KeyIds> {
87    ReadOnly(RwLockReadGuard<'a, KeyStoreInner<Ids>>),
88    ReadWrite(RwLockWriteGuard<'a, KeyStoreInner<Ids>>),
89}
90
91impl<Ids: KeyIds> GlobalKeys<'_, Ids> {
92    pub fn get(&self) -> &KeyStoreInner<Ids> {
93        match self {
94            GlobalKeys::ReadOnly(keys) => keys,
95            GlobalKeys::ReadWrite(keys) => keys,
96        }
97    }
98
99    pub fn get_mut(&mut self) -> Result<&mut KeyStoreInner<Ids>> {
100        match self {
101            GlobalKeys::ReadOnly(_) => Err(CryptoError::ReadOnlyKeyStore),
102            GlobalKeys::ReadWrite(keys) => Ok(keys),
103        }
104    }
105}
106
107impl<Ids: KeyIds> KeyStoreContext<'_, Ids> {
108    /// Clears all the local keys stored in this context
109    /// This will not affect the global keys even if this context has write access.
110    /// To clear the global keys, you need to use [super::KeyStore::clear] instead.
111    pub fn clear_local(&mut self) {
112        self.local_symmetric_keys.clear();
113        self.local_asymmetric_keys.clear();
114        self.local_signing_keys.clear();
115    }
116
117    /// Remove all symmetric keys from the context for which the predicate returns false
118    /// This will also remove the keys from the global store if this context has write access
119    pub fn retain_symmetric_keys(&mut self, f: fn(Ids::Symmetric) -> bool) {
120        if let Ok(keys) = self.global_keys.get_mut() {
121            keys.symmetric_keys.retain(f);
122        }
123        self.local_symmetric_keys.retain(f);
124    }
125
126    /// Remove all asymmetric keys from the context for which the predicate returns false
127    /// This will also remove the keys from the global store if this context has write access
128    pub fn retain_asymmetric_keys(&mut self, f: fn(Ids::Asymmetric) -> bool) {
129        if let Ok(keys) = self.global_keys.get_mut() {
130            keys.asymmetric_keys.retain(f);
131        }
132        self.local_asymmetric_keys.retain(f);
133    }
134
135    // TODO: All these encrypt x key with x key look like they need to be made generic,
136    // but I haven't found the best way to do that yet.
137
138    /// Decrypt a symmetric key into the context by using an already existing symmetric key
139    ///
140    /// # Arguments
141    ///
142    /// * `encryption_key` - The key id used to decrypt the `encrypted_key`. It must already exist
143    ///   in the context
144    /// * `new_key_id` - The key id where the decrypted key will be stored. If it already exists, it
145    ///   will be overwritten
146    /// * `encrypted_key` - The key to decrypt
147    pub fn unwrap_symmetric_key(
148        &mut self,
149        encryption_key: Ids::Symmetric,
150        new_key_id: Ids::Symmetric,
151        encrypted_key: &EncString,
152    ) -> Result<Ids::Symmetric> {
153        let mut new_key_material =
154            self.decrypt_data_with_symmetric_key(encryption_key, encrypted_key)?;
155
156        #[allow(deprecated)]
157        self.set_symmetric_key(
158            new_key_id,
159            SymmetricCryptoKey::try_from(new_key_material.as_mut_slice())?,
160        )?;
161
162        // Returning the new key identifier for convenience
163        Ok(new_key_id)
164    }
165
166    /// Encrypt and return a symmetric key from the context by using an already existing symmetric
167    /// key
168    ///
169    /// # Arguments
170    ///
171    /// * `wrapping_key` - The key id used to wrap (encrypt) the `key_to_wrap`. It must already
172    ///   exist in the context
173    /// * `key_to_wrap` - The key id to wrap. It must already exist in the context
174    pub fn wrap_symmetric_key(
175        &self,
176        wrapping_key: Ids::Symmetric,
177        key_to_wrap: Ids::Symmetric,
178    ) -> Result<EncString> {
179        use SymmetricCryptoKey::*;
180
181        let wrapping_key_instance = self.get_symmetric_key(wrapping_key)?;
182        let key_to_wrap_instance = self.get_symmetric_key(key_to_wrap)?;
183        // `Aes256CbcHmacKey` can wrap keys by encrypting their byte serialization obtained using
184        // `SymmetricCryptoKey::to_encoded()`. `XChaCha20Poly1305Key` need to specify the
185        // content format to be either octet stream, in case the wrapped key is a Aes256CbcHmacKey
186        // or `Aes256CbcKey`, or by specifying the content format to be CoseKey, in case the
187        // wrapped key is a `XChaCha20Poly1305Key`.
188        match (wrapping_key_instance, key_to_wrap_instance) {
189            (Aes256CbcHmacKey(_), Aes256CbcHmacKey(_) | Aes256CbcKey(_)) => self
190                .encrypt_data_with_symmetric_key(
191                    wrapping_key,
192                    key_to_wrap_instance.to_encoded().as_slice(),
193                ),
194            _ => Err(CryptoError::OperationNotSupported(
195                UnsupportedOperation::EncryptionNotImplementedForKey,
196            )),
197        }
198    }
199
200    /// Decapsulate a symmetric key into the context by using an already existing asymmetric key
201    ///
202    /// # Arguments
203    ///
204    /// * `decapsulation_key` - The key id used to decrypt the `encrypted_key`. It must already
205    ///   exist in the context
206    /// * `new_key_id` - The key id where the decrypted key will be stored. If it already exists, it
207    ///   will be overwritten
208    /// * `encapsulated_shared_key` - The symmetric key to decrypt
209    pub fn decapsulate_key_unsigned(
210        &mut self,
211        decapsulation_key: Ids::Asymmetric,
212        new_key_id: Ids::Symmetric,
213        encapsulated_shared_key: &UnsignedSharedKey,
214    ) -> Result<Ids::Symmetric> {
215        let decapsulation_key = self.get_asymmetric_key(decapsulation_key)?;
216        let decapsulated_key =
217            encapsulated_shared_key.decapsulate_key_unsigned(decapsulation_key)?;
218
219        #[allow(deprecated)]
220        self.set_symmetric_key(new_key_id, decapsulated_key)?;
221
222        // Returning the new key identifier for convenience
223        Ok(new_key_id)
224    }
225
226    /// Encapsulate and return a symmetric key from the context by using an already existing
227    /// asymmetric key
228    ///
229    /// # Arguments
230    ///
231    /// * `encapsulation_key` - The key id used to encrypt the `encapsulated_key`. It must already
232    ///   exist in the context
233    /// * `shared_key` - The key id to encrypt. It must already exist in the context
234    pub fn encapsulate_key_unsigned(
235        &self,
236        encapsulation_key: Ids::Asymmetric,
237        shared_key: Ids::Symmetric,
238    ) -> Result<UnsignedSharedKey> {
239        UnsignedSharedKey::encapsulate_key_unsigned(
240            self.get_symmetric_key(shared_key)?,
241            &self.get_asymmetric_key(encapsulation_key)?.to_public_key(),
242        )
243    }
244
245    /// Returns `true` if the context has a symmetric key with the given identifier
246    pub fn has_symmetric_key(&self, key_id: Ids::Symmetric) -> bool {
247        self.get_symmetric_key(key_id).is_ok()
248    }
249
250    /// Returns `true` if the context has an asymmetric key with the given identifier
251    pub fn has_asymmetric_key(&self, key_id: Ids::Asymmetric) -> bool {
252        self.get_asymmetric_key(key_id).is_ok()
253    }
254
255    /// Returns `true` if the context has a signing key with the given identifier
256    pub fn has_signing_key(&self, key_id: Ids::Signing) -> bool {
257        self.get_signing_key(key_id).is_ok()
258    }
259
260    /// Generate a new random symmetric key and store it in the context
261    pub fn generate_symmetric_key(&mut self, key_id: Ids::Symmetric) -> Result<Ids::Symmetric> {
262        let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
263        #[allow(deprecated)]
264        self.set_symmetric_key(key_id, key)?;
265        Ok(key_id)
266    }
267
268    /// Generate a new signature key using the current default algorithm, and store it in the
269    /// context
270    pub fn make_signing_key(&mut self, key_id: Ids::Signing) -> Result<Ids::Signing> {
271        let key = SigningKey::make(SignatureAlgorithm::default_algorithm());
272        #[allow(deprecated)]
273        self.set_signing_key(key_id, key)?;
274        Ok(key_id)
275    }
276
277    /// Derive a shareable key using hkdf from secret and name and store it in the context.
278    ///
279    /// A specialized variant of this function was called `CryptoService.makeSendKey` in the
280    /// Bitwarden `clients` repository.
281    pub fn derive_shareable_key(
282        &mut self,
283        key_id: Ids::Symmetric,
284        secret: Zeroizing<[u8; 16]>,
285        name: &str,
286        info: Option<&str>,
287    ) -> Result<Ids::Symmetric> {
288        #[allow(deprecated)]
289        self.set_symmetric_key(
290            key_id,
291            SymmetricCryptoKey::Aes256CbcHmacKey(derive_shareable_key(secret, name, info)),
292        )?;
293        Ok(key_id)
294    }
295
296    #[deprecated(note = "This function should ideally never be used outside this crate")]
297    #[allow(missing_docs)]
298    pub fn dangerous_get_symmetric_key(
299        &self,
300        key_id: Ids::Symmetric,
301    ) -> Result<&SymmetricCryptoKey> {
302        self.get_symmetric_key(key_id)
303    }
304
305    #[deprecated(note = "This function should ideally never be used outside this crate")]
306    #[allow(missing_docs)]
307    pub fn dangerous_get_asymmetric_key(
308        &self,
309        key_id: Ids::Asymmetric,
310    ) -> Result<&AsymmetricCryptoKey> {
311        self.get_asymmetric_key(key_id)
312    }
313
314    /// Makes a signed public key from an asymmetric private key and signing key stored in context.
315    /// Signing a public key asserts ownership, and makes the claim to other users that if they want
316    /// to share with you, they can use this public key.
317    pub fn make_signed_public_key(
318        &self,
319        private_key_id: Ids::Asymmetric,
320        signing_key_id: Ids::Signing,
321    ) -> Result<SignedPublicKey> {
322        let public_key = self.get_asymmetric_key(private_key_id)?.to_public_key();
323        let signing_key = self.get_signing_key(signing_key_id)?;
324        let signed_public_key =
325            SignedPublicKeyMessage::from_public_key(&public_key)?.sign(signing_key)?;
326        Ok(signed_public_key)
327    }
328
329    fn get_symmetric_key(&self, key_id: Ids::Symmetric) -> Result<&SymmetricCryptoKey> {
330        if key_id.is_local() {
331            self.local_symmetric_keys.get(key_id)
332        } else {
333            self.global_keys.get().symmetric_keys.get(key_id)
334        }
335        .ok_or_else(|| crate::CryptoError::MissingKeyId(format!("{key_id:?}")))
336    }
337
338    fn get_asymmetric_key(&self, key_id: Ids::Asymmetric) -> Result<&AsymmetricCryptoKey> {
339        if key_id.is_local() {
340            self.local_asymmetric_keys.get(key_id)
341        } else {
342            self.global_keys.get().asymmetric_keys.get(key_id)
343        }
344        .ok_or_else(|| crate::CryptoError::MissingKeyId(format!("{key_id:?}")))
345    }
346
347    fn get_signing_key(&self, key_id: Ids::Signing) -> Result<&SigningKey> {
348        if key_id.is_local() {
349            self.local_signing_keys.get(key_id)
350        } else {
351            self.global_keys.get().signing_keys.get(key_id)
352        }
353        .ok_or_else(|| crate::CryptoError::MissingKeyId(format!("{key_id:?}")))
354    }
355
356    #[deprecated(note = "This function should ideally never be used outside this crate")]
357    #[allow(missing_docs)]
358    pub fn set_symmetric_key(
359        &mut self,
360        key_id: Ids::Symmetric,
361        key: SymmetricCryptoKey,
362    ) -> Result<()> {
363        if key_id.is_local() {
364            self.local_symmetric_keys.upsert(key_id, key);
365        } else {
366            self.global_keys
367                .get_mut()?
368                .symmetric_keys
369                .upsert(key_id, key);
370        }
371        Ok(())
372    }
373
374    #[deprecated(note = "This function should ideally never be used outside this crate")]
375    #[allow(missing_docs)]
376    pub fn set_asymmetric_key(
377        &mut self,
378        key_id: Ids::Asymmetric,
379        key: AsymmetricCryptoKey,
380    ) -> Result<()> {
381        if key_id.is_local() {
382            self.local_asymmetric_keys.upsert(key_id, key);
383        } else {
384            self.global_keys
385                .get_mut()?
386                .asymmetric_keys
387                .upsert(key_id, key);
388        }
389        Ok(())
390    }
391
392    /// Sets a signing key in the context
393    #[deprecated(note = "This function should ideally never be used outside this crate")]
394    pub fn set_signing_key(&mut self, key_id: Ids::Signing, key: SigningKey) -> Result<()> {
395        if key_id.is_local() {
396            self.local_signing_keys.upsert(key_id, key);
397        } else {
398            self.global_keys.get_mut()?.signing_keys.upsert(key_id, key);
399        }
400        Ok(())
401    }
402
403    pub(crate) fn decrypt_data_with_symmetric_key(
404        &self,
405        key: Ids::Symmetric,
406        data: &EncString,
407    ) -> Result<Vec<u8>> {
408        let key = self.get_symmetric_key(key)?;
409
410        match (data, key) {
411            (EncString::Aes256Cbc_B64 { iv, data }, SymmetricCryptoKey::Aes256CbcKey(key)) => {
412                crate::aes::decrypt_aes256(iv, data.clone(), &key.enc_key)
413            }
414            (
415                EncString::Aes256Cbc_HmacSha256_B64 { iv, mac, data },
416                SymmetricCryptoKey::Aes256CbcHmacKey(key),
417            ) => crate::aes::decrypt_aes256_hmac(iv, mac, data.clone(), &key.mac_key, &key.enc_key),
418            _ => Err(CryptoError::InvalidKey),
419        }
420    }
421
422    pub(crate) fn encrypt_data_with_symmetric_key(
423        &self,
424        key: Ids::Symmetric,
425        data: &[u8],
426    ) -> Result<EncString> {
427        let key = self.get_symmetric_key(key)?;
428        match key {
429            SymmetricCryptoKey::Aes256CbcKey(_) => Err(CryptoError::OperationNotSupported(
430                UnsupportedOperation::EncryptionNotImplementedForKey,
431            )),
432            SymmetricCryptoKey::Aes256CbcHmacKey(key) => EncString::encrypt_aes256_hmac(data, key),
433            SymmetricCryptoKey::XChaCha20Poly1305Key(key) => {
434                EncString::encrypt_xchacha20_poly1305(data, key)
435            }
436        }
437    }
438
439    /// Signs the given data using the specified signing key, for the given
440    /// [crate::SigningNamespace] and returns the signature and the serialized message. See
441    /// [crate::SigningKey::sign]
442    #[allow(unused)]
443    pub(crate) fn sign<Message: Serialize>(
444        &self,
445        key: Ids::Signing,
446        message: &Message,
447        namespace: &crate::SigningNamespace,
448    ) -> Result<SignedObject> {
449        self.get_signing_key(key)?.sign(message, namespace)
450    }
451
452    /// Signs the given data using the specified signing key, for the given
453    /// [crate::SigningNamespace] and returns the signature and the serialized message. See
454    /// [crate::SigningKey::sign_detached]
455    #[allow(unused)]
456    pub(crate) fn sign_detached<Message: Serialize>(
457        &self,
458        key: Ids::Signing,
459        message: &Message,
460        namespace: &crate::SigningNamespace,
461    ) -> Result<(Signature, signing::SerializedMessage)> {
462        self.get_signing_key(key)?.sign_detached(message, namespace)
463    }
464}
465
466#[cfg(test)]
467#[allow(deprecated)]
468mod tests {
469    use serde::{Deserialize, Serialize};
470
471    use crate::{
472        store::{tests::DataView, KeyStore},
473        traits::tests::{TestIds, TestSigningKey, TestSymmKey},
474        CryptoError, Decryptable, Encryptable, SignatureAlgorithm, SigningKey, SigningNamespace,
475        SymmetricCryptoKey,
476    };
477
478    #[test]
479    fn test_set_signing_key() {
480        let store: KeyStore<TestIds> = KeyStore::default();
481
482        // Generate and insert a key
483        let key_a0_id = TestSigningKey::A(0);
484        let key_a0 = SigningKey::make(SignatureAlgorithm::Ed25519);
485        store
486            .context_mut()
487            .set_signing_key(key_a0_id, key_a0)
488            .unwrap();
489    }
490
491    #[test]
492    fn test_set_keys_for_encryption() {
493        let store: KeyStore<TestIds> = KeyStore::default();
494
495        // Generate and insert a key
496        let key_a0_id = TestSymmKey::A(0);
497        let key_a0 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
498
499        store
500            .context_mut()
501            .set_symmetric_key(TestSymmKey::A(0), key_a0.clone())
502            .unwrap();
503
504        assert!(store.context().has_symmetric_key(key_a0_id));
505
506        // Encrypt some data with the key
507        let data = DataView("Hello, World!".to_string(), key_a0_id);
508        let _encrypted = data.encrypt(&mut store.context(), key_a0_id).unwrap();
509    }
510
511    #[test]
512    fn test_key_encryption() {
513        let store: KeyStore<TestIds> = KeyStore::default();
514
515        let mut ctx = store.context();
516
517        // Generate and insert a key
518        let key_1_id = TestSymmKey::C(1);
519        let key_1 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
520
521        ctx.set_symmetric_key(key_1_id, key_1.clone()).unwrap();
522
523        assert!(ctx.has_symmetric_key(key_1_id));
524
525        // Generate and insert a new key
526        let key_2_id = TestSymmKey::C(2);
527        let key_2 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
528
529        ctx.set_symmetric_key(key_2_id, key_2.clone()).unwrap();
530
531        assert!(ctx.has_symmetric_key(key_2_id));
532
533        // Encrypt the new key with the old key
534        let key_2_enc = ctx.wrap_symmetric_key(key_1_id, key_2_id).unwrap();
535
536        // Decrypt the new key with the old key in a different identifier
537        let new_key_id = TestSymmKey::C(3);
538
539        ctx.unwrap_symmetric_key(key_1_id, new_key_id, &key_2_enc)
540            .unwrap();
541
542        // Now `key_2_id` and `new_key_id` contain the same key, so we should be able to encrypt
543        // with one and decrypt with the other
544
545        let data = DataView("Hello, World!".to_string(), key_2_id);
546        let encrypted = data.encrypt(&mut ctx, key_2_id).unwrap();
547
548        let decrypted1 = encrypted.decrypt(&mut ctx, key_2_id).unwrap();
549        let decrypted2 = encrypted.decrypt(&mut ctx, new_key_id).unwrap();
550
551        // Assert that the decrypted data is the same
552        assert_eq!(decrypted1.0, decrypted2.0);
553    }
554
555    #[test]
556    fn test_signing() {
557        let store: KeyStore<TestIds> = KeyStore::default();
558
559        // Generate and insert a key
560        let key_a0_id = TestSigningKey::A(0);
561        let key_a0 = SigningKey::make(SignatureAlgorithm::Ed25519);
562        let verifying_key = key_a0.to_verifying_key();
563        store
564            .context_mut()
565            .set_signing_key(key_a0_id, key_a0)
566            .unwrap();
567
568        assert!(store.context().has_signing_key(key_a0_id));
569
570        // Sign some data with the key
571        #[derive(Serialize, Deserialize)]
572        struct TestData {
573            data: String,
574        }
575        let signed_object = store
576            .context()
577            .sign(
578                key_a0_id,
579                &TestData {
580                    data: "Hello".to_string(),
581                },
582                &SigningNamespace::ExampleNamespace,
583            )
584            .unwrap();
585        let payload: Result<TestData, CryptoError> =
586            signed_object.verify_and_unwrap(&verifying_key, &SigningNamespace::ExampleNamespace);
587        assert!(payload.is_ok());
588
589        let (signature, serialized_message) = store
590            .context()
591            .sign_detached(
592                key_a0_id,
593                &TestData {
594                    data: "Hello".to_string(),
595                },
596                &SigningNamespace::ExampleNamespace,
597            )
598            .unwrap();
599        assert!(signature.verify(
600            serialized_message.as_bytes(),
601            &verifying_key,
602            &SigningNamespace::ExampleNamespace
603        ))
604    }
605}