diff --git a/edwards_curve/edparams.go b/edwards_curve/edparams.go index b1de598..107dba3 100644 --- a/edwards_curve/edparams.go +++ b/edwards_curve/edparams.go @@ -10,8 +10,8 @@ var ( func init() { // https://neuromancer.sk/std/other/Ed25519 - qEd25519, _ = new(big.Int).SetString("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed", 16) - n, _ := new(big.Int).SetString("1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed", 16) + qEd25519 = newBigInt("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed") + n := newBigInt("1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed") // TODO: is this ok? // h := big.NewInt(8) // rEd25519 = new(big.Int).Mul(n, h) @@ -24,6 +24,10 @@ 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 } +func (fp Ed25519) Generator() (*big.Int, *big.Int) { + return newBigInt("216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A"), + newBigInt("6666666666666666666666666666666666666666666666666666666666666658") +} type Ed25519Scalars struct{} diff --git a/edwards_curve/edpoint.go b/edwards_curve/edpoint.go index 7ee6543..17a7c1f 100644 --- a/edwards_curve/edpoint.go +++ b/edwards_curve/edpoint.go @@ -44,8 +44,8 @@ func newBigInt(s string) *big.Int { // 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] + a emulated.Element[T] + d emulated.Element[T] // api is the native api, we construct it ourselves to be sure api frontend.API @@ -61,7 +61,7 @@ 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) { +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) @@ -71,8 +71,8 @@ func newCurve[T, S emulated.FieldParams](api frontend.API, A, D, Gx, Gy emulated return nil, fmt.Errorf("new scalar api: %w", err) } return &Curve[T, S]{ - A: A, - D: D, + a: a, + d: d, api: api, baseApi: ba, scalarApi: sa, @@ -102,50 +102,69 @@ func (c *Curve[T, S]) AssertIsEqual(p, q AffinePoint[T]) { 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) + axx := c.baseApi.Mul(xx, c.a) lhs := c.baseApi.Add(axx, yy) - dxx := c.baseApi.Mul(xx, c.D) + 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)) +func (c *Curve[T, S]) Add(q, r AffinePoint[T]) AffinePoint[T] { + // u = (x1 + y1) * (x2 + y2) + u1 := c.baseApi.Mul(q.X, c.a) + u1 = c.baseApi.Sub(q.Y, u1) + u2 := c.baseApi.Add(r.X, r.Y) + u := c.baseApi.Mul(u1, u2) -// // xr = lambda**2-p.x-p1.x -// xr := c.baseApi.Sub(c.baseApi.Mul(lambda, lambda), c.baseApi.Add(q.X, r.X)) + // v0 = x1 * y2 + v0 := c.baseApi.Mul(r.Y, q.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) + // v1 = x2 * y1 + v1 := c.baseApi.Mul(r.X, q.Y) -// return AffinePoint[T]{ -// X: xr.(emulated.Element[T]), -// Y: py.(emulated.Element[T]), -// } -// } + // v2 = d * v0 * v1 + v2 := c.baseApi.Mul(c.d, v0, v1) -// func (c *Curve[T, S]) Double(p AffinePoint[T]) AffinePoint[T] { + var px, py frontend.Variable -// // 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)) + // x = (v0 + v1) / (1 + v2) + px = c.baseApi.Add(v0, v1) + px = c.baseApi.DivUnchecked(px, c.baseApi.Add(1, v2)) -// // xr = lambda**2-p1.x-p1.x -// xr := c.baseApi.Sub(c.baseApi.Mul(lambda, lambda), c.baseApi.Mul(p.X, 2)) + // y = (u + a * v0 - v1) / (1 - v2) + py = c.baseApi.Mul(c.a, v0) + py = c.baseApi.Sub(py, v1) + py = c.baseApi.Add(py, u) + py = c.baseApi.DivUnchecked(py, c.baseApi.Sub(1, v2)) -// // 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: px.(emulated.Element[T]), + Y: py.(emulated.Element[T]), + } +} + +func (c *Curve[T, S]) Double(p AffinePoint[T]) AffinePoint[T] { + u := c.baseApi.Mul(p.X, p.Y) + v := c.baseApi.Mul(p.X, p.X) + w := c.baseApi.Mul(p.Y, p.Y) -// return AffinePoint[T]{ -// X: xr.(emulated.Element[T]), -// Y: py.(emulated.Element[T]), -// } -// } + n1 := c.baseApi.Mul(2, u) + av := c.baseApi.Mul(v, c.a) + n2 := c.baseApi.Sub(w, av) + d1 := c.baseApi.Add(w, av) + d2 := c.baseApi.Sub(2, d1) + + px := c.baseApi.DivUnchecked(n1, d1) + py := c.baseApi.DivUnchecked(n2, d2) + + return AffinePoint[T]{ + X: px.(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) @@ -156,18 +175,18 @@ func (c *Curve[T, S]) Select(b frontend.Variable, p, q AffinePoint[T]) AffinePoi } } -// 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 -// } +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 +} diff --git a/edwards_curve/edpoint_test.go b/edwards_curve/edpoint_test.go index 760216a..c53f476 100644 --- a/edwards_curve/edpoint_test.go +++ b/edwards_curve/edpoint_test.go @@ -1,6 +1,7 @@ package edwards_curve import ( + "math/big" "testing" "github.com/consensys/gnark-crypto/ecc" @@ -23,7 +24,7 @@ func (c *OnCurveTest[T, S]) Define(api frontend.API) error { return nil } -func TestGenerator(t *testing.T) { +func TestGeneratorIsOnCurve(t *testing.T) { assert := test.NewAssert(t) circuit := OnCurveTest[Ed25519, Ed25519Scalars]{} witness := OnCurveTest[Ed25519, Ed25519Scalars]{ @@ -36,6 +37,52 @@ func TestGenerator(t *testing.T) { assert.NoError(err) } +// s1*x1 + s2*x2 = y +type MulAddTest[T, S emulated.FieldParams] struct { + X1, X2 AffinePoint[T] + S1, S2 emulated.Element[S] + Y AffinePoint[T] +} + +func (c *MulAddTest[T, S]) Define(api frontend.API) error { + cr, err := New[T, S](api) + if err != nil { + return err + } + X1S1 := cr.ScalarMul(c.X1, c.S1) + X2S2 := cr.ScalarMul(c.X2, c.S2) + sum := cr.Add(X1S1, X2S2) + cr.AssertIsEqual(sum, c.Y) + cr.scalarApi.AssertIsEqual( + cr.scalarApi.Add(c.S1, c.S2), + emulated.NewElement[S](big.NewInt(1)), + ) + return nil +} + +func TestGeneratorGeneratesCurveOfCorrectOrder(t *testing.T) { + assert := test.NewAssert(t) + Gx, Gy := Ed25519{}.Generator() + G := AffinePoint[Ed25519]{ + X: emulated.NewElement[Ed25519](Gx), + Y: emulated.NewElement[Ed25519](Gy), + } + for i := 2; i <= 3; i++ { + S1 := new(big.Int).Sub(rEd25519, big.NewInt(int64(i - 1))) + S2 := big.NewInt(int64(i)) + circuit := MulAddTest[Ed25519, Ed25519Scalars]{} + witness := MulAddTest[Ed25519, Ed25519Scalars]{ + X1: G, + X2: G, + S1: emulated.NewElement[Ed25519Scalars](S1), + S2: emulated.NewElement[Ed25519Scalars](S2), + Y: G, + } + err := test.IsSolved(&circuit, &witness, testCurve.ScalarField()) + assert.NoError(err) + } +} + var testCurve = ecc.BN254 // type NegTest[T, S emulated.FieldParams] struct {