macula_rust/ucan/
did_key.rs1use super::Refusal;
7use crate::node_key::carried_key_well_formed;
8use crate::profile::Profile;
9
10const CODEC_MLDSA87: u64 = 0x1212;
11const CODEC_MLDSA87_RSA4096: u64 = 0x30_0087;
12
13const PREFIX: &str = "did:key:z";
14
15pub const MAX_DID_KEY_ENCODED: usize = 4_400;
22const ALPHABET: &[u8; 58] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
23
24pub fn did_key(carried: &[u8], profile: Profile) -> String {
26 let mut bytes = varint(codec(profile));
27 bytes.extend_from_slice(carried);
28 format!("{PREFIX}{}", base58btc_encode(&bytes))
29}
30
31pub fn carried_key(did: &str, profile: Profile) -> Result<Vec<u8>, Refusal> {
34 let encoded = did.strip_prefix(PREFIX).ok_or(Refusal::Malformed)?;
35 if encoded.len() > MAX_DID_KEY_ENCODED {
36 return Err(Refusal::Malformed);
37 }
38 let prefix = varint(codec(profile));
39 let decoded = base58btc_decode(encoded).ok_or(Refusal::Malformed)?;
40 match decoded.strip_prefix(prefix.as_slice()) {
41 Some(carried) if !carried.is_empty() && carried_key_well_formed(carried, profile) => {
42 Ok(carried.to_vec())
43 }
44 _ => Err(Refusal::Malformed),
45 }
46}
47
48fn codec(profile: Profile) -> u64 {
49 match profile {
50 Profile::PqPure => CODEC_MLDSA87,
51 Profile::PqHybrid => CODEC_MLDSA87_RSA4096,
52 }
53}
54
55fn varint(mut n: u64) -> Vec<u8> {
57 let mut out = Vec::new();
58 while n >= 0x80 {
59 out.push((n & 0x7f) as u8 | 0x80);
60 n >>= 7;
61 }
62 out.push(n as u8);
63 out
64}
65
66fn base58btc_encode(bytes: &[u8]) -> String {
69 let zeros = bytes.iter().take_while(|&&b| b == 0).count();
70 let mut digits: Vec<u8> = Vec::new();
71 for &byte in &bytes[zeros..] {
72 let mut carry = byte as u32;
73 for digit in digits.iter_mut() {
74 carry += (*digit as u32) << 8;
75 *digit = (carry % 58) as u8;
76 carry /= 58;
77 }
78 while carry > 0 {
79 digits.push((carry % 58) as u8);
80 carry /= 58;
81 }
82 }
83 let mut out = String::with_capacity(zeros + digits.len());
84 out.extend(std::iter::repeat_n('1', zeros));
85 out.extend(digits.iter().rev().map(|&d| ALPHABET[d as usize] as char));
86 out
87}
88
89fn base58btc_decode(text: &str) -> Option<Vec<u8>> {
92 let ones = text.bytes().take_while(|&b| b == b'1').count();
93 let mut value: Vec<u8> = Vec::new();
94 for c in text[ones..].bytes() {
95 let mut carry = ALPHABET.iter().position(|&a| a == c)? as u32;
96 for byte in value.iter_mut() {
97 carry += (*byte as u32) * 58;
98 *byte = (carry & 0xff) as u8;
99 carry >>= 8;
100 }
101 while carry > 0 {
102 value.push((carry & 0xff) as u8);
103 carry >>= 8;
104 }
105 }
106 let mut out = vec![0u8; ones];
107 out.extend(value.iter().rev());
108 Some(out)
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn base58btc_round_trips_and_keeps_leading_zeros() {
117 for bytes in [
118 vec![],
119 vec![0],
120 vec![0, 0, 1],
121 vec![0xff; 40],
122 (0u8..=255).collect::<Vec<_>>(),
123 ] {
124 let text = base58btc_encode(&bytes);
125 assert_eq!(base58btc_decode(&text), Some(bytes.clone()), "{text}");
126 }
127 assert_eq!(base58btc_encode(&[0, 0, 0x3a]), "1121");
128 assert_eq!(base58btc_decode("0OIl"), None);
129 }
130
131 #[test]
132 fn the_key_types_are_leb128_varints() {
133 assert_eq!(varint(CODEC_MLDSA87), vec![0x92, 0x24]);
134 assert_eq!(varint(CODEC_MLDSA87_RSA4096), vec![0x87, 0x81, 0xc0, 0x01]);
135 }
136
137 #[test]
138 fn a_did_key_of_another_shape_carries_no_key() {
139 assert_eq!(
140 carried_key("did:key:x", Profile::PqPure),
141 Err(Refusal::Malformed)
142 );
143 assert_eq!(
144 carried_key("did:key:z", Profile::PqPure),
145 Err(Refusal::Malformed)
146 );
147 let short = did_key(&[1, 2, 3], Profile::PqPure);
148 assert_eq!(
149 carried_key(&short, Profile::PqPure),
150 Err(Refusal::Malformed)
151 );
152 }
153}