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:
parent
4fec9da3d6
commit
c7eda7a14a
10
src/error.rs
10
src/error.rs
|
@ -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>;
|
||||
|
|
87
src/lib.rs
87
src/lib.rs
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue