1
use std::sync::Arc;
2

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

            
7
use super::Session;
8
use crate::{Key, Secret, crypto, dbus::Error, secret::ContentType};
9

            
10
#[derive(Debug, Serialize, Deserialize, Type)]
11
#[zvariant(signature = "(oayays)")]
12
/// Same as [`DBusSecret`] without tying the session path to a [`Session`] type.
13
pub struct DBusSecretInner(
14
    pub OwnedObjectPath,
15
    pub Vec<u8>,
16
    pub Vec<u8>,
17
    pub ContentType,
18
);
19

            
20
#[derive(Debug, Type, Zeroize, ZeroizeOnDrop)]
21
#[zvariant(signature = "(oayays)")]
22
pub struct DBusSecret<'a> {
23
    #[zeroize(skip)]
24
    pub(crate) session: Arc<Session<'a>>,
25
    pub(crate) parameters: Vec<u8>,
26
    pub(crate) value: Vec<u8>,
27
    #[zeroize(skip)]
28
    pub(crate) content_type: ContentType,
29
}
30

            
31
impl<'a> DBusSecret<'a> {
32
    /// Create a new plain (unencrypted) DBusSecret
33
4
    pub fn new(session: Arc<Session<'a>>, secret: impl Into<Secret>) -> Self {
34
4
        let secret = secret.into();
35
        Self {
36
            session,
37
4
            parameters: vec![],
38
8
            value: secret.as_bytes().to_vec(),
39
4
            content_type: secret.content_type(),
40
        }
41
    }
42

            
43
    /// Create a new encrypted DBusSecret
44
6
    pub fn new_encrypted(
45
        session: Arc<Session<'a>>,
46
        secret: impl Into<Secret>,
47
        aes_key: &Key,
48
    ) -> Result<Self, crate::dbus::Error> {
49
12
        let iv = crypto::generate_iv()?;
50
6
        let secret = secret.into();
51
6
        Ok(Self {
52
6
            session,
53
12
            value: crypto::encrypt(secret.as_bytes(), aes_key, &iv)?,
54
6
            parameters: iv,
55
6
            content_type: secret.content_type(),
56
        })
57
    }
58

            
59
4
    pub(crate) async fn from_inner(
60
        cnx: &zbus::Connection,
61
        inner: DBusSecretInner,
62
    ) -> Result<Self, Error> {
63
4
        Ok(Self {
64
8
            session: Arc::new(Session::new(cnx, inner.0).await?),
65
4
            parameters: inner.1,
66
4
            value: inner.2,
67
4
            content_type: inner.3,
68
        })
69
    }
70

            
71
4
    pub fn decrypt(&self, key: Option<&Arc<Key>>) -> Result<Secret, Error> {
72
4
        let value = match key {
73
12
            Some(key) => &crypto::decrypt(&self.value, key, &self.parameters)?,
74
6
            None => &self.value,
75
        };
76
8
        Ok(Secret::with_content_type(self.content_type, value))
77
    }
78

            
79
    /// Session used to encode the secret
80
4
    pub fn session(&self) -> &Session<'_> {
81
4
        &self.session
82
    }
83

            
84
    /// Algorithm dependent parameters for secret value encoding
85
4
    pub fn parameters(&self) -> &[u8] {
86
4
        &self.parameters
87
    }
88

            
89
    /// Possibly encoded secret value
90
4
    pub fn value(&self) -> &[u8] {
91
4
        &self.value
92
    }
93

            
94
    /// Content type of the secret
95
4
    pub fn content_type(&self) -> ContentType {
96
4
        self.content_type
97
    }
98
}
99

            
100
impl Serialize for DBusSecret<'_> {
101
9
    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
102
    where
103
        S: serde::Serializer,
104
    {
105
9
        let mut tuple_serializer = serializer.serialize_tuple(4)?;
106
18
        tuple_serializer.serialize_element(self.session().inner().path())?;
107
8
        tuple_serializer.serialize_element(self.parameters())?;
108
8
        tuple_serializer.serialize_element(self.value())?;
109
8
        tuple_serializer.serialize_element(self.content_type().as_str())?;
110
8
        tuple_serializer.end()
111
    }
112
}
113

            
114
#[cfg(test)]
115
mod tests {
116
    use super::*;
117

            
118
    #[test]
119
    fn signature() {
120
        assert_eq!(DBusSecret::SIGNATURE, "(oayays)");
121
    }
122
}