Skip to content

Commit

Permalink
Merge pull request #215 from Al-Kindi-0/al-protocol-security
Browse files Browse the repository at this point in the history
Proven security for protocol
  • Loading branch information
irakliyk authored Oct 16, 2023
2 parents 4543689 + 75707a9 commit 4af4947
Showing 1 changed file with 116 additions and 42 deletions.
158 changes: 116 additions & 42 deletions air/src/proof/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ pub use table::Table;

const GRINDING_CONTRIBUTION_FLOOR: u32 = 80;

#[cfg(feature = "std")]
const MAX_PROXIMITY_PARAMETER: u64 = 1000;

// STARK PROOF
// ================================================================================================
/// A proof generated by Winterfell prover.
Expand Down Expand Up @@ -204,58 +207,129 @@ fn get_conjectured_security(
cmp::min(cmp::min(field_security, query_security) - 1, collision_resistance)
}

#[cfg(feature = "std")]
/// Estimates proven security level for the specified proof parameters.
#[cfg(feature = "std")]
fn get_proven_security(
options: &ProofOptions,
base_field_bits: u32,
lde_domain_size: u64,
trace_domain_size: u64,
collision_resistance: u32,
) -> u32 {
let extension_field_bits = (base_field_bits * options.field_extension().degree()) as f64;
let m_min: usize = 3;
let m_max = compute_upper_m(trace_domain_size);

let m_optimal = (m_min as u32..m_max as u32)
.max_by_key(|&a| {
proven_security_protocol_for_m(
options,
base_field_bits,
lde_domain_size,
trace_domain_size,
a as usize,
)
})
.expect(
"Should not fail since m_max is larger than m_min for all trace sizes of length greater than 4",
);

cmp::min(
proven_security_protocol_for_m(
options,
base_field_bits,
lde_domain_size,
trace_domain_size,
m_optimal as usize,
),
collision_resistance as u64,
) as u32
}

let blowup_bits = options.blowup_factor().ilog2() as f64;
/// Computes proven security level for the specified proof parameters for a fixed
/// value of the proximity parameter m in the list-decoding regime.
#[cfg(feature = "std")]
fn proven_security_protocol_for_m(
options: &ProofOptions,
base_field_bits: u32,
lde_domain_size: u64,
trace_domain_size: u64,
m: usize,
) -> u64 {
let extension_field_bits = (base_field_bits * options.field_extension().degree()) as f64;
let num_fri_queries = options.num_queries() as f64;
let lde_size_bits = lde_domain_size.trailing_zeros() as f64;

// blowup_plus_bits is the number of bits in the blowup factor which is the inverse of
// `\rho^+ := (trace_domain_size + 2) / lde_domain_size`. `\rho^+` is used in order to define a larger
// agreement parameter `\alpha^+ := (1 + 1/2m)\sqrt{rho^+} := 1 - \theta^+`. The reason for
// running FRI with a larger agreement parameter is to account for the simplified
// DEEP composition polynomial. See Protocol 3 in https://eprint.iacr.org/2022/1216.
let blowup_plus_bits = ((lde_domain_size as f64) / (trace_domain_size as f64 + 2_f64)).log2();

// m is a parameter greater or equal to 3.
// A larger m gives a worse field security bound but a better query security bound.
// An optimal value of m is then a value that would balance field and query security
// but there is no simple closed form solution.
// This sets m so that field security is equal to the best query security for any value
// of m, unless the calculated value is less than 3 in which case it gets rounded up to 3.
let mut m = extension_field_bits + 1.0;
m -= options.grinding_factor() as f64;
m -= 1.5 * blowup_bits;
m -= 0.5 * num_fri_queries * blowup_plus_bits;
m -= 2.0 * lde_size_bits;
m /= 7.0;
m = 2.0_f64.powf(m);
m -= 0.5;
m = m.max(3.0);

// compute pre-FRI query security
// this considers only the third component given in the corresponding part of eq. 20
// in https://eprint.iacr.org/2021/582, i.e. (m+1/2)^7.n^2 / (2\rho^1.5.q) as all
// other terms are negligible in comparison.
let pre_query_security = (extension_field_bits + 1.0
- 3.0 / 2.0 * blowup_bits
- 2.0 * lde_size_bits
- 7.0 * (m + 0.5).log2()) as u32;

// compute security we get by executing multiple query rounds
let security_per_query = 0.5 * blowup_plus_bits - (1.0 + 1.0 / (2.0 * m)).log2();
let mut query_security = (security_per_query * num_fri_queries) as u32;
let m = m as f64;
let rho = 1.0 / options.blowup_factor() as f64;
let alpha = (1.0 + 0.5 / m) * rho.sqrt();
let theta = 1.0 - alpha;
let max_deg = options.blowup_factor() as f64;

let lde_domain_size = lde_domain_size as f64;
let trace_domain_size = trace_domain_size as f64;

// Computes FRI commit-phase (i.e., pre-query) soundness error.
// This considers only the first term given in eq. 7 in https://eprint.iacr.org/2022/1216.pdf,
// i.e. 0.5 * (m + 0.5)^7 * n^2 / (rho^1.5.q) as all other terms are negligible in comparison.
let fri_commit_err_bits = extension_field_bits
- ((0.5 * (m + 0.5).powf(7.0) / rho.powf(1.5)) * lde_domain_size.powf(2.0)).log2();

// Compute FRI query-phase soundness error
let fri_queries_err_bits =
options.grinding_factor() as f64 - ((1.0 - theta).powf(num_fri_queries)).log2();

// Combined error for FRI
let fri_err_bits = cmp::min(fri_commit_err_bits as u64, fri_queries_err_bits as u64);
if fri_err_bits < 1 {
return 0;
}
let fri_err_bits = fri_err_bits - 1;

// To apply Theorem 8 in https://eprint.iacr.org/2022/1216.pdf, we need to apply FRI with
// a slightly larger agreement parameter alpha.
// More concretely, we need alpha > rho_plus.sqrt() where rho_plus is the rate in function field
// F(Z) and defined as (trace_domain_size + 2.0) / lde_domain_size .
// This means that the range of m needs to be restricted in order to ensure that
// alpha := 1 - theta := rho.sqrt() * (1 + 1/2m) is greater than rho_plus.sqrt().
// Determining the range of m is the responsibility of the calling function.
// Now, once m is fixed, we need to make sure that we choose an m_plus such that
// alpha <= rho_plus.sqrt() * (1 + 1/2m_plus). This m_plus will be used to define
// the list-decoding list size in F(Z).

// Modified rate in function field F(Z)
let rho_plus = (trace_domain_size + 2.0) / lde_domain_size;
// New proximity parameter m_plus, corresponding to rho_plus, needed to make sure that
// alpha < rho_plus.sqrt() * (1 + 1 / (2 * m_plus))
let m_plus = (1.0 / (2.0 * (alpha / rho_plus.sqrt() - 1.0))).ceil();

// List size
let l_plus = (2.0 * m_plus + 1.0) / (2.0 * rho_plus.sqrt());

// ALI related soundness error. Note that C here is equal to 1 because of our use of
// linear batching.
let ali_err_bits = -l_plus.log2() + extension_field_bits;

// DEEP related soundness error. Note that this uses that the denominator |F| - |D ∪ H|
// can be approximated by |F| for all practical domain sizes.
let deep_err_bits =
-(l_plus * (max_deg * (trace_domain_size + 1.0) + (trace_domain_size - 1.0))).log2()
+ extension_field_bits;

let min = cmp::min(cmp::min(fri_err_bits, ali_err_bits as u64), deep_err_bits as u64);
if min < 1 {
return 0;
}

query_security += options.grinding_factor();
min - 1
}

cmp::min(cmp::min(pre_query_security, query_security) - 1, collision_resistance)
/// Computes the largest proximity parameter m needed for Theorem 8
/// in <https://eprint.iacr.org/2022/1216.pdf> to work.
#[cfg(feature = "std")]
fn compute_upper_m(h: u64) -> f64 {
let h = h as f64;
let m_max = (0.25 * h * (1.0 + (1.0 + 2.0 / h).sqrt())).ceil();

// We cap the range to 1000 as the optimal m value will be in the lower range of [m_min, m_max]
// since increasing m too much will lead to a deterioration in the FRI commit soundness making
// any benefit gained in the FRI query soundess mute.
cmp::min(m_max as u64, MAX_PROXIMITY_PARAMETER) as f64
}

0 comments on commit 4af4947

Please sign in to comment.