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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions circuits/bin/dkg/share_decryption/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
// without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

use lib::configs::default::dkg::{L_THRESHOLD, N, SHARE_DECRYPTION_BIT_MSG};
use lib::configs::default::dkg::{
L_THRESHOLD, N, SHARE_DECRYPTION_BIT_AGG, SHARE_DECRYPTION_BIT_MSG,
};
use lib::configs::default::H;
use lib::core::dkg::share_decryption::ShareDecryption;
use lib::math::polynomial::Polynomial;
Expand All @@ -13,7 +15,7 @@ fn main(
expected_commitments: pub [[Field; L_THRESHOLD]; H],
decrypted_shares: [[Polynomial<N>; L_THRESHOLD]; H],
) -> pub Field {
let share_decryption: ShareDecryption<N, L_THRESHOLD, H, SHARE_DECRYPTION_BIT_MSG> =
let share_decryption: ShareDecryption<N, L_THRESHOLD, H, SHARE_DECRYPTION_BIT_MSG, SHARE_DECRYPTION_BIT_AGG> =
ShareDecryption::new(expected_commitments, decrypted_shares);

share_decryption.execute()
Expand Down
6 changes: 5 additions & 1 deletion circuits/lib/src/configs/insecure/dkg.nr
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
// or FITNESS FOR A PARTICULAR PURPOSE.

use crate::configs::default::{N_PARTIES, T};
pub use crate::configs::insecure::threshold::{L as L_THRESHOLD, QIS as QIS_THRESHOLD};
pub use crate::configs::insecure::threshold::{
L as L_THRESHOLD, QIS as QIS_THRESHOLD,
THRESHOLD_SHARE_DECRYPTION_BIT_SK as SHARE_DECRYPTION_BIT_AGG,
};
use crate::core::dkg::share_computation::chunk::Configs as ShareComputationChunkConfigs;
use crate::core::dkg::share_encryption::Configs as ShareEncryptionConfigs;

Expand Down Expand Up @@ -131,3 +134,4 @@ share_decryption_e_sm (CIRCUIT 4b - BFV DECRYPTION E_SM)
************************************/

pub global SHARE_DECRYPTION_BIT_MSG: u32 = 36;
// SHARE_DECRYPTION_BIT_AGG: see `pub use` of `THRESHOLD_SHARE_DECRYPTION_BIT_SK` (C6 `BIT_SK`).
6 changes: 3 additions & 3 deletions circuits/lib/src/configs/insecure/threshold.nr
Original file line number Diff line number Diff line change
Expand Up @@ -1088,7 +1088,7 @@ pk_aggregation (CIRCUIT 5)
-------------------------------------
************************************/

pub global PK_AGGREGATION_BIT_PK: u32 = 36;
pub global PK_AGGREGATION_BIT_PK: u32 = 35;

pub global PK_AGGREGATION_CONFIGS: PkAggregationConfigs<L> = PkAggregationConfigs::new(QIS);

Expand All @@ -1098,8 +1098,8 @@ user_data_encryption (USED FOR DATA ENCRYPTION)
-------------------------------------
************************************/

pub global USER_DATA_ENCRYPTION_BIT_PK: u32 = 36;
pub global USER_DATA_ENCRYPTION_BIT_CT: u32 = 36;
pub global USER_DATA_ENCRYPTION_BIT_PK: u32 = 35;
pub global USER_DATA_ENCRYPTION_BIT_CT: u32 = 35;
pub global USER_DATA_ENCRYPTION_BIT_U: u32 = 1;
pub global USER_DATA_ENCRYPTION_BIT_E0: u32 = 5;
pub global USER_DATA_ENCRYPTION_BIT_E1: u32 = 5;
Expand Down
8 changes: 6 additions & 2 deletions circuits/lib/src/configs/secure/dkg.nr
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
// or FITNESS FOR A PARTICULAR PURPOSE.

use crate::configs::default::{N_PARTIES, T};
pub use crate::configs::secure::threshold::{L as L_THRESHOLD, QIS as QIS_THRESHOLD};
pub use crate::configs::secure::threshold::{
L as L_THRESHOLD, QIS as QIS_THRESHOLD,
THRESHOLD_SHARE_DECRYPTION_BIT_SK as SHARE_DECRYPTION_BIT_AGG,
};
use crate::core::dkg::share_computation::chunk::Configs as ShareComputationChunkConfigs;
use crate::core::dkg::share_encryption::Configs as ShareEncryptionConfigs;

Expand Down Expand Up @@ -136,4 +139,5 @@ share_decryption_e_sm (CIRCUIT 4b - BFV DECRYPTION E_SM)
-------------------------------------
************************************/

pub global SHARE_DECRYPTION_BIT_MSG: u32 = 55;
pub global SHARE_DECRYPTION_BIT_MSG: u32 = 54;
// SHARE_DECRYPTION_BIT_AGG: see `pub use` of `THRESHOLD_SHARE_DECRYPTION_BIT_SK` (C6 `BIT_SK`).
6 changes: 3 additions & 3 deletions circuits/lib/src/configs/secure/threshold.nr
Original file line number Diff line number Diff line change
Expand Up @@ -32845,7 +32845,7 @@ pk_aggregation (CIRCUIT 5)
-------------------------------------
************************************/

pub global PK_AGGREGATION_BIT_PK: u32 = 53;
pub global PK_AGGREGATION_BIT_PK: u32 = 52;
pub global PK_AGGREGATION_CONFIGS: PkAggregationConfigs<L> = PkAggregationConfigs::new(QIS);

/************************************
Expand All @@ -32854,8 +32854,8 @@ user_data_encryption (USED FOR DATA ENCRYPTION)
-------------------------------------
************************************/

pub global USER_DATA_ENCRYPTION_BIT_PK: u32 = 53;
pub global USER_DATA_ENCRYPTION_BIT_CT: u32 = 53;
pub global USER_DATA_ENCRYPTION_BIT_PK: u32 = 52;
pub global USER_DATA_ENCRYPTION_BIT_CT: u32 = 52;
pub global USER_DATA_ENCRYPTION_BIT_U: u32 = 1;
pub global USER_DATA_ENCRYPTION_BIT_E0: u32 = 105;
pub global USER_DATA_ENCRYPTION_BIT_E1: u32 = 5;
Expand Down
9 changes: 6 additions & 3 deletions circuits/lib/src/core/dkg/share_decryption.nr
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ use crate::math::polynomial::Polynomial;
/// **Produces:**
/// - C4a: `commit(agg_sk)` -> C6 (threshold share decryption).
/// - C4b: `commit(agg_e_sm)` -> C6 (threshold share decryption).
pub struct ShareDecryption<let N: u32, let L: u32, let H: u32, let BIT_MSG: u32> {
///
/// `BIT_MSG` hashes per-share plaintexts (aligned with C2). `BIT_AGG` hashes the CRT aggregate
/// (`compute_aggregated_shares_commitment`) and must match threshold C6 `BIT_SK` / `BIT_E_SM`.
pub struct ShareDecryption<let N: u32, let L: u32, let H: u32, let BIT_MSG: u32, let BIT_AGG: u32> {
/// Expected commitments to the share polynomials, produced in C2a (for C4a) or C2b (for C4b)
/// via commit_to_party_shares. Organised as [party_idx][mod_idx], covering all H honest
/// parties and L CRT moduli.
Expand All @@ -32,7 +35,7 @@ pub struct ShareDecryption<let N: u32, let L: u32, let H: u32, let BIT_MSG: u32>
decrypted_shares: [[Polynomial<N>; L]; H],
}

impl<let N: u32, let L: u32, let H: u32, let BIT_MSG: u32> ShareDecryption<N, L, H, BIT_MSG> {
impl<let N: u32, let L: u32, let H: u32, let BIT_MSG: u32, let BIT_AGG: u32> ShareDecryption<N, L, H, BIT_MSG, BIT_AGG> {
pub fn new(
expected_commitments: [[Field; L]; H],
decrypted_shares: [[Polynomial<N>; L]; H],
Expand Down Expand Up @@ -104,6 +107,6 @@ impl<let N: u32, let L: u32, let H: u32, let BIT_MSG: u32> ShareDecryption<N, L,
let aggregated = self.compute_aggregated_shares();

// Step 3: Publish commitment to the aggregate (C6 input).
compute_aggregated_shares_commitment::<N, L, BIT_MSG>(aggregated)
compute_aggregated_shares_commitment::<N, L, BIT_AGG>(aggregated)
}
}
71 changes: 70 additions & 1 deletion crates/polynomial/src/crt_polynomial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@

//! CRT (Chinese Remainder Theorem) polynomial representation.

use std::sync::Arc;

use crate::polynomial::Polynomial;
use crate::utils::reduce;
use fhe_math::rq::{Poly, Representation};
use fhe_math::rq::traits::TryConvertFrom;
use fhe_math::rq::{Context, Poly, Representation};
use ndarray::Array2;
use num_bigint::BigInt;
use num_traits::{ToPrimitive, Zero};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use thiserror::Error;
Expand Down Expand Up @@ -94,6 +99,57 @@ impl CrtPolynomial {
Self { limbs }
}

/// Builds an fhe-math `Poly` in PowerBasis representation (inverse of [`Self::from_fhe_polynomial`]).
///
/// Coefficients are reduced to `[0, q_i)` per limb using `moduli[i]` before packing the RNS buffer.
/// `ctx` must match the TRBFV/BFV context: `ctx.q.len()` equals limb count, and each limb has
/// `ctx.degree` coefficients.
///
/// # Errors
///
/// Returns [`fhe_math::Error`] if limb counts or coefficient lengths disagree with `ctx`, ndarray
/// layout fails, a coefficient does not fit `u64`, or `Poly::try_convert_from` fails.
pub fn to_fhe_polynomial(
&self,
ctx: &Arc<Context>,
moduli: &[u64],
) -> Result<Poly, fhe_math::Error> {
let degree = ctx.degree;
let l = ctx.q.len();
if self.limbs.len() != l {
return Err(fhe_math::Error::Default(format!(
"CrtPolynomial::to_fhe_polynomial: {} limbs != ctx.q.len() {}",
self.limbs.len(),
l
)));
}
if moduli.len() != l {
return Err(fhe_math::Error::Default(format!(
"CrtPolynomial::to_fhe_polynomial: {} moduli != ctx.q.len() {}",
moduli.len(),
l
)));
}
let mut data = Vec::with_capacity(l * degree);
for i in 0..l {
let coeffs = self.limb(i).coefficients();
if coeffs.len() != degree {
return Err(fhe_math::Error::Default(format!(
"CrtPolynomial::to_fhe_polynomial: limb {i} len {} != ctx.degree {degree}",
coeffs.len()
)));
}
for j in 0..degree {
let u = bigint_to_u64_mod(&coeffs[j], moduli[i])?;
data.push(u);
}
}
let arr = Array2::from_shape_vec((l, degree), data).map_err(|e| {
fhe_math::Error::Default(format!("CrtPolynomial::to_fhe_polynomial: ndarray {e}"))
})?;
Poly::try_convert_from(arr, ctx, false, Representation::PowerBasis)
}

/// Reverses the coefficient order of every limb in-place.
///
/// For each limb, converts between descending degree (a_n, …, a_0) and ascending
Expand Down Expand Up @@ -205,3 +261,16 @@ impl CrtPolynomial {
&self.limbs[i]
}
}

fn bigint_to_u64_mod(c: &BigInt, m: u64) -> Result<u64, fhe_math::Error> {
let bm = BigInt::from(m);
let mut r = c % &bm;
if r < BigInt::zero() {
Comment thread
cedoor marked this conversation as resolved.
r += bm;
}
r.to_u64().ok_or_else(|| {
fhe_math::Error::Default(format!(
"CrtPolynomial::to_fhe_polynomial: coefficient does not fit u64 after mod {m}: {r}"
))
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,14 @@ share_decryption_e_sm (CIRCUIT 4b - BFV DECRYPTION E_SM)
************************************/

pub global {}_BIT_MSG: u32 = {};
pub global {}_BIT_AGG: u32 = {};
"#,
preset.dkg_counterpart().unwrap().metadata().degree,
preset.dkg_counterpart().unwrap().metadata().num_moduli,
prefix,
configs.bits.msg_bit,
prefix,
configs.bits.agg_bit,
)
}

Expand Down Expand Up @@ -117,5 +120,8 @@ mod tests {
assert!(artifacts
.configs
.contains(format!("{}_BIT_MSG: u32 = {}", prefix, configs.bits.msg_bit).as_str()));
assert!(artifacts
.configs
.contains(format!("{}_BIT_AGG: u32 = {}", prefix, configs.bits.agg_bit).as_str()));
}
}
34 changes: 25 additions & 9 deletions crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,22 @@
//! [`Configs`], [`Bounds`], [`Bits`], and [`Inputs`] are produced from BFV parameters
//! and (for input) honest ciphertexts and secret key. Input values are normalized for the ZKP
//! field so the Noir circuit's range checks and commitment checks succeed.
//!
//! Bit widths:
//! - **`msg_bit`** — [`crate::compute_msg_bit`] on the **DKG** BFV params: coefficients in
//! `[0, t)` so the bound is `t − 1`. Matches C2 share-encryption
//! `compute_share_encryption_commitment_from_message` on per-share plaintexts. Emitted as
//! `SHARE_DECRYPTION_BIT_MSG` in codegen.
//! - **`agg_bit`** — [`crate::compute_modulus_bit`] on the **threshold** BFV params: same as C6
//! aggregate hashing. Emitted as `SHARE_DECRYPTION_BIT_AGG`; the Noir C4 circuit uses it for
//! `compute_aggregated_shares_commitment` on the sum (per-share verification still uses `BIT_MSG`).

use crate::circuits::commitments::compute_share_encryption_commitment_from_message;
use crate::dkg::share_decryption::ShareDecryptionCircuit;
use crate::dkg::share_decryption::ShareDecryptionCircuitData;
use crate::CircuitsErrors;
use crate::{bigint_2d_to_json_values, calculate_bit_width, poly_coefficients_to_toml_json};
use crate::{bigint_2d_to_json_values, poly_coefficients_to_toml_json};
use crate::{compute_modulus_bit, compute_msg_bit};
use crate::{CircuitComputation, Computation};
use e3_fhe_params::build_pair_for_preset;
use e3_fhe_params::BfvPreset;
Expand Down Expand Up @@ -68,11 +78,15 @@ pub struct Configs {
pub bounds: Bounds,
}

/// Bit widths used by the Noir prover (e.g. for packing message coefficients).
/// Bit widths used by the Noir prover and witness recomputation.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Bits {
/// Bit width for plaintext/message coefficients (in [0, t)).
/// Per-share message coefficients in `[0, t)`; matches C2 share encryption
/// (`compute_msg_bit` on DKG params).
pub msg_bit: u32,
/// CRT aggregate polynomials (same ring semantics as C6 `sk` / `e_sm`);
/// matches [`crate::compute_modulus_bit`] on threshold params.
pub agg_bit: u32,
}

/// Coefficient bounds for the share-decryption circuit (currently empty; bounds are derived from plaintext modulus).
Expand Down Expand Up @@ -123,11 +137,12 @@ impl Computation for Bits {
type Error = crate::utils::ZkHelpersUtilsError;

fn compute(preset: Self::Preset, _: &Self::Data) -> Result<Self, Self::Error> {
let (_, dkg_params) = build_pair_for_preset(preset)
let (threshold_params, dkg_params) = build_pair_for_preset(preset)
.map_err(|e| crate::utils::ZkHelpersUtilsError::ParseBound(e.to_string()))?;

Ok(Bits {
msg_bit: calculate_bit_width(BigInt::from(dkg_params.plaintext())),
msg_bit: compute_msg_bit(&dkg_params),
agg_bit: compute_modulus_bit(&threshold_params),
})
}
}
Expand Down Expand Up @@ -155,7 +170,7 @@ impl Computation for Inputs {
let mut expected_commitments: Vec<Vec<BigInt>> = Vec::new();
let mut decrypted_shares: Vec<Vec<Vec<BigInt>>> = Vec::new();

let msg_bit = calculate_bit_width(BigInt::from(dkg_params.plaintext()));
let msg_bit = compute_msg_bit(&dkg_params);

// Decrypt each ciphertext and compute its commitment
for party_cts in data.honest_ciphertexts.iter() {
Expand Down Expand Up @@ -240,8 +255,9 @@ mod tests {
let bits = Bits::compute(BfvPreset::InsecureThreshold512, &bounds).unwrap();

let (_, dkg_params) = build_pair_for_preset(BfvPreset::InsecureThreshold512).unwrap();
let expected_msg_bit = calculate_bit_width(BigInt::from(dkg_params.plaintext()));
assert_eq!(bits.msg_bit, expected_msg_bit);
let (threshold_params, _) = build_pair_for_preset(BfvPreset::InsecureThreshold512).unwrap();
assert_eq!(bits.msg_bit, compute_msg_bit(&dkg_params));
assert_eq!(bits.agg_bit, compute_modulus_bit(&threshold_params));
}

#[test]
Expand Down Expand Up @@ -299,7 +315,7 @@ mod tests {

let (threshold_params, dkg_params) = build_pair_for_preset(preset).unwrap();
let threshold_l = threshold_params.moduli().len();
let msg_bit = calculate_bit_width(BigInt::from(dkg_params.plaintext()));
let msg_bit = compute_msg_bit(&dkg_params);

let inputs = Inputs::compute(preset, &sample).unwrap();
assert_eq!(
Expand Down
1 change: 1 addition & 0 deletions crates/zk-prover/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ walkdir = "2.5"
e3-test-helpers = { workspace = true }
ark-bn254 = { workspace = true }
ark-ff = { workspace = true }
num-traits = { workspace = true }
paste = "1"
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }

Expand Down
Loading
Loading