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    /// 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        AsymmetricCryptoKey, ContentFormat, Decryptable, KeyStore, PrimitiveEncryptable,
173        PublicKeyEncryptionAlgorithm, SymmetricKeyAlgorithm,
174        traits::{encryptable::PrimitiveEncryptableWithContentType, tests::*},
175    };
176
177    fn test_store() -> KeyStore<TestIds> {
178        let store = KeyStore::<TestIds>::default();
179
180        let asymm_key = AsymmetricCryptoKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
181
182        let mut ctx = store.context_mut();
183        let local_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
184        ctx.persist_symmetric_key(local_key_id, TestSymmKey::A(0))
185            .unwrap();
186        #[allow(deprecated)]
187        ctx.set_asymmetric_key(TestAsymmKey::A(0), asymm_key.clone())
188            .unwrap();
189        drop(ctx);
190
191        store
192    }
193
194    #[test]
195    fn test_encryptable_bytes() {
196        let store = test_store();
197        let mut ctx = store.context();
198        let key = TestSymmKey::A(0);
199
200        let vec_data = vec![1, 2, 3, 4, 5];
201        let slice_data: &[u8] = &vec_data;
202
203        let vec_encrypted = vec_data
204            .encrypt(&mut ctx, key, ContentFormat::OctetStream)
205            .unwrap();
206        let slice_encrypted = slice_data
207            .encrypt(&mut ctx, key, ContentFormat::OctetStream)
208            .unwrap();
209
210        let vec_decrypted: Vec<u8> = vec_encrypted.decrypt(&mut ctx, key).unwrap();
211        let slice_decrypted: Vec<u8> = slice_encrypted.decrypt(&mut ctx, key).unwrap();
212
213        assert_eq!(vec_data, vec_decrypted);
214        assert_eq!(slice_data, slice_decrypted);
215    }
216
217    #[test]
218    fn test_encryptable_string() {
219        let store = test_store();
220        let mut ctx = store.context();
221        let key = TestSymmKey::A(0);
222
223        let string_data = "Hello, World!".to_string();
224        let str_data: &str = string_data.as_str();
225
226        let string_encrypted = string_data.encrypt(&mut ctx, key).unwrap();
227        let str_encrypted = str_data.encrypt(&mut ctx, key).unwrap();
228
229        let string_decrypted: String = string_encrypted.decrypt(&mut ctx, key).unwrap();
230        let str_decrypted: String = str_encrypted.decrypt(&mut ctx, key).unwrap();
231
232        assert_eq!(string_data, string_decrypted);
233        assert_eq!(str_data, str_decrypted);
234    }
235
236    #[test]
237    fn test_encryptable_option_some() {
238        let store = test_store();
239        let mut ctx = store.context();
240        let key = TestSymmKey::A(0);
241
242        let string_data = Some("Hello, World!".to_string());
243
244        let string_encrypted = string_data.encrypt(&mut ctx, key).unwrap();
245
246        let string_decrypted: Option<String> = string_encrypted.decrypt(&mut ctx, key).unwrap();
247
248        assert_eq!(string_data, string_decrypted);
249    }
250
251    #[test]
252    fn test_encryptable_option_none() {
253        let store = test_store();
254        let mut ctx = store.context();
255
256        let key = TestSymmKey::A(0);
257        let none_data: Option<String> = None;
258        let string_encrypted = none_data.encrypt(&mut ctx, key).unwrap();
259        assert_eq!(string_encrypted, None);
260
261        // The None implementation will not do any decrypt operations, so it won't fail even if the
262        // key doesn't exist
263        let bad_key = TestSymmKey::B((0, 1));
264        let string_encrypted_bad = none_data.encrypt(&mut ctx, bad_key).unwrap();
265        assert_eq!(string_encrypted_bad, None);
266    }
267}