diff --git a/src/error.rs b/src/error.rs index 97a00d2..145bd7e 100644 --- a/src/error.rs +++ b/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 = ::std::result::Result; diff --git a/src/lib.rs b/src/lib.rs index 2a0eb70..2bd8dd3 100644 --- a/src/lib.rs +++ b/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>(bytes: B) -> FromBytesResult { + let mut compressed: ::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 { - 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>(bytes: B) -> FromBytesResult { + Ok(PublicKeyShare(PublicKey::from_bytes(bytes)?)) + } + /// Returns a byte string representation of the public key share. - pub fn to_bytes(&self) -> Vec { + 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>(bytes: B) -> FromBytesResult { + let mut compressed: ::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>(bytes: B) -> FromBytesResult { + 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!(::Compressed::size(), PK_SIZE); + assert_eq!(::Compressed::size(), SIG_SIZE); + } } diff --git a/src/mock/mod.rs b/src/mock/mod.rs index 5e1c30e..edcca1a 100644 --- a/src/mock/mod.rs +++ b/src/mock/mod.rs @@ -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!(::size(), PK_SIZE); + assert_eq!(::size(), SIG_SIZE); + } } diff --git a/src/serde_impl.rs b/src/serde_impl.rs index f98745f..d0a4e1e 100644 --- a/src/serde_impl.rs +++ b/src/serde_impl.rs @@ -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 std::fmt; + use std::marker::PhantomData; + use pairing::{CurveAffine, CurveProjective, EncodedPoint}; - use serde::de::Error as DeserializeError; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; + 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(c: &C, s: S) -> Result @@ -55,7 +57,12 @@ pub mod projective { S: Serializer, C: CurveProjective, { - c.into_affine().into_compressed().as_ref().serialize(s) + let len = ::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 @@ -63,14 +70,32 @@ pub mod projective { D: Deserializer<'de>, C: CurveProjective, { - let bytes = >::deserialize(d)?; - if bytes.len() != ::Compressed::size() { - return Err(D::Error::custom(ERR_LEN)); + struct TupleVisitor { + _ph: PhantomData, } - let mut compressed = ::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 { + type Value = C; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + let len = ::Compressed::size(); + write!(f, "a tuple of size {}", len) + } + + #[inline] + fn visit_seq>(self, mut seq: A) -> Result { + let mut compressed = ::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 = ::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}; - /// A wrapper type to facilitate serialization and deserialization of field elements. - pub struct FieldWrap(B, PhantomData); + use {Fr, FrRepr}; - impl FieldWrap { - pub fn new(f: B) -> Self { - FieldWrap(f, PhantomData) - } - } + /// A wrapper type to facilitate serialization and deserialization of field elements. + pub struct FieldWrap(B); - impl FieldWrap { - pub fn into_inner(self) -> F { + impl FieldWrap { + pub fn into_inner(self) -> Fr { self.0 } } - impl> Serialize for FieldWrap { + impl> Serialize for FieldWrap { fn serialize(&self, s: S) -> Result { - 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 { + impl<'de> Deserialize<'de> for FieldWrap { fn deserialize>(d: D) -> Result { - let bytes: Vec = 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(vec: &[F], s: S) -> Result - where - S: Serializer, - F: PrimeField, - { - let wrap_vec: Vec> = vec.iter().map(FieldWrap::new).collect(); + pub fn serialize(vec: &[Fr], s: S) -> Result { + let wrap_vec: Vec> = vec.iter().map(FieldWrap).collect(); wrap_vec.serialize(s) } - pub fn deserialize<'de, D, F>(d: D) -> Result, D::Error> - where - D: Deserializer<'de>, - F: PrimeField, - { - let wrap_vec = >>::deserialize(d)?; - Ok(wrap_vec.into_iter().map(|FieldWrap(f, _)| f).collect()) + pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result, D::Error> { + let wrap_vec = >>::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 { + pub struct Vecs { #[serde(with = "super::projective_vec")] - curve_points: Vec, + curve_points: Vec, #[serde(with = "super::field_vec")] - field_elements: Vec, + field_elements: Vec, } - impl PartialEq for Vecs { + 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 = Vecs { + let vecs = Vecs { curve_points: rng.gen_iter().take(10).collect(), field_elements: rng.gen_iter().take(10).collect(), };