1
use std::collections::HashMap;
2

            
3
use serde::{Deserialize, Serialize};
4
use zbus::zvariant::Type;
5

            
6
use super::{Error, UnlockedItem};
7
use crate::{Key, Mac, crypto};
8

            
9
#[derive(Deserialize, Serialize, Type, Debug, Clone)]
10
pub(crate) struct EncryptedItem {
11
    pub(crate) hashed_attributes: HashMap<String, Mac>,
12
    #[serde(with = "serde_bytes")]
13
    pub(crate) blob: Vec<u8>,
14
}
15

            
16
impl EncryptedItem {
17
4
    pub fn has_attribute(&self, key: &str, value_mac: &Mac) -> bool {
18
4
        self.hashed_attributes.get(key) == Some(value_mac)
19
    }
20

            
21
4
    fn try_decrypt_inner(&self, key: &Key) -> Result<UnlockedItem, Error> {
22
4
        let n = self.blob.len();
23
4
        let n_mac = crypto::mac_len();
24
4
        let n_iv = crypto::iv_len();
25

            
26
        // The encrypted data, the iv, and the mac are concatenated into blob.
27
4
        let (encrypted_data_with_iv, mac_tag) = &self.blob.split_at(n - n_mac);
28

            
29
        // verify item
30
4
        if !crypto::verify_mac(encrypted_data_with_iv, key, mac_tag)? {
31
2
            return Err(Error::MacError);
32
        }
33

            
34
8
        let (encrypted_data, iv) = encrypted_data_with_iv.split_at(n - n_mac - n_iv);
35

            
36
        // decrypt item
37
4
        let decrypted = crypto::decrypt(encrypted_data, key, iv)?;
38

            
39
8
        let item = UnlockedItem::try_from(decrypted.as_slice())?;
40

            
41
8
        Self::validate(&self.hashed_attributes, &item, key)?;
42

            
43
4
        Ok(item)
44
    }
45

            
46
4
    pub fn is_valid(&self, key: &Key) -> bool {
47
4
        self.try_decrypt_inner(key).is_ok()
48
    }
49

            
50
4
    pub fn decrypt(self, key: &Key) -> Result<UnlockedItem, Error> {
51
4
        self.try_decrypt_inner(key)
52
    }
53

            
54
4
    fn validate(
55
        hashed_attributes: &HashMap<String, Mac>,
56
        item: &UnlockedItem,
57
        key: &Key,
58
    ) -> Result<(), Error> {
59
8
        for (attribute_key, hashed_attribute) in hashed_attributes.iter() {
60
8
            if let Some(attribute_plaintext) = item.attributes().get(attribute_key) {
61
4
                if !crypto::verify_mac(
62
4
                    attribute_plaintext.as_bytes(),
63
                    key,
64
4
                    hashed_attribute.as_slice(),
65
                )? {
66
                    return Err(Error::HashedAttributeMac(attribute_key.to_owned()));
67
                }
68
            } else {
69
                return Err(Error::HashedAttributeMac(attribute_key.to_owned()));
70
            }
71
        }
72

            
73
4
        Ok(())
74
    }
75
}