From c5390da48a5ccf17ea31e7bf8bc76ce48507356d Mon Sep 17 00:00:00 2001 From: Andreas Fackler Date: Mon, 11 Jun 2018 18:23:20 +0200 Subject: [PATCH] Serde support --- Cargo.toml | 4 + src/bls12_381/fq12.rs | 1 + src/bls12_381/fq2.rs | 1 + src/bls12_381/fq6.rs | 1 + src/bls12_381/mod.rs | 2 + src/bls12_381/serde_impl.rs | 203 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 +- 7 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 src/bls12_381/serde_impl.rs diff --git a/Cargo.toml b/Cargo.toml index 5f16018a..b4759d67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,12 @@ repository = "https://github.com/ebfull/pairing" rand = "0.4" byteorder = "1" ff = { version = "0.4", features = ["derive"] } +serde = { version = "1.0", optional = true, features = ["derive"] } [features] unstable-features = ["expose-arith"] expose-arith = [] default = [] + +[dev-dependencies] +serde_json = "1.0" diff --git a/src/bls12_381/fq12.rs b/src/bls12_381/fq12.rs index b24fcaaa..1aa59907 100644 --- a/src/bls12_381/fq12.rs +++ b/src/bls12_381/fq12.rs @@ -6,6 +6,7 @@ use rand::{Rand, Rng}; /// An element of Fq12, represented by c0 + c1 * w. #[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Fq12 { pub c0: Fq6, pub c1: Fq6, diff --git a/src/bls12_381/fq2.rs b/src/bls12_381/fq2.rs index 363439a6..8fd45576 100644 --- a/src/bls12_381/fq2.rs +++ b/src/bls12_381/fq2.rs @@ -6,6 +6,7 @@ use std::cmp::Ordering; /// An element of Fq2, represented by c0 + c1 * u. #[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Fq2 { pub c0: Fq, pub c1: Fq, diff --git a/src/bls12_381/fq6.rs b/src/bls12_381/fq6.rs index 36c6e285..a71e6161 100644 --- a/src/bls12_381/fq6.rs +++ b/src/bls12_381/fq6.rs @@ -5,6 +5,7 @@ use rand::{Rand, Rng}; /// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2). #[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Fq6 { pub c0: Fq2, pub c1: Fq2, diff --git a/src/bls12_381/mod.rs b/src/bls12_381/mod.rs index 106591e3..28346e37 100644 --- a/src/bls12_381/mod.rs +++ b/src/bls12_381/mod.rs @@ -5,6 +5,8 @@ mod fq2; mod fq6; mod fr; +#[cfg(feature = "serde")] +mod serde_impl; #[cfg(test)] mod tests; diff --git a/src/bls12_381/serde_impl.rs b/src/bls12_381/serde_impl.rs new file mode 100644 index 00000000..c7f0dd66 --- /dev/null +++ b/src/bls12_381/serde_impl.rs @@ -0,0 +1,203 @@ +use std::fmt; +use std::marker::PhantomData; + +use super::{Fq, FqRepr, Fr, FrRepr, G1Affine, G2Affine, G1, G2}; +use {CurveAffine, CurveProjective, EncodedPoint, PrimeField}; + +use serde::de::{Error as DeserializeError, SeqAccess, Visitor}; +use serde::ser::SerializeTuple; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +const ERR_CODE: &str = "deserialized bytes don't encode a group element"; + +impl Serialize for G1 { + fn serialize(&self, s: S) -> Result { + self.into_affine().serialize(s) + } +} + +impl<'de> Deserialize<'de> for G1 { + fn deserialize>(d: D) -> Result { + Ok(G1Affine::deserialize(d)?.into_projective()) + } +} + +impl Serialize for G1Affine { + fn serialize(&self, s: S) -> Result { + serialize_affine(self, s) + } +} + +impl<'de> Deserialize<'de> for G1Affine { + fn deserialize>(d: D) -> Result { + Ok(deserialize_affine(d)?) + } +} + +impl Serialize for G2 { + fn serialize(&self, s: S) -> Result { + self.into_affine().serialize(s) + } +} + +impl<'de> Deserialize<'de> for G2 { + fn deserialize>(d: D) -> Result { + Ok(G2Affine::deserialize(d)?.into_projective()) + } +} + +impl Serialize for G2Affine { + fn serialize(&self, s: S) -> Result { + serialize_affine(self, s) + } +} + +impl<'de> Deserialize<'de> for G2Affine { + fn deserialize>(d: D) -> Result { + Ok(deserialize_affine(d)?) + } +} + +/// Serializes a group element using its compressed representation. +fn serialize_affine(c: &C, s: S) -> Result { + let len = C::Compressed::size(); + let mut tup = s.serialize_tuple(len)?; + for byte in c.into_compressed().as_ref() { + tup.serialize_element(byte)?; + } + tup.end() +} + +/// Deserializes the compressed representation of a group element. +fn deserialize_affine<'de, D: Deserializer<'de>, C: CurveAffine>(d: D) -> Result { + struct TupleVisitor { + _ph: PhantomData, + } + + impl<'de, C: CurveAffine> Visitor<'de> for TupleVisitor { + type Value = C; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + let len = C::Compressed::size(); + write!(f, "a tuple of size {}", len) + } + + #[inline] + fn visit_seq>(self, mut seq: A) -> Result { + let mut compressed = C::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); + compressed.into_affine().map_err(to_err) + } + } + + let len = C::Compressed::size(); + d.deserialize_tuple(len, TupleVisitor { _ph: PhantomData }) +} + +impl Serialize for Fr { + fn serialize(&self, s: S) -> Result { + self.into_repr().serialize(s) + } +} + +impl<'de> Deserialize<'de> for Fr { + fn deserialize>(d: D) -> Result { + Fr::from_repr(FrRepr::deserialize(d)?).map_err(|_| D::Error::custom(ERR_CODE)) + } +} + +impl Serialize for FrRepr { + fn serialize(&self, s: S) -> Result { + self.0.serialize(s) + } +} + +impl<'de> Deserialize<'de> for FrRepr { + fn deserialize>(d: D) -> Result { + Ok(FrRepr(<_>::deserialize(d)?)) + } +} + +impl Serialize for Fq { + fn serialize(&self, s: S) -> Result { + self.into_repr().serialize(s) + } +} + +impl<'de> Deserialize<'de> for Fq { + fn deserialize>(d: D) -> Result { + Fq::from_repr(FqRepr::deserialize(d)?).map_err(|_| D::Error::custom(ERR_CODE)) + } +} + +impl Serialize for FqRepr { + fn serialize(&self, s: S) -> Result { + self.0.serialize(s) + } +} + +impl<'de> Deserialize<'de> for FqRepr { + fn deserialize>(d: D) -> Result { + Ok(FqRepr(<_>::deserialize(d)?)) + } +} + +#[cfg(test)] +mod tests { + extern crate serde_json; + + use super::*; + use bls12_381::Fq12; + + use std::fmt::Debug; + + use rand::{Rng, SeedableRng, XorShiftRng}; + + fn test_roundtrip Deserialize<'a> + Debug + PartialEq>(t: &T) { + let ser = serde_json::to_vec(t).unwrap(); + assert_eq!(*t, serde_json::from_slice(&ser).unwrap()); + } + + #[test] + fn serde_g1() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let g: G1 = rng.gen(); + test_roundtrip(&g); + test_roundtrip(&g.into_affine()); + } + + #[test] + fn serde_g2() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let g: G2 = rng.gen(); + test_roundtrip(&g); + test_roundtrip(&g.into_affine()); + } + + #[test] + fn serde_fr() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let f: Fr = rng.gen(); + test_roundtrip(&f); + test_roundtrip(&f.into_repr()); + } + + #[test] + fn serde_fq() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let f: Fq = rng.gen(); + test_roundtrip(&f); + test_roundtrip(&f.into_repr()); + } + + #[test] + fn serde_fq12() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let f: Fq12 = rng.gen(); + test_roundtrip(&f); + } +} diff --git a/src/lib.rs b/src/lib.rs index bbced76f..2b591749 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,9 +12,11 @@ #![deny(missing_debug_implementations)] extern crate byteorder; -#[macro_use] extern crate ff; extern crate rand; +#[cfg(feature = "serde")] +#[macro_use(Serialize, Deserialize)] +extern crate serde; #[cfg(test)] pub mod tests;