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
20use crate::{ContentFormat, CryptoError, EncString, KeySlotId, KeySlotIds, store::KeyStoreContext};
21
22/// An encryption operation that takes the input value and encrypts the fields on it recursively.
23/// Implementations should generally consist of calling [PrimitiveEncryptable::encrypt] for all the
24/// fields of the type. Sometimes, it is necessary to call
25/// [CompositeEncryptable::encrypt_composite], if the object is not a flat struct.
26pub trait CompositeEncryptable<Ids: KeySlotIds, Key: KeySlotId, Output> {
27    /// # ⚠️ IMPORTANT NOTE ⚠️
28    /// This is not intended to be used for new designs, and only meant to support old designs.
29    /// Composite encryption does not provide integrity over the entire document, just individual
30    /// parts of it and is subject to specific tampering attacks where fields can be rearranged
31    /// or replaced with fields from other documents. Use [`crate::safe::DataEnvelope`] instead!
32    ///
33    /// For a struct made up of many small encstrings, such as a cipher, this takes the struct
34    /// and recursively encrypts all the fields / sub-structs.
35    fn encrypt_composite(
36        &self,
37        ctx: &mut KeyStoreContext<Ids>,
38        key: Key,
39    ) -> Result<Output, CryptoError>;
40}
41
42impl<Ids: KeySlotIds, Key: KeySlotId, T: CompositeEncryptable<Ids, Key, Output>, Output>
43    CompositeEncryptable<Ids, Key, Option<Output>> for Option<T>
44{
45    fn encrypt_composite(
46        &self,
47        ctx: &mut KeyStoreContext<Ids>,
48        key: Key,
49    ) -> Result<Option<Output>, CryptoError> {
50        self.as_ref()
51            .map(|value| value.encrypt_composite(ctx, key))
52            .transpose()
53    }
54}
55
56impl<Ids: KeySlotIds, Key: KeySlotId, T: CompositeEncryptable<Ids, Key, Output>, Output>
57    CompositeEncryptable<Ids, Key, Vec<Output>> for Vec<T>
58{
59    fn encrypt_composite(
60        &self,
61        ctx: &mut KeyStoreContext<Ids>,
62        key: Key,
63    ) -> Result<Vec<Output>, CryptoError> {
64        self.iter()
65            .map(|value| value.encrypt_composite(ctx, key))
66            .collect()
67    }
68}
69
70/// An encryption operation that takes the input value - a primitive such as `String` and encrypts
71/// it into the output value. The implementation decides the content format.
72pub trait PrimitiveEncryptable<Ids: KeySlotIds, Key: KeySlotId, Output> {
73    /// # ⚠️ IMPORTANT NOTE ⚠️
74    /// Most likely, you do not want to use this but want to use [`crate::safe::DataEnvelope`]
75    /// instead.
76    ///
77    /// Encrypts a primitive without requiring an externally provided content type
78    fn encrypt(&self, ctx: &mut KeyStoreContext<Ids>, key: Key) -> Result<Output, CryptoError>;
79}
80
81impl<Ids: KeySlotIds, Key: KeySlotId, T: PrimitiveEncryptable<Ids, Key, Output>, Output>
82    PrimitiveEncryptable<Ids, Key, Option<Output>> for Option<T>
83{
84    fn encrypt(
85        &self,
86        ctx: &mut KeyStoreContext<Ids>,
87        key: Key,
88    ) -> Result<Option<Output>, CryptoError> {
89        self.as_ref()
90            .map(|value| value.encrypt(ctx, key))
91            .transpose()
92    }
93}
94
95impl<Ids: KeySlotIds> PrimitiveEncryptable<Ids, Ids::Symmetric, EncString> for &str {
96    fn encrypt(
97        &self,
98        ctx: &mut KeyStoreContext<Ids>,
99        key: Ids::Symmetric,
100    ) -> Result<EncString, CryptoError> {
101        self.as_bytes().encrypt(ctx, key, ContentFormat::Utf8)
102    }
103}
104
105impl<Ids: KeySlotIds> PrimitiveEncryptable<Ids, Ids::Symmetric, EncString> for String {
106    fn encrypt(
107        &self,
108        ctx: &mut KeyStoreContext<Ids>,
109        key: Ids::Symmetric,
110    ) -> Result<EncString, CryptoError> {
111        self.as_bytes().encrypt(ctx, key, ContentFormat::Utf8)
112    }
113}
114
115/// An encryption operation that takes the input value - a primitive such as `Vec<u8>` - and
116/// encrypts it into the output value. The caller must specify the content format.
117pub(crate) trait PrimitiveEncryptableWithContentType<Ids: KeySlotIds, Key: KeySlotId, Output> {
118    /// # ⚠️ IMPORTANT NOTE ⚠️
119    /// Most likely, you do not want to use this but want to use [`crate::safe::DataEnvelope`]
120    /// instead.
121    ///
122    /// Encrypts a primitive, given an externally provided content type
123    fn encrypt(
124        &self,
125        ctx: &mut KeyStoreContext<Ids>,
126        key: Key,
127        content_format: ContentFormat,
128    ) -> Result<Output, CryptoError>;
129}
130
131impl<Ids: KeySlotIds> PrimitiveEncryptableWithContentType<Ids, Ids::Symmetric, EncString>
132    for &[u8]
133{
134    fn encrypt(
135        &self,
136        ctx: &mut KeyStoreContext<Ids>,
137        key: Ids::Symmetric,
138        content_format: ContentFormat,
139    ) -> Result<EncString, CryptoError> {
140        ctx.encrypt_data_with_symmetric_key(key, self, content_format)
141    }
142}
143
144impl<Ids: KeySlotIds> PrimitiveEncryptableWithContentType<Ids, Ids::Symmetric, EncString>
145    for Vec<u8>
146{
147    fn encrypt(
148        &self,
149        ctx: &mut KeyStoreContext<Ids>,
150        key: Ids::Symmetric,
151        content_format: ContentFormat,
152    ) -> Result<EncString, CryptoError> {
153        ctx.encrypt_data_with_symmetric_key(key, self, content_format)
154    }
155}
156
157impl<
158    Ids: KeySlotIds,
159    Key: KeySlotId,
160    T: PrimitiveEncryptableWithContentType<Ids, Key, Output>,
161    Output,
162> PrimitiveEncryptableWithContentType<Ids, Key, Option<Output>> for Option<T>
163{
164    fn encrypt(
165        &self,
166        ctx: &mut KeyStoreContext<Ids>,
167        key: Key,
168        content_format: crate::ContentFormat,
169    ) -> Result<Option<Output>, CryptoError> {
170        self.as_ref()
171            .map(|value| value.encrypt(ctx, key, content_format))
172            .transpose()
173    }
174}
175
176impl<
177    Ids: KeySlotIds,
178    Key: KeySlotId,
179    T: PrimitiveEncryptableWithContentType<Ids, Key, Output>,
180    Output,
181> PrimitiveEncryptableWithContentType<Ids, Key, Vec<Output>> for Vec<T>
182{
183    fn encrypt(
184        &self,
185        ctx: &mut KeyStoreContext<Ids>,
186        key: Key,
187        content_format: ContentFormat,
188    ) -> Result<Vec<Output>, CryptoError> {
189        self.iter()
190            .map(|value| value.encrypt(ctx, key, content_format))
191            .collect()
192    }
193}
194
195#[cfg(test)]
196mod tests {
197    use crate::{
198        ContentFormat, Decryptable, KeyStore, PrimitiveEncryptable, PrivateKey,
199        PublicKeyEncryptionAlgorithm, SymmetricKeyAlgorithm,
200        traits::{encryptable::PrimitiveEncryptableWithContentType, tests::*},
201    };
202
203    fn test_store() -> KeyStore<TestIds> {
204        let store = KeyStore::<TestIds>::default();
205
206        let private_key = PrivateKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
207
208        let mut ctx = store.context_mut();
209        let local_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
210        ctx.persist_symmetric_key(local_key_id, TestSymmKey::A(0))
211            .unwrap();
212        #[allow(deprecated)]
213        ctx.set_private_key(TestPrivateKey::A(0), private_key.clone())
214            .unwrap();
215        drop(ctx);
216
217        store
218    }
219
220    #[test]
221    fn test_encryptable_bytes() {
222        let store = test_store();
223        let mut ctx = store.context();
224        let key = TestSymmKey::A(0);
225
226        let vec_data = vec![1, 2, 3, 4, 5];
227        let slice_data: &[u8] = &vec_data;
228
229        let vec_encrypted = vec_data
230            .encrypt(&mut ctx, key, ContentFormat::OctetStream)
231            .unwrap();
232        let slice_encrypted = slice_data
233            .encrypt(&mut ctx, key, ContentFormat::OctetStream)
234            .unwrap();
235
236        let vec_decrypted: Vec<u8> = vec_encrypted.decrypt(&mut ctx, key).unwrap();
237        let slice_decrypted: Vec<u8> = slice_encrypted.decrypt(&mut ctx, key).unwrap();
238
239        assert_eq!(vec_data, vec_decrypted);
240        assert_eq!(slice_data, slice_decrypted);
241    }
242
243    #[test]
244    fn test_encryptable_string() {
245        let store = test_store();
246        let mut ctx = store.context();
247        let key = TestSymmKey::A(0);
248
249        let string_data = "Hello, World!".to_string();
250        let str_data: &str = string_data.as_str();
251
252        let string_encrypted = string_data.encrypt(&mut ctx, key).unwrap();
253        let str_encrypted = str_data.encrypt(&mut ctx, key).unwrap();
254
255        let string_decrypted: String = string_encrypted.decrypt(&mut ctx, key).unwrap();
256        let str_decrypted: String = str_encrypted.decrypt(&mut ctx, key).unwrap();
257
258        assert_eq!(string_data, string_decrypted);
259        assert_eq!(str_data, str_decrypted);
260    }
261
262    #[test]
263    fn test_encryptable_option_some() {
264        let store = test_store();
265        let mut ctx = store.context();
266        let key = TestSymmKey::A(0);
267
268        let string_data = Some("Hello, World!".to_string());
269
270        let string_encrypted = string_data.encrypt(&mut ctx, key).unwrap();
271
272        let string_decrypted: Option<String> = string_encrypted.decrypt(&mut ctx, key).unwrap();
273
274        assert_eq!(string_data, string_decrypted);
275    }
276
277    #[test]
278    fn test_encryptable_option_none() {
279        let store = test_store();
280        let mut ctx = store.context();
281
282        let key = TestSymmKey::A(0);
283        let none_data: Option<String> = None;
284        let string_encrypted = none_data.encrypt(&mut ctx, key).unwrap();
285        assert_eq!(string_encrypted, None);
286
287        // The None implementation will not do any decrypt operations, so it won't fail even if the
288        // key doesn't exist
289        let bad_key = TestSymmKey::B((0, 1));
290        let string_encrypted_bad = none_data.encrypt(&mut ctx, bad_key).unwrap();
291        assert_eq!(string_encrypted_bad, None);
292    }
293}