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, KeyId, KeyIds, 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: KeyIds, Key: KeyId, 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: KeyIds, Key: KeyId, 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: KeyIds, Key: KeyId, 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: KeyIds, Key: KeyId, 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: KeyIds, Key: KeyId, 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: KeyIds> 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: KeyIds> 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: KeyIds, Key: KeyId, 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: KeyIds> PrimitiveEncryptableWithContentType<Ids, Ids::Symmetric, EncString> for &[u8] {
132    fn encrypt(
133        &self,
134        ctx: &mut KeyStoreContext<Ids>,
135        key: Ids::Symmetric,
136        content_format: ContentFormat,
137    ) -> Result<EncString, CryptoError> {
138        ctx.encrypt_data_with_symmetric_key(key, self, content_format)
139    }
140}
141
142impl<Ids: KeyIds> PrimitiveEncryptableWithContentType<Ids, Ids::Symmetric, EncString> for Vec<u8> {
143    fn encrypt(
144        &self,
145        ctx: &mut KeyStoreContext<Ids>,
146        key: Ids::Symmetric,
147        content_format: ContentFormat,
148    ) -> Result<EncString, CryptoError> {
149        ctx.encrypt_data_with_symmetric_key(key, self, content_format)
150    }
151}
152
153impl<Ids: KeyIds, Key: KeyId, T: PrimitiveEncryptableWithContentType<Ids, Key, Output>, Output>
154    PrimitiveEncryptableWithContentType<Ids, Key, Option<Output>> for Option<T>
155{
156    fn encrypt(
157        &self,
158        ctx: &mut KeyStoreContext<Ids>,
159        key: Key,
160        content_format: crate::ContentFormat,
161    ) -> Result<Option<Output>, CryptoError> {
162        self.as_ref()
163            .map(|value| value.encrypt(ctx, key, content_format))
164            .transpose()
165    }
166}
167
168impl<Ids: KeyIds, Key: KeyId, T: PrimitiveEncryptableWithContentType<Ids, Key, Output>, Output>
169    PrimitiveEncryptableWithContentType<Ids, Key, Vec<Output>> for Vec<T>
170{
171    fn encrypt(
172        &self,
173        ctx: &mut KeyStoreContext<Ids>,
174        key: Key,
175        content_format: ContentFormat,
176    ) -> Result<Vec<Output>, CryptoError> {
177        self.iter()
178            .map(|value| value.encrypt(ctx, key, content_format))
179            .collect()
180    }
181}
182
183#[cfg(test)]
184mod tests {
185    use crate::{
186        ContentFormat, Decryptable, KeyStore, PrimitiveEncryptable, PrivateKey,
187        PublicKeyEncryptionAlgorithm, SymmetricKeyAlgorithm,
188        traits::{encryptable::PrimitiveEncryptableWithContentType, tests::*},
189    };
190
191    fn test_store() -> KeyStore<TestIds> {
192        let store = KeyStore::<TestIds>::default();
193
194        let private_key = PrivateKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
195
196        let mut ctx = store.context_mut();
197        let local_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
198        ctx.persist_symmetric_key(local_key_id, TestSymmKey::A(0))
199            .unwrap();
200        #[allow(deprecated)]
201        ctx.set_private_key(TestPrivateKey::A(0), private_key.clone())
202            .unwrap();
203        drop(ctx);
204
205        store
206    }
207
208    #[test]
209    fn test_encryptable_bytes() {
210        let store = test_store();
211        let mut ctx = store.context();
212        let key = TestSymmKey::A(0);
213
214        let vec_data = vec![1, 2, 3, 4, 5];
215        let slice_data: &[u8] = &vec_data;
216
217        let vec_encrypted = vec_data
218            .encrypt(&mut ctx, key, ContentFormat::OctetStream)
219            .unwrap();
220        let slice_encrypted = slice_data
221            .encrypt(&mut ctx, key, ContentFormat::OctetStream)
222            .unwrap();
223
224        let vec_decrypted: Vec<u8> = vec_encrypted.decrypt(&mut ctx, key).unwrap();
225        let slice_decrypted: Vec<u8> = slice_encrypted.decrypt(&mut ctx, key).unwrap();
226
227        assert_eq!(vec_data, vec_decrypted);
228        assert_eq!(slice_data, slice_decrypted);
229    }
230
231    #[test]
232    fn test_encryptable_string() {
233        let store = test_store();
234        let mut ctx = store.context();
235        let key = TestSymmKey::A(0);
236
237        let string_data = "Hello, World!".to_string();
238        let str_data: &str = string_data.as_str();
239
240        let string_encrypted = string_data.encrypt(&mut ctx, key).unwrap();
241        let str_encrypted = str_data.encrypt(&mut ctx, key).unwrap();
242
243        let string_decrypted: String = string_encrypted.decrypt(&mut ctx, key).unwrap();
244        let str_decrypted: String = str_encrypted.decrypt(&mut ctx, key).unwrap();
245
246        assert_eq!(string_data, string_decrypted);
247        assert_eq!(str_data, str_decrypted);
248    }
249
250    #[test]
251    fn test_encryptable_option_some() {
252        let store = test_store();
253        let mut ctx = store.context();
254        let key = TestSymmKey::A(0);
255
256        let string_data = Some("Hello, World!".to_string());
257
258        let string_encrypted = string_data.encrypt(&mut ctx, key).unwrap();
259
260        let string_decrypted: Option<String> = string_encrypted.decrypt(&mut ctx, key).unwrap();
261
262        assert_eq!(string_data, string_decrypted);
263    }
264
265    #[test]
266    fn test_encryptable_option_none() {
267        let store = test_store();
268        let mut ctx = store.context();
269
270        let key = TestSymmKey::A(0);
271        let none_data: Option<String> = None;
272        let string_encrypted = none_data.encrypt(&mut ctx, key).unwrap();
273        assert_eq!(string_encrypted, None);
274
275        // The None implementation will not do any decrypt operations, so it won't fail even if the
276        // key doesn't exist
277        let bad_key = TestSymmKey::B((0, 1));
278        let string_encrypted_bad = none_data.encrypt(&mut ctx, bad_key).unwrap();
279        assert_eq!(string_encrypted_bad, None);
280    }
281}