Skip to content

fix: rule out zero residue witness in BW6-761 AssertFinalExponentiationIsOne - #1871

Merged
yelhousni merged 2 commits into
masterfrom
fix/bw6761-finalexp-residue-nonzero
Oct 6, 2026
Merged

yelhousni merged 2 commits into
masterfrom
fix/bw6761-finalexp-residue-nonzero

Conversation

@ivokub

@ivokub ivokub commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Description

Pairing.AssertFinalExponentiationIsOne for BW6-761 checks
x == residueWitness^Λ with residueWitness obtained from a hint, but did not
constrain the hint output to be non-zero. The relation is homogeneous in the
hint output: for a zero accumulator (x = 0), the all-zero residue witness
degenerates both exponentiation chains to 0 and DivUnchecked(0, 0) only
enforces 0 = quotient·0, leaving the quotient unconstrained. The final
assertion then reduces to 0 == 0 and passes, accepting a zero accumulator even
though 0 is not in the multiplicative target group and its final exponentiation
is not one.

This is the same bug class as
GHSA-3mvx-pp85-pm65,
fixed for BN254, BLS12-381 (emulated) and BLS12-377 (native) in b1fbb16 by
anchoring the residue witness to be invertible. The BW6-761 variant of the
gadget was missed. The fix mirrors it: Ext6.Inverse asserts
residueWitness·residueWitness⁻¹ == 1, unsatisfiable at residueWitness == 0.
Cost: one E6 inverse hint plus one E6 multiplication per call.

PairingCheck is not affected: a zero residueWitnessInv freezes the Miller
accumulator at 0, so the result == 1 assertion can never pass.

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How has this been tested?

  • New regression test TestFinalExponentiationIsOneRejectsZeroAccumulator:
    a zero accumulator must be rejected. The honest hint already returns the
    all-zero residue witness for x = 0, so the exploit path is exercised
    end-to-end — the test fails before the fix (zero accumulator accepted)
    and passes after.
  • go test -short ./std/algebra/emulated/sw_bw6761/... passes, including
    the existing completeness test TestFinalExponentiationIsOneTestSolve
    (valid non-zero residue witnesses remain invertible).
  • gofmt/go vet clean on the changed package.

How has this been benchmarked?

Not benchmarked: the change adds a fixed-cost soundness anchor (one E6
Inverse per AssertFinalExponentiationIsOne call); no algorithmic changes.

Checklist:

  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added tests that prove my fix is effective or that my feature works
  • I did not modify files generated from templates
  • golangci-lint does not output errors locally — not run: local binary is
    built with go1.25 while the module targets Go 1.26; deferring to CI
  • New and existing unit tests pass locally with my changes

Note

High Risk
This closes a proof soundness bug in pairing final-exponentiation checks that could wrongly accept a zero accumulator; the fix is localized but security-critical for circuits using this gadget.

Overview
Fixes a soundness gap in BW6-761 emulated AssertFinalExponentiationIsOne: the hint-derived residueWitness was not forced to be non-zero, so for accumulator x = 0 an all-zero witness could satisfy x == residueWitness^Λ via degenerate exponentiation and unconstrained DivUnchecked(0, 0).

The change adds Ext6.Inverse on residueWitness (invertibility anchor residueWitness·residueWitness⁻¹ == 1), matching the prior fix on BN254/BLS curves that were missed for BW6-761. Cost is one E6 inverse per assertion call.

Adds regression TestFinalExponentiationIsOneRejectsZeroAccumulator, which feeds a zero GT directly into the assertion circuit and expects unsatisfiability.

Reviewed by Cursor Bugbot for commit 347f228. Bugbot is set up for automated code reviews on this repo. Configure here.

ivokub added 2 commits October 6, 2026 08:33
Signed-off-by: Ivo Kubjas <ivo.kubjas@consensys.com>
Signed-off-by: Ivo Kubjas <ivo.kubjas@consensys.com>
@ivokub
ivokub requested a review from a team as a code owner October 6, 2026 08:42
@ivokub
ivokub requested a review from yelhousni October 6, 2026 08:44

@yelhousni yelhousni left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consistent with the prior fix: sw_bn254/pairing.go:175 and sw_bls12381/pairing.go:609 already call pr.Ext12.Inverse(residueWitness) with near-identical comments and native/sw_bls12377 is also covered. BW6-761 was the only missed site.

@yelhousni
yelhousni merged commit 436d5ac into master Oct 6, 2026
16 checks passed
@yelhousni
yelhousni deleted the fix/bw6761-finalexp-residue-nonzero branch October 6, 2026 15:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants