bitwarden_crypto/store/
context.rs

1use std::{
2    cell::Cell,
3    sync::{RwLockReadGuard, RwLockWriteGuard},
4};
5
6use coset::iana::KeyOperation;
7use serde::Serialize;
8use zeroize::Zeroizing;
9
10use super::KeyStoreInner;
11use crate::{
12    AsymmetricCryptoKey, AsymmetricPublicCryptoKey, BitwardenLegacyKeyBytes, ContentFormat,
13    CoseEncrypt0Bytes, CoseKeyBytes, CoseSerializable, CryptoError, EncString, KeyDecryptable,
14    KeyEncryptable, KeyId, KeyIds, LocalId, Pkcs8PrivateKeyBytes, PublicKeyEncryptionAlgorithm,
15    Result, RotatedUserKeys, Signature, SignatureAlgorithm, SignedObject, SignedPublicKey,
16    SignedPublicKeyMessage, SigningKey, SymmetricCryptoKey, SymmetricKeyAlgorithm,
17    UnsignedSharedKey, VerifyingKey, derive_shareable_key, error::UnsupportedOperationError,
18    signing, store::backend::StoreBackend,
19};
20
21/// The context of a crypto operation using [super::KeyStore]
22///
23/// This will usually be accessed from an implementation of [crate::Decryptable] or
24/// [crate::CompositeEncryptable], [crate::PrimitiveEncryptable],
25/// but can also be obtained
26/// through [super::KeyStore::context]
27///
28/// This context contains access to the user keys stored in the [super::KeyStore] (sometimes
29/// referred to as `global keys`) and it also contains it's own individual secure backend for key
30/// storage. Keys stored in this individual backend are usually referred to as `local keys`, they
31/// will be cleared when this context goes out of scope and is dropped and they do not affect either
32/// the global [super::KeyStore] or other instances of contexts.
33///
34/// This context-local storage is recommended for ephemeral and temporary keys that are decrypted
35/// during the course of a decrypt/encrypt operation, but won't be used after the operation itself
36/// is complete.
37///
38/// ```rust
39/// # use bitwarden_crypto::*;
40/// # key_ids! {
41/// #     #[symmetric]
42/// #     pub enum SymmKeyId {
43/// #         User,
44/// #         #[local]
45/// #         Local(LocalId),
46/// #     }
47/// #     #[asymmetric]
48/// #     pub enum AsymmKeyId {
49/// #         UserPrivate,
50/// #         #[local]
51/// #         Local(LocalId),
52/// #     }
53/// #     #[signing]
54/// #     pub enum SigningKeyId {
55/// #         UserSigning,
56/// #         #[local]
57/// #         Local(LocalId),
58/// #     }
59/// #     pub Ids => SymmKeyId, AsymmKeyId, SigningKeyId;
60/// # }
61/// struct Data {
62///     key: EncString,
63///     name: String,
64/// }
65/// # impl IdentifyKey<SymmKeyId> for Data {
66/// #    fn key_identifier(&self) -> SymmKeyId {
67/// #        SymmKeyId::User
68/// #    }
69/// # }
70///
71///
72/// impl CompositeEncryptable<Ids, SymmKeyId, EncString> for Data {
73///     fn encrypt_composite(&self, ctx: &mut KeyStoreContext<Ids>, key: SymmKeyId) -> Result<EncString, CryptoError> {
74///         let local_key_id = ctx.unwrap_symmetric_key(key, &self.key)?;
75///         self.name.encrypt(ctx, local_key_id)
76///     }
77/// }
78/// ```
79#[must_use]
80pub struct KeyStoreContext<'a, Ids: KeyIds> {
81    pub(super) global_keys: GlobalKeys<'a, Ids>,
82
83    pub(super) local_symmetric_keys: Box<dyn StoreBackend<Ids::Symmetric>>,
84    pub(super) local_asymmetric_keys: Box<dyn StoreBackend<Ids::Asymmetric>>,
85    pub(super) local_signing_keys: Box<dyn StoreBackend<Ids::Signing>>,
86
87    pub(super) security_state_version: u64,
88
89    // Make sure the context is !Send & !Sync
90    pub(super) _phantom: std::marker::PhantomData<(Cell<()>, RwLockReadGuard<'static, ()>)>,
91}
92
93/// A KeyStoreContext is usually limited to a read only access to the global keys,
94/// which allows us to have multiple read only contexts at the same time and do multitheaded
95/// encryption/decryption. We also have the option to create a read/write context, which allows us
96/// to modify the global keys, but only allows one context at a time. This is controlled by a
97/// [std::sync::RwLock] on the global keys, and this struct stores both types of guards.
98pub(crate) enum GlobalKeys<'a, Ids: KeyIds> {
99    ReadOnly(RwLockReadGuard<'a, KeyStoreInner<Ids>>),
100    ReadWrite(RwLockWriteGuard<'a, KeyStoreInner<Ids>>),
101}
102
103impl<Ids: KeyIds> GlobalKeys<'_, Ids> {
104    /// Get a shared reference to the underlying `KeyStoreInner`.
105    ///
106    /// This returns a shared reference regardless of whether the global keys were locked
107    /// for read-only or read-write access. Callers who need mutable access should use
108    /// `get_mut` which will return an error when the context is read-only.
109    pub fn get(&self) -> &KeyStoreInner<Ids> {
110        match self {
111            GlobalKeys::ReadOnly(keys) => keys,
112            GlobalKeys::ReadWrite(keys) => keys,
113        }
114    }
115
116    /// Get a mutable reference to the underlying `KeyStoreInner`.
117    ///
118    /// This will succeed only when the context was created with write access. If the
119    /// context is read-only an error (`CryptoError::ReadOnlyKeyStore`) is returned.
120    ///
121    /// # Errors
122    /// Returns [`CryptoError::ReadOnlyKeyStore`] when attempting to get mutable access from
123    /// a read-only context.
124    pub fn get_mut(&mut self) -> Result<&mut KeyStoreInner<Ids>> {
125        match self {
126            GlobalKeys::ReadOnly(_) => Err(CryptoError::ReadOnlyKeyStore),
127            GlobalKeys::ReadWrite(keys) => Ok(keys),
128        }
129    }
130}
131
132impl<Ids: KeyIds> KeyStoreContext<'_, Ids> {
133    /// Clears all the local keys stored in this context
134    /// This will not affect the global keys even if this context has write access.
135    /// To clear the global keys, you need to use [super::KeyStore::clear] instead.
136    pub fn clear_local(&mut self) {
137        self.local_symmetric_keys.clear();
138        self.local_asymmetric_keys.clear();
139        self.local_signing_keys.clear();
140    }
141
142    /// Returns the version of the security state of the key context. This describes the user's
143    /// encryption version and can be used to disable certain old / dangerous format features
144    /// safely.
145    pub fn get_security_state_version(&self) -> u64 {
146        self.security_state_version
147    }
148
149    /// Remove all symmetric keys from the context for which the predicate returns false
150    /// This will also remove the keys from the global store if this context has write access
151    pub fn retain_symmetric_keys(&mut self, f: fn(Ids::Symmetric) -> bool) {
152        if let Ok(keys) = self.global_keys.get_mut() {
153            keys.symmetric_keys.retain(f);
154        }
155        self.local_symmetric_keys.retain(f);
156    }
157
158    /// Remove all asymmetric keys from the context for which the predicate returns false
159    /// This will also remove the keys from the global store if this context has write access
160    pub fn retain_asymmetric_keys(&mut self, f: fn(Ids::Asymmetric) -> bool) {
161        if let Ok(keys) = self.global_keys.get_mut() {
162            keys.asymmetric_keys.retain(f);
163        }
164        self.local_asymmetric_keys.retain(f);
165    }
166
167    fn drop_symmetric_key(&mut self, key_id: Ids::Symmetric) -> Result<()> {
168        if key_id.is_local() {
169            self.local_symmetric_keys.remove(key_id);
170        } else {
171            self.global_keys.get_mut()?.symmetric_keys.remove(key_id);
172        }
173        Ok(())
174    }
175
176    fn drop_asymmetric_key(&mut self, key_id: Ids::Asymmetric) -> Result<()> {
177        if key_id.is_local() {
178            self.local_asymmetric_keys.remove(key_id);
179        } else {
180            self.global_keys.get_mut()?.asymmetric_keys.remove(key_id);
181        }
182        Ok(())
183    }
184
185    fn drop_signing_key(&mut self, key_id: Ids::Signing) -> Result<()> {
186        if key_id.is_local() {
187            self.local_signing_keys.remove(key_id);
188        } else {
189            self.global_keys.get_mut()?.signing_keys.remove(key_id);
190        }
191        Ok(())
192    }
193
194    // TODO: All these encrypt x key with x key look like they need to be made generic,
195    // but I haven't found the best way to do that yet.
196
197    /// Decrypt a symmetric key into the context by using an already existing symmetric key
198    ///
199    /// # Arguments
200    ///
201    /// * `wrapping_key` - The key id used to decrypt the `wrapped_key`. It must already exist in
202    ///   the context
203    /// * `new_key_id` - The key id where the decrypted key will be stored. If it already exists, it
204    ///   will be overwritten
205    /// * `wrapped_key` - The key to decrypt
206    pub fn unwrap_symmetric_key(
207        &mut self,
208        wrapping_key: Ids::Symmetric,
209        wrapped_key: &EncString,
210    ) -> Result<Ids::Symmetric> {
211        let wrapping_key = self.get_symmetric_key(wrapping_key)?;
212
213        let key = match (wrapped_key, wrapping_key) {
214            (EncString::Aes256Cbc_B64 { iv, data }, SymmetricCryptoKey::Aes256CbcKey(key)) => {
215                SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(
216                    crate::aes::decrypt_aes256(iv, data.clone(), &key.enc_key)?,
217                ))?
218            }
219            (
220                EncString::Aes256Cbc_HmacSha256_B64 { iv, mac, data },
221                SymmetricCryptoKey::Aes256CbcHmacKey(key),
222            ) => SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(
223                crate::aes::decrypt_aes256_hmac(iv, mac, data.clone(), &key.mac_key, &key.enc_key)?,
224            ))?,
225            (
226                EncString::Cose_Encrypt0_B64 { data },
227                SymmetricCryptoKey::XChaCha20Poly1305Key(key),
228            ) => {
229                let (content_bytes, content_format) = crate::cose::decrypt_xchacha20_poly1305(
230                    &CoseEncrypt0Bytes::from(data.clone()),
231                    key,
232                )?;
233                match content_format {
234                    ContentFormat::BitwardenLegacyKey => {
235                        SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(content_bytes))?
236                    }
237                    ContentFormat::CoseKey => SymmetricCryptoKey::try_from_cose(&content_bytes)?,
238                    _ => return Err(CryptoError::InvalidKey),
239                }
240            }
241            _ => return Err(CryptoError::InvalidKey),
242        };
243
244        let new_key_id = Ids::Symmetric::new_local(LocalId::new());
245
246        #[allow(deprecated)]
247        self.set_symmetric_key(new_key_id, key)?;
248
249        // Returning the new key identifier for convenience
250        Ok(new_key_id)
251    }
252
253    /// Move a symmetric key from a local identifier to a global identifier within the context
254    ///
255    /// The key value is copied to `to` and the original identifier `from` is removed.
256    ///
257    /// # Errors
258    /// Returns an error if the source key does not exist or if setting the destination key
259    /// fails (for example due to read-only global store).
260    pub fn persist_symmetric_key(
261        &mut self,
262        from: Ids::Symmetric,
263        to: Ids::Symmetric,
264    ) -> Result<()> {
265        if !from.is_local() || to.is_local() {
266            return Err(CryptoError::InvalidKeyStoreOperation);
267        }
268        let key = self.get_symmetric_key(from)?.to_owned();
269        self.drop_symmetric_key(from)?;
270        #[allow(deprecated)]
271        self.set_symmetric_key(to, key)?;
272        Ok(())
273    }
274
275    /// Move an asymmetric key from a local identifier to a global identifier within the context
276    ///
277    /// The key value is copied to `to` and the original identifier `from` is removed.
278    ///
279    /// # Errors
280    /// Returns an error if the source key does not exist or if setting the destination key
281    /// fails (for example due to read-only global store).
282    pub fn persist_asymmetric_key(
283        &mut self,
284        from: Ids::Asymmetric,
285        to: Ids::Asymmetric,
286    ) -> Result<()> {
287        if !from.is_local() || to.is_local() {
288            return Err(CryptoError::InvalidKeyStoreOperation);
289        }
290        let key = self.get_asymmetric_key(from)?.to_owned();
291        self.drop_asymmetric_key(from)?;
292        #[allow(deprecated)]
293        self.set_asymmetric_key(to, key)?;
294        Ok(())
295    }
296
297    /// Move a signing key from a local identifier to a global identifier within the context
298    ///
299    /// The key value at `from` will be copied to `to` and the original `from` will be removed.
300    ///
301    /// # Errors
302    /// Returns an error if the source key does not exist or updating the destination fails.
303    pub fn persist_signing_key(&mut self, from: Ids::Signing, to: Ids::Signing) -> Result<()> {
304        if !from.is_local() || to.is_local() {
305            return Err(CryptoError::InvalidKeyStoreOperation);
306        }
307        let key = self.get_signing_key(from)?.to_owned();
308        self.drop_signing_key(from)?;
309        #[allow(deprecated)]
310        self.set_signing_key(to, key)?;
311        Ok(())
312    }
313
314    /// Wrap (encrypt) a signing key with a symmetric key.
315    ///
316    /// The signing key identified by `key_to_wrap` will be serialized to COSE and encrypted
317    /// with the symmetric `wrapping_key`, returning an `EncString` suitable for storage or
318    /// transport.
319    ///
320    /// # Errors
321    /// Returns an error if either key id does not exist or the encryption fails.
322    pub fn wrap_signing_key(
323        &self,
324        wrapping_key: Ids::Symmetric,
325        key_to_wrap: Ids::Signing,
326    ) -> Result<EncString> {
327        let wrapping_key = self.get_symmetric_key(wrapping_key)?;
328        let signing_key = self.get_signing_key(key_to_wrap)?.to_owned();
329        signing_key.to_cose().encrypt_with_key(wrapping_key)
330    }
331
332    /// Wrap (encrypt) a private/asymmetric key with a symmetric key.
333    ///
334    /// The private key identified by `key_to_wrap` will be serialized to DER (PKCS#8) and
335    /// encrypted with `wrapping_key`, returning an `EncString` suitable for storage.
336    ///
337    /// # Errors
338    /// Returns an error if the keys are missing or serialization/encryption fails.
339    pub fn wrap_private_key(
340        &self,
341        wrapping_key: Ids::Symmetric,
342        key_to_wrap: Ids::Asymmetric,
343    ) -> Result<EncString> {
344        let wrapping_key = self.get_symmetric_key(wrapping_key)?;
345        let private_key = self.get_asymmetric_key(key_to_wrap)?.to_owned();
346        private_key.to_der()?.encrypt_with_key(wrapping_key)
347    }
348
349    /// Decrypt and import a previously wrapped asymmetric private key into the context.
350    ///
351    /// The `wrapped_key` will be decrypted using `wrapping_key` and parsed as a PKCS#8
352    /// private key; the resulting key will be inserted as a local asymmetric key and the
353    /// new local identifier returned.
354    ///
355    /// # Errors
356    /// Returns an error if decryption or parsing fails.
357    pub fn unwrap_private_key(
358        &mut self,
359        wrapping_key: Ids::Symmetric,
360        wrapped_key: &EncString,
361    ) -> Result<Ids::Asymmetric> {
362        let wrapping_key = self.get_symmetric_key(wrapping_key)?;
363        let private_key_bytes: Vec<u8> = wrapped_key.decrypt_with_key(wrapping_key)?;
364        let private_key =
365            AsymmetricCryptoKey::from_der(&Pkcs8PrivateKeyBytes::from(private_key_bytes))?;
366        self.add_local_asymmetric_key(private_key)
367    }
368
369    /// Decrypt and import a previously wrapped signing key into the context.
370    ///
371    /// The wrapped COSE key will be decrypted with `wrapping_key` and parsed into a
372    /// `SigningKey` which is inserted as a local signing key. The new local identifier
373    /// is returned.
374    ///
375    /// # Errors
376    /// Returns an error if decryption or parsing fails.
377    pub fn unwrap_signing_key(
378        &mut self,
379        wrapping_key: Ids::Symmetric,
380        wrapped_key: &EncString,
381    ) -> Result<Ids::Signing> {
382        let wrapping_key = self.get_symmetric_key(wrapping_key)?;
383        let signing_key_bytes: Vec<u8> = wrapped_key.decrypt_with_key(wrapping_key)?;
384        let signing_key = SigningKey::from_cose(&CoseKeyBytes::from(signing_key_bytes))?;
385        self.add_local_signing_key(signing_key)
386    }
387
388    /// Return the verifying (public) key corresponding to a signing key identifier.
389    ///
390    /// This converts the stored `SigningKey` into a `VerifyingKey` suitable for
391    /// signature verification operations.
392    ///
393    /// # Errors
394    /// Returns an error if the signing key id does not exist.
395    pub fn get_verifying_key(&self, signing_key_id: Ids::Signing) -> Result<VerifyingKey> {
396        let signing_key = self.get_signing_key(signing_key_id)?;
397        Ok(signing_key.to_verifying_key())
398    }
399
400    /// Return the public key corresponding to an asymmetric (private) key identifier.
401    ///
402    /// This converts the stored private key into its public key representation.
403    ///
404    /// # Errors
405    /// Returns an error if the asymmetric key id does not exist.
406    pub fn get_public_key(
407        &self,
408        asymmetric_key_id: Ids::Asymmetric,
409    ) -> Result<AsymmetricPublicCryptoKey> {
410        let asymmetric_key = self.get_asymmetric_key(asymmetric_key_id)?;
411        Ok(asymmetric_key.to_public_key())
412    }
413
414    /// Encrypt and return a symmetric key from the context by using an already existing symmetric
415    /// key
416    ///
417    /// # Arguments
418    ///
419    /// * `wrapping_key` - The key id used to wrap (encrypt) the `key_to_wrap`. It must already
420    ///   exist in the context
421    /// * `key_to_wrap` - The key id to wrap. It must already exist in the context
422    pub fn wrap_symmetric_key(
423        &self,
424        wrapping_key: Ids::Symmetric,
425        key_to_wrap: Ids::Symmetric,
426    ) -> Result<EncString> {
427        use SymmetricCryptoKey::*;
428
429        let wrapping_key_instance = self.get_symmetric_key(wrapping_key)?;
430        let key_to_wrap_instance = self.get_symmetric_key(key_to_wrap)?;
431        // `Aes256CbcHmacKey` can wrap keys by encrypting their byte serialization obtained using
432        // `SymmetricCryptoKey::to_encoded()`. `XChaCha20Poly1305Key` need to specify the
433        // content format to be either octet stream, in case the wrapped key is a Aes256CbcHmacKey
434        // or `Aes256CbcKey`, or by specifying the content format to be CoseKey, in case the
435        // wrapped key is a `XChaCha20Poly1305Key`.
436        match (wrapping_key_instance, key_to_wrap_instance) {
437            (
438                Aes256CbcHmacKey(_),
439                Aes256CbcHmacKey(_) | Aes256CbcKey(_) | XChaCha20Poly1305Key(_),
440            ) => self.encrypt_data_with_symmetric_key(
441                wrapping_key,
442                key_to_wrap_instance
443                    .to_encoded()
444                    .as_ref()
445                    .to_vec()
446                    .as_slice(),
447                ContentFormat::BitwardenLegacyKey,
448            ),
449            (XChaCha20Poly1305Key(_), _) => {
450                let encoded = key_to_wrap_instance.to_encoded_raw();
451                let content_format = encoded.content_format();
452                self.encrypt_data_with_symmetric_key(
453                    wrapping_key,
454                    Into::<Vec<u8>>::into(encoded).as_slice(),
455                    content_format,
456                )
457            }
458            _ => Err(CryptoError::OperationNotSupported(
459                UnsupportedOperationError::EncryptionNotImplementedForKey,
460            )),
461        }
462    }
463
464    /// Decapsulate a symmetric key into the context by using an already existing asymmetric key
465    ///
466    /// # Arguments
467    ///
468    /// * `decapsulation_key` - The key id used to decrypt the `encrypted_key`. It must already
469    ///   exist in the context
470    /// * `new_key_id` - The key id where the decrypted key will be stored. If it already exists, it
471    ///   will be overwritten
472    /// * `encapsulated_shared_key` - The symmetric key to decrypt
473    pub fn decapsulate_key_unsigned(
474        &mut self,
475        decapsulation_key: Ids::Asymmetric,
476        new_key_id: Ids::Symmetric,
477        encapsulated_shared_key: &UnsignedSharedKey,
478    ) -> Result<Ids::Symmetric> {
479        let decapsulation_key = self.get_asymmetric_key(decapsulation_key)?;
480        let decapsulated_key =
481            encapsulated_shared_key.decapsulate_key_unsigned(decapsulation_key)?;
482
483        #[allow(deprecated)]
484        self.set_symmetric_key(new_key_id, decapsulated_key)?;
485
486        // Returning the new key identifier for convenience
487        Ok(new_key_id)
488    }
489
490    /// Encapsulate and return a symmetric key from the context by using an already existing
491    /// asymmetric key
492    ///
493    /// # Arguments
494    ///
495    /// * `encapsulation_key` - The key id used to encrypt the `encapsulated_key`. It must already
496    ///   exist in the context
497    /// * `shared_key` - The key id to encrypt. It must already exist in the context
498    pub fn encapsulate_key_unsigned(
499        &self,
500        encapsulation_key: Ids::Asymmetric,
501        shared_key: Ids::Symmetric,
502    ) -> Result<UnsignedSharedKey> {
503        UnsignedSharedKey::encapsulate_key_unsigned(
504            self.get_symmetric_key(shared_key)?,
505            &self.get_asymmetric_key(encapsulation_key)?.to_public_key(),
506        )
507    }
508
509    /// Returns `true` if the context has a symmetric key with the given identifier
510    pub fn has_symmetric_key(&self, key_id: Ids::Symmetric) -> bool {
511        self.get_symmetric_key(key_id).is_ok()
512    }
513
514    /// Returns `true` if the context has an asymmetric key with the given identifier
515    pub fn has_asymmetric_key(&self, key_id: Ids::Asymmetric) -> bool {
516        self.get_asymmetric_key(key_id).is_ok()
517    }
518
519    /// Returns `true` if the context has a signing key with the given identifier
520    pub fn has_signing_key(&self, key_id: Ids::Signing) -> bool {
521        self.get_signing_key(key_id).is_ok()
522    }
523
524    /// Generate a new random symmetric key and store it in the context
525    pub fn generate_symmetric_key(&mut self) -> Ids::Symmetric {
526        self.add_local_symmetric_key(SymmetricCryptoKey::make_aes256_cbc_hmac_key())
527    }
528
529    /// Generate a new symmetric encryption key using the specified algorithm and store it in the
530    /// context as a local key
531    pub fn make_symmetric_key(&mut self, algorithm: SymmetricKeyAlgorithm) -> Ids::Symmetric {
532        self.add_local_symmetric_key(SymmetricCryptoKey::make(algorithm))
533    }
534
535    /// Makes a new asymmetric encryption key using the current default algorithm, and stores it in
536    /// the context as a local key
537    pub fn make_private_key(
538        &mut self,
539        algorithm: PublicKeyEncryptionAlgorithm,
540    ) -> Result<Ids::Asymmetric> {
541        self.add_local_asymmetric_key(AsymmetricCryptoKey::make(algorithm))
542    }
543
544    /// Makes a new signing key using the current default algorithm, and stores it in the context as
545    /// a local key
546    pub fn make_signing_key(&mut self, algorithm: SignatureAlgorithm) -> Result<Ids::Signing> {
547        self.add_local_signing_key(SigningKey::make(algorithm))
548    }
549
550    /// Makes a new asymmetric encryption key using the current default algorithm, and stores it in
551    /// the context
552    pub fn make_asymmetric_key(&mut self) -> Result<Ids::Asymmetric> {
553        let key = AsymmetricCryptoKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
554        self.add_local_asymmetric_key(key)
555    }
556
557    /// Derive a shareable key using hkdf from secret and name and store it in the context.
558    ///
559    /// A specialized variant of this function was called `CryptoService.makeSendKey` in the
560    /// Bitwarden `clients` repository.
561    pub fn derive_shareable_key(
562        &mut self,
563        secret: Zeroizing<[u8; 16]>,
564        name: &str,
565        info: Option<&str>,
566    ) -> Result<Ids::Symmetric> {
567        let key_id = Ids::Symmetric::new_local(LocalId::new());
568        #[allow(deprecated)]
569        self.set_symmetric_key(
570            key_id,
571            SymmetricCryptoKey::Aes256CbcHmacKey(derive_shareable_key(secret, name, info)),
572        )?;
573        Ok(key_id)
574    }
575
576    /// Return a reference to a symmetric key stored in the context.
577    ///
578    /// Deprecated: intended only for internal use and tests. This exposes the underlying
579    /// `SymmetricCryptoKey` reference directly and should not be used by external code. Use
580    /// the higher-level APIs (for example encryption/decryption helpers) or `get_symmetric_key`
581    /// internally when possible.
582    ///
583    /// # Errors
584    /// Returns [`CryptoError::MissingKeyId`] if the key id does not exist in the context.
585    #[deprecated(note = "This function should ideally never be used outside this crate")]
586    pub fn dangerous_get_symmetric_key(
587        &self,
588        key_id: Ids::Symmetric,
589    ) -> Result<&SymmetricCryptoKey> {
590        self.get_symmetric_key(key_id)
591    }
592
593    /// Return a reference to an asymmetric (private) key stored in the context.
594    ///
595    /// Deprecated: intended only for internal use and tests. This exposes the underlying
596    /// `AsymmetricCryptoKey` reference directly and should not be used by external code. Prefer
597    /// using the public key via `get_public_key` or other higher-level APIs instead.
598    ///
599    /// # Errors
600    /// Returns [`CryptoError::MissingKeyId`] if the key id does not exist in the context.
601    #[deprecated(note = "This function should ideally never be used outside this crate")]
602    pub fn dangerous_get_asymmetric_key(
603        &self,
604        key_id: Ids::Asymmetric,
605    ) -> Result<&AsymmetricCryptoKey> {
606        self.get_asymmetric_key(key_id)
607    }
608
609    /// Makes a signed public key from an asymmetric private key and signing key stored in context.
610    /// Signing a public key asserts ownership, and makes the claim to other users that if they want
611    /// to share with you, they can use this public key.
612    pub fn make_signed_public_key(
613        &self,
614        private_key_id: Ids::Asymmetric,
615        signing_key_id: Ids::Signing,
616    ) -> Result<SignedPublicKey> {
617        let public_key = self.get_asymmetric_key(private_key_id)?.to_public_key();
618        let signing_key = self.get_signing_key(signing_key_id)?;
619        let signed_public_key =
620            SignedPublicKeyMessage::from_public_key(&public_key)?.sign(signing_key)?;
621        Ok(signed_public_key)
622    }
623
624    pub(crate) fn get_symmetric_key(&self, key_id: Ids::Symmetric) -> Result<&SymmetricCryptoKey> {
625        if key_id.is_local() {
626            self.local_symmetric_keys.get(key_id)
627        } else {
628            self.global_keys.get().symmetric_keys.get(key_id)
629        }
630        .ok_or_else(|| crate::CryptoError::MissingKeyId(format!("{key_id:?}")))
631    }
632
633    pub(super) fn get_asymmetric_key(
634        &self,
635        key_id: Ids::Asymmetric,
636    ) -> Result<&AsymmetricCryptoKey> {
637        if key_id.is_local() {
638            self.local_asymmetric_keys.get(key_id)
639        } else {
640            self.global_keys.get().asymmetric_keys.get(key_id)
641        }
642        .ok_or_else(|| crate::CryptoError::MissingKeyId(format!("{key_id:?}")))
643    }
644
645    pub(super) fn get_signing_key(&self, key_id: Ids::Signing) -> Result<&SigningKey> {
646        if key_id.is_local() {
647            self.local_signing_keys.get(key_id)
648        } else {
649            self.global_keys.get().signing_keys.get(key_id)
650        }
651        .ok_or_else(|| crate::CryptoError::MissingKeyId(format!("{key_id:?}")))
652    }
653
654    /// Set a symmetric key in the context.
655    ///
656    /// # Errors
657    /// Returns [`CryptoError::ReadOnlyKeyStore`] if the context does not have write access when
658    /// attempting to modify the global store.
659    #[deprecated(note = "This function should ideally never be used outside this crate")]
660    pub fn set_symmetric_key(
661        &mut self,
662        key_id: Ids::Symmetric,
663        key: SymmetricCryptoKey,
664    ) -> Result<()> {
665        self.set_symmetric_key_internal(key_id, key)
666    }
667
668    pub(crate) fn set_symmetric_key_internal(
669        &mut self,
670        key_id: Ids::Symmetric,
671        key: SymmetricCryptoKey,
672    ) -> Result<()> {
673        if key_id.is_local() {
674            self.local_symmetric_keys.upsert(key_id, key);
675        } else {
676            self.global_keys
677                .get_mut()?
678                .symmetric_keys
679                .upsert(key_id, key);
680        }
681        Ok(())
682    }
683
684    /// Add a new symmetric key to the local context, returning a new unique identifier for it.
685    pub fn add_local_symmetric_key(&mut self, key: SymmetricCryptoKey) -> Ids::Symmetric {
686        let key_id = Ids::Symmetric::new_local(LocalId::new());
687        self.local_symmetric_keys.upsert(key_id, key);
688        key_id
689    }
690
691    /// Get the type of a symmetric key stored in the context.
692    pub fn get_symmetric_key_algorithm(
693        &self,
694        key_id: Ids::Symmetric,
695    ) -> Result<SymmetricKeyAlgorithm> {
696        let key = self.get_symmetric_key(key_id)?;
697        match key {
698            // Note this is dropped soon
699            SymmetricCryptoKey::Aes256CbcKey(_) => Err(CryptoError::OperationNotSupported(
700                UnsupportedOperationError::EncryptionNotImplementedForKey,
701            )),
702            SymmetricCryptoKey::Aes256CbcHmacKey(_) => Ok(SymmetricKeyAlgorithm::Aes256CbcHmac),
703            SymmetricCryptoKey::XChaCha20Poly1305Key(_) => {
704                Ok(SymmetricKeyAlgorithm::XChaCha20Poly1305)
705            }
706        }
707    }
708
709    /// Set an asymmetric (private) key in the context.
710    ///
711    /// # Errors
712    /// Returns [`CryptoError::ReadOnlyKeyStore`] if attempting to write to the global store when
713    /// the context is read-only.
714    #[deprecated(note = "This function should ideally never be used outside this crate")]
715    pub fn set_asymmetric_key(
716        &mut self,
717        key_id: Ids::Asymmetric,
718        key: AsymmetricCryptoKey,
719    ) -> Result<()> {
720        if key_id.is_local() {
721            self.local_asymmetric_keys.upsert(key_id, key);
722        } else {
723            self.global_keys
724                .get_mut()?
725                .asymmetric_keys
726                .upsert(key_id, key);
727        }
728        Ok(())
729    }
730
731    /// Add a new asymmetric key to the local context, returning a new unique identifier for it.
732    pub fn add_local_asymmetric_key(
733        &mut self,
734        key: AsymmetricCryptoKey,
735    ) -> Result<Ids::Asymmetric> {
736        let key_id = Ids::Asymmetric::new_local(LocalId::new());
737        self.local_asymmetric_keys.upsert(key_id, key);
738        Ok(key_id)
739    }
740
741    /// Sets a signing key in the context
742    ///
743    /// # Errors
744    /// Returns [`CryptoError::ReadOnlyKeyStore`] if attempting to write to the global store when
745    /// the context is read-only.
746    #[deprecated(note = "This function should ideally never be used outside this crate")]
747    pub fn set_signing_key(&mut self, key_id: Ids::Signing, key: SigningKey) -> Result<()> {
748        if key_id.is_local() {
749            self.local_signing_keys.upsert(key_id, key);
750        } else {
751            self.global_keys.get_mut()?.signing_keys.upsert(key_id, key);
752        }
753        Ok(())
754    }
755
756    /// Add a new signing key to the local context, returning a new unique identifier for it.
757    pub fn add_local_signing_key(&mut self, key: SigningKey) -> Result<Ids::Signing> {
758        let key_id = Ids::Signing::new_local(LocalId::new());
759        self.local_signing_keys.upsert(key_id, key);
760        Ok(key_id)
761    }
762
763    pub(crate) fn decrypt_data_with_symmetric_key(
764        &self,
765        key: Ids::Symmetric,
766        data: &EncString,
767    ) -> Result<Vec<u8>> {
768        let key = self.get_symmetric_key(key)?;
769
770        match (data, key) {
771            (EncString::Aes256Cbc_B64 { iv, data }, SymmetricCryptoKey::Aes256CbcKey(key)) => {
772                crate::aes::decrypt_aes256(iv, data.clone(), &key.enc_key)
773            }
774            (
775                EncString::Aes256Cbc_HmacSha256_B64 { iv, mac, data },
776                SymmetricCryptoKey::Aes256CbcHmacKey(key),
777            ) => crate::aes::decrypt_aes256_hmac(iv, mac, data.clone(), &key.mac_key, &key.enc_key),
778            (
779                EncString::Cose_Encrypt0_B64 { data },
780                SymmetricCryptoKey::XChaCha20Poly1305Key(key),
781            ) => {
782                let (data, _) = crate::cose::decrypt_xchacha20_poly1305(
783                    &CoseEncrypt0Bytes::from(data.clone()),
784                    key,
785                )?;
786                Ok(data)
787            }
788            _ => Err(CryptoError::InvalidKey),
789        }
790    }
791
792    pub(crate) fn encrypt_data_with_symmetric_key(
793        &self,
794        key: Ids::Symmetric,
795        data: &[u8],
796        content_format: ContentFormat,
797    ) -> Result<EncString> {
798        let key = self.get_symmetric_key(key)?;
799        match key {
800            SymmetricCryptoKey::Aes256CbcKey(_) => Err(CryptoError::OperationNotSupported(
801                UnsupportedOperationError::EncryptionNotImplementedForKey,
802            )),
803            SymmetricCryptoKey::Aes256CbcHmacKey(key) => EncString::encrypt_aes256_hmac(data, key),
804            SymmetricCryptoKey::XChaCha20Poly1305Key(key) => {
805                if !key.supported_operations.contains(&KeyOperation::Encrypt) {
806                    return Err(CryptoError::KeyOperationNotSupported(KeyOperation::Encrypt));
807                }
808                EncString::encrypt_xchacha20_poly1305(data, key, content_format)
809            }
810        }
811    }
812
813    /// Signs the given data using the specified signing key, for the given
814    /// [crate::SigningNamespace] and returns the signature and the serialized message. See
815    /// [crate::SigningKey::sign]
816    pub fn sign<Message: Serialize>(
817        &self,
818        key: Ids::Signing,
819        message: &Message,
820        namespace: &crate::SigningNamespace,
821    ) -> Result<SignedObject> {
822        self.get_signing_key(key)?.sign(message, namespace)
823    }
824
825    /// Signs the given data using the specified signing key, for the given
826    /// [crate::SigningNamespace] and returns the signature and the serialized message. See
827    /// [crate::SigningKey::sign_detached]
828    #[allow(unused)]
829    pub(crate) fn sign_detached<Message: Serialize>(
830        &self,
831        key: Ids::Signing,
832        message: &Message,
833        namespace: &crate::SigningNamespace,
834    ) -> Result<(Signature, signing::SerializedMessage)> {
835        self.get_signing_key(key)?.sign_detached(message, namespace)
836    }
837
838    /// Re-encrypts the user's keys with the provided symmetric key for a v2 user.
839    pub fn dangerous_get_v2_rotated_account_keys(
840        &self,
841        current_user_private_key_id: Ids::Asymmetric,
842        current_user_signing_key_id: Ids::Signing,
843    ) -> Result<RotatedUserKeys> {
844        crate::dangerous_get_v2_rotated_account_keys(
845            current_user_private_key_id,
846            current_user_signing_key_id,
847            self,
848        )
849    }
850}
851
852#[cfg(test)]
853#[allow(deprecated)]
854mod tests {
855    use serde::{Deserialize, Serialize};
856
857    use crate::{
858        AsymmetricCryptoKey, AsymmetricPublicCryptoKey, CompositeEncryptable, CoseKeyBytes,
859        CoseSerializable, CryptoError, Decryptable, KeyDecryptable, Pkcs8PrivateKeyBytes,
860        SignatureAlgorithm, SigningKey, SigningNamespace, SymmetricCryptoKey,
861        SymmetricKeyAlgorithm,
862        store::{
863            KeyStore,
864            tests::{Data, DataView},
865        },
866        traits::tests::{TestIds, TestSigningKey, TestSymmKey},
867    };
868
869    #[test]
870    fn test_set_signing_key() {
871        let store: KeyStore<TestIds> = KeyStore::default();
872
873        // Generate and insert a key
874        let key_a0_id = TestSigningKey::A(0);
875        let key_a0 = SigningKey::make(SignatureAlgorithm::Ed25519);
876        store
877            .context_mut()
878            .set_signing_key(key_a0_id, key_a0)
879            .unwrap();
880    }
881
882    #[test]
883    fn test_set_keys_for_encryption() {
884        let store: KeyStore<TestIds> = KeyStore::default();
885
886        // Generate and insert a key
887        let key_a0_id = TestSymmKey::A(0);
888        let mut ctx = store.context_mut();
889        let local_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
890        ctx.persist_symmetric_key(local_key_id, TestSymmKey::A(0))
891            .unwrap();
892
893        assert!(ctx.has_symmetric_key(key_a0_id));
894
895        // Encrypt some data with the key
896        let data = DataView("Hello, World!".to_string(), key_a0_id);
897        let _encrypted: Data = data.encrypt_composite(&mut ctx, key_a0_id).unwrap();
898    }
899
900    #[test]
901    fn test_key_encryption() {
902        let store: KeyStore<TestIds> = KeyStore::default();
903
904        let mut ctx = store.context();
905
906        // Generate and insert a key
907        let key_1_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
908
909        assert!(ctx.has_symmetric_key(key_1_id));
910
911        // Generate and insert a new key
912        let key_2_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
913
914        assert!(ctx.has_symmetric_key(key_2_id));
915
916        // Encrypt the new key with the old key
917        let key_2_enc = ctx.wrap_symmetric_key(key_1_id, key_2_id).unwrap();
918
919        // Decrypt the new key with the old key in a different identifier
920        let new_key_id = ctx.unwrap_symmetric_key(key_1_id, &key_2_enc).unwrap();
921
922        // Now `key_2_id` and `new_key_id` contain the same key, so we should be able to encrypt
923        // with one and decrypt with the other
924
925        let data = DataView("Hello, World!".to_string(), key_2_id);
926        let encrypted = data.encrypt_composite(&mut ctx, key_2_id).unwrap();
927
928        let decrypted1 = encrypted.decrypt(&mut ctx, key_2_id).unwrap();
929        let decrypted2 = encrypted.decrypt(&mut ctx, new_key_id).unwrap();
930
931        // Assert that the decrypted data is the same
932        assert_eq!(decrypted1.0, decrypted2.0);
933    }
934
935    #[test]
936    fn test_wrap_unwrap() {
937        let store: KeyStore<TestIds> = KeyStore::default();
938        let mut ctx = store.context_mut();
939
940        // Aes256 CBC HMAC keys
941        let key_aes_1_id = TestSymmKey::A(1);
942        let local_key_1_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
943        ctx.persist_symmetric_key(local_key_1_id, key_aes_1_id)
944            .unwrap();
945        let key_aes_2_id = TestSymmKey::A(2);
946        let local_key_2_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
947        ctx.persist_symmetric_key(local_key_2_id, key_aes_2_id)
948            .unwrap();
949
950        // XChaCha20 Poly1305 keys
951        let key_xchacha_3_id = TestSymmKey::A(3);
952        let key_xchacha_3 = SymmetricCryptoKey::make_xchacha20_poly1305_key();
953        ctx.set_symmetric_key(key_xchacha_3_id, key_xchacha_3.clone())
954            .unwrap();
955        let key_xchacha_4_id = TestSymmKey::A(4);
956        let key_xchacha_4 = SymmetricCryptoKey::make_xchacha20_poly1305_key();
957        ctx.set_symmetric_key(key_xchacha_4_id, key_xchacha_4.clone())
958            .unwrap();
959
960        // Wrap and unwrap the keys
961        let wrapped_key_1_2 = ctx.wrap_symmetric_key(key_aes_1_id, key_aes_2_id).unwrap();
962        let wrapped_key_1_3 = ctx
963            .wrap_symmetric_key(key_aes_1_id, key_xchacha_3_id)
964            .unwrap();
965        let wrapped_key_3_1 = ctx
966            .wrap_symmetric_key(key_xchacha_3_id, key_aes_1_id)
967            .unwrap();
968        let wrapped_key_3_4 = ctx
969            .wrap_symmetric_key(key_xchacha_3_id, key_xchacha_4_id)
970            .unwrap();
971
972        // Unwrap the keys
973        let _unwrapped_key_2 = ctx
974            .unwrap_symmetric_key(key_aes_1_id, &wrapped_key_1_2)
975            .unwrap();
976        let _unwrapped_key_3 = ctx
977            .unwrap_symmetric_key(key_aes_1_id, &wrapped_key_1_3)
978            .unwrap();
979        let _unwrapped_key_1 = ctx
980            .unwrap_symmetric_key(key_xchacha_3_id, &wrapped_key_3_1)
981            .unwrap();
982        let _unwrapped_key_4 = ctx
983            .unwrap_symmetric_key(key_xchacha_3_id, &wrapped_key_3_4)
984            .unwrap();
985    }
986
987    #[test]
988    fn test_signing() {
989        let store: KeyStore<TestIds> = KeyStore::default();
990
991        // Generate and insert a key
992        let key_a0_id = TestSigningKey::A(0);
993        let key_a0 = SigningKey::make(SignatureAlgorithm::Ed25519);
994        let verifying_key = key_a0.to_verifying_key();
995        store
996            .context_mut()
997            .set_signing_key(key_a0_id, key_a0)
998            .unwrap();
999
1000        assert!(store.context().has_signing_key(key_a0_id));
1001
1002        // Sign some data with the key
1003        #[derive(Serialize, Deserialize)]
1004        struct TestData {
1005            data: String,
1006        }
1007        let signed_object = store
1008            .context()
1009            .sign(
1010                key_a0_id,
1011                &TestData {
1012                    data: "Hello".to_string(),
1013                },
1014                &SigningNamespace::ExampleNamespace,
1015            )
1016            .unwrap();
1017        let payload: Result<TestData, CryptoError> =
1018            signed_object.verify_and_unwrap(&verifying_key, &SigningNamespace::ExampleNamespace);
1019        assert!(payload.is_ok());
1020
1021        let (signature, serialized_message) = store
1022            .context()
1023            .sign_detached(
1024                key_a0_id,
1025                &TestData {
1026                    data: "Hello".to_string(),
1027                },
1028                &SigningNamespace::ExampleNamespace,
1029            )
1030            .unwrap();
1031        assert!(signature.verify(
1032            serialized_message.as_bytes(),
1033            &verifying_key,
1034            &SigningNamespace::ExampleNamespace
1035        ))
1036    }
1037
1038    #[test]
1039    fn test_account_key_rotation() {
1040        let store: KeyStore<TestIds> = KeyStore::default();
1041        let mut ctx = store.context_mut();
1042
1043        // Make the keys
1044        let current_user_signing_key_id =
1045            ctx.make_signing_key(SignatureAlgorithm::Ed25519).unwrap();
1046        let current_user_private_key_id = ctx.make_asymmetric_key().unwrap();
1047
1048        // Get the rotated account keys
1049        let rotated_keys = ctx
1050            .dangerous_get_v2_rotated_account_keys(
1051                current_user_private_key_id,
1052                current_user_signing_key_id,
1053            )
1054            .unwrap();
1055
1056        // Public/Private key
1057        assert_eq!(
1058            AsymmetricPublicCryptoKey::from_der(&rotated_keys.public_key)
1059                .unwrap()
1060                .to_der()
1061                .unwrap(),
1062            ctx.get_asymmetric_key(current_user_private_key_id)
1063                .unwrap()
1064                .to_public_key()
1065                .to_der()
1066                .unwrap()
1067        );
1068        let decrypted_private_key: Vec<u8> = rotated_keys
1069            .private_key
1070            .decrypt_with_key(&rotated_keys.user_key)
1071            .unwrap();
1072        let private_key =
1073            AsymmetricCryptoKey::from_der(&Pkcs8PrivateKeyBytes::from(decrypted_private_key))
1074                .unwrap();
1075        assert_eq!(
1076            private_key.to_der().unwrap(),
1077            ctx.get_asymmetric_key(current_user_private_key_id)
1078                .unwrap()
1079                .to_der()
1080                .unwrap()
1081        );
1082
1083        // Signing Key
1084        let decrypted_signing_key: Vec<u8> = rotated_keys
1085            .signing_key
1086            .decrypt_with_key(&rotated_keys.user_key)
1087            .unwrap();
1088        let signing_key =
1089            SigningKey::from_cose(&CoseKeyBytes::from(decrypted_signing_key)).unwrap();
1090        assert_eq!(
1091            signing_key.to_cose(),
1092            ctx.get_signing_key(current_user_signing_key_id)
1093                .unwrap()
1094                .to_cose(),
1095        );
1096
1097        // Signed Public Key
1098        let signed_public_key = rotated_keys.signed_public_key;
1099        let unwrapped_key = signed_public_key
1100            .verify_and_unwrap(
1101                &ctx.get_signing_key(current_user_signing_key_id)
1102                    .unwrap()
1103                    .to_verifying_key(),
1104            )
1105            .unwrap();
1106        assert_eq!(
1107            unwrapped_key.to_der().unwrap(),
1108            ctx.get_asymmetric_key(current_user_private_key_id)
1109                .unwrap()
1110                .to_public_key()
1111                .to_der()
1112                .unwrap()
1113        );
1114    }
1115
1116    #[test]
1117    fn test_encrypt_fails_when_operation_not_allowed() {
1118        use coset::iana::KeyOperation;
1119        let store = KeyStore::<TestIds>::default();
1120        let mut ctx = store.context_mut();
1121        let key_id = TestSymmKey::A(0);
1122        // Key with only Decrypt allowed
1123        let key = SymmetricCryptoKey::XChaCha20Poly1305Key(crate::XChaCha20Poly1305Key {
1124            key_id: [0u8; 16],
1125            enc_key: Box::pin([0u8; 32].into()),
1126            supported_operations: vec![KeyOperation::Decrypt],
1127        });
1128        ctx.set_symmetric_key(key_id, key).unwrap();
1129        let data = DataView("should fail".to_string(), key_id);
1130        let result = data.encrypt_composite(&mut ctx, key_id);
1131        assert!(
1132            matches!(
1133                result,
1134                Err(CryptoError::KeyOperationNotSupported(KeyOperation::Encrypt))
1135            ),
1136            "Expected encrypt to fail with KeyOperationNotSupported",
1137        );
1138    }
1139
1140    #[test]
1141    fn test_move_key() {
1142        let store: KeyStore<TestIds> = KeyStore::default();
1143        let mut ctx = store.context_mut();
1144
1145        // Generate and insert a key
1146        let key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
1147
1148        assert!(ctx.has_symmetric_key(key));
1149
1150        // Move the key to a new identifier
1151        let new_key_id = TestSymmKey::A(1);
1152        ctx.persist_symmetric_key(key, new_key_id).unwrap();
1153
1154        // Ensure the old key id is gone and the new `one has the key
1155        assert!(!ctx.has_symmetric_key(key));
1156        assert!(ctx.has_symmetric_key(new_key_id));
1157    }
1158}