1use std::collections::HashMap;
4
5#[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 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 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#[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#[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 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}