Skip to content

Commit

Permalink
Update fac
Browse files Browse the repository at this point in the history
  • Loading branch information
fjarri committed Dec 21, 2024
1 parent 7505ee2 commit ce8b653
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 109 deletions.
2 changes: 1 addition & 1 deletion synedrion/src/cggmp21/sigma/dec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ impl<P: SchemeParams> DecProof<P> {
let e_scalar = Scalar::from_xof_reader(&mut reader);
let e = public_signed_from_scalar::<P>(&e_scalar);

let z1 = (alpha.to_wide() + secret.y.mul_wide(&e)).to_public();
let z1 = (alpha.to_wide() + secret.y.mul_wide_public(&e)).to_public();
let z2 = (nu + mu * e.to_wide()).to_public();

let omega = secret.rho.to_masked(&r, &e);
Expand Down
110 changes: 45 additions & 65 deletions synedrion/src/cggmp21/sigma/fac.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
//! No small factor proof ($\Pi^{fac}$, Section C.5, Fig. 28)
//! Small-Factor Proof ($\Pi^{fac}$, Section A.4, Fig. 26)
use crypto_bigint::Integer;
use rand_core::CryptoRngCore;
use serde::{Deserialize, Serialize};

use super::super::{conversion::public_signed_from_scalar, SchemeParams};
use super::super::SchemeParams;
use crate::{
curve::Scalar,
paillier::{PaillierParams, PublicKeyPaillier, RPCommitmentWire, RPParams, SecretKeyPaillier},
tools::hashing::{Chain, Hashable, XofHasher},
uint::{HasWide, PublicSigned, SecretSigned},
Expand All @@ -18,25 +17,24 @@ const HASH_TAG: &[u8] = b"P_fac";
ZK proof: No small factor proof.
Secret inputs:
- primes $p$, $q$ such that $p, q < \pm \sqrt{N_0} 2^\ell$.
- primes $p$, $q$ such that $p, q < ±\sqrt{N_0} 2^\ell$.
Public inputs:
- Paillier public key $N_0 = p * q$,
- Setup parameters ($\hat{N}$, $s$, $t$).
*/
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct FacProof<P: SchemeParams> {
e: Scalar,
e: PublicSigned<<P::Paillier as PaillierParams>::Uint>,
cap_p: RPCommitmentWire<P::Paillier>,
cap_q: RPCommitmentWire<P::Paillier>,
cap_a: RPCommitmentWire<P::Paillier>,
cap_b: RPCommitmentWire<P::Paillier>,
cap_t: RPCommitmentWire<P::Paillier>,
sigma: PublicSigned<<P::Paillier as PaillierParams>::ExtraWideUint>,
z1: PublicSigned<<P::Paillier as PaillierParams>::WideUint>,
z2: PublicSigned<<P::Paillier as PaillierParams>::WideUint>,
omega1: PublicSigned<<P::Paillier as PaillierParams>::WideUint>,
omega2: PublicSigned<<P::Paillier as PaillierParams>::WideUint>,
w1: PublicSigned<<P::Paillier as PaillierParams>::WideUint>,
w2: PublicSigned<<P::Paillier as PaillierParams>::WideUint>,
v: PublicSigned<<P::Paillier as PaillierParams>::ExtraWideUint>,
}

Expand All @@ -49,6 +47,9 @@ impl<P: SchemeParams> FacProof<P> {
) -> Self {
let pk0 = sk0.public_key();

// TODO (#27): this assertion is currently not satisfied for TestParams.
// assert!(pk0.modulus().bits_vartime() > 4 * P::L_BOUND);

let hat_cap_n = setup.modulus(); // $\hat{N}$

// NOTE: using `2^(Paillier::PRIME_BITS - 2)` as $\sqrt{N_0}$ (which is its lower bound)
Expand All @@ -68,12 +69,6 @@ impl<P: SchemeParams> FacProof<P> {
// N_0 \hat{N}
let scale = pk0.modulus().mul_wide(hat_cap_n);

let sigma = SecretSigned::<<P::Paillier as PaillierParams>::Uint>::random_in_exponent_range_scaled_wide(
rng,
P::L_BOUND,
&scale,
)
.to_public();
let r = SecretSigned::<<P::Paillier as PaillierParams>::Uint>::random_in_exponent_range_scaled_wide(
rng,
P::L_BOUND + P::EPS_BOUND,
Expand All @@ -89,7 +84,7 @@ impl<P: SchemeParams> FacProof<P> {
let cap_q = setup.commit(&q, &nu);
let cap_a = setup.commit(&alpha, &x).to_wire();
let cap_b = setup.commit(&beta, &y).to_wire();
let cap_t = (&cap_q.pow(&alpha) * &setup.commit_zero(&r)).to_wire();
let cap_t = (&cap_q.pow(&alpha) * &setup.commit_zero_value(&r)).to_wire();
let cap_q = cap_q.to_wire();

let mut reader = XofHasher::new_with_dst(HASH_TAG)
Expand All @@ -99,45 +94,33 @@ impl<P: SchemeParams> FacProof<P> {
.chain(&cap_a)
.chain(&cap_b)
.chain(&cap_t)
.chain(&sigma)
// public parameters
.chain(pk0.as_wire())
.chain(&setup.to_wire())
.chain(aux)
.finalize_to_reader();

// Non-interactive challenge
let e_scalar = Scalar::from_xof_reader(&mut reader);
let e = public_signed_from_scalar::<P>(&e_scalar);
let e = PublicSigned::from_xof_reader_in_exponent_range(&mut reader, P::L_BOUND);
let e_wide = e.to_wide();

let p_wide = sk0.p_wide_signed();

let hat_sigma = sigma
.checked_sub(&(p_wide * &nu).to_public().to_wide())
.expect("doesn't overflow by construction");
let z1 = (alpha + (p * e).to_wide()).to_public();
let z1 = (alpha + (&p * e).to_wide()).to_public();
let z2 = (beta + (q * e).to_wide()).to_public();
let omega1 = (x + mu * e_wide).to_public();
let omega2 = (nu * e_wide + y).to_public();
let v = (r
+ (hat_sigma
.checked_mul(&e_wide.to_wide())
.expect("doesn't overflow by construction")))
.to_public();
let w1 = (x + mu * e_wide).to_public();
let w2 = (y + &nu * e_wide).to_public();
let v = (r - p.mul_wide_public(&e).mul_wide(&nu)).to_public();

Self {
e: e_scalar,
e,
cap_p,
cap_q,
cap_a,
cap_b,
cap_t,
sigma,
z1,
z2,
omega1,
omega2,
w1,
w2,
v,
}
}
Expand All @@ -155,65 +138,62 @@ impl<P: SchemeParams> FacProof<P> {
.chain(&self.cap_a)
.chain(&self.cap_b)
.chain(&self.cap_t)
.chain(&self.sigma)
// public parameters
.chain(pk0.as_wire())
.chain(&setup.to_wire())
.chain(aux)
.finalize_to_reader();

// Non-interactive challenge
let e_scalar = Scalar::from_xof_reader(&mut reader);

if e_scalar != self.e {
return false;
}

let e = public_signed_from_scalar::<P>(&e_scalar);

// R = s^{N_0} t^\sigma
let cap_r = &setup.commit(&pk0.modulus_signed(), &self.sigma);
let e = PublicSigned::from_xof_reader_in_exponent_range(&mut reader, P::L_BOUND);

// s^{z_1} t^{\omega_1} == A * P^e \mod \hat{N}
let cap_a = self.cap_a.to_precomputed(setup);
let cap_p = self.cap_p.to_precomputed(setup);
if setup.commit(&self.z1, &self.omega1) != &cap_a * &cap_p.pow(&e) {
return false;
}

// s^{z_2} t^{\omega_2} == B * Q^e \mod \hat{N}
let cap_b = self.cap_b.to_precomputed(setup);
let cap_q = self.cap_q.to_precomputed(setup);
if setup.commit(&self.z2, &self.omega2) != &cap_b * &cap_q.pow(&e) {
return false;
}

// Q^{z_1} * t^v == T * R^e \mod \hat{N}
let cap_t = self.cap_t.to_precomputed(setup);
if &cap_q.pow(&self.z1) * &setup.commit_zero(&self.v) != &cap_t * &cap_r.pow(&e) {
if e != self.e {
return false;
}

// NOTE: since when creating this proof we generated `alpha` and `beta`
// using the approximation `sqrt(N_0) ~ 2^(PRIME_BITS - 2)`,
// this is the bound we are using here as well.

// z1 \in \pm \sqrt{N_0} 2^{\ell + \eps}
// z1 ∈ ±\sqrt{N_0} 2^{\ell + \eps}
if !self
.z1
.is_in_exponent_range(P::L_BOUND + P::EPS_BOUND + <P::Paillier as PaillierParams>::PRIME_BITS - 2)
{
return false;
}

// z2 \in \pm \sqrt{N_0} 2^{\ell + \eps}
// z2 ∈ ±\sqrt{N_0} 2^{\ell + \eps}
if !self
.z2
.is_in_exponent_range(P::L_BOUND + P::EPS_BOUND + <P::Paillier as PaillierParams>::PRIME_BITS - 2)
{
return false;
}

// R = s^{N_0}
let cap_r = &setup.commit_zero_randomizer(&pk0.modulus_signed());

// s^{z_1} t^{w_1} == A P^e \mod \hat{N}
let cap_a = self.cap_a.to_precomputed(setup);
let cap_p = self.cap_p.to_precomputed(setup);
if setup.commit(&self.z1, &self.w1) != &cap_a * &cap_p.pow(&e) {
return false;

Check warning on line 181 in synedrion/src/cggmp21/sigma/fac.rs

View check run for this annotation

Codecov / codecov/patch

synedrion/src/cggmp21/sigma/fac.rs#L181

Added line #L181 was not covered by tests
}

// s^{z_2} t^{w_2} == B Q^e \mod \hat{N}
let cap_b = self.cap_b.to_precomputed(setup);
let cap_q = self.cap_q.to_precomputed(setup);
if setup.commit(&self.z2, &self.w2) != &cap_b * &cap_q.pow(&e) {
return false;

Check warning on line 188 in synedrion/src/cggmp21/sigma/fac.rs

View check run for this annotation

Codecov / codecov/patch

synedrion/src/cggmp21/sigma/fac.rs#L188

Added line #L188 was not covered by tests
}

// Q^{z_1} * t^v == T R^e \mod \hat{N}
let cap_t = self.cap_t.to_precomputed(setup);
if &cap_q.pow(&self.z1) * &setup.commit_zero_value(&self.v) != &cap_t * &cap_r.pow(&e) {
return false;

Check warning on line 194 in synedrion/src/cggmp21/sigma/fac.rs

View check run for this annotation

Codecov / codecov/patch

synedrion/src/cggmp21/sigma/fac.rs#L194

Added line #L194 was not covered by tests
}

true
}
}
Expand Down
2 changes: 1 addition & 1 deletion synedrion/src/cggmp21/sigma/mul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ impl<P: SchemeParams> MulProof<P> {
.to_wide()
.into_signed()
.expect("conversion to `WideUint` provides enough space for a sign bit")
+ secret.x.mul_wide(&e))
+ secret.x.mul_wide_public(&e))
.to_public();
let u = secret.rho.to_masked(&r, &e);
let v = secret.rho_x.to_masked(&s, &e);
Expand Down
4 changes: 0 additions & 4 deletions synedrion/src/paillier/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,6 @@ where
self.primes.q_signed()
}

pub fn p_wide_signed(&self) -> SecretSigned<P::WideUint> {
self.primes.p_wide_signed()
}

/// Returns Euler's totient function (`φ(n)`) of the modulus, wrapped in a [`Secret`].
pub fn totient_wide_unsigned(&self) -> SecretUnsigned<P::WideUint> {
self.primes.totient_wide_unsigned()
Expand Down
10 changes: 9 additions & 1 deletion synedrion/src/paillier/ring_pedersen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,21 @@ impl<P: PaillierParams> RPParams<P> {
}

/// Creates a commitment for a secret `randomizer` and the value 0.
pub fn commit_zero<R>(&self, randomizer: &R) -> RPCommitment<P>
pub fn commit_zero_value<R>(&self, randomizer: &R) -> RPCommitment<P>
where
P::UintMod: Exponentiable<R>,
{
RPCommitment(self.base_randomizer.pow(randomizer))
}

/// Creates a commitment for a secret `randomizer` and the value 0.
pub fn commit_zero_randomizer<R>(&self, value: &R) -> RPCommitment<P>
where
P::UintMod: Exponentiable<R>,
{
RPCommitment(self.base_value.pow(value))
}

pub fn to_wire(&self) -> RPParamsWire<P> {
RPParamsWire {
modulus: self.modulus.to_wire(),
Expand Down
4 changes: 0 additions & 4 deletions synedrion/src/paillier/rsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,6 @@ impl<P: PaillierParams> SecretPrimes<P> {
Secret::init_with(|| NonZero::new(*self.q().expose_secret()).expect("`q` is non-zero"))
}

pub fn p_wide_signed(&self) -> SecretSigned<P::WideUint> {
self.p_signed().to_wide()
}

pub fn totient(&self) -> &Secret<P::Uint> {
&self.totient
}
Expand Down
30 changes: 29 additions & 1 deletion synedrion/src/tools/hashing.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crypto_bigint::{Encoding, Integer, NonZero};
use crypto_bigint::{Bounded, Encoding, Integer, NonZero};
use digest::{Digest, ExtendableOutput, Update, XofReader};
use hashing_serializer::HashingSerializer;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -141,6 +141,34 @@ impl<T: Serialize> Hashable for T {
}
}

pub(crate) fn uint_from_xof_2<T>(reader: &mut impl XofReader, n_bits: u32) -> T
where
T: Integer + Bounded + Encoding,
{
assert!(n_bits <= T::BITS);
let n_bytes = n_bits.div_ceil(8) as usize;

// If the number of bits is not a multiple of 8, use a mask to zeroize the high bits in the
// gererated random bytestring, so that we don't have to reject too much.
let mask = if n_bits & 7 != 0 {
(1 << (n_bits & 7)) - 1

Check warning on line 154 in synedrion/src/tools/hashing.rs

View check run for this annotation

Codecov / codecov/patch

synedrion/src/tools/hashing.rs#L154

Added line #L154 was not covered by tests
} else {
u8::MAX
};

let mut bytes = T::zero().to_le_bytes();
let buf = bytes
.as_mut()
.get_mut(0..n_bytes)
.expect("`n_bytes` does not exceed `T::BYTES` as asserted above");
reader.read(buf);
bytes.as_mut().last_mut().map(|byte| {
*byte &= mask;
Some(byte)
});
T::from_le_bytes(bytes)
}

/// Build a `T` integer from an extendable Reader function. The resulting `T` is guaranteed to be
/// smaller than the modulus (uses rejection sampling).
pub(crate) fn uint_from_xof<T>(reader: &mut impl XofReader, modulus: &NonZero<T>) -> T
Expand Down
Loading

0 comments on commit ce8b653

Please sign in to comment.