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

Argument grouping for ZK proofs #169

Merged
merged 6 commits into from
Dec 19, 2024
Merged
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
387 changes: 239 additions & 148 deletions synedrion/src/cggmp21/interactive_signing.rs

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions synedrion/src/cggmp21/sigma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ mod mul_star;
mod prm;
mod sch;

pub(crate) use aff_g::AffGProof;
pub(crate) use dec::DecProof;
pub(crate) use enc::EncProof;
pub(crate) use aff_g::{AffGProof, AffGPublicInputs, AffGSecretInputs};
pub(crate) use dec::{DecProof, DecPublicInputs, DecSecretInputs};
pub(crate) use enc::{EncProof, EncPublicInputs, EncSecretInputs};
pub(crate) use fac::FacProof;
pub(crate) use log_star::LogStarProof;
pub(crate) use log_star::{LogStarProof, LogStarPublicInputs, LogStarSecretInputs};
pub(crate) use mod_::ModProof;
pub(crate) use mul::MulProof;
pub(crate) use mul_star::MulStarProof;
pub(crate) use mul::{MulProof, MulPublicInputs, MulSecretInputs};
pub(crate) use mul_star::{MulStarProof, MulStarPublicInputs, MulStarSecretInputs};
pub(crate) use prm::PrmProof;
pub(crate) use sch::{SchCommitment, SchProof, SchSecret};
191 changes: 105 additions & 86 deletions synedrion/src/cggmp21/sigma/aff_g.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,37 @@ use crate::{

const HASH_TAG: &[u8] = b"P_aff_g";

/**
ZK proof: Paillier Affine Operation with Group Commitment in Range.

NOTE: deviation from the paper here.
The proof in the paper assumes $D = C (*) x (+) enc_0(y, \rho)$.
But the way it is used in the Presigning, $D$ will actually be $... (+) enc_0(-y, \rho)$.
So we have to negate several variables when constructing the proof for the whole thing to work.

Secret inputs:
- $x \in \pm 2^\ell$,
- $y \in \pm 2^{\ell^\prime}$,
- $\rho$, a Paillier randomizer for the public key $N_0$,
- $\rho_y$, a Paillier randomizer for the public key $N_1$.

Public inputs:
- Paillier public keys $N_0$, $N_1$,
- Paillier ciphertext $C$ encrypted with $N_0$,
- Paillier ciphertext $D = C (*) x (+) enc_0(-y, \rho)$,
- Paillier ciphertext $Y = enc_1(y, \rho_y)$,
- Point $X = g * x$, where $g$ is the curve generator,
- Setup parameters ($\hat{N}$, $s$, $t$).
*/
pub(crate) struct AffGSecretInputs<'a, P: SchemeParams> {
/// $x \in \pm 2^\ell$.
pub x: &'a SecretSigned<<P::Paillier as PaillierParams>::Uint>,
/// $y \in \pm 2^{\ell^\prime}$.
pub y: &'a SecretSigned<<P::Paillier as PaillierParams>::Uint>,
/// $\rho$, a Paillier randomizer for the public key $N_0$.
pub rho: &'a Randomizer<P::Paillier>,
/// $\rho_y$, a Paillier randomizer for the public key $N_1$.
pub rho_y: &'a Randomizer<P::Paillier>,
}

pub(crate) struct AffGPublicInputs<'a, P: SchemeParams> {
/// Paillier public keys $N_0$.
pub pk0: &'a PublicKeyPaillier<P::Paillier>,
/// Paillier public keys $N_1$.
pub pk1: &'a PublicKeyPaillier<P::Paillier>,
/// Paillier ciphertext $C$ encrypted with $N_0$.
pub cap_c: &'a Ciphertext<P::Paillier>,
/// Paillier ciphertext $D = C (*) x (+) enc_0(-y, \rho)$.
// NOTE: deviation from the paper here.
// The proof in the paper assumes $D = C (*) x (+) enc_0(y, \rho)$.
// But the way it is used in the Presigning, $D$ will actually be $... (+) enc_0(-y, \rho)$.
// So we have to negate several variables when constructing the proof for the whole thing to work.
pub cap_d: &'a Ciphertext<P::Paillier>,
/// Paillier ciphertext $Y = enc_1(y, \rho_y)$.
pub cap_y: &'a Ciphertext<P::Paillier>,
/// Point $X = g * x$, where $g$ is the curve generator.
pub cap_x: &'a Point,
}

/// ZK proof: Paillier Affine Operation with Group Commitment in Range.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct AffGProof<P: SchemeParams> {
e: PublicSigned<<P::Paillier as PaillierParams>::Uint>,
Expand All @@ -60,52 +69,43 @@ pub(crate) struct AffGProof<P: SchemeParams> {
}

impl<P: SchemeParams> AffGProof<P> {
#[allow(clippy::too_many_arguments)]
pub fn new(
rng: &mut impl CryptoRngCore,
x: &SecretSigned<<P::Paillier as PaillierParams>::Uint>,
y: &SecretSigned<<P::Paillier as PaillierParams>::Uint>,
rho: &Randomizer<P::Paillier>,
rho_y: &Randomizer<P::Paillier>,
pk0: &PublicKeyPaillier<P::Paillier>,
pk1: &PublicKeyPaillier<P::Paillier>,
cap_c: &Ciphertext<P::Paillier>,
cap_d: &Ciphertext<P::Paillier>,
cap_y: &Ciphertext<P::Paillier>,
cap_x: &Point,
secret: AffGSecretInputs<'_, P>,
public: AffGPublicInputs<'_, P>,
setup: &RPParams<P::Paillier>,
aux: &impl Hashable,
) -> Self {
x.assert_exponent_range(P::L_BOUND);
y.assert_exponent_range(P::LP_BOUND);
assert!(cap_c.public_key() == pk0);
assert!(cap_d.public_key() == pk0);
assert!(cap_y.public_key() == pk1);
secret.x.assert_exponent_range(P::L_BOUND);
secret.y.assert_exponent_range(P::LP_BOUND);
assert!(public.cap_c.public_key() == public.pk0);
assert!(public.cap_d.public_key() == public.pk0);
assert!(public.cap_y.public_key() == public.pk1);

let hat_cap_n = setup.modulus();

let alpha = SecretSigned::random_in_exp_range(rng, P::L_BOUND + P::EPS_BOUND);
let beta = SecretSigned::random_in_exp_range(rng, P::LP_BOUND + P::EPS_BOUND);

let r = Randomizer::random(rng, pk0);
let r_y = Randomizer::random(rng, pk1);
let r = Randomizer::random(rng, public.pk0);
let r_y = Randomizer::random(rng, public.pk1);

let gamma = SecretSigned::random_in_exp_range_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n);
let m = SecretSigned::random_in_exp_range_scaled(rng, P::L_BOUND, hat_cap_n);
let delta = SecretSigned::random_in_exp_range_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n);
let mu = SecretSigned::random_in_exp_range_scaled(rng, P::L_BOUND, hat_cap_n);

let cap_a = (cap_c * &alpha + Ciphertext::new_with_randomizer_signed(pk0, &beta, &r)).to_wire();
let cap_a = (public.cap_c * &alpha + Ciphertext::new_with_randomizer_signed(public.pk0, &beta, &r)).to_wire();
let cap_b_x = secret_scalar_from_signed::<P>(&alpha).mul_by_generator();
let cap_b_y = Ciphertext::new_with_randomizer_signed(pk1, &beta, &r_y).to_wire();
let cap_b_y = Ciphertext::new_with_randomizer_signed(public.pk1, &beta, &r_y).to_wire();
let cap_e = setup.commit(&alpha, &gamma).to_wire();
let cap_s = setup.commit(x, &m).to_wire();
let cap_s = setup.commit(secret.x, &m).to_wire();
let cap_f = setup.commit(&beta, &delta).to_wire();

// NOTE: deviation from the paper to support a different $D$
// (see the comment in `AffGProof`)
// (see the comment in `AffGPublicInputs`)
// Original: $s^y$. Modified: $s^{-y}$
let cap_t = setup.commit(&(-y), &mu).to_wire();
let cap_t = setup.commit(&(-secret.y), &mu).to_wire();

let mut reader = XofHasher::new_with_dst(HASH_TAG)
// commitments
Expand All @@ -117,12 +117,12 @@ impl<P: SchemeParams> AffGProof<P> {
.chain(&cap_s)
.chain(&cap_t)
// public parameters
.chain(pk0.as_wire())
.chain(pk1.as_wire())
.chain(&cap_c.to_wire())
.chain(&cap_d.to_wire())
.chain(&cap_y.to_wire())
.chain(cap_x)
.chain(public.pk0.as_wire())
.chain(public.pk1.as_wire())
.chain(&public.cap_c.to_wire())
.chain(&public.cap_d.to_wire())
.chain(&public.cap_y.to_wire())
.chain(public.cap_x)
.chain(&setup.to_wire())
.chain(aux)
.finalize_to_reader();
Expand All @@ -131,23 +131,23 @@ impl<P: SchemeParams> AffGProof<P> {
let e = PublicSigned::from_xof_reader_bounded(&mut reader, &P::CURVE_ORDER);
let e_wide = e.to_wide();

let z1 = (alpha + x * e).to_public();
let z1 = (alpha + secret.x * e).to_public();

// NOTE: deviation from the paper to support a different $D$
// (see the comment in `AffGProof`)
// (see the comment in `AffGPublicInputs`)
// Original: $z_2 = \beta + e y$
// Modified: $z_2 = \beta - e y$
let z2 = (beta + (-y) * e).to_public();
let z2 = (beta + (-secret.y) * e).to_public();

let z3 = (gamma + m * e_wide).to_public();
let z4 = (delta + mu * e_wide).to_public();

let omega = rho.to_masked(&r, &e);
let omega = secret.rho.to_masked(&r, &e);

// NOTE: deviation from the paper to support a different $D$
// (see the comment in `AffGProof`)
// (see the comment in `AffGPublicInputs`)
// Original: $\rho_y^e$. Modified: $\rho_y^{-e}$.
let omega_y = rho_y.to_masked(&r_y, &-e);
let omega_y = secret.rho_y.to_masked(&r_y, &-e);

Self {
e,
Expand All @@ -168,20 +168,10 @@ impl<P: SchemeParams> AffGProof<P> {
}

#[allow(clippy::too_many_arguments)]
pub fn verify(
&self,
pk0: &PublicKeyPaillier<P::Paillier>,
pk1: &PublicKeyPaillier<P::Paillier>,
cap_c: &Ciphertext<P::Paillier>,
cap_d: &Ciphertext<P::Paillier>,
cap_y: &Ciphertext<P::Paillier>,
cap_x: &Point,
setup: &RPParams<P::Paillier>,
aux: &impl Hashable,
) -> bool {
assert!(cap_c.public_key() == pk0);
assert!(cap_d.public_key() == pk0);
assert!(cap_y.public_key() == pk1);
pub fn verify(&self, public: AffGPublicInputs<'_, P>, setup: &RPParams<P::Paillier>, aux: &impl Hashable) -> bool {
assert!(public.cap_c.public_key() == public.pk0);
assert!(public.cap_d.public_key() == public.pk0);
assert!(public.cap_y.public_key() == public.pk1);

let mut reader = XofHasher::new_with_dst(HASH_TAG)
// commitments
Expand All @@ -193,12 +183,12 @@ impl<P: SchemeParams> AffGProof<P> {
.chain(&self.cap_s)
.chain(&self.cap_t)
// public parameters
.chain(pk0.as_wire())
.chain(pk1.as_wire())
.chain(&cap_c.to_wire())
.chain(&cap_d.to_wire())
.chain(&cap_y.to_wire())
.chain(cap_x)
.chain(public.pk0.as_wire())
.chain(public.pk1.as_wire())
.chain(&public.cap_c.to_wire())
.chain(&public.cap_d.to_wire())
.chain(&public.cap_y.to_wire())
.chain(public.cap_x)
.chain(&setup.to_wire())
.chain(aux)
.finalize_to_reader();
Expand All @@ -222,24 +212,26 @@ impl<P: SchemeParams> AffGProof<P> {

// C^{z_1} (1 + N_0)^{z_2} \omega^{N_0} = A D^e \mod N_0^2
// => C (*) z_1 (+) encrypt_0(z_2, \omega) = A (+) D (*) e
if cap_c * &self.z1 + Ciphertext::new_public_with_randomizer_signed(pk0, &self.z2, &self.omega)
!= cap_d * &e + self.cap_a.to_precomputed(pk0)
if public.cap_c * &self.z1 + Ciphertext::new_public_with_randomizer_signed(public.pk0, &self.z2, &self.omega)
!= public.cap_d * &e + self.cap_a.to_precomputed(public.pk0)
{
return false;
}

// g^{z_1} = B_x X^e
if scalar_from_signed::<P>(&self.z1).mul_by_generator() != self.cap_b_x + cap_x * &scalar_from_signed::<P>(&e) {
if scalar_from_signed::<P>(&self.z1).mul_by_generator()
!= self.cap_b_x + public.cap_x * &scalar_from_signed::<P>(&e)
{
return false;
}

// NOTE: deviation from the paper to support a different `D`
// (see the comment in `AffGProof`)
// (see the comment in `AffGPublicInputs`)
// Original: `Y^e`. Modified `Y^{-e}`.
// (1 + N_1)^{z_2} \omega_y^{N_1} = B_y Y^(-e) \mod N_1^2
// => encrypt_1(z_2, \omega_y) = B_y (+) Y (*) (-e)
if Ciphertext::new_public_with_randomizer_signed(pk1, &self.z2, &self.omega_y)
!= cap_y * &(-e) + self.cap_b_y.to_precomputed(pk1)
if Ciphertext::new_public_with_randomizer_signed(public.pk1, &self.z2, &self.omega_y)
!= public.cap_y * &(-e) + self.cap_b_y.to_precomputed(public.pk1)
{
return false;
}
Expand All @@ -266,7 +258,7 @@ impl<P: SchemeParams> AffGProof<P> {
mod tests {
use rand_core::OsRng;

use super::AffGProof;
use super::{AffGProof, AffGPublicInputs, AffGSecretInputs};
use crate::{
cggmp21::{conversion::secret_scalar_from_signed, SchemeParams, TestParams},
paillier::{Ciphertext, RPParams, Randomizer, SecretKeyPaillierWire},
Expand Down Expand Up @@ -301,8 +293,35 @@ mod tests {
let cap_x = secret_scalar_from_signed::<TestParams>(&x).mul_by_generator();

let proof = AffGProof::<Params>::new(
&mut OsRng, &x, &y, &rho, &rho_y, pk0, pk1, &cap_c, &cap_d, &cap_y, &cap_x, &rp_params, &aux,
&mut OsRng,
AffGSecretInputs {
x: &x,
y: &y,
rho: &rho,
rho_y: &rho_y,
},
AffGPublicInputs {
pk0,
pk1,
cap_c: &cap_c,
cap_d: &cap_d,
cap_y: &cap_y,
cap_x: &cap_x,
},
&rp_params,
&aux,
);
assert!(proof.verify(pk0, pk1, &cap_c, &cap_d, &cap_y, &cap_x, &rp_params, &aux));
assert!(proof.verify(
AffGPublicInputs {
pk0,
pk1,
cap_c: &cap_c,
cap_d: &cap_d,
cap_y: &cap_y,
cap_x: &cap_x,
},
&rp_params,
&aux
));
}
}
Loading
Loading