From 23c6de89fe14cfdb60e23b1916811b8fade9b729 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Thu, 15 Sep 2022 23:22:39 -0700 Subject: [PATCH 01/18] Replace AsBackend/FromBackend with auto-derived AsRef/From --- umbral-pre/Cargo.toml | 7 +- umbral-pre/src/bindings_python.rs | 160 +++++------------------------- 2 files changed, 29 insertions(+), 138 deletions(-) diff --git a/umbral-pre/Cargo.toml b/umbral-pre/Cargo.toml index 2be47f6..32cbbfb 100644 --- a/umbral-pre/Cargo.toml +++ b/umbral-pre/Cargo.toml @@ -19,7 +19,8 @@ serde = { version = "1", default-features = false, features = ["derive"], option base64 = { version = "0.13", default-features = false, features = ["alloc"] } pyo3 = { version = "0.16", optional = true } js-sys = { version = "0.3", optional = true } -wasm-bindgen = {version = "0.2.74", optional = true } +wasm-bindgen = { version = "0.2.74", optional = true } +derive_more = { version = "0.99", optional = true, default_features = false, features = ["as_ref", "from"] } # These packages are among the dependencies of the packages above. # Their versions should be updated when the main packages above are updated. @@ -43,8 +44,8 @@ rmp-serde = "0.15" [features] default = ["default-rng"] bench-internals = ["default-rng"] -bindings-python = ["pyo3", "std"] -bindings-wasm = [ "serde-support", "js-sys", "wasm-bindgen"] +bindings-python = ["pyo3", "std", "derive_more"] +bindings-wasm = ["serde-support", "js-sys", "wasm-bindgen", "derive_more"] default-rng = ["getrandom", "rand_core/getrandom"] serde-support = ["serde"] std = [] diff --git a/umbral-pre/src/bindings_python.rs b/umbral-pre/src/bindings_python.rs index c8398f0..84835a6 100644 --- a/umbral-pre/src/bindings_python.rs +++ b/umbral-pre/src/bindings_python.rs @@ -26,22 +26,12 @@ use crate::{ SerializableToSecretArray, }; -// Helper traits to generalize implementing various Python protocol functions for our types. - -trait AsBackend { - fn as_backend(&self) -> &T; -} - -trait FromBackend { - fn from_backend(backend: T) -> Self; -} - fn to_bytes(obj: &T) -> PyResult where - T: AsBackend, + T: AsRef, U: SerializableToArray, { - let serialized = obj.as_backend().to_array(); + let serialized = obj.as_ref().to_array(); Python::with_gil(|py| -> PyResult { Ok(PyBytes::new(py, serialized.as_slice()).into()) }) @@ -50,11 +40,11 @@ where // Can't keep the secret in Python anymore, so this function does the same as `to_bytes()` fn to_secret_bytes(obj: &T) -> PyResult where - T: AsBackend, + T: AsRef, U: SerializableToSecretArray, { // Dereferencing a secret. - let serialized = obj.as_backend().to_secret_array().as_secret().clone(); + let serialized = obj.as_ref().to_secret_array().as_secret().clone(); Python::with_gil(|py| -> PyResult { Ok(PyBytes::new(py, serialized.as_slice()).into()) }) @@ -62,20 +52,20 @@ where fn from_bytes(data: &[u8]) -> PyResult where - T: FromBackend, + T: From, U: DeserializableFromArray + HasTypeName, { U::from_bytes(data) - .map(T::from_backend) + .map(T::from) .map_err(|err| PyValueError::new_err(format!("{}", err))) } fn hash(obj: &T) -> PyResult where - T: AsBackend, + T: AsRef, U: SerializableToArray + HasTypeName, { - let serialized = obj.as_backend().to_array(); + let serialized = obj.as_ref().to_array(); // call `hash((class_name, bytes(obj)))` Python::with_gil(|py| { @@ -88,7 +78,7 @@ where fn richcmp(obj: &T, other: PyRef<'_, T>, op: CompareOp) -> PyResult where - T: PyClass + PartialEq + AsBackend, + T: PyClass + PartialEq + AsRef, U: HasTypeName, { match op { @@ -104,22 +94,11 @@ where create_exception!(umbral, VerificationError, PyException); #[pyclass(module = "umbral")] +#[derive(derive_more::AsRef, derive_more::From)] pub struct SecretKey { pub backend: umbral_pre::SecretKey, } -impl AsBackend for SecretKey { - fn as_backend(&self) -> &umbral_pre::SecretKey { - &self.backend - } -} - -impl FromBackend for SecretKey { - fn from_backend(backend: umbral_pre::SecretKey) -> Self { - Self { backend } - } -} - #[pymethods] impl SecretKey { #[staticmethod] @@ -141,7 +120,7 @@ impl SecretKey { #[staticmethod] pub fn from_bytes(data: &[u8]) -> PyResult { - from_bytes(data) + from_bytes::<_, umbral_pre::SecretKey>(data) } #[staticmethod] @@ -155,22 +134,11 @@ impl SecretKey { } #[pyclass(module = "umbral")] +#[derive(derive_more::AsRef, derive_more::From)] pub struct SecretKeyFactory { backend: umbral_pre::SecretKeyFactory, } -impl AsBackend for SecretKeyFactory { - fn as_backend(&self) -> &umbral_pre::SecretKeyFactory { - &self.backend - } -} - -impl FromBackend for SecretKeyFactory { - fn from_backend(backend: umbral_pre::SecretKeyFactory) -> Self { - Self { backend } - } -} - #[pymethods] impl SecretKeyFactory { #[staticmethod] @@ -212,7 +180,7 @@ impl SecretKeyFactory { #[staticmethod] pub fn from_bytes(data: &[u8]) -> PyResult { - from_bytes(data) + from_bytes::<_, umbral_pre::SecretKeyFactory>(data) } #[staticmethod] @@ -226,28 +194,16 @@ impl SecretKeyFactory { } #[pyclass(module = "umbral")] -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, derive_more::AsRef, derive_more::From)] pub struct PublicKey { pub backend: umbral_pre::PublicKey, } -impl AsBackend for PublicKey { - fn as_backend(&self) -> &umbral_pre::PublicKey { - &self.backend - } -} - -impl FromBackend for PublicKey { - fn from_backend(backend: umbral_pre::PublicKey) -> Self { - Self { backend } - } -} - #[pymethods] impl PublicKey { #[staticmethod] pub fn from_bytes(data: &[u8]) -> PyResult { - from_bytes(data) + from_bytes::<_, umbral_pre::PublicKey>(data) } #[staticmethod] @@ -277,12 +233,6 @@ pub struct Signer { pub backend: umbral_pre::Signer, } -impl AsBackend for Signer { - fn as_backend(&self) -> &umbral_pre::Signer { - &self.backend - } -} - #[pymethods] impl Signer { #[new] @@ -310,28 +260,16 @@ impl Signer { } #[pyclass(module = "umbral")] -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, derive_more::AsRef, derive_more::From)] pub struct Signature { backend: umbral_pre::Signature, } -impl AsBackend for Signature { - fn as_backend(&self) -> &umbral_pre::Signature { - &self.backend - } -} - -impl FromBackend for Signature { - fn from_backend(backend: umbral_pre::Signature) -> Self { - Self { backend } - } -} - #[pymethods] impl Signature { #[staticmethod] pub fn from_bytes(data: &[u8]) -> PyResult { - from_bytes(data) + from_bytes::<_, umbral_pre::Signature>(data) } pub fn verify(&self, verifying_pk: &PublicKey, message: &[u8]) -> bool { @@ -361,28 +299,16 @@ impl Signature { } #[pyclass(module = "umbral")] -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, derive_more::AsRef, derive_more::From)] pub struct Capsule { pub backend: umbral_pre::Capsule, } -impl AsBackend for Capsule { - fn as_backend(&self) -> &umbral_pre::Capsule { - &self.backend - } -} - -impl FromBackend for Capsule { - fn from_backend(backend: umbral_pre::Capsule) -> Self { - Self { backend } - } -} - #[pymethods] impl Capsule { #[staticmethod] pub fn from_bytes(data: &[u8]) -> PyResult { - from_bytes(data) + from_bytes::<_, umbral_pre::Capsule>(data) } #[staticmethod] @@ -438,23 +364,11 @@ pub fn decrypt_original( } #[pyclass(module = "umbral")] -#[derive(PartialEq)] +#[derive(PartialEq, derive_more::AsRef, derive_more::From)] pub struct KeyFrag { backend: umbral_pre::KeyFrag, } -impl AsBackend for KeyFrag { - fn as_backend(&self) -> &umbral_pre::KeyFrag { - &self.backend - } -} - -impl FromBackend for KeyFrag { - fn from_backend(backend: umbral_pre::KeyFrag) -> Self { - Self { backend } - } -} - #[pymethods] impl KeyFrag { pub fn verify( @@ -484,7 +398,7 @@ impl KeyFrag { #[staticmethod] pub fn from_bytes(data: &[u8]) -> PyResult { - from_bytes(data) + from_bytes::<_, umbral_pre::KeyFrag>(data) } #[staticmethod] @@ -510,17 +424,11 @@ impl KeyFrag { } #[pyclass(module = "umbral")] -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, derive_more::AsRef)] pub struct VerifiedKeyFrag { pub backend: umbral_pre::VerifiedKeyFrag, } -impl AsBackend for VerifiedKeyFrag { - fn as_backend(&self) -> &umbral_pre::VerifiedKeyFrag { - &self.backend - } -} - #[pymethods] impl VerifiedKeyFrag { #[staticmethod] @@ -587,23 +495,11 @@ pub fn generate_kfrags( } #[pyclass(module = "umbral")] -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, derive_more::AsRef, derive_more::From)] pub struct CapsuleFrag { backend: umbral_pre::CapsuleFrag, } -impl AsBackend for CapsuleFrag { - fn as_backend(&self) -> &umbral_pre::CapsuleFrag { - &self.backend - } -} - -impl FromBackend for CapsuleFrag { - fn from_backend(backend: umbral_pre::CapsuleFrag) -> Self { - Self { backend } - } -} - #[pymethods] impl CapsuleFrag { pub fn verify( @@ -635,7 +531,7 @@ impl CapsuleFrag { #[staticmethod] pub fn from_bytes(data: &[u8]) -> PyResult { - from_bytes(data) + from_bytes::<_, umbral_pre::CapsuleFrag>(data) } #[staticmethod] @@ -661,17 +557,11 @@ impl CapsuleFrag { } #[pyclass(module = "umbral")] -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, derive_more::AsRef)] pub struct VerifiedCapsuleFrag { pub backend: umbral_pre::VerifiedCapsuleFrag, } -impl AsBackend for VerifiedCapsuleFrag { - fn as_backend(&self) -> &umbral_pre::VerifiedCapsuleFrag { - &self.backend - } -} - #[pymethods] impl VerifiedCapsuleFrag { fn __richcmp__(&self, other: PyRef<'_, VerifiedCapsuleFrag>, op: CompareOp) -> PyResult { From c384fa6597bce9e8838f81776f6b49bcd30c4383 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Thu, 15 Sep 2022 23:34:15 -0700 Subject: [PATCH 02/18] Replace inner() and new() with auto-derived AsRef and From --- umbral-pre/src/bindings_wasm.rs | 64 +++++---------------------------- 1 file changed, 8 insertions(+), 56 deletions(-) diff --git a/umbral-pre/src/bindings_wasm.rs b/umbral-pre/src/bindings_wasm.rs index 7b84c23..bc39f70 100644 --- a/umbral-pre/src/bindings_wasm.rs +++ b/umbral-pre/src/bindings_wasm.rs @@ -27,6 +27,7 @@ fn map_js_err(err: T) -> JsValue { } #[wasm_bindgen] +#[derive(derive_more::AsRef)] pub struct SecretKey(umbral_pre::SecretKey); #[wasm_bindgen] @@ -65,12 +66,6 @@ impl SecretKey { } } -impl SecretKey { - pub fn inner(&self) -> &umbral_pre::SecretKey { - &self.0 - } -} - #[wasm_bindgen] pub struct SecretKeyFactory(umbral_pre::SecretKeyFactory); @@ -127,7 +122,7 @@ impl SecretKeyFactory { } #[wasm_bindgen] -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, derive_more::AsRef, derive_more::From)] pub struct PublicKey(umbral_pre::PublicKey); #[wasm_bindgen] @@ -155,17 +150,8 @@ impl PublicKey { } } -impl PublicKey { - pub fn new(pk: umbral_pre::PublicKey) -> Self { - PublicKey(pk) - } - - pub fn inner(&self) -> &umbral_pre::PublicKey { - &self.0 - } -} - #[wasm_bindgen] +#[derive(derive_more::AsRef)] pub struct Signer(umbral_pre::Signer); #[wasm_bindgen] @@ -191,12 +177,6 @@ impl Signer { } } -impl Signer { - pub fn inner(&self) -> &umbral_pre::Signer { - &self.0 - } -} - #[wasm_bindgen] pub struct Signature(umbral_pre::Signature); @@ -230,7 +210,7 @@ impl Signature { } #[wasm_bindgen] -#[derive(Clone, Copy, Serialize, Deserialize)] +#[derive(Clone, Copy, Serialize, Deserialize, derive_more::AsRef, derive_more::From)] pub struct Capsule(umbral_pre::Capsule); #[wasm_bindgen] @@ -269,16 +249,6 @@ impl Capsule { } } -impl Capsule { - pub fn new(capsule: umbral_pre::Capsule) -> Self { - Capsule(capsule) - } - - pub fn inner(&self) -> &umbral_pre::Capsule { - &self.0 - } -} - #[wasm_bindgen] #[derive(Clone)] pub struct CapsuleFrag(umbral_pre::CapsuleFrag); @@ -329,7 +299,9 @@ impl CapsuleFrag { } #[wasm_bindgen] -#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)] +#[derive( + Clone, Serialize, Deserialize, PartialEq, Debug, derive_more::AsRef, derive_more::From, +)] pub struct VerifiedCapsuleFrag(umbral_pre::VerifiedCapsuleFrag); #[wasm_bindgen] @@ -357,16 +329,6 @@ impl VerifiedCapsuleFrag { } } -impl VerifiedCapsuleFrag { - pub fn new(verified_cfrag: umbral_pre::VerifiedCapsuleFrag) -> Self { - VerifiedCapsuleFrag(verified_cfrag) - } - - pub fn inner(&self) -> umbral_pre::VerifiedCapsuleFrag { - self.0.clone() - } -} - #[wasm_bindgen] pub struct CapsuleWithFrags { capsule: Capsule, @@ -538,7 +500,7 @@ impl KeyFrag { } #[wasm_bindgen] -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone, derive_more::AsRef, derive_more::From)] pub struct VerifiedKeyFrag(umbral_pre::VerifiedKeyFrag); #[wasm_bindgen] @@ -566,16 +528,6 @@ impl VerifiedKeyFrag { } } -impl VerifiedKeyFrag { - pub fn new(vkfrag: umbral_pre::VerifiedKeyFrag) -> Self { - Self(vkfrag) - } - - pub fn inner(&self) -> &umbral_pre::VerifiedKeyFrag { - &self.0 - } -} - #[allow(clippy::too_many_arguments)] #[wasm_bindgen(js_name = generateKFrags)] pub fn generate_kfrags( From 6589cef0745c7d042be9b83ff724c1e33ad7c324 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Thu, 15 Sep 2022 23:52:40 -0700 Subject: [PATCH 03/18] Use an explicit Error in Results instead of JsValue --- umbral-pre/src/bindings_wasm.rs | 40 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/umbral-pre/src/bindings_wasm.rs b/umbral-pre/src/bindings_wasm.rs index bc39f70..0d9de1d 100644 --- a/umbral-pre/src/bindings_wasm.rs +++ b/umbral-pre/src/bindings_wasm.rs @@ -22,8 +22,8 @@ use serde::{Deserialize, Serialize}; use crate as umbral_pre; use crate::{DeserializableFromArray, SerializableToArray, SerializableToSecretArray}; -fn map_js_err(err: T) -> JsValue { - Error::new(&format!("{}", err)).into() +fn map_js_err(err: T) -> Error { + Error::new(&format!("{}", err)) } #[wasm_bindgen] @@ -53,7 +53,7 @@ impl SecretKey { } #[wasm_bindgen(js_name = fromBytes)] - pub fn from_bytes(data: &[u8]) -> Result { + pub fn from_bytes(data: &[u8]) -> Result { umbral_pre::SecretKey::from_bytes(data) .map(Self) .map_err(map_js_err) @@ -82,7 +82,7 @@ impl SecretKeyFactory { } #[wasm_bindgen(js_name = fromSecureRandomness)] - pub fn from_secure_randomness(seed: &[u8]) -> Result { + pub fn from_secure_randomness(seed: &[u8]) -> Result { umbral_pre::SecretKeyFactory::from_secure_randomness(seed) .map(Self) .map_err(map_js_err) @@ -108,7 +108,7 @@ impl SecretKeyFactory { } #[wasm_bindgen(js_name = fromBytes)] - pub fn from_bytes(data: &[u8]) -> Result { + pub fn from_bytes(data: &[u8]) -> Result { umbral_pre::SecretKeyFactory::from_bytes(data) .map(Self) .map_err(map_js_err) @@ -133,7 +133,7 @@ impl PublicKey { } #[wasm_bindgen(js_name = fromBytes)] - pub fn from_bytes(data: &[u8]) -> Result { + pub fn from_bytes(data: &[u8]) -> Result { umbral_pre::PublicKey::from_bytes(data) .map(PublicKey) .map_err(map_js_err) @@ -192,7 +192,7 @@ impl Signature { } #[wasm_bindgen(js_name = fromBytes)] - pub fn from_bytes(data: &[u8]) -> Result { + pub fn from_bytes(data: &[u8]) -> Result { umbral_pre::Signature::from_bytes(data) .map(Self) .map_err(map_js_err) @@ -232,7 +232,7 @@ impl Capsule { } #[wasm_bindgen(js_name = fromBytes)] - pub fn from_bytes(data: &[u8]) -> Result { + pub fn from_bytes(data: &[u8]) -> Result { umbral_pre::Capsule::from_bytes(data) .map(Capsule) .map_err(map_js_err) @@ -262,7 +262,7 @@ impl CapsuleFrag { verifying_pk: &PublicKey, delegating_pk: &PublicKey, receiving_pk: &PublicKey, - ) -> Result { + ) -> Result { self.0 .verify( &capsule.0, @@ -281,7 +281,7 @@ impl CapsuleFrag { } #[wasm_bindgen(js_name = fromBytes)] - pub fn from_bytes(data: &[u8]) -> Result { + pub fn from_bytes(data: &[u8]) -> Result { umbral_pre::CapsuleFrag::from_bytes(data) .map(Self) .map_err(map_js_err) @@ -307,7 +307,7 @@ pub struct VerifiedCapsuleFrag(umbral_pre::VerifiedCapsuleFrag); #[wasm_bindgen] impl VerifiedCapsuleFrag { #[wasm_bindgen(js_name = fromVerifiedBytes)] - pub fn from_verified_bytes(bytes: &[u8]) -> Result { + pub fn from_verified_bytes(bytes: &[u8]) -> Result { umbral_pre::VerifiedCapsuleFrag::from_verified_bytes(bytes) .map(VerifiedCapsuleFrag) .map_err(map_js_err) @@ -353,7 +353,7 @@ impl CapsuleWithFrags { receiving_sk: &SecretKey, delegating_pk: &PublicKey, ciphertext: &[u8], - ) -> Result, JsValue> { + ) -> Result, Error> { let backend_cfrags: Vec = self.cfrags.iter().cloned().map(|x| x.0).collect(); umbral_pre::decrypt_reencrypted( @@ -391,7 +391,7 @@ impl EncryptionResult { } #[wasm_bindgen] -pub fn encrypt(delegating_pk: &PublicKey, plaintext: &[u8]) -> Result { +pub fn encrypt(delegating_pk: &PublicKey, plaintext: &[u8]) -> Result { let backend_pk = delegating_pk.0; umbral_pre::encrypt(&backend_pk, plaintext) .map(|(capsule, ciphertext)| EncryptionResult::new(ciphertext, Capsule(capsule))) @@ -403,7 +403,7 @@ pub fn decrypt_original( delegating_sk: &SecretKey, capsule: &Capsule, ciphertext: &[u8], -) -> Result, JsValue> { +) -> Result, Error> { umbral_pre::decrypt_original(&delegating_sk.0, &capsule.0, ciphertext).map_err(map_js_err) } @@ -417,7 +417,7 @@ impl KeyFrag { // So we have to use 4 functions instead of 1. Yikes. #[wasm_bindgen] - pub fn verify(self, verifying_pk: &PublicKey) -> Result { + pub fn verify(self, verifying_pk: &PublicKey) -> Result { self.0 .verify(&verifying_pk.0, None, None) .map(VerifiedKeyFrag) @@ -430,7 +430,7 @@ impl KeyFrag { self, verifying_pk: &PublicKey, delegating_pk: &PublicKey, - ) -> Result { + ) -> Result { let backend_delegating_pk = delegating_pk.0; self.0 @@ -445,7 +445,7 @@ impl KeyFrag { self, verifying_pk: &PublicKey, receiving_pk: &PublicKey, - ) -> Result { + ) -> Result { let backend_receiving_pk = receiving_pk.0; self.0 @@ -461,7 +461,7 @@ impl KeyFrag { verifying_pk: &PublicKey, delegating_pk: &PublicKey, receiving_pk: &PublicKey, - ) -> Result { + ) -> Result { let backend_delegating_pk = delegating_pk.0; let backend_receiving_pk = receiving_pk.0; @@ -482,7 +482,7 @@ impl KeyFrag { } #[wasm_bindgen(js_name = fromBytes)] - pub fn from_bytes(data: &[u8]) -> Result { + pub fn from_bytes(data: &[u8]) -> Result { umbral_pre::KeyFrag::from_bytes(data) .map(Self) .map_err(map_js_err) @@ -506,7 +506,7 @@ pub struct VerifiedKeyFrag(umbral_pre::VerifiedKeyFrag); #[wasm_bindgen] impl VerifiedKeyFrag { #[wasm_bindgen(js_name = fromVerifiedBytes)] - pub fn from_verified_bytes(bytes: &[u8]) -> Result { + pub fn from_verified_bytes(bytes: &[u8]) -> Result { umbral_pre::VerifiedKeyFrag::from_verified_bytes(bytes) .map(VerifiedKeyFrag) .map_err(map_js_err) From 78774452f94e4a956b132515eb2be41cfd03e933 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Sat, 17 Sep 2022 22:02:20 -0700 Subject: [PATCH 04/18] Use a workaround to support Option<&...> in WASM bindings --- umbral-pre/Cargo.toml | 3 +- umbral-pre/src/bindings_wasm.rs | 87 ++++++++++++++------------------- 2 files changed, 38 insertions(+), 52 deletions(-) diff --git a/umbral-pre/Cargo.toml b/umbral-pre/Cargo.toml index 32cbbfb..8342b02 100644 --- a/umbral-pre/Cargo.toml +++ b/umbral-pre/Cargo.toml @@ -21,6 +21,7 @@ pyo3 = { version = "0.16", optional = true } js-sys = { version = "0.3", optional = true } wasm-bindgen = { version = "0.2.74", optional = true } derive_more = { version = "0.99", optional = true, default_features = false, features = ["as_ref", "from"] } +wasm-bindgen-derive = { version = "0.1", optional = true } # These packages are among the dependencies of the packages above. # Their versions should be updated when the main packages above are updated. @@ -45,7 +46,7 @@ rmp-serde = "0.15" default = ["default-rng"] bench-internals = ["default-rng"] bindings-python = ["pyo3", "std", "derive_more"] -bindings-wasm = ["serde-support", "js-sys", "wasm-bindgen", "derive_more"] +bindings-wasm = ["serde-support", "js-sys", "wasm-bindgen", "derive_more", "wasm-bindgen-derive"] default-rng = ["getrandom", "rand_core/getrandom"] serde-support = ["serde"] std = [] diff --git a/umbral-pre/src/bindings_wasm.rs b/umbral-pre/src/bindings_wasm.rs index 0d9de1d..d1b0b73 100644 --- a/umbral-pre/src/bindings_wasm.rs +++ b/umbral-pre/src/bindings_wasm.rs @@ -16,6 +16,7 @@ use core::fmt; use js_sys::Error; use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; +use wasm_bindgen_derive::TryFromJsValue; use serde::{Deserialize, Serialize}; @@ -26,6 +27,24 @@ fn map_js_err(err: T) -> Error { Error::new(&format!("{}", err)) } +/// Tries to convert an optional value (either `null` or a `#[wasm_bindgen]` marked structure) +/// from `JsValue` to the Rust type. +// This is necessary since wasm-bindgen does not support having a paramter of `Option<&T>`, +// and using `Option` consumes the argument +// (see https://github.com/rustwasm/wasm-bindgen/issues/2370). +fn try_from_js_option<'a, T>(value: &'a JsValue) -> Result, JsValue> +where + T: TryFrom<&'a JsValue>, + >::Error: core::fmt::Display, +{ + let typed_value = if value.is_null() { + None + } else { + Some(T::try_from(value).map_err(map_js_err)?) + }; + Ok(typed_value) +} + #[wasm_bindgen] #[derive(derive_more::AsRef)] pub struct SecretKey(umbral_pre::SecretKey); @@ -121,8 +140,9 @@ impl SecretKeyFactory { } } +#[derive(TryFromJsValue)] #[wasm_bindgen] -#[derive(Serialize, Deserialize, derive_more::AsRef, derive_more::From)] +#[derive(Clone, Serialize, Deserialize, derive_more::AsRef, derive_more::From)] pub struct PublicKey(umbral_pre::PublicKey); #[wasm_bindgen] @@ -407,69 +427,34 @@ pub fn decrypt_original( umbral_pre::decrypt_original(&delegating_sk.0, &capsule.0, ciphertext).map_err(map_js_err) } +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "PublicKey | null")] + pub type OptionPublicKey; +} + #[wasm_bindgen] pub struct KeyFrag(umbral_pre::KeyFrag); #[wasm_bindgen] impl KeyFrag { - // TODO (#25): `Option<&PublicKey> are currently not supported. - // See https://github.com/rustwasm/wasm-bindgen/issues/2370 - // So we have to use 4 functions instead of 1. Yikes. - #[wasm_bindgen] - pub fn verify(self, verifying_pk: &PublicKey) -> Result { - self.0 - .verify(&verifying_pk.0, None, None) - .map(VerifiedKeyFrag) - .map_err(|(err, _)| err) - .map_err(map_js_err) - } - - #[wasm_bindgen(js_name = verifyWithDelegatingKey)] - pub fn verify_with_delegating_key( - self, - verifying_pk: &PublicKey, - delegating_pk: &PublicKey, - ) -> Result { - let backend_delegating_pk = delegating_pk.0; - - self.0 - .verify(&verifying_pk.0, Some(&backend_delegating_pk), None) - .map(VerifiedKeyFrag) - .map_err(|(err, _)| err) - .map_err(map_js_err) - } - - #[wasm_bindgen(js_name = verifyWithReceivingKey)] - pub fn verify_with_receiving_key( - self, - verifying_pk: &PublicKey, - receiving_pk: &PublicKey, - ) -> Result { - let backend_receiving_pk = receiving_pk.0; - - self.0 - .verify(&verifying_pk.0, None, Some(&backend_receiving_pk)) - .map(VerifiedKeyFrag) - .map_err(|(err, _)| err) - .map_err(map_js_err) - } - - #[wasm_bindgen(js_name = verifyWithDelegatingAndReceivingKeys)] - pub fn verify_with_delegating_and_receiving_keys( + pub fn verify( self, verifying_pk: &PublicKey, - delegating_pk: &PublicKey, - receiving_pk: &PublicKey, + // TODO: replace with `Option<&PublicKey>` when `wasm-bindgen` supports it. + // See https://github.com/rustwasm/wasm-bindgen/issues/2370 + delegating_pk: &OptionPublicKey, + receiving_pk: &OptionPublicKey, ) -> Result { - let backend_delegating_pk = delegating_pk.0; - let backend_receiving_pk = receiving_pk.0; + let typed_delegating_pk = try_from_js_option::(delegating_pk.as_ref())?; + let typed_receiving_pk = try_from_js_option::(receiving_pk.as_ref())?; self.0 .verify( &verifying_pk.0, - Some(&backend_delegating_pk), - Some(&backend_receiving_pk), + typed_delegating_pk.as_ref().map(|pk| &pk.0), + typed_receiving_pk.as_ref().map(|pk| &pk.0), ) .map(VerifiedKeyFrag) .map_err(|(err, _)| err) From 233731d5da3665e26f22e36bb2410ab6d7116954 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Sat, 17 Sep 2022 22:58:32 -0700 Subject: [PATCH 05/18] Generate a correct TypeScript signature for the return value of generate_kfrags() --- umbral-pre/src/bindings_wasm.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/umbral-pre/src/bindings_wasm.rs b/umbral-pre/src/bindings_wasm.rs index d1b0b73..4d5fed5 100644 --- a/umbral-pre/src/bindings_wasm.rs +++ b/umbral-pre/src/bindings_wasm.rs @@ -16,6 +16,7 @@ use core::fmt; use js_sys::Error; use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; +use wasm_bindgen::JsCast; use wasm_bindgen_derive::TryFromJsValue; use serde::{Deserialize, Serialize}; @@ -513,6 +514,12 @@ impl VerifiedKeyFrag { } } +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "VerifiedKeyFrag[]")] + pub type VerifiedKeyFragArray; +} + #[allow(clippy::too_many_arguments)] #[wasm_bindgen(js_name = generateKFrags)] pub fn generate_kfrags( @@ -523,7 +530,7 @@ pub fn generate_kfrags( shares: usize, sign_delegating_key: bool, sign_receiving_key: bool, -) -> Vec { +) -> VerifiedKeyFragArray { let backend_kfrags = umbral_pre::generate_kfrags( &delegating_sk.0, &receiving_pk.0, @@ -535,14 +542,16 @@ pub fn generate_kfrags( ); // TODO (#26): Apparently we cannot just return a vector of things, - // so we have to convert them to JsValues manually. + // so we have to convert them to JsValues manually and use a custom return type + // to generate a correct signature for TypeScript. // See https://github.com/rustwasm/wasm-bindgen/issues/111 backend_kfrags .iter() .cloned() .map(VerifiedKeyFrag) .map(JsValue::from) - .collect() + .collect::() + .unchecked_into::() } #[wasm_bindgen] From 36f490c898d7fcb0bcc5c5360e1de48565400da0 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Fri, 16 Sep 2022 16:19:47 -0700 Subject: [PATCH 06/18] Get rid of Capsule::with_cfrag() workaround --- umbral-pre/src/bindings_wasm.rs | 90 +++++++++++++++------------------ 1 file changed, 40 insertions(+), 50 deletions(-) diff --git a/umbral-pre/src/bindings_wasm.rs b/umbral-pre/src/bindings_wasm.rs index 4d5fed5..3d8a38f 100644 --- a/umbral-pre/src/bindings_wasm.rs +++ b/umbral-pre/src/bindings_wasm.rs @@ -11,7 +11,7 @@ extern crate alloc; use alloc::boxed::Box; use alloc::format; use alloc::string::String; -use alloc::{vec, vec::Vec}; +use alloc::vec::Vec; use core::fmt; use js_sys::Error; @@ -236,17 +236,6 @@ pub struct Capsule(umbral_pre::Capsule); #[wasm_bindgen] impl Capsule { - // TODO (#23): have to add cfrags one by one since `wasm_bindgen` currently does not support - // Vec as a parameter. - // Will probably be fixed along with https://github.com/rustwasm/wasm-bindgen/issues/111 - #[wasm_bindgen(js_name = withCFrag)] - pub fn with_cfrag(&self, cfrag: &VerifiedCapsuleFrag) -> CapsuleWithFrags { - CapsuleWithFrags { - capsule: *self, - cfrags: vec![cfrag.clone()], - } - } - #[wasm_bindgen(js_name = toBytes)] pub fn to_bytes(&self) -> Box<[u8]> { self.0.to_array().to_vec().into_boxed_slice() @@ -319,6 +308,7 @@ impl CapsuleFrag { } } +#[derive(TryFromJsValue)] #[wasm_bindgen] #[derive( Clone, Serialize, Deserialize, PartialEq, Debug, derive_more::AsRef, derive_more::From, @@ -350,44 +340,6 @@ impl VerifiedCapsuleFrag { } } -#[wasm_bindgen] -pub struct CapsuleWithFrags { - capsule: Capsule, - cfrags: Vec, -} - -#[wasm_bindgen] -impl CapsuleWithFrags { - #[wasm_bindgen(js_name = withCFrag)] - pub fn with_cfrag(&self, cfrag: &VerifiedCapsuleFrag) -> CapsuleWithFrags { - let mut new_cfrags = self.cfrags.clone(); - new_cfrags.push(cfrag.clone()); - Self { - capsule: self.capsule, - cfrags: new_cfrags, - } - } - - #[wasm_bindgen(js_name = decryptReencrypted)] - pub fn decrypt_reencrypted( - &self, - receiving_sk: &SecretKey, - delegating_pk: &PublicKey, - ciphertext: &[u8], - ) -> Result, Error> { - let backend_cfrags: Vec = - self.cfrags.iter().cloned().map(|x| x.0).collect(); - umbral_pre::decrypt_reencrypted( - &receiving_sk.0, - &delegating_pk.0, - &self.capsule.0, - backend_cfrags, - ciphertext, - ) - .map_err(map_js_err) - } -} - #[wasm_bindgen] pub struct EncryptionResult { ciphertext: Box<[u8]>, @@ -428,6 +380,44 @@ pub fn decrypt_original( umbral_pre::decrypt_original(&delegating_sk.0, &capsule.0, ciphertext).map_err(map_js_err) } +// TODO (#23): using a custom type since `wasm_bindgen` currently does not support +// Vec as a parameter. +// Will probably be fixed along with https://github.com/rustwasm/wasm-bindgen/issues/111 +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "VerifiedCapsuleFrag[]")] + pub type VerifiedCapsuleFragArray; +} + +#[wasm_bindgen(js_name = decryptReencrypted)] +pub fn decrypt_reencrypted( + receiving_sk: &SecretKey, + delegating_pk: &PublicKey, + capsule: &Capsule, + vcfrags: &VerifiedCapsuleFragArray, + ciphertext: &[u8], +) -> Result, Error> { + let js_val = vcfrags.as_ref(); + if !js_sys::Array::is_array(js_val) { + return Err(Error::new("`vcfrags` must be an array")); + } + let array = js_sys::Array::from(js_val); + let length: usize = array.length().try_into().map_err(map_js_err)?; + let mut backend_vcfrags = Vec::::with_capacity(length); + for js in array.iter() { + let typed_vcfrag = VerifiedCapsuleFrag::try_from(&js).map_err(map_js_err)?; + backend_vcfrags.push(typed_vcfrag.0); + } + umbral_pre::decrypt_reencrypted( + &receiving_sk.0, + &delegating_pk.0, + &capsule.0, + backend_vcfrags, + ciphertext, + ) + .map_err(map_js_err) +} + #[wasm_bindgen] extern "C" { #[wasm_bindgen(typescript_type = "PublicKey | null")] From 62b4f5f767c9aaba8a3b98abd4677a6e6011a26e Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Fri, 16 Sep 2022 17:26:46 -0700 Subject: [PATCH 07/18] Update JS examples --- umbral-pre-wasm/README.md | 12 +++--------- umbral-pre-wasm/examples/bundler/index.js | 10 +++------- umbral-pre-wasm/examples/node/index.js | 10 +++------- umbral-pre-wasm/examples/react/src/App.js | 10 +++------- .../examples/webpack-5-experiments/index.js | 8 +++----- 5 files changed, 15 insertions(+), 35 deletions(-) diff --git a/umbral-pre-wasm/README.md b/umbral-pre-wasm/README.md index 513ae2a..f799e86 100644 --- a/umbral-pre-wasm/README.md +++ b/umbral-pre-wasm/README.md @@ -39,7 +39,7 @@ let plaintext_bytes = enc.encode(plaintext); // The API here slightly differs from that in Rust. // Since wasm-pack does not support returning tuples, we return an object containing // the ciphertext and the capsule. -let result = umbral.encrypt(alice_pk, plaintext_bytes); +let {capsule, ciphertext} = umbral.encrypt(alice_pk, plaintext_bytes); let ciphertext = result.ciphertext; let capsule = result.capsule; @@ -75,14 +75,8 @@ let cfrag1 = umbral.reencrypt(capsule, kfrags[1]); // Finally, Bob opens the capsule by using at least `threshold` cfrags, // and then decrypts the re-encrypted ciphertext. - -// Another deviation from the Rust API. -// wasm-pack does not support taking arrays as arguments, -// so we build a capsule+cfrags object before decryption. -let plaintext_bob = capsule - .withCFrag(cfrag0) - .withCFrag(cfrag1) - .decryptReencrypted(bob_sk, alice_pk, ciphertext); +let plaintext_bob = umbral.decryptReencrypted( + bob_sk, alice_pk, capsule, [cfrag0, cfrag1], ciphertext); console.assert(dec.decode(plaintext_bob) === plaintext, "decryptReencrypted() failed"); ``` diff --git a/umbral-pre-wasm/examples/bundler/index.js b/umbral-pre-wasm/examples/bundler/index.js index 07f011f..41fe957 100644 --- a/umbral-pre-wasm/examples/bundler/index.js +++ b/umbral-pre-wasm/examples/bundler/index.js @@ -27,9 +27,7 @@ let plaintext_bytes = enc.encode(plaintext); // The API here slightly differs from that in Rust. // Since wasm-pack does not support returning tuples, we return an object containing // the ciphertext and the capsule. -let result = umbral.encrypt(alice_pk, plaintext_bytes); -let ciphertext = result.ciphertext; -let capsule = result.capsule; +let {capsule, ciphertext} = umbral.encrypt(alice_pk, plaintext_bytes); // Since data was encrypted with Alice's public key, Alice can open the capsule // and decrypt the ciphertext with her private key. @@ -73,10 +71,8 @@ let cfrag1 = umbral.reencrypt(capsule, kfrags[1]); // Another deviation from the Rust API. // wasm-pack does not support taking arrays as arguments, // so we build a capsule+cfrags object before decryption. -let plaintext_bob = capsule - .withCFrag(cfrag0) - .withCFrag(cfrag1) - .decryptReencrypted(bob_sk, alice_pk, ciphertext); +let plaintext_bob = umbral.decryptReencrypted( + bob_sk, alice_pk, capsule, [cfrag0, cfrag1], ciphertext); console.assert(dec.decode(plaintext_bob) === plaintext, "decryptReencrypted() failed"); diff --git a/umbral-pre-wasm/examples/node/index.js b/umbral-pre-wasm/examples/node/index.js index 07f011f..41fe957 100644 --- a/umbral-pre-wasm/examples/node/index.js +++ b/umbral-pre-wasm/examples/node/index.js @@ -27,9 +27,7 @@ let plaintext_bytes = enc.encode(plaintext); // The API here slightly differs from that in Rust. // Since wasm-pack does not support returning tuples, we return an object containing // the ciphertext and the capsule. -let result = umbral.encrypt(alice_pk, plaintext_bytes); -let ciphertext = result.ciphertext; -let capsule = result.capsule; +let {capsule, ciphertext} = umbral.encrypt(alice_pk, plaintext_bytes); // Since data was encrypted with Alice's public key, Alice can open the capsule // and decrypt the ciphertext with her private key. @@ -73,10 +71,8 @@ let cfrag1 = umbral.reencrypt(capsule, kfrags[1]); // Another deviation from the Rust API. // wasm-pack does not support taking arrays as arguments, // so we build a capsule+cfrags object before decryption. -let plaintext_bob = capsule - .withCFrag(cfrag0) - .withCFrag(cfrag1) - .decryptReencrypted(bob_sk, alice_pk, ciphertext); +let plaintext_bob = umbral.decryptReencrypted( + bob_sk, alice_pk, capsule, [cfrag0, cfrag1], ciphertext); console.assert(dec.decode(plaintext_bob) === plaintext, "decryptReencrypted() failed"); diff --git a/umbral-pre-wasm/examples/react/src/App.js b/umbral-pre-wasm/examples/react/src/App.js index 3141a11..ed8fe7d 100644 --- a/umbral-pre-wasm/examples/react/src/App.js +++ b/umbral-pre-wasm/examples/react/src/App.js @@ -52,9 +52,7 @@ function App() { // The API here slightly differs from that in Rust. // Since wasm-pack does not support returning tuples, we return an object containing // the ciphertext and the capsule. - let result = umbral.encrypt(alice_pk, plaintext_bytes); - let ciphertext = result.ciphertext; - let capsule = result.capsule; + let {capsule, ciphertext} = umbral.encrypt(alice_pk, plaintext_bytes); // Since data was encrypted with Alice's public key, Alice can open the capsule // and decrypt the ciphertext with her private key. @@ -106,10 +104,8 @@ function App() { // Another deviation from the Rust API. // wasm-pack does not support taking arrays as arguments, // so we build a capsule+cfrags object before decryption. - let plaintext_bob = capsule - .withCFrag(cfrag0) - .withCFrag(cfrag1) - .decryptReencrypted(bob_sk, alice_pk, ciphertext); + let plaintext_bob = umbral.decryptReencrypted( + bob_sk, alice_pk, capsule, [cfrag0, cfrag1], ciphertext); console.assert( dec.decode(plaintext_bob) === plaintext, diff --git a/umbral-pre-wasm/examples/webpack-5-experiments/index.js b/umbral-pre-wasm/examples/webpack-5-experiments/index.js index 07f011f..aab52dc 100644 --- a/umbral-pre-wasm/examples/webpack-5-experiments/index.js +++ b/umbral-pre-wasm/examples/webpack-5-experiments/index.js @@ -27,7 +27,7 @@ let plaintext_bytes = enc.encode(plaintext); // The API here slightly differs from that in Rust. // Since wasm-pack does not support returning tuples, we return an object containing // the ciphertext and the capsule. -let result = umbral.encrypt(alice_pk, plaintext_bytes); +let {capsule, ciphertext} = umbral.encrypt(alice_pk, plaintext_bytes); let ciphertext = result.ciphertext; let capsule = result.capsule; @@ -73,10 +73,8 @@ let cfrag1 = umbral.reencrypt(capsule, kfrags[1]); // Another deviation from the Rust API. // wasm-pack does not support taking arrays as arguments, // so we build a capsule+cfrags object before decryption. -let plaintext_bob = capsule - .withCFrag(cfrag0) - .withCFrag(cfrag1) - .decryptReencrypted(bob_sk, alice_pk, ciphertext); +let plaintext_bob = umbral.decryptReencrypted( + bob_sk, alice_pk, capsule, [cfrag0, cfrag1], ciphertext); console.assert(dec.decode(plaintext_bob) === plaintext, "decryptReencrypted() failed"); From d63b8b772b0d0b984e626db15a2e73cb38a4b865 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Fri, 16 Sep 2022 21:10:14 -0700 Subject: [PATCH 08/18] Extract array processing into a function --- umbral-pre/src/bindings_wasm.rs | 41 ++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/umbral-pre/src/bindings_wasm.rs b/umbral-pre/src/bindings_wasm.rs index 3d8a38f..674f4bb 100644 --- a/umbral-pre/src/bindings_wasm.rs +++ b/umbral-pre/src/bindings_wasm.rs @@ -30,10 +30,10 @@ fn map_js_err(err: T) -> Error { /// Tries to convert an optional value (either `null` or a `#[wasm_bindgen]` marked structure) /// from `JsValue` to the Rust type. -// This is necessary since wasm-bindgen does not support having a paramter of `Option<&T>`, -// and using `Option` consumes the argument +// TODO (#25): This is necessary since wasm-bindgen does not support +// having a parameter of `Option<&T>`, and using `Option` consumes the argument // (see https://github.com/rustwasm/wasm-bindgen/issues/2370). -fn try_from_js_option<'a, T>(value: &'a JsValue) -> Result, JsValue> +fn try_from_js_option<'a, T>(value: &'a JsValue) -> Result, Error> where T: TryFrom<&'a JsValue>, >::Error: core::fmt::Display, @@ -46,6 +46,28 @@ where Ok(typed_value) } +/// Tries to convert a JS array from `JsValue` to a vector of Rust type elements. +// TODO (#23): This is necessary since wasm-bindgen does not support +// having a parameter of `Vec<&T>` +// (see https://github.com/rustwasm/wasm-bindgen/issues/111). +fn try_from_js_array(value: &JsValue) -> Result, Error> +where + for<'a> T: TryFrom<&'a JsValue>, + for<'a> >::Error: core::fmt::Display, +{ + if !js_sys::Array::is_array(value) { + return Err(Error::new("Got a non-array argument where an array was expected")); + } + let array = js_sys::Array::from(value); + let length: usize = array.length().try_into().map_err(map_js_err)?; + let mut result = Vec::::with_capacity(length); + for js in array.iter() { + let typed_elem = T::try_from(&js).map_err(map_js_err)?; + result.push(typed_elem); + } + Ok(result) +} + #[wasm_bindgen] #[derive(derive_more::AsRef)] pub struct SecretKey(umbral_pre::SecretKey); @@ -397,17 +419,8 @@ pub fn decrypt_reencrypted( vcfrags: &VerifiedCapsuleFragArray, ciphertext: &[u8], ) -> Result, Error> { - let js_val = vcfrags.as_ref(); - if !js_sys::Array::is_array(js_val) { - return Err(Error::new("`vcfrags` must be an array")); - } - let array = js_sys::Array::from(js_val); - let length: usize = array.length().try_into().map_err(map_js_err)?; - let mut backend_vcfrags = Vec::::with_capacity(length); - for js in array.iter() { - let typed_vcfrag = VerifiedCapsuleFrag::try_from(&js).map_err(map_js_err)?; - backend_vcfrags.push(typed_vcfrag.0); - } + let typed_vcfrags = try_from_js_array::(vcfrags.as_ref())?; + let backend_vcfrags = typed_vcfrags.into_iter().map(|vcfrag| vcfrag.0); umbral_pre::decrypt_reencrypted( &receiving_sk.0, &delegating_pk.0, From 9d7404a2247e41f52693f0bc47095dea0242aba5 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Fri, 16 Sep 2022 21:40:18 -0700 Subject: [PATCH 09/18] No cloning in generate_kfrags() --- umbral-pre/src/bindings_wasm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/umbral-pre/src/bindings_wasm.rs b/umbral-pre/src/bindings_wasm.rs index 674f4bb..897dd96 100644 --- a/umbral-pre/src/bindings_wasm.rs +++ b/umbral-pre/src/bindings_wasm.rs @@ -549,8 +549,8 @@ pub fn generate_kfrags( // to generate a correct signature for TypeScript. // See https://github.com/rustwasm/wasm-bindgen/issues/111 backend_kfrags - .iter() - .cloned() + .into_vec() + .into_iter() .map(VerifiedKeyFrag) .map(JsValue::from) .collect::() From 2deefc419b1c2b78091e9cac7bec28ae88cd9631 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Fri, 16 Sep 2022 22:13:19 -0700 Subject: [PATCH 10/18] Add a trait required by nucypher-core --- umbral-pre/src/bindings_wasm.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/umbral-pre/src/bindings_wasm.rs b/umbral-pre/src/bindings_wasm.rs index 897dd96..f355c64 100644 --- a/umbral-pre/src/bindings_wasm.rs +++ b/umbral-pre/src/bindings_wasm.rs @@ -252,6 +252,7 @@ impl Signature { } } +#[derive(TryFromJsValue)] #[wasm_bindgen] #[derive(Clone, Copy, Serialize, Deserialize, derive_more::AsRef, derive_more::From)] pub struct Capsule(umbral_pre::Capsule); From c7a05d4a9df207da6b1750d185b62f5bd6876dbd Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Sat, 17 Sep 2022 22:01:46 -0700 Subject: [PATCH 11/18] Remove serde usage from WASM bindgins - don't need it anymore --- umbral-pre/Cargo.toml | 2 +- umbral-pre/src/bindings_wasm.rs | 11 +++++------ umbral-pre/src/capsule_frag.rs | 1 - umbral-pre/src/key_frag.rs | 1 - umbral-pre/src/lib.rs | 2 +- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/umbral-pre/Cargo.toml b/umbral-pre/Cargo.toml index 8342b02..757e03b 100644 --- a/umbral-pre/Cargo.toml +++ b/umbral-pre/Cargo.toml @@ -46,7 +46,7 @@ rmp-serde = "0.15" default = ["default-rng"] bench-internals = ["default-rng"] bindings-python = ["pyo3", "std", "derive_more"] -bindings-wasm = ["serde-support", "js-sys", "wasm-bindgen", "derive_more", "wasm-bindgen-derive"] +bindings-wasm = ["js-sys", "wasm-bindgen", "derive_more", "wasm-bindgen-derive"] default-rng = ["getrandom", "rand_core/getrandom"] serde-support = ["serde"] std = [] diff --git a/umbral-pre/src/bindings_wasm.rs b/umbral-pre/src/bindings_wasm.rs index f355c64..b22a8b3 100644 --- a/umbral-pre/src/bindings_wasm.rs +++ b/umbral-pre/src/bindings_wasm.rs @@ -19,8 +19,6 @@ use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; use wasm_bindgen::JsCast; use wasm_bindgen_derive::TryFromJsValue; -use serde::{Deserialize, Serialize}; - use crate as umbral_pre; use crate::{DeserializableFromArray, SerializableToArray, SerializableToSecretArray}; @@ -165,7 +163,7 @@ impl SecretKeyFactory { #[derive(TryFromJsValue)] #[wasm_bindgen] -#[derive(Clone, Serialize, Deserialize, derive_more::AsRef, derive_more::From)] +#[derive(Clone, derive_more::AsRef, derive_more::From)] pub struct PublicKey(umbral_pre::PublicKey); #[wasm_bindgen] @@ -254,7 +252,7 @@ impl Signature { #[derive(TryFromJsValue)] #[wasm_bindgen] -#[derive(Clone, Copy, Serialize, Deserialize, derive_more::AsRef, derive_more::From)] +#[derive(Clone, Copy, derive_more::AsRef, derive_more::From)] pub struct Capsule(umbral_pre::Capsule); #[wasm_bindgen] @@ -334,7 +332,7 @@ impl CapsuleFrag { #[derive(TryFromJsValue)] #[wasm_bindgen] #[derive( - Clone, Serialize, Deserialize, PartialEq, Debug, derive_more::AsRef, derive_more::From, + Clone, PartialEq, Debug, derive_more::AsRef, derive_more::From, )] pub struct VerifiedCapsuleFrag(umbral_pre::VerifiedCapsuleFrag); @@ -489,8 +487,9 @@ impl KeyFrag { } } +#[derive(TryFromJsValue)] #[wasm_bindgen] -#[derive(Serialize, Deserialize, Clone, derive_more::AsRef, derive_more::From)] +#[derive(Clone, derive_more::AsRef, derive_more::From)] pub struct VerifiedKeyFrag(umbral_pre::VerifiedKeyFrag); #[wasm_bindgen] diff --git a/umbral-pre/src/capsule_frag.rs b/umbral-pre/src/capsule_frag.rs index 79efdcd..e5dac99 100644 --- a/umbral-pre/src/capsule_frag.rs +++ b/umbral-pre/src/capsule_frag.rs @@ -315,7 +315,6 @@ impl CapsuleFrag { /// Can be serialized, but cannot be deserialized directly. /// It can only be obtained from [`CapsuleFrag::verify`] or [`CapsuleFrag::skip_verification`]. #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "bindings-wasm", derive(Serialize, Deserialize))] pub struct VerifiedCapsuleFrag { cfrag: CapsuleFrag, } diff --git a/umbral-pre/src/key_frag.rs b/umbral-pre/src/key_frag.rs index 47d0a98..4ec260e 100644 --- a/umbral-pre/src/key_frag.rs +++ b/umbral-pre/src/key_frag.rs @@ -372,7 +372,6 @@ impl KeyFrag { /// Can be serialized, but cannot be deserialized directly. /// It can only be obtained from [`KeyFrag::verify`] or [`KeyFrag::skip_verification`]. #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "bindings-wasm", derive(Serialize, Deserialize))] pub struct VerifiedKeyFrag { kfrag: KeyFrag, } diff --git a/umbral-pre/src/lib.rs b/umbral-pre/src/lib.rs index 01466f0..02fbcca 100644 --- a/umbral-pre/src/lib.rs +++ b/umbral-pre/src/lib.rs @@ -139,7 +139,7 @@ mod pre; mod secret_box; mod traits; -#[cfg(any(feature = "serde-support", feature = "bindings-wasm"))] +#[cfg(feature = "serde-support")] #[cfg_attr(docsrs, doc(cfg(feature = "serde-support")))] pub mod serde_bytes; From c3377362a9c552a36581a6d26fd8c2c77063fa7f Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Sat, 17 Sep 2022 22:56:28 -0700 Subject: [PATCH 12/18] Simplify cast to Array in WASM bindings --- umbral-pre/src/bindings_wasm.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/umbral-pre/src/bindings_wasm.rs b/umbral-pre/src/bindings_wasm.rs index b22a8b3..613d3c7 100644 --- a/umbral-pre/src/bindings_wasm.rs +++ b/umbral-pre/src/bindings_wasm.rs @@ -53,10 +53,9 @@ where for<'a> T: TryFrom<&'a JsValue>, for<'a> >::Error: core::fmt::Display, { - if !js_sys::Array::is_array(value) { - return Err(Error::new("Got a non-array argument where an array was expected")); - } - let array = js_sys::Array::from(value); + let array: &js_sys::Array = value + .dyn_ref() + .ok_or_else(|| Error::new("Got a non-array argument where an array was expected"))?; let length: usize = array.length().try_into().map_err(map_js_err)?; let mut result = Vec::::with_capacity(length); for js in array.iter() { From afed7f683807de26dbd0cbefde41993ad78410bf Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Sat, 17 Sep 2022 22:57:35 -0700 Subject: [PATCH 13/18] Derive Into for some WASM bindings --- umbral-pre/Cargo.toml | 2 +- umbral-pre/src/bindings_wasm.rs | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/umbral-pre/Cargo.toml b/umbral-pre/Cargo.toml index 757e03b..71f36e1 100644 --- a/umbral-pre/Cargo.toml +++ b/umbral-pre/Cargo.toml @@ -20,7 +20,7 @@ base64 = { version = "0.13", default-features = false, features = ["alloc"] } pyo3 = { version = "0.16", optional = true } js-sys = { version = "0.3", optional = true } wasm-bindgen = { version = "0.2.74", optional = true } -derive_more = { version = "0.99", optional = true, default_features = false, features = ["as_ref", "from"] } +derive_more = { version = "0.99", optional = true, default_features = false, features = ["as_ref", "from", "into"] } wasm-bindgen-derive = { version = "0.1", optional = true } # These packages are among the dependencies of the packages above. diff --git a/umbral-pre/src/bindings_wasm.rs b/umbral-pre/src/bindings_wasm.rs index 613d3c7..7b0a54d 100644 --- a/umbral-pre/src/bindings_wasm.rs +++ b/umbral-pre/src/bindings_wasm.rs @@ -162,7 +162,7 @@ impl SecretKeyFactory { #[derive(TryFromJsValue)] #[wasm_bindgen] -#[derive(Clone, derive_more::AsRef, derive_more::From)] +#[derive(Clone, derive_more::AsRef, derive_more::From, derive_more::Into)] pub struct PublicKey(umbral_pre::PublicKey); #[wasm_bindgen] @@ -251,7 +251,7 @@ impl Signature { #[derive(TryFromJsValue)] #[wasm_bindgen] -#[derive(Clone, Copy, derive_more::AsRef, derive_more::From)] +#[derive(Clone, Copy, derive_more::AsRef, derive_more::From, derive_more::Into)] pub struct Capsule(umbral_pre::Capsule); #[wasm_bindgen] @@ -330,9 +330,7 @@ impl CapsuleFrag { #[derive(TryFromJsValue)] #[wasm_bindgen] -#[derive( - Clone, PartialEq, Debug, derive_more::AsRef, derive_more::From, -)] +#[derive(Clone, PartialEq, Debug, derive_more::AsRef, derive_more::From, derive_more::Into)] pub struct VerifiedCapsuleFrag(umbral_pre::VerifiedCapsuleFrag); #[wasm_bindgen] @@ -488,7 +486,7 @@ impl KeyFrag { #[derive(TryFromJsValue)] #[wasm_bindgen] -#[derive(Clone, derive_more::AsRef, derive_more::From)] +#[derive(Clone, derive_more::AsRef, derive_more::From, derive_more::Into)] pub struct VerifiedKeyFrag(umbral_pre::VerifiedKeyFrag); #[wasm_bindgen] From e1330018da0ed0883914789791587c59026454b8 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Sun, 18 Sep 2022 21:34:58 -0700 Subject: [PATCH 14/18] Some simplification using derived From traits --- umbral-pre/src/bindings_python.rs | 60 ++++++++++--------------------- 1 file changed, 18 insertions(+), 42 deletions(-) diff --git a/umbral-pre/src/bindings_python.rs b/umbral-pre/src/bindings_python.rs index 84835a6..cb13739 100644 --- a/umbral-pre/src/bindings_python.rs +++ b/umbral-pre/src/bindings_python.rs @@ -143,9 +143,7 @@ pub struct SecretKeyFactory { impl SecretKeyFactory { #[staticmethod] pub fn random() -> Self { - Self { - backend: umbral_pre::SecretKeyFactory::random(), - } + umbral_pre::SecretKeyFactory::random().into() } #[staticmethod] @@ -156,22 +154,16 @@ impl SecretKeyFactory { #[staticmethod] pub fn from_secure_randomness(seed: &[u8]) -> PyResult { umbral_pre::SecretKeyFactory::from_secure_randomness(seed) - .map(|backend_sk| SecretKeyFactory { - backend: backend_sk, - }) + .map(SecretKeyFactory::from) .map_err(|err| PyValueError::new_err(format!("{}", err))) } pub fn make_key(&self, label: &[u8]) -> SecretKey { - SecretKey { - backend: self.backend.make_key(label), - } + self.backend.make_key(label).into() } pub fn make_factory(&self, label: &[u8]) -> Self { - Self { - backend: self.backend.make_factory(label), - } + self.backend.make_factory(label).into() } pub fn to_secret_bytes(&self) -> PyResult { @@ -229,6 +221,7 @@ impl PublicKey { } #[pyclass(module = "umbral")] +#[derive(derive_more::From)] pub struct Signer { pub backend: umbral_pre::Signer, } @@ -237,21 +230,15 @@ pub struct Signer { impl Signer { #[new] pub fn new(sk: &SecretKey) -> Self { - Self { - backend: umbral_pre::Signer::new(sk.backend.clone()), - } + umbral_pre::Signer::new(sk.backend.clone()).into() } pub fn sign(&self, message: &[u8]) -> Signature { - Signature { - backend: self.backend.sign(message), - } + self.backend.sign(message).into() } pub fn verifying_key(&self) -> PublicKey { - PublicKey { - backend: self.backend.verifying_key(), - } + self.backend.verifying_key().into() } fn __str__(&self) -> PyResult { @@ -341,12 +328,7 @@ pub fn encrypt( ) -> PyResult<(Capsule, PyObject)> { umbral_pre::encrypt(&delegating_pk.backend, plaintext) .map(|(backend_capsule, ciphertext)| { - ( - Capsule { - backend: backend_capsule, - }, - PyBytes::new(py, &ciphertext).into(), - ) + (backend_capsule.into(), PyBytes::new(py, &ciphertext).into()) }) .map_err(|err| PyValueError::new_err(format!("{}", err))) } @@ -385,9 +367,7 @@ impl KeyFrag { receiving_pk.map(|pk| &pk.backend), ) .map_err(|(err, _kfrag)| VerificationError::new_err(format!("{}", err))) - .map(|backend_vkfrag| VerifiedKeyFrag { - backend: backend_vkfrag, - }) + .map(VerifiedKeyFrag::from) } pub fn skip_verification(&self) -> VerifiedKeyFrag { @@ -424,7 +404,7 @@ impl KeyFrag { } #[pyclass(module = "umbral")] -#[derive(PartialEq, Clone, derive_more::AsRef)] +#[derive(PartialEq, Clone, derive_more::AsRef, derive_more::From)] pub struct VerifiedKeyFrag { pub backend: umbral_pre::VerifiedKeyFrag, } @@ -434,7 +414,7 @@ impl VerifiedKeyFrag { #[staticmethod] pub fn from_verified_bytes(data: &[u8]) -> PyResult { umbral_pre::VerifiedKeyFrag::from_verified_bytes(data) - .map(|vkfrag| Self { backend: vkfrag }) + .map(Self::from) .map_err(|err| PyValueError::new_err(format!("{}", err))) } @@ -518,9 +498,7 @@ impl CapsuleFrag { &receiving_pk.backend, ) .map_err(|(err, _cfrag)| VerificationError::new_err(format!("{}", err))) - .map(|backend_vcfrag| VerifiedCapsuleFrag { - backend: backend_vcfrag, - }) + .map(VerifiedCapsuleFrag::from) } pub fn skip_verification(&self) -> VerifiedCapsuleFrag { @@ -557,7 +535,7 @@ impl CapsuleFrag { } #[pyclass(module = "umbral")] -#[derive(PartialEq, Clone, derive_more::AsRef)] +#[derive(PartialEq, Clone, derive_more::AsRef, derive_more::From)] pub struct VerifiedCapsuleFrag { pub backend: umbral_pre::VerifiedCapsuleFrag, } @@ -579,7 +557,7 @@ impl VerifiedCapsuleFrag { #[staticmethod] pub fn from_verified_bytes(data: &[u8]) -> PyResult { umbral_pre::VerifiedCapsuleFrag::from_verified_bytes(data) - .map(|vcfrag| Self { backend: vcfrag }) + .map(Self::from) .map_err(|err| PyValueError::new_err(format!("{}", err))) } @@ -602,9 +580,7 @@ impl VerifiedCapsuleFrag { #[pyfunction] pub fn reencrypt(capsule: &Capsule, kfrag: &VerifiedKeyFrag) -> VerifiedCapsuleFrag { let backend_vcfrag = umbral_pre::reencrypt(&capsule.backend, kfrag.backend.clone()); - VerifiedCapsuleFrag { - backend: backend_vcfrag, - } + VerifiedCapsuleFrag::from(backend_vcfrag) } #[pyfunction] @@ -616,11 +592,11 @@ pub fn decrypt_reencrypted( verified_cfrags: Vec, ciphertext: &[u8], ) -> PyResult { - let backend_cfrags: Vec = verified_cfrags + let backend_cfrags = verified_cfrags .iter() .cloned() .map(|vcfrag| vcfrag.backend) - .collect(); + .collect::>(); umbral_pre::decrypt_reencrypted( &receiving_sk.backend, &delegating_pk.backend, From 14b21fa595b6ee1efb5588ee3ddbc89af8f46354 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Mon, 19 Sep 2022 15:00:49 -0700 Subject: [PATCH 15/18] Make all `backend` fields private in Python bindings, use derived Into instead --- umbral-pre/src/bindings_python.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/umbral-pre/src/bindings_python.rs b/umbral-pre/src/bindings_python.rs index cb13739..3e2f3fb 100644 --- a/umbral-pre/src/bindings_python.rs +++ b/umbral-pre/src/bindings_python.rs @@ -96,7 +96,7 @@ create_exception!(umbral, VerificationError, PyException); #[pyclass(module = "umbral")] #[derive(derive_more::AsRef, derive_more::From)] pub struct SecretKey { - pub backend: umbral_pre::SecretKey, + backend: umbral_pre::SecretKey, } #[pymethods] @@ -186,9 +186,9 @@ impl SecretKeyFactory { } #[pyclass(module = "umbral")] -#[derive(Clone, PartialEq, Eq, derive_more::AsRef, derive_more::From)] +#[derive(Clone, PartialEq, Eq, derive_more::AsRef, derive_more::From, derive_more::Into)] pub struct PublicKey { - pub backend: umbral_pre::PublicKey, + backend: umbral_pre::PublicKey, } #[pymethods] @@ -221,9 +221,9 @@ impl PublicKey { } #[pyclass(module = "umbral")] -#[derive(derive_more::From)] +#[derive(derive_more::AsRef, derive_more::From)] pub struct Signer { - pub backend: umbral_pre::Signer, + backend: umbral_pre::Signer, } #[pymethods] @@ -286,9 +286,9 @@ impl Signature { } #[pyclass(module = "umbral")] -#[derive(Clone, PartialEq, derive_more::AsRef, derive_more::From)] +#[derive(Clone, PartialEq, derive_more::AsRef, derive_more::From, derive_more::Into)] pub struct Capsule { - pub backend: umbral_pre::Capsule, + backend: umbral_pre::Capsule, } #[pymethods] @@ -404,9 +404,9 @@ impl KeyFrag { } #[pyclass(module = "umbral")] -#[derive(PartialEq, Clone, derive_more::AsRef, derive_more::From)] +#[derive(PartialEq, Clone, derive_more::AsRef, derive_more::From, derive_more::Into)] pub struct VerifiedKeyFrag { - pub backend: umbral_pre::VerifiedKeyFrag, + backend: umbral_pre::VerifiedKeyFrag, } #[pymethods] @@ -535,9 +535,9 @@ impl CapsuleFrag { } #[pyclass(module = "umbral")] -#[derive(PartialEq, Clone, derive_more::AsRef, derive_more::From)] +#[derive(PartialEq, Clone, derive_more::AsRef, derive_more::From, derive_more::Into)] pub struct VerifiedCapsuleFrag { - pub backend: umbral_pre::VerifiedCapsuleFrag, + backend: umbral_pre::VerifiedCapsuleFrag, } #[pymethods] From ec3a59526e543268c6ce1ade77b4cd88eb9424cf Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Mon, 19 Sep 2022 15:12:03 -0700 Subject: [PATCH 16/18] Group all custom Typescrip types --- umbral-pre/src/bindings_wasm.rs | 36 ++++++++++++++------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/umbral-pre/src/bindings_wasm.rs b/umbral-pre/src/bindings_wasm.rs index 7b0a54d..b31c4f0 100644 --- a/umbral-pre/src/bindings_wasm.rs +++ b/umbral-pre/src/bindings_wasm.rs @@ -22,6 +22,18 @@ use wasm_bindgen_derive::TryFromJsValue; use crate as umbral_pre; use crate::{DeserializableFromArray, SerializableToArray, SerializableToSecretArray}; +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "VerifiedCapsuleFrag[]")] + pub type VerifiedCapsuleFragArray; + + #[wasm_bindgen(typescript_type = "PublicKey | null")] + pub type OptionPublicKey; + + #[wasm_bindgen(typescript_type = "VerifiedKeyFrag[]")] + pub type VerifiedKeyFragArray; +} + fn map_js_err(err: T) -> Error { Error::new(&format!("{}", err)) } @@ -398,15 +410,6 @@ pub fn decrypt_original( umbral_pre::decrypt_original(&delegating_sk.0, &capsule.0, ciphertext).map_err(map_js_err) } -// TODO (#23): using a custom type since `wasm_bindgen` currently does not support -// Vec as a parameter. -// Will probably be fixed along with https://github.com/rustwasm/wasm-bindgen/issues/111 -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "VerifiedCapsuleFrag[]")] - pub type VerifiedCapsuleFragArray; -} - #[wasm_bindgen(js_name = decryptReencrypted)] pub fn decrypt_reencrypted( receiving_sk: &SecretKey, @@ -415,6 +418,9 @@ pub fn decrypt_reencrypted( vcfrags: &VerifiedCapsuleFragArray, ciphertext: &[u8], ) -> Result, Error> { + // TODO (#23): using a custom type since `wasm_bindgen` currently does not support + // Vec as a parameter. + // Will probably be fixed along with https://github.com/rustwasm/wasm-bindgen/issues/111 let typed_vcfrags = try_from_js_array::(vcfrags.as_ref())?; let backend_vcfrags = typed_vcfrags.into_iter().map(|vcfrag| vcfrag.0); umbral_pre::decrypt_reencrypted( @@ -427,12 +433,6 @@ pub fn decrypt_reencrypted( .map_err(map_js_err) } -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "PublicKey | null")] - pub type OptionPublicKey; -} - #[wasm_bindgen] pub struct KeyFrag(umbral_pre::KeyFrag); @@ -514,12 +514,6 @@ impl VerifiedKeyFrag { } } -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "VerifiedKeyFrag[]")] - pub type VerifiedKeyFragArray; -} - #[allow(clippy::too_many_arguments)] #[wasm_bindgen(js_name = generateKFrags)] pub fn generate_kfrags( From 7196ed64ab35c6da008cb5f686fd02ac75e19165 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Mon, 19 Sep 2022 15:12:17 -0700 Subject: [PATCH 17/18] Update the changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39f60b0..73771fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Replaced `AsBackend`/`FromBackend`, `.inner()`, `.new()`, and `pub backend` with derived `AsRef`/`From`/`Into` where appropriate. (#[103]) +- Using a workaround with `wasm-bindgen-derive` to support `Option<&T>` and `&Vec` arguments, and `Vec` return values in WASM bindings. Generating correct TypeScript signatures in all the relevant cases. Affected API: `Capsule.decryptReencrypted()`, `KeyFrag.verify()`, `generate_kfrags()`. (#[103]) +- Removed `serde` usage in WASM bindings. ([#103]) + + ### Added - `Eq` markers for the types that only had `PartialEq` before. ([#100]) @@ -20,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#100]: https://github.com/nucypher/rust-umbral/pull/100 [#101]: https://github.com/nucypher/rust-umbral/pull/101 [#102]: https://github.com/nucypher/rust-umbral/pull/102 +[#103]: https://github.com/nucypher/rust-umbral/pull/103 ## [0.6.0] - 2022-08-15 From c4b4591458301c9b2c5010ae26f7ea6a199f7575 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Mon, 19 Sep 2022 15:27:07 -0700 Subject: [PATCH 18/18] Return a tuple from encrypt() instead of an EncryptionResult object --- CHANGELOG.md | 1 + umbral-pre-wasm/README.md | 5 +-- umbral-pre-wasm/examples/bundler/index.js | 5 +-- umbral-pre-wasm/examples/node/index.js | 5 +-- umbral-pre-wasm/examples/react/src/App.js | 5 +-- umbral-pre/src/bindings_wasm.rs | 41 ++++++++--------------- 6 files changed, 19 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73771fe..fdfe99a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Replaced `AsBackend`/`FromBackend`, `.inner()`, `.new()`, and `pub backend` with derived `AsRef`/`From`/`Into` where appropriate. (#[103]) - Using a workaround with `wasm-bindgen-derive` to support `Option<&T>` and `&Vec` arguments, and `Vec` return values in WASM bindings. Generating correct TypeScript signatures in all the relevant cases. Affected API: `Capsule.decryptReencrypted()`, `KeyFrag.verify()`, `generate_kfrags()`. (#[103]) - Removed `serde` usage in WASM bindings. ([#103]) +- `encrypt()` now returns an actual tuple in WASM bindings instead of a special object. (#[103]) ### Added diff --git a/umbral-pre-wasm/README.md b/umbral-pre-wasm/README.md index f799e86..1a8c852 100644 --- a/umbral-pre-wasm/README.md +++ b/umbral-pre-wasm/README.md @@ -36,10 +36,7 @@ let bob_pk = bob_sk.publicKey(); let plaintext = "Plaintext message"; let plaintext_bytes = enc.encode(plaintext); -// The API here slightly differs from that in Rust. -// Since wasm-pack does not support returning tuples, we return an object containing -// the ciphertext and the capsule. -let {capsule, ciphertext} = umbral.encrypt(alice_pk, plaintext_bytes); +let [capsule, ciphertext] = umbral.encrypt(alice_pk, plaintext_bytes); let ciphertext = result.ciphertext; let capsule = result.capsule; diff --git a/umbral-pre-wasm/examples/bundler/index.js b/umbral-pre-wasm/examples/bundler/index.js index 41fe957..c5bc7b3 100644 --- a/umbral-pre-wasm/examples/bundler/index.js +++ b/umbral-pre-wasm/examples/bundler/index.js @@ -24,10 +24,7 @@ let bob_pk = bob_sk.publicKey(); let plaintext = "Plaintext message"; let plaintext_bytes = enc.encode(plaintext); -// The API here slightly differs from that in Rust. -// Since wasm-pack does not support returning tuples, we return an object containing -// the ciphertext and the capsule. -let {capsule, ciphertext} = umbral.encrypt(alice_pk, plaintext_bytes); +let [capsule, ciphertext] = umbral.encrypt(alice_pk, plaintext_bytes); // Since data was encrypted with Alice's public key, Alice can open the capsule // and decrypt the ciphertext with her private key. diff --git a/umbral-pre-wasm/examples/node/index.js b/umbral-pre-wasm/examples/node/index.js index 41fe957..c5bc7b3 100644 --- a/umbral-pre-wasm/examples/node/index.js +++ b/umbral-pre-wasm/examples/node/index.js @@ -24,10 +24,7 @@ let bob_pk = bob_sk.publicKey(); let plaintext = "Plaintext message"; let plaintext_bytes = enc.encode(plaintext); -// The API here slightly differs from that in Rust. -// Since wasm-pack does not support returning tuples, we return an object containing -// the ciphertext and the capsule. -let {capsule, ciphertext} = umbral.encrypt(alice_pk, plaintext_bytes); +let [capsule, ciphertext] = umbral.encrypt(alice_pk, plaintext_bytes); // Since data was encrypted with Alice's public key, Alice can open the capsule // and decrypt the ciphertext with her private key. diff --git a/umbral-pre-wasm/examples/react/src/App.js b/umbral-pre-wasm/examples/react/src/App.js index ed8fe7d..81d24a9 100644 --- a/umbral-pre-wasm/examples/react/src/App.js +++ b/umbral-pre-wasm/examples/react/src/App.js @@ -49,10 +49,7 @@ function App() { let plaintext_bytes = enc.encode(plaintext); - // The API here slightly differs from that in Rust. - // Since wasm-pack does not support returning tuples, we return an object containing - // the ciphertext and the capsule. - let {capsule, ciphertext} = umbral.encrypt(alice_pk, plaintext_bytes); + let [capsule, ciphertext] = umbral.encrypt(alice_pk, plaintext_bytes); // Since data was encrypted with Alice's public key, Alice can open the capsule // and decrypt the ciphertext with her private key. diff --git a/umbral-pre/src/bindings_wasm.rs b/umbral-pre/src/bindings_wasm.rs index b31c4f0..8914547 100644 --- a/umbral-pre/src/bindings_wasm.rs +++ b/umbral-pre/src/bindings_wasm.rs @@ -14,7 +14,7 @@ use alloc::string::String; use alloc::vec::Vec; use core::fmt; -use js_sys::Error; +use js_sys::{Error, Uint8Array}; use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; use wasm_bindgen::JsCast; use wasm_bindgen_derive::TryFromJsValue; @@ -32,6 +32,9 @@ extern "C" { #[wasm_bindgen(typescript_type = "VerifiedKeyFrag[]")] pub type VerifiedKeyFragArray; + + #[wasm_bindgen(typescript_type = "[Capsule, Uint8Array]")] + pub type EncryptionResult; } fn map_js_err(err: T) -> Error { @@ -370,35 +373,19 @@ impl VerifiedCapsuleFrag { } } -#[wasm_bindgen] -pub struct EncryptionResult { - ciphertext: Box<[u8]>, - pub capsule: Capsule, -} - -#[wasm_bindgen] -impl EncryptionResult { - fn new(ciphertext: Box<[u8]>, capsule: Capsule) -> Self { - Self { - ciphertext, - capsule, - } - } - - // TODO (#24): currently can't just make the field public because `Box` doesn't implement `Copy`. - // See https://github.com/rustwasm/wasm-bindgen/issues/439 - #[wasm_bindgen(getter)] - pub fn ciphertext(&self) -> Box<[u8]> { - self.ciphertext.clone() - } -} - #[wasm_bindgen] pub fn encrypt(delegating_pk: &PublicKey, plaintext: &[u8]) -> Result { let backend_pk = delegating_pk.0; - umbral_pre::encrypt(&backend_pk, plaintext) - .map(|(capsule, ciphertext)| EncryptionResult::new(ciphertext, Capsule(capsule))) - .map_err(map_js_err) + let (capsule, ciphertext) = umbral_pre::encrypt(&backend_pk, plaintext).map_err(map_js_err)?; + + // TODO (#24): wasm-bindgen does not allow one to return a tuple directly. + // Have to cast it manually. + let capsule_js: JsValue = Capsule::from(capsule).into(); + let ciphertext_js: JsValue = Uint8Array::from(ciphertext.as_ref()).into(); + Ok([capsule_js, ciphertext_js] + .into_iter() + .collect::() + .unchecked_into::()) } #[wasm_bindgen(js_name = decryptOriginal)]