diff --git a/Cargo.toml b/Cargo.toml index 1cf1fe17..7ee59008 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,8 @@ optional = true [dependencies.ff] version = "0.12" default-features = false +features = ["derive"] + [dependencies.group] version = "0.12" @@ -54,7 +56,7 @@ version = "0.6" default-features = false [dependencies.subtle] -version = "2.2.1" +version = "2.4.1" default-features = false [dependencies.zeroize] diff --git a/src/g1.rs b/src/g1.rs index 619e5354..0e542d0b 100644 --- a/src/g1.rs +++ b/src/g1.rs @@ -1071,6 +1071,89 @@ impl UncompressedEncoding for G1Affine { } } +#[cfg(feature = "serde")] +mod serde_support { + use super::{fmt, G1Affine, G1Projective}; + + use serde::de::Visitor; + use serde::{self, Deserialize, Deserializer, Serialize, Serializer}; + + impl Serialize for G1Affine { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use serde::ser::SerializeTuple; + let mut tup = serializer.serialize_tuple(48)?; + for byte in self.to_compressed().iter() { + tup.serialize_element(byte)?; + } + tup.end() + } + } + + impl<'de> Deserialize<'de> for G1Affine { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct G1AffineVisitor; + + impl<'de> Visitor<'de> for G1AffineVisitor { + type Value = G1Affine; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a 48-byte compressed canonical bls12_381 G1 point") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut bytes = [0u8; 48]; + for i in 0..48 { + bytes[i] = seq.next_element()?.ok_or_else(|| { + serde::de::Error::invalid_length(i, &"expected 48 bytes") + })?; + } + + let res = G1Affine::from_compressed(&bytes); + if res.is_some().into() { + Ok(res.unwrap()) + } else { + Err(serde::de::Error::custom( + &"G1 point was not canonically encoded", + )) + } + } + } + + deserializer.deserialize_tuple(48, G1AffineVisitor) + } + } + + impl Serialize for G1Projective { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + G1Affine::from(*self).serialize(serializer) + } + } + + impl<'de> Deserialize<'de> for G1Projective { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + G1Affine::deserialize(deserializer).map(G1Projective::from) + } + } +} + +#[cfg(feature = "serde")] +pub use self::serde_support::*; + #[test] fn test_beta() { assert_eq!( @@ -1706,6 +1789,38 @@ fn test_batch_normalize() { } } +#[test] +#[cfg(feature = "serde")] +fn test_affine_serde_serialization() { + use serde_test::{assert_tokens, Token}; + + let g = G1Affine::generator(); + let raw_bytes = g.to_compressed(); + + let expected_tokens = std::iter::once(Token::Tuple { len: 48 }) + .chain(raw_bytes.iter().map(|&b| Token::U8(b))) + .chain(std::iter::once(Token::TupleEnd)) + .collect::>(); + + assert_tokens(&g, &expected_tokens); +} + +#[test] +#[cfg(feature = "serde")] +fn test_projective_serde_serialization() { + use serde_test::{assert_tokens, Token}; + + let g = G1Projective::generator(); + let raw_bytes = G1Affine::from(g).to_compressed(); + + let expected_tokens = std::iter::once(Token::Tuple { len: 48 }) + .chain(raw_bytes.iter().map(|&b| Token::U8(b))) + .chain(std::iter::once(Token::TupleEnd)) + .collect::>(); + + assert_tokens(&g, &expected_tokens); +} + #[cfg(feature = "zeroize")] #[test] fn test_zeroize() { diff --git a/src/g2.rs b/src/g2.rs index 3f0f3852..72ef003c 100644 --- a/src/g2.rs +++ b/src/g2.rs @@ -1215,6 +1215,90 @@ impl UncompressedEncoding for G2Affine { } } +#[cfg(feature = "serde")] +mod serde_support { + use super::{fmt, G2Affine, G2Projective}; + + use serde::de::Visitor; + + use serde::{self, Deserialize, Deserializer, Serialize, Serializer}; + + impl Serialize for G2Affine { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use serde::ser::SerializeTuple; + let mut tup = serializer.serialize_tuple(96)?; + for byte in self.to_compressed().iter() { + tup.serialize_element(byte)?; + } + tup.end() + } + } + + impl<'de> Deserialize<'de> for G2Affine { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct G2AffineVisitor; + + impl<'de> Visitor<'de> for G2AffineVisitor { + type Value = G2Affine; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a 96-byte compressed canonical bls12_381 G2 point") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut bytes = [0u8; 96]; + for i in 0..96 { + bytes[i] = seq.next_element()?.ok_or_else(|| { + serde::de::Error::invalid_length(i, &"expected 96 bytes") + })?; + } + + let res = G2Affine::from_compressed(&bytes); + if res.is_some().into() { + Ok(res.unwrap()) + } else { + Err(serde::de::Error::custom( + &"G2 point was not canonically encoded", + )) + } + } + } + + deserializer.deserialize_tuple(96, G2AffineVisitor) + } + } + + impl Serialize for G2Projective { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + G2Affine::from(*self).serialize(serializer) + } + } + + impl<'de> Deserialize<'de> for G2Projective { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + G2Affine::deserialize(deserializer).map(G2Projective::from) + } + } +} + +#[cfg(feature = "serde")] +pub use self::serde_support::*; + #[test] fn test_is_on_curve() { assert!(bool::from(G2Affine::identity().is_on_curve())); @@ -2109,6 +2193,38 @@ fn test_batch_normalize() { } } +#[test] +#[cfg(feature = "serde")] +fn test_affine_serde_serialization() { + use serde_test::{assert_tokens, Token}; + + let g = G2Affine::generator(); + let raw_compressed_bytes = g.to_compressed(); + + let expected_tokens = std::iter::once(Token::Tuple { len: 96 }) + .chain(raw_compressed_bytes.iter().map(|&b| Token::U8(b))) + .chain(std::iter::once(Token::TupleEnd)) + .collect::>(); + + assert_tokens(&g, &expected_tokens); +} + +#[test] +#[cfg(feature = "serde")] +fn test_projective_serde_serialization() { + use serde_test::{assert_tokens, Token}; + + let g = G2Projective::generator(); + let raw_compressed_bytes = G2Affine::from(g).to_compressed(); + + let expected_tokens = std::iter::once(Token::Tuple { len: 96 }) + .chain(raw_compressed_bytes.iter().map(|&b| Token::U8(b))) + .chain(std::iter::once(Token::TupleEnd)) + .collect::>(); + + assert_tokens(&g, &expected_tokens); +} + #[cfg(feature = "zeroize")] #[test] fn test_zeroize() { diff --git a/src/scalar.rs b/src/scalar.rs index 84810c8f..f5edae76 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -785,6 +785,70 @@ where } } +#[cfg(feature = "serde")] +mod serde_support { + use super::{fmt, Scalar}; + + use serde::de::Visitor; + use serde::{self, Deserialize, Deserializer, Serialize, Serializer}; + impl Serialize for Scalar { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use serde::ser::SerializeTuple; + let mut tup = serializer.serialize_tuple(32)?; + for byte in self.to_bytes().iter() { + tup.serialize_element(byte)?; + } + tup.end() + } + } + + impl<'de> Deserialize<'de> for Scalar { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct ScalarVisitor; + + impl<'de> Visitor<'de> for ScalarVisitor { + type Value = Scalar; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a 32-byte canonical bls12_381 scalar") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut bytes = [0u8; 32]; + for i in 0..32 { + bytes[i] = seq.next_element()?.ok_or_else(|| { + serde::de::Error::invalid_length(i, &"expected 32 bytes") + })?; + } + + let res = Scalar::from_bytes(&bytes); + if res.is_some().into() { + Ok(res.unwrap()) + } else { + Err(serde::de::Error::custom( + &"scalar was not canonically encoded", + )) + } + } + } + + deserializer.deserialize_tuple(32, ScalarVisitor) + } + } +} + +#[cfg(feature = "serde")] +pub use self::serde_support::*; + #[test] fn test_inv() { // Compute -(q^{-1} mod 2^64) mod 2^64 by exponentiating @@ -1231,6 +1295,22 @@ fn test_double() { assert_eq!(a.double(), a + a); } +#[test] +#[cfg(feature = "serde")] +fn test_serde_serialization() { + use serde_test::{assert_tokens, Token}; + + let s = R; + let raw_bytes = s.to_bytes(); + + let expected_tokens = std::iter::once(Token::Tuple { len: 32 }) + .chain(raw_bytes.iter().map(|&b| Token::U8(b))) + .chain(std::iter::once(Token::TupleEnd)) + .collect::>(); + + assert_tokens(&s, &expected_tokens) +} + #[cfg(feature = "zeroize")] #[test] fn test_zeroize() {