Skip to main content

agentstategraph_policy/
verifier.rs

1//! Signature verifier trait for `PolicyStore`.
2//!
3//! This trait lives in the main `agentstategraph-policy` crate (rather
4//! than in `agentstategraph-policy-sign`) so the policy crate does not
5//! need to pull in crypto dependencies to hold the hook. §2c will ship
6//! a concrete `Ed25519SignatureVerifier` implementation that composes
7//! the `-sign` crate's `Ed25519Verifier` + canonical-JSON helper.
8//!
9//! Implementations are expected to:
10//!
11//! 1. Compute the canonical-JSON bytes of the policy with the
12//!    `signature` field excluded (via
13//!    `agentstategraph_policy_sign::canonicalize` or equivalent).
14//! 2. Unpack the policy's `signature` field into
15//!    `(signer_key_id, raw_bytes)` and call their verifier.
16
17use thiserror::Error;
18
19use crate::types::Policy;
20
21/// Verify a policy's embedded signature.
22///
23/// Implementations are expected to compute canonical bytes via the
24/// `agentstategraph-policy-sign` crate.
25pub trait SignatureVerifier: Send + Sync {
26    /// Verify the signature on `policy`. Returns `Ok(())` on a valid
27    /// signature; any rejection surfaces as an error.
28    fn verify_policy(&self, policy: &Policy) -> Result<(), SignatureVerificationError>;
29}
30
31/// Why a signature was rejected.
32#[derive(Debug, Error)]
33pub enum SignatureVerificationError {
34    /// The policy carries no signature at all. Only fatal when the
35    /// caller treats unsigned policies as invalid (e.g. server config
36    /// `require_signed_policies=true`); the policy crate itself maps
37    /// this to "treat as not-currently-active" in that mode.
38    #[error("policy has no signature")]
39    Missing,
40
41    /// The signature algorithm tag is not supported by this verifier.
42    #[error("unsupported algorithm")]
43    UnsupportedAlgorithm,
44
45    /// The signature payload (length, hex encoding, key id) is
46    /// malformed.
47    #[error("invalid signature length or encoding")]
48    Encoding,
49
50    /// Cryptographic verification rejected the signature.
51    #[error("signature rejected: {0}")]
52    Invalid(String),
53
54    /// Any other failure — key-not-found, I/O, canonicalization, etc.
55    #[error(transparent)]
56    Other(#[from] Box<dyn std::error::Error + Send + Sync>),
57}