agent_toolprint/
did_key.rs1use async_trait::async_trait;
4use chrono::{DateTime, Utc};
5
6use crate::error::Error;
7
8const ED25519_MULTICODEC: [u8; 2] = [0xed, 0x01];
9
10#[async_trait]
12pub trait Resolver: Send + Sync {
13 async fn resolve(&self, did: &str, key_id: &str, at_time: DateTime<Utc>) -> Option<Vec<u8>>;
14}
15
16pub fn parse_did_key(did: &str) -> Result<Vec<u8>, Error> {
17 const PREFIX: &str = "did:key:";
18 if !did.starts_with(PREFIX) {
19 return Err(Error::DidKey("not a did:key".into()));
20 }
21 let multibase = &did[PREFIX.len()..];
22 if !multibase.starts_with('z') {
23 return Err(Error::DidKey(
24 "did:key must use 'z' (base58btc) multibase prefix".into(),
25 ));
26 }
27 let decoded = bs58::decode(&multibase[1..])
28 .into_vec()
29 .map_err(|e| Error::DidKey(format!("base58 decode: {e}")))?;
30 if decoded.len() != 34
31 || decoded[0] != ED25519_MULTICODEC[0]
32 || decoded[1] != ED25519_MULTICODEC[1]
33 {
34 return Err(Error::DidKey(
35 "did:key must encode an Ed25519 public key (multicodec 0xed01)".into(),
36 ));
37 }
38 Ok(decoded[2..].to_vec())
39}
40
41pub fn did_key_from_ed25519_pubkey(pk: &[u8]) -> Result<String, Error> {
42 if pk.len() != 32 {
43 return Err(Error::DidKey("Ed25519 public key must be 32 bytes".into()));
44 }
45 let mut mc = Vec::with_capacity(34);
46 mc.extend_from_slice(&ED25519_MULTICODEC);
47 mc.extend_from_slice(pk);
48 Ok(format!("did:key:z{}", bs58::encode(mc).into_string()))
49}
50
51pub struct DidKeyResolver;
53
54#[async_trait]
55impl Resolver for DidKeyResolver {
56 async fn resolve(&self, did: &str, _key_id: &str, _at_time: DateTime<Utc>) -> Option<Vec<u8>> {
57 parse_did_key(did).ok()
58 }
59}