nucypher_core/
access_control.rs

1use alloc::boxed::Box;
2use alloc::string::String;
3
4use ferveo::api::{encrypt, Ciphertext, DkgPublicKey, SecretBox};
5use ferveo::Error;
6use serde::{Deserialize, Serialize};
7use umbral_pre::serde_bytes;
8
9use crate::conditions::Conditions;
10use crate::versioning::{
11    messagepack_deserialize, messagepack_serialize, ProtocolObject, ProtocolObjectInner,
12};
13
14/// Authenticated data for encrypted data.
15#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
16pub struct AuthenticatedData {
17    /// The public key for the encrypted data
18    pub public_key: DkgPublicKey,
19
20    /// The conditions associated with the encrypted data
21    pub conditions: Conditions,
22}
23
24impl Eq for AuthenticatedData {}
25
26impl AuthenticatedData {
27    /// Creates a new access control policy.
28    pub fn new(public_key: &DkgPublicKey, conditions: &Conditions) -> Self {
29        AuthenticatedData {
30            public_key: *public_key,
31            conditions: conditions.clone(),
32        }
33    }
34
35    /// Return the aad.
36    pub fn aad(&self) -> Result<Box<[u8]>, Error> {
37        Ok([
38            self.public_key.to_bytes()?.to_vec(),
39            self.conditions.as_ref().as_bytes().to_vec(),
40        ]
41        .concat()
42        .into_boxed_slice())
43    }
44}
45
46impl<'a> ProtocolObjectInner<'a> for AuthenticatedData {
47    fn version() -> (u16, u16) {
48        (1, 0)
49    }
50
51    fn brand() -> [u8; 4] {
52        *b"AuDa"
53    }
54
55    fn unversioned_to_bytes(&self) -> Box<[u8]> {
56        messagepack_serialize(&self)
57    }
58
59    fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
60        if minor_version == 0 {
61            Some(messagepack_deserialize(bytes))
62        } else {
63            None
64        }
65    }
66}
67
68impl<'a> ProtocolObject<'a> for AuthenticatedData {}
69
70/// Encrypt data based on conditions and dkg public key.
71pub fn encrypt_for_dkg(
72    data: &[u8],
73    public_key: &DkgPublicKey,
74    conditions: &Conditions,
75) -> Result<(Ciphertext, AuthenticatedData), Error> {
76    let auth_data = AuthenticatedData::new(public_key, conditions);
77    let ciphertext = encrypt(
78        SecretBox::new(data.to_vec()),
79        auth_data.aad()?.as_ref(),
80        public_key,
81    )?;
82    Ok((ciphertext, auth_data))
83}
84
85/// Access control policy data for encrypted data.
86#[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Clone)]
87pub struct AccessControlPolicy {
88    /// The authenticated data for the access control policy
89    pub auth_data: AuthenticatedData,
90
91    /// The authorization data for the authenticated data
92    #[serde(with = "serde_bytes::as_base64")]
93    pub authorization: Box<[u8]>,
94}
95
96impl AccessControlPolicy {
97    /// Creates a new access control policy.
98    pub fn new(auth_data: &AuthenticatedData, authorization: &[u8]) -> Self {
99        AccessControlPolicy {
100            auth_data: auth_data.clone(),
101            authorization: authorization.to_vec().into(),
102        }
103    }
104
105    /// Return the aad.
106    pub fn aad(&self) -> Result<Box<[u8]>, Error> {
107        self.auth_data.aad()
108    }
109
110    /// Return the DKG public key
111    pub fn public_key(&self) -> DkgPublicKey {
112        self.auth_data.public_key
113    }
114
115    /// Return the conditions
116    pub fn conditions(&self) -> Conditions {
117        self.auth_data.conditions.clone()
118    }
119}
120
121impl<'a> ProtocolObjectInner<'a> for AccessControlPolicy {
122    fn version() -> (u16, u16) {
123        (1, 0)
124    }
125
126    fn brand() -> [u8; 4] {
127        *b"ACPo"
128    }
129
130    fn unversioned_to_bytes(&self) -> Box<[u8]> {
131        messagepack_serialize(&self)
132    }
133
134    fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
135        if minor_version == 0 {
136            Some(messagepack_deserialize(bytes))
137        } else {
138            None
139        }
140    }
141}
142
143impl<'a> ProtocolObject<'a> for AccessControlPolicy {}
144
145#[cfg(test)]
146mod tests {
147    use crate::access_control::{AccessControlPolicy, AuthenticatedData};
148    use crate::conditions::Conditions;
149    use crate::test_utils::util::random_dkg_pubkey;
150    use crate::versioning::ProtocolObject;
151
152    #[test]
153    fn authenticated_data() {
154        let dkg_pk = random_dkg_pubkey();
155        let conditions = Conditions::new("abcd");
156
157        let auth_data = AuthenticatedData::new(&dkg_pk, &conditions);
158
159        // check aad for auth data; expected to be dkg public key + conditions
160        let mut expected_aad = dkg_pk.to_bytes().unwrap().to_vec();
161        expected_aad.extend(conditions.as_ref().as_bytes());
162        let auth_data_aad = auth_data.aad().unwrap();
163        assert_eq!(expected_aad.into_boxed_slice(), auth_data_aad);
164
165        assert_eq!(auth_data.public_key, dkg_pk);
166        assert_eq!(auth_data.conditions, conditions);
167
168        let auth_data_2 = AuthenticatedData::new(&dkg_pk, &conditions);
169        assert_eq!(auth_data, auth_data_2);
170
171        // mimic serialization/deserialization over the wire
172        let serialized_auth_data = auth_data.to_bytes();
173        let deserialized_auth_data = AuthenticatedData::from_bytes(&serialized_auth_data).unwrap();
174        assert_eq!(auth_data.public_key, deserialized_auth_data.public_key);
175        assert_eq!(auth_data.conditions, deserialized_auth_data.conditions);
176    }
177
178    #[test]
179    fn access_control_policy() {
180        let dkg_pk = random_dkg_pubkey();
181        let conditions = Conditions::new("abcd");
182
183        let auth_data = AuthenticatedData::new(&dkg_pk, &conditions);
184        let authorization = b"we_dont_need_no_stinking_badges";
185        let acp = AccessControlPolicy::new(&auth_data, authorization);
186
187        // check that aad for auth_data and acp are the same
188        assert_eq!(auth_data.aad().unwrap(), acp.aad().unwrap());
189
190        // mimic serialization/deserialization over the wire
191        let serialized_acp = acp.to_bytes();
192        let deserialized_acp = AccessControlPolicy::from_bytes(&serialized_acp).unwrap();
193        assert_eq!(auth_data.public_key, deserialized_acp.public_key());
194        assert_eq!(auth_data.conditions, deserialized_acp.conditions());
195        assert_eq!(
196            authorization.to_vec().into_boxed_slice(),
197            deserialized_acp.authorization
198        );
199    }
200}