use std::sync::Arc;
use affinidi_crypto::KeyType;
use affinidi_data_integrity::{DataIntegrityError, ResolvedKey, VerificationMethodResolver};
use affinidi_did_common::verification_method::VerificationRelationship;
use affinidi_did_resolver_cache_sdk::DIDCacheClient;
use affinidi_encoding::{ED25519_PUB, P256_PUB, P384_PUB, SECP256K1_PUB, X25519_PUB};
use async_trait::async_trait;
#[derive(Clone)]
pub struct CachedDidResolver {
client: Arc<DIDCacheClient>,
}
impl CachedDidResolver {
pub fn new(client: Arc<DIDCacheClient>) -> Self {
Self { client }
}
pub fn client(&self) -> &Arc<DIDCacheClient> {
&self.client
}
}
#[async_trait]
impl VerificationMethodResolver for CachedDidResolver {
async fn resolve_vm(&self, vm: &str) -> Result<ResolvedKey, DataIntegrityError> {
let did = vm.split('#').next().unwrap_or(vm);
let resolve = self
.client
.resolve(did)
.await
.map_err(|e| DataIntegrityError::Resolver(format!("resolve {did}: {e}")))?;
let doc = resolve.doc;
let fragment = vm.find('#').map(|i| &vm[i..]);
let refers = |id: &str| id == vm || fragment.is_some_and(|f| id == f);
let authorised = |rels: &[VerificationRelationship]| {
rels.iter().find_map(|r| match r {
VerificationRelationship::Reference(id) if refers(id) => Some(None),
VerificationRelationship::VerificationMethod(m) if refers(m.id.as_str()) => {
Some(Some((**m).clone()))
}
_ => None,
})
};
let embedded = authorised(&doc.authentication)
.or_else(|| authorised(&doc.assertion_method))
.ok_or_else(|| {
DataIntegrityError::Resolver(format!(
"verificationMethod {vm} is not an authentication or assertionMethod key of {did}"
))
})?;
let method = match embedded {
Some(method) => method,
None => doc
.verification_method
.iter()
.find(|m| refers(m.id.as_str()))
.cloned()
.ok_or_else(|| {
DataIntegrityError::Resolver(format!(
"verificationMethod {vm} not present in DID document for {did}"
))
})?,
};
let (codec, public_key_bytes) = method
.decode_public_key()
.map_err(|e| DataIntegrityError::Resolver(format!("{vm}: {e}")))?;
let key_type = codec_to_key_type(codec).ok_or_else(|| {
DataIntegrityError::Resolver(format!("unsupported multicodec 0x{codec:x} on {vm}"))
})?;
Ok(ResolvedKey::new(key_type, public_key_bytes))
}
}
fn codec_to_key_type(codec: u64) -> Option<KeyType> {
match codec {
c if c == ED25519_PUB => Some(KeyType::Ed25519),
c if c == X25519_PUB => Some(KeyType::X25519),
c if c == P256_PUB => Some(KeyType::P256),
c if c == P384_PUB => Some(KeyType::P384),
c if c == SECP256K1_PUB => Some(KeyType::Secp256k1),
_ => None,
}
}