From 89335208f8f390462893fa44bb3fcde66e4d0cff Mon Sep 17 00:00:00 2001 From: Enrico Marconi Date: Tue, 12 Mar 2024 16:21:55 +0100 Subject: [PATCH 1/8] Add support for arbitrary (custom) verification method data --- identity_verification/Cargo.toml | 2 +- .../src/verification_method/material.rs | 91 +++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) 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 23a4843cc6..45c0cbad69 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; @@ -24,6 +30,9 @@ pub enum MethodData { /// 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 { @@ -60,6 +69,7 @@ impl MethodData { BaseEncoding::decode_multibase(input).map_err(|_| Error::InvalidKeyDataMultibase) } Self::PublicKeyBase58(input) => BaseEncoding::decode_base58(input).map_err(|_| Error::InvalidKeyDataBase58), + _ => unreachable!(), } } @@ -94,6 +104,87 @@ impl Debug for MethodData { 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)] +pub struct CustomMethodData { + pub name: String, + 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 Some((name, data)) = map.next_entry::()? else { + return Err(serde::de::Error::custom("empty custom method data")); + }; + + Ok(CustomMethodData { name, 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); + } +} From 01eacf2e3ecc6a116fa23a2791196fff26b13d4e Mon Sep 17 00:00:00 2001 From: Enrico Marconi Date: Tue, 12 Mar 2024 17:02:33 +0100 Subject: [PATCH 2/8] wasm bindings --- bindings/wasm/docs/api-reference.md | 126 +++++++++--------- .../wasm/src/verification/wasm_method_data.rs | 45 +++++-- .../src/verification_method/material.rs | 24 ++-- .../src/verification_method/mod.rs | 1 + 4 files changed, 111 insertions(+), 85 deletions(-) diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 73ff47d4c6..af791dc8e5 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -11,6 +11,9 @@ if the object is being concurrently modified.

Credential
+
CustomMethodData
+

A custom verification method data format.

+
DIDUrl

A method agnostic DID Url.

@@ -187,6 +190,8 @@ working with storage backed DID documents.

## Members
+
CredentialStatus
+
StatusCheck

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

@@ -204,15 +209,6 @@ working with storage backed DID documents.

SkipAll

Skip all status checks.

-
StatusPurpose
-

Purpose of a StatusList2021.

-
-
MethodRelationship
-
-
CredentialStatus
-
-
StateMetadataEncoding
-
FailFast

Declares when validation should return if an error occurs.

@@ -236,11 +232,21 @@ This variant is the default.

Any

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

+
StatusPurpose
+

Purpose of a StatusList2021.

+
+
MethodRelationship
+
+
StateMetadataEncoding
+
## Functions
+
start()
+

Initializes the console error panic hook for better error messages

+
encodeB64(data)string

Encode the given bytes in url-safe base64.

@@ -255,9 +261,6 @@ 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.

-
start()
-

Initializes the console error panic hook for better error messages

-
@@ -1138,6 +1141,21 @@ Deserializes an instance from a JSON object. | --- | --- | | json | any | + + +## CustomMethodData +A custom verification method data format. + +**Kind**: global class + + +### new CustomMethodData(name, data) + +| Param | Type | +| --- | --- | +| name | string | +| data | any | + ## DIDUrl @@ -4343,7 +4361,7 @@ Supported verification method data formats. * [MethodData](#MethodData) * _instance_ - * [.tryBlockchainAccountId()](#MethodData+tryBlockchainAccountId) ⇒ string + * [.tryCustom()](#MethodData+tryCustom) ⇒ [CustomMethodData](#CustomMethodData) * [.tryDecode()](#MethodData+tryDecode) ⇒ Uint8Array * [.tryPublicKeyJwk()](#MethodData+tryPublicKeyJwk) ⇒ [Jwk](#Jwk) * [.toJSON()](#MethodData+toJSON) ⇒ any @@ -4352,13 +4370,13 @@ Supported verification method data formats. * [.newBase58(data)](#MethodData.newBase58) ⇒ [MethodData](#MethodData) * [.newMultibase(data)](#MethodData.newMultibase) ⇒ [MethodData](#MethodData) * [.newJwk(key)](#MethodData.newJwk) ⇒ [MethodData](#MethodData) - * [.newBlockchainAccountId(data)](#MethodData.newBlockchainAccountId) ⇒ [MethodData](#MethodData) + * [.newCustom(name, data)](#MethodData.newCustom) ⇒ [MethodData](#MethodData) * [.fromJSON(json)](#MethodData.fromJSON) ⇒ [MethodData](#MethodData) - + -### methodData.tryBlockchainAccountId() ⇒ string -Returns the wrapped blockchain account id if the format is `BlockchainAccountId`. +### methodData.tryCustom() ⇒ [CustomMethodData](#CustomMethodData) +Returns the wrapped custom method data format is `Custom`. **Kind**: instance method of [MethodData](#MethodData) @@ -4427,16 +4445,17 @@ An error is thrown if the given `key` contains any private components. | --- | --- | | key | [Jwk](#Jwk) | - + -### MethodData.newBlockchainAccountId(data) ⇒ [MethodData](#MethodData) +### MethodData.newCustom(name, data) ⇒ [MethodData](#MethodData) Creates a new [MethodData](#MethodData) variant in CAIP-10 format. **Kind**: static method of [MethodData](#MethodData) | Param | Type | | --- | --- | -| data | string | +| name | string | +| data | any | @@ -5032,11 +5051,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) @@ -5079,12 +5096,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) @@ -5106,17 +5117,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 @@ -6159,6 +6159,10 @@ Deserializes an instance from a JSON object. | --- | --- | | json | any | + + +## CredentialStatus +**Kind**: global variable ## StatusCheck @@ -6189,24 +6193,6 @@ Validate the status if supported, skip any unsupported ## SkipAll Skip all status checks. -**Kind**: global variable - - -## StatusPurpose -Purpose of a [StatusList2021](#StatusList2021). - -**Kind**: global variable - - -## MethodRelationship -**Kind**: global variable - - -## CredentialStatus -**Kind**: global variable - - -## StateMetadataEncoding **Kind**: global variable @@ -6253,6 +6239,26 @@ The holder must match the subject only for credentials where the [`nonTransferab The holder is not required to have any kind of relationship to any credential subject. **Kind**: global variable + + +## StatusPurpose +Purpose of a [StatusList2021](#StatusList2021). + +**Kind**: global variable + + +## MethodRelationship +**Kind**: global variable + + +## StateMetadataEncoding +**Kind**: global variable + + +## start() +Initializes the console error panic hook for better error messages + +**Kind**: global function ## encodeB64(data) ⇒ string @@ -6297,9 +6303,3 @@ prior to calling the function. | decodedSignature | Uint8Array | | publicKey | [Jwk](#Jwk) | - - -## start() -Initializes the console error panic hook for better error messages - -**Kind**: global function diff --git a/bindings/wasm/src/verification/wasm_method_data.rs b/bindings/wasm/src/verification/wasm_method_data.rs index 809eab22e4..ae63ec970f 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::*; @@ -46,21 +47,22 @@ impl WasmMethodData { } /// Creates a new {@link MethodData} variant in CAIP-10 format. - #[wasm_bindgen(js_name = newBlockchainAccountId)] - pub fn new_blockchain_account_id(data: String) -> Self { - Self(MethodData::new_blockchain_account_id(data)) + #[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 blockchain account id if the format is `BlockchainAccountId`. - #[wasm_bindgen(js_name = tryBlockchainAccountId)] - pub fn try_blockchain_account_id(&self) -> Result { + /// Returns the wrapped custom method data format is `Custom`. + #[wasm_bindgen(js_name = tryCustom)] + pub fn try_custom(&self) -> Result { self .0 - .blockchain_account_id() - .map(|id| id.to_string()) + .custom() + .map(|custom| custom.clone().into()) .ok_or(WasmError::new( Cow::Borrowed("MethodDataFormatError"), - Cow::Borrowed("method data format is not BlockchainAccountId"), + Cow::Borrowed("method data format is not Custom"), )) .wasm_result() } @@ -98,3 +100,28 @@ 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 + } +} diff --git a/identity_verification/src/verification_method/material.rs b/identity_verification/src/verification_method/material.rs index 45c0cbad69..0da4997aab 100644 --- a/identity_verification/src/verification_method/material.rs +++ b/identity_verification/src/verification_method/material.rs @@ -27,9 +27,6 @@ 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), @@ -48,9 +45,9 @@ impl MethodData { Self::PublicKeyMultibase(BaseEncoding::encode_multibase(&data, None)) } - /// Verification Material in CAIP-10 format. - pub fn new_blockchain_account_id(data: String) -> Self { - Self::BlockchainAccountId(data) + /// 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`. @@ -62,14 +59,13 @@ 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) => { BaseEncoding::decode_multibase(input).map_err(|_| Error::InvalidKeyDataMultibase) } Self::PublicKeyBase58(input) => BaseEncoding::decode_base58(input).map_err(|_| Error::InvalidKeyDataBase58), - _ => unreachable!(), } } @@ -87,10 +83,10 @@ impl MethodData { self.public_key_jwk().ok_or(Error::NotPublicKeyJwk) } - /// Returns the wrapped Blockchain Account Id if the format is [`MethodData::BlockchainAccountId`]. - pub fn blockchain_account_id(&self) -> Option<&str> { - if let Self::BlockchainAccountId(id) = self { - Some(id) + /// 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 } @@ -103,15 +99,17 @@ 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, } diff --git a/identity_verification/src/verification_method/mod.rs b/identity_verification/src/verification_method/mod.rs index af6da98529..fce0b82107 100644 --- a/identity_verification/src/verification_method/mod.rs +++ b/identity_verification/src/verification_method/mod.rs @@ -16,6 +16,7 @@ mod method_type; pub use self::builder::MethodBuilder; pub use self::material::MethodData; +pub use self::material::CustomMethodData; pub use self::method::VerificationMethod; pub use self::method_ref::MethodRef; pub use self::method_relationship::MethodRelationship; From 4c3877acd5855a23a7dac7e39b8d2e4bf340610e Mon Sep 17 00:00:00 2001 From: Enrico Marconi Date: Tue, 12 Mar 2024 17:16:45 +0100 Subject: [PATCH 3/8] custom method type + wasm --- bindings/wasm/docs/api-reference.md | 137 +++++++++--------- .../wasm/src/verification/wasm_method_type.rs | 7 +- .../src/verification_method/method_type.rs | 9 +- .../src/verification_method/mod.rs | 2 +- 4 files changed, 79 insertions(+), 76 deletions(-) diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index af791dc8e5..38d3645c5f 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -190,7 +190,26 @@ working with storage backed DID documents.

## Members
-
CredentialStatus
+
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.

+
+
StatusPurpose
+

Purpose of a StatusList2021.

+
+
StateMetadataEncoding
StatusCheck

Controls validation behaviour when checking whether or not a credential has been revoked by its @@ -218,26 +237,7 @@ working with storage backed DID documents.

FirstError

Return after the first error occurs.

-
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.

-
-
MethodRelationship
-
-
StateMetadataEncoding
+
CredentialStatus
@@ -4608,7 +4608,7 @@ Supported verification method types. * [.Ed25519VerificationKey2018()](#MethodType.Ed25519VerificationKey2018) ⇒ [MethodType](#MethodType) * [.X25519KeyAgreementKey2019()](#MethodType.X25519KeyAgreementKey2019) ⇒ [MethodType](#MethodType) * [.JsonWebKey()](#MethodType.JsonWebKey) ⇒ [MethodType](#MethodType) - * [.EcdsaSecp256k1RecoverySignature2020()](#MethodType.EcdsaSecp256k1RecoverySignature2020) ⇒ [MethodType](#MethodType) + * [.custom(type_)](#MethodType.custom) ⇒ [MethodType](#MethodType) * [.fromJSON(json)](#MethodType.fromJSON) ⇒ [MethodType](#MethodType) @@ -4644,12 +4644,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.EcdsaSecp256k1RecoverySignature2020() ⇒ [MethodType](#MethodType) -The `EcdsaSecp256k1RecoverySignature2020` method type. +### MethodType.custom(type_) ⇒ [MethodType](#MethodType) +A custom method. **Kind**: static method of [MethodType](#MethodType) + +| Param | Type | +| --- | --- | +| type_ | string | + ### MethodType.fromJSON(json) ⇒ [MethodType](#MethodType) @@ -6159,9 +6164,46 @@ Deserializes an instance from a JSON object. | --- | --- | | json | any | - + + +## MethodRelationship +**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. -## CredentialStatus +**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. + +**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`. + +**Kind**: global variable + + +## Any +The holder is not required to have any kind of relationship to any credential subject. + +**Kind**: global variable + + +## StatusPurpose +Purpose of a [StatusList2021](#StatusList2021). + +**Kind**: global variable + + +## StateMetadataEncoding **Kind**: global variable @@ -6212,46 +6254,9 @@ Return all errors that occur during validation. Return after the first error occurs. **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. - -**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. - -**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`. - -**Kind**: global variable - - -## Any -The holder is not required to have any kind of relationship to any credential subject. - -**Kind**: global variable - - -## StatusPurpose -Purpose of a [StatusList2021](#StatusList2021). - -**Kind**: global variable - - -## MethodRelationship -**Kind**: global variable - + -## StateMetadataEncoding +## CredentialStatus **Kind**: global variable diff --git a/bindings/wasm/src/verification/wasm_method_type.rs b/bindings/wasm/src/verification/wasm_method_type.rs index 850ba08890..4b7d297a62 100644 --- a/bindings/wasm/src/verification/wasm_method_type.rs +++ b/bindings/wasm/src/verification/wasm_method_type.rs @@ -27,10 +27,9 @@ impl WasmMethodType { WasmMethodType(MethodType::JSON_WEB_KEY) } - /// The `EcdsaSecp256k1RecoverySignature2020` method type. - #[wasm_bindgen(js_name = EcdsaSecp256k1RecoverySignature2020)] - pub fn ecdsa_secp256k1_recovery_signature_2020() -> WasmMethodType { - WasmMethodType(MethodType::ECDSA_SECP256K1_RECOVERY_SIGNATURE_2020) + /// A custom method. + pub fn custom(type_: String) -> WasmMethodType { + WasmMethodType(MethodType::custom(type_)) } /// Returns the {@link MethodType} as a string. 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 fce0b82107..585b58639c 100644 --- a/identity_verification/src/verification_method/mod.rs +++ b/identity_verification/src/verification_method/mod.rs @@ -15,8 +15,8 @@ mod method_scope; mod method_type; pub use self::builder::MethodBuilder; -pub use self::material::MethodData; pub use self::material::CustomMethodData; +pub use self::material::MethodData; pub use self::method::VerificationMethod; pub use self::method_ref::MethodRef; pub use self::method_relationship::MethodRelationship; From 55ba842a1deed608e7a0baf681225f1727c4eadd Mon Sep 17 00:00:00 2001 From: Enrico Marconi Date: Wed, 13 Mar 2024 11:02:35 +0100 Subject: [PATCH 4/8] workaround serde's issue --- .../src/verification_method/material.rs | 10 +++-- .../src/verification_method/method.rs | 39 ++++++++++++++++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/identity_verification/src/verification_method/material.rs b/identity_verification/src/verification_method/material.rs index 0da4997aab..8e881253c5 100644 --- a/identity_verification/src/verification_method/material.rs +++ b/identity_verification/src/verification_method/material.rs @@ -144,11 +144,15 @@ impl<'de> Visitor<'de> for CustomMethodDataVisitor { where A: serde::de::MapAccess<'de>, { - let Some((name, data)) = map.next_entry::()? else { - return Err(serde::de::Error::custom("empty custom method data")); + 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(CustomMethodData { name, data }) + Ok(custom_method_data) } } diff --git a/identity_verification/src/verification_method/method.rs b/identity_verification/src/verification_method/method.rs index 360f2efe55..349ab6ae1a 100644 --- a/identity_verification/src/verification_method/method.rs +++ b/identity_verification/src/verification_method/method.rs @@ -28,8 +28,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 +245,40 @@ impl KeyComparable for VerificationMethod { self.id() } } + +// Horrible workaround for a tracked serde issue https://github.com/serde-rs/serde/issues/2200 +#[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 data_json = serde_json::to_value(&data).unwrap(); + let data_type = data_json.as_object().unwrap().into_iter().next().unwrap().0; + properties.remove(data_type); + + VerificationMethod { + id, + controller, + type_, + data, + properties, + } + } +} From 84a5586c4ddeec520e24c16e5eebc03397175677 Mon Sep 17 00:00:00 2001 From: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:54:44 +0100 Subject: [PATCH 5/8] Update bindings/wasm/src/verification/wasm_method_data.rs Co-authored-by: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com> --- bindings/wasm/src/verification/wasm_method_data.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/wasm/src/verification/wasm_method_data.rs b/bindings/wasm/src/verification/wasm_method_data.rs index ae63ec970f..a8cf793207 100644 --- a/bindings/wasm/src/verification/wasm_method_data.rs +++ b/bindings/wasm/src/verification/wasm_method_data.rs @@ -46,7 +46,7 @@ impl WasmMethodData { Ok(Self(MethodData::PublicKeyJwk(key.0.clone()))) } - /// Creates a new {@link MethodData} variant in CAIP-10 format. + /// 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()?; From c42c50f353327ddbb331897afe0cf900440bfb2c Mon Sep 17 00:00:00 2001 From: Enrico Marconi Date: Thu, 14 Mar 2024 16:03:25 +0100 Subject: [PATCH 6/8] review comments --- bindings/wasm/src/verification/wasm_method_data.rs | 3 +++ identity_verification/src/verification_method/method.rs | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/bindings/wasm/src/verification/wasm_method_data.rs b/bindings/wasm/src/verification/wasm_method_data.rs index a8cf793207..58a9c65820 100644 --- a/bindings/wasm/src/verification/wasm_method_data.rs +++ b/bindings/wasm/src/verification/wasm_method_data.rs @@ -125,3 +125,6 @@ impl From for CustomMethodData { value.0 } } + +impl_wasm_clone!(WasmCustomMethodData, CustomMethodData); +impl_wasm_json!(WasmCustomMethodData, CustomMethodData); diff --git a/identity_verification/src/verification_method/method.rs b/identity_verification/src/verification_method/method.rs index 349ab6ae1a..c22cecd059 100644 --- a/identity_verification/src/verification_method/method.rs +++ b/identity_verification/src/verification_method/method.rs @@ -246,7 +246,9 @@ impl KeyComparable for VerificationMethod { } } -// Horrible workaround for a tracked serde issue https://github.com/serde-rs/serde/issues/2200 +// 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) cousing duplication of data (in this case +// it ends up in the properties object). This workaround simply remove the duplication. #[derive(Deserialize)] struct _VerificationMethod { #[serde(deserialize_with = "deserialize_id_with_fragment")] From b6772e762462bbba49b542b0961f00a1601dd845 Mon Sep 17 00:00:00 2001 From: Enrico Marconi Date: Thu, 14 Mar 2024 16:10:52 +0100 Subject: [PATCH 7/8] fmt --- identity_verification/src/verification_method/method.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/identity_verification/src/verification_method/method.rs b/identity_verification/src/verification_method/method.rs index c22cecd059..5baa36f1b6 100644 --- a/identity_verification/src/verification_method/method.rs +++ b/identity_verification/src/verification_method/method.rs @@ -247,7 +247,7 @@ impl KeyComparable for VerificationMethod { } // 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) cousing duplication of data (in this case +// the input when deserializing flattened enums (MethodData in this case) cousing duplication of data (in this case // it ends up in the properties object). This workaround simply remove the duplication. #[derive(Deserialize)] struct _VerificationMethod { From 158bce6a16352bc67b2b8c2e1ac8efefa14244f6 Mon Sep 17 00:00:00 2001 From: Enrico Marconi Date: Mon, 18 Mar 2024 16:22:14 +0100 Subject: [PATCH 8/8] review comment --- bindings/wasm/docs/api-reference.md | 178 +++++++++++------- .../src/verification_method/method.rs | 15 +- 2 files changed, 115 insertions(+), 78 deletions(-) diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 38d3645c5f..2f50e4ed3d 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -190,8 +190,9 @@ working with storage backed DID documents.

## Members
-
MethodRelationship
-
+
StatusPurpose
+

Purpose of a StatusList2021.

+
SubjectHolderRelationship

Declares how credential subjects must relate to the presentation holder.

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

@@ -206,11 +207,21 @@ This variant is the default.

Any

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

-
StatusPurpose
-

Purpose of a StatusList2021.

-
StateMetadataEncoding
+
FailFast
+

Declares when validation should return if an error occurs.

+
+
AllErrors
+

Return all errors that occur during validation.

+
+
FirstError
+

Return after the first error occurs.

+
+
MethodRelationship
+
+
CredentialStatus
+
StatusCheck

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

@@ -228,31 +239,11 @@ This variant is the default.

SkipAll

Skip all status checks.

-
FailFast
-

Declares when validation should return if an error occurs.

-
-
AllErrors
-

Return all errors that occur during validation.

-
-
FirstError
-

Return after the first error occurs.

-
-
CredentialStatus
-
## 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.

This function is useful when one is composing a IJwsVerifier that delegates @@ -261,6 +252,15 @@ 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

+
@@ -1147,6 +1147,15 @@ Deserializes an instance from a JSON object. 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) @@ -1156,6 +1165,29 @@ A custom verification method data format. | 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 @@ -4448,7 +4480,7 @@ An error is thrown if the given `key` contains any private components. ### MethodData.newCustom(name, data) ⇒ [MethodData](#MethodData) -Creates a new [MethodData](#MethodData) variant in CAIP-10 format. +Creates a new custom [MethodData](#MethodData). **Kind**: static method of [MethodData](#MethodData) @@ -6164,9 +6196,11 @@ Deserializes an instance from a JSON object. | --- | --- | | json | any | - + + +## StatusPurpose +Purpose of a [StatusList2021](#StatusList2021). -## MethodRelationship **Kind**: global variable @@ -6195,15 +6229,35 @@ The holder must match the subject only for credentials where the [`nonTransferab The holder is not required to have any kind of relationship to any credential subject. **Kind**: global variable - + -## StatusPurpose -Purpose of a [StatusList2021](#StatusList2021). +## StateMetadataEncoding +**Kind**: global variable + + +## FailFast +Declares when validation should return if an error occurs. **Kind**: global variable - + + +## AllErrors +Return all errors that occur during validation. -## StateMetadataEncoding +**Kind**: global variable + + +## FirstError +Return after the first error occurs. + +**Kind**: global variable + + +## MethodRelationship +**Kind**: global variable + + +## CredentialStatus **Kind**: global variable @@ -6236,34 +6290,28 @@ Validate the status if supported, skip any unsupported Skip all status checks. **Kind**: global variable - - -## FailFast -Declares when validation should return if an error occurs. - -**Kind**: global variable - + -## AllErrors -Return all errors that occur during validation. +## verifyEd25519(alg, signingInput, decodedSignature, publicKey) +Verify a JWS signature secured with the `EdDSA` algorithm and curve `Ed25519`. -**Kind**: global variable - +This function is useful when one is composing a `IJwsVerifier` that delegates +`EdDSA` verification with curve `Ed25519` to this function. -## FirstError -Return after the first error occurs. +# Warning -**Kind**: global variable - +This function does not check whether `alg = EdDSA` in the protected header. Callers are expected to assert this +prior to calling the function. -## CredentialStatus -**Kind**: global variable - +**Kind**: global function -## start() -Initializes the console error panic hook for better error messages +| Param | Type | +| --- | --- | +| alg | JwsAlgorithm | +| signingInput | Uint8Array | +| decodedSignature | Uint8Array | +| publicKey | [Jwk](#Jwk) | -**Kind**: global function ## encodeB64(data) ⇒ string @@ -6286,25 +6334,9 @@ Decode the given url-safe base64-encoded slice into its raw bytes. | --- | --- | | data | Uint8Array | - - -## verifyEd25519(alg, signingInput, decodedSignature, publicKey) -Verify a JWS signature secured with the `EdDSA` algorithm and curve `Ed25519`. - -This function is useful when one is composing a `IJwsVerifier` that delegates -`EdDSA` verification with curve `Ed25519` to this function. - -# Warning + -This function does not check whether `alg = EdDSA` in the protected header. Callers are expected to assert this -prior to calling the function. +## start() +Initializes the console error panic hook for better error messages **Kind**: global function - -| Param | Type | -| --- | --- | -| alg | JwsAlgorithm | -| signingInput | Uint8Array | -| decodedSignature | Uint8Array | -| publicKey | [Jwk](#Jwk) | - diff --git a/identity_verification/src/verification_method/method.rs b/identity_verification/src/verification_method/method.rs index 5baa36f1b6..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; @@ -247,8 +248,8 @@ impl KeyComparable for VerificationMethod { } // 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) cousing duplication of data (in this case -// it ends up in the properties object). This workaround simply remove the duplication. +// 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")] @@ -271,9 +272,13 @@ impl From<_VerificationMethod> for VerificationMethod { data, mut properties, } = value; - let data_json = serde_json::to_value(&data).unwrap(); - let data_type = data_json.as_object().unwrap().into_iter().next().unwrap().0; - properties.remove(data_type); + 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,