anya_core/web5/
identity.rs

1use std::error::Error;
2// Web5 Identity Implementation
3// Provides DID (Decentralized Identity) functionality
4// as part of the Web5 integration - [AIR-012] Operational Reliability
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::{Arc, Mutex};
9use std::time::{SystemTime, UNIX_EPOCH};
10
11// Define Result type for Web5
12pub type Web5Result<T> = Result<T, Web5Error>;
13
14// Define Error enum for Web5
15#[derive(Debug, thiserror::Error)]
16pub enum Web5Error {
17    #[error("Identity error: {0}")]
18    Identity(String),
19
20    #[error("Protocol error: {0}")]
21    Protocol(String),
22
23    #[error("Communication error: {0}")]
24    Communication(String),
25
26    #[error("Storage error: {0}")]
27    Storage(String),
28
29    #[error("Credential error: {0}")]
30    Credential(String),
31
32    #[error("Not found: {0}")]
33    NotFound(String),
34
35    #[error("DWN error: {0}")]
36    DWNError(String),
37
38    #[error("Serialization error: {0}")]
39    SerializationError(String),
40}
41
42// [AIS-3] Implementation for From<Box<dyn std::error::Error>> for Web5Error
43// This allows the ? operator to work correctly when converting from Box<dyn Error> to Web5Error
44impl From<Box<dyn std::error::Error>> for Web5Error {
45    fn from(err: Box<dyn std::error::Error>) -> Self {
46        Web5Error::Protocol(err.to_string())
47    }
48}
49
50// [AIS-3] Implementation for From<String> for Web5Error
51// This allows the ? operator to work correctly when converting from String to Web5Error
52impl From<String> for Web5Error {
53    fn from(err: String) -> Self {
54        Web5Error::Protocol(err)
55    }
56}
57
58// [AIS-3] Implementation for From<&str> for Web5Error
59// This allows the ? operator to work correctly when converting from &str to Web5Error
60impl From<&str> for Web5Error {
61    fn from(err: &str) -> Self {
62        Web5Error::Protocol(err.to_string())
63    }
64}
65
66/// DID Manager
67///
68/// Core component responsible for decentralized identity management.
69/// Implements the ports and adapters pattern for extensibility.
70#[derive(Clone, Debug)]
71pub struct DIDManager {
72    /// DIDs managed by this instance
73    dids: Arc<Mutex<HashMap<String, DID>>>,
74    /// Default DID to use
75    default_did: Option<String>,
76    /// DID method to use
77    method: String,
78}
79
80/// Decentralized Identifier
81///
82/// Represents a DID with its document and private keys.
83#[derive(Clone, Debug, Serialize, Deserialize)]
84pub struct DID {
85    /// DID URI (e.g., "did:ion:123...")
86    pub id: String,
87    /// DID Document
88    pub document: DIDDocument,
89    /// Private keys associated with this DID
90    #[serde(skip_serializing)]
91    pub private_keys: HashMap<String, Vec<u8>>,
92}
93
94/// DID Document
95///
96/// The public representation of a DID, containing verification methods
97/// and service endpoints as defined in the DID Core specification.
98#[derive(Clone, Debug, Serialize, Deserialize)]
99pub struct DIDDocument {
100    /// DID context
101    #[serde(rename = "@context")]
102    pub context: Vec<String>,
103    /// DID URI
104    pub id: String,
105    /// Verification methods
106    #[serde(default)]
107    pub verification_method: Vec<VerificationMethod>,
108    /// Authentication methods
109    #[serde(default)]
110    pub authentication: Vec<String>,
111    /// Assertion methods
112    #[serde(default)]
113    pub assertion_method: Vec<String>,
114    /// Service endpoints
115    #[serde(default)]
116    pub service: Vec<Service>,
117}
118
119/// Verification Method
120///
121/// A cryptographic mechanism used for authentication and
122/// digital signatures within a DID.
123#[derive(Clone, Debug, Serialize, Deserialize)]
124pub struct VerificationMethod {
125    /// ID of the verification method
126    pub id: String,
127    /// Type of the verification method
128    #[serde(rename = "type")]
129    pub vm_type: String,
130    /// Controller of the verification method
131    pub controller: String,
132    /// Public key in JWK format
133    #[serde(skip_serializing_if = "Option::is_none")]
134    pub public_key_jwk: Option<JWK>,
135}
136
137/// JSON Web Key
138///
139/// A cryptographic key representation in JSON format.
140#[derive(Clone, Debug, Serialize, Deserialize)]
141pub struct JWK {
142    /// Key type
143    pub kty: String,
144    /// Curve (for EC keys)
145    #[serde(skip_serializing_if = "Option::is_none")]
146    pub crv: Option<String>,
147    /// X coordinate (for EC keys)
148    #[serde(skip_serializing_if = "Option::is_none")]
149    pub x: Option<String>,
150    /// Y coordinate (for EC keys)
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub y: Option<String>,
153    /// Key ID
154    #[serde(skip_serializing_if = "Option::is_none")]
155    pub kid: Option<String>,
156}
157
158/// Service
159///
160/// A service endpoint for a DID.
161#[derive(Clone, Debug, Serialize, Deserialize)]
162pub struct Service {
163    /// ID of the service
164    pub id: String,
165    /// Type of the service
166    #[serde(rename = "type")]
167    pub service_type: String,
168    /// Service endpoint URL
169    pub service_endpoint: String,
170}
171
172impl DIDManager {
173    /// Create a new DID manager with the specified method
174    pub fn new(method: &str) -> Self {
175        Self {
176            dids: Arc::new(Mutex::new(HashMap::new())),
177            default_did: None,
178            method: method.to_string(),
179        }
180    }
181
182    /// Create a new DID with the configured method
183    pub fn create_did(&self) -> Web5Result<DID> {
184        // Generate a random ID for the DID
185        let id = format!("did:{}:{}", self.method, generate_random_id());
186
187        // Generate a key pair for this DID
188        let private_key = generate_private_key();
189        let public_key_jwk = generate_public_key_jwk(&private_key);
190
191        // Create verification method
192        let verification_method = VerificationMethod {
193            id: format!("{id}#key-1"),
194            vm_type: "JsonWebKey2020".to_string(),
195            controller: id.clone(),
196            public_key_jwk: Some(public_key_jwk),
197        };
198
199        // Create a basic DID document
200        let document = DIDDocument {
201            context: vec!["https://www.w3.org/ns/did/v1".to_string()],
202            id: id.clone(),
203            verification_method: vec![verification_method],
204            authentication: vec![format!("{}#key-1", id)],
205            assertion_method: vec![format!("{}#key-1", id)],
206            service: Vec::new(),
207        };
208
209        // Create the DID with private keys
210        let mut private_keys = HashMap::new();
211        private_keys.insert("key-1".to_string(), private_key);
212
213        let did = DID {
214            id: id.clone(),
215            document,
216            private_keys,
217        };
218
219        // Store the DID
220        {
221            let mut dids = self
222                .dids
223                .lock()
224                .map_err(|e| format!("Mutex lock error: {e}"))?;
225            dids.insert(id.clone(), did.clone());
226        }
227
228        Ok(did)
229    }
230
231    /// Resolve a DID to its document
232    pub fn resolve_did(&self, did: &str) -> Result<DIDDocument, Box<dyn Error>> {
233        // First, check if we have the DID locally
234        let dids = self
235            .dids
236            .lock()
237            .map_err(|e| format!("Mutex lock error: {e}"))?;
238        if let Some(did_obj) = dids.get(did) {
239            return Ok(did_obj.document.clone());
240        }
241
242        // If not found locally, return an error (future: implement remote resolution)
243        Err(format!("DID not found: {did}").into())
244    }
245
246    /// Set the default DID
247    pub fn set_default_did(&mut self, did: &str) -> Result<(), Box<dyn Error>> {
248        let dids = self
249            .dids
250            .lock()
251            .map_err(|e| format!("Mutex lock error: {e}"))?;
252        if dids.contains_key(did) {
253            self.default_did = Some(did.to_string());
254            Ok(())
255        } else {
256            Err(format!("DID {did} not found").into())
257        }
258    }
259
260    /// Get the default DID
261    pub fn get_default_did(&self) -> Result<Option<String>, Box<dyn Error>> {
262        Ok(self.default_did.clone())
263    }
264
265    /// Sign data with a DID's private key
266    pub fn sign(&self, did: &str, data: &[u8]) -> Result<Vec<u8>, Box<dyn Error>> {
267        // Get the DID
268        let dids = self
269            .dids
270            .lock()
271            .map_err(|e| format!("Mutex lock error: {e}"))?;
272        let did_obj = dids
273            .get(did)
274            .ok_or_else(|| format!("DID not found: {did}"))?;
275
276        // Get the first private key for signing
277        if let Some((_, private_key_bytes)) = did_obj.private_keys.iter().next() {
278            // Parse the private key
279            let private_key = secp256k1::SecretKey::from_slice(private_key_bytes)
280                .map_err(|e| format!("Invalid private key: {e}"))?;
281
282            // Create secp256k1 context
283            let secp = secp256k1::Secp256k1::signing_only();
284
285            // Hash the data (using SHA256)
286            let hash = {
287                use sha2::{Digest, Sha256};
288                let mut hasher = Sha256::new();
289                hasher.update(data);
290                hasher.finalize()
291            };
292
293            // Create message from hash
294            let message = secp256k1::Message::from_digest_slice(&hash)
295                .map_err(|e| format!("Failed to create message: {e}"))?;
296
297            // Sign the message
298            let signature = secp.sign_ecdsa(&message, &private_key);
299
300            // Return the signature bytes
301            Ok(signature.serialize_compact().to_vec())
302        } else {
303            Err("No private keys found for DID".into())
304        }
305    }
306
307    /// Get a list of all DIDs
308    pub fn dids(&self) -> Result<Vec<String>, Box<dyn Error>> {
309        let dids = self
310            .dids
311            .lock()
312            .map_err(|e| format!("Mutex lock error: {e}"))?;
313        Ok(dids.keys().cloned().collect())
314    }
315
316    /// Get a DID by ID
317    pub fn get_did(&self, did_id: &str) -> Web5Result<Option<DID>> {
318        let dids = self
319            .dids
320            .lock()
321            .map_err(|e| Web5Error::Storage(format!("Mutex lock error: {e}")))?;
322        Ok(dids.get(did_id).cloned())
323    }
324
325    /// List all DIDs
326    pub fn list_dids(&self) -> Vec<DID> {
327        let dids = self
328            .dids
329            .lock()
330            .unwrap_or_else(|_| panic!("Failed to lock mutex"));
331        dids.values().cloned().collect()
332    }
333}
334
335/// Identity manager for Web5 DID operations
336#[derive(Debug, Clone)]
337pub struct IdentityManager {
338    did_manager: DIDManager,
339}
340
341impl IdentityManager {
342    pub fn new(namespace: &str) -> Self {
343        Self {
344            did_manager: DIDManager::new(namespace),
345        }
346    }
347
348    pub fn create_identity(&mut self) -> Web5Result<DID> {
349        self.did_manager.create_did()
350    }
351
352    pub fn get_identity(&self, did_id: &str) -> Web5Result<Option<DID>> {
353        self.did_manager.get_did(did_id)
354    }
355
356    pub fn list_identities(&self) -> Vec<DID> {
357        self.did_manager.list_dids()
358    }
359}
360
361/// Generate a random ID for a DID
362/// [AIS-3] Properly handles errors without using ? operator
363fn generate_random_id() -> String {
364    let now = SystemTime::now()
365        .duration_since(UNIX_EPOCH)
366        .unwrap_or_default()
367        .as_secs();
368
369    format!("{now:x}")
370}
371
372/// Generate a private key for cryptographic operations
373fn generate_private_key() -> Vec<u8> {
374    // Generate a 32-byte private key (simplified implementation)
375    use rand::RngCore;
376    let mut key = vec![0u8; 32];
377    rand::thread_rng().fill_bytes(&mut key);
378    key
379}
380
381/// Generate a public key JWK from a private key
382fn generate_public_key_jwk(private_key: &[u8]) -> JWK {
383    // Simplified implementation - in production this would derive the actual public key
384    // from the private key using proper cryptographic operations
385    use base64::Engine;
386
387    // For demonstration, we'll create a placeholder JWK
388    JWK {
389        kty: "EC".to_string(),
390        crv: Some("secp256k1".to_string()),
391        x: Some(base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&private_key[..16])),
392        y: Some(base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&private_key[16..])),
393        kid: Some("key-1".to_string()),
394    }
395}
396
397#[cfg(test)]
398mod tests {
399    use super::*;
400
401    #[test]
402    fn test_create_did() -> Result<(), Box<dyn Error>> {
403        let manager = DIDManager::new("example");
404        let did = manager.create_did()?;
405        assert!(!did.id.is_empty());
406        assert!(did.id.starts_with("did:example:"));
407        assert!(!did.private_keys.is_empty());
408        Ok(())
409    }
410
411    #[test]
412    fn test_default_did() -> Result<(), Box<dyn Error>> {
413        let mut manager = DIDManager::new("example");
414        let did = manager.create_did()?;
415
416        // Initially no default DID
417        assert!(manager.get_default_did()?.is_none());
418
419        // Set and get default DID
420        manager.set_default_did(&did.id)?;
421        assert_eq!(manager.get_default_did()?.unwrap(), did.id);
422        Ok(())
423    }
424}