From 382137dbee02b5fcd8d9b179a30770c45e18a77e Mon Sep 17 00:00:00 2001 From: armfazh Date: Fri, 24 Jul 2020 16:06:44 -0700 Subject: [PATCH 1/6] Adds decaf prime-order group. Squashed commit of the following: commit ec347f5313ca0b5aec78b2513c10faa479c05175 Author: armfazh Date: Fri Jul 24 15:28:34 2020 -0700 Adding decaf group. commit 796b37efe1b9fbfea7951402a9a4713efcf022be Author: armfazh Date: Wed Jul 22 19:44:07 2020 -0700 Updates internal packages of Ed448. commit f5957e78c9e4aef7ef651e4a28f7a346df7dc0be Author: armfazh Date: Wed Jul 22 19:11:15 2020 -0700 Updating Decaf documentation. commit 6f573e6b57fc5dc29989375a0335779943aad843 Author: armfazh Date: Wed Jul 22 13:56:12 2020 -0700 Updating documentation of ted448 internal package. commit 7e80bfc07a2c5dfac54a774a2b219c8bc0b295f3 Author: armfazh Date: Wed Jun 10 01:56:31 2020 -0700 Adapting ed448 for using the internal ted448 package. commit 44ce6c5b3e34ea6deaba5b85385dc3cbf5936a95 Author: armfazh Date: Tue Jun 9 23:36:17 2020 -0700 ted448 point public fields. commit 13dd30d0a93248c5a0b8f169bb95c535cb6322bc Author: armfazh Date: Tue Jun 9 20:32:32 2020 -0700 Moving twist implementation to an internal package. commit 4cdcb71b4235ec985d2e2ba92714fdc8ba00f535 Author: armfazh Date: Mon Jun 1 01:30:22 2020 -0700 Solving scalar constant-time operations. commit 26b9ea40fc336fa75578b70b0ee690389783a481 Author: armfazh Date: Thu May 21 09:21:36 2020 -0700 Review comments on formulas. commit ff821b5ca9323783e7b0f49519edb0b431f0f898 Author: armfazh Date: Tue May 19 16:54:36 2020 -0700 Adding tests for detecting decaf/point invalid encodings. commit 3881ea88e03ee17abbd7412f2aad28fcc90589a5 Author: armfazh Date: Fri May 15 15:13:53 2020 -0700 One test for decaf, unmarshaling straight-line code, and check for errors. commit 9b048c0b03bf9c02c9d1d118f0cae12b4c7fd97e Author: armfazh Date: Thu May 14 18:12:57 2020 -0700 Updating interface for decaf and curve. commit a99153c40441744c12459e8cb6f7c33cd08d462d Author: armfazh Date: Thu May 14 16:10:04 2020 -0700 Adding goldilocks documentation. commit a62d6bd75c486e44776fa782a3cf5ff349665740 Author: armfazh Date: Thu May 14 14:15:02 2020 -0700 Adding decaf v1.1 and kat tests. commit c72bdfad536a30a92eb6e630b10e39d67518325b Author: armfazh Date: Wed May 13 13:30:11 2020 -0700 Removing non-used fp functions. commit d4fc8652e073c89cfaa5907d164a320dce366c23 Author: armfazh Date: Tue May 12 11:47:57 2020 -0700 Adding some helper functions. commit 6173c837fd8410648973152e34c4db80d1cf06f8 Author: armfazh Date: Tue May 12 05:15:55 2020 -0700 Decaf decoding working. More tests needed. commit daa36c12b73865f403bfc63ba1f0a8e81a94982c Author: armfazh Date: Mon May 11 16:16:16 2020 -0700 Decaf encoding is working, except by the choice of generator. commit 8933f6c70552c83b92d4fc69b7a2872cff40cb0d Author: armfazh Date: Thu May 7 01:19:47 2020 -0700 Decaf encoding requires cannon sqrt. commit 9479b45bbacc2cb658569c8a70dc6366c6414663 Author: armfazh Date: Wed Apr 29 14:51:21 2020 -0700 Adding support for decaf quotient group. --- README.md | 3 +- ecc/decaf/constants.go | 28 ++ ecc/decaf/decaf.go | 191 ++++++++++++++ ecc/decaf/decaf_test.go | 239 ++++++++++++++++++ ecc/decaf/testdata/decafv1.0_vectors.json | 172 +++++++++++++ ecc/goldilocks/constants.go | 71 ------ ecc/goldilocks/curve.go | 80 ------ ecc/goldilocks/curve_test.go | 113 --------- ecc/goldilocks/goldilocks.go | 177 +++++++++++++ ecc/goldilocks/goldilocks_test.go | 122 +++++++++ ecc/goldilocks/isogeny.go | 52 ---- ecc/goldilocks/isogeny_test.go | 35 --- ecc/goldilocks/point.go | 171 ------------- ecc/goldilocks/point_test.go | 101 -------- ecc/goldilocks/twist.go | 138 ---------- ecc/goldilocks/twistPoint.go | 135 ---------- ecc/goldilocks/twist_basemult.go | 62 ----- internal/ted448/basemult.go | 63 +++++ internal/ted448/constants.go | 51 ++++ internal/ted448/curve.go | 181 +++++++++++++ internal/ted448/curve_test.go | 172 +++++++++++++ internal/ted448/point.go | 174 +++++++++++++ {ecc/goldilocks => internal/ted448}/scalar.go | 86 +++++-- .../ted448}/scalar_test.go | 44 ++-- .../ted448/tables.go | 75 +++--- math/fp448/fp.go | 3 + sign/ed25519/ed25519_test.go | 10 +- sign/ed25519/extra_test.go | 4 +- sign/ed448/ed448.go | 52 ++-- sign/ed448/ed448_test.go | 20 +- 30 files changed, 1738 insertions(+), 1087 deletions(-) create mode 100644 ecc/decaf/constants.go create mode 100644 ecc/decaf/decaf.go create mode 100644 ecc/decaf/decaf_test.go create mode 100644 ecc/decaf/testdata/decafv1.0_vectors.json delete mode 100644 ecc/goldilocks/constants.go delete mode 100644 ecc/goldilocks/curve.go delete mode 100644 ecc/goldilocks/curve_test.go create mode 100644 ecc/goldilocks/goldilocks.go create mode 100644 ecc/goldilocks/goldilocks_test.go delete mode 100644 ecc/goldilocks/isogeny.go delete mode 100644 ecc/goldilocks/isogeny_test.go delete mode 100644 ecc/goldilocks/point.go delete mode 100644 ecc/goldilocks/point_test.go delete mode 100644 ecc/goldilocks/twist.go delete mode 100644 ecc/goldilocks/twistPoint.go delete mode 100644 ecc/goldilocks/twist_basemult.go create mode 100644 internal/ted448/basemult.go create mode 100644 internal/ted448/constants.go create mode 100644 internal/ted448/curve.go create mode 100644 internal/ted448/curve_test.go create mode 100644 internal/ted448/point.go rename {ecc/goldilocks => internal/ted448}/scalar.go (76%) rename {ecc/goldilocks => internal/ted448}/scalar_test.go (68%) rename ecc/goldilocks/twistTables.go => internal/ted448/tables.go (98%) diff --git a/README.md b/README.md index 8f2fa0a4..bb946f8e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ # CIRCL -[![GitHub release](https://img.shields.io/github/release/cloudflare/circl.svg)](https://GitHub.com/cloudflare/circl/releases/) [![CIRCL](https://github.com/cloudflare/circl/workflows/CIRCL/badge.svg)](https://github.com/cloudflare/circl/actions) [![GoDoc](https://godoc.org/github.com/cloudflare/circl?status.svg)](https://pkg.go.dev/github.com/cloudflare/circl?tab=overview) [![Go Report Card](https://goreportcard.com/badge/github.com/cloudflare/circl)](https://goreportcard.com/report/github.com/cloudflare/circl) @@ -38,7 +37,7 @@ go get -u github.com/cloudflare/circl #### Groups based on Elliptic Curves - P-256, P-384, P-521, [FIPS 186-4](https://doi.org/10.6028/NIST.FIPS.186-4) - - [Ristretto](https://datatracker.ietf.org/doc/draft-irtf-cfrg-ristretto255-decaf448/01/) + - [Ristretto, Decaf](https://datatracker.ietf.org/doc/draft-irtf-cfrg-ristretto255-decaf448/01/) - [Hash to Curve](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/) #### High-Level Protocols diff --git a/ecc/decaf/constants.go b/ecc/decaf/constants.go new file mode 100644 index 00000000..a57435a7 --- /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 00000000..62278dc2 --- /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 00000000..be4d87ce --- /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 00000000..61f6788e --- /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 b6b236e5..00000000 --- a/ecc/goldilocks/constants.go +++ /dev/null @@ -1,71 +0,0 @@ -package goldilocks - -import fp "github.com/cloudflare/circl/math/fp448" - -var ( - // genX is the x-coordinate of the generator of Goldilocks curve. - genX = fp.Elt{ - 0x5e, 0xc0, 0x0c, 0xc7, 0x2b, 0xa8, 0x26, 0x26, - 0x8e, 0x93, 0x00, 0x8b, 0xe1, 0x80, 0x3b, 0x43, - 0x11, 0x65, 0xb6, 0x2a, 0xf7, 0x1a, 0xae, 0x12, - 0x64, 0xa4, 0xd3, 0xa3, 0x24, 0xe3, 0x6d, 0xea, - 0x67, 0x17, 0x0f, 0x47, 0x70, 0x65, 0x14, 0x9e, - 0xda, 0x36, 0xbf, 0x22, 0xa6, 0x15, 0x1d, 0x22, - 0xed, 0x0d, 0xed, 0x6b, 0xc6, 0x70, 0x19, 0x4f, - } - // genY is the y-coordinate of the generator of Goldilocks curve. - genY = fp.Elt{ - 0x14, 0xfa, 0x30, 0xf2, 0x5b, 0x79, 0x08, 0x98, - 0xad, 0xc8, 0xd7, 0x4e, 0x2c, 0x13, 0xbd, 0xfd, - 0xc4, 0x39, 0x7c, 0xe6, 0x1c, 0xff, 0xd3, 0x3a, - 0xd7, 0xc2, 0xa0, 0x05, 0x1e, 0x9c, 0x78, 0x87, - 0x40, 0x98, 0xa3, 0x6c, 0x73, 0x73, 0xea, 0x4b, - 0x62, 0xc7, 0xc9, 0x56, 0x37, 0x20, 0x76, 0x88, - 0x24, 0xbc, 0xb6, 0x6e, 0x71, 0x46, 0x3f, 0x69, - } - // paramD is -39081 in Fp. - paramD = fp.Elt{ - 0x56, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - } - // order is 2^446-0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d, - // which is the number of points in the prime subgroup. - order = Scalar{ - 0xf3, 0x44, 0x58, 0xab, 0x92, 0xc2, 0x78, 0x23, - 0x55, 0x8f, 0xc5, 0x8d, 0x72, 0xc2, 0x6c, 0x21, - 0x90, 0x36, 0xd6, 0xae, 0x49, 0xdb, 0x4e, 0xc4, - 0xe9, 0x23, 0xca, 0x7c, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, - } - // residue448 is 2^448 mod order. - residue448 = [4]uint64{ - 0x721cf5b5529eec34, 0x7a4cf635c8e9c2ab, 0xeec492d944a725bf, 0x20cd77058, - } - // invFour is 1/4 mod order. - invFour = Scalar{ - 0x3d, 0x11, 0xd6, 0xaa, 0xa4, 0x30, 0xde, 0x48, - 0xd5, 0x63, 0x71, 0xa3, 0x9c, 0x30, 0x5b, 0x08, - 0xa4, 0x8d, 0xb5, 0x6b, 0xd2, 0xb6, 0x13, 0x71, - 0xfa, 0x88, 0x32, 0xdf, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, - } - // paramDTwist is -39082 in Fp. The D parameter of the twist curve. - paramDTwist = fp.Elt{ - 0x55, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - } -) diff --git a/ecc/goldilocks/curve.go b/ecc/goldilocks/curve.go deleted file mode 100644 index 5a939100..00000000 --- a/ecc/goldilocks/curve.go +++ /dev/null @@ -1,80 +0,0 @@ -// Package goldilocks provides elliptic curve operations over the goldilocks curve. -package goldilocks - -import fp "github.com/cloudflare/circl/math/fp448" - -// Curve is the Goldilocks curve x^2+y^2=z^2-39081x^2y^2. -type Curve struct{} - -// Identity returns the identity point. -func (Curve) Identity() *Point { - return &Point{ - y: fp.One(), - z: fp.One(), - } -} - -// IsOnCurve returns true if the point lies on the curve. -func (Curve) IsOnCurve(P *Point) bool { - x2, y2, t, t2, z2 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} - rhs, lhs := &fp.Elt{}, &fp.Elt{} - fp.Mul(t, &P.ta, &P.tb) // t = ta*tb - fp.Sqr(x2, &P.x) // x^2 - fp.Sqr(y2, &P.y) // y^2 - fp.Sqr(z2, &P.z) // z^2 - fp.Sqr(t2, t) // t^2 - fp.Add(lhs, x2, y2) // x^2 + y^2 - fp.Mul(rhs, t2, ¶mD) // dt^2 - fp.Add(rhs, rhs, z2) // z^2 + dt^2 - fp.Sub(lhs, lhs, rhs) // x^2 + y^2 - (z^2 + dt^2) - eq0 := fp.IsZero(lhs) - - fp.Mul(lhs, &P.x, &P.y) // xy - fp.Mul(rhs, t, &P.z) // tz - fp.Sub(lhs, lhs, rhs) // xy - tz - eq1 := fp.IsZero(lhs) - return eq0 && eq1 -} - -// Generator returns the generator point. -func (Curve) Generator() *Point { - return &Point{ - x: genX, - y: genY, - z: fp.One(), - ta: genX, - tb: genY, - } -} - -// Order returns the number of points in the prime subgroup. -func (Curve) Order() Scalar { return order } - -// Double returns 2P. -func (Curve) Double(P *Point) *Point { R := *P; R.Double(); return &R } - -// Add returns P+Q. -func (Curve) Add(P, Q *Point) *Point { R := *P; R.Add(Q); return &R } - -// ScalarMult returns kP. This function runs in constant time. -func (e Curve) ScalarMult(k *Scalar, P *Point) *Point { - k4 := &Scalar{} - k4.divBy4(k) - return e.pull(twistCurve{}.ScalarMult(k4, e.push(P))) -} - -// ScalarBaseMult returns kG where G is the generator point. This function runs in constant time. -func (e Curve) ScalarBaseMult(k *Scalar) *Point { - k4 := &Scalar{} - k4.divBy4(k) - return e.pull(twistCurve{}.ScalarBaseMult(k4)) -} - -// CombinedMult returns mG+nP, where G is the generator point. This function is non-constant time. -func (e Curve) CombinedMult(m, n *Scalar, P *Point) *Point { - m4 := &Scalar{} - n4 := &Scalar{} - m4.divBy4(m) - n4.divBy4(n) - return e.pull(twistCurve{}.CombinedMult(m4, n4, twistCurve{}.pull(P))) -} diff --git a/ecc/goldilocks/curve_test.go b/ecc/goldilocks/curve_test.go deleted file mode 100644 index 95c15d96..00000000 --- a/ecc/goldilocks/curve_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package goldilocks_test - -import ( - "crypto/rand" - "testing" - - "github.com/cloudflare/circl/ecc/goldilocks" - "github.com/cloudflare/circl/internal/test" -) - -func TestScalarMult(t *testing.T) { - const testTimes = 1 << 8 - var e goldilocks.Curve - k := &goldilocks.Scalar{} - zero := &goldilocks.Scalar{} - - t.Run("rG=0", func(t *testing.T) { - order := e.Order() - for i := 0; i < testTimes; i++ { - got := e.ScalarBaseMult(&order) - got.ToAffine() - want := e.Identity() - - if !e.IsOnCurve(got) || !e.IsOnCurve(want) || !got.IsEqual(want) { - want.ToAffine() - test.ReportError(t, got, want) - } - } - }) - t.Run("rP=0", func(t *testing.T) { - order := e.Order() - for i := 0; i < testTimes; i++ { - P := randomPoint() - - got := e.ScalarMult(&order, P) - got.ToAffine() - want := e.Identity() - - if !e.IsOnCurve(got) || !e.IsOnCurve(want) || !got.IsEqual(want) { - want.ToAffine() - test.ReportError(t, got, want, P, order) - } - } - }) - t.Run("kG", func(t *testing.T) { - I := e.Identity() - for i := 0; i < testTimes; i++ { - _, _ = rand.Read(k[:]) - - got := e.ScalarBaseMult(k) - want := e.CombinedMult(k, zero, I) // k*G + 0*I - - if !e.IsOnCurve(got) || !e.IsOnCurve(want) || !got.IsEqual(want) { - test.ReportError(t, got, want, k) - } - } - }) - t.Run("kP", func(t *testing.T) { - for i := 0; i < testTimes; i++ { - P := randomPoint() - _, _ = rand.Read(k[:]) - - got := e.ScalarMult(k, P) - want := e.CombinedMult(zero, k, P) - - if !e.IsOnCurve(got) || !e.IsOnCurve(want) || !got.IsEqual(want) { - test.ReportError(t, got, want, P, k) - } - } - }) - t.Run("kG+lP", func(t *testing.T) { - G := e.Generator() - l := &goldilocks.Scalar{} - for i := 0; i < testTimes; i++ { - P := randomPoint() - _, _ = rand.Read(k[:]) - _, _ = rand.Read(l[:]) - - kG := e.ScalarMult(k, G) - lP := e.ScalarMult(l, P) - got := e.Add(kG, lP) - want := e.CombinedMult(k, l, P) - - if !e.IsOnCurve(got) || !e.IsOnCurve(want) || !got.IsEqual(want) { - test.ReportError(t, got, want, P, k, l) - } - } - }) -} - -func BenchmarkCurve(b *testing.B) { - var e goldilocks.Curve - var k, l goldilocks.Scalar - _, _ = rand.Read(k[:]) - _, _ = rand.Read(l[:]) - P := randomPoint() - - b.Run("ScalarMult", func(b *testing.B) { - for i := 0; i < b.N; i++ { - P = e.ScalarMult(&k, P) - } - }) - b.Run("ScalarBaseMult", func(b *testing.B) { - for i := 0; i < b.N; i++ { - e.ScalarBaseMult(&k) - } - }) - b.Run("CombinedMult", func(b *testing.B) { - for i := 0; i < b.N; i++ { - P = e.CombinedMult(&k, &l, P) - } - }) -} diff --git a/ecc/goldilocks/goldilocks.go b/ecc/goldilocks/goldilocks.go new file mode 100644 index 00000000..93e406e6 --- /dev/null +++ b/ecc/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/ecc/goldilocks/goldilocks_test.go b/ecc/goldilocks/goldilocks_test.go new file mode 100644 index 00000000..3dde2a3f --- /dev/null +++ b/ecc/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) + } + }) +} diff --git a/ecc/goldilocks/isogeny.go b/ecc/goldilocks/isogeny.go deleted file mode 100644 index b1daab85..00000000 --- a/ecc/goldilocks/isogeny.go +++ /dev/null @@ -1,52 +0,0 @@ -package goldilocks - -import fp "github.com/cloudflare/circl/math/fp448" - -func (Curve) pull(P *twistPoint) *Point { return twistCurve{}.push(P) } -func (twistCurve) pull(P *Point) *twistPoint { return Curve{}.push(P) } - -// push sends a point on the Goldilocks curve to a point on the twist curve. -func (Curve) push(P *Point) *twistPoint { - Q := &twistPoint{} - Px, Py, Pz := &P.x, &P.y, &P.z - a, b, c, d, e, f, g, h := &Q.x, &Q.y, &Q.z, &fp.Elt{}, &Q.ta, &Q.x, &Q.y, &Q.tb - fp.Add(e, Px, Py) // x+y - fp.Sqr(a, Px) // A = x^2 - fp.Sqr(b, Py) // B = y^2 - fp.Sqr(c, Pz) // z^2 - fp.Add(c, c, c) // C = 2*z^2 - *d = *a // D = A - fp.Sqr(e, e) // (x+y)^2 - fp.Sub(e, e, a) // (x+y)^2-A - fp.Sub(e, e, b) // E = (x+y)^2-A-B - fp.Add(h, b, d) // H = B+D - fp.Sub(g, b, d) // G = B-D - fp.Sub(f, c, h) // F = C-H - fp.Mul(&Q.z, f, g) // Z = F * G - fp.Mul(&Q.x, e, f) // X = E * F - fp.Mul(&Q.y, g, h) // Y = G * H, // T = E * H - return Q -} - -// push sends a point on the twist curve to a point on the Goldilocks curve. -func (twistCurve) push(P *twistPoint) *Point { - Q := &Point{} - Px, Py, Pz := &P.x, &P.y, &P.z - a, b, c, d, e, f, g, h := &Q.x, &Q.y, &Q.z, &fp.Elt{}, &Q.ta, &Q.x, &Q.y, &Q.tb - fp.Add(e, Px, Py) // x+y - fp.Sqr(a, Px) // A = x^2 - fp.Sqr(b, Py) // B = y^2 - fp.Sqr(c, Pz) // z^2 - fp.Add(c, c, c) // C = 2*z^2 - fp.Neg(d, a) // D = -A - fp.Sqr(e, e) // (x+y)^2 - fp.Sub(e, e, a) // (x+y)^2-A - fp.Sub(e, e, b) // E = (x+y)^2-A-B - fp.Add(h, b, d) // H = B+D - fp.Sub(g, b, d) // G = B-D - fp.Sub(f, c, h) // F = C-H - fp.Mul(&Q.z, f, g) // Z = F * G - fp.Mul(&Q.x, e, f) // X = E * F - fp.Mul(&Q.y, g, h) // Y = G * H, // T = E * H - return Q -} diff --git a/ecc/goldilocks/isogeny_test.go b/ecc/goldilocks/isogeny_test.go deleted file mode 100644 index ffaf8c05..00000000 --- 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 11f73de0..00000000 --- a/ecc/goldilocks/point.go +++ /dev/null @@ -1,171 +0,0 @@ -package goldilocks - -import ( - "errors" - "fmt" - - fp "github.com/cloudflare/circl/math/fp448" -) - -// Point is a point on the Goldilocks Curve. -type Point struct{ x, y, z, ta, tb fp.Elt } - -func (P Point) String() string { - return fmt.Sprintf("x: %v\ny: %v\nz: %v\nta: %v\ntb: %v", P.x, P.y, P.z, P.ta, P.tb) -} - -// FromAffine creates a point from affine coordinates. -func FromAffine(x, y *fp.Elt) (*Point, error) { - P := &Point{ - x: *x, - y: *y, - z: fp.One(), - ta: *x, - tb: *y, - } - if !(Curve{}).IsOnCurve(P) { - return P, errors.New("point not on curve") - } - return P, nil -} - -// isLessThan returns true if 0 <= x < y, and assumes that slices are of the -// same length and are interpreted in little-endian order. -func isLessThan(x, y []byte) bool { - i := len(x) - 1 - for i > 0 && x[i] == y[i] { - i-- - } - return x[i] < y[i] -} - -// FromBytes returns a point from the input buffer. -func FromBytes(in []byte) (*Point, error) { - if len(in) < fp.Size+1 { - return nil, errors.New("wrong input length") - } - err := errors.New("invalid decoding") - P := &Point{} - signX := in[fp.Size] >> 7 - copy(P.y[:], in[:fp.Size]) - p := fp.P() - if !isLessThan(P.y[:], p[:]) { - return nil, err - } - - u, v := &fp.Elt{}, &fp.Elt{} - one := fp.One() - fp.Sqr(u, &P.y) // u = y^2 - fp.Mul(v, u, ¶mD) // v = dy^2 - fp.Sub(u, u, &one) // u = y^2-1 - fp.Sub(v, v, &one) // v = dy^2-1 - isQR := fp.InvSqrt(&P.x, u, v) // x = sqrt(u/v) - if !isQR { - return nil, err - } - fp.Modp(&P.x) // x = x mod p - if fp.IsZero(&P.x) && signX == 1 { - return nil, err - } - if signX != (P.x[0] & 1) { - fp.Neg(&P.x, &P.x) - } - P.ta = P.x - P.tb = P.y - P.z = fp.One() - return P, nil -} - -// IsIdentity returns true is P is the identity Point. -func (P *Point) IsIdentity() bool { - return fp.IsZero(&P.x) && !fp.IsZero(&P.y) && !fp.IsZero(&P.z) && P.y == P.z -} - -// IsEqual returns true if P is equivalent to Q. -func (P *Point) IsEqual(Q *Point) bool { - l, r := &fp.Elt{}, &fp.Elt{} - fp.Mul(l, &P.x, &Q.z) - fp.Mul(r, &Q.x, &P.z) - fp.Sub(l, l, r) - b := fp.IsZero(l) - fp.Mul(l, &P.y, &Q.z) - fp.Mul(r, &Q.y, &P.z) - fp.Sub(l, l, r) - b = b && fp.IsZero(l) - fp.Mul(l, &P.ta, &P.tb) - fp.Mul(l, l, &Q.z) - fp.Mul(r, &Q.ta, &Q.tb) - fp.Mul(r, r, &P.z) - fp.Sub(l, l, r) - b = b && fp.IsZero(l) - return b -} - -// Neg obtains the inverse of the Point. -func (P *Point) Neg() { fp.Neg(&P.x, &P.x); fp.Neg(&P.ta, &P.ta) } - -// ToAffine returns the x,y affine coordinates of P. -func (P *Point) ToAffine() (x, y fp.Elt) { - fp.Inv(&P.z, &P.z) // 1/z - fp.Mul(&P.x, &P.x, &P.z) // x/z - fp.Mul(&P.y, &P.y, &P.z) // y/z - fp.Modp(&P.x) - fp.Modp(&P.y) - fp.SetOne(&P.z) - P.ta = P.x - P.tb = P.y - return P.x, P.y -} - -// ToBytes stores P into a slice of bytes. -func (P *Point) ToBytes(out []byte) error { - if len(out) < fp.Size+1 { - return errors.New("invalid decoding") - } - x, y := P.ToAffine() - out[fp.Size] = (x[0] & 1) << 7 - return fp.ToBytes(out[:fp.Size], &y) -} - -// MarshalBinary encodes the receiver into a binary form and returns the result. -func (P *Point) MarshalBinary() (data []byte, err error) { - data = make([]byte, fp.Size+1) - err = P.ToBytes(data[:fp.Size+1]) - return data, err -} - -// UnmarshalBinary must be able to decode the form generated by MarshalBinary. -func (P *Point) UnmarshalBinary(data []byte) error { Q, err := FromBytes(data); *P = *Q; return err } - -// Double sets P = 2Q. -func (P *Point) Double() { P.Add(P) } - -// Add sets P =P+Q.. -func (P *Point) Add(Q *Point) { - // This is formula (5) from "Twisted Edwards Curves Revisited" by - // Hisil H., Wong K.KH., Carter G., Dawson E. (2008) - // https://doi.org/10.1007/978-3-540-89255-7_20 - x1, y1, z1, ta1, tb1 := &P.x, &P.y, &P.z, &P.ta, &P.tb - x2, y2, z2, ta2, tb2 := &Q.x, &Q.y, &Q.z, &Q.ta, &Q.tb - x3, y3, z3, E, H := &P.x, &P.y, &P.z, &P.ta, &P.tb - A, B, C, D := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} - t1, t2, F, G := C, D, &fp.Elt{}, &fp.Elt{} - fp.Mul(t1, ta1, tb1) // t1 = ta1*tb1 - fp.Mul(t2, ta2, tb2) // t2 = ta2*tb2 - fp.Mul(A, x1, x2) // A = x1*x2 - fp.Mul(B, y1, y2) // B = y1*y2 - fp.Mul(C, t1, t2) // t1*t2 - fp.Mul(C, C, ¶mD) // C = d*t1*t2 - fp.Mul(D, z1, z2) // D = z1*z2 - fp.Add(F, x1, y1) // x1+y1 - fp.Add(E, x2, y2) // x2+y2 - fp.Mul(E, E, F) // (x1+y1)*(x2+y2) - fp.Sub(E, E, A) // (x1+y1)*(x2+y2)-A - fp.Sub(E, E, B) // E = (x1+y1)*(x2+y2)-A-B - fp.Sub(F, D, C) // F = D-C - fp.Add(G, D, C) // G = D+C - fp.Sub(H, B, A) // H = B-A - fp.Mul(z3, F, G) // Z = F * G - fp.Mul(x3, E, F) // X = E * F - fp.Mul(y3, G, H) // Y = G * H, T = E * H -} diff --git a/ecc/goldilocks/point_test.go b/ecc/goldilocks/point_test.go deleted file mode 100644 index a25bb5e0..00000000 --- a/ecc/goldilocks/point_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package goldilocks_test - -import ( - "crypto/rand" - "encoding" - "testing" - - "github.com/cloudflare/circl/ecc/goldilocks" - "github.com/cloudflare/circl/internal/test" -) - -func randomPoint() *goldilocks.Point { - var k goldilocks.Scalar - _, _ = rand.Read(k[:]) - return goldilocks.Curve{}.ScalarBaseMult(&k) -} - -func TestPointAdd(t *testing.T) { - const testTimes = 1 << 10 - var e goldilocks.Curve - for i := 0; i < testTimes; i++ { - P := randomPoint() - // 16P = 2^4P - got := e.Double(e.Double(e.Double(e.Double(P)))) - // 16P = P+P...+P - Q := e.Identity() - for j := 0; j < 16; j++ { - Q = e.Add(Q, P) - } - want := Q - if !e.IsOnCurve(got) || !e.IsOnCurve(want) || !got.IsEqual(want) { - test.ReportError(t, got, want, P) - } - } -} - -func TestPointNeg(t *testing.T) { - const testTimes = 1 << 10 - var e goldilocks.Curve - for i := 0; i < testTimes; i++ { - P := randomPoint() - Q := *P - Q.Neg() - R := e.Add(P, &Q) - got := R.IsIdentity() - want := true - if got != want { - test.ReportError(t, got, want, P) - } - } -} - -func TestPointAffine(t *testing.T) { - const testTimes = 1 << 10 - for i := 0; i < testTimes; i++ { - got := randomPoint() - x, y := got.ToAffine() - want, err := goldilocks.FromAffine(&x, &y) - if !got.IsEqual(want) || err != nil { - test.ReportError(t, got, want) - } - } -} - -func TestPointMarshal(t *testing.T) { - const testTimes = 1 << 10 - var want error - for i := 0; i < testTimes; i++ { - var P interface{} = randomPoint() - mar, _ := P.(encoding.BinaryMarshaler) - data, got := mar.MarshalBinary() - if got != want { - test.ReportError(t, got, want, P) - } - unmar, _ := P.(encoding.BinaryUnmarshaler) - got = unmar.UnmarshalBinary(data) - if got != want { - test.ReportError(t, got, want, P) - } - } -} - -func BenchmarkPoint(b *testing.B) { - P := randomPoint() - Q := randomPoint() - b.Run("ToAffine", func(b *testing.B) { - for i := 0; i < b.N; i++ { - P.ToAffine() - } - }) - b.Run("Add", func(b *testing.B) { - for i := 0; i < b.N; i++ { - P.Add(Q) - } - }) - b.Run("Double", func(b *testing.B) { - for i := 0; i < b.N; i++ { - P.Double() - } - }) -} diff --git a/ecc/goldilocks/twist.go b/ecc/goldilocks/twist.go deleted file mode 100644 index 8cd4e333..00000000 --- a/ecc/goldilocks/twist.go +++ /dev/null @@ -1,138 +0,0 @@ -package goldilocks - -import ( - "crypto/subtle" - "math/bits" - - "github.com/cloudflare/circl/internal/conv" - "github.com/cloudflare/circl/math" - fp "github.com/cloudflare/circl/math/fp448" -) - -// twistCurve is -x^2+y^2=1-39082x^2y^2 and is 4-isogeneous to Goldilocks. -type twistCurve struct{} - -// Identity returns the identity point. -func (twistCurve) Identity() *twistPoint { - return &twistPoint{ - y: fp.One(), - z: fp.One(), - } -} - -// subYDiv16 update x = (x - y) / 16. -func subYDiv16(x *scalar64, y int64) { - s := uint64(y >> 63) - x0, b0 := bits.Sub64((*x)[0], uint64(y), 0) - x1, b1 := bits.Sub64((*x)[1], s, b0) - x2, b2 := bits.Sub64((*x)[2], s, b1) - x3, b3 := bits.Sub64((*x)[3], s, b2) - x4, b4 := bits.Sub64((*x)[4], s, b3) - x5, b5 := bits.Sub64((*x)[5], s, b4) - x6, _ := bits.Sub64((*x)[6], s, b5) - x[0] = (x0 >> 4) | (x1 << 60) - x[1] = (x1 >> 4) | (x2 << 60) - x[2] = (x2 >> 4) | (x3 << 60) - x[3] = (x3 >> 4) | (x4 << 60) - x[4] = (x4 >> 4) | (x5 << 60) - x[5] = (x5 >> 4) | (x6 << 60) - x[6] = (x6 >> 4) -} - -func recodeScalar(d *[113]int8, k *Scalar) { - var k64 scalar64 - k64.fromScalar(k) - for i := 0; i < 112; i++ { - d[i] = int8((k64[0] & 0x1f) - 16) - subYDiv16(&k64, int64(d[i])) - } - d[112] = int8(k64[0]) -} - -// ScalarMult returns kP. -func (e twistCurve) ScalarMult(k *Scalar, P *twistPoint) *twistPoint { - var TabP [8]preTwistPointProy - var S preTwistPointProy - var d [113]int8 - - var isZero int - if k.IsZero() { - isZero = 1 - } - subtle.ConstantTimeCopy(isZero, k[:], order[:]) - - minusK := *k - isEven := 1 - int(k[0]&0x1) - minusK.Neg() - subtle.ConstantTimeCopy(isEven, k[:], minusK[:]) - recodeScalar(&d, k) - - P.oddMultiples(TabP[:]) - Q := e.Identity() - for i := 112; i >= 0; i-- { - Q.Double() - Q.Double() - Q.Double() - Q.Double() - mask := d[i] >> 7 - absDi := (d[i] + mask) ^ mask - inx := int32((absDi - 1) >> 1) - sig := int((d[i] >> 7) & 0x1) - for j := range TabP { - S.cmov(&TabP[j], uint(subtle.ConstantTimeEq(inx, int32(j)))) - } - S.cneg(sig) - Q.mixAdd(&S) - } - Q.cneg(uint(isEven)) - return Q -} - -const ( - omegaFix = 7 - omegaVar = 5 -) - -// CombinedMult returns mG+nP. -func (e twistCurve) CombinedMult(m, n *Scalar, P *twistPoint) *twistPoint { - nafFix := math.OmegaNAF(conv.BytesLe2BigInt(m[:]), omegaFix) - nafVar := math.OmegaNAF(conv.BytesLe2BigInt(n[:]), omegaVar) - - if len(nafFix) > len(nafVar) { - nafVar = append(nafVar, make([]int32, len(nafFix)-len(nafVar))...) - } else if len(nafFix) < len(nafVar) { - nafFix = append(nafFix, make([]int32, len(nafVar)-len(nafFix))...) - } - - var TabQ [1 << (omegaVar - 2)]preTwistPointProy - P.oddMultiples(TabQ[:]) - Q := e.Identity() - for i := len(nafFix) - 1; i >= 0; i-- { - Q.Double() - // Generator point - if nafFix[i] != 0 { - idxM := absolute(nafFix[i]) >> 1 - R := tabVerif[idxM] - if nafFix[i] < 0 { - R.neg() - } - Q.mixAddZ1(&R) - } - // Variable input point - if nafVar[i] != 0 { - idxN := absolute(nafVar[i]) >> 1 - S := TabQ[idxN] - if nafVar[i] < 0 { - S.neg() - } - Q.mixAdd(&S) - } - } - return Q -} - -// absolute returns always a positive value. -func absolute(x int32) int32 { - mask := x >> 31 - return (x + mask) ^ mask -} diff --git a/ecc/goldilocks/twistPoint.go b/ecc/goldilocks/twistPoint.go deleted file mode 100644 index c55db77b..00000000 --- a/ecc/goldilocks/twistPoint.go +++ /dev/null @@ -1,135 +0,0 @@ -package goldilocks - -import ( - "fmt" - - fp "github.com/cloudflare/circl/math/fp448" -) - -type twistPoint struct{ x, y, z, ta, tb fp.Elt } - -type preTwistPointAffine struct{ addYX, subYX, dt2 fp.Elt } - -type preTwistPointProy struct { - preTwistPointAffine - z2 fp.Elt -} - -func (P *twistPoint) String() string { - return fmt.Sprintf("x: %v\ny: %v\nz: %v\nta: %v\ntb: %v", P.x, P.y, P.z, P.ta, P.tb) -} - -// cneg conditionally negates the point if b=1. -func (P *twistPoint) cneg(b uint) { - t := &fp.Elt{} - fp.Neg(t, &P.x) - fp.Cmov(&P.x, t, b) - fp.Neg(t, &P.ta) - fp.Cmov(&P.ta, t, b) -} - -// Double updates P with 2P. -func (P *twistPoint) Double() { - // This is formula (7) from "Twisted Edwards Curves Revisited" by - // Hisil H., Wong K.KH., Carter G., Dawson E. (2008) - // https://doi.org/10.1007/978-3-540-89255-7_20 - Px, Py, Pz, Pta, Ptb := &P.x, &P.y, &P.z, &P.ta, &P.tb - a, b, c, e, f, g, h := Px, Py, Pz, Pta, Px, Py, Ptb - fp.Add(e, Px, Py) // x+y - fp.Sqr(a, Px) // A = x^2 - fp.Sqr(b, Py) // B = y^2 - fp.Sqr(c, Pz) // z^2 - fp.Add(c, c, c) // C = 2*z^2 - fp.Add(h, a, b) // H = A+B - fp.Sqr(e, e) // (x+y)^2 - fp.Sub(e, e, h) // E = (x+y)^2-A-B - fp.Sub(g, b, a) // G = B-A - fp.Sub(f, c, g) // F = C-G - fp.Mul(Pz, f, g) // Z = F * G - fp.Mul(Px, e, f) // X = E * F - fp.Mul(Py, g, h) // Y = G * H, T = E * H -} - -// mixAdd calculates P= P+Q, where Q is a precomputed point with Z_Q = 1. -func (P *twistPoint) mixAddZ1(Q *preTwistPointAffine) { - fp.Add(&P.z, &P.z, &P.z) // D = 2*z1 (z2=1) - P.coreAddition(Q) -} - -// coreAddition calculates P=P+Q for curves with A=-1. -func (P *twistPoint) coreAddition(Q *preTwistPointAffine) { - // This is the formula following (5) from "Twisted Edwards Curves Revisited" by - // Hisil H., Wong K.KH., Carter G., Dawson E. (2008) - // https://doi.org/10.1007/978-3-540-89255-7_20 - Px, Py, Pz, Pta, Ptb := &P.x, &P.y, &P.z, &P.ta, &P.tb - addYX2, subYX2, dt2 := &Q.addYX, &Q.subYX, &Q.dt2 - a, b, c, d, e, f, g, h := Px, Py, &fp.Elt{}, Pz, Pta, Px, Py, Ptb - fp.Mul(c, Pta, Ptb) // t1 = ta*tb - fp.Sub(h, Py, Px) // y1-x1 - fp.Add(b, Py, Px) // y1+x1 - fp.Mul(a, h, subYX2) // A = (y1-x1)*(y2-x2) - fp.Mul(b, b, addYX2) // B = (y1+x1)*(y2+x2) - fp.Mul(c, c, dt2) // C = 2*D*t1*t2 - fp.Sub(e, b, a) // E = B-A - fp.Add(h, b, a) // H = B+A - fp.Sub(f, d, c) // F = D-C - fp.Add(g, d, c) // G = D+C - fp.Mul(Pz, f, g) // Z = F * G - fp.Mul(Px, e, f) // X = E * F - fp.Mul(Py, g, h) // Y = G * H, T = E * H -} - -func (P *preTwistPointAffine) neg() { - P.addYX, P.subYX = P.subYX, P.addYX - fp.Neg(&P.dt2, &P.dt2) -} - -func (P *preTwistPointAffine) cneg(b int) { - t := &fp.Elt{} - fp.Cswap(&P.addYX, &P.subYX, uint(b)) - fp.Neg(t, &P.dt2) - fp.Cmov(&P.dt2, t, uint(b)) -} - -func (P *preTwistPointAffine) cmov(Q *preTwistPointAffine, b uint) { - fp.Cmov(&P.addYX, &Q.addYX, b) - fp.Cmov(&P.subYX, &Q.subYX, b) - fp.Cmov(&P.dt2, &Q.dt2, b) -} - -// mixAdd calculates P= P+Q, where Q is a precomputed point with Z_Q != 1. -func (P *twistPoint) mixAdd(Q *preTwistPointProy) { - fp.Mul(&P.z, &P.z, &Q.z2) // D = 2*z1*z2 - P.coreAddition(&Q.preTwistPointAffine) -} - -// oddMultiples calculates T[i] = (2*i-1)P for 0 < i < len(T). -func (P *twistPoint) oddMultiples(T []preTwistPointProy) { - if n := len(T); n > 0 { - T[0].FromTwistPoint(P) - _2P := *P - _2P.Double() - R := &preTwistPointProy{} - R.FromTwistPoint(&_2P) - for i := 1; i < n; i++ { - P.mixAdd(R) - T[i].FromTwistPoint(P) - } - } -} - -// cmov conditionally moves Q into P if b=1. -func (P *preTwistPointProy) cmov(Q *preTwistPointProy, b uint) { - P.preTwistPointAffine.cmov(&Q.preTwistPointAffine, b) - fp.Cmov(&P.z2, &Q.z2, b) -} - -// FromTwistPoint precomputes some coordinates of Q for missed addition. -func (P *preTwistPointProy) FromTwistPoint(Q *twistPoint) { - fp.Add(&P.addYX, &Q.y, &Q.x) // addYX = X + Y - fp.Sub(&P.subYX, &Q.y, &Q.x) // subYX = Y - X - fp.Mul(&P.dt2, &Q.ta, &Q.tb) // T = ta*tb - fp.Mul(&P.dt2, &P.dt2, ¶mDTwist) // D*T - fp.Add(&P.dt2, &P.dt2, &P.dt2) // dt2 = 2*D*T - fp.Add(&P.z2, &Q.z, &Q.z) // z2 = 2*Z -} diff --git a/ecc/goldilocks/twist_basemult.go b/ecc/goldilocks/twist_basemult.go deleted file mode 100644 index f6ac5edb..00000000 --- a/ecc/goldilocks/twist_basemult.go +++ /dev/null @@ -1,62 +0,0 @@ -package goldilocks - -import ( - "crypto/subtle" - - mlsb "github.com/cloudflare/circl/math/mlsbset" -) - -const ( - // MLSBRecoding parameters - fxT = 448 - fxV = 2 - fxW = 3 - fx2w1 = 1 << (uint(fxW) - 1) -) - -// ScalarBaseMult returns kG where G is the generator point. -func (e twistCurve) ScalarBaseMult(k *Scalar) *twistPoint { - m, err := mlsb.New(fxT, fxV, fxW) - if err != nil { - panic(err) - } - if m.IsExtended() { - panic("not extended") - } - - var isZero int - if k.IsZero() { - isZero = 1 - } - subtle.ConstantTimeCopy(isZero, k[:], order[:]) - - minusK := *k - isEven := 1 - int(k[0]&0x1) - minusK.Neg() - subtle.ConstantTimeCopy(isEven, k[:], minusK[:]) - c, err := m.Encode(k[:]) - if err != nil { - panic(err) - } - - gP := c.Exp(groupMLSB{}) - P := gP.(*twistPoint) - P.cneg(uint(isEven)) - return P -} - -type groupMLSB struct{} - -func (e groupMLSB) ExtendedEltP() mlsb.EltP { return nil } -func (e groupMLSB) Sqr(x mlsb.EltG) { x.(*twistPoint).Double() } -func (e groupMLSB) Mul(x mlsb.EltG, y mlsb.EltP) { x.(*twistPoint).mixAddZ1(y.(*preTwistPointAffine)) } -func (e groupMLSB) Identity() mlsb.EltG { return twistCurve{}.Identity() } -func (e groupMLSB) NewEltP() mlsb.EltP { return &preTwistPointAffine{} } -func (e groupMLSB) Lookup(a mlsb.EltP, v uint, s, u int32) { - Tabj := &tabFixMult[v] - P := a.(*preTwistPointAffine) - for k := range Tabj { - P.cmov(&Tabj[k], uint(subtle.ConstantTimeEq(int32(k), u))) - } - P.cneg(int(s >> 31)) -} diff --git a/internal/ted448/basemult.go b/internal/ted448/basemult.go new file mode 100644 index 00000000..0c92b5f6 --- /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 00000000..3698ee9e --- /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 00000000..7d9f9668 --- /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 00000000..b8383998 --- /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 00000000..4306cca9 --- /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 852d74f3..c3bf8d0d 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 68% rename from ecc/goldilocks/scalar_test.go rename to internal/ted448/scalar_test.go index 26bedf62..4658a3db 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,12 +34,11 @@ func TestReduceModOrder(t *testing.T) { } func testOp(t *testing.T, - f func(z, x, y *goldilocks.Scalar), - g func(z, x, y *big.Int), -) { + 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[:]) @@ -66,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) @@ -105,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 ed432e02..d7e85888 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 a5e36600..c2d27ba2 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_test.go b/sign/ed25519/ed25519_test.go index 13977873..4d8596a1 100644 --- a/sign/ed25519/ed25519_test.go +++ b/sign/ed25519/ed25519_test.go @@ -1,6 +1,7 @@ package ed25519_test import ( + "crypto/rand" "testing" "github.com/cloudflare/circl/sign/ed25519" @@ -58,9 +59,8 @@ func TestPublic(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) } } @@ -75,8 +75,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) } @@ -89,8 +88,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 cd1a42bd..85db8a69 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 6a26e586..8ac01260 100644 --- a/sign/ed448/ed448.go +++ b/sign/ed448/ed448.go @@ -98,9 +98,9 @@ func (priv PrivateKey) Equal(x crypto.PrivateKey) bool { // Public returns the PublicKey corresponding to priv. func (priv PrivateKey) Public() crypto.PublicKey { - publicKey := make([]byte, PublicKeySize) + publicKey := make(PublicKey, PublicKeySize) copy(publicKey, priv[SeedSize:]) - return PublicKey(publicKey) + return publicKey } // Seed returns the private key seed corresponding to priv. It is provided for @@ -183,7 +183,7 @@ func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) { // with RFC 8032. RFC 8032's private keys correspond to seeds in this // package. func NewKeyFromSeed(seed []byte) PrivateKey { - privateKey := make([]byte, PrivateKeySize) + privateKey := make(PrivateKey, PrivateKeySize) newKeyFromSeed(privateKey, seed) return privateKey } @@ -200,8 +200,15 @@ func newKeyFromSeed(privateKey, seed []byte) { s := &goldilocks.Scalar{} deriveSecretScalar(s, h[:paramB]) + var P goldilocks.Point + P.ScalarBaseMult(s) + var encP [goldilocks.EncodingSize]byte + if err := P.Encode(&encP); err != nil { + panic(err) + } + copy(privateKey[:SeedSize], seed) - _ = goldilocks.Curve{}.ScalarBaseMult(s).ToBytes(privateKey[SeedSize:]) + copy(privateKey[SeedSize:], encP[:]) } func signAll(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash bool) { @@ -232,10 +239,7 @@ func signAll(signature []byte, privateKey PrivateKey, message, ctx []byte, preHa // 2. Compute SHAKE256(dom4(F, C) || prefix || PH(M), 114). var rPM [hashSize]byte - H.Reset() - writeDom(&H, ctx, preHash) - _, _ = H.Write(prefix) _, _ = H.Write(PHM) _, _ = H.Read(rPM[:]) @@ -243,17 +247,17 @@ func signAll(signature []byte, privateKey PrivateKey, message, ctx []byte, preHa // 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[:]) @@ -266,7 +270,7 @@ func signAll(signature []byte, privateKey PrivateKey, message, ctx []byte, preHa 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[:]) } @@ -299,7 +303,10 @@ func verify(public PublicKey, message, signature, ctx []byte, preHash bool) bool return false } - P, err := goldilocks.FromBytes(public) + var encPublic [goldilocks.EncodingSize]byte + copy(encPublic[:], public) + P := &goldilocks.Point{} + err := P.Decode(&encPublic) if err != nil { return false } @@ -332,10 +339,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 @@ -390,7 +401,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-- @@ -398,7 +409,8 @@ func isLessThanOrder(x []byte) bool { return x[paramB-1] == 0 && x[i] < order[i] } -func writeDom(h io.Writer, ctx []byte, preHash bool) { +func writeDom(h sha3.ShakeHash, ctx []byte, preHash bool) { + h.Reset() dom4 := "SigEd448" _, _ = h.Write([]byte(dom4)) diff --git a/sign/ed448/ed448_test.go b/sign/ed448/ed448_test.go index 91349609..0b1f061d 100644 --- a/sign/ed448/ed448_test.go +++ b/sign/ed448/ed448_test.go @@ -12,15 +12,6 @@ import ( "github.com/cloudflare/circl/sign/ed448" ) -type zeroReader struct{} - -func (zeroReader) Read(buf []byte) (int, error) { - for i := range buf { - buf[i] = 0 - } - return len(buf), nil -} - func TestEqual(t *testing.T) { public, private, _ := ed448.GenerateKey(rand.Reader) @@ -213,9 +204,8 @@ func TestErrors(t *testing.T) { } func BenchmarkKeyGeneration(b *testing.B) { - var zero zeroReader for i := 0; i < b.N; i++ { - if _, _, err := ed448.GenerateKey(zero); err != nil { + if _, _, err := ed448.GenerateKey(rand.Reader); err != nil { b.Fatal(err) } } @@ -230,8 +220,7 @@ func BenchmarkNewKeyFromSeed(b *testing.B) { } func BenchmarkSigning(b *testing.B) { - var zero zeroReader - _, priv, err := ed448.GenerateKey(zero) + _, priv, err := ed448.GenerateKey(rand.Reader) if err != nil { b.Fatal(err) } @@ -245,8 +234,7 @@ func BenchmarkSigning(b *testing.B) { } func BenchmarkVerification(b *testing.B) { - var zero zeroReader - pub, priv, err := ed448.GenerateKey(zero) + pub, priv, err := ed448.GenerateKey(rand.Reader) if err != nil { b.Fatal(err) } @@ -268,7 +256,7 @@ func BenchmarkEd448Ph(b *testing.B) { ctx := "" b.ResetTimer() for i := 0; i < b.N; i++ { - _ = ed448.SignPh(key, msg, ctx) + ed448.SignPh(key, msg, ctx) } }) b.Run("Verify", func(b *testing.B) { From c8c168a539c9f1b789008b164f65c1f9e84a4b11 Mon Sep 17 00:00:00 2001 From: armfazh Date: Tue, 18 Aug 2020 04:19:20 -0700 Subject: [PATCH 2/6] Returning int instead of bool in fp448 predicates. --- ecc/goldilocks/goldilocks.go | 32 ++--- {ecc/decaf => group/decaf448}/constants.go | 2 +- {ecc/decaf => group/decaf448}/decaf.go | 34 ++++-- {ecc/decaf => group/decaf448}/decaf_test.go | 110 +++++++++++------- .../decaf448}/testdata/decafv1.0_vectors.json | 0 internal/ted448/curve.go | 9 +- internal/ted448/curve_test.go | 17 +++ internal/ted448/point.go | 15 ++- math/fp448/fp.go | 16 ++- math/fp448/fp_test.go | 14 +-- 10 files changed, 162 insertions(+), 87 deletions(-) rename {ecc/decaf => group/decaf448}/constants.go (98%) rename {ecc/decaf => group/decaf448}/decaf.go (90%) rename {ecc/decaf => group/decaf448}/decaf_test.go (70%) rename {ecc/decaf => group/decaf448}/testdata/decafv1.0_vectors.json (100%) diff --git a/ecc/goldilocks/goldilocks.go b/ecc/goldilocks/goldilocks.go index 93e406e6..1deb5fc4 100644 --- a/ecc/goldilocks/goldilocks.go +++ b/ecc/goldilocks/goldilocks.go @@ -18,8 +18,9 @@ package goldilocks import ( + "crypto/subtle" "errors" - "unsafe" + "math/bits" "github.com/cloudflare/circl/internal/ted448" fp "github.com/cloudflare/circl/math/fp448" @@ -39,8 +40,8 @@ var ErrInvalidDecoding = errors.New("invalid decoding") // 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 + isByteZero := subtle.ConstantTimeByteEq(data[EncodingSize-1]&0x7F, 0x00) + signX := int(data[EncodingSize-1] >> 7) copy(y[:], data[:fp.Size]) p := fp.P() isLessThanP := isLessThan(y[:], p[:]) @@ -52,18 +53,21 @@ func (P *Point) Decode(data *[EncodingSize]byte) error { 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))) + isValidXSign := 1 - (fp.IsZero(x) & signX) + fp.Neg(u, x) // u = -x + fp.Cmov(x, u, uint(signX^fp.Parity(x))) // if signX != x mod 2 + + b0 := isByteZero + b1 := isLessThanP + b2 := isQR + b3 := isValidXSign + b := uint(subtle.ConstantTimeEq(int32(8*b3+4*b2+2*b1+b0), 0xF)) fp.Cmov(&P.X, x, b) fp.Cmov(&P.Y, y, b) fp.Cmov(&P.Ta, x, b) fp.Cmov(&P.Tb, y, b) fp.Cmov(&P.Z, &one, b) - if !isValid { + if b == 0 { return ErrInvalidDecoding } return nil @@ -143,14 +147,16 @@ func isogeny4(Q, P *ted448.Point, isPull bool) { 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 +// isLessThan returns 1 if 0 <= x < y, and assumes that slices are of the // same length and are interpreted in little-endian order. -func isLessThan(x, y []byte) bool { +func isLessThan(x, y []byte) int { i := len(x) - 1 for i > 0 && x[i] == y[i] { i-- } - return x[i] < y[i] + xi := int(x[i]) + yi := int(y[i]) + return ((xi - yi) >> (bits.UintSize - 1)) & 1 } var ( diff --git a/ecc/decaf/constants.go b/group/decaf448/constants.go similarity index 98% rename from ecc/decaf/constants.go rename to group/decaf448/constants.go index a57435a7..a500f80d 100644 --- a/ecc/decaf/constants.go +++ b/group/decaf448/constants.go @@ -1,4 +1,4 @@ -package decaf +package decaf448 import ( "errors" diff --git a/ecc/decaf/decaf.go b/group/decaf448/decaf.go similarity index 90% rename from ecc/decaf/decaf.go rename to group/decaf448/decaf.go index 62278dc2..e98a2277 100644 --- a/ecc/decaf/decaf.go +++ b/group/decaf448/decaf.go @@ -1,4 +1,4 @@ -// Package decaf provides a prime-order group derived from a quotient of +// Package decaf448 provides a prime-order group derived from a quotient of // Edwards curves. // // Decaf Group @@ -29,10 +29,11 @@ // (4) https://sourceforge.net/p/ed448goldilocks/code/ci/v1.0/tree/ // // (5) https://mailarchive.ietf.org/arch/msg/cfrg/S4YUTt_5eD4kwYbDuhEK0tXT1aM/ -package decaf +package decaf448 import ( - "unsafe" + "crypto/subtle" + "math/bits" "github.com/cloudflare/circl/internal/ted448" fp "github.com/cloudflare/circl/math/fp448" @@ -79,7 +80,12 @@ func Mul(c *Elt, n *Scalar, a *Elt) { ted448.ScalarMult(&c.p, n, &a.p) } 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) } +func (e *Elt) IsIdentity() bool { + b0 := fp.IsZero(&e.p.X) + b1 := 1 - fp.IsZero(&e.p.Y) + b2 := 1 - fp.IsZero(&e.p.Z) + return subtle.ConstantTimeEq(int32(4*b2+2*b1+b0), 0x7) == 1 +} // IsEqual returns True if e=a, where = is an equivalence relation. func (e *Elt) IsEqual(a *Elt) bool { @@ -87,7 +93,7 @@ func (e *Elt) IsEqual(a *Elt) bool { 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) + return fp.IsZero(l) == 1 } // UnmarshalBinary interprets the first EncodingSize bytes passed in data, and @@ -101,7 +107,7 @@ func (e *Elt) UnmarshalBinary(data []byte) error { copy(s[:], data[:EncodingSize]) p := fp.P() isLessThanP := isLessThan(s[:], p[:]) - isPositiveS := fp.Parity(s) == 0 + isPositiveS := 1 - fp.Parity(s) den, num := &fp.Elt{}, &fp.Elt{} isr, altx, t0 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{} @@ -132,14 +138,16 @@ func (e *Elt) UnmarshalBinary(data []byte) error { 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)))) + b0 := isPositiveS + b1 := isLessThanP + b2 := isQR + b := uint(subtle.ConstantTimeEq(int32(4*b2+2*b1+b0), 0x7)) 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 { + if b == 0 { return ErrInvalidDecoding } return nil @@ -180,12 +188,14 @@ func (e *Elt) marshalBinary(enc []byte) error { return fp.ToBytes(enc[:], s) } -// isLessThan returns true if 0 <= x < y, and assumes that slices are of the +// isLessThan returns 1 if 0 <= x < y, and assumes that slices are of the // same length and are interpreted in little-endian order. -func isLessThan(x, y []byte) bool { +func isLessThan(x, y []byte) int { i := len(x) - 1 for i > 0 && x[i] == y[i] { i-- } - return x[i] < y[i] + xi := int(x[i]) + yi := int(y[i]) + return ((xi - yi) >> (bits.UintSize - 1)) & 1 } diff --git a/ecc/decaf/decaf_test.go b/group/decaf448/decaf_test.go similarity index 70% rename from ecc/decaf/decaf_test.go rename to group/decaf448/decaf_test.go index be4d87ce..50614601 100644 --- a/ecc/decaf/decaf_test.go +++ b/group/decaf448/decaf_test.go @@ -1,4 +1,4 @@ -package decaf_test +package decaf448_test import ( "bytes" @@ -9,7 +9,7 @@ import ( "os" "testing" - "github.com/cloudflare/circl/ecc/decaf" + "github.com/cloudflare/circl/group/decaf448" "github.com/cloudflare/circl/internal/test" fp "github.com/cloudflare/circl/math/fp448" ) @@ -44,8 +44,8 @@ func (kat *testJSONFile) readFile(t *testing.T, fileName string) { } } -func verify(t *testing.T, i int, gotkG *decaf.Elt, wantEnckG []byte) { - wantkG := &decaf.Elt{} +func verify(t *testing.T, i int, gotkG *decaf448.Elt, wantEnckG []byte) { + wantkG := &decaf448.Elt{} gotEnckG, err := gotkG.MarshalBinary() got := err == nil && bytes.Equal(gotEnckG, wantEnckG) @@ -56,8 +56,8 @@ func verify(t *testing.T, i int, gotkG *decaf.Elt, wantEnckG []byte) { err = wantkG.UnmarshalBinary(wantEnckG) got = err == nil && - decaf.IsValid(gotkG) && - decaf.IsValid(wantkG) && + decaf448.IsValid(gotkG) && + decaf448.IsValid(wantkG) && gotkG.IsEqual(wantkG) want = true if got != want { @@ -76,34 +76,34 @@ func TestDecafv1_0(t *testing.T) { test.ReportError(t, got, want) } got = kat.Version - want = decaf.Version + want = decaf448.Version if got != want { test.ReportError(t, got, want) } - var scalar decaf.Scalar - var P decaf.Elt - G := decaf.Generator() + var scalar decaf448.Scalar + var P decaf448.Elt + G := decaf448.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) + decaf448.MulGen(&P, &scalar) verify(t, i, &P, wantEnckG) - decaf.Mul(&P, &scalar, G) + decaf448.Mul(&P, &scalar, G) verify(t, i, &P, wantEnckG) - decaf.Mul(&P, &scalar, &P) + decaf448.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 + var e decaf448.Elt + var enc [decaf448.EncodingSize]byte for i := 0; i < testTimes; i++ { for found := false; !found; { @@ -119,32 +119,32 @@ func TestDecafRandom(t *testing.T) { } } -func randomPoint() decaf.Elt { - var k decaf.Scalar +func randomPoint() decaf448.Elt { + var k decaf448.Scalar _, _ = rand.Read(k[:]) - var P decaf.Elt - decaf.MulGen(&P, &k) + var P decaf448.Elt + decaf448.MulGen(&P, &k) return P } func TestPointAdd(t *testing.T) { const testTimes = 1 << 10 - Q := &decaf.Elt{} + Q := &decaf448.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 + decaf448.Double(Q, &P) // 2P + decaf448.Double(Q, Q) // 4P + decaf448.Double(Q, Q) // 8P + decaf448.Double(Q, Q) // 16P got := Q // R = 16P = P+P...+P - R := decaf.Identity() + R := decaf448.Identity() for j := 0; j < 16; j++ { - decaf.Add(R, R, &P) + decaf448.Add(R, R, &P) } want := R - if !decaf.IsValid(got) || !decaf.IsValid(want) || !got.IsEqual(want) { + if !decaf448.IsValid(got) || !decaf448.IsValid(want) || !got.IsEqual(want) { test.ReportError(t, got, want, P) } } @@ -152,11 +152,11 @@ func TestPointAdd(t *testing.T) { func TestPointNeg(t *testing.T) { const testTimes = 1 << 10 - Q := &decaf.Elt{} + Q := &decaf448.Elt{} for i := 0; i < testTimes; i++ { P := randomPoint() - decaf.Neg(Q, &P) - decaf.Add(Q, Q, &P) + decaf448.Neg(Q, &P) + decaf448.Add(Q, Q, &P) got := Q.IsIdentity() want := true if got != want { @@ -167,12 +167,12 @@ func TestPointNeg(t *testing.T) { func TestDecafOrder(t *testing.T) { const testTimes = 1 << 10 - Q := &decaf.Elt{} - order := decaf.Order() + Q := &decaf448.Elt{} + order := decaf448.Order() for i := 0; i < testTimes; i++ { P := randomPoint() - decaf.Mul(Q, &order, &P) + decaf448.Mul(Q, &order, &P) got := Q.IsIdentity() want := true if got != want { @@ -193,10 +193,10 @@ func TestDecafInvalid(t *testing.T) { nonQR[:], // s=4 and (a^2s^4 + (2a - 4d)*s^2 + 1) is non-QR. } - var e decaf.Elt + var e decaf448.Elt for _, enc := range badEncodings { got := e.UnmarshalBinary(enc) - want := decaf.ErrInvalidDecoding + want := decaf448.ErrInvalidDecoding if got != want { test.ReportError(t, got, want, enc) } @@ -204,26 +204,54 @@ func TestDecafInvalid(t *testing.T) { } func BenchmarkDecaf(b *testing.B) { - var k, l decaf.Scalar + var k, l decaf448.Scalar _, _ = rand.Read(k[:]) _, _ = rand.Read(l[:]) - G := decaf.Generator() - P := decaf.Generator() + G := decaf448.Generator() + P := decaf448.Generator() + Z := decaf448.Identity() enc, _ := G.MarshalBinary() + x := &fp.Elt{} + y := &fp.Elt{} + _, _ = rand.Read(y[:]) + // p := fp.P() + // one := fp.One() + // fp.Sub(y, &p, &one) + b.Run("Add", func(b *testing.B) { for i := 0; i < b.N; i++ { - decaf.Add(P, P, G) + decaf448.Add(P, P, G) + } + }) + b.Run("IsZeroSub0", func(b *testing.B) { + for i := 0; i < b.N; i++ { + fp.IsZero(x) + } + }) + b.Run("IsZeroSub1", func(b *testing.B) { + for i := 0; i < b.N; i++ { + fp.IsZero(y) + } + }) + b.Run("IsZeroSi", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = Z.IsIdentity() + } + }) + b.Run("IsZero", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = P.IsIdentity() } }) b.Run("Mul", func(b *testing.B) { for i := 0; i < b.N; i++ { - decaf.Mul(G, &k, G) + decaf448.Mul(G, &k, G) } }) b.Run("MulGen", func(b *testing.B) { for i := 0; i < b.N; i++ { - decaf.MulGen(P, &k) + decaf448.MulGen(P, &k) } }) b.Run("Marshal", func(b *testing.B) { diff --git a/ecc/decaf/testdata/decafv1.0_vectors.json b/group/decaf448/testdata/decafv1.0_vectors.json similarity index 100% rename from ecc/decaf/testdata/decafv1.0_vectors.json rename to group/decaf448/testdata/decafv1.0_vectors.json diff --git a/internal/ted448/curve.go b/internal/ted448/curve.go index 7d9f9668..a58d05c8 100644 --- a/internal/ted448/curve.go +++ b/internal/ted448/curve.go @@ -46,7 +46,12 @@ func ParamD() fp.Elt { return paramD } // IsOnCurve returns true if the point lies on the curve. func IsOnCurve(P *Point) bool { - eq0 := *P != Point{} + eq0 := fp.IsZero(&P.X) + eq0 &= fp.IsZero(&P.Y) + eq0 &= fp.IsZero(&P.Z) + eq0 &= fp.IsZero(&P.Ta) + eq0 &= fp.IsZero(&P.Tb) + eq0 = 1 - eq0 x2, y2, t, t2, z2 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} rhs, lhs := &fp.Elt{}, &fp.Elt{} fp.Mul(t, &P.Ta, &P.Tb) // t = ta*tb @@ -63,7 +68,7 @@ func IsOnCurve(P *Point) bool { fp.Mul(rhs, t, &P.Z) // tz fp.Sub(lhs, lhs, rhs) // xy - tz eq2 := fp.IsZero(lhs) - return eq0 && eq1 && eq2 + return subtle.ConstantTimeByteEq(byte(4*eq2+2*eq1+eq0), 0x7) == 1 } // subYDiv16 update x = (x - y) / 16. diff --git a/internal/ted448/curve_test.go b/internal/ted448/curve_test.go index b8383998..33a5d3c1 100644 --- a/internal/ted448/curve_test.go +++ b/internal/ted448/curve_test.go @@ -39,6 +39,23 @@ func TestPointAdd(t *testing.T) { } } +func TestIsOnCurve(t *testing.T) { + for _, v := range []struct { + P ted448.Point + want bool + }{ + {ted448.Point{}, false}, + {ted448.Identity(), true}, + {ted448.Generator(), true}, + {randomPoint(), true}, + } { + got := ted448.IsOnCurve(&v.P) + if got != v.want { + test.ReportError(t, got, v.want, v.P) + } + } +} + func TestPointNeg(t *testing.T) { const testTimes = 1 << 10 for i := 0; i < testTimes; i++ { diff --git a/internal/ted448/point.go b/internal/ted448/point.go index 4306cca9..64f8b2e5 100644 --- a/internal/ted448/point.go +++ b/internal/ted448/point.go @@ -1,6 +1,7 @@ package ted448 import ( + "crypto/subtle" "fmt" fp "github.com/cloudflare/circl/math/fp448" @@ -108,7 +109,11 @@ func (P *Point) mixAdd(Q *prePointProy) { // 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 + b0 := fp.IsZero(&P.X) + b1 := 1 - fp.IsZero(&P.Y) + b2 := 1 - fp.IsZero(&P.Z) + b3 := fp.IsEqual(&P.Y, &P.Z) + return subtle.ConstantTimeEq(int32(8*b3+4*b2+2*b1+b0), 0xF) == 1 } // IsEqual returns True if P is equivalent to Q. @@ -117,18 +122,18 @@ func (P *Point) IsEqual(Q *Point) bool { fp.Mul(l, &P.X, &Q.Z) fp.Mul(r, &Q.X, &P.Z) fp.Sub(l, l, r) - b := fp.IsZero(l) + b0 := 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) + b1 := fp.IsZero(l) fp.Mul(l, &P.Ta, &P.Tb) fp.Mul(l, l, &Q.Z) fp.Mul(r, &Q.Ta, &Q.Tb) fp.Mul(r, r, &P.Z) fp.Sub(l, l, r) - b = b && fp.IsZero(l) - return b + b2 := fp.IsZero(l) + return subtle.ConstantTimeEq(int32(4*b2+2*b1+b0), 0x7) == 1 } // Neg obtains the inverse of P. diff --git a/math/fp448/fp.go b/math/fp448/fp.go index c2d27ba2..d3dc98fc 100644 --- a/math/fp448/fp.go +++ b/math/fp448/fp.go @@ -2,6 +2,7 @@ package fp448 import ( + "crypto/subtle" "errors" "github.com/cloudflare/circl/internal/conv" @@ -39,11 +40,14 @@ func ToBytes(b []byte, x *Elt) error { return nil } -// IsZero returns true if x is equal to 0. -func IsZero(x *Elt) bool { Modp(x); return *x == Elt{} } +// IsEqual returns 1 if x is equal to y; otherwise 0. +func IsEqual(x, y *Elt) int { Modp(x); Modp(y); return subtle.ConstantTimeCompare(x[:], y[:]) } -// IsOne returns true if x is equal to 1. -func IsOne(x *Elt) bool { Modp(x); return *x == Elt{1} } +// IsZero returns 1 if x is equal to 0; otherwise 0. +func IsZero(x *Elt) int { Modp(x); z := Elt{}; return subtle.ConstantTimeCompare(x[:], z[:]) } + +// IsOne returns true if x is equal to 1; otherwise 0. +func IsOne(x *Elt) int { Modp(x); o := Elt{1}; return subtle.ConstantTimeCompare(x[:], o[:]) } // Parity returns the last bit of x. func Parity(x *Elt) int { Modp(x); return int(x[0] & 1) } @@ -61,9 +65,9 @@ func Neg(z, x *Elt) { Sub(z, &p, x) } func Modp(z *Elt) { Sub(z, z, &p) } // InvSqrt calculates z = sqrt(x/y) iff x/y is a quadratic-residue. If so, -// isQR = true; otherwise, isQR = false, since x/y is a quadratic non-residue, +// isQR = 1; otherwise, isQR = 0, since x/y is a quadratic non-residue, // and z = sqrt(-x/y). -func InvSqrt(z, x, y *Elt) (isQR bool) { +func InvSqrt(z, x, y *Elt) (isQR int) { // First note that x^(2(k+1)) = x^(p-1)/2 * x = legendre(x) * x // so that's x if x is a quadratic residue and -x otherwise. // Next, y^(6k+3) = y^(4k+2) * y^(2k+1) = y^(p-1) * y^((p-1)/2) = legendre(y). diff --git a/math/fp448/fp_test.go b/math/fp448/fp_test.go index b867d6fe..0fca8e4d 100644 --- a/math/fp448/fp_test.go +++ b/math/fp448/fp_test.go @@ -196,21 +196,21 @@ func TestModp(t *testing.T) { func TestIsZero(t *testing.T) { var x Elt got := IsZero(&x) - want := true + want := 1 if got != want { test.ReportError(t, got, want, x) } SetOne(&x) got = IsZero(&x) - want = false + want = 0 if got != want { test.ReportError(t, got, want, x) } x = P() got = IsZero(&x) - want = true + want = 1 if got != want { test.ReportError(t, got, want, x) } @@ -299,7 +299,7 @@ func TestInvSqrt(t *testing.T) { exp := big.NewInt(1) exp.Add(p, exp).Rsh(exp, 2) var frac, root, sqRoot big.Int - var wantQR bool + var wantQR int var want *big.Int for i := 0; i < numTests; i++ { _, _ = rand.Read(x[:]) @@ -317,13 +317,13 @@ func TestInvSqrt(t *testing.T) { if sqRoot.Cmp(&frac) == 0 { want = &root - wantQR = true + wantQR = 1 } else { want = big.NewInt(0) - wantQR = false + wantQR = 0 } - if wantQR { + if wantQR == 1 { if gotQR != wantQR || got.Cmp(want) != 0 { test.ReportError(t, got, want, x, y) } From 9679bf8c1423251aee5c2bc1a5c732652c26efb6 Mon Sep 17 00:00:00 2001 From: armfazh Date: Tue, 25 Aug 2020 16:20:46 -0700 Subject: [PATCH 3/6] Using exponentiation of inverting scalars. --- group/decaf448/decaf.go | 2 +- internal/ted448/constants.go | 9 +++++++ internal/ted448/point.go | 11 +++++---- internal/ted448/scalar.go | 46 +++++++++++++++++++++--------------- 4 files changed, 43 insertions(+), 25 deletions(-) diff --git a/group/decaf448/decaf.go b/group/decaf448/decaf.go index e98a2277..7cbd4274 100644 --- a/group/decaf448/decaf.go +++ b/group/decaf448/decaf.go @@ -84,7 +84,7 @@ func (e *Elt) IsIdentity() bool { b0 := fp.IsZero(&e.p.X) b1 := 1 - fp.IsZero(&e.p.Y) b2 := 1 - fp.IsZero(&e.p.Z) - return subtle.ConstantTimeEq(int32(4*b2+2*b1+b0), 0x7) == 1 + return (b0 & b1 & b2) == 1 } // IsEqual returns True if e=a, where = is an equivalence relation. diff --git a/internal/ted448/constants.go b/internal/ted448/constants.go index 3698ee9e..a496f60f 100644 --- a/internal/ted448/constants.go +++ b/internal/ted448/constants.go @@ -44,6 +44,15 @@ var ( 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, } + orderMinusTwo = Scalar{ + 0xf1, 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/point.go b/internal/ted448/point.go index 64f8b2e5..cf0a3633 100644 --- a/internal/ted448/point.go +++ b/internal/ted448/point.go @@ -46,12 +46,13 @@ func (P *Point) Double() { 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(e, e, h) // E = (x+y)^2-A-B = 2xy fp.Sub(g, b, a) // G = B-A - fp.Sub(f, c, g) // F = C-G - 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 + fp.Sub(f, c, g) // F = C-G = 2z^2-y^2+x^2 + fp.Mul(Pz, f, g) // Z = F * G = (y^2-x^2)(2z^2-y^2+x^2) + fp.Mul(Px, e, f) // X = E * F = 2xy(2z^2-y^2+x^2) + fp.Mul(Py, g, h) // Y = G * H = (x^2-y^2)(x^2+y^2) + // T = E * H = 2xy(x^2+y^2) } // mixAdd calulates P= P+Q, where Q is a precomputed448 point with Z_Q = 1. diff --git a/internal/ted448/scalar.go b/internal/ted448/scalar.go index c3bf8d0d..5ce9c4be 100644 --- a/internal/ted448/scalar.go +++ b/internal/ted448/scalar.go @@ -1,7 +1,6 @@ package ted448 import ( - "crypto/rand" "encoding/binary" "math/bits" @@ -202,32 +201,41 @@ func (z *Scalar) Sub(x, y *Scalar) { // Mul calculates z = x*y mod order. func (z *Scalar) Mul(x, y *Scalar) { var z64, x64, y64 scalar64 - prod := (&[_N + 1]uint64{})[:] x64.fromScalar(x) y64.fromScalar(y) + coremul(&z64, &x64, &y64) + z64.modOrder() + z64.toScalar(z) +} + +func coremul(z64, x64, y64 *scalar64) { + var p64 scalar64 + prod := (&[_N + 1]uint64{})[:] mulWord(prod, x64[:], y64[_N-1]) - copy(z64[:], prod[:_N]) - z64.reduceOneWord(prod[_N]) + copy(p64[:], prod[:_N]) + p64.reduceOneWord(prod[_N]) for i := _N - 2; i >= 0; i-- { - h := z64.leftShift(0) - z64.reduceOneWord(h) + h := p64.leftShift(0) + p64.reduceOneWord(h) mulWord(prod, x64[:], y64[i]) - c := add(z64[:], z64[:], prod[:_N]) - z64.reduceOneWord(prod[_N] + c) + c := add(p64[:], p64[:], prod[:_N]) + p64.reduceOneWord(prod[_N] + c) } - z64.modOrder() - z64.toScalar(z) + *z64 = p64 } // 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) + var x64 scalar64 + x64.fromScalar(x) + t := &scalar64{1} + for i := 8*len(orderMinusTwo) - 1; i >= 0; i-- { + coremul(t, t, t) + b := (orderMinusTwo[i/8] >> uint(i%8)) & 1 + if b != 0 { + coremul(t, t, &x64) + } + } + t.modOrder() + t.toScalar(z) } From 65407fef735fa8eadd3a19ebde0ac7cd4fd1a120 Mon Sep 17 00:00:00 2001 From: armfazh Date: Tue, 25 Aug 2020 17:13:14 -0700 Subject: [PATCH 4/6] Fixing sha3 import paths. --- sign/ed448/ed448.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/sign/ed448/ed448.go b/sign/ed448/ed448.go index 8ac01260..a8e0a64a 100644 --- a/sign/ed448/ed448.go +++ b/sign/ed448/ed448.go @@ -28,10 +28,8 @@ import ( "crypto" cryptoRand "crypto/rand" "crypto/subtle" - "errors" "fmt" "io" - "strconv" "github.com/cloudflare/circl/ecc/goldilocks" "github.com/cloudflare/circl/internal/sha3" @@ -66,7 +64,8 @@ type SignerOptions struct { // Its length must be less or equal than 255 bytes. Context string - // Scheme is an identifier for choosing a signature scheme. + // Scheme is an identifier for choosing a signature scheme. The zero value + // is ED448. Scheme SchemeID } @@ -155,7 +154,7 @@ func (priv PrivateKey) Sign( case scheme == ED448Ph && opts.HashFunc() == crypto.Hash(0): return SignPh(priv, message, ctx), nil default: - return nil, errors.New("ed448: bad hash algorithm") + return nil, fmt.Errorf("ed448: bad hash algorithm") } } @@ -188,9 +187,9 @@ func NewKeyFromSeed(seed []byte) PrivateKey { return privateKey } -func newKeyFromSeed(privateKey, seed []byte) { +func newKeyFromSeed(privateKey PrivateKey, seed []byte) { if l := len(seed); l != SeedSize { - panic("ed448: bad seed length: " + strconv.Itoa(l)) + panic(fmt.Errorf("ed448: bad seed length: %v", l)) } var h [hashSize]byte @@ -213,7 +212,7 @@ func newKeyFromSeed(privateKey, seed []byte) { func signAll(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash bool) { if len(ctx) > ContextMaxSize { - panic(fmt.Errorf("ed448: bad context length: " + strconv.Itoa(len(ctx)))) + panic(fmt.Errorf("ed448: bad context length: %v", len(ctx))) } H := sha3.NewShake256() From 45008f18eebffef90fbddd59a4f21a0e76be5685 Mon Sep 17 00:00:00 2001 From: armfazh Date: Wed, 26 Aug 2020 05:16:20 -0700 Subject: [PATCH 5/6] Addition chain for scalar exponentiation. --- internal/ted448/constants.go | 9 --- internal/ted448/scalar.go | 109 ++++++++++++++++++++++++++--------- 2 files changed, 82 insertions(+), 36 deletions(-) diff --git a/internal/ted448/constants.go b/internal/ted448/constants.go index a496f60f..3698ee9e 100644 --- a/internal/ted448/constants.go +++ b/internal/ted448/constants.go @@ -44,15 +44,6 @@ var ( 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, } - orderMinusTwo = Scalar{ - 0xf1, 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/scalar.go b/internal/ted448/scalar.go index 5ce9c4be..6f7f16bc 100644 --- a/internal/ted448/scalar.go +++ b/internal/ted448/scalar.go @@ -60,6 +60,39 @@ func (z *scalar64) cmov(x *scalar64, b uint64) { } } +// mul calculates z = x * y. +func (z *scalar64) mul(x, y *scalar64) *scalar64 { + var t scalar64 + prod := (&[_N + 1]uint64{})[:] + mulWord(prod, x[:], y[_N-1]) + copy(t[:], prod[:_N]) + t.reduceOneWord(prod[_N]) + for i := _N - 2; i >= 0; i-- { + h := t.leftShift(0) + t.reduceOneWord(h) + mulWord(prod, x[:], y[i]) + c := add(t[:], t[:], prod[:_N]) + t.reduceOneWord(prod[_N] + c) + } + *z = t + return z +} + +// sqrn calculates z = x^(2^n). +func (z *scalar64) sqrn(x *scalar64, n uint) *scalar64 { + t := *x + for i := uint(0); i < n; i++ { + t.mul(&t, &t) + } + *z = t + return z +} + +// sqrnmul calculates z = x^(2^n) * y. +func (z *scalar64) sqrnmul(x *scalar64, n uint, y *scalar64) *scalar64 { + return z.mul(z.sqrn(x, n), y) +} + // add calculates z = x + y. Assumes len(z) > max(len(x),len(y)). func add(z, x, y []uint64) uint64 { l, L, zz := len(x), len(y), y @@ -203,39 +236,61 @@ func (z *Scalar) Mul(x, y *Scalar) { var z64, x64, y64 scalar64 x64.fromScalar(x) y64.fromScalar(y) - coremul(&z64, &x64, &y64) + z64.mul(&x64, &y64) z64.modOrder() z64.toScalar(z) } -func coremul(z64, x64, y64 *scalar64) { - var p64 scalar64 - prod := (&[_N + 1]uint64{})[:] - mulWord(prod, x64[:], y64[_N-1]) - copy(p64[:], prod[:_N]) - p64.reduceOneWord(prod[_N]) - for i := _N - 2; i >= 0; i-- { - h := p64.leftShift(0) - p64.reduceOneWord(h) - mulWord(prod, x64[:], y64[i]) - c := add(p64[:], p64[:], prod[:_N]) - p64.reduceOneWord(prod[_N] + c) - } - *z64 = p64 -} - // Inv calculates z = 1/x mod order. func (z *Scalar) Inv(x *Scalar) { var x64 scalar64 x64.fromScalar(x) - t := &scalar64{1} - for i := 8*len(orderMinusTwo) - 1; i >= 0; i-- { - coremul(t, t, t) - b := (orderMinusTwo[i/8] >> uint(i%8)) & 1 - if b != 0 { - coremul(t, t, &x64) - } - } - t.modOrder() - t.toScalar(z) + + x10 := (&scalar64{}).mul(&x64, &x64) // x10 = 2 * 1 + x11 := (&scalar64{}).mul(x10, &x64) // x11 = 1 + x10 + x100 := (&scalar64{}).mul(x11, &x64) // x100 = 1 + x11 + x101 := (&scalar64{}).mul(x100, &x64) // x101 = 1 + x100 + x1001 := (&scalar64{}).mul(x100, x101) // x1001 = x100 + x101 + x1011 := (&scalar64{}).mul(x10, x1001) // x1011 = x10 + x1001 + x1101 := (&scalar64{}).mul(x10, x1011) // x1101 = x10 + x1011 + x1111 := (&scalar64{}).mul(x10, x1101) // x1111 = x10 + x1101 + x10001 := (&scalar64{}).mul(x10, x1111) // x10001 = x10 + x1111 + x10011 := (&scalar64{}).mul(x10, x10001) // x10011 = x10 + x10001 + x10101 := (&scalar64{}).mul(x10, x10011) // x10101 = x10 + x10011 + x10111 := (&scalar64{}).mul(x10, x10101) // x10111 = x10 + x10101 + x11001 := (&scalar64{}).mul(x10, x10111) // x11001 = x10 + x10111 + x11011 := (&scalar64{}).mul(x10, x11001) // x11011 = x10 + x11001 + x11101 := (&scalar64{}).mul(x10, x11011) // x11101 = x10 + x11011 + x11111 := (&scalar64{}).mul(x10, x11101) // x11111 = x10 + x11101 + x111110 := (&scalar64{}).mul(x11111, x11111) // x111110 = 2 * x11111 + x1111100 := (&scalar64{}).mul(x111110, x111110) // x1111100 = 2 * x111110 + var i24, i41, i73, i129, t = &scalar64{}, &scalar64{}, &scalar64{}, + &scalar64{}, &scalar64{} + var x222, i262, i279, i298, i312, i331, i343, i365, + i375, i396, i411, i431, i444, i464, i478, i498, iret *scalar64 = t, t, + t, t, t, t, t, t, t, t, t, t, t, t, t, t, t + + i24.sqrnmul(x1111100, 5, x1111100) // i24 = x1111100 << 5 + x1111100 + i41.sqrnmul(i41.sqrnmul(i24, 4, x111110), 11, i24) // i41 = (i24 << 4 + x111110) << 11 + i24 + i73.sqrnmul(i73.sqrnmul(i41, 4, x111110), 26, i41) // i73 = (i41 << 4 + x111110) << 26 + i41 + i129.sqrnmul(i73, 55, i73) // i129 = i73 << 55 + i73 + x222.mul(x222.sqrnmul(i129, 110, x11), i129) // x222 = i129 << 110 + x11 + i129 + i262.sqrn(i262.sqrnmul(i262.sqrnmul(x222, 6, x11111), 7, x11001), 6) // i262 = ((x222 << 6 + x11111) << 7 + x11001) << 6 + i279.sqrnmul(i279.sqrnmul(i279.mul(x10001, i262), 8, x11111), 6, x10011) // i279 = ((x10001 + i262) << 8 + x11111) << 6 + x10011 + i298.sqrn(i298.sqrnmul(i298.sqrnmul(i279, 5, x10001), 8, x10011), 4) // i298 = ((i279 << 5 + x10001) << 8 + x10011) << 4 + i312.sqrnmul(i312.sqrnmul(i312.mul(x1011, i298), 6, x11011), 5, x1001) // i312 = ((x1011 + i298) << 6 + x11011) << 5 + x1001 + i331.sqrn(i331.sqrnmul(i331.sqrnmul(i312, 6, x1101), 6, x11101), 5) // i331 = ((i312 << 6 + x1101) << 6 + x11101) << 5 + i343.sqrnmul(i343.sqrnmul(i343.mul(x10101, i331), 5, x10001), 4, x1011) // i343 = ((x10101 + i331) << 5 + x10001) << 4 + x1011 + i365.sqrn(i365.sqrnmul(i365.sqrnmul(i343, 5, x1001), 7, &x64), 8) // i365 = ((i343 << 5 + x1001) << 7 + 1) << 8 + i375.sqrnmul(i375.sqrnmul(i375.mul(x1011, i365), 6, x11001), 1, &x64) // i375 = 2*((x1011 + i365) << 6 + x11001) + 1 + i396.sqrn(i396.sqrnmul(i396.sqrnmul(i375, 9, x10011), 4, x1001), 6) // i396 = ((i375 << 9 + x10011) << 4 + x1001) << 6 + i411.sqrnmul(i411.sqrnmul(i411.mul(x10001, i396), 5, x10111), 7, x1011) // i411 = ((x10001 + i396) << 5 + x10111) << 7 + x1011 + i431.sqrn(i431.sqrnmul(i431.sqrnmul(i411, 7, x1111), 6, x10101), 5) // i431 = ((i411 << 7 + x1111) << 6 + x10101) << 5 + i444.sqrnmul(i444.sqrnmul(i444.mul(x1001, i431), 8, x11011), 2, x11) // i444 = ((x1001 + i431) << 8 + x11011) << 2 + x11 + i464.sqrn(i464.sqrnmul(i464.sqrnmul(i444, 5, x11), 7, x101), 6) // i464 = ((i444 << 5 + x11) << 7 + x101) << 6 + i478.sqrnmul(i478.sqrnmul(i478.mul(x1001, i464), 6, x10101), 5, x1101) // i478 = ((x1001 + i464) << 6 + x10101) << 5 + x1101 + i498.sqrn(i498.sqrnmul(i498.sqrnmul(i478, 3, x11), 9, x10001), 6) // i498 = ((i478 << 3 + x11) << 9 + x10001) << 6 + iret.sqrnmul(iret.mul(x1111, i498), 4, &x64) // z = (x1111 + i498) << 4 + 1 + iret.modOrder() + iret.toScalar(z) } From 3bf0eae706b55b9d2e227caa81749aeaa24cc75c Mon Sep 17 00:00:00 2001 From: armfazh Date: Sat, 5 Mar 2022 04:49:33 -0800 Subject: [PATCH 6/6] Completing the Group interface and hash to curve. --- ecc/goldilocks/goldilocks.go | 310 ++++++++++----- ecc/goldilocks/goldilocks_test.go | 230 +++++++++--- .../goldilocks/internal}/ted448/basemult.go | 0 .../goldilocks/internal}/ted448/constants.go | 2 +- .../goldilocks/internal}/ted448/curve.go | 9 +- .../goldilocks/internal}/ted448/curve_test.go | 65 ++-- .../goldilocks/internal}/ted448/point.go | 25 +- .../goldilocks/internal}/ted448/scalar.go | 75 +++- ecc/goldilocks/internal/ted448/scalar_test.go | 170 +++++++++ .../goldilocks/internal}/ted448/tables.go | 0 ecc/goldilocks/isogeny_test.go | 44 +++ group/decaf.go | 352 ++++++++++++++++++ group/decaf448/constants.go | 28 -- group/decaf448/decaf.go | 201 ---------- group/decaf448/decaf_test.go | 267 ------------- .../decaf448/testdata/decafv1.0_vectors.json | 172 --------- group/decaf_test.go | 128 +++++++ group/group.go | 4 +- group/group_test.go | 1 + group/short.go | 6 +- group/testdata/decaf_vectors.json | 88 +++++ internal/ted448/scalar_test.go | 118 ------ sign/ed25519/ed25519_test.go | 10 +- sign/ed25519/extra_test.go | 4 +- sign/ed448/ed448.go | 27 +- 25 files changed, 1322 insertions(+), 1014 deletions(-) rename {internal => ecc/goldilocks/internal}/ted448/basemult.go (100%) rename {internal => ecc/goldilocks/internal}/ted448/constants.go (98%) rename {internal => ecc/goldilocks/internal}/ted448/curve.go (94%) rename {internal => ecc/goldilocks/internal}/ted448/curve_test.go (69%) rename {internal => ecc/goldilocks/internal}/ted448/point.go (89%) rename {internal => ecc/goldilocks/internal}/ted448/scalar.go (81%) create mode 100644 ecc/goldilocks/internal/ted448/scalar_test.go rename {internal => ecc/goldilocks/internal}/ted448/tables.go (100%) create mode 100644 ecc/goldilocks/isogeny_test.go create mode 100644 group/decaf.go delete mode 100644 group/decaf448/constants.go delete mode 100644 group/decaf448/decaf.go delete mode 100644 group/decaf448/decaf_test.go delete mode 100644 group/decaf448/testdata/decafv1.0_vectors.json create mode 100644 group/decaf_test.go create mode 100644 group/testdata/decaf_vectors.json delete mode 100644 internal/ted448/scalar_test.go diff --git a/ecc/goldilocks/goldilocks.go b/ecc/goldilocks/goldilocks.go index 1deb5fc4..acc1cf35 100644 --- a/ecc/goldilocks/goldilocks.go +++ b/ecc/goldilocks/goldilocks.go @@ -1,4 +1,4 @@ -// Package goldilocks provides arithmetic operations on the Goldilocks curve. +// Package goldilocks provides arithmetic operations on the Goldilocks (edwards448) curve. // // Goldilocks Curve // @@ -19,79 +19,85 @@ package goldilocks import ( "crypto/subtle" + "encoding/binary" "errors" "math/bits" - "github.com/cloudflare/circl/internal/ted448" + ted "github.com/cloudflare/circl/ecc/goldilocks/internal/ted448" fp "github.com/cloudflare/circl/math/fp448" ) -type Scalar = ted448.Scalar +// Point defines a point on the Goldilocks curve using extended projective +// coordinates. For any affine point (x,y) it holds x = X/Z, y = Y/Z, and +// T = Ta*Tb = X*Y/Z. +type Point ted.Point -type Point ted448.Point +// Identity returns the identity point. +func Identity() Point { return Point(ted.Identity()) } -// EncodingSize is the size (in bytes) of an encoded point on the Goldilocks curve. -const EncodingSize = fp.Size + 1 +// Generator returns the generator point. +func Generator() Point { return Point{X: genX, Y: genY, Z: fp.One(), Ta: genX, Tb: genY} } -// ErrInvalidDecoding alerts of an error during decoding a point. -var ErrInvalidDecoding = errors.New("invalid decoding") +// Order returns the number of points in the prime subgroup in little-endian order. +func Order() []byte { r := ted.Order(); return r[:] } -// Decode if succeeds constructs a point by decoding the first -// EncodingSize bytes of data. -func (P *Point) Decode(data *[EncodingSize]byte) error { - x, y := &fp.Elt{}, &fp.Elt{} - isByteZero := subtle.ConstantTimeByteEq(data[EncodingSize-1]&0x7F, 0x00) - signX := int(data[EncodingSize-1] >> 7) - copy(y[:], data[:fp.Size]) - p := fp.P() - isLessThanP := isLessThan(y[:], p[:]) +// ParamD is the D parameter of the Goldilocks curve, D=-39081 in Fp. +func ParamD() fp.Elt { return paramD } - u, v := &fp.Elt{}, &fp.Elt{} - one := fp.One() - fp.Sqr(u, y) // u = y^2 - fp.Mul(v, u, ¶mD) // v = dy^2 - fp.Sub(u, u, &one) // u = y^2-1 - fp.Sub(v, v, &one) // v = dy^2-a - isQR := fp.InvSqrt(x, u, v) // x = sqrt(u/v) - isValidXSign := 1 - (fp.IsZero(x) & signX) - fp.Neg(u, x) // u = -x - fp.Cmov(x, u, uint(signX^fp.Parity(x))) // if signX != x mod 2 +func (P Point) String() string { return ted.Point(P).String() } +func (P *Point) ToAffine() { (*ted.Point)(P).ToAffine() } +func (P *Point) Neg() { (*ted.Point)(P).Neg() } +func (P *Point) IsEqual(Q *Point) int { return (*ted.Point)(P).IsEqual((*ted.Point)(Q)) } +func (P *Point) Double() { P.Add(P) } +func (P *Point) Add(Q *Point) { + // Formula as in Eq.(5) of "Twisted Edwards Curves Revisited" by + // Hisil H., Wong K.KH., Carter G., Dawson E. (2008) + // https://doi.org/10.1007/978-3-540-89255-7_20 + // Formula for curves with a=1. + Px, Py, Pz, Pta, Ptb := &P.X, &P.Y, &P.Z, &P.Ta, &P.Tb + Qx, Qy, Qz, Qta, Qtb := &Q.X, &Q.Y, &Q.Z, &Q.Ta, &Q.Tb - b0 := isByteZero - b1 := isLessThanP - b2 := isQR - b3 := isValidXSign - b := uint(subtle.ConstantTimeEq(int32(8*b3+4*b2+2*b1+b0), 0xF)) - fp.Cmov(&P.X, x, b) - fp.Cmov(&P.Y, y, b) - fp.Cmov(&P.Ta, x, b) - fp.Cmov(&P.Tb, y, b) - fp.Cmov(&P.Z, &one, b) - if b == 0 { - return ErrInvalidDecoding - } - return nil + a, b, c, d := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + e, f, g, h := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + ee, ff := &fp.Elt{}, &fp.Elt{} + + fp.Mul(a, Px, Qx) // A = x1*x2 + fp.Mul(b, Py, Qy) // B = y1*y2 + fp.Mul(c, Pta, Ptb) // C = d*t1*t2 + fp.Mul(c, c, Qta) // + fp.Mul(c, c, Qtb) // + fp.Mul(c, c, ¶mD) // + fp.Mul(d, Pz, Qz) // D = z1*z2 + fp.Add(ee, Px, Py) // x1+y1 + fp.Add(ff, Qx, Qy) // x2+y2 + fp.Mul(e, ee, ff) // E = (x1+y1)*(x2+y2)-A-B + fp.Sub(e, e, a) // + fp.Sub(e, e, b) // + fp.Sub(f, d, c) // F = D-C + fp.Add(g, d, c) // g = D+C + fp.Sub(h, b, a) // H = B-A + fp.Mul(Px, e, f) // X = E * F + fp.Mul(Py, g, h) // Y = G * H + fp.Mul(Pz, f, g) // Z = F * G + P.Ta, P.Tb = *e, *h // T = E * H } -// 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) +func (P *Point) CMov(Q *Point, b uint) { + fp.Cmov(&P.X, &Q.X, b) + fp.Cmov(&P.Y, &Q.Y, b) + fp.Cmov(&P.Z, &Q.Z, b) + fp.Cmov(&P.Ta, &Q.Ta, b) + fp.Cmov(&P.Tb, &Q.Tb, b) } // ScalarBaseMult calculates P = kG, where G is the generator of the Goldilocks // curve. This function runs in constant time. func (P *Point) ScalarBaseMult(k *Scalar) { - k4 := &Scalar{} - divBy4(k4, k) - var Q ted448.Point - ted448.ScalarBaseMult(&Q, k4) + // TODO: recheck if this works for any scalar, likely yes. + k4 := &ted.Scalar{} + divBy4ModOrder(k4, &k.k) + var Q ted.Point + ted.ScalarBaseMult(&Q, k4) push(P, &Q) } @@ -99,31 +105,56 @@ func (P *Point) ScalarBaseMult(k *Scalar) { // 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 + var m4, n4 ted.Scalar + divBy4ModOrder(&m4, &m.k) + divBy4ModOrder(&n4, &n.k) + var R, phiQ ted.Point pull(&phiQ, Q) - ted448.CombinedMult(&R, m4, n4, &phiQ) + ted.CombinedMult(&R, &m4, &n4, &phiQ) push(P, &R) } -func (P *Point) Neg() { fp.Neg(&P.X, &P.X); fp.Neg(&P.Ta, &P.Ta) } +func (P *Point) ScalarMult(k *Scalar, Q *Point) { + var T [4]Point + T[0] = Identity() + T[1] = *Q + T[2] = *Q + T[2].Double() + T[3] = T[2] + T[3].Add(Q) -// Order returns a scalar with the order of the group. -func Order() Scalar { return ted448.Order() } + var R Point + kMod4 := int32(k.k[0] & 0b11) + for i := range T { + R.CMov(&T[i], uint(subtle.ConstantTimeEq(int32(i), kMod4))) + } -// divBy4 calculates z = x/4 mod order. -func divBy4(z, x *Scalar) { z.Mul(x, &invFour) } + var kDiv4 ted.Scalar + for i := 0; i < ScalarSize-1; i++ { + kDiv4[i] = (k.k[i+1] << 6) | (k.k[i] >> 2) + } + kDiv4[ScalarSize-1] = k.k[ScalarSize-1] >> 2 + + var phikQ, phiQ ted.Point + pull(&phiQ, Q) + ted.ScalarMult(&phikQ, &kDiv4, &phiQ) + push(P, &phikQ) + P.Add(&R) +} + +// divBy4ModOrder calculates z = x/4 mod order. +func divBy4ModOrder(z, x *ted.Scalar) { + z.Mul(x, &invFour) +} // pull calculates Q = Iso4(P), where P is a Goldilocks point and Q is a ted448 point. -func pull(Q *ted448.Point, P *Point) { isogeny4(Q, (*ted448.Point)(P), true) } +func pull(Q *ted.Point, P *Point) { isogeny4(Q, (*ted.Point)(P), true) } // push calculates Q = Iso4^-1(P), where P is a ted448 point and Q is a Goldilocks point. -func push(Q *Point, P *ted448.Point) { isogeny4((*ted448.Point)(Q), P, false) } +func push(Q *Point, P *ted.Point) { isogeny4((*ted.Point)(Q), P, false) } // isogeny4 is a birational map between ted448 and Goldilocks curves. -func isogeny4(Q, P *ted448.Point, isPull bool) { +func isogeny4(Q, P *ted.Point, isPull bool) { Px, Py, Pz := &P.X, &P.Y, &P.Z a, b, c, d, e, f, g, h := &Q.X, &Q.Y, &Q.Z, &fp.Elt{}, &Q.Ta, &Q.X, &Q.Y, &Q.Tb fp.Add(e, Px, Py) // x+y @@ -147,6 +178,78 @@ func isogeny4(Q, P *ted448.Point, isPull bool) { fp.Mul(&Q.Y, g, h) // Y = G * H, // T = E * H } +type Scalar struct{ k ted.Scalar } + +func (z Scalar) String() string { return z.k.String() } +func (z *Scalar) Add(x, y *Scalar) { z.k.Add(&x.k, &y.k) } +func (z *Scalar) Sub(x, y *Scalar) { z.k.Sub(&x.k, &y.k) } +func (z *Scalar) Mul(x, y *Scalar) { z.k.Mul(&x.k, &y.k) } +func (z *Scalar) Neg(x *Scalar) { z.k.Neg(&x.k) } +func (z *Scalar) Inv(x *Scalar) { z.k.Inv(&x.k) } +func (z *Scalar) IsEqual(x *Scalar) int { return subtle.ConstantTimeCompare(z.k[:], x.k[:]) } +func (z *Scalar) SetUint64(n uint64) { z.k = ted.Scalar{}; binary.LittleEndian.PutUint64(z.k[:], n) } + +// UnmarshalBinary recovers the scalar from its byte representation in big-endian order. +func (z *Scalar) UnmarshalBinary(b []byte) error { return z.k.UnmarshalBinary(b) } + +// MarshalBinary returns the scalar byte representation in big-endian order. +func (z *Scalar) MarshalBinary() ([]byte, error) { return z.k.MarshalBinary() } + +// ToBytesLE returns the scalar byte representation in little-endian order. +func (z *Scalar) ToBytesLE() []byte { return z.k.ToBytesLE() } + +// ToBytesBE returns the scalar byte representation in big-endian order. +func (z *Scalar) ToBytesBE() []byte { return z.k.ToBytesBE() } + +// FromBytesLE stores z = x mod order, where x is a number stored in little-endian order. +func (z *Scalar) FromBytesLE(x []byte) { z.k.FromBytesLE(x) } + +// FromBytesBE stores z = x mod order, where x is a number stored in big-endian order. +func (z *Scalar) FromBytesBE(x []byte) { z.k.FromBytesBE(x) } + +var ( + // genX is the x-coordinate of the generator of Goldilocks curve. + genX = fp.Elt{ // little-endian + 0x5e, 0xc0, 0x0c, 0xc7, 0x2b, 0xa8, 0x26, 0x26, + 0x8e, 0x93, 0x00, 0x8b, 0xe1, 0x80, 0x3b, 0x43, + 0x11, 0x65, 0xb6, 0x2a, 0xf7, 0x1a, 0xae, 0x12, + 0x64, 0xa4, 0xd3, 0xa3, 0x24, 0xe3, 0x6d, 0xea, + 0x67, 0x17, 0x0f, 0x47, 0x70, 0x65, 0x14, 0x9e, + 0xda, 0x36, 0xbf, 0x22, 0xa6, 0x15, 0x1d, 0x22, + 0xed, 0x0d, 0xed, 0x6b, 0xc6, 0x70, 0x19, 0x4f, + } + // genY is the y-coordinate of the generator of Goldilocks curve. + genY = fp.Elt{ // little-endian + 0x14, 0xfa, 0x30, 0xf2, 0x5b, 0x79, 0x08, 0x98, + 0xad, 0xc8, 0xd7, 0x4e, 0x2c, 0x13, 0xbd, 0xfd, + 0xc4, 0x39, 0x7c, 0xe6, 0x1c, 0xff, 0xd3, 0x3a, + 0xd7, 0xc2, 0xa0, 0x05, 0x1e, 0x9c, 0x78, 0x87, + 0x40, 0x98, 0xa3, 0x6c, 0x73, 0x73, 0xea, 0x4b, + 0x62, 0xc7, 0xc9, 0x56, 0x37, 0x20, 0x76, 0x88, + 0x24, 0xbc, 0xb6, 0x6e, 0x71, 0x46, 0x3f, 0x69, + } + // paramD is the D parameter of the Goldilocks curve, D=-39081 in Fp. + paramD = fp.Elt{ // little-endian + 0x56, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + } + // invFour is 1/4 mod order, where order = ted.Order(). + invFour = ted.Scalar{ // little-endian + 0x3d, 0x11, 0xd6, 0xaa, 0xa4, 0x30, 0xde, 0x48, + 0xd5, 0x63, 0x71, 0xa3, 0x9c, 0x30, 0x5b, 0x08, + 0xa4, 0x8d, 0xb5, 0x6b, 0xd2, 0xb6, 0x13, 0x71, + 0xfa, 0x88, 0x32, 0xdf, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, + } +) + // isLessThan returns 1 if 0 <= x < y, and assumes that slices are of the // same length and are interpreted in little-endian order. func isLessThan(x, y []byte) int { @@ -159,25 +262,56 @@ func isLessThan(x, y []byte) int { return ((xi - yi) >> (bits.UintSize - 1)) & 1 } -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, +// Decode if succeeds constructs a point by decoding the first +// EncodingSize bytes of data. +func (P *Point) Decode(data *[EncodingSize]byte) error { + x, y := &fp.Elt{}, &fp.Elt{} + isByteZero := subtle.ConstantTimeByteEq(data[EncodingSize-1]&0x7F, 0x00) + signX := int(data[EncodingSize-1] >> 7) + copy(y[:], data[:fp.Size]) + p := fp.P() + isLessThanP := isLessThan(y[:], p[:]) + + u, v := &fp.Elt{}, &fp.Elt{} + one := fp.One() + fp.Sqr(u, y) // u = y^2 + fp.Mul(v, u, ¶mD) // v = dy^2 + fp.Sub(u, u, &one) // u = y^2-1 + fp.Sub(v, v, &one) // v = dy^2-a + isQR := fp.InvSqrt(x, u, v) // x = sqrt(u/v) + isValidXSign := 1 - (fp.IsZero(x) & signX) + fp.Neg(u, x) // u = -x + fp.Cmov(x, u, uint(signX^fp.Parity(x))) // if signX != x mod 2 + + b0 := isByteZero + b1 := isLessThanP + b2 := isQR + b3 := isValidXSign + b := uint(subtle.ConstantTimeEq(int32(8*b3+4*b2+2*b1+b0), 0xF)) + fp.Cmov(&P.X, x, b) + fp.Cmov(&P.Y, y, b) + fp.Cmov(&P.Ta, x, b) + fp.Cmov(&P.Tb, y, b) + fp.Cmov(&P.Z, &one, b) + if b == 0 { + return ErrInvalidDecoding } + return nil +} + +// Encode sets data with the unique encoding of the point P. +func (P *Point) Encode(data *[EncodingSize]byte) error { + P.ToAffine() + data[EncodingSize-1] = (P.X[0] & 1) << 7 + return fp.ToBytes(data[:fp.Size], &P.Y) +} + +const ( + // EncodingSize is the size (in bytes) of an encoded point on the Goldilocks curve. + EncodingSize = fp.Size + 1 + // ScalarSize is the size (in bytes) of scalars. + ScalarSize = ted.ScalarSize ) + +// ErrInvalidDecoding alerts of an error during decoding a point. +var ErrInvalidDecoding = errors.New("goldilocks: invalid point decoding") diff --git a/ecc/goldilocks/goldilocks_test.go b/ecc/goldilocks/goldilocks_test.go index 3dde2a3f..d50cda41 100644 --- a/ecc/goldilocks/goldilocks_test.go +++ b/ecc/goldilocks/goldilocks_test.go @@ -1,70 +1,139 @@ -package goldilocks +package goldilocks_test import ( "crypto/rand" + "errors" "testing" - "github.com/cloudflare/circl/internal/ted448" + goldilocks "github.com/cloudflare/circl/ecc/goldilocks" + ted "github.com/cloudflare/circl/ecc/goldilocks/internal/ted448" "github.com/cloudflare/circl/internal/test" fp "github.com/cloudflare/circl/math/fp448" ) -func randomTwistPoint() ted448.Point { - var k ted448.Scalar - _, _ = rand.Read(k[:]) - var P ted448.Point - ted448.ScalarBaseMult(&P, &k) +func rndScalar(t testing.TB) *goldilocks.Scalar { + var buf [ted.ScalarSize]byte + _, err := rand.Read(buf[:]) + if err != nil { + t.Fatal(err) + } + var s goldilocks.Scalar + s.FromBytesLE(buf[:]) + return &s +} + +func randomPoint(t testing.TB) (P goldilocks.Point) { + P.ScalarBaseMult(rndScalar(t)) return P } -func TestIsogeny(t *testing.T) { +func TestPointAdd(t *testing.T) { + const testTimes = 1 << 10 + + t.Run("P+0=P", func(t *testing.T) { + I := goldilocks.Identity() + for i := 0; i < testTimes; i++ { + P := randomPoint(t) + got := P + got.Add(&I) + want := P + if got.IsEqual(&want) == 0 { + test.ReportError(t, got, want, P) + } + } + }) + + t.Run("P+(-P)=0", func(t *testing.T) { + for i := 0; i < testTimes; i++ { + P := randomPoint(t) + got := P + got.Neg() + got.Add(&P) + want := goldilocks.Identity() + if got.IsEqual(&want) == 0 { + test.ReportError(t, got, want, P) + } + } + }) + + t.Run("16P", func(t *testing.T) { + for i := 0; i < testTimes; i++ { + P := randomPoint(t) + // 16P = P+P+... + R := goldilocks.Identity() + for i := 0; i < 16; i++ { + R.Add(&P) + } + got := R + // 16P = 2*2*2*2*P + P.Double() + P.Double() + P.Double() + P.Double() + want := P + if got.IsEqual(&want) == 0 { + test.ReportError(t, got, want, P) + } + } + }) +} + +func TestScalarBaseMult(t *testing.T) { const testTimes = 1 << 10 - var phiP Point - var Q ted448.Point + var got, want goldilocks.Point + G := goldilocks.Generator() 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) + k := rndScalar(t) + got.ScalarBaseMult(k) + want.ScalarMult(k, &G) + if got.IsEqual(&want) == 0 { + test.ReportError(t, got, want, k) } } } func TestScalarMult(t *testing.T) { - const testTimes = 1 << 10 - k := &Scalar{} - zero := &Scalar{} - var P, Q, I Point - var got, want [EncodingSize]byte - _I := ted448.Identity() - push(&I, &_I) + const testTimes = 1 << 9 + zero := &goldilocks.Scalar{} + var got, want goldilocks.Point 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 { + k := rndScalar(t) + Q := randomPoint(t) + got.ScalarMult(k, &Q) + want.CombinedMult(zero, k, &Q) // 0*G + k*Q + if got.IsEqual(&want) == 0 { test.ReportError(t, got, want, k) } } } +func TestCombinedMult(t *testing.T) { + const testTimes = 1 << 9 + var got, want, R goldilocks.Point + for i := 0; i < testTimes; i++ { + k1 := rndScalar(t) + k2 := rndScalar(t) + Q := randomPoint(t) + got.CombinedMult(k1, k2, &Q) // k1*G + k2*Q + + R.ScalarBaseMult(k1) + want.ScalarMult(k2, &Q) + want.Add(&R) + + if got.IsEqual(&want) == 0 { + test.ReportError(t, got, want, k1, k2, Q) + } + } +} + func TestPointEncoding(t *testing.T) { const testTimes = 1 << 10 - var want, got [EncodingSize]byte - var P Point + var want, got [goldilocks.EncodingSize]byte + var P goldilocks.Point for i := 0; i < testTimes; i++ { for found := false; !found; { _, _ = rand.Read(want[:]) - want[EncodingSize-1] &= 0x80 + want[goldilocks.EncodingSize-1] &= 0x80 err := P.Decode(&want) found = err == nil } @@ -79,42 +148,101 @@ func TestPointInvalid(t *testing.T) { p := fp.P() one := fp.One() - var byteDirty, bigY, wrongSignX, nonQR [EncodingSize]byte - byteDirty[EncodingSize-1] = 0x33 + var byteDirty, bigY, wrongSignX, nonQR [goldilocks.EncodingSize]byte + byteDirty[goldilocks.EncodingSize-1] = 0x33 copy(bigY[:], p[:]) copy(wrongSignX[:], one[:]) - wrongSignX[EncodingSize-1] = 1 << 7 + wrongSignX[goldilocks.EncodingSize-1] = 1 << 7 nonQR[0] = 2 // smallest y such that (y^2+a)/(dy^2-a) is not a square. - badEncodings := []*[EncodingSize]byte{ + badEncodings := []*[goldilocks.EncodingSize]byte{ &byteDirty, // the last byte is not {0x00,0x80}. &bigY, // y is out of the interval [0,p-1]. &wrongSignX, // x has wrong sign. &nonQR, // y=2 and (y^2+a)/(dy^2-a) is not a square. } - var P Point + var P goldilocks.Point for _, enc := range badEncodings { got := P.Decode(enc) - want := ErrInvalidDecoding - if got != want { + want := goldilocks.ErrInvalidDecoding + if !errors.Is(got, want) { test.ReportError(t, got, want, enc) } } } +func BenchmarkPoint(b *testing.B) { + k := rndScalar(b) + l := rndScalar(b) + P := randomPoint(b) + Q := randomPoint(b) + + b.Run("Add", func(b *testing.B) { + for i := 0; i < b.N; i++ { + P.Add(&Q) + } + }) + b.Run("Double", func(b *testing.B) { + for i := 0; i < b.N; i++ { + P.Double() + } + }) + b.Run("ScalarMult", func(b *testing.B) { + for i := 0; i < b.N; i++ { + Q.ScalarMult(k, &P) + } + }) + b.Run("ScalarBaseMult", func(b *testing.B) { + for i := 0; i < b.N; i++ { + P.ScalarBaseMult(k) + } + }) + b.Run("CombinedMult", func(b *testing.B) { + for i := 0; i < b.N; i++ { + Q.CombinedMult(k, l, &P) + } + }) +} + +func BenchmarkScalar(b *testing.B) { + x := rndScalar(b) + y := rndScalar(b) + z := rndScalar(b) + + b.Run("Add", func(b *testing.B) { + for i := 0; i < b.N; i++ { + z.Add(x, y) + } + }) + b.Run("Sub", func(b *testing.B) { + for i := 0; i < b.N; i++ { + z.Sub(x, y) + } + }) + b.Run("Mul", func(b *testing.B) { + for i := 0; i < b.N; i++ { + z.Mul(x, y) + } + }) + b.Run("Inv", func(b *testing.B) { + for i := 0; i < b.N; i++ { + z.Inv(x) + } + }) +} + func BenchmarkEncoding(b *testing.B) { - var data [EncodingSize]byte - var k Scalar - _, _ = rand.Read(k[:]) - var P Point - P.ScalarBaseMult(&k) - b.Run("Marshal", func(b *testing.B) { + var data [goldilocks.EncodingSize]byte + k := rndScalar(b) + var P goldilocks.Point + P.ScalarBaseMult(k) + b.Run("Encode", func(b *testing.B) { for i := 0; i < b.N; i++ { _ = P.Encode(&data) } }) - b.Run("Unmarshal", func(b *testing.B) { + b.Run("Decode", func(b *testing.B) { for i := 0; i < b.N; i++ { _ = P.Decode(&data) } diff --git a/internal/ted448/basemult.go b/ecc/goldilocks/internal/ted448/basemult.go similarity index 100% rename from internal/ted448/basemult.go rename to ecc/goldilocks/internal/ted448/basemult.go diff --git a/internal/ted448/constants.go b/ecc/goldilocks/internal/ted448/constants.go similarity index 98% rename from internal/ted448/constants.go rename to ecc/goldilocks/internal/ted448/constants.go index 3698ee9e..2f0655ec 100644 --- a/internal/ted448/constants.go +++ b/ecc/goldilocks/internal/ted448/constants.go @@ -35,7 +35,7 @@ var ( } // order is 2^446-0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d, // which is the number of points in the prime subgroup. - order = Scalar{ + order = Scalar{ // little-endian 0xf3, 0x44, 0x58, 0xab, 0x92, 0xc2, 0x78, 0x23, 0x55, 0x8f, 0xc5, 0x8d, 0x72, 0xc2, 0x6c, 0x21, 0x90, 0x36, 0xd6, 0xae, 0x49, 0xdb, 0x4e, 0xc4, diff --git a/internal/ted448/curve.go b/ecc/goldilocks/internal/ted448/curve.go similarity index 94% rename from internal/ted448/curve.go rename to ecc/goldilocks/internal/ted448/curve.go index a58d05c8..84f47967 100644 --- a/internal/ted448/curve.go +++ b/ecc/goldilocks/internal/ted448/curve.go @@ -19,8 +19,6 @@ // 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 ( @@ -38,11 +36,8 @@ 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 } +// Order returns the number of points in the prime subgroup in little-endian order. +func Order() (r [ScalarSize]byte) { r = order; return r } // IsOnCurve returns true if the point lies on the curve. func IsOnCurve(P *Point) bool { diff --git a/internal/ted448/curve_test.go b/ecc/goldilocks/internal/ted448/curve_test.go similarity index 69% rename from internal/ted448/curve_test.go rename to ecc/goldilocks/internal/ted448/curve_test.go index 33a5d3c1..8bff1f33 100644 --- a/internal/ted448/curve_test.go +++ b/ecc/goldilocks/internal/ted448/curve_test.go @@ -4,7 +4,7 @@ import ( "crypto/rand" "testing" - "github.com/cloudflare/circl/internal/ted448" + "github.com/cloudflare/circl/ecc/goldilocks/internal/ted448" "github.com/cloudflare/circl/internal/test" ) @@ -27,13 +27,18 @@ func TestPointAdd(t *testing.T) { Q.Double() // 8P Q.Double() // 16P got := Q + got.ToAffine() + // R = 16P = P+P...+P R := ted448.Identity() for j := 0; j < 16; j++ { R.Add(&P) } want := R - if !ted448.IsOnCurve(&got) || !ted448.IsOnCurve(&want) || !got.IsEqual(&want) { + want.ToAffine() + if !ted448.IsOnCurve(&got) || + !ted448.IsOnCurve(&want) || + got.IsEqual(&want) == 0 { test.ReportError(t, got, want, P) } } @@ -64,7 +69,7 @@ func TestPointNeg(t *testing.T) { Q.Neg() Q.Add(&P) got := Q.IsIdentity() - want := true + want := 1 if got != want { test.ReportError(t, got, want, P) } @@ -73,30 +78,33 @@ func TestPointNeg(t *testing.T) { func TestScalarMult(t *testing.T) { const testTimes = 1 << 8 + order := ted448.Scalar(ted448.Order()) t.Run("rG=0", func(t *testing.T) { got := &ted448.Point{} - 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) { + if !ted448.IsOnCurve(got) || + !ted448.IsOnCurve(&want) || + got.IsEqual(&want) == 0 { test.ReportError(t, got, want) } } }) t.Run("rP=0", func(t *testing.T) { got := &ted448.Point{} - 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) + if !ted448.IsOnCurve(got) || + !ted448.IsOnCurve(&want) || + got.IsEqual(&want) == 0 { + test.ReportError(t, got, want, P) } } }) @@ -112,7 +120,9 @@ func TestScalarMult(t *testing.T) { ted448.ScalarBaseMult(got, k) ted448.CombinedMult(want, k, zero, &I) // k*G + 0*I - if !ted448.IsOnCurve(got) || !ted448.IsOnCurve(want) || !got.IsEqual(want) { + if !ted448.IsOnCurve(got) || + !ted448.IsOnCurve(want) || + got.IsEqual(want) == 0 { test.ReportError(t, got, want, k) } } @@ -129,7 +139,9 @@ func TestScalarMult(t *testing.T) { ted448.ScalarMult(got, k, &P) ted448.CombinedMult(want, zero, k, &P) - if !ted448.IsOnCurve(got) || !ted448.IsOnCurve(want) || !got.IsEqual(want) { + if !ted448.IsOnCurve(got) || + !ted448.IsOnCurve(want) || + got.IsEqual(want) == 0 { test.ReportError(t, got, want, P, k) } } @@ -152,38 +164,11 @@ func TestScalarMult(t *testing.T) { got := kG ted448.CombinedMult(want, k, l, &P) - if !ted448.IsOnCurve(got) || !ted448.IsOnCurve(want) || !got.IsEqual(want) { + if !ted448.IsOnCurve(got) || + !ted448.IsOnCurve(want) || + got.IsEqual(want) == 0 { 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/ecc/goldilocks/internal/ted448/point.go similarity index 89% rename from internal/ted448/point.go rename to ecc/goldilocks/internal/ted448/point.go index cf0a3633..5e4829be 100644 --- a/internal/ted448/point.go +++ b/ecc/goldilocks/internal/ted448/point.go @@ -66,6 +66,7 @@ func (P *Point) coreAddition(Q *prePointAffine) { // Formula as in Eq.(5) of "Twisted Edwards Curves Revisited" by // Hisil H., Wong K.KH., Carter G., Dawson E. (2008) // https://doi.org/10.1007/978-3-540-89255-7_20 + // Formula for curves with a=-1. Px, Py, Pz, Pta, Ptb := &P.X, &P.Y, &P.Z, &P.Ta, &P.Tb addYX2, subYX2, dt2 := &Q.addYX, &Q.subYX, &Q.dt2 a, b, c, d, e, f, g, h := Px, Py, &fp.Elt{}, Pz, Pta, Px, Py, Ptb @@ -108,17 +109,17 @@ func (P *Point) mixAdd(Q *prePointProy) { P.coreAddition(&Q.prePointAffine) } -// IsIdentity returns True is P is the identity. -func (P *Point) IsIdentity() bool { +// IsIdentity returns 1 if P is the identity, otherwise 0. +func (P *Point) IsIdentity() int { b0 := fp.IsZero(&P.X) b1 := 1 - fp.IsZero(&P.Y) b2 := 1 - fp.IsZero(&P.Z) b3 := fp.IsEqual(&P.Y, &P.Z) - return subtle.ConstantTimeEq(int32(8*b3+4*b2+2*b1+b0), 0xF) == 1 + return subtle.ConstantTimeEq(int32(8*b3+4*b2+2*b1+b0), 0xF) } -// IsEqual returns True if P is equivalent to Q. -func (P *Point) IsEqual(Q *Point) bool { +// IsEqual returns 1 if P is equivalent to Q, otherwise 0. +func (P *Point) IsEqual(Q *Point) int { l, r := &fp.Elt{}, &fp.Elt{} fp.Mul(l, &P.X, &Q.Z) fp.Mul(r, &Q.X, &P.Z) @@ -134,7 +135,7 @@ func (P *Point) IsEqual(Q *Point) bool { fp.Mul(r, r, &P.Z) fp.Sub(l, l, r) b2 := fp.IsZero(l) - return subtle.ConstantTimeEq(int32(4*b2+2*b1+b0), 0x7) == 1 + return subtle.ConstantTimeEq(int32(4*b2+2*b1+b0), 0x7) } // Neg obtains the inverse of P. @@ -147,6 +148,18 @@ func (P *Point) Add(Q *Point) { P.mixAdd(preB) } +func (P *Point) ToAffine() { + invZ := &fp.Elt{} + fp.Inv(invZ, &P.Z) // 1/z + fp.Mul(&P.X, &P.X, invZ) // x/z + fp.Mul(&P.Y, &P.Y, invZ) // y/z + fp.Modp(&P.X) + fp.Modp(&P.Y) + P.Ta = P.X + P.Tb = P.Y + fp.SetOne(&P.Z) +} + // oddMultiples calculates T[i] = (2*i-1)P for 0 < i < len(T). func (P *Point) oddMultiples(T []prePointProy) { if n := len(T); n > 0 { diff --git a/internal/ted448/scalar.go b/ecc/goldilocks/internal/ted448/scalar.go similarity index 81% rename from internal/ted448/scalar.go rename to ecc/goldilocks/internal/ted448/scalar.go index 6f7f16bc..5b6887ab 100644 --- a/internal/ted448/scalar.go +++ b/ecc/goldilocks/internal/ted448/scalar.go @@ -2,6 +2,8 @@ package ted448 import ( "encoding/binary" + "fmt" + "io" "math/bits" "github.com/cloudflare/circl/internal/conv" @@ -17,7 +19,7 @@ const ( // Scalar represents a positive integer stored in little-endian order. type Scalar [ScalarSize]byte -func (z Scalar) String() string { return conv.BytesLe2Hex(z[:]) } +func (z Scalar) String() string { z.red(); return conv.BytesLe2Hex(z[:]) } type scalar64 [_N]uint64 @@ -181,13 +183,25 @@ func (z *scalar64) modOrder() { } } -// FromBytes stores z = x mod order, where x is a number stored in little-endian order. -func (z *Scalar) FromBytes(x []byte) { +func invertEndianness(v []byte) { + for i := 0; i < len(v)/2; i++ { + v[i], v[len(v)-1-i] = v[len(v)-1-i], v[i] + } +} + +// FromBytesBE stores z = x mod order, where x is a number stored in big-endian order. +func (z *Scalar) FromBytesBE(x []byte) { + revX := make([]byte, len(x)) + copy(revX, x) + invertEndianness(revX) + z.FromBytesLE(revX) +} + +// FromBytesLE stores z = x mod order, where x is a number stored in little-endian order. +func (z *Scalar) FromBytesLE(x []byte) { n := len(x) nCeil := (n + 7) >> 3 - for i := range z { - z[i] = 0 - } + *z = Scalar{} if nCeil < _N { copy(z[:], x) return @@ -204,8 +218,47 @@ func (z *Scalar) FromBytes(x []byte) { z64.toScalar(z) } -// Red reduces z mod order. -func (z *Scalar) Red() { var t scalar64; t.fromScalar(z); t.modOrder(); t.toScalar(z) } +// ToBytesBE returns the scalar byte representation in big-endian order. +func (z *Scalar) ToBytesBE() []byte { b := z.ToBytesLE(); invertEndianness(b); return b } + +// ToBytesLE returns the scalar byte representation in little-endian order. +func (z *Scalar) ToBytesLE() []byte { z.red(); k := *z; return k[:] } + +// MarshalBinary returns the scalar byte representation in big-endian order. +func (z *Scalar) MarshalBinary() ([]byte, error) { return z.ToBytesBE(), nil } + +// UnmarshalBinary recovers the scalar from its byte representation in big-endian order. +func (z *Scalar) UnmarshalBinary(data []byte) error { + if len(data) < ScalarSize { + return io.ErrShortBuffer + } + + var x Scalar + copy(x[:], data[:ScalarSize]) + invertEndianness(x[:]) + // Check that input is fully-reduced, i.e., 0 <= data < order. + if isLessThan(x[:], order[:]) == 0 { + return fmt.Errorf("ted448: unmarshaling a scalar not in range [0, order)") + } + *z = x + + return nil +} + +// isLessThan returns 1 if 0 <= x < y, and assumes that slices are of the +// same length and are interpreted in little-endian order. +func isLessThan(x, y []byte) int { + i := len(x) - 1 + for i > 0 && x[i] == y[i] { + i-- + } + xi := int(x[i]) + yi := int(y[i]) + return ((xi - yi) >> (bits.UintSize - 1)) & 1 +} + +// red reduces z mod order. +func (z *Scalar) red() { var t scalar64; t.fromScalar(z); t.modOrder(); t.toScalar(z) } // Neg calculates z = -x mod order. func (z *Scalar) Neg(x *Scalar) { z.Sub(&order, x) } @@ -264,10 +317,10 @@ func (z *Scalar) Inv(x *Scalar) { x11111 := (&scalar64{}).mul(x10, x11101) // x11111 = x10 + x11101 x111110 := (&scalar64{}).mul(x11111, x11111) // x111110 = 2 * x11111 x1111100 := (&scalar64{}).mul(x111110, x111110) // x1111100 = 2 * x111110 - var i24, i41, i73, i129, t = &scalar64{}, &scalar64{}, &scalar64{}, + i24, i41, i73, i129, t := &scalar64{}, &scalar64{}, &scalar64{}, &scalar64{}, &scalar64{} - var x222, i262, i279, i298, i312, i331, i343, i365, - i375, i396, i411, i431, i444, i464, i478, i498, iret *scalar64 = t, t, + x222, i262, i279, i298, i312, i331, i343, i365, + i375, i396, i411, i431, i444, i464, i478, i498, iret := t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t i24.sqrnmul(x1111100, 5, x1111100) // i24 = x1111100 << 5 + x1111100 diff --git a/ecc/goldilocks/internal/ted448/scalar_test.go b/ecc/goldilocks/internal/ted448/scalar_test.go new file mode 100644 index 00000000..072476b4 --- /dev/null +++ b/ecc/goldilocks/internal/ted448/scalar_test.go @@ -0,0 +1,170 @@ +package ted448_test + +import ( + "bytes" + "crypto/rand" + "encoding" + "math/big" + "testing" + + "github.com/cloudflare/circl/ecc/goldilocks/internal/ted448" + "github.com/cloudflare/circl/internal/conv" + "github.com/cloudflare/circl/internal/test" + fp "github.com/cloudflare/circl/math/fp448" +) + +var bigOrder, _ = new(big.Int).SetString("3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3", 16) + +func rndScalar(t testing.TB) *ted448.Scalar { + var buf [ted448.ScalarSize]byte + _, err := rand.Read(buf[:]) + if err != nil { + t.Fatal(err) + } + var s ted448.Scalar + s.FromBytesLE(buf[:]) + return &s +} + +func toBig(s *ted448.Scalar) *big.Int { + return new(big.Int).SetBytes(s.ToBytesBE()) +} + +func TestReduceModOrder(t *testing.T) { + const max = 3*fp.Size - 1 + var b [max]byte + _, _ = rand.Read(b[:]) + var z ted448.Scalar + for i := 0; i < max; i++ { + x := b[0:i] + bigX := conv.BytesLe2BigInt(x) + + z.FromBytesLE(x) + got := toBig(&z) + got.Mod(got, bigOrder) + + want := bigX.Mod(bigX, bigOrder) + + if got.Cmp(want) != 0 { + test.ReportError(t, got, want, x, i) + } + } +} + +func testOp(t *testing.T, + f func(z, x, y *ted448.Scalar), + g func(z, x, y *big.Int), +) { + t.Helper() + const testTimes = 1 << 8 + want := new(big.Int) + var z ted448.Scalar + for i := 0; i < testTimes; i++ { + x := rndScalar(t) + y := rndScalar(t) + bigX := toBig(x) + bigY := toBig(y) + + f(&z, x, y) + got := toBig(&z) + + g(want, bigX, bigY) + want.Mod(want, bigOrder) + if got.Cmp(want) != 0 { + test.ReportError(t, got.Text(16), want.Text(16), x, y) + } + } +} + +type canMarshal interface { + encoding.BinaryMarshaler + encoding.BinaryUnmarshaler +} + +func testMarshal(t *testing.T, x, y canMarshal, name string) { + t.Helper() + + wantBytes, err := x.MarshalBinary() + test.CheckNoErr(t, err, "error on marshaling "+name) + + err = y.UnmarshalBinary(wantBytes) + test.CheckNoErr(t, err, "error on unmarshaling "+name) + + gotBytes, err := x.MarshalBinary() + test.CheckNoErr(t, err, "error on marshaling "+name) + + if !bytes.Equal(gotBytes, wantBytes) { + test.ReportError(t, gotBytes, wantBytes) + } + + b, _ := x.MarshalBinary() + err = y.UnmarshalBinary(b[:0]) + test.CheckIsErr(t, err, "should trigger unmarshal error") + + order := bigOrder.Bytes() + err = y.UnmarshalBinary(order) + test.CheckIsErr(t, err, "should trigger unmarshal error") + + order[0] += 1 + err = y.UnmarshalBinary(order[:]) + test.CheckIsErr(t, err, "should trigger unmarshal error") + + order[0] -= 2 + err = y.UnmarshalBinary(order[:]) + test.CheckNoErr(t, err, "should not trigger unmarshal error") +} + +func testFromBytes(t *testing.T) { + const testTimes = 1 << 8 + var got, want ted448.Scalar + for i := 0; i < testTimes; i++ { + x := rndScalar(t) + + got.FromBytesLE(x.ToBytesLE()) + want.FromBytesBE(x.ToBytesBE()) + + if got != want { + test.ReportError(t, got, want, x) + } + + s := x.String() + got1, ok := new(big.Int).SetString(s, 0) + want1 := toBig(x) + + if !ok || got1.Cmp(want1) != 0 { + test.ReportError(t, got1, want1, x) + } + } +} + +func TestScalar(t *testing.T) { + t.Run("Add", func(t *testing.T) { + testOp(t, + func(z, x, y *ted448.Scalar) { z.Add(x, y) }, + func(z, x, y *big.Int) { z.Add(x, y) }) + }) + t.Run("Sub", func(t *testing.T) { + testOp(t, + func(z, x, y *ted448.Scalar) { z.Sub(x, y) }, + func(z, x, y *big.Int) { z.Sub(x, y) }) + }) + t.Run("Mul", func(t *testing.T) { + testOp(t, + func(z, x, y *ted448.Scalar) { z.Mul(x, y) }, + func(z, x, y *big.Int) { z.Mul(x, y) }) + }) + t.Run("Neg", func(t *testing.T) { + testOp(t, + func(z, x, y *ted448.Scalar) { z.Neg(x) }, + func(z, x, y *big.Int) { z.Neg(x) }) + }) + t.Run("Inv", func(t *testing.T) { + testOp(t, + func(z, x, y *ted448.Scalar) { z.Inv(x) }, + func(z, x, y *big.Int) { z.ModInverse(x, bigOrder) }) + }) + t.Run("Marshal", func(t *testing.T) { + testMarshal(t, rndScalar(t), new(ted448.Scalar), "scalar") + }) + t.Run("FromBytes", testFromBytes) +} diff --git a/internal/ted448/tables.go b/ecc/goldilocks/internal/ted448/tables.go similarity index 100% rename from internal/ted448/tables.go rename to ecc/goldilocks/internal/ted448/tables.go diff --git a/ecc/goldilocks/isogeny_test.go b/ecc/goldilocks/isogeny_test.go new file mode 100644 index 00000000..48f504f3 --- /dev/null +++ b/ecc/goldilocks/isogeny_test.go @@ -0,0 +1,44 @@ +package goldilocks + +import ( + "crypto/rand" + "testing" + + ted "github.com/cloudflare/circl/ecc/goldilocks/internal/ted448" + "github.com/cloudflare/circl/internal/test" +) + +func rndScalar(t testing.TB) *ted.Scalar { + var buf [ted.ScalarSize]byte + _, err := rand.Read(buf[:]) + if err != nil { + t.Fatal(err) + } + var s ted.Scalar + s.FromBytesLE(buf[:]) + return &s +} + +func randomTwistPoint(t *testing.T) (P ted.Point) { + ted.ScalarBaseMult(&P, rndScalar(t)) + return P +} + +func TestIsogeny(t *testing.T) { + const testTimes = 1 << 10 + var phiP Point + var Q ted.Point + for i := 0; i < testTimes; i++ { + P := randomTwistPoint(t) + R := P + push(&phiP, &P) + pull(&Q, &phiP) + R.Double() // 2P + R.Double() // 4P + got := Q + want := R + if got.IsEqual(&want) == 0 { + test.ReportError(t, got, want, P) + } + } +} diff --git a/group/decaf.go b/group/decaf.go new file mode 100644 index 00000000..474793d0 --- /dev/null +++ b/group/decaf.go @@ -0,0 +1,352 @@ +package group + +import ( + "crypto/subtle" + "io" + "math/bits" + + curve "github.com/cloudflare/circl/ecc/goldilocks" + "github.com/cloudflare/circl/expander" + fp "github.com/cloudflare/circl/math/fp448" + "github.com/cloudflare/circl/xof" +) + +// Decaf Group +// +// Decaf (3) is a prime-order group constructed as a quotient of groups. A Decaf +// element can be represented by any point in the coset P+J[2], where J is a +// Jacobi quartic curve and J[2] are its 2-torsion points. +// Since P+J[2] has four points, Decaf specifies rules to choose one canonical +// representative, which has a unique encoding. Two representations are +// equivalent if they belong to the same coset. +// +// The types Elt and Scalar provide methods to perform arithmetic operations on +// the Decaf group. +// +// This implementation is compatible with v03 of draft-irtf-cfrg-ristretto255-decaf448 (4). +// +// References +// +// (1) https://www.shiftleft.org/papers/goldilocks +// +// (2) https://tools.ietf.org/html/rfc7748 +// +// (3) https://doi.org/10.1007/978-3-662-47989-6_34 and https://www.shiftleft.org/papers/decaf +// +// (4) https://datatracker.ietf.org/doc/draft-irtf-cfrg-ristretto255-decaf448/ + +// Decaf448 is a quotient group generated from the edwards448 curve. +var Decaf448 Group = decaf448{} + +type decaf448 struct{} + +func (g decaf448) String() string { return "decaf448" } +func (g decaf448) Params() *Params { return &Params{fp.Size, fp.Size, curve.ScalarSize} } +func (g decaf448) NewElement() Element { return g.Identity() } +func (g decaf448) NewScalar() Scalar { return new(dScl) } +func (g decaf448) Identity() Element { return &dElt{curve.Identity()} } +func (g decaf448) Order() Scalar { r := &dScl{}; r.k.FromBytesLE(curve.Order()); return r } +func (g decaf448) Generator() Element { + e := curve.Generator() + e.Double() // Since decaf.Generator() == 2*goldilocks.Generator(). + return &dElt{e} +} + +func (g decaf448) RandomElement(rd io.Reader) Element { + b := make([]byte, fp.Size) + if n, err := io.ReadFull(rd, b); err != nil || n != len(b) { + panic(err) + } + return g.HashToElement(b, nil) +} + +func (g decaf448) RandomScalar(rd io.Reader) Scalar { + b := make([]byte, fp.Size) + if n, err := io.ReadFull(rd, b); err != nil || n != len(b) { + panic(err) + } + return g.HashToScalar(b, nil) +} + +func (g decaf448) RandomNonZeroScalar(rd io.Reader) Scalar { + zero := g.NewScalar() + for { + s := g.RandomScalar(rd) + if !s.IsEqual(zero) { + return s + } + } +} + +func (g decaf448) HashToElementNonUniform(data, dst []byte) Element { + return g.HashToElement(data, dst) +} + +func (g decaf448) HashToElement(data, dst []byte) Element { + // Compliaint with draft-irtf-cfrg-hash-to-curve. + // Appendix C - Hashing to decaf448 + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-C + // SuiteID: decaf448_XOF:SHAKE256_D448MAP_RO_ + var buf [2 * fp.Size]byte + exp := expander.NewExpanderXOF(xof.SHAKE256, 224, dst) + uniformBytes := exp.Expand(data, 2*fp.Size) + copy(buf[:], uniformBytes) + return g.oneway(&buf) +} + +func (g decaf448) HashToScalar(data, dst []byte) Scalar { + // Section 5.4 - Scalar field + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-03#section-5.4 + exp := expander.NewExpanderXOF(xof.SHAKE256, 224, dst) + uniformBytes := exp.Expand(data, 64) + s := new(dScl) + s.k.FromBytesLE(uniformBytes) + return s +} + +func (g decaf448) oneway(data *[2 * fp.Size]byte) *dElt { + // Complaiant with draft-irtf-cfrg-ristretto255-decaf448-03 + // Section 5.3.4 - One-way Map + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-03#section-5.3.4 + var buf [fp.Size]byte + copy(buf[:], data[:fp.Size]) + p1 := g.mapFunc(&buf) + copy(buf[:], data[fp.Size:2*fp.Size]) + p2 := g.mapFunc(&buf) + p1.Add(&p2) + return &dElt{p: p1} +} + +func (g decaf448) mapFunc(data *[fp.Size]byte) (P curve.Point) { + t := (*fp.Elt)(data) + fp.Modp(t) + + one := fp.One() + d := curve.ParamD() + + r, u0, u1, u2, v := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + tv, sgn, s := &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + w0, w1, w2, w3 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + + fp.Sqr(r, t) // r = -t^2 + fp.Neg(r, r) // + fp.Sub(u0, r, &one) // u0 = d * (r-1) + fp.Mul(u0, u0, &d) // + fp.Add(u1, u0, &one) // u1 = (u0 + 1) * (u0 - r) + fp.Sub(u0, u0, r) // + fp.Mul(u1, u1, u0) // + fp.Add(u2, r, &one) // u2 = (r + 1) * u1 + fp.Mul(u2, u2, u1) // + isQR := fp.InvSqrt(v, &aMinusTwoD, u2) // (isQR, v) = sqrt(ONE_MINUS_TWO_D / (r + 1) * u1) + fp.Mul(tv, t, v) // v = CT_SELECT(v IF isQR ELSE t * v) + fp.Cmov(v, tv, uint(1-isQR)) // + fp.Neg(sgn, &one) // sgn = CT_SELECT(1 IF isQR ELSE -1) + fp.Cmov(sgn, &one, uint(isQR)) // + fp.Add(s, r, &one) // s = v * (r + 1) + fp.Mul(s, s, v) // + ctAbs(w0, s) // w0 = 2 * CT_ABS(s) + fp.Add(w0, w0, w0) // + fp.Sqr(w1, s) // w1 = s^2 + 1 + fp.Sub(w2, w1, &one) // w2 = s^2 - 1 + fp.Add(w1, w1, &one) // + fp.Sub(w3, r, &one) // w3 = v_prime * s * (r - 1) * ONE_MINUS_TWO_D + sgn + fp.Mul(w3, w3, s) // + fp.Mul(w3, w3, v) // + fp.Mul(w3, w3, &aMinusTwoD) // + fp.Add(w3, w3, sgn) // + fp.Mul(&P.X, w0, w3) // X = w0 * w3 + fp.Mul(&P.Y, w2, w1) // Y = w2 * w1 + fp.Mul(&P.Z, w1, w3) // Z = w1 * w3 + P.Ta, P.Tb = *w0, *w2 // T = w0 * w2 + + return P +} + +type dElt struct{ p curve.Point } + +func (e dElt) String() string { return e.p.String() } +func (e *dElt) Set(a Element) Element { e.p = a.(*dElt).p; return e } +func (e *dElt) Copy() Element { return &dElt{e.p} } +func (e *dElt) Add(a, b Element) Element { e.Set(a); e.p.Add(&b.(*dElt).p); return e } +func (e *dElt) Dbl(a Element) Element { e.Set(a); e.p.Double(); return e } +func (e *dElt) Neg(a Element) Element { e.Set(a); e.p.Neg(); return e } +func (e *dElt) Mul(a Element, s Scalar) Element { e.p.ScalarMult(&s.(*dScl).k, &a.(*dElt).p); return e } +func (e *dElt) MulGen(s Scalar) Element { + k := &s.(*dScl).k + k2 := &curve.Scalar{} + k2.Add(k, k) // Since decaf.Generator() == 2*goldilocks.Generator(). + e.p.ScalarBaseMult(k2) + return e +} + +func (e *dElt) IsIdentity() bool { + // From Decaf, Section 4.5 - Equality + // In particular, for a curve of cofactor exactly 4, + // a point (X : Y : Z : T ) is equal to the identity precisely when X = 0. + return fp.IsZero(&e.p.X) == 1 +} + +func (e *dElt) IsEqual(a Element) bool { + // Section 5.3.3 - Equals + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-03#section-5.3.3 + aa := a.(*dElt) + l, r := &fp.Elt{}, &fp.Elt{} + fp.Mul(l, &e.p.X, &aa.p.Y) + fp.Mul(r, &aa.p.X, &e.p.Y) + fp.Sub(l, l, r) + return fp.IsZero(l) == 1 +} + +func (e *dElt) MarshalBinaryCompress() ([]byte, error) { return e.MarshalBinary() } +func (e *dElt) MarshalBinary() ([]byte, error) { + var encS [fp.Size]byte + err := e.marshalBinary(encS[:]) + return encS[:], err +} + +func (e *dElt) marshalBinary(enc []byte) error { + // Section 5.3.2 - Encode + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-03#section-5.3.2 + x, ta, tb, z := &e.p.X, &e.p.Ta, &e.p.Tb, &e.p.Z + t, u1, u2, u3 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + v, ir, rt, w, s := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + + one := fp.One() + fp.Mul(t, ta, tb) // t = ta*tb + plus, minus := *x, *t // + fp.AddSub(&plus, &minus) // (plus,minus) = (x+t,x-t) + fp.Mul(u1, &plus, &minus) // u1 = (x+t)*(x-t) + fp.Sqr(v, x) // v = u1 * ONE_MINUS_D * x0^2 + fp.Mul(v, v, &aMinusD) // + fp.Mul(v, v, u1) // + _ = fp.InvSqrt(ir, &one, v) // ir = sqrt(1/v) + fp.Mul(w, ir, u1) // rt = CT_ABS(ir * u1 * SQRT_MINUS_D) + fp.Mul(w, w, &sqrtMinusD) // + ctAbs(rt, w) // + fp.Mul(u2, rt, z) // u2 = INVSQRT_MINUS_D * rt * z0 - t0 + fp.Mul(u2, u2, &invSqrtMinusD) // + fp.Sub(u2, u2, t) // + fp.Mul(u3, x, u2) // s = CT_ABS(ONE_MINUS_D * ir * x0 * u2) + fp.Mul(u3, u3, ir) // + fp.Mul(u3, u3, &aMinusD) // + ctAbs(s, u3) // + + return fp.ToBytes(enc[:], s) +} + +func (e *dElt) UnmarshalBinary(data []byte) error { + // Section 5.3.1 - Decode + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-03#section-5.3.1 + if len(data) < fp.Size { + return io.ErrShortBuffer + } + + p := fp.P() + s := &fp.Elt{} + copy(s[:], data[:fp.Size]) + isLessThanP := isLessThan(s[:], p[:]) + isPositiveS := 1 - fp.Parity(s) + + one := fp.One() + paramD := curve.ParamD() + + x, y := &fp.Elt{}, &fp.Elt{} + ss, u1, u2, u3 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + ir, v, w := &fp.Elt{}, &fp.Elt{}, &fp.Elt{} + + fp.Sqr(ss, s) // ss = s^2 + fp.Add(u1, &one, ss) // u1 = 1 - a*s^2 + fp.Mul(u2, ss, ¶mD) // u2 = d*s^2 + fp.Add(u2, u2, u2) // = 2*d*s^2 + fp.Add(u2, u2, u2) // = 4*d*s^2 + fp.Sqr(v, u1) // v = u1^2 = (1 + a*s^2)^2 + fp.Sub(u2, v, u2) // u2 = u1^2 - 4*d*s^2 + fp.Mul(w, u2, v) // w = u2 * u1^2 + isQR := fp.InvSqrt(ir, &one, w) // ir = sqrt(1/(u2 * u1^2)) + fp.Mul(w, s, ir) // w = ir*u1 + fp.Mul(w, w, u1) // = s*ir*u1 + fp.Mul(w, w, &sqrtMinusD) // = s*ir*u1*sqrt(-d) + fp.Add(w, w, w) // = 2*s*ir*u1*sqrt(-d) + ctAbs(u3, w) // u3 = CT_ABS(w) + fp.Mul(x, u3, ir) // x = u3 * ir * u2 * INVSQRT_MINUS_D + fp.Mul(x, x, u2) // + fp.Mul(x, x, &invSqrtMinusD) // + fp.Sub(y, &one, ss) // y = (1 - a*s^2) * ir * u1 + fp.Mul(y, y, ir) // + fp.Mul(y, y, u1) // + + b0 := isPositiveS + b1 := isLessThanP + b2 := isQR + b := uint(subtle.ConstantTimeEq(int32(4*b2+2*b1+b0), 0b111)) + fp.Cmov(&e.p.X, x, b) + fp.Cmov(&e.p.Y, y, b) + fp.Cmov(&e.p.Ta, x, b) + fp.Cmov(&e.p.Tb, y, b) + fp.Cmov(&e.p.Z, &one, b) + if b == 0 { + return ErrInvalidDecoding + } + return nil +} + +type dScl struct{ k curve.Scalar } + +func (s *dScl) String() string { return s.k.String() } +func (s *dScl) SetUint64(n uint64) { s.k.SetUint64(n) } +func (s *dScl) Set(a Scalar) Scalar { s.k = a.(*dScl).k; return s } +func (s *dScl) Copy() Scalar { return &dScl{k: s.k} } +func (s *dScl) Add(a, b Scalar) Scalar { s.k.Add(&a.(*dScl).k, &b.(*dScl).k); return s } +func (s *dScl) Sub(a, b Scalar) Scalar { s.k.Sub(&a.(*dScl).k, &b.(*dScl).k); return s } +func (s *dScl) Mul(a, b Scalar) Scalar { s.k.Mul(&a.(*dScl).k, &b.(*dScl).k); return s } +func (s *dScl) Neg(a Scalar) Scalar { s.k.Neg(&a.(*dScl).k); return s } +func (s *dScl) Inv(a Scalar) Scalar { s.k.Inv(&a.(*dScl).k); return s } +func (s *dScl) MarshalBinary() ([]byte, error) { return s.k.MarshalBinary() } +func (s *dScl) UnmarshalBinary(b []byte) error { return s.k.UnmarshalBinary(b) } +func (s *dScl) IsEqual(a Scalar) bool { return s.k.IsEqual(&a.(*dScl).k) == 1 } + +func ctAbs(z, x *fp.Elt) { + minusX := &fp.Elt{} + fp.Neg(minusX, x) + *z = *x + fp.Cmov(z, minusX, uint(fp.Parity(x))) +} + +// isLessThan returns 1 if 0 <= x < y, and assumes that slices are of the +// same length and are interpreted in little-endian order. +func isLessThan(x, y []byte) int { + i := len(x) - 1 + for i > 0 && x[i] == y[i] { + i-- + } + xi := int(x[i]) + yi := int(y[i]) + return ((xi - yi) >> (bits.UintSize - 1)) & 1 +} + +var ( + // aMinusD is paramA-paramD = (-1)-(-39081) = 39082. + aMinusD = fp.Elt{0xaa, 0x98} + // aMinusTwoD is paramA-2*paramD = (-1)-2*(-39081) = 78163. + aMinusTwoD = fp.Elt{0x53, 0x31, 0x01} + // sqrtMinusD is the smallest root of sqrt(paramD) = sqrt(39081). + sqrtMinusD = fp.Elt{ + 0x36, 0x27, 0x57, 0x45, 0x0f, 0xef, 0x42, 0x96, + 0x52, 0xce, 0x20, 0xaa, 0xf6, 0x7b, 0x33, 0x60, + 0xd2, 0xde, 0x6e, 0xfd, 0xf4, 0x66, 0x9a, 0x83, + 0xba, 0x14, 0x8c, 0x96, 0x80, 0xd7, 0xa2, 0x64, + 0x4b, 0xd5, 0xb8, 0xa5, 0xb8, 0xa7, 0xf1, 0xa1, + 0xa0, 0x6a, 0xa2, 0x2f, 0x72, 0x8d, 0xf6, 0x3b, + 0x68, 0xf7, 0x24, 0xeb, 0xfb, 0x62, 0xd9, 0x22, + } + // invSqrtMinusD is the smallest root of sqrt(1/paramD) = sqrt(1/39081). + invSqrtMinusD = fp.Elt{ + 0x2c, 0x68, 0x78, 0xb8, 0x5e, 0xbb, 0xaf, 0x53, + 0xf3, 0x94, 0x9e, 0xf1, 0x79, 0x24, 0xbb, 0xef, + 0x15, 0xba, 0x1f, 0xc2, 0xe2, 0x7e, 0x70, 0xbe, + 0x1a, 0x52, 0xa6, 0x28, 0xf1, 0x56, 0xba, 0xd6, + 0xa7, 0x27, 0x5b, 0x3a, 0x0c, 0x95, 0x90, 0x5a, + 0x07, 0xc8, 0xca, 0x0b, 0x5a, 0xe3, 0x2b, 0x90, + 0x57, 0xc0, 0x22, 0xe2, 0x52, 0x06, 0xf4, 0x6e, + } +) diff --git a/group/decaf448/constants.go b/group/decaf448/constants.go deleted file mode 100644 index a500f80d..00000000 --- a/group/decaf448/constants.go +++ /dev/null @@ -1,28 +0,0 @@ -package decaf448 - -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/group/decaf448/decaf.go b/group/decaf448/decaf.go deleted file mode 100644 index 7cbd4274..00000000 --- a/group/decaf448/decaf.go +++ /dev/null @@ -1,201 +0,0 @@ -// Package decaf448 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 decaf448 - -import ( - "crypto/subtle" - "math/bits" - - "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 { - b0 := fp.IsZero(&e.p.X) - b1 := 1 - fp.IsZero(&e.p.Y) - b2 := 1 - fp.IsZero(&e.p.Z) - return (b0 & b1 & b2) == 1 -} - -// 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) == 1 -} - -// 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 := 1 - fp.Parity(s) - - 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 - - b0 := isPositiveS - b1 := isLessThanP - b2 := isQR - b := uint(subtle.ConstantTimeEq(int32(4*b2+2*b1+b0), 0x7)) - fp.Cmov(&e.p.X, x, b) - fp.Cmov(&e.p.Y, y, b) - fp.Cmov(&e.p.Ta, x, b) - fp.Cmov(&e.p.Tb, y, b) - fp.Cmov(&e.p.Z, &one, b) - if b == 0 { - return ErrInvalidDecoding - } - return nil -} - -// 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 1 if 0 <= x < y, and assumes that slices are of the -// same length and are interpreted in little-endian order. -func isLessThan(x, y []byte) int { - i := len(x) - 1 - for i > 0 && x[i] == y[i] { - i-- - } - xi := int(x[i]) - yi := int(y[i]) - return ((xi - yi) >> (bits.UintSize - 1)) & 1 -} diff --git a/group/decaf448/decaf_test.go b/group/decaf448/decaf_test.go deleted file mode 100644 index 50614601..00000000 --- a/group/decaf448/decaf_test.go +++ /dev/null @@ -1,267 +0,0 @@ -package decaf448_test - -import ( - "bytes" - "crypto/rand" - "encoding/hex" - "encoding/json" - "io/ioutil" - "os" - "testing" - - "github.com/cloudflare/circl/group/decaf448" - "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 *decaf448.Elt, wantEnckG []byte) { - wantkG := &decaf448.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 && - decaf448.IsValid(gotkG) && - decaf448.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 = decaf448.Version - if got != want { - test.ReportError(t, got, want) - } - var scalar decaf448.Scalar - var P decaf448.Elt - G := decaf448.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) - - decaf448.MulGen(&P, &scalar) - verify(t, i, &P, wantEnckG) - - decaf448.Mul(&P, &scalar, G) - verify(t, i, &P, wantEnckG) - - decaf448.Mul(&P, &scalar, &P) - verify(t, i, &P, wantEnckP) - } -} - -func TestDecafRandom(t *testing.T) { - const testTimes = 1 << 10 - var e decaf448.Elt - var enc [decaf448.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() decaf448.Elt { - var k decaf448.Scalar - _, _ = rand.Read(k[:]) - var P decaf448.Elt - decaf448.MulGen(&P, &k) - return P -} - -func TestPointAdd(t *testing.T) { - const testTimes = 1 << 10 - Q := &decaf448.Elt{} - for i := 0; i < testTimes; i++ { - P := randomPoint() - // Q = 16P = 2^4P - decaf448.Double(Q, &P) // 2P - decaf448.Double(Q, Q) // 4P - decaf448.Double(Q, Q) // 8P - decaf448.Double(Q, Q) // 16P - got := Q - // R = 16P = P+P...+P - R := decaf448.Identity() - for j := 0; j < 16; j++ { - decaf448.Add(R, R, &P) - } - want := R - if !decaf448.IsValid(got) || !decaf448.IsValid(want) || !got.IsEqual(want) { - test.ReportError(t, got, want, P) - } - } -} - -func TestPointNeg(t *testing.T) { - const testTimes = 1 << 10 - Q := &decaf448.Elt{} - for i := 0; i < testTimes; i++ { - P := randomPoint() - decaf448.Neg(Q, &P) - decaf448.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 := &decaf448.Elt{} - order := decaf448.Order() - for i := 0; i < testTimes; i++ { - P := randomPoint() - - decaf448.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 decaf448.Elt - for _, enc := range badEncodings { - got := e.UnmarshalBinary(enc) - want := decaf448.ErrInvalidDecoding - if got != want { - test.ReportError(t, got, want, enc) - } - } -} - -func BenchmarkDecaf(b *testing.B) { - var k, l decaf448.Scalar - _, _ = rand.Read(k[:]) - _, _ = rand.Read(l[:]) - G := decaf448.Generator() - P := decaf448.Generator() - Z := decaf448.Identity() - enc, _ := G.MarshalBinary() - - x := &fp.Elt{} - y := &fp.Elt{} - _, _ = rand.Read(y[:]) - // p := fp.P() - // one := fp.One() - // fp.Sub(y, &p, &one) - - b.Run("Add", func(b *testing.B) { - for i := 0; i < b.N; i++ { - decaf448.Add(P, P, G) - } - }) - b.Run("IsZeroSub0", func(b *testing.B) { - for i := 0; i < b.N; i++ { - fp.IsZero(x) - } - }) - b.Run("IsZeroSub1", func(b *testing.B) { - for i := 0; i < b.N; i++ { - fp.IsZero(y) - } - }) - b.Run("IsZeroSi", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = Z.IsIdentity() - } - }) - b.Run("IsZero", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = P.IsIdentity() - } - }) - b.Run("Mul", func(b *testing.B) { - for i := 0; i < b.N; i++ { - decaf448.Mul(G, &k, G) - } - }) - b.Run("MulGen", func(b *testing.B) { - for i := 0; i < b.N; i++ { - decaf448.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/group/decaf448/testdata/decafv1.0_vectors.json b/group/decaf448/testdata/decafv1.0_vectors.json deleted file mode 100644 index 61f6788e..00000000 --- a/group/decaf448/testdata/decafv1.0_vectors.json +++ /dev/null @@ -1,172 +0,0 @@ -{ - "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/group/decaf_test.go b/group/decaf_test.go new file mode 100644 index 00000000..4313f415 --- /dev/null +++ b/group/decaf_test.go @@ -0,0 +1,128 @@ +package group + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "io/ioutil" + "os" + "testing" + + "github.com/cloudflare/circl/internal/test" + fp "github.com/cloudflare/circl/math/fp448" +) + +type testJSONFile struct { + Multiples []string `json:"multiples"` + Invalid []struct { + Description string `json:"description"` + Encodings []string `json:"encodings"` + } `json:"invalid"` + Oneway []struct { + Input string `json:"input"` + Output string `json:"output"` + } `json:"oneway"` +} + +func (kat *testJSONFile) readFile(t *testing.T, fileName string) { + jsonFile, err := os.Open(fileName) + if err != nil { + t.Fatalf("File %v can not be opened. Error: %v", fileName, err) + } + defer jsonFile.Close() + input, _ := ioutil.ReadAll(jsonFile) + + err = json.Unmarshal(input, &kat) + if err != nil { + t.Fatalf("File %v can not be loaded. Error: %v", fileName, err) + } +} + +func (kat *testJSONFile) multiples(t *testing.T) { + k := Decaf448.NewScalar() + got := Decaf448.NewElement() + want := Decaf448.NewElement() + + for i, vi := range kat.Multiples { + k.SetUint64(uint64(i)) + got.MulGen(k) + gotEnc, err := got.MarshalBinary() + test.CheckNoErr(t, err, "marshaling element") + + wantEnc, err := hex.DecodeString(vi) + test.CheckNoErr(t, err, "bad hex encoding") + err = want.UnmarshalBinary(wantEnc) + test.CheckNoErr(t, err, "bad element unmarshaling") + + if !got.IsEqual(want) { + test.ReportError(t, got, want, i) + } + + if !bytes.Equal(gotEnc, wantEnc) { + test.ReportError(t, gotEnc, wantEnc, i) + } + } +} + +func (kat *testJSONFile) invalid(t *testing.T) { + P := Decaf448.NewElement() + for _, vi := range kat.Invalid { + for _, vj := range vi.Encodings { + enc, err := hex.DecodeString(vj) + test.CheckNoErr(t, err, "bad hex encoding") + + err = P.UnmarshalBinary(enc) + test.CheckIsErr(t, err, "should trigger error: "+vi.Description) + } + } +} + +func (kat *testJSONFile) oneway(t *testing.T) { + var g decaf448 + var msg [2 * fp.Size]byte + for _, vi := range kat.Oneway { + input, err := hex.DecodeString(vi.Input) + test.CheckNoErr(t, err, "bad hex encoding") + test.CheckOk(len(input) == 2*fp.Size, "bad input size", t) + + copy(msg[:], input) + P := g.oneway(&msg) + got, err := P.MarshalBinary() + test.CheckNoErr(t, err, "bad element marshalling") + + want, err := hex.DecodeString(vi.Output) + test.CheckNoErr(t, err, "bad hex encoding") + + if !bytes.Equal(got, want) { + test.ReportError(t, got, want, input) + } + } +} + +func TestDecaf(t *testing.T) { + var kat testJSONFile + kat.readFile(t, "testdata/decaf_vectors.json") + + t.Run("multiples", kat.multiples) + t.Run("invalid", kat.invalid) + t.Run("oneway", kat.oneway) +} + +func TestDecafInvalid(t *testing.T) { + bigS := fp.P() + negativeS := fp.Elt{1} // the smallest s that is negative + nonQR := fp.Elt{4} // the shortest s such that (a^2s^4 + (2a - 4d)*s^2 + 1) is non-QR. + + badEncodings := [][]byte{ + {}, // wrong size input + bigS[:], // s is out of the interval [0,p-1]. + negativeS[:], // s is not positive + nonQR[:], // s=4 and (a^2s^4 + (2a - 4d)*s^2 + 1) is non-QR. + } + + e := decaf448{}.NewElement() + for _, enc := range badEncodings { + err := e.UnmarshalBinary(enc) + test.CheckIsErr(t, err, "should trigger an error") + } +} diff --git a/group/group.go b/group/group.go index fb56eba2..0498037f 100644 --- a/group/group.go +++ b/group/group.go @@ -61,6 +61,6 @@ type Scalar interface { } var ( - ErrType = errors.New("type mismatch") - ErrUnmarshal = errors.New("error unmarshaling") + ErrType = errors.New("group: type mismatch") + ErrInvalidDecoding = errors.New("group: invalid decoding") ) diff --git a/group/group_test.go b/group/group_test.go index 76a0b1f8..e15e5771 100644 --- a/group/group_test.go +++ b/group/group_test.go @@ -15,6 +15,7 @@ var allGroups = []group.Group{ group.P384, group.P521, group.Ristretto255, + group.Decaf448, } func TestGroup(t *testing.T) { diff --git a/group/short.go b/group/short.go index 151a749e..d759e867 100644 --- a/group/short.go +++ b/group/short.go @@ -199,17 +199,17 @@ func (e *wElt) UnmarshalBinary(b []byte) error { case l == 1+byteLen && (b[0] == 0x02 || b[0] == 0x03): // compressed x, y := elliptic.UnmarshalCompressed(e.wG.c, b) if x == nil { - return ErrUnmarshal + return ErrInvalidDecoding } e.x, e.y = x, y case l == 1+2*byteLen && b[0] == 0x04: // uncompressed x, y := elliptic.Unmarshal(e.wG.c, b) if x == nil { - return ErrUnmarshal + return ErrInvalidDecoding } e.x, e.y = x, y default: - return ErrUnmarshal + return ErrInvalidDecoding } return nil } diff --git a/group/testdata/decaf_vectors.json b/group/testdata/decaf_vectors.json new file mode 100644 index 00000000..7eeba49e --- /dev/null +++ b/group/testdata/decaf_vectors.json @@ -0,0 +1,88 @@ +{ + "multiples": [ + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "6666666666666666666666666666666666666666666666666666666633333333333333333333333333333333333333333333333333333333", + "c898eb4f87f97c564c6fd61fc7e49689314a1f818ec85eeb3bd5514ac816d38778f69ef347a89fca817e66defdedce178c7cc709b2116e75", + "a0c09bf2ba7208fda0f4bfe3d0f5b29a543012306d43831b5adc6fe7f8596fa308763db15468323b11cf6e4aeb8c18fe44678f44545a69bc", + "b46f1836aa287c0a5a5653f0ec5ef9e903f436e21c1570c29ad9e5f596da97eeaf17150ae30bcb3174d04bc2d712c8c7789d7cb4fda138f4", + "1c5bbecf4741dfaae79db72dface00eaaac502c2060934b6eaaeca6a20bd3da9e0be8777f7d02033d1b15884232281a41fc7f80eed04af5e", + "86ff0182d40f7f9edb7862515821bd67bfd6165a3c44de95d7df79b8779ccf6460e3c68b70c16aaa280f2d7b3f22d745b97a89906cfc476c", + "502bcb6842eb06f0e49032bae87c554c031d6d4d2d7694efbf9c468d48220c50f8ca28843364d70cee92d6fe246e61448f9db9808b3b2408", + "0c9810f1e2ebd389caa789374d78007974ef4d17227316f40e578b336827da3f6b482a4794eb6a3975b971b5e1388f52e91ea2f1bcb0f912", + "20d41d85a18d5657a29640321563bbd04c2ffbd0a37a7ba43a4f7d263ce26faf4e1f74f9f4b590c69229ae571fe37fa639b5b8eb48bd9a55", + "e6b4b8f408c7010d0601e7eda0c309a1a42720d6d06b5759fdc4e1efe22d076d6c44d42f508d67be462914d28b8edce32e7094305164af17", + "be88bbb86c59c13d8e9d09ab98105f69c2d1dd134dbcd3b0863658f53159db64c0e139d180f3c89b8296d0ae324419c06fa87fc7daaf34c1", + "a456f9369769e8f08902124a0314c7a06537a06e32411f4f93415950a17badfa7442b6217434a3a05ef45be5f10bd7b2ef8ea00c431edec5", + "186e452c4466aa4383b4c00210d52e7922dbf9771e8b47e229a9b7b73c8d10fd7ef0b6e41530f91f24a3ed9ab71fa38b98b2fe4746d51d68", + "4ae7fdcae9453f195a8ead5cbe1a7b9699673b52c40ab27927464887be53237f7f3a21b938d40d0ec9e15b1d5130b13ffed81373a53e2b43", + "841981c3bfeec3f60cfeca75d9d8dc17f46cf0106f2422b59aec580a58f342272e3a5e575a055ddb051390c54c24c6ecb1e0aceb075f6056" + ], + "invalid": [ + { + "description": "non-canonical field encodings", + "encodings": [ + "8e24f838059ee9fef1e209126defe53dcd74ef9b6304601c6966099effffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "86fcc7212bd4a0b980928666dc28c444a605ef38e09fb569e28d4443ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "866d54bd4c4ff41a55d4eefdbeca73cbd653c7bd3135b383708ec0bdffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "4a380ccdab9c86364a89e77a464d64f9157538cfdfa686adc0d5ece4ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "f22d9d4c945dd44d11e0b1d3d3d358d959b4844d83b08c44e659d79fffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "8cdffc681aa99e9c818c8ef4c3808b58e86acdef1ab68c8477af185bffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0e1c12ac7b5920effbd044e897c57634e2d05b5c27f8fa3df8a086a1ffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ] + }, + { + "description": "negative field elements", + "encodings": [ + "15141bd2121837ef71a0016bd11be757507221c26542244f23806f3fd3496b7d4c36826276f3bf5deea2c60c4fa4cec69946876da497e795", + "455d380238434ab740a56267f4f46b7d2eb2dd8ee905e51d7b0ae8a6cb2bae501e67df34ab21fa45946068c9f233939b1d9521a998b7cb93", + "810b1d8e8bf3a9c023294bbfd3d905a97531709bdc0f42390feedd7010f77e98686d400c9c86ed250ceecd9de0a18888ffecda0f4ea1c60d", + "d3af9cc41be0e5de83c0c6273bedcb9351970110044a9a41c7b9b2267cdb9d7bf4dc9c2fdb8bed32878184604f1d9944305a8df4274ce301", + "9312bcab09009e4330ff89c4bc1e9e000d863efc3c863d3b6c507a40fd2cdefde1bf0892b4b5ed9780b91ed1398fb4a7344c605aa5efda74", + "53d11bce9e62a29d63ed82ae93761bdd76e38c21e2822d6ebee5eb1c5b8a03eaf9df749e2490eda9d8ac27d1f71150de93668074d18d1c3a", + "697c1aed3cd8858515d4be8ac158b229fe184d79cb2b06e49210a6f3a7cd537bcd9bd390d96c4ab6a4406da5d93640726285370cfa95df80" + ] + }, + { + "description": "non-square x^2", + "encodings": [ + "58ad48715c9a102569b68b88362a4b0645781f5a19eb7e59c6a4686fd0f0750ff42e3d7af1ab38c29d69b670f31258919c9fdbf6093d06c0", + "8ca37ee2b15693f06e910cf43c4e32f1d5551dda8b1e48cb6ddd55e440dbc7b296b601919a4e4069f59239ca247ff693f7daa42f086122b1", + "982c0ec7f43d9f97c0a74b36db0abd9ca6bfb98123a90782787242c8a523cdc76df14a910d54471127e7662a1059201f902940cd39d57af5", + "baa9ab82d07ca282b968a911a6c3728d74bf2fe258901925787f03ee4be7e3cb6684fd1bcfe5071a9a974ad249a4aaa8ca81264216c68574", + "2ed9ffe2ded67a372b181ac524996402c42970629db03f5e8636cbaf6074b523d154a7a8c4472c4c353ab88cd6fec7da7780834cc5bd5242", + "f063769e4241e76d815800e4933a3a144327a30ec40758ad3723a788388399f7b3f5d45b6351eb8eddefda7d5bff4ee920d338a8b89d8b63", + "5a0104f1f55d152ceb68bc138182499891d90ee8f09b40038ccc1e07cb621fd462f781d045732a4f0bda73f0b2acf94355424ff0388d4b9c" + ] + } + ], + "oneway": [ + { + "input": "cbb8c991fd2f0b7e1913462d6463e4fd2ce4ccdd28274dc2ca1f4165d5ee6cdccea57be3416e166fd06718a31af45a2f8e987e301be59ae6673e963001dbbda80df47014a21a26d6c7eb4ebe0312aa6fffb8d1b26bc62ca40ed51f8057a635a02c2b8c83f48fa6a2d70f58a1185902c0", + "output": "0c709c9607dbb01c94513358745b7c23953d03b33e39c7234e268d1d6e24f34014ccbc2216b965dd231d5327e591dc3c0e8844ccfd568848" + }, + { + "input": "b6d8da654b13c3101d6634a231569e6b85961c3f4b460a08ac4a5857069576b64428676584baa45b97701be6d0b0ba18ac28d443403b45699ea0fbd1164f5893d39ad8f29e48e399aec5902508ea95e33bc1e9e4620489d684eb5c26bc1ad1e09aba61fabc2cdfee0b6b6862ffc8e55a", + "output": "76ab794e28ff1224c727fa1016bf7f1d329260b7218a39aea2fdb17d8bd9119017b093d641cedf74328c327184dc6f2a64bd90eddccfcdab" + }, + { + "input": "36a69976c3e5d74e4904776993cbac27d10f25f5626dd45c51d15dcf7b3e6a5446a6649ec912a56895d6baa9dc395ce9e34b868d9fb2c1fc72eb6495702ea4f446c9b7a188a4e0826b1506b0747a6709f37988ff1aeb5e3788d5076ccbb01a4bc6623c92ff147a1e21b29cc3fdd0e0f4", + "output": "c8d7ac384143500e50890a1c25d643343accce584caf2544f9249b2bf4a6921082be0e7f3669bb5ec24535e6c45621e1f6dec676edd8b664" + }, + { + "input": "d5938acbba432ecd5617c555a6a777734494f176259bff9dab844c81aadcf8f7abd1a9001d89c7008c1957272c1786a4293bb0ee7cb37cf3988e2513b14e1b75249a5343643d3c5e5545a0c1a2a4d3c685927c38bc5e5879d68745464e2589e000b31301f1dfb7471a4f1300d6fd0f99", + "output": "62beffc6b8ee11ccd79dbaac8f0252c750eb052b192f41eeecb12f2979713b563caf7d22588eca5e80995241ef963e7ad7cb7962f343a973" + }, + { + "input": "4dec58199a35f531a5f0a9f71a53376d7b4bdd6bbd2904234a8ea65bbacbce2a542291378157a8f4be7b6a092672a34d85e473b26ccfbd4cdc6739783dc3f4f6ee3537b7aed81df898c7ea0ae89a15b5559596c2a5eeacf8b2b362f3db2940e3798b63203cae77c4683ebaed71533e51", + "output": "f4ccb31d263731ab88bed634304956d2603174c66da38742053fa37dd902346c3862155d68db63be87439e3d68758ad7268e239d39c4fd3b" + }, + { + "input": "df2aa1536abb4acab26efa538ce07fd7bca921b13e17bc5ebcba7d1b6b733deda1d04c220f6b5ab35c61b6bcb15808251cab909a01465b8ae3fc770850c66246d5a9eae9e2877e0826e2b8dc1bc08009590bc6778a84e919fbd28e02a0f9c49b48dc689eb5d5d922dc01469968ee81b5", + "output": "7e79b00e8e0a76a67c0040f62713b8b8c6d6f05e9c6d02592e8a22ea896f5deacc7c7df5ed42beae6fedb9000285b482aa504e279fd49c32" + }, + { + "input": "e9fb440282e07145f1f7f5ecf3c273212cd3d26b836b41b02f108431488e5e84bd15f2418b3d92a3380dd66a374645c2a995976a015632d36a6c2189f202fc766e1c82f50ad9189be190a1f0e8f9b9e69c9c18cc98fdd885608f68bf0fdedd7b894081a63f70016a8abf04953affbefa", + "output": "20b171cb16be977f15e013b9752cf86c54c631c4fc8cbf7c03c4d3ac9b8e8640e7b0e9300b987fe0ab5044669314f6ed1650ae037db853f1" + } + ] +} diff --git a/internal/ted448/scalar_test.go b/internal/ted448/scalar_test.go deleted file mode 100644 index 4658a3db..00000000 --- a/internal/ted448/scalar_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package ted448_test - -import ( - "crypto/rand" - "math/big" - "testing" - - "github.com/cloudflare/circl/internal/conv" - "github.com/cloudflare/circl/internal/ted448" - "github.com/cloudflare/circl/internal/test" -) - -func TestReduceModOrder(t *testing.T) { - order := ted448.Order() - bigOrder := conv.BytesLe2BigInt(order[:]) - const max = 3*ted448.ScalarSize - 1 - var b [max]byte - _, _ = rand.Read(b[:]) - var z ted448.Scalar - for i := 0; i < max; i++ { - x := b[0:i] - bigX := conv.BytesLe2BigInt(x) - - z.FromBytes(x) - got := conv.BytesLe2BigInt(z[:]) - got.Mod(got, bigOrder) - - want := bigX.Mod(bigX, bigOrder) - - if got.Cmp(want) != 0 { - test.ReportError(t, got, want, x, i) - } - } -} - -func testOp(t *testing.T, - f func(z, x, y *ted448.Scalar), - g func(z, x, y *big.Int)) { - const testTimes = 1 << 8 - var x, y, z ted448.Scalar - order := ted448.Order() - want := new(big.Int) - bigOrder := conv.BytesLe2BigInt(order[:]) - - for i := 0; i < testTimes; i++ { - _, _ = rand.Read(x[:]) - _, _ = rand.Read(y[:]) - bigX := conv.BytesLe2BigInt(x[:]) - bigY := conv.BytesLe2BigInt(y[:]) - - f(&z, &x, &y) - got := conv.BytesLe2BigInt(z[:]) - - g(want, bigX, bigY) - want.Mod(want, bigOrder) - if got.Cmp(want) != 0 { - test.ReportError(t, got.Text(16), want.Text(16), - conv.BytesLe2Hex(x[:]), - conv.BytesLe2Hex(y[:])) - } - } -} - -func TestScalar(t *testing.T) { - t.Run("Add", func(t *testing.T) { - testOp(t, - func(z, x, y *ted448.Scalar) { z.Add(x, y) }, - func(z, x, y *big.Int) { z.Add(x, y) }) - }) - t.Run("Sub", func(t *testing.T) { - testOp(t, - func(z, x, y *ted448.Scalar) { z.Sub(x, y) }, - func(z, x, y *big.Int) { z.Sub(x, y) }) - }) - t.Run("Mul", func(t *testing.T) { - testOp(t, - func(z, x, y *ted448.Scalar) { z.Mul(x, y) }, - func(z, x, y *big.Int) { z.Mul(x, y) }) - }) - t.Run("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 * 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) - } - }) - b.Run("Sub", func(b *testing.B) { - for i := 0; i < b.N; i++ { - z.Sub(&x, &y) - } - }) - b.Run("Mul", func(b *testing.B) { - for i := 0; i < b.N; i++ { - z.Mul(&x, &y) - } - }) - b.Run("Red", func(b *testing.B) { - for i := 0; i < b.N; i++ { - z.FromBytes(k[:]) - } - }) - b.Run("Inv", func(b *testing.B) { - for i := 0; i < b.N; i++ { - z.Inv(&x) - } - }) -} diff --git a/sign/ed25519/ed25519_test.go b/sign/ed25519/ed25519_test.go index 4d8596a1..13977873 100644 --- a/sign/ed25519/ed25519_test.go +++ b/sign/ed25519/ed25519_test.go @@ -1,7 +1,6 @@ package ed25519_test import ( - "crypto/rand" "testing" "github.com/cloudflare/circl/sign/ed25519" @@ -59,8 +58,9 @@ func TestPublic(t *testing.T) { } func BenchmarkKeyGeneration(b *testing.B) { + var zero zeroReader for i := 0; i < b.N; i++ { - if _, _, err := ed25519.GenerateKey(rand.Reader); err != nil { + if _, _, err := ed25519.GenerateKey(zero); err != nil { b.Fatal(err) } } @@ -75,7 +75,8 @@ func BenchmarkNewKeyFromSeed(b *testing.B) { } func BenchmarkSigning(b *testing.B) { - _, priv, err := ed25519.GenerateKey(rand.Reader) + var zero zeroReader + _, priv, err := ed25519.GenerateKey(zero) if err != nil { b.Fatal(err) } @@ -88,7 +89,8 @@ func BenchmarkSigning(b *testing.B) { } func BenchmarkVerification(b *testing.B) { - pub, priv, err := ed25519.GenerateKey(rand.Reader) + var zero zeroReader + pub, priv, err := ed25519.GenerateKey(zero) if err != nil { b.Fatal(err) } diff --git a/sign/ed25519/extra_test.go b/sign/ed25519/extra_test.go index 85db8a69..cd1a42bd 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 a8e0a64a..3088d264 100644 --- a/sign/ed448/ed448.go +++ b/sign/ed448/ed448.go @@ -245,10 +245,10 @@ func signAll(signature []byte, privateKey PrivateKey, message, ctx []byte, preHa // 3. Compute the point [r]B. r := &goldilocks.Scalar{} - r.FromBytes(rPM[:]) + r.FromBytesLE(rPM[:]) var R goldilocks.Point - var encR [goldilocks.EncodingSize]byte R.ScalarBaseMult(r) + var encR [goldilocks.EncodingSize]byte if err := R.Encode(&encR); err != nil { panic(err) } @@ -263,14 +263,15 @@ func signAll(signature []byte, privateKey PrivateKey, message, ctx []byte, preHa // 5. Compute S = (r + k * s) mod order. k := &goldilocks.Scalar{} - k.FromBytes(hRAM[:]) + k.FromBytesLE(hRAM[:]) S := &goldilocks.Scalar{} S.Mul(k, s) S.Add(S, r) + encS := S.ToBytesLE() // 6. The signature is the concatenation of R and S. copy(signature[:paramB], encR[:]) - copy(signature[paramB:], S[:]) + copy(signature[paramB:], encS[:]) } // Sign signs the message with privateKey and returns a signature. @@ -324,28 +325,28 @@ func verify(public PublicKey, message, signature, ctx []byte, preHash bool) bool } var hRAM [hashSize]byte - R := signature[:paramB] + sigR := signature[:paramB] writeDom(&H, ctx, preHash) - _, _ = H.Write(R) + _, _ = H.Write(sigR) _, _ = H.Write(public) _, _ = H.Write(PHM) _, _ = H.Read(hRAM[:]) k := &goldilocks.Scalar{} - k.FromBytes(hRAM[:]) + k.FromBytesLE(hRAM[:]) S := &goldilocks.Scalar{} - S.FromBytes(signature[paramB:]) + S.FromBytesLE(signature[paramB:]) P.Neg() - var Q goldilocks.Point - Q.CombinedMult(S, k, P) + var R goldilocks.Point + R.CombinedMult(S, k, P) var encR [goldilocks.EncodingSize]byte - if err = Q.Encode(&encR); err != nil { + if err = R.Encode(&encR); err != nil { panic(err) } - return bytes.Equal(R, encR[:]) + return bytes.Equal(sigR, encR[:]) } // VerifyAny returns true if the signature is valid. Failure cases are invalid @@ -395,7 +396,7 @@ func deriveSecretScalar(s *goldilocks.Scalar, h []byte) { h[0] &= 0xFC // The two least significant bits of the first octet are cleared, h[paramB-1] = 0x00 // all eight bits the last octet are cleared, and h[paramB-2] |= 0x80 // the highest bit of the second to last octet is set. - s.FromBytes(h[:paramB]) + s.FromBytesLE(h[:paramB]) } // isLessThanOrder returns true if 0 <= x < order and if the last byte of x is zero.