oxirs_did/
lib.rs

1//! # OxiRS DID
2//!
3//! W3C Decentralized Identifiers (DID) and Verifiable Credentials (VC) implementation
4//! for OxiRS, enabling signed RDF graphs and trust layer for data sovereignty.
5//!
6//! ## Features
7//!
8//! - **DID Methods**: did:key (Ed25519), did:web (HTTP-based)
9//! - **Verifiable Credentials**: W3C VC Data Model 2.0
10//! - **Signed Graphs**: RDF Dataset Canonicalization + Ed25519 signatures
11//! - **Key Management**: Secure key storage and derivation
12//!
13//! ## Example
14//!
15//! ```rust,ignore
16//! use oxirs_did::{Did, DidResolver, VerifiableCredential, CredentialIssuer};
17//!
18//! // Create DID from key
19//! let did = Did::new_key(&public_key)?;
20//!
21//! // Issue credential
22//! let issuer = CredentialIssuer::new(keystore);
23//! let vc = issuer.issue(subject, types, &issuer_did).await?;
24//!
25//! // Verify credential
26//! let verifier = CredentialVerifier::new(resolver);
27//! let result = verifier.verify(&vc).await?;
28//! ```
29
30pub mod did;
31pub mod key_management;
32pub mod proof;
33pub mod rdf_integration;
34pub mod signed_graph;
35pub mod vc;
36
37use chrono::{DateTime, Utc};
38use serde::{Deserialize, Serialize};
39use thiserror::Error;
40
41// Re-exports
42pub use did::{Did, DidDocument, DidResolver};
43pub use key_management::Keystore;
44pub use proof::{Proof, ProofPurpose, ProofType};
45pub use signed_graph::SignedGraph;
46pub use vc::{
47    CredentialIssuer, CredentialSubject, CredentialVerifier, VerifiableCredential,
48    VerifiablePresentation,
49};
50
51/// DID error types
52#[derive(Error, Debug)]
53pub enum DidError {
54    #[error("Invalid DID format: {0}")]
55    InvalidFormat(String),
56
57    #[error("Unsupported DID method: {0}")]
58    UnsupportedMethod(String),
59
60    #[error("Resolution failed: {0}")]
61    ResolutionFailed(String),
62
63    #[error("Verification failed: {0}")]
64    VerificationFailed(String),
65
66    #[error("Signing failed: {0}")]
67    SigningFailed(String),
68
69    #[error("Key not found: {0}")]
70    KeyNotFound(String),
71
72    #[error("Invalid key: {0}")]
73    InvalidKey(String),
74
75    #[error("Credential expired")]
76    CredentialExpired,
77
78    #[error("Invalid proof: {0}")]
79    InvalidProof(String),
80
81    #[error("Canonicalization failed: {0}")]
82    CanonicalizationFailed(String),
83
84    #[error("Serialization error: {0}")]
85    SerializationError(String),
86
87    #[error("Network error: {0}")]
88    NetworkError(String),
89
90    #[error("Internal error: {0}")]
91    InternalError(String),
92}
93
94pub type DidResult<T> = Result<T, DidError>;
95
96/// Verification method in DID Document
97#[derive(Debug, Clone, Serialize, Deserialize)]
98#[serde(rename_all = "camelCase")]
99pub struct VerificationMethod {
100    /// Verification method ID
101    pub id: String,
102    /// Type of verification method
103    #[serde(rename = "type")]
104    pub method_type: String,
105    /// Controller DID
106    pub controller: String,
107    /// Public key in multibase format
108    #[serde(skip_serializing_if = "Option::is_none")]
109    pub public_key_multibase: Option<String>,
110    /// Public key in JWK format
111    #[serde(skip_serializing_if = "Option::is_none")]
112    pub public_key_jwk: Option<serde_json::Value>,
113}
114
115impl VerificationMethod {
116    /// Create Ed25519 verification method
117    pub fn ed25519(id: &str, controller: &str, public_key: &[u8]) -> Self {
118        // Multibase encode with base58btc prefix 'z'
119        let multibase = format!("z{}", bs58::encode(public_key).into_string());
120
121        Self {
122            id: id.to_string(),
123            method_type: "Ed25519VerificationKey2020".to_string(),
124            controller: controller.to_string(),
125            public_key_multibase: Some(multibase),
126            public_key_jwk: None,
127        }
128    }
129
130    /// Get public key bytes
131    pub fn get_public_key_bytes(&self) -> DidResult<Vec<u8>> {
132        if let Some(ref multibase) = self.public_key_multibase {
133            // Remove multibase prefix and decode
134            if let Some(stripped) = multibase.strip_prefix('z') {
135                bs58::decode(stripped)
136                    .into_vec()
137                    .map_err(|e| DidError::InvalidKey(e.to_string()))
138            } else {
139                Err(DidError::InvalidKey("Unknown multibase prefix".to_string()))
140            }
141        } else {
142            Err(DidError::InvalidKey("No public key available".to_string()))
143        }
144    }
145}
146
147/// Service endpoint in DID Document
148#[derive(Debug, Clone, Serialize, Deserialize)]
149#[serde(rename_all = "camelCase")]
150pub struct Service {
151    /// Service ID
152    pub id: String,
153    /// Service type
154    #[serde(rename = "type")]
155    pub service_type: String,
156    /// Service endpoint URL
157    pub service_endpoint: String,
158}
159
160/// Verification result
161#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct VerificationResult {
163    /// Whether verification succeeded
164    pub valid: bool,
165    /// Verified issuer DID
166    pub issuer: Option<String>,
167    /// Verification timestamp
168    pub verified_at: DateTime<Utc>,
169    /// Error message if verification failed
170    pub error: Option<String>,
171    /// Checks performed
172    pub checks: Vec<VerificationCheck>,
173}
174
175/// Individual verification check
176#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct VerificationCheck {
178    /// Check name
179    pub name: String,
180    /// Whether check passed
181    pub passed: bool,
182    /// Details
183    pub details: Option<String>,
184}
185
186impl VerificationResult {
187    pub fn success(issuer: &str) -> Self {
188        Self {
189            valid: true,
190            issuer: Some(issuer.to_string()),
191            verified_at: Utc::now(),
192            error: None,
193            checks: vec![],
194        }
195    }
196
197    pub fn failure(error: &str) -> Self {
198        Self {
199            valid: false,
200            issuer: None,
201            verified_at: Utc::now(),
202            error: Some(error.to_string()),
203            checks: vec![],
204        }
205    }
206
207    pub fn with_check(mut self, name: &str, passed: bool, details: Option<&str>) -> Self {
208        self.checks.push(VerificationCheck {
209            name: name.to_string(),
210            passed,
211            details: details.map(String::from),
212        });
213        self
214    }
215}
216
217#[cfg(test)]
218mod tests {
219    use super::*;
220
221    #[test]
222    fn test_verification_method_ed25519() {
223        let public_key = vec![
224            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
225            25, 26, 27, 28, 29, 30, 31, 32,
226        ];
227
228        let vm = VerificationMethod::ed25519("did:key:z123#key-1", "did:key:z123", &public_key);
229
230        assert_eq!(vm.method_type, "Ed25519VerificationKey2020");
231        assert!(vm.public_key_multibase.is_some());
232
233        let recovered = vm.get_public_key_bytes().unwrap();
234        assert_eq!(recovered, public_key);
235    }
236
237    #[test]
238    fn test_verification_result() {
239        let result = VerificationResult::success("did:key:z123")
240            .with_check("signature", true, None)
241            .with_check("expiration", true, Some("Not expired"));
242
243        assert!(result.valid);
244        assert_eq!(result.checks.len(), 2);
245    }
246}