use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use super::crypto::{hex_decode, hex_encode};
use super::error::DidError;
use super::identifier::Did;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct VerificationMethod {
pub id: String,
#[serde(rename = "type")]
pub method_type: String,
pub controller: Did,
pub public_key_hex: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub key_version: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub key_status: Option<String>,
}
impl VerificationMethod {
pub fn local(id: impl Into<String>, controller: Did, public_key: impl AsRef<[u8]>) -> Self {
Self {
id: id.into(),
method_type: "TypesecDemoKey2026".to_owned(),
controller,
public_key_hex: hex_encode(public_key.as_ref()),
key_version: None,
key_status: None,
}
}
pub(super) fn public_key(&self) -> Result<Vec<u8>, DidError> {
hex_decode(&self.public_key_hex)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DidService {
pub id: String,
#[serde(rename = "type")]
pub service_type: String,
pub service_endpoint: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DidDocument {
pub id: Did,
#[serde(default)]
pub verification_method: Vec<VerificationMethod>,
#[serde(default)]
pub authentication: Vec<String>,
#[serde(default)]
pub key_agreement: Vec<String>,
#[serde(default)]
pub service: Vec<DidService>,
}
impl DidDocument {
pub fn single_key(did: Did, public_key: impl AsRef<[u8]>) -> Self {
let key_id = format!("{did}#key-1");
Self {
id: did.clone(),
verification_method: vec![VerificationMethod::local(&key_id, did, public_key)],
authentication: vec![key_id.clone()],
key_agreement: vec![key_id],
service: Vec::new(),
}
}
pub fn with_signing_and_agreement_keys(
did: Did,
signing_public: impl AsRef<[u8]>,
agreement_public: impl AsRef<[u8]>,
) -> Self {
let signing_id = format!("{did}#key-1");
let agreement_id = format!("{did}#key-2");
Self {
id: did.clone(),
verification_method: vec![
VerificationMethod {
id: signing_id.clone(),
method_type: "Ed25519VerificationKey2020".to_owned(),
controller: did.clone(),
public_key_hex: hex_encode(signing_public.as_ref()),
key_version: Some(1),
key_status: Some("active".to_owned()),
},
VerificationMethod {
id: agreement_id.clone(),
method_type: "X25519KeyAgreementKey2020".to_owned(),
controller: did,
public_key_hex: hex_encode(agreement_public.as_ref()),
key_version: Some(1),
key_status: Some("active".to_owned()),
},
],
authentication: vec![signing_id],
key_agreement: vec![agreement_id],
service: Vec::new(),
}
}
fn method(&self, id: &str) -> Option<&VerificationMethod> {
self.verification_method
.iter()
.find(|method| method.id == id)
}
pub(super) fn authentication_key(&self, kid: &str) -> Result<&VerificationMethod, DidError> {
if !self.authentication.iter().any(|id| id == kid) {
return Err(DidError::MissingVerificationMethod(kid.to_owned()));
}
self.method(kid)
.ok_or_else(|| DidError::MissingVerificationMethod(kid.to_owned()))
}
pub(super) fn key_agreement_key(&self) -> Result<&VerificationMethod, DidError> {
let kid = self
.key_agreement
.first()
.ok_or(DidError::MissingKeyAgreement)?;
self.method(kid)
.ok_or_else(|| DidError::MissingVerificationMethod(kid.clone()))
}
pub(super) fn key_agreement_keys(&self) -> Result<Vec<&VerificationMethod>, DidError> {
if self.key_agreement.is_empty() {
return Err(DidError::MissingKeyAgreement);
}
self.key_agreement
.iter()
.map(|kid| {
self.method(kid)
.ok_or_else(|| DidError::MissingVerificationMethod(kid.clone()))
})
.collect()
}
}
pub trait DidResolver: Send + Sync {
fn resolve(&self, did: &Did) -> Result<DidDocument, DidError>;
}
#[derive(Debug, Default, Clone)]
pub struct StaticDidResolver {
documents: HashMap<Did, DidDocument>,
}
impl StaticDidResolver {
pub fn new() -> Self {
Self::default()
}
pub fn with_document(mut self, document: DidDocument) -> Self {
self.documents.insert(document.id.clone(), document);
self
}
}
impl DidResolver for StaticDidResolver {
fn resolve(&self, did: &Did) -> Result<DidDocument, DidError> {
self.documents
.get(did)
.cloned()
.ok_or_else(|| DidError::Unresolved(did.to_string()))
}
}