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