1use crate::error::VerifyError;
7
8pub const ED25519_MULTICODEC_PREFIX: [u8; 2] = [0xed, 0x01];
10
11pub fn decode_did_key(did: &str) -> Result<[u8; 32], VerifyError> {
16 let multibase = did
17 .strip_prefix("did:key:")
18 .ok_or(VerifyError::DidKey("missing 'did:key:' prefix"))?;
19 decode_multibase(multibase)
20}
21
22pub fn decode_multibase(multibase: &str) -> Result<[u8; 32], VerifyError> {
24 let b58 = multibase
25 .strip_prefix('z')
26 .ok_or(VerifyError::DidKey("missing 'z' multibase prefix"))?;
27 let decoded = bs58::decode(b58)
28 .into_vec()
29 .map_err(|_| VerifyError::DidKey("invalid base58btc"))?;
30 if decoded.len() != 2 + 32 {
31 return Err(VerifyError::DidKey("unexpected multicodec length"));
32 }
33 if decoded[0..2] != ED25519_MULTICODEC_PREFIX {
34 return Err(VerifyError::DidKey("not an ed25519-pub multicodec (expected 0xed01)"));
35 }
36 let mut key = [0u8; 32];
37 key.copy_from_slice(&decoded[2..]);
38 Ok(key)
39}
40
41pub fn encode_multibase(raw: &[u8; 32]) -> String {
43 let mut buf = Vec::with_capacity(2 + 32);
44 buf.extend_from_slice(&ED25519_MULTICODEC_PREFIX);
45 buf.extend_from_slice(raw);
46 format!("z{}", bs58::encode(buf).into_string())
47}
48
49pub fn encode_did_key(raw: &[u8; 32]) -> String {
51 format!("did:key:{}", encode_multibase(raw))
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 const DID: &str = "did:key:z6Mko5TBPGKHkCxSgmf3aC6p6SGj2auwCfRmBydXJFEwL4ev";
59 const RAW_HEX: &str = "8022fe847be6106443a4030d74d390c8d9a91319b9f51526bc7c3d88a27c9b7b";
60
61 #[test]
62 fn decode_roundtrip() {
63 let key = decode_did_key(DID).unwrap();
64 assert_eq!(hex::encode(key), RAW_HEX);
65 assert_eq!(encode_did_key(&key), DID);
66 }
67
68 #[test]
69 fn rejects_non_ed25519() {
70 let mut bad = vec![0x12u8, 0x00];
72 bad.extend_from_slice(&[0u8; 32]);
73 let mb = format!("z{}", bs58::encode(bad).into_string());
74 assert!(decode_multibase(&mb).is_err());
75 }
76}