1use ed25519_dalek::{Signer, SigningKey, Verifier, VerifyingKey};
7use rand::rngs::OsRng;
8use serde::{Deserialize, Serialize};
9
10pub const MAX_DELEGATION_DEPTH: u32 = 10;
12
13#[derive(Debug)]
15pub struct AgentIdentity {
16 pub did: String,
18 pub public_key: VerifyingKey,
20 pub capabilities: Vec<String>,
22 pub parent_did: Option<String>,
24 pub delegation_depth: u32,
26 signing_key: SigningKey,
27}
28
29impl AgentIdentity {
30 pub fn generate(agent_id: &str, capabilities: Vec<String>) -> Result<Self, IdentityError> {
32 let signing_key = SigningKey::generate(&mut OsRng);
33 let public_key = signing_key.verifying_key();
34 Ok(Self {
35 did: format!("did:agentmesh:{}", agent_id),
36 public_key,
37 capabilities,
38 parent_did: None,
39 delegation_depth: 0,
40 signing_key,
41 })
42 }
43
44 pub fn sign(&self, data: &[u8]) -> Vec<u8> {
46 self.signing_key.sign(data).to_bytes().to_vec()
47 }
48
49 pub fn verify(&self, data: &[u8], signature: &[u8]) -> bool {
51 if signature.len() != 64 {
52 return false;
53 }
54 let sig_bytes: [u8; 64] = signature.try_into().unwrap();
55 let sig = ed25519_dalek::Signature::from_bytes(&sig_bytes);
56 self.public_key.verify(data, &sig).is_ok()
57 }
58
59 pub fn delegate(&self, name: &str, capabilities: Vec<String>) -> Result<Self, IdentityError> {
65 if self.delegation_depth >= MAX_DELEGATION_DEPTH {
66 return Err(IdentityError::DelegationDepthExceeded {
67 current: self.delegation_depth,
68 max: MAX_DELEGATION_DEPTH,
69 });
70 }
71
72 for cap in &capabilities {
74 if !self.capabilities.contains(cap) {
75 return Err(IdentityError::CapabilityNotInParent {
76 capability: cap.clone(),
77 });
78 }
79 }
80
81 let signing_key = SigningKey::generate(&mut OsRng);
82 let public_key = signing_key.verifying_key();
83
84 Ok(Self {
85 did: format!("did:agentmesh:{}", name),
86 public_key,
87 capabilities,
88 parent_did: Some(self.did.clone()),
89 delegation_depth: self.delegation_depth + 1,
90 signing_key,
91 })
92 }
93
94 pub fn to_json(&self) -> Result<String, IdentityError> {
96 let public = PublicIdentity {
97 did: self.did.clone(),
98 public_key: self.public_key.to_bytes().to_vec(),
99 capabilities: self.capabilities.clone(),
100 };
101 serde_json::to_string(&public).map_err(IdentityError::Serialization)
102 }
103
104 pub fn from_json(json: &str) -> Result<PublicIdentity, IdentityError> {
108 serde_json::from_str(json).map_err(IdentityError::Serialization)
109 }
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct PublicIdentity {
115 pub did: String,
116 pub public_key: Vec<u8>,
117 #[serde(default)]
118 pub capabilities: Vec<String>,
119}
120
121impl PublicIdentity {
122 pub fn verify(&self, data: &[u8], signature: &[u8]) -> bool {
124 if self.public_key.len() != 32 || signature.len() != 64 {
125 return false;
126 }
127 let key_bytes: [u8; 32] = self.public_key.as_slice().try_into().unwrap();
128 let sig_bytes: [u8; 64] = signature.try_into().unwrap();
129 if let Ok(verifying_key) = VerifyingKey::from_bytes(&key_bytes) {
130 let sig = ed25519_dalek::Signature::from_bytes(&sig_bytes);
131 verifying_key.verify(data, &sig).is_ok()
132 } else {
133 false
134 }
135 }
136}
137
138#[derive(Debug, thiserror::Error)]
140pub enum IdentityError {
141 #[error("serialization error: {0}")]
142 Serialization(serde_json::Error),
143
144 #[error("maximum delegation depth ({max}) exceeded (current depth: {current})")]
145 DelegationDepthExceeded { current: u32, max: u32 },
146
147 #[error("cannot delegate capability '{capability}' — not in parent's capabilities")]
148 CapabilityNotInParent { capability: String },
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_generate_and_did() {
157 let id = AgentIdentity::generate("test-agent", vec!["data.read".into()]).unwrap();
158 assert_eq!(id.did, "did:agentmesh:test-agent");
159 assert_eq!(id.capabilities, vec!["data.read"]);
160 }
161
162 #[test]
163 fn test_sign_and_verify() {
164 let id = AgentIdentity::generate("signer", vec![]).unwrap();
165 let data = b"hello world";
166 let sig = id.sign(data);
167 assert!(id.verify(data, &sig));
168 assert!(!id.verify(b"wrong data", &sig));
169 }
170
171 #[test]
172 fn test_json_roundtrip() {
173 let id = AgentIdentity::generate("json-agent", vec!["cap1".into()]).unwrap();
174 let json = id.to_json().unwrap();
175 let public = AgentIdentity::from_json(&json).unwrap();
176 assert_eq!(public.did, "did:agentmesh:json-agent");
177 assert_eq!(public.capabilities, vec!["cap1"]);
178
179 let sig = id.sign(b"payload");
181 assert!(public.verify(b"payload", &sig));
182 }
183
184 #[test]
185 fn test_bad_signature_rejected() {
186 let id = AgentIdentity::generate("agent", vec![]).unwrap();
187 assert!(!id.verify(b"data", &[0u8; 64]));
188 assert!(!id.verify(b"data", &[0u8; 32])); }
190
191 #[test]
192 fn test_multiple_identities_different_dids() {
193 let id1 = AgentIdentity::generate("agent-1", vec![]).unwrap();
194 let id2 = AgentIdentity::generate("agent-2", vec![]).unwrap();
195 assert_ne!(id1.did, id2.did);
196 }
197
198 #[test]
199 fn test_multiple_identities_different_key_pairs() {
200 let id1 = AgentIdentity::generate("agent-a", vec![]).unwrap();
201 let id2 = AgentIdentity::generate("agent-b", vec![]).unwrap();
202 assert_ne!(id1.public_key.to_bytes(), id2.public_key.to_bytes());
203 }
204
205 #[test]
206 fn test_sign_empty_data() {
207 let id = AgentIdentity::generate("empty-signer", vec![]).unwrap();
208 let sig = id.sign(b"");
209 assert_eq!(sig.len(), 64);
210 assert!(id.verify(b"", &sig));
211 }
212
213 #[test]
214 fn test_cross_identity_verification_fails() {
215 let id1 = AgentIdentity::generate("signer-1", vec![]).unwrap();
216 let id2 = AgentIdentity::generate("signer-2", vec![]).unwrap();
217 let sig = id1.sign(b"test data");
218 assert!(!id2.verify(b"test data", &sig));
220 }
221
222 #[test]
223 fn test_public_identity_from_json_verifies_signatures() {
224 let id = AgentIdentity::generate("json-verify", vec!["read".into()]).unwrap();
225 let json = id.to_json().unwrap();
226 let public = AgentIdentity::from_json(&json).unwrap();
227 let data = b"important payload";
228 let sig = id.sign(data);
229 assert!(public.verify(data, &sig));
230 assert!(!public.verify(b"wrong data", &sig));
232 }
233
234 #[test]
235 fn test_invalid_json_returns_error() {
236 let result = AgentIdentity::from_json("not valid json {{{");
237 assert!(result.is_err());
238 assert!(matches!(
239 result.unwrap_err(),
240 IdentityError::Serialization(_)
241 ));
242 }
243
244 #[test]
245 fn test_public_identity_empty_public_key_rejects() {
246 let public = PublicIdentity {
247 did: "did:agentmesh:test".to_string(),
248 public_key: vec![], capabilities: vec![],
250 };
251 assert!(!public.verify(b"data", &[0u8; 64]));
252 }
253
254 #[test]
255 fn test_capabilities_roundtrip_json() {
256 let caps = vec![
257 "data.read".to_string(),
258 "data.write".to_string(),
259 "admin".to_string(),
260 ];
261 let id = AgentIdentity::generate("cap-agent", caps.clone()).unwrap();
262 let json = id.to_json().unwrap();
263 let public = AgentIdentity::from_json(&json).unwrap();
264 assert_eq!(public.capabilities, caps);
265 }
266
267 #[test]
268 fn test_did_format() {
269 let id = AgentIdentity::generate("my-agent", vec![]).unwrap();
270 assert!(id.did.starts_with("did:agentmesh:"));
271 assert_eq!(id.did, "did:agentmesh:my-agent");
272 }
273
274 #[test]
279 fn test_delegate_creates_child_with_parent_did() {
280 let parent =
281 AgentIdentity::generate("parent", vec!["read".into(), "write".into()]).unwrap();
282 let child = parent.delegate("child", vec!["read".into()]).unwrap();
283 assert_eq!(child.parent_did, Some("did:agentmesh:parent".to_string()));
284 assert_eq!(child.delegation_depth, 1);
285 assert_eq!(child.capabilities, vec!["read"]);
286 }
287
288 #[test]
289 fn test_delegate_narrows_capabilities() {
290 let parent =
291 AgentIdentity::generate("parent", vec!["read".into(), "write".into()]).unwrap();
292 let child = parent.delegate("child", vec!["read".into()]).unwrap();
293 assert!(!child.capabilities.contains(&"write".to_string()));
294 }
295
296 #[test]
297 fn test_delegate_rejects_superset() {
298 let parent = AgentIdentity::generate("parent", vec!["read".into()]).unwrap();
299 let result = parent.delegate("child", vec!["read".into(), "admin".into()]);
300 assert!(result.is_err());
301 match result.unwrap_err() {
302 IdentityError::CapabilityNotInParent { capability } => {
303 assert_eq!(capability, "admin");
304 }
305 other => panic!("expected CapabilityNotInParent, got {:?}", other),
306 }
307 }
308
309 #[test]
310 fn test_delegate_depth_increments() {
311 let root = AgentIdentity::generate("root", vec!["read".into()]).unwrap();
312 let d1 = root.delegate("d1", vec!["read".into()]).unwrap();
313 let d2 = d1.delegate("d2", vec!["read".into()]).unwrap();
314 assert_eq!(d2.delegation_depth, 2);
315 assert_eq!(d2.parent_did, Some("did:agentmesh:d1".to_string()));
316 }
317
318 #[test]
319 fn test_delegate_max_depth_enforced() {
320 let mut current = AgentIdentity::generate("root", vec!["read".into()]).unwrap();
321 for i in 0..MAX_DELEGATION_DEPTH {
322 current = current
323 .delegate(&format!("child-{}", i), vec!["read".into()])
324 .unwrap();
325 }
326 let result = current.delegate("one-too-many", vec!["read".into()]);
327 assert!(result.is_err());
328 assert!(matches!(
329 result.unwrap_err(),
330 IdentityError::DelegationDepthExceeded { .. }
331 ));
332 }
333
334 #[test]
335 fn test_delegate_child_has_own_keypair() {
336 let parent = AgentIdentity::generate("parent", vec!["read".into()]).unwrap();
337 let child = parent.delegate("child", vec!["read".into()]).unwrap();
338 assert_ne!(parent.public_key.to_bytes(), child.public_key.to_bytes());
339 }
340
341 #[test]
342 fn test_delegate_child_can_sign_and_verify() {
343 let parent = AgentIdentity::generate("parent", vec!["read".into()]).unwrap();
344 let child = parent.delegate("child", vec!["read".into()]).unwrap();
345 let data = b"delegation payload";
346 let sig = child.sign(data);
347 assert!(child.verify(data, &sig));
348 assert!(!parent.verify(data, &sig));
350 }
351
352 #[test]
353 fn test_root_identity_has_no_parent() {
354 let root = AgentIdentity::generate("root", vec![]).unwrap();
355 assert!(root.parent_did.is_none());
356 assert_eq!(root.delegation_depth, 0);
357 }
358
359 #[test]
360 fn test_delegate_empty_capabilities_allowed() {
361 let parent = AgentIdentity::generate("parent", vec!["read".into()]).unwrap();
362 let child = parent.delegate("child", vec![]).unwrap();
363 assert!(child.capabilities.is_empty());
364 assert_eq!(child.delegation_depth, 1);
365 }
366}