Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Organize serialization #177

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
89 changes: 12 additions & 77 deletions derive/src/field/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8> {
self.to_bytes().to_vec()
Expand Down Expand Up @@ -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<Self> {
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))
Expand All @@ -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);
Expand Down Expand Up @@ -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()
}
Expand All @@ -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(
Expand All @@ -463,15 +463,15 @@ pub(crate) fn impl_field(input: TokenStream) -> TokenStream {

fn from_repr(repr: Self::Repr) -> subtle::CtOption<Self> {
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()
}

Expand All @@ -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::<Vec<_>>();
Self(inner.try_into().unwrap())
}

fn from_raw_bytes(bytes: &[u8]) -> Option<Self> {
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<u8> {
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<R: std::io::Read>(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<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
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<W: std::io::Write>(&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 {
Expand Down Expand Up @@ -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
};
Expand Down
4 changes: 2 additions & 2 deletions src/bls12381/g1.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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<G1Affine> for G1Compressed {
Expand Down
8 changes: 4 additions & 4 deletions src/bls12381/g2.rs
Original file line number Diff line number Diff line change
@@ -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::serde::{Compressed, CompressedFlagConfig};
use crate::{
impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative,
impl_binops_multiplicative_mixed, new_curve_impl,
Expand Down Expand Up @@ -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<u8> {
self.to_bytes().to_vec()
Expand Down
10 changes: 5 additions & 5 deletions src/bn256/curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8> {
self.to_bytes().to_vec()
Expand All @@ -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
);

Expand All @@ -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
);

Expand Down Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion src/bn256/fq12.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading
Loading