1use crate::{signing, Did, Error, Result, RootKey};
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12#[serde(rename_all = "camelCase")]
13pub struct VerificationMethod {
14 pub id: String,
16 #[serde(rename = "type")]
18 pub type_: String,
19 pub controller: String,
21 pub public_key_multibase: String,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(rename_all = "camelCase")]
28pub struct Service {
29 pub id: String,
31 #[serde(rename = "type")]
33 pub type_: String,
34 pub service_endpoint: String,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40#[serde(rename_all = "camelCase")]
41pub struct DocumentProof {
42 #[serde(rename = "type")]
44 pub type_: String,
45 pub created: DateTime<Utc>,
47 pub verification_method: String,
49 pub proof_value: String,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
55#[serde(rename_all = "camelCase")]
56pub struct DidDocument {
57 #[serde(rename = "@context")]
59 pub context: Vec<String>,
60 pub id: String,
62 pub controller: String,
64 pub verification_method: Vec<VerificationMethod>,
66 pub authentication: Vec<String>,
68 #[serde(skip_serializing_if = "Option::is_none")]
70 pub assertion_method: Option<Vec<String>>,
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub service: Option<Vec<Service>>,
74 pub created: DateTime<Utc>,
76 pub updated: DateTime<Utc>,
78 #[serde(skip_serializing_if = "Option::is_none")]
80 pub proof: Option<DocumentProof>,
81}
82
83impl DidDocument {
84 pub fn new(root_key: &RootKey) -> Self {
86 let did = root_key.did();
87 let did_str = did.to_string();
88 let now = Utc::now();
89
90 let pubkey_multibase = format!("z{}", did.key_id());
92
93 Self {
94 context: vec![
95 "https://www.w3.org/ns/did/v1".to_string(),
96 "https://w3id.org/security/suites/ed25519-2020/v1".to_string(),
97 ],
98 id: did_str.clone(),
99 controller: did_str.clone(),
100 verification_method: vec![VerificationMethod {
101 id: format!("{}#root", did_str),
102 type_: "Ed25519VerificationKey2020".to_string(),
103 controller: did_str.clone(),
104 public_key_multibase: pubkey_multibase,
105 }],
106 authentication: vec![format!("{}#root", did_str)],
107 assertion_method: Some(vec![format!("{}#root", did_str)]),
108 service: None,
109 created: now,
110 updated: now,
111 proof: None,
112 }
113 }
114
115 pub fn add_service(mut self, id: &str, type_: &str, endpoint: &str) -> Self {
117 let service = Service {
118 id: format!("{}#{}", self.id, id),
119 type_: type_.to_string(),
120 service_endpoint: endpoint.to_string(),
121 };
122
123 match &mut self.service {
124 Some(services) => services.push(service),
125 None => self.service = Some(vec![service]),
126 }
127
128 self.updated = Utc::now();
129 self
130 }
131
132 pub fn with_handshake_endpoint(self, endpoint: &str) -> Self {
134 self.add_service("handshake", "AIPHandshake", endpoint)
135 }
136
137 pub fn sign(mut self, root_key: &RootKey) -> Result<Self> {
139 self.proof = None;
141 self.updated = Utc::now();
142
143 let canonical = signing::canonicalize(&self)?;
145 let signature = root_key.sign(&canonical);
146 let sig_b64 = base64::Engine::encode(
147 &base64::engine::general_purpose::STANDARD,
148 signature.to_bytes(),
149 );
150
151 self.proof = Some(DocumentProof {
152 type_: "Ed25519Signature2020".to_string(),
153 created: Utc::now(),
154 verification_method: format!("{}#root", self.id),
155 proof_value: sig_b64,
156 });
157
158 Ok(self)
159 }
160
161 pub fn verify(&self) -> Result<()> {
163 let proof = self.proof.as_ref().ok_or(Error::InvalidSignature)?;
164
165 let did: Did = self.id.parse()?;
167 let public_key = did.public_key()?;
168
169 let mut unsigned = self.clone();
171 unsigned.proof = None;
172
173 let canonical = signing::canonicalize(&unsigned)?;
174
175 let sig_bytes = base64::Engine::decode(
177 &base64::engine::general_purpose::STANDARD,
178 &proof.proof_value,
179 )
180 .map_err(|_| Error::InvalidSignature)?;
181
182 let signature = ed25519_dalek::Signature::from_bytes(
183 &sig_bytes.try_into().map_err(|_| Error::InvalidSignature)?,
184 );
185
186 crate::keys::verify(&public_key, &canonical, &signature)?;
187
188 Ok(())
189 }
190
191 pub fn handshake_endpoint(&self) -> Option<&str> {
193 self.service.as_ref()?.iter().find_map(|s| {
194 if s.type_ == "AIPHandshake" {
195 Some(s.service_endpoint.as_str())
196 } else {
197 None
198 }
199 })
200 }
201
202 pub fn did(&self) -> Result<Did> {
204 self.id.parse()
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn test_document_creation() {
214 let key = RootKey::generate();
215 let doc = DidDocument::new(&key);
216
217 assert!(doc.id.starts_with("did:key:"));
218 assert_eq!(doc.verification_method.len(), 1);
219 assert!(doc.proof.is_none());
220 }
221
222 #[test]
223 fn test_document_signing() {
224 let key = RootKey::generate();
225 let doc = DidDocument::new(&key)
226 .with_handshake_endpoint("https://example.com/handshake")
227 .sign(&key)
228 .unwrap();
229
230 assert!(doc.proof.is_some());
231 doc.verify().unwrap();
232 }
233
234 #[test]
235 fn test_document_tamper_detection() {
236 let key = RootKey::generate();
237 let mut doc = DidDocument::new(&key).sign(&key).unwrap();
238
239 doc.controller = "did:key:ATTACKER".to_string();
241
242 assert!(doc.verify().is_err());
244 }
245
246 #[test]
247 fn test_handshake_endpoint() {
248 let key = RootKey::generate();
249 let doc = DidDocument::new(&key).with_handshake_endpoint("https://example.com/hs");
250
251 assert_eq!(doc.handshake_endpoint(), Some("https://example.com/hs"));
252 }
253}