Skip to content

Commit

Permalink
Generalise CredentialValidator, PresentationValidator to support …
Browse files Browse the repository at this point in the history
…arbitrary DID Documents (#935)

* Add dynamic dispatch traits

* Implement Document for IotaDocument

* Generalise CredentialValidator to use ValidatorDocument

* Generalise PresentationValidator to use ValidatorDocument

* Move validator module to identity_credential

* Add CredentialValidator::extract_issuer, PresentationValidator::extract_holder

* Fix tests, examples compilation

* Fix identity_account_storage test compilation with no-default-features

* Add unit test for presentation, credential validation with mixed DID
Method issuers

* Add extract_issuer, extract_holder to Wasm bindings

* Update Wasm bindings

* Remove old comments, unused imports

* Switch parameters back to generics where possible

* Change error type

* Fix intra-doc links

* Fix error documentation
  • Loading branch information
cycraig authored Jul 11, 2022
1 parent 88f8a5a commit f0e4529
Show file tree
Hide file tree
Showing 39 changed files with 1,331 additions and 638 deletions.
135 changes: 90 additions & 45 deletions bindings/wasm/docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,9 @@ publishing to the Tangle.
**Kind**: global class

* [Account](#Account)
* [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ <code>Promise.&lt;void&gt;</code>
* [.createService(options)](#Account+createService) ⇒ <code>Promise.&lt;void&gt;</code>
* [.createMethod(options)](#Account+createMethod) ⇒ <code>Promise.&lt;void&gt;</code>
* [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ <code>Promise.&lt;void&gt;</code>
* [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ <code>Promise.&lt;void&gt;</code>
* [.did()](#Account+did)[<code>DID</code>](#DID)
* [.autopublish()](#Account+autopublish) ⇒ <code>boolean</code>
* [.autosave()](#Account+autosave)[<code>AutoSave</code>](#AutoSave)
Expand All @@ -242,25 +242,22 @@ publishing to the Tangle.
* [.unrevokeCredentials(fragment, credentialIndices)](#Account+unrevokeCredentials) ⇒ <code>Promise.&lt;void&gt;</code>
* [.encryptData(plaintext, associated_data, encryption_algorithm, cek_algorithm, public_key)](#Account+encryptData)[<code>Promise.&lt;EncryptedData&gt;</code>](#EncryptedData)
* [.decryptData(data, encryption_algorithm, cek_algorithm, fragment)](#Account+decryptData) ⇒ <code>Promise.&lt;Uint8Array&gt;</code>
* [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ <code>Promise.&lt;void&gt;</code>
* [.deleteMethod(options)](#Account+deleteMethod) ⇒ <code>Promise.&lt;void&gt;</code>
* [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ <code>Promise.&lt;void&gt;</code>
* [.deleteService(options)](#Account+deleteService) ⇒ <code>Promise.&lt;void&gt;</code>
* [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ <code>Promise.&lt;void&gt;</code>
* [.setController(options)](#Account+setController) ⇒ <code>Promise.&lt;void&gt;</code>
* [.createService(options)](#Account+createService) ⇒ <code>Promise.&lt;void&gt;</code>

<a name="Account+attachMethodRelationships"></a>
* [.deleteMethod(options)](#Account+deleteMethod) ⇒ <code>Promise.&lt;void&gt;</code>

### account.attachMethodRelationships(options) ⇒ <code>Promise.&lt;void&gt;</code>
Attach one or more verification relationships to a method.
<a name="Account+createService"></a>

Note: the method must exist and be in the set of verification methods;
it cannot be an embedded method.
### account.createService(options) ⇒ <code>Promise.&lt;void&gt;</code>
Adds a new Service to the DID Document.

**Kind**: instance method of [<code>Account</code>](#Account)

| Param | Type |
| --- | --- |
| options | <code>AttachMethodRelationshipOptions</code> |
| options | <code>CreateServiceOptions</code> |

<a name="Account+createMethod"></a>

Expand All @@ -273,16 +270,19 @@ Adds a new verification method to the DID document.
| --- | --- |
| options | <code>CreateMethodOptions</code> |

<a name="Account+detachMethodRelationships"></a>
<a name="Account+attachMethodRelationships"></a>

### account.detachMethodRelationships(options) ⇒ <code>Promise.&lt;void&gt;</code>
Detaches the given relationship from the given method, if the method exists.
### account.attachMethodRelationships(options) ⇒ <code>Promise.&lt;void&gt;</code>
Attach one or more verification relationships to a method.

Note: the method must exist and be in the set of verification methods;
it cannot be an embedded method.

**Kind**: instance method of [<code>Account</code>](#Account)

| Param | Type |
| --- | --- |
| options | <code>DetachMethodRelationshipOptions</code> |
| options | <code>AttachMethodRelationshipOptions</code> |

<a name="Account+did"></a>

Expand Down Expand Up @@ -475,38 +475,38 @@ Returns the decrypted text.
| cek_algorithm | [<code>CekAlgorithm</code>](#CekAlgorithm) |
| fragment | <code>string</code> |

<a name="Account+setAlsoKnownAs"></a>
<a name="Account+detachMethodRelationships"></a>

### account.setAlsoKnownAs(options) ⇒ <code>Promise.&lt;void&gt;</code>
Sets the `alsoKnownAs` property in the DID document.
### account.detachMethodRelationships(options) ⇒ <code>Promise.&lt;void&gt;</code>
Detaches the given relationship from the given method, if the method exists.

**Kind**: instance method of [<code>Account</code>](#Account)

| Param | Type |
| --- | --- |
| options | <code>SetAlsoKnownAsOptions</code> |
| options | <code>DetachMethodRelationshipOptions</code> |

<a name="Account+deleteMethod"></a>
<a name="Account+deleteService"></a>

### account.deleteMethod(options) ⇒ <code>Promise.&lt;void&gt;</code>
Deletes a verification method if the method exists.
### account.deleteService(options) ⇒ <code>Promise.&lt;void&gt;</code>
Deletes a Service if it exists.

**Kind**: instance method of [<code>Account</code>](#Account)

| Param | Type |
| --- | --- |
| options | <code>DeleteMethodOptions</code> |
| options | <code>DeleteServiceOptions</code> |

<a name="Account+deleteService"></a>
<a name="Account+setAlsoKnownAs"></a>

### account.deleteService(options) ⇒ <code>Promise.&lt;void&gt;</code>
Deletes a Service if it exists.
### account.setAlsoKnownAs(options) ⇒ <code>Promise.&lt;void&gt;</code>
Sets the `alsoKnownAs` property in the DID document.

**Kind**: instance method of [<code>Account</code>](#Account)

| Param | Type |
| --- | --- |
| options | <code>DeleteServiceOptions</code> |
| options | <code>SetAlsoKnownAsOptions</code> |

<a name="Account+setController"></a>

Expand All @@ -519,16 +519,16 @@ Sets the controllers of the DID document.
| --- | --- |
| options | <code>SetControllerOptions</code> |

<a name="Account+createService"></a>
<a name="Account+deleteMethod"></a>

### account.createService(options) ⇒ <code>Promise.&lt;void&gt;</code>
Adds a new Service to the DID Document.
### account.deleteMethod(options) ⇒ <code>Promise.&lt;void&gt;</code>
Deletes a verification method if the method exists.

**Kind**: instance method of [<code>Account</code>](#Account)

| Param | Type |
| --- | --- |
| options | <code>CreateServiceOptions</code> |
| options | <code>DeleteMethodOptions</code> |

<a name="AccountBuilder"></a>

Expand Down Expand Up @@ -1176,6 +1176,7 @@ Deserializes a `CredentialValidationOptions` from a JSON object.
* [.verifySignature(credential, trusted_issuers, options)](#CredentialValidator.verifySignature)
* [.check_subject_holder_relationship(credential, holder_url, relationship)](#CredentialValidator.check_subject_holder_relationship)
* [.checkStatus(credential, trustedIssuers, statusCheck)](#CredentialValidator.checkStatus)
* [.extractIssuer(credential)](#CredentialValidator.extractIssuer)[<code>DID</code>](#DID)

<a name="CredentialValidator.validate"></a>

Expand Down Expand Up @@ -1269,7 +1270,7 @@ to verify the credential's signature will be made and an error is returned upon
| Param | Type |
| --- | --- |
| credential | [<code>Credential</code>](#Credential) |
| trusted_issuers | [<code>Array.&lt;Document&gt;</code>](#Document) \| [<code>Array.&lt;ResolvedDocument&gt;</code>](#ResolvedDocument) |
| trusted_issuers | <code>Array.&lt;(Document\|ResolvedDocument)&gt;</code> |
| options | [<code>VerifierOptions</code>](#VerifierOptions) |

<a name="CredentialValidator.check_subject_holder_relationship"></a>
Expand Down Expand Up @@ -1298,9 +1299,24 @@ Only supports `BitmapRevocation2022`.
| Param | Type |
| --- | --- |
| credential | [<code>Credential</code>](#Credential) |
| trustedIssuers | [<code>Array.&lt;Document&gt;</code>](#Document) \| [<code>Array.&lt;ResolvedDocument&gt;</code>](#ResolvedDocument) |
| trustedIssuers | <code>Array.&lt;(Document\|ResolvedDocument)&gt;</code> |
| statusCheck | <code>number</code> |

<a name="CredentialValidator.extractIssuer"></a>

### CredentialValidator.extractIssuer(credential) ⇒ [<code>DID</code>](#DID)
Utility for extracting the issuer field of a `Credential` as a DID.

### Errors

Fails if the issuer field is not a valid DID.

**Kind**: static method of [<code>CredentialValidator</code>](#CredentialValidator)

| Param | Type |
| --- | --- |
| credential | [<code>Credential</code>](#Credential) |

<a name="DID"></a>

## DID
Expand Down Expand Up @@ -1755,6 +1771,7 @@ Deserializes a `DiffMessage` from a JSON object.
* [.service()](#Document+service)[<code>Array.&lt;Service&gt;</code>](#Service)
* [.insertService(service)](#Document+insertService) ⇒ <code>boolean</code>
* [.removeService(did)](#Document+removeService) ⇒ <code>boolean</code>
* [.resolveService(query)](#Document+resolveService)[<code>Service</code>](#Service) \| <code>undefined</code>
* [.methods()](#Document+methods)[<code>Array.&lt;VerificationMethod&gt;</code>](#VerificationMethod)
* [.insertMethod(method, scope)](#Document+insertMethod)
* [.removeMethod(did)](#Document+removeMethod)
Expand Down Expand Up @@ -1782,8 +1799,8 @@ Deserializes a `DiffMessage` from a JSON object.
* [.metadataPreviousMessageId()](#Document+metadataPreviousMessageId) ⇒ <code>string</code>
* [.setMetadataPreviousMessageId(value)](#Document+setMetadataPreviousMessageId)
* [.proof()](#Document+proof)[<code>Proof</code>](#Proof) \| <code>undefined</code>
* [.revokeCredentials(fragment, credentialIndices)](#Document+revokeCredentials)
* [.unrevokeCredentials(fragment, credentialIndices)](#Document+unrevokeCredentials)
* [.revokeCredentials(serviceQuery, credentialIndices)](#Document+revokeCredentials)
* [.unrevokeCredentials(serviceQuery, credentialIndices)](#Document+unrevokeCredentials)
* [.toJSON()](#Document+toJSON) ⇒ <code>any</code>
* [.clone()](#Document+clone)[<code>Document</code>](#Document)
* _static_
Expand Down Expand Up @@ -1916,6 +1933,18 @@ Returns `true` if a service was removed.
| --- | --- |
| did | [<code>DIDUrl</code>](#DIDUrl) |

<a name="Document+resolveService"></a>

### document.resolveService(query) ⇒ [<code>Service</code>](#Service) \| <code>undefined</code>
Returns the first [Service](#Service) with an `id` property matching the provided `query`,
if present.

**Kind**: instance method of [<code>Document</code>](#Document)

| Param | Type |
| --- | --- |
| query | [<code>DIDUrl</code>](#DIDUrl) \| <code>string</code> |

<a name="Document+methods"></a>

### document.methods() ⇒ [<code>Array.&lt;VerificationMethod&gt;</code>](#VerificationMethod)
Expand Down Expand Up @@ -2252,28 +2281,28 @@ Returns a copy of the proof.
**Kind**: instance method of [<code>Document</code>](#Document)
<a name="Document+revokeCredentials"></a>

### document.revokeCredentials(fragment, credentialIndices)
If the document has a `RevocationBitmap` service identified by `fragment`,
### document.revokeCredentials(serviceQuery, credentialIndices)
If the document has a `RevocationBitmap` service identified by `serviceQuery`,
revoke all credentials with a revocationBitmapIndex in `credentialIndices`.

**Kind**: instance method of [<code>Document</code>](#Document)

| Param | Type |
| --- | --- |
| fragment | <code>string</code> |
| serviceQuery | [<code>DIDUrl</code>](#DIDUrl) \| <code>string</code> |
| credentialIndices | <code>number</code> \| <code>Array.&lt;number&gt;</code> |

<a name="Document+unrevokeCredentials"></a>

### document.unrevokeCredentials(fragment, credentialIndices)
If the document has a `RevocationBitmap` service identified by `fragment`,
### document.unrevokeCredentials(serviceQuery, credentialIndices)
If the document has a `RevocationBitmap` service identified by `serviceQuery`,
unrevoke all credentials with a revocationBitmapIndex in `credentialIndices`.

**Kind**: instance method of [<code>Document</code>](#Document)

| Param | Type |
| --- | --- |
| fragment | <code>string</code> |
| serviceQuery | [<code>DIDUrl</code>](#DIDUrl) \| <code>string</code> |
| credentialIndices | <code>number</code> \| <code>Array.&lt;number&gt;</code> |

<a name="Document+toJSON"></a>
Expand Down Expand Up @@ -3591,6 +3620,7 @@ Deserializes a `PresentationValidationOptions` from a JSON object.
* [.validate(presentation, holder, issuers, options, fail_fast)](#PresentationValidator.validate)
* [.verifyPresentationSignature(presentation, holder, options)](#PresentationValidator.verifyPresentationSignature)
* [.checkStructure(presentation)](#PresentationValidator.checkStructure)
* [.extractHolder(presentation)](#PresentationValidator.extractHolder)[<code>DID</code>](#DID)

<a name="PresentationValidator.validate"></a>

Expand Down Expand Up @@ -3628,7 +3658,7 @@ An error is returned whenever a validated condition is not satisfied.
| --- | --- |
| presentation | [<code>Presentation</code>](#Presentation) |
| holder | [<code>Document</code>](#Document) \| [<code>ResolvedDocument</code>](#ResolvedDocument) |
| issuers | [<code>Array.&lt;Document&gt;</code>](#Document) \| [<code>Array.&lt;ResolvedDocument&gt;</code>](#ResolvedDocument) |
| issuers | <code>Array.&lt;(Document\|ResolvedDocument)&gt;</code> |
| options | [<code>PresentationValidationOptions</code>](#PresentationValidationOptions) |
| fail_fast | <code>number</code> |

Expand Down Expand Up @@ -3663,6 +3693,21 @@ Validates the semantic structure of the `Presentation`.
| --- | --- |
| presentation | [<code>Presentation</code>](#Presentation) |

<a name="PresentationValidator.extractHolder"></a>

### PresentationValidator.extractHolder(presentation) ⇒ [<code>DID</code>](#DID)
Utility for extracting the holder field of a `Presentation` as a DID.

### Errors

Fails if the holder field is missing or not a valid DID.

**Kind**: static method of [<code>PresentationValidator</code>](#PresentationValidator)

| Param | Type |
| --- | --- |
| presentation | [<code>Presentation</code>](#Presentation) |

<a name="Proof"></a>

## Proof
Expand Down Expand Up @@ -4185,8 +4230,8 @@ according to the `fail_fast` parameter.
| presentation | [<code>Presentation</code>](#Presentation) |
| options | [<code>PresentationValidationOptions</code>](#PresentationValidationOptions) |
| fail_fast | <code>number</code> |
| holder | [<code>ResolvedDocument</code>](#ResolvedDocument) \| <code>undefined</code> |
| issuers | [<code>Array.&lt;ResolvedDocument&gt;</code>](#ResolvedDocument) \| <code>undefined</code> |
| holder | [<code>Document</code>](#Document) \| [<code>ResolvedDocument</code>](#ResolvedDocument) \| <code>undefined</code> |
| issuers | <code>Array.&lt;(Document\|ResolvedDocument)&gt;</code> \| <code>undefined</code> |

<a name="Resolver.builder"></a>

Expand Down
33 changes: 23 additions & 10 deletions bindings/wasm/src/credential/credential_validator.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use identity_iota::client::CredentialValidator;
use identity_iota::client::ResolvedIotaDocument;
use identity_iota::client::StatusCheck;
use identity_iota::client::ValidationError;
use identity_iota::core::Url;
use identity_iota::credential::CredentialValidator;
use identity_iota::credential::StatusCheck;
use identity_iota::credential::ValidationError;
use identity_iota::iota_core::IotaDID;
use identity_iota::iota_core::IotaDocument;
use wasm_bindgen::prelude::*;

use crate::common::WasmTimestamp;
use crate::credential::validation_options::WasmFailFast;
use crate::credential::validation_options::WasmStatusCheck;
use crate::did::ArrayDocumentOrArrayResolvedDocument;
use crate::did::ArrayDocumentOrResolvedDocument;
use crate::did::DocumentOrResolvedDocument;
use crate::did::WasmDID;
use crate::did::WasmVerifierOptions;
use crate::error::Result;
use crate::error::WasmResult;
Expand Down Expand Up @@ -59,7 +61,7 @@ impl WasmCredentialValidator {
fail_fast: WasmFailFast,
) -> Result<()> {
let issuer_doc: ResolvedIotaDocument = issuer.into_serde().wasm_result()?;
CredentialValidator::validate(&credential.0, &issuer_doc, &options.0, fail_fast.into()).wasm_result()
CredentialValidator::validate(&credential.0, &issuer_doc.document, &options.0, fail_fast.into()).wasm_result()
}

/// Validates the semantic structure of the `Credential`.
Expand Down Expand Up @@ -98,11 +100,11 @@ impl WasmCredentialValidator {
#[wasm_bindgen(js_name = verifySignature)]
pub fn verify_signature(
credential: &WasmCredential,
trusted_issuers: &ArrayDocumentOrArrayResolvedDocument,
trusted_issuers: &ArrayDocumentOrResolvedDocument,
options: &WasmVerifierOptions,
) -> Result<()> {
let trusted_issuers: Vec<ResolvedIotaDocument> = trusted_issuers.into_serde().wasm_result()?;
CredentialValidator::verify_signature(&credential.0, trusted_issuers.as_slice(), &options.0).wasm_result()
let issuers: Vec<IotaDocument> = trusted_issuers.into_serde().wasm_result()?;
CredentialValidator::verify_signature(&credential.0, &issuers, &options.0).wasm_result()
}

/// Validate that the relationship between the `holder` and the credential subjects is in accordance with
Expand All @@ -123,11 +125,22 @@ impl WasmCredentialValidator {
#[allow(non_snake_case)]
pub fn check_status(
credential: &WasmCredential,
trustedIssuers: &ArrayDocumentOrArrayResolvedDocument,
trustedIssuers: &ArrayDocumentOrResolvedDocument,
statusCheck: WasmStatusCheck,
) -> Result<()> {
let trusted_issuers: Vec<IotaDocument> = trustedIssuers.into_serde().wasm_result()?;
let status_check: StatusCheck = StatusCheck::from(statusCheck);
CredentialValidator::check_status(&credential.0, trusted_issuers.as_slice(), status_check).wasm_result()
CredentialValidator::check_status(&credential.0, &trusted_issuers, status_check).wasm_result()
}

/// Utility for extracting the issuer field of a `Credential` as a DID.
///
/// ### Errors
///
/// Fails if the issuer field is not a valid DID.
#[wasm_bindgen(js_name = extractIssuer)]
pub fn extract_issuer(credential: &WasmCredential) -> Result<WasmDID> {
let did: IotaDID = CredentialValidator::extract_issuer(&credential.0).wasm_result()?;
Ok(WasmDID::from(did))
}
}
Loading

0 comments on commit f0e4529

Please sign in to comment.