Skip to content
Closed
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
9 changes: 6 additions & 3 deletions std/algebra/emulated/sw_bls12381/pairing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func (c *PairingCheckCircuit) Define(api frontend.API) error {
if err != nil {
return fmt.Errorf("new pairing: %w", err)
}
err = pairing.PairingCheck([]*G1Affine{&c.In1G1, &c.In1G1, &c.In2G1, &c.In2G1}, []*G2Affine{&c.In1G2, &c.In2G2, &c.In1G2, &c.In2G2})
err = pairing.PairingCheck([]*G1Affine{&c.In1G1, &c.In2G1}, []*G2Affine{&c.In1G2, &c.In2G2})
if err != nil {
return fmt.Errorf("pair: %w", err)
}
Expand All @@ -186,10 +186,13 @@ func (c *PairingCheckCircuit) Define(api frontend.API) error {

func TestPairingCheckTestSolve(t *testing.T) {
assert := test.NewAssert(t)
// e(a,2b) * e(-2a,b) == 1
p1, q1 := randomG1G2Affines()
_, q2 := randomG1G2Affines()
var p2 bls12381.G1Affine
p2.Neg(&p1)
p2.Double(&p1).Neg(&p2)
var q2 bls12381.G2Affine
q2.Set(&q1)
q1.Double(&q1)
witness := PairingCheckCircuit{
In1G1: NewG1Affine(p1),
In1G2: NewG2Affine(q1),
Expand Down
9 changes: 6 additions & 3 deletions std/algebra/emulated/sw_bn254/pairing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ func (c *PairingCheckCircuit) Define(api frontend.API) error {
if err != nil {
return fmt.Errorf("new pairing: %w", err)
}
err = pairing.PairingCheck([]*G1Affine{&c.In1G1, &c.In1G1, &c.In2G1, &c.In2G1}, []*G2Affine{&c.In1G2, &c.In2G2, &c.In1G2, &c.In2G2})
err = pairing.PairingCheck([]*G1Affine{&c.In1G1, &c.In2G1}, []*G2Affine{&c.In1G2, &c.In2G2})
if err != nil {
return fmt.Errorf("pair: %w", err)
}
Expand All @@ -223,10 +223,13 @@ func (c *PairingCheckCircuit) Define(api frontend.API) error {

func TestPairingCheckTestSolve(t *testing.T) {
assert := test.NewAssert(t)
// e(a,2b) * e(-2a,b) == 1
p1, q1 := randomG1G2Affines()
_, q2 := randomG1G2Affines()
var p2 bn254.G1Affine
p2.Neg(&p1)
p2.Double(&p1).Neg(&p2)
var q2 bn254.G2Affine
q2.Set(&q1)
q1.Double(&q1)
witness := PairingCheckCircuit{
In1G1: NewG1Affine(p1),
In1G2: NewG2Affine(q1),
Expand Down
9 changes: 6 additions & 3 deletions std/algebra/emulated/sw_bw6761/pairing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func (c *PairingCheckCircuit) Define(api frontend.API) error {
if err != nil {
return fmt.Errorf("new pairing: %w", err)
}
err = pairing.PairingCheck([]*G1Affine{&c.In1G1, &c.In1G1, &c.In2G1, &c.In2G1}, []*G2Affine{&c.In1G2, &c.In2G2, &c.In1G2, &c.In2G2})
err = pairing.PairingCheck([]*G1Affine{&c.In1G1, &c.In2G1}, []*G2Affine{&c.In1G2, &c.In2G2})
if err != nil {
return fmt.Errorf("pair: %w", err)
}
Expand All @@ -184,10 +184,13 @@ func (c *PairingCheckCircuit) Define(api frontend.API) error {

func TestPairingCheckTestSolve(t *testing.T) {
assert := test.NewAssert(t)
// e(a,2b) * e(-2a,b) == 1
p1, q1 := randomG1G2Affines()
_, q2 := randomG1G2Affines()
var p2 bw6761.G1Affine
p2.Neg(&p1)
p2.Double(&p1).Neg(&p2)
var q2 bw6761.G2Affine
q2.Set(&q1)
q1.Double(&q1)
witness := PairingCheckCircuit{
In1G1: NewG1Affine(p1),
In1G2: NewG2Affine(q1),
Expand Down
67 changes: 64 additions & 3 deletions std/algebra/native/fields_bls12377/e12_pairing.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package fields_bls12377

import "github.com/consensys/gnark/frontend"
import (
"github.com/consensys/gnark/frontend"
)

// nSquareKarabina2345 repeated compressed cyclotmic square
func (e *E12) nSquareKarabina2345(api frontend.API, n int) {
Expand Down Expand Up @@ -125,7 +127,7 @@ func (e *E12) MulBy01234(api frontend.API, x [5]E2) *E12 {
return e
}

// ExpX0 compute e1^X0, where X0=9586122913090633729
// ExpX0 compute e1^X0, where X0=0x8508c00000000001
func (e *E12) ExpX0(api frontend.API, e1 E12) *E12 {

res := e1
Expand All @@ -148,7 +150,7 @@ func (e *E12) ExpX0(api frontend.API, e1 E12) *E12 {

}

// ExpX0Minus1Square computes e1^((X0-1)^2), where X0=9586122913090633729
// ExpX0Minus1Square computes e1^((X0-1)^2), where X0=0x8508c00000000001
func (e *E12) ExpX0Minus1Square(api frontend.API, e1 E12) *E12 {

var t0, t1, t2, t3, res E12
Expand Down Expand Up @@ -176,3 +178,62 @@ func (e *E12) ExpX0Minus1Square(api frontend.API, e1 E12) *E12 {
return e

}

// ExpU compute e1^U, where U=(X0-1)^2/3 and X0=0x8508c00000000001
func (e *E12) ExpU(api frontend.API, e1 E12) *E12 {

var t0, t1, t2, t3 E12
t0.CyclotomicSquare(api, e1)
e.Mul(api, e1, t0)
t0.Mul(api, t0, *e)
t1.CyclotomicSquare(api, t0)
t2.Mul(api, e1, t1)
t1.CyclotomicSquare(api, t2)
t1.Mul(api, e1, t1)
t3.CyclotomicSquare(api, t1)
t3.nSquareKarabina2345(api, 7)
t2.Mul(api, t2, t3)
t2.nSquareKarabina2345(api, 6)
t1.Mul(api, t1, t2)
t1.nSquareKarabina2345(api, 4)
t0.Mul(api, t0, t1)
t0.nSquareKarabina2345(api, 4)
t0.Mul(api, e1, t0)
t0.nSquareKarabina2345(api, 6)
e.Mul(api, *e, t0)
e.nSquareKarabina2345(api, 92)

return e
}

// FinalExponentiationCheck checks that a Miller function output x lies in the
// same equivalence class as the reduced pairing. This replaces the final
// exponentiation step in-circuit.
// The method follows Section 4 of [On Proving Pairings] paper by A. Novakovic and L. Eagen.
//
// [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf
func (x *E12) FinalExponentiationCheck(api frontend.API) *E12 {
res, err := api.NewHint(finalExpHint, 12, x.C0.B0.A0, x.C0.B0.A1, x.C0.B1.A0, x.C0.B1.A1, x.C0.B2.A0, x.C0.B2.A1, x.C1.B0.A0, x.C1.B0.A1, x.C1.B1.A0, x.C1.B1.A1, x.C1.B2.A0, x.C1.B2.A1)
if err != nil {
// err is non-nil only for invalid number of inputs
panic(err)
}

var residueWitness, t0, t1 E12
residueWitness.assign(res[:12])

// Check that x == residueWitness^r by checking that:
// x^k == residueWitness^(q-u)
// where k = (u-1)^2/3, u=0x8508c00000000001 the BLS12-377 seed
// and residueWitness from the hint.
t0.Frobenius(api, residueWitness)
// exponentiation by u
t1.ExpX0(api, residueWitness)
t0.DivUnchecked(api, t0, t1)
// exponentiation by U=(u-1)^2/3
t1.ExpU(api, *x)

t0.AssertIsEqual(api, t1)

return nil
}
41 changes: 41 additions & 0 deletions std/algebra/native/fields_bls12377/hints.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func GetHints() []solver.Hint {
inverseE2Hint,
inverseE6Hint,
inverseE12Hint,
finalExpHint,
}
}

Expand Down Expand Up @@ -183,3 +184,43 @@ func inverseE12Hint(_ *big.Int, inputs []*big.Int, res []*big.Int) error {

return nil
}

func finalExpHint(_ *big.Int, inputs, outputs []*big.Int) error {
// This follows section 4.1 of https://eprint.iacr.org/2024/640.pdf (Th. 1)
var millerLoop, residueWitness bls12377.E12
var rInv big.Int

millerLoop.C0.B0.A0.SetBigInt(inputs[0])
millerLoop.C0.B0.A1.SetBigInt(inputs[1])
millerLoop.C0.B1.A0.SetBigInt(inputs[2])
millerLoop.C0.B1.A1.SetBigInt(inputs[3])
millerLoop.C0.B2.A0.SetBigInt(inputs[4])
millerLoop.C0.B2.A1.SetBigInt(inputs[5])
millerLoop.C1.B0.A0.SetBigInt(inputs[6])
millerLoop.C1.B0.A1.SetBigInt(inputs[7])
millerLoop.C1.B1.A0.SetBigInt(inputs[8])
millerLoop.C1.B1.A1.SetBigInt(inputs[9])
millerLoop.C1.B2.A0.SetBigInt(inputs[10])
millerLoop.C1.B2.A1.SetBigInt(inputs[11])

// compute r-th root:
// Exponentiate to rInv where
// rInv = 1/r mod (p^12-1)/r
rInv.SetString("10208748786837724877156759805239199917177088105850452097581398357781838343395535357717189281303817989030853426518402432972842003898131337328036123767288343472740290055992597327047843733820915319931789911460922332092893340406586659134897564792528992841360758317184371729851779228767022291575333619687450508149408291808665688425747351943672765401504532352302933193380323797562510328188696218527623617803336085522730402534476672219254656573543964081905263468361471236263414063051754798717965006033535737262535426559024265337552194770692912115634227850824349287678445075667227500062644865552196269410386640693498752691820776510255615212781349365166954324182425386812172284685207545357376138024103577229096125179210029150108085917426622002636460159193270457153824484424740291304472618370893768349010724508505559223070061402562692522679753894779470665228357312064233458448950731987606712484774731314132451528794596084167373606499619244419076763354699647800243598600024554183146018594109053978613659377521869110074983807776968064443737295525761159893356460041590615623520614511285178649677625190127954790028024727845772017830862600186003274909528270245217670645634670358694128233669703480796660621582552083085232238280068277127279315415621696399036462472389073266975782056160166232984523898881", 10)
residueWitness.Exp(millerLoop, &rInv)

residueWitness.C0.B0.A0.BigInt(outputs[0])
residueWitness.C0.B0.A1.BigInt(outputs[1])
residueWitness.C0.B1.A0.BigInt(outputs[2])
residueWitness.C0.B1.A1.BigInt(outputs[3])
residueWitness.C0.B2.A0.BigInt(outputs[4])
residueWitness.C0.B2.A1.BigInt(outputs[5])
residueWitness.C1.B0.A0.BigInt(outputs[6])
residueWitness.C1.B0.A1.BigInt(outputs[7])
residueWitness.C1.B1.A0.BigInt(outputs[8])
residueWitness.C1.B1.A1.BigInt(outputs[9])
residueWitness.C1.B2.A0.BigInt(outputs[10])
residueWitness.C1.B2.A1.BigInt(outputs[11])

return nil
}
17 changes: 13 additions & 4 deletions std/algebra/native/sw_bls12377/pairing.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,13 +253,22 @@ func Pair(api frontend.API, P []G1Affine, Q []G2Affine) (GT, error) {
//
// This function doesn't check that the inputs are in the correct subgroups
func PairingCheck(api frontend.API, P []G1Affine, Q []G2Affine) error {
f, err := Pair(api, P, Q)
f, err := MillerLoop(api, P, Q)
if err != nil {
return err
}
var one GT
one.SetOne()
f.AssertIsEqual(api, one)
// We perform the easy part of the final exp to push f to the cyclotomic
// subgroup so that FinalExponentiationCheck is carried with optimized
// cyclotomic squaring (e.g. Karabina12345).
//
// f = f^(p⁶-1)(p²+1)
var buf GT
buf.Conjugate(api, f)
buf.DivUnchecked(api, buf, f)
f.FrobeniusSquare(api, buf).
Mul(api, f, buf)

f.FinalExponentiationCheck(api)

return nil
}
Expand Down
16 changes: 12 additions & 4 deletions std/algebra/native/sw_bls12377/pairing2.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,13 +298,21 @@ func (p *Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error {
for i := range Q {
inQ[i] = *Q[i]
}
res, err := Pair(p.api, inP, inQ)
res, err := MillerLoop(p.api, inP, inQ)
if err != nil {
return err
}
var one fields_bls12377.E12
one.SetOne()
res.AssertIsEqual(p.api, one)
// We perform the easy part of the final exp to push res to the cyclotomic
// subgroup so that FinalExponentiationCheck is carried with optimized
// cyclotomic squaring (e.g. Karabina12345).
//
// res = res^(p⁶-1)(p²+1)
var buf GT
buf.Conjugate(p.api, res)
buf.DivUnchecked(p.api, buf, res)
res.FrobeniusSquare(p.api, buf).Mul(p.api, res, buf)

res.FinalExponentiationCheck(p.api)
return nil
}

Expand Down
4 changes: 3 additions & 1 deletion std/algebra/native/sw_bls12377/pairing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,11 @@ func pairingData() (P bls12377.G1Affine, Q bls12377.G2Affine, milRes, pairingRes
}

func pairingCheckData() (P [2]bls12377.G1Affine, Q [2]bls12377.G2Affine) {
// e(a,2b) * e(-2a,b) == 1
_, _, P[0], Q[0] = bls12377.Generators()
P[1].Neg(&P[0])
P[1].Double(&P[0]).Neg(&P[1])
Q[1].Set(&Q[0])
Q[0].Double(&Q[0])

return
}
Expand Down