Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use static arrays in FerveoPublicKey serialization #136

Merged
merged 4 commits into from
Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion ferveo-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ 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"] }
serde_with = "2.2.0"

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>;
3 changes: 3 additions & 0 deletions ferveo-python/ferveo/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class FerveoPublicKey:
def __hash__(self) -> int:
...

def __richcmp__(self, other: FerveoPublicKey, op: int) -> bool:
...


class Validator:

Expand Down
34 changes: 27 additions & 7 deletions ferveo-python/test/test_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
Keypair,
Validator,
Dkg,
DkgPublicKey
DkgPublicKey,
FerveoPublicKey,
SharedSecret,
)


Expand Down Expand Up @@ -34,26 +36,44 @@ def make_dkg_public_key():


def make_shared_secret():
# TODO: implement this
# TODO: Implement this
# SharedSecret.from_bytes(os.urandom(584))
pass


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


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the commented-out TODOs in this test file now be uncommented due to the updated bindings?

Copy link
Member Author

@piotr-roslaniec piotr-roslaniec Jul 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried to update some of these tests but I didn't implement __richcmp__ for all types involved, so I'm going to cut some corners here. Ideally, we would have all class methods implemented, but that's a bit of work and not really worth the effort at this point IMHO.

# def test_shared_secret_serialization():
# shared_secret = create_shared_secret_instance()
# shared_secret = make_shared_secret()
# serialized = bytes(shared_secret)
# deserialized = SharedSecret.from_bytes(serialized)
# TODO: Implement comparison
# assert shared_secret == deserialized
# # TODO: Implement __richcmp__
# # assert shared_secret == deserialized
# assert serialized == bytes(deserialized)

def test_keypair_serialization():
keypair = Keypair.random()
serialized = bytes(keypair)
deserialized = Keypair.from_bytes(serialized)
# TODO: Implement comparison
# assert keypair == deserialized
# TODO: Implement __richcmp__
# assert serialized == deserialized
assert serialized == bytes(deserialized)


def test_dkg_public_key_serialization():
dkg_pk = make_dkg_public_key()
serialized = bytes(dkg_pk)
deserialized = DkgPublicKey.from_bytes(serialized)
# TODO: Implement __richcmp__
assert serialized == bytes(deserialized)
assert len(serialized) == DkgPublicKey.serialized_size()


def test_public_key_serialization():
pk = make_pk()
serialized = bytes(pk)
deserialized = FerveoPublicKey.from_bytes(serialized)
assert pk == deserialized
assert len(serialized) == FerveoPublicKey.serialized_size()
Copy link
Member

@derekpierre derekpierre Jul 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to also use from_bytes(...) in the test and compare the deserialized object to the original? So not just the serialization, but the deserialization as well.

Same for L59 as well.

17 changes: 15 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 Expand Up @@ -357,6 +362,14 @@ mod test_ferveo_api {
(messages, validators, validator_keypairs)
}

#[test]
fn test_dkg_pk_serialization() {
let dkg_pk = DkgPublicKey::random();
let serialized = dkg_pk.to_bytes().unwrap();
let deserialized = DkgPublicKey::from_bytes(&serialized).unwrap();
assert_eq!(dkg_pk, deserialized);
}

#[test]
fn test_server_api_tdec_precomputed() {
let rng = &mut StdRng::seed_from_u64(0);
Expand Down
Loading
Loading