use std::error::Error;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::{SystemTime, UNIX_EPOCH};
pub type Web5Result<T> = Result<T, Web5Error>;
#[derive(Debug, thiserror::Error)]
pub enum Web5Error {
#[error("Identity error: {0}")]
Identity(String),
#[error("Protocol error: {0}")]
Protocol(String),
#[error("Communication error: {0}")]
Communication(String),
#[error("Storage error: {0}")]
Storage(String),
#[error("Credential error: {0}")]
Credential(String),
#[error("Not found: {0}")]
NotFound(String),
#[error("DWN error: {0}")]
DWNError(String),
#[error("Serialization error: {0}")]
SerializationError(String),
}
impl From<Box<dyn std::error::Error>> for Web5Error {
fn from(err: Box<dyn std::error::Error>) -> Self {
Web5Error::Protocol(err.to_string())
}
}
impl From<String> for Web5Error {
fn from(err: String) -> Self {
Web5Error::Protocol(err)
}
}
impl From<&str> for Web5Error {
fn from(err: &str) -> Self {
Web5Error::Protocol(err.to_string())
}
}
#[derive(Clone, Debug)]
pub struct DIDManager {
dids: Arc<Mutex<HashMap<String, DID>>>,
default_did: Option<String>,
method: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DID {
pub id: String,
pub document: DIDDocument,
#[serde(skip_serializing)]
pub private_keys: HashMap<String, Vec<u8>>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DIDDocument {
#[serde(rename = "@context")]
pub context: Vec<String>,
pub id: String,
#[serde(default)]
pub verification_method: Vec<VerificationMethod>,
#[serde(default)]
pub authentication: Vec<String>,
#[serde(default)]
pub assertion_method: Vec<String>,
#[serde(default)]
pub service: Vec<Service>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct VerificationMethod {
pub id: String,
#[serde(rename = "type")]
pub vm_type: String,
pub controller: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub public_key_jwk: Option<JWK>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct JWK {
pub kty: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub crv: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub x: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub y: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub kid: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Service {
pub id: String,
#[serde(rename = "type")]
pub service_type: String,
pub service_endpoint: String,
}
impl DIDManager {
pub fn new(method: &str) -> Self {
Self {
dids: Arc::new(Mutex::new(HashMap::new())),
default_did: None,
method: method.to_string(),
}
}
pub fn create_did(&self) -> Web5Result<DID> {
let id = format!("did:{}:{}", self.method, generate_random_id());
let private_key = generate_private_key();
let public_key_jwk = generate_public_key_jwk(&private_key);
let verification_method = VerificationMethod {
id: format!("{id}#key-1"),
vm_type: "JsonWebKey2020".to_string(),
controller: id.clone(),
public_key_jwk: Some(public_key_jwk),
};
let document = DIDDocument {
context: vec!["https://www.w3.org/ns/did/v1".to_string()],
id: id.clone(),
verification_method: vec![verification_method],
authentication: vec![format!("{}#key-1", id)],
assertion_method: vec![format!("{}#key-1", id)],
service: Vec::new(),
};
let mut private_keys = HashMap::new();
private_keys.insert("key-1".to_string(), private_key);
let did = DID {
id: id.clone(),
document,
private_keys,
};
{
let mut dids = self
.dids
.lock()
.map_err(|e| format!("Mutex lock error: {e}"))?;
dids.insert(id.clone(), did.clone());
}
Ok(did)
}
pub fn resolve_did(&self, did: &str) -> Result<DIDDocument, Box<dyn Error>> {
let dids = self
.dids
.lock()
.map_err(|e| format!("Mutex lock error: {e}"))?;
if let Some(did_obj) = dids.get(did) {
return Ok(did_obj.document.clone());
}
Err(format!("DID not found: {did}").into())
}
pub fn set_default_did(&mut self, did: &str) -> Result<(), Box<dyn Error>> {
let dids = self
.dids
.lock()
.map_err(|e| format!("Mutex lock error: {e}"))?;
if dids.contains_key(did) {
self.default_did = Some(did.to_string());
Ok(())
} else {
Err(format!("DID {did} not found").into())
}
}
pub fn get_default_did(&self) -> Result<Option<String>, Box<dyn Error>> {
Ok(self.default_did.clone())
}
pub fn sign(&self, did: &str, data: &[u8]) -> Result<Vec<u8>, Box<dyn Error>> {
let dids = self
.dids
.lock()
.map_err(|e| format!("Mutex lock error: {e}"))?;
let did_obj = dids
.get(did)
.ok_or_else(|| format!("DID not found: {did}"))?;
if let Some((_, private_key_bytes)) = did_obj.private_keys.iter().next() {
let private_key = secp256k1::SecretKey::from_slice(private_key_bytes)
.map_err(|e| format!("Invalid private key: {e}"))?;
let secp = secp256k1::Secp256k1::signing_only();
let hash = {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(data);
hasher.finalize()
};
let message = secp256k1::Message::from_digest_slice(&hash)
.map_err(|e| format!("Failed to create message: {e}"))?;
let signature = secp.sign_ecdsa(&message, &private_key);
Ok(signature.serialize_compact().to_vec())
} else {
Err("No private keys found for DID".into())
}
}
pub fn dids(&self) -> Result<Vec<String>, Box<dyn Error>> {
let dids = self
.dids
.lock()
.map_err(|e| format!("Mutex lock error: {e}"))?;
Ok(dids.keys().cloned().collect())
}
pub fn get_did(&self, did_id: &str) -> Web5Result<Option<DID>> {
let dids = self
.dids
.lock()
.map_err(|e| Web5Error::Storage(format!("Mutex lock error: {e}")))?;
Ok(dids.get(did_id).cloned())
}
pub fn list_dids(&self) -> Vec<DID> {
let dids = self
.dids
.lock()
.unwrap_or_else(|_| panic!("Failed to lock mutex"));
dids.values().cloned().collect()
}
}
#[derive(Debug, Clone)]
pub struct IdentityManager {
did_manager: DIDManager,
}
impl IdentityManager {
pub fn new(namespace: &str) -> Self {
Self {
did_manager: DIDManager::new(namespace),
}
}
pub fn create_identity(&mut self) -> Web5Result<DID> {
self.did_manager.create_did()
}
pub fn get_identity(&self, did_id: &str) -> Web5Result<Option<DID>> {
self.did_manager.get_did(did_id)
}
pub fn list_identities(&self) -> Vec<DID> {
self.did_manager.list_dids()
}
}
fn generate_random_id() -> String {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
format!("{now:x}")
}
fn generate_private_key() -> Vec<u8> {
use rand::RngCore;
let mut key = vec![0u8; 32];
rand::thread_rng().fill_bytes(&mut key);
key
}
fn generate_public_key_jwk(private_key: &[u8]) -> JWK {
use base64::Engine;
JWK {
kty: "EC".to_string(),
crv: Some("secp256k1".to_string()),
x: Some(base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&private_key[..16])),
y: Some(base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&private_key[16..])),
kid: Some("key-1".to_string()),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_did() -> Result<(), Box<dyn Error>> {
let manager = DIDManager::new("example");
let did = manager.create_did()?;
assert!(!did.id.is_empty());
assert!(did.id.starts_with("did:example:"));
assert!(!did.private_keys.is_empty());
Ok(())
}
#[test]
fn test_default_did() -> Result<(), Box<dyn Error>> {
let mut manager = DIDManager::new("example");
let did = manager.create_did()?;
assert!(manager.get_default_did()?.is_none());
manager.set_default_did(&did.id)?;
assert_eq!(manager.get_default_did()?.unwrap(), did.id);
Ok(())
}
}