use chrono::Utc;
use std::time::Duration;
use tenuo::{
constraints::ConstraintSet,
crypto::SigningKey,
planes::{ControlPlane, DataPlane},
revocation::{RevocationRequest, SignedRevocationList},
revocation_manager::RevocationManager,
warrant::Warrant,
Error,
};
#[test]
fn test_single_warrant_revocation() {
let kp = SigningKey::generate();
let warrant = Warrant::builder()
.capability("test", ConstraintSet::new())
.ttl(Duration::from_secs(600))
.holder(kp.public_key())
.build(&kp)
.unwrap();
let mut data_plane = DataPlane::new();
data_plane.trust_issuer("root", kp.public_key());
assert!(data_plane
.verify_chain(std::slice::from_ref(&warrant))
.is_ok());
let srl = SignedRevocationList::builder()
.revoke(warrant.id().to_string())
.version(1)
.build(&kp)
.unwrap();
data_plane
.set_revocation_list(srl, &kp.public_key())
.unwrap();
let warrant_id = warrant.id().to_string();
match data_plane.verify_chain(&[warrant]) {
Err(Error::WarrantRevoked(id)) => assert_eq!(id, warrant_id),
res => panic!("Expected WarrantRevoked, got {:?}", res),
}
}
#[test]
fn test_chain_revocation_child() {
let root_kp = SigningKey::generate();
let child_kp = SigningKey::generate();
let grandchild_kp = SigningKey::generate();
let root = Warrant::builder()
.capability("test", ConstraintSet::new())
.ttl(Duration::from_secs(600))
.holder(child_kp.public_key())
.build(&root_kp)
.unwrap();
let child = root
.attenuate()
.inherit_all()
.holder(grandchild_kp.public_key())
.build(&child_kp) .unwrap();
let mut data_plane = DataPlane::new();
data_plane.trust_issuer("root", root_kp.public_key());
assert!(data_plane
.verify_chain(&[root.clone(), child.clone()])
.is_ok());
let srl = SignedRevocationList::builder()
.revoke(child.id().to_string())
.version(1)
.build(&root_kp)
.unwrap();
data_plane
.set_revocation_list(srl, &root_kp.public_key())
.unwrap();
match data_plane.verify_chain(&[root.clone(), child.clone()]) {
Err(Error::WarrantRevoked(id)) => assert_eq!(id, child.id().to_string()),
res => panic!("Expected WarrantRevoked, got {:?}", res),
}
}
#[test]
fn test_chain_revocation_parent_cascades() {
let root_kp = SigningKey::generate();
let child_kp = SigningKey::generate();
let grandchild_kp = SigningKey::generate();
let root = Warrant::builder()
.capability("test", ConstraintSet::new())
.ttl(Duration::from_secs(600))
.holder(child_kp.public_key())
.build(&root_kp)
.unwrap();
let child = root
.attenuate()
.inherit_all()
.holder(grandchild_kp.public_key())
.build(&child_kp) .unwrap();
let mut data_plane = DataPlane::new();
data_plane.trust_issuer("root", root_kp.public_key());
let srl = SignedRevocationList::builder()
.revoke(root.id().to_string())
.version(1)
.build(&root_kp)
.unwrap();
data_plane
.set_revocation_list(srl, &root_kp.public_key())
.unwrap();
match data_plane.verify_chain(&[root.clone(), child.clone()]) {
Err(Error::WarrantRevoked(id)) => assert_eq!(id, root.id().to_string()),
res => panic!("Expected WarrantRevoked, got {:?}", res),
}
}
#[test]
fn test_cascading_revocation_multiple_warrants() {
let cp_keypair = SigningKey::generate();
let issuer_keypair = SigningKey::generate();
let control_plane = ControlPlane::new(cp_keypair.clone());
let mut data_plane = DataPlane::new();
data_plane.trust_issuer("control-plane", control_plane.public_key());
data_plane.trust_issuer("issuer", issuer_keypair.public_key());
let warrant1 = Warrant::builder()
.capability("test_tool_1", ConstraintSet::new())
.ttl(Duration::from_secs(3600))
.holder(issuer_keypair.public_key())
.build(&issuer_keypair)
.unwrap();
let warrant2 = Warrant::builder()
.capability("test_tool_2", ConstraintSet::new())
.ttl(Duration::from_secs(3600))
.holder(issuer_keypair.public_key())
.build(&issuer_keypair)
.unwrap();
assert!(data_plane
.verify_chain(std::slice::from_ref(&warrant1))
.is_ok());
assert!(data_plane
.verify_chain(std::slice::from_ref(&warrant2))
.is_ok());
let affected_ids = vec![warrant1.id().to_string(), warrant2.id().to_string()];
let manager = RevocationManager::new();
let srl = manager
.generate_srl_with_cascade(&cp_keypair, 1, &affected_ids)
.unwrap();
data_plane
.set_revocation_list(srl, &control_plane.public_key())
.unwrap();
assert!(matches!(
data_plane.verify_chain(&[warrant1]),
Err(Error::WarrantRevoked(_))
));
assert!(matches!(
data_plane.verify_chain(&[warrant2]),
Err(Error::WarrantRevoked(_))
));
}
#[test]
fn test_revocation_request_flow() {
let cp_keypair = SigningKey::generate();
let issuer_keypair = SigningKey::generate();
let mut manager = RevocationManager::new();
let request =
RevocationRequest::new("warrant_to_revoke", "key compromised", &issuer_keypair).unwrap();
manager
.submit_request(
request,
"warrant_to_revoke",
&issuer_keypair.public_key(),
None,
Utc::now() + chrono::Duration::hours(1),
&cp_keypair.public_key(),
)
.unwrap();
let srl = manager.generate_srl(&cp_keypair, 1).unwrap();
assert!(srl.is_revoked("warrant_to_revoke"));
assert!(!srl.is_revoked("other_warrant"));
}