credo/
claims_and_permissions_v1.rs

1use std::collections::HashMap;
2
3use litl::impl_debug_as_litl;
4use ridl::{
5    asymm_encr::{EncrFromAnon, RecipientID},
6    signing::SignerID,
7    symm_encr::{Encrypted, KeyID, KeySecret},
8};
9use serde_derive::{Deserialize, Serialize};
10use ti64::MsSinceEpoch;
11
12use crate::{ClaimID, ScopeID};
13
14#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
15pub struct CredoV1Claim {
16    pub by: SignerID,
17    // TODO: get rid of this completely and use only time witnesses instead
18    pub made_at: MsSinceEpoch,
19    #[serde(flatten)]
20    pub body: ClaimBody,
21}
22
23impl CredoV1Claim {
24    pub fn is_revocation_of(&self, revoked_id: &ClaimID, as_of: MsSinceEpoch) -> bool {
25        match &self.body {
26            ClaimBody::Revocation {
27                revoked_claim_id,
28                as_of: revocation_as_of,
29            } => revoked_claim_id == revoked_id && *revocation_as_of <= as_of,
30            _ => false,
31        }
32    }
33}
34
35pub const WRITE_TO_SCOPE: &str = "writeToScope";
36
37#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
38#[serde(tag = "type")]
39#[serde(rename_all = "camelCase")]
40pub enum ClaimBody {
41    Statement {
42        path: String,
43        value: litl::Val,
44    },
45    AddSharedSecretRecipient {
46        secret_kind: String,
47        recipient: RecipientID,
48    },
49    Permission {
50        to: SignerID,
51        permitted: PermissionKind,
52        as_of: MsSinceEpoch,
53    },
54    Revocation {
55        revoked_claim_id: ClaimID,
56        as_of: MsSinceEpoch,
57    },
58    RevealSharedSecret {
59        secret_kind: String,
60        key_id: KeyID,
61        // TODO: this is a bit redundant, EncrFromAnon already has recipient in it
62        encrypted_per_recipient: HashMap<RecipientID, EncrFromAnon<KeySecret>>,
63    },
64    // TODO: the wording around this is very confusing, fix before #21
65    EntrustToSharedSecret {
66        secret_kind: String,
67        entrusted_secret_name: String,
68        for_key_id: KeyID,
69        encrypted: Encrypted<litl::Val>,
70    },
71    TimeWitness {},
72    InheritFrom {
73        parent: ScopeID,
74    },
75}
76
77impl_debug_as_litl!(ClaimBody);
78
79#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
80#[serde(tag = "type")]
81#[serde(rename_all = "camelCase")]
82pub enum PermissionKind {
83    MakeStatement { path_prefix: String },
84    AddSharedSecretRecipient { secret_kind: String },
85    RevealSharedSecret { secret_kind: String },
86    EntrustToSharedSecret { secret_kind: String },
87    TimeWitness,
88    // TODO: limit to specific parent scope?
89    InheritFrom,
90    Delegate { delegated: Box<PermissionKind> },
91    DelegateInfinitely { delegated: Box<PermissionKind> },
92}
93
94impl PermissionKind {
95    pub fn permits_claim(&self, example: &ClaimBody) -> bool {
96        match (self, example) {
97            (PermissionKind::MakeStatement { path_prefix }, ClaimBody::Statement { path, .. }) => {
98                path.starts_with(path_prefix)
99            }
100            (
101                PermissionKind::AddSharedSecretRecipient {
102                    secret_kind: permission_secret_kind,
103                },
104                ClaimBody::AddSharedSecretRecipient { secret_kind, .. },
105            ) => permission_secret_kind == secret_kind,
106            (
107                PermissionKind::RevealSharedSecret {
108                    secret_kind: permission_secret_kind,
109                },
110                ClaimBody::RevealSharedSecret { secret_kind, .. },
111            ) => permission_secret_kind == secret_kind,
112            (
113                PermissionKind::EntrustToSharedSecret {
114                    secret_kind: permission_secret_kind,
115                },
116                ClaimBody::EntrustToSharedSecret { secret_kind, .. },
117            ) => permission_secret_kind == secret_kind,
118            (PermissionKind::TimeWitness, ClaimBody::TimeWitness {}) => true,
119            (PermissionKind::InheritFrom, ClaimBody::InheritFrom { .. }) => true,
120            (
121                PermissionKind::Delegate {
122                    delegated: delegated_permission,
123                }
124                | PermissionKind::DelegateInfinitely {
125                    delegated: delegated_permission,
126                },
127                ClaimBody::Permission {
128                    permitted: permitted_permission,
129                    ..
130                },
131            ) => match permitted_permission {
132                PermissionKind::Delegate {delegated: other_delegated_permission} => {
133                    other_delegated_permission.is_stricter_than_or_equal_to(delegated_permission)
134                }
135                simple_permitted_permission => simple_permitted_permission.is_stricter_than_or_equal_to(delegated_permission),
136            }
137            _ => false,
138        }
139    }
140
141    pub fn is_stricter_than_or_equal_to(&self, other: &PermissionKind) -> bool {
142        match (self, other) {
143            (
144                PermissionKind::MakeStatement { path_prefix },
145                PermissionKind::MakeStatement {
146                    path_prefix: other_path_prefix,
147                },
148            ) => path_prefix.starts_with(other_path_prefix),
149            (
150                PermissionKind::AddSharedSecretRecipient { secret_kind },
151                PermissionKind::AddSharedSecretRecipient {
152                    secret_kind: secret_kind_other,
153                },
154            ) => secret_kind == secret_kind_other,
155            (
156                PermissionKind::RevealSharedSecret { secret_kind },
157                PermissionKind::RevealSharedSecret {
158                    secret_kind: secret_kind_other,
159                },
160            ) => secret_kind == secret_kind_other,
161            (
162                PermissionKind::EntrustToSharedSecret { secret_kind },
163                PermissionKind::EntrustToSharedSecret {
164                    secret_kind: secret_kind_other,
165                },
166            ) => secret_kind == secret_kind_other,
167            (PermissionKind::TimeWitness, PermissionKind::TimeWitness) => true,
168            (PermissionKind::InheritFrom, PermissionKind::InheritFrom) => true,
169            _ => false,
170        }
171    }
172}
173
174#[cfg(test)]
175mod test {
176    use std::collections::HashMap;
177
178    use ridl::{signing::SignerSecret, asymm_encr::RecipientSecret, symm_encr::KeySecret};
179    use caro::ObjectID;
180
181    use crate::ClaimID;
182
183    use super::{ClaimBody, PermissionKind};
184
185    #[test]
186    fn permissions_permit_same_but_dont_permit_different_claim_type() {
187        let statement_permission = PermissionKind::MakeStatement {
188            path_prefix: "foo".to_string(),
189        };
190
191        let add_shared_secret_recp_permission = PermissionKind::AddSharedSecretRecipient {
192            secret_kind: "foo".to_string(),
193        };
194
195        let reveal_shared_secret_permission = PermissionKind::RevealSharedSecret {
196            secret_kind: "foo".to_string(),
197        };
198
199        let entrust_to_shared_secret_permission = PermissionKind::EntrustToSharedSecret {
200            secret_kind: "foo".to_string(),
201        };
202
203        let time_witness_permission = PermissionKind::TimeWitness;
204
205        let inherit_from_permission = PermissionKind::InheritFrom;
206
207        let statement_claim = ClaimBody::Statement {
208            path: "foo".to_string(),
209            value: litl::Val::bool(true),
210        };
211
212        let add_shared_secret_recp_claim = ClaimBody::AddSharedSecretRecipient {
213            secret_kind: "foo".to_string(),
214            recipient: ridl::asymm_encr::RecipientSecret::new_random().pub_id(),
215        };
216
217        let permission_claim = ClaimBody::Permission {
218            to: SignerSecret::new_random().pub_id(),
219            permitted: PermissionKind::TimeWitness,
220            as_of: ti64::now(),
221        };
222
223        let revocation_claim = ClaimBody::Revocation {
224            revoked_claim_id: ClaimID::test_random(),
225            as_of: ti64::now(),
226        };
227
228        let reveal_shared_secret_claim = ClaimBody::RevealSharedSecret {
229            secret_kind: "foo".to_string(),
230            key_id: ridl::symm_encr::KeySecret::new_random().id,
231            encrypted_per_recipient: HashMap::new(),
232        };
233
234        let key_secret = ridl::symm_encr::KeySecret::new_random();
235
236        let entrust_to_shared_secret_claim = ClaimBody::EntrustToSharedSecret {
237            secret_kind: "foo".to_string(),
238            entrusted_secret_name: "foo".to_string(),
239            for_key_id: key_secret.id,
240            encrypted: key_secret.encrypt(
241                &litl::Val::bool(true),
242            ),
243        };
244
245        let time_witness_claim = ClaimBody::TimeWitness {};
246
247        let inherit_from_claim = ClaimBody::InheritFrom {
248            parent: crate::ScopeID(ObjectID::test_random()),
249        };
250
251        // should permit
252
253        assert!(statement_permission.permits_claim(&statement_claim));
254        assert!(add_shared_secret_recp_permission.permits_claim(&add_shared_secret_recp_claim));
255        assert!(reveal_shared_secret_permission.permits_claim(&reveal_shared_secret_claim));
256        assert!(entrust_to_shared_secret_permission.permits_claim(&entrust_to_shared_secret_claim));
257        assert!(time_witness_permission.permits_claim(&time_witness_claim));
258        assert!(inherit_from_permission.permits_claim(&inherit_from_claim));
259
260        // should not permit
261
262        assert!(!statement_permission.permits_claim(&add_shared_secret_recp_claim));
263        assert!(!statement_permission.permits_claim(&permission_claim));
264        assert!(!statement_permission.permits_claim(&revocation_claim));
265        assert!(!statement_permission.permits_claim(&reveal_shared_secret_claim));
266        assert!(!statement_permission.permits_claim(&entrust_to_shared_secret_claim));
267        assert!(!statement_permission.permits_claim(&time_witness_claim));
268        assert!(!statement_permission.permits_claim(&inherit_from_claim));
269
270        assert!(!add_shared_secret_recp_permission.permits_claim(&statement_claim));
271        assert!(!add_shared_secret_recp_permission.permits_claim(&permission_claim));
272        assert!(!add_shared_secret_recp_permission.permits_claim(&revocation_claim));
273        assert!(!add_shared_secret_recp_permission.permits_claim(&reveal_shared_secret_claim));
274        assert!(!add_shared_secret_recp_permission.permits_claim(&entrust_to_shared_secret_claim));
275        assert!(!add_shared_secret_recp_permission.permits_claim(&time_witness_claim));
276        assert!(!add_shared_secret_recp_permission.permits_claim(&inherit_from_claim));
277
278        assert!(!reveal_shared_secret_permission.permits_claim(&statement_claim));
279        assert!(!reveal_shared_secret_permission.permits_claim(&add_shared_secret_recp_claim));
280        assert!(!reveal_shared_secret_permission.permits_claim(&permission_claim));
281        assert!(!reveal_shared_secret_permission.permits_claim(&revocation_claim));
282        assert!(!reveal_shared_secret_permission.permits_claim(&entrust_to_shared_secret_claim));
283        assert!(!reveal_shared_secret_permission.permits_claim(&time_witness_claim));
284        assert!(!reveal_shared_secret_permission.permits_claim(&inherit_from_claim));
285
286        assert!(!entrust_to_shared_secret_permission.permits_claim(&statement_claim));
287        assert!(!entrust_to_shared_secret_permission.permits_claim(&add_shared_secret_recp_claim));
288        assert!(!entrust_to_shared_secret_permission.permits_claim(&permission_claim));
289        assert!(!entrust_to_shared_secret_permission.permits_claim(&revocation_claim));
290        assert!(!entrust_to_shared_secret_permission.permits_claim(&reveal_shared_secret_claim));
291        assert!(!entrust_to_shared_secret_permission.permits_claim(&time_witness_claim));
292        assert!(!entrust_to_shared_secret_permission.permits_claim(&inherit_from_claim));
293
294        assert!(!time_witness_permission.permits_claim(&statement_claim));
295        assert!(!time_witness_permission.permits_claim(&add_shared_secret_recp_claim));
296        assert!(!time_witness_permission.permits_claim(&permission_claim));
297        assert!(!time_witness_permission.permits_claim(&revocation_claim));
298        assert!(!time_witness_permission.permits_claim(&reveal_shared_secret_claim));
299        assert!(!time_witness_permission.permits_claim(&entrust_to_shared_secret_claim));
300        assert!(!time_witness_permission.permits_claim(&inherit_from_claim));
301
302        assert!(!inherit_from_permission.permits_claim(&statement_claim));
303        assert!(!inherit_from_permission.permits_claim(&add_shared_secret_recp_claim));
304        assert!(!inherit_from_permission.permits_claim(&permission_claim));
305        assert!(!inherit_from_permission.permits_claim(&revocation_claim));
306        assert!(!inherit_from_permission.permits_claim(&reveal_shared_secret_claim));
307        assert!(!inherit_from_permission.permits_claim(&entrust_to_shared_secret_claim));
308        assert!(!inherit_from_permission.permits_claim(&time_witness_claim));
309    }
310
311    #[test]
312    fn statement_permissions_should_only_permit_correct_prefix() {
313        let statement_permission = PermissionKind::MakeStatement {
314            path_prefix: "foo".to_string(),
315        };
316
317        let statement_claim = ClaimBody::Statement {
318            path: "foo".to_string(),
319            value: litl::Val::bool(true),
320        };
321
322        let statement_claim_wrong_prefix = ClaimBody::Statement {
323            path: "bar".to_string(),
324            value: litl::Val::bool(true),
325        };
326
327        assert!(statement_permission.permits_claim(&statement_claim));
328        assert!(!statement_permission.permits_claim(&statement_claim_wrong_prefix));
329    }
330
331    #[test]
332    fn adding_shared_secret_recp_only_permits_same_secret_kind() {
333        let add_shared_secret_recp_permission = PermissionKind::AddSharedSecretRecipient {
334            secret_kind: "foo".to_string(),
335        };
336
337        let add_shared_secret_recp_claim = ClaimBody::AddSharedSecretRecipient {
338            secret_kind: "foo".to_string(),
339            recipient: RecipientSecret::new_random().pub_id()
340        };
341
342        let add_shared_secret_recp_claim_wrong_kind = ClaimBody::AddSharedSecretRecipient {
343            secret_kind: "bar".to_string(),
344            recipient: RecipientSecret::new_random().pub_id()
345        };
346
347        assert!(add_shared_secret_recp_permission.permits_claim(&add_shared_secret_recp_claim));
348        assert!(!add_shared_secret_recp_permission.permits_claim(&add_shared_secret_recp_claim_wrong_kind));
349    }
350
351    #[test]
352    fn entrust_to_shared_secret_only_permits_same_secret_kind() {
353        let entrust_to_shared_secret_permission = PermissionKind::EntrustToSharedSecret {
354            secret_kind: "foo".to_string(),
355        };
356
357        let key_secret = KeySecret::new_random();
358
359        let entrust_to_shared_secret_claim = ClaimBody::EntrustToSharedSecret {
360            secret_kind: "foo".to_string(),
361            entrusted_secret_name: "bar".to_string(),
362            encrypted: key_secret.encrypt(&litl::Val::bool(true)),
363            for_key_id: key_secret.id,
364        };
365
366        let entrust_to_shared_secret_claim_wrong_kind = ClaimBody::EntrustToSharedSecret {
367            secret_kind: "bar".to_string(),
368            entrusted_secret_name: "bar".to_string(),
369            encrypted: key_secret.encrypt(&litl::Val::bool(true)),
370            for_key_id: key_secret.id,
371        };
372
373        assert!(entrust_to_shared_secret_permission.permits_claim(&entrust_to_shared_secret_claim));
374        assert!(!entrust_to_shared_secret_permission.permits_claim(&entrust_to_shared_secret_claim_wrong_kind));
375    }
376
377    #[test]
378    fn simple_delegate_permits_permissions() {
379        let delegation_permission = PermissionKind::Delegate { delegated: Box::new(PermissionKind::MakeStatement { path_prefix: "foo".to_string() }) };
380
381        let permission_claim = ClaimBody::Permission{
382            permitted: PermissionKind::MakeStatement { path_prefix: "foo".to_string() },
383            to: SignerSecret::new_random().pub_id(),
384            as_of: ti64::now(),
385        };
386
387        assert!(delegation_permission.permits_claim(&permission_claim));
388
389        // TODO: add more permission cases?
390        // TODO: add tests for correct strictness
391    }
392
393    #[test]
394    fn delegate_infinitely_permits_permission() {
395        let infinite_delegation_permission = PermissionKind::DelegateInfinitely { delegated: Box::new(PermissionKind::MakeStatement { path_prefix: "foo".to_string() }) };
396
397        let permission_claim = ClaimBody::Permission{
398            permitted: PermissionKind::MakeStatement { path_prefix: "foo".to_string() },
399            to: SignerSecret::new_random().pub_id(),
400            as_of: ti64::now(),
401        };
402
403        assert!(infinite_delegation_permission.permits_claim(&permission_claim));
404        // TODO: add tests for correct strictness
405    }
406
407    #[test]
408    fn delegate_infinitely_permits_delegation() {
409        let infinite_delegation_permission = PermissionKind::DelegateInfinitely { delegated: Box::new(PermissionKind::MakeStatement { path_prefix: "foo".to_string() }) };
410
411        let delegation_permission = PermissionKind::Delegate { delegated: Box::new(PermissionKind::MakeStatement { path_prefix: "foo".to_string() }) };
412
413        let permission_claim = ClaimBody::Permission{
414            permitted: delegation_permission,
415            to: SignerSecret::new_random().pub_id(),
416            as_of: ti64::now(),
417        };
418
419        assert!(infinite_delegation_permission.permits_claim(&permission_claim));
420
421        // TODO: add tests for correct strictness
422    }
423
424}