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
152 changes: 152 additions & 0 deletions src/io/utils/simulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,158 @@ where
found
}

pub(crate) fn select_min_secure_ring_dim_gaussian_only<P, BuildParams>(
protocol_name: &str,
crt_depth: usize,
min_log_ring_dim: usize,
max_log_ring_dim: usize,
security_bits: usize,
error_sigma: f64,
skip_lattice_check: bool,
lattice_cache: &mut SecureRingDimLatticeCache,
mut build_params: BuildParams,
) -> Option<SecureRingDimSearchResult>
where
P: PolyParams,
BuildParams: FnMut(u32) -> P,
{
assert!(
min_log_ring_dim <= max_log_ring_dim,
"{protocol_name} log-ring-dimension search range must be non-empty"
);
assert!(
max_log_ring_dim < u32::BITS as usize,
"{protocol_name} max_log_ring_dim must be less than 32"
);
assert!(
error_sigma >= 0.0,
"{protocol_name} lattice-estimator Gaussian stddev must be nonnegative"
);
if skip_lattice_check {
assert_eq!(
min_log_ring_dim, max_log_ring_dim,
"{protocol_name} explicit lattice-check skip requires a single log_ring_dim"
);
let ring_dim = 1u32
.checked_shl(min_log_ring_dim.try_into().expect("log_ring_dim must fit in u32"))
.expect("ring_dim shift overflow");
info!(
protocol_name,
crt_depth,
log_ring_dim = min_log_ring_dim,
ring_dim,
"skipping Gaussian lattice-estimator security check because log_ring_dim was explicit"
);
return Some(SecureRingDimSearchResult {
log_ring_dim: min_log_ring_dim,
ring_dim,
achieved_secpar_for_gauss: None,
achieved_secpar_for_cbd: None,
});
}
let s_dist = Distribution::Ternary;
let e_dist_gauss =
Distribution::DiscreteGaussian { stddev: error_sigma.to_string(), mean: None, n: None };
let required_security: u64 = security_bits.try_into().expect("security_bits must fit in u64");
let mut low = min_log_ring_dim;
let mut high = max_log_ring_dim;
let mut found = None;
while low <= high {
let log_ring_dim = low + (high - low) / 2;
let ring_dim = 1u32
.checked_shl(log_ring_dim.try_into().expect("log_ring_dim must fit in u32"))
.expect("ring_dim shift overflow");
if let Some(cached) = lattice_cache.secure_for(crt_depth, log_ring_dim) {
info!(
protocol_name,
crt_depth,
log_ring_dim,
ring_dim,
achieved_secpar_for_gauss = cached.achieved_secpar_for_gauss,
"skipping Gaussian lattice-estimator security check using larger CRT-depth cache"
);
found = Some(cached);
if log_ring_dim == 0 {
break;
}
high = log_ring_dim - 1;
continue;
}
let params = build_params(ring_dim);
let q: Arc<BigUint> = params.modulus().into();
let ring_dim_big = BigUint::from(ring_dim);
info!(
protocol_name,
crt_depth,
log_ring_dim,
ring_dim,
modulus_bits = q.bits(),
required_security,
error_sigma,
"running Gaussian lattice-estimator security check for CRT-depth ring-dimension candidate"
);
match run_lattice_estimator_cli_with_timeout(
&ring_dim_big,
q.as_ref(),
&s_dist,
&e_dist_gauss,
None,
false,
LATTICE_ESTIMATOR_TIMEOUT,
) {
Ok(achieved_secpar_for_gauss) => {
info!(
protocol_name,
crt_depth,
log_ring_dim,
ring_dim,
achieved_secpar_for_gauss,
required_security,
"evaluated CRT-depth ring-dimension Gaussian security candidate"
);
if achieved_secpar_for_gauss >= required_security {
let result = SecureRingDimSearchResult {
log_ring_dim,
ring_dim,
achieved_secpar_for_gauss: Some(achieved_secpar_for_gauss),
achieved_secpar_for_cbd: None,
};
lattice_cache.record(crt_depth, result);
found = Some(result);
if log_ring_dim == 0 {
break;
}
high = log_ring_dim - 1;
} else {
low = log_ring_dim + 1;
}
}
Err(err) => {
info!(
protocol_name,
crt_depth,
log_ring_dim,
ring_dim,
gauss_error = ?err,
"Gaussian lattice-estimator failed for CRT-depth ring-dimension candidate"
);
low = log_ring_dim + 1;
}
}
}
if found.is_none() {
info!(
protocol_name,
crt_depth,
min_log_ring_dim,
max_log_ring_dim,
required_security,
"no Gaussian-secure ring dimension found for CRT-depth candidate"
);
}
found
}

#[derive(Debug, Clone, Copy)]
pub(crate) struct CpuRingGswContextConfig {
pub p_moduli_bits: usize,
Expand Down
84 changes: 78 additions & 6 deletions src/we/diamond_we/simulation.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{
circuit::{Evaluable, PolyCircuit},
input_injector::DiamondInputErrorSimulation,
io::utils::simulation as sim_utils,
lookup::PltEvaluator,
matrix::PolyMatrix,
poly::{Poly, PolyParams, dcrt::poly::DCRTPoly},
Expand Down Expand Up @@ -36,6 +37,10 @@ pub struct DiamondWEErrorSimulation {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DiamondWECrtDepthSearchResult {
pub crt_depth: usize,
pub log_ring_dim: usize,
pub ring_dim: u32,
pub achieved_secpar_for_gauss: Option<u64>,
pub achieved_secpar_for_cbd: Option<u64>,
pub simulation: DiamondWEErrorSimulation,
}

Expand All @@ -54,6 +59,9 @@ fn diamond_we_correctness_margin_holds(
pub fn diamond_we_find_crt_depth<M, US, HS, TS, PKPE, PKST, ENCPE, ENCST, PE, ST, BuildCandidate>(
min_crt_depth: usize,
max_crt_depth: usize,
min_log_ring_dim: usize,
max_log_ring_dim: usize,
security_bits: usize,
circuit: &PolyCircuit<DCRTPoly>,
mut build_candidate: BuildCandidate,
plt_evaluator: Option<&PE>,
Expand All @@ -66,18 +74,48 @@ where
TS: PolyTrapdoorSampler<M = M> + Send + Sync,
PE: PltEvaluator<ErrorNorm>,
ST: SlotTransferEvaluator<ErrorNorm>,
BuildCandidate: FnMut(usize) -> DiamondWE<M, US, HS, TS, PKPE, PKST, ENCPE, ENCST>,
BuildCandidate: FnMut(u32, usize) -> DiamondWE<M, US, HS, TS, PKPE, PKST, ENCPE, ENCST>,
{
assert!(min_crt_depth > 0, "minimum CRT depth must be positive");
assert!(min_crt_depth <= max_crt_depth, "CRT-depth search range must be non-empty");
info!(
min_crt_depth,
max_crt_depth, "starting DiamondWE CRT-depth search with q/4 correctness margin"
max_crt_depth,
min_log_ring_dim,
max_log_ring_dim,
security_bits,
"starting DiamondWE CRT-depth search with q/4 correctness margin"
);
let force_lattice_check = std::env::var_os("MXX_IO_FORCE_LATTICE_CHECK").is_some();
let explicit_log_ring_dim = min_log_ring_dim == max_log_ring_dim && !force_lattice_check;
if !explicit_log_ring_dim {
sim_utils::assert_lattice_estimator_available("DiamondWE");
}
let mut lattice_cache = sim_utils::SecureRingDimLatticeCache::default();
let mut high = max_crt_depth;
let upper_valid = loop {
info!(crt_depth = high, "evaluating DiamondWE CRT-depth upper-bound candidate");
let candidate = build_candidate(high);
let min_ring_dim = 1u32
.checked_shl(min_log_ring_dim.try_into().expect("min_log_ring_dim must fit in u32"))
.expect("minimum ring_dim shift overflow");
let probe_candidate = build_candidate(min_ring_dim, high);
let Some(ring_dim_search) = sim_utils::select_min_secure_ring_dim_gaussian_only(
"DiamondWE",
high,
min_log_ring_dim,
max_log_ring_dim,
security_bits,
probe_candidate.injector.error_sigma,
explicit_log_ring_dim,
&mut lattice_cache,
|ring_dim| {
let candidate = build_candidate(ring_dim, high);
candidate.injector.params.clone()
},
) else {
return None;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Continue searching when the upper bound is insecure

When lattice checking is enabled for a ring-dimension range, this early return None makes the whole CRT-depth search fail as soon as max_crt_depth is insecure for all allowed ring dimensions. Security decreases as the CRT modulus grows, so a smaller CRT depth may still satisfy both the Gaussian security check and the q/4 correctness margin; the search should not discard those lower depths just because the initial upper-bound probe is insecure.

Useful? React with 👍 / 👎.

};
let candidate = build_candidate(ring_dim_search.ring_dim, high);
let slot_transfer_evaluator = slot_transfer_evaluator
.map(|evaluator| evaluator as &dyn SlotTransferEvaluator<ErrorNorm>);
let simulation =
Expand All @@ -92,7 +130,14 @@ where
"DiamondWE CRT-depth upper-bound candidate evaluated"
);
if valid {
break DiamondWECrtDepthSearchResult { crt_depth: high, simulation };
break DiamondWECrtDepthSearchResult {
crt_depth: high,
log_ring_dim: ring_dim_search.log_ring_dim,
ring_dim: ring_dim_search.ring_dim,
achieved_secpar_for_gauss: ring_dim_search.achieved_secpar_for_gauss,
achieved_secpar_for_cbd: ring_dim_search.achieved_secpar_for_cbd,
simulation,
};
}
let next_high =
high.checked_mul(2).expect("DiamondWE CRT-depth search upper bound overflowed usize");
Expand All @@ -110,7 +155,27 @@ where
while low <= high {
let crt_depth = low + (high - low) / 2;
info!(crt_depth, low, high, "evaluating DiamondWE CRT-depth candidate");
let candidate = build_candidate(crt_depth);
let min_ring_dim = 1u32
.checked_shl(min_log_ring_dim.try_into().expect("min_log_ring_dim must fit in u32"))
.expect("minimum ring_dim shift overflow");
let probe_candidate = build_candidate(min_ring_dim, crt_depth);
let Some(ring_dim_search) = sim_utils::select_min_secure_ring_dim_gaussian_only(
"DiamondWE",
crt_depth,
min_log_ring_dim,
max_log_ring_dim,
security_bits,
probe_candidate.injector.error_sigma,
explicit_log_ring_dim,
&mut lattice_cache,
|ring_dim| {
let candidate = build_candidate(ring_dim, crt_depth);
candidate.injector.params.clone()
},
) else {
return None;
};
let candidate = build_candidate(ring_dim_search.ring_dim, crt_depth);
let slot_transfer_evaluator = slot_transfer_evaluator
.map(|evaluator| evaluator as &dyn SlotTransferEvaluator<ErrorNorm>);
let simulation =
Expand All @@ -125,7 +190,14 @@ where
"DiamondWE CRT-depth candidate evaluated"
);
if valid {
result = Some(DiamondWECrtDepthSearchResult { crt_depth, simulation });
result = Some(DiamondWECrtDepthSearchResult {
crt_depth,
log_ring_dim: ring_dim_search.log_ring_dim,
ring_dim: ring_dim_search.ring_dim,
achieved_secpar_for_gauss: ring_dim_search.achieved_secpar_for_gauss,
achieved_secpar_for_cbd: ring_dim_search.achieved_secpar_for_cbd,
simulation,
});
if crt_depth == min_crt_depth {
break;
}
Expand Down
Loading
Loading