bitwarden_crypto/traits/
decryptable.rs

1use crate::{store::KeyStoreContext, CryptoError, EncString, KeyId, KeyIds};
2
3/// A decryption operation that takes the input value and decrypts it into the output value.
4/// Implementations should generally consist of calling [Decryptable::decrypt] for all the fields of
5/// the type.
6pub trait Decryptable<Ids: KeyIds, Key: KeyId, Output> {
7    fn decrypt(&self, ctx: &mut KeyStoreContext<Ids>, key: Key) -> Result<Output, CryptoError>;
8}
9
10impl<Ids: KeyIds> Decryptable<Ids, Ids::Symmetric, Vec<u8>> for EncString {
11    fn decrypt(
12        &self,
13        ctx: &mut KeyStoreContext<Ids>,
14        key: Ids::Symmetric,
15    ) -> Result<Vec<u8>, CryptoError> {
16        ctx.decrypt_data_with_symmetric_key(key, self)
17    }
18}
19
20impl<Ids: KeyIds> Decryptable<Ids, Ids::Symmetric, String> for EncString {
21    fn decrypt(
22        &self,
23        ctx: &mut KeyStoreContext<Ids>,
24        key: Ids::Symmetric,
25    ) -> Result<String, CryptoError> {
26        let bytes: Vec<u8> = self.decrypt(ctx, key)?;
27        String::from_utf8(bytes).map_err(|_| CryptoError::InvalidUtf8String)
28    }
29}
30
31impl<Ids: KeyIds, Key: KeyId, T: Decryptable<Ids, Key, Output>, Output>
32    Decryptable<Ids, Key, Option<Output>> for Option<T>
33{
34    fn decrypt(
35        &self,
36        ctx: &mut KeyStoreContext<Ids>,
37        key: Key,
38    ) -> Result<Option<Output>, CryptoError> {
39        self.as_ref()
40            .map(|value| value.decrypt(ctx, key))
41            .transpose()
42    }
43}
44
45impl<Ids: KeyIds, Key: KeyId, T: Decryptable<Ids, Key, Output>, Output>
46    Decryptable<Ids, Key, Vec<Output>> for Vec<T>
47{
48    fn decrypt(
49        &self,
50        ctx: &mut KeyStoreContext<Ids>,
51        key: Key,
52    ) -> Result<Vec<Output>, CryptoError> {
53        self.iter().map(|value| value.decrypt(ctx, key)).collect()
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use crate::{traits::tests::*, Decryptable, EncString, KeyStore, SymmetricCryptoKey};
60
61    fn test_store() -> KeyStore<TestIds> {
62        let store = KeyStore::<TestIds>::default();
63
64        let key = SymmetricCryptoKey::try_from("sJnO8rVi0dTwND43n0T9x7665s8mVUYNAaJ4nm7gx1iia1I7947URL60nwfIHaf9QJePO4VkNN0oT9jh4iC6aA==".to_string()).unwrap();
65
66        #[allow(deprecated)]
67        store
68            .context_mut()
69            .set_symmetric_key(TestSymmKey::A(0), key.clone())
70            .unwrap();
71
72        store
73    }
74
75    #[test]
76    fn test_decryptable_bytes() {
77        let store = test_store();
78        let mut ctx = store.context();
79        let key = TestSymmKey::A(0);
80
81        let data_encrypted: EncString = "2.kTtIypq9OLzd5iMMbU11pQ==|J4i3hTtGVdg7EZ+AQv/ujg==|QJpSpotQVpIW8j8dR/8l015WJzAIxBaOmrz4Uj/V1JA=".parse().unwrap();
82
83        let data_decrypted: Vec<u8> = data_encrypted.decrypt(&mut ctx, key).unwrap();
84        assert_eq!(data_decrypted, &[1, 2, 3, 4, 5]);
85    }
86
87    #[test]
88    fn test_decryptable_string() {
89        let store = test_store();
90        let mut ctx = store.context();
91        let key = TestSymmKey::A(0);
92
93        let data_encrypted: EncString = "2.fkvl0+sL1lwtiOn1eewsvQ==|dT0TynLl8YERZ8x7dxC+DQ==|cWhiRSYHOi/AA2LiV/JBJWbO9C7pbUpOM6TMAcV47hE=".parse().unwrap();
94
95        let data_decrypted: String = data_encrypted.decrypt(&mut ctx, key).unwrap();
96        assert_eq!(data_decrypted, "Hello, World!");
97    }
98
99    #[test]
100    fn test_decryptable_option_some() {
101        let store = test_store();
102        let mut ctx = store.context();
103        let key = TestSymmKey::A(0);
104
105        let data_encrypted: EncString = "2.fkvl0+sL1lwtiOn1eewsvQ==|dT0TynLl8YERZ8x7dxC+DQ==|cWhiRSYHOi/AA2LiV/JBJWbO9C7pbUpOM6TMAcV47hE=".parse().unwrap();
106        let data_encrypted_some = Some(data_encrypted);
107
108        let string_decrypted: Option<String> = data_encrypted_some.decrypt(&mut ctx, key).unwrap();
109        assert_eq!(string_decrypted, Some("Hello, World!".to_string()));
110    }
111
112    #[test]
113    fn test_decryptable_option_none() {
114        let store = test_store();
115        let mut ctx = store.context();
116
117        let key = TestSymmKey::A(0);
118        let none_data: Option<EncString> = None;
119        let string_decrypted: Option<String> = none_data.decrypt(&mut ctx, key).unwrap();
120        assert_eq!(string_decrypted, None);
121
122        // The None implementation will not do any decrypt operations, so it won't fail even if the
123        // key doesn't exist
124        let bad_key = TestSymmKey::B((0, 1));
125        let string_decrypted_bad: Option<String> = none_data.decrypt(&mut ctx, bad_key).unwrap();
126        assert_eq!(string_decrypted_bad, None);
127    }
128}