diff --git a/README.md b/README.md index 658479444f..41ba9d8519 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ ## Introduction -IOTA Identity is a [Rust](https://www.rust-lang.org/) implementation of decentralized digital identity, also known as Self-Sovereign Identity (SSI). It implements the W3C [Decentralized Identifiers (DID)](https://www.w3.org/TR/did-core/) and [Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) specifications. This library can be used to create, resolve and authenticate digital identities and to create verifiable credentials and presentations in order to share information in a verifiable manner and establish trust in the digital world. It does so while supporting secure storage of cryptographic keys, which can be implemented for your preferred key management system. Many of the individual libraries (Rust crates) are agnostic over the concrete DID method, with the exception of some libraries dedicated to implement the [IOTA DID method](https://wiki.iota.org/shimmer/identity.rs/specs/did/iota_did_method_spec/), which is an implementation of decentralized digital identity on the IOTA and Shimmer networks. Written in stable Rust, IOTA Identity has strong guarantees of memory safety and process integrity while maintaining exceptional performance. +IOTA Identity is a [Rust](https://www.rust-lang.org/) implementation of decentralized digital identity, also known as Self-Sovereign Identity (SSI). It implements the W3C [Decentralized Identifiers (DID)](https://www.w3.org/TR/did-core/) and [Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) specifications. This library can be used to create, resolve and authenticate digital identities and to create verifiable credentials and presentations in order to share information in a verifiable manner and establish trust in the digital world. It does so while supporting secure storage of cryptographic keys, which can be implemented for your preferred key management system. Many of the individual libraries (Rust crates) are agnostic over the concrete DID method, with the exception of some libraries dedicated to implement the [IOTA DID method](https://wiki.iota.org/identity.rs/specs/did/iota_did_method_spec/), which is an implementation of decentralized digital identity on the IOTA and Shimmer networks. Written in stable Rust, IOTA Identity has strong guarantees of memory safety and process integrity while maintaining exceptional performance. ## Bindings @@ -36,8 +36,8 @@ IOTA Identity is a [Rust](https://www.rust-lang.org/) implementation of decentra - API References: - [Rust API Reference](https://docs.rs/identity_iota/latest/identity_iota/): Package documentation (cargo docs). - - [Wasm API Reference](https://wiki.iota.org/shimmer/identity.rs/libraries/wasm/api_reference/): Wasm Package documentation. -- [Identity Documentation Pages](https://wiki.iota.org/shimmer/identity.rs/introduction): Supplementing documentation with context around identity and simple examples on library usage. + - [Wasm API Reference](https://wiki.iota.org/identity.rs/libraries/wasm/api_reference/): Wasm Package documentation. +- [Identity Documentation Pages](https://wiki.iota.org/identity.rs/introduction): Supplementing documentation with context around identity and simple examples on library usage. - [Examples](https://github.com/iotaledger/identity.rs/blob/HEAD/examples): Practical code snippets to get you started with the library. ## Prerequisites @@ -238,7 +238,7 @@ For detailed development progress, see the IOTA Identity development [kanban boa We would love to have you help us with the development of IOTA Identity. Each and every contribution is greatly valued! -Please review the [contribution](https://wiki.iota.org/shimmer/identity.rs/contribute) and [workflow](https://wiki.iota.org/shimmer/identity.rs/workflow) sections in the [IOTA Wiki](https://wiki.iota.org/). +Please review the [contribution](https://wiki.iota.org/identity.rs/contribute) and [workflow](https://wiki.iota.org/identity.rs/workflow) sections in the [IOTA Wiki](https://wiki.iota.org/). To contribute directly to the repository, simply fork the project, push your changes to your fork and create a pull request to get them included! diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index e83bbc81b6..6fcacf2843 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -11,9 +11,16 @@ if the object is being concurrently modified.

Credential
+
CustomMethodData
+

A custom verification method data format.

+
DIDUrl

A method agnostic DID Url.

+
DecodedJptCredential
+
+
DecodedJptPresentation
+
DecodedJws

A cryptographically verified decoded token from a JWS.

Contains the decoded headers and the raw claims.

@@ -64,11 +71,32 @@ if the object is being concurrently modified.

An extension interface that provides helper functions for publication and resolution of DID documents in Alias Outputs.

+
Jpt
+

A JSON Proof Token (JPT).

+
+
JptCredentialValidationOptions
+

Options to declare validation criteria for Jpt.

+
+
JptCredentialValidator
+
+
JptCredentialValidatorUtils
+

Utility functions for validating JPT credentials.

+
+
JptPresentationValidationOptions
+

Options to declare validation criteria for a Jpt presentation.

+
+
JptPresentationValidator
+
+
JptPresentationValidatorUtils
+

Utility functions for verifying JPT presentations.

+
Jwk
JwkGenOutput

The result of a key generation in JwkStorage.

+
JwpVerificationOptions
+
Jws

A wrapper around a JSON Web Signature (JWS).

@@ -141,6 +169,9 @@ verifiable Credentials and Pre
RevocationBitmap

A compressed bitmap for managing credential revocation.

+
RevocationTimeframeStatus
+

Information used to determine the current status of a Credential.

+
SdJwt

Representation of an SD-JWT of the format <Issuer-signed JWT>~<Disclosure 1>~<Disclosure 2>~...~<Disclosure N>~<optional KB-JWT>.

@@ -187,25 +218,6 @@ working with storage backed DID documents.

## Members
-
CredentialStatus
-
-
SubjectHolderRelationship
-

Declares how credential subjects must relate to the presentation holder.

-

See also the Subject-Holder Relationship section of the specification.

-
-
AlwaysSubject
-

The holder must always match the subject on all credentials, regardless of their nonTransferable property. -This variant is the default.

-
-
SubjectOnNonTransferable
-

The holder must match the subject only for credentials where the nonTransferable property is true.

-
-
Any
-

The holder is not required to have any kind of relationship to any credential subject.

-
-
StatusPurpose
-

Purpose of a StatusList2021.

-
FailFast

Declares when validation should return if an error occurs.

@@ -215,6 +227,15 @@ This variant is the default.

FirstError

Return after the first error occurs.

+
MethodRelationship
+
+
CredentialStatus
+
+
StateMetadataEncoding
+
+
StatusPurpose
+

Purpose of a StatusList2021.

+
StatusCheck

Controls validation behaviour when checking whether or not a credential has been revoked by its credentialStatus.

@@ -232,17 +253,30 @@ This variant is the default.

SkipAll

Skip all status checks.

-
StateMetadataEncoding
-
-
MethodRelationship
-
+
SubjectHolderRelationship
+

Declares how credential subjects must relate to the presentation holder.

+

See also the Subject-Holder Relationship section of the specification.

+
+
AlwaysSubject
+

The holder must always match the subject on all credentials, regardless of their nonTransferable property. +This variant is the default.

+
+
SubjectOnNonTransferable
+

The holder must match the subject only for credentials where the nonTransferable property is true.

+
+
Any
+

The holder is not required to have any kind of relationship to any credential subject.

+
## Functions
-
start()
-

Initializes the console error panic hook for better error messages

+
encodeB64(data)string
+

Encode the given bytes in url-safe base64.

+
+
decodeB64(data)Uint8Array
+

Decode the given url-safe base64-encoded slice into its raw bytes.

verifyEd25519(alg, signingInput, decodedSignature, publicKey)

Verify a JWS signature secured with the EdDSA algorithm and curve Ed25519.

@@ -252,11 +286,8 @@ This variant is the default.

This function does not check whether alg = EdDSA in the protected header. Callers are expected to assert this prior to calling the function.

-
encodeB64(data)string
-

Encode the given bytes in url-safe base64.

-
-
decodeB64(data)Uint8Array
-

Decode the given url-safe base64-encoded slice into its raw bytes.

+
start()
+

Initializes the console error panic hook for better error messages

@@ -1138,6 +1169,53 @@ Deserializes an instance from a JSON object. | --- | --- | | json | any | + + +## CustomMethodData +A custom verification method data format. + +**Kind**: global class + +* [CustomMethodData](#CustomMethodData) + * [new CustomMethodData(name, data)](#new_CustomMethodData_new) + * _instance_ + * [.clone()](#CustomMethodData+clone) ⇒ [CustomMethodData](#CustomMethodData) + * [.toJSON()](#CustomMethodData+toJSON) ⇒ any + * _static_ + * [.fromJSON(json)](#CustomMethodData.fromJSON) ⇒ [CustomMethodData](#CustomMethodData) + + + +### new CustomMethodData(name, data) + +| Param | Type | +| --- | --- | +| name | string | +| data | any | + + + +### customMethodData.clone() ⇒ [CustomMethodData](#CustomMethodData) +Deep clones the object. + +**Kind**: instance method of [CustomMethodData](#CustomMethodData) + + +### customMethodData.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [CustomMethodData](#CustomMethodData) + + +### CustomMethodData.fromJSON(json) ⇒ [CustomMethodData](#CustomMethodData) +Deserializes an instance from a JSON object. + +**Kind**: static method of [CustomMethodData](#CustomMethodData) + +| Param | Type | +| --- | --- | +| json | any | + ## DIDUrl @@ -1285,6 +1363,69 @@ Deserializes an instance from a JSON object. | --- | --- | | json | any | + + +## DecodedJptCredential +**Kind**: global class + +* [DecodedJptCredential](#DecodedJptCredential) + * [.clone()](#DecodedJptCredential+clone) ⇒ [DecodedJptCredential](#DecodedJptCredential) + * [.credential()](#DecodedJptCredential+credential) ⇒ [Credential](#Credential) + * [.customClaims()](#DecodedJptCredential+customClaims) ⇒ Map.<string, any> + + + +### decodedJptCredential.clone() ⇒ [DecodedJptCredential](#DecodedJptCredential) +Deep clones the object. + +**Kind**: instance method of [DecodedJptCredential](#DecodedJptCredential) + + +### decodedJptCredential.credential() ⇒ [Credential](#Credential) +Returns the [Credential](#Credential) embedded into this JPT. + +**Kind**: instance method of [DecodedJptCredential](#DecodedJptCredential) + + +### decodedJptCredential.customClaims() ⇒ Map.<string, any> +Returns the custom claims parsed from the JPT. + +**Kind**: instance method of [DecodedJptCredential](#DecodedJptCredential) + + +## DecodedJptPresentation +**Kind**: global class + +* [DecodedJptPresentation](#DecodedJptPresentation) + * [.clone()](#DecodedJptPresentation+clone) ⇒ [DecodedJptPresentation](#DecodedJptPresentation) + * [.credential()](#DecodedJptPresentation+credential) ⇒ [Credential](#Credential) + * [.customClaims()](#DecodedJptPresentation+customClaims) ⇒ Map.<string, any> + * [.aud()](#DecodedJptPresentation+aud) ⇒ string \| undefined + + + +### decodedJptPresentation.clone() ⇒ [DecodedJptPresentation](#DecodedJptPresentation) +Deep clones the object. + +**Kind**: instance method of [DecodedJptPresentation](#DecodedJptPresentation) + + +### decodedJptPresentation.credential() ⇒ [Credential](#Credential) +Returns the [Credential](#Credential) embedded into this JPT. + +**Kind**: instance method of [DecodedJptPresentation](#DecodedJptPresentation) + + +### decodedJptPresentation.customClaims() ⇒ Map.<string, any> +Returns the custom claims parsed from the JPT. + +**Kind**: instance method of [DecodedJptPresentation](#DecodedJptPresentation) + + +### decodedJptPresentation.aud() ⇒ string \| undefined +Returns the `aud` property parsed from the JWT claims. + +**Kind**: instance method of [DecodedJptPresentation](#DecodedJptPresentation) ## DecodedJws @@ -1967,7 +2108,7 @@ if the object is being concurrently modified. * _instance_ * [.id()](#IotaDocument+id) ⇒ [IotaDID](#IotaDID) * [.controller()](#IotaDocument+controller) ⇒ [Array.<IotaDID>](#IotaDID) - * [.setController(controllers)](#IotaDocument+setController) + * [.setController(controller)](#IotaDocument+setController) * [.alsoKnownAs()](#IotaDocument+alsoKnownAs) ⇒ Array.<string> * [.setAlsoKnownAs(urls)](#IotaDocument+setAlsoKnownAs) * [.properties()](#IotaDocument+properties) ⇒ Map.<string, any> @@ -2042,7 +2183,7 @@ during resolution and are omitted when publishing. **Kind**: instance method of [IotaDocument](#IotaDocument) -### iotaDocument.setController(controllers) +### iotaDocument.setController(controller) Sets the controllers of the document. Note: Duplicates will be ignored. @@ -2052,7 +2193,7 @@ Use `null` to remove all controllers. | Param | Type | | --- | --- | -| controllers | [CoreDID](#CoreDID) \| [Array.<CoreDID>](#CoreDID) \| null | +| controller | [Array.<IotaDID>](#IotaDID) \| null | @@ -2747,6 +2888,292 @@ Fetches the `IAliasOutput` associated with the given DID. | client | IIotaIdentityClient | | did | [IotaDID](#IotaDID) | + + +## Jpt +A JSON Proof Token (JPT). + +**Kind**: global class + +* [Jpt](#Jpt) + * [new Jpt(jpt_string)](#new_Jpt_new) + * [.toString()](#Jpt+toString) ⇒ string + * [.clone()](#Jpt+clone) ⇒ [Jpt](#Jpt) + + + +### new Jpt(jpt_string) +Creates a new [Jpt](#Jpt). + + +| Param | Type | +| --- | --- | +| jpt_string | string | + + + +### jpt.toString() ⇒ string +**Kind**: instance method of [Jpt](#Jpt) + + +### jpt.clone() ⇒ [Jpt](#Jpt) +Deep clones the object. + +**Kind**: instance method of [Jpt](#Jpt) + + +## JptCredentialValidationOptions +Options to declare validation criteria for [Jpt](#Jpt). + +**Kind**: global class + +* [JptCredentialValidationOptions](#JptCredentialValidationOptions) + * [new JptCredentialValidationOptions([opts])](#new_JptCredentialValidationOptions_new) + * _instance_ + * [.clone()](#JptCredentialValidationOptions+clone) ⇒ [JptCredentialValidationOptions](#JptCredentialValidationOptions) + * [.toJSON()](#JptCredentialValidationOptions+toJSON) ⇒ any + * _static_ + * [.fromJSON(json)](#JptCredentialValidationOptions.fromJSON) ⇒ [JptCredentialValidationOptions](#JptCredentialValidationOptions) + + + +### new JptCredentialValidationOptions([opts]) +Creates a new default istance. + + +| Param | Type | +| --- | --- | +| [opts] | IJptCredentialValidationOptions \| undefined | + + + +### jptCredentialValidationOptions.clone() ⇒ [JptCredentialValidationOptions](#JptCredentialValidationOptions) +Deep clones the object. + +**Kind**: instance method of [JptCredentialValidationOptions](#JptCredentialValidationOptions) + + +### jptCredentialValidationOptions.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [JptCredentialValidationOptions](#JptCredentialValidationOptions) + + +### JptCredentialValidationOptions.fromJSON(json) ⇒ [JptCredentialValidationOptions](#JptCredentialValidationOptions) +Deserializes an instance from a JSON object. + +**Kind**: static method of [JptCredentialValidationOptions](#JptCredentialValidationOptions) + +| Param | Type | +| --- | --- | +| json | any | + + + +## JptCredentialValidator +**Kind**: global class + + +### JptCredentialValidator.validate(credential_jpt, issuer, options, fail_fast) ⇒ [DecodedJptCredential](#DecodedJptCredential) +**Kind**: static method of [JptCredentialValidator](#JptCredentialValidator) + +| Param | Type | +| --- | --- | +| credential_jpt | [Jpt](#Jpt) | +| issuer | [CoreDocument](#CoreDocument) | +| options | [JptCredentialValidationOptions](#JptCredentialValidationOptions) | +| fail_fast | [FailFast](#FailFast) | + + + +## JptCredentialValidatorUtils +Utility functions for validating JPT credentials. + +**Kind**: global class + +* [JptCredentialValidatorUtils](#JptCredentialValidatorUtils) + * [.extractIssuer(credential)](#JptCredentialValidatorUtils.extractIssuer) ⇒ [CoreDID](#CoreDID) + * [.extractIssuerFromIssuedJpt(credential)](#JptCredentialValidatorUtils.extractIssuerFromIssuedJpt) ⇒ [CoreDID](#CoreDID) + * [.checkTimeframesWithValidityTimeframe2024(credential, validity_timeframe, status_check)](#JptCredentialValidatorUtils.checkTimeframesWithValidityTimeframe2024) + * [.checkRevocationWithValidityTimeframe2024(credential, issuer, status_check)](#JptCredentialValidatorUtils.checkRevocationWithValidityTimeframe2024) + * [.checkTimeframesAndRevocationWithValidityTimeframe2024(credential, issuer, validity_timeframe, status_check)](#JptCredentialValidatorUtils.checkTimeframesAndRevocationWithValidityTimeframe2024) + + + +### JptCredentialValidatorUtils.extractIssuer(credential) ⇒ [CoreDID](#CoreDID) +Utility for extracting the issuer field of a [`Credential`](`Credential`) as a DID. +# Errors +Fails if the issuer field is not a valid DID. + +**Kind**: static method of [JptCredentialValidatorUtils](#JptCredentialValidatorUtils) + +| Param | Type | +| --- | --- | +| credential | [Credential](#Credential) | + + + +### JptCredentialValidatorUtils.extractIssuerFromIssuedJpt(credential) ⇒ [CoreDID](#CoreDID) +Utility for extracting the issuer field of a credential in JPT representation as DID. +# Errors +If the JPT decoding fails or the issuer field is not a valid DID. + +**Kind**: static method of [JptCredentialValidatorUtils](#JptCredentialValidatorUtils) + +| Param | Type | +| --- | --- | +| credential | [Jpt](#Jpt) | + + + +### JptCredentialValidatorUtils.checkTimeframesWithValidityTimeframe2024(credential, validity_timeframe, status_check) +**Kind**: static method of [JptCredentialValidatorUtils](#JptCredentialValidatorUtils) + +| Param | Type | +| --- | --- | +| credential | [Credential](#Credential) | +| validity_timeframe | [Timestamp](#Timestamp) \| undefined | +| status_check | [StatusCheck](#StatusCheck) | + + + +### JptCredentialValidatorUtils.checkRevocationWithValidityTimeframe2024(credential, issuer, status_check) +Checks whether the credential status has been revoked. + +Only supports `RevocationTimeframe2024`. + +**Kind**: static method of [JptCredentialValidatorUtils](#JptCredentialValidatorUtils) + +| Param | Type | +| --- | --- | +| credential | [Credential](#Credential) | +| issuer | [CoreDocument](#CoreDocument) | +| status_check | [StatusCheck](#StatusCheck) | + + + +### JptCredentialValidatorUtils.checkTimeframesAndRevocationWithValidityTimeframe2024(credential, issuer, validity_timeframe, status_check) +Checks whether the credential status has been revoked or the timeframe interval is INVALID + +Only supports `RevocationTimeframe2024`. + +**Kind**: static method of [JptCredentialValidatorUtils](#JptCredentialValidatorUtils) + +| Param | Type | +| --- | --- | +| credential | [Credential](#Credential) | +| issuer | [CoreDocument](#CoreDocument) | +| validity_timeframe | [Timestamp](#Timestamp) \| undefined | +| status_check | [StatusCheck](#StatusCheck) | + + + +## JptPresentationValidationOptions +Options to declare validation criteria for a [Jpt](#Jpt) presentation. + +**Kind**: global class + +* [JptPresentationValidationOptions](#JptPresentationValidationOptions) + * [new JptPresentationValidationOptions([opts])](#new_JptPresentationValidationOptions_new) + * _instance_ + * [.clone()](#JptPresentationValidationOptions+clone) ⇒ [JptPresentationValidationOptions](#JptPresentationValidationOptions) + * [.toJSON()](#JptPresentationValidationOptions+toJSON) ⇒ any + * _static_ + * [.fromJSON(json)](#JptPresentationValidationOptions.fromJSON) ⇒ [JptPresentationValidationOptions](#JptPresentationValidationOptions) + + + +### new JptPresentationValidationOptions([opts]) + +| Param | Type | +| --- | --- | +| [opts] | IJptPresentationValidationOptions \| undefined | + + + +### jptPresentationValidationOptions.clone() ⇒ [JptPresentationValidationOptions](#JptPresentationValidationOptions) +Deep clones the object. + +**Kind**: instance method of [JptPresentationValidationOptions](#JptPresentationValidationOptions) + + +### jptPresentationValidationOptions.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [JptPresentationValidationOptions](#JptPresentationValidationOptions) + + +### JptPresentationValidationOptions.fromJSON(json) ⇒ [JptPresentationValidationOptions](#JptPresentationValidationOptions) +Deserializes an instance from a JSON object. + +**Kind**: static method of [JptPresentationValidationOptions](#JptPresentationValidationOptions) + +| Param | Type | +| --- | --- | +| json | any | + + + +## JptPresentationValidator +**Kind**: global class + + +### JptPresentationValidator.validate(presentation_jpt, issuer, options, fail_fast) ⇒ [DecodedJptPresentation](#DecodedJptPresentation) +Decodes and validates a Presented [Credential](#Credential) issued as a JPT (JWP Presented Form). A +[DecodedJptPresentation](#DecodedJptPresentation) is returned upon success. + +The following properties are validated according to `options`: +- the holder's proof on the JWP, +- the expiration date, +- the issuance date, +- the semantic structure. + +**Kind**: static method of [JptPresentationValidator](#JptPresentationValidator) + +| Param | Type | +| --- | --- | +| presentation_jpt | [Jpt](#Jpt) | +| issuer | [CoreDocument](#CoreDocument) | +| options | [JptPresentationValidationOptions](#JptPresentationValidationOptions) | +| fail_fast | [FailFast](#FailFast) | + + + +## JptPresentationValidatorUtils +Utility functions for verifying JPT presentations. + +**Kind**: global class + +* [JptPresentationValidatorUtils](#JptPresentationValidatorUtils) + * [.extractIssuerFromPresentedJpt(presentation)](#JptPresentationValidatorUtils.extractIssuerFromPresentedJpt) ⇒ [CoreDID](#CoreDID) + * [.checkTimeframesWithValidityTimeframe2024(credential, validity_timeframe, status_check)](#JptPresentationValidatorUtils.checkTimeframesWithValidityTimeframe2024) + + + +### JptPresentationValidatorUtils.extractIssuerFromPresentedJpt(presentation) ⇒ [CoreDID](#CoreDID) +Utility for extracting the issuer field of a credential in JPT representation as DID. +# Errors +If the JPT decoding fails or the issuer field is not a valid DID. + +**Kind**: static method of [JptPresentationValidatorUtils](#JptPresentationValidatorUtils) + +| Param | Type | +| --- | --- | +| presentation | [Jpt](#Jpt) | + + + +### JptPresentationValidatorUtils.checkTimeframesWithValidityTimeframe2024(credential, validity_timeframe, status_check) +Check timeframe interval in credentialStatus with `RevocationTimeframeStatus`. + +**Kind**: static method of [JptPresentationValidatorUtils](#JptPresentationValidatorUtils) + +| Param | Type | +| --- | --- | +| credential | [Credential](#Credential) | +| validity_timeframe | [Timestamp](#Timestamp) \| undefined | +| status_check | [StatusCheck](#StatusCheck) | + ## Jwk @@ -2963,6 +3390,51 @@ Deserializes an instance from a JSON object. | --- | --- | | json | any | + + +## JwpVerificationOptions +**Kind**: global class + +* [JwpVerificationOptions](#JwpVerificationOptions) + * _instance_ + * [.clone()](#JwpVerificationOptions+clone) ⇒ [JwpVerificationOptions](#JwpVerificationOptions) + * [.toJSON()](#JwpVerificationOptions+toJSON) ⇒ any + * _static_ + * [.fromJSON(json)](#JwpVerificationOptions.fromJSON) ⇒ [JwpVerificationOptions](#JwpVerificationOptions) + * [.new([opts])](#JwpVerificationOptions.new) ⇒ [JwpVerificationOptions](#JwpVerificationOptions) + + + +### jwpVerificationOptions.clone() ⇒ [JwpVerificationOptions](#JwpVerificationOptions) +Deep clones the object. + +**Kind**: instance method of [JwpVerificationOptions](#JwpVerificationOptions) + + +### jwpVerificationOptions.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [JwpVerificationOptions](#JwpVerificationOptions) + + +### JwpVerificationOptions.fromJSON(json) ⇒ [JwpVerificationOptions](#JwpVerificationOptions) +Deserializes an instance from a JSON object. + +**Kind**: static method of [JwpVerificationOptions](#JwpVerificationOptions) + +| Param | Type | +| --- | --- | +| json | any | + + + +### JwpVerificationOptions.new([opts]) ⇒ [JwpVerificationOptions](#JwpVerificationOptions) +**Kind**: static method of [JwpVerificationOptions](#JwpVerificationOptions) + +| Param | Type | +| --- | --- | +| [opts] | IJwpVerificationOptions \| undefined | + ## Jws @@ -4343,6 +4815,7 @@ Supported verification method data formats. * [MethodData](#MethodData) * _instance_ + * [.tryCustom()](#MethodData+tryCustom) ⇒ [CustomMethodData](#CustomMethodData) * [.tryDecode()](#MethodData+tryDecode) ⇒ Uint8Array * [.tryPublicKeyJwk()](#MethodData+tryPublicKeyJwk) ⇒ [Jwk](#Jwk) * [.toJSON()](#MethodData+toJSON) ⇒ any @@ -4351,8 +4824,15 @@ Supported verification method data formats. * [.newBase58(data)](#MethodData.newBase58) ⇒ [MethodData](#MethodData) * [.newMultibase(data)](#MethodData.newMultibase) ⇒ [MethodData](#MethodData) * [.newJwk(key)](#MethodData.newJwk) ⇒ [MethodData](#MethodData) + * [.newCustom(name, data)](#MethodData.newCustom) ⇒ [MethodData](#MethodData) * [.fromJSON(json)](#MethodData.fromJSON) ⇒ [MethodData](#MethodData) + + +### methodData.tryCustom() ⇒ [CustomMethodData](#CustomMethodData) +Returns the wrapped custom method data format is `Custom`. + +**Kind**: instance method of [MethodData](#MethodData) ### methodData.tryDecode() ⇒ Uint8Array @@ -4419,6 +4899,18 @@ An error is thrown if the given `key` contains any private components. | --- | --- | | key | [Jwk](#Jwk) | + + +### MethodData.newCustom(name, data) ⇒ [MethodData](#MethodData) +Creates a new custom [MethodData](#MethodData). + +**Kind**: static method of [MethodData](#MethodData) + +| Param | Type | +| --- | --- | +| name | string | +| data | any | + ### MethodData.fromJSON(json) ⇒ [MethodData](#MethodData) @@ -4570,6 +5062,7 @@ Supported verification method types. * [.Ed25519VerificationKey2018()](#MethodType.Ed25519VerificationKey2018) ⇒ [MethodType](#MethodType) * [.X25519KeyAgreementKey2019()](#MethodType.X25519KeyAgreementKey2019) ⇒ [MethodType](#MethodType) * [.JsonWebKey()](#MethodType.JsonWebKey) ⇒ [MethodType](#MethodType) + * [.custom(type_)](#MethodType.custom) ⇒ [MethodType](#MethodType) * [.fromJSON(json)](#MethodType.fromJSON) ⇒ [MethodType](#MethodType) @@ -4605,6 +5098,17 @@ A verification method for use with JWT verification as prescribed by the [Jwk](# in the `publicKeyJwk` entry. **Kind**: static method of [MethodType](#MethodType) + + +### MethodType.custom(type_) ⇒ [MethodType](#MethodType) +A custom method. + +**Kind**: static method of [MethodType](#MethodType) + +| Param | Type | +| --- | --- | +| type_ | string | + ### MethodType.fromJSON(json) ⇒ [MethodType](#MethodType) @@ -4990,6 +5494,85 @@ if it is a valid Revocation Bitmap Service. | --- | --- | | service | [Service](#Service) | + + +## RevocationTimeframeStatus +Information used to determine the current status of a [Credential](#Credential). + +**Kind**: global class + +* [RevocationTimeframeStatus](#RevocationTimeframeStatus) + * [new RevocationTimeframeStatus(id, index, duration, [start_validity])](#new_RevocationTimeframeStatus_new) + * _instance_ + * [.clone()](#RevocationTimeframeStatus+clone) ⇒ [RevocationTimeframeStatus](#RevocationTimeframeStatus) + * [.toJSON()](#RevocationTimeframeStatus+toJSON) ⇒ any + * [.startValidityTimeframe()](#RevocationTimeframeStatus+startValidityTimeframe) ⇒ [Timestamp](#Timestamp) + * [.endValidityTimeframe()](#RevocationTimeframeStatus+endValidityTimeframe) ⇒ [Timestamp](#Timestamp) + * [.id()](#RevocationTimeframeStatus+id) ⇒ string + * [.index()](#RevocationTimeframeStatus+index) ⇒ number + * _static_ + * [.fromJSON(json)](#RevocationTimeframeStatus.fromJSON) ⇒ [RevocationTimeframeStatus](#RevocationTimeframeStatus) + + + +### new RevocationTimeframeStatus(id, index, duration, [start_validity]) +Creates a new `RevocationTimeframeStatus`. + + +| Param | Type | +| --- | --- | +| id | string | +| index | number | +| duration | [Duration](#Duration) | +| [start_validity] | [Timestamp](#Timestamp) \| undefined | + + + +### revocationTimeframeStatus.clone() ⇒ [RevocationTimeframeStatus](#RevocationTimeframeStatus) +Deep clones the object. + +**Kind**: instance method of [RevocationTimeframeStatus](#RevocationTimeframeStatus) + + +### revocationTimeframeStatus.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [RevocationTimeframeStatus](#RevocationTimeframeStatus) + + +### revocationTimeframeStatus.startValidityTimeframe() ⇒ [Timestamp](#Timestamp) +Get startValidityTimeframe value. + +**Kind**: instance method of [RevocationTimeframeStatus](#RevocationTimeframeStatus) + + +### revocationTimeframeStatus.endValidityTimeframe() ⇒ [Timestamp](#Timestamp) +Get endValidityTimeframe value. + +**Kind**: instance method of [RevocationTimeframeStatus](#RevocationTimeframeStatus) + + +### revocationTimeframeStatus.id() ⇒ string +Return the URL fo the `RevocationBitmapStatus`. + +**Kind**: instance method of [RevocationTimeframeStatus](#RevocationTimeframeStatus) + + +### revocationTimeframeStatus.index() ⇒ number +Return the index of the credential in the issuer's revocation bitmap if it can be decoded. + +**Kind**: instance method of [RevocationTimeframeStatus](#RevocationTimeframeStatus) + + +### RevocationTimeframeStatus.fromJSON(json) ⇒ [RevocationTimeframeStatus](#RevocationTimeframeStatus) +Deserializes an instance from a JSON object. + +**Kind**: static method of [RevocationTimeframeStatus](#RevocationTimeframeStatus) + +| Param | Type | +| --- | --- | +| json | any | + ## SdJwt @@ -5006,11 +5589,9 @@ Representation of an SD-JWT of the format * [.jwt()](#SdJwt+jwt) ⇒ string * [.disclosures()](#SdJwt+disclosures) ⇒ Array.<string> * [.keyBindingJwt()](#SdJwt+keyBindingJwt) ⇒ string \| undefined - * [.toJSON()](#SdJwt+toJSON) ⇒ any * [.clone()](#SdJwt+clone) ⇒ [SdJwt](#SdJwt) * _static_ * [.parse(sd_jwt)](#SdJwt.parse) ⇒ [SdJwt](#SdJwt) - * [.fromJSON(json)](#SdJwt.fromJSON) ⇒ [SdJwt](#SdJwt) @@ -5053,12 +5634,6 @@ The disclosures part. ### sdJwt.keyBindingJwt() ⇒ string \| undefined The optional key binding JWT. -**Kind**: instance method of [SdJwt](#SdJwt) - - -### sdJwt.toJSON() ⇒ any -Serializes this to a JSON object. - **Kind**: instance method of [SdJwt](#SdJwt) @@ -5080,17 +5655,6 @@ Returns `DeserializationError` if parsing fails. | --- | --- | | sd_jwt | string | - - -### SdJwt.fromJSON(json) ⇒ [SdJwt](#SdJwt) -Deserializes an instance from a JSON object. - -**Kind**: static method of [SdJwt](#SdJwt) - -| Param | Type | -| --- | --- | -| json | any | - ## SdJwtCredentialValidator @@ -5967,6 +6531,7 @@ A DID Document Verification Method. **Kind**: global class * [VerificationMethod](#VerificationMethod) + * [new VerificationMethod(id, controller, type_, data)](#new_VerificationMethod_new) * _instance_ * [.id()](#VerificationMethod+id) ⇒ [DIDUrl](#DIDUrl) * [.setId(id)](#VerificationMethod+setId) @@ -5984,6 +6549,19 @@ A DID Document Verification Method. * [.newFromJwk(did, key, [fragment])](#VerificationMethod.newFromJwk) ⇒ [VerificationMethod](#VerificationMethod) * [.fromJSON(json)](#VerificationMethod.fromJSON) ⇒ [VerificationMethod](#VerificationMethod) + + +### new VerificationMethod(id, controller, type_, data) +Create a custom [VerificationMethod](#VerificationMethod). + + +| Param | Type | +| --- | --- | +| id | [DIDUrl](#DIDUrl) | +| controller | [CoreDID](#CoreDID) | +| type_ | [MethodType](#MethodType) | +| data | [MethodData](#MethodData) | + ### verificationMethod.id() ⇒ [DIDUrl](#DIDUrl) @@ -6119,60 +6697,41 @@ Deserializes an instance from a JSON object. | --- | --- | | json | any | - + -## CredentialStatus -**Kind**: global variable - +## FailFast +Declares when validation should return if an error occurs. -## SubjectHolderRelationship -Declares how credential subjects must relate to the presentation holder. +**Kind**: global variable + -See also the [Subject-Holder Relationship](https://www.w3.org/TR/vc-data-model/#subject-holder-relationships) section of the specification. +## AllErrors +Return all errors that occur during validation. **Kind**: global variable - + -## AlwaysSubject -The holder must always match the subject on all credentials, regardless of their [`nonTransferable`](https://www.w3.org/TR/vc-data-model/#nontransferable-property) property. -This variant is the default. +## FirstError +Return after the first error occurs. **Kind**: global variable - - -## SubjectOnNonTransferable -The holder must match the subject only for credentials where the [`nonTransferable`](https://www.w3.org/TR/vc-data-model/#nontransferable-property) property is `true`. + +## MethodRelationship **Kind**: global variable - + -## Any -The holder is not required to have any kind of relationship to any credential subject. +## CredentialStatus +**Kind**: global variable + +## StateMetadataEncoding **Kind**: global variable ## StatusPurpose Purpose of a [StatusList2021](#StatusList2021). -**Kind**: global variable - - -## FailFast -Declares when validation should return if an error occurs. - -**Kind**: global variable - - -## AllErrors -Return all errors that occur during validation. - -**Kind**: global variable - - -## FirstError -Return after the first error occurs. - **Kind**: global variable @@ -6205,20 +6764,55 @@ Validate the status if supported, skip any unsupported Skip all status checks. **Kind**: global variable - + + +## SubjectHolderRelationship +Declares how credential subjects must relate to the presentation holder. + +See also the [Subject-Holder Relationship](https://www.w3.org/TR/vc-data-model/#subject-holder-relationships) section of the specification. -## StateMetadataEncoding **Kind**: global variable - + + +## AlwaysSubject +The holder must always match the subject on all credentials, regardless of their [`nonTransferable`](https://www.w3.org/TR/vc-data-model/#nontransferable-property) property. +This variant is the default. -## MethodRelationship **Kind**: global variable - + -## start() -Initializes the console error panic hook for better error messages +## SubjectOnNonTransferable +The holder must match the subject only for credentials where the [`nonTransferable`](https://www.w3.org/TR/vc-data-model/#nontransferable-property) property is `true`. + +**Kind**: global variable + + +## Any +The holder is not required to have any kind of relationship to any credential subject. + +**Kind**: global variable + + +## encodeB64(data) ⇒ string +Encode the given bytes in url-safe base64. **Kind**: global function + +| Param | Type | +| --- | --- | +| data | Uint8Array | + + + +## decodeB64(data) ⇒ Uint8Array +Decode the given url-safe base64-encoded slice into its raw bytes. + +**Kind**: global function + +| Param | Type | +| --- | --- | +| data | Uint8Array | + ## verifyEd25519(alg, signingInput, decodedSignature, publicKey) @@ -6241,25 +6835,9 @@ prior to calling the function. | decodedSignature | Uint8Array | | publicKey | [Jwk](#Jwk) | - - -## encodeB64(data) ⇒ string -Encode the given bytes in url-safe base64. - -**Kind**: global function - -| Param | Type | -| --- | --- | -| data | Uint8Array | - - + -## decodeB64(data) ⇒ Uint8Array -Decode the given url-safe base64-encoded slice into its raw bytes. +## start() +Initializes the console error panic hook for better error messages **Kind**: global function - -| Param | Type | -| --- | --- | -| data | Uint8Array | - diff --git a/bindings/wasm/src/common/types.rs b/bindings/wasm/src/common/types.rs index 295e0ea447..8264e923ce 100644 --- a/bindings/wasm/src/common/types.rs +++ b/bindings/wasm/src/common/types.rs @@ -75,3 +75,9 @@ impl TryFrom<&Object> for MapStringAny { Ok(map.unchecked_into::()) } } + +impl Default for MapStringAny { + fn default() -> Self { + js_sys::Map::new().unchecked_into() + } +} diff --git a/bindings/wasm/src/credential/jpt.rs b/bindings/wasm/src/credential/jpt.rs new file mode 100644 index 0000000000..090110b018 --- /dev/null +++ b/bindings/wasm/src/credential/jpt.rs @@ -0,0 +1,36 @@ +use identity_iota::credential::Jpt; +use wasm_bindgen::prelude::*; + +/// A JSON Proof Token (JPT). +#[wasm_bindgen(js_name = Jpt)] +pub struct WasmJpt(pub(crate) Jpt); + +#[wasm_bindgen(js_class = Jpt)] +impl WasmJpt { + /// Creates a new {@link Jpt}. + #[wasm_bindgen(constructor)] + pub fn new(jpt_string: String) -> Self { + WasmJpt(Jpt::new(jpt_string)) + } + + // Returns the string representation for this {@link Jpt}. + #[allow(clippy::inherent_to_string)] + #[wasm_bindgen(js_name = "toString")] + pub fn to_string(&self) -> String { + self.0.as_str().to_owned() + } +} + +impl_wasm_clone!(WasmJpt, Jpt); + +impl From for WasmJpt { + fn from(value: Jpt) -> Self { + WasmJpt(value) + } +} + +impl From for Jpt { + fn from(value: WasmJpt) -> Self { + value.0 + } +} diff --git a/bindings/wasm/src/credential/jpt_credential_validator/decoded_jpt_credential.rs b/bindings/wasm/src/credential/jpt_credential_validator/decoded_jpt_credential.rs new file mode 100644 index 0000000000..9cd56b2d23 --- /dev/null +++ b/bindings/wasm/src/credential/jpt_credential_validator/decoded_jpt_credential.rs @@ -0,0 +1,42 @@ +use identity_iota::core::Object; +use identity_iota::credential::DecodedJptCredential; +use wasm_bindgen::prelude::*; + +use crate::common::MapStringAny; +use crate::credential::WasmCredential; +use crate::error::Result; + +#[wasm_bindgen(js_name = DecodedJptCredential)] +pub struct WasmDecodedJptCredential(pub(crate) DecodedJptCredential); + +impl_wasm_clone!(WasmDecodedJptCredential, DecodedJptCredential); + +#[wasm_bindgen(js_class = DecodedJptCredential)] +impl WasmDecodedJptCredential { + /// Returns the {@link Credential} embedded into this JPT. + #[wasm_bindgen] + pub fn credential(&self) -> WasmCredential { + WasmCredential(self.0.credential.clone()) + } + + /// Returns the custom claims parsed from the JPT. + #[wasm_bindgen(js_name = "customClaims")] + pub fn custom_claims(&self) -> Result { + match self.0.custom_claims.clone() { + Some(obj) => MapStringAny::try_from(obj), + None => Ok(MapStringAny::default()), + } + } +} + +impl From for WasmDecodedJptCredential { + fn from(value: DecodedJptCredential) -> Self { + WasmDecodedJptCredential(value) + } +} + +impl From for DecodedJptCredential { + fn from(value: WasmDecodedJptCredential) -> Self { + value.0 + } +} diff --git a/bindings/wasm/src/credential/jpt_credential_validator/jpt_credential_validation_options.rs b/bindings/wasm/src/credential/jpt_credential_validator/jpt_credential_validation_options.rs new file mode 100644 index 0000000000..55ff8bd0b2 --- /dev/null +++ b/bindings/wasm/src/credential/jpt_credential_validator/jpt_credential_validation_options.rs @@ -0,0 +1,77 @@ +use identity_iota::credential::JptCredentialValidationOptions; +use wasm_bindgen::prelude::*; + +use crate::error::Result; +use crate::error::WasmResult; + +/// Options to declare validation criteria for {@link Jpt}. +#[derive(Debug, Default, Clone)] +#[wasm_bindgen(js_name = "JptCredentialValidationOptions", inspectable)] +pub struct WasmJptCredentialValidationOptions(pub(crate) JptCredentialValidationOptions); + +impl_wasm_clone!(WasmJptCredentialValidationOptions, JptCredentialValidationOptions); +impl_wasm_json!(WasmJptCredentialValidationOptions, JptCredentialValidationOptions); + +#[wasm_bindgen(js_class = JptCredentialValidationOptions)] +impl WasmJptCredentialValidationOptions { + /// Creates a new default istance. + #[wasm_bindgen(constructor)] + pub fn new(opts: Option) -> Result { + if let Some(opts) = opts { + opts.into_serde().wasm_result().map(WasmJptCredentialValidationOptions) + } else { + Ok(WasmJptCredentialValidationOptions::default()) + } + } +} + +impl From for WasmJptCredentialValidationOptions { + fn from(value: JptCredentialValidationOptions) -> Self { + WasmJptCredentialValidationOptions(value) + } +} + +impl From for JptCredentialValidationOptions { + fn from(value: WasmJptCredentialValidationOptions) -> Self { + value.0 + } +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "IJptCredentialValidationOptions")] + pub type IJptCredentialValidationOptions; +} + +#[wasm_bindgen(typescript_custom_section)] +const I_JPT_CREDENTIAL_VALIDATION_OPTIONS: &'static str = r#" +/** Holds options to create a new {@link JptCredentialValidationOptions}. */ +interface IJptCredentialValidationOptions { + /** + * Declare that the credential is **not** considered valid if it expires before this {@link Timestamp}. + * Uses the current datetime during validation if not set. + */ + readonly earliestExpiryDate?: Timestamp; + + /** + * Declare that the credential is **not** considered valid if it was issued later than this {@link Timestamp}. + * Uses the current datetime during validation if not set. + */ + readonly latestIssuanceDate?: Timestamp; + + /** + * Validation behaviour for [`credentialStatus`](https://www.w3.org/TR/vc-data-model/#status). + */ + readonly status?: StatusCheck; + + /** Declares how credential subjects must relate to the presentation holder during validation. + * + * + */ + readonly subjectHolderRelationship?: [string, SubjectHolderRelationship]; + + /** + * Options which affect the verification of the proof on the credential. + */ + readonly verificationOptions?: JwpVerificationOptions; +}"#; diff --git a/bindings/wasm/src/credential/jpt_credential_validator/jpt_credential_validator.rs b/bindings/wasm/src/credential/jpt_credential_validator/jpt_credential_validator.rs new file mode 100644 index 0000000000..5ee75daf8d --- /dev/null +++ b/bindings/wasm/src/credential/jpt_credential_validator/jpt_credential_validator.rs @@ -0,0 +1,30 @@ +use crate::common::ImportedDocumentLock; +use crate::credential::WasmDecodedJptCredential; +use crate::credential::WasmFailFast; +use crate::credential::WasmJpt; +use crate::credential::WasmJptCredentialValidationOptions; +use crate::did::WasmCoreDocument; +use crate::error::Result; +use crate::error::WasmResult; +use identity_iota::credential::JptCredentialValidator; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(js_name = JptCredentialValidator)] +pub struct WasmJptCredentialValidator; + +#[wasm_bindgen(js_class = JptCredentialValidator)] +impl WasmJptCredentialValidator { + #[wasm_bindgen] + pub fn validate( + credential_jpt: &WasmJpt, + issuer: WasmCoreDocument, + options: &WasmJptCredentialValidationOptions, + fail_fast: WasmFailFast, + ) -> Result { + let issuer_doc = ImportedDocumentLock::Core(issuer.0); + let doc = issuer_doc.try_read()?; + JptCredentialValidator::validate(&credential_jpt.0, &doc, &options.0, fail_fast.into()) + .wasm_result() + .map(WasmDecodedJptCredential) + } +} diff --git a/bindings/wasm/src/credential/jpt_credential_validator/jpt_credential_validator_utils.rs b/bindings/wasm/src/credential/jpt_credential_validator/jpt_credential_validator_utils.rs new file mode 100644 index 0000000000..18e27cf4fd --- /dev/null +++ b/bindings/wasm/src/credential/jpt_credential_validator/jpt_credential_validator_utils.rs @@ -0,0 +1,95 @@ +use crate::common::ImportedDocumentLock; +use crate::common::WasmTimestamp; +use crate::credential::options::WasmStatusCheck; +use crate::credential::WasmCredential; +use crate::credential::WasmJpt; +use crate::did::WasmCoreDID; +use crate::did::WasmCoreDocument; +use crate::error::Result; +use crate::error::WasmResult; +use identity_iota::core::Object; +use identity_iota::credential::JptCredentialValidatorUtils; +use identity_iota::did::CoreDID; +use wasm_bindgen::prelude::*; + +/// Utility functions for validating JPT credentials. +#[wasm_bindgen(js_name = JptCredentialValidatorUtils)] +#[derive(Default)] +pub struct WasmJptCredentialValidatorUtils; + +#[wasm_bindgen(js_class = JptCredentialValidatorUtils)] +impl WasmJptCredentialValidatorUtils { + #[wasm_bindgen(constructor)] + pub fn new() -> WasmJptCredentialValidatorUtils { + WasmJptCredentialValidatorUtils + } + + /// Utility for extracting the issuer field of a {@link `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 { + JptCredentialValidatorUtils::extract_issuer::(&credential.0) + .wasm_result() + .map(WasmCoreDID::from) + } + /// Utility for extracting the issuer field of a credential in JPT representation as DID. + /// # Errors + /// If the JPT decoding fails or the issuer field is not a valid DID. + #[wasm_bindgen(js_name = "extractIssuerFromIssuedJpt")] + pub fn extract_issuer_from_issued_jpt(credential: &WasmJpt) -> Result { + JptCredentialValidatorUtils::extract_issuer_from_issued_jpt::(&credential.0) + .wasm_result() + .map(WasmCoreDID::from) + } + + #[wasm_bindgen(js_name = "checkTimeframesWithValidityTimeframe2024")] + pub fn check_timeframes_with_validity_timeframe_2024( + credential: &WasmCredential, + validity_timeframe: Option, + status_check: WasmStatusCheck, + ) -> Result<()> { + JptCredentialValidatorUtils::check_timeframes_with_validity_timeframe_2024( + &credential.0, + validity_timeframe.map(|t| t.0), + status_check.into(), + ) + .wasm_result() + } + + /// Checks whether the credential status has been revoked. + /// + /// Only supports `RevocationTimeframe2024`. + #[wasm_bindgen(js_name = "checkRevocationWithValidityTimeframe2024")] + pub fn check_revocation_with_validity_timeframe_2024( + credential: &WasmCredential, + issuer: WasmCoreDocument, + status_check: WasmStatusCheck, + ) -> Result<()> { + let issuer_doc = ImportedDocumentLock::Core(issuer.0); + let doc = issuer_doc.try_read()?; + JptCredentialValidatorUtils::check_revocation_with_validity_timeframe_2024(&credential.0, &doc, status_check.into()) + .wasm_result() + } + + /// Checks whether the credential status has been revoked or the timeframe interval is INVALID + /// + /// Only supports `RevocationTimeframe2024`. + #[wasm_bindgen(js_name = "checkTimeframesAndRevocationWithValidityTimeframe2024")] + pub fn check_timeframes_and_revocation_with_validity_timeframe_2024( + credential: &WasmCredential, + issuer: WasmCoreDocument, + validity_timeframe: Option, + status_check: WasmStatusCheck, + ) -> Result<()> { + let issuer_doc = ImportedDocumentLock::Core(issuer.0); + let doc = issuer_doc.try_read()?; + JptCredentialValidatorUtils::check_timeframes_and_revocation_with_validity_timeframe_2024( + &credential.0, + &doc, + validity_timeframe.map(|t| t.0), + status_check.into(), + ) + .wasm_result() + } +} diff --git a/bindings/wasm/src/credential/jpt_credential_validator/jwp_verification_options.rs b/bindings/wasm/src/credential/jpt_credential_validator/jwp_verification_options.rs new file mode 100644 index 0000000000..2f107276fc --- /dev/null +++ b/bindings/wasm/src/credential/jpt_credential_validator/jwp_verification_options.rs @@ -0,0 +1,45 @@ +use crate::error::Result; +use crate::error::WasmResult; +use identity_iota::document::verifiable::JwpVerificationOptions; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(js_name = JwpVerificationOptions, inspectable)] +#[derive(Clone, Debug, Default)] +pub struct WasmJwpVerificationOptions(pub(crate) JwpVerificationOptions); + +impl_wasm_clone!(WasmJwpVerificationOptions, JwpVerificationOptions); +impl_wasm_json!(WasmJwpVerificationOptions, JwpVerificationOptions); + +#[wasm_bindgen(js_class = JwpVerificationOptions)] +impl WasmJwpVerificationOptions { + pub fn new(opts: Option) -> Result { + if let Some(opts) = opts { + opts.into_serde().wasm_result().map(WasmJwpVerificationOptions) + } else { + Ok(WasmJwpVerificationOptions::default()) + } + } +} + +// Interface to allow creating {@link JwpVerificationOptions} easily. +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "IJwpVerificationOptions")] + pub type IJwpVerificationOptions; +} + +#[wasm_bindgen(typescript_custom_section)] +const I_JWP_VERIFICATION_OPTIONS: &'static str = r#" +/** Holds options to create a new {@link JwpVerificationOptions}. */ +interface IJwpVerificationOptions { + /** + * Verify the signing verification method relation matches this. + */ + readonly methodScope?: MethodScope; + + /** + * The DID URL of the method, whose JWK should be used to verify the JWP. + * If unset, the `kid` of the JWP is used as the DID URL. + */ + readonly methodId?: DIDUrl; +}"#; diff --git a/bindings/wasm/src/credential/jpt_credential_validator/mod.rs b/bindings/wasm/src/credential/jpt_credential_validator/mod.rs new file mode 100644 index 0000000000..963d7493ca --- /dev/null +++ b/bindings/wasm/src/credential/jpt_credential_validator/mod.rs @@ -0,0 +1,11 @@ +mod decoded_jpt_credential; +mod jpt_credential_validation_options; +mod jpt_credential_validator; +mod jpt_credential_validator_utils; +mod jwp_verification_options; + +pub use decoded_jpt_credential::*; +pub use jpt_credential_validation_options::*; +pub use jpt_credential_validator::*; +pub use jpt_credential_validator_utils::*; +pub use jwp_verification_options::*; diff --git a/bindings/wasm/src/credential/jpt_presentiation_validation/decoded_jpt_presentation.rs b/bindings/wasm/src/credential/jpt_presentiation_validation/decoded_jpt_presentation.rs new file mode 100644 index 0000000000..7f6c7f3377 --- /dev/null +++ b/bindings/wasm/src/credential/jpt_presentiation_validation/decoded_jpt_presentation.rs @@ -0,0 +1,48 @@ +use identity_iota::core::Object; +use identity_iota::credential::DecodedJptPresentation; +use wasm_bindgen::prelude::*; + +use crate::common::MapStringAny; +use crate::credential::WasmCredential; +use crate::error::Result; + +#[wasm_bindgen(js_name = DecodedJptPresentation)] +pub struct WasmDecodedJptPresentation(pub(crate) DecodedJptPresentation); + +impl_wasm_clone!(WasmDecodedJptPresentation, DecodedJptPresentation); + +#[wasm_bindgen(js_class = DecodedJptPresentation)] +impl WasmDecodedJptPresentation { + /// Returns the {@link Credential} embedded into this JPT. + #[wasm_bindgen] + pub fn credential(&self) -> WasmCredential { + WasmCredential(self.0.credential.clone()) + } + + /// Returns the custom claims parsed from the JPT. + #[wasm_bindgen(js_name = "customClaims")] + pub fn custom_claims(&self) -> Result { + match self.0.custom_claims.clone() { + Some(obj) => MapStringAny::try_from(obj), + None => Ok(MapStringAny::default()), + } + } + + /// Returns the `aud` property parsed from the JWT claims. + #[wasm_bindgen] + pub fn aud(&self) -> Option { + self.0.aud.as_ref().map(ToString::to_string) + } +} + +impl From for WasmDecodedJptPresentation { + fn from(value: DecodedJptPresentation) -> Self { + WasmDecodedJptPresentation(value) + } +} + +impl From for DecodedJptPresentation { + fn from(value: WasmDecodedJptPresentation) -> Self { + value.0 + } +} \ No newline at end of file diff --git a/bindings/wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validation_options.rs b/bindings/wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validation_options.rs new file mode 100644 index 0000000000..4be235cb3d --- /dev/null +++ b/bindings/wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validation_options.rs @@ -0,0 +1,59 @@ +use identity_iota::credential::JptPresentationValidationOptions; +use wasm_bindgen::prelude::*; + +use crate::error::Result; +use crate::error::WasmResult; + +/// Options to declare validation criteria for a {@link Jpt} presentation. +#[derive(Debug, Default, Clone)] +#[wasm_bindgen(js_name = "JptPresentationValidationOptions", inspectable)] +pub struct WasmJptPresentationValidationOptions(pub(crate) JptPresentationValidationOptions); + +impl_wasm_clone!(WasmJptPresentationValidationOptions, JptPresentationValidationOptions); +impl_wasm_json!(WasmJptPresentationValidationOptions, JptPresentationValidationOptions); + +#[wasm_bindgen(js_class = JptPresentationValidationOptions)] +impl WasmJptPresentationValidationOptions { + #[wasm_bindgen(constructor)] + pub fn new(opts: Option) -> Result { + if let Some(opts) = opts { + opts.into_serde().wasm_result().map(WasmJptPresentationValidationOptions) + } else { + Ok(WasmJptPresentationValidationOptions::default()) + } + } +} + +impl From for WasmJptPresentationValidationOptions { + fn from(value: JptPresentationValidationOptions) -> Self { + WasmJptPresentationValidationOptions(value) + } +} + +impl From for JptPresentationValidationOptions { + fn from(value: WasmJptPresentationValidationOptions) -> Self { + value.0 + } +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "IJptPresentationValidationOptions")] + pub type IJptPresentationValidationOptions; +} + +#[wasm_bindgen(typescript_custom_section)] +const I_JPT_PRESENTATION_VALIDATION_OPTIONS: &'static str = r#" +/** Holds options to create a new {@link JptPresentationValidationOptions}. */ +interface IJptPresentationValidationOptions { + /** + * The nonce to be placed in the Presentation Protected Header. + */ + readonly nonce?: string; + + /** + * Options which affect the verification of the proof on the credential. + */ + readonly verificationOptions?: JwpVerificationOptions; +}"#; + diff --git a/bindings/wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validator.rs b/bindings/wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validator.rs new file mode 100644 index 0000000000..e059d99f68 --- /dev/null +++ b/bindings/wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validator.rs @@ -0,0 +1,38 @@ +use crate::common::ImportedDocumentLock; +use crate::credential::WasmDecodedJptPresentation; +use crate::credential::WasmFailFast; +use crate::credential::WasmJpt; +use crate::credential::WasmJptPresentationValidationOptions; +use crate::did::WasmCoreDocument; +use crate::error::Result; +use crate::error::WasmResult; +use identity_iota::credential::JptPresentationValidator; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(js_name = JptPresentationValidator)] +pub struct WasmJptPresentationValidator; + +#[wasm_bindgen(js_class = JptPresentationValidator)] +impl WasmJptPresentationValidator { + /// Decodes and validates a Presented {@link Credential} issued as a JPT (JWP Presented Form). A + /// {@link DecodedJptPresentation} is returned upon success. + /// + /// The following properties are validated according to `options`: + /// - the holder's proof on the JWP, + /// - the expiration date, + /// - the issuance date, + /// - the semantic structure. + #[wasm_bindgen] + pub fn validate( + presentation_jpt: &WasmJpt, + issuer: WasmCoreDocument, + options: &WasmJptPresentationValidationOptions, + fail_fast: WasmFailFast, + ) -> Result { + let issuer_doc = ImportedDocumentLock::Core(issuer.0); + let doc = issuer_doc.try_read()?; + JptPresentationValidator::validate(&presentation_jpt.0, &doc, &options.0, fail_fast.into()) + .wasm_result() + .map(WasmDecodedJptPresentation) + } +} diff --git a/bindings/wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validator_utils.rs b/bindings/wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validator_utils.rs new file mode 100644 index 0000000000..c27e3429ed --- /dev/null +++ b/bindings/wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validator_utils.rs @@ -0,0 +1,38 @@ +use crate::common::WasmTimestamp; +use crate::credential::{options::WasmStatusCheck, WasmCredential, WasmJpt}; +use crate::did::WasmCoreDID; +use crate::error::{Result, WasmResult}; +use identity_iota::credential::JptPresentationValidatorUtils; +use wasm_bindgen::prelude::*; + +/// Utility functions for verifying JPT presentations. +#[wasm_bindgen(js_name = JptPresentationValidatorUtils)] +pub struct WasmJptPresentationValidatorUtils; + +#[wasm_bindgen(js_class = JptPresentationValidatorUtils)] +impl WasmJptPresentationValidatorUtils { + /// Utility for extracting the issuer field of a credential in JPT representation as DID. + /// # Errors + /// If the JPT decoding fails or the issuer field is not a valid DID. + #[wasm_bindgen(js_name = "extractIssuerFromPresentedJpt")] + pub fn extract_issuer_from_presented_jpt(presentation: &WasmJpt) -> Result { + JptPresentationValidatorUtils::extract_issuer_from_presented_jpt(&presentation.0) + .wasm_result() + .map(WasmCoreDID) + } + + /// Check timeframe interval in credentialStatus with `RevocationTimeframeStatus`. + #[wasm_bindgen(js_name = "checkTimeframesWithValidityTimeframe2024")] + pub fn check_timeframes_with_validity_timeframe_2024( + credential: &WasmCredential, + validity_timeframe: Option, + status_check: WasmStatusCheck, + ) -> Result<()> { + JptPresentationValidatorUtils::check_timeframes_with_validity_timeframe_2024( + &credential.0, + validity_timeframe.map(|t| t.0), + status_check.into(), + ) + .wasm_result() + } +} diff --git a/bindings/wasm/src/credential/jpt_presentiation_validation/mod.rs b/bindings/wasm/src/credential/jpt_presentiation_validation/mod.rs new file mode 100644 index 0000000000..274a455416 --- /dev/null +++ b/bindings/wasm/src/credential/jpt_presentiation_validation/mod.rs @@ -0,0 +1,9 @@ +mod decoded_jpt_presentation; +mod jpt_presentation_validation_options; +mod jpt_presentation_validator; +mod jpt_presentation_validator_utils; + +pub use decoded_jpt_presentation::*; +pub use jpt_presentation_validation_options::*; +pub use jpt_presentation_validator_utils::*; +pub use jpt_presentation_validator::*; \ No newline at end of file diff --git a/bindings/wasm/src/credential/mod.rs b/bindings/wasm/src/credential/mod.rs index 832eac1cd4..7ece9f9e8f 100644 --- a/bindings/wasm/src/credential/mod.rs +++ b/bindings/wasm/src/credential/mod.rs @@ -6,6 +6,8 @@ pub use self::credential::WasmCredential; pub use self::credential_builder::*; pub use self::domain_linkage_configuration::WasmDomainLinkageConfiguration; +pub use self::jpt::*; +pub use self::jpt_credential_validator::*; pub use self::jws::WasmJws; pub use self::jwt::WasmJwt; pub use self::jwt_credential_validation::*; @@ -16,12 +18,15 @@ pub use self::presentation::*; pub use self::proof::WasmProof; pub use self::revocation::*; pub use self::types::*; +pub use self::jpt_presentiation_validation::*; mod credential; mod credential_builder; mod domain_linkage_configuration; mod domain_linkage_credential_builder; mod domain_linkage_validator; +mod jpt; +mod jpt_credential_validator; mod jws; mod jwt; mod jwt_credential_validation; @@ -32,3 +37,4 @@ mod presentation; mod proof; mod revocation; mod types; +mod jpt_presentiation_validation; diff --git a/bindings/wasm/src/credential/revocation/mod.rs b/bindings/wasm/src/credential/revocation/mod.rs index 7ad04980b4..c0f075df39 100644 --- a/bindings/wasm/src/credential/revocation/mod.rs +++ b/bindings/wasm/src/credential/revocation/mod.rs @@ -2,3 +2,4 @@ // SPDX-License-Identifier: Apache-2.0 pub mod status_list_2021; +pub mod validity_timeframe_2024; diff --git a/bindings/wasm/src/credential/revocation/validity_timeframe_2024/mod.rs b/bindings/wasm/src/credential/revocation/validity_timeframe_2024/mod.rs new file mode 100644 index 0000000000..2da8555a22 --- /dev/null +++ b/bindings/wasm/src/credential/revocation/validity_timeframe_2024/mod.rs @@ -0,0 +1,3 @@ +mod status; + +pub use status::*; diff --git a/bindings/wasm/src/credential/revocation/validity_timeframe_2024/status.rs b/bindings/wasm/src/credential/revocation/validity_timeframe_2024/status.rs new file mode 100644 index 0000000000..469716f3ec --- /dev/null +++ b/bindings/wasm/src/credential/revocation/validity_timeframe_2024/status.rs @@ -0,0 +1,72 @@ +use identity_iota::credential::RevocationTimeframeStatus; +use identity_iota::did::DIDUrl; +use wasm_bindgen::prelude::*; + +use crate::common::WasmDuration; +use crate::common::WasmTimestamp; +use crate::error::Result; +use crate::error::WasmResult; + +/// Information used to determine the current status of a {@link Credential}. +#[wasm_bindgen(js_name = RevocationTimeframeStatus, inspectable)] +pub struct WasmRevocationTimeframeStatus(pub(crate) RevocationTimeframeStatus); + +impl_wasm_clone!(WasmRevocationTimeframeStatus, RevocationTimeframeStatus); +impl_wasm_json!(WasmRevocationTimeframeStatus, RevocationTimeframeStatus); + +#[wasm_bindgen(js_class = RevocationTimeframeStatus)] +impl WasmRevocationTimeframeStatus { + /// Creates a new `RevocationTimeframeStatus`. + #[wasm_bindgen(constructor)] + pub fn new( + id: String, + index: u32, + duration: WasmDuration, + start_validity: Option, + ) -> Result { + RevocationTimeframeStatus::new( + start_validity.map(|t| t.0), + duration.0, + DIDUrl::parse(id).wasm_result()?, + index, + ) + .wasm_result() + .map(WasmRevocationTimeframeStatus) + } + + /// Get startValidityTimeframe value. + #[wasm_bindgen(js_name = "startValidityTimeframe")] + pub fn start_validity_timeframe(&self) -> Result { + self.0.start_validity_timeframe().wasm_result().map(WasmTimestamp) + } + + /// Get endValidityTimeframe value. + #[wasm_bindgen(js_name = "endValidityTimeframe")] + pub fn end_validity_timeframe(&self) -> Result { + self.0.end_validity_timeframe().wasm_result().map(WasmTimestamp) + } + + /// Return the URL fo the `RevocationBitmapStatus`. + #[wasm_bindgen] + pub fn id(&self) -> Result { + self.0.id().wasm_result().map(|url| url.to_string()) + } + + /// Return the index of the credential in the issuer's revocation bitmap if it can be decoded. + #[wasm_bindgen] + pub fn index(&self) -> Result { + self.0.index().wasm_result() + } +} + +impl From for WasmRevocationTimeframeStatus { + fn from(value: RevocationTimeframeStatus) -> Self { + WasmRevocationTimeframeStatus(value) + } +} + +impl From for RevocationTimeframeStatus { + fn from(value: WasmRevocationTimeframeStatus) -> Self { + value.0 + } +} diff --git a/bindings/wasm/src/sd_jwt/wasm_sd_jwt.rs b/bindings/wasm/src/sd_jwt/wasm_sd_jwt.rs index 7b4f201206..c55de229e6 100644 --- a/bindings/wasm/src/sd_jwt/wasm_sd_jwt.rs +++ b/bindings/wasm/src/sd_jwt/wasm_sd_jwt.rs @@ -77,5 +77,4 @@ impl WasmSdJwt { } } -impl_wasm_json!(WasmSdJwt, SdJwt); impl_wasm_clone!(WasmSdJwt, SdJwt); diff --git a/bindings/wasm/src/verification/wasm_method_data.rs b/bindings/wasm/src/verification/wasm_method_data.rs index 5bba4aa5a9..58a9c65820 100644 --- a/bindings/wasm/src/verification/wasm_method_data.rs +++ b/bindings/wasm/src/verification/wasm_method_data.rs @@ -1,6 +1,7 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use identity_iota::verification::CustomMethodData; use identity_iota::verification::MethodData; use wasm_bindgen::prelude::*; @@ -45,6 +46,27 @@ impl WasmMethodData { Ok(Self(MethodData::PublicKeyJwk(key.0.clone()))) } + /// Creates a new custom {@link MethodData}. + #[wasm_bindgen(js_name = newCustom)] + pub fn new_custom(name: String, data: JsValue) -> Result { + let data = data.into_serde::().wasm_result()?; + Ok(Self(MethodData::Custom(CustomMethodData { name, data }))) + } + + /// Returns the wrapped custom method data format is `Custom`. + #[wasm_bindgen(js_name = tryCustom)] + pub fn try_custom(&self) -> Result { + self + .0 + .custom() + .map(|custom| custom.clone().into()) + .ok_or(WasmError::new( + Cow::Borrowed("MethodDataFormatError"), + Cow::Borrowed("method data format is not Custom"), + )) + .wasm_result() + } + /// Returns a `Uint8Array` containing the decoded bytes of the {@link MethodData}. /// /// This is generally a public key identified by a {@link MethodData} value. @@ -78,3 +100,31 @@ impl From for WasmMethodData { WasmMethodData(data) } } + +/// A custom verification method data format. +#[wasm_bindgen(js_name = CustomMethodData, inspectable)] +pub struct WasmCustomMethodData(pub(crate) CustomMethodData); + +#[wasm_bindgen(js_class = CustomMethodData)] +impl WasmCustomMethodData { + #[wasm_bindgen(constructor)] + pub fn new(name: String, data: JsValue) -> Result { + let data = data.into_serde::().wasm_result()?; + Ok(Self(CustomMethodData { name, data })) + } +} + +impl From for WasmCustomMethodData { + fn from(value: CustomMethodData) -> Self { + Self(value) + } +} + +impl From for CustomMethodData { + fn from(value: WasmCustomMethodData) -> Self { + value.0 + } +} + +impl_wasm_clone!(WasmCustomMethodData, CustomMethodData); +impl_wasm_json!(WasmCustomMethodData, CustomMethodData); diff --git a/bindings/wasm/src/verification/wasm_method_type.rs b/bindings/wasm/src/verification/wasm_method_type.rs index 9fb1fff660..4b7d297a62 100644 --- a/bindings/wasm/src/verification/wasm_method_type.rs +++ b/bindings/wasm/src/verification/wasm_method_type.rs @@ -27,6 +27,11 @@ impl WasmMethodType { WasmMethodType(MethodType::JSON_WEB_KEY) } + /// A custom method. + pub fn custom(type_: String) -> WasmMethodType { + WasmMethodType(MethodType::custom(type_)) + } + /// Returns the {@link MethodType} as a string. #[allow(clippy::inherent_to_string)] #[wasm_bindgen(js_name = toString)] diff --git a/bindings/wasm/src/verification/wasm_verification_method.rs b/bindings/wasm/src/verification/wasm_verification_method.rs index 62b5103c9d..6f01436ffe 100644 --- a/bindings/wasm/src/verification/wasm_verification_method.rs +++ b/bindings/wasm/src/verification/wasm_verification_method.rs @@ -8,6 +8,7 @@ use crate::did::WasmCoreDID; use crate::did::WasmDIDUrl; use crate::error::Result; use crate::error::WasmResult; +use identity_iota::core::Object; use identity_iota::did::CoreDID; use identity_iota::verification::VerificationMethod; use wasm_bindgen::prelude::*; @@ -37,6 +38,24 @@ impl WasmVerificationMethod { .wasm_result() } + /// Create a custom {@link VerificationMethod}. + #[wasm_bindgen(constructor)] + pub fn new( + id: &WasmDIDUrl, + controller: &WasmCoreDID, + type_: &WasmMethodType, + data: &WasmMethodData, + ) -> Result { + VerificationMethod::builder(Object::new()) + .type_(type_.0.clone()) + .data(data.0.clone()) + .controller(controller.0.clone()) + .id(id.0.clone()) + .build() + .map(Self) + .wasm_result() + } + /// Returns a copy of the {@link DIDUrl} of the {@link VerificationMethod}'s `id`. #[wasm_bindgen] pub fn id(&self) -> WasmDIDUrl { diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 136049bb28..53c15c483a 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -14,7 +14,7 @@ iota-sdk = { version = "1.0", default-features = false, features = ["tls", "clie json-proof-token.workspace = true primitive-types = "0.12.1" rand = "0.8.5" -sd-jwt-payload = { version = "0.2.0", default-features = false, features = ["sha"] } +sd-jwt-payload = { version = "0.2.1", default-features = false, features = ["sha"] } serde_json = { version = "1.0", default-features = false } tokio = { version = "1.29", default-features = false, features = ["rt"] } @@ -99,4 +99,4 @@ name = "9_zkp" [[example]] path = "1_advanced/10_zkp_revocation.rs" -name = "10_zkp_revocation" \ No newline at end of file +name = "10_zkp_revocation" diff --git a/identity_credential/Cargo.toml b/identity_credential/Cargo.toml index c32afa27d5..c56a657199 100644 --- a/identity_credential/Cargo.toml +++ b/identity_credential/Cargo.toml @@ -12,6 +12,7 @@ rust-version.workspace = true description = "An implementation of the Verifiable Credentials standard." [dependencies] +async-trait = { version = "0.1.64", default-features = false } flate2 = { version = "1.0.28", default-features = false, features = ["rust_backend"], optional = true } futures = { version = "0.3", default-features = false, optional = true } identity_core = { version = "=1.1.1", path = "../identity_core", default-features = false } @@ -20,10 +21,11 @@ identity_document = { version = "=1.1.1", path = "../identity_document", default identity_verification = { version = "=1.1.1", path = "../identity_verification", default-features = false } indexmap = { version = "2.0", default-features = false, features = ["std", "serde"] } itertools = { version = "0.11", default-features = false, features = ["use_std"], optional = true } +json-proof-token.workspace = true once_cell = { version = "1.18", default-features = false, features = ["std"] } reqwest = { version = "0.11", default-features = false, features = ["default-tls", "json", "stream"], optional = true } -roaring = { version = "0.10", default-features = false, features = ["std"], optional = true } -sd-jwt-payload = { version = "0.2.0", default-features = false, features = ["sha"], optional = true } +roaring = { version = "0.10.2", default-features = false, features = ["serde"], optional = true } +sd-jwt-payload = { version = "0.2.1", default-features = false, features = ["sha"], optional = true } serde.workspace = true serde-aux = { version = "4.3.1", default-features = false } serde_json.workspace = true @@ -31,9 +33,7 @@ serde_repr = { version = "0.1", default-features = false, optional = true } strum.workspace = true thiserror.workspace = true url = { version = "2.5", default-features = false } -json-proof-token.workspace = true zkryptium.workspace = true -async-trait = { version = "0.1.64", default-features = false } [dev-dependencies] anyhow = "1.0.62" diff --git a/identity_credential/src/revocation/validity_timeframe_2024/revocation_timeframe_status.rs b/identity_credential/src/revocation/validity_timeframe_2024/revocation_timeframe_status.rs index 90ad071197..d961746d55 100644 --- a/identity_credential/src/revocation/validity_timeframe_2024/revocation_timeframe_status.rs +++ b/identity_credential/src/revocation/validity_timeframe_2024/revocation_timeframe_status.rs @@ -79,12 +79,12 @@ impl RevocationTimeframeStatus { }) } - /// Get startValidityTimeframe value + /// Get startValidityTimeframe value. pub fn start_validity_timeframe(&self) -> Timestamp { self.start_validity_timeframe } - /// Get endValidityTimeframe value + /// Get endValidityTimeframe value. pub fn end_validity_timeframe(&self) -> Timestamp { self.end_validity_timeframe } diff --git a/identity_credential/src/validator/jpt_credential_validation/jpt_credential_validator_utils.rs b/identity_credential/src/validator/jpt_credential_validation/jpt_credential_validator_utils.rs index 26761f1736..e1f292f57a 100644 --- a/identity_credential/src/validator/jpt_credential_validation/jpt_credential_validator_utils.rs +++ b/identity_credential/src/validator/jpt_credential_validation/jpt_credential_validator_utils.rs @@ -124,7 +124,7 @@ impl JptCredentialValidatorUtils { } } - /// Checks whether the credential status has been revoked + /// Checks whether the credential status has been revoked. /// /// Only supports `RevocationTimeframe2024`. pub fn check_revocation_with_validity_timeframe_2024< diff --git a/identity_iota/README.md b/identity_iota/README.md index e210d6e10d..69d68defd8 100644 --- a/identity_iota/README.md +++ b/identity_iota/README.md @@ -24,7 +24,7 @@ ## Introduction -IOTA Identity is a [Rust](https://www.rust-lang.org/) implementation of decentralized digital identity, also known as Self-Sovereign Identity (SSI). It implements the W3C [Decentralized Identifiers (DID)](https://www.w3.org/TR/did-core/) and [Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) specifications. This library can be used to create, resolve and authenticate digital identities and to create verifiable credentials and presentations in order to share information in a verifiable manner and establish trust in the digital world. It does so while supporting secure storage of cryptographic keys, which can be implemented for your preferred key management system. Many of the individual libraries (Rust crates) are agnostic over the concrete DID method, with the exception of some libraries dedicated to implement the [IOTA DID method](https://wiki.iota.org/shimmer/identity.rs/specs/did/iota_did_method_spec/), which is an implementation of decentralized digital identity on the IOTA and Shimmer networks. Written in stable Rust, IOTA Identity has strong guarantees of memory safety and process integrity while maintaining exceptional performance. +IOTA Identity is a [Rust](https://www.rust-lang.org/) implementation of decentralized digital identity, also known as Self-Sovereign Identity (SSI). It implements the W3C [Decentralized Identifiers (DID)](https://www.w3.org/TR/did-core/) and [Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) specifications. This library can be used to create, resolve and authenticate digital identities and to create verifiable credentials and presentations in order to share information in a verifiable manner and establish trust in the digital world. It does so while supporting secure storage of cryptographic keys, which can be implemented for your preferred key management system. Many of the individual libraries (Rust crates) are agnostic over the concrete DID method, with the exception of some libraries dedicated to implement the [IOTA DID method](https://wiki.iota.org/identity.rs/specs/did/iota_did_method_spec/), which is an implementation of decentralized digital identity on the IOTA and Shimmer networks. Written in stable Rust, IOTA Identity has strong guarantees of memory safety and process integrity while maintaining exceptional performance. ## Bindings @@ -36,8 +36,8 @@ IOTA Identity is a [Rust](https://www.rust-lang.org/) implementation of decentra - API References: - [Rust API Reference](https://docs.rs/identity_iota/latest/identity_iota/): Package documentation (cargo docs). - - [Wasm API Reference](https://wiki.iota.org/shimmer/identity.rs/libraries/wasm/api_reference/): Wasm Package documentation. -- [Identity Documentation Pages](https://wiki.iota.org/shimmer/identity.rs/introduction): Supplementing documentation with context around identity and simple examples on library usage. + - [Wasm API Reference](https://wiki.iota.org/identity.rs/libraries/wasm/api_reference/): Wasm Package documentation. +- [Identity Documentation Pages](https://wiki.iota.org/identity.rs/introduction): Supplementing documentation with context around identity and simple examples on library usage. - [Examples](https://github.com/iotaledger/identity.rs/blob/HEAD/examples): Practical code snippets to get you started with the library. ## Prerequisites @@ -74,7 +74,7 @@ version = "1.0.0" edition = "2021" [dependencies] -identity_iota = {version = "1.1.1", features = ["memstore"]} +identity_iota = { version = "1.1.1", features = ["memstore"] } iota-sdk = { version = "1.0.2", default-features = true, features = ["tls", "client", "stronghold"] } tokio = { version = "1", features = ["full"] } anyhow = "1.0.62" @@ -214,7 +214,7 @@ For detailed development progress, see the IOTA Identity development [kanban boa We would love to have you help us with the development of IOTA Identity. Each and every contribution is greatly valued! -Please review the [contribution](https://wiki.iota.org/shimmer/identity.rs/contribute) and [workflow](https://wiki.iota.org/shimmer/identity.rs/workflow) sections in the [IOTA Wiki](https://wiki.iota.org/). +Please review the [contribution](https://wiki.iota.org/identity.rs/contribute) and [workflow](https://wiki.iota.org/identity.rs/workflow) sections in the [IOTA Wiki](https://wiki.iota.org/). To contribute directly to the repository, simply fork the project, push your changes to your fork and create a pull request to get them included! diff --git a/identity_storage/Cargo.toml b/identity_storage/Cargo.toml index c20138235d..b5743e2d75 100644 --- a/identity_storage/Cargo.toml +++ b/identity_storage/Cargo.toml @@ -21,16 +21,15 @@ identity_document = { version = "=1.1.1", path = "../identity_document", default identity_iota_core = { version = "=1.1.1", path = "../identity_iota_core", default-features = false, optional = true } identity_verification = { version = "=1.1.1", path = "../identity_verification", default_features = false } iota-crypto = { version = "0.23", default-features = false, features = ["ed25519"], optional = true } +json-proof-token.workspace = true rand = { version = "0.8.5", default-features = false, features = ["std", "std_rng"], optional = true } seahash = { version = "4.1.0", default_features = false } serde.workspace = true serde_json.workspace = true thiserror.workspace = true tokio = { version = "1.29.0", default-features = false, features = ["macros", "sync"], optional = true } -json-proof-token.workspace = true zkryptium.workspace = true - [dev-dependencies] identity_credential = { version = "=1.1.1", path = "../identity_credential", features = ["revocation-bitmap"] } identity_eddsa_verifier = { version = "=1.1.1", path = "../identity_eddsa_verifier", default-features = false, features = ["ed25519"] } diff --git a/identity_verification/Cargo.toml b/identity_verification/Cargo.toml index 1b6bb11d77..e400aeebeb 100644 --- a/identity_verification/Cargo.toml +++ b/identity_verification/Cargo.toml @@ -13,8 +13,8 @@ identity_core = { version = "=1.1.1", path = "./../identity_core", default-featu identity_did = { version = "=1.1.1", path = "./../identity_did", default-features = false } identity_jose = { version = "=1.1.1", path = "./../identity_jose", default-features = false } serde.workspace = true +serde_json.workspace = true strum.workspace = true thiserror.workspace = true [dev-dependencies] -serde_json.workspace = true diff --git a/identity_verification/src/verification_method/material.rs b/identity_verification/src/verification_method/material.rs index 4d5f5775aa..8e881253c5 100644 --- a/identity_verification/src/verification_method/material.rs +++ b/identity_verification/src/verification_method/material.rs @@ -5,6 +5,12 @@ use crate::jose::jwk::Jwk; use core::fmt::Debug; use core::fmt::Formatter; use identity_core::convert::BaseEncoding; +use serde::de::Visitor; +use serde::ser::SerializeMap; +use serde::Deserialize; +use serde::Serialize; +use serde::Serializer; +use serde_json::Value; use crate::error::Error; use crate::error::Result; @@ -21,9 +27,9 @@ pub enum MethodData { PublicKeyBase58(String), /// Verification Material in the JSON Web Key format. PublicKeyJwk(Jwk), - /// Verification Material in CAIP-10 format. - /// [CAIP-10](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-10.md) - BlockchainAccountId(String), + /// Arbitrary verification material. + #[serde(untagged)] + Custom(CustomMethodData), } impl MethodData { @@ -39,6 +45,11 @@ impl MethodData { Self::PublicKeyMultibase(BaseEncoding::encode_multibase(&data, None)) } + /// Creates a new `MethodData` variant from custom data. + pub fn new_custom(data: impl Into) -> Self { + Self::Custom(data.into()) + } + /// Returns a `Vec` containing the decoded bytes of the `MethodData`. /// /// This is generally a public key identified by a `MethodType` value. @@ -48,7 +59,7 @@ impl MethodData { /// represented as a vector of bytes. pub fn try_decode(&self) -> Result> { match self { - Self::PublicKeyJwk(_) | Self::BlockchainAccountId(_) => Err(Error::InvalidMethodDataTransformation( + Self::PublicKeyJwk(_) | Self::Custom(_) => Err(Error::InvalidMethodDataTransformation( "method data is not base encoded", )), Self::PublicKeyMultibase(input) => { @@ -71,6 +82,15 @@ impl MethodData { pub fn try_public_key_jwk(&self) -> Result<&Jwk> { self.public_key_jwk().ok_or(Error::NotPublicKeyJwk) } + + /// Returns the custom method data, if any. + pub fn custom(&self) -> Option<&CustomMethodData> { + if let Self::Custom(method_data) = self { + Some(method_data) + } else { + None + } + } } impl Debug for MethodData { @@ -79,7 +99,94 @@ impl Debug for MethodData { Self::PublicKeyJwk(inner) => f.write_fmt(format_args!("PublicKeyJwk({inner:#?})")), Self::PublicKeyMultibase(inner) => f.write_fmt(format_args!("PublicKeyMultibase({inner})")), Self::PublicKeyBase58(inner) => f.write_fmt(format_args!("PublicKeyBase58({inner})")), - Self::BlockchainAccountId(inner) => f.write_fmt(format_args!("BlockchainAccountId({inner})")), + Self::Custom(CustomMethodData { name, data }) => f.write_fmt(format_args!("{name}({data})")), } } } + +#[derive(Clone, Debug, PartialEq, Eq)] +/// Custom verification method. +pub struct CustomMethodData { + /// Verification method's name. + pub name: String, + /// Verification method's data. + pub data: Value, +} + +impl Serialize for CustomMethodData { + fn serialize(&self, serializer: S) -> std::prelude::v1::Result + where + S: Serializer, + { + let mut map = serializer.serialize_map(Some(1))?; + map.serialize_entry(&self.name, &self.data)?; + map.end() + } +} + +impl<'de> Deserialize<'de> for CustomMethodData { + fn deserialize(deserializer: D) -> std::prelude::v1::Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_map(CustomMethodDataVisitor) + } +} + +struct CustomMethodDataVisitor; + +impl<'de> Visitor<'de> for CustomMethodDataVisitor { + type Value = CustomMethodData; + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("\"\": ") + } + fn visit_map(self, mut map: A) -> std::prelude::v1::Result + where + A: serde::de::MapAccess<'de>, + { + let mut custom_method_data = CustomMethodData { + name: String::default(), + data: Value::Null, + }; + while let Some((name, data)) = map.next_entry::()? { + custom_method_data = CustomMethodData { name, data }; + } + + Ok(custom_method_data) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn serialize_custom_method_data() { + let custom = MethodData::Custom(CustomMethodData { + name: "anArbitraryMethod".to_owned(), + data: json!({"a": 1, "b": 2}), + }); + let target_str = json!({ + "anArbitraryMethod": {"a": 1, "b": 2}, + }) + .to_string(); + assert_eq!(serde_json::to_string(&custom).unwrap(), target_str); + } + #[test] + fn deserialize_custom_method_data() { + let inner_data = json!({ + "firstCustomField": "a random string", + "secondCustomField": 420, + }); + let json_method_data = json!({ + "myCustomVerificationMethod": &inner_data, + }); + let custom = serde_json::from_value::(json_method_data.clone()).unwrap(); + let target_method_data = MethodData::Custom(CustomMethodData { + name: "myCustomVerificationMethod".to_owned(), + data: inner_data, + }); + assert_eq!(custom, target_method_data); + } +} diff --git a/identity_verification/src/verification_method/method.rs b/identity_verification/src/verification_method/method.rs index 360f2efe55..8c48e06893 100644 --- a/identity_verification/src/verification_method/method.rs +++ b/identity_verification/src/verification_method/method.rs @@ -20,6 +20,7 @@ use crate::verification_method::MethodBuilder; use crate::verification_method::MethodData; use crate::verification_method::MethodRef; use crate::verification_method::MethodType; +use crate::CustomMethodData; use identity_did::CoreDID; use identity_did::DIDUrl; use identity_did::DID; @@ -28,8 +29,8 @@ use identity_did::DID; /// /// [Specification](https://www.w3.org/TR/did-core/#verification-method-properties) #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[serde(from = "_VerificationMethod")] pub struct VerificationMethod { - #[serde(deserialize_with = "deserialize_id_with_fragment")] pub(crate) id: DIDUrl, pub(crate) controller: CoreDID, #[serde(rename = "type")] @@ -245,3 +246,46 @@ impl KeyComparable for VerificationMethod { self.id() } } + +// Horrible workaround for a tracked serde issue https://github.com/serde-rs/serde/issues/2200. Serde doesn't "consume" +// the input when deserializing flattened enums (MethodData in this case) causing duplication of data (in this case +// it ends up in the properties object). This workaround simply removes the duplication. +#[derive(Deserialize)] +struct _VerificationMethod { + #[serde(deserialize_with = "deserialize_id_with_fragment")] + pub(crate) id: DIDUrl, + pub(crate) controller: CoreDID, + #[serde(rename = "type")] + pub(crate) type_: MethodType, + #[serde(flatten)] + pub(crate) data: MethodData, + #[serde(flatten)] + pub(crate) properties: Object, +} + +impl From<_VerificationMethod> for VerificationMethod { + fn from(value: _VerificationMethod) -> Self { + let _VerificationMethod { + id, + controller, + type_, + data, + mut properties, + } = value; + let key = match &data { + MethodData::PublicKeyBase58(_) => "publicKeyBase58", + MethodData::PublicKeyJwk(_) => "publicKeyJwk", + MethodData::PublicKeyMultibase(_) => "publicKeyMultibase", + MethodData::Custom(CustomMethodData { name, .. }) => name.as_str(), + }; + properties.remove(key); + + VerificationMethod { + id, + controller, + type_, + data, + properties, + } + } +} diff --git a/identity_verification/src/verification_method/method_type.rs b/identity_verification/src/verification_method/method_type.rs index aa80ef4580..ae3877948d 100644 --- a/identity_verification/src/verification_method/method_type.rs +++ b/identity_verification/src/verification_method/method_type.rs @@ -12,7 +12,6 @@ use crate::error::Result; const ED25519_VERIFICATION_KEY_2018_STR: &str = "Ed25519VerificationKey2018"; const X25519_KEY_AGREEMENT_KEY_2019_STR: &str = "X25519KeyAgreementKey2019"; const JSON_WEB_KEY_METHOD_TYPE: &str = "JsonWebKey"; -const ECDSA_SECP256K1_RECOVERY_SIGNATURE_2020_STR: &str = "EcdsaSecp256k1RecoverySignature2020"; /// verification method types. #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] @@ -26,9 +25,10 @@ impl MethodType { /// A verification method for use with JWT verification as prescribed by the [`Jwk`](::identity_jose::jwk::Jwk) /// in the [`publicKeyJwk`](crate::MethodData::PublicKeyJwk) entry. pub const JSON_WEB_KEY: Self = Self(Cow::Borrowed(JSON_WEB_KEY_METHOD_TYPE)); - /// The `EcdsaSecp256k1RecoverySignature2020` method type. - pub const ECDSA_SECP256K1_RECOVERY_SIGNATURE_2020: Self = - Self(Cow::Borrowed(ECDSA_SECP256K1_RECOVERY_SIGNATURE_2020_STR)); + /// Construct a custom method type. + pub fn custom(type_: impl AsRef) -> Self { + Self(Cow::Owned(type_.as_ref().to_owned())) + } } impl MethodType { @@ -58,7 +58,6 @@ impl FromStr for MethodType { ED25519_VERIFICATION_KEY_2018_STR => Ok(Self::ED25519_VERIFICATION_KEY_2018), X25519_KEY_AGREEMENT_KEY_2019_STR => Ok(Self::X25519_KEY_AGREEMENT_KEY_2019), JSON_WEB_KEY_METHOD_TYPE => Ok(Self::JSON_WEB_KEY), - ECDSA_SECP256K1_RECOVERY_SIGNATURE_2020_STR => Ok(Self::ECDSA_SECP256K1_RECOVERY_SIGNATURE_2020), _ => Ok(Self(Cow::Owned(string.to_owned()))), } } diff --git a/identity_verification/src/verification_method/mod.rs b/identity_verification/src/verification_method/mod.rs index af6da98529..585b58639c 100644 --- a/identity_verification/src/verification_method/mod.rs +++ b/identity_verification/src/verification_method/mod.rs @@ -15,6 +15,7 @@ mod method_scope; mod method_type; pub use self::builder::MethodBuilder; +pub use self::material::CustomMethodData; pub use self::material::MethodData; pub use self::method::VerificationMethod; pub use self::method_ref::MethodRef;