1use base64::engine::general_purpose::URL_SAFE_NO_PAD;
4use base64::Engine;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8use crate::bridges::{Bridge, BridgeError, BridgeKind};
9use crate::generated::{
10 ActorIdentity, ActorIdentity_IdentityVersion, ActorType, AuthorityRoot, AuthorityRoot_Kind,
11 PublicKey, PublicKey_Purpose, TrustLevel,
12};
13
14#[derive(Clone, Debug, Serialize, Deserialize)]
15pub struct DidVerificationMethod {
16 pub id: String,
17 #[serde(rename = "type")]
18 pub kind: String,
19 pub controller: String,
20 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub public_key_multibase: Option<String>,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
23 pub public_key_jwk: Option<Value>,
24}
25
26#[derive(Clone, Debug, Serialize, Deserialize)]
27pub struct DidDocument {
28 pub id: String,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub verification_method: Option<Vec<DidVerificationMethod>>,
31 #[serde(default, skip_serializing_if = "Option::is_none")]
32 pub authentication: Option<Vec<Value>>,
33 #[serde(default, skip_serializing_if = "Option::is_none")]
34 pub controller: Option<Value>,
35}
36
37#[derive(Clone, Debug, Default)]
38pub struct DidBridgeConfig {
39 pub bridge_id: String,
40 pub trust_domain: String,
41 pub allowed_methods: Option<Vec<String>>,
42}
43
44pub struct DidBridge {
45 cfg: DidBridgeConfig,
46}
47
48impl DidBridge {
49 pub fn new(cfg: DidBridgeConfig) -> Self {
50 DidBridge { cfg }
51 }
52
53 pub fn resolve_did_key(&self, did_url: &str) -> Result<DidDocument, BridgeError> {
54 let method = parse_did_method(did_url)?;
55 if let Some(allow) = &self.cfg.allowed_methods {
56 if !allow.iter().any(|m| m == method) {
57 return Err(BridgeError::Rejected(format!(
58 "DID method {} not in allow-list",
59 method
60 )));
61 }
62 }
63 if method != "key" {
64 return Err(BridgeError::Unsupported(format!(
65 "method {} not supported by built-in resolver; provide a custom resolver",
66 method
67 )));
68 }
69 let multibase = did_url
70 .strip_prefix("did:key:")
71 .ok_or_else(|| BridgeError::InvalidInput(format!("not did:key: {}", did_url)))?;
72 let id = format!("did:key:{}", multibase);
73 Ok(DidDocument {
74 id: id.clone(),
75 verification_method: Some(vec![DidVerificationMethod {
76 id: format!("{}#{}", id, multibase),
77 kind: "Ed25519VerificationKey2020".into(),
78 controller: id.clone(),
79 public_key_multibase: Some(multibase.to_string()),
80 public_key_jwk: None,
81 }]),
82 authentication: Some(vec![Value::String(format!("{}#{}", id, multibase))]),
83 controller: Some(Value::String(id)),
84 })
85 }
86
87 pub fn accept(&self, document: &DidDocument) -> Result<ActorIdentity, BridgeError> {
88 let vms = document
89 .verification_method
90 .as_ref()
91 .ok_or_else(|| BridgeError::Rejected("DID has no verification methods".into()))?;
92 if vms.is_empty() {
93 return Err(BridgeError::Rejected(
94 "DID verification_method is empty".into(),
95 ));
96 }
97 let vm = &vms[0];
98 let pk = extract_public_key(vm).ok_or_else(|| {
99 BridgeError::Unsupported(format!("vm {} has no usable public key", vm.id))
100 })?;
101 let actor_id = format!(
102 "tf:actor:human:{}/{}",
103 self.cfg.trust_domain,
104 url_encode(&document.id)
105 );
106 Ok(ActorIdentity {
107 identity_version: ActorIdentity_IdentityVersion::V1,
108 actor_id,
109 actor_type: ActorType::Human,
110 instance_id: None,
111 public_keys: vec![PublicKey {
112 key_id: vm.id.clone(),
113 algorithm: pk.algorithm,
114 public_key: base64::engine::general_purpose::STANDARD.encode(&pk.bytes),
115 purpose: PublicKey_Purpose::Signing,
116 valid_from: None,
117 valid_until: None,
118 }],
119 trust_levels: vec![TrustLevel::T2],
120 authority_roots: vec![AuthorityRoot {
121 kind: AuthorityRoot_Kind::Federation,
122 id: vm.controller.clone(),
123 }],
124 attestations: None,
125 valid_from: now_iso8601(),
126 valid_until: None,
127 revocation_ref: None,
128 signature: None,
129 })
130 }
131}
132
133impl Bridge for DidBridge {
134 fn bridge_id(&self) -> &str {
135 &self.cfg.bridge_id
136 }
137 fn kind(&self) -> BridgeKind {
138 BridgeKind::Did
139 }
140 fn trust_domain(&self) -> &str {
141 &self.cfg.trust_domain
142 }
143}
144
145struct ProjectedKey {
146 algorithm: String,
147 bytes: Vec<u8>,
148}
149
150fn extract_public_key(vm: &DidVerificationMethod) -> Option<ProjectedKey> {
151 if let Some(mb) = &vm.public_key_multibase {
152 let decoded = decode_multibase(mb)?;
153 if decoded.len() >= 2 && decoded[0] == 0xed && decoded[1] == 0x01 {
154 return Some(ProjectedKey {
155 algorithm: "ed25519".into(),
156 bytes: decoded[2..].to_vec(),
157 });
158 }
159 return Some(ProjectedKey {
160 algorithm: vm.kind.to_lowercase(),
161 bytes: decoded,
162 });
163 }
164 if let Some(jwk) = &vm.public_key_jwk {
165 if jwk.get("kty").and_then(Value::as_str) == Some("OKP")
166 && jwk.get("crv").and_then(Value::as_str) == Some("Ed25519")
167 {
168 if let Some(x) = jwk.get("x").and_then(Value::as_str) {
169 let bytes = URL_SAFE_NO_PAD.decode(x).ok()?;
170 return Some(ProjectedKey {
171 algorithm: "ed25519".into(),
172 bytes,
173 });
174 }
175 }
176 }
177 None
178}
179
180fn parse_did_method(did: &str) -> Result<&str, BridgeError> {
181 let rest = did
182 .strip_prefix("did:")
183 .ok_or_else(|| BridgeError::InvalidInput(format!("not a DID: {}", did)))?;
184 let end = rest
185 .find(':')
186 .ok_or_else(|| BridgeError::InvalidInput(format!("not a DID: {}", did)))?;
187 Ok(&rest[..end])
188}
189
190fn url_encode(s: &str) -> String {
191 let mut out = String::with_capacity(s.len());
192 for b in s.bytes() {
193 match b {
194 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
195 out.push(b as char);
196 }
197 _ => out.push_str(&format!("%{:02X}", b)),
198 }
199 }
200 out
201}
202
203fn decode_multibase(s: &str) -> Option<Vec<u8>> {
204 if s.is_empty() {
205 return None;
206 }
207 let prefix = s.as_bytes()[0];
208 let body = &s[1..];
209 match prefix {
210 b'z' => base58btc_decode(body),
211 b'm' => base64::engine::general_purpose::STANDARD.decode(body).ok(),
212 b'u' => URL_SAFE_NO_PAD.decode(body).ok(),
213 _ => None,
214 }
215}
216
217const BASE58_ALPHABET: &[u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
218
219fn base58btc_decode(s: &str) -> Option<Vec<u8>> {
220 if s.is_empty() {
221 return Some(Vec::new());
222 }
223 let mut zeros = 0usize;
224 while zeros < s.len() && s.as_bytes()[zeros] == b'1' {
225 zeros += 1;
226 }
227 let size = ((s.len() - zeros) as f64 * 0.733).ceil() as usize + 1;
228 let mut b256 = vec![0u8; size];
229 for i in zeros..s.len() {
230 let c = s.as_bytes()[i];
231 let idx = BASE58_ALPHABET.iter().position(|&b| b == c)?;
232 let mut carry = idx;
233 for j in (0..size).rev() {
234 carry += b256[j] as usize * 58;
235 b256[j] = (carry & 0xff) as u8;
236 carry >>= 8;
237 }
238 if carry != 0 {
239 return None;
240 }
241 }
242 let mut start = 0usize;
243 while start < size && b256[start] == 0 {
244 start += 1;
245 }
246 let mut out = vec![0u8; zeros];
247 out.extend_from_slice(&b256[start..]);
248 Some(out)
249}
250
251pub fn ed25519_public_key_to_did_key(pub_bytes: &[u8]) -> Result<String, BridgeError> {
252 if pub_bytes.len() != 32 {
253 return Err(BridgeError::InvalidInput(format!(
254 "ed25519 public key must be 32 bytes, got {}",
255 pub_bytes.len()
256 )));
257 }
258 let mut prefixed = Vec::with_capacity(2 + 32);
259 prefixed.push(0xed);
260 prefixed.push(0x01);
261 prefixed.extend_from_slice(pub_bytes);
262 Ok(format!("z{}", base58btc_encode(&prefixed)))
263}
264
265fn base58btc_encode(bytes: &[u8]) -> String {
266 if bytes.is_empty() {
267 return String::new();
268 }
269 let mut zeros = 0usize;
270 while zeros < bytes.len() && bytes[zeros] == 0 {
271 zeros += 1;
272 }
273 let size = ((bytes.len() as f64) * 1.366).ceil() as usize + 1;
274 let mut b58 = vec![0u8; size];
275 for &byte in bytes.iter().skip(zeros) {
276 let mut carry = byte as usize;
277 for j in (0..size).rev() {
278 carry += b58[j] as usize * 256;
279 b58[j] = (carry % 58) as u8;
280 carry /= 58;
281 }
282 }
283 let mut start = 0usize;
284 while start < size && b58[start] == 0 {
285 start += 1;
286 }
287 let mut out = String::new();
288 for _ in 0..zeros {
289 out.push('1');
290 }
291 for &b in &b58[start..] {
292 out.push(BASE58_ALPHABET[b as usize] as char);
293 }
294 out
295}
296
297fn now_iso8601() -> String {
298 let secs = std::time::SystemTime::now()
299 .duration_since(std::time::UNIX_EPOCH)
300 .unwrap_or_default()
301 .as_secs() as i64;
302 let (y, m, d, h, mi, s) = secs_to_ymdhms(secs);
303 format!("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z", y, m, d, h, mi, s)
304}
305
306fn secs_to_ymdhms(secs: i64) -> (i32, u32, u32, u32, u32, u32) {
307 let days = secs.div_euclid(86_400);
308 let time = secs.rem_euclid(86_400);
309 let hour = (time / 3600) as u32;
310 let minute = ((time % 3600) / 60) as u32;
311 let second = (time % 60) as u32;
312 let z = days + 719_468;
313 let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
314 let doe = (z - era * 146_097) as u64;
315 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
316 let y = yoe as i64 + era * 400;
317 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
318 let mp = (5 * doy + 2) / 153;
319 let d = (doy - (153 * mp + 2) / 5 + 1) as u32;
320 let m = if mp < 10 {
321 (mp + 3) as u32
322 } else {
323 (mp - 9) as u32
324 };
325 let year = if m <= 2 { y + 1 } else { y };
326 (year as i32, m, d, hour, minute, second)
327}