From 60cad202a2bf62cec27ecfd5cd1a81effe1e4e37 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 3 Jul 2023 15:46:30 +0200 Subject: [PATCH] feat: expose ferveo variant in bindings --- ferveo-python/ferveo/__init__.py | 2 ++ ferveo-python/ferveo/__init__.pyi | 14 ++++++++ ferveo-python/test/test_serialization.py | 7 +++- ferveo/src/api.rs | 32 +++++++++++++++++- ferveo/src/bindings_python.rs | 43 +++++++++++++++++++++++- ferveo/src/bindings_wasm.rs | 37 ++++++++++++++++++++ ferveo/src/lib.rs | 3 ++ 7 files changed, 135 insertions(+), 3 deletions(-) diff --git a/ferveo-python/ferveo/__init__.py b/ferveo-python/ferveo/__init__.py index fbaab504..869f3a18 100644 --- a/ferveo-python/ferveo/__init__.py +++ b/ferveo-python/ferveo/__init__.py @@ -15,6 +15,7 @@ DkgPublicKey, SharedSecret, ValidatorMessage, + FerveoVariant, ThresholdEncryptionError, InvalidShareNumberParameter, InvalidDkgStateToDeal, @@ -32,4 +33,5 @@ ValidatorsNotSorted, ValidatorPublicKeyMismatch, SerializationError, + InvalidVariant, ) diff --git a/ferveo-python/ferveo/__init__.pyi b/ferveo-python/ferveo/__init__.pyi index 170e98b0..b31a7b6c 100644 --- a/ferveo-python/ferveo/__init__.pyi +++ b/ferveo-python/ferveo/__init__.pyi @@ -170,6 +170,16 @@ class SharedSecret: ... +# TODO: Figure out how to make this a proper enum or expose a plain class +class FerveoVariant: + + def from_string(self, variant: str) -> FerveoVariant: + ... + + def __str__(self) -> str: + ... + + def encrypt(message: bytes, add: bytes, dkg_public_key: DkgPublicKey) -> Ciphertext: ... @@ -260,3 +270,7 @@ class ValidatorPublicKeyMismatch(Exception): class SerializationError(Exception): pass + + +class InvalidVariant(Exception): + pass diff --git a/ferveo-python/test/test_serialization.py b/ferveo-python/test/test_serialization.py index e5de35f0..da776f1e 100644 --- a/ferveo-python/test/test_serialization.py +++ b/ferveo-python/test/test_serialization.py @@ -4,6 +4,7 @@ Dkg, DkgPublicKey, FerveoPublicKey, + FerveoVariant, ) @@ -64,7 +65,11 @@ def test_dkg_public_key_serialization(): assert len(serialized) == DkgPublicKey.serialized_size() -def test_dkg_public_key_serialization(): +def test_public_key_serialization(): pk = make_pk() serialized = bytes(pk) assert len(serialized) == FerveoPublicKey.serialized_size() + +# TODO: Consider different API, FerveoVariant.Precomputed etc. +def test_ferveo_variant_serialization(): + variant = FerveoVariant('FerveoVariant::Precomputed') diff --git a/ferveo/src/api.rs b/ferveo/src/api.rs index 99c5af02..1c3bfe35 100644 --- a/ferveo/src/api.rs +++ b/ferveo/src/api.rs @@ -1,4 +1,4 @@ -use std::io; +use std::{fmt, io}; use ark_poly::{EvaluationDomain, Radix2EvaluationDomain}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; @@ -69,6 +69,36 @@ pub fn decrypt_with_shared_secret( .map_err(Error::from) } +#[serde_as] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum FerveoVariant { + Simple = 0_isize, + Precomputed = 1_isize, +} + +impl fmt::Display for FerveoVariant { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + +impl FerveoVariant { + pub fn as_str(&self) -> &'static str { + match self { + FerveoVariant::Simple => "FerveoVariant::Simple", + FerveoVariant::Precomputed => "FerveoVariant::Precomputed", + } + } + + pub fn from_string(s: &str) -> Result { + match s { + "FerveoVariant::Simple" => Ok(FerveoVariant::Simple), + "FerveoVariant::Precomputed" => Ok(FerveoVariant::Precomputed), + _ => Err(Error::InvalidVariant(s.to_string())), + } + } +} + #[serde_as] #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct DkgPublicKey( diff --git a/ferveo/src/bindings_python.rs b/ferveo/src/bindings_python.rs index 05756164..408a4164 100644 --- a/ferveo/src/bindings_python.rs +++ b/ferveo/src/bindings_python.rs @@ -6,7 +6,7 @@ use pyo3::{ create_exception, exceptions::{PyException, PyRuntimeError, PyValueError}, prelude::*, - types::{PyBytes, PyUnicode}, + types::{PyBytes, PyString, PyUnicode}, PyClass, }; use rand::thread_rng; @@ -94,6 +94,9 @@ impl From for PyErr { expected, actual )) } + Error::InvalidVariant(variant) => { + InvalidVariant::new_err(variant.to_string()) + } }, _ => default(), } @@ -128,6 +131,7 @@ create_exception!(exceptions, ValidatorsNotSorted, PyValueError); create_exception!(exceptions, ValidatorPublicKeyMismatch, PyValueError); create_exception!(exceptions, SerializationError, PyValueError); create_exception!(exceptions, InvalidByteLength, PyValueError); +create_exception!(exceptions, InvalidVariant, PyValueError); fn from_py_bytes(bytes: &[u8]) -> PyResult { T::from_bytes(bytes) @@ -278,6 +282,41 @@ pub fn decrypt_with_shared_secret( .map_err(|err| FerveoPythonError::FerveoError(err).into()) } +#[pyclass(module = "ferveo")] +pub enum FerveoVariant { + Simple = api::FerveoVariant::Simple as isize, + Precomputed = api::FerveoVariant::Precomputed as isize, +} + +impl From for FerveoVariant { + fn from(variant: api::FerveoVariant) -> Self { + match variant { + api::FerveoVariant::Simple => FerveoVariant::Simple, + api::FerveoVariant::Precomputed => FerveoVariant::Precomputed, + } + } +} + +#[pymethods] +impl FerveoVariant { + #[new] + pub fn new(s: &PyString) -> PyResult { + api::FerveoVariant::from_string(s.to_string().as_str()) + .map_err(|err| FerveoPythonError::FerveoError(err).into()) + .map(|variant| variant.into()) + } + + #[getter] + pub fn __str__(&self) -> String { + match self { + FerveoVariant::Simple => api::FerveoVariant::Simple.to_string(), + FerveoVariant::Precomputed => { + api::FerveoVariant::Precomputed.to_string() + } + } + } +} + #[pyclass(module = "ferveo")] #[derive(derive_more::AsRef)] pub struct SharedSecret(api::SharedSecret); @@ -600,6 +639,7 @@ pub fn make_ferveo_py_module(py: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; // Exceptions m.add( @@ -655,6 +695,7 @@ pub fn make_ferveo_py_module(py: Python<'_>, m: &PyModule) -> PyResult<()> { py.get_type::(), )?; m.add("SerializationError", py.get_type::())?; + m.add("InvalidVariant", py.get_type::())?; Ok(()) } diff --git a/ferveo/src/bindings_wasm.rs b/ferveo/src/bindings_wasm.rs index 8e071564..5f23596a 100644 --- a/ferveo/src/bindings_wasm.rs +++ b/ferveo/src/bindings_wasm.rs @@ -161,6 +161,43 @@ macro_rules! generate_common_methods { }; } +#[derive(TryFromJsValue)] +#[wasm_bindgen] +#[derive(Clone, Debug, derive_more::AsRef, derive_more::From)] +pub struct FerveoVariant { + variant: api::FerveoVariant, +} + +#[wasm_bindgen] +impl FerveoVariant { + #[wasm_bindgen(js_name = "fromString")] + pub fn from_string(s: &str) -> JsResult { + let variant = api::FerveoVariant::from_string(s).map_err(map_js_err)?; + Ok(Self { variant }) + } + + // Allow `to_string` here because we want to expose it to bindings + #[allow(clippy::inherent_to_string)] + #[wasm_bindgen(js_name = "toString")] + pub fn to_string(&self) -> String { + self.variant.to_string() + } + + #[wasm_bindgen(js_name = "Precomputed", getter)] + pub fn precomputed() -> FerveoVariant { + Self { + variant: api::FerveoVariant::Precomputed, + } + } + + #[wasm_bindgen(js_name = "Simple", getter)] + pub fn simple() -> FerveoVariant { + Self { + variant: api::FerveoVariant::Simple, + } + } +} + #[derive(TryFromJsValue)] #[wasm_bindgen] #[derive(Clone, Debug, derive_more::AsRef, derive_more::From)] diff --git a/ferveo/src/lib.rs b/ferveo/src/lib.rs index 7e1b3657..036e2fca 100644 --- a/ferveo/src/lib.rs +++ b/ferveo/src/lib.rs @@ -101,6 +101,9 @@ pub enum Error { #[error("Invalid byte length. Expected {0}, got {1}")] InvalidByteLength(usize, usize), + + #[error("Invalid variant: {0}")] + InvalidVariant(String), } pub type Result = std::result::Result;