Skip to content
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
2 changes: 1 addition & 1 deletion circuits/lib/src/configs/insecure/threshold.nr
Original file line number Diff line number Diff line change
Expand Up @@ -1167,7 +1167,7 @@ pub global THRESHOLD_SHARE_DECRYPTION_BIT_R1: u32 = 43;
pub global THRESHOLD_SHARE_DECRYPTION_BIT_R2: u32 = 35;
pub global THRESHOLD_SHARE_DECRYPTION_BIT_D: u32 = 35;

pub global THRESHOLD_SHARE_DECRYPTION_R1_BOUNDS: [Field; L] = [8796083584897, 8796061564801];
pub global THRESHOLD_SHARE_DECRYPTION_R1_BOUNDS: [Field; L] = [8796083584898, 8796061564802];
pub global THRESHOLD_SHARE_DECRYPTION_R2_BOUNDS: [Field; L] = [34359701504, 34359615488];

pub global THRESHOLD_SHARE_DECRYPTION_CONFIGS: ShareDecryptionConfigs<L> = ShareDecryptionConfigs::new(
Expand Down
2 changes: 1 addition & 1 deletion circuits/lib/src/configs/secure/threshold.nr
Original file line number Diff line number Diff line change
Expand Up @@ -32916,7 +32916,7 @@ pub global THRESHOLD_SHARE_DECRYPTION_BIT_R2: u32 = 52;
pub global THRESHOLD_SHARE_DECRYPTION_BIT_D: u32 = 52;

pub global THRESHOLD_SHARE_DECRYPTION_R1_BOUNDS: [Field; L] =
[4611686035875690497, 9223372037660080129, 9223372045176272897, 9223372051618723841];
[4611686035875690498, 9223372037660080130, 9223372045176272898, 9223372051618723842];
pub global THRESHOLD_SHARE_DECRYPTION_R2_BOUNDS: [Field; L] =
[1125899911102464, 2251799813881856, 2251799815716864, 2251799817289728];

Expand Down
74 changes: 22 additions & 52 deletions circuits/lib/src/core/threshold/pk_generation.nr
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,7 @@ impl<let N: u32, let L: u32, let BIT_EEK: u32, let BIT_SK: u32, let BIT_E_SM: u3
}

/// Flattens all witness data into a single array for Fiat-Shamir challenge generation
fn payload(
self,
sk_commitment: Field,
pk_commitment: Field,
e_sm_commitment: Field,
) -> Vec<Field> {
fn payload(self, sk_commitment: Field, pk_commitment: Field) -> Vec<Field> {
let mut inputs = Vec::new();

// Flatten CRS polynomials a (L polynomials of degree N)
Expand All @@ -117,7 +112,6 @@ impl<let N: u32, let L: u32, let BIT_EEK: u32, let BIT_SK: u32, let BIT_E_SM: u3
// Use commitments instead of full polynomials
inputs.push(sk_commitment);
inputs.push(pk_commitment);
inputs.push(e_sm_commitment);

// Flatten quotient polynomials (L polynomials each)
inputs = flatten::<_, _, BIT_R1>(inputs, self.r1);
Expand All @@ -129,38 +123,24 @@ impl<let N: u32, let L: u32, let BIT_EEK: u32, let BIT_SK: u32, let BIT_E_SM: u3
/// Main execution function
/// Returns (commit(threshold_sk), commit(threshold_pk), commit(e_sm))
pub fn execute(self) -> (Field, Field, Field) {
// Step 1: Perform range checks on all secret witness values
self.perform_range_checks();

// Step 2: Compute commitments
let sk_commitment = compute_share_computation_sk_commitment::<N, BIT_SK>(self.sk);
let e_sm_commitment =
compute_share_computation_e_sm_commitment::<N, L, BIT_E_SM>(self.e_sm);
let pk_commitment = compute_threshold_pk_commitment::<N, L, BIT_PK>(self.pk0, self.pk1);

// Step 3: Generate Fiat-Shamir challenges using commitments
let gammas = self.generate_challenge(sk_commitment, pk_commitment, e_sm_commitment);
let gamma = self.generate_challenge(sk_commitment, pk_commitment);
self.verify_evaluations(gamma);

// Step 4: Verify public key equations for each modulus
for i in 0..L {
let gamma = gammas.get(i);
self.verify_public_key_for_modulus(i, gamma);
}

// Step 5: Return all commitments
(sk_commitment, pk_commitment, e_sm_commitment)
}

/// Generates Fiat-Shamir challenge values using the SAFE cryptographic sponge
fn generate_challenge(
self,
sk_commitment: Field,
pk_commitment: Field,
e_sm_commitment: Field,
) -> Vec<Field> {
let inputs = self.payload(sk_commitment, pk_commitment, e_sm_commitment);

compute_threshold_pk_challenge::<L>(inputs)
fn generate_challenge(self, sk_commitment: Field, pk_commitment: Field) -> Field {
let inputs = self.payload(sk_commitment, pk_commitment);

compute_threshold_pk_challenge(inputs)
}

/// Performs range checks on all secret witness values
Expand Down Expand Up @@ -190,32 +170,22 @@ impl<let N: u32, let L: u32, let BIT_EEK: u32, let BIT_SK: u32, let BIT_E_SM: u3
}
}

/// Verifies the threshold public key generation equations for a specific CRT basis
fn verify_public_key_for_modulus(self, i: u32, gamma: Field) {
// Evaluate all polynomials at the random challenge point gamma
let a_at_gamma = self.a.map(|a_poly| a_poly.eval(gamma));

let eek_at_gamma = self.eek.eval(gamma);
let sk_at_gamma = self.sk.eval(gamma);

let r1_at_gamma = self.r1.map(|r1_poly| r1_poly.eval(gamma));
let r2_at_gamma = self.r2.map(|r2_poly| r2_poly.eval(gamma));

let pk0_at_gamma = self.pk0.map(|pk0_poly| pk0_poly.eval(gamma));
let pk1_at_gamma = self.pk1.map(|pk1_poly| pk1_poly.eval(gamma));

// Evaluate the cyclotomic polynomial X^N + 1 at gamma
fn verify_evaluations(self, gamma: Field) {
let cyclo_at_gamma = gamma.pow_32(N as Field) + 1;
let sk_at_gamma = self.sk.eval(gamma);
let eek_at_gamma = self.eek.eval(gamma);

// pk0_i = -a_i * sk + eek + r2_i * (X^N + 1) + r1_i * q_i
let expected_pk0 = -a_at_gamma[i] * sk_at_gamma
+ eek_at_gamma
+ r2_at_gamma[i] * cyclo_at_gamma
+ r1_at_gamma[i] * self.configs.qis[i];

assert(pk0_at_gamma[i] == expected_pk0, "Public key equation 1 failed");

// Equation 2: pk1_i = a_i
assert(pk1_at_gamma[i] == a_at_gamma[i], "Public key equation 2 failed");
for i in 0..L {
let expected_pk0 = -self.a[i].eval(gamma) * sk_at_gamma
+ eek_at_gamma
+ self.r2[i].eval(gamma) * cyclo_at_gamma
+ self.r1[i].eval(gamma) * self.configs.qis[i];

assert(self.pk0[i].eval(gamma) == expected_pk0, "Public key equation 1 failed");
assert(
self.pk1[i].eval(gamma) == self.a[i].eval(gamma),
"Public key equation 2 failed",
);
}
}
}
4 changes: 2 additions & 2 deletions circuits/lib/src/math/commitments.nr
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,8 @@ pub fn compute_ciphertext_commitment<let N: u32, let L: u32, let BIT_CT: u32>(

/// COMMITMENTS FOR CHALLENGES

pub fn compute_threshold_pk_challenge<let L: u32>(payload: Vec<Field>) -> Vec<Field> {
compute_challenge::<L>(payload, DS_CLG_PK_GENERATION)
pub fn compute_threshold_pk_challenge(payload: Vec<Field>) -> Field {
compute_commitment(payload, DS_CLG_PK_GENERATION)
}

pub fn compute_share_encryption_challenge<let L: u32>(payload: Vec<Field>) -> Vec<Field> {
Expand Down
29 changes: 14 additions & 15 deletions crates/zk-helpers/src/circuits/commitments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,21 +424,16 @@ pub fn compute_aggregated_shares_commitment(agg_shares: &CrtPolynomial, bit_msg:
///
/// # Arguments
/// * `payload` - Prepared payload as a vector of field elements
/// * `l` - Number of moduli
///
/// # Returns
/// A vector of `BigInt` challenges (2*L elements)
pub fn compute_threshold_pk_challenge(payload: Vec<Field>, l: usize) -> Vec<BigInt> {
/// A `BigInt` representing the commitment hash value
pub fn compute_threshold_pk_challenge(payload: Vec<Field>) -> BigInt {
let input_size = payload.len() as u32;
let io_pattern = [0x80000000 | input_size, (2 * l as u32)];
let io_pattern = [0x80000000 | input_size, 1];

compute_commitments(payload, DS_CLG_PK_GENERATION, io_pattern)
.into_iter()
.map(|challenge_field| {
let challenge_bytes = challenge_field.into_bigint().to_bytes_le();
BigInt::from_bytes_le(num_bigint::Sign::Plus, &challenge_bytes)
})
.collect()
let challenge_field = compute_commitments(payload, DS_CLG_PK_GENERATION, io_pattern)[0];
let challenge_bytes = challenge_field.into_bigint().to_bytes_le();
BigInt::from_bytes_le(num_bigint::Sign::Plus, &challenge_bytes)
}

/// Compute share encryption challenge.
Expand Down Expand Up @@ -600,12 +595,16 @@ mod tests {
}

#[test]
fn compute_threshold_pk_challenge_returns_2l_elements() {
fn compute_threshold_pk_challenge_returns_single_bigint() {
let payload = vec![Field::from(1u64), Field::from(2u64)];
let l = 3;
let input_size = payload.len() as u32;
let io_pattern = [0x80000000 | input_size, 1];
let expected_challenge = field_to_bigint(
compute_commitments(payload.clone(), DS_CLG_PK_GENERATION, io_pattern)[0],
);

let challenges = compute_threshold_pk_challenge(payload, l);
assert_eq!(challenges.len(), 2 * l);
let challenge = compute_threshold_pk_challenge(payload);
assert_eq!(challenge, expected_challenge);
}

#[test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,11 @@ impl Computation for Bounds {
// r_2j bounds: [- (q_j-1)/2 , (q_j-1)/2] (cyclotomic quotients)
r2_bounds.push(qi_bound.clone());

// r_1j upper bound: (qi_bound * (qi_bound * n + 3) - qi_bound) / qi_bigint
// r_1j upper bound: (n * ((q_j-1)/2)^2 + 4 * (q_j-1)/2) / q_j
// Symmetric lower bound used by range_check_2bounds. Variables: qi_bound = (q_j-1)/2,
// qi_bigint = q_j, n = degree.
r1_bounds.push(
(&qi_bound * (&qi_bound.clone() * &n + BigInt::from(3)) - &qi_bound.clone())
(&qi_bound.clone() * &qi_bound.clone() * &n + BigInt::from(4) * &qi_bound.clone())
/ &qi_bigint,
);
}
Expand Down
Loading