Skip to main content

bitwarden_crypto/traits/
encryptable.rs

1//! This module defines traits for encrypting data. There are three categories here.
2//!
3//! Some (legacy) encryptables are made up of many small individually encrypted items. For instance,
4//! a cipher is currently made up of many small `EncString`s and some further json objects that
5//! themselves contain `EncString`s. The use of this is generally discouraged for new designs.
6//! Still, this is generally the only trait that should be implemented outside of the crypto crate.
7//!
8//! Encrypting data directly, a content type must be provided, since an encrypted byte array alone
9//! is not enough to tell the decryption code how to interpret the decrypted bytes. For this, there
10//! are two traits, `PrimitiveEncryptable` and `PrimitiveEncryptableWithContentType`. The former
11//! assumes that the implementation provides content format when encrypting, based on the type
12//! of struct that is being encrypted. The latter allows the caller to specify the content format
13//! at runtime, which is only allowed within the crypto crate.
14//!
15//! `PrimitiveEncryptable` is implemented for `crate::content_format::Bytes<C>` types, where `C` is
16//! a type that implements the `ConstContentFormat` trait. This allows for compile-time type
17//! checking of the content format, and the risk of using the wrong content format is limited to
18//! converting untyped bytes into a `Bytes<C>`
19//!
20//! # Round-trip contract
21//!
22//! The encrypt traits here and [`crate::Decryptable`] form a symmetric pair with a contract that
23//! callers rely on:
24//!
25//! - Decryption MUST yield a value that contains no encrypted material — no wrapped keys and no
26//!   residual [`EncString`]s that are still bound to the key that was used to decrypt. Everything
27//!   protected by that key must be fully decrypted.
28//! - Encryption MUST produce output that is fully encrypted under the provided `key`. No encrypted
29//!   items may be copied from the input. Plaintext data co-existing with encrypted data is allowed,
30//!   and may be copied through.
31//!
32//! Together these mean that decrypting with one key and re-encrypting with another round-trips: for
33//! any two valid keys `K` and `K1`, `decrypt(encrypt(decrypt(x, K), K1), K1) = decrypt(x, K)`.
34
35use crate::{ContentFormat, CryptoError, EncString, KeySlotId, KeySlotIds, store::KeyStoreContext};
36
37/// An encryption operation that takes the input value and encrypts the fields on it recursively.
38/// Implementations should generally consist of calling [PrimitiveEncryptable::encrypt] for all the
39/// fields of the type. Sometimes, it is necessary to call
40/// [CompositeEncryptable::encrypt_composite], if the object is not a flat struct.
41pub trait CompositeEncryptable<Ids: KeySlotIds, Key: KeySlotId, Output> {
42    /// # ⚠️ IMPORTANT NOTE ⚠️
43    /// This is not intended to be used for new designs, and only meant to support old designs.
44    /// Composite encryption does not provide integrity over the entire document, just individual
45    /// parts of it and is subject to specific tampering attacks where fields can be rearranged
46    /// or replaced with fields from other documents. Use [`crate::safe::DataEnvelope`] instead!
47    ///
48    /// For a struct made up of many small encstrings, such as a cipher, this takes the struct
49    /// and recursively encrypts all the fields / sub-structs.
50    ///
51    /// # Contract
52    /// The returned value MUST be fully encrypted under `key`. Implementations MUST NOT reuse
53    /// ciphertext embedded in `self` (for example a still-wrapped per-item key carried over from a
54    /// previous decryption); any such key material must be re-derived and re-wrapped under `key`.
55    /// This guarantees that a value produced by [`Decryptable::decrypt`] can be re-encrypted under
56    /// any valid key and then decrypted with that same key — i.e. `decrypt(K)` then `encrypt(K1)`
57    /// then `decrypt(K1)` succeeds.
58    ///
59    /// [`Decryptable::decrypt`]: crate::Decryptable::decrypt
60    fn encrypt_composite(
61        &self,
62        ctx: &mut KeyStoreContext<Ids>,
63        key: Key,
64    ) -> Result<Output, CryptoError>;
65}
66
67impl<Ids: KeySlotIds, Key: KeySlotId, T: CompositeEncryptable<Ids, Key, Output>, Output>
68    CompositeEncryptable<Ids, Key, Option<Output>> for Option<T>
69{
70    fn encrypt_composite(
71        &self,
72        ctx: &mut KeyStoreContext<Ids>,
73        key: Key,
74    ) -> Result<Option<Output>, CryptoError> {
75        self.as_ref()
76            .map(|value| value.encrypt_composite(ctx, key))
77            .transpose()
78    }
79}
80
81impl<Ids: KeySlotIds, Key: KeySlotId, T: CompositeEncryptable<Ids, Key, Output>, Output>
82    CompositeEncryptable<Ids, Key, Vec<Output>> for Vec<T>
83{
84    fn encrypt_composite(
85        &self,
86        ctx: &mut KeyStoreContext<Ids>,
87        key: Key,
88    ) -> Result<Vec<Output>, CryptoError> {
89        self.iter()
90            .map(|value| value.encrypt_composite(ctx, key))
91            .collect()
92    }
93}
94
95/// An encryption operation that takes the input value - a primitive such as `String` and encrypts
96/// it into the output value. The implementation decides the content format.
97pub trait PrimitiveEncryptable<Ids: KeySlotIds, Key: KeySlotId, Output> {
98    /// # ⚠️ IMPORTANT NOTE ⚠️
99    /// Most likely, you do not want to use this but want to use [`crate::safe::DataEnvelope`]
100    /// instead.
101    ///
102    /// Encrypts a primitive without requiring an externally provided content type
103    ///
104    /// # Contract
105    /// The returned value MUST be fully encrypted under `key`, so that the corresponding
106    /// [`Decryptable::decrypt`] yields the original primitive and a decrypt-then-encrypt under a
107    /// different key round-trips.
108    ///
109    /// [`Decryptable::decrypt`]: crate::Decryptable::decrypt
110    fn encrypt(&self, ctx: &mut KeyStoreContext<Ids>, key: Key) -> Result<Output, CryptoError>;
111}
112
113impl<Ids: KeySlotIds, Key: KeySlotId, T: PrimitiveEncryptable<Ids, Key, Output>, Output>
114    PrimitiveEncryptable<Ids, Key, Option<Output>> for Option<T>
115{
116    fn encrypt(
117        &self,
118        ctx: &mut KeyStoreContext<Ids>,
119        key: Key,
120    ) -> Result<Option<Output>, CryptoError> {
121        self.as_ref()
122            .map(|value| value.encrypt(ctx, key))
123            .transpose()
124    }
125}
126
127impl<Ids: KeySlotIds> PrimitiveEncryptable<Ids, Ids::Symmetric, EncString> for &str {
128    fn encrypt(
129        &self,
130        ctx: &mut KeyStoreContext<Ids>,
131        key: Ids::Symmetric,
132    ) -> Result<EncString, CryptoError> {
133        self.as_bytes().encrypt(ctx, key, ContentFormat::Utf8)
134    }
135}
136
137impl<Ids: KeySlotIds> PrimitiveEncryptable<Ids, Ids::Symmetric, EncString> for String {
138    fn encrypt(
139        &self,
140        ctx: &mut KeyStoreContext<Ids>,
141        key: Ids::Symmetric,
142    ) -> Result<EncString, CryptoError> {
143        self.as_bytes().encrypt(ctx, key, ContentFormat::Utf8)
144    }
145}
146
147/// An encryption operation that takes the input value - a primitive such as `Vec<u8>` - and
148/// encrypts it into the output value. The caller must specify the content format.
149pub(crate) trait PrimitiveEncryptableWithContentType<Ids: KeySlotIds, Key: KeySlotId, Output> {
150    /// # ⚠️ IMPORTANT NOTE ⚠️
151    /// Most likely, you do not want to use this but want to use [`crate::safe::DataEnvelope`]
152    /// instead.
153    ///
154    /// Encrypts a primitive, given an externally provided content type
155    fn encrypt(
156        &self,
157        ctx: &mut KeyStoreContext<Ids>,
158        key: Key,
159        content_format: ContentFormat,
160    ) -> Result<Output, CryptoError>;
161}
162
163impl<Ids: KeySlotIds> PrimitiveEncryptableWithContentType<Ids, Ids::Symmetric, EncString>
164    for &[u8]
165{
166    fn encrypt(
167        &self,
168        ctx: &mut KeyStoreContext<Ids>,
169        key: Ids::Symmetric,
170        content_format: ContentFormat,
171    ) -> Result<EncString, CryptoError> {
172        ctx.encrypt_data_with_symmetric_key(key, self, content_format)
173    }
174}
175
176impl<Ids: KeySlotIds> PrimitiveEncryptableWithContentType<Ids, Ids::Symmetric, EncString>
177    for Vec<u8>
178{
179    fn encrypt(
180        &self,
181        ctx: &mut KeyStoreContext<Ids>,
182        key: Ids::Symmetric,
183        content_format: ContentFormat,
184    ) -> Result<EncString, CryptoError> {
185        ctx.encrypt_data_with_symmetric_key(key, self, content_format)
186    }
187}
188
189impl<
190    Ids: KeySlotIds,
191    Key: KeySlotId,
192    T: PrimitiveEncryptableWithContentType<Ids, Key, Output>,
193    Output,
194> PrimitiveEncryptableWithContentType<Ids, Key, Option<Output>> for Option<T>
195{
196    fn encrypt(
197        &self,
198        ctx: &mut KeyStoreContext<Ids>,
199        key: Key,
200        content_format: crate::ContentFormat,
201    ) -> Result<Option<Output>, CryptoError> {
202        self.as_ref()
203            .map(|value| value.encrypt(ctx, key, content_format))
204            .transpose()
205    }
206}
207
208impl<
209    Ids: KeySlotIds,
210    Key: KeySlotId,
211    T: PrimitiveEncryptableWithContentType<Ids, Key, Output>,
212    Output,
213> PrimitiveEncryptableWithContentType<Ids, Key, Vec<Output>> for Vec<T>
214{
215    fn encrypt(
216        &self,
217        ctx: &mut KeyStoreContext<Ids>,
218        key: Key,
219        content_format: ContentFormat,
220    ) -> Result<Vec<Output>, CryptoError> {
221        self.iter()
222            .map(|value| value.encrypt(ctx, key, content_format))
223            .collect()
224    }
225}
226
227#[cfg(test)]
228mod tests {
229    use crate::{
230        ContentFormat, Decryptable, KeyStore, PrimitiveEncryptable, PrivateKey,
231        PublicKeyEncryptionAlgorithm, SymmetricKeyAlgorithm,
232        traits::{encryptable::PrimitiveEncryptableWithContentType, tests::*},
233    };
234
235    fn test_store() -> KeyStore<TestIds> {
236        let store = KeyStore::<TestIds>::default();
237
238        let private_key = PrivateKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
239
240        let mut ctx = store.context_mut();
241        let local_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
242        ctx.persist_symmetric_key(local_key_id, TestSymmKey::A(0))
243            .unwrap();
244        #[allow(deprecated)]
245        ctx.set_private_key(TestPrivateKey::A(0), private_key.clone())
246            .unwrap();
247        drop(ctx);
248
249        store
250    }
251
252    #[test]
253    fn test_encryptable_bytes() {
254        let store = test_store();
255        let mut ctx = store.context();
256        let key = TestSymmKey::A(0);
257
258        let vec_data = vec![1, 2, 3, 4, 5];
259        let slice_data: &[u8] = &vec_data;
260
261        let vec_encrypted = vec_data
262            .encrypt(&mut ctx, key, ContentFormat::OctetStream)
263            .unwrap();
264        let slice_encrypted = slice_data
265            .encrypt(&mut ctx, key, ContentFormat::OctetStream)
266            .unwrap();
267
268        let vec_decrypted: Vec<u8> = vec_encrypted.decrypt(&mut ctx, key).unwrap();
269        let slice_decrypted: Vec<u8> = slice_encrypted.decrypt(&mut ctx, key).unwrap();
270
271        assert_eq!(vec_data, vec_decrypted);
272        assert_eq!(slice_data, slice_decrypted);
273    }
274
275    #[test]
276    fn test_encryptable_string() {
277        let store = test_store();
278        let mut ctx = store.context();
279        let key = TestSymmKey::A(0);
280
281        let string_data = "Hello, World!".to_string();
282        let str_data: &str = string_data.as_str();
283
284        let string_encrypted = string_data.encrypt(&mut ctx, key).unwrap();
285        let str_encrypted = str_data.encrypt(&mut ctx, key).unwrap();
286
287        let string_decrypted: String = string_encrypted.decrypt(&mut ctx, key).unwrap();
288        let str_decrypted: String = str_encrypted.decrypt(&mut ctx, key).unwrap();
289
290        assert_eq!(string_data, string_decrypted);
291        assert_eq!(str_data, str_decrypted);
292    }
293
294    #[test]
295    fn test_encryptable_option_some() {
296        let store = test_store();
297        let mut ctx = store.context();
298        let key = TestSymmKey::A(0);
299
300        let string_data = Some("Hello, World!".to_string());
301
302        let string_encrypted = string_data.encrypt(&mut ctx, key).unwrap();
303
304        let string_decrypted: Option<String> = string_encrypted.decrypt(&mut ctx, key).unwrap();
305
306        assert_eq!(string_data, string_decrypted);
307    }
308
309    #[test]
310    fn test_encryptable_option_none() {
311        let store = test_store();
312        let mut ctx = store.context();
313
314        let key = TestSymmKey::A(0);
315        let none_data: Option<String> = None;
316        let string_encrypted = none_data.encrypt(&mut ctx, key).unwrap();
317        assert_eq!(string_encrypted, None);
318
319        // The None implementation will not do any decrypt operations, so it won't fail even if the
320        // key doesn't exist
321        let bad_key = TestSymmKey::B((0, 1));
322        let string_encrypted_bad = none_data.encrypt(&mut ctx, bad_key).unwrap();
323        assert_eq!(string_encrypted_bad, None);
324    }
325}