Skip to content

Commit

Permalink
Add wasm foundation (#363)
Browse files Browse the repository at this point in the history
  • Loading branch information
KendallWeihe authored Sep 25, 2024
1 parent a87c16a commit ebbdf34
Show file tree
Hide file tree
Showing 67 changed files with 7,736 additions and 406 deletions.
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,11 @@ Cargo.lock

# Do not put native binaries in source control
bound/kt/src/main/resources/*.dylib
bound/kt/src/main/resources/*.so
bound/kt/src/main/resources/*.so

bound/LICENSE
.vscode/
bound/typescript/dist/*
!bound/typescript/dist/index.js
bound/typescript/tests/compiled
bound/typescript/src/wasm/generated.js
15 changes: 8 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[workspace]
members = [
"crates/web5",
"crates/web5_cli",
"bindings/web5_uniffi",
"bindings/web5_uniffi_wrapper",
]
default-members = [
members = [
"bindings/web5_uniffi",
"bindings/web5_uniffi_wrapper",
"bindings/web5_wasm",
"crates/http-std",
"crates/web5",
"crates/web5_cli",
]
default-members = ["crates/web5"]
resolver = "2"

[workspace.package]
Expand All @@ -18,6 +18,7 @@ license-file = "LICENSE"
[workspace.dependencies]
base64 = "0.22.0"
chrono = { version = "0.4.37", features = ["std"] }
lazy_static = "1.5.0"
thiserror = "1.0.50"
rand = "0.8.5"
serde = { version = "1.0.193", features = ["derive"] }
Expand Down
8 changes: 7 additions & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ setup:
git submodule update --init --recursive
fi
if [[ "$(cargo 2>&1)" == *"rustup could not choose a version of cargo to run"* ]]; then
rustup default 1.74.0
rustup default 1.76.0 # TODO undo this
rustup target add aarch64-apple-darwin
fi
if ! command -v wasm-pack >/dev/null || [[ "$(wasm-pack --version)" != "wasm-pack 0.13.0" ]]; then
cargo install wasm-pack --version 0.13.0
fi

docs: setup
cargo doc --open --no-deps
Expand Down Expand Up @@ -46,3 +49,6 @@ test-bound: setup

test-kotlin: setup
cd bound/kt && mvn clean test

wasm: setup
(cd bindings/web5_wasm; wasm-pack build --target nodejs --out-dir ../../bound/typescript/pkg)
File renamed without changes.
2 changes: 1 addition & 1 deletion bin/corepack
2 changes: 1 addition & 1 deletion bin/node
2 changes: 1 addition & 1 deletion bin/npm
2 changes: 1 addition & 1 deletion bin/npx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ WORKDIR /usr/src/myapp
COPY Cargo.toml ./
COPY bindings/web5_uniffi_wrapper ./bindings/web5_uniffi_wrapper
COPY bindings/web5_uniffi ./bindings/web5_uniffi
COPY bindings/web5_wasm ./bindings/web5_wasm
COPY crates/http-std ./crates/http-std
COPY crates/web5 ./crates/web5
COPY crates/web5_cli ./crates/web5_cli

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,30 @@ RUN apk add --no-cache \
git \
perl \
make \
bash
bash \
openssl-dev \
openssl-libs-static # TODO remove above two once we remove reqwest

# Install rust
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | bash -s -- -y --no-modify-path --default-toolchain 1.74.0
ENV PATH="/root/.cargo/bin:${PATH}"

# TODO remove once we remove reqwest
# Set environment variables to ensure vendored OpenSSL is used
ENV OPENSSL_STATIC=1
ENV OPENSSL_LIB_DIR=/usr/lib
ENV OPENSSL_INCLUDE_DIR=/usr/include
ENV PKG_CONFIG_ALLOW_CROSS=1
ENV PKG_CONFIG_PATH=/usr/lib/pkgconfig
ENV LIBRARY_PATH="/usr/lib:/usr/local/lib"

# Copy the source code to the container
WORKDIR /usr/src/myapp
COPY Cargo.toml ./
COPY bindings/web5_uniffi_wrapper ./bindings/web5_uniffi_wrapper
COPY bindings/web5_uniffi ./bindings/web5_uniffi
COPY bindings/web5_wasm ./bindings/web5_wasm
COPY crates/http-std ./crates/http-std
COPY crates/web5 ./crates/web5
COPY crates/web5_cli ./crates/web5_cli

Expand All @@ -34,7 +47,8 @@ RUN cargo build --release --package web5_uniffi
# Compile as a dynamic lib (.so) from our static lib (.a) while keeping dependencies self-contained
RUN gcc -shared -o target/release/libweb5_uniffi.so -Wl,--whole-archive \
target/release/libweb5_uniffi.a -Wl,--no-whole-archive -static-libgcc \
-Wl,-Bdynamic -fPIC
-L/usr/lib -lssl -lcrypto -Wl,-Bdynamic -fPIC
# -Wl,-Bdynamic -fPIC # TODO replace above line with this line one we remove reqwest

# Set the entrypoint, so that we can `docker cp` the build output
CMD tail -f /dev/null
16 changes: 16 additions & 0 deletions bindings/web5_wasm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "web5_wasm"
version = "0.1.0"
edition = "2021"
homepage.workspace = true
repository.workspace = true
license-file.workspace = true

[dependencies]
serde = { workspace = true }
serde-wasm-bindgen = "0.6.5"
wasm-bindgen = "0.2.93"
web5 = { path = "../../crates/web5" }

[lib]
crate-type = ["cdylib"]
63 changes: 63 additions & 0 deletions bindings/web5_wasm/src/crypto/dsa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use super::jwk::WasmJwk;
use crate::errors::{map_err, Result};
use std::sync::Arc;
use wasm_bindgen::prelude::wasm_bindgen;
use web5::crypto::dsa::{
ed25519::{Ed25519Generator, Ed25519Signer},
secp256k1::{Secp256k1Generator, Secp256k1Signer},
Signer,
};

#[wasm_bindgen]
pub struct WasmSigner {
inner: Arc<dyn Signer>,
}

impl From<Ed25519Signer> for WasmSigner {
fn from(value: Ed25519Signer) -> Self {
Self {
inner: Arc::new(value),
}
}
}

impl From<Secp256k1Signer> for WasmSigner {
fn from(value: Secp256k1Signer) -> Self {
Self {
inner: Arc::new(value),
}
}
}

impl From<Arc<dyn Signer>> for WasmSigner {
fn from(value: Arc<dyn Signer>) -> Self {
Self { inner: value }
}
}

#[wasm_bindgen]
impl WasmSigner {
pub fn sign(&self, payload: &[u8]) -> Result<Vec<u8>> {
self.inner.sign(payload).map_err(map_err)
}
}

#[wasm_bindgen]
pub fn generate_ed25519_key() -> Result<WasmJwk> {
Ok(Ed25519Generator::generate().into())
}

#[wasm_bindgen]
pub fn generate_secp256k1_key() -> Result<WasmJwk> {
Ok(Secp256k1Generator::generate().into())
}

#[wasm_bindgen]
pub fn new_ed25519_signer(jwk: WasmJwk) -> Result<WasmSigner> {
Ok(Ed25519Signer::new(jwk.into()).into())
}

#[wasm_bindgen]
pub fn new_secp256k1_signer(jwk: WasmJwk) -> Result<WasmSigner> {
Ok(Secp256k1Signer::new(jwk.into()).into())
}
79 changes: 79 additions & 0 deletions bindings/web5_wasm/src/crypto/jwk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use crate::errors::{map_err, Result};
use wasm_bindgen::prelude::wasm_bindgen;
use web5::crypto::jwk::Jwk;

#[wasm_bindgen]
pub struct WasmJwk {
inner: Jwk,
}

impl From<Jwk> for WasmJwk {
fn from(value: Jwk) -> Self {
Self { inner: value }
}
}

impl From<WasmJwk> for Jwk {
fn from(value: WasmJwk) -> Self {
value.inner
}
}

#[wasm_bindgen]
impl WasmJwk {
#[wasm_bindgen(constructor)]
pub fn new(
alg: Option<String>,
kty: String,
crv: String,
d: Option<String>,
x: String,
y: Option<String>,
) -> WasmJwk {
WasmJwk {
inner: Jwk {
alg,
kty,
crv,
d,
x,
y,
},
}
}

#[wasm_bindgen]
pub fn compute_thumbprint(&self) -> Result<String> {
self.inner.compute_thumbprint().map_err(map_err)
}

#[wasm_bindgen(getter)]
pub fn alg(&self) -> Option<String> {
self.inner.alg.clone()
}

#[wasm_bindgen(getter)]
pub fn kty(&self) -> String {
self.inner.kty.clone()
}

#[wasm_bindgen(getter)]
pub fn crv(&self) -> String {
self.inner.crv.clone()
}

#[wasm_bindgen(getter)]
pub fn d(&self) -> Option<String> {
self.inner.d.clone()
}

#[wasm_bindgen(getter)]
pub fn x(&self) -> String {
self.inner.x.clone()
}

#[wasm_bindgen(getter)]
pub fn y(&self) -> Option<String> {
self.inner.y.clone()
}
}
74 changes: 74 additions & 0 deletions bindings/web5_wasm/src/crypto/key_managers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use super::{dsa::WasmSigner, jwk::WasmJwk};
use crate::errors::{map_err, Result};
use std::sync::Arc;
use wasm_bindgen::prelude::wasm_bindgen;
use web5::crypto::{
jwk::Jwk,
key_managers::{in_memory_key_manager::InMemoryKeyManager, KeyManager},
};

#[wasm_bindgen]
pub struct WasmKeyManager {
inner: Arc<dyn KeyManager>,
}

impl From<InMemoryKeyManager> for WasmKeyManager {
fn from(value: InMemoryKeyManager) -> Self {
Self {
inner: Arc::new(value),
}
}
}

#[wasm_bindgen]
impl WasmKeyManager {
pub fn import_private_jwk(&self, private_jwk: WasmJwk) -> Result<WasmJwk> {
Ok(self
.inner
.import_private_jwk(private_jwk.into())
.map_err(map_err)?
.into())
}

pub fn get_signer(&self, public_jwk: WasmJwk) -> Result<WasmSigner> {
Ok(self
.inner
.get_signer(public_jwk.into())
.map_err(map_err)?
.into())
}
}

#[wasm_bindgen]
pub fn new_in_memory_key_manager() -> Result<WasmKeyManager> {
Ok(InMemoryKeyManager::new().into())
}

#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(
typescript_type = "{ importPrivateJwk: (privateJwk: WasmJwk) => WasmJwk, getSigner: (publicJwk: WasmJwk) => WasmSigner }"
)]
pub type ForeignKeyManager;

#[wasm_bindgen(method)]
fn import_private_jwk(this: &ForeignKeyManager, private_jwk: WasmJwk) -> WasmJwk;

#[wasm_bindgen(method)]
fn get_signer(this: &ForeignKeyManager, public_jwk: WasmJwk) -> WasmSigner;
}

#[wasm_bindgen]
pub fn poc_key_manager_from_foreign(key_manager: &ForeignKeyManager) -> WasmSigner {
let private_jwk = Jwk {
alg: Some("Ed25519".to_string()),
kty: "OKP".to_string(),
crv: "Ed25519".to_string(),
d: Some("UMxzGsW84I6kS3JkenqYI1gH0GmvxYG2ovI69Vlno8g".to_string()),
x: "EzbXpICojY4ZI2i775GwkkTIbe5nuLL13JbdzUfsO6Q".to_string(),
y: None,
};

let public_jwk = key_manager.import_private_jwk(private_jwk.into());
key_manager.get_signer(public_jwk)
}
3 changes: 3 additions & 0 deletions bindings/web5_wasm/src/crypto/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod dsa;
pub mod jwk;
pub mod key_managers;
Loading

0 comments on commit ebbdf34

Please sign in to comment.