Skip to content

Panic when evaluating s(X, Y) with x = 0 or y = 0 #53

@ConstanceBeguier

Description

@ConstanceBeguier

Crate

ragu_circuits

Severity

Repo commit

c9649de

Description

If x = 0 or y = 0, many linear constraint terms vanish, meaning the corresponding constraints are no longer enforced in the aggregated polynomial check. This can weaken soundness by allowing invalid witnesses to pass verification.

Since these values are expected to be non-zero verifier challenges, evaluating the polynomial at zero should be explicitly disallowed.

Code

File name: ragu_circuits/src/sx.rs

pub fn eval<F: Field, C: Circuit<F>, R: Rank>(
    circuit: &C,
    x: F,
    key: F,
) -> Result<unstructured::Polynomial<F, R>> {
    if x == F::ZERO {
        // The polynomial is zero if x is zero.
        return Ok(Polynomial::new());
    }

    let multiplication_constraints = 0;
    let linear_constraints = 0;
    let x_inv = x.invert().expect("x is not zero");
    let xn = x.pow_vartime([R::n() as u64]);
    let xn2 = xn.square();
    let current_u_x = xn2 * x_inv;
    let current_v_x = xn2;
    let xn4 = xn2.square();
    let current_w_x = xn4 * x_inv;

    let mut collector = Collector::<F, R> {
        result: unstructured::Polynomial::new(),
        multiplication_constraints,
        linear_constraints,
        x,
        x_inv,
        current_u_x,
        current_v_x,
        current_w_x,
        one: current_w_x,
        available_b: None,
        _marker: core::marker::PhantomData,
    };
    let (key_wire, _, one) = collector.mul(|| unreachable!())?;

    // Enforce linear constraint key_wire = key to randomize non-trivial
    // evaluations of this circuit polynomial.
    collector.enforce_zero(|lc| {
        lc.add(&key_wire)
            .add_term(&one, Coeff::NegativeArbitrary(key))
    })?;

    let mut outputs = vec![];
    let (io, _) = circuit.witness(&mut collector, Empty)?;
    io.write(&mut collector, &mut outputs)?;
    for output in outputs {
        collector.enforce_zero(|lc| lc.add(output.wire()))?;
    }
    collector.enforce_zero(|lc| lc.add(&one))?;

    collector.result[0..collector.linear_constraints].reverse();
    assert_eq!(collector.result[0], collector.one);

    Ok(collector.result)
}

Recommendations

Return an error or panic when x == 0 or y == 0, and document that verifier challenges must be sampled from F \ {0}.

pub fn eval<F: Field, C: Circuit<F>, R: Rank>(
    circuit: &C,
    x: F,
    key: F,
) -> Result<unstructured::Polynomial<F, R>> {
    // In the ZKP setting, x is a verifier challenge and must be non-zero.
    // If x == 0, linear constraints encoded in s(X, Y) may vanish,
    // weakening soundness of the aggregated check.
    assert_ne!(x, F::ZERO, "challenge x must be non-zero");

    let multiplication_constraints = 0;
    let linear_constraints = 0;
    let x_inv = x.invert().expect("x is not zero");
    let xn = x.pow_vartime([R::n() as u64]);
    let xn2 = xn.square();
    let current_u_x = xn2 * x_inv;
    let current_v_x = xn2;
    let xn4 = xn2.square();
    let current_w_x = xn4 * x_inv;

    ...
}

Also affected

The same change should be applied to:

  • the eval function in ragu_circuits/src/s/sxy.rs
  • the eval function in ragu_circuits/src/s/sy.rs
  • the StageObject::sxy function in ragu_circuits/src/staging/object.rs
  • the StageObject::sx function in ragu_circuits/src/staging/object.rs
  • the StageObject::sy function in ragu_circuits/src/staging/object.rs

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions