Skip to main content

agent_identity/
lib.rs

1//! Agent identity, key management, and trust verification.
2
3use std::collections::HashMap;
4
5/// An agent's cryptographic identity.
6#[derive(Debug, Clone)]
7pub struct AgentIdentity {
8    pub agent_id: String,
9    pub public_key: Vec<u8>,
10    pub created_at: u64,
11    pub expires_at: Option<u64>,
12    pub roles: Vec<String>,
13    pub metadata: HashMap<String, String>,
14}
15
16impl AgentIdentity {
17    pub fn new(agent_id: &str, public_key: Vec<u8>) -> Self {
18        Self {
19            agent_id: agent_id.to_string(),
20            public_key,
21            created_at: 0,
22            expires_at: None,
23            roles: vec![],
24            metadata: HashMap::new(),
25        }
26    }
27
28    pub fn with_role(mut self, role: &str) -> Self {
29        self.roles.push(role.to_string());
30        self
31    }
32    pub fn with_expiry(mut self, ts: u64) -> Self {
33        self.expires_at = Some(ts);
34        self
35    }
36
37    pub fn is_expired(&self, now: u64) -> bool {
38        self.expires_at.is_some_and(|exp| now > exp)
39    }
40
41    pub fn has_role(&self, role: &str) -> bool {
42        self.roles.iter().any(|r| r == role)
43    }
44
45    /// Simple key fingerprint (hex of first 8 bytes).
46    pub fn fingerprint(&self) -> String {
47        self.public_key
48            .iter()
49            .take(8)
50            .map(|b| format!("{:02x}", b))
51            .collect::<Vec<_>>()
52            .join(":")
53    }
54
55    /// Verify this identity has required roles.
56    pub fn verify_roles(&self, required: &[&str]) -> Result<(), Vec<String>> {
57        let missing: Vec<String> = required
58            .iter()
59            .filter(|&&r| !self.has_role(r))
60            .map(|&r| r.to_string())
61            .collect();
62        if missing.is_empty() {
63            Ok(())
64        } else {
65            Err(missing)
66        }
67    }
68}
69
70/// A trust store for managing known agent identities.
71#[derive(Debug, Clone)]
72pub struct TrustStore {
73    identities: HashMap<String, AgentIdentity>,
74    revoked: Vec<String>,
75}
76
77impl Default for TrustStore {
78    fn default() -> Self {
79        Self::new()
80    }
81}
82
83impl TrustStore {
84    pub fn new() -> Self {
85        Self {
86            identities: HashMap::new(),
87            revoked: vec![],
88        }
89    }
90
91    pub fn register(&mut self, identity: AgentIdentity) {
92        self.identities.insert(identity.agent_id.clone(), identity);
93    }
94
95    pub fn revoke(&mut self, agent_id: &str) {
96        self.revoked.push(agent_id.to_string());
97    }
98
99    pub fn is_trusted(&self, agent_id: &str) -> bool {
100        !self.revoked.contains(&agent_id.to_string()) && self.identities.contains_key(agent_id)
101    }
102
103    pub fn get(&self, agent_id: &str) -> Option<&AgentIdentity> {
104        self.identities.get(agent_id)
105    }
106
107    pub fn verify(&self, agent_id: &str, now: u64) -> VerificationResult {
108        if self.revoked.contains(&agent_id.to_string()) {
109            return VerificationResult::Revoked;
110        }
111        match self.identities.get(agent_id) {
112            None => VerificationResult::Unknown,
113            Some(id) if id.is_expired(now) => VerificationResult::Expired,
114            Some(_) => VerificationResult::Trusted,
115        }
116    }
117
118    pub fn len(&self) -> usize {
119        self.identities.len()
120    }
121    pub fn is_empty(&self) -> bool {
122        self.identities.is_empty()
123    }
124}
125
126#[derive(Debug, Clone, Copy, PartialEq)]
127pub enum VerificationResult {
128    Trusted,
129    Unknown,
130    Expired,
131    Revoked,
132}
133
134/// A challenge-response token for authentication.
135#[derive(Debug, Clone)]
136pub struct AuthToken {
137    pub agent_id: String,
138    pub challenge: Vec<u8>,
139    pub response: Vec<u8>,
140    pub timestamp: u64,
141}
142
143impl AuthToken {
144    pub fn new(agent_id: &str, challenge: Vec<u8>, response: Vec<u8>, timestamp: u64) -> Self {
145        Self {
146            agent_id: agent_id.to_string(),
147            challenge,
148            response,
149            timestamp,
150        }
151    }
152
153    /// Simple verification: check response matches expected.
154    pub fn verify_response(&self, expected: &[u8]) -> bool {
155        self.response == expected
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162
163    #[test]
164    fn test_create_identity() {
165        let id = AgentIdentity::new("agent-1", vec![1, 2, 3, 4, 5, 6, 7, 8]).with_role("admin");
166        assert_eq!(id.agent_id, "agent-1");
167        assert!(id.has_role("admin"));
168        assert!(!id.has_role("user"));
169    }
170
171    #[test]
172    fn test_fingerprint() {
173        let id = AgentIdentity::new(
174            "agent-1",
175            vec![0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89],
176        );
177        assert_eq!(id.fingerprint(), "ab:cd:ef:01:23:45:67:89");
178    }
179
180    #[test]
181    fn test_expiry() {
182        let id = AgentIdentity::new("agent-1", vec![1, 2, 3]).with_expiry(1000);
183        assert!(id.is_expired(1001));
184        assert!(!id.is_expired(999));
185    }
186
187    #[test]
188    fn test_verify_roles() {
189        let id = AgentIdentity::new("agent-1", vec![1, 2, 3])
190            .with_role("read")
191            .with_role("write");
192        assert!(id.verify_roles(&["read"]).is_ok());
193        assert!(id.verify_roles(&["read", "write"]).is_ok());
194        assert!(id.verify_roles(&["admin"]).is_err());
195    }
196
197    #[test]
198    fn test_trust_store() {
199        let mut store = TrustStore::new();
200        let id = AgentIdentity::new("agent-1", vec![1, 2, 3]);
201        store.register(id);
202        assert!(store.is_trusted("agent-1"));
203        assert!(!store.is_trusted("unknown"));
204    }
205
206    #[test]
207    fn test_revoke() {
208        let mut store = TrustStore::new();
209        store.register(AgentIdentity::new("agent-1", vec![1, 2, 3]));
210        store.revoke("agent-1");
211        assert!(!store.is_trusted("agent-1"));
212    }
213
214    #[test]
215    fn test_verification() {
216        let mut store = TrustStore::new();
217        store.register(AgentIdentity::new("agent-1", vec![1, 2, 3]).with_expiry(1000));
218        assert_eq!(store.verify("agent-1", 500), VerificationResult::Trusted);
219        assert_eq!(store.verify("agent-1", 1001), VerificationResult::Expired);
220        assert_eq!(store.verify("unknown", 500), VerificationResult::Unknown);
221    }
222
223    #[test]
224    fn test_auth_token() {
225        let token = AuthToken::new("agent-1", vec![1, 2, 3], vec![4, 5, 6], 0);
226        assert!(token.verify_response(&[4, 5, 6]));
227        assert!(!token.verify_response(&[7, 8, 9]));
228    }
229}