Skip to content

Commit ba36609

Browse files
authored
Add WASM bindings for EcDSA JWS Verifier (#1396)
* wasm bindings for ecdsa verifier * make signature verifier optional, defaulting to a compound verifier * update comments on new default wasm jws verifier * cargo fmt * dprint fmt
1 parent deecc7e commit ba36609

File tree

10 files changed

+260
-159
lines changed

10 files changed

+260
-159
lines changed

bindings/wasm/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ async-trait = { version = "0.1", default-features = false }
2020
bls12_381_plus = "0.8.17"
2121
console_error_panic_hook = { version = "0.1" }
2222
futures = { version = "0.3" }
23+
identity_ecdsa_verifier = { path = "../../identity_ecdsa_verifier", default-features = false, features = ["es256", "es256k"] }
2324
identity_eddsa_verifier = { path = "../../identity_eddsa_verifier", default-features = false, features = ["ed25519"] }
2425
js-sys = { version = "0.3.61" }
2526
json-proof-token = "0.3.4"

bindings/wasm/docs/api-reference.md

+167-121
Large diffs are not rendered by default.

bindings/wasm/src/credential/domain_linkage_validator.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ pub struct WasmJwtDomainLinkageValidator {
2424
#[wasm_bindgen(js_class = JwtDomainLinkageValidator)]
2525
impl WasmJwtDomainLinkageValidator {
2626
/// Creates a new {@link JwtDomainLinkageValidator}. If a `signatureVerifier` is provided it will be used when
27-
/// verifying decoded JWS signatures, otherwise the default which is only capable of handling the `EdDSA`
28-
/// algorithm will be used.
27+
/// verifying decoded JWS signatures, otherwise a default verifier capable of handling the `EdDSA`, `ES256`, `ES256K`
28+
/// algorithms will be used.
2929
#[wasm_bindgen(constructor)]
3030
#[allow(non_snake_case)]
31-
pub fn new(signatureVerifier: IJwsVerifier) -> WasmJwtDomainLinkageValidator {
31+
pub fn new(signatureVerifier: Option<IJwsVerifier>) -> WasmJwtDomainLinkageValidator {
3232
let signature_verifier = WasmJwsVerifier::new(signatureVerifier);
3333
WasmJwtDomainLinkageValidator {
3434
validator: JwtDomainLinkageValidator::with_signature_verifier(signature_verifier),

bindings/wasm/src/credential/jwt_credential_validation/jwt_credential_validator.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ pub struct WasmJwtCredentialValidator(JwtCredentialValidator<WasmJwsVerifier>);
3737
#[wasm_bindgen(js_class = JwtCredentialValidator)]
3838
impl WasmJwtCredentialValidator {
3939
/// Creates a new {@link JwtCredentialValidator}. If a `signatureVerifier` is provided it will be used when
40-
/// verifying decoded JWS signatures, otherwise the default which is only capable of handling the `EdDSA`
41-
/// algorithm will be used.
40+
/// verifying decoded JWS signatures, otherwise a default verifier capable of handling the `EdDSA`, `ES256`, `ES256K`
41+
/// algorithms will be used.
4242
#[wasm_bindgen(constructor)]
4343
#[allow(non_snake_case)]
44-
pub fn new(signatureVerifier: IJwsVerifier) -> WasmJwtCredentialValidator {
44+
pub fn new(signatureVerifier: Option<IJwsVerifier>) -> WasmJwtCredentialValidator {
4545
let signature_verifier = WasmJwsVerifier::new(signatureVerifier);
4646
WasmJwtCredentialValidator(JwtCredentialValidator::with_signature_verifier(signature_verifier))
4747
}

bindings/wasm/src/credential/jwt_credential_validation/sd_jwt_validator.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ pub struct WasmSdJwtCredentialValidator(SdJwtCredentialValidator<WasmJwsVerifier
2929
#[wasm_bindgen(js_class = SdJwtCredentialValidator)]
3030
impl WasmSdJwtCredentialValidator {
3131
/// Creates a new `SdJwtCredentialValidator`. If a `signatureVerifier` is provided it will be used when
32-
/// verifying decoded JWS signatures, otherwise the default which is only capable of handling the `EdDSA`
33-
/// algorithm will be used.
32+
/// verifying decoded JWS signatures, otherwise a default verifier capable of handling the `EdDSA`, `ES256`, `ES256K`
33+
/// algorithms will be used.
3434
#[wasm_bindgen(constructor)]
3535
#[allow(non_snake_case)]
36-
pub fn new(signatureVerifier: IJwsVerifier) -> WasmSdJwtCredentialValidator {
36+
pub fn new(signatureVerifier: Option<IJwsVerifier>) -> WasmSdJwtCredentialValidator {
3737
let signature_verifier = WasmJwsVerifier::new(signatureVerifier);
3838
WasmSdJwtCredentialValidator(SdJwtCredentialValidator::with_signature_verifier(
3939
signature_verifier,

bindings/wasm/src/credential/jwt_presentation_validation/jwt_presentation_validator.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ pub struct WasmJwtPresentationValidator(JwtPresentationValidator<WasmJwsVerifier
2323
#[wasm_bindgen(js_class = JwtPresentationValidator)]
2424
impl WasmJwtPresentationValidator {
2525
/// Creates a new {@link JwtPresentationValidator}. If a `signatureVerifier` is provided it will be used when
26-
/// verifying decoded JWS signatures, otherwise the default which is only capable of handling the `EdDSA`
27-
/// algorithm will be used.
26+
/// verifying decoded JWS signatures, otherwise a default verifier capable of handling the `EdDSA`, `ES256`, `ES256K`
27+
/// algorithms will be used.
2828
#[wasm_bindgen(constructor)]
2929
#[allow(non_snake_case)]
30-
pub fn new(signatureVerifier: IJwsVerifier) -> WasmJwtPresentationValidator {
30+
pub fn new(signatureVerifier: Option<IJwsVerifier>) -> WasmJwtPresentationValidator {
3131
let signature_verifier = WasmJwsVerifier::new(signatureVerifier);
3232
WasmJwtPresentationValidator(JwtPresentationValidator::with_signature_verifier(signature_verifier))
3333
}

bindings/wasm/src/did/wasm_core_document.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -495,8 +495,9 @@ impl WasmCoreDocument {
495495
// ===========================================================================
496496

497497
/// Decodes and verifies the provided JWS according to the passed `options` and `signatureVerifier`.
498-
/// If no `signatureVerifier` argument is provided a default verifier will be used that is (only) capable of
499-
/// verifying EdDSA signatures.
498+
/// If a `signatureVerifier` is provided it will be used when
499+
/// verifying decoded JWS signatures, otherwise a default verifier capable of handling the `EdDSA`, `ES256`, `ES256K`
500+
/// algorithms will be used.
500501
///
501502
/// Regardless of which options are passed the following conditions must be met in order for a verification attempt to
502503
/// take place.
@@ -509,7 +510,7 @@ impl WasmCoreDocument {
509510
&self,
510511
jws: &WasmJws,
511512
options: &WasmJwsVerificationOptions,
512-
signatureVerifier: IJwsVerifier,
513+
signatureVerifier: Option<IJwsVerifier>,
513514
detachedPayload: Option<String>,
514515
) -> Result<WasmDecodedJws> {
515516
let jws_verifier = WasmJwsVerifier::new(signatureVerifier);

bindings/wasm/src/iota/iota_document.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,9 @@ impl WasmIotaDocument {
384384
// ===========================================================================
385385

386386
/// Decodes and verifies the provided JWS according to the passed `options` and `signatureVerifier`.
387-
/// If no `signatureVerifier` argument is provided a default verifier will be used that is (only) capable of
388-
/// verifying EdDSA signatures.
387+
/// If a `signatureVerifier` is provided it will be used when
388+
/// verifying decoded JWS signatures, otherwise a default verifier capable of handling the `EdDSA`, `ES256`, `ES256K`
389+
/// algorithms will be used.
389390
///
390391
/// Regardless of which options are passed the following conditions must be met in order for a verification attempt to
391392
/// take place.
@@ -397,7 +398,7 @@ impl WasmIotaDocument {
397398
&self,
398399
jws: &WasmJws,
399400
options: &WasmJwsVerificationOptions,
400-
signatureVerifier: IJwsVerifier,
401+
signatureVerifier: Option<IJwsVerifier>,
401402
detachedPayload: Option<String>,
402403
) -> Result<WasmDecodedJws> {
403404
let jws_verifier = WasmJwsVerifier::new(signatureVerifier);

bindings/wasm/src/verification/custom_verification.rs

+31-20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Copyright 2020-2023 IOTA Stiftung
22
// SPDX-License-Identifier: Apache-2.0
33

4+
use identity_ecdsa_verifier::EcDSAJwsVerifier;
5+
use identity_eddsa_verifier::EdDSAJwsVerifier;
6+
use identity_iota::verification::jws::JwsAlgorithm;
47
use identity_iota::verification::jws::JwsVerifier;
58
use identity_iota::verification::jws::SignatureVerificationError;
69
use identity_iota::verification::jws::SignatureVerificationErrorKind;
@@ -10,12 +13,12 @@ use wasm_bindgen::prelude::*;
1013
use crate::jose::WasmJwk;
1114

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

1720
impl WasmJwsVerifier {
18-
pub(crate) fn new(verifier: IJwsVerifier) -> Self {
21+
pub(crate) fn new(verifier: Option<IJwsVerifier>) -> Self {
1922
Self(verifier)
2023
}
2124
}
@@ -26,22 +29,30 @@ impl JwsVerifier for WasmJwsVerifier {
2629
input: identity_iota::verification::jws::VerificationInput,
2730
public_key: &identity_iota::verification::jwk::Jwk,
2831
) -> Result<(), identity_iota::verification::jws::SignatureVerificationError> {
29-
let VerificationInput {
30-
alg,
31-
signing_input,
32-
decoded_signature,
33-
} = input;
34-
let verification_result = IJwsVerifier::verify(
35-
&self.0,
36-
alg.name().to_owned(),
37-
signing_input.into(),
38-
decoded_signature.into(),
39-
WasmJwk(public_key.to_owned()),
40-
);
41-
// Convert error
42-
crate::error::stringify_js_error(verification_result).map_err(|error_string| {
43-
SignatureVerificationError::new(SignatureVerificationErrorKind::Unspecified).with_custom_message(error_string)
44-
})
32+
if let Some(verifier) = &self.0 {
33+
let VerificationInput {
34+
alg,
35+
signing_input,
36+
decoded_signature,
37+
} = input;
38+
let verification_result = IJwsVerifier::verify(
39+
verifier,
40+
alg.name().to_owned(),
41+
signing_input.into(),
42+
decoded_signature.into(),
43+
WasmJwk(public_key.to_owned()),
44+
);
45+
// Convert error
46+
crate::error::stringify_js_error(verification_result).map_err(|error_string| {
47+
SignatureVerificationError::new(SignatureVerificationErrorKind::Unspecified).with_custom_message(error_string)
48+
})
49+
} else {
50+
match input.alg {
51+
JwsAlgorithm::EdDSA => EdDSAJwsVerifier::default().verify(input, public_key),
52+
JwsAlgorithm::ES256 | JwsAlgorithm::ES256K => EcDSAJwsVerifier::default().verify(input, public_key),
53+
_ => Err(identity_iota::verification::jws::SignatureVerificationErrorKind::UnsupportedAlg.into()),
54+
}
55+
}
4556
}
4657
}
4758
#[wasm_bindgen(typescript_custom_section)]

bindings/wasm/src/verification/jws_verifier.rs

+41
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright 2020-2023 IOTA Stiftung
22
// SPDX-License-Identifier: Apache-2.0
33

4+
use identity_ecdsa_verifier::EcDSAJwsVerifier;
45
use identity_eddsa_verifier::Ed25519Verifier;
56
use identity_eddsa_verifier::EdDSAJwsVerifier;
67
use identity_iota::verification::jws::JwsAlgorithm;
@@ -80,3 +81,43 @@ impl WasmEdDSAJwsVerifier {
8081
EdDSAJwsVerifier::default().verify(input, &publicKey.0).wasm_result()
8182
}
8283
}
84+
85+
/// An implementor of `IJwsVerifier` that can handle the
86+
/// `EcDSA` algorithm.
87+
#[wasm_bindgen(js_name = EcDSAJwsVerifier)]
88+
pub struct WasmEcDSAJwsVerifier();
89+
90+
#[wasm_bindgen(js_class = EcDSAJwsVerifier)]
91+
#[allow(clippy::new_without_default)]
92+
impl WasmEcDSAJwsVerifier {
93+
/// Constructs an EcDSAJwsVerifier.
94+
#[wasm_bindgen(constructor)]
95+
pub fn new() -> Self {
96+
Self()
97+
}
98+
99+
/// Verify a JWS signature secured with the `EcDSA` algorithm.
100+
/// Only the `ES256` and `ES256K` curves are supported for now.
101+
///
102+
/// # Warning
103+
///
104+
/// This function does not check the `alg` property in the protected header. Callers are expected to assert this
105+
/// prior to calling the function.
106+
#[wasm_bindgen]
107+
#[allow(non_snake_case)]
108+
pub fn verify(
109+
&self,
110+
alg: WasmJwsAlgorithm,
111+
signingInput: &[u8],
112+
decodedSignature: &[u8],
113+
publicKey: &WasmJwk,
114+
) -> Result<(), JsValue> {
115+
let alg: JwsAlgorithm = JwsAlgorithm::try_from(alg)?;
116+
let input: VerificationInput = VerificationInput {
117+
alg,
118+
signing_input: signingInput.into(),
119+
decoded_signature: decodedSignature.into(),
120+
};
121+
EcDSAJwsVerifier::default().verify(input, &publicKey.0).wasm_result()
122+
}
123+
}

0 commit comments

Comments
 (0)