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
    pub(crate) blob: Vec<u8>,
13
}
14

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

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

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

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

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

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

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

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

            
42
4
        Ok(item)
43
    }
44

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

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

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

            
72
4
        Ok(())
73
    }
74
}