iop_proto_wasm/
sign.rs

1use super::*;
2
3/// Binary data signed by a multicipher key.
4#[wasm_bindgen(js_name = SignedBytes)]
5pub struct JsSignedBytes {
6    inner: Signed<Box<[u8]>>,
7}
8
9#[wasm_bindgen(js_class = SignedBytes)]
10impl JsSignedBytes {
11    /// Create {@link SignedBytes} from its parts.
12    #[wasm_bindgen(constructor)]
13    pub fn new(
14        public_key: &JsMPublicKey, content: &[u8], signature: &JsMSignature,
15    ) -> Result<JsSignedBytes, JsValue> {
16        let inner = Signed::new(
17            public_key.inner().to_owned(),
18            content.to_owned().into_boxed_slice(),
19            signature.inner().to_owned(),
20        );
21        Ok(Self { inner })
22    }
23
24    /// Accessor of the {@link PublicKey} that signed the binary data.
25    #[wasm_bindgen(getter, js_name = publicKey)]
26    pub fn public_key(&self) -> JsMPublicKey {
27        JsMPublicKey::from(self.inner.public_key().to_owned())
28    }
29
30    /// Accessor of the binary data.
31    #[wasm_bindgen(getter)]
32    pub fn content(&self) -> Box<[u8]> {
33        self.inner.content().clone()
34    }
35
36    /// Accessor of the {@link Signature}.
37    #[wasm_bindgen(getter)]
38    pub fn signature(&self) -> JsMSignature {
39        JsMSignature::from(self.inner.signature().to_owned())
40    }
41
42    /// Verify if {@link signature} was made by the private key that belongs to {@link publicKey} on the {@link content}.
43    #[wasm_bindgen]
44    pub fn validate(&self) -> bool {
45        self.inner.validate()
46    }
47
48    /// Not only validate the signature, but also check if the provided {@link KeyId} was made from {@link publicKey}.
49    ///
50    /// @see validate
51    #[wasm_bindgen(js_name = validateWithKeyId)]
52    pub fn validate_with_keyid(&self, signer_id: &JsMKeyId) -> bool {
53        self.inner.validate_with_keyid(Some(signer_id.inner()))
54    }
55
56    /// Not only validate the signature, but also check the signing key had impersonation right the whole time period specified by the
57    /// optional upper and lower block height boundaries. The DID document serialized as a string provides the whole history of key
58    /// rights, so depending on the use-case there are three possible outcomes:
59    ///
60    /// - The signing key had impersonation right the whole time and the signature is valid (green)
61    /// - Cannot prove if the signing key had impersonation right the whole time, but no other issues found (yellow)
62    /// - The signature is invalid or we can prove the signing key did not have impersonation right at any point in
63    ///   the given time interval (red)
64    ///
65    /// The return value is a {@link ValidationResult}
66    #[wasm_bindgen(js_name = validateWithDidDoc)]
67    pub fn validate_with_did_doc(
68        &self, did_doc_str: &str, from_height_inc: Option<BlockHeight>,
69        until_height_exc: Option<BlockHeight>,
70    ) -> Result<JsValue, JsValue> {
71        validate_with_did_doc(&self.inner, did_doc_str, from_height_inc, until_height_exc)
72    }
73}
74
75impl From<Signed<Box<[u8]>>> for JsSignedBytes {
76    fn from(inner: Signed<Box<[u8]>>) -> Self {
77        Self { inner }
78    }
79}
80
81impl Wraps<Signed<Box<[u8]>>> for JsSignedBytes {
82    fn inner(&self) -> &Signed<Box<[u8]>> {
83        &self.inner
84    }
85}
86
87/// JSON signed by a multicipher key. Since the signature is done on a digest created by {@link digestJson}, the same signature can be
88/// validated against different selectively revealed JSON documents.
89///
90/// @see selectiveDigestJson
91#[wasm_bindgen(js_name = SignedJson)]
92pub struct JsSignedJson {
93    inner: Signed<serde_json::Value>,
94}
95
96#[wasm_bindgen(js_class = SignedJson)]
97impl JsSignedJson {
98    /// Create {@link SignedJson} from its parts.
99    #[wasm_bindgen(constructor)]
100    pub fn new(
101        public_key: &JsMPublicKey, content: &JsValue, signature: &JsMSignature,
102    ) -> Result<JsSignedJson, JsValue> {
103        let inner = Signed::new(
104            public_key.inner().to_owned(),
105            content.into_serde().map_err_to_js()?,
106            signature.inner().to_owned(),
107        );
108        Ok(Self { inner })
109    }
110
111    /// Accessor of the {@link PublicKey} that signed the binary data.
112    #[wasm_bindgen(getter, js_name = publicKey)]
113    pub fn public_key(&self) -> JsMPublicKey {
114        JsMPublicKey::from(self.inner.public_key().to_owned())
115    }
116
117    /// Accessor of the JSON content.
118    #[wasm_bindgen(getter)]
119    pub fn content(&self) -> Result<JsValue, JsValue> {
120        JsValue::from_serde(self.inner.content()).map_err_to_js()
121    }
122
123    /// Accessor of the {@link Signature}.
124    #[wasm_bindgen(getter)]
125    pub fn signature(&self) -> JsMSignature {
126        JsMSignature::from(self.inner.signature().to_owned())
127    }
128
129    /// Verify if {@link signature} was made by the private key that belongs to {@link publicKey} on the {@link content}.
130    #[wasm_bindgen]
131    pub fn validate(&self) -> bool {
132        self.inner.validate()
133    }
134
135    /// Not only validate the signature, but also check if the provided {@link KeyId} was made from {@link publicKey}.
136    ///
137    /// @see validate
138    #[wasm_bindgen(js_name = validateWithKeyId)]
139    pub fn validate_with_keyid(&self, signer_id: &JsMKeyId) -> bool {
140        self.inner.validate_with_keyid(Some(signer_id.inner()))
141    }
142
143    /// Not only validate the signature, but also check the signing key had impersonation right the whole time period specified by the
144    /// optional upper and lower block height boundaries. The DID document serialized as a string provides the whole history of key
145    /// rights, so depending on the use-case there are three possible outcomes:
146    ///
147    /// - The signing key had impersonation right the whole time and the signature is valid (green)
148    /// - Cannot prove if the signing key had impersonation right the whole time, but no other issues found (yellow)
149    /// - The signature is invalid or we can prove the signing key did not have impersonation right at any point in
150    ///   the given time interval (red)
151    ///
152    /// The return value is a {@link ValidationResult}
153    #[wasm_bindgen(js_name = validateWithDidDoc)]
154    pub fn validate_with_did_doc(
155        &self, did_doc_str: &str, from_height_inc: Option<BlockHeight>,
156        until_height_exc: Option<BlockHeight>,
157    ) -> Result<JsValue, JsValue> {
158        validate_with_did_doc(&self.inner, did_doc_str, from_height_inc, until_height_exc)
159    }
160
161    /// Serialize this object as a JSON in a format used by IOP SSI in several places
162    #[wasm_bindgen(js_name = toJSON)]
163    pub fn to_json(&self) -> Result<JsValue, JsValue> {
164        JsValue::from_serde(&self.inner).map_err_to_js()
165    }
166
167    /// Deserialize a {@SignedJson} from a JSON in a format used by IOP SSI in several places
168    #[wasm_bindgen(js_name = fromJSON)]
169    pub fn from_json(json: &JsValue) -> Result<JsSignedJson, JsValue> {
170        let parsed: Signed<serde_json::Value> = json.into_serde().map_err_to_js()?;
171        Ok(parsed.into())
172    }
173}
174
175impl From<Signed<serde_json::Value>> for JsSignedJson {
176    fn from(inner: Signed<serde_json::Value>) -> Self {
177        Self { inner }
178    }
179}
180
181impl Wraps<Signed<serde_json::Value>> for JsSignedJson {
182    fn inner(&self) -> &Signed<serde_json::Value> {
183        &self.inner
184    }
185}
186
187fn validate_with_did_doc<T: Signable>(
188    signed: &Signed<T>, did_doc_str: &str, from_height_inc: Option<BlockHeight>,
189    until_height_exc: Option<BlockHeight>,
190) -> Result<JsValue, JsValue> {
191    let did_doc = serde_json::from_str(did_doc_str).map_err_to_js()?;
192    let result = signed
193        .validate_with_did_doc(&did_doc, from_height_inc, until_height_exc)
194        .map_err_to_js()?;
195    Ok(JsValidationResult { inner: result }.into())
196}
197
198/// A single issue found while validating against a DID document.
199///
200/// @see SignedBytes.validateWithDidDoc, SignedJson.validateWithDidDoc
201#[wasm_bindgen(js_name = ValidationIssue)]
202pub struct JsValidationIssue {
203    inner: ValidationIssue,
204}
205
206#[wasm_bindgen(js_class = ValidationIssue)]
207impl JsValidationIssue {
208    /// Error code of the issue
209    #[wasm_bindgen(getter)]
210    pub fn code(&self) -> u32 {
211        self.inner.code()
212    }
213
214    /// Severity of the issue ('warning' or 'error')
215    #[wasm_bindgen(getter)]
216    pub fn severity(&self) -> String {
217        self.inner.severity().to_string()
218    }
219
220    /// Description of the issue as a string
221    #[wasm_bindgen(getter)]
222    pub fn reason(&self) -> String {
223        self.inner.reason().to_string()
224    }
225}
226
227impl From<ValidationIssue> for JsValidationIssue {
228    fn from(inner: ValidationIssue) -> Self {
229        Self { inner }
230    }
231}
232
233impl Wraps<ValidationIssue> for JsValidationIssue {
234    fn inner(&self) -> &ValidationIssue {
235        &self.inner
236    }
237}
238
239/// All issues found while validating against a DID document.
240///
241/// @see SignedBytes.validateWithDidDoc, SignedJson.validateWithDidDoc
242#[wasm_bindgen(js_name = ValidationResult)]
243pub struct JsValidationResult {
244    inner: ValidationResult,
245}
246
247#[wasm_bindgen(js_class = ValidationResult)]
248impl JsValidationResult {
249    /// Status of the validation based on the highest severity found among the issues ('invalid', 'maybe valid' or 'valid')
250    #[wasm_bindgen(getter)]
251    pub fn status(&self) -> String {
252        self.inner.status().to_string()
253    }
254
255    /// An array of all issues. Treat each item as a {@link ValidationIssue}.
256    #[wasm_bindgen(getter)]
257    pub fn messages(&self) -> Box<[JsValue]> {
258        let msgs = self
259            .inner
260            .issues()
261            .iter()
262            .map(|issue| JsValidationIssue { inner: issue.to_owned() }.into())
263            .collect::<Vec<_>>();
264        msgs.into_boxed_slice()
265    }
266}
267
268impl From<ValidationResult> for JsValidationResult {
269    fn from(inner: ValidationResult) -> Self {
270        Self { inner }
271    }
272}
273
274impl Wraps<ValidationResult> for JsValidationResult {
275    fn inner(&self) -> &ValidationResult {
276        &self.inner
277    }
278}