From 36032e01bdf68cc8b6ed13dc069c3c1570eb6ad0 Mon Sep 17 00:00:00 2001 From: adria0 Date: Fri, 4 Oct 2024 13:30:58 +0200 Subject: [PATCH 1/4] Remove SerdeObject --- derive/src/field/mod.rs | 89 ++++-------------------------- src/bls12381/g1.rs | 4 +- src/bls12381/g2.rs | 8 +-- src/bn256/curve.rs | 8 +-- src/derive/curve.rs | 100 ++++------------------------------ src/derive/field/tower.rs | 43 +-------------- src/{serde.rs => encoding.rs} | 43 +++------------ src/grumpkin/curve.rs | 2 +- src/lib.rs | 2 +- src/pluto_eris/curve.rs | 10 ++-- src/secp256k1/curve.rs | 2 +- src/secp256r1/curve.rs | 2 +- src/secq256k1/curve.rs | 2 +- src/tests/curve.rs | 29 +--------- src/tests/field/serde.rs | 20 +------ 15 files changed, 55 insertions(+), 309 deletions(-) rename src/{serde.rs => encoding.rs} (83%) diff --git a/derive/src/field/mod.rs b/derive/src/field/mod.rs index 566fb9ee..0caf049c 100644 --- a/derive/src/field/mod.rs +++ b/derive/src/field/mod.rs @@ -284,8 +284,8 @@ pub(crate) fn impl_field(input: TokenStream) -> TokenStream { } } - impl crate::serde::endian::EndianRepr for #field { - const ENDIAN: crate::serde::endian::Endian = crate::serde::endian::Endian::#endian; + impl crate::encoding::endian::EndianRepr for #field { + const ENDIAN: crate::encoding::endian::Endian = crate::encoding::endian::Endian::#endian; fn to_bytes(&self) -> Vec { self.to_bytes().to_vec() @@ -326,7 +326,7 @@ pub(crate) fn impl_field(input: TokenStream) -> TokenStream { /// Attempts to convert a <#endian>-endian byte representation of /// a scalar into a `$field`, failing if the input is not canonical. pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> subtle::CtOption { - use crate::serde::endian::EndianRepr; + use crate::encoding::endian::EndianRepr; let mut el = #field::default(); #field::ENDIAN.from_bytes(bytes, &mut el.0); subtle::CtOption::new(el * Self::R2, subtle::Choice::from(Self::is_less_than_modulus(&el.0) as u8)) @@ -336,7 +336,7 @@ pub(crate) fn impl_field(input: TokenStream) -> TokenStream { /// Converts an element of `$field` into a byte representation in /// <#endian>-endian byte order. pub fn to_bytes(&self) -> [u8; Self::SIZE] { - use crate::serde::endian::EndianRepr; + use crate::encoding::endian::EndianRepr; let el = self.from_mont(); let mut res = [0; Self::SIZE]; #field::ENDIAN.to_bytes(&mut res, &el); @@ -422,15 +422,15 @@ pub(crate) fn impl_field(input: TokenStream) -> TokenStream { let impl_prime_field = quote! { // TODO use ::core::borrow::Borrow or AsRef - impl From<#field> for crate::serde::Repr<{ #field::SIZE }> { - fn from(value: #field) -> crate::serde::Repr<{ #field::SIZE }> { + impl From<#field> for crate::encoding::Repr<{ #field::SIZE }> { + fn from(value: #field) -> crate::encoding::Repr<{ #field::SIZE }> { use ff::PrimeField; value.to_repr() } } - impl<'a> From<&'a #field> for crate::serde::Repr<{ #field::SIZE }> { - fn from(value: &'a #field) -> crate::serde::Repr<{ #field::SIZE }> { + impl<'a> From<&'a #field> for crate::encoding::Repr<{ #field::SIZE }> { + fn from(value: &'a #field) -> crate::encoding::Repr<{ #field::SIZE }> { use ff::PrimeField; value.to_repr() } @@ -447,7 +447,7 @@ pub(crate) fn impl_field(input: TokenStream) -> TokenStream { const DELTA: Self = Self(#delta); const MODULUS: &'static str = #modulus_str; - type Repr = crate::serde::Repr<{ #field::SIZE }>; + type Repr = crate::encoding::Repr<{ #field::SIZE }>; fn from_u128(v: u128) -> Self { Self::R2 * Self( @@ -463,15 +463,15 @@ pub(crate) fn impl_field(input: TokenStream) -> TokenStream { fn from_repr(repr: Self::Repr) -> subtle::CtOption { let mut el = #field::default(); - crate::serde::endian::Endian::LE.from_bytes(repr.as_ref(), &mut el.0); + crate::encoding::endian::Endian::LE.from_bytes(repr.as_ref(), &mut el.0); subtle::CtOption::new(el * Self::R2, subtle::Choice::from(Self::is_less_than_modulus(&el.0) as u8)) } fn to_repr(&self) -> Self::Repr { - use crate::serde::endian::Endian; + use crate::encoding::endian::Endian; let el = self.from_mont(); let mut res = [0; #size]; - crate::serde::endian::Endian::LE.to_bytes(&mut res, &el); + crate::encoding::endian::Endian::LE.to_bytes(&mut res, &el); res.into() } @@ -481,70 +481,6 @@ pub(crate) fn impl_field(input: TokenStream) -> TokenStream { } }; - let impl_serde_object = quote! { - impl crate::serde::SerdeObject for #field { - fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { - debug_assert_eq!(bytes.len(), #size); - - let inner = (0..#num_limbs) - .map(|off| { - u64::from_le_bytes(bytes[off * 8..(off + 1) * 8].try_into().unwrap()) - }) - .collect::>(); - Self(inner.try_into().unwrap()) - } - - fn from_raw_bytes(bytes: &[u8]) -> Option { - if bytes.len() != #size { - return None; - } - let elt = Self::from_raw_bytes_unchecked(bytes); - Self::is_less_than_modulus(&elt.0).then(|| elt) - } - - fn to_raw_bytes(&self) -> Vec { - let mut res = Vec::with_capacity(#num_limbs * 4); - for limb in self.0.iter() { - res.extend_from_slice(&limb.to_le_bytes()); - } - res - } - - fn read_raw_unchecked(reader: &mut R) -> Self { - let inner = [(); #num_limbs].map(|_| { - let mut buf = [0; 8]; - reader.read_exact(&mut buf).unwrap(); - u64::from_le_bytes(buf) - }); - Self(inner) - } - - fn read_raw(reader: &mut R) -> std::io::Result { - let mut inner = [0u64; #num_limbs]; - for limb in inner.iter_mut() { - let mut buf = [0; 8]; - reader.read_exact(&mut buf)?; - *limb = u64::from_le_bytes(buf); - } - let elt = Self(inner); - Self::is_less_than_modulus(&elt.0) - .then(|| elt) - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidData, - "input number is not less than field modulus", - ) - }) - } - fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { - for limb in self.0.iter() { - writer.write_all(&limb.to_le_bytes())?; - } - Ok(()) - } - } - }; - #[cfg(feature = "asm")] let impl_arith = { if num_limbs == 4 && num_bits < 256 { @@ -605,7 +541,6 @@ pub(crate) fn impl_field(input: TokenStream) -> TokenStream { #impl_arith_always_const #impl_field #impl_prime_field - #impl_serde_object #impl_from_uniform_bytes #impl_zeta }; diff --git a/src/bls12381/g1.rs b/src/bls12381/g1.rs index 451f1f0e..d32e121f 100644 --- a/src/bls12381/g1.rs +++ b/src/bls12381/g1.rs @@ -1,6 +1,6 @@ use super::fq::Fq; use super::Fr; -use crate::serde::{Compressed, CompressedFlagConfig}; +use crate::encoding::{Compressed, CompressedFlagConfig}; use crate::{ impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, impl_binops_multiplicative_mixed, new_curve_impl, @@ -27,7 +27,7 @@ new_curve_impl!( B, "bls12381_g1", |domain_prefix| hash_to_curve(domain_prefix, hash_to_curve_suite(b"BLS12381G1_XMD:SHA-256_SSWU_RO_")), - crate::serde::CompressedFlagConfig::ThreeSpare + crate::encoding::CompressedFlagConfig::ThreeSpare ); impl Compressed for G1Compressed { diff --git a/src/bls12381/g2.rs b/src/bls12381/g2.rs index df77bb51..dcc7a974 100644 --- a/src/bls12381/g2.rs +++ b/src/bls12381/g2.rs @@ -6,7 +6,7 @@ use crate::ff::{Field, PrimeField}; use crate::ff_ext::ExtField; use crate::group::Curve; use crate::group::{cofactor::CofactorGroup, prime::PrimeCurveAffine, Group, GroupEncoding}; -use crate::serde::{Compressed, CompressedFlagConfig}; +use crate::encoding::{Compressed, CompressedFlagConfig}; use crate::{ impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, impl_binops_multiplicative_mixed, new_curve_impl, @@ -75,12 +75,12 @@ new_curve_impl!( G2_B, "bls12381_g2", |domain_prefix| hash_to_curve(domain_prefix, hash_to_curve_suite(b"BLS12381G2_XMD:SHA-256_SSWU_RO_")), - crate::serde::CompressedFlagConfig::ThreeSpare + crate::encoding::CompressedFlagConfig::ThreeSpare ); -impl crate::serde::endian::EndianRepr for Fq2 { - const ENDIAN: crate::serde::endian::Endian = Fq::ENDIAN; +impl crate::encoding::endian::EndianRepr for Fq2 { + const ENDIAN: crate::encoding::endian::Endian = Fq::ENDIAN; fn to_bytes(&self) -> Vec { self.to_bytes().to_vec() diff --git a/src/bn256/curve.rs b/src/bn256/curve.rs index dadf31d6..d64ffd89 100644 --- a/src/bn256/curve.rs +++ b/src/bn256/curve.rs @@ -23,8 +23,8 @@ use rand::RngCore; use std::convert::TryInto; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; -impl crate::serde::endian::EndianRepr for Fq2 { - const ENDIAN: crate::serde::endian::Endian = Fq::ENDIAN; +impl crate::encoding::endian::EndianRepr for Fq2 { + const ENDIAN: crate::encoding::endian::Endian = Fq::ENDIAN; fn to_bytes(&self) -> Vec { self.to_bytes().to_vec() @@ -46,7 +46,7 @@ new_curve_impl!( G1_B, "bn256_g1", |domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, G1::default_hash_to_curve_suite()), - crate::serde::CompressedFlagConfig::TwoSpare, + crate::encoding::CompressedFlagConfig::TwoSpare, standard_sign ); @@ -61,7 +61,7 @@ new_curve_impl!( G2_B, "bn256_g2", |domain_prefix| hash_to_curve_g2(domain_prefix), - crate::serde::CompressedFlagConfig::TwoSpare, + crate::encoding::CompressedFlagConfig::TwoSpare, standard_sign ); diff --git a/src/derive/curve.rs b/src/derive/curve.rs index 84f4c56f..8c22769c 100644 --- a/src/derive/curve.rs +++ b/src/derive/curve.rs @@ -66,8 +66,8 @@ macro_rules! new_curve_impl { ) => { paste::paste! { - impl $crate::serde::Compressed<$name_affine> for [<$name:upper Compressed >] { - const CONFIG: $crate::serde::CompressedFlagConfig = $flag_config; + impl $crate::encoding::Compressed<$name_affine> for [<$name:upper Compressed >] { + const CONFIG: $crate::encoding::CompressedFlagConfig = $flag_config; fn sign(c: &$name_affine) -> subtle::Choice { Choice::from(c.y.to_repr()[0] as u8 & 1) & !c.is_identity() } @@ -101,22 +101,22 @@ macro_rules! new_curve_impl { paste::paste! { const [< $name:upper _COMPRESSED_SIZE >]: usize = $base::SIZE + if $flag_config.has_extra_byte() { 1 } else { 0 }; - pub type [<$name:upper Compressed >] = $crate::serde::Repr<[< $name:upper _COMPRESSED_SIZE >]>; - pub type [<$name Uncompressed >] = $crate::serde::Repr<{ 2*$base::SIZE }>; + pub type [<$name:upper Compressed >] = $crate::encoding::Repr<[< $name:upper _COMPRESSED_SIZE >]>; + pub type [<$name Uncompressed >] = $crate::encoding::Repr<{ 2*$base::SIZE }>; impl GroupEncoding for $name_affine { type Repr = [<$name:upper Compressed >]; fn from_bytes(bytes: &Self::Repr) -> CtOption { - >::decode(bytes.clone()) + >::decode(bytes.clone()) } fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - >::decode(bytes.clone()) + >::decode(bytes.clone()) } fn to_bytes(&self) -> Self::Repr { - >::encode(&self) + >::encode(&self) } } @@ -124,15 +124,15 @@ macro_rules! new_curve_impl { type Repr = [<$name:upper Compressed >]; fn from_bytes(bytes: &Self::Repr) -> CtOption { - >::decode(bytes.clone()).map(Self::from) + >::decode(bytes.clone()).map(Self::from) } fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - >::decode(bytes.clone()).map(Self::from) + >::decode(bytes.clone()).map(Self::from) } fn to_bytes(&self) -> Self::Repr { - >::encode(&self.to_affine()) + >::encode(&self.to_affine()) } } @@ -613,47 +613,6 @@ macro_rules! new_curve_impl { } } - impl $crate::serde::SerdeObject for $name { - fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { - debug_assert_eq!(bytes.len(), 3 * $base::SIZE); - let [x, y, z] = [0, 1, 2] - .map(|i| $base::from_raw_bytes_unchecked(&bytes[i * $base::SIZE..(i + 1) * $base::SIZE])); - Self { x, y, z } - } - fn from_raw_bytes(bytes: &[u8]) -> Option { - if bytes.len() != 3 * $base::SIZE { - return None; - } - let [x, y, z] = - [0, 1, 2].map(|i| $base::from_raw_bytes(&bytes[i * $base::SIZE..(i + 1) * $base::SIZE])); - x.zip(y).zip(z).and_then(|((x, y), z)| { - let res = Self { x, y, z }; - // Check that the point is on the curve. - bool::from(res.is_on_curve()).then(|| res) - }) - } - fn to_raw_bytes(&self) -> Vec { - let mut res = Vec::with_capacity(3 * $base::SIZE); - Self::write_raw(self, &mut res).unwrap(); - res - } - fn read_raw_unchecked(reader: &mut R) -> Self { - let [x, y, z] = [(); 3].map(|_| $base::read_raw_unchecked(reader)); - Self { x, y, z } - } - fn read_raw(reader: &mut R) -> std::io::Result { - let x = $base::read_raw(reader)?; - let y = $base::read_raw(reader)?; - let z = $base::read_raw(reader)?; - Ok(Self { x, y, z }) - } - fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { - self.x.write_raw(writer)?; - self.y.write_raw(writer)?; - self.z.write_raw(writer) - } - } - impl group::prime::PrimeGroup for $name {} impl group::prime::PrimeCurve for $name { @@ -714,45 +673,6 @@ macro_rules! new_curve_impl { impl cmp::Eq for $name_affine {} - - impl $crate::serde::SerdeObject for $name_affine { - fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { - debug_assert_eq!(bytes.len(), 2 * $base::SIZE); - let [x, y] = - [0, $base::SIZE].map(|i| $base::from_raw_bytes_unchecked(&bytes[i..i + $base::SIZE])); - Self { x, y } - } - fn from_raw_bytes(bytes: &[u8]) -> Option { - if bytes.len() != 2 * $base::SIZE { - return None; - } - let [x, y] = [0, $base::SIZE].map(|i| $base::from_raw_bytes(&bytes[i..i + $base::SIZE])); - x.zip(y).and_then(|(x, y)| { - let res = Self { x, y }; - // Check that the point is on the curve. - bool::from(res.is_on_curve()).then(|| res) - }) - } - fn to_raw_bytes(&self) -> Vec { - let mut res = Vec::with_capacity(2 * $base::SIZE); - Self::write_raw(self, &mut res).unwrap(); - res - } - fn read_raw_unchecked(reader: &mut R) -> Self { - let [x, y] = [(); 2].map(|_| $base::read_raw_unchecked(reader)); - Self { x, y } - } - fn read_raw(reader: &mut R) -> std::io::Result { - let x = $base::read_raw(reader)?; - let y = $base::read_raw(reader)?; - Ok(Self { x, y }) - } - fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { - self.x.write_raw(writer)?; - self.y.write_raw(writer) - } - } - impl group::prime::PrimeCurveAffine for $name_affine { type Curve = $name; type Scalar = $scalar; diff --git a/src/derive/field/tower.rs b/src/derive/field/tower.rs index 9f508fba..274eb46b 100644 --- a/src/derive/field/tower.rs +++ b/src/derive/field/tower.rs @@ -91,7 +91,7 @@ macro_rules! impl_tower2 { } impl PrimeField for $field { - type Repr = $crate::serde::Repr<{ $base::SIZE * 2 }>; + type Repr = $crate::encoding::Repr<{ $base::SIZE * 2 }>; const MODULUS: &'static str = <$base as PrimeField>::MODULUS; const MULTIPLICATIVE_GENERATOR: Self = $field { @@ -148,46 +148,9 @@ macro_rules! impl_tower2 { Choice::from(self.to_repr().as_ref()[0] & 1) } } - - impl $crate::serde::SerdeObject for $field { - fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { - debug_assert_eq!(bytes.len(), $base::SIZE * 2); - let [c0, c1] = [0, $base::SIZE] - .map(|i| $base::from_raw_bytes_unchecked(&bytes[i..i + $base::SIZE])); - Self { c0, c1 } - } - fn from_raw_bytes(bytes: &[u8]) -> Option { - if bytes.len() != $base::SIZE * 2 { - return None; - } - let [c0, c1] = - [0, $base::SIZE].map(|i| $base::from_raw_bytes(&bytes[i..i + $base::SIZE])); - c0.zip(c1).map(|(c0, c1)| Self { c0, c1 }) - } - fn to_raw_bytes(&self) -> Vec { - let mut res = Vec::with_capacity($base::SIZE * 2); - for limb in self.c0.0.iter().chain(self.c1.0.iter()) { - res.extend_from_slice(&limb.to_le_bytes()); - } - res - } - fn read_raw_unchecked(reader: &mut R) -> Self { - let [c0, c1] = [(); 2].map(|_| $base::read_raw_unchecked(reader)); - Self { c0, c1 } - } - fn read_raw(reader: &mut R) -> std::io::Result { - let c0 = $base::read_raw(reader)?; - let c1 = $base::read_raw(reader)?; - Ok(Self { c0, c1 }) - } - fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { - self.c0.write_raw(writer)?; - self.c1.write_raw(writer) - } - } - }; + } } - + #[macro_export] macro_rules! impl_tower2_from_uniform_bytes { ( diff --git a/src/serde.rs b/src/encoding.rs similarity index 83% rename from src/serde.rs rename to src/encoding.rs index 13779934..4f3d79dc 100644 --- a/src/serde.rs +++ b/src/encoding.rs @@ -1,7 +1,4 @@ -use std::{ - fmt::Debug, - io::{self, Read, Write}, -}; +use std::fmt::Debug; #[cfg(feature = "derive_serde")] use serde::{Deserialize, Serialize}; @@ -92,30 +89,6 @@ impl std::ops::IndexMut> for Repr { } } -/// Trait for converting raw bytes to/from the internal representation of a type. -/// For example, field elements are represented in Montgomery form and serialized/deserialized without Montgomery reduction. -pub trait SerdeObject: Sized { - /// The purpose of unchecked functions is to read the internal memory representation - /// of a type from bytes as quickly as possible. No sanitization checks are performed - /// to ensure the bytes represent a valid object. As such this function should only be - /// used internally as an extension of machine memory. It should not be used to deserialize - /// externally provided data. - fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self; - fn from_raw_bytes(bytes: &[u8]) -> Option; - - fn to_raw_bytes(&self) -> Vec; - - /// The purpose of unchecked functions is to read the internal memory representation - /// of a type from disk as quickly as possible. No sanitization checks are performed - /// to ensure the bytes represent a valid object. This function should only be used - /// internally when some machine state cannot be kept in memory (e.g., between runs) - /// and needs to be reloaded as quickly as possible. - fn read_raw_unchecked(reader: &mut R) -> Self; - fn read_raw(reader: &mut R) -> io::Result; - - fn write_raw(&self, writer: &mut W) -> io::Result<()>; -} - pub mod endian { pub trait EndianRepr: Sized { @@ -212,20 +185,20 @@ impl Flag { pub(crate) trait Compressed: Debug + Copy + Default + AsRef<[u8]> + AsMut<[u8]> + Send + Sync + 'static where - C::Base: crate::serde::endian::EndianRepr, + C::Base: crate::encoding::endian::EndianRepr, { const CONFIG: CompressedFlagConfig; fn flag_byte(&mut self) -> &mut u8 { - use crate::serde::endian::EndianRepr; + use crate::encoding::endian::EndianRepr; match Self::CONFIG { // Most sig byte is always the flag byte when extra byte flag is used CompressedFlagConfig::Extra => self.as_mut().first_mut().unwrap(), _ => match C::Base::ENDIAN { // Least sig byte is the flag byte - crate::serde::endian::Endian::LE => self.as_mut().last_mut().unwrap(), + crate::encoding::endian::Endian::LE => self.as_mut().last_mut().unwrap(), // Most sig byte is the flag byte - crate::serde::endian::Endian::BE => self.as_mut().first_mut().unwrap(), + crate::encoding::endian::Endian::BE => self.as_mut().first_mut().unwrap(), }, } } @@ -294,7 +267,7 @@ where } fn encode(c: &C) -> Self { - use crate::serde::endian::EndianRepr; + use crate::encoding::endian::EndianRepr; let mut this = Self::default(); let coordinates = c.coordinates().unwrap(); let x = coordinates.x(); @@ -332,9 +305,9 @@ where let x = match Self::CONFIG { CompressedFlagConfig::Extra => { // Most sig byte is always the flag byte when extra byte flag is used - ::from_bytes(&self.as_ref()[1..]) + ::from_bytes(&self.as_ref()[1..]) } - _ => ::from_bytes(self.as_ref()), + _ => ::from_bytes(self.as_ref()), }; x.and_then(|x| -> subtle::CtOption { diff --git a/src/grumpkin/curve.rs b/src/grumpkin/curve.rs index e212ad83..db7b69a8 100644 --- a/src/grumpkin/curve.rs +++ b/src/grumpkin/curve.rs @@ -31,7 +31,7 @@ new_curve_impl!( G1_B, "grumpkin_g1", |domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, G1::default_hash_to_curve_suite()), - crate::serde::CompressedFlagConfig::TwoSpare, + crate::encoding::CompressedFlagConfig::TwoSpare, standard_sign ); diff --git a/src/lib.rs b/src/lib.rs index f5caae2d..7b8654cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ pub mod ff_ext; pub mod fft; pub mod hash_to_curve; pub mod msm; -pub mod serde; +pub mod encoding; pub mod bls12381; pub mod bn256; diff --git a/src/pluto_eris/curve.rs b/src/pluto_eris/curve.rs index e0452a94..2faf5ffb 100644 --- a/src/pluto_eris/curve.rs +++ b/src/pluto_eris/curve.rs @@ -126,7 +126,7 @@ new_curve_impl!( PLUTO_B, "pluto", |domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, G1::default_hash_to_curve_suite()), - crate::serde::CompressedFlagConfig::TwoSpare, + crate::encoding::CompressedFlagConfig::TwoSpare, standard_sign ); @@ -171,7 +171,7 @@ new_curve_impl!( ERIS_B, "eris", |domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, Eris::default_hash_to_curve_suite()), - crate::serde::CompressedFlagConfig::TwoSpare, + crate::encoding::CompressedFlagConfig::TwoSpare, standard_sign ); @@ -242,8 +242,8 @@ impl Eris { } } -impl crate::serde::endian::EndianRepr for Fp2 { - const ENDIAN: crate::serde::endian::Endian = Fq::ENDIAN; +impl crate::encoding::endian::EndianRepr for Fp2 { + const ENDIAN: crate::encoding::endian::Endian = Fq::ENDIAN; fn to_bytes(&self) -> Vec { self.to_bytes().to_vec() @@ -265,7 +265,7 @@ new_curve_impl!( TRITON_B, "triton", |_| unimplemented!(), - crate::serde::CompressedFlagConfig::TwoSpare, + crate::encoding::CompressedFlagConfig::TwoSpare, standard_sign ); diff --git a/src/secp256k1/curve.rs b/src/secp256k1/curve.rs index 6214cad8..54fd0467 100644 --- a/src/secp256k1/curve.rs +++ b/src/secp256k1/curve.rs @@ -60,7 +60,7 @@ new_curve_impl!( SECP_B, "secp256k1", |domain_prefix| hash_to_curve(domain_prefix, hash_to_curve_suite(b"secp256k1_XMD:SHA-256_SSWU_RO_")), - crate::serde::CompressedFlagConfig::Extra, + crate::encoding::CompressedFlagConfig::Extra, standard_sign ); diff --git a/src/secp256r1/curve.rs b/src/secp256r1/curve.rs index 44486c1d..d8afc281 100644 --- a/src/secp256r1/curve.rs +++ b/src/secp256r1/curve.rs @@ -71,7 +71,7 @@ new_curve_impl!( SECP_B, "secp256r1", |domain_prefix| hash_to_curve(domain_prefix, hash_to_curve_suite(b"P256_XMD:SHA-256_SSWU_RO_")), - crate::serde::CompressedFlagConfig::Extra, + crate::encoding::CompressedFlagConfig::Extra, standard_sign ); diff --git a/src/secq256k1/curve.rs b/src/secq256k1/curve.rs index f08b515d..98bbf9db 100644 --- a/src/secq256k1/curve.rs +++ b/src/secq256k1/curve.rs @@ -43,7 +43,7 @@ new_curve_impl!( SECQ_B, "secq256k1", |domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, Secq256k1::default_hash_to_curve_suite()), - crate::serde::CompressedFlagConfig::Extra, + crate::encoding::CompressedFlagConfig::Extra, standard_sign ); diff --git a/src/tests/curve.rs b/src/tests/curve.rs index a482c9c0..080cd54e 100644 --- a/src/tests/curve.rs +++ b/src/tests/curve.rs @@ -282,32 +282,6 @@ macro_rules! curve_testing_suite { } } - // TODO Change name - macro_rules! random_serialization_test { - ($c: ident) => { - for _ in 0..100 { - let projective_point = $c::random(OsRng); - let affine_point: <$c as CurveExt>::AffineExt = projective_point.into(); - - let projective_bytes = projective_point.to_raw_bytes(); - let projective_point_rec = $c::from_raw_bytes(&projective_bytes).unwrap(); - assert_eq!(projective_point, projective_point_rec); - let mut buf = Vec::new(); - projective_point.write_raw(&mut buf).unwrap(); - let projective_point_rec = $c::read_raw(&mut &buf[..]).unwrap(); - assert_eq!(projective_point, projective_point_rec); - - let affine_bytes = affine_point.to_raw_bytes(); - let affine_point_rec = <$c as CurveExt>::AffineExt::from_raw_bytes(&affine_bytes).unwrap(); - assert_eq!(affine_point, affine_point_rec); - let mut buf = Vec::new(); - affine_point.write_raw(&mut buf).unwrap(); - let affine_point_rec = <$c as CurveExt>::AffineExt::read_raw(&mut &buf[..]).unwrap(); - assert_eq!(affine_point, affine_point_rec); - } - } - } - #[cfg(feature = "derive_serde")] macro_rules! random_serde_test { ($c: ident) => { @@ -345,7 +319,7 @@ macro_rules! curve_testing_suite { use $crate::ff::Field; use $crate::group::prime::PrimeCurveAffine; - use $crate::{group::GroupEncoding, serde::SerdeObject}; + use $crate::group::GroupEncoding; use $crate::{CurveAffine, CurveExt}; use rand_core::OsRng; @@ -367,7 +341,6 @@ macro_rules! curve_testing_suite { #[test] fn test_serialization() { $( - random_serialization_test!($curve); #[cfg(feature = "derive_serde")] random_serde_test!($curve); )* diff --git a/src/tests/field/serde.rs b/src/tests/field/serde.rs index 9192b09b..18c7d3f1 100644 --- a/src/tests/field/serde.rs +++ b/src/tests/field/serde.rs @@ -1,8 +1,6 @@ -use ff::{Field, FromUniformBytes, PrimeField}; +use ff::{FromUniformBytes, PrimeField}; use rand::RngCore; -use crate::serde::SerdeObject; - // Tests to_repr/ from_repr pub(crate) fn from_to_repr_test(mut rng: impl RngCore, n: usize) { // n = 1M @@ -14,21 +12,6 @@ pub(crate) fn from_to_repr_test(mut rng: impl RngCore, n: usize) } } -// Tests to_raw_bytes / from_raw_bytes + read_raw /write_raw -pub(crate) fn from_to_raw_bytes_test(mut rng: impl RngCore, n: usize) { - for _ in 0..n { - let a = F::random(&mut rng); - let bytes = a.to_raw_bytes(); - let b = F::from_raw_bytes(&bytes).unwrap(); - assert_eq!(a, b); - - let mut buf = Vec::new(); - a.write_raw(&mut buf).unwrap(); - let b = F::read_raw(&mut &buf[..]).unwrap(); - assert_eq!(a, b); - } -} - // Tests derive_serde #[cfg(feature = "derive_serde")] pub(crate) fn derive_serde_test(mut rng: impl RngCore, n: usize) @@ -67,7 +50,6 @@ pub(crate) fn test_bits(mut rng: impl RngCore, n: usize) macro_rules! serde_test { ($field:ident) => { test!(serde, $field, from_to_repr_test, 100_000); - test!(serde, $field, from_to_raw_bytes_test, 100_000); #[cfg(feature = "derive_serde")] test!(serde, $field, derive_serde_test, 100_000); }; From 38ce3c661b70697905fa7d818a55dfcb998af21c Mon Sep 17 00:00:00 2001 From: adria0 Date: Fri, 4 Oct 2024 15:22:26 +0200 Subject: [PATCH 2/4] Document serialization --- README.md | 17 +++++++++++++++++ src/bn256/fq12.rs | 2 +- src/ff_ext/inverse.rs | 4 ++-- src/ff_ext/jacobi.rs | 4 ++-- src/pluto_eris/mod.rs | 6 +++--- src/tests/field/serde.rs | 2 +- 6 files changed, 26 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index bfe3b538..0c5a2c05 100644 --- a/README.md +++ b/README.md @@ -60,3 +60,20 @@ The library's top-level directories are organized as follows: * `benches`: Contains benchmarking tests. * `script`: Contains utility scripts. * `src`: Contains the source code of the library, further subdivided into modules for each supported curve (`bn256`, `grumpkin`, `secp256k1`, `secp256r1`, `secq256k1`, `pasta`, `pluto`, `eris`) and additional functionalities (`derive`, `tests`). + +## Notes on serialization + +**Field Encodings** + +- `from_bytes`/`to_bytes`: They use an industry-standard format that is consistent with how curves are encoded. This format is what will be used internally by the Serde library to ensure interoperability. Provides a unified format for both field and curve serialization. Ensures a consistent, industry-standard serialization, using big or little endian depending on the curve +- `from_mont`/`to_mont`: These methods convert elements to and from the Montgomery form, which is an internal representation that is commonly used for efficient field arithmetic. Use these when working specifically with Montgomery-reduced values, especially in cryptographic computations. +- `from_raw`: Creates a field element from a raw integer (typically limbs or u64s). Use this method when directly converting an integer value into a field element. +- `from_uniform_bytes`: Converts a uniform random byte array into a valid field element. This is particularly useful in scenarios requiring a random element in the field, such as in cryptographic protocols or when hashing to the field. + +**Curve Encodings** + +- `GroupEncoding` trait methods: Implements the serialization and deserialization of curve points in a compressed format. Compression is slower but generates standardized encodings that are smaller in size. Suitable for storing and transmitting curve points efficiently. Serde will use this. +- `UncompressedEncoding` trait methods: Provides faster serialization/deserialization of curve points in an uncompressed format. The output is larger than that of GroupEncoding, but it's quicker to generate. When speed is prioritized over size. + +*Notes*: +- `from_bytes`, `to_bytes` from `EndianRepr` trait is only intended for internal use only. Do not use. \ No newline at end of file diff --git a/src/bn256/fq12.rs b/src/bn256/fq12.rs index c6186ac7..81ff8d59 100644 --- a/src/bn256/fq12.rs +++ b/src/bn256/fq12.rs @@ -6,7 +6,7 @@ use crate::ff_ext::{ ExtField, }; -/// -GAMMA is a quadratic non-residue in Fp6. Fp12 = Fp6[X]/(X^2 + GAMMA) +/// -GAMMA is a quadratic non-residue in Fp6. Fp12 = Fp6[\X]/(X^2 + GAMMA) /// We introduce the variable w such that w^2 = -GAMMA // GAMMA = - v /// An element of Fq12, represented by c0 + c1 * w. diff --git a/src/ff_ext/inverse.rs b/src/ff_ext/inverse.rs index d149b3ec..85092198 100644 --- a/src/ff_ext/inverse.rs +++ b/src/ff_ext/inverse.rs @@ -239,9 +239,9 @@ impl Mul> for i64 { /// /// For better understanding the implementation, the following resources are recommended: /// - D. Bernstein, B.-Y. Yang, "Fast constant-time gcd computation and modular inversion", -/// https://gcd.cr.yp.to/safegcd-20190413.pdf +/// /// - P. Wuille, "The safegcd implementation in libsecp256k1 explained", -/// https://github.com/bitcoin-core/secp256k1/blob/master/doc/safegcd_implementation.md +/// pub struct BYInverter { /// Modulus modulus: CInt<62, L>, diff --git a/src/ff_ext/jacobi.rs b/src/ff_ext/jacobi.rs index ed577b5e..c0b81496 100644 --- a/src/ff_ext/jacobi.rs +++ b/src/ff_ext/jacobi.rs @@ -325,9 +325,9 @@ fn jacobinary(mut n: u64, mut d: u64, mut t: u64) -> i64 { /// and some original optimizations. Only these differences have been commented; /// the aforesaid Pornin's method and the used ideas of M. Hamburg were given here: /// - T. Pornin, "Optimized Binary GCD for Modular Inversion", -/// https://eprint.iacr.org/2020/972.pdf +/// /// - M. Hamburg, "Computing the Jacobi symbol using Bernstein-Yang", -/// https://eprint.iacr.org/2021/1271.pdf +/// pub fn jacobi(n: &[u64], d: &[u64]) -> i64 { // Instead of the variable "j" taking the values from {-1, 1} and satisfying // at the end of the outer loop iteration the equation J = "j" * ("n" / |"d"|) diff --git a/src/pluto_eris/mod.rs b/src/pluto_eris/mod.rs index 0d0538ab..405b083a 100644 --- a/src/pluto_eris/mod.rs +++ b/src/pluto_eris/mod.rs @@ -2,9 +2,9 @@ //! //! Implementation of the Pluto / Eris half-pairing cycle of prime order elliptic curves. //! -//! Supporting evidence: https://github.com/daira/pluto-eris -//! Field constant derivation: https://github.com/davidnevadoc/ec-constants/tree/main/pluto_eris -//! Pairing constants derivation: https://github.com/John-Gong-Math/pluto_eris/blob/main/pluto_pairing.ipynb +//! Supporting evidence: +//! Field constant derivation: +//! Pairing constants derivation: mod curve; mod engine; mod fp; diff --git a/src/tests/field/serde.rs b/src/tests/field/serde.rs index 18c7d3f1..78c61ec1 100644 --- a/src/tests/field/serde.rs +++ b/src/tests/field/serde.rs @@ -16,7 +16,7 @@ pub(crate) fn from_to_repr_test(mut rng: impl RngCore, n: usize) #[cfg(feature = "derive_serde")] pub(crate) fn derive_serde_test(mut rng: impl RngCore, n: usize) where - for<'de> F: Field + serde::Serialize + serde::Deserialize<'de>, + for<'de> F: ff::Field + serde::Serialize + serde::Deserialize<'de>, { for _ in 0..n { // byte serialization From 5dd1d49dd6a24b3f9bfc2f4f0c530e9cda74c33b Mon Sep 17 00:00:00 2001 From: adria0 Date: Fri, 4 Oct 2024 15:23:54 +0200 Subject: [PATCH 3/4] Make clippy happy --- src/bn256/curve.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bn256/curve.rs b/src/bn256/curve.rs index d64ffd89..ba022846 100644 --- a/src/bn256/curve.rs +++ b/src/bn256/curve.rs @@ -164,7 +164,7 @@ impl group::cofactor::CofactorGroup for G1 { fn exp_by_x(g2: &G2) -> G2 { let x = super::BN_X; - (0..62).rev().fold(g2.clone(), |mut acc, i| { + (0..62).rev().fold(*g2, |mut acc, i| { println!("{}", ((x >> i) & 1) == 1); acc = acc.double(); From cb0a3f6cedb0bb4c7228e1fbc7a32311c72bac3d Mon Sep 17 00:00:00 2001 From: adria0 Date: Fri, 4 Oct 2024 15:24:17 +0200 Subject: [PATCH 4/4] fmt --- src/bls12381/g2.rs | 2 +- src/derive/field/tower.rs | 4 ++-- src/lib.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bls12381/g2.rs b/src/bls12381/g2.rs index dcc7a974..e84c5ffa 100644 --- a/src/bls12381/g2.rs +++ b/src/bls12381/g2.rs @@ -1,12 +1,12 @@ use crate::bls12381::fq::Fq; use crate::bls12381::fq2::Fq2; use crate::bls12381::fr::Fr; +use crate::encoding::{Compressed, CompressedFlagConfig}; use crate::ff::WithSmallOrderMulGroup; use crate::ff::{Field, PrimeField}; use crate::ff_ext::ExtField; use crate::group::Curve; use crate::group::{cofactor::CofactorGroup, prime::PrimeCurveAffine, Group, GroupEncoding}; -use crate::encoding::{Compressed, CompressedFlagConfig}; use crate::{ impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, impl_binops_multiplicative_mixed, new_curve_impl, diff --git a/src/derive/field/tower.rs b/src/derive/field/tower.rs index 274eb46b..58283f6a 100644 --- a/src/derive/field/tower.rs +++ b/src/derive/field/tower.rs @@ -148,9 +148,9 @@ macro_rules! impl_tower2 { Choice::from(self.to_repr().as_ref()[0] & 1) } } - } + }; } - + #[macro_export] macro_rules! impl_tower2_from_uniform_bytes { ( diff --git a/src/lib.rs b/src/lib.rs index 7b8654cf..e9793ed9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,9 @@ mod arithmetic; +pub mod encoding; pub mod ff_ext; pub mod fft; pub mod hash_to_curve; pub mod msm; -pub mod encoding; pub mod bls12381; pub mod bn256;