diff --git a/README.md b/README.md index ed45bfbd0..0827a2426 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,12 @@ Version numbers are [Semvers](https://semver.org/). We release a minor version f | PQ Key Exchange | SIDH | SIDH provide key exchange mechanisms using ephemeral keys. | Post-quantum key exchange in TLS | | PQ Key Exchange | cSIDH | Isogeny based drop-in replacement for Diffie–Hellman | Post-Quantum Key exchange. | | PQ KEM | SIKE | SIKE is a key encapsulation mechanism (KEM). | Post-quantum key exchange in TLS | +| PQ Digital Signatures | Dilithium, Hybrid modes | Lattice (Module LWE) based signature scheme | Post-Quantum PKI | | Key Exchange | X25519, X448 | RFC-7748 provides new key exchange mechanisms based on Montgomery elliptic curves. | TLS 1.3. Secure Shell. | | Key Exchange | FourQ | One of the fastest elliptic curves at 128-bit security level. | Experimental for key agreement and digital signatures. | | Key Exchange / Digital signatures | P-384 | Our optimizations reduce the burden when moving from P-256 to P-384. | ECDSA and ECDH using Suite B at top secret level. | | Digital Signatures | Ed25519, Ed448 | RFC-8032 provides new signature schemes based on Edwards curves. | Digital certificates and authentication. | -| PQ Digital Signatures | Dilithium, Hybrid modes | Lattice (Module LWE) based signature scheme | Post-Quantum PKI | +| Groups | Decaf | Prime-order groups. | Protocols based on the Discrete Logarithm Problem. | ### Work in Progress diff --git a/ecc/decaf/constants.go b/ecc/decaf/constants.go new file mode 100644 index 000000000..a57435a79 --- /dev/null +++ b/ecc/decaf/constants.go @@ -0,0 +1,28 @@ +package decaf + +import ( + "errors" + + fp "github.com/cloudflare/circl/math/fp448" +) + +// DecafEncodingSize is the size (in bytes) of an encoded Decaf element. +const EncodingSize = fp.Size + +// ErrInvalidDecoding alerts of an error during decoding a point. +var ErrInvalidDecoding = errors.New("invalid decoding") + +var ( + // aMinusD is paramA-paramD = (-1)-(-39082) = 39081. + aMinusD = fp.Elt{0xa9, 0x98} + // sqrtAMinusD is the smallest root of sqrt(paramA-paramD) = sqrt(39081). + sqrtAMinusD = 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, + } +) diff --git a/ecc/decaf/decaf.go b/ecc/decaf/decaf.go new file mode 100644 index 000000000..62278dc2c --- /dev/null +++ b/ecc/decaf/decaf.go @@ -0,0 +1,191 @@ +// Package decaf provides a prime-order group derived from a quotient of +// Edwards curves. +// +// 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. +// +// Version +// +// This implementation uses Decaf v1.0 of the encoding (see (4,5) for a complete +// specification). +// +// 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://sourceforge.net/p/ed448goldilocks/code/ci/v1.0/tree/ +// +// (5) https://mailarchive.ietf.org/arch/msg/cfrg/S4YUTt_5eD4kwYbDuhEK0tXT1aM/ +package decaf + +import ( + "unsafe" + + "github.com/cloudflare/circl/internal/ted448" + fp "github.com/cloudflare/circl/math/fp448" +) + +// Decaf v1.0 of the encoding. +const Version = "v1.0" + +// Elt is an element of the Decaf group. It must be always initialized using +// one of the Decaf functions. +type Elt struct{ p ted448.Point } + +// Scalar represents a positive integer stored in little-endian order. +type Scalar = ted448.Scalar + +func (e Elt) String() string { return e.p.String() } + +// IsValid returns True if a is a valid element of the group. +func IsValid(a *Elt) bool { return ted448.IsOnCurve(&a.p) } + +// Identity returns the identity element of the group. +func Identity() *Elt { return &Elt{ted448.Identity()} } + +// Generator returns the generator element of the group. +func Generator() *Elt { return &Elt{ted448.Generator()} } + +// Order returns a scalar with the order of the group. +func Order() Scalar { return ted448.Order() } + +// Neg calculates c=-a, where - is the inverse of the group operation. +func Neg(c, a *Elt) { c.p = a.p; c.p.Neg() } + +// Add calculates c=a+b, where + is the group operation. +func Add(c, a, b *Elt) { q := a.p; q.Add(&b.p); c.p = q } + +// Double calculates c=a+a, where + is the group operation. +func Double(c, a *Elt) { c.p = a.p; c.p.Double() } + +// Mul calculates c=n*a, where * is scalar multiplication on the group. +func Mul(c *Elt, n *Scalar, a *Elt) { ted448.ScalarMult(&c.p, n, &a.p) } + +// MulGen calculates c=n*g, where * is scalar multiplication on the group, +// and g is the generator of the group. +func MulGen(c *Elt, n *Scalar) { ted448.ScalarBaseMult(&c.p, n) } + +// IsIdentity returns True if e is the identity of the group. +func (e *Elt) IsIdentity() bool { return fp.IsZero(&e.p.X) && !fp.IsZero(&e.p.Y) && !fp.IsZero(&e.p.Z) } + +// IsEqual returns True if e=a, where = is an equivalence relation. +func (e *Elt) IsEqual(a *Elt) bool { + l, r := &fp.Elt{}, &fp.Elt{} + fp.Mul(l, &e.p.X, &a.p.Y) + fp.Mul(r, &a.p.X, &e.p.Y) + fp.Sub(l, l, r) + return fp.IsZero(l) +} + +// UnmarshalBinary interprets the first EncodingSize bytes passed in data, and +// returns a Decaf element. +func (e *Elt) UnmarshalBinary(data []byte) error { + if len(data) < EncodingSize { + return ErrInvalidDecoding + } + + s := &fp.Elt{} + copy(s[:], data[:EncodingSize]) + p := fp.P() + isLessThanP := isLessThan(s[:], p[:]) + isPositiveS := fp.Parity(s) == 0 + + den, num := &fp.Elt{}, &fp.Elt{} + isr, altx, t0 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + x, y := &fp.Elt{}, &fp.Elt{} + one := fp.One() + paramD := ted448.ParamD() + fp.Sqr(t0, s) // t0 = s^2 + fp.Sub(den, &one, t0) // den = 1 + a*s^2 + fp.Add(y, &one, t0) // y = 1 - a*s^2 + fp.Mul(num, t0, ¶mD) // num = d*s^2 + fp.Add(num, num, num) // = 2*d*s^2 + fp.Add(num, num, num) // = 4*d*s^2 + fp.Sqr(t0, den) // t0 = den^2 = (1 + a*s^2)^2 + fp.Sub(num, t0, num) // num = den^2 - 4*d*s^2 + fp.Mul(t0, t0, num) // t0 = den^2*num + isQR := fp.InvSqrt(isr, &one, t0) // isr = 1/(den*sqrt(num)) + fp.Mul(altx, isr, den) // altx = isr*den + fp.Mul(altx, altx, s) // = s*isr*den + fp.Add(altx, altx, altx) // = 2*s*isr*den + fp.Mul(altx, altx, &sqrtAMinusD) // = 2*s*isr*den*sqrt(A-D) + isNegX := fp.Parity(altx) // isNeg = sgn(altx) + fp.Neg(t0, isr) // t0 = -isr + fp.Cmov(isr, t0, uint(isNegX)) // if altx is negative then isr = -isr + fp.Mul(t0, isr, den) // t0 = isr*den + fp.Mul(x, t0, isr) // x = isr^2*den + fp.Mul(x, x, num) // x = isr^2*den*num + fp.Mul(x, x, s) // x = s*isr^2*den*num + fp.Add(x, x, x) // x = 2*s*isr^2*den*num + fp.Mul(y, y, t0) // y = (1 - a*s^2)*isr*den + + isValid := isPositiveS && isLessThanP && isQR + b := uint(*((*byte)(unsafe.Pointer(&isValid)))) + 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 !isValid { + return ErrInvalidDecoding + } + return nil +} + +// MarshalBinary returns a unique encoding of the element e. +func (e *Elt) MarshalBinary() ([]byte, error) { + var encS [EncodingSize]byte + err := e.marshalBinary(encS[:]) + return encS[:], err +} + +func (e *Elt) marshalBinary(enc []byte) error { + x, ta, tb, z := &e.p.X, &e.p.Ta, &e.p.Tb, &e.p.Z + t, t2, s := &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + one := fp.One() + fp.Mul(t, ta, tb) // t = ta*tb + t0, t1 := *x, *t // (t0,t1) = (x,t) + fp.AddSub(&t0, &t1) // (t0,t1) = (x+t,x-t) + fp.Mul(&t1, &t0, &t1) // t1 = num = (x+t)*(x-t) = x^2*(z^2-y^2)/z^2 + fp.Mul(&t0, &t1, &aMinusD) // t0 = (a-d)*(x+t)*(x-t) = (a-d)*x^2*(z^2-y^2)/z^2 + fp.Sqr(t2, x) // t2 = x^2 + fp.Mul(&t0, &t0, t2) // t0 = x^2*(a-d)*(x+t)*(x-t) = (a-d)*x^4*(z^2-y^2)/z^2 + fp.InvSqrt(&t0, &one, &t0) // t0 = isr = z/(x^2*sqrt((a-d)*(z^2-y^2))) + fp.Mul(&t1, &t1, &t0) // t1 = ratio = (z^2-y^2)/(z*sqrt((a-d)*(z^2-y^2))) + fp.Mul(t2, &t1, &sqrtAMinusD) // t2 = altx = sqrt((z^2-y^2))/z + isNeg := fp.Parity(t2) // isNeg = sgn(t2) + fp.Neg(t2, &t1) // t2 = -t1 + fp.Cmov(&t1, t2, uint(isNeg)) // if t2 is negative then t1 = -t1 + fp.Mul(s, &t1, z) // s = t1*z + fp.Sub(s, s, t) // s = t1*z - t + fp.Mul(s, s, x) // s = x*(t1*z - t) + fp.Mul(s, s, &t0) // s = isr*x*(t1*z - t) + fp.Mul(s, s, &aMinusD) // s = (a-d)*isr*x*(t1*z - t) + isNeg = fp.Parity(s) // isNeg = sgn(s) + fp.Neg(&t0, s) // t0 = -s + fp.Cmov(s, &t0, uint(isNeg)) // if s is negative then s = -s + return fp.ToBytes(enc[:], s) +} + +// 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] +} diff --git a/ecc/decaf/decaf_test.go b/ecc/decaf/decaf_test.go new file mode 100644 index 000000000..be4d87ce2 --- /dev/null +++ b/ecc/decaf/decaf_test.go @@ -0,0 +1,239 @@ +package decaf_test + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "encoding/json" + "io/ioutil" + "os" + "testing" + + "github.com/cloudflare/circl/ecc/decaf" + "github.com/cloudflare/circl/internal/test" + fp "github.com/cloudflare/circl/math/fp448" +) + +type testJSONFile struct { + Group string `json:"group"` + Version string `json:"version"` + Generator struct { + X string `json:"x"` + Y string `json:"y"` + T string `json:"t"` + Z string `json:"z"` + } `json:"generator"` + Vectors []struct { + K string `json:"k"` + KG string `json:"kG"` + KP string `json:"kP"` + } `json:"vectors"` +} + +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 verify(t *testing.T, i int, gotkG *decaf.Elt, wantEnckG []byte) { + wantkG := &decaf.Elt{} + + gotEnckG, err := gotkG.MarshalBinary() + got := err == nil && bytes.Equal(gotEnckG, wantEnckG) + want := true + if got != want { + test.ReportError(t, got, want, i) + } + + err = wantkG.UnmarshalBinary(wantEnckG) + got = err == nil && + decaf.IsValid(gotkG) && + decaf.IsValid(wantkG) && + gotkG.IsEqual(wantkG) + want = true + if got != want { + test.ReportError(t, got, want, i) + } +} + +// Source: https://gist.github.com/armfazh/af01e1794dcf6942f2d404c5a0832676 +func TestDecafv1_0(t *testing.T) { + var kat testJSONFile + kat.readFile(t, "testdata/decafv1.0_vectors.json") + + got := kat.Group + want := "decaf" + if got != want { + test.ReportError(t, got, want) + } + got = kat.Version + want = decaf.Version + if got != want { + test.ReportError(t, got, want) + } + var scalar decaf.Scalar + var P decaf.Elt + G := decaf.Generator() + for i := range kat.Vectors { + k, _ := hex.DecodeString(kat.Vectors[i].K) + wantEnckG, _ := hex.DecodeString(kat.Vectors[i].KG) + wantEnckP, _ := hex.DecodeString(kat.Vectors[i].KP) + scalar.FromBytes(k) + + decaf.MulGen(&P, &scalar) + verify(t, i, &P, wantEnckG) + + decaf.Mul(&P, &scalar, G) + verify(t, i, &P, wantEnckG) + + decaf.Mul(&P, &scalar, &P) + verify(t, i, &P, wantEnckP) + } +} + +func TestDecafRandom(t *testing.T) { + const testTimes = 1 << 10 + var e decaf.Elt + var enc [decaf.EncodingSize]byte + + for i := 0; i < testTimes; i++ { + for found := false; !found; { + _, _ = rand.Read(enc[:]) + err := e.UnmarshalBinary(enc[:]) + found = err == nil + } + got, err := e.MarshalBinary() + want := enc[:] + if err != nil || !bytes.Equal(got, want) { + test.ReportError(t, got, want, e) + } + } +} + +func randomPoint() decaf.Elt { + var k decaf.Scalar + _, _ = rand.Read(k[:]) + var P decaf.Elt + decaf.MulGen(&P, &k) + return P +} + +func TestPointAdd(t *testing.T) { + const testTimes = 1 << 10 + Q := &decaf.Elt{} + for i := 0; i < testTimes; i++ { + P := randomPoint() + // Q = 16P = 2^4P + decaf.Double(Q, &P) // 2P + decaf.Double(Q, Q) // 4P + decaf.Double(Q, Q) // 8P + decaf.Double(Q, Q) // 16P + got := Q + // R = 16P = P+P...+P + R := decaf.Identity() + for j := 0; j < 16; j++ { + decaf.Add(R, R, &P) + } + want := R + if !decaf.IsValid(got) || !decaf.IsValid(want) || !got.IsEqual(want) { + test.ReportError(t, got, want, P) + } + } +} + +func TestPointNeg(t *testing.T) { + const testTimes = 1 << 10 + Q := &decaf.Elt{} + for i := 0; i < testTimes; i++ { + P := randomPoint() + decaf.Neg(Q, &P) + decaf.Add(Q, Q, &P) + got := Q.IsIdentity() + want := true + if got != want { + test.ReportError(t, got, want, P) + } + } +} + +func TestDecafOrder(t *testing.T) { + const testTimes = 1 << 10 + Q := &decaf.Elt{} + order := decaf.Order() + for i := 0; i < testTimes; i++ { + P := randomPoint() + + decaf.Mul(Q, &order, &P) + got := Q.IsIdentity() + want := true + if got != want { + test.ReportError(t, got, want, P, order) + } + } +} + +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. + } + + var e decaf.Elt + for _, enc := range badEncodings { + got := e.UnmarshalBinary(enc) + want := decaf.ErrInvalidDecoding + if got != want { + test.ReportError(t, got, want, enc) + } + } +} + +func BenchmarkDecaf(b *testing.B) { + var k, l decaf.Scalar + _, _ = rand.Read(k[:]) + _, _ = rand.Read(l[:]) + G := decaf.Generator() + P := decaf.Generator() + enc, _ := G.MarshalBinary() + + b.Run("Add", func(b *testing.B) { + for i := 0; i < b.N; i++ { + decaf.Add(P, P, G) + } + }) + b.Run("Mul", func(b *testing.B) { + for i := 0; i < b.N; i++ { + decaf.Mul(G, &k, G) + } + }) + b.Run("MulGen", func(b *testing.B) { + for i := 0; i < b.N; i++ { + decaf.MulGen(P, &k) + } + }) + b.Run("Marshal", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, _ = G.MarshalBinary() + } + }) + b.Run("Unmarshal", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = P.UnmarshalBinary(enc) + } + }) +} diff --git a/ecc/decaf/testdata/decafv1.0_vectors.json b/ecc/decaf/testdata/decafv1.0_vectors.json new file mode 100644 index 000000000..61f6788ef --- /dev/null +++ b/ecc/decaf/testdata/decafv1.0_vectors.json @@ -0,0 +1,172 @@ +{ + "group": "decaf", + "version": "v1.0", + "generator": { + "x": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffe80000000000000000000000000000000000000000000000000000000", + "y": "8508de14f04286d48d06c13078ca240805264370504c74c393d5242c5045271414181844d73f48e5199b0c1e3ab470a1c86079b4dfdd4a64", + "t": "6d3669e173c6a450e23d5682a9ffe1ddc2b86da60f794be956382384a319b57519c9854dde98e342140362071833f4e093e3c816dc198105", + "z": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001" + }, + "vectors": [ + { + "k": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kG": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kP": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "k": "0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kG": "6666666666666666666666666666666666666666666666666666666633333333333333333333333333333333333333333333333333333333", + "kP": "6666666666666666666666666666666666666666666666666666666633333333333333333333333333333333333333333333333333333333" + }, + { + "k": "0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kG": "c898eb4f87f97c564c6fd61fc7e49689314a1f818ec85eeb3bd5514ac816d38778f69ef347a89fca817e66defdedce178c7cc709b2116e75", + "kP": "b46f1836aa287c0a5a5653f0ec5ef9e903f436e21c1570c29ad9e5f596da97eeaf17150ae30bcb3174d04bc2d712c8c7789d7cb4fda138f4" + }, + { + "k": "0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kG": "a0c09bf2ba7208fda0f4bfe3d0f5b29a543012306d43831b5adc6fe7f8596fa308763db15468323b11cf6e4aeb8c18fe44678f44545a69bc", + "kP": "20d41d85a18d5657a29640321563bbd04c2ffbd0a37a7ba43a4f7d263ce26faf4e1f74f9f4b590c69229ae571fe37fa639b5b8eb48bd9a55" + }, + { + "k": "0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kG": "b46f1836aa287c0a5a5653f0ec5ef9e903f436e21c1570c29ad9e5f596da97eeaf17150ae30bcb3174d04bc2d712c8c7789d7cb4fda138f4", + "kP": "66e5cf59220cec1b47914ff83187a90d731ca77ee4f00115e610e5798f19dd9cf38293aeef6aec91ae1b50cb09d7e2434806ff29d2a86170" + }, + { + "k": "0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kG": "1c5bbecf4741dfaae79db72dface00eaaac502c2060934b6eaaeca6a20bd3da9e0be8777f7d02033d1b15884232281a41fc7f80eed04af5e", + "kP": "642336c69b7b755769131248fedd3764f139ca44cc5d982b518f85e3516e2ce565706a775193512225cded5ff7ec538f3d0e485158199424" + }, + { + "k": "0600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kG": "86ff0182d40f7f9edb7862515821bd67bfd6165a3c44de95d7df79b8779ccf6460e3c68b70c16aaa280f2d7b3f22d745b97a89906cfc476c", + "kP": "dea6945dfffc6e52d3de765fb9ff7dab52bd4b264951d3350b3f1ba25cf6b9bfed3682e1a93e85e4d14e5e4cfcc24055cfcc20da76f37482" + }, + { + "k": "0700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kG": "502bcb6842eb06f0e49032bae87c554c031d6d4d2d7694efbf9c468d48220c50f8ca28843364d70cee92d6fe246e61448f9db9808b3b2408", + "kP": "7c4ef65864d5de7b2b8a25086d4725754fa64e9784b2f9a55f7f9887a1732c7f13fab453081415328a877bf6eca6d1bd55344a9c12971304" + }, + { + "k": "0800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kG": "0c9810f1e2ebd389caa789374d78007974ef4d17227316f40e578b336827da3f6b482a4794eb6a3975b971b5e1388f52e91ea2f1bcb0f912", + "kP": "d4cd125d65012e419e73383162837aa4eef1da706d3e8ba1eb9b86116f0a15feea9ab6195db555b8e9b5894f20a39a03131eb4e2ef645403" + }, + { + "k": "0900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kG": "20d41d85a18d5657a29640321563bbd04c2ffbd0a37a7ba43a4f7d263ce26faf4e1f74f9f4b590c69229ae571fe37fa639b5b8eb48bd9a55", + "kP": "887f28cc15265462ceaf1cf701612ffc1505cc686c9ba355a95cbffbb03c4863a94ddb91e40f4a3de06a6da6b715540c6d05b1654a80d956" + }, + { + "k": "0a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kG": "e6b4b8f408c7010d0601e7eda0c309a1a42720d6d06b5759fdc4e1efe22d076d6c44d42f508d67be462914d28b8edce32e7094305164af17", + "kP": "e8b29a0ac41f194f8a5d8f33970dd0f2004877788924d8be7b545eb65bd7079bf0252f87af7fea46deac9678857ccd2b89ea39e3b91338cc" + }, + { + "k": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kG": "be88bbb86c59c13d8e9d09ab98105f69c2d1dd134dbcd3b0863658f53159db64c0e139d180f3c89b8296d0ae324419c06fa87fc7daaf34c1", + "kP": "4adbe973f6a0390030cf094306ad65d826e6ce856e3f89713b1dfb5cef5bf388fbf413dd0c6cff477030c17f4c6a648410d79c8b75da224d" + }, + { + "k": "0c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kG": "a456f9369769e8f08902124a0314c7a06537a06e32411f4f93415950a17badfa7442b6217434a3a05ef45be5f10bd7b2ef8ea00c431edec5", + "kP": "8262db825edabdd8a621fb309c5d0276137b72ac15b5d85442e2ca18f4dc8907bf39511295d2271ed040c27f17f00b38fd5d3d8cc922868e" + }, + { + "k": "0d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kG": "186e452c4466aa4383b4c00210d52e7922dbf9771e8b47e229a9b7b73c8d10fd7ef0b6e41530f91f24a3ed9ab71fa38b98b2fe4746d51d68", + "kP": "3637f9cc7e147b6c23199c4bd9e21aa2eb30b6bdc656e7cb42a14b713f8e5bee177641283a939f0b88b39ac351828adaa2426afa6943b1c8" + }, + { + "k": "0e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kG": "4ae7fdcae9453f195a8ead5cbe1a7b9699673b52c40ab27927464887be53237f7f3a21b938d40d0ec9e15b1d5130b13ffed81373a53e2b43", + "kP": "1e27c8c3eb1c97c1c1a32e0cc5ce985ba5d6a1bd243dca8fc3c98846ccae8867ab0493dcd1956e08c4d28d50fc52ca1b90423adecd879555" + }, + { + "k": "0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "kG": "841981c3bfeec3f60cfeca75d9d8dc17f46cf0106f2422b59aec580a58f342272e3a5e575a055ddb051390c54c24c6ecb1e0aceb075f6056", + "kP": "ca51d5471817aa93af04519dadadb310c1cee4a787ad5ed5b61ff97205e47a4d872235d5c6248e1d93ea99eb0aef1343c0d4ecbc2ad017ed" + }, + { + "k": "cd4bfd5d4a9fec50baf25da395a60ede18e194b0058ae93dc32f7562fa850a1ac3c21bb5ad3484d7cecfcdd2804911247cd83bc5bc25d612", + "kG": "fea73f4e1fd35f08dd5492b4884e1767fdc1be5fe5594734e2e856774c0e30032e1e793658a14aa0e3142c6f82e95494fc446fb72a8420ee", + "kP": "fa47cf23a5212777b64d770eca84bb01bebd230624322a2f0ded1c415bc2cfafe0da503f452978d29e08d8328be1e17962476c43e4cec317" + }, + { + "k": "b682dfc043a227128fabe1a7d1e0914dd8834855bfd484034d4d11f26a72dd7cdff0f8ad11bb432c553b399b164d0a9a0ef182121d219020", + "kG": "debd39363471e04a4efe4535247962ecbc99d6a0367f12017d933d17d8760ce0f4d25a0e22720d614ba0fb7245fa499744cdbb238ae161e9", + "kP": "5ce02fac31ee168de7248f0c5bf49f0f8bb764a127df5de2da75bfd4ea9eb28c887096464d7c8392e6eae7e9ad93f1a68f20fb785e31662b" + }, + { + "k": "0ebecb9828fb636277a230a84b2b0a4bb421361b638872465e9475735967a0dad11ffece0e5b6b7ed215e0453c052267f358fa5581087cca", + "kG": "f2357983172f9375730efb111909daaf2b6cb42f51facbc887051d8f40ffb37eea11760a4477372c1aad9ce82aa53e742be5631cdc8ab6b4", + "kP": "661c4cfc45869a6e13665d6e81521d97e185e0b13a2175879842e8d49776b6b07aedef3027b1cc67e983592619cdc1f3633c20e71cee4009" + }, + { + "k": "041180838245a0907a3af4453e875a8416d92898969f11d8b2d32d2a820a0cddb8ae18490db758d1251a258e473db6787a9c17421f187767", + "kG": "30275e6664debac8510a362a2e991ad58af3b91dc6c604b73445a9cb1bcd34a98f744a8ee9be1c26efed0a7f5f7c96be498eb78fcdf23924", + "kP": "e24aecfe3e046a0e21c6444811def2efad99bb217aa27a1d8e6cedc06e319215789dbdaf7d5c4a0718e600887e213315fb328510b53b91a8" + }, + { + "k": "f0cfb0e6ef9370fec4b3a7ea437042f5797d37dd094d7d7dff605d6bd526238886a7711ba5c121a4b7c97852244d464957f0110fe5fa9ac6", + "kG": "c440295f0abe88e55ba7375b6b1012f4ce3965762e8bdd785f1fb0875f2966f8f5a5c6ea34dac0887bf5eaf0ec6d722092429c7ef6c98dd2", + "kP": "301c724b9b525f2896957cc8b2f1354643533bac5cde015de1bbcf6830f8d11a99c7dd71dee978498f757a9582c4b2045acef47d27aeba75" + }, + { + "k": "697a060c838c06fa1b207b8725bb70be9ec7042bc0feb03e9d2f8aeb066292c9e383bb9c84e5cc8a4e61478487893c0310198d0e53fa924e", + "kG": "7ee6c5f627d2c6b504281b988523aa9f8e50335e126e53ee5c3ddd1f20b47a9dfefe7bcc05c664d552e3e551af9ce0f6acc07a3fb5ba7f9a", + "kP": "4cd7d489b133bd140b1c6252389f1484e49c42ec6c18514c243d522938b94c1966b8db85e5f32e8c7760c5278189d856c8126197ba239657" + }, + { + "k": "7f45370488ce3016cd946a2e2e7421d7c0dfd22616e118a528eaf1dfbb8a64fb9cf1a6ad5784e36a490525cb4e63f5f7e946f252ebbb240b", + "kG": "e2e5e47f3526dd5dbaee86fa11a44dcf666dabe4c4bde539381b4362ac39ca5dde276ac478cfcf53dd7c1064bc967bd0156a7e8858c4058f", + "kP": "be1caf2f48cf6f913c89e3ff7e375fa12f59ee8ce9367b2a50992494e9c5971d01a320536e5c70570e271e8ab8df36f3e0e674143870af4c" + }, + { + "k": "df06b032a44ee9fc3a6c0483cfcdb84830aea72872b1cac5f24581751d6b88647222417323e14cae78f935f481cfbf8fd73e27b34d01754d", + "kG": "32043d552d170a2ea2ab47b51474c6a53d6411b719a655ea65545642c338020a79f2230f3a908c801e3b8658346385297762a9ff94982b85", + "kP": "8c0da76c871a5d1c927be0227413f7049934331d11b39392e3b356d2de48102c6085dcb4362c43d5f208385bb5c49cb7ba29f0a7468c10e8" + }, + { + "k": "0dd1515bd5496bedcbea5bee2d3afefb696ee1293e181517bb6fef0c0920d9040b7f2aa6d98778bfc2a523cec742554eff92d1886abf72c1", + "kG": "c8cbbf3ad3c9dbe3f2822ec1a4c2051d9180627c2f258252d71e5248a69139fd94185d75ea60215b0cacee80faa440b9e611ccb36ebb32e3", + "kP": "a60133f8c1ebb788b028a8878fe3489b0192ed47da2a17ede46b69b52afe14ab0794918fe58f65a407326f4e52280da326aded7e706ecfd5" + }, + { + "k": "8b2278b47df21f0d876dee53030bd943a2e0da8f44e8e744d69bd79c822a6fc39ec5d1800ef6ec52880e851e5c3cae064becdcf92af5a74d", + "kG": "40d94bf1dda5cf0f0203ac97a28a4f36b14cd91799a02403ac5295af8cd7d811a953d2eb9307616b81856dfb35be0d83535acc3c442d36e1", + "kP": "26dc585adfec359a902c08d8d02e95b846b2cfdf60c764715109ed59a305932f97eab1c86a6b58e7ff6a23463cfdeb684207d212151f02eb" + }, + { + "k": "2b3f6c5a2dbac76298b3bed3b08c1019f6a4aea10892b2b3ba6b8d5927fb8d9716f4b70192447dddccb58cd8d5dcd48d6c0baba5fee59503", + "kG": "c467a8f200c96ab010551f92db7237e16268f1f4e54d348e4d29a4f4f0d6c4648b23eab1f2a2022fc06d6f26111b67790e6428d53824eb2c", + "kP": "ba14aba5987f2354dc040bf6d77496980c6d834931be103bb60a9578da29dd5d025fa0f74afc1b3021110a6e91af051a14ca4f1ce7b0ff46" + }, + { + "k": "9ec27326e47d4d87e8662c253f15dff9e559054ebcde2b229f2aab8ac631f9a7499c6c9ce020587088afcb0efd909635d099e51f22a92b83", + "kG": "06c571fa30bbe9eca3c0331753748b94f6216530e98202646f3bc32563f1516426c998789256c6168483266c0f9a8ba8dece726470d7e7ef", + "kP": "26bfad2c14ddaa53e829873c0cf144b7b005c547f81da58d6cb0fc5ab9af5e6fa0c41d26546f921991970f265cbea3119fbf1396fc489062" + }, + { + "k": "5459967a472a137db3b3a34b53969d0e1a76b8f6aa00dcc2ec3a8b8d128bd740c3cc5421661828b3dfd835393dd7c42bb8f850a84fdd535f", + "kG": "d65a3c49f474f6139ea334279084ea49715b3db30157176334fac0120802f2012cd7f903e6a91b0fe522a1653822e122fd39e6652e741462", + "kP": "42bc2bf70c8eb5e6223371b88752e8ac60f8459efc0d3eecd04d396b24f43baaf7909996a5f73faedd5cf2edb0bf6d2318aca0951d073dae" + }, + { + "k": "1ce2c9b8efa2fa9773a33f2a17b04b8818ca385096c2880fd4e322fe2f7d9d8fbfcc494ab1b9ade54cde3ac9a7ce16734b8a79c45e473a93", + "kG": "2ceb14b03f4364d9adce90acf31b23d82ab36d1a8876b73c00d1d608247788349a12d14aa328f3a5b8ea0ac3a37b5bde713c2513c96514d2", + "kP": "264933a7700e14e712bbd3ddd37093ea9d54a5e37405a4c3cf091a74e234fc76725ffc10426897dca4b55092d035e559ac333f9b6ea20de5" + }, + { + "k": "3d996b79049ab852e907aac8477b53fd4366d51d91b7b3d3fc344d1a440200a1089ee04e033c100c159bb7be9cfc95c68b663e2b01aa2c3c", + "kG": "d4e53edf8c085acd4310f68da9d4fdd11948259dd3a87edd0196bad74f5338a4855ff704c82a78935bfbfbba80affd94f640deac10e8a434", + "kP": "eec2abcdce7c6843864aac3171db6e26a150bf71950cf4c3b3398a2063d15887918788bf6c353d82867ec3062f16bee21d4915662daa036e" + }, + { + "k": "fdfd39b441ccddb593eb27f2087a8679b33d49f91844a52cc15a0fcc5b689b1b4ff4010c8fc291ee09b22f68eb32b82f525b97c25e793974", + "kG": "b6083b4565745e266a0502fe1351cd0d29ea103c4c205b6a239fc7e356f21330e0f6a588c352e2d7293fad46431c0680c3b2df132766d231", + "kP": "5e0784c7112fb7265f91ed865ce459d6eaeba814892e06da439f9890630990f2911377823ad7ed38bec9bf2968bf43f36d1abe0e8d867f25" + } + ] +} diff --git a/ecc/goldilocks/constants.go b/ecc/goldilocks/constants.go deleted file mode 100644 index b6b236e5d..000000000 --- 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 5a939100d..000000000 --- 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 95c15d964..000000000 --- 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/isogeny.go b/ecc/goldilocks/isogeny.go deleted file mode 100644 index b1daab851..000000000 --- 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 deleted file mode 100644 index ffaf8c057..000000000 --- a/ecc/goldilocks/isogeny_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package goldilocks - -import ( - "crypto/rand" - "testing" - - "github.com/cloudflare/circl/internal/test" -) - -func randomPoint() *Point { - var k Scalar - _, _ = rand.Read(k[:]) - return Curve{}.ScalarBaseMult(&k) -} - -func TestIsogeny(t *testing.T) { - const testTimes = 1 << 10 - var gold Curve - var twist twistCurve - - for i := 0; i < testTimes; i++ { - P := randomPoint() - Q := gold.pull(gold.push(P)) // phi^-(phi^+(P)) - 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) { - test.ReportError(t, got, want, P) - } - } -} diff --git a/ecc/goldilocks/point.go b/ecc/goldilocks/point.go deleted file mode 100644 index a3ddc7cc0..000000000 --- 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") - } - var 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 a25bb5e0f..000000000 --- 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/twist.go b/ecc/goldilocks/twist.go deleted file mode 100644 index 8cd4e333b..000000000 --- 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 c55db77b0..000000000 --- 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 f6ac5edbb..000000000 --- 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/internal/ted448/basemult.go b/internal/ted448/basemult.go new file mode 100644 index 000000000..0c92b5f61 --- /dev/null +++ b/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/internal/ted448/constants.go b/internal/ted448/constants.go new file mode 100644 index 000000000..3698ee9e6 --- /dev/null +++ b/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{ + 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/internal/ted448/curve.go b/internal/ted448/curve.go new file mode 100644 index 000000000..7d9f96681 --- /dev/null +++ b/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) +// +// [RFC7748] Elliptic Curves for Security (https://rfc-editor.org/rfc/rfc7748.txt) +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. +func Order() Scalar { return order } + +// ParamD returns the number of points in the prime subgroup. +func ParamD() fp.Elt { return paramD } + +// IsOnCurve returns true if the point lies on the curve. +func IsOnCurve(P *Point) bool { + eq0 := *P != Point{} + 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 eq0 && eq1 && eq2 +} + +// 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/internal/ted448/curve_test.go b/internal/ted448/curve_test.go new file mode 100644 index 000000000..b8383998d --- /dev/null +++ b/internal/ted448/curve_test.go @@ -0,0 +1,172 @@ +package ted448_test + +import ( + "crypto/rand" + "testing" + + "github.com/cloudflare/circl/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 + // R = 16P = P+P...+P + R := ted448.Identity() + for j := 0; j < 16; j++ { + R.Add(&P) + } + want := R + if !ted448.IsOnCurve(&got) || !ted448.IsOnCurve(&want) || !got.IsEqual(&want) { + test.ReportError(t, got, want, 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 := true + if got != want { + test.ReportError(t, got, want, P) + } + } +} + +func TestScalarMult(t *testing.T) { + const testTimes = 1 << 8 + + t.Run("rG=0", func(t *testing.T) { + got := &ted448.Point{} + order := ted448.Order() + for i := 0; i < testTimes; i++ { + ted448.ScalarBaseMult(got, &order) + want := ted448.Identity() + + if !ted448.IsOnCurve(got) || !ted448.IsOnCurve(&want) || !got.IsEqual(&want) { + test.ReportError(t, got, want) + } + } + }) + t.Run("rP=0", func(t *testing.T) { + got := &ted448.Point{} + order := ted448.Order() + 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) { + test.ReportError(t, got, want, P, order) + } + } + }) + 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) { + 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) { + 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) { + test.ReportError(t, got, want, P, k, l) + } + } + }) +} + +func BenchmarkCurve(b *testing.B) { + var k, l ted448.Scalar + _, _ = rand.Read(k[:]) + _, _ = rand.Read(l[:]) + P := randomPoint() + Q := randomPoint() + + b.Run("Add", func(b *testing.B) { + for i := 0; i < b.N; i++ { + P.Add(&Q) + } + }) + b.Run("ScalarMult", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ted448.ScalarMult(&P, &k, &P) + } + }) + b.Run("ScalarBaseMult", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ted448.ScalarBaseMult(&P, &k) + } + }) + b.Run("CombinedMult", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ted448.CombinedMult(&P, &k, &l, &P) + } + }) +} diff --git a/internal/ted448/point.go b/internal/ted448/point.go new file mode 100644 index 000000000..4306cca9c --- /dev/null +++ b/internal/ted448/point.go @@ -0,0 +1,174 @@ +package ted448 + +import ( + "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 + 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 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 + 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 True is P is the identity. +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 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) +} + +// 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/scalar.go b/internal/ted448/scalar.go similarity index 76% rename from ecc/goldilocks/scalar.go rename to internal/ted448/scalar.go index 852d74f3a..c3bf8d0dc 100644 --- a/ecc/goldilocks/scalar.go +++ b/internal/ted448/scalar.go @@ -1,19 +1,25 @@ -package goldilocks +package ted448 import ( + "crypto/rand" "encoding/binary" "math/bits" -) -// ScalarSize is the size (in bytes) of scalars. -const ScalarSize = 56 // 448 / 8 + "github.com/cloudflare/circl/internal/conv" +) -//_N is the number of 64-bit words to store scalars. -const _N = 7 // 448 / 64 +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 { return conv.BytesLe2Hex(z[:]) } + type scalar64 [_N]uint64 func (z *scalar64) fromScalar(x *Scalar) { @@ -36,6 +42,25 @@ func (z *scalar64) toScalar(x *Scalar) { 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) + } +} + // 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 @@ -84,14 +109,6 @@ func mulWord(z, x []uint64, y uint64) { 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] @@ -111,6 +128,15 @@ func (z *scalar64) reduceOneWord(x uint64) { 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 @@ -119,7 +145,7 @@ func (z *scalar64) modOrder() { // 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 } + z.cmov(&x, 1-c) // if c != 0 { z = x } } } @@ -146,14 +172,11 @@ func (z *Scalar) FromBytes(x []byte) { 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) } +// 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) { @@ -162,20 +185,17 @@ func (z *Scalar) Add(x, y *Scalar) { y64.fromScalar(y) c := add(z64[:], x64[:], y64[:]) add(t[:], z64[:], residue448[:]) - z64.Cmov(c, &t) + 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, t scalar64 + var z64, x64, y64 scalar64 x64.fromScalar(x) y64.fromScalar(y) - c := sub(z64[:], x64[:], y64[:]) - sub(t[:], z64[:], residue448[:]) - z64.Cmov(c, &t) - z64.modOrder() + z64.sub(&x64, &y64) z64.toScalar(z) } @@ -199,5 +219,15 @@ func (z *Scalar) Mul(x, y *Scalar) { z64.toScalar(z) } -// IsZero returns true if z=0. -func (z *Scalar) IsZero() bool { z.Red(); return *z == Scalar{} } +// Inv calculates z = 1/x mod order. +func (z *Scalar) Inv(x *Scalar) { + var t, r Scalar + _, _ = rand.Read(r[:]) + r.Red() + t.Mul(x, &r) + bigT := conv.BytesLe2BigInt(t[:]) + bigOrder := conv.BytesLe2BigInt(order[:]) + bigT.ModInverse(bigT, bigOrder) + conv.BigInt2BytesLe(z[:], bigT) + z.Mul(z, &r) +} diff --git a/ecc/goldilocks/scalar_test.go b/internal/ted448/scalar_test.go similarity index 69% rename from ecc/goldilocks/scalar_test.go rename to internal/ted448/scalar_test.go index 5604f5135..4658a3db4 100644 --- a/ecc/goldilocks/scalar_test.go +++ b/internal/ted448/scalar_test.go @@ -1,23 +1,22 @@ -package goldilocks_test +package ted448_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/ted448" "github.com/cloudflare/circl/internal/test" ) func TestReduceModOrder(t *testing.T) { - order := goldilocks.Curve{}.Order() + order := ted448.Order() bigOrder := conv.BytesLe2BigInt(order[:]) - const max = 3*goldilocks.ScalarSize - 1 + const max = 3*ted448.ScalarSize - 1 var b [max]byte _, _ = rand.Read(b[:]) - var z goldilocks.Scalar + var z ted448.Scalar for i := 0; i < max; i++ { x := b[0:i] bigX := conv.BytesLe2BigInt(x) @@ -35,11 +34,11 @@ func TestReduceModOrder(t *testing.T) { } func testOp(t *testing.T, - f func(z, x, y *goldilocks.Scalar), + f func(z, x, y *ted448.Scalar), g func(z, x, y *big.Int)) { const testTimes = 1 << 8 - var x, y, z goldilocks.Scalar - order := goldilocks.Curve{}.Order() + var x, y, z ted448.Scalar + order := ted448.Order() want := new(big.Int) bigOrder := conv.BytesLe2BigInt(order[:]) @@ -65,25 +64,32 @@ func testOp(t *testing.T, 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 *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 *goldilocks.Scalar) { z.Sub(x, y) }, + 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 *goldilocks.Scalar) { z.Mul(x, y) }, + func(z, x, y *ted448.Scalar) { z.Mul(x, y) }, func(z, x, y *big.Int) { z.Mul(x, y) }) }) + t.Run("Inv", func(t *testing.T) { + order := ted448.Order() + bigOrder := conv.BytesLe2BigInt(order[:]) + testOp(t, + func(z, x, y *ted448.Scalar) { z.Inv(x) }, + func(z, x, y *big.Int) { z.ModInverse(x, bigOrder) }) + }) } func BenchmarkScalar(b *testing.B) { - var k [2 * goldilocks.ScalarSize]byte - var x, y, z goldilocks.Scalar - _ = binary.Read(rand.Reader, binary.LittleEndian, x[:]) + var k [2 * ted448.ScalarSize]byte + var x, y, z ted448.Scalar + _, _ = rand.Read(x[:]) b.Run("Add", func(b *testing.B) { for i := 0; i < b.N; i++ { z.Add(&x, &y) @@ -104,4 +110,9 @@ func BenchmarkScalar(b *testing.B) { z.FromBytes(k[:]) } }) + b.Run("Inv", func(b *testing.B) { + for i := 0; i < b.N; i++ { + z.Inv(&x) + } + }) } diff --git a/ecc/goldilocks/twistTables.go b/internal/ted448/tables.go similarity index 98% rename from ecc/goldilocks/twistTables.go rename to internal/ted448/tables.go index ed432e02c..d7e858886 100644 --- a/ecc/goldilocks/twistTables.go +++ b/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/math/fp448/fp.go b/math/fp448/fp.go index a5e36600b..c2d27ba2f 100644 --- a/math/fp448/fp.go +++ b/math/fp448/fp.go @@ -45,6 +45,9 @@ func IsZero(x *Elt) bool { Modp(x); return *x == Elt{} } // IsOne returns true if x is equal to 1. func IsOne(x *Elt) bool { Modp(x); return *x == Elt{1} } +// 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} } diff --git a/sign/ed25519/ed25519.go b/sign/ed25519/ed25519.go index 02adf0c67..a989bcb5d 100644 --- a/sign/ed25519/ed25519.go +++ b/sign/ed25519/ed25519.go @@ -43,8 +43,8 @@ import ( "crypto/subtle" "errors" "fmt" + "hash" "io" - "strconv" ) const ( @@ -95,17 +95,14 @@ type PrivateKey []byte // Equal reports whether priv and x have the same value. func (priv PrivateKey) Equal(x crypto.PrivateKey) bool { xx, ok := x.(PrivateKey) - if !ok { - return false - } - return subtle.ConstantTimeCompare(priv, xx) == 1 + return ok && subtle.ConstantTimeCompare(priv, xx) == 1 } // 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 @@ -163,10 +160,9 @@ func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) { return nil, nil, err } - privateKey := NewKeyFromSeed(seed) - publicKey := make([]byte, PublicKeySize) - copy(publicKey, privateKey[SeedSize:]) - + privateKey := make(PrivateKey, PrivateKeySize) + publicKey := make(PublicKey, PublicKeySize) + newKeyFromSeed(privateKey, publicKey, seed) return publicKey, privateKey, nil } @@ -175,27 +171,29 @@ 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) - newKeyFromSeed(privateKey, seed) + privateKey := make(PrivateKey, PrivateKeySize) + newKeyFromSeed(privateKey, PublicKey(privateKey[SeedSize:]), seed) return privateKey } -func newKeyFromSeed(privateKey, seed []byte) { +func newKeyFromSeed(privateKey PrivateKey, publicKey PublicKey, seed []byte) { if l := len(seed); l != SeedSize { - panic("ed25519: bad seed length: " + strconv.Itoa(l)) + panic(fmt.Errorf("ed25519: bad seed length: %v", l)) } var P pointR1 k := sha512.Sum512(seed) clamp(k[:]) reduceModOrder(k[:paramB], false) P.fixedMult(k[:paramB]) - copy(privateKey[:SeedSize], seed) _ = P.ToBytes(privateKey[SeedSize:]) + + copy(privateKey[:SeedSize], seed) + copy(publicKey[:PublicKeySize], privateKey[SeedSize:]) } func sign(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash bool) { if l := len(privateKey); l != PrivateKeySize { - panic("ed25519: bad private key length: " + strconv.Itoa(l)) + panic(fmt.Errorf("ed25519: bad private key length: %v", l)) } H := sha512.New() @@ -216,10 +214,7 @@ func sign(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash prefix, s := h[paramB:], h[:paramB] // 2. Compute SHA-512(dom2(F, C) || prefix || PH(M)) - H.Reset() - writeDom(H, ctx, preHash) - _, _ = H.Write(prefix) _, _ = H.Write(PHM) r := H.Sum(nil) @@ -234,10 +229,7 @@ func sign(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash } // 4. Compute SHA512(dom2(F, C) || R || A || PH(M)). - H.Reset() - writeDom(H, ctx, preHash) - _, _ = H.Write(R) _, _ = H.Write(privateKey[SeedSize:]) _, _ = H.Write(PHM) @@ -413,7 +405,8 @@ func isLessThanOrder(x []byte) bool { return x[i] < order[i] } -func writeDom(h io.Writer, ctx []byte, preHash bool) { +func writeDom(h hash.Hash, ctx []byte, preHash bool) { + h.Reset() dom2 := "SigEd25519 no Ed25519 collisions" if len(ctx) > 0 { diff --git a/sign/ed25519/ed25519_test.go b/sign/ed25519/ed25519_test.go index e7e39f5c2..ac734ebc5 100644 --- a/sign/ed25519/ed25519_test.go +++ b/sign/ed25519/ed25519_test.go @@ -1,20 +1,12 @@ package ed25519_test import ( + "crypto/rand" "testing" "github.com/cloudflare/circl/sign/ed25519" ) -type zeroReader struct{} - -func (zeroReader) Read(buf []byte) (int, error) { - for i := range buf { - buf[i] = 0 - } - return len(buf), nil -} - func TestMalleability(t *testing.T) { // https://tools.ietf.org/html/rfc8032#section-5.1.7 adds an additional test // that s be in [0, order). This prevents someone from adding a multiple of @@ -40,9 +32,8 @@ func TestMalleability(t *testing.T) { } func BenchmarkKeyGeneration(b *testing.B) { - var zero zeroReader for i := 0; i < b.N; i++ { - if _, _, err := ed25519.GenerateKey(zero); err != nil { + if _, _, err := ed25519.GenerateKey(rand.Reader); err != nil { b.Fatal(err) } } @@ -57,8 +48,7 @@ func BenchmarkNewKeyFromSeed(b *testing.B) { } func BenchmarkSigning(b *testing.B) { - var zero zeroReader - _, priv, err := ed25519.GenerateKey(zero) + _, priv, err := ed25519.GenerateKey(rand.Reader) if err != nil { b.Fatal(err) } @@ -71,8 +61,7 @@ func BenchmarkSigning(b *testing.B) { } func BenchmarkVerification(b *testing.B) { - var zero zeroReader - pub, priv, err := ed25519.GenerateKey(zero) + pub, priv, err := ed25519.GenerateKey(rand.Reader) if err != nil { b.Fatal(err) } diff --git a/sign/ed25519/extra_test.go b/sign/ed25519/extra_test.go index cd1a42bd7..85db8a699 100644 --- a/sign/ed25519/extra_test.go +++ b/sign/ed25519/extra_test.go @@ -149,7 +149,7 @@ func BenchmarkEd25519Ph(b *testing.B) { ctx := "" b.ResetTimer() for i := 0; i < b.N; i++ { - _ = ed25519.SignPh(key, msg, ctx) + ed25519.SignPh(key, msg, ctx) } }) b.Run("Verify", func(b *testing.B) { @@ -171,7 +171,7 @@ func BenchmarkEd25519Ctx(b *testing.B) { _, priv, _ := ed25519.GenerateKey(rand.Reader) b.ResetTimer() for i := 0; i < b.N; i++ { - _ = ed25519.SignWithCtx(priv, msg, ctx) + ed25519.SignWithCtx(priv, msg, ctx) } }) b.Run("Verify", func(b *testing.B) { diff --git a/sign/ed448/ed448.go b/sign/ed448/ed448.go index b8d044329..49c2e269d 100644 --- a/sign/ed448/ed448.go +++ b/sign/ed448/ed448.go @@ -28,13 +28,11 @@ import ( "crypto" cryptoRand "crypto/rand" "crypto/subtle" - "errors" "fmt" "io" - "strconv" - "github.com/cloudflare/circl/ecc/goldilocks" sha3 "github.com/cloudflare/circl/internal/shake" + "github.com/cloudflare/circl/sign/ed448/internal/goldilocks" ) const ( @@ -65,7 +63,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 } @@ -83,10 +82,7 @@ type PublicKey []byte // Equal reports whether pub and x have the same value. func (pub PublicKey) Equal(x crypto.PublicKey) bool { xx, ok := x.(PublicKey) - if !ok { - return false - } - return bytes.Equal(pub, xx) + return ok && bytes.Equal(pub, xx) } // PrivateKey is the type of Ed448 private keys. It implements crypto.Signer. @@ -95,17 +91,14 @@ type PrivateKey []byte // Equal reports whether priv and x have the same value. func (priv PrivateKey) Equal(x crypto.PrivateKey) bool { xx, ok := x.(PrivateKey) - if !ok { - return false - } - return subtle.ConstantTimeCompare(priv, xx) == 1 + return ok && subtle.ConstantTimeCompare(priv, xx) == 1 } // 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 @@ -143,7 +136,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") } } @@ -159,10 +152,9 @@ func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) { return nil, nil, err } - privateKey := NewKeyFromSeed(seed) - publicKey := make([]byte, PublicKeySize) - copy(publicKey, privateKey[SeedSize:]) - + privateKey := make(PrivateKey, PrivateKeySize) + publicKey := make(PublicKey, PublicKeySize) + newKeyFromSeed(privateKey, publicKey, seed) return publicKey, privateKey, nil } @@ -171,14 +163,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) - newKeyFromSeed(privateKey, seed) + privateKey := make(PrivateKey, PrivateKeySize) + newKeyFromSeed(privateKey, PublicKey(privateKey[SeedSize:]), seed) return privateKey } -func newKeyFromSeed(privateKey, seed []byte) { +func newKeyFromSeed(privateKey PrivateKey, publicKey PublicKey, 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 @@ -188,13 +180,21 @@ 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[:]) + copy(publicKey[:PublicKeySize], encP[:]) } func sign(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() @@ -220,10 +220,7 @@ func sign(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash // 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[:]) @@ -231,17 +228,17 @@ func sign(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash // 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 { + var R goldilocks.Point + var encR [goldilocks.EncodingSize]byte + R.ScalarBaseMult(r) + 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[:]) @@ -254,7 +251,7 @@ func sign(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash S.Add(S, r) // 6. The signature is the concatenation of R and S. - copy(signature[:paramB], R[:]) + copy(signature[:paramB], encR[:]) copy(signature[paramB:], S[:]) } @@ -287,7 +284,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 } @@ -320,10 +320,14 @@ func verify(public PublicKey, message, signature, ctx []byte, preHash bool) bool S := &goldilocks.Scalar{} S.FromBytes(signature[paramB:]) - encR := (&[paramB]byte{})[:] P.Neg() - _ = goldilocks.Curve{}.CombinedMult(S, k, P).ToBytes(encR) - return bytes.Equal(R, encR) + var Q goldilocks.Point + Q.CombinedMult(S, k, P) + var encR [goldilocks.EncodingSize]byte + if err = Q.Encode(&encR); err != nil { + panic(err) + } + return bytes.Equal(R, encR[:]) } // VerifyAny returns true if the signature is valid. Failure cases are invalid @@ -378,7 +382,7 @@ func deriveSecretScalar(s *goldilocks.Scalar, h []byte) { // 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-- @@ -386,7 +390,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.Shake, 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 91349609d..0b1f061de 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) { diff --git a/sign/ed448/internal/goldilocks/goldilocks.go b/sign/ed448/internal/goldilocks/goldilocks.go new file mode 100644 index 000000000..93e406e61 --- /dev/null +++ b/sign/ed448/internal/goldilocks/goldilocks.go @@ -0,0 +1,177 @@ +// Package goldilocks provides arithmetic operations on the Goldilocks 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 ( + "errors" + "unsafe" + + "github.com/cloudflare/circl/internal/ted448" + fp "github.com/cloudflare/circl/math/fp448" +) + +type Scalar = ted448.Scalar + +type Point ted448.Point + +// EncodingSize is the size (in bytes) of an encoded point on the Goldilocks curve. +const EncodingSize = fp.Size + 1 + +// ErrInvalidDecoding alerts of an error during decoding a point. +var ErrInvalidDecoding = errors.New("invalid decoding") + +// 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 := (data[EncodingSize-1] & 0x7F) == 0x00 + signX := 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 := !(fp.IsZero(x) && signX == 1) + fp.Neg(u, x) // u = -x + fp.Cmov(x, u, uint(signX^(x[0]&1))) // if signX != x mod 2 + + isValid := isByteZero && isLessThanP && isQR && isValidXSign + b := uint(*(*byte)(unsafe.Pointer(&isValid))) + 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 !isValid { + return ErrInvalidDecoding + } + return nil +} + +// Encode sets data with the unique encoding of the point P. +func (P *Point) Encode(data *[EncodingSize]byte) error { + x, y, invZ := &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + fp.Inv(invZ, &P.Z) // 1/z + fp.Mul(x, &P.X, invZ) // x/z + fp.Mul(y, &P.Y, invZ) // y/z + fp.Modp(x) + fp.Modp(y) + data[EncodingSize-1] = (x[0] & 1) << 7 + return fp.ToBytes(data[:fp.Size], y) +} + +// 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) { + k4 := &Scalar{} + divBy4(k4, k) + var Q ted448.Point + ted448.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) { + m4, n4 := &Scalar{}, &Scalar{} + divBy4(m4, m) + divBy4(n4, n) + var R, phiQ ted448.Point + pull(&phiQ, Q) + ted448.CombinedMult(&R, m4, n4, &phiQ) + push(P, &R) +} + +func (P *Point) Neg() { fp.Neg(&P.X, &P.X); fp.Neg(&P.Ta, &P.Ta) } + +// Order returns a scalar with the order of the group. +func Order() Scalar { return ted448.Order() } + +// divBy4 calculates z = x/4 mod order. +func divBy4(z, x *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 *ted448.Point, P *Point) { isogeny4(Q, (*ted448.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 *ted448.Point) { isogeny4((*ted448.Point)(Q), P, false) } + +// isogeny4 is a birational map between ted448 and Goldilocks curves. +func isogeny4(Q, P *ted448.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 +} + +// 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] +} + +var ( + // invFour is 1/4 mod order, where order = ted448.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, + } + // paramD is the D parameter of the Goldilocks curve, D=-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, + } +) diff --git a/sign/ed448/internal/goldilocks/goldilocks_test.go b/sign/ed448/internal/goldilocks/goldilocks_test.go new file mode 100644 index 000000000..3dde2a3fb --- /dev/null +++ b/sign/ed448/internal/goldilocks/goldilocks_test.go @@ -0,0 +1,122 @@ +package goldilocks + +import ( + "crypto/rand" + "testing" + + "github.com/cloudflare/circl/internal/ted448" + "github.com/cloudflare/circl/internal/test" + fp "github.com/cloudflare/circl/math/fp448" +) + +func randomTwistPoint() ted448.Point { + var k ted448.Scalar + _, _ = rand.Read(k[:]) + var P ted448.Point + ted448.ScalarBaseMult(&P, &k) + return P +} + +func TestIsogeny(t *testing.T) { + const testTimes = 1 << 10 + var phiP Point + var Q ted448.Point + for i := 0; i < testTimes; i++ { + P := randomTwistPoint() + R := P + push(&phiP, &P) + pull(&Q, &phiP) + R.Double() // 2P + R.Double() // 4P + got := Q + want := R + if !got.IsEqual(&want) { + test.ReportError(t, got, want, P) + } + } +} + +func TestScalarMult(t *testing.T) { + const testTimes = 1 << 10 + k := &Scalar{} + zero := &Scalar{} + var P, Q, I Point + var got, want [EncodingSize]byte + _I := ted448.Identity() + push(&I, &_I) + for i := 0; i < testTimes; i++ { + _, _ = rand.Read(k[:]) + + P.ScalarBaseMult(k) + Q.CombinedMult(k, zero, &I) // k*G + 0*I + err0 := P.Encode(&got) + err1 := Q.Encode(&want) + if err0 != nil || err1 != nil || got != want { + test.ReportError(t, got, want, k) + } + } +} + +func TestPointEncoding(t *testing.T) { + const testTimes = 1 << 10 + var want, got [EncodingSize]byte + var P Point + for i := 0; i < testTimes; i++ { + for found := false; !found; { + _, _ = rand.Read(want[:]) + want[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 [EncodingSize]byte + byteDirty[EncodingSize-1] = 0x33 + copy(bigY[:], p[:]) + copy(wrongSignX[:], one[:]) + wrongSignX[EncodingSize-1] = 1 << 7 + nonQR[0] = 2 // smallest y such that (y^2+a)/(dy^2-a) is not a square. + + badEncodings := []*[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 Point + for _, enc := range badEncodings { + got := P.Decode(enc) + want := ErrInvalidDecoding + if got != want { + test.ReportError(t, got, want, enc) + } + } +} + +func BenchmarkEncoding(b *testing.B) { + var data [EncodingSize]byte + var k Scalar + _, _ = rand.Read(k[:]) + var P Point + P.ScalarBaseMult(&k) + b.Run("Marshal", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = P.Encode(&data) + } + }) + b.Run("Unmarshal", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = P.Decode(&data) + } + }) +}