diff --git a/bindings/wasm/examples/src/0_basic/5_create_vc.ts b/bindings/wasm/examples/src/0_basic/5_create_vc.ts index 7c35ae3a2d..1b711e51ad 100644 --- a/bindings/wasm/examples/src/0_basic/5_create_vc.ts +++ b/bindings/wasm/examples/src/0_basic/5_create_vc.ts @@ -3,6 +3,7 @@ import { Credential, + EdDSAJwsVerifier, FailFast, JwkMemStore, JwsSignatureOptions, @@ -12,7 +13,7 @@ import { Storage, } from "@iota/identity-wasm/node"; import { Client, MnemonicSecretManager, Utils } from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid, EdDSAJwsVerifier } from "../util"; +import { API_ENDPOINT, createDid } from "../util"; /** * This example shows how to create a Verifiable Credential and validate it. diff --git a/bindings/wasm/examples/src/0_basic/6_create_vp.ts b/bindings/wasm/examples/src/0_basic/6_create_vp.ts index 5993b3b9f8..f78f19dd1b 100644 --- a/bindings/wasm/examples/src/0_basic/6_create_vp.ts +++ b/bindings/wasm/examples/src/0_basic/6_create_vp.ts @@ -5,6 +5,7 @@ import { CoreDID, Credential, Duration, + EdDSAJwsVerifier, FailFast, IotaIdentityClient, JwkMemStore, @@ -24,7 +25,7 @@ import { Timestamp, } from "@iota/identity-wasm/node"; import { Client, MnemonicSecretManager, Utils } from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid, EdDSAJwsVerifier } from "../util"; +import { API_ENDPOINT, createDid } from "../util"; /** * This example shows how to create a Verifiable Presentation and validate it. diff --git a/bindings/wasm/examples/src/0_basic/7_revoke_vc.ts b/bindings/wasm/examples/src/0_basic/7_revoke_vc.ts index 0df27e7d50..63b00ee4d7 100644 --- a/bindings/wasm/examples/src/0_basic/7_revoke_vc.ts +++ b/bindings/wasm/examples/src/0_basic/7_revoke_vc.ts @@ -3,10 +3,14 @@ import { Credential, + EdCurve, FailFast, + IJwsVerifier, IotaDocument, IotaIdentityClient, + Jwk, JwkMemStore, + JwsAlgorithm, JwsSignatureOptions, JwtCredentialValidationOptions, JwtCredentialValidator, @@ -16,9 +20,10 @@ import { Service, Storage, VerificationMethod, + verifyEd25519, } from "@iota/identity-wasm/node"; import { AliasOutput, Client, IRent, MnemonicSecretManager, Utils } from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid, EdDSAJwsVerifier } from "../util"; +import { API_ENDPOINT, createDid } from "../util"; /** * This example shows how to revoke a verifiable credential. @@ -134,7 +139,7 @@ export async function revokeVC() { console.log(`Credential JWT > ${credentialJwt.toString()}`); // Validate the credential using the issuer's DID Document. - let jwtCredentialValidator = new JwtCredentialValidator(new EdDSAJwsVerifier()); + let jwtCredentialValidator = new JwtCredentialValidator(new Ed25519JwsVerifier()); jwtCredentialValidator.validate( credentialJwt, issuerDocument, @@ -225,3 +230,16 @@ export async function revokeVC() { console.log(`Credential successfully revoked!`); } } + +// A custom JWS Verifier capabale of verifying EdDSA signatures with curve Ed25519. +class Ed25519JwsVerifier implements IJwsVerifier { + verify(alg: JwsAlgorithm, signingInput: Uint8Array, decodedSignature: Uint8Array, publicKey: Jwk) { + switch (alg) { + case JwsAlgorithm.EdDSA: + // This verifies that the curve is Ed25519 so we don't need to check ourselves. + return verifyEd25519(alg, signingInput, decodedSignature, publicKey); + default: + throw new Error(`unsupported jws algorithm ${alg}`); + } + } +} diff --git a/bindings/wasm/examples/src/1_advanced/5_domain_linkage.ts b/bindings/wasm/examples/src/1_advanced/5_domain_linkage.ts index 02f77dcb9a..a2af8f83fb 100644 --- a/bindings/wasm/examples/src/1_advanced/5_domain_linkage.ts +++ b/bindings/wasm/examples/src/1_advanced/5_domain_linkage.ts @@ -7,6 +7,7 @@ import { DIDUrl, DomainLinkageConfiguration, Duration, + EdDSAJwsVerifier, IotaDID, IotaDocument, IotaIdentityClient, @@ -20,7 +21,7 @@ import { Timestamp, } from "@iota/identity-wasm/node"; import { AliasOutput, Client, IRent, MnemonicSecretManager, Utils } from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid, EdDSAJwsVerifier } from "../util"; +import { API_ENDPOINT, createDid } from "../util"; /** * Demonstrates how to link a domain and a DID and verify the linkage. diff --git a/bindings/wasm/examples/src/util.ts b/bindings/wasm/examples/src/util.ts index 230a606559..41a0a5a7ee 100644 --- a/bindings/wasm/examples/src/util.ts +++ b/bindings/wasm/examples/src/util.ts @@ -1,13 +1,10 @@ import { - IJwsVerifier, IotaDocument, IotaIdentityClient, - Jwk, JwkMemStore, JwsAlgorithm, MethodScope, Storage, - verifyEdDSA, } from "@iota/identity-wasm/node"; import { type Address, @@ -22,18 +19,6 @@ import { export const API_ENDPOINT = "http://localhost:14265"; export const FAUCET_ENDPOINT = "http://localhost:8091/api/enqueue"; -// A JWS Verifier capabale of verifying EdDSA signatures with curve Ed25519. -export class EdDSAJwsVerifier implements IJwsVerifier { - verify(alg: JwsAlgorithm, signingInput: Uint8Array, decodedSignature: Uint8Array, publicKey: Jwk) { - switch (alg) { - case JwsAlgorithm.EdDSA: - return verifyEdDSA(alg, signingInput, decodedSignature, publicKey); - default: - throw new Error(`unsupported jws algorithm ${alg}`); - } - } -} - /** Creates a DID Document and publishes it in a new Alias Output. Its functionality is equivalent to the "create DID" example diff --git a/bindings/wasm/src/verification/jws_verifier.rs b/bindings/wasm/src/verification/jws_verifier.rs index 2129a5fcad..2e04d64e68 100644 --- a/bindings/wasm/src/verification/jws_verifier.rs +++ b/bindings/wasm/src/verification/jws_verifier.rs @@ -1,6 +1,7 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use identity_eddsa_verifier::Ed25519Verifier; use identity_eddsa_verifier::EdDSAJwsVerifier; use identity_iota::verification::jws::JwsAlgorithm; use identity_iota::verification::jws::JwsVerifier; @@ -11,18 +12,18 @@ use crate::error::WasmResult; use crate::jose::WasmJwk; use crate::jose::WasmJwsAlgorithm; -/// Verify a JWS signature secured with the `JwsAlgorithm::EdDSA` algorithm. -/// Only the `EdCurve::Ed25519` variant is supported for now. +/// Verify a JWS signature secured with the `EdDSA` algorithm and curve `Ed25519`. /// -/// This function is useful when one is building an `IJwsVerifier` that extends the default provided by -/// the IOTA Identity Framework. +/// This function is useful when one is composing a {@link 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. -#[wasm_bindgen(js_name = verifyEdDSA)] +#[wasm_bindgen(js_name = verifyEd25519)] #[allow(non_snake_case)] -pub fn verify_eddsa( +pub fn verify_ed25519( alg: WasmJwsAlgorithm, signingInput: &[u8], decodedSignature: &[u8], @@ -34,5 +35,48 @@ pub fn verify_eddsa( signing_input: Box::from(signingInput), decoded_signature: Box::from(decodedSignature), }; - EdDSAJwsVerifier::default().verify(input, &publicKey.0).wasm_result() + Ed25519Verifier::verify(input, &publicKey.0).wasm_result() +} + +/// An implementor of {@link IJwsVerifier} that can handle the +/// `EdDSA` algorithm. +#[wasm_bindgen(js_name = EdDSAJwsVerifier)] +pub struct WasmEdDSAJwsVerifier(); + +#[wasm_bindgen(js_class = EdDSAJwsVerifier)] +#[allow(clippy::new_without_default)] +impl WasmEdDSAJwsVerifier { + /// Constructs an EdDSAJwsVerifier. + #[wasm_bindgen(constructor)] + pub fn new() -> Self { + Self() + } + + /// Verify a JWS signature secured with the `EdDSA` algorithm. + /// Only the `Ed25519` curve is supported for now. + /// + /// This function is useful when one is building an `IJwsVerifier` that extends the default provided by + /// the IOTA Identity Framework. + /// + /// # Warning + /// + /// This function does not check whether `alg = EdDSA` in the protected header. Callers are expected to assert this + /// prior to calling the function. + #[wasm_bindgen] + #[allow(non_snake_case)] + pub fn verify( + &self, + alg: WasmJwsAlgorithm, + signingInput: &[u8], + decodedSignature: &[u8], + publicKey: &WasmJwk, + ) -> Result<(), JsValue> { + let alg: JwsAlgorithm = JwsAlgorithm::try_from(alg)?; + let input: VerificationInput = VerificationInput { + alg, + signing_input: signingInput.into(), + decoded_signature: decodedSignature.into(), + }; + EdDSAJwsVerifier::default().verify(input, &publicKey.0).wasm_result() + } } diff --git a/bindings/wasm/tests/credentials.ts b/bindings/wasm/tests/credentials.ts index 92f240dd0a..dfe4e5ed79 100644 --- a/bindings/wasm/tests/credentials.ts +++ b/bindings/wasm/tests/credentials.ts @@ -2,6 +2,7 @@ import * as assert from "assert"; import { CoreDocument, Credential, + EdDSAJwsVerifier, JwkMemStore, JwsAlgorithm, JwsSignatureOptions, @@ -251,7 +252,7 @@ describe("Presentation", function() { let issuer = JwtPresentationValidator.extractHolder(presentationJwt); assert.deepStrictEqual(issuer.toString(), doc.id().toString()); - const decodedPresentation = new JwtPresentationValidator().validate( + const decodedPresentation = new JwtPresentationValidator(new EdDSAJwsVerifier()).validate( presentationJwt, doc, new JwtPresentationValidationOptions(), diff --git a/bindings/wasm/tests/storage.ts b/bindings/wasm/tests/storage.ts index 7a3f39a6b6..ff673cfae1 100644 --- a/bindings/wasm/tests/storage.ts +++ b/bindings/wasm/tests/storage.ts @@ -4,6 +4,7 @@ import { Credential, DecodedJwtPresentation, Duration, + EdDSAJwsVerifier, FailFast, IJwsVerifier, IotaDocument, @@ -95,7 +96,7 @@ describe("#JwkStorageDocument", function() { ); // Verify the signature and obtain a decoded token. - const token = doc.verifyJws(jws, new JwsVerificationOptions()); + const token = doc.verifyJws(jws, new JwsVerificationOptions(), new EdDSAJwsVerifier()); assert.deepStrictEqual(testString, token.claims()); // Check that we can also verify it using a custom verifier @@ -138,7 +139,7 @@ describe("#JwkStorageDocument", function() { ); // Check that the credentialJwt can be decoded and verified - let credentialValidator = new JwtCredentialValidator(); + let credentialValidator = new JwtCredentialValidator(new EdDSAJwsVerifier()); const credentialRetrieved = credentialValidator .validate( credentialJwt, @@ -206,7 +207,7 @@ describe("#JwkStorageDocument", function() { ); // Verify the signature and obtain a decoded token. - const token = doc.verifyJws(jws, new JwsVerificationOptions()); + const token = doc.verifyJws(jws, new JwsVerificationOptions(), new EdDSAJwsVerifier()); assert.deepStrictEqual(testString, token.claims()); // Check that we can also verify it using a custom verifier @@ -248,7 +249,7 @@ describe("#JwkStorageDocument", function() { ); // Check that the credentialJwt can be decoded and verified - let credentialValidator = new JwtCredentialValidator(); + let credentialValidator = new JwtCredentialValidator(new EdDSAJwsVerifier()); const credentialRetrieved = credentialValidator .validate( credentialJwt,