1use cid::Cid;
2use ed25519_dalek::{Signature, Verifier, VerifyingKey};
3use ipld_core::ipld::Ipld;
4use serde::{Deserialize, Serialize};
5#[cfg(not(target_arch = "wasm32"))]
6use std::time::{SystemTime, UNIX_EPOCH};
7
8use crate::{
9 did::Did,
10 error::{MaError, Result},
11 key::{ED25519_PUB_CODEC, EDDSA_SIG_CODEC, EncryptionKey, SigningKey, X25519_PUB_CODEC},
12 multiformat::{
13 public_key_multibase_decode, signature_multibase_decode, signature_multibase_encode,
14 },
15};
16
17pub const DEFAULT_DID_CONTEXT: &[&str] = &["https://www.w3.org/ns/did/v1.1"];
18pub const DEFAULT_PROOF_TYPE: &str = "MultiformatSignature2023";
19pub const DEFAULT_PROOF_PURPOSE: &str = "assertionMethod";
20
21pub fn now_iso_utc() -> String {
23 #[cfg(target_arch = "wasm32")]
24 {
25 return js_sys::Date::new_0()
26 .to_iso_string()
27 .as_string()
28 .unwrap_or_else(|| "1970-01-01T00:00:00.000Z".to_string());
29 }
30
31 #[cfg(not(target_arch = "wasm32"))]
32 {
33 let duration = SystemTime::now()
34 .duration_since(UNIX_EPOCH)
35 .unwrap_or_default();
36 unix_millis_to_iso(duration.as_secs(), duration.subsec_millis())
37 }
38}
39
40#[cfg(not(target_arch = "wasm32"))]
41fn unix_millis_to_iso(secs: u64, millis: u32) -> String {
42 let z = (secs / 86400) as i64 + 719468;
44 let era = if z >= 0 { z } else { z - 146096 } / 146097;
45 let doe = (z - era * 146097) as u64;
46 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
47 let y = yoe as i64 + era * 400;
48 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
49 let mp = (5 * doy + 2) / 153;
50 let d = doy - (153 * mp + 2) / 5 + 1;
51 let m = if mp < 10 { mp + 3 } else { mp - 9 };
52 let y = if m <= 2 { y + 1 } else { y };
53 let tod = secs % 86400;
54 format!(
55 "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:03}Z",
56 y,
57 m,
58 d,
59 tod / 3600,
60 (tod % 3600) / 60,
61 tod % 60,
62 millis,
63 )
64}
65
66#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
67pub struct VerificationMethod {
68 pub id: String,
69 #[serde(rename = "type")]
70 pub key_type: String,
71 pub controller: String,
72 #[serde(rename = "publicKeyMultibase")]
73 pub public_key_multibase: String,
74}
75
76impl VerificationMethod {
77 pub fn new(
78 id: impl AsRef<str>,
79 controller: impl Into<String>,
80 key_type: impl Into<String>,
81 fragment: impl AsRef<str>,
82 public_key_multibase: impl Into<String>,
83 ) -> Result<Self> {
84 let base_id = id
85 .as_ref()
86 .split('#')
87 .next()
88 .ok_or(MaError::MissingIdentifier)?;
89
90 let method = Self {
91 id: format!("{base_id}#{}", fragment.as_ref()),
92 key_type: key_type.into(),
93 controller: controller.into(),
94 public_key_multibase: public_key_multibase.into(),
95 };
96 method.validate()?;
97 Ok(method)
98 }
99
100 pub fn fragment(&self) -> Result<String> {
101 let did = Did::try_from(self.id.as_str())?;
102 did.fragment.ok_or(MaError::MissingFragment)
103 }
104
105 pub fn validate(&self) -> Result<()> {
106 Did::validate_url(&self.id)?;
107
108 if self.key_type.is_empty() {
109 return Err(MaError::VerificationMethodMissingType);
110 }
111
112 if self.controller.is_empty() {
113 return Err(MaError::EmptyController);
114 }
115
116 Did::validate(&self.controller)?;
117
118 if self.public_key_multibase.is_empty() {
119 return Err(MaError::EmptyPublicKeyMultibase);
120 }
121
122 public_key_multibase_decode(&self.public_key_multibase)?;
123 Ok(())
124 }
125}
126
127#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
128pub struct Proof {
129 #[serde(rename = "type")]
130 pub proof_type: String,
131 #[serde(rename = "verificationMethod")]
132 pub verification_method: String,
133 #[serde(rename = "proofPurpose")]
134 pub proof_purpose: String,
135 #[serde(rename = "proofValue")]
136 pub proof_value: String,
137}
138
139impl Proof {
140 pub fn new(proof_value: impl Into<String>, verification_method: impl Into<String>) -> Self {
141 Self {
142 proof_type: DEFAULT_PROOF_TYPE.to_string(),
143 verification_method: verification_method.into(),
144 proof_purpose: DEFAULT_PROOF_PURPOSE.to_string(),
145 proof_value: proof_value.into(),
146 }
147 }
148
149 pub fn is_empty(&self) -> bool {
150 self.proof_value.is_empty()
151 }
152}
153
154fn is_valid_rfc3339_utc(value: &str) -> bool {
155 let trimmed = value.trim();
156 if !trimmed.ends_with('Z') {
158 return false;
159 }
160 let bytes = trimmed.as_bytes();
161 if bytes.len() < 20 {
162 return false;
163 }
164 let expected_punct = [
165 (4usize, b'-'),
166 (7usize, b'-'),
167 (10usize, b'T'),
168 (13usize, b':'),
169 (16usize, b':'),
170 ];
171 if expected_punct
172 .iter()
173 .any(|(idx, punct)| bytes.get(*idx).copied() != Some(*punct))
174 {
175 return false;
176 }
177 let core_digits = [0usize, 1, 2, 3, 5, 6, 8, 9, 11, 12, 14, 15, 17, 18];
178 if core_digits.iter().any(|idx| {
179 !bytes
180 .get(*idx)
181 .copied()
182 .unwrap_or_default()
183 .is_ascii_digit()
184 }) {
185 return false;
186 }
187 let tail = &trimmed[19..trimmed.len() - 1];
188 if tail.is_empty() {
189 return true;
190 }
191 if let Some(frac) = tail.strip_prefix('.') {
192 return !frac.is_empty() && frac.chars().all(|ch| ch.is_ascii_digit());
193 }
194 false
195}
196
197#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
253pub struct Document {
254 #[serde(rename = "@context")]
255 pub context: Vec<String>,
256 pub id: String,
257 pub controller: Vec<String>,
258 #[serde(rename = "verificationMethod")]
259 pub verification_method: Vec<VerificationMethod>,
260 #[serde(rename = "assertionMethod")]
261 pub assertion_method: Vec<String>,
262 #[serde(rename = "keyAgreement")]
263 pub key_agreement: Vec<String>,
264 pub proof: Proof,
265 #[serde(skip_serializing_if = "Option::is_none")]
266 pub identity: Option<String>,
267 #[serde(rename = "createdAt")]
268 pub created_at: String,
269 #[serde(rename = "updatedAt")]
270 pub updated_at: String,
271 #[serde(skip_serializing_if = "Option::is_none")]
272 pub ma: Option<Ipld>,
273}
274
275impl Document {
276 pub fn new(identity: &Did, controller: &Did) -> Self {
277 let now = now_iso_utc();
278 Self {
279 context: DEFAULT_DID_CONTEXT
280 .iter()
281 .map(|value| (*value).to_string())
282 .collect(),
283 id: identity.base_id(),
284 controller: vec![controller.base_id()],
285 verification_method: Vec::new(),
286 assertion_method: Vec::new(),
287 key_agreement: Vec::new(),
288 proof: Proof::default(),
289 identity: None,
290 created_at: now.clone(),
291 updated_at: now,
292 ma: None,
293 }
294 }
295
296 pub fn set_ma(&mut self, ma: Ipld) {
298 match &ma {
299 Ipld::Null => self.ma = None,
300 Ipld::Map(m) if m.is_empty() => self.ma = None,
301 _ => self.ma = Some(ma),
302 }
303 }
304
305 pub fn clear_ma(&mut self) {
307 self.ma = None;
308 }
309
310 pub fn to_cbor(&self) -> Result<Vec<u8>> {
311 serde_ipld_dagcbor::to_vec(self).map_err(|error| MaError::CborEncode(error.to_string()))
312 }
313
314 pub fn from_cbor(bytes: &[u8]) -> Result<Self> {
315 serde_ipld_dagcbor::from_slice(bytes)
316 .map_err(|error| MaError::CborDecode(error.to_string()))
317 }
318
319 pub fn marshal(&self) -> Result<String> {
320 self.to_json()
321 }
322
323 pub fn unmarshal(s: &str) -> Result<Self> {
324 Self::from_json(s)
325 }
326
327 fn to_json(&self) -> Result<String> {
328 let bytes = serde_ipld_dagjson::to_vec(self)
329 .map_err(|error| MaError::JsonEncode(error.to_string()))?;
330 String::from_utf8(bytes).map_err(|error| MaError::JsonEncode(error.to_string()))
331 }
332
333 fn from_json(s: &str) -> Result<Self> {
334 serde_ipld_dagjson::from_slice(s.as_bytes())
335 .map_err(|error| MaError::JsonDecode(error.to_string()))
336 }
337
338 pub fn add_controller(&mut self, controller: impl Into<String>) -> Result<()> {
339 let controller = controller.into();
340 Did::validate(&controller)?;
341 if !self.controller.contains(&controller) {
342 self.controller.push(controller);
343 }
344 Ok(())
345 }
346
347 pub fn add_verification_method(&mut self, method: VerificationMethod) -> Result<()> {
348 method.validate()?;
349 let duplicate = self.verification_method.iter().any(|existing| {
350 existing.id == method.id || existing.public_key_multibase == method.public_key_multibase
351 });
352
353 if !duplicate {
354 self.verification_method.push(method);
355 }
356
357 Ok(())
358 }
359
360 pub fn get_verification_method_by_id(&self, method_id: &str) -> Result<&VerificationMethod> {
361 self.verification_method
362 .iter()
363 .find(|method| method.id == method_id)
364 .ok_or_else(|| MaError::UnknownVerificationMethod(method_id.to_string()))
365 }
366
367 pub fn set_identity(&mut self, identity: impl Into<String>) -> Result<()> {
368 let identity = identity.into();
369 Cid::try_from(identity.as_str()).map_err(|_| MaError::InvalidIdentity)?;
370 self.identity = Some(identity);
371 Ok(())
372 }
373
374 pub fn touch(&mut self) {
376 self.updated_at = now_iso_utc();
377 }
378
379 pub fn assertion_method_public_key(&self) -> Result<VerifyingKey> {
380 let assertion_id = self
381 .assertion_method
382 .first()
383 .ok_or_else(|| MaError::UnknownVerificationMethod("assertionMethod".to_string()))?;
384 let vm = self.get_verification_method_by_id(assertion_id)?;
385 let (codec, public_key_bytes) = public_key_multibase_decode(&vm.public_key_multibase)?;
386 if codec != ED25519_PUB_CODEC {
387 return Err(MaError::InvalidMulticodec {
388 expected: ED25519_PUB_CODEC,
389 actual: codec,
390 });
391 }
392
393 let key_len = public_key_bytes.len();
394 let bytes: [u8; 32] =
395 public_key_bytes
396 .try_into()
397 .map_err(|_| MaError::InvalidKeyLength {
398 expected: 32,
399 actual: key_len,
400 })?;
401
402 VerifyingKey::from_bytes(&bytes).map_err(|_| MaError::Crypto)
403 }
404
405 pub fn key_agreement_public_key_bytes(&self) -> Result<[u8; 32]> {
406 let agreement_id = self
407 .key_agreement
408 .first()
409 .ok_or_else(|| MaError::UnknownVerificationMethod("keyAgreement".to_string()))?;
410 let vm = self.get_verification_method_by_id(agreement_id)?;
411 let (codec, public_key_bytes) = public_key_multibase_decode(&vm.public_key_multibase)?;
412 if codec != X25519_PUB_CODEC {
413 return Err(MaError::InvalidMulticodec {
414 expected: X25519_PUB_CODEC,
415 actual: codec,
416 });
417 }
418
419 let key_len = public_key_bytes.len();
420 public_key_bytes
421 .try_into()
422 .map_err(|_| MaError::InvalidKeyLength {
423 expected: 32,
424 actual: key_len,
425 })
426 }
427
428 pub fn payload_document(&self) -> Self {
429 let mut payload = self.clone();
430 payload.proof = Proof::default();
431 payload
432 }
433
434 pub fn payload_bytes(&self) -> Result<Vec<u8>> {
435 self.payload_document().to_cbor()
436 }
437
438 pub fn payload_hash(&self) -> Result<[u8; 32]> {
439 Ok(blake3::hash(&self.payload_bytes()?).into())
440 }
441
442 pub fn sign(
443 &mut self,
444 signing_key: &SigningKey,
445 verification_method: &VerificationMethod,
446 ) -> Result<()> {
447 if signing_key.public_key_multibase != verification_method.public_key_multibase {
448 return Err(MaError::InvalidPublicKeyMultibase);
449 }
450
451 let signature = signing_key.sign(&self.payload_hash()?);
452 let proof_value = signature_multibase_encode(EDDSA_SIG_CODEC, &signature)?;
453 self.proof = Proof::new(proof_value, verification_method.id.clone());
454 Ok(())
455 }
456
457 pub fn verify(&self) -> Result<()> {
458 if self.proof.is_empty() {
459 return Err(MaError::MissingProof);
460 }
461
462 let (codec, sig_bytes) = signature_multibase_decode(&self.proof.proof_value)?;
463 if codec != EDDSA_SIG_CODEC {
464 return Err(MaError::InvalidDocumentSignature);
465 }
466 let signature =
467 Signature::from_slice(&sig_bytes).map_err(|_| MaError::InvalidDocumentSignature)?;
468 let public_key = self.assertion_method_public_key()?;
469 public_key
470 .verify(&self.payload_hash()?, &signature)
471 .map_err(|_| MaError::InvalidDocumentSignature)
472 }
473
474 pub fn validate(&self) -> Result<()> {
475 if self.context.is_empty() {
476 return Err(MaError::EmptyContext);
477 }
478
479 Did::validate(&self.id)?;
480
481 if self.controller.is_empty() {
482 return Err(MaError::EmptyController);
483 }
484
485 for controller in &self.controller {
486 Did::validate(controller)?;
487 }
488
489 if let Some(identity) = &self.identity {
490 Cid::try_from(identity.as_str()).map_err(|_| MaError::InvalidIdentity)?;
491 }
492
493 if !is_valid_rfc3339_utc(&self.created_at) {
494 return Err(MaError::InvalidCreatedAt(self.created_at.clone()));
495 }
496
497 if !is_valid_rfc3339_utc(&self.updated_at) {
498 return Err(MaError::InvalidUpdatedAt(self.updated_at.clone()));
499 }
500
501 for method in &self.verification_method {
502 method.validate()?;
503 }
504
505 if self.assertion_method.is_empty() {
506 return Err(MaError::UnknownVerificationMethod(
507 "assertionMethod".to_string(),
508 ));
509 }
510
511 if self.key_agreement.is_empty() {
512 return Err(MaError::UnknownVerificationMethod(
513 "keyAgreement".to_string(),
514 ));
515 }
516
517 Ok(())
518 }
519}
520
521impl TryFrom<&EncryptionKey> for VerificationMethod {
522 type Error = MaError;
523
524 fn try_from(value: &EncryptionKey) -> Result<Self> {
525 let fragment = value.did.fragment.clone().ok_or(MaError::MissingFragment)?;
526 VerificationMethod::new(
527 value.did.base_id(),
528 value.did.base_id(),
529 value.key_type.clone(),
530 fragment,
531 value.public_key_multibase.clone(),
532 )
533 }
534}
535
536impl TryFrom<&SigningKey> for VerificationMethod {
537 type Error = MaError;
538
539 fn try_from(value: &SigningKey) -> Result<Self> {
540 let fragment = value.did.fragment.clone().ok_or(MaError::MissingFragment)?;
541 VerificationMethod::new(
542 value.did.base_id(),
543 value.did.base_id(),
544 value.key_type.clone(),
545 fragment,
546 value.public_key_multibase.clone(),
547 )
548 }
549}
550
551#[cfg(test)]
552mod tests {
553 use super::*;
554 use std::collections::BTreeMap;
555
556 #[test]
557 fn set_ma_stores_opaque_value() {
558 let root = Did::new_url(
559 "k51qzi5uqu5dj9807pbuod1pplf0vxh8m4lfy3ewl9qbm2s8dsf9ugdf9gedhr",
560 None::<String>,
561 )
562 .expect("valid test did");
563 let mut document = Document::new(&root, &root);
564
565 let ma = Ipld::Map(BTreeMap::from([(
566 "type".into(),
567 Ipld::String("agent".into()),
568 )]));
569 document.set_ma(ma.clone());
570 assert_eq!(document.ma.as_ref(), Some(&ma));
571 }
572
573 #[test]
574 fn clear_ma_removes_value() {
575 let root = Did::new_url(
576 "k51qzi5uqu5dj9807pbuod1pplf0vxh8m4lfy3ewl9qbm2s8dsf9ugdf9gedhr",
577 None::<String>,
578 )
579 .expect("valid test did");
580 let mut document = Document::new(&root, &root);
581
582 document.set_ma(Ipld::Map(BTreeMap::from([(
583 "type".into(),
584 Ipld::String("agent".into()),
585 )])));
586 assert!(document.ma.is_some());
587 document.clear_ma();
588 assert!(document.ma.is_none());
589 }
590
591 #[test]
592 fn set_ma_null_clears() {
593 let root = Did::new_url(
594 "k51qzi5uqu5dj9807pbuod1pplf0vxh8m4lfy3ewl9qbm2s8dsf9ugdf9gedhr",
595 None::<String>,
596 )
597 .expect("valid test did");
598 let mut document = Document::new(&root, &root);
599
600 document.set_ma(Ipld::Map(BTreeMap::from([(
601 "type".into(),
602 Ipld::String("agent".into()),
603 )])));
604 document.set_ma(Ipld::Null);
605 assert!(document.ma.is_none());
606 }
607
608 #[test]
609 fn validate_accepts_opaque_ma() {
610 let identity = crate::identity::generate_identity(
611 "k51qzi5uqu5dj9807pbuod1pplf0vxh8m4lfy3ewl9qbm2s8dsf9ugdf9gedhr",
612 )
613 .expect("generate identity");
614 let mut document = identity.document;
615 document.set_ma(Ipld::Map(BTreeMap::from([
616 ("type".into(), Ipld::String("bahner".into())),
617 ("custom".into(), Ipld::Integer(42)),
618 ])));
619 document
620 .validate()
621 .expect("validate should accept any ma value");
622 }
623}