Skip to content

Commit

Permalink
feat! use static arrays in ferveo public key serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
piotr-roslaniec committed Jul 3, 2023
1 parent 8dc57d3 commit f9ac1d7
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 96 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ferveo-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ ark-ec = "0.4"
ark-serialize = { version = "0.4", features = ["derive"] }
ark-std = "0.4"
bincode = "1.3.3"
generic-array = "0.14.7"
rand = "0.8"
rand_core = "0.6"
serde = { version = "1.0", features = ["derive"] }
Expand Down
48 changes: 27 additions & 21 deletions ferveo-common/src/keypair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,26 @@ use ark_std::{
rand::{prelude::StdRng, RngCore, SeedableRng},
UniformRand,
};
use rand_core::Error;
use generic_array::{typenum::U96, GenericArray};
use serde::*;
use serde_with::serde_as;

use crate::serialization;
use crate::{serialization, Error, Result};

// Normally, we would use a custom trait for this, but we can't because
// the arkworks will not let us create a blanket implementation for G1Affine
// and Fr types. So instead, we're using this shared utility function:
pub fn to_bytes<T: CanonicalSerialize>(
item: &T,
) -> Result<Vec<u8>, ark_serialize::SerializationError> {
pub fn to_bytes<T: CanonicalSerialize>(item: &T) -> Result<Vec<u8>> {
let mut writer = Vec::new();
item.serialize_compressed(&mut writer)?;
item.serialize_compressed(&mut writer)
.map_err(Error::SerializationError)?;
Ok(writer)
}

pub fn from_bytes<T: CanonicalDeserialize>(
bytes: &[u8],
) -> Result<T, ark_serialize::SerializationError> {
pub fn from_bytes<T: CanonicalDeserialize>(bytes: &[u8]) -> Result<T> {
let mut reader = io::Cursor::new(bytes);
let item = T::deserialize_compressed(&mut reader)?;
let item = T::deserialize_compressed(&mut reader)
.map_err(Error::SerializationError)?;
Ok(item)
}

Expand All @@ -39,17 +37,25 @@ pub struct PublicKey<E: Pairing> {
}

impl<E: Pairing> PublicKey<E> {
pub fn to_bytes(
&self,
) -> Result<Vec<u8>, ark_serialize::SerializationError> {
to_bytes(&self.encryption_key)
pub fn to_bytes(&self) -> Result<GenericArray<u8, U96>> {
let as_bytes = to_bytes(&self.encryption_key)?;
Ok(GenericArray::<u8, U96>::from_slice(&as_bytes).to_owned())
}

pub fn from_bytes(
bytes: &[u8],
) -> Result<Self, ark_serialize::SerializationError> {
let encryption_key = from_bytes(bytes)?;
Ok(PublicKey::<E> { encryption_key })
pub fn from_bytes(bytes: &[u8]) -> Result<PublicKey<E>> {
let bytes =
GenericArray::<u8, U96>::from_exact_iter(bytes.iter().cloned())
.ok_or_else(|| {
Error::InvalidByteLength(
Self::serialized_size(),
bytes.len(),
)
})?;
from_bytes(&bytes).map(|encryption_key| PublicKey { encryption_key })
}

pub fn serialized_size() -> usize {
96
}
}

Expand Down Expand Up @@ -129,9 +135,9 @@ impl<E: Pairing> Keypair<E> {
32
}

pub fn from_secure_randomness(bytes: &[u8]) -> Result<Self, Error> {
pub fn from_secure_randomness(bytes: &[u8]) -> Result<Self> {
if bytes.len() != Self::secure_randomness_size() {
return Err(Error::new("Invalid seed length"));
return Err(Error::InvalidSeedLength(bytes.len()));
}
let mut seed = [0; 32];
seed.copy_from_slice(bytes);
Expand Down
31 changes: 31 additions & 0 deletions ferveo-common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
pub mod keypair;
pub mod serialization;

use std::{fmt, fmt::Formatter};

pub use keypair::*;
pub use serialization::*;

#[derive(Debug)]
pub enum Error {
InvalidByteLength(usize, usize),
SerializationError(ark_serialize::SerializationError),
InvalidSeedLength(usize),
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Error::InvalidByteLength(expected, actual) => {
write!(
f,
"Invalid byte length: expected {}, actual {}",
expected, actual
)
}
Error::SerializationError(e) => {
write!(f, "Serialization error: {}", e)
}
Error::InvalidSeedLength(len) => {
write!(f, "Invalid seed length: {}", len)
}
}
}
}

type Result<T> = std::result::Result<T, Error>;
13 changes: 12 additions & 1 deletion ferveo-python/test/test_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
Keypair,
Validator,
Dkg,
DkgPublicKey
DkgPublicKey,
FerveoPublicKey,
)


Expand Down Expand Up @@ -38,6 +39,10 @@ def make_shared_secret():
pass


def make_pk():
return Keypair.random().public_key()


# def test_shared_secret_serialization():
# shared_secret = create_shared_secret_instance()
# serialized = bytes(shared_secret)
Expand All @@ -57,3 +62,9 @@ def test_dkg_public_key_serialization():
dkg_pk = make_dkg_public_key()
serialized = bytes(dkg_pk)
assert len(serialized) == DkgPublicKey.serialized_size()


def test_dkg_public_key_serialization():
pk = make_pk()
serialized = bytes(pk)
assert len(serialized) == FerveoPublicKey.serialized_size()
9 changes: 7 additions & 2 deletions ferveo/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,12 @@ impl DkgPublicKey {
pub fn from_bytes(bytes: &[u8]) -> Result<DkgPublicKey> {
let bytes =
GenericArray::<u8, U48>::from_exact_iter(bytes.iter().cloned())
.ok_or(Error::InvalidByteLength(48, bytes.len()))?;
.ok_or_else(|| {
Error::InvalidByteLength(
Self::serialized_size(),
bytes.len(),
)
})?;
from_bytes(&bytes).map(DkgPublicKey)
}

Expand Down Expand Up @@ -198,7 +203,7 @@ impl AggregatedTranscript {
shares_num: u32,
messages: &[ValidatorMessage],
) -> Result<bool> {
let pvss_params = crate::pvss::PubliclyVerifiableParams::<E>::default();
let pvss_params = PubliclyVerifiableParams::<E>::default();
let domain = Radix2EvaluationDomain::<Fr>::new(shares_num as usize)
.expect("Unable to construct an evaluation domain");

Expand Down
95 changes: 54 additions & 41 deletions ferveo/src/bindings_python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,18 @@ where
}
}

macro_rules! generate_common_methods {
// TODO: Consider implementing macros to generate following methods

// fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult<bool> {
// richcmp(self, other, op)
// }

// fn __hash__(&self) -> PyResult<isize> {
// let bytes = self.0.to_bytes()?;
// hash(stringify!($struct_name), &bytes)
// }

macro_rules! generate_bytes_serialization {
($struct_name:ident) => {
#[pymethods]
impl $struct_name {
Expand All @@ -184,17 +195,35 @@ macro_rules! generate_common_methods {
fn __bytes__(&self) -> PyResult<PyObject> {
to_py_bytes(&self.0)
}
}
};
}

// TODO: Consider implementing this for all structs - Requires PartialOrd and other traits
macro_rules! generate_boxed_bytes_serialization {
($struct_name:ident, $inner_struct_name:ident) => {
#[pymethods]
impl $struct_name {
#[staticmethod]
pub fn from_bytes(bytes: &[u8]) -> PyResult<Self> {
Ok($struct_name(
$inner_struct_name::from_bytes(bytes).map_err(|err| {
FerveoPythonError::Other(err.to_string())
})?,
))
}

// fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult<bool> {
// richcmp(self, other, op)
// }
fn __bytes__(&self) -> PyResult<PyObject> {
let bytes = self
.0
.to_bytes()
.map_err(|err| FerveoPythonError::Other(err.to_string()))?;
as_py_bytes(&bytes)
}

// fn __hash__(&self) -> PyResult<isize> {
// let bytes = self.0.to_bytes()?;
// hash(stringify!($struct_name), &bytes)
// }
#[staticmethod]
pub fn serialized_size() -> usize {
$inner_struct_name::serialized_size()
}
}
};
}
Expand Down Expand Up @@ -253,13 +282,13 @@ pub fn decrypt_with_shared_secret(
#[derive(derive_more::AsRef)]
pub struct SharedSecret(api::SharedSecret);

generate_common_methods!(SharedSecret);
generate_bytes_serialization!(SharedSecret);

#[pyclass(module = "ferveo")]
#[derive(derive_more::From, derive_more::AsRef)]
pub struct Keypair(api::Keypair);

generate_common_methods!(Keypair);
generate_bytes_serialization!(Keypair);

#[pymethods]
impl Keypair {
Expand All @@ -285,13 +314,15 @@ impl Keypair {
}
}

type InnerPublicKey = api::PublicKey;

#[pyclass(module = "ferveo")]
#[derive(
Clone, PartialEq, PartialOrd, Eq, derive_more::From, derive_more::AsRef,
)]
pub struct FerveoPublicKey(api::PublicKey);
pub struct FerveoPublicKey(InnerPublicKey);

generate_common_methods!(FerveoPublicKey);
generate_boxed_bytes_serialization!(FerveoPublicKey, InnerPublicKey);

#[pymethods]
impl FerveoPublicKey {
Expand All @@ -303,7 +334,7 @@ impl FerveoPublicKey {
let bytes = self
.0
.to_bytes()
.map_err(|err| FerveoPythonError::FerveoError(err.into()))?;
.map_err(|err| FerveoPythonError::Other(err.to_string()))?;
hash("FerveoPublicKey", &bytes)
}
}
Expand Down Expand Up @@ -339,33 +370,15 @@ impl Validator {
#[derive(Clone, derive_more::From, derive_more::AsRef)]
pub struct Transcript(api::Transcript);

generate_common_methods!(Transcript);
generate_bytes_serialization!(Transcript);

type InnerDkgPublicKey = api::DkgPublicKey;

#[pyclass(module = "ferveo")]
#[derive(Clone, derive_more::From, derive_more::AsRef)]
pub struct DkgPublicKey(api::DkgPublicKey);
pub struct DkgPublicKey(InnerDkgPublicKey);

#[pymethods]
impl DkgPublicKey {
#[staticmethod]
pub fn from_bytes(bytes: &[u8]) -> PyResult<Self> {
Ok(Self(
api::DkgPublicKey::from_bytes(bytes)
.map_err(FerveoPythonError::FerveoError)?,
))
}

fn __bytes__(&self) -> PyResult<PyObject> {
let bytes =
self.0.to_bytes().map_err(FerveoPythonError::FerveoError)?;
as_py_bytes(&bytes)
}

#[staticmethod]
pub fn serialized_size() -> usize {
api::DkgPublicKey::serialized_size()
}
}
generate_boxed_bytes_serialization!(DkgPublicKey, InnerDkgPublicKey);

#[pyclass(module = "ferveo")]
#[derive(derive_more::From, derive_more::AsRef, Clone)]
Expand Down Expand Up @@ -462,25 +475,25 @@ impl Dkg {
)]
pub struct Ciphertext(api::Ciphertext);

generate_common_methods!(Ciphertext);
generate_bytes_serialization!(Ciphertext);

#[pyclass(module = "ferveo")]
#[derive(Clone, derive_more::AsRef, derive_more::From)]
pub struct DecryptionShareSimple(api::DecryptionShareSimple);

generate_common_methods!(DecryptionShareSimple);
generate_bytes_serialization!(DecryptionShareSimple);

#[pyclass(module = "ferveo")]
#[derive(Clone, derive_more::AsRef, derive_more::From)]
pub struct DecryptionSharePrecomputed(api::DecryptionSharePrecomputed);

generate_common_methods!(DecryptionSharePrecomputed);
generate_bytes_serialization!(DecryptionSharePrecomputed);

#[pyclass(module = "ferveo")]
#[derive(derive_more::From, derive_more::AsRef)]
pub struct AggregatedTranscript(api::AggregatedTranscript);

generate_common_methods!(AggregatedTranscript);
generate_bytes_serialization!(AggregatedTranscript);

#[pymethods]
impl AggregatedTranscript {
Expand Down
Loading

0 comments on commit f9ac1d7

Please sign in to comment.