use std::collections::HashMap;
use litl::impl_debug_as_litl;
use ridl::{
asymm_encr::{EncrFromAnon, RecipientID},
signing::SignerID,
symm_encr::{Encrypted, KeyID, KeySecret},
};
use serde_derive::{Deserialize, Serialize};
use ti64::MsSinceEpoch;
use crate::{ClaimID, ScopeID};
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct CredoV1Claim {
pub by: SignerID,
pub made_at: MsSinceEpoch,
#[serde(flatten)]
pub body: ClaimBody,
}
impl CredoV1Claim {
pub fn is_revocation_of(&self, revoked_id: &ClaimID, as_of: MsSinceEpoch) -> bool {
match &self.body {
ClaimBody::Revocation {
revoked_claim_id,
as_of: revocation_as_of,
} => revoked_claim_id == revoked_id && *revocation_as_of <= as_of,
_ => false,
}
}
}
pub const WRITE_TO_SCOPE: &str = "writeToScope";
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type")]
#[serde(rename_all = "camelCase")]
pub enum ClaimBody {
Statement {
path: String,
value: litl::Val,
},
AddSharedSecretRecipient {
secret_kind: String,
recipient: RecipientID,
},
Permission {
to: SignerID,
permitted: PermissionKind,
as_of: MsSinceEpoch,
},
Revocation {
revoked_claim_id: ClaimID,
as_of: MsSinceEpoch,
},
RevealSharedSecret {
secret_kind: String,
key_id: KeyID,
encrypted_per_recipient: HashMap<RecipientID, EncrFromAnon<KeySecret>>,
},
EntrustToSharedSecret {
secret_kind: String,
entrusted_secret_name: String,
for_key_id: KeyID,
encrypted: Encrypted<litl::Val>,
},
TimeWitness {},
InheritFrom {
parent: ScopeID,
},
}
impl_debug_as_litl!(ClaimBody);
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type")]
#[serde(rename_all = "camelCase")]
pub enum PermissionKind {
MakeStatement { path_prefix: String },
AddSharedSecretRecipient { secret_kind: String },
RevealSharedSecret { secret_kind: String },
EntrustToSharedSecret { secret_kind: String },
TimeWitness,
InheritFrom,
Delegate { delegated: Box<PermissionKind> },
DelegateInfinitely { delegated: Box<PermissionKind> },
}
impl PermissionKind {
pub fn permits_claim(&self, example: &ClaimBody) -> bool {
match (self, example) {
(PermissionKind::MakeStatement { path_prefix }, ClaimBody::Statement { path, .. }) => {
path.starts_with(path_prefix)
}
(
PermissionKind::AddSharedSecretRecipient {
secret_kind: permission_secret_kind,
},
ClaimBody::AddSharedSecretRecipient { secret_kind, .. },
) => permission_secret_kind == secret_kind,
(
PermissionKind::RevealSharedSecret {
secret_kind: permission_secret_kind,
},
ClaimBody::RevealSharedSecret { secret_kind, .. },
) => permission_secret_kind == secret_kind,
(
PermissionKind::EntrustToSharedSecret {
secret_kind: permission_secret_kind,
},
ClaimBody::EntrustToSharedSecret { secret_kind, .. },
) => permission_secret_kind == secret_kind,
(PermissionKind::TimeWitness, ClaimBody::TimeWitness {}) => true,
(PermissionKind::InheritFrom, ClaimBody::InheritFrom { .. }) => true,
(
PermissionKind::Delegate {
delegated: delegated_permission,
}
| PermissionKind::DelegateInfinitely {
delegated: delegated_permission,
},
ClaimBody::Permission {
permitted: permitted_permission,
..
},
) => match permitted_permission {
PermissionKind::Delegate {delegated: other_delegated_permission} => {
other_delegated_permission.is_stricter_than_or_equal_to(delegated_permission)
}
simple_permitted_permission => simple_permitted_permission.is_stricter_than_or_equal_to(delegated_permission),
}
_ => false,
}
}
pub fn is_stricter_than_or_equal_to(&self, other: &PermissionKind) -> bool {
match (self, other) {
(
PermissionKind::MakeStatement { path_prefix },
PermissionKind::MakeStatement {
path_prefix: other_path_prefix,
},
) => path_prefix.starts_with(other_path_prefix),
(
PermissionKind::AddSharedSecretRecipient { secret_kind },
PermissionKind::AddSharedSecretRecipient {
secret_kind: secret_kind_other,
},
) => secret_kind == secret_kind_other,
(
PermissionKind::RevealSharedSecret { secret_kind },
PermissionKind::RevealSharedSecret {
secret_kind: secret_kind_other,
},
) => secret_kind == secret_kind_other,
(
PermissionKind::EntrustToSharedSecret { secret_kind },
PermissionKind::EntrustToSharedSecret {
secret_kind: secret_kind_other,
},
) => secret_kind == secret_kind_other,
(PermissionKind::TimeWitness, PermissionKind::TimeWitness) => true,
(PermissionKind::InheritFrom, PermissionKind::InheritFrom) => true,
_ => false,
}
}
}
#[cfg(test)]
mod test {
use std::collections::HashMap;
use ridl::{signing::SignerSecret, asymm_encr::RecipientSecret, symm_encr::KeySecret};
use caro::ObjectID;
use crate::ClaimID;
use super::{ClaimBody, PermissionKind};
#[test]
fn permissions_permit_same_but_dont_permit_different_claim_type() {
let statement_permission = PermissionKind::MakeStatement {
path_prefix: "foo".to_string(),
};
let add_shared_secret_recp_permission = PermissionKind::AddSharedSecretRecipient {
secret_kind: "foo".to_string(),
};
let reveal_shared_secret_permission = PermissionKind::RevealSharedSecret {
secret_kind: "foo".to_string(),
};
let entrust_to_shared_secret_permission = PermissionKind::EntrustToSharedSecret {
secret_kind: "foo".to_string(),
};
let time_witness_permission = PermissionKind::TimeWitness;
let inherit_from_permission = PermissionKind::InheritFrom;
let statement_claim = ClaimBody::Statement {
path: "foo".to_string(),
value: litl::Val::bool(true),
};
let add_shared_secret_recp_claim = ClaimBody::AddSharedSecretRecipient {
secret_kind: "foo".to_string(),
recipient: ridl::asymm_encr::RecipientSecret::new_random().pub_id(),
};
let permission_claim = ClaimBody::Permission {
to: SignerSecret::new_random().pub_id(),
permitted: PermissionKind::TimeWitness,
as_of: ti64::now(),
};
let revocation_claim = ClaimBody::Revocation {
revoked_claim_id: ClaimID::test_random(),
as_of: ti64::now(),
};
let reveal_shared_secret_claim = ClaimBody::RevealSharedSecret {
secret_kind: "foo".to_string(),
key_id: ridl::symm_encr::KeySecret::new_random().id,
encrypted_per_recipient: HashMap::new(),
};
let key_secret = ridl::symm_encr::KeySecret::new_random();
let entrust_to_shared_secret_claim = ClaimBody::EntrustToSharedSecret {
secret_kind: "foo".to_string(),
entrusted_secret_name: "foo".to_string(),
for_key_id: key_secret.id,
encrypted: key_secret.encrypt(
&litl::Val::bool(true),
),
};
let time_witness_claim = ClaimBody::TimeWitness {};
let inherit_from_claim = ClaimBody::InheritFrom {
parent: crate::ScopeID(ObjectID::test_random()),
};
assert!(statement_permission.permits_claim(&statement_claim));
assert!(add_shared_secret_recp_permission.permits_claim(&add_shared_secret_recp_claim));
assert!(reveal_shared_secret_permission.permits_claim(&reveal_shared_secret_claim));
assert!(entrust_to_shared_secret_permission.permits_claim(&entrust_to_shared_secret_claim));
assert!(time_witness_permission.permits_claim(&time_witness_claim));
assert!(inherit_from_permission.permits_claim(&inherit_from_claim));
assert!(!statement_permission.permits_claim(&add_shared_secret_recp_claim));
assert!(!statement_permission.permits_claim(&permission_claim));
assert!(!statement_permission.permits_claim(&revocation_claim));
assert!(!statement_permission.permits_claim(&reveal_shared_secret_claim));
assert!(!statement_permission.permits_claim(&entrust_to_shared_secret_claim));
assert!(!statement_permission.permits_claim(&time_witness_claim));
assert!(!statement_permission.permits_claim(&inherit_from_claim));
assert!(!add_shared_secret_recp_permission.permits_claim(&statement_claim));
assert!(!add_shared_secret_recp_permission.permits_claim(&permission_claim));
assert!(!add_shared_secret_recp_permission.permits_claim(&revocation_claim));
assert!(!add_shared_secret_recp_permission.permits_claim(&reveal_shared_secret_claim));
assert!(!add_shared_secret_recp_permission.permits_claim(&entrust_to_shared_secret_claim));
assert!(!add_shared_secret_recp_permission.permits_claim(&time_witness_claim));
assert!(!add_shared_secret_recp_permission.permits_claim(&inherit_from_claim));
assert!(!reveal_shared_secret_permission.permits_claim(&statement_claim));
assert!(!reveal_shared_secret_permission.permits_claim(&add_shared_secret_recp_claim));
assert!(!reveal_shared_secret_permission.permits_claim(&permission_claim));
assert!(!reveal_shared_secret_permission.permits_claim(&revocation_claim));
assert!(!reveal_shared_secret_permission.permits_claim(&entrust_to_shared_secret_claim));
assert!(!reveal_shared_secret_permission.permits_claim(&time_witness_claim));
assert!(!reveal_shared_secret_permission.permits_claim(&inherit_from_claim));
assert!(!entrust_to_shared_secret_permission.permits_claim(&statement_claim));
assert!(!entrust_to_shared_secret_permission.permits_claim(&add_shared_secret_recp_claim));
assert!(!entrust_to_shared_secret_permission.permits_claim(&permission_claim));
assert!(!entrust_to_shared_secret_permission.permits_claim(&revocation_claim));
assert!(!entrust_to_shared_secret_permission.permits_claim(&reveal_shared_secret_claim));
assert!(!entrust_to_shared_secret_permission.permits_claim(&time_witness_claim));
assert!(!entrust_to_shared_secret_permission.permits_claim(&inherit_from_claim));
assert!(!time_witness_permission.permits_claim(&statement_claim));
assert!(!time_witness_permission.permits_claim(&add_shared_secret_recp_claim));
assert!(!time_witness_permission.permits_claim(&permission_claim));
assert!(!time_witness_permission.permits_claim(&revocation_claim));
assert!(!time_witness_permission.permits_claim(&reveal_shared_secret_claim));
assert!(!time_witness_permission.permits_claim(&entrust_to_shared_secret_claim));
assert!(!time_witness_permission.permits_claim(&inherit_from_claim));
assert!(!inherit_from_permission.permits_claim(&statement_claim));
assert!(!inherit_from_permission.permits_claim(&add_shared_secret_recp_claim));
assert!(!inherit_from_permission.permits_claim(&permission_claim));
assert!(!inherit_from_permission.permits_claim(&revocation_claim));
assert!(!inherit_from_permission.permits_claim(&reveal_shared_secret_claim));
assert!(!inherit_from_permission.permits_claim(&entrust_to_shared_secret_claim));
assert!(!inherit_from_permission.permits_claim(&time_witness_claim));
}
#[test]
fn statement_permissions_should_only_permit_correct_prefix() {
let statement_permission = PermissionKind::MakeStatement {
path_prefix: "foo".to_string(),
};
let statement_claim = ClaimBody::Statement {
path: "foo".to_string(),
value: litl::Val::bool(true),
};
let statement_claim_wrong_prefix = ClaimBody::Statement {
path: "bar".to_string(),
value: litl::Val::bool(true),
};
assert!(statement_permission.permits_claim(&statement_claim));
assert!(!statement_permission.permits_claim(&statement_claim_wrong_prefix));
}
#[test]
fn adding_shared_secret_recp_only_permits_same_secret_kind() {
let add_shared_secret_recp_permission = PermissionKind::AddSharedSecretRecipient {
secret_kind: "foo".to_string(),
};
let add_shared_secret_recp_claim = ClaimBody::AddSharedSecretRecipient {
secret_kind: "foo".to_string(),
recipient: RecipientSecret::new_random().pub_id()
};
let add_shared_secret_recp_claim_wrong_kind = ClaimBody::AddSharedSecretRecipient {
secret_kind: "bar".to_string(),
recipient: RecipientSecret::new_random().pub_id()
};
assert!(add_shared_secret_recp_permission.permits_claim(&add_shared_secret_recp_claim));
assert!(!add_shared_secret_recp_permission.permits_claim(&add_shared_secret_recp_claim_wrong_kind));
}
#[test]
fn entrust_to_shared_secret_only_permits_same_secret_kind() {
let entrust_to_shared_secret_permission = PermissionKind::EntrustToSharedSecret {
secret_kind: "foo".to_string(),
};
let key_secret = KeySecret::new_random();
let entrust_to_shared_secret_claim = ClaimBody::EntrustToSharedSecret {
secret_kind: "foo".to_string(),
entrusted_secret_name: "bar".to_string(),
encrypted: key_secret.encrypt(&litl::Val::bool(true)),
for_key_id: key_secret.id,
};
let entrust_to_shared_secret_claim_wrong_kind = ClaimBody::EntrustToSharedSecret {
secret_kind: "bar".to_string(),
entrusted_secret_name: "bar".to_string(),
encrypted: key_secret.encrypt(&litl::Val::bool(true)),
for_key_id: key_secret.id,
};
assert!(entrust_to_shared_secret_permission.permits_claim(&entrust_to_shared_secret_claim));
assert!(!entrust_to_shared_secret_permission.permits_claim(&entrust_to_shared_secret_claim_wrong_kind));
}
#[test]
fn simple_delegate_permits_permissions() {
let delegation_permission = PermissionKind::Delegate { delegated: Box::new(PermissionKind::MakeStatement { path_prefix: "foo".to_string() }) };
let permission_claim = ClaimBody::Permission{
permitted: PermissionKind::MakeStatement { path_prefix: "foo".to_string() },
to: SignerSecret::new_random().pub_id(),
as_of: ti64::now(),
};
assert!(delegation_permission.permits_claim(&permission_claim));
}
#[test]
fn delegate_infinitely_permits_permission() {
let infinite_delegation_permission = PermissionKind::DelegateInfinitely { delegated: Box::new(PermissionKind::MakeStatement { path_prefix: "foo".to_string() }) };
let permission_claim = ClaimBody::Permission{
permitted: PermissionKind::MakeStatement { path_prefix: "foo".to_string() },
to: SignerSecret::new_random().pub_id(),
as_of: ti64::now(),
};
assert!(infinite_delegation_permission.permits_claim(&permission_claim));
}
#[test]
fn delegate_infinitely_permits_delegation() {
let infinite_delegation_permission = PermissionKind::DelegateInfinitely { delegated: Box::new(PermissionKind::MakeStatement { path_prefix: "foo".to_string() }) };
let delegation_permission = PermissionKind::Delegate { delegated: Box::new(PermissionKind::MakeStatement { path_prefix: "foo".to_string() }) };
let permission_claim = ClaimBody::Permission{
permitted: delegation_permission,
to: SignerSecret::new_random().pub_id(),
as_of: ti64::now(),
};
assert!(infinite_delegation_permission.permits_claim(&permission_claim));
}
}