Make serialization less wasteful.

This removes the unneeded length from key and signature representation,
removing 8 bytes from each. Also adds `from_bytes` and `to_bytes`
methods to convert keys and signatures.
This commit is contained in:
Andreas Fackler 2018-11-21 17:05:46 +01:00 committed by Andreas Fackler
parent 4fec9da3d6
commit c7eda7a14a
4 changed files with 161 additions and 68 deletions

View File

@ -28,3 +28,13 @@ mod tests {
is_send_and_sync(Error::NotEnoughShares);
}
}
/// An error reading a structure from an array of bytes.
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum FromBytesError {
#[fail(display = "Invalid representation.")]
Invalid,
}
/// The result of attempting to read a structure from an array of bytes.
pub type FromBytesResult<T> = ::std::result::Result<T, FromBytesError>;

View File

@ -44,29 +44,37 @@ use byteorder::{BigEndian, ByteOrder};
use hex_fmt::HexFmt;
use init_with::InitWith;
use log::debug;
use pairing::{CurveAffine, CurveProjective, Engine, Field};
use pairing::{CurveAffine, CurveProjective, EncodedPoint, Engine, Field};
use rand::{ChaChaRng, OsRng, Rand, Rng, SeedableRng};
use rand_derive::Rand;
use tiny_keccak::sha3_256;
use error::{Error, Result};
use error::{Error, FromBytesError, FromBytesResult, Result};
use into_fr::IntoFr;
use poly::{Commitment, Poly};
use secret::{clear_fr, ContainsSecret, MemRange, FR_SIZE};
use serde_derive::{Deserialize, Serialize};
#[cfg(not(feature = "use-insecure-test-only-mock-crypto"))]
pub use pairing::bls12_381::{Bls12 as PEngine, Fr, G1Affine, G2Affine, G1, G2};
pub use pairing::bls12_381::{Bls12 as PEngine, Fr, FrRepr, G1Affine, G2Affine, G1, G2};
#[cfg(feature = "use-insecure-test-only-mock-crypto")]
mod mock;
#[cfg(feature = "use-insecure-test-only-mock-crypto")]
pub use mock::{
Mersenne8 as Fr, Mocktography as PEngine, Ms8Affine as G1Affine, Ms8Affine as G2Affine,
Ms8Projective as G1, Ms8Projective as G2,
Mersenne8 as Fr, Mersenne8 as FrRepr, Mocktography as PEngine, Ms8Affine as G1Affine,
Ms8Affine as G2Affine, Ms8Projective as G1, Ms8Projective as G2, PK_SIZE, SIG_SIZE,
};
/// The size of a key's representation in bytes.
#[cfg(not(feature = "use-insecure-test-only-mock-crypto"))]
pub const PK_SIZE: usize = 48;
/// The size of a signature's representation in bytes.
#[cfg(not(feature = "use-insecure-test-only-mock-crypto"))]
pub const SIG_SIZE: usize = 96;
/// The number of words (`u32`) in a ChaCha RNG seed.
const CHACHA_RNG_SEED_SIZE: usize = 8;
@ -122,9 +130,20 @@ impl PublicKey {
Ciphertext(u, v, w)
}
/// Returns the key with the given representation, if valid.
pub fn from_bytes<B: Borrow<[u8; PK_SIZE]>>(bytes: B) -> FromBytesResult<Self> {
let mut compressed: <G1Affine as CurveAffine>::Compressed = EncodedPoint::empty();
compressed.as_mut().copy_from_slice(bytes.borrow());
let opt_affine = compressed.into_affine().ok();
let projective = opt_affine.ok_or(FromBytesError::Invalid)?.into_projective();
Ok(PublicKey(projective))
}
/// Returns a byte string representation of the public key.
pub fn to_bytes(&self) -> Vec<u8> {
self.0.into_affine().into_compressed().as_ref().to_vec()
pub fn to_bytes(&self) -> [u8; PK_SIZE] {
let mut bytes = [0u8; PK_SIZE];
bytes.copy_from_slice(self.0.into_affine().into_compressed().as_ref());
bytes
}
}
@ -159,8 +178,13 @@ impl PublicKeyShare {
PEngine::pairing(share.0, hash) == PEngine::pairing((self.0).0, *w)
}
/// Returns the key share with the given representation, if valid.
pub fn from_bytes<B: Borrow<[u8; PK_SIZE]>>(bytes: B) -> FromBytesResult<Self> {
Ok(PublicKeyShare(PublicKey::from_bytes(bytes)?))
}
/// Returns a byte string representation of the public key share.
pub fn to_bytes(&self) -> Vec<u8> {
pub fn to_bytes(&self) -> [u8; PK_SIZE] {
self.0.to_bytes()
}
}
@ -191,6 +215,22 @@ impl Signature {
debug!("Signature: {:0.10}, parity: {}", HexFmt(uncomp), parity);
parity
}
/// Returns the signature with the given representation, if valid.
pub fn from_bytes<B: Borrow<[u8; SIG_SIZE]>>(bytes: B) -> FromBytesResult<Self> {
let mut compressed: <G2Affine as CurveAffine>::Compressed = EncodedPoint::empty();
compressed.as_mut().copy_from_slice(bytes.borrow());
let opt_affine = compressed.into_affine().ok();
let projective = opt_affine.ok_or(FromBytesError::Invalid)?.into_projective();
Ok(Signature(projective))
}
/// Returns a byte string representation of the signature.
pub fn to_bytes(&self) -> [u8; SIG_SIZE] {
let mut bytes = [0u8; SIG_SIZE];
bytes.copy_from_slice(self.0.into_affine().into_compressed().as_ref());
bytes
}
}
/// A signature share.
@ -205,6 +245,18 @@ impl fmt::Debug for SignatureShare {
}
}
impl SignatureShare {
/// Returns the signature share with the given representation, if valid.
pub fn from_bytes<B: Borrow<[u8; SIG_SIZE]>>(bytes: B) -> FromBytesResult<Self> {
Ok(SignatureShare(Signature::from_bytes(bytes)?))
}
/// Returns a byte string representation of the signature share.
pub fn to_bytes(&self) -> [u8; SIG_SIZE] {
self.0.to_bytes()
}
}
/// A secret key; wraps a single prime field element. The field element is
/// heap allocated to avoid any stack copying that result when passing
/// `SecretKey`s between stack frames.
@ -827,6 +879,17 @@ mod tests {
assert_eq!(20, xwh(g0, &[0; 20]).len());
}
#[test]
fn test_from_to_bytes() {
let sk: SecretKey = random();
let sig = sk.sign("Please sign here: ______");
let pk = sk.public_key();
let pk2 = PublicKey::from_bytes(pk.to_bytes()).expect("invalid pk representation");
assert_eq!(pk, pk2);
let sig2 = Signature::from_bytes(sig.to_bytes()).expect("invalid sig representation");
assert_eq!(sig, sig2);
}
#[test]
fn test_serde() {
use bincode;
@ -836,9 +899,17 @@ mod tests {
let pk = sk.public_key();
let ser_pk = bincode::serialize(&pk).expect("serialize public key");
let deser_pk = bincode::deserialize(&ser_pk).expect("deserialize public key");
assert_eq!(ser_pk.len(), PK_SIZE);
assert_eq!(pk, deser_pk);
let ser_sig = bincode::serialize(&sig).expect("serialize signature");
let deser_sig = bincode::deserialize(&ser_sig).expect("deserialize signature");
assert_eq!(ser_sig.len(), SIG_SIZE);
assert_eq!(sig, deser_sig);
}
#[test]
fn test_size() {
assert_eq!(<G1Affine as CurveAffine>::Compressed::size(), PK_SIZE);
assert_eq!(<G2Affine as CurveAffine>::Compressed::size(), SIG_SIZE);
}
}

View File

@ -21,6 +21,11 @@ use super::{CurveAffine, CurveProjective, Engine};
pub use self::ms8::Mersenne8;
/// The size of a key's representation in bytes.
pub const PK_SIZE: usize = 4;
/// The size of a signature's representation in bytes.
pub const SIG_SIZE: usize = 4;
/// A `pairing` Engine based on `Mersenne8` prime fields.
#[derive(Clone, Debug)]
pub struct Mocktography;
@ -292,7 +297,7 @@ mod test {
// There are copy & pasted results of calculations from external programs in these tests.
#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))]
use super::{Mersenne8, Mocktography, Ms8Affine};
use super::{EncodedPoint, Mersenne8, Mocktography, Ms8Affine, PK_SIZE, SIG_SIZE};
use Engine;
#[test]
@ -319,4 +324,10 @@ mod test {
assert_eq!(Mocktography::pairing(q, p), Mersenne8::new(res));
}
}
#[test]
fn size() {
assert_eq!(<Ms8Affine as EncodedPoint>::size(), PK_SIZE);
assert_eq!(<Ms8Affine as EncodedPoint>::size(), SIG_SIZE);
}
}

View File

@ -1,9 +1,9 @@
use std::borrow::Cow;
use super::G1;
use serde::de::Error as DeserializeError;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_derive::{Deserialize, Serialize};
use G1;
use poly::{coeff_pos, BivarCommitment};
@ -43,11 +43,13 @@ impl<'de> Deserialize<'de> for BivarCommitment {
/// Serialization and deserialization of a group element's compressed representation.
pub mod projective {
use pairing::{CurveAffine, CurveProjective, EncodedPoint};
use serde::de::Error as DeserializeError;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;
use std::marker::PhantomData;
use pairing::{CurveAffine, CurveProjective, EncodedPoint};
use serde::de::{Error as DeserializeError, SeqAccess, Visitor};
use serde::{ser::SerializeTuple, Deserializer, Serializer};
const ERR_LEN: &str = "wrong length of deserialized group element";
const ERR_CODE: &str = "deserialized bytes don't encode a group element";
pub fn serialize<S, C>(c: &C, s: S) -> Result<S::Ok, S::Error>
@ -55,7 +57,12 @@ pub mod projective {
S: Serializer,
C: CurveProjective,
{
c.into_affine().into_compressed().as_ref().serialize(s)
let len = <C::Affine as CurveAffine>::Compressed::size();
let mut tup = s.serialize_tuple(len)?;
for byte in c.into_affine().into_compressed().as_ref() {
tup.serialize_element(byte)?;
}
tup.end()
}
pub fn deserialize<'de, D, C>(d: D) -> Result<C, D::Error>
@ -63,14 +70,32 @@ pub mod projective {
D: Deserializer<'de>,
C: CurveProjective,
{
let bytes = <Vec<u8>>::deserialize(d)?;
if bytes.len() != <C::Affine as CurveAffine>::Compressed::size() {
return Err(D::Error::custom(ERR_LEN));
struct TupleVisitor<C> {
_ph: PhantomData<C>,
}
let mut compressed = <C::Affine as CurveAffine>::Compressed::empty();
compressed.as_mut().copy_from_slice(&bytes);
let to_err = |_| D::Error::custom(ERR_CODE);
Ok(compressed.into_affine().map_err(to_err)?.into_projective())
impl<'de, C: CurveProjective> Visitor<'de> for TupleVisitor<C> {
type Value = C;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
let len = <C::Affine as CurveAffine>::Compressed::size();
write!(f, "a tuple of size {}", len)
}
#[inline]
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<C, A::Error> {
let mut compressed = <C::Affine as CurveAffine>::Compressed::empty();
for (i, byte) in compressed.as_mut().iter_mut().enumerate() {
let len_err = || DeserializeError::invalid_length(i, &self);
*byte = seq.next_element()?.ok_or_else(len_err)?;
}
let to_err = |_| DeserializeError::custom(ERR_CODE);
Ok(compressed.into_affine().map_err(to_err)?.into_projective())
}
}
let len = <C::Affine as CurveAffine>::Compressed::size();
d.deserialize_tuple(len, TupleVisitor { _ph: PhantomData })
}
}
@ -130,90 +155,66 @@ pub mod projective_vec {
/// Serialization and deserialization of vectors of field elements.
pub mod field_vec {
use std::borrow::Borrow;
use std::marker::PhantomData;
use pairing::{PrimeField, PrimeFieldRepr};
use pairing::PrimeField;
use serde::de::Error as DeserializeError;
use serde::ser::Error as SerializeError;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use {Fr, FrRepr};
/// A wrapper type to facilitate serialization and deserialization of field elements.
pub struct FieldWrap<F, B>(B, PhantomData<F>);
pub struct FieldWrap<B>(B);
impl<F, B> FieldWrap<F, B> {
pub fn new(f: B) -> Self {
FieldWrap(f, PhantomData)
}
}
impl<F> FieldWrap<F, F> {
pub fn into_inner(self) -> F {
impl FieldWrap<Fr> {
pub fn into_inner(self) -> Fr {
self.0
}
}
impl<F: PrimeField, B: Borrow<F>> Serialize for FieldWrap<F, B> {
impl<B: Borrow<Fr>> Serialize for FieldWrap<B> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let mut bytes = Vec::new();
self.0
.borrow()
.into_repr()
.write_be(&mut bytes)
.map_err(|_| S::Error::custom("failed to write bytes"))?;
bytes.serialize(s)
self.0.borrow().into_repr().0.serialize(s)
}
}
impl<'de, F: PrimeField> Deserialize<'de> for FieldWrap<F, F> {
impl<'de> Deserialize<'de> for FieldWrap<Fr> {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let bytes: Vec<u8> = Deserialize::deserialize(d)?;
let mut repr = F::zero().into_repr();
repr.read_be(&bytes[..])
.map_err(|_| D::Error::custom("failed to write bytes"))?;
Ok(FieldWrap::new(F::from_repr(repr).map_err(|_| {
let repr = FrRepr(Deserialize::deserialize(d)?);
Ok(FieldWrap(Fr::from_repr(repr).map_err(|_| {
D::Error::custom("invalid field element representation")
})?))
}
}
pub fn serialize<S, F>(vec: &[F], s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
F: PrimeField,
{
let wrap_vec: Vec<FieldWrap<F, &F>> = vec.iter().map(FieldWrap::new).collect();
pub fn serialize<S: Serializer>(vec: &[Fr], s: S) -> Result<S::Ok, S::Error> {
let wrap_vec: Vec<FieldWrap<&Fr>> = vec.iter().map(FieldWrap).collect();
wrap_vec.serialize(s)
}
pub fn deserialize<'de, D, F>(d: D) -> Result<Vec<F>, D::Error>
where
D: Deserializer<'de>,
F: PrimeField,
{
let wrap_vec = <Vec<FieldWrap<F, F>>>::deserialize(d)?;
Ok(wrap_vec.into_iter().map(|FieldWrap(f, _)| f).collect())
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<Fr>, D::Error> {
let wrap_vec = <Vec<FieldWrap<Fr>>>::deserialize(d)?;
Ok(wrap_vec.into_iter().map(FieldWrap::into_inner).collect())
}
}
#[cfg(test)]
mod tests {
use super::super::PEngine;
use bincode;
use pairing::Engine;
use rand::{self, Rng};
use serde_derive::{Deserialize, Serialize};
use poly::BivarPoly;
use {Fr, G1};
#[derive(Debug, Serialize, Deserialize)]
pub struct Vecs<E: Engine> {
pub struct Vecs {
#[serde(with = "super::projective_vec")]
curve_points: Vec<E::G1>,
curve_points: Vec<G1>,
#[serde(with = "super::field_vec")]
field_elements: Vec<E::Fr>,
field_elements: Vec<Fr>,
}
impl<E: Engine> PartialEq for Vecs<E> {
impl PartialEq for Vecs {
fn eq(&self, other: &Self) -> bool {
self.curve_points == other.curve_points && self.field_elements == other.field_elements
}
@ -222,7 +223,7 @@ mod tests {
#[test]
fn vecs() {
let mut rng = rand::thread_rng();
let vecs: Vecs<PEngine> = Vecs {
let vecs = Vecs {
curve_points: rng.gen_iter().take(10).collect(),
field_elements: rng.gen_iter().take(10).collect(),
};