diff --git a/tfhe-zk-pok/src/curve_api.rs b/tfhe-zk-pok/src/curve_api.rs index c29d9e828..ca83cd887 100644 --- a/tfhe-zk-pok/src/curve_api.rs +++ b/tfhe-zk-pok/src/curve_api.rs @@ -1,29 +1,14 @@ +use ark_ec::pairing::PairingOutput; +use ark_ec::short_weierstrass::Affine; use ark_ec::{AdditiveGroup as Group, CurveGroup, VariableBaseMSM}; use ark_ff::{BigInt, Field, MontFp, Zero}; use ark_poly::univariate::DensePolynomial; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Validate}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use core::fmt; use core::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign}; use serde::{Deserialize, Serialize}; -fn ark_se(a: &A, s: S) -> Result -where - S: serde::Serializer, -{ - let mut bytes = vec![]; - a.serialize_with_mode(&mut bytes, Compress::Yes) - .map_err(serde::ser::Error::custom)?; - s.serialize_bytes(&bytes) -} - -fn ark_de<'de, D, A: CanonicalDeserialize>(data: D) -> Result -where - D: serde::de::Deserializer<'de>, -{ - let s: Vec = serde::de::Deserialize::deserialize(data)?; - let a = A::deserialize_with_mode(s.as_slice(), Compress::Yes, Validate::Yes); - a.map_err(serde::de::Error::custom) -} +use crate::serialization::{SerializableAffine, SerializableFp, SerializableFp12, SerializableFp2}; struct MontIntDisplay<'a, T>(&'a T); diff --git a/tfhe-zk-pok/src/curve_api/bls12_381.rs b/tfhe-zk-pok/src/curve_api/bls12_381.rs index c02b02aef..caf9fcaa4 100644 --- a/tfhe-zk-pok/src/curve_api/bls12_381.rs +++ b/tfhe-zk-pok/src/curve_api/bls12_381.rs @@ -34,6 +34,8 @@ fn bigint_to_le_bytes(x: [u64; 6]) -> [u8; 6 * 8] { } mod g1 { + use crate::serialization::InvalidSerializedAffineError; + use super::*; #[derive( @@ -48,12 +50,31 @@ mod g1 { CanonicalSerialize, CanonicalDeserialize, )] + #[serde( + try_from = "SerializableAffine", + into = "SerializableAffine" + )] #[repr(transparent)] pub struct G1Affine { - #[serde(serialize_with = "ark_se", deserialize_with = "ark_de")] pub(crate) inner: ark_bls12_381::g1::G1Affine, } + impl From for SerializableAffine { + fn from(value: G1Affine) -> Self { + SerializableAffine::compressed(value.inner) + } + } + + impl TryFrom> for G1Affine { + type Error = InvalidSerializedAffineError; + + fn try_from(value: SerializableAffine) -> Result { + Ok(Self { + inner: value.try_into()?, + }) + } + } + impl G1Affine { pub fn multi_mul_scalar(bases: &[Self], scalars: &[Zp]) -> G1 { // SAFETY: interpreting a `repr(transparent)` pointer as its contents. @@ -80,12 +101,31 @@ mod g1 { CanonicalSerialize, CanonicalDeserialize, )] + #[serde( + try_from = "SerializableAffine", + into = "SerializableAffine" + )] #[repr(transparent)] pub struct G1 { - #[serde(serialize_with = "ark_se", deserialize_with = "ark_de")] pub(crate) inner: ark_bls12_381::G1Projective, } + impl From for SerializableAffine { + fn from(value: G1) -> Self { + SerializableAffine::compressed(value.inner.into_affine()) + } + } + + impl TryFrom> for G1 { + type Error = InvalidSerializedAffineError; + + fn try_from(value: SerializableAffine) -> Result { + Ok(Self { + inner: Affine::try_from(value)?.into(), + }) + } + } + impl fmt::Debug for G1 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("G1") @@ -210,6 +250,8 @@ mod g1 { } mod g2 { + use crate::serialization::InvalidSerializedAffineError; + use super::*; #[derive( @@ -224,12 +266,31 @@ mod g2 { CanonicalSerialize, CanonicalDeserialize, )] + #[serde( + try_from = "SerializableAffine", + into = "SerializableAffine" + )] #[repr(transparent)] pub struct G2Affine { - #[serde(serialize_with = "ark_se", deserialize_with = "ark_de")] pub(crate) inner: ark_bls12_381::g2::G2Affine, } + impl From for SerializableAffine { + fn from(value: G2Affine) -> Self { + SerializableAffine::compressed(value.inner) + } + } + + impl TryFrom> for G2Affine { + type Error = InvalidSerializedAffineError; + + fn try_from(value: SerializableAffine) -> Result { + Ok(Self { + inner: value.try_into()?, + }) + } + } + impl G2Affine { pub fn multi_mul_scalar(bases: &[Self], scalars: &[Zp]) -> G2 { // SAFETY: interpreting a `repr(transparent)` pointer as its contents. @@ -256,12 +317,31 @@ mod g2 { CanonicalSerialize, CanonicalDeserialize, )] + #[serde( + try_from = "SerializableAffine", + into = "SerializableAffine" + )] #[repr(transparent)] pub struct G2 { - #[serde(serialize_with = "ark_se", deserialize_with = "ark_de")] pub(crate) inner: ark_bls12_381::G2Projective, } + impl From for SerializableAffine { + fn from(value: G2) -> Self { + SerializableAffine::compressed(value.inner.into_affine()) + } + } + + impl TryFrom> for G2 { + type Error = InvalidSerializedAffineError; + + fn try_from(value: SerializableAffine) -> Result { + Ok(Self { + inner: Affine::try_from(value)?.into(), + }) + } + } + impl fmt::Debug for G2 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[allow(dead_code)] @@ -433,16 +513,34 @@ mod g2 { } mod gt { + use crate::serialization::InvalidArraySizeError; + use super::*; use ark_ec::pairing::Pairing; #[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] + #[serde(try_from = "SerializableFp12", into = "SerializableFp12")] #[repr(transparent)] pub struct Gt { - #[serde(serialize_with = "ark_se", deserialize_with = "ark_de")] inner: ark_ec::pairing::PairingOutput, } + impl From for SerializableFp12 { + fn from(value: Gt) -> Self { + value.inner.0.into() + } + } + + impl TryFrom for Gt { + type Error = InvalidArraySizeError; + + fn try_from(value: SerializableFp12) -> Result { + Ok(Self { + inner: PairingOutput(value.try_into()?), + }) + } + } + impl fmt::Debug for Gt { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[allow(dead_code)] @@ -566,6 +664,8 @@ mod gt { } mod zp { + use crate::serialization::InvalidArraySizeError; + use super::*; use ark_ff::Fp; use zeroize::Zeroize; @@ -605,12 +705,27 @@ mod zp { } #[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Zeroize)] + #[serde(try_from = "SerializableFp", into = "SerializableFp")] #[repr(transparent)] pub struct Zp { - #[serde(serialize_with = "ark_se", deserialize_with = "ark_de")] pub(crate) inner: ark_bls12_381::Fr, } + impl From for SerializableFp { + fn from(value: Zp) -> Self { + value.inner.into() + } + } + impl TryFrom for Zp { + type Error = InvalidArraySizeError; + + fn try_from(value: SerializableFp) -> Result { + Ok(Self { + inner: value.try_into()?, + }) + } + } + impl fmt::Debug for Zp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Zp") diff --git a/tfhe-zk-pok/src/curve_api/bls12_446.rs b/tfhe-zk-pok/src/curve_api/bls12_446.rs index 0cab45355..e29c7674f 100644 --- a/tfhe-zk-pok/src/curve_api/bls12_446.rs +++ b/tfhe-zk-pok/src/curve_api/bls12_446.rs @@ -34,6 +34,8 @@ fn bigint_to_le_bytes(x: [u64; 7]) -> [u8; 7 * 8] { } mod g1 { + use crate::serialization::InvalidSerializedAffineError; + use super::*; #[derive( @@ -45,15 +47,34 @@ mod g1 { Serialize, Deserialize, Hash, - CanonicalSerialize, CanonicalDeserialize, + CanonicalSerialize, + )] + #[serde( + try_from = "SerializableAffine", + into = "SerializableAffine" )] #[repr(transparent)] pub struct G1Affine { - #[serde(serialize_with = "ark_se", deserialize_with = "ark_de")] pub(crate) inner: crate::curve_446::g1::G1Affine, } + impl From for SerializableAffine { + fn from(value: G1Affine) -> Self { + SerializableAffine::compressed(value.inner) + } + } + + impl TryFrom> for G1Affine { + type Error = InvalidSerializedAffineError; + + fn try_from(value: SerializableAffine) -> Result { + Ok(Self { + inner: value.try_into()?, + }) + } + } + impl G1Affine { #[track_caller] pub fn multi_mul_scalar(bases: &[Self], scalars: &[Zp]) -> G1 { @@ -81,12 +102,31 @@ mod g1 { CanonicalSerialize, CanonicalDeserialize, )] + #[serde( + try_from = "SerializableAffine", + into = "SerializableAffine" + )] #[repr(transparent)] pub struct G1 { - #[serde(serialize_with = "ark_se", deserialize_with = "ark_de")] pub(crate) inner: crate::curve_446::g1::G1Projective, } + impl From for SerializableAffine { + fn from(value: G1) -> Self { + SerializableAffine::compressed(value.inner.into_affine()) + } + } + + impl TryFrom> for G1 { + type Error = InvalidSerializedAffineError; + + fn try_from(value: SerializableAffine) -> Result { + Ok(Self { + inner: Affine::try_from(value)?.into(), + }) + } + } + impl fmt::Debug for G1 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("G1") @@ -212,6 +252,7 @@ mod g1 { mod g2 { use super::*; + use crate::serialization::InvalidSerializedAffineError; #[derive( Copy, @@ -225,12 +266,31 @@ mod g2 { CanonicalSerialize, CanonicalDeserialize, )] + #[serde( + try_from = "SerializableAffine", + into = "SerializableAffine" + )] #[repr(transparent)] pub struct G2Affine { - #[serde(serialize_with = "ark_se", deserialize_with = "ark_de")] pub(crate) inner: crate::curve_446::g2::G2Affine, } + impl From for SerializableAffine { + fn from(value: G2Affine) -> Self { + SerializableAffine::compressed(value.inner) + } + } + + impl TryFrom> for G2Affine { + type Error = InvalidSerializedAffineError; + + fn try_from(value: SerializableAffine) -> Result { + Ok(Self { + inner: value.try_into()?, + }) + } + } + impl G2Affine { #[track_caller] pub fn multi_mul_scalar(bases: &[Self], scalars: &[Zp]) -> G2 { @@ -348,12 +408,31 @@ mod g2 { CanonicalSerialize, CanonicalDeserialize, )] + #[serde( + try_from = "SerializableAffine", + into = "SerializableAffine" + )] #[repr(transparent)] pub struct G2 { - #[serde(serialize_with = "ark_se", deserialize_with = "ark_de")] pub(crate) inner: crate::curve_446::g2::G2Projective, } + impl From for SerializableAffine { + fn from(value: G2) -> Self { + SerializableAffine::compressed(value.inner.into_affine()) + } + } + + impl TryFrom> for G2 { + type Error = InvalidSerializedAffineError; + + fn try_from(value: SerializableAffine) -> Result { + Ok(Self { + inner: Affine::try_from(value)?.into(), + }) + } + } + impl fmt::Debug for G2 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[allow(dead_code)] @@ -525,6 +604,7 @@ mod g2 { mod gt { use crate::curve_446::{Fq, Fq12, Fq2}; + use crate::serialization::InvalidSerializedAffineError; use super::*; use ark_ec::pairing::{MillerLoopOutput, Pairing}; @@ -699,12 +779,28 @@ mod gt { } #[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] + #[serde(try_from = "SerializableFp12", into = "SerializableFp12")] #[repr(transparent)] pub struct Gt { - #[serde(serialize_with = "ark_se", deserialize_with = "ark_de")] pub(crate) inner: ark_ec::pairing::PairingOutput, } + impl From for SerializableFp12 { + fn from(value: Gt) -> Self { + value.inner.0.into() + } + } + + impl TryFrom for Gt { + type Error = InvalidSerializedAffineError; + + fn try_from(value: SerializableFp12) -> Result { + Ok(Self { + inner: PairingOutput(value.try_into()?), + }) + } + } + impl fmt::Debug for Gt { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[allow(dead_code)] @@ -827,6 +923,7 @@ mod gt { mod zp { use super::*; + use crate::serialization::InvalidArraySizeError; use ark_ff::Fp; use zeroize::Zeroize; @@ -865,12 +962,27 @@ mod zp { } #[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Zeroize)] + #[serde(try_from = "SerializableFp", into = "SerializableFp")] #[repr(transparent)] pub struct Zp { - #[serde(serialize_with = "ark_se", deserialize_with = "ark_de")] pub inner: crate::curve_446::Fr, } + impl From for SerializableFp { + fn from(value: Zp) -> Self { + value.inner.into() + } + } + impl TryFrom for Zp { + type Error = InvalidArraySizeError; + + fn try_from(value: SerializableFp) -> Result { + Ok(Self { + inner: value.try_into()?, + }) + } + } + impl fmt::Debug for Zp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Zp") diff --git a/tfhe-zk-pok/src/lib.rs b/tfhe-zk-pok/src/lib.rs index 350888e6b..87a41c80e 100644 --- a/tfhe-zk-pok/src/lib.rs +++ b/tfhe-zk-pok/src/lib.rs @@ -3,5 +3,6 @@ pub use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Vali pub mod curve_446; pub mod curve_api; pub mod proofs; +pub mod serialization; mod four_squares; diff --git a/tfhe-zk-pok/src/serialization.rs b/tfhe-zk-pok/src/serialization.rs new file mode 100644 index 000000000..71900eace --- /dev/null +++ b/tfhe-zk-pok/src/serialization.rs @@ -0,0 +1,231 @@ +use std::error::Error; +use std::fmt::Display; +use std::marker::PhantomData; + +use ark_ec::short_weierstrass::{Affine, SWCurveConfig}; +use ark_ec::AffineRepr; +use ark_ff::{BigInt, Field, Fp, Fp2, Fp6, Fp6Config, FpConfig, QuadExtConfig, QuadExtField}; +use serde::{Deserialize, Serialize}; + +/// Error returned when a conversion from a vec to a fixed size array failed because the vec size is +/// incorrect +#[derive(Debug)] +pub struct InvalidArraySizeError { + expected_len: usize, + found_len: usize, +} + +impl Display for InvalidArraySizeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Invalid serialized array: found array of size {}, expected {}", + self.found_len, self.expected_len + ) + } +} + +impl Error for InvalidArraySizeError {} + +/// Tries to convert a Vec into a constant size array, and returns an [`InvalidArraySizeError`] if +/// the size does not match +fn try_vec_to_array(vec: Vec) -> Result<[T; N], InvalidArraySizeError> { + let len = vec.len(); + + vec.try_into().map_err(|_| InvalidArraySizeError { + expected_len: len, + found_len: N, + }) +} + +/// Serialization equivalent of the [`Fp`] struct, where the bigint is split into +/// multiple u64. +#[derive(Serialize, Deserialize)] +pub struct SerializableFp { + val: Vec, // Use a Vec since serde does not support fixed size arrays with a generic +} + +impl, const N: usize> From> for SerializableFp { + fn from(value: Fp) -> Self { + Self { + val: value.0 .0.to_vec(), + } + } +} + +impl, const N: usize> TryFrom for Fp { + type Error = InvalidArraySizeError; + + fn try_from(value: SerializableFp) -> Result { + Ok(Fp(BigInt(try_vec_to_array(value.val)?), PhantomData)) + } +} + +#[derive(Debug)] +pub enum InvalidSerializedAffineError { + InvalidFp(InvalidArraySizeError), + InvalidCompressedXCoordinate, +} + +impl Display for InvalidSerializedAffineError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + InvalidSerializedAffineError::InvalidFp(fp_error) => { + write!(f, "Invalid fp element in affine: {}", fp_error) + } + InvalidSerializedAffineError::InvalidCompressedXCoordinate => { + write!( + f, + "Cannot uncompress affine: X coordinate does not belong to the curve" + ) + } + } + } +} + +impl Error for InvalidSerializedAffineError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + InvalidSerializedAffineError::InvalidFp(fp_error) => Some(fp_error), + InvalidSerializedAffineError::InvalidCompressedXCoordinate => None, + } + } +} + +impl From for InvalidSerializedAffineError { + fn from(value: InvalidArraySizeError) -> Self { + Self::InvalidFp(value) + } +} + +/// Serialization equivalent to the [`Affine`], which support an optional compression mode +/// where only the `x` coordinate is stored, and the `y` is computed on load. +#[derive(Serialize, Deserialize)] +pub(crate) enum SerializableAffine { + Infinity, + Compressed { x: F, take_largest_y: bool }, + Uncompressed { x: F, y: F }, +} + +impl SerializableAffine { + #[allow(unused)] + pub fn uncompressed + Field, C: SWCurveConfig>( + value: Affine, + ) -> Self { + if value.is_zero() { + Self::Infinity + } else { + Self::Uncompressed { + x: value.x.into(), + y: value.y.into(), + } + } + } + + pub fn compressed + Field, C: SWCurveConfig>( + value: Affine, + ) -> Self { + if value.is_zero() { + Self::Infinity + } else { + let take_largest_y = value.y > -value.y; + Self::Compressed { + x: value.x.into(), + take_largest_y, + } + } + } +} + +impl TryFrom> for Affine +where + F: TryInto, +{ + type Error = InvalidSerializedAffineError; + + fn try_from(value: SerializableAffine) -> Result { + match value { + SerializableAffine::Infinity => Ok(Self::zero()), + SerializableAffine::Compressed { x, take_largest_y } => { + Self::get_point_from_x_unchecked(x.try_into()?, take_largest_y) + .ok_or(InvalidSerializedAffineError::InvalidCompressedXCoordinate) + } + SerializableAffine::Uncompressed { x, y } => { + Ok(Self::new_unchecked(x.try_into()?, y.try_into()?)) + } + } + } +} + +#[derive(Serialize, Deserialize)] +pub(crate) struct SerializableQuadExtField { + c0: F, + c1: F, +} + +pub(crate) type SerializableFp2 = SerializableQuadExtField; + +impl From> for SerializableQuadExtField +where + F: From, +{ + fn from(value: QuadExtField

) -> Self { + Self { + c0: value.c0.into(), + c1: value.c1.into(), + } + } +} + +impl TryFrom> for QuadExtField

+where + F: TryInto, +{ + type Error = InvalidArraySizeError; + + fn try_from(value: SerializableQuadExtField) -> Result { + Ok(QuadExtField { + c0: value.c0.try_into()?, + c1: value.c1.try_into()?, + }) + } +} + +#[derive(Serialize, Deserialize)] +pub(crate) struct SerializableCubicExtField { + c0: F, + c1: F, + c2: F, +} + +type SerializableFp6 = SerializableCubicExtField; + +impl From> for SerializableCubicExtField +where + F: From>, +{ + fn from(value: Fp6) -> Self { + Self { + c0: value.c0.into(), + c1: value.c1.into(), + c2: value.c2.into(), + } + } +} + +impl TryFrom> for Fp6 +where + F: TryInto, Error = InvalidArraySizeError>, +{ + type Error = InvalidArraySizeError; + + fn try_from(value: SerializableCubicExtField) -> Result { + Ok(Fp6 { + c0: value.c0.try_into()?, + c1: value.c1.try_into()?, + c2: value.c2.try_into()?, + }) + } +} + +pub(crate) type SerializableFp12 = SerializableQuadExtField;