pub mod hiding;
pub use crate::assumption::SecurityAssumption;
impl SecurityAssumption {
#[must_use]
pub const fn ood_error(
&self,
log_degree: usize,
log_inv_rate: usize,
field_size_bits: usize,
ood_samples: usize,
) -> f64 {
if matches!(self, Self::UniqueDecoding) {
return 0.;
}
let list_size_bits = self.list_size_bits(log_degree, log_inv_rate);
let error = 2. * list_size_bits + (log_degree * ood_samples) as f64;
(ood_samples * field_size_bits) as f64 + 1. - error
}
#[must_use]
pub fn determine_ood_samples(
&self,
security_level: usize,
log_degree: usize,
log_inv_rate: usize,
field_size_bits: usize,
) -> Option<usize> {
if matches!(self, Self::UniqueDecoding) {
return Some(0);
}
(1..64).find(|&ood_samples| {
self.ood_error(log_degree, log_inv_rate, field_size_bits, ood_samples)
>= security_level as f64
})
}
#[must_use]
pub const fn fold_sumcheck_error(
&self,
field_size_bits: usize,
num_variables: usize,
log_inv_rate: usize,
) -> f64 {
let list_size = self.list_size_bits(num_variables, log_inv_rate);
field_size_bits as f64 - (list_size + 1.)
}
#[must_use]
pub fn queries_combination_error(
&self,
field_size_bits: usize,
num_variables: usize,
log_inv_rate: usize,
ood_samples: usize,
num_queries: usize,
) -> f64 {
let list_size = self.list_size_bits(num_variables, log_inv_rate);
let log_combination = libm::log2((ood_samples + num_queries) as f64);
field_size_bits as f64 - (log_combination + list_size + 1.)
}
#[must_use]
pub fn folding_pow_bits(
&self,
security_level: usize,
field_size_bits: usize,
num_variables: usize,
log_inv_rate: usize,
) -> f64 {
let prox_gaps_error = self.prox_gaps_error(num_variables, log_inv_rate, field_size_bits, 2);
let sumcheck_error = self.fold_sumcheck_error(field_size_bits, num_variables, log_inv_rate);
let error = prox_gaps_error.min(sumcheck_error);
0_f64.max(security_level as f64 - error)
}
}
#[cfg(test)]
mod tests {
use super::*;
const KOALABEAR_QUINTIC_BITS: usize = 155;
const MAX_POW_BITS: f64 = 30.0;
#[test]
fn test_folding_pow_bits() {
let field_size_bits = 64;
let soundness = SecurityAssumption::CapacityBound;
let pow_bits = soundness.folding_pow_bits(
100, field_size_bits,
10, 5, );
assert!(pow_bits >= 0.);
}
#[test]
fn jb_prox_gap_covers_security_level_minus_pow_over_koalabear_quintic() {
let jb = SecurityAssumption::JohnsonBound;
let security_level: f64 = 128.0;
let min_required_bits = security_level - MAX_POW_BITS;
for log_inv_rate in 1..=2 {
for log_degree in 10..=22 {
let prox_gap_bits =
jb.prox_gaps_error(log_degree, log_inv_rate, KOALABEAR_QUINTIC_BITS, 2);
assert!(
prox_gap_bits > min_required_bits,
"prox-gap below {min_required_bits:.0} bits at \
log_degree={log_degree}, log_inv_rate={log_inv_rate}: \
got {prox_gap_bits:.2}"
);
}
}
}
#[test]
fn determine_ood_samples_reports_infeasibility() {
let jb = SecurityAssumption::JohnsonBound;
assert_eq!(jb.determine_ood_samples(100, 20, 2, 10), None);
assert!(jb.determine_ood_samples(100, 20, 2, 128).is_some());
assert_eq!(
SecurityAssumption::UniqueDecoding.determine_ood_samples(100, 20, 2, 10),
Some(0)
);
}
#[test]
fn jb_full_security_budget_reaches_128_bits() {
let jb = SecurityAssumption::JohnsonBound;
let security_level: usize = 128;
let min_with_pow = security_level as f64 - MAX_POW_BITS;
let log_degree = 20;
let log_inv_rate = 2;
let num_queries = jb.queries(security_level, log_inv_rate);
let ood_samples = jb
.determine_ood_samples(
security_level,
log_degree,
log_inv_rate,
KOALABEAR_QUINTIC_BITS,
)
.expect("quintic field is large enough for these parameters");
let prox_gap = jb.prox_gaps_error(log_degree, log_inv_rate, KOALABEAR_QUINTIC_BITS, 2);
let sumcheck = jb.fold_sumcheck_error(KOALABEAR_QUINTIC_BITS, log_degree, log_inv_rate);
let ood = jb.ood_error(
log_degree,
log_inv_rate,
KOALABEAR_QUINTIC_BITS,
ood_samples,
);
let query = jb.queries_error(log_inv_rate, num_queries);
let combination = jb.queries_combination_error(
KOALABEAR_QUINTIC_BITS,
log_degree,
log_inv_rate,
ood_samples,
num_queries,
);
assert!(
prox_gap >= min_with_pow,
"prox-gap {prox_gap:.2} bits < {min_with_pow:.0}"
);
assert!(
sumcheck >= min_with_pow,
"sumcheck {sumcheck:.2} bits < {min_with_pow:.0}"
);
assert!(
combination >= min_with_pow,
"combination {combination:.2} bits < {min_with_pow:.0}"
);
assert!(
ood >= security_level as f64,
"OOD {ood:.2} bits < {security_level}"
);
assert!(
query >= security_level as f64,
"query {query:.2} bits < {security_level}"
);
let pow = jb.folding_pow_bits(
security_level,
KOALABEAR_QUINTIC_BITS,
log_degree,
log_inv_rate,
);
assert!(
pow <= MAX_POW_BITS,
"PoW grinding {pow:.2} bits > {MAX_POW_BITS:.0} cap"
);
}
}