use base64ct::{Base64UrlUnpadded, Encoding};
use chrono::{Duration, Utc};
use delegated::{DelegationTokenBuilder, KIND_DELEGATION_TOKEN, SPEC_VERSION_CURRENT, envelope};
use ed25519_dalek::{SigningKey, VerifyingKey};
use serde::Serialize;
use serde_json::{Value, json};
const EVALUATE_AT: &str = "2026-07-01T12:00:00Z";
const ISSUER: &str = "https://issuer.example";
const KEY_ID: &str = "issuer-1";
fn evaluate_at() -> chrono::DateTime<Utc> {
EVALUATE_AT.parse().unwrap()
}
fn signing_key() -> SigningKey {
SigningKey::from_bytes(&[0x42; 32])
}
fn public_key_b64(key: &VerifyingKey) -> String {
Base64UrlUnpadded::encode_string(key.as_bytes())
}
fn signed_envelope(
key: &SigningKey,
token_id: &str,
nonce: &str,
issued_at: chrono::DateTime<Utc>,
expires_at: chrono::DateTime<Utc>,
request_id: Option<&str>,
) -> Value {
let token = DelegationTokenBuilder::new()
.token_id(token_id)
.issuer(ISSUER)
.agent_id("agent:scheduler")
.delegator_id("user:alice")
.audience("calendar-api")
.allowed_action("calendar.create")
.allowed_resource("calendar:alice")
.max_delegation_depth(0)
.issued_at(issued_at)
.expires_at(expires_at)
.nonce(nonce)
.key_id(KEY_ID)
.build_and_sign(key)
.expect("fixture token must build");
serde_json::to_value(envelope(token, request_id.map(str::to_owned))).unwrap()
}
#[derive(Serialize)]
struct Manifest {
spec_version: &'static str,
description: &'static str,
evaluate_at: &'static str,
signing_key: SigningKeyInfo,
trusted_issuer: &'static str,
trusted_key_id: &'static str,
vectors: Vec<Vector>,
}
#[derive(Serialize)]
struct SigningKeyInfo {
algorithm: &'static str,
seed_hex: &'static str,
public_key_b64url: String,
}
#[derive(Serialize)]
struct Vector {
id: &'static str,
description: &'static str,
operation: OperationSpec,
#[serde(skip_serializing_if = "Option::is_none")]
trust_state: Option<TrustStateSpec>,
#[serde(skip_serializing_if = "Option::is_none")]
steps: Option<Vec<StepSpec>>,
#[serde(skip_serializing_if = "Option::is_none")]
envelope: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
raw_request: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
expected: Option<ExpectedSpec>,
}
#[derive(Serialize, Clone)]
struct OperationSpec {
audience: &'static str,
action: &'static str,
#[serde(skip_serializing_if = "Option::is_none")]
resource: Option<&'static str>,
#[serde(skip_serializing_if = "Option::is_none")]
delegation_depth: Option<u16>,
}
#[derive(Serialize)]
struct TrustStateSpec {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
revoke_tokens: Vec<[&'static str; 2]>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
deny_agents: Vec<[&'static str; 2]>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
preconsume_nonces: Vec<[&'static str; 2]>,
}
#[derive(Serialize)]
struct StepSpec {
#[serde(skip_serializing_if = "Option::is_none")]
envelope: Option<Value>,
expected: ExpectedSpec,
}
#[derive(Serialize, Clone)]
struct ExpectedSpec {
allowed: bool,
stage: &'static str,
#[serde(skip_serializing_if = "Option::is_none")]
reason_contains: Option<&'static str>,
}
fn main() {
let key = signing_key();
let now = evaluate_at();
let operation = OperationSpec {
audience: "calendar-api",
action: "calendar.create",
resource: Some("calendar:alice"),
delegation_depth: Some(0),
};
let allow_envelope = signed_envelope(
&key,
"token-allow",
"nonce-allow-00000001",
now - Duration::minutes(1),
now + Duration::minutes(10),
Some("req-allow"),
);
let mut tampered = allow_envelope.clone();
tampered["delegation_token"]["agent_id"] = json!("agent:attacker");
let expired = signed_envelope(
&key,
"token-expired",
"nonce-expired-000001",
now - Duration::hours(2),
now - Duration::hours(1),
None,
);
let future = signed_envelope(
&key,
"token-future",
"nonce-future-0000001",
now + Duration::hours(1),
now + Duration::hours(2),
None,
);
let too_long = signed_envelope(
&key,
"token-too-long",
"nonce-too-long-00001",
now - Duration::minutes(1),
now + Duration::hours(25),
None,
);
let wrong_version = {
let mut envelope = signed_envelope(
&key,
"token-bad-version",
"nonce-bad-version-01",
now - Duration::minutes(1),
now + Duration::minutes(10),
None,
);
envelope["spec_version"] = json!("0.1");
envelope
};
let unknown_field = {
let mut envelope = allow_envelope.clone();
envelope["unexpected"] = json!("value");
envelope
};
let manifest = Manifest {
spec_version: SPEC_VERSION_CURRENT,
description: "Delegated 0.2 reference vectors for evaluator conformance",
evaluate_at: EVALUATE_AT,
signing_key: SigningKeyInfo {
algorithm: "Ed25519",
seed_hex: "4242424242424242424242424242424242424242424242424242424242424242",
public_key_b64url: public_key_b64(&key.verifying_key()),
},
trusted_issuer: ISSUER,
trusted_key_id: KEY_ID,
vectors: vec![
Vector {
id: "allow-basic",
description: "Valid capability authorizes the matching host operation",
operation: operation.clone(),
trust_state: None,
steps: None,
envelope: Some(allow_envelope.clone()),
raw_request: None,
expected: Some(ExpectedSpec {
allowed: true,
stage: "authorized",
reason_contains: None,
}),
},
Vector {
id: "deny-untrusted-issuer",
description: "Verifier without a pinned issuer key denies",
operation: operation.clone(),
trust_state: None,
steps: None,
envelope: Some(allow_envelope.clone()),
raw_request: None,
expected: Some(ExpectedSpec {
allowed: false,
stage: "verify_issuer",
reason_contains: Some("not trusted"),
}),
},
Vector {
id: "deny-tampered-claims",
description: "Modified claims fail signature verification",
operation: operation.clone(),
trust_state: None,
steps: None,
envelope: Some(tampered),
raw_request: None,
expected: Some(ExpectedSpec {
allowed: false,
stage: "verify_issuer",
reason_contains: Some("signature"),
}),
},
Vector {
id: "deny-expired",
description: "Expired capabilities are rejected",
operation: operation.clone(),
trust_state: None,
steps: None,
envelope: Some(expired),
raw_request: None,
expected: Some(ExpectedSpec {
allowed: false,
stage: "validate_lifetime",
reason_contains: Some("expired"),
}),
},
Vector {
id: "deny-not-yet-active",
description: "Future-dated capabilities are rejected",
operation: operation.clone(),
trust_state: None,
steps: None,
envelope: Some(future),
raw_request: None,
expected: Some(ExpectedSpec {
allowed: false,
stage: "validate_lifetime",
reason_contains: Some("not active"),
}),
},
Vector {
id: "deny-lifetime-too-long",
description: "Capabilities exceeding max lifetime are rejected",
operation: operation.clone(),
trust_state: None,
steps: None,
envelope: Some(too_long),
raw_request: None,
expected: Some(ExpectedSpec {
allowed: false,
stage: "validate_lifetime",
reason_contains: Some("lifetime"),
}),
},
Vector {
id: "deny-wrong-action",
description: "Host operation action must match token allowlist",
operation: OperationSpec {
action: "calendar.delete",
..operation.clone()
},
trust_state: None,
steps: None,
envelope: Some(allow_envelope.clone()),
raw_request: None,
expected: Some(ExpectedSpec {
allowed: false,
stage: "bind_operation",
reason_contains: Some("action"),
}),
},
Vector {
id: "deny-missing-resource",
description: "Constrained tokens require a host resource",
operation: OperationSpec {
resource: None,
..operation.clone()
},
trust_state: None,
steps: None,
envelope: Some(allow_envelope.clone()),
raw_request: None,
expected: Some(ExpectedSpec {
allowed: false,
stage: "bind_operation",
reason_contains: Some("resource is required"),
}),
},
Vector {
id: "deny-revoked-token",
description: "Revoked token identifiers are denied",
operation: operation.clone(),
trust_state: Some(TrustStateSpec {
revoke_tokens: vec![[ISSUER, "token-allow"]],
deny_agents: vec![],
preconsume_nonces: vec![],
}),
steps: None,
envelope: Some(allow_envelope.clone()),
raw_request: None,
expected: Some(ExpectedSpec {
allowed: false,
stage: "revocation",
reason_contains: Some("revoked"),
}),
},
Vector {
id: "deny-denied-agent",
description: "Emergency-denied agents are rejected",
operation: operation.clone(),
trust_state: Some(TrustStateSpec {
revoke_tokens: vec![],
deny_agents: vec![[ISSUER, "agent:scheduler"]],
preconsume_nonces: vec![],
}),
steps: None,
envelope: Some(allow_envelope.clone()),
raw_request: None,
expected: Some(ExpectedSpec {
allowed: false,
stage: "revocation",
reason_contains: Some("denied"),
}),
},
Vector {
id: "deny-replay",
description: "Second use of the same nonce is rejected",
operation: operation.clone(),
trust_state: None,
steps: Some(vec![
StepSpec {
envelope: Some(allow_envelope.clone()),
expected: ExpectedSpec {
allowed: true,
stage: "authorized",
reason_contains: None,
},
},
StepSpec {
envelope: Some(allow_envelope.clone()),
expected: ExpectedSpec {
allowed: false,
stage: "replay",
reason_contains: Some("nonce"),
},
},
]),
envelope: None,
raw_request: None,
expected: None,
},
Vector {
id: "deny-unknown-fields",
description: "Unknown envelope fields are rejected",
operation: operation.clone(),
trust_state: None,
steps: None,
envelope: Some(unknown_field),
raw_request: None,
expected: Some(ExpectedSpec {
allowed: false,
stage: "normalize_request",
reason_contains: None,
}),
},
Vector {
id: "deny-unsupported-version",
description: "Unsupported spec versions are rejected",
operation: operation.clone(),
trust_state: None,
steps: None,
envelope: Some(wrong_version),
raw_request: None,
expected: Some(ExpectedSpec {
allowed: false,
stage: "normalize_request",
reason_contains: Some("spec_version"),
}),
},
Vector {
id: "deny-oversized-envelope",
description: "Oversized requests are rejected before parsing",
operation: operation.clone(),
trust_state: None,
steps: None,
envelope: None,
raw_request: Some(" ".repeat(delegated::DEFAULT_MAX_ENVELOPE_BYTES + 1)),
expected: Some(ExpectedSpec {
allowed: false,
stage: "normalize_request",
reason_contains: Some("byte limit"),
}),
},
Vector {
id: "deny-invalid-token-kind",
description: "Wrong token kind is rejected during normalization",
operation,
trust_state: None,
steps: None,
envelope: Some({
let mut envelope = allow_envelope;
envelope["delegation_token"]["kind"] = json!(KIND_DELEGATION_TOKEN);
envelope["delegation_token"]["kind"] = json!("WrongCapability");
envelope
}),
raw_request: None,
expected: Some(ExpectedSpec {
allowed: false,
stage: "normalize_request",
reason_contains: Some("kind"),
}),
},
],
};
println!(
"{}",
serde_json::to_string_pretty(&manifest).expect("manifest must serialize")
);
}