@ -1,2 +1,12 @@ |
|||||
# gnark-ed25519 |
# gnark-ed25519 |
||||
ed25519 implementation in Gnark |
ed25519 implementation in Gnark |
||||
|
|
||||
|
To test: |
||||
|
``` |
||||
|
go test gnark-ed25519/edwards_curve |
||||
|
``` |
||||
|
|
||||
|
To build and run: |
||||
|
``` |
||||
|
go build gnark-ed25519 && ./gnark-ed25519 |
||||
|
``` |
@ -0,0 +1,34 @@ |
|||||
|
package edwards_curve |
||||
|
|
||||
|
import ( |
||||
|
"math/big" |
||||
|
) |
||||
|
|
||||
|
var ( |
||||
|
qEd25519, rEd25519 *big.Int |
||||
|
) |
||||
|
|
||||
|
func init() { |
||||
|
// https://neuromancer.sk/std/other/Ed25519
|
||||
|
qEd25519, _ = new(big.Int).SetString("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed", 16) |
||||
|
n, _ := new(big.Int).SetString("1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed", 16) |
||||
|
// TODO: is this ok?
|
||||
|
// h := big.NewInt(8)
|
||||
|
// rEd25519 = new(big.Int).Mul(n, h)
|
||||
|
rEd25519 = n |
||||
|
} |
||||
|
|
||||
|
type Ed25519 struct{} |
||||
|
|
||||
|
func (fp Ed25519) NbLimbs() uint { return 4 } |
||||
|
func (fp Ed25519) BitsPerLimb() uint { return 64 } |
||||
|
func (fp Ed25519) IsPrime() bool { return true } |
||||
|
func (fp Ed25519) Modulus() *big.Int { return qEd25519 } |
||||
|
|
||||
|
type Ed25519Scalars struct{} |
||||
|
|
||||
|
func (fp Ed25519Scalars) NbLimbs() uint { return 4 } |
||||
|
func (fp Ed25519Scalars) BitsPerLimb() uint { return 64 } |
||||
|
func (fp Ed25519Scalars) IsPrime() bool { return true } |
||||
|
func (fp Ed25519Scalars) Modulus() *big.Int { return rEd25519 } |
||||
|
|
@ -0,0 +1,173 @@ |
|||||
|
package edwards_curve |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"math/big" |
||||
|
|
||||
|
"github.com/consensys/gnark/frontend" |
||||
|
"github.com/consensys/gnark/std/math/emulated" |
||||
|
) |
||||
|
|
||||
|
func New[T, S emulated.FieldParams](api frontend.API) (*Curve[T, S], error) { |
||||
|
var t T |
||||
|
var s S |
||||
|
var gxb, gyb *big.Int |
||||
|
var A, D *big.Int |
||||
|
_, is_25519_t := any(t).(Ed25519) |
||||
|
_, is_25519_s := any(s).(Ed25519Scalars) |
||||
|
if is_25519_t && is_25519_s { |
||||
|
// https://neuromancer.sk/std/other/Ed25519
|
||||
|
gxb = newBigInt("216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A") |
||||
|
gyb = newBigInt("6666666666666666666666666666666666666666666666666666666666666658") |
||||
|
A = newBigInt("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec") |
||||
|
D = newBigInt("52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3") |
||||
|
} else { |
||||
|
return nil, fmt.Errorf("unknown curve") |
||||
|
} |
||||
|
return newCurve[T, S]( |
||||
|
api, |
||||
|
emulated.NewElement[T](A), |
||||
|
emulated.NewElement[T](D), |
||||
|
emulated.NewElement[T](gxb), |
||||
|
emulated.NewElement[T](gyb)) |
||||
|
} |
||||
|
|
||||
|
func newBigInt(s string) *big.Int { |
||||
|
result, success := new(big.Int).SetString(s, 16) |
||||
|
if !success { |
||||
|
panic("invalid bigint") |
||||
|
} |
||||
|
return result |
||||
|
} |
||||
|
|
||||
|
// TODO: could also have a type constraint for curve parameters (fields,
|
||||
|
// equation and generator). But for now we don't do arbitrary curves.
|
||||
|
|
||||
|
type Curve[T, S emulated.FieldParams] struct { |
||||
|
A emulated.Element[T] |
||||
|
D emulated.Element[T] |
||||
|
|
||||
|
// api is the native api, we construct it ourselves to be sure
|
||||
|
api frontend.API |
||||
|
// baseApi is the api for point operations
|
||||
|
baseApi frontend.API |
||||
|
// scalarApi is the api for scalar operations
|
||||
|
scalarApi frontend.API |
||||
|
|
||||
|
g AffinePoint[T] |
||||
|
} |
||||
|
|
||||
|
func (c *Curve[T, S]) Generator() AffinePoint[T] { |
||||
|
return c.g |
||||
|
} |
||||
|
|
||||
|
func newCurve[T, S emulated.FieldParams](api frontend.API, A, D, Gx, Gy emulated.Element[T]) (*Curve[T, S], error) { |
||||
|
ba, err := emulated.NewField[T](api) |
||||
|
if err != nil { |
||||
|
return nil, fmt.Errorf("new base api: %w", err) |
||||
|
} |
||||
|
sa, err := emulated.NewField[S](api) |
||||
|
if err != nil { |
||||
|
return nil, fmt.Errorf("new scalar api: %w", err) |
||||
|
} |
||||
|
return &Curve[T, S]{ |
||||
|
A: A, |
||||
|
D: D, |
||||
|
api: api, |
||||
|
baseApi: ba, |
||||
|
scalarApi: sa, |
||||
|
g: AffinePoint[T]{ |
||||
|
X: Gx, |
||||
|
Y: Gy, |
||||
|
}, |
||||
|
}, nil |
||||
|
} |
||||
|
|
||||
|
type AffinePoint[T emulated.FieldParams] struct { |
||||
|
X, Y emulated.Element[T] |
||||
|
} |
||||
|
|
||||
|
func (c *Curve[T, S]) Neg(p AffinePoint[T]) AffinePoint[T] { |
||||
|
return AffinePoint[T]{ |
||||
|
X: p.X, |
||||
|
Y: c.baseApi.Neg(p.Y).(emulated.Element[T]), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Curve[T, S]) AssertIsEqual(p, q AffinePoint[T]) { |
||||
|
c.baseApi.AssertIsEqual(p.X, q.X) |
||||
|
c.baseApi.AssertIsEqual(p.Y, q.Y) |
||||
|
} |
||||
|
|
||||
|
func (c *Curve[T, S]) AssertIsOnCurve(p AffinePoint[T]) { |
||||
|
xx := c.baseApi.Mul(p.X, p.X) |
||||
|
yy := c.baseApi.Mul(p.Y, p.Y) |
||||
|
fmt.Println(xx) |
||||
|
fmt.Println(c.A) |
||||
|
axx := c.baseApi.Mul(xx, c.A) |
||||
|
lhs := c.baseApi.Add(axx, yy) |
||||
|
|
||||
|
dxx := c.baseApi.Mul(xx, c.D) |
||||
|
dxxyy := c.baseApi.Mul(dxx, yy) |
||||
|
rhs := c.baseApi.Add(dxxyy, 1) |
||||
|
|
||||
|
c.baseApi.AssertIsEqual(lhs, rhs) |
||||
|
} |
||||
|
|
||||
|
// func (c *Curve[T, S]) Add(q, r AffinePoint[T]) AffinePoint[T] {
|
||||
|
// // compute lambda = (p1.y-p.y)/(p1.x-p.x)
|
||||
|
// lambda := c.baseApi.DivUnchecked(c.baseApi.Sub(r.Y, q.Y), c.baseApi.Sub(r.X, q.X))
|
||||
|
|
||||
|
// // xr = lambda**2-p.x-p1.x
|
||||
|
// xr := c.baseApi.Sub(c.baseApi.Mul(lambda, lambda), c.baseApi.Add(q.X, r.X))
|
||||
|
|
||||
|
// // p.y = lambda(p.x-xr) - p.y
|
||||
|
// py := c.baseApi.Sub(c.baseApi.Mul(lambda, c.baseApi.Sub(q.X, xr)), q.Y)
|
||||
|
|
||||
|
// return AffinePoint[T]{
|
||||
|
// X: xr.(emulated.Element[T]),
|
||||
|
// Y: py.(emulated.Element[T]),
|
||||
|
// }
|
||||
|
// }
|
||||
|
|
||||
|
// func (c *Curve[T, S]) Double(p AffinePoint[T]) AffinePoint[T] {
|
||||
|
|
||||
|
// // compute lambda = (3*p1.x**2+a)/2*p1.y, here we assume a=0 (j invariant 0 curve)
|
||||
|
// lambda := c.baseApi.DivUnchecked(c.baseApi.Mul(p.X, p.X, 3), c.baseApi.Mul(p.Y, 2))
|
||||
|
|
||||
|
// // xr = lambda**2-p1.x-p1.x
|
||||
|
// xr := c.baseApi.Sub(c.baseApi.Mul(lambda, lambda), c.baseApi.Mul(p.X, 2))
|
||||
|
|
||||
|
// // p.y = lambda(p.x-xr) - p.y
|
||||
|
// py := c.baseApi.Sub(c.baseApi.Mul(lambda, c.baseApi.Sub(p.X, xr)), p.Y)
|
||||
|
|
||||
|
// return AffinePoint[T]{
|
||||
|
// X: xr.(emulated.Element[T]),
|
||||
|
// Y: py.(emulated.Element[T]),
|
||||
|
// }
|
||||
|
// }
|
||||
|
|
||||
|
func (c *Curve[T, S]) Select(b frontend.Variable, p, q AffinePoint[T]) AffinePoint[T] { |
||||
|
x := c.baseApi.Select(b, p.X, q.X) |
||||
|
y := c.baseApi.Select(b, p.Y, q.Y) |
||||
|
return AffinePoint[T]{ |
||||
|
X: x.(emulated.Element[T]), |
||||
|
Y: y.(emulated.Element[T]), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// func (c *Curve[T, S]) ScalarMul(p AffinePoint[T], s emulated.Element[S]) AffinePoint[T] {
|
||||
|
// res := p
|
||||
|
// acc := c.Double(p)
|
||||
|
|
||||
|
// sBits := c.scalarApi.ToBinary(s)
|
||||
|
// for i := 1; i < len(sBits); i++ {
|
||||
|
// tmp := c.Add(res, acc)
|
||||
|
// res = c.Select(sBits[i], tmp, res)
|
||||
|
// acc = c.Double(acc)
|
||||
|
// }
|
||||
|
|
||||
|
// tmp := c.Add(res, c.Neg(p))
|
||||
|
// res = c.Select(sBits[0], res, tmp)
|
||||
|
// return res
|
||||
|
// }
|
@ -0,0 +1,183 @@ |
|||||
|
package edwards_curve |
||||
|
|
||||
|
import ( |
||||
|
"testing" |
||||
|
|
||||
|
"github.com/consensys/gnark-crypto/ecc" |
||||
|
"github.com/consensys/gnark/frontend" |
||||
|
"github.com/consensys/gnark/std/math/emulated" |
||||
|
"github.com/consensys/gnark/test" |
||||
|
// "github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||
|
) |
||||
|
|
||||
|
type OnCurveTest[T, S emulated.FieldParams] struct { |
||||
|
P AffinePoint[T] |
||||
|
} |
||||
|
|
||||
|
func (c *OnCurveTest[T, S]) Define(api frontend.API) error { |
||||
|
cr, err := New[T, S](api) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
cr.AssertIsOnCurve(c.P) |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func TestGenerator(t *testing.T) { |
||||
|
assert := test.NewAssert(t) |
||||
|
circuit := OnCurveTest[Ed25519, Ed25519Scalars]{} |
||||
|
witness := OnCurveTest[Ed25519, Ed25519Scalars]{ |
||||
|
P: AffinePoint[Ed25519]{ |
||||
|
X: emulated.NewElement[Ed25519](newBigInt("216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A")), |
||||
|
Y: emulated.NewElement[Ed25519](newBigInt("6666666666666666666666666666666666666666666666666666666666666658")), |
||||
|
}, |
||||
|
} |
||||
|
err := test.IsSolved(&circuit, &witness, testCurve.ScalarField()) |
||||
|
assert.NoError(err) |
||||
|
} |
||||
|
|
||||
|
var testCurve = ecc.BN254 |
||||
|
|
||||
|
// type NegTest[T, S emulated.FieldParams] struct {
|
||||
|
// P, Q AffinePoint[T]
|
||||
|
// }
|
||||
|
|
||||
|
// func (c *NegTest[T, S]) Define(api frontend.API) error {
|
||||
|
// cr, err := New[T, S](api)
|
||||
|
// if err != nil {
|
||||
|
// return err
|
||||
|
// }
|
||||
|
// res := cr.Neg(c.P)
|
||||
|
// cr.AssertIsEqual(res, c.Q)
|
||||
|
// return nil
|
||||
|
// }
|
||||
|
|
||||
|
// func TestNeg(t *testing.T) {
|
||||
|
// assert := test.NewAssert(t)
|
||||
|
// secpCurve := secp256k1.S256()
|
||||
|
// yn := new(big.Int).Sub(secpCurve.P, secpCurve.Gy)
|
||||
|
// circuit := NegTest[emulated.Secp256k1, emulated.Secp256k1Scalars]{}
|
||||
|
// witness := NegTest[emulated.Secp256k1, emulated.Secp256k1Scalars]{
|
||||
|
// P: AffinePoint[emulated.Secp256k1]{
|
||||
|
// X: emulated.NewElement[emulated.Secp256k1](secpCurve.Gx),
|
||||
|
// Y: emulated.NewElement[emulated.Secp256k1](secpCurve.Gy),
|
||||
|
// },
|
||||
|
// Q: AffinePoint[emulated.Secp256k1]{
|
||||
|
// X: emulated.NewElement[emulated.Secp256k1](secpCurve.Gx),
|
||||
|
// Y: emulated.NewElement[emulated.Secp256k1](yn),
|
||||
|
// },
|
||||
|
// }
|
||||
|
// err := test.IsSolved(&circuit, &witness, testCurve.ScalarField())
|
||||
|
// assert.NoError(err)
|
||||
|
// }
|
||||
|
|
||||
|
// type AddTest[T, S emulated.FieldParams] struct {
|
||||
|
// P, Q, R AffinePoint[T]
|
||||
|
// }
|
||||
|
|
||||
|
// func (c *AddTest[T, S]) Define(api frontend.API) error {
|
||||
|
// cr, err := New[T, S](api)
|
||||
|
// if err != nil {
|
||||
|
// return err
|
||||
|
// }
|
||||
|
// res := cr.Add(c.P, c.Q)
|
||||
|
// cr.AssertIsEqual(res, c.R)
|
||||
|
// return nil
|
||||
|
// }
|
||||
|
|
||||
|
// func TestAdd(t *testing.T) {
|
||||
|
// assert := test.NewAssert(t)
|
||||
|
// secpCurve := secp256k1.S256()
|
||||
|
// xd, yd := secpCurve.Double(secpCurve.Gx, secpCurve.Gy)
|
||||
|
// xa, ya := secpCurve.Add(xd, yd, secpCurve.Gx, secpCurve.Gy)
|
||||
|
// circuit := AddTest[emulated.Secp256k1, emulated.Secp256k1Scalars]{}
|
||||
|
// witness := AddTest[emulated.Secp256k1, emulated.Secp256k1Scalars]{
|
||||
|
// P: AffinePoint[emulated.Secp256k1]{
|
||||
|
// X: emulated.NewElement[emulated.Secp256k1](secpCurve.Gx),
|
||||
|
// Y: emulated.NewElement[emulated.Secp256k1](secpCurve.Gy),
|
||||
|
// },
|
||||
|
// Q: AffinePoint[emulated.Secp256k1]{
|
||||
|
// X: emulated.NewElement[emulated.Secp256k1](xd),
|
||||
|
// Y: emulated.NewElement[emulated.Secp256k1](yd),
|
||||
|
// },
|
||||
|
// R: AffinePoint[emulated.Secp256k1]{
|
||||
|
// X: emulated.NewElement[emulated.Secp256k1](xa),
|
||||
|
// Y: emulated.NewElement[emulated.Secp256k1](ya),
|
||||
|
// },
|
||||
|
// }
|
||||
|
// err := test.IsSolved(&circuit, &witness, testCurve.ScalarField())
|
||||
|
// assert.NoError(err)
|
||||
|
// }
|
||||
|
|
||||
|
// type DoubleTest[T, S emulated.FieldParams] struct {
|
||||
|
// P, Q AffinePoint[T]
|
||||
|
// }
|
||||
|
|
||||
|
// func (c *DoubleTest[T, S]) Define(api frontend.API) error {
|
||||
|
// cr, err := New[T, S](api)
|
||||
|
// if err != nil {
|
||||
|
// return err
|
||||
|
// }
|
||||
|
// res := cr.Double(c.P)
|
||||
|
// cr.AssertIsEqual(res, c.Q)
|
||||
|
// return nil
|
||||
|
// }
|
||||
|
|
||||
|
// func TestDouble(t *testing.T) {
|
||||
|
// assert := test.NewAssert(t)
|
||||
|
// secpCurve := secp256k1.S256()
|
||||
|
// xd, yd := secpCurve.Double(secpCurve.Gx, secpCurve.Gy)
|
||||
|
// circuit := DoubleTest[emulated.Secp256k1, emulated.Secp256k1Scalars]{}
|
||||
|
// witness := DoubleTest[emulated.Secp256k1, emulated.Secp256k1Scalars]{
|
||||
|
// P: AffinePoint[emulated.Secp256k1]{
|
||||
|
// X: emulated.NewElement[emulated.Secp256k1](secpCurve.Gx),
|
||||
|
// Y: emulated.NewElement[emulated.Secp256k1](secpCurve.Gy),
|
||||
|
// },
|
||||
|
// Q: AffinePoint[emulated.Secp256k1]{
|
||||
|
// X: emulated.NewElement[emulated.Secp256k1](xd),
|
||||
|
// Y: emulated.NewElement[emulated.Secp256k1](yd),
|
||||
|
// },
|
||||
|
// }
|
||||
|
// err := test.IsSolved(&circuit, &witness, testCurve.ScalarField())
|
||||
|
// assert.NoError(err)
|
||||
|
// }
|
||||
|
|
||||
|
// type ScalarMulTest[T, S emulated.FieldParams] struct {
|
||||
|
// P, Q AffinePoint[T]
|
||||
|
// S emulated.Element[S]
|
||||
|
// }
|
||||
|
|
||||
|
// func (c *ScalarMulTest[T, S]) Define(api frontend.API) error {
|
||||
|
// cr, err := New[T, S](api)
|
||||
|
// if err != nil {
|
||||
|
// return err
|
||||
|
// }
|
||||
|
// res := cr.ScalarMul(c.P, c.S)
|
||||
|
// cr.AssertIsEqual(res, c.Q)
|
||||
|
// return nil
|
||||
|
// }
|
||||
|
|
||||
|
// func TestScalarMul(t *testing.T) {
|
||||
|
// assert := test.NewAssert(t)
|
||||
|
// secpCurve := secp256k1.S256()
|
||||
|
// s, ok := new(big.Int).SetString("44693544921776318736021182399461740191514036429448770306966433218654680512345", 10)
|
||||
|
// assert.True(ok)
|
||||
|
// sx, sy := secpCurve.ScalarMult(secpCurve.Gx, secpCurve.Gy, s.Bytes())
|
||||
|
|
||||
|
// circuit := ScalarMulTest[emulated.Secp256k1, emulated.Secp256k1Scalars]{}
|
||||
|
// witness := ScalarMulTest[emulated.Secp256k1, emulated.Secp256k1Scalars]{
|
||||
|
// S: emulated.NewElement[emulated.Secp256k1Scalars](s),
|
||||
|
// P: AffinePoint[emulated.Secp256k1]{
|
||||
|
// X: emulated.NewElement[emulated.Secp256k1](secpCurve.Gx),
|
||||
|
// Y: emulated.NewElement[emulated.Secp256k1](secpCurve.Gy),
|
||||
|
// },
|
||||
|
// Q: AffinePoint[emulated.Secp256k1]{
|
||||
|
// X: emulated.NewElement[emulated.Secp256k1](sx),
|
||||
|
// Y: emulated.NewElement[emulated.Secp256k1](sy),
|
||||
|
// },
|
||||
|
// }
|
||||
|
// err := test.IsSolved(&circuit, &witness, testCurve.ScalarField())
|
||||
|
// assert.NoError(err)
|
||||
|
// // _, err = frontend.Compile(testCurve.ScalarField(), r1cs.NewBuilder, &circuit)
|
||||
|
// // assert.NoError(err)
|
||||
|
// }
|
@ -1,20 +1,24 @@ |
|||||
module cubic |
|
||||
|
module gnark-ed25519 |
||||
|
|
||||
go 1.19 |
go 1.19 |
||||
|
|
||||
require ( |
require ( |
||||
github.com/blang/semver/v4 v4.0.0 // indirect |
github.com/blang/semver/v4 v4.0.0 // indirect |
||||
github.com/consensys/bavard v0.1.13 // indirect |
github.com/consensys/bavard v0.1.13 // indirect |
||||
github.com/consensys/gnark v0.7.2-0.20220915193524-b33f88c79af6 // indirect |
|
||||
|
github.com/consensys/gnark v0.7.2-0.20220921094618-a121a3074ee8 // indirect |
||||
github.com/consensys/gnark-crypto v0.8.1-0.20220819163559-143c75519b0e // indirect |
github.com/consensys/gnark-crypto v0.8.1-0.20220819163559-143c75519b0e // indirect |
||||
|
github.com/davecgh/go-spew v1.1.1 // indirect |
||||
github.com/fxamacker/cbor/v2 v2.2.0 // indirect |
github.com/fxamacker/cbor/v2 v2.2.0 // indirect |
||||
github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1 // indirect |
github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1 // indirect |
||||
github.com/mattn/go-colorable v0.1.13 // indirect |
github.com/mattn/go-colorable v0.1.13 // indirect |
||||
github.com/mattn/go-isatty v0.0.16 // indirect |
github.com/mattn/go-isatty v0.0.16 // indirect |
||||
github.com/mmcloughlin/addchain v0.4.0 // indirect |
github.com/mmcloughlin/addchain v0.4.0 // indirect |
||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect |
||||
github.com/rs/zerolog v1.28.0 // indirect |
github.com/rs/zerolog v1.28.0 // indirect |
||||
|
github.com/stretchr/testify v1.8.0 // indirect |
||||
github.com/x448/float16 v0.8.4 // indirect |
github.com/x448/float16 v0.8.4 // indirect |
||||
golang.org/x/exp v0.0.0-20220713135740-79cabaa25d75 // indirect |
golang.org/x/exp v0.0.0-20220713135740-79cabaa25d75 // indirect |
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect |
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect |
||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect |
||||
rsc.io/tmplfunc v0.0.3 // indirect |
rsc.io/tmplfunc v0.0.3 // indirect |
||||
) |
) |