I'm trying to write a WASM module which uses ECDSA in part of its communication protocol with a backend server. I was hoping to use ring for this.
Various functions from ring::signature e.g. verify, result in the line
import * as __wbg_star0 from 'env';
being generated in the .js file generated by wasm-bindgen. This prevents my browser loading the WASM module and it gives the message:
Error resolving module specifier: env
Is ECDSA sign/verify supported for WASM. If so, how do I use it?
The port's incomplete, and possibly in need of a sponsor. If you search through the issues (including closed) you can get a better idea of where it's at.
(I'm not associated with this project, I'm just replying as the maintainer appears to be otherwise engaged at the minute.)
In which wasm platform are you trying to use ring? Browser? Node? Wasmer? Something else?
For another datapoint, I would also love to use _ring_ with WebAssembly, and am targeting the browser. In particular, I'm trying to use rustls, which depends on _ring_.
@briansmith I tried using it for the browser, but indeed get the same error. It seems the C files aren't implemented yet in wasm?

I have too little knowledge about ring to actually say what's going on. Any advice?
I intend to add more functionality to wasm builds soon.
A few months ago, when I started the port, I couldn't get the wasm32 toolchain to handle some of the ECC code. It seems like the wasm32 backend of clang wasn't quite up to snuff, and/or there was a problem with the wasm32 "linker". I wouldn't be surprised if things are better now.
Before I can add more stuff here, I need to merge the latest BoringSSL changes. I'm doing that now.
I wish I could help with that, but I'm afraid it's outside of my expertise. Anything else I can look into to help?
As I mentioned above, it would be useful to know which ring features are needed in wasm32. I probably won't have them all working all at once, but one at a time, so it would be useful to prioritize the work.
I've had success compiling the LIMBS subdirectory to wasm using emscripten. Currently the functionality I at least _know_ I require is montgomery multiplication. If I understand correctly, the port involves writing this assembly code in either C or Rust, correct?
As far as I see it's mainly the LIMBS and GFp for my usecase (using rustls).
I understand that when people talk about "limbs" they mean the underlying bigint math. But, which features (ECDSA signing, ECDSA verification, RSA signing, RSA verification, ECDH, etc.) do you need? Just because something compiles and the tests seem to pass, doesn't mean that it is correct, especially w.r.t. the side-channel mitigations.
It's also useful to know which wasm32 environment people are deploying to. Just browsers? Node? Something else?
Hey Brian, I'm mainly interested in compiling and running the QUIC protocol in both the browser and node environments. I'm planning to leverage existing QUIC implementations written in Rust. I don't know the full list of cryptographic functions QUIC uses, but if you aren't sure either, I'm happy to compile one. Thanks so much.
I'm deploying to Cloudflare Workers, which should be the same as browser (Chromium).
I need RSA verification, I've implemented a full FIDO2 authentication into workers, but it's too slow with the Javascript implementation, so was looking into Rust -> WASM for that reason.
Ring seems to be the only mature library for this.
UPDATE: Specifically, ECDSA with SHA-256 (ES256) and RSASSA-PKCS1-v1_5 with SHA-256 (RS256) at minimum.
I'll add my use case as well then, I'm porting a ECDH Triple Diffie–Hellman key exchange to wasm - targeting the browser. I'm using it (implemented in JS atm) for end-to-end encrypted WebRTC signalling.
My plan is to do AES-GCM first, then RSA, and then make a plan for the rest. Issue #104 is tracking the AES-GCM effort.
PR #992 is the next step in expanding the wasm32 functionality. It it the first step to allowing ring to use C code in WebAssembly. Going forward I intend to make it unnecessary to have a C compiler for targeting wasm32; this is a stepping stone to that.
I expect that somebody could now enable Ed25519 and X25519 support on top of PR #992, guarded by the wasm32_c feature and tested in the same way as demonstrated for HMAC and PBKDF2 in PR #992.
RSA signatures (signing and verifying) is in PR #996.
Since #992 and #996 have been done it's closer to potentially work in WASM, however it's quite hard to test the current progress for Webassembly.
For example, I'm trying to use Keats/jsonwebtoken that depends on Ring and even with the latest changes, I have the following symbols that are problematic:
(import "env" "GFp_nistz256_sqr_mont" (func $env.GFp_nistz256_sqr_mont (type $t1)))
(import "env" "GFp_nistz256_point_mul_base" (func $env.GFp_nistz256_point_mul_base (type $t1)))
(import "env" "GFp_p256_scalar_mul_mont" (func $env.GFp_p256_scalar_mul_mont (type $t0)))
(import "env" "GFp_p256_scalar_sqr_mont" (func $env.GFp_p256_scalar_sqr_mont (type $t1)))
(import "env" "GFp_p256_scalar_sqr_rep_mont" (func $env.GFp_p256_scalar_sqr_rep_mont (type $t0)))
(import "env" "GFp_p384_elem_mul_mont" (func $env.GFp_p384_elem_mul_mont (type $t0)))
(import "env" "GFp_nistz384_point_mul" (func $env.GFp_nistz384_point_mul (type $t4)))
(import "env" "GFp_p384_scalar_mul_mont" (func $env.GFp_p384_scalar_mul_mont (type $t0)))
(import "env" "GFp_nistz256_add" (func $env.GFp_nistz256_add (type $t0)))
(import "env" "GFp_nistz256_mul_mont" (func $env.GFp_nistz256_mul_mont (type $t0)))
(import "env" "GFp_nistz256_point_add" (func $env.GFp_nistz256_point_add (type $t0)))
(import "env" "GFp_nistz256_point_mul" (func $env.GFp_nistz256_point_mul (type $t4)))
(import "env" "GFp_p384_elem_add" (func $env.GFp_p384_elem_add (type $t0)))
(import "env" "GFp_nistz384_point_add" (func $env.GFp_nistz384_point_add (type $t0)))
Is there a way to disable EC at compile-time from Ring so that I can test the JWT RSA code path within WebAssembly?
My plan is to implement those functions in a way that works on all targets, including WebAssembly. I don't intend to spend effort to work around the lack of the functions in the interim because I don't have time to do one and then the other.
(import "env" "GFp_nistz256_point_mul_base" (func $env.GFp_nistz256_point_mul_base (type $t1))) (import "env" "GFp_p256_scalar_mul_mont" (func $env.GFp_p256_scalar_mul_mont (type $t0))) (import "env" "GFp_p256_scalar_sqr_mont" (func $env.GFp_p256_scalar_sqr_mont (type $t1)))
What tools were you using to get this output? I usually target browsers for webassembly and I don't get nearly as useful error messages; I just get some vague error about "env" that doesn't list the missing symbols.
Thanks that makes sense.
To get that output I used wasm2wat which can also be used online here https://webassembly.github.io/wabt/demo/wasm2wat/.
The trace was obtained by running wasm2wat on the result of wasm-pack build on my code.
For reference, here's how to go about testing whether a particular module works in wasm via wasm-bindgen-test. I have particular interest in the key agreement module, so here's a patch that enables a single, failing browser test.
From 8cddf22bc0b2d9227dc65e8075118534071a81ec Mon Sep 17 00:00:00 2001
From: Anthony Miyaguchi <[email protected]>
Date: Fri, 22 Jan 2021 16:20:30 -0800
Subject: [PATCH] Enable failing agreement tests (dependent on ecdsa)
---
build.rs | 9 +++++----
tests/agreement_tests.rs | 8 ++++++++
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/build.rs b/build.rs
index f2db9a841..5517dcded 100644
--- a/build.rs
+++ b/build.rs
@@ -45,10 +45,11 @@ const RING_SRCS: &[(&[&str], &str)] = &[
(&[AARCH64, ARM, X86_64, X86], "crypto/crypto.c"),
(&[AARCH64, ARM, X86_64, X86], "crypto/curve25519/curve25519.c"),
- (&[AARCH64, ARM, X86_64, X86], "crypto/fipsmodule/ec/ecp_nistz.c"),
- (&[AARCH64, ARM, X86_64, X86], "crypto/fipsmodule/ec/ecp_nistz256.c"),
- (&[AARCH64, ARM, X86_64, X86], "crypto/fipsmodule/ec/gfp_p256.c"),
- (&[AARCH64, ARM, X86_64, X86], "crypto/fipsmodule/ec/gfp_p384.c"),
+ (&[], "crypto/curve25519/curve25519.c"),
+ (&[], "crypto/fipsmodule/ec/ecp_nistz.c"),
+ (&[], "crypto/fipsmodule/ec/ecp_nistz256.c"),
+ (&[], "crypto/fipsmodule/ec/gfp_p256.c"),
+ (&[], "crypto/fipsmodule/ec/gfp_p384.c"),
(&[X86_64, X86], "crypto/cpu-intel.c"),
diff --git a/tests/agreement_tests.rs b/tests/agreement_tests.rs
index 416201537..ae7891802 100644
--- a/tests/agreement_tests.rs
+++ b/tests/agreement_tests.rs
@@ -16,7 +16,15 @@ extern crate alloc;
use ring::{agreement, error, rand, test, test_file};
+#[cfg(target_arch = "wasm32")]
+use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
+
+#[cfg(target_arch = "wasm32")]
+wasm_bindgen_test_configure!(run_in_browser);
+
#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+#[cfg(any(not(target_arch = "wasm32"), feature = "wasm32_c"))]
fn agreement_traits() {
use alloc::vec::Vec;
--
2.30.0
Run the tests using wasm-pack with the feature gate enabled for the C modules.
% wasm-pack test --firefox --headless -- --features wasm32_c --test agreement_tests
This fails with the following error:
JavaScript error: http://127.0.0.1:50603/wasm-bindgen-test, line 1: TypeError: Error resolving module specifier “env”. Relative module specifiers must start with “./”, “../” or “/”.
Find the path to the built wasm module from the test output and inspect the imports, which match with most of the other comments here:
% wasm2wat target/wasm32-unknown-unknown/debug/deps/agreement_tests-0e45a4224d6f1446.wasm | grep 'import "env"'
(import "env" "GFp_nistz256_add" (func $GFp_nistz256_add (type 6)))
(import "env" "GFp_nistz256_mul_mont" (func $GFp_nistz256_mul_mont (type 6)))
(import "env" "GFp_nistz256_sqr_mont" (func $GFp_nistz256_sqr_mont (type 5)))
(import "env" "GFp_nistz256_point_double" (func $GFp_nistz256_point_double (type 5)))
(import "env" "GFp_nistz256_neg" (func $GFp_nistz256_neg (type 5)))
(import "env" "GFp_bn_mul_mont" (func $GFp_bn_mul_mont (type 19)))
These files only exist in generated assembly, so a cross-architecture implementation of these functions would solve most of the issues here.
Most helpful comment
I intend to add more functionality to wasm builds soon.
A few months ago, when I started the port, I couldn't get the wasm32 toolchain to handle some of the ECC code. It seems like the wasm32 backend of clang wasn't quite up to snuff, and/or there was a problem with the wasm32 "linker". I wouldn't be surprised if things are better now.
Before I can add more stuff here, I need to merge the latest BoringSSL changes. I'm doing that now.