caelum_diddoc/lib.rs
1//! # **D**ecentralized **Id**entity **Doc**uments
2//!
3//! The `caelum_diddoc` crate is aimed to help `javascript` and `rust` developers manage `DID docs`
4//! (**D**ecentralized **Id**entity **Doc**uments) in an easy manner.
5//! Following [w3's specifications](https://w3c-ccg.github.io/did-spec/#did-documents) on DID
6//! Documents this crate aims to be a [Resolver](https://w3c-ccg.github.io/did-spec/#did-resolvers).
7//! This means it will create, read, update and delete all properties of a DID document. As well as
8//! interacting with other DIDs and DID document, using proof verification.
9//! Also, it will be able to interact with others on other Decentralized Identifier Registry.
10//! This will can effectively be used to perform all the operations required of a CKMS
11//! (cryptographic key management system), such as key registration, replacement, rotation,
12//! recovery, and expiration.
13//! More details in [w3's method specifications](https://w3c-ccg.github.io/did-spec/#did-methods).
14//!
15#[cfg(feature = "serde-serialize")]
16extern crate serde;
17
18extern crate serde;
19
20extern crate serde_json;
21extern crate wasm_bindgen;
22
23use wasm_bindgen::prelude::*;
24
25use crate::did_doc_authentication::Authentication;
26use crate::did_doc_public_key::DidDocPublicKey;
27use crate::did_document::DidDocument;
28use crate::service::Service;
29
30pub mod did_doc_authentication;
31pub mod did_doc_public_key;
32pub mod did_document;
33pub mod proof;
34pub mod service;
35
36#[wasm_bindgen]
37pub struct DidDoc {
38 ctx: JsValue,
39}
40
41#[wasm_bindgen]
42impl DidDoc {
43 // CONSTRUCTORS
44 /// # Web assembly Constructor
45 /// `DidDoc`'s constructor will create an empty `DidDoc` object. An empty object
46 /// is not a valid `DidDoc`.
47 /// ```javascript
48 /// import { DidDoc } from 'caelum_diddoc';
49 /// let d = new DidDoc("my_context", "my_id");
50 /// ```
51 ///
52 #[wasm_bindgen(constructor)]
53 pub fn new(context: String, id: String) -> DidDoc {
54 let d = DidDocument::new(context, id);
55 DidDoc { ctx: d.to_json() }
56 }
57
58 /// # Web assembly Constructor
59 /// `DidDoc`'s constructor will create an empty `DidDoc` object. An empty object
60 /// is not a valid `DidDoc`.
61 /// ```javascript
62 /// import { DidDoc } from 'caelum_diddoc';
63 /// let d = DidDoc.default();
64 /// ```
65 ///
66 #[wasm_bindgen(js_name = default)]
67 pub fn default() -> DidDoc {
68 let d = DidDocument::default();
69 DidDoc { ctx: d.to_json() }
70 }
71
72 /// # WebAssembly Constructor `formJSON`
73 /// `DidDoc`'s constructor will create a `DidDoc` object from input. Input must be a
74 /// JSON object with all properties defined. If no value is wanted for certain property, must
75 /// be empty. This is also true for sub properties (properties of properties).
76 /// ```javascript
77 /// import { DidDoc } from 'caelum_diddoc';
78 /// let myDidDoc_json = {
79 /// "@context": [
80 /// "https://www.w3.org/2018/credentials/v1",
81 /// "https://www.w3.org/2018/credentials/examples/v1"
82 /// ],
83 /// "id": "http://example.com/credentials/4643",
84 /// "authentication": [
85 /// {
86 /// "id": "did:example:ebfeb1276e12ec21f712ebc6f1c",
87 /// "type": "OpenIdConnectVersion1.0Service",
88 /// "owner": "OWNER!",
89 /// "PublicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"
90 /// },
91 /// "did:lrn:123412341234#my_id"
92 /// ],
93 /// "publicKey": [{
94 /// "id": "did:example:ebfeb1276e12ec21f712ebc6f1c",
95 /// "type": "OpenIdConnectVersion1.0Service",
96 /// "owner": "OWNER!",
97 /// "PublicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"
98 /// }],
99 /// "service": [{
100 /// "id": "id",
101 /// "type": "type",
102 /// "serviceEndpoint": "serviceEndpoint"
103 /// }],
104 /// "updated": "1982-02-02-00T00:00Z",
105 /// "created": "1982-02-02-00T00:00Z",
106 /// "proof": {
107 /// "type": "RsaSignature2018",
108 /// "created": "2018-06-17T10:03:48Z",
109 /// "verificationMethod": "did:example:ebfeb1276e12ec21f712ebc6f1c#k1",
110 /// "signatureValue": "pY9...Cky6Ed = "
111 /// }
112 /// };
113 /// let myDidDoc = DidDoc.fromJSON(myDidDoc_json);
114 /// ```
115 ///
116 #[wasm_bindgen(js_name = fromJSON)]
117 pub fn from_json(json: JsValue) -> DidDoc {
118 DidDoc { ctx: json }
119 }
120
121 // SETTERS
122 /// # Set context
123 /// Specifications describe a `@context` as a mandatory field that:
124 /// > one or more URIs (An identifier as defined by [RFC3986](https://w3c-ccg.github.io/did-spec/#bib-rfc3986).)
125 /// > where the value of the first URI is https://www.w3.org/2019/did/v1.
126 /// > If more than one URI is provided, the URIs MUST be interpreted as an ordered set.
127 /// > It is RECOMMENDED that dereferencing the URIs results in a document containing machine-readable information about
128 /// > the context.
129 ///
130 /// And must follow the following rules:
131 ///
132 /// 1. A DID Document MUST have exactly one top-level context statement.
133 /// 2. The key for this property MUST be @context.
134 /// 3. The value of this key MUST be the URL for the generic DID context: https://w3id.org/did/v1.
135 ///
136 /// By default `context` will be set to
137 /// ```json
138 /// {
139 /// @context: [
140 /// "https://www.w3.org/2018/credentials/v1",
141 /// "https://www.w3.org/2018/credentials/examples/v1"
142 /// ]
143 /// }
144 /// ```
145 /// To overwrite default `context`s use method `setContext()` can be used.
146 /// ```javascript
147 /// import { DidDoc } from 'caelum_diddoc';
148 /// let vc = new DidDoc("my_id")
149 /// .setContext("new_context");
150 /// ```
151 ///
152 #[wasm_bindgen(js_name = setContext)]
153 pub fn set_context(self, c: String) -> DidDoc {
154 let mut d: DidDocument = self.ctx.into_serde().unwrap();
155 d.context = vec![c];
156 DidDoc { ctx: d.to_json() }
157 }
158
159 /// # Set Id (Did Subject)
160 /// A DID subject goes by the following rules:
161 ///
162 /// 1. A DID Document MUST have exactly one DID subject.
163 /// 2. The key for this property MUST be id.
164 /// 3. The value of this key MUST be a valid DID.
165 /// 4. When this DID Document is registered with the target Decentralized Identifier Registry, the registered DID MUST
166 /// match this DID subject value.
167 ///
168 /// Similarly to `@context`, in `caelum_diddoc`, `id`s are created in the `DidDoc` constructor. Users can add an id subject using
169 /// method `add_id()`, and can overwrite them using setter `set_id()`. `id`s are not array so no adder is implemented,
170 /// only a setter and a getter.
171 ///
172 /// By default `id` will be set by the constructor.
173 /// To overwrite default `id` use method `setId()` can be used.
174 /// ```javascript
175 /// import { DidDoc } from 'caelum_diddoc';
176 /// let vc = new DidDoc("my_id")
177 /// .setId("_id");
178 /// ```
179 ///
180 #[wasm_bindgen(js_name = setId)]
181 pub fn set_id(self, id: String) -> DidDoc {
182 let mut d: DidDocument = self.ctx.into_serde().unwrap();
183 d.id = id;
184 DidDoc { ctx: d.to_json() }
185 }
186
187 /// # Set DID Authentication
188 /// Following w3's documentation:
189 ///
190 /// > Authentication is the mechanism by which a DID subject can cryptographically prove that
191 /// they are associated with a DID.
192 ///
193 /// The rules for Authentication are:
194 ///
195 /// 1. A DID Document MAY include an `authentication` property.
196 /// 2. The value of the `authentication` property should be an array of verification methods.
197 /// 3. Each verification method MAY be embedded or referenced. An example of a verification
198 /// method is a public key.
199 ///
200 /// By default `authentication` is set to an empty object with empty properties (`type` and
201 /// `publicKey`).
202 /// ```javascript
203 /// let d = new DidDoc("my_context", "my_id")
204 /// .setAuthentication({
205 /// "id": "did:example:ebfeb1276e12ec21f712ebc6f1c",
206 /// "type": "OpenIdConnectVersion1.0Service",
207 /// "owner": "OWNER!",
208 /// "PublicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"
209 /// });
210 /// ```
211 ///
212 #[wasm_bindgen(js_name = setAuthentication)]
213 pub fn set_authentication(self, a: JsValue) -> DidDoc {
214 let mut d: DidDocument = self.ctx.into_serde().unwrap();
215 d.authentication = a.into_serde().unwrap();
216 DidDoc { ctx: d.to_json() }
217 }
218
219 /// # Set Public Key
220 /// >Public keys are used for digital signatures, encryption and other cryptographic operations, which in turn are the
221 /// basis for purposes such as authentication or establishing secure communication with service endpoints. In addition,
222 /// public keys may play a role in authorization mechanisms of DID CRUD operations
223 ///
224 ///
225 /// The rules for public keys are:
226 ///
227 /// 1. A DID Document MAY include a publicKey property.
228 /// 2. The value of the `publicKey` property MUST be an array of public keys, and every public key property MUST be in the
229 /// [Linked Data Cryptographic Suite Registry](https://w3c-ccg.github.io/ld-cryptosuite-registry/).
230 /// 3. Each public key MUST include `id` and `type` properties, and exactly one value property. The array of public keys
231 /// SHOULD NOT contain duplicate entries with the same id and different value properties with different formats.
232 /// 4. Each public key MUST include a `controller` property, which identifies the controller of the corresponding private key.
233 /// 5. A registry of key types and formats is available in [Registries](https://w3c-ccg.github.io/did-spec/#registries) .
234 ///
235 ///
236 /// When creating a `publicKey` the following fields must be included (empty if not needed):
237 ///
238 /// 1. `id`
239 /// 2. `owner`
240 /// 3. `type`
241 /// 4. A valid key-value (exactly one) encryption method as the key, and the public key corresponding to that encrpytion
242 /// method, as the value.
243 ///
244 /// ```javascript
245 /// let d = new DidDoc("my_context", "my_id")
246 /// .setPublicKey({
247 /// "id": "did:sov:WRfXPg8dantKVubE3HX8pw#key-1",
248 /// "type": "Ed25519VerificationKey2018",
249 /// "owner": "NEW_OWNER",
250 /// "publicKeyBase58": "~P7F3BNs5VmQ6eVpwkNKJ5D"
251 /// });
252 /// ```
253 ///
254 #[wasm_bindgen(js_name = setPublicKey)]
255 pub fn set_public_key(self, p: JsValue) -> DidDoc {
256 let mut d: DidDocument = self.ctx.into_serde().unwrap();
257 d.public_key = p.into_serde().unwrap();
258 DidDoc { ctx: d.to_json() }
259 }
260
261 /// # Set Service
262 /// As w3's documentation explains:
263 /// > A service endpoint may represent any type of service the subject wishes to advertise, including decentralized
264 /// identity management services for further discovery, authentication, authorization, or interaction.
265 ///
266 /// The rules for service endpoints are:
267 ///
268 /// 1. A DID Document MAY include a service property.
269 /// 2. The value of the service property should be an array of service endpoints.
270 /// 3. Each service endpoint must include id, type, and serviceEndpoint properties, and MAY include additional properties.
271 /// 4. The service endpoint protocol SHOULD be published in an open standard specification.
272 /// 5. The value of the serviceEndpoint property MUST be a JSON-LD object or a valid URI conforming to
273 /// [RFC3986](https://w3c-ccg.github.io/did-spec/#bib-rfc3986) and normalized according to the rules in section 6 of
274 /// [RFC3986](https://w3c-ccg.github.io/did-spec/#bib-rfc3986) and to any normalization rules in its applicable URI
275 /// scheme specification.
276 /// ```javascript
277 /// let d = new DidDoc("my_context", "my_id")
278 /// .setService({
279 /// "id": "did:ser:18y34b91c612c4123gwrh45y",
280 /// "type": "agent",
281 /// "serviceEndpoint": "https://agents.danubeclouds.com/agent/WRfXPg8dantKVubE3HX8pw"
282 /// });
283 /// ```
284 ///
285 #[wasm_bindgen(js_name = setService)]
286 pub fn set_service(self, s: JsValue) -> DidDoc {
287 let mut d: DidDocument = self.ctx.into_serde().unwrap();
288 d.service = s.into_serde().unwrap();
289 DidDoc { ctx: d.to_json() }
290 }
291
292 /// # Set Proof
293 /// A proof on a DID Document is cryptographic proof of the integrity of the DID Document according to either:
294 ///
295 /// 1. The subject as defined as a [service endpoint](https://w3c-ccg.github.io/did-spec/#service-endpoints), or if
296 /// not present:
297 /// 2. The delegate as a [generic DID parameter](https://w3c-ccg.github.io/did-spec/#generic-did-parameter-names).
298 ///
299 /// This proof is NOT proof of
300 /// [the binding between a DID and a DID Document](https://w3c-ccg.github.io/did-spec/#binding-of-identity).
301 ///
302 /// The rules for a proof are:
303 ///
304 /// 1. A DID Document MAY have exactly one property representing a proof.
305 /// 2. The key for this property MUST be proof.
306 /// 3. The value of this key MUST be a valid JSON-LD proof as defined by
307 /// [Linked Data Proofs](https://w3c-dvcg.github.io/ld-signatures/).
308 ///
309 /// ```javascript
310 /// let d = new DidDoc("my_context", "my_id")
311 /// .setProof({
312 /// "type": "RsaSignature2018",
313 /// "created": "2018-06-17T10:03:48Z",
314 /// "verificationMethod": "did:example:ebfeb1276e12ec21f712ebc6f1c#k1",
315 /// "signatureValue": "pY9...Cky6Ed = "
316 /// });
317 /// ```
318 ///
319 #[wasm_bindgen(js_name = setProof)]
320 pub fn set_proof(self, proof: JsValue) -> DidDoc {
321 let mut d: DidDocument = self.ctx.into_serde().unwrap();
322 d.proof = proof.into_serde().unwrap();
323 DidDoc { ctx: d.to_json() }
324 }
325
326 /// # Set Created
327 /// Standard metadata for identifier records includes a timestamp of the original creation. The rules for including a
328 /// creation timestamp are:
329 ///
330 /// 1. A DID Document MUST have zero or one property representing a creation timestamp. It is RECOMMENDED to include
331 /// this property.
332 /// 2. The key for this property MUST be created.
333 /// 3. The value of this key MUST be a valid XML datetime value as defined in section 3.3.7 of
334 /// [W3C XML Schema Definition Language (XSD) 1.1 Part 2: Datatypes](https://www.w3.org/TR/xmlschema11-2/)
335 /// [[XMLSCHEMA11-2](https://w3c-ccg.github.io/did-spec/#bib-xmlschema11-2)].
336 /// 4. This datetime value MUST be normalized to UTC 00:00 as indicated by the trailing "Z".
337 /// 5. Method specifications that rely on DLTs SHOULD require time values that are after the known
338 /// ["median time past" (defined in Bitcoin BIP 113)](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki),
339 /// when the DLT supports such a notion.
340 ///
341 /// ```javascript
342 /// let d = new DidDoc("my_context", "my_id")
343 /// .setCreated("2002-10-10T17:00:00Z");
344 /// ```
345 ///
346 #[wasm_bindgen(js_name = setCreated)]
347 pub fn set_created(self, c: String) -> DidDoc {
348 let mut d: DidDocument = self.ctx.into_serde().unwrap();
349 d.created = c;
350 DidDoc { ctx: d.to_json() }
351 }
352
353 /// # Set Updated
354 /// Standard metadata for identifier records includes a timestamp of the most recent change. The rules for including
355 /// an updated timestamp are:
356 ///
357 /// 1. A DID Document MUST have zero or one property representing an updated timestamp. It is RECOMMENDED to include this property.
358 /// 2. The key for this property MUST be updated.
359 /// 3. The value of this key MUST be a valid XML datetime value as defined in section 3.3.7 of
360 /// [W3C XML Schema Definition Language (XSD) 1.1 Part 2: Datatypes](https://www.w3.org/TR/xmlschema11-2/)
361 /// [[XMLSCHEMA11-2](https://w3c-ccg.github.io/did-spec/#bib-xmlschema11-2)].
362 /// 4. This datetime value MUST be normalized to UTC 00:00 as indicated by the trailing "Z".
363 /// 5. Method specifications that rely on DLTs SHOULD require time values that are after the known
364 /// ["median time past" (defined in Bitcoin BIP 113)](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki),
365 /// when the DLT supports such a notion.
366 ///
367 /// ```javascript
368 /// let d = new DidDoc("my_context", "my_id")
369 /// .setUpdated("2016-10-17T02:41:00Z");
370 /// ```
371 ///
372 #[wasm_bindgen(js_name = setUpdated)]
373 pub fn set_updated(self, u: String) -> DidDoc {
374 let mut d: DidDocument = self.ctx.into_serde().unwrap();
375 d.updated = u;
376 DidDoc { ctx: d.to_json() }
377 }
378
379 // GETTERS
380 /// # Get Context
381 /// ```javascript
382 /// let dContext = dd.getContext();
383 /// ```
384 ///
385 #[wasm_bindgen(js_name = getContext)]
386 pub fn get_context(&self) -> JsValue {
387 let dd: DidDocument = self.ctx.into_serde().unwrap();
388 JsValue::from_serde(&dd.context).unwrap()
389 }
390
391 /// # Get DID Identification
392 /// ```javascript
393 /// let dId = dd.getId();
394 /// ```
395 ///
396 #[wasm_bindgen(js_name = getId)]
397 pub fn get_id(&self) -> JsValue {
398 let dd: DidDocument = self.ctx.into_serde().unwrap();
399 JsValue::from_serde(&dd.id).unwrap()
400 }
401
402 /// # Get DID Authentication
403 /// ```javascript
404 /// let dAuthentication = dd.getAuthentication();
405 /// ```
406 ///
407 #[wasm_bindgen(js_name = getAuthentication)]
408 pub fn get_authentication(&self) -> JsValue {
409 let dd: DidDocument = self.ctx.into_serde().unwrap();
410 JsValue::from_serde(&dd.authentication).unwrap()
411 }
412
413 /// # Get Public Key
414 /// ```javascript
415 /// let dPublicKey = dd.getPublicKey();
416 /// ```
417 ///
418 #[wasm_bindgen(js_name = getPublicKey)]
419 pub fn get_public_key(&self) -> JsValue {
420 let dd: DidDocument = self.ctx.into_serde().unwrap();
421 JsValue::from_serde(&dd.public_key).unwrap()
422 }
423
424 /// # Get Services
425 /// ```javascript
426 /// let dServices = dd.getService();
427 /// ```
428 ///
429 #[wasm_bindgen(js_name = getService)]
430 pub fn get_service(&self) -> JsValue {
431 let dd: DidDocument = self.ctx.into_serde().unwrap();
432 JsValue::from_serde(&dd.service).unwrap()
433 }
434
435 /// # Get Proof
436 /// ```javascript
437 /// let dProof = dd.getProof();
438 /// ```
439 ///
440 #[wasm_bindgen(js_name = getProof)]
441 pub fn get_proof(&self) -> JsValue {
442 let dd: DidDocument = self.ctx.into_serde().unwrap();
443 JsValue::from_serde(&dd.proof).unwrap()
444 }
445
446 /// # Get DID Creation (Created)
447 /// ```javascript
448 /// let dCreated = dd.getCreated();
449 /// ```
450 ///
451 #[wasm_bindgen(js_name = getCreated)]
452 pub fn get_created(&self) -> JsValue {
453 let dd: DidDocument = self.ctx.into_serde().unwrap();
454 JsValue::from_serde(&dd.created).unwrap()
455 }
456
457 /// # Get DID last update (Updated)
458 /// ```javascript
459 /// let dUpdated = dd.getUpdated();
460 /// ```
461 ///
462 #[wasm_bindgen(js_name = getUpdated)]
463 pub fn get_updated(&self) -> JsValue {
464 let dd: DidDocument = self.ctx.into_serde().unwrap();
465 JsValue::from_serde(&dd.updated).unwrap()
466 }
467
468 // ADDERS
469 /// # Add Context
470 /// ```javascript
471 /// let d = new DidDoc("my_context", "my_id")
472 /// .addContext("my_second_context");
473 /// ```
474 ///
475 #[wasm_bindgen(js_name = addContext)]
476 pub fn add_context(self, c: String) -> DidDoc {
477 let mut d: DidDocument = self.ctx.into_serde().unwrap();
478 d.context.push(c);
479 DidDoc { ctx: d.to_json() }
480 }
481
482 /// # Add Authentication
483 /// ```javascript
484 /// let d = new DidDoc("my_context", "my_id")
485 /// .addAuthentication({
486 /// "id": "did:example:ebfeb1276e12ec21f712ebc6f1c",
487 /// "type": "OpenIdConnectVersion1.0Service",
488 /// "owner": "OWNER!",
489 /// "PublicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"
490 /// });
491 /// ```
492 ///
493 #[wasm_bindgen(js_name = addAuthentication)]
494 pub fn add_authentication(self, json_auth: JsValue) -> DidDoc {
495 let auth: Authentication = json_auth.into_serde().unwrap();
496 let mut d: DidDocument = self.ctx.into_serde().unwrap();
497 d.authentication.push(auth);
498 DidDoc { ctx: d.to_json() }
499 }
500
501 /// # Add Public Key
502 /// ```javascript
503 /// let d = new DidDoc("my_context", "my_id")
504 /// .addPublicKey({
505 /// "id": "did:sov:WRfXPg8dantKVubE3HX8pw#key-1",
506 /// "type": "Ed25519VerificationKey2018",
507 /// "owner": "", // empty `owner`
508 /// "publicKeyBase58": "~P7F3BNs5VmQ6eVpwkNKJ5D"
509 /// });
510 /// ```
511 ///
512 #[wasm_bindgen(js_name = addPublicKey)]
513 pub fn add_public_key(self, json_publickey: JsValue) -> DidDoc {
514 let publickey: DidDocPublicKey = json_publickey.into_serde().unwrap();
515 let mut d: DidDocument = self.ctx.into_serde().unwrap();
516 d.public_key.push(publickey);
517 DidDoc { ctx: d.to_json() }
518 }
519
520 /// # Add Service
521 /// ```javascript
522 /// let d = new DidDoc("my_context", "my_id")
523 /// .addService({
524 /// "id": "did:ser:18y34b91c612c4123gwrh45y",
525 /// "type": "agent",
526 /// "serviceEndpoint": "https://agents.danubeclouds.com/agent/WRfXPg8dantKVubE3HX8pw"
527 /// })
528 /// .setService({
529 /// "id": "did:ser:18y34b91c612c4123gwrh45y",
530 /// "type": "agent",
531 /// "serviceEndpoint": "https://agents.danubeclouds.com/agent/WRfXPg8dantKVubE3HX8pw"
532 /// });
533 /// ```
534 ///
535 #[wasm_bindgen(js_name = addService)]
536 pub fn add_service(self, json_service: JsValue) -> DidDoc {
537 let service: Service = json_service.into_serde().unwrap();
538 let mut d: DidDocument = self.ctx.into_serde().unwrap();
539 d.service.push(service);
540 DidDoc { ctx: d.to_json() }
541 }
542
543 /// # Generate JSON from Did Document
544 /// ```javascript
545 /// let my_json = dd.toJSON();
546 /// ```
547 ///
548 #[wasm_bindgen(js_name = toJSON)]
549 pub fn to_json(&self) -> JsValue {
550 self.ctx.clone()
551 }
552}