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}