diff --git a/nix/dev-shell.nix b/nix/dev-shell.nix --- a/nix/dev-shell.nix +++ b/nix/dev-shell.nix @@ -40,6 +40,7 @@ , sqlite , terraform , rustfmt +, wasm-pack , yarn }: @@ -62,6 +63,7 @@ yarn python3 redis + wasm-pack # native dependencies # C/CXX toolchains are already brought in with mkShell diff --git a/shared/comm-opaque2/.gitignore b/shared/comm-opaque2/.gitignore --- a/shared/comm-opaque2/.gitignore +++ b/shared/comm-opaque2/.gitignore @@ -1 +1,2 @@ target +pkg diff --git a/shared/comm-opaque2/Cargo.lock b/shared/comm-opaque2/Cargo.lock --- a/shared/comm-opaque2/Cargo.lock +++ b/shared/comm-opaque2/Cargo.lock @@ -77,6 +77,12 @@ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + [[package]] name = "byteorder" version = "1.4.3" @@ -100,10 +106,23 @@ version = "0.1.0" dependencies = [ "argon2", + "getrandom", "log", "opaque-ke", "rand", "tonic", + "wasm-bindgen", + "wasm-bindgen-test", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", ] [[package]] @@ -290,8 +309,10 @@ checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -351,6 +372,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.140" @@ -497,6 +527,12 @@ "getrandom", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "sec1" version = "0.3.0" @@ -721,6 +757,106 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db36fc0f9fb209e88fb3642590ae0205bb5a56216dabd963ba15879fe53a30b" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0734759ae6b3b1717d661fe4f016efcfb9828f5edb4520c18eaee05af3b43be9" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "windows-sys" version = "0.45.0" diff --git a/shared/comm-opaque2/Cargo.toml b/shared/comm-opaque2/Cargo.toml --- a/shared/comm-opaque2/Cargo.toml +++ b/shared/comm-opaque2/Cargo.toml @@ -8,7 +8,17 @@ [dependencies] argon2 = "0.4" +getrandom = { version = "0.2", features = [ "js", "wasm-bindgen" ] } log = "0.4" opaque-ke = { version = "2.0", features = [ "argon2" ] } rand = "0.8" tonic = { version = "0.8", default-features = false } +wasm-bindgen = "0.2" + +[dev-dependencies] +wasm-bindgen-test = "0.3" + +[profile.release] +# Optimise for small code size +opt-level = "s" +strip = "debuginfo" diff --git a/shared/comm-opaque2/src/client/login.rs b/shared/comm-opaque2/src/client/login.rs --- a/shared/comm-opaque2/src/client/login.rs +++ b/shared/comm-opaque2/src/client/login.rs @@ -3,18 +3,22 @@ CredentialResponse, }; use rand::rngs::OsRng; +use wasm_bindgen::prelude::wasm_bindgen; -use crate::Cipher; +use crate::{error::OpaqueError, Cipher}; +#[wasm_bindgen] pub struct Login { state: Option>, password: Option, rng: OsRng, export_key: Option>, - pub session_key: Option>, + session_key: Option>, } +#[wasm_bindgen] impl Login { + #[wasm_bindgen(constructor)] pub fn new() -> Login { Login { state: None, @@ -25,7 +29,8 @@ } } - pub fn start(&mut self, password: &str) -> Result, ProtocolError> { + #[wasm_bindgen] + pub fn start(&mut self, password: &str) -> Result, OpaqueError> { let client_start_result = ClientLogin::::start(&mut self.rng, password.as_bytes())?; self.state = Some(client_start_result.state); @@ -33,10 +38,11 @@ Ok(client_start_result.message.serialize().to_vec()) } + #[wasm_bindgen] pub fn finish( &mut self, response_payload: &[u8], - ) -> Result, ProtocolError> { + ) -> Result, OpaqueError> { let response = CredentialResponse::deserialize(response_payload)?; let password = self .password @@ -57,4 +63,12 @@ Ok(result.message.serialize().to_vec()) } + + #[wasm_bindgen(getter)] + pub fn session_key(&self) -> Result, OpaqueError> { + match &self.session_key { + Some(v) => Ok(v.clone()), + None => Err(ProtocolError::InvalidLoginError.into()), + } + } } diff --git a/shared/comm-opaque2/src/client/register.rs b/shared/comm-opaque2/src/client/register.rs --- a/shared/comm-opaque2/src/client/register.rs +++ b/shared/comm-opaque2/src/client/register.rs @@ -3,16 +3,21 @@ ClientRegistrationFinishParameters, RegistrationResponse, }; use rand::rngs::OsRng; +use wasm_bindgen::prelude::*; +use crate::error::OpaqueError; use crate::Cipher; +#[wasm_bindgen] pub struct Registration { state: Option>, rng: OsRng, export_key: Option>, } +#[wasm_bindgen] impl Registration { + #[wasm_bindgen(constructor)] pub fn new() -> Registration { Registration { state: None, @@ -21,18 +26,20 @@ } } - pub fn start(&mut self, password: &str) -> Result, ProtocolError> { + #[wasm_bindgen] + pub fn start(&mut self, password: &str) -> Result, OpaqueError> { let result = ClientRegistration::::start(&mut self.rng, password.as_bytes())?; self.state = Some(result.state); Ok(result.message.serialize().to_vec()) } + #[wasm_bindgen] pub fn finish( &mut self, password: &str, response_payload: &[u8], - ) -> Result, ProtocolError> { + ) -> Result, OpaqueError> { let response = RegistrationResponse::deserialize(response_payload)?; let state = self .state diff --git a/shared/comm-opaque2/src/error.rs b/shared/comm-opaque2/src/error.rs new file mode 100644 --- /dev/null +++ b/shared/comm-opaque2/src/error.rs @@ -0,0 +1,41 @@ +use opaque_ke::errors::ProtocolError; +use std::ops::Deref; +use wasm_bindgen::{JsError, JsValue}; + +/// Due to orphan instances, we can not directly bridge +/// opaque_ke::errors::ProtocolError to wasm_bindgen::JsValue. Instead we +/// must define our own type, and add the impl's ourselves +#[derive(Debug)] +pub struct OpaqueError(ProtocolError); + +impl Into for OpaqueError { + fn into(self) -> JsValue { + JsValue::from(protocol_error_to_js_error(self.0)) + } +} + +impl From for OpaqueError { + fn from(error: ProtocolError) -> OpaqueError { + OpaqueError(error) + } +} + +impl Deref for OpaqueError { + type Target = ProtocolError; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn protocol_error_to_js_error(error: ProtocolError) -> JsError { + match error { + ProtocolError::IdentityGroupElementError => JsError::new("server error"), + ProtocolError::InvalidLoginError => JsError::new("login failed"), + ProtocolError::LibraryError(_) => JsError::new("internal error"), + ProtocolError::ReflectedValueError => { + JsError::new("invalid server response") + } + ProtocolError::SerializationError => JsError::new("invalid argument"), + } +} diff --git a/shared/comm-opaque2/src/lib.rs b/shared/comm-opaque2/src/lib.rs --- a/shared/comm-opaque2/src/lib.rs +++ b/shared/comm-opaque2/src/lib.rs @@ -1,4 +1,5 @@ pub mod client; +pub mod error; pub mod grpc; pub mod opaque; pub mod server; @@ -47,6 +48,8 @@ server_login.finish(&client_upload).unwrap(); - assert_eq!(login_client.session_key.is_some(), true); - assert_eq!(login_client.session_key, server_login.session_key); + assert_eq!( + login_client.session_key().unwrap(), + server_login.session_key.unwrap() + ); }