use crate::error::{IdentityError, Result};
use super::capability::capabilities_cover;
use super::grant::{TrustGrant, TrustId};
use super::revocation::Revocation;
use super::verify::TrustVerification;
pub fn verify_trust_chain(
chain: &[TrustGrant],
requested_capability: &str,
revocations: &[Revocation],
) -> Result<TrustVerification> {
let now = crate::time::now_micros();
if chain.is_empty() {
return Err(IdentityError::InvalidChain);
}
let mut trust_chain_ids: Vec<TrustId> = Vec::new();
let mut all_valid = true;
let mut sig_valid = true;
let mut time_valid = true;
let mut not_revoked = true;
let mut cap_granted = true;
for (i, grant) in chain.iter().enumerate() {
trust_chain_ids.push(grant.id.clone());
if grant.verify_signature().is_err() {
sig_valid = false;
all_valid = false;
}
if !grant.constraints.is_time_valid(now) {
time_valid = false;
all_valid = false;
}
if revocations.iter().any(|r| r.trust_id == grant.id) {
not_revoked = false;
all_valid = false;
}
if !capabilities_cover(&grant.capabilities, requested_capability) {
cap_granted = false;
all_valid = false;
}
if i > 0 {
let parent = &chain[i - 1];
if !parent.delegation_allowed {
return Err(IdentityError::DelegationNotAllowed);
}
if let Some(max_depth) = parent.max_delegation_depth {
if grant.delegation_depth > max_depth {
return Err(IdentityError::DelegationDepthExceeded);
}
}
if grant.grantor != parent.grantee {
return Err(IdentityError::InvalidChain);
}
}
}
Ok(TrustVerification {
signature_valid: sig_valid,
time_valid,
not_revoked,
uses_valid: true, capability_granted: cap_granted,
trust_chain: trust_chain_ids,
is_valid: all_valid,
verified_at: now,
})
}
pub fn validate_delegation(
parent_grant: &TrustGrant,
requested_capabilities: &[super::capability::Capability],
) -> Result<()> {
if !parent_grant.delegation_allowed {
return Err(IdentityError::DelegationNotAllowed);
}
let next_depth = parent_grant.delegation_depth + 1;
if let Some(max_depth) = parent_grant.max_delegation_depth {
if next_depth > max_depth {
return Err(IdentityError::DelegationDepthExceeded);
}
}
for cap in requested_capabilities {
if !capabilities_cover(&parent_grant.capabilities, &cap.uri) {
return Err(IdentityError::TrustNotGranted(format!(
"parent grant does not cover capability: {}",
cap.uri
)));
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::identity::IdentityAnchor;
use crate::trust::capability::Capability;
use crate::trust::grant::TrustGrantBuilder;
use crate::trust::revocation::{Revocation, RevocationReason};
fn make_key_b64(anchor: &IdentityAnchor) -> String {
base64::Engine::encode(
&base64::engine::general_purpose::STANDARD,
anchor.verifying_key_bytes(),
)
}
#[test]
fn test_single_grant_chain() {
let a = IdentityAnchor::new(None);
let b = IdentityAnchor::new(None);
let grant = TrustGrantBuilder::new(a.id(), b.id(), make_key_b64(&b))
.capability(Capability::new("read:calendar"))
.sign(a.signing_key())
.unwrap();
let result = verify_trust_chain(&[grant], "read:calendar", &[]).unwrap();
assert!(result.is_valid);
assert_eq!(result.trust_chain.len(), 1);
}
#[test]
fn test_delegation_chain_a_b_c() {
let a = IdentityAnchor::new(None);
let b = IdentityAnchor::new(None);
let c = IdentityAnchor::new(None);
let ab = TrustGrantBuilder::new(a.id(), b.id(), make_key_b64(&b))
.capability(Capability::new("read:*"))
.allow_delegation(2)
.sign(a.signing_key())
.unwrap();
let bc = TrustGrantBuilder::new(b.id(), c.id(), make_key_b64(&c))
.capability(Capability::new("read:calendar"))
.delegated_from(ab.id.clone(), 1)
.sign(b.signing_key())
.unwrap();
let result = verify_trust_chain(&[ab, bc], "read:calendar", &[]).unwrap();
assert!(result.is_valid);
assert_eq!(result.trust_chain.len(), 2);
}
#[test]
fn test_delegation_not_allowed() {
let a = IdentityAnchor::new(None);
let b = IdentityAnchor::new(None);
let c = IdentityAnchor::new(None);
let ab = TrustGrantBuilder::new(a.id(), b.id(), make_key_b64(&b))
.capability(Capability::new("read:*"))
.sign(a.signing_key())
.unwrap();
let bc = TrustGrantBuilder::new(b.id(), c.id(), make_key_b64(&c))
.capability(Capability::new("read:calendar"))
.delegated_from(ab.id.clone(), 1)
.sign(b.signing_key())
.unwrap();
let result = verify_trust_chain(&[ab, bc], "read:calendar", &[]);
assert!(matches!(result, Err(IdentityError::DelegationNotAllowed)));
}
#[test]
fn test_delegation_depth_exceeded() {
let a = IdentityAnchor::new(None);
let b = IdentityAnchor::new(None);
let c = IdentityAnchor::new(None);
let d = IdentityAnchor::new(None);
let ab = TrustGrantBuilder::new(a.id(), b.id(), make_key_b64(&b))
.capability(Capability::new("read:*"))
.allow_delegation(1)
.sign(a.signing_key())
.unwrap();
let bc = TrustGrantBuilder::new(b.id(), c.id(), make_key_b64(&c))
.capability(Capability::new("read:*"))
.allow_delegation(1)
.delegated_from(ab.id.clone(), 1)
.sign(b.signing_key())
.unwrap();
let cd = TrustGrantBuilder::new(c.id(), d.id(), make_key_b64(&d))
.capability(Capability::new("read:calendar"))
.delegated_from(bc.id.clone(), 2)
.sign(c.signing_key())
.unwrap();
let result = verify_trust_chain(&[ab, bc, cd], "read:calendar", &[]);
assert!(matches!(
result,
Err(IdentityError::DelegationDepthExceeded)
));
}
#[test]
fn test_revoked_link_invalidates_chain() {
let a = IdentityAnchor::new(None);
let b = IdentityAnchor::new(None);
let c = IdentityAnchor::new(None);
let ab = TrustGrantBuilder::new(a.id(), b.id(), make_key_b64(&b))
.capability(Capability::new("read:*"))
.allow_delegation(2)
.sign(a.signing_key())
.unwrap();
let bc = TrustGrantBuilder::new(b.id(), c.id(), make_key_b64(&c))
.capability(Capability::new("read:calendar"))
.delegated_from(ab.id.clone(), 1)
.sign(b.signing_key())
.unwrap();
let revocation = Revocation::create(
ab.id.clone(),
a.id(),
RevocationReason::ManualRevocation,
a.signing_key(),
);
let result = verify_trust_chain(&[ab, bc], "read:calendar", &[revocation]).unwrap();
assert!(!result.not_revoked);
assert!(!result.is_valid);
}
#[test]
fn test_validate_delegation_ok() {
let a = IdentityAnchor::new(None);
let b = IdentityAnchor::new(None);
let grant = TrustGrantBuilder::new(a.id(), b.id(), make_key_b64(&b))
.capability(Capability::new("read:*"))
.allow_delegation(2)
.sign(a.signing_key())
.unwrap();
let caps = vec![Capability::new("read:calendar")];
assert!(validate_delegation(&grant, &caps).is_ok());
}
#[test]
fn test_validate_delegation_capability_not_covered() {
let a = IdentityAnchor::new(None);
let b = IdentityAnchor::new(None);
let grant = TrustGrantBuilder::new(a.id(), b.id(), make_key_b64(&b))
.capability(Capability::new("read:calendar"))
.allow_delegation(2)
.sign(a.signing_key())
.unwrap();
let caps = vec![Capability::new("write:calendar")];
assert!(validate_delegation(&grant, &caps).is_err());
}
#[test]
fn test_empty_chain_error() {
let result = verify_trust_chain(&[], "read:calendar", &[]);
assert!(matches!(result, Err(IdentityError::InvalidChain)));
}
}