diff --git a/Cargo.toml b/Cargo.toml index 984a56d..01a4800 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,10 @@ all-features = true rustdoc-args = ["--cfg", "docsrs", "--html-in-header", "katex-header.html"] [dev-dependencies] +bincode = "1.3" criterion = "0.3" rand_xorshift = "0.3" +serde_json = "1.0" [[bench]] name = "hashtocurve" @@ -57,6 +59,10 @@ lazy_static = { version = "1.4.0", optional = true } # gpu dependencies ec-gpu = { version = "0.1.0", optional = true } +# serde dependencies +serde_crate = { version = "1.0.16", optional = true, default-features = false, features = ["alloc"], package = "serde" } +hex = { version = "0.4", optional = true, default-features = false, features = ["alloc", "serde"] } + [features] default = ["bits", "sqrt-table"] alloc = ["group/alloc", "blake2b_simd"] @@ -65,3 +71,4 @@ gpu = ["alloc", "ec-gpu"] sqrt-table = ["alloc", "lazy_static"] repr-c = [] uninline-portable = [] +serde = ["hex", "serde_crate"] diff --git a/src/lib.rs b/src/lib.rs index 16d8a36..4d22495 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,9 @@ pub mod vesta; #[cfg(feature = "alloc")] mod hashtocurve; +#[cfg(feature = "serde")] +mod serde_impl; + pub use curves::*; pub use fields::*; diff --git a/src/serde_impl.rs b/src/serde_impl.rs new file mode 100644 index 0000000..e0cfb03 --- /dev/null +++ b/src/serde_impl.rs @@ -0,0 +1,324 @@ +use ff::PrimeField; +use group::GroupEncoding; +use serde_crate::{ + de::Error as DeserializeError, Deserialize, Deserializer, Serialize, Serializer, +}; + +use crate::{ + curves::{EpAffine, EqAffine}, + fields::{Fp, Fq}, +}; + +/// Serializes bytes to human readable or compact representation. +/// +/// Depending on whether the serializer is a human readable one or not, the bytes are either +/// encoded as a hex string or a list of bytes. +fn serialize_bytes(bytes: [u8; 32], s: S) -> Result { + if s.is_human_readable() { + hex::serde::serialize(bytes, s) + } else { + bytes.serialize(s) + } +} + +/// Deserialize bytes from human readable or compact representation. +/// +/// Depending on whether the deserializer is a human readable one or not, the bytes are either +/// decoded from a hex string or a list of bytes. +fn deserialize_bytes<'de, D: Deserializer<'de>>(d: D) -> Result<[u8; 32], D::Error> { + if d.is_human_readable() { + hex::serde::deserialize(d) + } else { + <[u8; 32]>::deserialize(d) + } +} + +impl Serialize for Fp { + fn serialize(&self, s: S) -> Result { + serialize_bytes(self.to_repr(), s) + } +} + +impl<'de> Deserialize<'de> for Fp { + fn deserialize>(d: D) -> Result { + let bytes = deserialize_bytes(d)?; + match Fp::from_repr(bytes).into() { + Some(fq) => Ok(fq), + None => Err(D::Error::custom( + "deserialized bytes don't encode a Pallas field element", + )), + } + } +} + +impl Serialize for Fq { + fn serialize(&self, s: S) -> Result { + serialize_bytes(self.to_repr(), s) + } +} + +impl<'de> Deserialize<'de> for Fq { + fn deserialize>(d: D) -> Result { + let bytes = deserialize_bytes(d)?; + match Fq::from_repr(bytes).into() { + Some(fq) => Ok(fq), + None => Err(D::Error::custom( + "deserialized bytes don't encode a Vesta field element", + )), + } + } +} + +impl Serialize for EpAffine { + fn serialize(&self, s: S) -> Result { + serialize_bytes(self.to_bytes(), s) + } +} + +impl<'de> Deserialize<'de> for EpAffine { + fn deserialize>(d: D) -> Result { + let bytes = deserialize_bytes(d)?; + match EpAffine::from_bytes(&bytes).into() { + Some(ep_affine) => Ok(ep_affine), + None => Err(D::Error::custom( + "deserialized bytes don't encode a Pallas curve point", + )), + } + } +} + +impl Serialize for EqAffine { + fn serialize(&self, s: S) -> Result { + serialize_bytes(self.to_bytes(), s) + } +} + +impl<'de> Deserialize<'de> for EqAffine { + fn deserialize>(d: D) -> Result { + let bytes = deserialize_bytes(d)?; + match EqAffine::from_bytes(&bytes).into() { + Some(eq_affine) => Ok(eq_affine), + None => Err(D::Error::custom( + "deserialized bytes don't encode a Vesta curve point", + )), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use core::fmt::Debug; + + use ff::Field; + use group::{prime::PrimeCurveAffine, Curve, Group}; + use rand::SeedableRng; + use rand_xorshift::XorShiftRng; + + use crate::curves::{Ep, Eq}; + + fn test_roundtrip Deserialize<'a> + Debug + PartialEq>(t: &T) { + let serialized_json = serde_json::to_vec(t).unwrap(); + assert_eq!(*t, serde_json::from_slice(&serialized_json).unwrap()); + + let serialized_bincode = bincode::serialize(t).unwrap(); + assert_eq!(*t, bincode::deserialize(&serialized_bincode).unwrap()); + } + + #[test] + fn serde_fp() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..100 { + let f = Fp::random(&mut rng); + test_roundtrip(&f); + } + + let f = Fp::zero(); + test_roundtrip(&f); + assert_eq!( + serde_json::from_slice::( + br#""0000000000000000000000000000000000000000000000000000000000000000""# + ) + .unwrap(), + f + ); + assert_eq!( + bincode::deserialize::(&[ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ]) + .unwrap(), + f + ); + + let f = Fp::one(); + test_roundtrip(&f); + assert_eq!( + serde_json::from_slice::( + br#""0100000000000000000000000000000000000000000000000000000000000000""# + ) + .unwrap(), + f + ); + assert_eq!( + bincode::deserialize::(&[ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ]) + .unwrap(), + f + ); + } + + #[test] + fn serde_fq() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..100 { + let f = Fq::random(&mut rng); + test_roundtrip(&f); + } + + let f = Fq::zero(); + test_roundtrip(&f); + assert_eq!( + serde_json::from_slice::( + br#""0000000000000000000000000000000000000000000000000000000000000000""# + ) + .unwrap(), + f + ); + assert_eq!( + bincode::deserialize::(&[ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ]) + .unwrap(), + f + ); + + let f = Fq::one(); + test_roundtrip(&f); + assert_eq!( + serde_json::from_slice::( + br#""0100000000000000000000000000000000000000000000000000000000000000""# + ) + .unwrap(), + f + ); + assert_eq!( + bincode::deserialize::(&[ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ]) + .unwrap(), + f + ); + } + + #[test] + fn serde_ep_affine() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..100 { + let f = Ep::random(&mut rng); + test_roundtrip(&f.to_affine()); + } + + let f = EpAffine::identity(); + test_roundtrip(&f); + assert_eq!( + serde_json::from_slice::( + br#""0000000000000000000000000000000000000000000000000000000000000000""# + ) + .unwrap(), + f + ); + assert_eq!( + bincode::deserialize::(&[ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ]) + .unwrap(), + f + ); + + let f = EpAffine::generator(); + test_roundtrip(&f); + assert_eq!( + serde_json::from_slice::( + br#""00000000ed302d991bf94c09fc98462200000000000000000000000000000040""# + ) + .unwrap(), + f + ); + assert_eq!( + bincode::deserialize::(&[ + 0, 0, 0, 0, 237, 48, 45, 153, 27, 249, 76, 9, 252, 152, 70, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 64 + ]) + .unwrap(), + f + ); + } + + #[test] + fn serde_eq_affine() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..100 { + let f = Eq::random(&mut rng); + test_roundtrip(&f.to_affine()); + } + + let f = EqAffine::identity(); + test_roundtrip(&f); + assert_eq!( + serde_json::from_slice::( + br#""0000000000000000000000000000000000000000000000000000000000000000""# + ) + .unwrap(), + f + ); + assert_eq!( + bincode::deserialize::(&[ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ]) + .unwrap(), + f + ); + + let f = EqAffine::generator(); + test_roundtrip(&f); + assert_eq!( + serde_json::from_slice::( + br#""0000000021eb468cdda89409fc98462200000000000000000000000000000040""# + ) + .unwrap(), + f + ); + assert_eq!( + bincode::deserialize::(&[ + 0, 0, 0, 0, 33, 235, 70, 140, 221, 168, 148, 9, 252, 152, 70, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 64 + ]) + .unwrap(), + f + ); + } +}