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