Skip to main content

oxirs_did/
lib.rs

1//! # OxiRS DID
2//!
3//! [![Version](https://img.shields.io/badge/version-0.2.4-blue)](https://github.com/cool-japan/oxirs/releases)
4//!
5//! **Status**: Production Release (v0.2.4)
6//!
7//! W3C Decentralized Identifiers (DID) and Verifiable Credentials (VC) implementation
8//! for OxiRS, enabling signed RDF graphs and trust layer for data sovereignty.
9//!
10//! ## Features
11//!
12//! - **DID Methods**: did:key (Ed25519), did:web (HTTP-based)
13//! - **Verifiable Credentials**: W3C VC Data Model 2.0
14//! - **Signed Graphs**: RDF Dataset Canonicalization + Ed25519 signatures
15//! - **Key Management**: Secure key storage and derivation
16//!
17//! ## Example
18//!
19//! ```rust,ignore
20//! use oxirs_did::{Did, DidResolver, VerifiableCredential, CredentialIssuer};
21//!
22//! // Create DID from key
23//! let did = Did::new_key(&public_key)?;
24//!
25//! // Issue credential
26//! let issuer = CredentialIssuer::new(keystore);
27//! let vc = issuer.issue(subject, types, &issuer_did).await?;
28//!
29//! // Verify credential
30//! let verifier = CredentialVerifier::new(resolver);
31//! let result = verifier.verify(&vc).await?;
32//! ```
33
34pub mod did;
35pub mod did_web;
36pub mod key_management;
37pub mod kms;
38pub mod proof;
39pub mod rdf_integration;
40pub mod revocation;
41#[cfg(feature = "bbs-plus")]
42pub mod signatures;
43pub mod signed_graph;
44pub mod url;
45pub mod vc;
46#[cfg(feature = "zkp")]
47pub mod zkp;
48
49// v1.1.0 DID document versioning
50pub mod document_versioning;
51
52// v1.1.0: Verifiable Credential exchange protocols (VP creation, verification, JWT-like encoding)
53pub mod credential_exchange;
54
55// v1.1.0 round 5: DH/ECDH key agreement for DID-based communication
56pub mod key_agreement;
57
58// v1.1.0 round 6: W3C Verifiable Presentation builder
59pub mod presentation_builder;
60
61// v1.1.0 round 7: Verifiable Credential structural verification (W3C VC Data Model)
62pub mod vc_verifier;
63
64// v1.1.0 round 13: VP construction, credential selection, proof stubs, and selective disclosure
65pub mod vc_presenter;
66
67// v1.1.0 round 14: DID trust chain validation (leaf→root certification chain)
68pub mod trust_chain;
69
70// v1.1.0 round 15: DID authentication method management and challenge-response
71pub mod authentication;
72
73// v1.1.0 round 16: Verifiable Presentation request/response handling and validation
74pub mod presentation_request;
75
76// v1.1.0 round 11: In-memory DID document resolver with registration, deactivation and service management
77pub mod did_resolver;
78
79// v1.1.0 round 12: DID identity registry with resolution, update, deactivation, and method lookup
80pub mod identity_registry;
81
82// v1.1.0 round 13: W3C Verifiable Credential schema validation
83pub mod credential_schema;
84
85// v1.1.0 round 12: DID key lifecycle management (generation, rotation, status, purposes)
86pub mod key_manager;
87
88// v1.1.0 round 11: Linked Data Proof purpose validation (authentication, assertion, key agreement, capability)
89pub mod proof_purpose;
90
91use chrono::{DateTime, Utc};
92use serde::{Deserialize, Serialize};
93use thiserror::Error;
94
95// Re-exports
96#[cfg(feature = "did-ethr")]
97pub use did::methods::{DidEthr, DidEthrMethod, EthNetwork};
98#[cfg(feature = "did-ion")]
99pub use did::methods::{
100    DidIon, DidIonMethod, IonCreateOperation, IonDocument, IonKeyDescriptor, IonKeyPurpose,
101    IonOperationType, IonService,
102};
103pub use did::{ChainNamespace, Did, DidDocument, DidPkh, DidPkhMethod, DidResolver};
104pub use key_management::{
105    generate_rotation_key, KeyExpiry, KeyRotation, KeyRotationManager, KeyRotationReason,
106    KeyRotationRecord, KeyRotationRegistry, Keystore, LifecycleKeyRotationRecord,
107    VerificationKey as ManagedVerificationKey,
108};
109pub use kms::{
110    create_mock_kms, KeyUsage, KmsAlgorithm, KmsBackend, KmsDidSigner, KmsKeyMetadata, KmsProvider,
111    MockAwsKms, MockAzureKms, MockGcpKms,
112};
113pub use proof::{
114    jws::{
115        attach_jws_proof, extract_jws_proof, sign_document, verify_document, CompactJws,
116        JsonWebSignature2020, JwsAlgorithm, JwsHeader, JwsSigner, JwsVerifier,
117    },
118    Proof, ProofPurpose, ProofType,
119};
120pub use revocation::{
121    BloomFilter, CredentialStatus, RevocationEntry, RevocationList2020, RevocationRegistry,
122    RevocationRegistry2020, RevocationStatus, StatusList2021, StatusList2021Inner,
123    StatusListCredential, StatusPurpose, MIN_LIST_SIZE,
124};
125#[cfg(feature = "bbs-plus")]
126pub use signatures::{
127    BbsKeyPair, BbsPlusSignature, BbsProof, BbsProofRequest, EcdsaJwsSigner, EcdsaJwsVerifier,
128    Ed25519JwsSigner, Ed25519JwsVerifier, Es256Signer, Es256Verifier,
129    JwsAlgorithm as SignaturesJwsAlgorithm, JwsHeader as SignaturesJwsHeader, JwsPayload,
130    JwsSignature, JwsSignatureHeader, JwsSigner as SignaturesJwsSigner, JwsSignerTrait,
131    JwsVerifier as SignaturesJwsVerifier, JwsVerifierTrait, MockJwsSigner, MockJwsVerifier,
132    P256KeyPair, Rs256Signer, Rs256Verifier, RsaKeyPair,
133};
134pub use signed_graph::SignedGraph;
135pub use url::{DereferencedResource, DidDereferencer, DidUrl};
136pub use vc::{
137    CredentialIssuer, CredentialSubject, CredentialVerifier, VerifiableCredential,
138    VerifiablePresentation,
139};
140#[cfg(feature = "zkp")]
141pub use zkp::{
142    prove_selective, verify_selective, AttributeCommitment, CredentialAttribute,
143    DisclosurePresentation, PedersenParams, PedersenSelectiveDisclosureProof, SchnorrProof,
144    SelectiveDisclosureCredential, SelectiveDisclosureProof, SelectiveDisclosureRequest,
145    ZkpProofRequest,
146};
147
148/// DID error types
149#[derive(Error, Debug)]
150pub enum DidError {
151    #[error("Invalid DID format: {0}")]
152    InvalidFormat(String),
153
154    #[error("Unsupported DID method: {0}")]
155    UnsupportedMethod(String),
156
157    #[error("Resolution failed: {0}")]
158    ResolutionFailed(String),
159
160    #[error("Verification failed: {0}")]
161    VerificationFailed(String),
162
163    #[error("Signing failed: {0}")]
164    SigningFailed(String),
165
166    #[error("Key not found: {0}")]
167    KeyNotFound(String),
168
169    #[error("Invalid key: {0}")]
170    InvalidKey(String),
171
172    #[error("Credential expired")]
173    CredentialExpired,
174
175    #[error("Invalid proof: {0}")]
176    InvalidProof(String),
177
178    #[error("Canonicalization failed: {0}")]
179    CanonicalizationFailed(String),
180
181    #[error("Serialization error: {0}")]
182    SerializationError(String),
183
184    #[error("Network error: {0}")]
185    NetworkError(String),
186
187    #[error("Internal error: {0}")]
188    InternalError(String),
189}
190
191pub type DidResult<T> = Result<T, DidError>;
192
193/// Verification method in DID Document
194#[derive(Debug, Clone, Serialize, Deserialize)]
195#[serde(rename_all = "camelCase")]
196pub struct VerificationMethod {
197    /// Verification method ID
198    pub id: String,
199    /// Type of verification method
200    #[serde(rename = "type")]
201    pub method_type: String,
202    /// Controller DID
203    pub controller: String,
204    /// Public key in multibase format
205    #[serde(skip_serializing_if = "Option::is_none")]
206    pub public_key_multibase: Option<String>,
207    /// Public key in JWK format
208    #[serde(skip_serializing_if = "Option::is_none")]
209    pub public_key_jwk: Option<serde_json::Value>,
210    /// Blockchain account ID (CAIP-10 format, for did:pkh)
211    #[serde(skip_serializing_if = "Option::is_none")]
212    pub blockchain_account_id: Option<String>,
213}
214
215impl VerificationMethod {
216    /// Create Ed25519 verification method
217    pub fn ed25519(id: &str, controller: &str, public_key: &[u8]) -> Self {
218        // Multibase encode with base58btc prefix 'z'
219        let multibase = format!("z{}", bs58::encode(public_key).into_string());
220
221        Self {
222            id: id.to_string(),
223            method_type: "Ed25519VerificationKey2020".to_string(),
224            controller: controller.to_string(),
225            public_key_multibase: Some(multibase),
226            public_key_jwk: None,
227            blockchain_account_id: None,
228        }
229    }
230
231    /// Create a blockchain account verification method (for did:pkh)
232    ///
233    /// Uses CAIP-10 blockchain account ID format instead of a public key.
234    pub fn blockchain(
235        id: &str,
236        controller: &str,
237        method_type: &str,
238        blockchain_account_id: &str,
239    ) -> Self {
240        Self {
241            id: id.to_string(),
242            method_type: method_type.to_string(),
243            controller: controller.to_string(),
244            public_key_multibase: None,
245            public_key_jwk: None,
246            blockchain_account_id: Some(blockchain_account_id.to_string()),
247        }
248    }
249
250    /// Create a JWK verification method
251    pub fn jwk(id: &str, controller: &str, method_type: &str, jwk: serde_json::Value) -> Self {
252        Self {
253            id: id.to_string(),
254            method_type: method_type.to_string(),
255            controller: controller.to_string(),
256            public_key_multibase: None,
257            public_key_jwk: Some(jwk),
258            blockchain_account_id: None,
259        }
260    }
261
262    /// Get public key bytes
263    pub fn get_public_key_bytes(&self) -> DidResult<Vec<u8>> {
264        if let Some(ref multibase) = self.public_key_multibase {
265            // Remove multibase prefix and decode
266            if let Some(stripped) = multibase.strip_prefix('z') {
267                bs58::decode(stripped)
268                    .into_vec()
269                    .map_err(|e| DidError::InvalidKey(e.to_string()))
270            } else {
271                Err(DidError::InvalidKey("Unknown multibase prefix".to_string()))
272            }
273        } else if self.blockchain_account_id.is_some() {
274            Err(DidError::InvalidKey(
275                "Blockchain account verification methods do not expose raw public keys".to_string(),
276            ))
277        } else {
278            Err(DidError::InvalidKey("No public key available".to_string()))
279        }
280    }
281}
282
283/// Service endpoint in DID Document
284#[derive(Debug, Clone, Serialize, Deserialize)]
285#[serde(rename_all = "camelCase")]
286pub struct Service {
287    /// Service ID
288    pub id: String,
289    /// Service type
290    #[serde(rename = "type")]
291    pub service_type: String,
292    /// Service endpoint URL
293    pub service_endpoint: String,
294}
295
296/// Verification result
297#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct VerificationResult {
299    /// Whether verification succeeded
300    pub valid: bool,
301    /// Verified issuer DID
302    pub issuer: Option<String>,
303    /// Verification timestamp
304    pub verified_at: DateTime<Utc>,
305    /// Error message if verification failed
306    pub error: Option<String>,
307    /// Checks performed
308    pub checks: Vec<VerificationCheck>,
309}
310
311/// Individual verification check
312#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct VerificationCheck {
314    /// Check name
315    pub name: String,
316    /// Whether check passed
317    pub passed: bool,
318    /// Details
319    pub details: Option<String>,
320}
321
322impl VerificationResult {
323    pub fn success(issuer: &str) -> Self {
324        Self {
325            valid: true,
326            issuer: Some(issuer.to_string()),
327            verified_at: Utc::now(),
328            error: None,
329            checks: vec![],
330        }
331    }
332
333    pub fn failure(error: &str) -> Self {
334        Self {
335            valid: false,
336            issuer: None,
337            verified_at: Utc::now(),
338            error: Some(error.to_string()),
339            checks: vec![],
340        }
341    }
342
343    pub fn with_check(mut self, name: &str, passed: bool, details: Option<&str>) -> Self {
344        self.checks.push(VerificationCheck {
345            name: name.to_string(),
346            passed,
347            details: details.map(String::from),
348        });
349        self
350    }
351}
352
353#[cfg(test)]
354mod tests {
355    use super::*;
356
357    #[test]
358    fn test_verification_method_ed25519() {
359        let public_key = vec![
360            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
361            25, 26, 27, 28, 29, 30, 31, 32,
362        ];
363
364        let vm = VerificationMethod::ed25519("did:key:z123#key-1", "did:key:z123", &public_key);
365
366        assert_eq!(vm.method_type, "Ed25519VerificationKey2020");
367        assert!(vm.public_key_multibase.is_some());
368
369        let recovered = vm.get_public_key_bytes().unwrap();
370        assert_eq!(recovered, public_key);
371    }
372
373    #[test]
374    fn test_verification_result() {
375        let result = VerificationResult::success("did:key:z123")
376            .with_check("signature", true, None)
377            .with_check("expiration", true, Some("Not expired"));
378
379        assert!(result.valid);
380        assert_eq!(result.checks.len(), 2);
381    }
382}