1use crate::{Error, Result};
10use ed25519_dalek::VerifyingKey;
11use std::fmt;
12use std::str::FromStr;
13
14const ED25519_MULTICODEC: [u8; 2] = [0xed, 0x01];
17
18const BASE58BTC_PREFIX: char = 'z';
20
21#[derive(Debug, Clone, PartialEq, Eq, Hash)]
23pub struct Did {
24 public_key: [u8; 32],
25}
26
27impl Did {
28 pub fn new(public_key: VerifyingKey) -> Self {
30 Self {
31 public_key: public_key.to_bytes(),
32 }
33 }
34
35 pub fn public_key_bytes(&self) -> &[u8; 32] {
37 &self.public_key
38 }
39
40 pub fn public_key(&self) -> Result<VerifyingKey> {
42 VerifyingKey::from_bytes(&self.public_key).map_err(|e| Error::InvalidDid(e.to_string()))
43 }
44
45 pub fn key_id(&self) -> String {
47 let mut bytes = Vec::with_capacity(34);
48 bytes.extend_from_slice(&ED25519_MULTICODEC);
49 bytes.extend_from_slice(&self.public_key);
50 format!("{}{}", BASE58BTC_PREFIX, bs58::encode(&bytes).into_string())
51 }
52}
53
54impl fmt::Display for Did {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 write!(f, "did:key:{}", self.key_id())
57 }
58}
59
60impl FromStr for Did {
61 type Err = Error;
62
63 fn from_str(s: &str) -> Result<Self> {
64 let key_part = s
66 .strip_prefix("did:key:")
67 .ok_or_else(|| Error::InvalidDid("must start with 'did:key:'".into()))?;
68
69 let encoded = key_part
71 .strip_prefix(BASE58BTC_PREFIX)
72 .ok_or_else(|| Error::InvalidDid("must use base58btc encoding (z prefix)".into()))?;
73
74 let bytes = bs58::decode(encoded)
76 .into_vec()
77 .map_err(|e| Error::Base58(e.to_string()))?;
78
79 if bytes.len() != 34 {
81 return Err(Error::InvalidDid(format!(
82 "expected 34 bytes, got {}",
83 bytes.len()
84 )));
85 }
86
87 if bytes[0..2] != ED25519_MULTICODEC {
89 return Err(Error::InvalidDid(
90 "unsupported key type (expected Ed25519 multicodec 0xed01)".into(),
91 ));
92 }
93
94 let mut public_key = [0u8; 32];
96 public_key.copy_from_slice(&bytes[2..34]);
97
98 VerifyingKey::from_bytes(&public_key)
100 .map_err(|e| Error::InvalidDid(format!("invalid Ed25519 key: {}", e)))?;
101
102 Ok(Self { public_key })
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use ed25519_dalek::SigningKey;
110 use rand::rngs::OsRng;
111
112 #[test]
113 fn test_did_roundtrip() {
114 let signing_key = SigningKey::generate(&mut OsRng);
115 let did = Did::new(signing_key.verifying_key());
116
117 let did_str = did.to_string();
118 assert!(did_str.starts_with("did:key:z6Mk"), "got: {}", did_str);
119
120 let parsed: Did = did_str.parse().unwrap();
121 assert_eq!(did, parsed);
122 }
123
124 #[test]
125 fn test_known_vector() {
126 let did_str = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK";
129 let parsed: Did = did_str.parse().unwrap();
130 assert_eq!(parsed.to_string(), did_str);
131 }
132
133 #[test]
134 fn test_invalid_prefix() {
135 let result: std::result::Result<Did, _> = "did:aip:1:abc".parse();
136 assert!(result.is_err());
137 }
138
139 #[test]
140 fn test_invalid_multibase() {
141 let result: std::result::Result<Did, _> =
143 "did:key:f6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".parse();
144 assert!(result.is_err());
145 }
146}