Skip to main content

hessra_context_token/
verify.rs

1extern crate biscuit_auth as biscuit;
2
3use biscuit::Biscuit;
4use biscuit::macros::authorizer;
5use chrono::Utc;
6use hessra_token_core::{PublicKey, TokenError};
7
8/// Verifier for context tokens.
9///
10/// Checks that the context token is valid (not expired, properly signed).
11///
12/// # Example
13/// ```rust
14/// use hessra_context_token::{HessraContext, ContextVerifier};
15/// use hessra_token_core::{KeyPair, TokenTimeConfig};
16///
17/// let keypair = KeyPair::new();
18/// let public_key = keypair.public();
19///
20/// let token = HessraContext::new("agent:test".to_string(), TokenTimeConfig::default())
21///     .issue(&keypair)
22///     .expect("Failed to create context token");
23///
24/// ContextVerifier::new(token, public_key)
25///     .verify()
26///     .expect("Should verify");
27/// ```
28pub struct ContextVerifier {
29    token: String,
30    public_key: PublicKey,
31}
32
33impl ContextVerifier {
34    /// Creates a new context verifier.
35    ///
36    /// # Arguments
37    /// * `token` - The base64-encoded context token to verify
38    /// * `public_key` - The public key used to verify the token signature
39    pub fn new(token: String, public_key: PublicKey) -> Self {
40        Self { token, public_key }
41    }
42
43    /// Verify the context token.
44    ///
45    /// Checks that:
46    /// - The token signature is valid
47    /// - The token has not expired
48    ///
49    /// # Returns
50    /// * `Ok(())` - If the token is valid
51    /// * `Err(TokenError)` - If verification fails
52    pub fn verify(self) -> Result<(), TokenError> {
53        let biscuit = Biscuit::from_base64(&self.token, self.public_key)?;
54        let now = Utc::now().timestamp();
55
56        let authz = authorizer!(
57            r#"
58                time({now});
59                allow if true;
60            "#
61        );
62
63        authz
64            .build(&biscuit)
65            .map_err(|e| TokenError::internal(format!("failed to build authorizer: {e}")))?
66            .authorize()
67            .map_err(TokenError::from)?;
68
69        Ok(())
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use crate::mint::HessraContext;
77    use hessra_token_core::{KeyPair, TokenTimeConfig};
78
79    #[test]
80    fn test_verify_valid_token() {
81        let keypair = KeyPair::new();
82        let public_key = keypair.public();
83
84        let token = HessraContext::new("agent:test".to_string(), TokenTimeConfig::default())
85            .issue(&keypair)
86            .expect("Failed to create context token");
87
88        ContextVerifier::new(token, public_key)
89            .verify()
90            .expect("Should verify valid token");
91    }
92
93    #[test]
94    fn test_verify_expired_token() {
95        let keypair = KeyPair::new();
96        let public_key = keypair.public();
97
98        let expired_config = TokenTimeConfig {
99            start_time: Some(0),
100            duration: 1,
101        };
102
103        let token = HessraContext::new("agent:test".to_string(), expired_config)
104            .issue(&keypair)
105            .expect("Failed to create expired context token");
106
107        let result = ContextVerifier::new(token, public_key).verify();
108        assert!(result.is_err(), "Expired token should fail verification");
109    }
110
111    #[test]
112    fn test_verify_wrong_key() {
113        let keypair = KeyPair::new();
114        let wrong_keypair = KeyPair::new();
115        let wrong_public_key = wrong_keypair.public();
116
117        let token = HessraContext::new("agent:test".to_string(), TokenTimeConfig::default())
118            .issue(&keypair)
119            .expect("Failed to create context token");
120
121        let result = ContextVerifier::new(token, wrong_public_key).verify();
122        assert!(result.is_err(), "Token verified with wrong key should fail");
123    }
124
125    #[test]
126    fn test_verify_tainted_token() {
127        let keypair = KeyPair::new();
128        let public_key = keypair.public();
129
130        let token = HessraContext::new("agent:test".to_string(), TokenTimeConfig::default())
131            .issue(&keypair)
132            .expect("Failed to create context token");
133
134        // Add taint -- token should still verify
135        let tainted = crate::taint::add_taint(
136            &token,
137            public_key,
138            &["PII:SSN".to_string()],
139            "data:user-ssn".to_string(),
140        )
141        .expect("Failed to add taint");
142
143        ContextVerifier::new(tainted, public_key)
144            .verify()
145            .expect("Tainted token should still verify");
146    }
147}