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)
|
|
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]) AssertIsZero(p AffinePoint[T]) {
|
|
c.baseApi.AssertIsEqual(p.X, 0)
|
|
c.baseApi.AssertIsEqual(p.Y, 1)
|
|
}
|
|
|
|
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)
|
|
|
|
// v0 = x1 * y2
|
|
v0 := c.baseApi.Mul(r.Y, q.X)
|
|
|
|
// v1 = x2 * y1
|
|
v1 := c.baseApi.Mul(r.X, q.Y)
|
|
|
|
// v2 = d * v0 * v1
|
|
v2 := c.baseApi.Mul(c.d, v0, v1)
|
|
|
|
var px, py frontend.Variable
|
|
|
|
// x = (v0 + v1) / (1 + v2)
|
|
px = c.baseApi.Add(v0, v1)
|
|
px = c.baseApi.DivUnchecked(px, c.baseApi.Add(1, v2))
|
|
|
|
// 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))
|
|
|
|
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)
|
|
|
|
n1 := c.baseApi.Add(u, 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)
|
|
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] {
|
|
return c.ScalarMulBinary(p, c.scalarApi.ToBinary(s))
|
|
}
|
|
|
|
func (c *Curve[T, S]) ScalarMulBinary(p AffinePoint[T], sBits []frontend.Variable) AffinePoint[T] {
|
|
res := AffinePoint[T]{
|
|
X: emulated.NewElement[T](0),
|
|
Y: emulated.NewElement[T](1),
|
|
}
|
|
acc := p
|
|
|
|
for i := 0; i < len(sBits); i++ {
|
|
tmp := c.Add(res, acc)
|
|
res = c.Select(sBits[i], tmp, res)
|
|
acc = c.Double(acc)
|
|
}
|
|
|
|
return res
|
|
}
|