Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: emulated subgroup check #629

Merged
merged 33 commits into from
Apr 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
263b4b3
feat: add IsOnCurve to sw_emulated
yelhousni Mar 23, 2023
fc31d31
feat: add IsOnCurve to sw_bn254/g2
yelhousni Mar 24, 2023
6d0884c
Merge branch 'develop' into feat/emulated/subgroup-check
yelhousni Mar 24, 2023
45394fc
feat(pairing): check points are on curve and twist
yelhousni Mar 24, 2023
d82be56
Merge branch 'develop' into feat/emulated/subgroup-check
yelhousni Apr 7, 2023
52f2465
test: test bls12-381 in sw_emulated + comments
yelhousni Apr 7, 2023
b9484d1
test: add bn254 and bl12381 test of AssertIsOnCurve
yelhousni Apr 7, 2023
3bea95b
feat(sw_bn254): add AssertIsOnG2
yelhousni Apr 7, 2023
d62695e
feat(sw_bls12381): add AssertIsOnG1 and AssertIsOnG2
yelhousni Apr 7, 2023
ec8253a
Merge branch 'develop' into feat/emulated/subgroup-check
yelhousni Apr 11, 2023
ba144cc
chore: point to gnark-crypto@develop
yelhousni Apr 11, 2023
0e0958c
feat: use AssertIsOnG2 for ECPAIR precompile + comments
yelhousni Apr 11, 2023
760d534
fix: add (0,0) case to curve membership
yelhousni Apr 13, 2023
b6cc505
feat(sw_bn254): G2 membership without hints
yelhousni Apr 17, 2023
1da8f15
feat(sw_bn254): endomorphism optims for G2 membership
yelhousni Apr 17, 2023
3979f8d
perf(sw_bn254): optim of fixed scalar Mul on G2
yelhousni Apr 17, 2023
73f927c
perf(sw_bn254): use 2-NAF for fixed scalar Mul on G2
yelhousni Apr 17, 2023
ead049c
fix(sw_bn254): fix size of 2-naf table of the seed
yelhousni Apr 17, 2023
b5caf13
perf(sw_bn254): use addchain/doubleAndAdd for fixed scalar mul
yelhousni Apr 17, 2023
e955bb5
fix: use jacobain double for test
yelhousni Apr 17, 2023
0d17cd7
refactor: init emulated constants once
ivokub Apr 17, 2023
d6a7232
refactor: g2 gadget as pointer
ivokub Apr 17, 2023
8c87b18
docs: subgroup check in doc-example
ivokub Apr 17, 2023
c89e536
refactor: init point at return
ivokub Apr 17, 2023
ee8e883
refactor: use assertIsOnCurve from sw_emulated
ivokub Apr 18, 2023
36ee060
refactor: init b of twist once
ivokub Apr 18, 2023
93604b7
refactor: in method work with pointers instead of values
ivokub Apr 18, 2023
48a3742
Merge branch 'develop' into feat/emulated/subgroup-check
yelhousni Apr 18, 2023
af9d1d4
feat(sw_bls12381): G1 and G2 membership without hints
yelhousni Apr 18, 2023
20aba9c
chore: go get gnark-crypto@develop
yelhousni Apr 18, 2023
462d04f
docs: typo
ivokub Apr 25, 2023
5da7411
Merge branch 'develop' into feat/emulated/subgroup-check
ivokub Apr 25, 2023
07416c7
test: use assertless sampling
ivokub Apr 25, 2023
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
4 changes: 4 additions & 0 deletions std/algebra/emulated/sw_bls12381/doc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ func (c *PairCircuit) Define(api frontend.API) error {
if err != nil {
return fmt.Errorf("new pairing: %w", err)
}
// Pair method does not check that the points are in the proper groups.
pairing.AssertIsOnG1(&c.InG1)
pairing.AssertIsOnG2(&c.InG2)
// Compute the pairing
res, err := pairing.Pair([]*sw_bls12381.G1Affine{&c.InG1}, []*sw_bls12381.G2Affine{&c.InG2})
if err != nil {
return fmt.Errorf("pair: %w", err)
Expand Down
29 changes: 29 additions & 0 deletions std/algebra/emulated/sw_bls12381/g1.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package sw_bls12381

import (
"fmt"

bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/algebra/emulated/sw_emulated"
"github.com/consensys/gnark/std/math/emulated"
)
Expand All @@ -14,3 +17,29 @@ func NewG1Affine(v bls12381.G1Affine) G1Affine {
Y: emulated.ValueOf[emulated.BLS12381Fp](v.Y),
}
}

type G1 struct {
curveF *emulated.Field[emulated.BLS12381Fp]
w *emulated.Element[emulated.BLS12381Fp]
}

func NewG1(api frontend.API) (*G1, error) {
ba, err := emulated.NewField[emulated.BLS12381Fp](api)
if err != nil {
return nil, fmt.Errorf("new base api: %w", err)
}
w := emulated.ValueOf[emulated.BLS12381Fp]("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436")
return &G1{
curveF: ba,
w: &w,
}, nil
}

func (g1 *G1) phi(q *G1Affine) *G1Affine {
x := g1.curveF.Mul(&q.X, g1.w)

return &G1Affine{
X: *x,
Y: q.Y,
}
}
195 changes: 195 additions & 0 deletions std/algebra/emulated/sw_bls12381/g2.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,39 @@
package sw_bls12381

import (
"math/big"

bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/algebra/emulated/fields_bls12381"
"github.com/consensys/gnark/std/math/emulated"
)

type G2 struct {
*fields_bls12381.Ext2
u1, w *emulated.Element[emulated.BLS12381Fp]
v *fields_bls12381.E2
}

type G2Affine struct {
X, Y fields_bls12381.E2
}

func NewG2(api frontend.API) *G2 {
w := emulated.ValueOf[emulated.BLS12381Fp]("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436")
u1 := emulated.ValueOf[emulated.BLS12381Fp]("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939437")
v := fields_bls12381.E2{
A0: emulated.ValueOf[emulated.BLS12381Fp]("2973677408986561043442465346520108879172042883009249989176415018091420807192182638567116318576472649347015917690530"),
A1: emulated.ValueOf[emulated.BLS12381Fp]("1028732146235106349975324479215795277384839936929757896155643118032610843298655225875571310552543014690878354869257"),
}
return &G2{
Ext2: fields_bls12381.NewExt2(api),
w: &w,
u1: &u1,
v: &v,
}
}

func NewG2Affine(v bls12381.G2Affine) G2Affine {
return G2Affine{
X: fields_bls12381.E2{
Expand All @@ -22,3 +46,174 @@ func NewG2Affine(v bls12381.G2Affine) G2Affine {
},
}
}

func (g2 *G2) psi(q *G2Affine) *G2Affine {
x := g2.Ext2.MulByElement(&q.X, g2.u1)
y := g2.Ext2.Conjugate(&q.Y)
y = g2.Ext2.Mul(y, g2.v)

return &G2Affine{
X: fields_bls12381.E2{A0: x.A1, A1: x.A0},
Y: *y,
}
}

func (g2 *G2) scalarMulBySeed(q *G2Affine) *G2Affine {

z := g2.triple(q)
z = g2.double(z)
z = g2.doubleAndAdd(z, q)
z = g2.doubleN(z, 2)
z = g2.doubleAndAdd(z, q)
z = g2.doubleN(z, 8)
z = g2.doubleAndAdd(z, q)
z = g2.doubleN(z, 31)
z = g2.doubleAndAdd(z, q)
z = g2.doubleN(z, 16)

return g2.neg(z)
}

func (g2 G2) add(p, q *G2Affine) *G2Affine {
// compute λ = (q.y-p.y)/(q.x-p.x)
qypy := g2.Ext2.Sub(&q.Y, &p.Y)
qxpx := g2.Ext2.Sub(&q.X, &p.X)
λ := g2.Ext2.DivUnchecked(qypy, qxpx)

// xr = λ²-p.x-q.x
λλ := g2.Ext2.Square(λ)
qxpx = g2.Ext2.Add(&p.X, &q.X)
xr := g2.Ext2.Sub(λλ, qxpx)

// p.y = λ(p.x-r.x) - p.y
pxrx := g2.Ext2.Sub(&p.X, xr)
λpxrx := g2.Ext2.Mul(λ, pxrx)
yr := g2.Ext2.Sub(λpxrx, &p.Y)

return &G2Affine{
X: *xr,
Y: *yr,
}
}

func (g2 G2) neg(p *G2Affine) *G2Affine {
xr := &p.X
yr := g2.Ext2.Neg(&p.Y)
return &G2Affine{
X: *xr,
Y: *yr,
}
}

func (g2 G2) sub(p, q *G2Affine) *G2Affine {
qNeg := g2.neg(q)
return g2.add(p, qNeg)
}

func (g2 *G2) double(p *G2Affine) *G2Affine {
// compute λ = (3p.x²)/2*p.y
xx3a := g2.Square(&p.X)
xx3a = g2.MulByConstElement(xx3a, big.NewInt(3))
y2 := g2.Double(&p.Y)
λ := g2.DivUnchecked(xx3a, y2)

// xr = λ²-2p.x
x2 := g2.Double(&p.X)
λλ := g2.Square(λ)
xr := g2.Sub(λλ, x2)

// yr = λ(p-xr) - p.y
pxrx := g2.Sub(&p.X, xr)
λpxrx := g2.Mul(λ, pxrx)
yr := g2.Sub(λpxrx, &p.Y)

return &G2Affine{
X: *xr,
Y: *yr,
}
}

func (g2 *G2) doubleN(p *G2Affine, n int) *G2Affine {
pn := p
for s := 0; s < n; s++ {
pn = g2.double(pn)
}
return pn
}

func (g2 G2) triple(p *G2Affine) *G2Affine {

// compute λ1 = (3p.x²)/2p.y
xx := g2.Square(&p.X)
xx = g2.MulByConstElement(xx, big.NewInt(3))
y2 := g2.Double(&p.Y)
λ1 := g2.DivUnchecked(xx, y2)

// xr = λ1²-2p.x
x2 := g2.MulByConstElement(&p.X, big.NewInt(2))
λ1λ1 := g2.Square(λ1)
x2 = g2.Sub(λ1λ1, x2)

// ommit y2 computation, and
// compute λ2 = 2p.y/(x2 − p.x) − λ1.
x1x2 := g2.Sub(&p.X, x2)
λ2 := g2.DivUnchecked(y2, x1x2)
λ2 = g2.Sub(λ2, λ1)

// xr = λ²-p.x-x2
λ2λ2 := g2.Square(λ2)
qxrx := g2.Add(x2, &p.X)
xr := g2.Sub(λ2λ2, qxrx)

// yr = λ(p.x-xr) - p.y
pxrx := g2.Sub(&p.X, xr)
λ2pxrx := g2.Mul(λ2, pxrx)
yr := g2.Sub(λ2pxrx, &p.Y)

return &G2Affine{
X: *xr,
Y: *yr,
}
}

func (g2 G2) doubleAndAdd(p, q *G2Affine) *G2Affine {

// compute λ1 = (q.y-p.y)/(q.x-p.x)
yqyp := g2.Ext2.Sub(&q.Y, &p.Y)
xqxp := g2.Ext2.Sub(&q.X, &p.X)
λ1 := g2.Ext2.DivUnchecked(yqyp, xqxp)

// compute x2 = λ1²-p.x-q.x
λ1λ1 := g2.Ext2.Square(λ1)
xqxp = g2.Ext2.Add(&p.X, &q.X)
x2 := g2.Ext2.Sub(λ1λ1, xqxp)

// ommit y2 computation
// compute λ2 = -λ1-2*p.y/(x2-p.x)
ypyp := g2.Ext2.Add(&p.Y, &p.Y)
x2xp := g2.Ext2.Sub(x2, &p.X)
λ2 := g2.Ext2.DivUnchecked(ypyp, x2xp)
λ2 = g2.Ext2.Add(λ1, λ2)
λ2 = g2.Ext2.Neg(λ2)

// compute x3 =λ2²-p.x-x3
λ2λ2 := g2.Ext2.Square(λ2)
x3 := g2.Ext2.Sub(λ2λ2, &p.X)
x3 = g2.Ext2.Sub(x3, x2)

// compute y3 = λ2*(p.x - x3)-p.y
y3 := g2.Ext2.Sub(&p.X, x3)
y3 = g2.Ext2.Mul(λ2, y3)
y3 = g2.Ext2.Sub(y3, &p.Y)

return &G2Affine{
X: *x3,
Y: *y3,
}
}

// AssertIsEqual asserts that p and q are the same point.
func (g2 *G2) AssertIsEqual(p, q *G2Affine) {
g2.Ext2.AssertIsEqual(&p.X, &q.X)
g2.Ext2.AssertIsEqual(&p.Y, &q.Y)
}
120 changes: 120 additions & 0 deletions std/algebra/emulated/sw_bls12381/g2_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package sw_bls12381

import (
"math/big"
"testing"

"github.com/consensys/gnark-crypto/ecc"
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/test"
)

type addG2Circuit struct {
In1, In2 G2Affine
Res G2Affine
}

func (c *addG2Circuit) Define(api frontend.API) error {
g2 := NewG2(api)
res := g2.add(&c.In1, &c.In2)
g2.AssertIsEqual(res, &c.Res)
return nil
}

func TestAddG2TestSolve(t *testing.T) {
assert := test.NewAssert(t)
_, in1 := randomG1G2Affines(assert)
_, in2 := randomG1G2Affines(assert)
var res bls12381.G2Affine
res.Add(&in1, &in2)
witness := addG2Circuit{
In1: NewG2Affine(in1),
In2: NewG2Affine(in2),
Res: NewG2Affine(res),
}
err := test.IsSolved(&addG2Circuit{}, &witness, ecc.BN254.ScalarField())
assert.NoError(err)
}

type doubleG2Circuit struct {
In1 G2Affine
Res G2Affine
}

func (c *doubleG2Circuit) Define(api frontend.API) error {
g2 := NewG2(api)
res := g2.double(&c.In1)
g2.AssertIsEqual(res, &c.Res)
return nil
}

func TestDoubleG2TestSolve(t *testing.T) {
assert := test.NewAssert(t)
_, in1 := randomG1G2Affines(assert)
var res bls12381.G2Affine
var in1Jac, resJac bls12381.G2Jac
in1Jac.FromAffine(&in1)
resJac.Double(&in1Jac)
res.FromJacobian(&resJac)
witness := doubleG2Circuit{
In1: NewG2Affine(in1),
Res: NewG2Affine(res),
}
err := test.IsSolved(&doubleG2Circuit{}, &witness, ecc.BN254.ScalarField())
assert.NoError(err)
}

type doubleAndAddG2Circuit struct {
In1, In2 G2Affine
Res G2Affine
}

func (c *doubleAndAddG2Circuit) Define(api frontend.API) error {
g2 := NewG2(api)
res := g2.doubleAndAdd(&c.In1, &c.In2)
g2.AssertIsEqual(res, &c.Res)
return nil
}

func TestDoubleAndAddG2TestSolve(t *testing.T) {
assert := test.NewAssert(t)
_, in1 := randomG1G2Affines(assert)
_, in2 := randomG1G2Affines(assert)
var res bls12381.G2Affine
res.Double(&in1).
Add(&res, &in2)
witness := doubleAndAddG2Circuit{
In1: NewG2Affine(in1),
In2: NewG2Affine(in2),
Res: NewG2Affine(res),
}
err := test.IsSolved(&doubleAndAddG2Circuit{}, &witness, ecc.BN254.ScalarField())
assert.NoError(err)
}

type scalarMulG2BySeedCircuit struct {
In1 G2Affine
Res G2Affine
}

func (c *scalarMulG2BySeedCircuit) Define(api frontend.API) error {
g2 := NewG2(api)
res := g2.scalarMulBySeed(&c.In1)
g2.AssertIsEqual(res, &c.Res)
return nil
}

func TestScalarMulG2BySeedTestSolve(t *testing.T) {
assert := test.NewAssert(t)
_, in1 := randomG1G2Affines(assert)
var res bls12381.G2Affine
x0, _ := new(big.Int).SetString("15132376222941642752", 10)
res.ScalarMultiplication(&in1, x0).Neg(&res)
witness := scalarMulG2BySeedCircuit{
In1: NewG2Affine(in1),
Res: NewG2Affine(res),
}
err := test.IsSolved(&scalarMulG2BySeedCircuit{}, &witness, ecc.BN254.ScalarField())
assert.NoError(err)
}
Loading