W3C DID and Verifiable Credentials implementation with Signed RDF Graphs
Full implementation of W3C Decentralized Identifiers (DID) and Verifiable Credentials (VC) specifications, with support for cryptographically signed RDF graphs.
- DID Core 1.0: W3C Recommendation compliant
- VC Data Model 2.0: Verifiable Credentials with Ed25519 proofs
- DID Methods:
did:key(default, no network),did:web(featuredid-web),did:ethr(default, ERC-1056),did:ion(default, Sidetree/ION),did:pkh(CAIP-10 blockchain accounts) - RDFC-1.0: RDF Dataset Canonicalization for graph signing
- Proof Suites: Ed25519Signature2020, JWS (
JsonWebSignature2020), BBS+ signatures (featurebbs-plus, default) - Key Management: Key rotation and lifecycle tracking, ECDH key agreement
- Revocation: W3C
StatusList2021andRevocationList2020 - Trust & Presentation: Trust chain verification, Verifiable Presentation builder/presenter
- Selective Disclosure: ZKP-based Pedersen commitments (feature
zkp, default)
- ✅ W3C DID Core 1.0
- ✅ W3C VC Data Model 2.0
- ✅ Ed25519Signature2020
- ✅ RDFC-1.0
- ✅ Multicodec
- ✅ Multibase
use oxirs_did::{Did, DidResolver};
use oxirs_did::proof::ed25519::Ed25519Signer;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Generate Ed25519 keypair
let signer = Ed25519Signer::generate();
let public_key = signer.public_key_bytes();
// Create did:key from public key
let did = Did::new_key_ed25519(&public_key)?;
println!("DID: {}", did);
// Output: did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
// Resolve DID to DID Document
let resolver = DidResolver::new();
let doc = resolver.resolve(&did).await?;
println!("DID Document: {}", serde_json::to_string_pretty(&doc)?);
Ok(())
}use oxirs_did::{CredentialIssuer, CredentialSubject, DidResolver, Keystore};
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Issuer identity — the keystore generates and holds the signing key
let keystore = Arc::new(Keystore::new());
let issuer_did = keystore.generate_ed25519().await?;
// Create credential subject
let subject = CredentialSubject::new(Some("did:key:z6Mk..."))
.with_claim("email", "alice@example.com")
.with_claim("role", "Researcher");
// Issue (sign) the credential
let resolver = Arc::new(DidResolver::new());
let issuer = CredentialIssuer::new(keystore, resolver);
let vc = issuer
.issue(&issuer_did, subject, vec!["EmailCredential".to_string()])
.await?;
println!("{}", serde_json::to_string_pretty(&vc)?);
Ok(())
}use oxirs_did::{CredentialVerifier, DidResolver, VerifiableCredential};
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let resolver = Arc::new(DidResolver::new());
let verifier = CredentialVerifier::new(resolver);
// Parse a VC received as JSON (e.g. from an API or file)
let vc: VerifiableCredential = serde_json::from_str(vc_json)?;
// Verify
let result = verifier.verify(&vc).await?;
if result.valid {
println!("✓ Credential is VALID");
println!(" Issued by: {}", result.issuer.unwrap_or_default());
} else {
println!("✗ Credential is INVALID: {}", result.error.unwrap_or_default());
}
for check in &result.checks {
println!(" [{}] {}", if check.passed { "✓" } else { "✗" }, check.name);
}
Ok(())
}use oxirs_did::signed_graph::{SignedGraph, RdfTriple};
use oxirs_did::proof::ed25519::Ed25519Signer;
use oxirs_did::{Did, DidResolver};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create signer
let signer = Ed25519Signer::generate();
let did = Did::new_key_ed25519(&signer.public_key_bytes())?;
// Create RDF graph
let triples = vec![
RdfTriple::iri(
"http://data.example/entity-1",
"http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
"http://schema.org/Dataset"
),
];
// Sign graph
let graph = SignedGraph::new(
"http://data.example/research-2025",
triples,
did.clone()
);
let signed = graph.sign(&signer)?;
println!("Graph hash: {}", signed.hash()?);
// Verify later
let resolver = DidResolver::new();
let valid = signed.verify(&resolver).await?;
println!("Verification: {}", if valid.valid { "✓" } else { "✗" });
Ok(())
}No network required, deterministic from public key:
did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
└─ Base58btc(0xed01 + Ed25519_public_key)
HTTPS-based resolution:
did:web:example.com
→ https://example.com/.well-known/did.json
did:web:example.com:users:alice
→ https://example.com/users/alice/did.json
did:web:example.com%3A8080
→ https://example.com:8080/.well-known/did.json
- did:ethr —
did:ethr:[network:]<ethereum-address>, based on the ERC-1056 Ethereum DID Registry contract - did:ion —
did:ion:<unique-suffix>, Sidetree-anchored DIDs on the ION network (Bitcoin-anchored PKI) - did:pkh —
did:pkh:<CAIP-2 chain namespace>:<account address>, wraps an existing blockchain account (Ethereum, Solana, Bitcoin, ...) as a DID with no separate registration step
[dependencies]
oxirs-did = { version = "0.4.1", features = ["did-web", "signed-graphs"] }Available features:
did-key(default) - did:key methoddid-web- did:web method (requires reqwest)did-ebsi- European Blockchain Services Infrastructure (requires reqwest)did-ethr(default) - did:ethr method (ERC-1056)did-ion(default) - did:ion method (Sidetree/ION)vc-data-model-2(default) - W3C VC 2.0signed-graphs- RDF graph signing/verificationkey-management- Key storagebbs-plus(default) - BBS+ signatures for selective disclosurezkp(default) - ZKP-based selective disclosure (Pedersen commitments)zkp-ristretto- Hardened Pedersen commitments over the Ristretto255 groupkeygen- RSA key-pair generation helpersfips- FIPS 140-2 cryptographic boundary marker (seedocs/policies/fips-boundary.md)
- Trustworthy AI: Sign training datasets for provenance tracking
- IoT Identity: Decentralized identity for edge devices
- Supply Chain: Verifiable product certifications
- Research Data: Signed datasets for reproducibility
- Federated Systems: Self-sovereign identity across systems
ed25519-dalek- Ed25519 signaturesp256,bls12_381_plus,rsa- Additional signature suites (ECDSA P-256, BLS12-381/BBS+, RSA)sha2,sha3,hmac- Cryptographic hashingbs58- Multiformat (multibase) encodingscirs2-core- Secure random number generation
Licensed under the Apache License, Version 2.0.