diff --git a/README.md b/README.md index 8f2fa0a4..bb946f8e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ # CIRCL -[![GitHub release](https://img.shields.io/github/release/cloudflare/circl.svg)](https://GitHub.com/cloudflare/circl/releases/) [![CIRCL](https://github.com/cloudflare/circl/workflows/CIRCL/badge.svg)](https://github.com/cloudflare/circl/actions) [![GoDoc](https://godoc.org/github.com/cloudflare/circl?status.svg)](https://pkg.go.dev/github.com/cloudflare/circl?tab=overview) [![Go Report Card](https://goreportcard.com/badge/github.com/cloudflare/circl)](https://goreportcard.com/report/github.com/cloudflare/circl) @@ -38,7 +37,7 @@ go get -u github.com/cloudflare/circl #### Groups based on Elliptic Curves - P-256, P-384, P-521, [FIPS 186-4](https://doi.org/10.6028/NIST.FIPS.186-4) - - [Ristretto](https://datatracker.ietf.org/doc/draft-irtf-cfrg-ristretto255-decaf448/01/) + - [Ristretto, Decaf](https://datatracker.ietf.org/doc/draft-irtf-cfrg-ristretto255-decaf448/01/) - [Hash to Curve](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/) #### High-Level Protocols diff --git a/ecc/goldilocks/constants.go b/ecc/goldilocks/constants.go deleted file mode 100644 index b6b236e5..00000000 --- a/ecc/goldilocks/constants.go +++ /dev/null @@ -1,71 +0,0 @@ -package goldilocks - -import fp "github.com/cloudflare/circl/math/fp448" - -var ( - // genX is the x-coordinate of the generator of Goldilocks curve. - genX = fp.Elt{ - 0x5e, 0xc0, 0x0c, 0xc7, 0x2b, 0xa8, 0x26, 0x26, - 0x8e, 0x93, 0x00, 0x8b, 0xe1, 0x80, 0x3b, 0x43, - 0x11, 0x65, 0xb6, 0x2a, 0xf7, 0x1a, 0xae, 0x12, - 0x64, 0xa4, 0xd3, 0xa3, 0x24, 0xe3, 0x6d, 0xea, - 0x67, 0x17, 0x0f, 0x47, 0x70, 0x65, 0x14, 0x9e, - 0xda, 0x36, 0xbf, 0x22, 0xa6, 0x15, 0x1d, 0x22, - 0xed, 0x0d, 0xed, 0x6b, 0xc6, 0x70, 0x19, 0x4f, - } - // genY is the y-coordinate of the generator of Goldilocks curve. - genY = fp.Elt{ - 0x14, 0xfa, 0x30, 0xf2, 0x5b, 0x79, 0x08, 0x98, - 0xad, 0xc8, 0xd7, 0x4e, 0x2c, 0x13, 0xbd, 0xfd, - 0xc4, 0x39, 0x7c, 0xe6, 0x1c, 0xff, 0xd3, 0x3a, - 0xd7, 0xc2, 0xa0, 0x05, 0x1e, 0x9c, 0x78, 0x87, - 0x40, 0x98, 0xa3, 0x6c, 0x73, 0x73, 0xea, 0x4b, - 0x62, 0xc7, 0xc9, 0x56, 0x37, 0x20, 0x76, 0x88, - 0x24, 0xbc, 0xb6, 0x6e, 0x71, 0x46, 0x3f, 0x69, - } - // paramD is -39081 in Fp. - paramD = fp.Elt{ - 0x56, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - } - // order is 2^446-0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d, - // which is the number of points in the prime subgroup. - order = Scalar{ - 0xf3, 0x44, 0x58, 0xab, 0x92, 0xc2, 0x78, 0x23, - 0x55, 0x8f, 0xc5, 0x8d, 0x72, 0xc2, 0x6c, 0x21, - 0x90, 0x36, 0xd6, 0xae, 0x49, 0xdb, 0x4e, 0xc4, - 0xe9, 0x23, 0xca, 0x7c, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, - } - // residue448 is 2^448 mod order. - residue448 = [4]uint64{ - 0x721cf5b5529eec34, 0x7a4cf635c8e9c2ab, 0xeec492d944a725bf, 0x20cd77058, - } - // invFour is 1/4 mod order. - invFour = Scalar{ - 0x3d, 0x11, 0xd6, 0xaa, 0xa4, 0x30, 0xde, 0x48, - 0xd5, 0x63, 0x71, 0xa3, 0x9c, 0x30, 0x5b, 0x08, - 0xa4, 0x8d, 0xb5, 0x6b, 0xd2, 0xb6, 0x13, 0x71, - 0xfa, 0x88, 0x32, 0xdf, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, - } - // paramDTwist is -39082 in Fp. The D parameter of the twist curve. - paramDTwist = fp.Elt{ - 0x55, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - } -) diff --git a/ecc/goldilocks/curve.go b/ecc/goldilocks/curve.go deleted file mode 100644 index 5a939100..00000000 --- a/ecc/goldilocks/curve.go +++ /dev/null @@ -1,80 +0,0 @@ -// Package goldilocks provides elliptic curve operations over the goldilocks curve. -package goldilocks - -import fp "github.com/cloudflare/circl/math/fp448" - -// Curve is the Goldilocks curve x^2+y^2=z^2-39081x^2y^2. -type Curve struct{} - -// Identity returns the identity point. -func (Curve) Identity() *Point { - return &Point{ - y: fp.One(), - z: fp.One(), - } -} - -// IsOnCurve returns true if the point lies on the curve. -func (Curve) IsOnCurve(P *Point) bool { - x2, y2, t, t2, z2 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} - rhs, lhs := &fp.Elt{}, &fp.Elt{} - fp.Mul(t, &P.ta, &P.tb) // t = ta*tb - fp.Sqr(x2, &P.x) // x^2 - fp.Sqr(y2, &P.y) // y^2 - fp.Sqr(z2, &P.z) // z^2 - fp.Sqr(t2, t) // t^2 - fp.Add(lhs, x2, y2) // x^2 + y^2 - fp.Mul(rhs, t2, ¶mD) // dt^2 - fp.Add(rhs, rhs, z2) // z^2 + dt^2 - fp.Sub(lhs, lhs, rhs) // x^2 + y^2 - (z^2 + dt^2) - eq0 := fp.IsZero(lhs) - - fp.Mul(lhs, &P.x, &P.y) // xy - fp.Mul(rhs, t, &P.z) // tz - fp.Sub(lhs, lhs, rhs) // xy - tz - eq1 := fp.IsZero(lhs) - return eq0 && eq1 -} - -// Generator returns the generator point. -func (Curve) Generator() *Point { - return &Point{ - x: genX, - y: genY, - z: fp.One(), - ta: genX, - tb: genY, - } -} - -// Order returns the number of points in the prime subgroup. -func (Curve) Order() Scalar { return order } - -// Double returns 2P. -func (Curve) Double(P *Point) *Point { R := *P; R.Double(); return &R } - -// Add returns P+Q. -func (Curve) Add(P, Q *Point) *Point { R := *P; R.Add(Q); return &R } - -// ScalarMult returns kP. This function runs in constant time. -func (e Curve) ScalarMult(k *Scalar, P *Point) *Point { - k4 := &Scalar{} - k4.divBy4(k) - return e.pull(twistCurve{}.ScalarMult(k4, e.push(P))) -} - -// ScalarBaseMult returns kG where G is the generator point. This function runs in constant time. -func (e Curve) ScalarBaseMult(k *Scalar) *Point { - k4 := &Scalar{} - k4.divBy4(k) - return e.pull(twistCurve{}.ScalarBaseMult(k4)) -} - -// CombinedMult returns mG+nP, where G is the generator point. This function is non-constant time. -func (e Curve) CombinedMult(m, n *Scalar, P *Point) *Point { - m4 := &Scalar{} - n4 := &Scalar{} - m4.divBy4(m) - n4.divBy4(n) - return e.pull(twistCurve{}.CombinedMult(m4, n4, twistCurve{}.pull(P))) -} diff --git a/ecc/goldilocks/curve_test.go b/ecc/goldilocks/curve_test.go deleted file mode 100644 index 95c15d96..00000000 --- a/ecc/goldilocks/curve_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package goldilocks_test - -import ( - "crypto/rand" - "testing" - - "github.com/cloudflare/circl/ecc/goldilocks" - "github.com/cloudflare/circl/internal/test" -) - -func TestScalarMult(t *testing.T) { - const testTimes = 1 << 8 - var e goldilocks.Curve - k := &goldilocks.Scalar{} - zero := &goldilocks.Scalar{} - - t.Run("rG=0", func(t *testing.T) { - order := e.Order() - for i := 0; i < testTimes; i++ { - got := e.ScalarBaseMult(&order) - got.ToAffine() - want := e.Identity() - - if !e.IsOnCurve(got) || !e.IsOnCurve(want) || !got.IsEqual(want) { - want.ToAffine() - test.ReportError(t, got, want) - } - } - }) - t.Run("rP=0", func(t *testing.T) { - order := e.Order() - for i := 0; i < testTimes; i++ { - P := randomPoint() - - got := e.ScalarMult(&order, P) - got.ToAffine() - want := e.Identity() - - if !e.IsOnCurve(got) || !e.IsOnCurve(want) || !got.IsEqual(want) { - want.ToAffine() - test.ReportError(t, got, want, P, order) - } - } - }) - t.Run("kG", func(t *testing.T) { - I := e.Identity() - for i := 0; i < testTimes; i++ { - _, _ = rand.Read(k[:]) - - got := e.ScalarBaseMult(k) - want := e.CombinedMult(k, zero, I) // k*G + 0*I - - if !e.IsOnCurve(got) || !e.IsOnCurve(want) || !got.IsEqual(want) { - test.ReportError(t, got, want, k) - } - } - }) - t.Run("kP", func(t *testing.T) { - for i := 0; i < testTimes; i++ { - P := randomPoint() - _, _ = rand.Read(k[:]) - - got := e.ScalarMult(k, P) - want := e.CombinedMult(zero, k, P) - - if !e.IsOnCurve(got) || !e.IsOnCurve(want) || !got.IsEqual(want) { - test.ReportError(t, got, want, P, k) - } - } - }) - t.Run("kG+lP", func(t *testing.T) { - G := e.Generator() - l := &goldilocks.Scalar{} - for i := 0; i < testTimes; i++ { - P := randomPoint() - _, _ = rand.Read(k[:]) - _, _ = rand.Read(l[:]) - - kG := e.ScalarMult(k, G) - lP := e.ScalarMult(l, P) - got := e.Add(kG, lP) - want := e.CombinedMult(k, l, P) - - if !e.IsOnCurve(got) || !e.IsOnCurve(want) || !got.IsEqual(want) { - test.ReportError(t, got, want, P, k, l) - } - } - }) -} - -func BenchmarkCurve(b *testing.B) { - var e goldilocks.Curve - var k, l goldilocks.Scalar - _, _ = rand.Read(k[:]) - _, _ = rand.Read(l[:]) - P := randomPoint() - - b.Run("ScalarMult", func(b *testing.B) { - for i := 0; i < b.N; i++ { - P = e.ScalarMult(&k, P) - } - }) - b.Run("ScalarBaseMult", func(b *testing.B) { - for i := 0; i < b.N; i++ { - e.ScalarBaseMult(&k) - } - }) - b.Run("CombinedMult", func(b *testing.B) { - for i := 0; i < b.N; i++ { - P = e.CombinedMult(&k, &l, P) - } - }) -} diff --git a/ecc/goldilocks/goldilocks.go b/ecc/goldilocks/goldilocks.go new file mode 100644 index 00000000..acc1cf35 --- /dev/null +++ b/ecc/goldilocks/goldilocks.go @@ -0,0 +1,317 @@ +// Package goldilocks provides arithmetic operations on the Goldilocks (edwards448) curve. +// +// Goldilocks Curve +// +// The goldilocks curve is defined over GF(2^448-2^224-1) as +// Goldilocks: ax^2+y^2 = 1 + dx^2y^2, where a=1 and d=-39081. +// This curve was proposed by Hamburg (1) and is also known as edwards448 +// after RFC-7748 (2). +// +// The datatypes Point and Scalar provide methods to perform arithmetic +// operations on the Goldilocks curve. +// +// References +// +// (1) https://www.shiftleft.org/papers/goldilocks +// +// (2) https://tools.ietf.org/html/rfc7748 +package goldilocks + +import ( + "crypto/subtle" + "encoding/binary" + "errors" + "math/bits" + + ted "github.com/cloudflare/circl/ecc/goldilocks/internal/ted448" + fp "github.com/cloudflare/circl/math/fp448" +) + +// Point defines a point on the Goldilocks curve using extended projective +// coordinates. For any affine point (x,y) it holds x = X/Z, y = Y/Z, and +// T = Ta*Tb = X*Y/Z. +type Point ted.Point + +// Identity returns the identity point. +func Identity() Point { return Point(ted.Identity()) } + +// Generator returns the generator point. +func Generator() Point { return Point{X: genX, Y: genY, Z: fp.One(), Ta: genX, Tb: genY} } + +// Order returns the number of points in the prime subgroup in little-endian order. +func Order() []byte { r := ted.Order(); return r[:] } + +// ParamD is the D parameter of the Goldilocks curve, D=-39081 in Fp. +func ParamD() fp.Elt { return paramD } + +func (P Point) String() string { return ted.Point(P).String() } +func (P *Point) ToAffine() { (*ted.Point)(P).ToAffine() } +func (P *Point) Neg() { (*ted.Point)(P).Neg() } +func (P *Point) IsEqual(Q *Point) int { return (*ted.Point)(P).IsEqual((*ted.Point)(Q)) } +func (P *Point) Double() { P.Add(P) } +func (P *Point) Add(Q *Point) { + // Formula as in Eq.(5) of "Twisted Edwards Curves Revisited" by + // Hisil H., Wong K.KH., Carter G., Dawson E. (2008) + // https://doi.org/10.1007/978-3-540-89255-7_20 + // Formula for curves with a=1. + Px, Py, Pz, Pta, Ptb := &P.X, &P.Y, &P.Z, &P.Ta, &P.Tb + Qx, Qy, Qz, Qta, Qtb := &Q.X, &Q.Y, &Q.Z, &Q.Ta, &Q.Tb + + a, b, c, d := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + e, f, g, h := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + ee, ff := &fp.Elt{}, &fp.Elt{} + + fp.Mul(a, Px, Qx) // A = x1*x2 + fp.Mul(b, Py, Qy) // B = y1*y2 + fp.Mul(c, Pta, Ptb) // C = d*t1*t2 + fp.Mul(c, c, Qta) // + fp.Mul(c, c, Qtb) // + fp.Mul(c, c, ¶mD) // + fp.Mul(d, Pz, Qz) // D = z1*z2 + fp.Add(ee, Px, Py) // x1+y1 + fp.Add(ff, Qx, Qy) // x2+y2 + fp.Mul(e, ee, ff) // E = (x1+y1)*(x2+y2)-A-B + fp.Sub(e, e, a) // + fp.Sub(e, e, b) // + fp.Sub(f, d, c) // F = D-C + fp.Add(g, d, c) // g = D+C + fp.Sub(h, b, a) // H = B-A + fp.Mul(Px, e, f) // X = E * F + fp.Mul(Py, g, h) // Y = G * H + fp.Mul(Pz, f, g) // Z = F * G + P.Ta, P.Tb = *e, *h // T = E * H +} + +func (P *Point) CMov(Q *Point, b uint) { + fp.Cmov(&P.X, &Q.X, b) + fp.Cmov(&P.Y, &Q.Y, b) + fp.Cmov(&P.Z, &Q.Z, b) + fp.Cmov(&P.Ta, &Q.Ta, b) + fp.Cmov(&P.Tb, &Q.Tb, b) +} + +// ScalarBaseMult calculates P = kG, where G is the generator of the Goldilocks +// curve. This function runs in constant time. +func (P *Point) ScalarBaseMult(k *Scalar) { + // TODO: recheck if this works for any scalar, likely yes. + k4 := &ted.Scalar{} + divBy4ModOrder(k4, &k.k) + var Q ted.Point + ted.ScalarBaseMult(&Q, k4) + push(P, &Q) +} + +// CombinedMult calculates P = mG+nQ, where G is the generator of the Goldilocks +// curve. This function does NOT run in constant time as is only used for +// signature verification. +func (P *Point) CombinedMult(m, n *Scalar, Q *Point) { + var m4, n4 ted.Scalar + divBy4ModOrder(&m4, &m.k) + divBy4ModOrder(&n4, &n.k) + var R, phiQ ted.Point + pull(&phiQ, Q) + ted.CombinedMult(&R, &m4, &n4, &phiQ) + push(P, &R) +} + +func (P *Point) ScalarMult(k *Scalar, Q *Point) { + var T [4]Point + T[0] = Identity() + T[1] = *Q + T[2] = *Q + T[2].Double() + T[3] = T[2] + T[3].Add(Q) + + var R Point + kMod4 := int32(k.k[0] & 0b11) + for i := range T { + R.CMov(&T[i], uint(subtle.ConstantTimeEq(int32(i), kMod4))) + } + + var kDiv4 ted.Scalar + for i := 0; i < ScalarSize-1; i++ { + kDiv4[i] = (k.k[i+1] << 6) | (k.k[i] >> 2) + } + kDiv4[ScalarSize-1] = k.k[ScalarSize-1] >> 2 + + var phikQ, phiQ ted.Point + pull(&phiQ, Q) + ted.ScalarMult(&phikQ, &kDiv4, &phiQ) + push(P, &phikQ) + P.Add(&R) +} + +// divBy4ModOrder calculates z = x/4 mod order. +func divBy4ModOrder(z, x *ted.Scalar) { + z.Mul(x, &invFour) +} + +// pull calculates Q = Iso4(P), where P is a Goldilocks point and Q is a ted448 point. +func pull(Q *ted.Point, P *Point) { isogeny4(Q, (*ted.Point)(P), true) } + +// push calculates Q = Iso4^-1(P), where P is a ted448 point and Q is a Goldilocks point. +func push(Q *Point, P *ted.Point) { isogeny4((*ted.Point)(Q), P, false) } + +// isogeny4 is a birational map between ted448 and Goldilocks curves. +func isogeny4(Q, P *ted.Point, isPull bool) { + Px, Py, Pz := &P.X, &P.Y, &P.Z + a, b, c, d, e, f, g, h := &Q.X, &Q.Y, &Q.Z, &fp.Elt{}, &Q.Ta, &Q.X, &Q.Y, &Q.Tb + fp.Add(e, Px, Py) // x+y + fp.Sqr(a, Px) // A = x^2 + fp.Sqr(b, Py) // B = y^2 + fp.Sqr(c, Pz) // z^2 + fp.Add(c, c, c) // C = 2*z^2 + if isPull { + *d = *a // D = A + } else { + fp.Neg(d, a) // D = -A + } + fp.Sqr(e, e) // (x+y)^2 + fp.Sub(e, e, a) // (x+y)^2-A + fp.Sub(e, e, b) // E = (x+y)^2-A-B + fp.Add(h, b, d) // H = B+D + fp.Sub(g, b, d) // G = B-D + fp.Sub(f, c, h) // F = C-H + fp.Mul(&Q.Z, f, g) // Z = F * G + fp.Mul(&Q.X, e, f) // X = E * F + fp.Mul(&Q.Y, g, h) // Y = G * H, // T = E * H +} + +type Scalar struct{ k ted.Scalar } + +func (z Scalar) String() string { return z.k.String() } +func (z *Scalar) Add(x, y *Scalar) { z.k.Add(&x.k, &y.k) } +func (z *Scalar) Sub(x, y *Scalar) { z.k.Sub(&x.k, &y.k) } +func (z *Scalar) Mul(x, y *Scalar) { z.k.Mul(&x.k, &y.k) } +func (z *Scalar) Neg(x *Scalar) { z.k.Neg(&x.k) } +func (z *Scalar) Inv(x *Scalar) { z.k.Inv(&x.k) } +func (z *Scalar) IsEqual(x *Scalar) int { return subtle.ConstantTimeCompare(z.k[:], x.k[:]) } +func (z *Scalar) SetUint64(n uint64) { z.k = ted.Scalar{}; binary.LittleEndian.PutUint64(z.k[:], n) } + +// UnmarshalBinary recovers the scalar from its byte representation in big-endian order. +func (z *Scalar) UnmarshalBinary(b []byte) error { return z.k.UnmarshalBinary(b) } + +// MarshalBinary returns the scalar byte representation in big-endian order. +func (z *Scalar) MarshalBinary() ([]byte, error) { return z.k.MarshalBinary() } + +// ToBytesLE returns the scalar byte representation in little-endian order. +func (z *Scalar) ToBytesLE() []byte { return z.k.ToBytesLE() } + +// ToBytesBE returns the scalar byte representation in big-endian order. +func (z *Scalar) ToBytesBE() []byte { return z.k.ToBytesBE() } + +// FromBytesLE stores z = x mod order, where x is a number stored in little-endian order. +func (z *Scalar) FromBytesLE(x []byte) { z.k.FromBytesLE(x) } + +// FromBytesBE stores z = x mod order, where x is a number stored in big-endian order. +func (z *Scalar) FromBytesBE(x []byte) { z.k.FromBytesBE(x) } + +var ( + // genX is the x-coordinate of the generator of Goldilocks curve. + genX = fp.Elt{ // little-endian + 0x5e, 0xc0, 0x0c, 0xc7, 0x2b, 0xa8, 0x26, 0x26, + 0x8e, 0x93, 0x00, 0x8b, 0xe1, 0x80, 0x3b, 0x43, + 0x11, 0x65, 0xb6, 0x2a, 0xf7, 0x1a, 0xae, 0x12, + 0x64, 0xa4, 0xd3, 0xa3, 0x24, 0xe3, 0x6d, 0xea, + 0x67, 0x17, 0x0f, 0x47, 0x70, 0x65, 0x14, 0x9e, + 0xda, 0x36, 0xbf, 0x22, 0xa6, 0x15, 0x1d, 0x22, + 0xed, 0x0d, 0xed, 0x6b, 0xc6, 0x70, 0x19, 0x4f, + } + // genY is the y-coordinate of the generator of Goldilocks curve. + genY = fp.Elt{ // little-endian + 0x14, 0xfa, 0x30, 0xf2, 0x5b, 0x79, 0x08, 0x98, + 0xad, 0xc8, 0xd7, 0x4e, 0x2c, 0x13, 0xbd, 0xfd, + 0xc4, 0x39, 0x7c, 0xe6, 0x1c, 0xff, 0xd3, 0x3a, + 0xd7, 0xc2, 0xa0, 0x05, 0x1e, 0x9c, 0x78, 0x87, + 0x40, 0x98, 0xa3, 0x6c, 0x73, 0x73, 0xea, 0x4b, + 0x62, 0xc7, 0xc9, 0x56, 0x37, 0x20, 0x76, 0x88, + 0x24, 0xbc, 0xb6, 0x6e, 0x71, 0x46, 0x3f, 0x69, + } + // paramD is the D parameter of the Goldilocks curve, D=-39081 in Fp. + paramD = fp.Elt{ // little-endian + 0x56, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + } + // invFour is 1/4 mod order, where order = ted.Order(). + invFour = ted.Scalar{ // little-endian + 0x3d, 0x11, 0xd6, 0xaa, 0xa4, 0x30, 0xde, 0x48, + 0xd5, 0x63, 0x71, 0xa3, 0x9c, 0x30, 0x5b, 0x08, + 0xa4, 0x8d, 0xb5, 0x6b, 0xd2, 0xb6, 0x13, 0x71, + 0xfa, 0x88, 0x32, 0xdf, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, + } +) + +// isLessThan returns 1 if 0 <= x < y, and assumes that slices are of the +// same length and are interpreted in little-endian order. +func isLessThan(x, y []byte) int { + i := len(x) - 1 + for i > 0 && x[i] == y[i] { + i-- + } + xi := int(x[i]) + yi := int(y[i]) + return ((xi - yi) >> (bits.UintSize - 1)) & 1 +} + +// Decode if succeeds constructs a point by decoding the first +// EncodingSize bytes of data. +func (P *Point) Decode(data *[EncodingSize]byte) error { + x, y := &fp.Elt{}, &fp.Elt{} + isByteZero := subtle.ConstantTimeByteEq(data[EncodingSize-1]&0x7F, 0x00) + signX := int(data[EncodingSize-1] >> 7) + copy(y[:], data[:fp.Size]) + p := fp.P() + isLessThanP := isLessThan(y[:], p[:]) + + u, v := &fp.Elt{}, &fp.Elt{} + one := fp.One() + fp.Sqr(u, y) // u = y^2 + fp.Mul(v, u, ¶mD) // v = dy^2 + fp.Sub(u, u, &one) // u = y^2-1 + fp.Sub(v, v, &one) // v = dy^2-a + isQR := fp.InvSqrt(x, u, v) // x = sqrt(u/v) + isValidXSign := 1 - (fp.IsZero(x) & signX) + fp.Neg(u, x) // u = -x + fp.Cmov(x, u, uint(signX^fp.Parity(x))) // if signX != x mod 2 + + b0 := isByteZero + b1 := isLessThanP + b2 := isQR + b3 := isValidXSign + b := uint(subtle.ConstantTimeEq(int32(8*b3+4*b2+2*b1+b0), 0xF)) + fp.Cmov(&P.X, x, b) + fp.Cmov(&P.Y, y, b) + fp.Cmov(&P.Ta, x, b) + fp.Cmov(&P.Tb, y, b) + fp.Cmov(&P.Z, &one, b) + if b == 0 { + return ErrInvalidDecoding + } + return nil +} + +// Encode sets data with the unique encoding of the point P. +func (P *Point) Encode(data *[EncodingSize]byte) error { + P.ToAffine() + data[EncodingSize-1] = (P.X[0] & 1) << 7 + return fp.ToBytes(data[:fp.Size], &P.Y) +} + +const ( + // EncodingSize is the size (in bytes) of an encoded point on the Goldilocks curve. + EncodingSize = fp.Size + 1 + // ScalarSize is the size (in bytes) of scalars. + ScalarSize = ted.ScalarSize +) + +// ErrInvalidDecoding alerts of an error during decoding a point. +var ErrInvalidDecoding = errors.New("goldilocks: invalid point decoding") diff --git a/ecc/goldilocks/goldilocks_test.go b/ecc/goldilocks/goldilocks_test.go new file mode 100644 index 00000000..d50cda41 --- /dev/null +++ b/ecc/goldilocks/goldilocks_test.go @@ -0,0 +1,250 @@ +package goldilocks_test + +import ( + "crypto/rand" + "errors" + "testing" + + goldilocks "github.com/cloudflare/circl/ecc/goldilocks" + ted "github.com/cloudflare/circl/ecc/goldilocks/internal/ted448" + "github.com/cloudflare/circl/internal/test" + fp "github.com/cloudflare/circl/math/fp448" +) + +func rndScalar(t testing.TB) *goldilocks.Scalar { + var buf [ted.ScalarSize]byte + _, err := rand.Read(buf[:]) + if err != nil { + t.Fatal(err) + } + var s goldilocks.Scalar + s.FromBytesLE(buf[:]) + return &s +} + +func randomPoint(t testing.TB) (P goldilocks.Point) { + P.ScalarBaseMult(rndScalar(t)) + return P +} + +func TestPointAdd(t *testing.T) { + const testTimes = 1 << 10 + + t.Run("P+0=P", func(t *testing.T) { + I := goldilocks.Identity() + for i := 0; i < testTimes; i++ { + P := randomPoint(t) + got := P + got.Add(&I) + want := P + if got.IsEqual(&want) == 0 { + test.ReportError(t, got, want, P) + } + } + }) + + t.Run("P+(-P)=0", func(t *testing.T) { + for i := 0; i < testTimes; i++ { + P := randomPoint(t) + got := P + got.Neg() + got.Add(&P) + want := goldilocks.Identity() + if got.IsEqual(&want) == 0 { + test.ReportError(t, got, want, P) + } + } + }) + + t.Run("16P", func(t *testing.T) { + for i := 0; i < testTimes; i++ { + P := randomPoint(t) + // 16P = P+P+... + R := goldilocks.Identity() + for i := 0; i < 16; i++ { + R.Add(&P) + } + got := R + // 16P = 2*2*2*2*P + P.Double() + P.Double() + P.Double() + P.Double() + want := P + if got.IsEqual(&want) == 0 { + test.ReportError(t, got, want, P) + } + } + }) +} + +func TestScalarBaseMult(t *testing.T) { + const testTimes = 1 << 10 + var got, want goldilocks.Point + G := goldilocks.Generator() + for i := 0; i < testTimes; i++ { + k := rndScalar(t) + got.ScalarBaseMult(k) + want.ScalarMult(k, &G) + if got.IsEqual(&want) == 0 { + test.ReportError(t, got, want, k) + } + } +} + +func TestScalarMult(t *testing.T) { + const testTimes = 1 << 9 + zero := &goldilocks.Scalar{} + var got, want goldilocks.Point + for i := 0; i < testTimes; i++ { + k := rndScalar(t) + Q := randomPoint(t) + got.ScalarMult(k, &Q) + want.CombinedMult(zero, k, &Q) // 0*G + k*Q + if got.IsEqual(&want) == 0 { + test.ReportError(t, got, want, k) + } + } +} + +func TestCombinedMult(t *testing.T) { + const testTimes = 1 << 9 + var got, want, R goldilocks.Point + for i := 0; i < testTimes; i++ { + k1 := rndScalar(t) + k2 := rndScalar(t) + Q := randomPoint(t) + got.CombinedMult(k1, k2, &Q) // k1*G + k2*Q + + R.ScalarBaseMult(k1) + want.ScalarMult(k2, &Q) + want.Add(&R) + + if got.IsEqual(&want) == 0 { + test.ReportError(t, got, want, k1, k2, Q) + } + } +} + +func TestPointEncoding(t *testing.T) { + const testTimes = 1 << 10 + var want, got [goldilocks.EncodingSize]byte + var P goldilocks.Point + for i := 0; i < testTimes; i++ { + for found := false; !found; { + _, _ = rand.Read(want[:]) + want[goldilocks.EncodingSize-1] &= 0x80 + err := P.Decode(&want) + found = err == nil + } + err := P.Encode(&got) + if err != nil || got != want { + test.ReportError(t, got, want, P) + } + } +} + +func TestPointInvalid(t *testing.T) { + p := fp.P() + one := fp.One() + + var byteDirty, bigY, wrongSignX, nonQR [goldilocks.EncodingSize]byte + byteDirty[goldilocks.EncodingSize-1] = 0x33 + copy(bigY[:], p[:]) + copy(wrongSignX[:], one[:]) + wrongSignX[goldilocks.EncodingSize-1] = 1 << 7 + nonQR[0] = 2 // smallest y such that (y^2+a)/(dy^2-a) is not a square. + + badEncodings := []*[goldilocks.EncodingSize]byte{ + &byteDirty, // the last byte is not {0x00,0x80}. + &bigY, // y is out of the interval [0,p-1]. + &wrongSignX, // x has wrong sign. + &nonQR, // y=2 and (y^2+a)/(dy^2-a) is not a square. + } + + var P goldilocks.Point + for _, enc := range badEncodings { + got := P.Decode(enc) + want := goldilocks.ErrInvalidDecoding + if !errors.Is(got, want) { + test.ReportError(t, got, want, enc) + } + } +} + +func BenchmarkPoint(b *testing.B) { + k := rndScalar(b) + l := rndScalar(b) + P := randomPoint(b) + Q := randomPoint(b) + + b.Run("Add", func(b *testing.B) { + for i := 0; i < b.N; i++ { + P.Add(&Q) + } + }) + b.Run("Double", func(b *testing.B) { + for i := 0; i < b.N; i++ { + P.Double() + } + }) + b.Run("ScalarMult", func(b *testing.B) { + for i := 0; i < b.N; i++ { + Q.ScalarMult(k, &P) + } + }) + b.Run("ScalarBaseMult", func(b *testing.B) { + for i := 0; i < b.N; i++ { + P.ScalarBaseMult(k) + } + }) + b.Run("CombinedMult", func(b *testing.B) { + for i := 0; i < b.N; i++ { + Q.CombinedMult(k, l, &P) + } + }) +} + +func BenchmarkScalar(b *testing.B) { + x := rndScalar(b) + y := rndScalar(b) + z := rndScalar(b) + + b.Run("Add", func(b *testing.B) { + for i := 0; i < b.N; i++ { + z.Add(x, y) + } + }) + b.Run("Sub", func(b *testing.B) { + for i := 0; i < b.N; i++ { + z.Sub(x, y) + } + }) + b.Run("Mul", func(b *testing.B) { + for i := 0; i < b.N; i++ { + z.Mul(x, y) + } + }) + b.Run("Inv", func(b *testing.B) { + for i := 0; i < b.N; i++ { + z.Inv(x) + } + }) +} + +func BenchmarkEncoding(b *testing.B) { + var data [goldilocks.EncodingSize]byte + k := rndScalar(b) + var P goldilocks.Point + P.ScalarBaseMult(k) + b.Run("Encode", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = P.Encode(&data) + } + }) + b.Run("Decode", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = P.Decode(&data) + } + }) +} diff --git a/ecc/goldilocks/internal/ted448/basemult.go b/ecc/goldilocks/internal/ted448/basemult.go new file mode 100644 index 00000000..0c92b5f6 --- /dev/null +++ b/ecc/goldilocks/internal/ted448/basemult.go @@ -0,0 +1,63 @@ +package ted448 + +import ( + "crypto/subtle" + + mlsb "github.com/cloudflare/circl/math/mlsbset" +) + +const ( + // MLSBRecoding parameters + fxT = 448 + fxV = 2 + fxW = 3 + fx2w1 = 1 << (uint(fxW) - 1) +) + +// ScalarBaseMult calculates R = kG, where G is the generator point. +func ScalarBaseMult(R *Point, k *Scalar) { + m, err := mlsb.New(fxT, fxV, fxW) + if err != nil { + panic(err) + } + if m.IsExtended() { + panic("not extended") + } + + var k64, _k64, order64 scalar64 + k64.fromScalar(k) + order64.fromScalar(&order) + k64.cmov(&order64, uint64(k64.isZero())) + + isEven := 1 - int(k64[0]&0x1) + _k64.sub(&order64, &k64) + k64.cmov(&_k64, uint64(isEven)) + var scalar Scalar + k64.toScalar(&scalar) + + c, err := m.Encode(scalar[:]) + if err != nil { + panic(err) + } + + gP := c.Exp(groupMLSB{}) + P := gP.(*Point) + P.cneg(uint(isEven)) + *R = *P +} + +type groupMLSB struct{} + +func (e groupMLSB) ExtendedEltP() mlsb.EltP { return nil } +func (e groupMLSB) Sqr(x mlsb.EltG) { x.(*Point).Double() } +func (e groupMLSB) Mul(x mlsb.EltG, y mlsb.EltP) { x.(*Point).mixAddZ1(y.(*prePointAffine)) } +func (e groupMLSB) Identity() mlsb.EltG { I := Identity(); return &I } +func (e groupMLSB) NewEltP() mlsb.EltP { return &prePointAffine{} } +func (e groupMLSB) Lookup(a mlsb.EltP, v uint, s, u int32) { + Tabj := &tabFixMult[v] + P := a.(*prePointAffine) + for k := range Tabj { + P.cmov(&Tabj[k], uint(subtle.ConstantTimeEq(int32(k), u))) + } + P.cneg(int(s >> 31)) +} diff --git a/ecc/goldilocks/internal/ted448/constants.go b/ecc/goldilocks/internal/ted448/constants.go new file mode 100644 index 00000000..2f0655ec --- /dev/null +++ b/ecc/goldilocks/internal/ted448/constants.go @@ -0,0 +1,51 @@ +package ted448 + +import fp "github.com/cloudflare/circl/math/fp448" + +var ( + // genX is the x-coordinate of the generator of ted448 curve. + genX = fp.Elt{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xfe, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + } + // genY is the y-coordinate of the generator of ted448 curve. + genY = fp.Elt{ + 0x64, 0x4a, 0xdd, 0xdf, 0xb4, 0x79, 0x60, 0xc8, + 0xa1, 0x70, 0xb4, 0x3a, 0x1e, 0x0c, 0x9b, 0x19, + 0xe5, 0x48, 0x3f, 0xd7, 0x44, 0x18, 0x18, 0x14, + 0x14, 0x27, 0x45, 0x50, 0x2c, 0x24, 0xd5, 0x93, + 0xc3, 0x74, 0x4c, 0x50, 0x70, 0x43, 0x26, 0x05, + 0x08, 0x24, 0xca, 0x78, 0x30, 0xc1, 0x06, 0x8d, + 0xd4, 0x86, 0x42, 0xf0, 0x14, 0xde, 0x08, 0x85, + } + // paramD is -39082 in Fp. The D parameter of the ted448 curve. + paramD = fp.Elt{ + 0x55, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + } + // order is 2^446-0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d, + // which is the number of points in the prime subgroup. + order = Scalar{ // little-endian + 0xf3, 0x44, 0x58, 0xab, 0x92, 0xc2, 0x78, 0x23, + 0x55, 0x8f, 0xc5, 0x8d, 0x72, 0xc2, 0x6c, 0x21, + 0x90, 0x36, 0xd6, 0xae, 0x49, 0xdb, 0x4e, 0xc4, + 0xe9, 0x23, 0xca, 0x7c, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, + } + // residue448 is 2^448 mod order. + residue448 = [4]uint64{ + 0x721cf5b5529eec34, 0x7a4cf635c8e9c2ab, 0xeec492d944a725bf, 0x20cd77058, + } +) diff --git a/ecc/goldilocks/internal/ted448/curve.go b/ecc/goldilocks/internal/ted448/curve.go new file mode 100644 index 00000000..84f47967 --- /dev/null +++ b/ecc/goldilocks/internal/ted448/curve.go @@ -0,0 +1,181 @@ +// Package ted448 provides operations on a twist curve of the Goldilocks curve. +// +// The twist curve is defined over Fp = GF(2^448-2^224-1) as +// ted448: ax^2+y^2 = 1 + dx^2y^2, where a=-1 and d=-39082. +// The ted448 curve provides fast arithmetic operations due to a=-1. +// +// Isogenies +// +// The ted448 curve is 4-degree isogeneous to the Goldilocks curve, and the +// explicit map Iso4 is given in [Ham, Sec 2]. +// +// The ted448 curve is 2-degree isogeneous to the Jacobi quartic used in Decaf. +// +// Generator Point +// +// The generator of ted448 is returned by Generator(), and is equal to +// Iso4(Gx,Gy), where (Gx,Gy) is the generator of the Goldilocks curve. +// +// References +// +// [Ham] Twisting Edwards curves with isogenies, Hamburg. (https://www.shiftleft.org/papers/isogeny) +package ted448 + +import ( + "crypto/subtle" + "math/bits" + + "github.com/cloudflare/circl/internal/conv" + "github.com/cloudflare/circl/math" + fp "github.com/cloudflare/circl/math/fp448" +) + +// Identity returns the identity point. +func Identity() Point { return Point{Y: fp.One(), Z: fp.One()} } + +// Generator returns the generator point. +func Generator() Point { return Point{X: genX, Y: genY, Z: fp.One(), Ta: genX, Tb: genY} } + +// Order returns the number of points in the prime subgroup in little-endian order. +func Order() (r [ScalarSize]byte) { r = order; return r } + +// IsOnCurve returns true if the point lies on the curve. +func IsOnCurve(P *Point) bool { + eq0 := fp.IsZero(&P.X) + eq0 &= fp.IsZero(&P.Y) + eq0 &= fp.IsZero(&P.Z) + eq0 &= fp.IsZero(&P.Ta) + eq0 &= fp.IsZero(&P.Tb) + eq0 = 1 - eq0 + x2, y2, t, t2, z2 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + rhs, lhs := &fp.Elt{}, &fp.Elt{} + fp.Mul(t, &P.Ta, &P.Tb) // t = ta*tb + fp.Sqr(x2, &P.X) // x^2 + fp.Sqr(y2, &P.Y) // y^2 + fp.Sqr(z2, &P.Z) // z^2 + fp.Sqr(t2, t) // t^2 + fp.Sub(lhs, y2, x2) // -x^2 + y^2, since a=-1 + fp.Mul(rhs, t2, ¶mD) // dt^2 + fp.Add(rhs, rhs, z2) // z^2 + dt^2 + fp.Sub(lhs, lhs, rhs) // ax^2 + y^2 - (z^2 + dt^2) + eq1 := fp.IsZero(lhs) + fp.Mul(lhs, &P.X, &P.Y) // xy + fp.Mul(rhs, t, &P.Z) // tz + fp.Sub(lhs, lhs, rhs) // xy - tz + eq2 := fp.IsZero(lhs) + return subtle.ConstantTimeByteEq(byte(4*eq2+2*eq1+eq0), 0x7) == 1 +} + +// subYDiv16 update x = (x - y) / 16. +func subYDiv16(x *scalar64, y int64) { + s := uint64(y >> 63) + x0, b0 := bits.Sub64((*x)[0], uint64(y), 0) + x1, b1 := bits.Sub64((*x)[1], s, b0) + x2, b2 := bits.Sub64((*x)[2], s, b1) + x3, b3 := bits.Sub64((*x)[3], s, b2) + x4, b4 := bits.Sub64((*x)[4], s, b3) + x5, b5 := bits.Sub64((*x)[5], s, b4) + x6, _ := bits.Sub64((*x)[6], s, b5) + x[0] = (x0 >> 4) | (x1 << 60) + x[1] = (x1 >> 4) | (x2 << 60) + x[2] = (x2 >> 4) | (x3 << 60) + x[3] = (x3 >> 4) | (x4 << 60) + x[4] = (x4 >> 4) | (x5 << 60) + x[5] = (x5 >> 4) | (x6 << 60) + x[6] = (x6 >> 4) +} + +func recodeScalar(d *[113]int8, k *scalar64) { + for i := 0; i < 112; i++ { + d[i] = int8((k[0] & 0x1f) - 16) + subYDiv16(k, int64(d[i])) + } + d[112] = int8(k[0]) +} + +// ScalarMult calculates R = kP. +func ScalarMult(R *Point, k *Scalar, P *Point) { + var TabP [8]prePointProy + var S prePointProy + var d [113]int8 + + var k64, _k64, order64 scalar64 + k64.fromScalar(k) + order64.fromScalar(&order) + k64.cmov(&order64, uint64(k64.isZero())) + + isEven := 1 - int(k64[0]&0x1) + _k64.sub(&order64, &k64) + k64.cmov(&_k64, uint64(isEven)) + + recodeScalar(&d, &k64) + + P.oddMultiples(TabP[:]) + Q := Identity() + for i := 112; i >= 0; i-- { + Q.Double() + Q.Double() + Q.Double() + Q.Double() + mask := d[i] >> 7 + absDi := (d[i] + mask) ^ mask + inx := int32((absDi - 1) >> 1) + sig := int((d[i] >> 7) & 0x1) + for j := range TabP { + S.cmov(&TabP[j], uint(subtle.ConstantTimeEq(inx, int32(j)))) + } + S.cneg(sig) + Q.mixAdd(&S) + } + Q.cneg(uint(isEven)) + *R = Q +} + +const ( + omegaFix = 7 + omegaVar = 5 +) + +// CombinedMult calculates R = mG+nP using a non-constant-time procedure. +func CombinedMult(R *Point, m, n *Scalar, P *Point) { + nafFix := math.OmegaNAF(conv.BytesLe2BigInt(m[:]), omegaFix) + nafVar := math.OmegaNAF(conv.BytesLe2BigInt(n[:]), omegaVar) + + if len(nafFix) > len(nafVar) { + nafVar = append(nafVar, make([]int32, len(nafFix)-len(nafVar))...) + } else if len(nafFix) < len(nafVar) { + nafFix = append(nafFix, make([]int32, len(nafVar)-len(nafFix))...) + } + + var TabQ [1 << (omegaVar - 2)]prePointProy + P.oddMultiples(TabQ[:]) + Q := Identity() + for i := len(nafFix) - 1; i >= 0; i-- { + Q.Double() + // Generator point + if nafFix[i] != 0 { + idxM := absolute(nafFix[i]) >> 1 + R := tabVerif[idxM] + if nafFix[i] < 0 { + R.neg() + } + Q.mixAddZ1(&R) + } + // Variable input point + if nafVar[i] != 0 { + idxN := absolute(nafVar[i]) >> 1 + S := TabQ[idxN] + if nafVar[i] < 0 { + S.neg() + } + Q.mixAdd(&S) + } + } + *R = Q +} + +// absolute returns always a positive value. +func absolute(x int32) int32 { + mask := x >> 31 + return (x + mask) ^ mask +} diff --git a/ecc/goldilocks/internal/ted448/curve_test.go b/ecc/goldilocks/internal/ted448/curve_test.go new file mode 100644 index 00000000..8bff1f33 --- /dev/null +++ b/ecc/goldilocks/internal/ted448/curve_test.go @@ -0,0 +1,174 @@ +package ted448_test + +import ( + "crypto/rand" + "testing" + + "github.com/cloudflare/circl/ecc/goldilocks/internal/ted448" + "github.com/cloudflare/circl/internal/test" +) + +func randomPoint() ted448.Point { + var k ted448.Scalar + _, _ = rand.Read(k[:]) + var P ted448.Point + ted448.ScalarBaseMult(&P, &k) + return P +} + +func TestPointAdd(t *testing.T) { + const testTimes = 1 << 10 + for i := 0; i < testTimes; i++ { + P := randomPoint() + Q := P + // Q = 16P = 2^4P + Q.Double() // 2P + Q.Double() // 4P + Q.Double() // 8P + Q.Double() // 16P + got := Q + got.ToAffine() + + // R = 16P = P+P...+P + R := ted448.Identity() + for j := 0; j < 16; j++ { + R.Add(&P) + } + want := R + want.ToAffine() + if !ted448.IsOnCurve(&got) || + !ted448.IsOnCurve(&want) || + got.IsEqual(&want) == 0 { + test.ReportError(t, got, want, P) + } + } +} + +func TestIsOnCurve(t *testing.T) { + for _, v := range []struct { + P ted448.Point + want bool + }{ + {ted448.Point{}, false}, + {ted448.Identity(), true}, + {ted448.Generator(), true}, + {randomPoint(), true}, + } { + got := ted448.IsOnCurve(&v.P) + if got != v.want { + test.ReportError(t, got, v.want, v.P) + } + } +} + +func TestPointNeg(t *testing.T) { + const testTimes = 1 << 10 + for i := 0; i < testTimes; i++ { + P := randomPoint() + Q := P + Q.Neg() + Q.Add(&P) + got := Q.IsIdentity() + want := 1 + if got != want { + test.ReportError(t, got, want, P) + } + } +} + +func TestScalarMult(t *testing.T) { + const testTimes = 1 << 8 + order := ted448.Scalar(ted448.Order()) + + t.Run("rG=0", func(t *testing.T) { + got := &ted448.Point{} + for i := 0; i < testTimes; i++ { + ted448.ScalarBaseMult(got, &order) + want := ted448.Identity() + + if !ted448.IsOnCurve(got) || + !ted448.IsOnCurve(&want) || + got.IsEqual(&want) == 0 { + test.ReportError(t, got, want) + } + } + }) + t.Run("rP=0", func(t *testing.T) { + got := &ted448.Point{} + for i := 0; i < testTimes; i++ { + P := randomPoint() + + ted448.ScalarMult(got, &order, &P) + want := ted448.Identity() + + if !ted448.IsOnCurve(got) || + !ted448.IsOnCurve(&want) || + got.IsEqual(&want) == 0 { + test.ReportError(t, got, want, P) + } + } + }) + t.Run("kG", func(t *testing.T) { + k := &ted448.Scalar{} + zero := &ted448.Scalar{} + got := &ted448.Point{} + want := &ted448.Point{} + I := ted448.Identity() + for i := 0; i < testTimes; i++ { + _, _ = rand.Read(k[:]) + + ted448.ScalarBaseMult(got, k) + ted448.CombinedMult(want, k, zero, &I) // k*G + 0*I + + if !ted448.IsOnCurve(got) || + !ted448.IsOnCurve(want) || + got.IsEqual(want) == 0 { + test.ReportError(t, got, want, k) + } + } + }) + t.Run("kP", func(t *testing.T) { + k := &ted448.Scalar{} + zero := &ted448.Scalar{} + got := &ted448.Point{} + want := &ted448.Point{} + for i := 0; i < testTimes; i++ { + P := randomPoint() + _, _ = rand.Read(k[:]) + + ted448.ScalarMult(got, k, &P) + ted448.CombinedMult(want, zero, k, &P) + + if !ted448.IsOnCurve(got) || + !ted448.IsOnCurve(want) || + got.IsEqual(want) == 0 { + test.ReportError(t, got, want, P, k) + } + } + }) + t.Run("kG+lP", func(t *testing.T) { + want := &ted448.Point{} + kG := &ted448.Point{} + lP := &ted448.Point{} + G := ted448.Generator() + k := &ted448.Scalar{} + l := &ted448.Scalar{} + for i := 0; i < testTimes; i++ { + P := randomPoint() + _, _ = rand.Read(k[:]) + _, _ = rand.Read(l[:]) + + ted448.ScalarMult(kG, k, &G) + ted448.ScalarMult(lP, l, &P) + kG.Add(lP) + got := kG + ted448.CombinedMult(want, k, l, &P) + + if !ted448.IsOnCurve(got) || + !ted448.IsOnCurve(want) || + got.IsEqual(want) == 0 { + test.ReportError(t, got, want, P, k, l) + } + } + }) +} diff --git a/ecc/goldilocks/internal/ted448/point.go b/ecc/goldilocks/internal/ted448/point.go new file mode 100644 index 00000000..5e4829be --- /dev/null +++ b/ecc/goldilocks/internal/ted448/point.go @@ -0,0 +1,193 @@ +package ted448 + +import ( + "crypto/subtle" + "fmt" + + fp "github.com/cloudflare/circl/math/fp448" +) + +// Point defines a point on the ted448 curve using extended projective +// coordinates. Thus, for any affine point (x,y) it holds x=X/Z, y = Y/Z, and +// T = Ta*Tb = X*Y/Z. +type Point struct{ X, Y, Z, Ta, Tb fp.Elt } + +type prePointAffine struct{ addYX, subYX, dt2 fp.Elt } + +type prePointProy struct { + prePointAffine + z2 fp.Elt +} + +func (P Point) String() string { + return fmt.Sprintf("x: %v\ny: %v\nta: %v\ntb: %v\nz: %v", P.X, P.Y, P.Ta, P.Tb, P.Z) +} + +// cneg conditionally negates the point if b=1. +func (P *Point) cneg(b uint) { + t := &fp.Elt{} + fp.Neg(t, &P.X) + fp.Cmov(&P.X, t, b) + fp.Neg(t, &P.Ta) + fp.Cmov(&P.Ta, t, b) +} + +// Double updates P with 2P. +func (P *Point) Double() { + // This is formula (7) from "Twisted Edwards Curves Revisited" by + // Hisil H., Wong K.KH., Carter G., Dawson E. (2008) + // https://doi.org/10.1007/978-3-540-89255-7_20 + Px, Py, Pz, Pta, Ptb := &P.X, &P.Y, &P.Z, &P.Ta, &P.Tb + a, b, c, e, f, g, h := Px, Py, Pz, Pta, Px, Py, Ptb + fp.Add(e, Px, Py) // x+y + fp.Sqr(a, Px) // A = x^2 + fp.Sqr(b, Py) // B = y^2 + fp.Sqr(c, Pz) // z^2 + fp.Add(c, c, c) // C = 2*z^2 + fp.Add(h, a, b) // H = A+B + fp.Sqr(e, e) // (x+y)^2 + fp.Sub(e, e, h) // E = (x+y)^2-A-B = 2xy + fp.Sub(g, b, a) // G = B-A + fp.Sub(f, c, g) // F = C-G = 2z^2-y^2+x^2 + fp.Mul(Pz, f, g) // Z = F * G = (y^2-x^2)(2z^2-y^2+x^2) + fp.Mul(Px, e, f) // X = E * F = 2xy(2z^2-y^2+x^2) + fp.Mul(Py, g, h) // Y = G * H = (x^2-y^2)(x^2+y^2) + // T = E * H = 2xy(x^2+y^2) +} + +// mixAdd calulates P= P+Q, where Q is a precomputed448 point with Z_Q = 1. +func (P *Point) mixAddZ1(Q *prePointAffine) { + fp.Add(&P.Z, &P.Z, &P.Z) // D = 2*z1 (z2=1) + P.coreAddition(Q) +} + +// coreAddition calculates P=P+Q for curves with A=-1. +func (P *Point) coreAddition(Q *prePointAffine) { + // Formula as in Eq.(5) of "Twisted Edwards Curves Revisited" by + // Hisil H., Wong K.KH., Carter G., Dawson E. (2008) + // https://doi.org/10.1007/978-3-540-89255-7_20 + // Formula for curves with a=-1. + Px, Py, Pz, Pta, Ptb := &P.X, &P.Y, &P.Z, &P.Ta, &P.Tb + addYX2, subYX2, dt2 := &Q.addYX, &Q.subYX, &Q.dt2 + a, b, c, d, e, f, g, h := Px, Py, &fp.Elt{}, Pz, Pta, Px, Py, Ptb + fp.Mul(c, Pta, Ptb) // t1 = ta*tb + fp.Sub(h, Py, Px) // y1-x1 + fp.Add(b, Py, Px) // y1+x1 + fp.Mul(a, h, subYX2) // A = (y1-x1)*(y2-x2) + fp.Mul(b, b, addYX2) // B = (y1+x1)*(y2+x2) + fp.Mul(c, c, dt2) // C = 2*D*t1*t2 + fp.Sub(e, b, a) // E = B-A + fp.Add(h, b, a) // H = B+A + fp.Sub(f, d, c) // F = D-C + fp.Add(g, d, c) // G = D+C + fp.Mul(Pz, f, g) // Z = F * G + fp.Mul(Px, e, f) // X = E * F + fp.Mul(Py, g, h) // Y = G * H, T = E * H +} + +func (P *prePointAffine) neg() { + P.addYX, P.subYX = P.subYX, P.addYX + fp.Neg(&P.dt2, &P.dt2) +} + +func (P *prePointAffine) cneg(b int) { + t := &fp.Elt{} + fp.Cswap(&P.addYX, &P.subYX, uint(b)) + fp.Neg(t, &P.dt2) + fp.Cmov(&P.dt2, t, uint(b)) +} + +func (P *prePointAffine) cmov(Q *prePointAffine, b uint) { + fp.Cmov(&P.addYX, &Q.addYX, b) + fp.Cmov(&P.subYX, &Q.subYX, b) + fp.Cmov(&P.dt2, &Q.dt2, b) +} + +// mixAdd calculates P= P+Q, where Q is a precomputed448 point with Z_Q != 1. +func (P *Point) mixAdd(Q *prePointProy) { + fp.Mul(&P.Z, &P.Z, &Q.z2) // D = 2*z1*z2 + P.coreAddition(&Q.prePointAffine) +} + +// IsIdentity returns 1 if P is the identity, otherwise 0. +func (P *Point) IsIdentity() int { + b0 := fp.IsZero(&P.X) + b1 := 1 - fp.IsZero(&P.Y) + b2 := 1 - fp.IsZero(&P.Z) + b3 := fp.IsEqual(&P.Y, &P.Z) + return subtle.ConstantTimeEq(int32(8*b3+4*b2+2*b1+b0), 0xF) +} + +// IsEqual returns 1 if P is equivalent to Q, otherwise 0. +func (P *Point) IsEqual(Q *Point) int { + l, r := &fp.Elt{}, &fp.Elt{} + fp.Mul(l, &P.X, &Q.Z) + fp.Mul(r, &Q.X, &P.Z) + fp.Sub(l, l, r) + b0 := fp.IsZero(l) + fp.Mul(l, &P.Y, &Q.Z) + fp.Mul(r, &Q.Y, &P.Z) + fp.Sub(l, l, r) + b1 := fp.IsZero(l) + fp.Mul(l, &P.Ta, &P.Tb) + fp.Mul(l, l, &Q.Z) + fp.Mul(r, &Q.Ta, &Q.Tb) + fp.Mul(r, r, &P.Z) + fp.Sub(l, l, r) + b2 := fp.IsZero(l) + return subtle.ConstantTimeEq(int32(4*b2+2*b1+b0), 0x7) +} + +// Neg obtains the inverse of P. +func (P *Point) Neg() { fp.Neg(&P.X, &P.X); fp.Neg(&P.Ta, &P.Ta) } + +// Add calculates P = P+Q. +func (P *Point) Add(Q *Point) { + preB := &prePointProy{} + preB.FromPoint(Q) + P.mixAdd(preB) +} + +func (P *Point) ToAffine() { + invZ := &fp.Elt{} + fp.Inv(invZ, &P.Z) // 1/z + fp.Mul(&P.X, &P.X, invZ) // x/z + fp.Mul(&P.Y, &P.Y, invZ) // y/z + fp.Modp(&P.X) + fp.Modp(&P.Y) + P.Ta = P.X + P.Tb = P.Y + fp.SetOne(&P.Z) +} + +// oddMultiples calculates T[i] = (2*i-1)P for 0 < i < len(T). +func (P *Point) oddMultiples(T []prePointProy) { + if n := len(T); n > 0 { + Q := *P + T[0].FromPoint(&Q) + _2P := *P + _2P.Double() + R := &prePointProy{} + R.FromPoint(&_2P) + for i := 1; i < n; i++ { + Q.mixAdd(R) + T[i].FromPoint(&Q) + } + } +} + +// cmov conditionally moves Q into P if b=1. +func (P *prePointProy) cmov(Q *prePointProy, b uint) { + P.prePointAffine.cmov(&Q.prePointAffine, b) + fp.Cmov(&P.z2, &Q.z2, b) +} + +// FromPoint precomputes some coordinates of Q for mised addition. +func (P *prePointProy) FromPoint(Q *Point) { + fp.Add(&P.addYX, &Q.Y, &Q.X) // addYX = X + Y + fp.Sub(&P.subYX, &Q.Y, &Q.X) // subYX = Y - X + fp.Mul(&P.dt2, &Q.Ta, &Q.Tb) // T = ta*tb + fp.Mul(&P.dt2, &P.dt2, ¶mD) // D*T + fp.Add(&P.dt2, &P.dt2, &P.dt2) // dt2 = 2*D*T + fp.Add(&P.z2, &Q.Z, &Q.Z) // z2 = 2*Z +} diff --git a/ecc/goldilocks/internal/ted448/scalar.go b/ecc/goldilocks/internal/ted448/scalar.go new file mode 100644 index 00000000..5b6887ab --- /dev/null +++ b/ecc/goldilocks/internal/ted448/scalar.go @@ -0,0 +1,349 @@ +package ted448 + +import ( + "encoding/binary" + "fmt" + "io" + "math/bits" + + "github.com/cloudflare/circl/internal/conv" +) + +const ( + // ScalarSize is the size (in bytes) of scalars. + ScalarSize = 56 + //_N is the number of 64-bit words to store scalars. + _N = 7 // 448 / 64 +) + +// Scalar represents a positive integer stored in little-endian order. +type Scalar [ScalarSize]byte + +func (z Scalar) String() string { z.red(); return conv.BytesLe2Hex(z[:]) } + +type scalar64 [_N]uint64 + +func (z *scalar64) fromScalar(x *Scalar) { + z[0] = binary.LittleEndian.Uint64(x[0*8 : 1*8]) + z[1] = binary.LittleEndian.Uint64(x[1*8 : 2*8]) + z[2] = binary.LittleEndian.Uint64(x[2*8 : 3*8]) + z[3] = binary.LittleEndian.Uint64(x[3*8 : 4*8]) + z[4] = binary.LittleEndian.Uint64(x[4*8 : 5*8]) + z[5] = binary.LittleEndian.Uint64(x[5*8 : 6*8]) + z[6] = binary.LittleEndian.Uint64(x[6*8 : 7*8]) +} + +func (z *scalar64) toScalar(x *Scalar) { + binary.LittleEndian.PutUint64(x[0*8:1*8], z[0]) + binary.LittleEndian.PutUint64(x[1*8:2*8], z[1]) + binary.LittleEndian.PutUint64(x[2*8:3*8], z[2]) + binary.LittleEndian.PutUint64(x[3*8:4*8], z[3]) + binary.LittleEndian.PutUint64(x[4*8:5*8], z[4]) + binary.LittleEndian.PutUint64(x[5*8:6*8], z[5]) + binary.LittleEndian.PutUint64(x[6*8:7*8], z[6]) +} + +// isZero returns 1 if z=0. +func (z *scalar64) isZero() uint { + z.modOrder() + var z64 uint64 + for i := range z { + z64 |= z[i] + } + z32 := uint32(z64&0xFFFFFFFF) | (uint32(z64>>32) & 0xFFFFFFF) + return uint((uint64(z32) - 1) >> 63) +} + +// cmov moves x into z if b=1. +func (z *scalar64) cmov(x *scalar64, b uint64) { + m := -(b & 1) + for i := range z { + z[i] = (z[i] &^ m) | (x[i] & m) + } +} + +// mul calculates z = x * y. +func (z *scalar64) mul(x, y *scalar64) *scalar64 { + var t scalar64 + prod := (&[_N + 1]uint64{})[:] + mulWord(prod, x[:], y[_N-1]) + copy(t[:], prod[:_N]) + t.reduceOneWord(prod[_N]) + for i := _N - 2; i >= 0; i-- { + h := t.leftShift(0) + t.reduceOneWord(h) + mulWord(prod, x[:], y[i]) + c := add(t[:], t[:], prod[:_N]) + t.reduceOneWord(prod[_N] + c) + } + *z = t + return z +} + +// sqrn calculates z = x^(2^n). +func (z *scalar64) sqrn(x *scalar64, n uint) *scalar64 { + t := *x + for i := uint(0); i < n; i++ { + t.mul(&t, &t) + } + *z = t + return z +} + +// sqrnmul calculates z = x^(2^n) * y. +func (z *scalar64) sqrnmul(x *scalar64, n uint, y *scalar64) *scalar64 { + return z.mul(z.sqrn(x, n), y) +} + +// add calculates z = x + y. Assumes len(z) > max(len(x),len(y)). +func add(z, x, y []uint64) uint64 { + l, L, zz := len(x), len(y), y + if l > L { + l, L, zz = L, l, x + } + c := uint64(0) + for i := 0; i < l; i++ { + z[i], c = bits.Add64(x[i], y[i], c) + } + for i := l; i < L; i++ { + z[i], c = bits.Add64(zz[i], 0, c) + } + return c +} + +// sub calculates z = x - y. Assumes len(z) > max(len(x),len(y)). +func sub(z, x, y []uint64) uint64 { + l, L, zz := len(x), len(y), y + if l > L { + l, L, zz = L, l, x + } + c := uint64(0) + for i := 0; i < l; i++ { + z[i], c = bits.Sub64(x[i], y[i], c) + } + for i := l; i < L; i++ { + z[i], c = bits.Sub64(zz[i], 0, c) + } + return c +} + +// mulWord calculates z = x * y. Assumes len(z) >= len(x)+1. +func mulWord(z, x []uint64, y uint64) { + for i := range z { + z[i] = 0 + } + carry := uint64(0) + for i := range x { + hi, lo := bits.Mul64(x[i], y) + lo, cc := bits.Add64(lo, z[i], 0) + hi, _ = bits.Add64(hi, 0, cc) + z[i], cc = bits.Add64(lo, carry, 0) + carry, _ = bits.Add64(hi, 0, cc) + } + z[len(x)] = carry +} + +// leftShift shifts to the left the words of z returning the more significant word. +func (z *scalar64) leftShift(low uint64) uint64 { + high := z[_N-1] + for i := _N - 1; i > 0; i-- { + z[i] = z[i-1] + } + z[0] = low + return high +} + +// reduceOneWord calculates z = z + 2^448*x such that the result fits in a Scalar. +func (z *scalar64) reduceOneWord(x uint64) { + prod := (&scalar64{})[:] + mulWord(prod, residue448[:], x) + cc := add(z[:], z[:], prod) + mulWord(prod, residue448[:], cc) + add(z[:], z[:], prod) +} + +// Sub calculates z = x-y mod order. +func (z *scalar64) sub(x, y *scalar64) { + var t scalar64 + c := sub(z[:], x[:], y[:]) + sub(t[:], z[:], residue448[:]) + z.cmov(&t, c) + z.modOrder() +} + +// modOrder reduces z mod order. +func (z *scalar64) modOrder() { + var o64, x scalar64 + o64.fromScalar(&order) + // Performs: while (z >= order) { z = z-order } + // At most 8 (eight) iterations reduce 3 bits by subtracting. + for i := 0; i < 8; i++ { + c := sub(x[:], z[:], o64[:]) // (c || x) = z-order + z.cmov(&x, 1-c) // if c != 0 { z = x } + } +} + +func invertEndianness(v []byte) { + for i := 0; i < len(v)/2; i++ { + v[i], v[len(v)-1-i] = v[len(v)-1-i], v[i] + } +} + +// FromBytesBE stores z = x mod order, where x is a number stored in big-endian order. +func (z *Scalar) FromBytesBE(x []byte) { + revX := make([]byte, len(x)) + copy(revX, x) + invertEndianness(revX) + z.FromBytesLE(revX) +} + +// FromBytesLE stores z = x mod order, where x is a number stored in little-endian order. +func (z *Scalar) FromBytesLE(x []byte) { + n := len(x) + nCeil := (n + 7) >> 3 + *z = Scalar{} + if nCeil < _N { + copy(z[:], x) + return + } + copy(z[:], x[8*(nCeil-_N):]) + var z64 scalar64 + z64.fromScalar(z) + for i := nCeil - _N - 1; i >= 0; i-- { + low := binary.LittleEndian.Uint64(x[8*i:]) + high := z64.leftShift(low) + z64.reduceOneWord(high) + } + z64.modOrder() + z64.toScalar(z) +} + +// ToBytesBE returns the scalar byte representation in big-endian order. +func (z *Scalar) ToBytesBE() []byte { b := z.ToBytesLE(); invertEndianness(b); return b } + +// ToBytesLE returns the scalar byte representation in little-endian order. +func (z *Scalar) ToBytesLE() []byte { z.red(); k := *z; return k[:] } + +// MarshalBinary returns the scalar byte representation in big-endian order. +func (z *Scalar) MarshalBinary() ([]byte, error) { return z.ToBytesBE(), nil } + +// UnmarshalBinary recovers the scalar from its byte representation in big-endian order. +func (z *Scalar) UnmarshalBinary(data []byte) error { + if len(data) < ScalarSize { + return io.ErrShortBuffer + } + + var x Scalar + copy(x[:], data[:ScalarSize]) + invertEndianness(x[:]) + // Check that input is fully-reduced, i.e., 0 <= data < order. + if isLessThan(x[:], order[:]) == 0 { + return fmt.Errorf("ted448: unmarshaling a scalar not in range [0, order)") + } + *z = x + + return nil +} + +// isLessThan returns 1 if 0 <= x < y, and assumes that slices are of the +// same length and are interpreted in little-endian order. +func isLessThan(x, y []byte) int { + i := len(x) - 1 + for i > 0 && x[i] == y[i] { + i-- + } + xi := int(x[i]) + yi := int(y[i]) + return ((xi - yi) >> (bits.UintSize - 1)) & 1 +} + +// red reduces z mod order. +func (z *Scalar) red() { var t scalar64; t.fromScalar(z); t.modOrder(); t.toScalar(z) } + +// Neg calculates z = -x mod order. +func (z *Scalar) Neg(x *Scalar) { z.Sub(&order, x) } + +// Add calculates z = x+y mod order. +func (z *Scalar) Add(x, y *Scalar) { + var z64, x64, y64, t scalar64 + x64.fromScalar(x) + y64.fromScalar(y) + c := add(z64[:], x64[:], y64[:]) + add(t[:], z64[:], residue448[:]) + z64.cmov(&t, c) + z64.modOrder() + z64.toScalar(z) +} + +// Sub calculates z = x-y mod order. +func (z *Scalar) Sub(x, y *Scalar) { + var z64, x64, y64 scalar64 + x64.fromScalar(x) + y64.fromScalar(y) + z64.sub(&x64, &y64) + z64.toScalar(z) +} + +// Mul calculates z = x*y mod order. +func (z *Scalar) Mul(x, y *Scalar) { + var z64, x64, y64 scalar64 + x64.fromScalar(x) + y64.fromScalar(y) + z64.mul(&x64, &y64) + z64.modOrder() + z64.toScalar(z) +} + +// Inv calculates z = 1/x mod order. +func (z *Scalar) Inv(x *Scalar) { + var x64 scalar64 + x64.fromScalar(x) + + x10 := (&scalar64{}).mul(&x64, &x64) // x10 = 2 * 1 + x11 := (&scalar64{}).mul(x10, &x64) // x11 = 1 + x10 + x100 := (&scalar64{}).mul(x11, &x64) // x100 = 1 + x11 + x101 := (&scalar64{}).mul(x100, &x64) // x101 = 1 + x100 + x1001 := (&scalar64{}).mul(x100, x101) // x1001 = x100 + x101 + x1011 := (&scalar64{}).mul(x10, x1001) // x1011 = x10 + x1001 + x1101 := (&scalar64{}).mul(x10, x1011) // x1101 = x10 + x1011 + x1111 := (&scalar64{}).mul(x10, x1101) // x1111 = x10 + x1101 + x10001 := (&scalar64{}).mul(x10, x1111) // x10001 = x10 + x1111 + x10011 := (&scalar64{}).mul(x10, x10001) // x10011 = x10 + x10001 + x10101 := (&scalar64{}).mul(x10, x10011) // x10101 = x10 + x10011 + x10111 := (&scalar64{}).mul(x10, x10101) // x10111 = x10 + x10101 + x11001 := (&scalar64{}).mul(x10, x10111) // x11001 = x10 + x10111 + x11011 := (&scalar64{}).mul(x10, x11001) // x11011 = x10 + x11001 + x11101 := (&scalar64{}).mul(x10, x11011) // x11101 = x10 + x11011 + x11111 := (&scalar64{}).mul(x10, x11101) // x11111 = x10 + x11101 + x111110 := (&scalar64{}).mul(x11111, x11111) // x111110 = 2 * x11111 + x1111100 := (&scalar64{}).mul(x111110, x111110) // x1111100 = 2 * x111110 + i24, i41, i73, i129, t := &scalar64{}, &scalar64{}, &scalar64{}, + &scalar64{}, &scalar64{} + x222, i262, i279, i298, i312, i331, i343, i365, + i375, i396, i411, i431, i444, i464, i478, i498, iret := t, t, + t, t, t, t, t, t, t, t, t, t, t, t, t, t, t + + i24.sqrnmul(x1111100, 5, x1111100) // i24 = x1111100 << 5 + x1111100 + i41.sqrnmul(i41.sqrnmul(i24, 4, x111110), 11, i24) // i41 = (i24 << 4 + x111110) << 11 + i24 + i73.sqrnmul(i73.sqrnmul(i41, 4, x111110), 26, i41) // i73 = (i41 << 4 + x111110) << 26 + i41 + i129.sqrnmul(i73, 55, i73) // i129 = i73 << 55 + i73 + x222.mul(x222.sqrnmul(i129, 110, x11), i129) // x222 = i129 << 110 + x11 + i129 + i262.sqrn(i262.sqrnmul(i262.sqrnmul(x222, 6, x11111), 7, x11001), 6) // i262 = ((x222 << 6 + x11111) << 7 + x11001) << 6 + i279.sqrnmul(i279.sqrnmul(i279.mul(x10001, i262), 8, x11111), 6, x10011) // i279 = ((x10001 + i262) << 8 + x11111) << 6 + x10011 + i298.sqrn(i298.sqrnmul(i298.sqrnmul(i279, 5, x10001), 8, x10011), 4) // i298 = ((i279 << 5 + x10001) << 8 + x10011) << 4 + i312.sqrnmul(i312.sqrnmul(i312.mul(x1011, i298), 6, x11011), 5, x1001) // i312 = ((x1011 + i298) << 6 + x11011) << 5 + x1001 + i331.sqrn(i331.sqrnmul(i331.sqrnmul(i312, 6, x1101), 6, x11101), 5) // i331 = ((i312 << 6 + x1101) << 6 + x11101) << 5 + i343.sqrnmul(i343.sqrnmul(i343.mul(x10101, i331), 5, x10001), 4, x1011) // i343 = ((x10101 + i331) << 5 + x10001) << 4 + x1011 + i365.sqrn(i365.sqrnmul(i365.sqrnmul(i343, 5, x1001), 7, &x64), 8) // i365 = ((i343 << 5 + x1001) << 7 + 1) << 8 + i375.sqrnmul(i375.sqrnmul(i375.mul(x1011, i365), 6, x11001), 1, &x64) // i375 = 2*((x1011 + i365) << 6 + x11001) + 1 + i396.sqrn(i396.sqrnmul(i396.sqrnmul(i375, 9, x10011), 4, x1001), 6) // i396 = ((i375 << 9 + x10011) << 4 + x1001) << 6 + i411.sqrnmul(i411.sqrnmul(i411.mul(x10001, i396), 5, x10111), 7, x1011) // i411 = ((x10001 + i396) << 5 + x10111) << 7 + x1011 + i431.sqrn(i431.sqrnmul(i431.sqrnmul(i411, 7, x1111), 6, x10101), 5) // i431 = ((i411 << 7 + x1111) << 6 + x10101) << 5 + i444.sqrnmul(i444.sqrnmul(i444.mul(x1001, i431), 8, x11011), 2, x11) // i444 = ((x1001 + i431) << 8 + x11011) << 2 + x11 + i464.sqrn(i464.sqrnmul(i464.sqrnmul(i444, 5, x11), 7, x101), 6) // i464 = ((i444 << 5 + x11) << 7 + x101) << 6 + i478.sqrnmul(i478.sqrnmul(i478.mul(x1001, i464), 6, x10101), 5, x1101) // i478 = ((x1001 + i464) << 6 + x10101) << 5 + x1101 + i498.sqrn(i498.sqrnmul(i498.sqrnmul(i478, 3, x11), 9, x10001), 6) // i498 = ((i478 << 3 + x11) << 9 + x10001) << 6 + iret.sqrnmul(iret.mul(x1111, i498), 4, &x64) // z = (x1111 + i498) << 4 + 1 + iret.modOrder() + iret.toScalar(z) +} diff --git a/ecc/goldilocks/internal/ted448/scalar_test.go b/ecc/goldilocks/internal/ted448/scalar_test.go new file mode 100644 index 00000000..072476b4 --- /dev/null +++ b/ecc/goldilocks/internal/ted448/scalar_test.go @@ -0,0 +1,170 @@ +package ted448_test + +import ( + "bytes" + "crypto/rand" + "encoding" + "math/big" + "testing" + + "github.com/cloudflare/circl/ecc/goldilocks/internal/ted448" + "github.com/cloudflare/circl/internal/conv" + "github.com/cloudflare/circl/internal/test" + fp "github.com/cloudflare/circl/math/fp448" +) + +var bigOrder, _ = new(big.Int).SetString("3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3", 16) + +func rndScalar(t testing.TB) *ted448.Scalar { + var buf [ted448.ScalarSize]byte + _, err := rand.Read(buf[:]) + if err != nil { + t.Fatal(err) + } + var s ted448.Scalar + s.FromBytesLE(buf[:]) + return &s +} + +func toBig(s *ted448.Scalar) *big.Int { + return new(big.Int).SetBytes(s.ToBytesBE()) +} + +func TestReduceModOrder(t *testing.T) { + const max = 3*fp.Size - 1 + var b [max]byte + _, _ = rand.Read(b[:]) + var z ted448.Scalar + for i := 0; i < max; i++ { + x := b[0:i] + bigX := conv.BytesLe2BigInt(x) + + z.FromBytesLE(x) + got := toBig(&z) + got.Mod(got, bigOrder) + + want := bigX.Mod(bigX, bigOrder) + + if got.Cmp(want) != 0 { + test.ReportError(t, got, want, x, i) + } + } +} + +func testOp(t *testing.T, + f func(z, x, y *ted448.Scalar), + g func(z, x, y *big.Int), +) { + t.Helper() + const testTimes = 1 << 8 + want := new(big.Int) + var z ted448.Scalar + for i := 0; i < testTimes; i++ { + x := rndScalar(t) + y := rndScalar(t) + bigX := toBig(x) + bigY := toBig(y) + + f(&z, x, y) + got := toBig(&z) + + g(want, bigX, bigY) + want.Mod(want, bigOrder) + if got.Cmp(want) != 0 { + test.ReportError(t, got.Text(16), want.Text(16), x, y) + } + } +} + +type canMarshal interface { + encoding.BinaryMarshaler + encoding.BinaryUnmarshaler +} + +func testMarshal(t *testing.T, x, y canMarshal, name string) { + t.Helper() + + wantBytes, err := x.MarshalBinary() + test.CheckNoErr(t, err, "error on marshaling "+name) + + err = y.UnmarshalBinary(wantBytes) + test.CheckNoErr(t, err, "error on unmarshaling "+name) + + gotBytes, err := x.MarshalBinary() + test.CheckNoErr(t, err, "error on marshaling "+name) + + if !bytes.Equal(gotBytes, wantBytes) { + test.ReportError(t, gotBytes, wantBytes) + } + + b, _ := x.MarshalBinary() + err = y.UnmarshalBinary(b[:0]) + test.CheckIsErr(t, err, "should trigger unmarshal error") + + order := bigOrder.Bytes() + err = y.UnmarshalBinary(order) + test.CheckIsErr(t, err, "should trigger unmarshal error") + + order[0] += 1 + err = y.UnmarshalBinary(order[:]) + test.CheckIsErr(t, err, "should trigger unmarshal error") + + order[0] -= 2 + err = y.UnmarshalBinary(order[:]) + test.CheckNoErr(t, err, "should not trigger unmarshal error") +} + +func testFromBytes(t *testing.T) { + const testTimes = 1 << 8 + var got, want ted448.Scalar + for i := 0; i < testTimes; i++ { + x := rndScalar(t) + + got.FromBytesLE(x.ToBytesLE()) + want.FromBytesBE(x.ToBytesBE()) + + if got != want { + test.ReportError(t, got, want, x) + } + + s := x.String() + got1, ok := new(big.Int).SetString(s, 0) + want1 := toBig(x) + + if !ok || got1.Cmp(want1) != 0 { + test.ReportError(t, got1, want1, x) + } + } +} + +func TestScalar(t *testing.T) { + t.Run("Add", func(t *testing.T) { + testOp(t, + func(z, x, y *ted448.Scalar) { z.Add(x, y) }, + func(z, x, y *big.Int) { z.Add(x, y) }) + }) + t.Run("Sub", func(t *testing.T) { + testOp(t, + func(z, x, y *ted448.Scalar) { z.Sub(x, y) }, + func(z, x, y *big.Int) { z.Sub(x, y) }) + }) + t.Run("Mul", func(t *testing.T) { + testOp(t, + func(z, x, y *ted448.Scalar) { z.Mul(x, y) }, + func(z, x, y *big.Int) { z.Mul(x, y) }) + }) + t.Run("Neg", func(t *testing.T) { + testOp(t, + func(z, x, y *ted448.Scalar) { z.Neg(x) }, + func(z, x, y *big.Int) { z.Neg(x) }) + }) + t.Run("Inv", func(t *testing.T) { + testOp(t, + func(z, x, y *ted448.Scalar) { z.Inv(x) }, + func(z, x, y *big.Int) { z.ModInverse(x, bigOrder) }) + }) + t.Run("Marshal", func(t *testing.T) { + testMarshal(t, rndScalar(t), new(ted448.Scalar), "scalar") + }) + t.Run("FromBytes", testFromBytes) +} diff --git a/ecc/goldilocks/twistTables.go b/ecc/goldilocks/internal/ted448/tables.go similarity index 98% rename from ecc/goldilocks/twistTables.go rename to ecc/goldilocks/internal/ted448/tables.go index ed432e02..d7e85888 100644 --- a/ecc/goldilocks/twistTables.go +++ b/ecc/goldilocks/internal/ted448/tables.go @@ -1,8 +1,8 @@ -package goldilocks +package ted448 import fp "github.com/cloudflare/circl/math/fp448" -var tabFixMult = [fxV][fx2w1]preTwistPointAffine{ +var tabFixMult = [fxV][fx2w1]prePointAffine{ { { addYX: fp.Elt{0x65, 0x4a, 0xdd, 0xdf, 0xb4, 0x79, 0x60, 0xc8, 0xa1, 0x70, 0xb4, 0x3a, 0x1e, 0x0c, 0x9b, 0x19, 0xe5, 0x48, 0x3f, 0xd7, 0x44, 0x18, 0x18, 0x14, 0x14, 0x27, 0x45, 0xd0, 0x2b, 0x24, 0xd5, 0x93, 0xc3, 0x74, 0x4c, 0x50, 0x70, 0x43, 0x26, 0x05, 0x08, 0x24, 0xca, 0x78, 0x30, 0xc1, 0x06, 0x8d, 0xd4, 0x86, 0x42, 0xf0, 0x14, 0xde, 0x08, 0x05}, @@ -49,166 +49,165 @@ var tabFixMult = [fxV][fx2w1]preTwistPointAffine{ }, } -// tabVerif contains the odd multiples of P. The entry T[i] = (2i+1)P, where -// P = phi(G) and G is the generator of the Goldilocks curve, and phi is a -// 4-degree isogeny. -var tabVerif = [1 << (omegaFix - 2)]preTwistPointAffine{ - { /* 1P*/ +// tabVerif contains the odd multiples of G. The entry T[i] = (2i+1)G, where +// G is the generator of the ted448 curve. +var tabVerif = [1 << (omegaFix - 2)]prePointAffine{ + { /* 1G*/ addYX: fp.Elt{0x65, 0x4a, 0xdd, 0xdf, 0xb4, 0x79, 0x60, 0xc8, 0xa1, 0x70, 0xb4, 0x3a, 0x1e, 0x0c, 0x9b, 0x19, 0xe5, 0x48, 0x3f, 0xd7, 0x44, 0x18, 0x18, 0x14, 0x14, 0x27, 0x45, 0xd0, 0x2b, 0x24, 0xd5, 0x93, 0xc3, 0x74, 0x4c, 0x50, 0x70, 0x43, 0x26, 0x05, 0x08, 0x24, 0xca, 0x78, 0x30, 0xc1, 0x06, 0x8d, 0xd4, 0x86, 0x42, 0xf0, 0x14, 0xde, 0x08, 0x05}, subYX: fp.Elt{0x64, 0x4a, 0xdd, 0xdf, 0xb4, 0x79, 0x60, 0xc8, 0xa1, 0x70, 0xb4, 0x3a, 0x1e, 0x0c, 0x9b, 0x19, 0xe5, 0x48, 0x3f, 0xd7, 0x44, 0x18, 0x18, 0x14, 0x14, 0x27, 0x45, 0xd0, 0x2d, 0x24, 0xd5, 0x93, 0xc3, 0x74, 0x4c, 0x50, 0x70, 0x43, 0x26, 0x05, 0x08, 0x24, 0xca, 0x78, 0x30, 0xc1, 0x06, 0x8d, 0xd4, 0x86, 0x42, 0xf0, 0x14, 0xde, 0x08, 0x05}, dt2: fp.Elt{0x1a, 0x33, 0xea, 0x64, 0x45, 0x1c, 0xdf, 0x17, 0x1d, 0x16, 0x34, 0x28, 0xd6, 0x61, 0x19, 0x67, 0x79, 0xb4, 0x13, 0xcf, 0x3e, 0x7c, 0x0e, 0x72, 0xda, 0xf1, 0x5f, 0xda, 0xe6, 0xcf, 0x42, 0xd3, 0xb6, 0x17, 0xc2, 0x68, 0x13, 0x2d, 0xd9, 0x60, 0x3e, 0xae, 0xf0, 0x5b, 0x96, 0xf0, 0xcd, 0xaf, 0xea, 0xb7, 0x0d, 0x59, 0x16, 0xa7, 0xff, 0x55}, }, - { /* 3P*/ + { /* 3G*/ addYX: fp.Elt{0xd1, 0xe9, 0xa8, 0x33, 0x20, 0x76, 0x18, 0x08, 0x45, 0x2a, 0xc9, 0x67, 0x2a, 0xc3, 0x15, 0x24, 0xf9, 0x74, 0x21, 0x30, 0x99, 0x59, 0x8b, 0xb2, 0xf0, 0xa4, 0x07, 0xe2, 0x6a, 0x36, 0x8d, 0xd9, 0xd2, 0x4a, 0x7f, 0x73, 0x50, 0x39, 0x3d, 0xaa, 0xa7, 0x51, 0x73, 0x0d, 0x2b, 0x8b, 0x96, 0x47, 0xac, 0x3c, 0x5d, 0xaa, 0x39, 0x9c, 0xcf, 0xd5}, subYX: fp.Elt{0x6b, 0x11, 0x5d, 0x1a, 0xf9, 0x41, 0x9d, 0xc5, 0x30, 0x3e, 0xad, 0x25, 0x2c, 0x04, 0x45, 0xea, 0xcc, 0x67, 0x07, 0x85, 0xe9, 0xda, 0x0e, 0xb5, 0x40, 0xb7, 0x32, 0xb4, 0x49, 0xdd, 0xff, 0xaa, 0xfc, 0xbb, 0x19, 0xca, 0x8b, 0x79, 0x2b, 0x8f, 0x8d, 0x00, 0x33, 0xc2, 0xad, 0xe9, 0xd3, 0x12, 0xa8, 0xaa, 0x87, 0x62, 0xad, 0x2d, 0xff, 0xa4}, dt2: fp.Elt{0xb0, 0xaf, 0x3b, 0xea, 0xf0, 0x42, 0x0b, 0x5e, 0x88, 0xd3, 0x98, 0x08, 0x87, 0x59, 0x72, 0x0a, 0xc2, 0xdf, 0xcb, 0x7f, 0x59, 0xb5, 0x4c, 0x63, 0x68, 0xe8, 0x41, 0x38, 0x67, 0x4f, 0xe9, 0xc6, 0xb2, 0x6b, 0x08, 0xa7, 0xf7, 0x0e, 0xcd, 0xea, 0xca, 0x3d, 0xaf, 0x8e, 0xda, 0x4b, 0x2e, 0xd2, 0x88, 0x64, 0x8d, 0xc5, 0x5f, 0x76, 0x0f, 0x3d}, }, - { /* 5P*/ + { /* 5G*/ addYX: fp.Elt{0xe5, 0x65, 0xc9, 0xe2, 0x75, 0xf0, 0x7d, 0x1a, 0xba, 0xa4, 0x40, 0x4b, 0x93, 0x12, 0xa2, 0x80, 0x95, 0x0d, 0x03, 0x93, 0xe8, 0xa5, 0x4d, 0xe2, 0x3d, 0x81, 0xf5, 0xce, 0xd4, 0x2d, 0x25, 0x59, 0x16, 0x5c, 0xe7, 0xda, 0xc7, 0x45, 0xd2, 0x7e, 0x2c, 0x38, 0xd4, 0x37, 0x64, 0xb2, 0xc2, 0x28, 0xc5, 0x72, 0x16, 0x32, 0x45, 0x36, 0x6f, 0x9f}, subYX: fp.Elt{0x09, 0xf4, 0x7e, 0xbd, 0x89, 0xdb, 0x19, 0x58, 0xe1, 0x08, 0x00, 0x8a, 0xf4, 0x5f, 0x2a, 0x32, 0x40, 0xf0, 0x2c, 0x3f, 0x5d, 0xe4, 0xfc, 0x89, 0x11, 0x24, 0xb4, 0x2f, 0x97, 0xad, 0xac, 0x8f, 0x19, 0xab, 0xfa, 0x12, 0xe5, 0xf9, 0x50, 0x4e, 0x50, 0x6f, 0x32, 0x30, 0x88, 0xa6, 0xe5, 0x48, 0x28, 0xa2, 0x1b, 0x9f, 0xcd, 0xe2, 0x43, 0x38}, dt2: fp.Elt{0xa9, 0xcc, 0x53, 0x39, 0x86, 0x02, 0x60, 0x75, 0x34, 0x99, 0x57, 0xbd, 0xfc, 0x5a, 0x8e, 0xce, 0x5e, 0x98, 0x22, 0xd0, 0xa5, 0x24, 0xff, 0x90, 0x28, 0x9f, 0x58, 0xf3, 0x39, 0xe9, 0xba, 0x36, 0x23, 0xfb, 0x7f, 0x41, 0xcc, 0x2b, 0x5a, 0x25, 0x3f, 0x4c, 0x2a, 0xf1, 0x52, 0x6f, 0x2f, 0x07, 0xe3, 0x88, 0x81, 0x77, 0xdd, 0x7c, 0x88, 0x82}, }, - { /* 7P*/ + { /* 7G*/ addYX: fp.Elt{0xf7, 0xee, 0x88, 0xfd, 0x3a, 0xbf, 0x7e, 0x28, 0x39, 0x23, 0x79, 0xe6, 0x5c, 0x56, 0xcb, 0xb5, 0x48, 0x6a, 0x80, 0x6d, 0x37, 0x60, 0x6c, 0x10, 0x35, 0x49, 0x4b, 0x46, 0x60, 0xd4, 0x79, 0xd4, 0x53, 0xd3, 0x67, 0x88, 0xd0, 0x41, 0xd5, 0x43, 0x85, 0xc8, 0x71, 0xe3, 0x1c, 0xb6, 0xda, 0x22, 0x64, 0x8f, 0x80, 0xac, 0xad, 0x7d, 0xd5, 0x82}, subYX: fp.Elt{0x92, 0x40, 0xc1, 0x83, 0x21, 0x9b, 0xd5, 0x7d, 0x3f, 0x29, 0xb6, 0x26, 0xef, 0x12, 0xb9, 0x27, 0x39, 0x42, 0x37, 0x97, 0x09, 0x9a, 0x08, 0xe1, 0x68, 0xb6, 0x7a, 0x3f, 0x9f, 0x45, 0xf8, 0x37, 0x19, 0x83, 0x97, 0xe6, 0x73, 0x30, 0x32, 0x35, 0xcf, 0xae, 0x5c, 0x12, 0x68, 0xdf, 0x6e, 0x2b, 0xde, 0x83, 0xa0, 0x44, 0x74, 0x2e, 0x4a, 0xe9}, dt2: fp.Elt{0xcb, 0x22, 0x0a, 0xda, 0x6b, 0xc1, 0x8a, 0x29, 0xa1, 0xac, 0x8b, 0x5b, 0x8b, 0x32, 0x20, 0xf2, 0x21, 0xae, 0x0c, 0x43, 0xc4, 0xd7, 0x19, 0x37, 0x3d, 0x79, 0x25, 0x98, 0x6c, 0x9c, 0x22, 0x31, 0x2a, 0x55, 0x9f, 0xda, 0x5e, 0xa8, 0x13, 0xdb, 0x8e, 0x2e, 0x16, 0x39, 0xf4, 0x91, 0x6f, 0xec, 0x71, 0x71, 0xc9, 0x10, 0xf2, 0xa4, 0x8f, 0x11}, }, - { /* 9P*/ + { /* 9G*/ addYX: fp.Elt{0x85, 0xdd, 0x37, 0x62, 0x74, 0x8e, 0x33, 0x5b, 0x25, 0x12, 0x1b, 0xe7, 0xdf, 0x47, 0xe5, 0x12, 0xfd, 0x3a, 0x3a, 0xf5, 0x5d, 0x4c, 0xa2, 0x29, 0x3c, 0x5c, 0x2f, 0xee, 0x18, 0x19, 0x0a, 0x2b, 0xef, 0x67, 0x50, 0x7a, 0x0d, 0x29, 0xae, 0x55, 0x82, 0xcd, 0xd6, 0x41, 0x90, 0xb4, 0x13, 0x31, 0x5d, 0x11, 0xb8, 0xaa, 0x12, 0x86, 0x08, 0xac}, subYX: fp.Elt{0xcc, 0x37, 0x8d, 0x83, 0x5f, 0xfd, 0xde, 0xd5, 0xf7, 0xf1, 0xae, 0x0a, 0xa7, 0x0b, 0xeb, 0x6d, 0x19, 0x8a, 0xb6, 0x1a, 0x59, 0xd8, 0xff, 0x3c, 0xbc, 0xbc, 0xef, 0x9c, 0xda, 0x7b, 0x75, 0x12, 0xaf, 0x80, 0x8f, 0x2c, 0x3c, 0xaa, 0x0b, 0x17, 0x86, 0x36, 0x78, 0x18, 0xc8, 0x8a, 0xf6, 0xb8, 0x2c, 0x2f, 0x57, 0x2c, 0x62, 0x57, 0xf6, 0x90}, dt2: fp.Elt{0x83, 0xbc, 0xa2, 0x07, 0xa5, 0x38, 0x96, 0xea, 0xfe, 0x11, 0x46, 0x1d, 0x3b, 0xcd, 0x42, 0xc5, 0xee, 0x67, 0x04, 0x72, 0x08, 0xd8, 0xd9, 0x96, 0x07, 0xf7, 0xac, 0xc3, 0x64, 0xf1, 0x98, 0x2c, 0x55, 0xd7, 0x7d, 0xc8, 0x6c, 0xbd, 0x2c, 0xff, 0x15, 0xd6, 0x6e, 0xb8, 0x17, 0x8e, 0xa8, 0x27, 0x66, 0xb1, 0x73, 0x79, 0x96, 0xff, 0x29, 0x10}, }, - { /* 11P*/ + { /* 11G*/ addYX: fp.Elt{0x76, 0xcb, 0x9b, 0x0c, 0x5b, 0xfe, 0xe1, 0x2a, 0xdd, 0x6f, 0x6c, 0xdd, 0x6f, 0xb4, 0xc0, 0xc2, 0x1b, 0x4b, 0x38, 0xe8, 0x66, 0x8c, 0x1e, 0x31, 0x63, 0xb9, 0x94, 0xcd, 0xc3, 0x8c, 0x44, 0x25, 0x7b, 0xd5, 0x39, 0x80, 0xfc, 0x01, 0xaa, 0xf7, 0x2a, 0x61, 0x8a, 0x25, 0xd2, 0x5f, 0xc5, 0x66, 0x38, 0xa4, 0x17, 0xcf, 0x3e, 0x11, 0x0f, 0xa3}, subYX: fp.Elt{0xe0, 0xb6, 0xd1, 0x9c, 0x71, 0x49, 0x2e, 0x7b, 0xde, 0x00, 0xda, 0x6b, 0xf1, 0xec, 0xe6, 0x7a, 0x15, 0x38, 0x71, 0xe9, 0x7b, 0xdb, 0xf8, 0x98, 0xc0, 0x91, 0x2e, 0x53, 0xee, 0x92, 0x87, 0x25, 0xc9, 0xb0, 0xbb, 0x33, 0x15, 0x46, 0x7f, 0xfd, 0x4f, 0x8b, 0x77, 0x05, 0x96, 0xb6, 0xe2, 0x08, 0xdb, 0x0d, 0x09, 0xee, 0x5b, 0xd1, 0x2a, 0x63}, dt2: fp.Elt{0x8f, 0x7b, 0x57, 0x8c, 0xbf, 0x06, 0x0d, 0x43, 0x21, 0x92, 0x94, 0x2d, 0x6a, 0x38, 0x07, 0x0f, 0xa0, 0xf1, 0xe3, 0xd8, 0x2a, 0xbf, 0x46, 0xc6, 0x9e, 0x1f, 0x8f, 0x2b, 0x46, 0x84, 0x0b, 0x74, 0xed, 0xff, 0xf8, 0xa5, 0x94, 0xae, 0xf1, 0x67, 0xb1, 0x9b, 0xdd, 0x4a, 0xd0, 0xdb, 0xc2, 0xb5, 0x58, 0x49, 0x0c, 0xa9, 0x1d, 0x7d, 0xa9, 0xd3}, }, - { /* 13P*/ + { /* 13G*/ addYX: fp.Elt{0x73, 0x84, 0x2e, 0x31, 0x1f, 0xdc, 0xed, 0x9f, 0x74, 0xfa, 0xe0, 0x35, 0xb1, 0x85, 0x6a, 0x8d, 0x86, 0xd0, 0xff, 0xd6, 0x08, 0x43, 0x73, 0x1a, 0xd5, 0xf8, 0x43, 0xd4, 0xb3, 0xe5, 0x3f, 0xa8, 0x84, 0x17, 0x59, 0x65, 0x4e, 0xe6, 0xee, 0x54, 0x9c, 0xda, 0x5e, 0x7e, 0x98, 0x29, 0x6d, 0x73, 0x34, 0x1f, 0x99, 0x80, 0x54, 0x54, 0x81, 0x0b}, subYX: fp.Elt{0xb1, 0xe5, 0xbb, 0x80, 0x22, 0x9c, 0x81, 0x6d, 0xaf, 0x27, 0x65, 0x6f, 0x7e, 0x9c, 0xb6, 0x8d, 0x35, 0x5c, 0x2e, 0x20, 0x48, 0x7a, 0x28, 0xf0, 0x97, 0xfe, 0xb7, 0x71, 0xce, 0xd6, 0xad, 0x3a, 0x81, 0xf6, 0x74, 0x5e, 0xf3, 0xfd, 0x1b, 0xd4, 0x1e, 0x7c, 0xc2, 0xb7, 0xc8, 0xa6, 0xc9, 0x89, 0x03, 0x47, 0xec, 0x24, 0xd6, 0x0e, 0xec, 0x9c}, dt2: fp.Elt{0x91, 0x0a, 0x43, 0x34, 0x20, 0xc2, 0x64, 0xf7, 0x4e, 0x48, 0xc8, 0xd2, 0x95, 0x83, 0xd1, 0xa4, 0xfb, 0x4e, 0x41, 0x3b, 0x0d, 0xd5, 0x07, 0xd9, 0xf1, 0x13, 0x16, 0x78, 0x54, 0x57, 0xd0, 0xf1, 0x4f, 0x20, 0xac, 0xcf, 0x9c, 0x3b, 0x33, 0x0b, 0x99, 0x54, 0xc3, 0x7f, 0x3e, 0x57, 0x26, 0x86, 0xd5, 0xa5, 0x2b, 0x8d, 0xe3, 0x19, 0x36, 0xf7}, }, - { /* 15P*/ + { /* 15G*/ addYX: fp.Elt{0x23, 0x69, 0x47, 0x14, 0xf9, 0x9a, 0x50, 0xff, 0x64, 0xd1, 0x50, 0x35, 0xc3, 0x11, 0xd3, 0x19, 0xcf, 0x87, 0xda, 0x30, 0x0b, 0x50, 0xda, 0xc0, 0xe0, 0x25, 0x00, 0xe5, 0x68, 0x93, 0x04, 0xc2, 0xaf, 0xbd, 0x2f, 0x36, 0x5f, 0x47, 0x96, 0x10, 0xa8, 0xbd, 0xe4, 0x88, 0xac, 0x80, 0x52, 0x61, 0x73, 0xe9, 0x63, 0xdd, 0x99, 0xad, 0x20, 0x5b}, subYX: fp.Elt{0x1b, 0x5e, 0xa2, 0x2a, 0x25, 0x0f, 0x86, 0xc0, 0xb1, 0x2e, 0x0c, 0x13, 0x40, 0x8d, 0xf0, 0xe6, 0x00, 0x55, 0x08, 0xc5, 0x7d, 0xf4, 0xc9, 0x31, 0x25, 0x3a, 0x99, 0x69, 0xdd, 0x67, 0x63, 0x9a, 0xd6, 0x89, 0x2e, 0xa1, 0x19, 0xca, 0x2c, 0xd9, 0x59, 0x5f, 0x5d, 0xc3, 0x6e, 0x62, 0x36, 0x12, 0x59, 0x15, 0xe1, 0xdc, 0xa4, 0xad, 0xc9, 0xd0}, dt2: fp.Elt{0xbc, 0xea, 0xfc, 0xaf, 0x66, 0x23, 0xb7, 0x39, 0x6b, 0x2a, 0x96, 0xa8, 0x54, 0x43, 0xe9, 0xaa, 0x32, 0x40, 0x63, 0x92, 0x5e, 0xdf, 0x35, 0xc2, 0x9f, 0x24, 0x0c, 0xed, 0xfc, 0xde, 0x73, 0x8f, 0xa7, 0xd5, 0xa3, 0x2b, 0x18, 0x1f, 0xb0, 0xf8, 0xeb, 0x55, 0xd9, 0xc3, 0xfd, 0x28, 0x7c, 0x4f, 0xce, 0x0d, 0xf7, 0xae, 0xc2, 0x83, 0xc3, 0x78}, }, - { /* 17P*/ + { /* 17G*/ addYX: fp.Elt{0x71, 0xe6, 0x60, 0x93, 0x37, 0xdb, 0x01, 0xa5, 0x4c, 0xba, 0xe8, 0x8e, 0xd5, 0xf9, 0xd3, 0x98, 0xe5, 0xeb, 0xab, 0x3a, 0x15, 0x8b, 0x35, 0x60, 0xbe, 0xe5, 0x9c, 0x2d, 0x10, 0x9b, 0x2e, 0xcf, 0x65, 0x64, 0xea, 0x8f, 0x72, 0xce, 0xf5, 0x18, 0xe5, 0xe2, 0xf0, 0x0e, 0xae, 0x04, 0xec, 0xa0, 0x20, 0x65, 0x63, 0x07, 0xb1, 0x9f, 0x03, 0x97}, subYX: fp.Elt{0x9e, 0x41, 0x64, 0x30, 0x95, 0x7f, 0x3a, 0x89, 0x7b, 0x0a, 0x79, 0x59, 0x23, 0x9a, 0x3b, 0xfe, 0xa4, 0x13, 0x08, 0xb2, 0x2e, 0x04, 0x50, 0x10, 0x30, 0xcd, 0x2e, 0xa4, 0x91, 0x71, 0x50, 0x36, 0x4a, 0x02, 0xf4, 0x8d, 0xa3, 0x36, 0x1b, 0xf4, 0x52, 0xba, 0x15, 0x04, 0x8b, 0x80, 0x25, 0xd9, 0xae, 0x67, 0x20, 0xd9, 0x88, 0x8f, 0x97, 0xa6}, dt2: fp.Elt{0xb5, 0xe7, 0x46, 0xbd, 0x55, 0x23, 0xa0, 0x68, 0xc0, 0x12, 0xd9, 0xf1, 0x0a, 0x75, 0xe2, 0xda, 0xf4, 0x6b, 0xca, 0x14, 0xe4, 0x9f, 0x0f, 0xb5, 0x3c, 0xa6, 0xa5, 0xa2, 0x63, 0x94, 0xd1, 0x1c, 0x39, 0x58, 0x57, 0x02, 0x27, 0x98, 0xb6, 0x47, 0xc6, 0x61, 0x4b, 0x5c, 0xab, 0x6f, 0x2d, 0xab, 0xe3, 0xc1, 0x69, 0xf9, 0x12, 0xb0, 0xc8, 0xd5}, }, - { /* 19P*/ + { /* 19G*/ addYX: fp.Elt{0x19, 0x7d, 0xd5, 0xac, 0x79, 0xa2, 0x82, 0x9b, 0x28, 0x31, 0x22, 0xc0, 0x73, 0x02, 0x76, 0x17, 0x10, 0x70, 0x79, 0x57, 0xc9, 0x84, 0x62, 0x8e, 0x04, 0x04, 0x61, 0x67, 0x08, 0x48, 0xb4, 0x4b, 0xde, 0x53, 0x8c, 0xff, 0x36, 0x1b, 0x62, 0x86, 0x5d, 0xe1, 0x9b, 0xb1, 0xe5, 0xe8, 0x44, 0x64, 0xa1, 0x68, 0x3f, 0xa8, 0x45, 0x52, 0x91, 0xed}, subYX: fp.Elt{0x42, 0x1a, 0x36, 0x1f, 0x90, 0x15, 0x24, 0x8d, 0x24, 0x80, 0xe6, 0xfe, 0x1e, 0xf0, 0xad, 0xaf, 0x6a, 0x93, 0xf0, 0xa6, 0x0d, 0x5d, 0xea, 0xf6, 0x62, 0x96, 0x7a, 0x05, 0x76, 0x85, 0x74, 0x32, 0xc7, 0xc8, 0x64, 0x53, 0x62, 0xe7, 0x54, 0x84, 0xe0, 0x40, 0x66, 0x19, 0x70, 0x40, 0x95, 0x35, 0x68, 0x64, 0x43, 0xcd, 0xba, 0x29, 0x32, 0xa8}, dt2: fp.Elt{0x3e, 0xf6, 0xd6, 0xe4, 0x99, 0xeb, 0x20, 0x66, 0x08, 0x2e, 0x26, 0x64, 0xd7, 0x76, 0xf3, 0xb4, 0xc5, 0xa4, 0x35, 0x92, 0xd2, 0x99, 0x70, 0x5a, 0x1a, 0xe9, 0xe9, 0x3d, 0x3b, 0xe1, 0xcd, 0x0e, 0xee, 0x24, 0x13, 0x03, 0x22, 0xd6, 0xd6, 0x72, 0x08, 0x2b, 0xde, 0xfd, 0x93, 0xed, 0x0c, 0x7f, 0x5e, 0x31, 0x22, 0x4d, 0x80, 0x78, 0xc0, 0x48}, }, - { /* 21P*/ + { /* 21G*/ addYX: fp.Elt{0x8f, 0x72, 0xd2, 0x9e, 0xc4, 0xcd, 0x2c, 0xbf, 0xa8, 0xd3, 0x24, 0x62, 0x28, 0xee, 0x39, 0x0a, 0x19, 0x3a, 0x58, 0xff, 0x21, 0x2e, 0x69, 0x6c, 0x6e, 0x18, 0xd0, 0xcd, 0x61, 0xc1, 0x18, 0x02, 0x5a, 0xe9, 0xe3, 0xef, 0x1f, 0x8e, 0x10, 0xe8, 0x90, 0x2b, 0x48, 0xcd, 0xee, 0x38, 0xbd, 0x3a, 0xca, 0xbc, 0x2d, 0xe2, 0x3a, 0x03, 0x71, 0x02}, subYX: fp.Elt{0xf8, 0xa4, 0x32, 0x26, 0x66, 0xaf, 0x3b, 0x53, 0xe7, 0xb0, 0x91, 0x92, 0xf5, 0x3c, 0x74, 0xce, 0xf2, 0xdd, 0x68, 0xa9, 0xf4, 0xcd, 0x5f, 0x60, 0xab, 0x71, 0xdf, 0xcd, 0x5c, 0x5d, 0x51, 0x72, 0x3a, 0x96, 0xea, 0xd6, 0xde, 0x54, 0x8e, 0x55, 0x4c, 0x08, 0x4c, 0x60, 0xdd, 0x34, 0xa9, 0x6f, 0xf3, 0x04, 0x02, 0xa8, 0xa6, 0x4e, 0x4d, 0x62}, dt2: fp.Elt{0x76, 0x4a, 0xae, 0x38, 0x62, 0x69, 0x72, 0xdc, 0xe8, 0x43, 0xbe, 0x1d, 0x61, 0xde, 0x31, 0xc3, 0x42, 0x8f, 0x33, 0x9d, 0xca, 0xc7, 0x9c, 0xec, 0x6a, 0xe2, 0xaa, 0x01, 0x49, 0x78, 0x8d, 0x72, 0x4f, 0x38, 0xea, 0x52, 0xc2, 0xd3, 0xc9, 0x39, 0x71, 0xba, 0xb9, 0x09, 0x9b, 0xa3, 0x7f, 0x45, 0x43, 0x65, 0x36, 0x29, 0xca, 0xe7, 0x5c, 0x5f}, }, - { /* 23P*/ + { /* 23G*/ addYX: fp.Elt{0x89, 0x42, 0x35, 0x48, 0x6d, 0x74, 0xe5, 0x1f, 0xc3, 0xdd, 0x28, 0x5b, 0x84, 0x41, 0x33, 0x9f, 0x42, 0xf3, 0x1d, 0x5d, 0x15, 0x6d, 0x76, 0x33, 0x36, 0xaf, 0xe9, 0xdd, 0xfa, 0x63, 0x4f, 0x7a, 0x9c, 0xeb, 0x1c, 0x4f, 0x34, 0x65, 0x07, 0x54, 0xbb, 0x4c, 0x8b, 0x62, 0x9d, 0xd0, 0x06, 0x99, 0xb3, 0xe9, 0xda, 0x85, 0x19, 0xb0, 0x3d, 0x3c}, subYX: fp.Elt{0xbb, 0x99, 0xf6, 0xbf, 0xaf, 0x2c, 0x22, 0x0d, 0x7a, 0xaa, 0x98, 0x6f, 0x01, 0x82, 0x99, 0xcf, 0x88, 0xbd, 0x0e, 0x3a, 0x89, 0xe0, 0x9c, 0x8c, 0x17, 0x20, 0xc4, 0xe0, 0xcf, 0x43, 0x7a, 0xef, 0x0d, 0x9f, 0x87, 0xd4, 0xfb, 0xf2, 0x96, 0xb8, 0x03, 0xe8, 0xcb, 0x5c, 0xec, 0x65, 0x5f, 0x49, 0xa4, 0x7c, 0x85, 0xb4, 0xf6, 0xc7, 0xdb, 0xa3}, dt2: fp.Elt{0x11, 0xf3, 0x32, 0xa3, 0xa7, 0xb2, 0x7d, 0x51, 0x82, 0x44, 0xeb, 0xa2, 0x7d, 0x72, 0xcb, 0xc6, 0xf6, 0xc7, 0xb2, 0x38, 0x0e, 0x0f, 0x4f, 0x29, 0x00, 0xe4, 0x5b, 0x94, 0x46, 0x86, 0x66, 0xa1, 0x83, 0xb3, 0xeb, 0x15, 0xb6, 0x31, 0x50, 0x28, 0xeb, 0xed, 0x0d, 0x32, 0x39, 0xe9, 0x23, 0x81, 0x99, 0x3e, 0xff, 0x17, 0x4c, 0x11, 0x43, 0xd1}, }, - { /* 25P*/ + { /* 25G*/ addYX: fp.Elt{0xce, 0xe7, 0xf8, 0x94, 0x8f, 0x96, 0xf8, 0x96, 0xe6, 0x72, 0x20, 0x44, 0x2c, 0xa7, 0xfc, 0xba, 0xc8, 0xe1, 0xbb, 0xc9, 0x16, 0x85, 0xcd, 0x0b, 0xe5, 0xb5, 0x5a, 0x7f, 0x51, 0x43, 0x63, 0x8b, 0x23, 0x8e, 0x1d, 0x31, 0xff, 0x46, 0x02, 0x66, 0xcc, 0x9e, 0x4d, 0xa2, 0xca, 0xe2, 0xc7, 0xfd, 0x22, 0xb1, 0xdb, 0xdf, 0x6f, 0xe6, 0xa5, 0x82}, subYX: fp.Elt{0xd0, 0xf5, 0x65, 0x40, 0xec, 0x8e, 0x65, 0x42, 0x78, 0xc1, 0x65, 0xe4, 0x10, 0xc8, 0x0b, 0x1b, 0xdd, 0x96, 0x68, 0xce, 0xee, 0x45, 0x55, 0xd8, 0x6e, 0xd3, 0xe6, 0x77, 0x19, 0xae, 0xc2, 0x8d, 0x8d, 0x3e, 0x14, 0x3f, 0x6d, 0x00, 0x2f, 0x9b, 0xd1, 0x26, 0x60, 0x28, 0x0f, 0x3a, 0x47, 0xb3, 0xe6, 0x68, 0x28, 0x24, 0x25, 0xca, 0xc8, 0x06}, dt2: fp.Elt{0x54, 0xbb, 0x60, 0x92, 0xdb, 0x8f, 0x0f, 0x38, 0xe0, 0xe6, 0xe4, 0xc9, 0xcc, 0x14, 0x62, 0x01, 0xc4, 0x2b, 0x0f, 0xcf, 0xed, 0x7d, 0x8e, 0xa4, 0xd9, 0x73, 0x0b, 0xba, 0x0c, 0xaf, 0x0c, 0xf9, 0xe2, 0xeb, 0x29, 0x2a, 0x53, 0xdf, 0x2c, 0x5a, 0xfa, 0x8f, 0xc1, 0x01, 0xd7, 0xb1, 0x45, 0x73, 0x92, 0x32, 0x83, 0x85, 0x12, 0x74, 0x89, 0x44}, }, - { /* 27P*/ + { /* 27G*/ addYX: fp.Elt{0x0b, 0x73, 0x3c, 0xc2, 0xb1, 0x2e, 0xe1, 0xa7, 0xf5, 0xc9, 0x7a, 0xfb, 0x3d, 0x2d, 0xac, 0x59, 0xdb, 0xfa, 0x36, 0x11, 0xd1, 0x13, 0x04, 0x51, 0x1d, 0xab, 0x9b, 0x6b, 0x93, 0xfe, 0xda, 0xb0, 0x8e, 0xb4, 0x79, 0x11, 0x21, 0x0f, 0x65, 0xb9, 0xbb, 0x79, 0x96, 0x2a, 0xfd, 0x30, 0xe0, 0xb4, 0x2d, 0x9a, 0x55, 0x25, 0x5d, 0xd4, 0xad, 0x2a}, subYX: fp.Elt{0x9e, 0xc5, 0x04, 0xfe, 0xec, 0x3c, 0x64, 0x1c, 0xed, 0x95, 0xed, 0xae, 0xaf, 0x5c, 0x6e, 0x08, 0x9e, 0x02, 0x29, 0x59, 0x7e, 0x5f, 0xc4, 0x9a, 0xd5, 0x32, 0x72, 0x86, 0xe1, 0x4e, 0x3c, 0xce, 0x99, 0x69, 0x3b, 0xc4, 0xdd, 0x4d, 0xb7, 0xbb, 0xda, 0x3b, 0x1a, 0x99, 0xaa, 0x62, 0x15, 0xc1, 0xf0, 0xb6, 0x6c, 0xec, 0x56, 0xc1, 0xff, 0x0c}, dt2: fp.Elt{0x2f, 0xf1, 0x3f, 0x7a, 0x2d, 0x56, 0x19, 0x7f, 0xea, 0xbe, 0x59, 0x2e, 0x13, 0x67, 0x81, 0xfb, 0xdb, 0xc8, 0xa3, 0x1d, 0xd5, 0xe9, 0x13, 0x8b, 0x29, 0xdf, 0xcf, 0x9f, 0xe7, 0xd9, 0x0b, 0x70, 0xd3, 0x15, 0x57, 0x4a, 0xe9, 0x50, 0x12, 0x1b, 0x81, 0x4b, 0x98, 0x98, 0xa8, 0x31, 0x1d, 0x27, 0x47, 0x38, 0xed, 0x57, 0x99, 0x26, 0xb2, 0xee}, }, - { /* 29P*/ + { /* 29G*/ addYX: fp.Elt{0x1c, 0xb2, 0xb2, 0x67, 0x3b, 0x8b, 0x3d, 0x5a, 0x30, 0x7e, 0x38, 0x7e, 0x3c, 0x3d, 0x28, 0x56, 0x59, 0xd8, 0x87, 0x53, 0x8b, 0xe6, 0x6c, 0x5d, 0xe5, 0x0a, 0x33, 0x10, 0xce, 0xa2, 0x17, 0x0d, 0xe8, 0x76, 0xee, 0x68, 0xa8, 0x72, 0x54, 0xbd, 0xa6, 0x24, 0x94, 0x6e, 0x77, 0xc7, 0x53, 0xb7, 0x89, 0x1c, 0x7a, 0xe9, 0x78, 0x9a, 0x74, 0x5f}, subYX: fp.Elt{0x76, 0x96, 0x1c, 0xcf, 0x08, 0x55, 0xd8, 0x1e, 0x0d, 0xa3, 0x59, 0x95, 0x32, 0xf4, 0xc2, 0x8e, 0x84, 0x5e, 0x4b, 0x04, 0xda, 0x71, 0xc9, 0x78, 0x52, 0xde, 0x14, 0xb4, 0x31, 0xf4, 0xd4, 0xb8, 0x58, 0xc5, 0x20, 0xe8, 0xdd, 0x15, 0xb5, 0xee, 0xea, 0x61, 0xe0, 0xf5, 0xd6, 0xae, 0x55, 0x59, 0x05, 0x3e, 0xaf, 0x74, 0xac, 0x1f, 0x17, 0x82}, dt2: fp.Elt{0x59, 0x24, 0xcd, 0xfc, 0x11, 0x7e, 0x85, 0x18, 0x3d, 0x69, 0xf7, 0x71, 0x31, 0x66, 0x98, 0x42, 0x95, 0x00, 0x8c, 0xb2, 0xae, 0x39, 0x7e, 0x85, 0xd6, 0xb0, 0x02, 0xec, 0xce, 0xfc, 0x25, 0xb2, 0xe3, 0x99, 0x8e, 0x5b, 0x61, 0x96, 0x2e, 0x6d, 0x96, 0x57, 0x71, 0xa5, 0x93, 0x41, 0x0e, 0x6f, 0xfd, 0x0a, 0xbf, 0xa9, 0xf7, 0x56, 0xa9, 0x3e}, }, - { /* 31P*/ + { /* 31G*/ addYX: fp.Elt{0xa2, 0x2e, 0x0c, 0x17, 0x4d, 0xcc, 0x85, 0x2c, 0x18, 0xa0, 0xd2, 0x08, 0xba, 0x11, 0xfa, 0x47, 0x71, 0x86, 0xaf, 0x36, 0x6a, 0xd7, 0xfe, 0xb9, 0xb0, 0x2f, 0x89, 0x98, 0x49, 0x69, 0xf8, 0x6a, 0xad, 0x27, 0x5e, 0x0a, 0x22, 0x60, 0x5e, 0x5d, 0xca, 0x06, 0x51, 0x27, 0x99, 0x29, 0x85, 0x68, 0x98, 0xe1, 0xc4, 0x21, 0x50, 0xa0, 0xe9, 0xc1}, subYX: fp.Elt{0x4d, 0x70, 0xee, 0x91, 0x92, 0x3f, 0xb7, 0xd3, 0x1d, 0xdb, 0x8d, 0x6e, 0x16, 0xf5, 0x65, 0x7d, 0x5f, 0xb5, 0x6c, 0x59, 0x26, 0x70, 0x4b, 0xf2, 0xfc, 0xe7, 0xdf, 0x86, 0xfe, 0xa5, 0xa7, 0xa6, 0x5d, 0xfb, 0x06, 0xe9, 0xf9, 0xcc, 0xc0, 0x37, 0xcc, 0xd8, 0x09, 0x04, 0xd2, 0xa5, 0x1d, 0xd7, 0xb7, 0xce, 0x92, 0xac, 0x3c, 0xad, 0xfb, 0xae}, dt2: fp.Elt{0x17, 0xa3, 0x9a, 0xc7, 0x86, 0x2a, 0x51, 0xf7, 0x96, 0x79, 0x49, 0x22, 0x2e, 0x5a, 0x01, 0x5c, 0xb5, 0x95, 0xd4, 0xe8, 0xcb, 0x00, 0xca, 0x2d, 0x55, 0xb6, 0x34, 0x36, 0x0b, 0x65, 0x46, 0xf0, 0x49, 0xfc, 0x87, 0x86, 0xe5, 0xc3, 0x15, 0xdb, 0x32, 0xcd, 0xf2, 0xd3, 0x82, 0x4c, 0xe6, 0x61, 0x8a, 0xaf, 0xd4, 0x9e, 0x0f, 0x5a, 0xf2, 0x81}, }, - { /* 33P*/ + { /* 33G*/ addYX: fp.Elt{0x88, 0x10, 0xc0, 0xcb, 0xf5, 0x77, 0xae, 0xa5, 0xbe, 0xf6, 0xcd, 0x2e, 0x8b, 0x7e, 0xbd, 0x79, 0x62, 0x4a, 0xeb, 0x69, 0xc3, 0x28, 0xaa, 0x72, 0x87, 0xa9, 0x25, 0x87, 0x46, 0xea, 0x0e, 0x62, 0xa3, 0x6a, 0x1a, 0xe2, 0xba, 0xdc, 0x81, 0x10, 0x33, 0x01, 0xf6, 0x16, 0x89, 0x80, 0xc6, 0xcd, 0xdb, 0xdc, 0xba, 0x0e, 0x09, 0x4a, 0x35, 0x4a}, subYX: fp.Elt{0x86, 0xb2, 0x2b, 0xd0, 0xb8, 0x4a, 0x6d, 0x66, 0x7b, 0x32, 0xdf, 0x3b, 0x1a, 0x19, 0x1f, 0x63, 0xee, 0x1f, 0x3d, 0x1c, 0x5c, 0x14, 0x60, 0x5b, 0x72, 0x49, 0x07, 0xb1, 0x0d, 0x72, 0xc6, 0x35, 0xf0, 0xbc, 0x5e, 0xda, 0x80, 0x6b, 0x64, 0x5b, 0xe5, 0x34, 0x54, 0x39, 0xdd, 0xe6, 0x3c, 0xcb, 0xe5, 0x29, 0x32, 0x06, 0xc6, 0xb1, 0x96, 0x34}, dt2: fp.Elt{0x85, 0x86, 0xf5, 0x84, 0x86, 0xe6, 0x77, 0x8a, 0x71, 0x85, 0x0c, 0x4f, 0x81, 0x5b, 0x29, 0x06, 0xb5, 0x2e, 0x26, 0x71, 0x07, 0x78, 0x07, 0xae, 0xbc, 0x95, 0x46, 0xc3, 0x65, 0xac, 0xe3, 0x76, 0x51, 0x7d, 0xd4, 0x85, 0x31, 0xe3, 0x43, 0xf3, 0x1b, 0x7c, 0xf7, 0x6b, 0x2c, 0xf8, 0x1c, 0xbb, 0x8d, 0xca, 0xab, 0x4b, 0xba, 0x7f, 0xa4, 0xe2}, }, - { /* 35P*/ + { /* 35G*/ addYX: fp.Elt{0x1a, 0xee, 0xe7, 0xa4, 0x8a, 0x9d, 0x53, 0x80, 0xc6, 0xb8, 0x4e, 0xdc, 0x89, 0xe0, 0xc4, 0x2b, 0x60, 0x52, 0x6f, 0xec, 0x81, 0xd2, 0x55, 0x6b, 0x1b, 0x6f, 0x17, 0x67, 0x8e, 0x42, 0x26, 0x4c, 0x65, 0x23, 0x29, 0xc6, 0x7b, 0xcd, 0x9f, 0xad, 0x4b, 0x42, 0xd3, 0x0c, 0x75, 0xc3, 0x8a, 0xf5, 0xbe, 0x9e, 0x55, 0xf7, 0x47, 0x5d, 0xbd, 0x3a}, subYX: fp.Elt{0x0d, 0xa8, 0x3b, 0xf9, 0xc7, 0x7e, 0xc6, 0x86, 0x94, 0xc0, 0x01, 0xff, 0x27, 0xce, 0x43, 0xac, 0xe5, 0xe1, 0xd2, 0x8d, 0xc1, 0x22, 0x31, 0xbe, 0xe1, 0xaf, 0xf9, 0x4a, 0x78, 0xa1, 0x0c, 0xaa, 0xd4, 0x80, 0xe4, 0x09, 0x8d, 0xfb, 0x1d, 0x52, 0xc8, 0x60, 0x2d, 0xf2, 0xa2, 0x89, 0x02, 0x56, 0x3d, 0x56, 0x27, 0x85, 0xc7, 0xf0, 0x2b, 0x9a}, dt2: fp.Elt{0x62, 0x7c, 0xc7, 0x6b, 0x2c, 0x9d, 0x0a, 0x7c, 0xe5, 0x50, 0x3c, 0xe6, 0x87, 0x1c, 0x82, 0x30, 0x67, 0x3c, 0x39, 0xb6, 0xa0, 0x31, 0xfb, 0x03, 0x7b, 0xa1, 0x58, 0xdf, 0x12, 0x76, 0x5d, 0x5d, 0x0a, 0x8f, 0x9b, 0x37, 0x32, 0xc3, 0x60, 0x33, 0xea, 0x9f, 0x0a, 0x99, 0xfa, 0x20, 0xd0, 0x33, 0x21, 0xc3, 0x94, 0xd4, 0x86, 0x49, 0x7c, 0x4e}, }, - { /* 37P*/ + { /* 37G*/ addYX: fp.Elt{0xc7, 0x0c, 0x71, 0xfe, 0x55, 0xd1, 0x95, 0x8f, 0x43, 0xbb, 0x6b, 0x74, 0x30, 0xbd, 0xe8, 0x6f, 0x1c, 0x1b, 0x06, 0x62, 0xf5, 0xfc, 0x65, 0xa0, 0xeb, 0x81, 0x12, 0xc9, 0x64, 0x66, 0x61, 0xde, 0xf3, 0x6d, 0xd4, 0xae, 0x8e, 0xb1, 0x72, 0xe0, 0xcd, 0x37, 0x01, 0x28, 0x52, 0xd7, 0x39, 0x46, 0x0c, 0x55, 0xcf, 0x47, 0x70, 0xef, 0xa1, 0x17}, subYX: fp.Elt{0x8d, 0x58, 0xde, 0x83, 0x88, 0x16, 0x0e, 0x12, 0x42, 0x03, 0x50, 0x60, 0x4b, 0xdf, 0xbf, 0x95, 0xcc, 0x7d, 0x18, 0x17, 0x7e, 0x31, 0x5d, 0x8a, 0x66, 0xc1, 0xcf, 0x14, 0xea, 0xf4, 0xf4, 0xe5, 0x63, 0x2d, 0x32, 0x86, 0x9b, 0xed, 0x1f, 0x4f, 0x03, 0xaf, 0x33, 0x92, 0xcb, 0xaf, 0x9c, 0x05, 0x0d, 0x47, 0x1b, 0x42, 0xba, 0x13, 0x22, 0x98}, dt2: fp.Elt{0xb5, 0x48, 0xeb, 0x7d, 0x3d, 0x10, 0x9f, 0x59, 0xde, 0xf8, 0x1c, 0x4f, 0x7d, 0x9d, 0x40, 0x4d, 0x9e, 0x13, 0x24, 0xb5, 0x21, 0x09, 0xb7, 0xee, 0x98, 0x5c, 0x56, 0xbc, 0x5e, 0x2b, 0x78, 0x38, 0x06, 0xac, 0xe3, 0xe0, 0xfa, 0x2e, 0xde, 0x4f, 0xd2, 0xb3, 0xfb, 0x2d, 0x71, 0x84, 0xd1, 0x9d, 0x12, 0x5b, 0x35, 0xc8, 0x03, 0x68, 0x67, 0xc7}, }, - { /* 39P*/ + { /* 39G*/ addYX: fp.Elt{0xb6, 0x65, 0xfb, 0xa7, 0x06, 0x35, 0xbb, 0xe0, 0x31, 0x8d, 0x91, 0x40, 0x98, 0xab, 0x30, 0xe4, 0xca, 0x12, 0x59, 0x89, 0xed, 0x65, 0x5d, 0x7f, 0xae, 0x69, 0xa0, 0xa4, 0xfa, 0x78, 0xb4, 0xf7, 0xed, 0xae, 0x86, 0x78, 0x79, 0x64, 0x24, 0xa6, 0xd4, 0xe1, 0xf6, 0xd3, 0xa0, 0x89, 0xba, 0x20, 0xf4, 0x54, 0x0d, 0x8f, 0xdb, 0x1a, 0x79, 0xdb}, subYX: fp.Elt{0xe1, 0x82, 0x0c, 0x4d, 0xde, 0x9f, 0x40, 0xf0, 0xc1, 0xbd, 0x8b, 0xd3, 0x24, 0x03, 0xcd, 0xf2, 0x92, 0x7d, 0xe2, 0x68, 0x7f, 0xf1, 0xbe, 0x69, 0xde, 0x34, 0x67, 0x4c, 0x85, 0x3b, 0xec, 0x98, 0xcc, 0x4d, 0x3e, 0xc0, 0x96, 0x27, 0xe6, 0x75, 0xfc, 0xdf, 0x37, 0xc0, 0x1e, 0x27, 0xe0, 0xf6, 0xc2, 0xbd, 0xbc, 0x3d, 0x9b, 0x39, 0xdc, 0xe2}, dt2: fp.Elt{0xd8, 0x29, 0xa7, 0x39, 0xe3, 0x9f, 0x2f, 0x0e, 0x4b, 0x24, 0x21, 0x70, 0xef, 0xfd, 0x91, 0xea, 0xbf, 0xe1, 0x72, 0x90, 0xcc, 0xc9, 0x84, 0x0e, 0xad, 0xd5, 0xe6, 0xbb, 0xc5, 0x99, 0x7f, 0xa4, 0xf0, 0x2e, 0xcc, 0x95, 0x64, 0x27, 0x19, 0xd8, 0x4c, 0x27, 0x0d, 0xff, 0xb6, 0x29, 0xe2, 0x6c, 0xfa, 0xbb, 0x4d, 0x9c, 0xbb, 0xaf, 0xa5, 0xec}, }, - { /* 41P*/ + { /* 41G*/ addYX: fp.Elt{0xd6, 0x33, 0x3f, 0x9f, 0xcf, 0xfd, 0x4c, 0xd1, 0xfe, 0xe5, 0xeb, 0x64, 0x27, 0xae, 0x7a, 0xa2, 0x82, 0x50, 0x6d, 0xaa, 0xe3, 0x5d, 0xe2, 0x48, 0x60, 0xb3, 0x76, 0x04, 0xd9, 0x19, 0xa7, 0xa1, 0x73, 0x8d, 0x38, 0xa9, 0xaf, 0x45, 0xb5, 0xb2, 0x62, 0x9b, 0xf1, 0x35, 0x7b, 0x84, 0x66, 0xeb, 0x06, 0xef, 0xf1, 0xb2, 0x2d, 0x6a, 0x61, 0x15}, subYX: fp.Elt{0x86, 0x50, 0x42, 0xf7, 0xda, 0x59, 0xb2, 0xcf, 0x0d, 0x3d, 0xee, 0x8e, 0x53, 0x5d, 0xf7, 0x9e, 0x6a, 0x26, 0x2d, 0xc7, 0x8c, 0x8e, 0x18, 0x50, 0x6d, 0xb7, 0x51, 0x4c, 0xa7, 0x52, 0x6e, 0x0e, 0x0a, 0x16, 0x74, 0xb2, 0x81, 0x8b, 0x56, 0x27, 0x22, 0x84, 0xf4, 0x56, 0xc5, 0x06, 0xe1, 0x8b, 0xca, 0x2d, 0xdb, 0x9a, 0xf6, 0x10, 0x9c, 0x51}, dt2: fp.Elt{0x1f, 0x16, 0xa2, 0x78, 0x96, 0x1b, 0x85, 0x9c, 0x76, 0x49, 0xd4, 0x0f, 0xac, 0xb0, 0xf4, 0xd0, 0x06, 0x2c, 0x7e, 0x6d, 0x6e, 0x8e, 0xc7, 0x9f, 0x18, 0xad, 0xfc, 0x88, 0x0c, 0x0c, 0x09, 0x05, 0x05, 0xa0, 0x79, 0x72, 0x32, 0x72, 0x87, 0x0f, 0x49, 0x87, 0x0c, 0xb4, 0x12, 0xc2, 0x09, 0xf8, 0x9f, 0x30, 0x72, 0xa9, 0x47, 0x13, 0x93, 0x49}, }, - { /* 43P*/ + { /* 43G*/ addYX: fp.Elt{0xcc, 0xb1, 0x4c, 0xd3, 0xc0, 0x9e, 0x9e, 0x4d, 0x6d, 0x28, 0x0b, 0xa5, 0x94, 0xa7, 0x2e, 0xc2, 0xc7, 0xaf, 0x29, 0x73, 0xc9, 0x68, 0xea, 0x0f, 0x34, 0x37, 0x8d, 0x96, 0x8f, 0x3a, 0x3d, 0x73, 0x1e, 0x6d, 0x9f, 0xcf, 0x8d, 0x83, 0xb5, 0x71, 0xb9, 0xe1, 0x4b, 0x67, 0x71, 0xea, 0xcf, 0x56, 0xe5, 0xeb, 0x72, 0x15, 0x2f, 0x9e, 0xa8, 0xaa}, subYX: fp.Elt{0xf4, 0x3e, 0x85, 0x1c, 0x1a, 0xef, 0x50, 0xd1, 0xb4, 0x20, 0xb2, 0x60, 0x05, 0x98, 0xfe, 0x47, 0x3b, 0xc1, 0x76, 0xca, 0x2c, 0x4e, 0x5a, 0x42, 0xa3, 0xf7, 0x20, 0xaa, 0x57, 0x39, 0xee, 0x34, 0x1f, 0xe1, 0x68, 0xd3, 0x7e, 0x06, 0xc4, 0x6c, 0xc7, 0x76, 0x2b, 0xe4, 0x1c, 0x48, 0x44, 0xe6, 0xe5, 0x44, 0x24, 0x8d, 0xb3, 0xb6, 0x88, 0x32}, dt2: fp.Elt{0x18, 0xa7, 0xba, 0xd0, 0x44, 0x6f, 0x33, 0x31, 0x00, 0xf8, 0xf6, 0x12, 0xe3, 0xc5, 0xc7, 0xb5, 0x91, 0x9c, 0x91, 0xb5, 0x75, 0x18, 0x18, 0x8a, 0xab, 0xed, 0x24, 0x11, 0x2e, 0xce, 0x5a, 0x0f, 0x94, 0x5f, 0x2e, 0xca, 0xd3, 0x80, 0xea, 0xe5, 0x34, 0x96, 0x67, 0x8b, 0x6a, 0x26, 0x5e, 0xc8, 0x9d, 0x2c, 0x5e, 0x6c, 0xa2, 0x0c, 0xbf, 0xf0}, }, - { /* 45P*/ + { /* 45G*/ addYX: fp.Elt{0xb3, 0xbf, 0xa3, 0x85, 0xee, 0xf6, 0x58, 0x02, 0x78, 0xc4, 0x30, 0xd6, 0x57, 0x59, 0x8c, 0x88, 0x08, 0x7c, 0xbc, 0xbe, 0x0a, 0x74, 0xa9, 0xde, 0x69, 0xe7, 0x41, 0xd8, 0xbf, 0x66, 0x8d, 0x3d, 0x28, 0x00, 0x8c, 0x47, 0x65, 0x34, 0xfe, 0x86, 0x9e, 0x6a, 0xf2, 0x41, 0x6a, 0x94, 0xc4, 0x88, 0x75, 0x23, 0x0d, 0x52, 0x69, 0xee, 0x07, 0x89}, subYX: fp.Elt{0x22, 0x3c, 0xa1, 0x70, 0x58, 0x97, 0x93, 0xbe, 0x59, 0xa8, 0x0b, 0x8a, 0x46, 0x2a, 0x38, 0x1e, 0x08, 0x6b, 0x61, 0x9f, 0xf2, 0x4a, 0x8b, 0x80, 0x68, 0x6e, 0xc8, 0x92, 0x60, 0xf3, 0xc9, 0x89, 0xb2, 0x6d, 0x63, 0xb0, 0xeb, 0x83, 0x15, 0x63, 0x0e, 0x64, 0xbb, 0xb8, 0xfe, 0xb4, 0x81, 0x90, 0x01, 0x28, 0x10, 0xb9, 0x74, 0x6e, 0xde, 0xa4}, dt2: fp.Elt{0x1a, 0x23, 0x45, 0xa8, 0x6f, 0x4e, 0xa7, 0x4a, 0x0c, 0xeb, 0xb0, 0x43, 0xf9, 0xef, 0x99, 0x60, 0x5b, 0xdb, 0x66, 0xc0, 0x86, 0x71, 0x43, 0xb1, 0x22, 0x7b, 0x1c, 0xe7, 0x8d, 0x09, 0x1d, 0x83, 0x76, 0x9c, 0xd3, 0x5a, 0xdd, 0x42, 0xd9, 0x2f, 0x2d, 0xba, 0x7a, 0xc2, 0xd9, 0x6b, 0xd4, 0x7a, 0xf1, 0xd5, 0x5f, 0x6b, 0x85, 0xbf, 0x0b, 0xf1}, }, - { /* 47P*/ + { /* 47G*/ addYX: fp.Elt{0xb2, 0x83, 0xfa, 0x1f, 0xd2, 0xce, 0xb6, 0xf2, 0x2d, 0xea, 0x1b, 0xe5, 0x29, 0xa5, 0x72, 0xf9, 0x25, 0x48, 0x4e, 0xf2, 0x50, 0x1b, 0x39, 0xda, 0x34, 0xc5, 0x16, 0x13, 0xb4, 0x0c, 0xa1, 0x00, 0x79, 0x7a, 0xf5, 0x8b, 0xf3, 0x70, 0x14, 0xb6, 0xfc, 0x9a, 0x47, 0x68, 0x1e, 0x42, 0x70, 0x64, 0x2a, 0x84, 0x3e, 0x3d, 0x20, 0x58, 0xf9, 0x6a}, subYX: fp.Elt{0xd9, 0xee, 0xc0, 0xc4, 0xf5, 0xc2, 0x86, 0xaf, 0x45, 0xd2, 0xd2, 0x87, 0x1b, 0x64, 0xd5, 0xe0, 0x8c, 0x44, 0x00, 0x4f, 0x43, 0x89, 0x04, 0x48, 0x4a, 0x0b, 0xca, 0x94, 0x06, 0x2f, 0x23, 0x5b, 0x6c, 0x8d, 0x44, 0x66, 0x53, 0xf5, 0x5a, 0x20, 0x72, 0x28, 0x58, 0x84, 0xcc, 0x73, 0x22, 0x5e, 0xd1, 0x0b, 0x56, 0x5e, 0x6a, 0xa3, 0x11, 0x91}, dt2: fp.Elt{0x6e, 0x9f, 0x88, 0xa8, 0x68, 0x2f, 0x12, 0x37, 0x88, 0xfc, 0x92, 0x8f, 0x24, 0xeb, 0x5b, 0x2a, 0x2a, 0xd0, 0x14, 0x40, 0x4c, 0xa9, 0xa4, 0x03, 0x0c, 0x45, 0x48, 0x13, 0xe8, 0xa6, 0x37, 0xab, 0xc0, 0x06, 0x38, 0x6c, 0x96, 0x73, 0x40, 0x6c, 0xc6, 0xea, 0x56, 0xc6, 0xe9, 0x1a, 0x69, 0xeb, 0x7a, 0xd1, 0x33, 0x69, 0x58, 0x2b, 0xea, 0x2f}, }, - { /* 49P*/ + { /* 49G*/ addYX: fp.Elt{0x58, 0xa8, 0x05, 0x41, 0x00, 0x9d, 0xaa, 0xd9, 0x98, 0xcf, 0xb9, 0x41, 0xb5, 0x4a, 0x8d, 0xe2, 0xe7, 0xc0, 0x72, 0xef, 0xc8, 0x28, 0x6b, 0x68, 0x9d, 0xc9, 0xdf, 0x05, 0x8b, 0xd0, 0x04, 0x74, 0x79, 0x45, 0x52, 0x05, 0xa3, 0x6e, 0x35, 0x3a, 0xe3, 0xef, 0xb2, 0xdc, 0x08, 0x6f, 0x4e, 0x76, 0x85, 0x67, 0xba, 0x23, 0x8f, 0xdd, 0xaf, 0x09}, subYX: fp.Elt{0xb4, 0x38, 0xc8, 0xff, 0x4f, 0x65, 0x2a, 0x7e, 0xad, 0xb1, 0xc6, 0xb9, 0x3d, 0xd6, 0xf7, 0x14, 0xcf, 0xf6, 0x98, 0x75, 0xbb, 0x47, 0x83, 0x90, 0xe7, 0xe1, 0xf6, 0x14, 0x99, 0x7e, 0xfa, 0xe4, 0x77, 0x24, 0xe3, 0xe7, 0xf0, 0x1e, 0xdb, 0x27, 0x4e, 0x16, 0x04, 0xf2, 0x08, 0x52, 0xfc, 0xec, 0x55, 0xdb, 0x2e, 0x67, 0xe1, 0x94, 0x32, 0x89}, dt2: fp.Elt{0x00, 0xad, 0x03, 0x35, 0x1a, 0xb1, 0x88, 0xf0, 0xc9, 0x11, 0xe4, 0x12, 0x52, 0x61, 0xfd, 0x8a, 0x1b, 0x6a, 0x0a, 0x4c, 0x42, 0x46, 0x22, 0x0e, 0xa5, 0xf9, 0xe2, 0x50, 0xf2, 0xb2, 0x1f, 0x20, 0x78, 0x10, 0xf6, 0xbf, 0x7f, 0x0c, 0x9c, 0xad, 0x40, 0x8b, 0x82, 0xd4, 0xba, 0x69, 0x09, 0xac, 0x4b, 0x6d, 0xc4, 0x49, 0x17, 0x81, 0x57, 0x3b}, }, - { /* 51P*/ + { /* 51G*/ addYX: fp.Elt{0x0d, 0xfe, 0xb4, 0x35, 0x11, 0xbd, 0x1d, 0x6b, 0xc2, 0xc5, 0x3b, 0xd2, 0x23, 0x2c, 0x72, 0xe3, 0x48, 0xb1, 0x48, 0x73, 0xfb, 0xa3, 0x21, 0x6e, 0xc0, 0x09, 0x69, 0xac, 0xe1, 0x60, 0xbc, 0x24, 0x03, 0x99, 0x63, 0x0a, 0x00, 0xf0, 0x75, 0xf6, 0x92, 0xc5, 0xd6, 0xdb, 0x51, 0xd4, 0x7d, 0xe6, 0xf4, 0x11, 0x79, 0xd7, 0xc3, 0xaf, 0x48, 0xd0}, subYX: fp.Elt{0xf4, 0x4f, 0xaf, 0x31, 0xe3, 0x10, 0x89, 0x95, 0xf0, 0x8a, 0xf6, 0x31, 0x9f, 0x48, 0x02, 0xba, 0x42, 0x2b, 0x3c, 0x22, 0x8b, 0xcc, 0x12, 0x98, 0x6e, 0x7a, 0x64, 0x3a, 0xc4, 0xca, 0x32, 0x2a, 0x72, 0xf8, 0x2c, 0xcf, 0x78, 0x5e, 0x7a, 0x75, 0x6e, 0x72, 0x46, 0x48, 0x62, 0x28, 0xac, 0x58, 0x1a, 0xc6, 0x59, 0x88, 0x2a, 0x44, 0x9e, 0x83}, dt2: fp.Elt{0xb3, 0xde, 0x36, 0xfd, 0xeb, 0x1b, 0xd4, 0x24, 0x1b, 0x08, 0x8c, 0xfe, 0xa9, 0x41, 0xa1, 0x64, 0xf2, 0x6d, 0xdb, 0xf9, 0x94, 0xae, 0x86, 0x71, 0xab, 0x10, 0xbf, 0xa3, 0xb2, 0xa0, 0xdf, 0x10, 0x8c, 0x74, 0xce, 0xb3, 0xfc, 0xdb, 0xba, 0x15, 0xf6, 0x91, 0x7a, 0x9c, 0x36, 0x1e, 0x45, 0x07, 0x3c, 0xec, 0x1a, 0x61, 0x26, 0x93, 0xe3, 0x50}, }, - { /* 53P*/ + { /* 53G*/ addYX: fp.Elt{0xc5, 0x50, 0xc5, 0x83, 0xb0, 0xbd, 0xd9, 0xf6, 0x6d, 0x15, 0x5e, 0xc1, 0x1a, 0x33, 0xa0, 0xce, 0x13, 0x70, 0x3b, 0xe1, 0x31, 0xc6, 0xc4, 0x02, 0xec, 0x8c, 0xd5, 0x9c, 0x97, 0xd3, 0x12, 0xc4, 0xa2, 0xf9, 0xd5, 0xfb, 0x22, 0x69, 0x94, 0x09, 0x2f, 0x59, 0xce, 0xdb, 0xf2, 0xf2, 0x00, 0xe0, 0xa9, 0x08, 0x44, 0x2e, 0x8b, 0x6b, 0xf5, 0xb3}, subYX: fp.Elt{0x90, 0xdd, 0xec, 0xa2, 0x65, 0xb7, 0x61, 0xbc, 0xaa, 0x70, 0xa2, 0x15, 0xd8, 0xb0, 0xf8, 0x8e, 0x23, 0x3d, 0x9f, 0x46, 0xa3, 0x29, 0x20, 0xd1, 0xa1, 0x15, 0x81, 0xc6, 0xb6, 0xde, 0xbe, 0x60, 0x63, 0x24, 0xac, 0x15, 0xfb, 0xeb, 0xd3, 0xea, 0x57, 0x13, 0x86, 0x38, 0x1e, 0x22, 0xf4, 0x8c, 0x5d, 0xaf, 0x1b, 0x27, 0x21, 0x4f, 0xa3, 0x63}, dt2: fp.Elt{0x07, 0x15, 0x87, 0xc4, 0xfd, 0xa1, 0x97, 0x7a, 0x07, 0x1f, 0x56, 0xcc, 0xe3, 0x6a, 0x01, 0x90, 0xce, 0xf9, 0xfa, 0x50, 0xb2, 0xe0, 0x87, 0x8b, 0x6c, 0x63, 0x6c, 0xf6, 0x2a, 0x09, 0xef, 0xef, 0xd2, 0x31, 0x40, 0x25, 0xf6, 0x84, 0xcb, 0xe0, 0xc4, 0x23, 0xc1, 0xcb, 0xe2, 0x02, 0x83, 0x2d, 0xed, 0x74, 0x74, 0x8b, 0xf8, 0x7c, 0x81, 0x18}, }, - { /* 55P*/ + { /* 55G*/ addYX: fp.Elt{0x9e, 0xe5, 0x59, 0x95, 0x63, 0x2e, 0xac, 0x8b, 0x03, 0x3c, 0xc1, 0x8e, 0xe1, 0x5b, 0x56, 0x3c, 0x16, 0x41, 0xe4, 0xc2, 0x60, 0x0c, 0x6d, 0x65, 0x9f, 0xfc, 0x27, 0x68, 0x43, 0x44, 0x05, 0x12, 0x6c, 0xda, 0x04, 0xef, 0xcf, 0xcf, 0xdc, 0x0a, 0x1a, 0x7f, 0x12, 0xd3, 0xeb, 0x02, 0xb6, 0x04, 0xca, 0xd6, 0xcb, 0xf0, 0x22, 0xba, 0x35, 0x6d}, subYX: fp.Elt{0x09, 0x6d, 0xf9, 0x64, 0x4c, 0xe6, 0x41, 0xff, 0x01, 0x4d, 0xce, 0x1e, 0xfa, 0x38, 0xa2, 0x25, 0x62, 0xff, 0x03, 0x39, 0x18, 0x91, 0xbb, 0x9d, 0xce, 0x02, 0xf0, 0xf1, 0x3c, 0x55, 0x18, 0xa9, 0xab, 0x4d, 0xd2, 0x35, 0xfd, 0x8d, 0xa9, 0xb2, 0xad, 0xb7, 0x06, 0x6e, 0xc6, 0x69, 0x49, 0xd6, 0x98, 0x98, 0x0b, 0x22, 0x81, 0x6b, 0xbd, 0xa0}, dt2: fp.Elt{0x22, 0xf4, 0x85, 0x5d, 0x2b, 0xf1, 0x55, 0xa5, 0xd6, 0x27, 0x86, 0x57, 0x12, 0x1f, 0x16, 0x0a, 0x5a, 0x9b, 0xf2, 0x38, 0xb6, 0x28, 0xd8, 0x99, 0x0c, 0x89, 0x1d, 0x7f, 0xca, 0x21, 0x17, 0x1a, 0x0b, 0x02, 0x5f, 0x77, 0x2f, 0x73, 0x30, 0x7c, 0xc8, 0xd7, 0x2b, 0xcc, 0xe7, 0xf3, 0x21, 0xac, 0x53, 0xa7, 0x11, 0x5d, 0xd8, 0x1d, 0x9b, 0xf5}, }, - { /* 57P*/ + { /* 57G*/ addYX: fp.Elt{0x94, 0x63, 0x5d, 0xef, 0xfd, 0x6d, 0x25, 0x4e, 0x6d, 0x29, 0x03, 0xed, 0x24, 0x28, 0x27, 0x57, 0x47, 0x3e, 0x6a, 0x1a, 0xfe, 0x37, 0xee, 0x5f, 0x83, 0x29, 0x14, 0xfd, 0x78, 0x25, 0x8a, 0xe1, 0x02, 0x38, 0xd8, 0xca, 0x65, 0x55, 0x40, 0x7d, 0x48, 0x2c, 0x7c, 0x7e, 0x60, 0xb6, 0x0c, 0x6d, 0xf7, 0xe8, 0xb3, 0x62, 0x53, 0xd6, 0x9c, 0x2b}, subYX: fp.Elt{0x47, 0x25, 0x70, 0x62, 0xf5, 0x65, 0x93, 0x62, 0x08, 0xac, 0x59, 0x66, 0xdb, 0x08, 0xd9, 0x1a, 0x19, 0xaf, 0xf4, 0xef, 0x02, 0xa2, 0x78, 0xa9, 0x55, 0x1c, 0xfa, 0x08, 0x11, 0xcb, 0xa3, 0x71, 0x74, 0xb1, 0x62, 0xe7, 0xc7, 0xf3, 0x5a, 0xb5, 0x8b, 0xd4, 0xf6, 0x10, 0x57, 0x79, 0x72, 0x2f, 0x13, 0x86, 0x7b, 0x44, 0x5f, 0x48, 0xfd, 0x88}, dt2: fp.Elt{0x10, 0x02, 0xcd, 0x05, 0x9a, 0xc3, 0x32, 0x6d, 0x10, 0x3a, 0x74, 0xba, 0x06, 0xc4, 0x3b, 0x34, 0xbc, 0x36, 0xed, 0xa3, 0xba, 0x9a, 0xdb, 0x6d, 0xd4, 0x69, 0x99, 0x97, 0xd0, 0xe4, 0xdd, 0xf5, 0xd4, 0x7c, 0xd3, 0x4e, 0xab, 0xd1, 0x3b, 0xbb, 0xe9, 0xc7, 0x6a, 0x94, 0x25, 0x61, 0xf0, 0x06, 0xc5, 0x12, 0xa8, 0x86, 0xe5, 0x35, 0x46, 0xeb}, }, - { /* 59P*/ + { /* 59G*/ addYX: fp.Elt{0x9e, 0x95, 0x11, 0xc6, 0xc7, 0xe8, 0xee, 0x5a, 0x26, 0xa0, 0x72, 0x72, 0x59, 0x91, 0x59, 0x16, 0x49, 0x99, 0x7e, 0xbb, 0xd7, 0x15, 0xb4, 0xf2, 0x40, 0xf9, 0x5a, 0x4d, 0xc8, 0xa0, 0xe2, 0x34, 0x7b, 0x34, 0xf3, 0x99, 0xbf, 0xa9, 0xf3, 0x79, 0xc1, 0x1a, 0x0c, 0xf4, 0x86, 0x74, 0x4e, 0xcb, 0xbc, 0x90, 0xad, 0xb6, 0x51, 0x6d, 0xaa, 0x33}, subYX: fp.Elt{0x9f, 0xd1, 0xc5, 0xa2, 0x6c, 0x24, 0x88, 0x15, 0x71, 0x68, 0xf6, 0x07, 0x45, 0x02, 0xc4, 0x73, 0x7e, 0x75, 0x87, 0xca, 0x7c, 0xf0, 0x92, 0x00, 0x75, 0xd6, 0x5a, 0xdd, 0xe0, 0x64, 0x16, 0x9d, 0x62, 0x80, 0x33, 0x9f, 0xf4, 0x8e, 0x1a, 0x15, 0x1c, 0xd3, 0x0f, 0x4d, 0x4f, 0x62, 0x2d, 0xd7, 0xa5, 0x77, 0xe3, 0xea, 0xf0, 0xfb, 0x1a, 0xdb}, dt2: fp.Elt{0x6a, 0xa2, 0xb1, 0xaa, 0xfb, 0x5a, 0x32, 0x4e, 0xff, 0x47, 0x06, 0xd5, 0x9a, 0x4f, 0xce, 0x83, 0x5b, 0x82, 0x34, 0x3e, 0x47, 0xb8, 0xf8, 0xe9, 0x7c, 0x67, 0x69, 0x8d, 0x9c, 0xb7, 0xde, 0x57, 0xf4, 0x88, 0x41, 0x56, 0x0c, 0x87, 0x1e, 0xc9, 0x2f, 0x54, 0xbf, 0x5c, 0x68, 0x2c, 0xd9, 0xc4, 0xef, 0x53, 0x73, 0x1e, 0xa6, 0x38, 0x02, 0x10}, }, - { /* 61P*/ + { /* 61G*/ addYX: fp.Elt{0x08, 0x80, 0x4a, 0xc9, 0xb7, 0xa8, 0x88, 0xd9, 0xfc, 0x6a, 0xc0, 0x3e, 0xc2, 0x33, 0x4d, 0x2b, 0x2a, 0xa3, 0x6d, 0x72, 0x3e, 0xdc, 0x34, 0x68, 0x08, 0xbf, 0x27, 0xef, 0xf4, 0xff, 0xe2, 0x0c, 0x31, 0x0c, 0xa2, 0x0a, 0x1f, 0x65, 0xc1, 0x4c, 0x61, 0xd3, 0x1b, 0xbc, 0x25, 0xb1, 0xd0, 0xd4, 0x89, 0xb2, 0x53, 0xfb, 0x43, 0xa5, 0xaf, 0x04}, subYX: fp.Elt{0xe3, 0xe1, 0x37, 0xad, 0x58, 0xa9, 0x55, 0x81, 0xee, 0x64, 0x21, 0xb9, 0xf5, 0x4c, 0x35, 0xea, 0x4a, 0xd3, 0x26, 0xaa, 0x90, 0xd4, 0x60, 0x46, 0x09, 0x4b, 0x4a, 0x62, 0xf9, 0xcd, 0xe1, 0xee, 0xbb, 0xc2, 0x09, 0x0b, 0xb0, 0x96, 0x8e, 0x43, 0x77, 0xaf, 0x25, 0x20, 0x5e, 0x47, 0xe4, 0x1d, 0x50, 0x69, 0x74, 0x08, 0xd7, 0xb9, 0x90, 0x13}, dt2: fp.Elt{0x51, 0x91, 0x95, 0x64, 0x03, 0x16, 0xfd, 0x6e, 0x26, 0x94, 0x6b, 0x61, 0xe7, 0xd9, 0xe0, 0x4a, 0x6d, 0x7c, 0xfa, 0xc0, 0xe2, 0x43, 0x23, 0x53, 0x70, 0xf5, 0x6f, 0x73, 0x8b, 0x81, 0xb0, 0x0c, 0xee, 0x2e, 0x46, 0xf2, 0x8d, 0xa6, 0xfb, 0xb5, 0x1c, 0x33, 0xbf, 0x90, 0x59, 0xc9, 0x7c, 0xb8, 0x6f, 0xad, 0x75, 0x02, 0x90, 0x8e, 0x59, 0x75}, }, - { /* 63P*/ + { /* 63G*/ addYX: fp.Elt{0x36, 0x4d, 0x77, 0x04, 0xb8, 0x7d, 0x4a, 0xd1, 0xc5, 0xbb, 0x7b, 0x50, 0x5f, 0x8d, 0x9d, 0x62, 0x0f, 0x66, 0x71, 0xec, 0x87, 0xc5, 0x80, 0x82, 0xc8, 0xf4, 0x6a, 0x94, 0x92, 0x5b, 0xb0, 0x16, 0x9b, 0xb2, 0xc9, 0x6f, 0x2b, 0x2d, 0xee, 0x95, 0x73, 0x2e, 0xc2, 0x1b, 0xc5, 0x55, 0x36, 0x86, 0x24, 0xf8, 0x20, 0x05, 0x0d, 0x93, 0xd7, 0x76}, subYX: fp.Elt{0x7f, 0x01, 0xeb, 0x2e, 0x48, 0x4d, 0x1d, 0xf1, 0x06, 0x7e, 0x7c, 0x2a, 0x43, 0xbf, 0x28, 0xac, 0xe9, 0x58, 0x13, 0xc8, 0xbf, 0x8e, 0xc0, 0xef, 0xe8, 0x4f, 0x46, 0x8a, 0xe7, 0xc0, 0xf6, 0x0f, 0x0a, 0x03, 0x48, 0x91, 0x55, 0x39, 0x2a, 0xe3, 0xdc, 0xf6, 0x22, 0x9d, 0x4d, 0x71, 0x55, 0x68, 0x25, 0x6e, 0x95, 0x52, 0xee, 0x4c, 0xd9, 0x01}, dt2: fp.Elt{0xac, 0x33, 0x3f, 0x7c, 0x27, 0x35, 0x15, 0x91, 0x33, 0x8d, 0xf9, 0xc4, 0xf4, 0xf3, 0x90, 0x09, 0x75, 0x69, 0x62, 0x9f, 0x61, 0x35, 0x83, 0x92, 0x04, 0xef, 0x96, 0x38, 0x80, 0x9e, 0x88, 0xb3, 0x67, 0x95, 0xbe, 0x79, 0x3c, 0x35, 0xd8, 0xdc, 0xb2, 0x3e, 0x2d, 0xe6, 0x46, 0xbe, 0x81, 0xf3, 0x32, 0x0e, 0x37, 0x23, 0x75, 0x2a, 0x3d, 0xa0}, diff --git a/ecc/goldilocks/isogeny.go b/ecc/goldilocks/isogeny.go deleted file mode 100644 index b1daab85..00000000 --- a/ecc/goldilocks/isogeny.go +++ /dev/null @@ -1,52 +0,0 @@ -package goldilocks - -import fp "github.com/cloudflare/circl/math/fp448" - -func (Curve) pull(P *twistPoint) *Point { return twistCurve{}.push(P) } -func (twistCurve) pull(P *Point) *twistPoint { return Curve{}.push(P) } - -// push sends a point on the Goldilocks curve to a point on the twist curve. -func (Curve) push(P *Point) *twistPoint { - Q := &twistPoint{} - Px, Py, Pz := &P.x, &P.y, &P.z - a, b, c, d, e, f, g, h := &Q.x, &Q.y, &Q.z, &fp.Elt{}, &Q.ta, &Q.x, &Q.y, &Q.tb - fp.Add(e, Px, Py) // x+y - fp.Sqr(a, Px) // A = x^2 - fp.Sqr(b, Py) // B = y^2 - fp.Sqr(c, Pz) // z^2 - fp.Add(c, c, c) // C = 2*z^2 - *d = *a // D = A - fp.Sqr(e, e) // (x+y)^2 - fp.Sub(e, e, a) // (x+y)^2-A - fp.Sub(e, e, b) // E = (x+y)^2-A-B - fp.Add(h, b, d) // H = B+D - fp.Sub(g, b, d) // G = B-D - fp.Sub(f, c, h) // F = C-H - fp.Mul(&Q.z, f, g) // Z = F * G - fp.Mul(&Q.x, e, f) // X = E * F - fp.Mul(&Q.y, g, h) // Y = G * H, // T = E * H - return Q -} - -// push sends a point on the twist curve to a point on the Goldilocks curve. -func (twistCurve) push(P *twistPoint) *Point { - Q := &Point{} - Px, Py, Pz := &P.x, &P.y, &P.z - a, b, c, d, e, f, g, h := &Q.x, &Q.y, &Q.z, &fp.Elt{}, &Q.ta, &Q.x, &Q.y, &Q.tb - fp.Add(e, Px, Py) // x+y - fp.Sqr(a, Px) // A = x^2 - fp.Sqr(b, Py) // B = y^2 - fp.Sqr(c, Pz) // z^2 - fp.Add(c, c, c) // C = 2*z^2 - fp.Neg(d, a) // D = -A - fp.Sqr(e, e) // (x+y)^2 - fp.Sub(e, e, a) // (x+y)^2-A - fp.Sub(e, e, b) // E = (x+y)^2-A-B - fp.Add(h, b, d) // H = B+D - fp.Sub(g, b, d) // G = B-D - fp.Sub(f, c, h) // F = C-H - fp.Mul(&Q.z, f, g) // Z = F * G - fp.Mul(&Q.x, e, f) // X = E * F - fp.Mul(&Q.y, g, h) // Y = G * H, // T = E * H - return Q -} diff --git a/ecc/goldilocks/isogeny_test.go b/ecc/goldilocks/isogeny_test.go index ffaf8c05..48f504f3 100644 --- a/ecc/goldilocks/isogeny_test.go +++ b/ecc/goldilocks/isogeny_test.go @@ -4,31 +4,40 @@ import ( "crypto/rand" "testing" + ted "github.com/cloudflare/circl/ecc/goldilocks/internal/ted448" "github.com/cloudflare/circl/internal/test" ) -func randomPoint() *Point { - var k Scalar - _, _ = rand.Read(k[:]) - return Curve{}.ScalarBaseMult(&k) +func rndScalar(t testing.TB) *ted.Scalar { + var buf [ted.ScalarSize]byte + _, err := rand.Read(buf[:]) + if err != nil { + t.Fatal(err) + } + var s ted.Scalar + s.FromBytesLE(buf[:]) + return &s +} + +func randomTwistPoint(t *testing.T) (P ted.Point) { + ted.ScalarBaseMult(&P, rndScalar(t)) + return P } func TestIsogeny(t *testing.T) { const testTimes = 1 << 10 - var gold Curve - var twist twistCurve - + var phiP Point + var Q ted.Point for i := 0; i < testTimes; i++ { - P := randomPoint() - Q := gold.pull(gold.push(P)) // phi^-(phi^+(P)) + P := randomTwistPoint(t) + R := P + push(&phiP, &P) + pull(&Q, &phiP) + R.Double() // 2P + R.Double() // 4P got := Q - want := gold.Double(gold.Double(P)) // 4P - if !got.IsEqual(want) { - test.ReportError(t, got, want, P) - } - got = twist.push(twist.pull(Q)) // phi^-(phi^+(Q)) - want = gold.Double(gold.Double(Q)) // 4Q - if !got.IsEqual(want) { + want := R + if got.IsEqual(&want) == 0 { test.ReportError(t, got, want, P) } } diff --git a/ecc/goldilocks/point.go b/ecc/goldilocks/point.go deleted file mode 100644 index 11f73de0..00000000 --- a/ecc/goldilocks/point.go +++ /dev/null @@ -1,171 +0,0 @@ -package goldilocks - -import ( - "errors" - "fmt" - - fp "github.com/cloudflare/circl/math/fp448" -) - -// Point is a point on the Goldilocks Curve. -type Point struct{ x, y, z, ta, tb fp.Elt } - -func (P Point) String() string { - return fmt.Sprintf("x: %v\ny: %v\nz: %v\nta: %v\ntb: %v", P.x, P.y, P.z, P.ta, P.tb) -} - -// FromAffine creates a point from affine coordinates. -func FromAffine(x, y *fp.Elt) (*Point, error) { - P := &Point{ - x: *x, - y: *y, - z: fp.One(), - ta: *x, - tb: *y, - } - if !(Curve{}).IsOnCurve(P) { - return P, errors.New("point not on curve") - } - return P, nil -} - -// isLessThan returns true if 0 <= x < y, and assumes that slices are of the -// same length and are interpreted in little-endian order. -func isLessThan(x, y []byte) bool { - i := len(x) - 1 - for i > 0 && x[i] == y[i] { - i-- - } - return x[i] < y[i] -} - -// FromBytes returns a point from the input buffer. -func FromBytes(in []byte) (*Point, error) { - if len(in) < fp.Size+1 { - return nil, errors.New("wrong input length") - } - err := errors.New("invalid decoding") - P := &Point{} - signX := in[fp.Size] >> 7 - copy(P.y[:], in[:fp.Size]) - p := fp.P() - if !isLessThan(P.y[:], p[:]) { - return nil, err - } - - u, v := &fp.Elt{}, &fp.Elt{} - one := fp.One() - fp.Sqr(u, &P.y) // u = y^2 - fp.Mul(v, u, ¶mD) // v = dy^2 - fp.Sub(u, u, &one) // u = y^2-1 - fp.Sub(v, v, &one) // v = dy^2-1 - isQR := fp.InvSqrt(&P.x, u, v) // x = sqrt(u/v) - if !isQR { - return nil, err - } - fp.Modp(&P.x) // x = x mod p - if fp.IsZero(&P.x) && signX == 1 { - return nil, err - } - if signX != (P.x[0] & 1) { - fp.Neg(&P.x, &P.x) - } - P.ta = P.x - P.tb = P.y - P.z = fp.One() - return P, nil -} - -// IsIdentity returns true is P is the identity Point. -func (P *Point) IsIdentity() bool { - return fp.IsZero(&P.x) && !fp.IsZero(&P.y) && !fp.IsZero(&P.z) && P.y == P.z -} - -// IsEqual returns true if P is equivalent to Q. -func (P *Point) IsEqual(Q *Point) bool { - l, r := &fp.Elt{}, &fp.Elt{} - fp.Mul(l, &P.x, &Q.z) - fp.Mul(r, &Q.x, &P.z) - fp.Sub(l, l, r) - b := fp.IsZero(l) - fp.Mul(l, &P.y, &Q.z) - fp.Mul(r, &Q.y, &P.z) - fp.Sub(l, l, r) - b = b && fp.IsZero(l) - fp.Mul(l, &P.ta, &P.tb) - fp.Mul(l, l, &Q.z) - fp.Mul(r, &Q.ta, &Q.tb) - fp.Mul(r, r, &P.z) - fp.Sub(l, l, r) - b = b && fp.IsZero(l) - return b -} - -// Neg obtains the inverse of the Point. -func (P *Point) Neg() { fp.Neg(&P.x, &P.x); fp.Neg(&P.ta, &P.ta) } - -// ToAffine returns the x,y affine coordinates of P. -func (P *Point) ToAffine() (x, y fp.Elt) { - fp.Inv(&P.z, &P.z) // 1/z - fp.Mul(&P.x, &P.x, &P.z) // x/z - fp.Mul(&P.y, &P.y, &P.z) // y/z - fp.Modp(&P.x) - fp.Modp(&P.y) - fp.SetOne(&P.z) - P.ta = P.x - P.tb = P.y - return P.x, P.y -} - -// ToBytes stores P into a slice of bytes. -func (P *Point) ToBytes(out []byte) error { - if len(out) < fp.Size+1 { - return errors.New("invalid decoding") - } - x, y := P.ToAffine() - out[fp.Size] = (x[0] & 1) << 7 - return fp.ToBytes(out[:fp.Size], &y) -} - -// MarshalBinary encodes the receiver into a binary form and returns the result. -func (P *Point) MarshalBinary() (data []byte, err error) { - data = make([]byte, fp.Size+1) - err = P.ToBytes(data[:fp.Size+1]) - return data, err -} - -// UnmarshalBinary must be able to decode the form generated by MarshalBinary. -func (P *Point) UnmarshalBinary(data []byte) error { Q, err := FromBytes(data); *P = *Q; return err } - -// Double sets P = 2Q. -func (P *Point) Double() { P.Add(P) } - -// Add sets P =P+Q.. -func (P *Point) Add(Q *Point) { - // This is formula (5) from "Twisted Edwards Curves Revisited" by - // Hisil H., Wong K.KH., Carter G., Dawson E. (2008) - // https://doi.org/10.1007/978-3-540-89255-7_20 - x1, y1, z1, ta1, tb1 := &P.x, &P.y, &P.z, &P.ta, &P.tb - x2, y2, z2, ta2, tb2 := &Q.x, &Q.y, &Q.z, &Q.ta, &Q.tb - x3, y3, z3, E, H := &P.x, &P.y, &P.z, &P.ta, &P.tb - A, B, C, D := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} - t1, t2, F, G := C, D, &fp.Elt{}, &fp.Elt{} - fp.Mul(t1, ta1, tb1) // t1 = ta1*tb1 - fp.Mul(t2, ta2, tb2) // t2 = ta2*tb2 - fp.Mul(A, x1, x2) // A = x1*x2 - fp.Mul(B, y1, y2) // B = y1*y2 - fp.Mul(C, t1, t2) // t1*t2 - fp.Mul(C, C, ¶mD) // C = d*t1*t2 - fp.Mul(D, z1, z2) // D = z1*z2 - fp.Add(F, x1, y1) // x1+y1 - fp.Add(E, x2, y2) // x2+y2 - fp.Mul(E, E, F) // (x1+y1)*(x2+y2) - fp.Sub(E, E, A) // (x1+y1)*(x2+y2)-A - fp.Sub(E, E, B) // E = (x1+y1)*(x2+y2)-A-B - fp.Sub(F, D, C) // F = D-C - fp.Add(G, D, C) // G = D+C - fp.Sub(H, B, A) // H = B-A - fp.Mul(z3, F, G) // Z = F * G - fp.Mul(x3, E, F) // X = E * F - fp.Mul(y3, G, H) // Y = G * H, T = E * H -} diff --git a/ecc/goldilocks/point_test.go b/ecc/goldilocks/point_test.go deleted file mode 100644 index a25bb5e0..00000000 --- a/ecc/goldilocks/point_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package goldilocks_test - -import ( - "crypto/rand" - "encoding" - "testing" - - "github.com/cloudflare/circl/ecc/goldilocks" - "github.com/cloudflare/circl/internal/test" -) - -func randomPoint() *goldilocks.Point { - var k goldilocks.Scalar - _, _ = rand.Read(k[:]) - return goldilocks.Curve{}.ScalarBaseMult(&k) -} - -func TestPointAdd(t *testing.T) { - const testTimes = 1 << 10 - var e goldilocks.Curve - for i := 0; i < testTimes; i++ { - P := randomPoint() - // 16P = 2^4P - got := e.Double(e.Double(e.Double(e.Double(P)))) - // 16P = P+P...+P - Q := e.Identity() - for j := 0; j < 16; j++ { - Q = e.Add(Q, P) - } - want := Q - if !e.IsOnCurve(got) || !e.IsOnCurve(want) || !got.IsEqual(want) { - test.ReportError(t, got, want, P) - } - } -} - -func TestPointNeg(t *testing.T) { - const testTimes = 1 << 10 - var e goldilocks.Curve - for i := 0; i < testTimes; i++ { - P := randomPoint() - Q := *P - Q.Neg() - R := e.Add(P, &Q) - got := R.IsIdentity() - want := true - if got != want { - test.ReportError(t, got, want, P) - } - } -} - -func TestPointAffine(t *testing.T) { - const testTimes = 1 << 10 - for i := 0; i < testTimes; i++ { - got := randomPoint() - x, y := got.ToAffine() - want, err := goldilocks.FromAffine(&x, &y) - if !got.IsEqual(want) || err != nil { - test.ReportError(t, got, want) - } - } -} - -func TestPointMarshal(t *testing.T) { - const testTimes = 1 << 10 - var want error - for i := 0; i < testTimes; i++ { - var P interface{} = randomPoint() - mar, _ := P.(encoding.BinaryMarshaler) - data, got := mar.MarshalBinary() - if got != want { - test.ReportError(t, got, want, P) - } - unmar, _ := P.(encoding.BinaryUnmarshaler) - got = unmar.UnmarshalBinary(data) - if got != want { - test.ReportError(t, got, want, P) - } - } -} - -func BenchmarkPoint(b *testing.B) { - P := randomPoint() - Q := randomPoint() - b.Run("ToAffine", func(b *testing.B) { - for i := 0; i < b.N; i++ { - P.ToAffine() - } - }) - b.Run("Add", func(b *testing.B) { - for i := 0; i < b.N; i++ { - P.Add(Q) - } - }) - b.Run("Double", func(b *testing.B) { - for i := 0; i < b.N; i++ { - P.Double() - } - }) -} diff --git a/ecc/goldilocks/scalar.go b/ecc/goldilocks/scalar.go deleted file mode 100644 index 852d74f3..00000000 --- a/ecc/goldilocks/scalar.go +++ /dev/null @@ -1,203 +0,0 @@ -package goldilocks - -import ( - "encoding/binary" - "math/bits" -) - -// ScalarSize is the size (in bytes) of scalars. -const ScalarSize = 56 // 448 / 8 - -//_N is the number of 64-bit words to store scalars. -const _N = 7 // 448 / 64 - -// Scalar represents a positive integer stored in little-endian order. -type Scalar [ScalarSize]byte - -type scalar64 [_N]uint64 - -func (z *scalar64) fromScalar(x *Scalar) { - z[0] = binary.LittleEndian.Uint64(x[0*8 : 1*8]) - z[1] = binary.LittleEndian.Uint64(x[1*8 : 2*8]) - z[2] = binary.LittleEndian.Uint64(x[2*8 : 3*8]) - z[3] = binary.LittleEndian.Uint64(x[3*8 : 4*8]) - z[4] = binary.LittleEndian.Uint64(x[4*8 : 5*8]) - z[5] = binary.LittleEndian.Uint64(x[5*8 : 6*8]) - z[6] = binary.LittleEndian.Uint64(x[6*8 : 7*8]) -} - -func (z *scalar64) toScalar(x *Scalar) { - binary.LittleEndian.PutUint64(x[0*8:1*8], z[0]) - binary.LittleEndian.PutUint64(x[1*8:2*8], z[1]) - binary.LittleEndian.PutUint64(x[2*8:3*8], z[2]) - binary.LittleEndian.PutUint64(x[3*8:4*8], z[3]) - binary.LittleEndian.PutUint64(x[4*8:5*8], z[4]) - binary.LittleEndian.PutUint64(x[5*8:6*8], z[5]) - binary.LittleEndian.PutUint64(x[6*8:7*8], z[6]) -} - -// add calculates z = x + y. Assumes len(z) > max(len(x),len(y)). -func add(z, x, y []uint64) uint64 { - l, L, zz := len(x), len(y), y - if l > L { - l, L, zz = L, l, x - } - c := uint64(0) - for i := 0; i < l; i++ { - z[i], c = bits.Add64(x[i], y[i], c) - } - for i := l; i < L; i++ { - z[i], c = bits.Add64(zz[i], 0, c) - } - return c -} - -// sub calculates z = x - y. Assumes len(z) > max(len(x),len(y)). -func sub(z, x, y []uint64) uint64 { - l, L, zz := len(x), len(y), y - if l > L { - l, L, zz = L, l, x - } - c := uint64(0) - for i := 0; i < l; i++ { - z[i], c = bits.Sub64(x[i], y[i], c) - } - for i := l; i < L; i++ { - z[i], c = bits.Sub64(zz[i], 0, c) - } - return c -} - -// mulWord calculates z = x * y. Assumes len(z) >= len(x)+1. -func mulWord(z, x []uint64, y uint64) { - for i := range z { - z[i] = 0 - } - carry := uint64(0) - for i := range x { - hi, lo := bits.Mul64(x[i], y) - lo, cc := bits.Add64(lo, z[i], 0) - hi, _ = bits.Add64(hi, 0, cc) - z[i], cc = bits.Add64(lo, carry, 0) - carry, _ = bits.Add64(hi, 0, cc) - } - z[len(x)] = carry -} - -// Cmov moves x into z if b=1. -func (z *scalar64) Cmov(b uint64, x *scalar64) { - m := uint64(0) - b - for i := range z { - z[i] = (z[i] &^ m) | (x[i] & m) - } -} - -// leftShift shifts to the left the words of z returning the more significant word. -func (z *scalar64) leftShift(low uint64) uint64 { - high := z[_N-1] - for i := _N - 1; i > 0; i-- { - z[i] = z[i-1] - } - z[0] = low - return high -} - -// reduceOneWord calculates z = z + 2^448*x such that the result fits in a Scalar. -func (z *scalar64) reduceOneWord(x uint64) { - prod := (&scalar64{})[:] - mulWord(prod, residue448[:], x) - cc := add(z[:], z[:], prod) - mulWord(prod, residue448[:], cc) - add(z[:], z[:], prod) -} - -// modOrder reduces z mod order. -func (z *scalar64) modOrder() { - var o64, x scalar64 - o64.fromScalar(&order) - // Performs: while (z >= order) { z = z-order } - // At most 8 (eight) iterations reduce 3 bits by subtracting. - for i := 0; i < 8; i++ { - c := sub(x[:], z[:], o64[:]) // (c || x) = z-order - z.Cmov(1-c, &x) // if c != 0 { z = x } - } -} - -// FromBytes stores z = x mod order, where x is a number stored in little-endian order. -func (z *Scalar) FromBytes(x []byte) { - n := len(x) - nCeil := (n + 7) >> 3 - for i := range z { - z[i] = 0 - } - if nCeil < _N { - copy(z[:], x) - return - } - copy(z[:], x[8*(nCeil-_N):]) - var z64 scalar64 - z64.fromScalar(z) - for i := nCeil - _N - 1; i >= 0; i-- { - low := binary.LittleEndian.Uint64(x[8*i:]) - high := z64.leftShift(low) - z64.reduceOneWord(high) - } - z64.modOrder() - z64.toScalar(z) -} - -// divBy4 calculates z = x/4 mod order. -func (z *Scalar) divBy4(x *Scalar) { z.Mul(x, &invFour) } - -// Red reduces z mod order. -func (z *Scalar) Red() { var t scalar64; t.fromScalar(z); t.modOrder(); t.toScalar(z) } - -// Neg calculates z = -z mod order. -func (z *Scalar) Neg() { z.Sub(&order, z) } - -// Add calculates z = x+y mod order. -func (z *Scalar) Add(x, y *Scalar) { - var z64, x64, y64, t scalar64 - x64.fromScalar(x) - y64.fromScalar(y) - c := add(z64[:], x64[:], y64[:]) - add(t[:], z64[:], residue448[:]) - z64.Cmov(c, &t) - z64.modOrder() - z64.toScalar(z) -} - -// Sub calculates z = x-y mod order. -func (z *Scalar) Sub(x, y *Scalar) { - var z64, x64, y64, t scalar64 - x64.fromScalar(x) - y64.fromScalar(y) - c := sub(z64[:], x64[:], y64[:]) - sub(t[:], z64[:], residue448[:]) - z64.Cmov(c, &t) - z64.modOrder() - z64.toScalar(z) -} - -// Mul calculates z = x*y mod order. -func (z *Scalar) Mul(x, y *Scalar) { - var z64, x64, y64 scalar64 - prod := (&[_N + 1]uint64{})[:] - x64.fromScalar(x) - y64.fromScalar(y) - mulWord(prod, x64[:], y64[_N-1]) - copy(z64[:], prod[:_N]) - z64.reduceOneWord(prod[_N]) - for i := _N - 2; i >= 0; i-- { - h := z64.leftShift(0) - z64.reduceOneWord(h) - mulWord(prod, x64[:], y64[i]) - c := add(z64[:], z64[:], prod[:_N]) - z64.reduceOneWord(prod[_N] + c) - } - z64.modOrder() - z64.toScalar(z) -} - -// IsZero returns true if z=0. -func (z *Scalar) IsZero() bool { z.Red(); return *z == Scalar{} } diff --git a/ecc/goldilocks/scalar_test.go b/ecc/goldilocks/scalar_test.go deleted file mode 100644 index 26bedf62..00000000 --- a/ecc/goldilocks/scalar_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package goldilocks_test - -import ( - "crypto/rand" - "encoding/binary" - "math/big" - "testing" - - "github.com/cloudflare/circl/ecc/goldilocks" - "github.com/cloudflare/circl/internal/conv" - "github.com/cloudflare/circl/internal/test" -) - -func TestReduceModOrder(t *testing.T) { - order := goldilocks.Curve{}.Order() - bigOrder := conv.BytesLe2BigInt(order[:]) - const max = 3*goldilocks.ScalarSize - 1 - var b [max]byte - _, _ = rand.Read(b[:]) - var z goldilocks.Scalar - for i := 0; i < max; i++ { - x := b[0:i] - bigX := conv.BytesLe2BigInt(x) - - z.FromBytes(x) - got := conv.BytesLe2BigInt(z[:]) - got.Mod(got, bigOrder) - - want := bigX.Mod(bigX, bigOrder) - - if got.Cmp(want) != 0 { - test.ReportError(t, got, want, x, i) - } - } -} - -func testOp(t *testing.T, - f func(z, x, y *goldilocks.Scalar), - g func(z, x, y *big.Int), -) { - const testTimes = 1 << 8 - var x, y, z goldilocks.Scalar - order := goldilocks.Curve{}.Order() - want := new(big.Int) - bigOrder := conv.BytesLe2BigInt(order[:]) - - for i := 0; i < testTimes; i++ { - _, _ = rand.Read(x[:]) - _, _ = rand.Read(y[:]) - bigX := conv.BytesLe2BigInt(x[:]) - bigY := conv.BytesLe2BigInt(y[:]) - - f(&z, &x, &y) - got := conv.BytesLe2BigInt(z[:]) - - g(want, bigX, bigY) - want.Mod(want, bigOrder) - if got.Cmp(want) != 0 { - test.ReportError(t, got.Text(16), want.Text(16), - conv.BytesLe2Hex(x[:]), - conv.BytesLe2Hex(y[:])) - } - } -} - -func TestScalar(t *testing.T) { - t.Run("Add", func(t *testing.T) { - testOp(t, - func(z, x, y *goldilocks.Scalar) { z.Add(x, y) }, - func(z, x, y *big.Int) { z.Add(x, y) }) - }) - t.Run("Sub", func(t *testing.T) { - testOp(t, - func(z, x, y *goldilocks.Scalar) { z.Sub(x, y) }, - func(z, x, y *big.Int) { z.Sub(x, y) }) - }) - t.Run("Mul", func(t *testing.T) { - testOp(t, - func(z, x, y *goldilocks.Scalar) { z.Mul(x, y) }, - func(z, x, y *big.Int) { z.Mul(x, y) }) - }) -} - -func BenchmarkScalar(b *testing.B) { - var k [2 * goldilocks.ScalarSize]byte - var x, y, z goldilocks.Scalar - _ = binary.Read(rand.Reader, binary.LittleEndian, x[:]) - b.Run("Add", func(b *testing.B) { - for i := 0; i < b.N; i++ { - z.Add(&x, &y) - } - }) - b.Run("Sub", func(b *testing.B) { - for i := 0; i < b.N; i++ { - z.Sub(&x, &y) - } - }) - b.Run("Mul", func(b *testing.B) { - for i := 0; i < b.N; i++ { - z.Mul(&x, &y) - } - }) - b.Run("Red", func(b *testing.B) { - for i := 0; i < b.N; i++ { - z.FromBytes(k[:]) - } - }) -} diff --git a/ecc/goldilocks/twist.go b/ecc/goldilocks/twist.go deleted file mode 100644 index 8cd4e333..00000000 --- a/ecc/goldilocks/twist.go +++ /dev/null @@ -1,138 +0,0 @@ -package goldilocks - -import ( - "crypto/subtle" - "math/bits" - - "github.com/cloudflare/circl/internal/conv" - "github.com/cloudflare/circl/math" - fp "github.com/cloudflare/circl/math/fp448" -) - -// twistCurve is -x^2+y^2=1-39082x^2y^2 and is 4-isogeneous to Goldilocks. -type twistCurve struct{} - -// Identity returns the identity point. -func (twistCurve) Identity() *twistPoint { - return &twistPoint{ - y: fp.One(), - z: fp.One(), - } -} - -// subYDiv16 update x = (x - y) / 16. -func subYDiv16(x *scalar64, y int64) { - s := uint64(y >> 63) - x0, b0 := bits.Sub64((*x)[0], uint64(y), 0) - x1, b1 := bits.Sub64((*x)[1], s, b0) - x2, b2 := bits.Sub64((*x)[2], s, b1) - x3, b3 := bits.Sub64((*x)[3], s, b2) - x4, b4 := bits.Sub64((*x)[4], s, b3) - x5, b5 := bits.Sub64((*x)[5], s, b4) - x6, _ := bits.Sub64((*x)[6], s, b5) - x[0] = (x0 >> 4) | (x1 << 60) - x[1] = (x1 >> 4) | (x2 << 60) - x[2] = (x2 >> 4) | (x3 << 60) - x[3] = (x3 >> 4) | (x4 << 60) - x[4] = (x4 >> 4) | (x5 << 60) - x[5] = (x5 >> 4) | (x6 << 60) - x[6] = (x6 >> 4) -} - -func recodeScalar(d *[113]int8, k *Scalar) { - var k64 scalar64 - k64.fromScalar(k) - for i := 0; i < 112; i++ { - d[i] = int8((k64[0] & 0x1f) - 16) - subYDiv16(&k64, int64(d[i])) - } - d[112] = int8(k64[0]) -} - -// ScalarMult returns kP. -func (e twistCurve) ScalarMult(k *Scalar, P *twistPoint) *twistPoint { - var TabP [8]preTwistPointProy - var S preTwistPointProy - var d [113]int8 - - var isZero int - if k.IsZero() { - isZero = 1 - } - subtle.ConstantTimeCopy(isZero, k[:], order[:]) - - minusK := *k - isEven := 1 - int(k[0]&0x1) - minusK.Neg() - subtle.ConstantTimeCopy(isEven, k[:], minusK[:]) - recodeScalar(&d, k) - - P.oddMultiples(TabP[:]) - Q := e.Identity() - for i := 112; i >= 0; i-- { - Q.Double() - Q.Double() - Q.Double() - Q.Double() - mask := d[i] >> 7 - absDi := (d[i] + mask) ^ mask - inx := int32((absDi - 1) >> 1) - sig := int((d[i] >> 7) & 0x1) - for j := range TabP { - S.cmov(&TabP[j], uint(subtle.ConstantTimeEq(inx, int32(j)))) - } - S.cneg(sig) - Q.mixAdd(&S) - } - Q.cneg(uint(isEven)) - return Q -} - -const ( - omegaFix = 7 - omegaVar = 5 -) - -// CombinedMult returns mG+nP. -func (e twistCurve) CombinedMult(m, n *Scalar, P *twistPoint) *twistPoint { - nafFix := math.OmegaNAF(conv.BytesLe2BigInt(m[:]), omegaFix) - nafVar := math.OmegaNAF(conv.BytesLe2BigInt(n[:]), omegaVar) - - if len(nafFix) > len(nafVar) { - nafVar = append(nafVar, make([]int32, len(nafFix)-len(nafVar))...) - } else if len(nafFix) < len(nafVar) { - nafFix = append(nafFix, make([]int32, len(nafVar)-len(nafFix))...) - } - - var TabQ [1 << (omegaVar - 2)]preTwistPointProy - P.oddMultiples(TabQ[:]) - Q := e.Identity() - for i := len(nafFix) - 1; i >= 0; i-- { - Q.Double() - // Generator point - if nafFix[i] != 0 { - idxM := absolute(nafFix[i]) >> 1 - R := tabVerif[idxM] - if nafFix[i] < 0 { - R.neg() - } - Q.mixAddZ1(&R) - } - // Variable input point - if nafVar[i] != 0 { - idxN := absolute(nafVar[i]) >> 1 - S := TabQ[idxN] - if nafVar[i] < 0 { - S.neg() - } - Q.mixAdd(&S) - } - } - return Q -} - -// absolute returns always a positive value. -func absolute(x int32) int32 { - mask := x >> 31 - return (x + mask) ^ mask -} diff --git a/ecc/goldilocks/twistPoint.go b/ecc/goldilocks/twistPoint.go deleted file mode 100644 index c55db77b..00000000 --- a/ecc/goldilocks/twistPoint.go +++ /dev/null @@ -1,135 +0,0 @@ -package goldilocks - -import ( - "fmt" - - fp "github.com/cloudflare/circl/math/fp448" -) - -type twistPoint struct{ x, y, z, ta, tb fp.Elt } - -type preTwistPointAffine struct{ addYX, subYX, dt2 fp.Elt } - -type preTwistPointProy struct { - preTwistPointAffine - z2 fp.Elt -} - -func (P *twistPoint) String() string { - return fmt.Sprintf("x: %v\ny: %v\nz: %v\nta: %v\ntb: %v", P.x, P.y, P.z, P.ta, P.tb) -} - -// cneg conditionally negates the point if b=1. -func (P *twistPoint) cneg(b uint) { - t := &fp.Elt{} - fp.Neg(t, &P.x) - fp.Cmov(&P.x, t, b) - fp.Neg(t, &P.ta) - fp.Cmov(&P.ta, t, b) -} - -// Double updates P with 2P. -func (P *twistPoint) Double() { - // This is formula (7) from "Twisted Edwards Curves Revisited" by - // Hisil H., Wong K.KH., Carter G., Dawson E. (2008) - // https://doi.org/10.1007/978-3-540-89255-7_20 - Px, Py, Pz, Pta, Ptb := &P.x, &P.y, &P.z, &P.ta, &P.tb - a, b, c, e, f, g, h := Px, Py, Pz, Pta, Px, Py, Ptb - fp.Add(e, Px, Py) // x+y - fp.Sqr(a, Px) // A = x^2 - fp.Sqr(b, Py) // B = y^2 - fp.Sqr(c, Pz) // z^2 - fp.Add(c, c, c) // C = 2*z^2 - fp.Add(h, a, b) // H = A+B - fp.Sqr(e, e) // (x+y)^2 - fp.Sub(e, e, h) // E = (x+y)^2-A-B - fp.Sub(g, b, a) // G = B-A - fp.Sub(f, c, g) // F = C-G - fp.Mul(Pz, f, g) // Z = F * G - fp.Mul(Px, e, f) // X = E * F - fp.Mul(Py, g, h) // Y = G * H, T = E * H -} - -// mixAdd calculates P= P+Q, where Q is a precomputed point with Z_Q = 1. -func (P *twistPoint) mixAddZ1(Q *preTwistPointAffine) { - fp.Add(&P.z, &P.z, &P.z) // D = 2*z1 (z2=1) - P.coreAddition(Q) -} - -// coreAddition calculates P=P+Q for curves with A=-1. -func (P *twistPoint) coreAddition(Q *preTwistPointAffine) { - // This is the formula following (5) from "Twisted Edwards Curves Revisited" by - // Hisil H., Wong K.KH., Carter G., Dawson E. (2008) - // https://doi.org/10.1007/978-3-540-89255-7_20 - Px, Py, Pz, Pta, Ptb := &P.x, &P.y, &P.z, &P.ta, &P.tb - addYX2, subYX2, dt2 := &Q.addYX, &Q.subYX, &Q.dt2 - a, b, c, d, e, f, g, h := Px, Py, &fp.Elt{}, Pz, Pta, Px, Py, Ptb - fp.Mul(c, Pta, Ptb) // t1 = ta*tb - fp.Sub(h, Py, Px) // y1-x1 - fp.Add(b, Py, Px) // y1+x1 - fp.Mul(a, h, subYX2) // A = (y1-x1)*(y2-x2) - fp.Mul(b, b, addYX2) // B = (y1+x1)*(y2+x2) - fp.Mul(c, c, dt2) // C = 2*D*t1*t2 - fp.Sub(e, b, a) // E = B-A - fp.Add(h, b, a) // H = B+A - fp.Sub(f, d, c) // F = D-C - fp.Add(g, d, c) // G = D+C - fp.Mul(Pz, f, g) // Z = F * G - fp.Mul(Px, e, f) // X = E * F - fp.Mul(Py, g, h) // Y = G * H, T = E * H -} - -func (P *preTwistPointAffine) neg() { - P.addYX, P.subYX = P.subYX, P.addYX - fp.Neg(&P.dt2, &P.dt2) -} - -func (P *preTwistPointAffine) cneg(b int) { - t := &fp.Elt{} - fp.Cswap(&P.addYX, &P.subYX, uint(b)) - fp.Neg(t, &P.dt2) - fp.Cmov(&P.dt2, t, uint(b)) -} - -func (P *preTwistPointAffine) cmov(Q *preTwistPointAffine, b uint) { - fp.Cmov(&P.addYX, &Q.addYX, b) - fp.Cmov(&P.subYX, &Q.subYX, b) - fp.Cmov(&P.dt2, &Q.dt2, b) -} - -// mixAdd calculates P= P+Q, where Q is a precomputed point with Z_Q != 1. -func (P *twistPoint) mixAdd(Q *preTwistPointProy) { - fp.Mul(&P.z, &P.z, &Q.z2) // D = 2*z1*z2 - P.coreAddition(&Q.preTwistPointAffine) -} - -// oddMultiples calculates T[i] = (2*i-1)P for 0 < i < len(T). -func (P *twistPoint) oddMultiples(T []preTwistPointProy) { - if n := len(T); n > 0 { - T[0].FromTwistPoint(P) - _2P := *P - _2P.Double() - R := &preTwistPointProy{} - R.FromTwistPoint(&_2P) - for i := 1; i < n; i++ { - P.mixAdd(R) - T[i].FromTwistPoint(P) - } - } -} - -// cmov conditionally moves Q into P if b=1. -func (P *preTwistPointProy) cmov(Q *preTwistPointProy, b uint) { - P.preTwistPointAffine.cmov(&Q.preTwistPointAffine, b) - fp.Cmov(&P.z2, &Q.z2, b) -} - -// FromTwistPoint precomputes some coordinates of Q for missed addition. -func (P *preTwistPointProy) FromTwistPoint(Q *twistPoint) { - fp.Add(&P.addYX, &Q.y, &Q.x) // addYX = X + Y - fp.Sub(&P.subYX, &Q.y, &Q.x) // subYX = Y - X - fp.Mul(&P.dt2, &Q.ta, &Q.tb) // T = ta*tb - fp.Mul(&P.dt2, &P.dt2, ¶mDTwist) // D*T - fp.Add(&P.dt2, &P.dt2, &P.dt2) // dt2 = 2*D*T - fp.Add(&P.z2, &Q.z, &Q.z) // z2 = 2*Z -} diff --git a/ecc/goldilocks/twist_basemult.go b/ecc/goldilocks/twist_basemult.go deleted file mode 100644 index f6ac5edb..00000000 --- a/ecc/goldilocks/twist_basemult.go +++ /dev/null @@ -1,62 +0,0 @@ -package goldilocks - -import ( - "crypto/subtle" - - mlsb "github.com/cloudflare/circl/math/mlsbset" -) - -const ( - // MLSBRecoding parameters - fxT = 448 - fxV = 2 - fxW = 3 - fx2w1 = 1 << (uint(fxW) - 1) -) - -// ScalarBaseMult returns kG where G is the generator point. -func (e twistCurve) ScalarBaseMult(k *Scalar) *twistPoint { - m, err := mlsb.New(fxT, fxV, fxW) - if err != nil { - panic(err) - } - if m.IsExtended() { - panic("not extended") - } - - var isZero int - if k.IsZero() { - isZero = 1 - } - subtle.ConstantTimeCopy(isZero, k[:], order[:]) - - minusK := *k - isEven := 1 - int(k[0]&0x1) - minusK.Neg() - subtle.ConstantTimeCopy(isEven, k[:], minusK[:]) - c, err := m.Encode(k[:]) - if err != nil { - panic(err) - } - - gP := c.Exp(groupMLSB{}) - P := gP.(*twistPoint) - P.cneg(uint(isEven)) - return P -} - -type groupMLSB struct{} - -func (e groupMLSB) ExtendedEltP() mlsb.EltP { return nil } -func (e groupMLSB) Sqr(x mlsb.EltG) { x.(*twistPoint).Double() } -func (e groupMLSB) Mul(x mlsb.EltG, y mlsb.EltP) { x.(*twistPoint).mixAddZ1(y.(*preTwistPointAffine)) } -func (e groupMLSB) Identity() mlsb.EltG { return twistCurve{}.Identity() } -func (e groupMLSB) NewEltP() mlsb.EltP { return &preTwistPointAffine{} } -func (e groupMLSB) Lookup(a mlsb.EltP, v uint, s, u int32) { - Tabj := &tabFixMult[v] - P := a.(*preTwistPointAffine) - for k := range Tabj { - P.cmov(&Tabj[k], uint(subtle.ConstantTimeEq(int32(k), u))) - } - P.cneg(int(s >> 31)) -} diff --git a/group/decaf.go b/group/decaf.go new file mode 100644 index 00000000..474793d0 --- /dev/null +++ b/group/decaf.go @@ -0,0 +1,352 @@ +package group + +import ( + "crypto/subtle" + "io" + "math/bits" + + curve "github.com/cloudflare/circl/ecc/goldilocks" + "github.com/cloudflare/circl/expander" + fp "github.com/cloudflare/circl/math/fp448" + "github.com/cloudflare/circl/xof" +) + +// Decaf Group +// +// Decaf (3) is a prime-order group constructed as a quotient of groups. A Decaf +// element can be represented by any point in the coset P+J[2], where J is a +// Jacobi quartic curve and J[2] are its 2-torsion points. +// Since P+J[2] has four points, Decaf specifies rules to choose one canonical +// representative, which has a unique encoding. Two representations are +// equivalent if they belong to the same coset. +// +// The types Elt and Scalar provide methods to perform arithmetic operations on +// the Decaf group. +// +// This implementation is compatible with v03 of draft-irtf-cfrg-ristretto255-decaf448 (4). +// +// References +// +// (1) https://www.shiftleft.org/papers/goldilocks +// +// (2) https://tools.ietf.org/html/rfc7748 +// +// (3) https://doi.org/10.1007/978-3-662-47989-6_34 and https://www.shiftleft.org/papers/decaf +// +// (4) https://datatracker.ietf.org/doc/draft-irtf-cfrg-ristretto255-decaf448/ + +// Decaf448 is a quotient group generated from the edwards448 curve. +var Decaf448 Group = decaf448{} + +type decaf448 struct{} + +func (g decaf448) String() string { return "decaf448" } +func (g decaf448) Params() *Params { return &Params{fp.Size, fp.Size, curve.ScalarSize} } +func (g decaf448) NewElement() Element { return g.Identity() } +func (g decaf448) NewScalar() Scalar { return new(dScl) } +func (g decaf448) Identity() Element { return &dElt{curve.Identity()} } +func (g decaf448) Order() Scalar { r := &dScl{}; r.k.FromBytesLE(curve.Order()); return r } +func (g decaf448) Generator() Element { + e := curve.Generator() + e.Double() // Since decaf.Generator() == 2*goldilocks.Generator(). + return &dElt{e} +} + +func (g decaf448) RandomElement(rd io.Reader) Element { + b := make([]byte, fp.Size) + if n, err := io.ReadFull(rd, b); err != nil || n != len(b) { + panic(err) + } + return g.HashToElement(b, nil) +} + +func (g decaf448) RandomScalar(rd io.Reader) Scalar { + b := make([]byte, fp.Size) + if n, err := io.ReadFull(rd, b); err != nil || n != len(b) { + panic(err) + } + return g.HashToScalar(b, nil) +} + +func (g decaf448) RandomNonZeroScalar(rd io.Reader) Scalar { + zero := g.NewScalar() + for { + s := g.RandomScalar(rd) + if !s.IsEqual(zero) { + return s + } + } +} + +func (g decaf448) HashToElementNonUniform(data, dst []byte) Element { + return g.HashToElement(data, dst) +} + +func (g decaf448) HashToElement(data, dst []byte) Element { + // Compliaint with draft-irtf-cfrg-hash-to-curve. + // Appendix C - Hashing to decaf448 + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-C + // SuiteID: decaf448_XOF:SHAKE256_D448MAP_RO_ + var buf [2 * fp.Size]byte + exp := expander.NewExpanderXOF(xof.SHAKE256, 224, dst) + uniformBytes := exp.Expand(data, 2*fp.Size) + copy(buf[:], uniformBytes) + return g.oneway(&buf) +} + +func (g decaf448) HashToScalar(data, dst []byte) Scalar { + // Section 5.4 - Scalar field + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-03#section-5.4 + exp := expander.NewExpanderXOF(xof.SHAKE256, 224, dst) + uniformBytes := exp.Expand(data, 64) + s := new(dScl) + s.k.FromBytesLE(uniformBytes) + return s +} + +func (g decaf448) oneway(data *[2 * fp.Size]byte) *dElt { + // Complaiant with draft-irtf-cfrg-ristretto255-decaf448-03 + // Section 5.3.4 - One-way Map + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-03#section-5.3.4 + var buf [fp.Size]byte + copy(buf[:], data[:fp.Size]) + p1 := g.mapFunc(&buf) + copy(buf[:], data[fp.Size:2*fp.Size]) + p2 := g.mapFunc(&buf) + p1.Add(&p2) + return &dElt{p: p1} +} + +func (g decaf448) mapFunc(data *[fp.Size]byte) (P curve.Point) { + t := (*fp.Elt)(data) + fp.Modp(t) + + one := fp.One() + d := curve.ParamD() + + r, u0, u1, u2, v := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + tv, sgn, s := &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + w0, w1, w2, w3 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + + fp.Sqr(r, t) // r = -t^2 + fp.Neg(r, r) // + fp.Sub(u0, r, &one) // u0 = d * (r-1) + fp.Mul(u0, u0, &d) // + fp.Add(u1, u0, &one) // u1 = (u0 + 1) * (u0 - r) + fp.Sub(u0, u0, r) // + fp.Mul(u1, u1, u0) // + fp.Add(u2, r, &one) // u2 = (r + 1) * u1 + fp.Mul(u2, u2, u1) // + isQR := fp.InvSqrt(v, &aMinusTwoD, u2) // (isQR, v) = sqrt(ONE_MINUS_TWO_D / (r + 1) * u1) + fp.Mul(tv, t, v) // v = CT_SELECT(v IF isQR ELSE t * v) + fp.Cmov(v, tv, uint(1-isQR)) // + fp.Neg(sgn, &one) // sgn = CT_SELECT(1 IF isQR ELSE -1) + fp.Cmov(sgn, &one, uint(isQR)) // + fp.Add(s, r, &one) // s = v * (r + 1) + fp.Mul(s, s, v) // + ctAbs(w0, s) // w0 = 2 * CT_ABS(s) + fp.Add(w0, w0, w0) // + fp.Sqr(w1, s) // w1 = s^2 + 1 + fp.Sub(w2, w1, &one) // w2 = s^2 - 1 + fp.Add(w1, w1, &one) // + fp.Sub(w3, r, &one) // w3 = v_prime * s * (r - 1) * ONE_MINUS_TWO_D + sgn + fp.Mul(w3, w3, s) // + fp.Mul(w3, w3, v) // + fp.Mul(w3, w3, &aMinusTwoD) // + fp.Add(w3, w3, sgn) // + fp.Mul(&P.X, w0, w3) // X = w0 * w3 + fp.Mul(&P.Y, w2, w1) // Y = w2 * w1 + fp.Mul(&P.Z, w1, w3) // Z = w1 * w3 + P.Ta, P.Tb = *w0, *w2 // T = w0 * w2 + + return P +} + +type dElt struct{ p curve.Point } + +func (e dElt) String() string { return e.p.String() } +func (e *dElt) Set(a Element) Element { e.p = a.(*dElt).p; return e } +func (e *dElt) Copy() Element { return &dElt{e.p} } +func (e *dElt) Add(a, b Element) Element { e.Set(a); e.p.Add(&b.(*dElt).p); return e } +func (e *dElt) Dbl(a Element) Element { e.Set(a); e.p.Double(); return e } +func (e *dElt) Neg(a Element) Element { e.Set(a); e.p.Neg(); return e } +func (e *dElt) Mul(a Element, s Scalar) Element { e.p.ScalarMult(&s.(*dScl).k, &a.(*dElt).p); return e } +func (e *dElt) MulGen(s Scalar) Element { + k := &s.(*dScl).k + k2 := &curve.Scalar{} + k2.Add(k, k) // Since decaf.Generator() == 2*goldilocks.Generator(). + e.p.ScalarBaseMult(k2) + return e +} + +func (e *dElt) IsIdentity() bool { + // From Decaf, Section 4.5 - Equality + // In particular, for a curve of cofactor exactly 4, + // a point (X : Y : Z : T ) is equal to the identity precisely when X = 0. + return fp.IsZero(&e.p.X) == 1 +} + +func (e *dElt) IsEqual(a Element) bool { + // Section 5.3.3 - Equals + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-03#section-5.3.3 + aa := a.(*dElt) + l, r := &fp.Elt{}, &fp.Elt{} + fp.Mul(l, &e.p.X, &aa.p.Y) + fp.Mul(r, &aa.p.X, &e.p.Y) + fp.Sub(l, l, r) + return fp.IsZero(l) == 1 +} + +func (e *dElt) MarshalBinaryCompress() ([]byte, error) { return e.MarshalBinary() } +func (e *dElt) MarshalBinary() ([]byte, error) { + var encS [fp.Size]byte + err := e.marshalBinary(encS[:]) + return encS[:], err +} + +func (e *dElt) marshalBinary(enc []byte) error { + // Section 5.3.2 - Encode + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-03#section-5.3.2 + x, ta, tb, z := &e.p.X, &e.p.Ta, &e.p.Tb, &e.p.Z + t, u1, u2, u3 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + v, ir, rt, w, s := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + + one := fp.One() + fp.Mul(t, ta, tb) // t = ta*tb + plus, minus := *x, *t // + fp.AddSub(&plus, &minus) // (plus,minus) = (x+t,x-t) + fp.Mul(u1, &plus, &minus) // u1 = (x+t)*(x-t) + fp.Sqr(v, x) // v = u1 * ONE_MINUS_D * x0^2 + fp.Mul(v, v, &aMinusD) // + fp.Mul(v, v, u1) // + _ = fp.InvSqrt(ir, &one, v) // ir = sqrt(1/v) + fp.Mul(w, ir, u1) // rt = CT_ABS(ir * u1 * SQRT_MINUS_D) + fp.Mul(w, w, &sqrtMinusD) // + ctAbs(rt, w) // + fp.Mul(u2, rt, z) // u2 = INVSQRT_MINUS_D * rt * z0 - t0 + fp.Mul(u2, u2, &invSqrtMinusD) // + fp.Sub(u2, u2, t) // + fp.Mul(u3, x, u2) // s = CT_ABS(ONE_MINUS_D * ir * x0 * u2) + fp.Mul(u3, u3, ir) // + fp.Mul(u3, u3, &aMinusD) // + ctAbs(s, u3) // + + return fp.ToBytes(enc[:], s) +} + +func (e *dElt) UnmarshalBinary(data []byte) error { + // Section 5.3.1 - Decode + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-03#section-5.3.1 + if len(data) < fp.Size { + return io.ErrShortBuffer + } + + p := fp.P() + s := &fp.Elt{} + copy(s[:], data[:fp.Size]) + isLessThanP := isLessThan(s[:], p[:]) + isPositiveS := 1 - fp.Parity(s) + + one := fp.One() + paramD := curve.ParamD() + + x, y := &fp.Elt{}, &fp.Elt{} + ss, u1, u2, u3 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + ir, v, w := &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + + fp.Sqr(ss, s) // ss = s^2 + fp.Add(u1, &one, ss) // u1 = 1 - a*s^2 + fp.Mul(u2, ss, ¶mD) // u2 = d*s^2 + fp.Add(u2, u2, u2) // = 2*d*s^2 + fp.Add(u2, u2, u2) // = 4*d*s^2 + fp.Sqr(v, u1) // v = u1^2 = (1 + a*s^2)^2 + fp.Sub(u2, v, u2) // u2 = u1^2 - 4*d*s^2 + fp.Mul(w, u2, v) // w = u2 * u1^2 + isQR := fp.InvSqrt(ir, &one, w) // ir = sqrt(1/(u2 * u1^2)) + fp.Mul(w, s, ir) // w = ir*u1 + fp.Mul(w, w, u1) // = s*ir*u1 + fp.Mul(w, w, &sqrtMinusD) // = s*ir*u1*sqrt(-d) + fp.Add(w, w, w) // = 2*s*ir*u1*sqrt(-d) + ctAbs(u3, w) // u3 = CT_ABS(w) + fp.Mul(x, u3, ir) // x = u3 * ir * u2 * INVSQRT_MINUS_D + fp.Mul(x, x, u2) // + fp.Mul(x, x, &invSqrtMinusD) // + fp.Sub(y, &one, ss) // y = (1 - a*s^2) * ir * u1 + fp.Mul(y, y, ir) // + fp.Mul(y, y, u1) // + + b0 := isPositiveS + b1 := isLessThanP + b2 := isQR + b := uint(subtle.ConstantTimeEq(int32(4*b2+2*b1+b0), 0b111)) + fp.Cmov(&e.p.X, x, b) + fp.Cmov(&e.p.Y, y, b) + fp.Cmov(&e.p.Ta, x, b) + fp.Cmov(&e.p.Tb, y, b) + fp.Cmov(&e.p.Z, &one, b) + if b == 0 { + return ErrInvalidDecoding + } + return nil +} + +type dScl struct{ k curve.Scalar } + +func (s *dScl) String() string { return s.k.String() } +func (s *dScl) SetUint64(n uint64) { s.k.SetUint64(n) } +func (s *dScl) Set(a Scalar) Scalar { s.k = a.(*dScl).k; return s } +func (s *dScl) Copy() Scalar { return &dScl{k: s.k} } +func (s *dScl) Add(a, b Scalar) Scalar { s.k.Add(&a.(*dScl).k, &b.(*dScl).k); return s } +func (s *dScl) Sub(a, b Scalar) Scalar { s.k.Sub(&a.(*dScl).k, &b.(*dScl).k); return s } +func (s *dScl) Mul(a, b Scalar) Scalar { s.k.Mul(&a.(*dScl).k, &b.(*dScl).k); return s } +func (s *dScl) Neg(a Scalar) Scalar { s.k.Neg(&a.(*dScl).k); return s } +func (s *dScl) Inv(a Scalar) Scalar { s.k.Inv(&a.(*dScl).k); return s } +func (s *dScl) MarshalBinary() ([]byte, error) { return s.k.MarshalBinary() } +func (s *dScl) UnmarshalBinary(b []byte) error { return s.k.UnmarshalBinary(b) } +func (s *dScl) IsEqual(a Scalar) bool { return s.k.IsEqual(&a.(*dScl).k) == 1 } + +func ctAbs(z, x *fp.Elt) { + minusX := &fp.Elt{} + fp.Neg(minusX, x) + *z = *x + fp.Cmov(z, minusX, uint(fp.Parity(x))) +} + +// isLessThan returns 1 if 0 <= x < y, and assumes that slices are of the +// same length and are interpreted in little-endian order. +func isLessThan(x, y []byte) int { + i := len(x) - 1 + for i > 0 && x[i] == y[i] { + i-- + } + xi := int(x[i]) + yi := int(y[i]) + return ((xi - yi) >> (bits.UintSize - 1)) & 1 +} + +var ( + // aMinusD is paramA-paramD = (-1)-(-39081) = 39082. + aMinusD = fp.Elt{0xaa, 0x98} + // aMinusTwoD is paramA-2*paramD = (-1)-2*(-39081) = 78163. + aMinusTwoD = fp.Elt{0x53, 0x31, 0x01} + // sqrtMinusD is the smallest root of sqrt(paramD) = sqrt(39081). + sqrtMinusD = fp.Elt{ + 0x36, 0x27, 0x57, 0x45, 0x0f, 0xef, 0x42, 0x96, + 0x52, 0xce, 0x20, 0xaa, 0xf6, 0x7b, 0x33, 0x60, + 0xd2, 0xde, 0x6e, 0xfd, 0xf4, 0x66, 0x9a, 0x83, + 0xba, 0x14, 0x8c, 0x96, 0x80, 0xd7, 0xa2, 0x64, + 0x4b, 0xd5, 0xb8, 0xa5, 0xb8, 0xa7, 0xf1, 0xa1, + 0xa0, 0x6a, 0xa2, 0x2f, 0x72, 0x8d, 0xf6, 0x3b, + 0x68, 0xf7, 0x24, 0xeb, 0xfb, 0x62, 0xd9, 0x22, + } + // invSqrtMinusD is the smallest root of sqrt(1/paramD) = sqrt(1/39081). + invSqrtMinusD = fp.Elt{ + 0x2c, 0x68, 0x78, 0xb8, 0x5e, 0xbb, 0xaf, 0x53, + 0xf3, 0x94, 0x9e, 0xf1, 0x79, 0x24, 0xbb, 0xef, + 0x15, 0xba, 0x1f, 0xc2, 0xe2, 0x7e, 0x70, 0xbe, + 0x1a, 0x52, 0xa6, 0x28, 0xf1, 0x56, 0xba, 0xd6, + 0xa7, 0x27, 0x5b, 0x3a, 0x0c, 0x95, 0x90, 0x5a, + 0x07, 0xc8, 0xca, 0x0b, 0x5a, 0xe3, 0x2b, 0x90, + 0x57, 0xc0, 0x22, 0xe2, 0x52, 0x06, 0xf4, 0x6e, + } +) diff --git a/group/decaf_test.go b/group/decaf_test.go new file mode 100644 index 00000000..4313f415 --- /dev/null +++ b/group/decaf_test.go @@ -0,0 +1,128 @@ +package group + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "io/ioutil" + "os" + "testing" + + "github.com/cloudflare/circl/internal/test" + fp "github.com/cloudflare/circl/math/fp448" +) + +type testJSONFile struct { + Multiples []string `json:"multiples"` + Invalid []struct { + Description string `json:"description"` + Encodings []string `json:"encodings"` + } `json:"invalid"` + Oneway []struct { + Input string `json:"input"` + Output string `json:"output"` + } `json:"oneway"` +} + +func (kat *testJSONFile) readFile(t *testing.T, fileName string) { + jsonFile, err := os.Open(fileName) + if err != nil { + t.Fatalf("File %v can not be opened. Error: %v", fileName, err) + } + defer jsonFile.Close() + input, _ := ioutil.ReadAll(jsonFile) + + err = json.Unmarshal(input, &kat) + if err != nil { + t.Fatalf("File %v can not be loaded. Error: %v", fileName, err) + } +} + +func (kat *testJSONFile) multiples(t *testing.T) { + k := Decaf448.NewScalar() + got := Decaf448.NewElement() + want := Decaf448.NewElement() + + for i, vi := range kat.Multiples { + k.SetUint64(uint64(i)) + got.MulGen(k) + gotEnc, err := got.MarshalBinary() + test.CheckNoErr(t, err, "marshaling element") + + wantEnc, err := hex.DecodeString(vi) + test.CheckNoErr(t, err, "bad hex encoding") + err = want.UnmarshalBinary(wantEnc) + test.CheckNoErr(t, err, "bad element unmarshaling") + + if !got.IsEqual(want) { + test.ReportError(t, got, want, i) + } + + if !bytes.Equal(gotEnc, wantEnc) { + test.ReportError(t, gotEnc, wantEnc, i) + } + } +} + +func (kat *testJSONFile) invalid(t *testing.T) { + P := Decaf448.NewElement() + for _, vi := range kat.Invalid { + for _, vj := range vi.Encodings { + enc, err := hex.DecodeString(vj) + test.CheckNoErr(t, err, "bad hex encoding") + + err = P.UnmarshalBinary(enc) + test.CheckIsErr(t, err, "should trigger error: "+vi.Description) + } + } +} + +func (kat *testJSONFile) oneway(t *testing.T) { + var g decaf448 + var msg [2 * fp.Size]byte + for _, vi := range kat.Oneway { + input, err := hex.DecodeString(vi.Input) + test.CheckNoErr(t, err, "bad hex encoding") + test.CheckOk(len(input) == 2*fp.Size, "bad input size", t) + + copy(msg[:], input) + P := g.oneway(&msg) + got, err := P.MarshalBinary() + test.CheckNoErr(t, err, "bad element marshalling") + + want, err := hex.DecodeString(vi.Output) + test.CheckNoErr(t, err, "bad hex encoding") + + if !bytes.Equal(got, want) { + test.ReportError(t, got, want, input) + } + } +} + +func TestDecaf(t *testing.T) { + var kat testJSONFile + kat.readFile(t, "testdata/decaf_vectors.json") + + t.Run("multiples", kat.multiples) + t.Run("invalid", kat.invalid) + t.Run("oneway", kat.oneway) +} + +func TestDecafInvalid(t *testing.T) { + bigS := fp.P() + negativeS := fp.Elt{1} // the smallest s that is negative + nonQR := fp.Elt{4} // the shortest s such that (a^2s^4 + (2a - 4d)*s^2 + 1) is non-QR. + + badEncodings := [][]byte{ + {}, // wrong size input + bigS[:], // s is out of the interval [0,p-1]. + negativeS[:], // s is not positive + nonQR[:], // s=4 and (a^2s^4 + (2a - 4d)*s^2 + 1) is non-QR. + } + + e := decaf448{}.NewElement() + for _, enc := range badEncodings { + err := e.UnmarshalBinary(enc) + test.CheckIsErr(t, err, "should trigger an error") + } +} diff --git a/group/group.go b/group/group.go index fb56eba2..0498037f 100644 --- a/group/group.go +++ b/group/group.go @@ -61,6 +61,6 @@ type Scalar interface { } var ( - ErrType = errors.New("type mismatch") - ErrUnmarshal = errors.New("error unmarshaling") + ErrType = errors.New("group: type mismatch") + ErrInvalidDecoding = errors.New("group: invalid decoding") ) diff --git a/group/group_test.go b/group/group_test.go index 76a0b1f8..e15e5771 100644 --- a/group/group_test.go +++ b/group/group_test.go @@ -15,6 +15,7 @@ var allGroups = []group.Group{ group.P384, group.P521, group.Ristretto255, + group.Decaf448, } func TestGroup(t *testing.T) { diff --git a/group/short.go b/group/short.go index 151a749e..d759e867 100644 --- a/group/short.go +++ b/group/short.go @@ -199,17 +199,17 @@ func (e *wElt) UnmarshalBinary(b []byte) error { case l == 1+byteLen && (b[0] == 0x02 || b[0] == 0x03): // compressed x, y := elliptic.UnmarshalCompressed(e.wG.c, b) if x == nil { - return ErrUnmarshal + return ErrInvalidDecoding } e.x, e.y = x, y case l == 1+2*byteLen && b[0] == 0x04: // uncompressed x, y := elliptic.Unmarshal(e.wG.c, b) if x == nil { - return ErrUnmarshal + return ErrInvalidDecoding } e.x, e.y = x, y default: - return ErrUnmarshal + return ErrInvalidDecoding } return nil } diff --git a/group/testdata/decaf_vectors.json b/group/testdata/decaf_vectors.json new file mode 100644 index 00000000..7eeba49e --- /dev/null +++ b/group/testdata/decaf_vectors.json @@ -0,0 +1,88 @@ +{ + "multiples": [ + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "6666666666666666666666666666666666666666666666666666666633333333333333333333333333333333333333333333333333333333", + "c898eb4f87f97c564c6fd61fc7e49689314a1f818ec85eeb3bd5514ac816d38778f69ef347a89fca817e66defdedce178c7cc709b2116e75", + "a0c09bf2ba7208fda0f4bfe3d0f5b29a543012306d43831b5adc6fe7f8596fa308763db15468323b11cf6e4aeb8c18fe44678f44545a69bc", + "b46f1836aa287c0a5a5653f0ec5ef9e903f436e21c1570c29ad9e5f596da97eeaf17150ae30bcb3174d04bc2d712c8c7789d7cb4fda138f4", + "1c5bbecf4741dfaae79db72dface00eaaac502c2060934b6eaaeca6a20bd3da9e0be8777f7d02033d1b15884232281a41fc7f80eed04af5e", + "86ff0182d40f7f9edb7862515821bd67bfd6165a3c44de95d7df79b8779ccf6460e3c68b70c16aaa280f2d7b3f22d745b97a89906cfc476c", + "502bcb6842eb06f0e49032bae87c554c031d6d4d2d7694efbf9c468d48220c50f8ca28843364d70cee92d6fe246e61448f9db9808b3b2408", + "0c9810f1e2ebd389caa789374d78007974ef4d17227316f40e578b336827da3f6b482a4794eb6a3975b971b5e1388f52e91ea2f1bcb0f912", + "20d41d85a18d5657a29640321563bbd04c2ffbd0a37a7ba43a4f7d263ce26faf4e1f74f9f4b590c69229ae571fe37fa639b5b8eb48bd9a55", + "e6b4b8f408c7010d0601e7eda0c309a1a42720d6d06b5759fdc4e1efe22d076d6c44d42f508d67be462914d28b8edce32e7094305164af17", + "be88bbb86c59c13d8e9d09ab98105f69c2d1dd134dbcd3b0863658f53159db64c0e139d180f3c89b8296d0ae324419c06fa87fc7daaf34c1", + "a456f9369769e8f08902124a0314c7a06537a06e32411f4f93415950a17badfa7442b6217434a3a05ef45be5f10bd7b2ef8ea00c431edec5", + "186e452c4466aa4383b4c00210d52e7922dbf9771e8b47e229a9b7b73c8d10fd7ef0b6e41530f91f24a3ed9ab71fa38b98b2fe4746d51d68", + "4ae7fdcae9453f195a8ead5cbe1a7b9699673b52c40ab27927464887be53237f7f3a21b938d40d0ec9e15b1d5130b13ffed81373a53e2b43", + "841981c3bfeec3f60cfeca75d9d8dc17f46cf0106f2422b59aec580a58f342272e3a5e575a055ddb051390c54c24c6ecb1e0aceb075f6056" + ], + "invalid": [ + { + "description": "non-canonical field encodings", + "encodings": [ + "8e24f838059ee9fef1e209126defe53dcd74ef9b6304601c6966099effffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "86fcc7212bd4a0b980928666dc28c444a605ef38e09fb569e28d4443ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "866d54bd4c4ff41a55d4eefdbeca73cbd653c7bd3135b383708ec0bdffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "4a380ccdab9c86364a89e77a464d64f9157538cfdfa686adc0d5ece4ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "f22d9d4c945dd44d11e0b1d3d3d358d959b4844d83b08c44e659d79fffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "8cdffc681aa99e9c818c8ef4c3808b58e86acdef1ab68c8477af185bffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0e1c12ac7b5920effbd044e897c57634e2d05b5c27f8fa3df8a086a1ffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ] + }, + { + "description": "negative field elements", + "encodings": [ + "15141bd2121837ef71a0016bd11be757507221c26542244f23806f3fd3496b7d4c36826276f3bf5deea2c60c4fa4cec69946876da497e795", + "455d380238434ab740a56267f4f46b7d2eb2dd8ee905e51d7b0ae8a6cb2bae501e67df34ab21fa45946068c9f233939b1d9521a998b7cb93", + "810b1d8e8bf3a9c023294bbfd3d905a97531709bdc0f42390feedd7010f77e98686d400c9c86ed250ceecd9de0a18888ffecda0f4ea1c60d", + "d3af9cc41be0e5de83c0c6273bedcb9351970110044a9a41c7b9b2267cdb9d7bf4dc9c2fdb8bed32878184604f1d9944305a8df4274ce301", + "9312bcab09009e4330ff89c4bc1e9e000d863efc3c863d3b6c507a40fd2cdefde1bf0892b4b5ed9780b91ed1398fb4a7344c605aa5efda74", + "53d11bce9e62a29d63ed82ae93761bdd76e38c21e2822d6ebee5eb1c5b8a03eaf9df749e2490eda9d8ac27d1f71150de93668074d18d1c3a", + "697c1aed3cd8858515d4be8ac158b229fe184d79cb2b06e49210a6f3a7cd537bcd9bd390d96c4ab6a4406da5d93640726285370cfa95df80" + ] + }, + { + "description": "non-square x^2", + "encodings": [ + "58ad48715c9a102569b68b88362a4b0645781f5a19eb7e59c6a4686fd0f0750ff42e3d7af1ab38c29d69b670f31258919c9fdbf6093d06c0", + "8ca37ee2b15693f06e910cf43c4e32f1d5551dda8b1e48cb6ddd55e440dbc7b296b601919a4e4069f59239ca247ff693f7daa42f086122b1", + "982c0ec7f43d9f97c0a74b36db0abd9ca6bfb98123a90782787242c8a523cdc76df14a910d54471127e7662a1059201f902940cd39d57af5", + "baa9ab82d07ca282b968a911a6c3728d74bf2fe258901925787f03ee4be7e3cb6684fd1bcfe5071a9a974ad249a4aaa8ca81264216c68574", + "2ed9ffe2ded67a372b181ac524996402c42970629db03f5e8636cbaf6074b523d154a7a8c4472c4c353ab88cd6fec7da7780834cc5bd5242", + "f063769e4241e76d815800e4933a3a144327a30ec40758ad3723a788388399f7b3f5d45b6351eb8eddefda7d5bff4ee920d338a8b89d8b63", + "5a0104f1f55d152ceb68bc138182499891d90ee8f09b40038ccc1e07cb621fd462f781d045732a4f0bda73f0b2acf94355424ff0388d4b9c" + ] + } + ], + "oneway": [ + { + "input": "cbb8c991fd2f0b7e1913462d6463e4fd2ce4ccdd28274dc2ca1f4165d5ee6cdccea57be3416e166fd06718a31af45a2f8e987e301be59ae6673e963001dbbda80df47014a21a26d6c7eb4ebe0312aa6fffb8d1b26bc62ca40ed51f8057a635a02c2b8c83f48fa6a2d70f58a1185902c0", + "output": "0c709c9607dbb01c94513358745b7c23953d03b33e39c7234e268d1d6e24f34014ccbc2216b965dd231d5327e591dc3c0e8844ccfd568848" + }, + { + "input": "b6d8da654b13c3101d6634a231569e6b85961c3f4b460a08ac4a5857069576b64428676584baa45b97701be6d0b0ba18ac28d443403b45699ea0fbd1164f5893d39ad8f29e48e399aec5902508ea95e33bc1e9e4620489d684eb5c26bc1ad1e09aba61fabc2cdfee0b6b6862ffc8e55a", + "output": "76ab794e28ff1224c727fa1016bf7f1d329260b7218a39aea2fdb17d8bd9119017b093d641cedf74328c327184dc6f2a64bd90eddccfcdab" + }, + { + "input": "36a69976c3e5d74e4904776993cbac27d10f25f5626dd45c51d15dcf7b3e6a5446a6649ec912a56895d6baa9dc395ce9e34b868d9fb2c1fc72eb6495702ea4f446c9b7a188a4e0826b1506b0747a6709f37988ff1aeb5e3788d5076ccbb01a4bc6623c92ff147a1e21b29cc3fdd0e0f4", + "output": "c8d7ac384143500e50890a1c25d643343accce584caf2544f9249b2bf4a6921082be0e7f3669bb5ec24535e6c45621e1f6dec676edd8b664" + }, + { + "input": "d5938acbba432ecd5617c555a6a777734494f176259bff9dab844c81aadcf8f7abd1a9001d89c7008c1957272c1786a4293bb0ee7cb37cf3988e2513b14e1b75249a5343643d3c5e5545a0c1a2a4d3c685927c38bc5e5879d68745464e2589e000b31301f1dfb7471a4f1300d6fd0f99", + "output": "62beffc6b8ee11ccd79dbaac8f0252c750eb052b192f41eeecb12f2979713b563caf7d22588eca5e80995241ef963e7ad7cb7962f343a973" + }, + { + "input": "4dec58199a35f531a5f0a9f71a53376d7b4bdd6bbd2904234a8ea65bbacbce2a542291378157a8f4be7b6a092672a34d85e473b26ccfbd4cdc6739783dc3f4f6ee3537b7aed81df898c7ea0ae89a15b5559596c2a5eeacf8b2b362f3db2940e3798b63203cae77c4683ebaed71533e51", + "output": "f4ccb31d263731ab88bed634304956d2603174c66da38742053fa37dd902346c3862155d68db63be87439e3d68758ad7268e239d39c4fd3b" + }, + { + "input": "df2aa1536abb4acab26efa538ce07fd7bca921b13e17bc5ebcba7d1b6b733deda1d04c220f6b5ab35c61b6bcb15808251cab909a01465b8ae3fc770850c66246d5a9eae9e2877e0826e2b8dc1bc08009590bc6778a84e919fbd28e02a0f9c49b48dc689eb5d5d922dc01469968ee81b5", + "output": "7e79b00e8e0a76a67c0040f62713b8b8c6d6f05e9c6d02592e8a22ea896f5deacc7c7df5ed42beae6fedb9000285b482aa504e279fd49c32" + }, + { + "input": "e9fb440282e07145f1f7f5ecf3c273212cd3d26b836b41b02f108431488e5e84bd15f2418b3d92a3380dd66a374645c2a995976a015632d36a6c2189f202fc766e1c82f50ad9189be190a1f0e8f9b9e69c9c18cc98fdd885608f68bf0fdedd7b894081a63f70016a8abf04953affbefa", + "output": "20b171cb16be977f15e013b9752cf86c54c631c4fc8cbf7c03c4d3ac9b8e8640e7b0e9300b987fe0ab5044669314f6ed1650ae037db853f1" + } + ] +} diff --git a/math/fp448/fp.go b/math/fp448/fp.go index a5e36600..d3dc98fc 100644 --- a/math/fp448/fp.go +++ b/math/fp448/fp.go @@ -2,6 +2,7 @@ package fp448 import ( + "crypto/subtle" "errors" "github.com/cloudflare/circl/internal/conv" @@ -39,11 +40,17 @@ func ToBytes(b []byte, x *Elt) error { return nil } -// IsZero returns true if x is equal to 0. -func IsZero(x *Elt) bool { Modp(x); return *x == Elt{} } +// IsEqual returns 1 if x is equal to y; otherwise 0. +func IsEqual(x, y *Elt) int { Modp(x); Modp(y); return subtle.ConstantTimeCompare(x[:], y[:]) } -// IsOne returns true if x is equal to 1. -func IsOne(x *Elt) bool { Modp(x); return *x == Elt{1} } +// IsZero returns 1 if x is equal to 0; otherwise 0. +func IsZero(x *Elt) int { Modp(x); z := Elt{}; return subtle.ConstantTimeCompare(x[:], z[:]) } + +// IsOne returns true if x is equal to 1; otherwise 0. +func IsOne(x *Elt) int { Modp(x); o := Elt{1}; return subtle.ConstantTimeCompare(x[:], o[:]) } + +// Parity returns the last bit of x. +func Parity(x *Elt) int { Modp(x); return int(x[0] & 1) } // SetOne assigns x=1. func SetOne(x *Elt) { *x = Elt{1} } @@ -58,9 +65,9 @@ func Neg(z, x *Elt) { Sub(z, &p, x) } func Modp(z *Elt) { Sub(z, z, &p) } // InvSqrt calculates z = sqrt(x/y) iff x/y is a quadratic-residue. If so, -// isQR = true; otherwise, isQR = false, since x/y is a quadratic non-residue, +// isQR = 1; otherwise, isQR = 0, since x/y is a quadratic non-residue, // and z = sqrt(-x/y). -func InvSqrt(z, x, y *Elt) (isQR bool) { +func InvSqrt(z, x, y *Elt) (isQR int) { // First note that x^(2(k+1)) = x^(p-1)/2 * x = legendre(x) * x // so that's x if x is a quadratic residue and -x otherwise. // Next, y^(6k+3) = y^(4k+2) * y^(2k+1) = y^(p-1) * y^((p-1)/2) = legendre(y). diff --git a/math/fp448/fp_test.go b/math/fp448/fp_test.go index b867d6fe..0fca8e4d 100644 --- a/math/fp448/fp_test.go +++ b/math/fp448/fp_test.go @@ -196,21 +196,21 @@ func TestModp(t *testing.T) { func TestIsZero(t *testing.T) { var x Elt got := IsZero(&x) - want := true + want := 1 if got != want { test.ReportError(t, got, want, x) } SetOne(&x) got = IsZero(&x) - want = false + want = 0 if got != want { test.ReportError(t, got, want, x) } x = P() got = IsZero(&x) - want = true + want = 1 if got != want { test.ReportError(t, got, want, x) } @@ -299,7 +299,7 @@ func TestInvSqrt(t *testing.T) { exp := big.NewInt(1) exp.Add(p, exp).Rsh(exp, 2) var frac, root, sqRoot big.Int - var wantQR bool + var wantQR int var want *big.Int for i := 0; i < numTests; i++ { _, _ = rand.Read(x[:]) @@ -317,13 +317,13 @@ func TestInvSqrt(t *testing.T) { if sqRoot.Cmp(&frac) == 0 { want = &root - wantQR = true + wantQR = 1 } else { want = big.NewInt(0) - wantQR = false + wantQR = 0 } - if wantQR { + if wantQR == 1 { if gotQR != wantQR || got.Cmp(want) != 0 { test.ReportError(t, got, want, x, y) } diff --git a/sign/ed448/ed448.go b/sign/ed448/ed448.go index 6a26e586..3088d264 100644 --- a/sign/ed448/ed448.go +++ b/sign/ed448/ed448.go @@ -28,10 +28,8 @@ import ( "crypto" cryptoRand "crypto/rand" "crypto/subtle" - "errors" "fmt" "io" - "strconv" "github.com/cloudflare/circl/ecc/goldilocks" "github.com/cloudflare/circl/internal/sha3" @@ -66,7 +64,8 @@ type SignerOptions struct { // Its length must be less or equal than 255 bytes. Context string - // Scheme is an identifier for choosing a signature scheme. + // Scheme is an identifier for choosing a signature scheme. The zero value + // is ED448. Scheme SchemeID } @@ -98,9 +97,9 @@ func (priv PrivateKey) Equal(x crypto.PrivateKey) bool { // Public returns the PublicKey corresponding to priv. func (priv PrivateKey) Public() crypto.PublicKey { - publicKey := make([]byte, PublicKeySize) + publicKey := make(PublicKey, PublicKeySize) copy(publicKey, priv[SeedSize:]) - return PublicKey(publicKey) + return publicKey } // Seed returns the private key seed corresponding to priv. It is provided for @@ -155,7 +154,7 @@ func (priv PrivateKey) Sign( case scheme == ED448Ph && opts.HashFunc() == crypto.Hash(0): return SignPh(priv, message, ctx), nil default: - return nil, errors.New("ed448: bad hash algorithm") + return nil, fmt.Errorf("ed448: bad hash algorithm") } } @@ -183,14 +182,14 @@ func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) { // with RFC 8032. RFC 8032's private keys correspond to seeds in this // package. func NewKeyFromSeed(seed []byte) PrivateKey { - privateKey := make([]byte, PrivateKeySize) + privateKey := make(PrivateKey, PrivateKeySize) newKeyFromSeed(privateKey, seed) return privateKey } -func newKeyFromSeed(privateKey, seed []byte) { +func newKeyFromSeed(privateKey PrivateKey, seed []byte) { if l := len(seed); l != SeedSize { - panic("ed448: bad seed length: " + strconv.Itoa(l)) + panic(fmt.Errorf("ed448: bad seed length: %v", l)) } var h [hashSize]byte @@ -200,13 +199,20 @@ func newKeyFromSeed(privateKey, seed []byte) { s := &goldilocks.Scalar{} deriveSecretScalar(s, h[:paramB]) + var P goldilocks.Point + P.ScalarBaseMult(s) + var encP [goldilocks.EncodingSize]byte + if err := P.Encode(&encP); err != nil { + panic(err) + } + copy(privateKey[:SeedSize], seed) - _ = goldilocks.Curve{}.ScalarBaseMult(s).ToBytes(privateKey[SeedSize:]) + copy(privateKey[SeedSize:], encP[:]) } func signAll(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash bool) { if len(ctx) > ContextMaxSize { - panic(fmt.Errorf("ed448: bad context length: " + strconv.Itoa(len(ctx)))) + panic(fmt.Errorf("ed448: bad context length: %v", len(ctx))) } H := sha3.NewShake256() @@ -232,42 +238,40 @@ func signAll(signature []byte, privateKey PrivateKey, message, ctx []byte, preHa // 2. Compute SHAKE256(dom4(F, C) || prefix || PH(M), 114). var rPM [hashSize]byte - H.Reset() - writeDom(&H, ctx, preHash) - _, _ = H.Write(prefix) _, _ = H.Write(PHM) _, _ = H.Read(rPM[:]) // 3. Compute the point [r]B. r := &goldilocks.Scalar{} - r.FromBytes(rPM[:]) - R := (&[paramB]byte{})[:] - if err := (goldilocks.Curve{}.ScalarBaseMult(r).ToBytes(R)); err != nil { + r.FromBytesLE(rPM[:]) + var R goldilocks.Point + R.ScalarBaseMult(r) + var encR [goldilocks.EncodingSize]byte + if err := R.Encode(&encR); err != nil { panic(err) } + // 4. Compute SHAKE256(dom4(F, C) || R || A || PH(M), 114) var hRAM [hashSize]byte - H.Reset() - writeDom(&H, ctx, preHash) - - _, _ = H.Write(R) + _, _ = H.Write(encR[:]) _, _ = H.Write(privateKey[SeedSize:]) _, _ = H.Write(PHM) _, _ = H.Read(hRAM[:]) // 5. Compute S = (r + k * s) mod order. k := &goldilocks.Scalar{} - k.FromBytes(hRAM[:]) + k.FromBytesLE(hRAM[:]) S := &goldilocks.Scalar{} S.Mul(k, s) S.Add(S, r) + encS := S.ToBytesLE() // 6. The signature is the concatenation of R and S. - copy(signature[:paramB], R[:]) - copy(signature[paramB:], S[:]) + copy(signature[:paramB], encR[:]) + copy(signature[paramB:], encS[:]) } // Sign signs the message with privateKey and returns a signature. @@ -299,7 +303,10 @@ func verify(public PublicKey, message, signature, ctx []byte, preHash bool) bool return false } - P, err := goldilocks.FromBytes(public) + var encPublic [goldilocks.EncodingSize]byte + copy(encPublic[:], public) + P := &goldilocks.Point{} + err := P.Decode(&encPublic) if err != nil { return false } @@ -318,24 +325,28 @@ func verify(public PublicKey, message, signature, ctx []byte, preHash bool) bool } var hRAM [hashSize]byte - R := signature[:paramB] + sigR := signature[:paramB] writeDom(&H, ctx, preHash) - _, _ = H.Write(R) + _, _ = H.Write(sigR) _, _ = H.Write(public) _, _ = H.Write(PHM) _, _ = H.Read(hRAM[:]) k := &goldilocks.Scalar{} - k.FromBytes(hRAM[:]) + k.FromBytesLE(hRAM[:]) S := &goldilocks.Scalar{} - S.FromBytes(signature[paramB:]) + S.FromBytesLE(signature[paramB:]) - encR := (&[paramB]byte{})[:] P.Neg() - _ = goldilocks.Curve{}.CombinedMult(S, k, P).ToBytes(encR) - return bytes.Equal(R, encR) + var R goldilocks.Point + R.CombinedMult(S, k, P) + var encR [goldilocks.EncodingSize]byte + if err = R.Encode(&encR); err != nil { + panic(err) + } + return bytes.Equal(sigR, encR[:]) } // VerifyAny returns true if the signature is valid. Failure cases are invalid @@ -385,12 +396,12 @@ func deriveSecretScalar(s *goldilocks.Scalar, h []byte) { h[0] &= 0xFC // The two least significant bits of the first octet are cleared, h[paramB-1] = 0x00 // all eight bits the last octet are cleared, and h[paramB-2] |= 0x80 // the highest bit of the second to last octet is set. - s.FromBytes(h[:paramB]) + s.FromBytesLE(h[:paramB]) } // isLessThanOrder returns true if 0 <= x < order and if the last byte of x is zero. func isLessThanOrder(x []byte) bool { - order := goldilocks.Curve{}.Order() + order := goldilocks.Order() i := len(order) - 1 for i > 0 && x[i] == order[i] { i-- @@ -398,7 +409,8 @@ func isLessThanOrder(x []byte) bool { return x[paramB-1] == 0 && x[i] < order[i] } -func writeDom(h io.Writer, ctx []byte, preHash bool) { +func writeDom(h sha3.ShakeHash, ctx []byte, preHash bool) { + h.Reset() dom4 := "SigEd448" _, _ = h.Write([]byte(dom4)) diff --git a/sign/ed448/ed448_test.go b/sign/ed448/ed448_test.go index 91349609..0b1f061d 100644 --- a/sign/ed448/ed448_test.go +++ b/sign/ed448/ed448_test.go @@ -12,15 +12,6 @@ import ( "github.com/cloudflare/circl/sign/ed448" ) -type zeroReader struct{} - -func (zeroReader) Read(buf []byte) (int, error) { - for i := range buf { - buf[i] = 0 - } - return len(buf), nil -} - func TestEqual(t *testing.T) { public, private, _ := ed448.GenerateKey(rand.Reader) @@ -213,9 +204,8 @@ func TestErrors(t *testing.T) { } func BenchmarkKeyGeneration(b *testing.B) { - var zero zeroReader for i := 0; i < b.N; i++ { - if _, _, err := ed448.GenerateKey(zero); err != nil { + if _, _, err := ed448.GenerateKey(rand.Reader); err != nil { b.Fatal(err) } } @@ -230,8 +220,7 @@ func BenchmarkNewKeyFromSeed(b *testing.B) { } func BenchmarkSigning(b *testing.B) { - var zero zeroReader - _, priv, err := ed448.GenerateKey(zero) + _, priv, err := ed448.GenerateKey(rand.Reader) if err != nil { b.Fatal(err) } @@ -245,8 +234,7 @@ func BenchmarkSigning(b *testing.B) { } func BenchmarkVerification(b *testing.B) { - var zero zeroReader - pub, priv, err := ed448.GenerateKey(zero) + pub, priv, err := ed448.GenerateKey(rand.Reader) if err != nil { b.Fatal(err) } @@ -268,7 +256,7 @@ func BenchmarkEd448Ph(b *testing.B) { ctx := "" b.ResetTimer() for i := 0; i < b.N; i++ { - _ = ed448.SignPh(key, msg, ctx) + ed448.SignPh(key, msg, ctx) } }) b.Run("Verify", func(b *testing.B) {