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::{store::KeyStoreContext, ContentFormat, CryptoError, EncString, KeyId, KeyIds};
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    /// For a struct made up of many small encstrings, such as a cipher, this takes the struct
28    /// and recursively encrypts all the fields / sub-structs.
29    fn encrypt_composite(
30        &self,
31        ctx: &mut KeyStoreContext<Ids>,
32        key: Key,
33    ) -> Result<Output, CryptoError>;
34}
35
36impl<Ids: KeyIds, Key: KeyId, T: CompositeEncryptable<Ids, Key, Output>, Output>
37    CompositeEncryptable<Ids, Key, Option<Output>> for Option<T>
38{
39    fn encrypt_composite(
40        &self,
41        ctx: &mut KeyStoreContext<Ids>,
42        key: Key,
43    ) -> Result<Option<Output>, CryptoError> {
44        self.as_ref()
45            .map(|value| value.encrypt_composite(ctx, key))
46            .transpose()
47    }
48}
49
50impl<Ids: KeyIds, Key: KeyId, T: CompositeEncryptable<Ids, Key, Output>, Output>
51    CompositeEncryptable<Ids, Key, Vec<Output>> for Vec<T>
52{
53    fn encrypt_composite(
54        &self,
55        ctx: &mut KeyStoreContext<Ids>,
56        key: Key,
57    ) -> Result<Vec<Output>, CryptoError> {
58        self.iter()
59            .map(|value| value.encrypt_composite(ctx, key))
60            .collect()
61    }
62}
63
64/// An encryption operation that takes the input value - a primitive such as `String` and encrypts
65/// it into the output value. The implementation decides the content format.
66pub trait PrimitiveEncryptable<Ids: KeyIds, Key: KeyId, Output> {
67    /// Encrypts a primitive without requiring an externally provided content type
68    fn encrypt(&self, ctx: &mut KeyStoreContext<Ids>, key: Key) -> Result<Output, CryptoError>;
69}
70
71impl<Ids: KeyIds, Key: KeyId, T: PrimitiveEncryptable<Ids, Key, Output>, Output>
72    PrimitiveEncryptable<Ids, Key, Option<Output>> for Option<T>
73{
74    fn encrypt(
75        &self,
76        ctx: &mut KeyStoreContext<Ids>,
77        key: Key,
78    ) -> Result<Option<Output>, CryptoError> {
79        self.as_ref()
80            .map(|value| value.encrypt(ctx, key))
81            .transpose()
82    }
83}
84
85impl<Ids: KeyIds> PrimitiveEncryptable<Ids, Ids::Symmetric, EncString> for &str {
86    fn encrypt(
87        &self,
88        ctx: &mut KeyStoreContext<Ids>,
89        key: Ids::Symmetric,
90    ) -> Result<EncString, CryptoError> {
91        self.as_bytes().encrypt(ctx, key, ContentFormat::Utf8)
92    }
93}
94
95impl<Ids: KeyIds> PrimitiveEncryptable<Ids, Ids::Symmetric, EncString> for String {
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
105/// An encryption operation that takes the input value - a primitive such as `Vec<u8>` - and
106/// encrypts it into the output value. The caller must specify the content format.
107pub(crate) trait PrimitiveEncryptableWithContentType<Ids: KeyIds, Key: KeyId, Output> {
108    /// Encrypts a primitive, given an externally provided content type
109    fn encrypt(
110        &self,
111        ctx: &mut KeyStoreContext<Ids>,
112        key: Key,
113        content_format: ContentFormat,
114    ) -> Result<Output, CryptoError>;
115}
116
117impl<Ids: KeyIds> PrimitiveEncryptableWithContentType<Ids, Ids::Symmetric, EncString> for &[u8] {
118    fn encrypt(
119        &self,
120        ctx: &mut KeyStoreContext<Ids>,
121        key: Ids::Symmetric,
122        content_format: ContentFormat,
123    ) -> Result<EncString, CryptoError> {
124        ctx.encrypt_data_with_symmetric_key(key, self, content_format)
125    }
126}
127
128impl<Ids: KeyIds> PrimitiveEncryptableWithContentType<Ids, Ids::Symmetric, EncString> for Vec<u8> {
129    fn encrypt(
130        &self,
131        ctx: &mut KeyStoreContext<Ids>,
132        key: Ids::Symmetric,
133        content_format: ContentFormat,
134    ) -> Result<EncString, CryptoError> {
135        ctx.encrypt_data_with_symmetric_key(key, self, content_format)
136    }
137}
138
139impl<Ids: KeyIds, Key: KeyId, T: PrimitiveEncryptableWithContentType<Ids, Key, Output>, Output>
140    PrimitiveEncryptableWithContentType<Ids, Key, Option<Output>> for Option<T>
141{
142    fn encrypt(
143        &self,
144        ctx: &mut KeyStoreContext<Ids>,
145        key: Key,
146        content_format: crate::ContentFormat,
147    ) -> Result<Option<Output>, CryptoError> {
148        self.as_ref()
149            .map(|value| value.encrypt(ctx, key, content_format))
150            .transpose()
151    }
152}
153
154impl<Ids: KeyIds, Key: KeyId, T: PrimitiveEncryptableWithContentType<Ids, Key, Output>, Output>
155    PrimitiveEncryptableWithContentType<Ids, Key, Vec<Output>> for Vec<T>
156{
157    fn encrypt(
158        &self,
159        ctx: &mut KeyStoreContext<Ids>,
160        key: Key,
161        content_format: ContentFormat,
162    ) -> Result<Vec<Output>, CryptoError> {
163        self.iter()
164            .map(|value| value.encrypt(ctx, key, content_format))
165            .collect()
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use crate::{
172        traits::{encryptable::PrimitiveEncryptableWithContentType, tests::*},
173        AsymmetricCryptoKey, ContentFormat, Decryptable, KeyStore, PrimitiveEncryptable,
174        PublicKeyEncryptionAlgorithm, SymmetricCryptoKey,
175    };
176
177    fn test_store() -> KeyStore<TestIds> {
178        let store = KeyStore::<TestIds>::default();
179
180        let symm_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
181        let asymm_key = AsymmetricCryptoKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
182
183        #[allow(deprecated)]
184        store
185            .context_mut()
186            .set_symmetric_key(TestSymmKey::A(0), symm_key.clone())
187            .unwrap();
188        #[allow(deprecated)]
189        store
190            .context_mut()
191            .set_asymmetric_key(TestAsymmKey::A(0), asymm_key.clone())
192            .unwrap();
193
194        store
195    }
196
197    #[test]
198    fn test_encryptable_bytes() {
199        let store = test_store();
200        let mut ctx = store.context();
201        let key = TestSymmKey::A(0);
202
203        let vec_data = vec![1, 2, 3, 4, 5];
204        let slice_data: &[u8] = &vec_data;
205
206        let vec_encrypted = vec_data
207            .encrypt(&mut ctx, key, ContentFormat::OctetStream)
208            .unwrap();
209        let slice_encrypted = slice_data
210            .encrypt(&mut ctx, key, ContentFormat::OctetStream)
211            .unwrap();
212
213        let vec_decrypted: Vec<u8> = vec_encrypted.decrypt(&mut ctx, key).unwrap();
214        let slice_decrypted: Vec<u8> = slice_encrypted.decrypt(&mut ctx, key).unwrap();
215
216        assert_eq!(vec_data, vec_decrypted);
217        assert_eq!(slice_data, slice_decrypted);
218    }
219
220    #[test]
221    fn test_encryptable_string() {
222        let store = test_store();
223        let mut ctx = store.context();
224        let key = TestSymmKey::A(0);
225
226        let string_data = "Hello, World!".to_string();
227        let str_data: &str = string_data.as_str();
228
229        let string_encrypted = string_data.encrypt(&mut ctx, key).unwrap();
230        let str_encrypted = str_data.encrypt(&mut ctx, key).unwrap();
231
232        let string_decrypted: String = string_encrypted.decrypt(&mut ctx, key).unwrap();
233        let str_decrypted: String = str_encrypted.decrypt(&mut ctx, key).unwrap();
234
235        assert_eq!(string_data, string_decrypted);
236        assert_eq!(str_data, str_decrypted);
237    }
238
239    #[test]
240    fn test_encryptable_option_some() {
241        let store = test_store();
242        let mut ctx = store.context();
243        let key = TestSymmKey::A(0);
244
245        let string_data = Some("Hello, World!".to_string());
246
247        let string_encrypted = string_data.encrypt(&mut ctx, key).unwrap();
248
249        let string_decrypted: Option<String> = string_encrypted.decrypt(&mut ctx, key).unwrap();
250
251        assert_eq!(string_data, string_decrypted);
252    }
253
254    #[test]
255    fn test_encryptable_option_none() {
256        let store = test_store();
257        let mut ctx = store.context();
258
259        let key = TestSymmKey::A(0);
260        let none_data: Option<String> = None;
261        let string_encrypted = none_data.encrypt(&mut ctx, key).unwrap();
262        assert_eq!(string_encrypted, None);
263
264        // The None implementation will not do any decrypt operations, so it won't fail even if the
265        // key doesn't exist
266        let bad_key = TestSymmKey::B((0, 1));
267        let string_encrypted_bad = none_data.encrypt(&mut ctx, bad_key).unwrap();
268        assert_eq!(string_encrypted_bad, None);
269    }
270}