Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add WASM bindings for EcDSA JWS Verifier #1396

Merged
merged 7 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bindings/wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ async-trait = { version = "0.1", default-features = false }
bls12_381_plus = "0.8.17"
console_error_panic_hook = { version = "0.1" }
futures = { version = "0.3" }
identity_ecdsa_verifier = { path = "../../identity_ecdsa_verifier", default-features = false, features = ["es256", "es256k"] }
identity_eddsa_verifier = { path = "../../identity_eddsa_verifier", default-features = false, features = ["ed25519"] }
js-sys = { version = "0.3.61" }
json-proof-token = "0.3.4"
Expand Down
288 changes: 167 additions & 121 deletions bindings/wasm/docs/api-reference.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions bindings/wasm/src/credential/domain_linkage_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ pub struct WasmJwtDomainLinkageValidator {
#[wasm_bindgen(js_class = JwtDomainLinkageValidator)]
impl WasmJwtDomainLinkageValidator {
/// Creates a new {@link JwtDomainLinkageValidator}. If a `signatureVerifier` is provided it will be used when
/// verifying decoded JWS signatures, otherwise the default which is only capable of handling the `EdDSA`
/// algorithm will be used.
/// verifying decoded JWS signatures, otherwise a default verifier capable of handling the `EdDSA`, `ES256`, `ES256K`
/// algorithms will be used.
#[wasm_bindgen(constructor)]
#[allow(non_snake_case)]
pub fn new(signatureVerifier: IJwsVerifier) -> WasmJwtDomainLinkageValidator {
pub fn new(signatureVerifier: Option<IJwsVerifier>) -> WasmJwtDomainLinkageValidator {
let signature_verifier = WasmJwsVerifier::new(signatureVerifier);
WasmJwtDomainLinkageValidator {
validator: JwtDomainLinkageValidator::with_signature_verifier(signature_verifier),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ pub struct WasmJwtCredentialValidator(JwtCredentialValidator<WasmJwsVerifier>);
#[wasm_bindgen(js_class = JwtCredentialValidator)]
impl WasmJwtCredentialValidator {
/// Creates a new {@link JwtCredentialValidator}. If a `signatureVerifier` is provided it will be used when
/// verifying decoded JWS signatures, otherwise the default which is only capable of handling the `EdDSA`
/// algorithm will be used.
/// verifying decoded JWS signatures, otherwise a default verifier capable of handling the `EdDSA`, `ES256`, `ES256K`
/// algorithms will be used.
#[wasm_bindgen(constructor)]
#[allow(non_snake_case)]
pub fn new(signatureVerifier: IJwsVerifier) -> WasmJwtCredentialValidator {
pub fn new(signatureVerifier: Option<IJwsVerifier>) -> WasmJwtCredentialValidator {
let signature_verifier = WasmJwsVerifier::new(signatureVerifier);
WasmJwtCredentialValidator(JwtCredentialValidator::with_signature_verifier(signature_verifier))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ pub struct WasmSdJwtCredentialValidator(SdJwtCredentialValidator<WasmJwsVerifier
#[wasm_bindgen(js_class = SdJwtCredentialValidator)]
impl WasmSdJwtCredentialValidator {
/// Creates a new `SdJwtCredentialValidator`. If a `signatureVerifier` is provided it will be used when
/// verifying decoded JWS signatures, otherwise the default which is only capable of handling the `EdDSA`
/// algorithm will be used.
/// verifying decoded JWS signatures, otherwise a default verifier capable of handling the `EdDSA`, `ES256`, `ES256K`
/// algorithms will be used.
#[wasm_bindgen(constructor)]
#[allow(non_snake_case)]
pub fn new(signatureVerifier: IJwsVerifier) -> WasmSdJwtCredentialValidator {
pub fn new(signatureVerifier: Option<IJwsVerifier>) -> WasmSdJwtCredentialValidator {
let signature_verifier = WasmJwsVerifier::new(signatureVerifier);
WasmSdJwtCredentialValidator(SdJwtCredentialValidator::with_signature_verifier(
signature_verifier,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ pub struct WasmJwtPresentationValidator(JwtPresentationValidator<WasmJwsVerifier
#[wasm_bindgen(js_class = JwtPresentationValidator)]
impl WasmJwtPresentationValidator {
/// Creates a new {@link JwtPresentationValidator}. If a `signatureVerifier` is provided it will be used when
/// verifying decoded JWS signatures, otherwise the default which is only capable of handling the `EdDSA`
/// algorithm will be used.
/// verifying decoded JWS signatures, otherwise a default verifier capable of handling the `EdDSA`, `ES256`, `ES256K`
/// algorithms will be used.
#[wasm_bindgen(constructor)]
#[allow(non_snake_case)]
pub fn new(signatureVerifier: IJwsVerifier) -> WasmJwtPresentationValidator {
pub fn new(signatureVerifier: Option<IJwsVerifier>) -> WasmJwtPresentationValidator {
let signature_verifier = WasmJwsVerifier::new(signatureVerifier);
WasmJwtPresentationValidator(JwtPresentationValidator::with_signature_verifier(signature_verifier))
}
Expand Down
7 changes: 4 additions & 3 deletions bindings/wasm/src/did/wasm_core_document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,9 @@ impl WasmCoreDocument {
// ===========================================================================

/// Decodes and verifies the provided JWS according to the passed `options` and `signatureVerifier`.
/// If no `signatureVerifier` argument is provided a default verifier will be used that is (only) capable of
/// verifying EdDSA signatures.
/// If a `signatureVerifier` is provided it will be used when
/// verifying decoded JWS signatures, otherwise a default verifier capable of handling the `EdDSA`, `ES256`, `ES256K`
/// algorithms will be used.
///
/// Regardless of which options are passed the following conditions must be met in order for a verification attempt to
/// take place.
Expand All @@ -509,7 +510,7 @@ impl WasmCoreDocument {
&self,
jws: &WasmJws,
options: &WasmJwsVerificationOptions,
signatureVerifier: IJwsVerifier,
signatureVerifier: Option<IJwsVerifier>,
detachedPayload: Option<String>,
) -> Result<WasmDecodedJws> {
let jws_verifier = WasmJwsVerifier::new(signatureVerifier);
Expand Down
7 changes: 4 additions & 3 deletions bindings/wasm/src/iota/iota_document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,9 @@ impl WasmIotaDocument {
// ===========================================================================

/// Decodes and verifies the provided JWS according to the passed `options` and `signatureVerifier`.
/// If no `signatureVerifier` argument is provided a default verifier will be used that is (only) capable of
/// verifying EdDSA signatures.
/// If a `signatureVerifier` is provided it will be used when
/// verifying decoded JWS signatures, otherwise a default verifier capable of handling the `EdDSA`, `ES256`, `ES256K`
/// algorithms will be used.
///
/// Regardless of which options are passed the following conditions must be met in order for a verification attempt to
/// take place.
Expand All @@ -397,7 +398,7 @@ impl WasmIotaDocument {
&self,
jws: &WasmJws,
options: &WasmJwsVerificationOptions,
signatureVerifier: IJwsVerifier,
signatureVerifier: Option<IJwsVerifier>,
detachedPayload: Option<String>,
) -> Result<WasmDecodedJws> {
let jws_verifier = WasmJwsVerifier::new(signatureVerifier);
Expand Down
51 changes: 31 additions & 20 deletions bindings/wasm/src/verification/custom_verification.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Copyright 2020-2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use identity_ecdsa_verifier::EcDSAJwsVerifier;
use identity_eddsa_verifier::EdDSAJwsVerifier;
use identity_iota::verification::jws::JwsAlgorithm;
use identity_iota::verification::jws::JwsVerifier;
use identity_iota::verification::jws::SignatureVerificationError;
use identity_iota::verification::jws::SignatureVerificationErrorKind;
Expand All @@ -10,12 +13,12 @@ use wasm_bindgen::prelude::*;
use crate::jose::WasmJwk;

/// Wrapper that enables custom TS JWS signature verification plugins to be used where the
/// JwsVerifier trait is required. Falls back to the default implementation if a custom
/// implementation was not passed.
pub(crate) struct WasmJwsVerifier(IJwsVerifier);
/// JwsVerifier trait is required. Falls back to the default implementation capable of handling
/// EdDSA (ED25519), ES256, ES256K if a custom implementation is not passed.
pub(crate) struct WasmJwsVerifier(Option<IJwsVerifier>);

impl WasmJwsVerifier {
pub(crate) fn new(verifier: IJwsVerifier) -> Self {
pub(crate) fn new(verifier: Option<IJwsVerifier>) -> Self {
Self(verifier)
}
}
Expand All @@ -26,22 +29,30 @@ impl JwsVerifier for WasmJwsVerifier {
input: identity_iota::verification::jws::VerificationInput,
public_key: &identity_iota::verification::jwk::Jwk,
) -> Result<(), identity_iota::verification::jws::SignatureVerificationError> {
let VerificationInput {
alg,
signing_input,
decoded_signature,
} = input;
let verification_result = IJwsVerifier::verify(
&self.0,
alg.name().to_owned(),
signing_input.into(),
decoded_signature.into(),
WasmJwk(public_key.to_owned()),
);
// Convert error
crate::error::stringify_js_error(verification_result).map_err(|error_string| {
SignatureVerificationError::new(SignatureVerificationErrorKind::Unspecified).with_custom_message(error_string)
})
if let Some(verifier) = &self.0 {
let VerificationInput {
alg,
signing_input,
decoded_signature,
} = input;
let verification_result = IJwsVerifier::verify(
verifier,
alg.name().to_owned(),
signing_input.into(),
decoded_signature.into(),
WasmJwk(public_key.to_owned()),
);
// Convert error
crate::error::stringify_js_error(verification_result).map_err(|error_string| {
SignatureVerificationError::new(SignatureVerificationErrorKind::Unspecified).with_custom_message(error_string)
})
} else {
match input.alg {
JwsAlgorithm::EdDSA => EdDSAJwsVerifier::default().verify(input, public_key),
JwsAlgorithm::ES256 | JwsAlgorithm::ES256K => EcDSAJwsVerifier::default().verify(input, public_key),
_ => Err(identity_iota::verification::jws::SignatureVerificationErrorKind::UnsupportedAlg.into()),
}
}
}
}
#[wasm_bindgen(typescript_custom_section)]
Expand Down
41 changes: 41 additions & 0 deletions bindings/wasm/src/verification/jws_verifier.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2020-2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use identity_ecdsa_verifier::EcDSAJwsVerifier;
use identity_eddsa_verifier::Ed25519Verifier;
use identity_eddsa_verifier::EdDSAJwsVerifier;
use identity_iota::verification::jws::JwsAlgorithm;
Expand Down Expand Up @@ -80,3 +81,43 @@ impl WasmEdDSAJwsVerifier {
EdDSAJwsVerifier::default().verify(input, &publicKey.0).wasm_result()
}
}

/// An implementor of `IJwsVerifier` that can handle the
/// `EcDSA` algorithm.
#[wasm_bindgen(js_name = EcDSAJwsVerifier)]
pub struct WasmEcDSAJwsVerifier();

#[wasm_bindgen(js_class = EcDSAJwsVerifier)]
#[allow(clippy::new_without_default)]
impl WasmEcDSAJwsVerifier {
/// Constructs an EcDSAJwsVerifier.
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self()
}

/// Verify a JWS signature secured with the `EcDSA` algorithm.
/// Only the `ES256` and `ES256K` curves are supported for now.
///
/// # Warning
///
/// This function does not check the `alg` property 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(),
};
EcDSAJwsVerifier::default().verify(input, &publicKey.0).wasm_result()
}
}
Loading