did_simple/methods/
key.rs1use std::fmt::Display;
6
7use crate::{
8 key_algos::{Ed25519, KeyAlgo, StaticSigningAlgo},
9 url::{DidMethod, DidUrl},
10 utf8bytes::Utf8Bytes,
11 varint::decode_varint,
12};
13
14#[derive(Debug, Eq, PartialEq, Hash, Clone)]
17pub struct DidKey {
18 s: Utf8Bytes,
20 mb_value: Vec<u8>,
22 key_algo: KeyAlgo,
23 pubkey_bytes: std::ops::RangeFrom<usize>,
25}
26
27pub const PREFIX: &str = "did:key:";
28
29impl DidKey {
30 pub const PREFIX: &'static str = PREFIX;
31
32 pub fn as_str(&self) -> &str {
34 self.s.as_str()
35 }
36
37 pub fn as_slice(&self) -> &[u8] {
39 self.s.as_slice()
40 }
41
42 pub fn as_utf8_bytes(&self) -> &Utf8Bytes {
45 &self.s
46 }
47
48 pub fn key_algo(&self) -> KeyAlgo {
49 self.key_algo
50 }
51
52 pub fn pub_key(&self) -> &[u8] {
54 let result = match self.key_algo {
55 KeyAlgo::Ed25519 => &self.mb_value[self.pubkey_bytes.clone()],
56 };
57 debug_assert_eq!(result.len(), self.key_algo.verifying_key_len());
58 result
59 }
60}
61
62fn decode_multibase(
63 s: &Utf8Bytes,
64 out_buf: &mut Vec<u8>,
65) -> Result<(), MultibaseDecodeError> {
66 out_buf.clear();
67 let multibase_part = &s.as_slice()[PREFIX.len()..];
69 let base = multibase_part[0];
71 if base != b'z' {
72 return Err(MultibaseDecodeError::WrongBase(base));
73 }
74 bs58::decode(&multibase_part[1..])
75 .with_alphabet(bs58::Alphabet::BITCOIN)
76 .onto(out_buf)?;
77 Ok(())
78}
79
80#[derive(thiserror::Error, Debug)]
81pub enum MultibaseDecodeError {
82 #[error(
83 "Expected \"base58-btc\" encoding which should be identified in multibase as ascii 'z' (0x7a) but got {0:x}"
84 )]
85 WrongBase(u8),
86 #[error(transparent)]
87 Bs58(#[from] bs58::decode::Error),
88}
89
90impl TryFrom<DidUrl> for DidKey {
91 type Error = FromUrlError;
92
93 fn try_from(value: DidUrl) -> Result<Self, Self::Error> {
94 let m = value.method();
95 if m != DidMethod::Key {
96 return Err(FromUrlError::WrongMethod(m));
97 }
98 debug_assert_eq!(
99 value.as_slice().len() - value.method_specific_id().as_slice().len(),
100 PREFIX.len(),
101 "sanity check that prefix has expected length"
102 );
103
104 let s = value.as_utf8_bytes().clone();
105 let mut decoded_multibase = Vec::new();
106 decode_multibase(&s, &mut decoded_multibase)?;
107
108 let (multicodec_key_algo, tail_bytes) = decode_varint(&decoded_multibase)?;
110 let (key_algo, pub_key_len) = match multicodec_key_algo {
111 Ed25519::MULTICODEC_VALUE => (KeyAlgo::Ed25519, Ed25519::SIGNING_KEY_LEN),
112 _ => return Err(FromUrlError::UnknownKeyAlgo(multicodec_key_algo)),
113 };
114
115 if tail_bytes.len() != pub_key_len {
116 return Err(FromUrlError::MismatchedPubkeyLen(key_algo, pub_key_len));
117 }
118
119 let pubkey_bytes = (decoded_multibase.len() - pub_key_len)..;
120
121 Ok(Self {
122 s,
123 mb_value: decoded_multibase,
124 key_algo,
125 pubkey_bytes,
126 })
127 }
128}
129
130#[derive(thiserror::Error, Debug)]
131pub enum FromUrlError {
132 #[error("Expected \"key\" method but got {0:?}")]
133 WrongMethod(DidMethod),
134 #[error(transparent)]
135 MultibaseDecode(#[from] MultibaseDecodeError),
136 #[error("unknown multicodec value for key algorithm: decoded varint as {0}")]
137 UnknownKeyAlgo(u16),
138 #[error(transparent)]
139 Varint(#[from] crate::varint::DecodeError),
140 #[error("{0:?} requires pubkeys of length {} but got {1} bytes", .0.verifying_key_len())]
141 MismatchedPubkeyLen(KeyAlgo, usize),
142}
143
144impl Display for DidKey {
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146 self.as_str().fmt(f)
147 }
148}
149
150#[cfg(test)]
151mod test {
152 use super::*;
153
154 use eyre::WrapErr;
155 use hex_literal::hex;
156 use std::str::FromStr;
157 fn ed25519_examples() -> &'static [&'static str] {
160 &[
161 "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
162 "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
163 "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
164 ]
165 }
166
167 #[test]
168 fn test_try_from_url() -> eyre::Result<()> {
169 for &example in ed25519_examples() {
170 let url = DidUrl::from_str(example)
171 .wrap_err_with(|| format!("failed to parse DidUrl from {example}"))?;
172 assert_eq!(example, url.as_str());
173 let key_from_url = DidKey::try_from(url.clone())
174 .wrap_err_with(|| format!("failed to parse DidKey from {url}"))?;
175 assert_eq!(example, key_from_url.as_str());
176 assert_eq!(key_from_url.key_algo(), Ed25519);
177 }
178 Ok(())
179 }
180
181 #[test]
182 fn test_decode_multibase() -> eyre::Result<()> {
183 #[derive(Debug)]
184 struct Example {
185 decoded: &'static [u8],
186 encoded: &'static str,
187 }
188 let examples = [
190 Example {
191 decoded: b"Hello World!",
192 encoded: "2NEpo7TZRRrLZSi2U",
193 },
194 Example {
195 decoded: b"The quick brown fox jumps over the lazy dog.",
196 encoded: "USm3fpXnKG5EUBx2ndxBDMPVciP5hGey2Jh4NDv6gmeo1LkMeiKrLJUUBk6Z",
197 },
198 Example {
199 decoded: &hex!("0000287fb4cd"),
200 encoded: "11233QC4",
201 },
202 ];
203
204 let mut buf = Vec::new();
205 for e @ Example { decoded, encoded } in examples {
206 let s = format!("{}z{encoded}", PREFIX).into();
207 decode_multibase(&s, &mut buf)
208 .wrap_err_with(|| format!("Failed to decode example {e:?}"))?;
209 assert_eq!(buf, decoded, "failed comparison in example {e:?}");
210 }
211
212 Ok(())
213 }
214}