diff --git a/Cargo.toml b/Cargo.toml index f29c917e..a843a97c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ criterion = { version = "0.3", features = ["html_reports"] } rand_xorshift = "0.3" ark-std = { version = "0.3" } bincode = "1.3.3" +serde_json = "1.0.105" [dependencies] subtle = "2.4" @@ -30,6 +31,7 @@ num-traits = "0.2" paste = "1.0.11" serde = { version = "1.0", default-features = false, optional = true } serde_arrays = { version = "0.1.0", optional = true } +hex = { version = "0.4", optional = true, default-features = false, features = ["alloc", "serde"] } blake2b_simd = "1" [features] @@ -37,7 +39,7 @@ default = ["reexport", "bits"] asm = [] bits = ["ff/bits"] bn256-table = [] -derive_serde = ["serde/derive", "serde_arrays"] +derive_serde = ["serde/derive", "serde_arrays", "hex"] prefetch = [] print-trace = ["ark-std/print-trace"] reexport = [] diff --git a/src/derive/curve.rs b/src/derive/curve.rs index 1eeef572..23e5c717 100644 --- a/src/derive/curve.rs +++ b/src/derive/curve.rs @@ -289,8 +289,72 @@ macro_rules! new_curve_impl { } + /// A macro to help define point serialization using the [`group::GroupEncoding`] trait + /// This assumes both point types ($name, $nameaffine) implement [`group::GroupEncoding`]. + #[cfg(feature = "derive_serde")] + macro_rules! serialize_deserialize_to_from_bytes { + () => { + impl ::serde::Serialize for $name { + fn serialize(&self, serializer: S) -> Result { + let bytes = &self.to_bytes(); + if serializer.is_human_readable() { + ::hex::serde::serialize(&bytes.0, serializer) + } else { + ::serde_arrays::serialize(&bytes.0, serializer) + } + } + } + + paste::paste! { + use ::serde::de::Error as _; + impl<'de> ::serde::Deserialize<'de> for $name { + fn deserialize>( + deserializer: D, + ) -> Result { + let bytes = if deserializer.is_human_readable() { + ::hex::serde::deserialize(deserializer)? + } else { + ::serde_arrays::deserialize::<_, u8, [< $name _COMPRESSED_SIZE >]>(deserializer)? + }; + Option::from(Self::from_bytes(&[< $name Compressed >](bytes))).ok_or_else(|| { + D::Error::custom("deserialized bytes don't encode a valid field element") + }) + } + } + } + + impl ::serde::Serialize for $name_affine { + fn serialize(&self, serializer: S) -> Result { + let bytes = &self.to_bytes(); + if serializer.is_human_readable() { + ::hex::serde::serialize(&bytes.0, serializer) + } else { + ::serde_arrays::serialize(&bytes.0, serializer) + } + } + } + + paste::paste! { + use ::serde::de::Error as _; + impl<'de> ::serde::Deserialize<'de> for $name_affine { + fn deserialize>( + deserializer: D, + ) -> Result { + let bytes = if deserializer.is_human_readable() { + ::hex::serde::deserialize(deserializer)? + } else { + ::serde_arrays::deserialize::<_, u8, [< $name _COMPRESSED_SIZE >]>(deserializer)? + }; + Option::from(Self::from_bytes(&[< $name Compressed >](bytes))).ok_or_else(|| { + D::Error::custom("deserialized bytes don't encode a valid field element") + }) + } + } + } + }; + } + #[derive(Copy, Clone, Debug)] - #[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))] $($privacy)* struct $name { pub x: $base, pub y: $base, @@ -298,13 +362,13 @@ macro_rules! new_curve_impl { } #[derive(Copy, Clone, PartialEq)] - #[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))] $($privacy)* struct $name_affine { pub x: $base, pub y: $base, } - + #[cfg(feature = "derive_serde")] + serialize_deserialize_to_from_bytes!(); impl_compressed!(); impl_uncompressed!(); diff --git a/src/tests/curve.rs b/src/tests/curve.rs index 54d23791..2f93bbb4 100644 --- a/src/tests/curve.rs +++ b/src/tests/curve.rs @@ -74,12 +74,24 @@ where assert_eq!(projective_point.to_affine(), affine_point_rec); assert_eq!(affine_point, affine_point_rec); } + { + let affine_json = serde_json::to_string(&affine_point).unwrap(); + let reader = std::io::Cursor::new(affine_json); + let affine_point_rec: G::AffineExt = serde_json::from_reader(reader).unwrap(); + assert_eq!(affine_point, affine_point_rec); + } { let projective_bytes = bincode::serialize(&projective_point).unwrap(); let reader = std::io::Cursor::new(projective_bytes); let projective_point_rec: G = bincode::deserialize_from(reader).unwrap(); assert_eq!(projective_point, projective_point_rec); } + { + let projective_json = serde_json::to_string(&projective_point).unwrap(); + let reader = std::io::Cursor::new(projective_json); + let projective_point_rec: G = serde_json::from_reader(reader).unwrap(); + assert_eq!(projective_point, projective_point_rec); + } } }