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 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 encrypted_per_recipient: HashMap<RecipientID, EncrFromAnon<KeySecret>>,
63 },
64 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 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 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 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 }
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 }
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 }
423
424}