1
use std::collections::HashMap;
2

            
3
use serde::{Deserialize, Serialize};
4
use zbus::zvariant::Type;
5
use zeroize::{Zeroize, ZeroizeOnDrop};
6

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

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

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

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

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

            
31
        // verify item
32
18
        if !crypto::verify_mac(encrypted_data_with_iv, key, mac_tag)? {
33
6
            return Err(Error::MacError);
34
        }
35

            
36
36
        let (encrypted_data, iv) = encrypted_data_with_iv.split_at(n - n_mac - n_iv);
37

            
38
        // decrypt item
39
18
        let decrypted = crypto::decrypt(encrypted_data, key, iv)?;
40

            
41
38
        let item = UnlockedItem::try_from(decrypted.as_slice())?;
42

            
43
41
        Self::validate(&self.hashed_attributes, &item, key)?;
44

            
45
22
        Ok(item)
46
    }
47

            
48
17
    pub fn is_valid(&self, key: &Key) -> bool {
49
17
        self.try_decrypt_inner(key).is_ok()
50
    }
51

            
52
13
    pub fn decrypt(self, key: &Key) -> Result<UnlockedItem, Error> {
53
14
        self.try_decrypt_inner(key)
54
    }
55

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

            
75
23
        Ok(())
76
    }
77
}