From 4d11c7b5a69c8a045beeafc24feb90e540542632 Mon Sep 17 00:00:00 2001 From: George Tankersley Date: Mon, 26 Jun 2017 02:12:45 -0400 Subject: [PATCH] group logic WIP --- const.go | 17 +- ed25519.go | 220 +++--------- ed25519_test.go | 685 +++++++++++++++++--------------------- internal/group/ge.go | 209 ++++++++++++ internal/radix51/const.go | 4 + internal/radix51/fe.go | 40 ++- 6 files changed, 603 insertions(+), 572 deletions(-) create mode 100644 internal/group/ge.go diff --git a/const.go b/const.go index ab0e1d0..48b0745 100644 --- a/const.go +++ b/const.go @@ -6,21 +6,6 @@ package ed25519 import ( "math/big" - - "github.com/gtank/ed25519/internal/edwards25519" ) -var ( - // d2 is 2*d, a curve-specific constant used in our addition formula. - d2 = edwards25519.FieldElement{ - -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199, - } - - // the number 2 as a field element - feTwo = edwards25519.FieldElement{ - 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, - } - - bigZero = big.NewInt(0) - bigOne = big.NewInt(1) -) +var () diff --git a/ed25519.go b/ed25519.go index ca3d7ec..03e1586 100644 --- a/ed25519.go +++ b/ed25519.go @@ -9,9 +9,13 @@ import ( "math/big" "sync" - "github.com/gtank/ed25519/internal/edwards25519" + "github.com/gtank/ed25519/internal/group" + field "github.com/gtank/ed25519/internal/radix51" ) +var bigZero *big.Int +var bigOne *big.Int + type ed25519Curve struct { *elliptic.CurveParams } @@ -31,6 +35,8 @@ func initEd25519Params() { ed25519Params.Gx, _ = new(big.Int).SetString("15112221349535400772501151409588531511454012693041857206046113283949847762202", 10) ed25519Params.Gy, _ = new(big.Int).SetString("46316835694926478169428394003475163141307993866256225615783033603165251855960", 10) ed25519Params.BitSize = 256 + bigZero = big.NewInt(0) + bigOne = big.NewInt(1) } // Ed25519 returns a Curve that implements Ed25519. @@ -45,98 +51,44 @@ func (curve ed25519Curve) Params() *elliptic.CurveParams { } // IsOnCurve reports whether the given (x,y) lies on the curve by checking that -// -x^2 + y^2 - 1 - dx^2y^2 = 0. +// -x^2 + y^2 - 1 - dx^2y^2 = 0 (mod p). func (curve ed25519Curve) IsOnCurve(x, y *big.Int) bool { - lh := new(big.Int).Mul(x, x) // x^2 - y2 := new(big.Int).Mul(y, y) // y^2 - rh := new(big.Int).Mul(lh, y2) // x^2y^2 - rh.Mul(rh, curve.B) // dx^2y^2 with B repurposed as d - rh.Add(rh, bigOne) // 1 + dx^2y^2 - lh.Neg(lh) // -x^2 - lh.Add(lh, y2) // -x^2 + y^2 - lh.Sub(lh, rh) // -x^2 + y^2 - 1 - dx^2y^2 - lh.Mod(lh, curve.P) // -x^2 + y^2 - 1 - dx^2y^2 mod p - return lh.Cmp(bigZero) == 0 + var feX, feY, feB field.FieldElement + field.FeFromBig(&feX, x) + field.FeFromBig(&feY, y) + field.FeFromBig(&feB, curve.B) + + var lh, y2, rh field.FieldElement + field.FeSquare(&lh, &feX) // x^2 + field.FeSquare(&y2, &feY) // y^2 + field.FeMul(&rh, &lh, &y2) // x^2*y^2 + field.FeMul(&rh, &rh, &feB) // d*x^2*y^2 + field.FeAdd(&rh, &rh, &field.FieldOne) // 1 + d*x^2*y^2 + field.FeNeg(&lh, &lh) // -x^2 + field.FeAdd(&lh, &lh, &y2) // -x^2 + y^2 + field.FeSub(&lh, &lh, &rh) // -x^2 + y^2 - 1 - dx^2y^2 + field.FeReduce(&lh, &lh) // mod p + + return field.FeEqual(&lh, &field.FieldZero) } // Add returns the sum of (x1, y1) and (x2, y2). func (curve ed25519Curve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) { - var p1, p2, p3 edwards25519.ExtendedGroupElement + var p1, p2 group.ExtendedGroupElement - affineToExtended(&p1, x1, y1) - affineToExtended(&p2, x2, y2) - extendedAdd(&p3, &p1, &p2) + p1.FromAffine(x1, y1) + p2.FromAffine(x2, y2) - return extendedToAffine(&p3) // 1I + 2M + return p2.Add(&p1, &p2).ToAffine() } -// The internal edwards25519 package optimizes for a particular sequence of -// operations, so we reimplement addition (add-2008-hwcd-3) here to avoid the -// cost of converting between intermediate representations. -// TODO We know Z1=1 and Z2=1 here, so mmadd-2008-hwcd-3 (6M + 1S + 1*k + 9add) could apply -func extendedAdd(out, p1, p2 *edwards25519.ExtendedGroupElement) { - var tmp1, tmp2, A, B, C, D, E, F, G, H edwards25519.FieldElement - edwards25519.FeSub(&tmp1, &p1.Y, &p1.X) // tmp1 <-- Y1-X1 - edwards25519.FeSub(&tmp2, &p2.Y, &p2.X) // tmp2 <-- Y2-X2 - edwards25519.FeMul(&A, &tmp1, &tmp2) // A <-- tmp1*tmp2 = (Y1-X1)*(Y2-X2) - edwards25519.FeAdd(&tmp1, &p1.Y, &p1.X) // tmp1 <-- Y1+X1 - edwards25519.FeAdd(&tmp2, &p2.Y, &p2.X) // tmp2 <-- Y2+X2 - edwards25519.FeMul(&B, &tmp1, &tmp2) // B <-- tmp1*tmp2 = (Y1+X1)*(Y2+X2) - edwards25519.FeMul(&tmp1, &p1.T, &p2.T) // tmp1 <-- T1*T2 - edwards25519.FeMul(&C, &tmp1, &d2) // C <-- tmp1*2d = T1*2d*T2 - edwards25519.FeMul(&tmp1, &p1.Z, &p2.Z) // tmp1 <-- Z1*Z2 - edwards25519.FeAdd(&D, &tmp1, &tmp1) // D <-- tmp1 + tmp1 = 2*Z1*Z2 - edwards25519.FeSub(&E, &B, &A) // E <-- B-A - edwards25519.FeSub(&F, &D, &C) // F <-- D-C - edwards25519.FeAdd(&G, &D, &C) // G <-- D+C - edwards25519.FeAdd(&H, &B, &A) // H <-- B+A - edwards25519.FeMul(&out.X, &E, &F) // X3 <-- E*F - edwards25519.FeMul(&out.Y, &G, &H) // Y3 <-- G*H - edwards25519.FeMul(&out.T, &E, &H) // T3 <-- E*H - edwards25519.FeMul(&out.Z, &F, &G) // Z3 <-- F*G -} - -// Double returns 2*(x,y). Because we are always converting from affine, we can -// use "mdbl-2008-bbjlp" which assumes Z1=1. Reusing some intermediate -// values, the cost is 3S + 2M + 9*add + 1*a. +// Double returns 2*(x,y). func (curve ed25519Curve) Double(x1, y1 *big.Int) (x, y *big.Int) { - var p, q edwards25519.ProjectiveGroupElement - var t0, t1 edwards25519.FieldElement + var p group.ProjectiveGroupElement - affineToProjective(&p, x1, y1) + p.FromAffine(x1, y1) - // C = X1^2, D = Y1^2 - edwards25519.FeSquare(&t0, &p.X) - edwards25519.FeSquare(&t1, &p.Y) - edwards25519.FeMul(&p.Z, &p.X, &p.Y) - - // B = (X1+Y1)^2 = X1^2 + Y1^2 + 2*(X*Y) - edwards25519.FeAdd(&q.X, &t0, &t1) - edwards25519.FeAdd(&q.X, &q.X, &p.Z) - edwards25519.FeAdd(&q.X, &q.X, &p.Z) - - // E = a*C where a = -1 - edwards25519.FeNeg(&q.Z, &t0) - - // F = E + D - edwards25519.FeAdd(&p.X, &q.Z, &t1) - - // X3 = (B-C-D)*(F-2) - edwards25519.FeSub(&p.Y, &q.X, &t0) - edwards25519.FeSub(&p.Y, &p.Y, &t1) - edwards25519.FeSub(&p.Z, &p.X, &feTwo) - edwards25519.FeMul(&q.X, &p.Y, &p.Z) - - // Y3 = F*(E-D) - edwards25519.FeSub(&p.Y, &q.Z, &t1) - edwards25519.FeMul(&q.Y, &p.X, &p.Y) - - // Z3 = F^2 - 2*F - edwards25519.FeSquare(&q.Z, &p.X) - edwards25519.FeSub(&q.Z, &q.Z, &p.X) - edwards25519.FeSub(&q.Z, &q.Z, &p.X) - - return projectiveToAffine(&q) + return p.Double().ToAffine() } // ScalarMult returns k*(Bx,By) where k is a number in big-endian form. @@ -148,8 +100,7 @@ func (curve ed25519Curve) ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int) return } - var r0, r1 edwards25519.ExtendedGroupElement - var h edwards25519.CompletedGroupElement + var r0, r1 group.ExtendedGroupElement var s [32]byte curve.scalarFromBytes(&s, k) @@ -157,7 +108,7 @@ func (curve ed25519Curve) ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int) // Montgomery ladder init: // R_0 = O, R_1 = P r0.Zero() - affineToExtended(&r1, x1, y1) + r1.FromAffine(x1, y1) // Montgomery ladder step: // R_{1-b} = R_{1-b} + R_{b} @@ -165,17 +116,15 @@ func (curve ed25519Curve) ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int) for i := 255; i >= 0; i-- { var b = int32((s[i/8] >> uint(i&7)) & 1) if b == 0 { - extendedAdd(&r1, &r0, &r1) - r0.Double(&h) - h.ToExtended(&r0) + r1.Add(&r0, &r1) + r0.Double() } else { - extendedAdd(&r0, &r0, &r1) - r1.Double(&h) - h.ToExtended(&r1) + r0.Add(&r0, &r1) + r1.Double() } } - return extendedToAffine(&r0) + return r0.ToAffine() } // scalarFromBytes converts a big-endian value to a fixed-size little-endian @@ -194,84 +143,13 @@ func (curve ed25519Curve) scalarFromBytes(out *[32]byte, in []byte) { } } -// ScalarBaseMult returns k*G, where G is the base point of the group and k is -// an integer in big-endian form. -func (curve ed25519Curve) ScalarBaseMult(k []byte) (x, y *big.Int) { - var p edwards25519.ExtendedGroupElement - var scBytes [32]byte +// // ScalarBaseMult returns k*G, where G is the base point of the group and k is +// // an integer in big-endian form. +// func (curve ed25519Curve) ScalarBaseMult(k []byte) (x, y *big.Int) { +// var p edwards25519.ExtendedGroupElement +// var scBytes [32]byte - curve.scalarFromBytes(&scBytes, k) - edwards25519.GeScalarMultBase(&p, &scBytes) - return extendedToAffine(&p) -} - -// Converts (x,y) to (X:Y:T:Z) extended coordinates, or "P3" in ref10. As -// described in "Twisted Edwards Curves Revisited", Hisil-Wong-Carter-Dawson -// 2008, Section 3.1 (https://eprint.iacr.org/2008/522.pdf) -// See also https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3 -func affineToExtended(out *edwards25519.ExtendedGroupElement, x, y *big.Int) { - feFromBig(&out.X, x) - feFromBig(&out.Y, y) - edwards25519.FeMul(&out.T, &out.X, &out.Y) - edwards25519.FeOne(&out.Z) -} - -// Extended coordinates are XYZT with x = X/Z, y = Y/Z, or the "P3" -// representation in ref10. Extended->affine is the same operation as moving -// from projective to affine. Per HWCD, it is safe to move from extended to -// projective by simply ignoring T. -func extendedToAffine(in *edwards25519.ExtendedGroupElement) (*big.Int, *big.Int) { - var x, y, zinv edwards25519.FieldElement - var bigX, bigY = new(big.Int), new(big.Int) - - edwards25519.FeInvert(&zinv, &in.Z) - edwards25519.FeMul(&x, &in.X, &zinv) - edwards25519.FeMul(&y, &in.Y, &zinv) - - feToBig(bigX, &x) - feToBig(bigY, &y) - - return bigX, bigY -} - -// Projective coordinates are XYZ with x = X/Z, y = Y/Z, or the "P2" representation in ref10. -func affineToProjective(out *edwards25519.ProjectiveGroupElement, x, y *big.Int) { - feFromBig(&out.X, x) - feFromBig(&out.Y, y) - edwards25519.FeOne(&out.Z) -} - -func projectiveToAffine(in *edwards25519.ProjectiveGroupElement) (*big.Int, *big.Int) { - var x, y, zinv edwards25519.FieldElement - var bigX, bigY = new(big.Int), new(big.Int) - - edwards25519.FeInvert(&zinv, &in.Z) - edwards25519.FeMul(&x, &in.X, &zinv) - edwards25519.FeMul(&y, &in.Y, &zinv) - - feToBig(bigX, &x) - feToBig(bigY, &y) - - return bigX, bigY -} - -func feFromBig(h *edwards25519.FieldElement, in *big.Int) { - tmp := new(big.Int).Mod(in, ed25519.P) - tmpBytes := tmp.Bytes() - var buf, reverse [32]byte - copy(buf[32-len(tmpBytes):], tmpBytes) - for i := 0; i < 32; i++ { - reverse[i] = buf[31-i] - } - edwards25519.FeFromBytes(h, &reverse) -} - -func feToBig(out *big.Int, h *edwards25519.FieldElement) { - var buf, reverse [32]byte - edwards25519.FeToBytes(&buf, h) - for i := 0; i < 32; i++ { - reverse[i] = buf[31-i] - } - out.SetBytes(reverse[:]) - out.Mod(out, ed25519.P) -} +// curve.scalarFromBytes(&scBytes, k) +// edwards25519.GeScalarMultBase(&p, &scBytes) +// return extendedToAffine(&p) +// } diff --git a/ed25519_test.go b/ed25519_test.go index 0702e5b..dd3f590 100644 --- a/ed25519_test.go +++ b/ed25519_test.go @@ -5,93 +5,20 @@ package ed25519 import ( - "bytes" - "crypto/elliptic" "crypto/rand" - "crypto/sha512" - "encoding/hex" - "io" "math/big" "testing" - "github.com/gtank/ed25519/internal/edwards25519" - "github.com/gtank/ed25519/internal/radix51" + field "github.com/gtank/ed25519/internal/radix51" ) -// TEST MATH - -// d is a constant from the curve equation. it's easy to find the value in multiple formats. -var d_fe = edwards25519.FieldElement{ - -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116, -} -var d_bn, _ = new(big.Int).SetString("37095705934669439343138083508754565189542113879843219016388785533085940283555", 10) - -// 2*d -var d2_fe = edwards25519.FieldElement{ - -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199, -} -var d2_bn = new(big.Int).Mul(big.NewInt(2), d_bn) - -// A is a constant from the Montgomery form of curve25519. -var A_fe = edwards25519.FieldElement{ - 486662, 0, 0, 0, 0, 0, 0, 0, 0, 0, -} -var A_bn, _ = new(big.Int).SetString("486662", 10) - -var feConvTable = []struct { - name string - fe edwards25519.FieldElement - bn *big.Int -}{ - { - name: "d", - fe: d_fe, - bn: d_bn, - }, - { - name: "d2", - fe: d2_fe, - bn: d2_bn, - }, - { - name: "A", - fe: A_fe, - bn: A_bn, - }, -} - -func TestFeFromBig(t *testing.T) { - var _ = Ed25519().(ed25519Curve) - var fe edwards25519.FieldElement - for _, c := range feConvTable { - feFromBig(&fe, c.bn) - for i := 0; i < len(fe); i++ { - if fe[i] != c.fe[i] { - t.Error("bn->fe radix conversion failed") - } - } - } -} - -func TestFeToBig(t *testing.T) { - var curve = Ed25519().(ed25519Curve) - var bn, expectedBN = new(big.Int), new(big.Int) - for _, c := range feConvTable { - expectedBN.Mod(c.bn, curve.P) - feToBig(bn, &c.fe) - if bn.Cmp(expectedBN) != 0 { - t.Errorf("fe->bn radix conversion failed: %s", c.name) - } - } -} - func TestRadixRoundTrip(t *testing.T) { if testing.Short() { t.Skip() } var curve = Ed25519().(ed25519Curve) - var fe edwards25519.FieldElement + var fe field.FieldElement var out = new(big.Int) for i := 0; i < 1000; i++ { @@ -99,8 +26,8 @@ func TestRadixRoundTrip(t *testing.T) { if err != nil { t.Fatal(err) } - feFromBig(&fe, n) - feToBig(out, &fe) + field.FeFromBig(&fe, n) + out = field.FeToBig(&fe) if n.Cmp(out) != 0 { t.Errorf("fe<>bn failed for %x", n.Bytes()) } @@ -119,6 +46,16 @@ func TestIsOnCurve(t *testing.T) { } } +func BenchmarkIsOnCurve(b *testing.B) { + ed := Ed25519() + Gx, Gy := ed.Params().Gx, ed.Params().Gy + for i := 0; i < b.N; i++ { + if !ed.IsOnCurve(Gx, Gy) { + b.Error("ed25519 base point not on curve") + } + } +} + func TestAdd(t *testing.T) { c := Ed25519().(ed25519Curve) Bx, By := c.Params().Gx, c.Params().Gy @@ -129,6 +66,14 @@ func TestAdd(t *testing.T) { } } +func BenchmarkAdd(b *testing.B) { + c := Ed25519() + Gx, Gy := c.Params().Gx, c.Params().Gy + for i := 0; i < b.N; i++ { + Gx, Gy = c.Add(Gx, Gy, Gx, Gy) + } +} + func TestDouble(t *testing.T) { c := Ed25519() Gx, Gy := c.Params().Gx, c.Params().Gy @@ -148,321 +93,303 @@ func TestDouble(t *testing.T) { } } -// Test vector generated by instrumenting x/crypto/ed25519 GenerateKey -// seed: c240344fcc6615dda52da98149377ad2b13fdba2bc39a50ba9f3afb2cbd4abaa -// expanded: f04154b9d80963bb4c76214ece8a1049bdd16fbfc5003aff9835a59643ace276 -// public: 65a8343a83ec15e55050f12fc22f2c81a4fe7327c8da1524441f9ce5e5bc27dd - -func TestScalarMultBase(t *testing.T) { - c := Ed25519() - - // endian-swapped because x/crypto assumes this is always little endian from raw bytes - // and scalarFromBytes assumes it's coming from big.Int Bytes() - a, _ := hex.DecodeString("76e2ac4396a53598ff3a00c5bf6fd1bd49108ace4e21764cbb6309d8b95441f0") - if len(a) != 32 { - t.Errorf("failed decoding") - } - - Ax, Ay := c.ScalarBaseMult(a) - - if !c.IsOnCurve(Ax, Ay) { - t.Error("scalarmultbase result was off-curve") - } - - var A edwards25519.ExtendedGroupElement - pub, _ := hex.DecodeString("65a8343a83ec15e55050f12fc22f2c81a4fe7327c8da1524441f9ce5e5bc27dd") - var pubBytes [32]byte - copy(pubBytes[:], pub) - A.FromBytes(&pubBytes) - Bx, By := extendedToAffine(&A) - - if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 { - t.Error("scalarmultbase disagrees with x/crypto/ed25519") - } -} - -func TestScalarMultBaseIdentity(t *testing.T) { - var c = Ed25519() - var one = new(big.Int).Set(bigOne) - Ax, Ay := c.ScalarBaseMult(one.Bytes()) - - if Ax.Cmp(c.Params().Gx) != 0 || Ay.Cmp(c.Params().Gy) != 0 { - t.Errorf("precomputed 1*B != B") - } - - Ax, Ay = c.ScalarMult(Ax, Ay, one.Bytes()) - - if Ax.Cmp(c.Params().Gx) != 0 || Ay.Cmp(c.Params().Gy) != 0 { - t.Errorf("arbitrary 1*B != B") - } -} - -func TestScalarMultBaseInfinity(t *testing.T) { - c := Ed25519() - a, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") - if len(a) != 32 { - t.Errorf("failed decoding") - } - - Ax, Ay := c.ScalarBaseMult(a) - - if !c.IsOnCurve(Ax, Ay) { - t.Error("scalarmultbase result was off-curve") - } - - if Ax.Cmp(bigZero) != 0 || Ay.Cmp(bigOne) != 0 { - t.Error("scalarmultbase by 0 was not point at infinity") - } -} - -func TestScalarMultsAgreeAtInfinity(t *testing.T) { - c := Ed25519() - a, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") - if len(a) != 32 { - t.Errorf("failed decoding") - } - - Ax, Ay := c.ScalarBaseMult(a) - Bx, By := c.ScalarMult(c.Params().Gx, c.Params().Gy, a) - - if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 { - t.Error("scalarmultbase disagrees with scalarmult") - } -} - -func TestScalarMultsAgreeElsewhere(t *testing.T) { - c := Ed25519() - // head -c 32 /dev/urandom | sha256sum - a, _ := hex.DecodeString("c07eea55b3322f15099b6cf4d2b7e99d3d0fa6807f6fc7a46b5f7cb78daad4e0") - - Ax, Ay := c.ScalarBaseMult(a) - Bx, By := c.ScalarMult(c.Params().Gx, c.Params().Gy, a) - - if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 { - t.Error("scalarmultbase disagrees with scalarmult") - } - - if !c.IsOnCurve(Bx, By) { - t.Error("scalarmult is returning off-curve points") - } -} - -// TEST INTERFACE - -func TestMarshalingRoundTrip(t *testing.T) { - ed := Ed25519() - - a, _ := hex.DecodeString("c07eea55b3322f15099b6cf4d2b7e99d3d0fa6807f6fc7a46b5f7cb78daad4e0") - Ax, Ay := ed.ScalarBaseMult(a) - - if !ed.IsOnCurve(Ax, Ay) { - t.Error("scalarBaseMult is returning off-curve points") - } - - sec1A := elliptic.Marshal(ed, Ax, Ay) - Bx, By := elliptic.Unmarshal(ed, sec1A) - - if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 { - t.Error("point did not survive elliptic.Marshal roundtrip") - } - - if !testing.Short() { - for i := 0; i < 100; i++ { - _, err := io.ReadFull(rand.Reader, a) - if err != nil { - t.Fatal(err) - } - Ax, Ay := ed.ScalarBaseMult(a) - - if !ed.IsOnCurve(Ax, Ay) { - t.Error("scalarBaseMult is returning off-curve points") - } - - sec1A := elliptic.Marshal(ed, Ax, Ay) - Bx, By := elliptic.Unmarshal(ed, sec1A) - - if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 { - t.Error("point did not survive elliptic.Marshal roundtrip") - } - } - } -} - -// TEST APPLICATION - -func generateKey(r io.Reader) (sk *[32]byte, pk []byte, err error) { - if r == nil { - r = rand.Reader - } - - ed := Ed25519() - - sk = new([32]byte) - _, err = io.ReadFull(r, sk[:]) - if err != nil { - return nil, nil, err - } - - digest := sha512.Sum512(sk[:]) - digest[0] &= 248 - digest[31] &= 127 - digest[31] |= 64 - - // take the first half of expanded bytes & reverse; big.Int expects big-endian - reverseDigest := reverse32(digest[:32]) - scalar := new(big.Int).SetBytes(reverseDigest) - - // A = a*B - Ax, Ay := ed.ScalarBaseMult(scalar.Bytes()) - - // This works with the standard elliptic package marshaling - publicKey := elliptic.Marshal(ed, Ax, Ay) - - // but we can also render the compressed Edwards format - compressedEdwardsY := make([]byte, 32) - - // x, y here will be big-endian byte strings - x, y := publicKey[1:33], publicKey[33:] - - // RFC 8032: To form the encoding of the point, copy the least significant - // bit of the x-coordinate to the most significant bit of the final octet. - copy(compressedEdwardsY[:], y) - compressedEdwardsY[0] |= x[31] << 7 - compressedEdwardsY = reverse32(compressedEdwardsY) - - return sk, compressedEdwardsY, err -} - -func reverse32(b []byte) []byte { - var tmp = make([]byte, 32) - for i := 0; i <= 31; i++ { - tmp[i] = b[31-i] - } - return tmp -} - -// Test vector generated by instrumenting x/crypto/ed25519 GenerateKey(). These -// are raw values. The edwards code interprets them as little-endian, so they -// need to be reversed before use with big.Int. -var genKeyTest = struct { - seed, expanded, public string -}{ - seed: "c240344fcc6615dda52da98149377ad2b13fdba2bc39a50ba9f3afb2cbd4abaa", - expanded: "f04154b9d80963bb4c76214ece8a1049bdd16fbfc5003aff9835a59643ace276", - public: "65a8343a83ec15e55050f12fc22f2c81a4fe7327c8da1524441f9ce5e5bc27dd", -} - -func TestEdDSAGenerateKey(t *testing.T) { - fakeRandom, _ := hex.DecodeString(genKeyTest.seed) - fakeReader := bytes.NewBuffer(fakeRandom) - - sk, pk, err := generateKey(fakeReader) - if err != nil { - t.Fatal(err) - } - - expectedPK, _ := hex.DecodeString(genKeyTest.public) - if !bytes.Equal(sk[:], fakeRandom) || !bytes.Equal(pk, expectedPK) { - t.Error("generateKey output did not match test vector") - } -} - -// BENCHMARKS - -func BenchmarkIsOnCurve(b *testing.B) { - ed := Ed25519() - Gx, Gy := ed.Params().Gx, ed.Params().Gy - for i := 0; i < b.N; i++ { - if !ed.IsOnCurve(Gx, Gy) { - b.Error("ed25519 base point not on curve") - } - } -} - -func BenchmarkAdd(b *testing.B) { - ed := Ed25519() - Gx, Gy := ed.Params().Gx, ed.Params().Gy - for i := 0; i < b.N; i++ { - Gx, Gy = ed.Add(Gx, Gy, Gx, Gy) - } -} - func BenchmarkDouble(b *testing.B) { - ed := Ed25519() - Gx, Gy := ed.Params().Gx, ed.Params().Gy + c := Ed25519() + Gx, Gy := c.Params().Gx, c.Params().Gy for i := 0; i < b.N; i++ { - Gx, Gy = ed.Double(Gx, Gy) + Gx, Gy = c.Double(Gx, Gy) } } -func BenchmarkScalarBaseMult(b *testing.B) { - ed := Ed25519() +// // Test vector generated by instrumenting x/crypto/ed25519 GenerateKey +// // seed: c240344fcc6615dda52da98149377ad2b13fdba2bc39a50ba9f3afb2cbd4abaa +// // expanded: f04154b9d80963bb4c76214ece8a1049bdd16fbfc5003aff9835a59643ace276 +// // public: 65a8343a83ec15e55050f12fc22f2c81a4fe7327c8da1524441f9ce5e5bc27dd - var k [32]byte - _, err := io.ReadFull(rand.Reader, k[:]) - if err != nil { - b.Fatal(err) - } - k[0] &= 248 - k[31] &= 127 - k[31] |= 64 +// func TestScalarMultBase(t *testing.T) { +// c := Ed25519() - for i := 0; i < b.N; i++ { - _, _ = ed.ScalarBaseMult(k[:]) - } -} +// // endian-swapped because x/crypto assumes this is always little endian from raw bytes +// // and scalarFromBytes assumes it's coming from big.Int Bytes() +// a, _ := hex.DecodeString("76e2ac4396a53598ff3a00c5bf6fd1bd49108ace4e21764cbb6309d8b95441f0") +// if len(a) != 32 { +// t.Errorf("failed decoding") +// } -func BenchmarkScalarMult(b *testing.B) { - ed := Ed25519() - Bx, By := ed.Params().Gx, ed.Params().Gy +// Ax, Ay := c.ScalarBaseMult(a) - var k [32]byte - _, err := io.ReadFull(rand.Reader, k[:]) - if err != nil { - b.Fatal(err) - } - k[0] &= 248 - k[31] &= 127 - k[31] |= 64 +// if !c.IsOnCurve(Ax, Ay) { +// t.Error("scalarmultbase result was off-curve") +// } - for i := 0; i < b.N; i++ { - _, _ = ed.ScalarMult(Bx, By, k[:]) - } -} +// var A edwards25519.ExtendedGroupElement +// pub, _ := hex.DecodeString("65a8343a83ec15e55050f12fc22f2c81a4fe7327c8da1524441f9ce5e5bc27dd") +// var pubBytes [32]byte +// copy(pubBytes[:], pub) +// A.FromBytes(&pubBytes) +// Bx, By := extendedToAffine(&A) -// A is a constant from the Montgomery form of curve25519. -var radix25A = edwards25519.FieldElement{ - 486662, 0, 0, 0, 0, 0, 0, 0, 0, 0, -} +// if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 { +// t.Error("scalarmultbase disagrees with x/crypto/ed25519") +// } +// } -var radix51A = radix51.FieldElement{ - 486662, 0, 0, 0, 0, -} +// func TestScalarMultBaseIdentity(t *testing.T) { +// var c = Ed25519() +// var one = new(big.Int).Set(bigOne) +// Ax, Ay := c.ScalarBaseMult(one.Bytes()) -func BenchmarkFeMul25(b *testing.B) { - var h edwards25519.FieldElement - for i := 0; i < b.N; i++ { - edwards25519.FeMul(&h, &radix25A, &radix25A) - } -} +// if Ax.Cmp(c.Params().Gx) != 0 || Ay.Cmp(c.Params().Gy) != 0 { +// t.Errorf("precomputed 1*B != B") +// } -func BenchmarkFeMul51(b *testing.B) { - var h radix51.FieldElement - for i := 0; i < b.N; i++ { - radix51.FeMul(&h, &radix51A, &radix51A) - } -} +// Ax, Ay = c.ScalarMult(Ax, Ay, one.Bytes()) -func BenchmarkFeSquare25(b *testing.B) { - var h edwards25519.FieldElement - for i := 0; i < b.N; i++ { - edwards25519.FeSquare(&h, &radix25A) - } -} +// if Ax.Cmp(c.Params().Gx) != 0 || Ay.Cmp(c.Params().Gy) != 0 { +// t.Errorf("arbitrary 1*B != B") +// } +// } -func BenchmarkFeSquare51(b *testing.B) { - var h radix51.FieldElement - for i := 0; i < b.N; i++ { - radix51.FeSquare(&h, &radix51A) - } -} +// func TestScalarMultBaseInfinity(t *testing.T) { +// c := Ed25519() +// a, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") +// if len(a) != 32 { +// t.Errorf("failed decoding") +// } + +// Ax, Ay := c.ScalarBaseMult(a) + +// if !c.IsOnCurve(Ax, Ay) { +// t.Error("scalarmultbase result was off-curve") +// } + +// if Ax.Cmp(bigZero) != 0 || Ay.Cmp(bigOne) != 0 { +// t.Error("scalarmultbase by 0 was not point at infinity") +// } +// } + +// func TestScalarMultsAgreeAtInfinity(t *testing.T) { +// c := Ed25519() +// a, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") +// if len(a) != 32 { +// t.Errorf("failed decoding") +// } + +// Ax, Ay := c.ScalarBaseMult(a) +// Bx, By := c.ScalarMult(c.Params().Gx, c.Params().Gy, a) + +// if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 { +// t.Error("scalarmultbase disagrees with scalarmult") +// } +// } + +// func TestScalarMultsAgreeElsewhere(t *testing.T) { +// c := Ed25519() +// // head -c 32 /dev/urandom | sha256sum +// a, _ := hex.DecodeString("c07eea55b3322f15099b6cf4d2b7e99d3d0fa6807f6fc7a46b5f7cb78daad4e0") + +// Ax, Ay := c.ScalarBaseMult(a) +// Bx, By := c.ScalarMult(c.Params().Gx, c.Params().Gy, a) + +// if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 { +// t.Error("scalarmultbase disagrees with scalarmult") +// } + +// if !c.IsOnCurve(Bx, By) { +// t.Error("scalarmult is returning off-curve points") +// } +// } + +// // TEST INTERFACE + +// func TestMarshalingRoundTrip(t *testing.T) { +// ed := Ed25519() + +// a, _ := hex.DecodeString("c07eea55b3322f15099b6cf4d2b7e99d3d0fa6807f6fc7a46b5f7cb78daad4e0") +// Ax, Ay := ed.ScalarBaseMult(a) + +// if !ed.IsOnCurve(Ax, Ay) { +// t.Error("scalarBaseMult is returning off-curve points") +// } + +// sec1A := elliptic.Marshal(ed, Ax, Ay) +// Bx, By := elliptic.Unmarshal(ed, sec1A) + +// if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 { +// t.Error("point did not survive elliptic.Marshal roundtrip") +// } + +// if !testing.Short() { +// for i := 0; i < 100; i++ { +// _, err := io.ReadFull(rand.Reader, a) +// if err != nil { +// t.Fatal(err) +// } +// Ax, Ay := ed.ScalarBaseMult(a) + +// if !ed.IsOnCurve(Ax, Ay) { +// t.Error("scalarBaseMult is returning off-curve points") +// } + +// sec1A := elliptic.Marshal(ed, Ax, Ay) +// Bx, By := elliptic.Unmarshal(ed, sec1A) + +// if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 { +// t.Error("point did not survive elliptic.Marshal roundtrip") +// } +// } +// } +// } + +// // TEST APPLICATION + +// func generateKey(r io.Reader) (sk *[32]byte, pk []byte, err error) { +// if r == nil { +// r = rand.Reader +// } + +// ed := Ed25519() + +// sk = new([32]byte) +// _, err = io.ReadFull(r, sk[:]) +// if err != nil { +// return nil, nil, err +// } + +// digest := sha512.Sum512(sk[:]) +// digest[0] &= 248 +// digest[31] &= 127 +// digest[31] |= 64 + +// // take the first half of expanded bytes & reverse; big.Int expects big-endian +// reverseDigest := reverse32(digest[:32]) +// scalar := new(big.Int).SetBytes(reverseDigest) + +// // A = a*B +// Ax, Ay := ed.ScalarBaseMult(scalar.Bytes()) + +// // This works with the standard elliptic package marshaling +// publicKey := elliptic.Marshal(ed, Ax, Ay) + +// // but we can also render the compressed Edwards format +// compressedEdwardsY := make([]byte, 32) + +// // x, y here will be big-endian byte strings +// x, y := publicKey[1:33], publicKey[33:] + +// // RFC 8032: To form the encoding of the point, copy the least significant +// // bit of the x-coordinate to the most significant bit of the final octet. +// copy(compressedEdwardsY[:], y) +// compressedEdwardsY[0] |= x[31] << 7 +// compressedEdwardsY = reverse32(compressedEdwardsY) + +// return sk, compressedEdwardsY, err +// } + +// func reverse32(b []byte) []byte { +// var tmp = make([]byte, 32) +// for i := 0; i <= 31; i++ { +// tmp[i] = b[31-i] +// } +// return tmp +// } + +// // Test vector generated by instrumenting x/crypto/ed25519 GenerateKey(). These +// // are raw values. The edwards code interprets them as little-endian, so they +// // need to be reversed before use with big.Int. +// var genKeyTest = struct { +// seed, expanded, public string +// }{ +// seed: "c240344fcc6615dda52da98149377ad2b13fdba2bc39a50ba9f3afb2cbd4abaa", +// expanded: "f04154b9d80963bb4c76214ece8a1049bdd16fbfc5003aff9835a59643ace276", +// public: "65a8343a83ec15e55050f12fc22f2c81a4fe7327c8da1524441f9ce5e5bc27dd", +// } + +// func TestEdDSAGenerateKey(t *testing.T) { +// fakeRandom, _ := hex.DecodeString(genKeyTest.seed) +// fakeReader := bytes.NewBuffer(fakeRandom) + +// sk, pk, err := generateKey(fakeReader) +// if err != nil { +// t.Fatal(err) +// } + +// expectedPK, _ := hex.DecodeString(genKeyTest.public) +// if !bytes.Equal(sk[:], fakeRandom) || !bytes.Equal(pk, expectedPK) { +// t.Error("generateKey output did not match test vector") +// } +// } + +// // BENCHMARKS + +// func BenchmarkScalarBaseMult(b *testing.B) { +// ed := Ed25519() + +// var k [32]byte +// _, err := io.ReadFull(rand.Reader, k[:]) +// if err != nil { +// b.Fatal(err) +// } +// k[0] &= 248 +// k[31] &= 127 +// k[31] |= 64 + +// for i := 0; i < b.N; i++ { +// _, _ = ed.ScalarBaseMult(k[:]) +// } +// } + +// func BenchmarkScalarMult(b *testing.B) { +// ed := Ed25519() +// Bx, By := ed.Params().Gx, ed.Params().Gy + +// var k [32]byte +// _, err := io.ReadFull(rand.Reader, k[:]) +// if err != nil { +// b.Fatal(err) +// } +// k[0] &= 248 +// k[31] &= 127 +// k[31] |= 64 + +// for i := 0; i < b.N; i++ { +// _, _ = ed.ScalarMult(Bx, By, k[:]) +// } +// } + +// // A is a constant from the Montgomery form of curve25519. +// var radix25A = edwards25519.FieldElement{ +// 486662, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// } + +// var radix51A = radix51.FieldElement{ +// 486662, 0, 0, 0, 0, +// } + +// func BenchmarkFeMul25(b *testing.B) { +// var h edwards25519.FieldElement +// for i := 0; i < b.N; i++ { +// edwards25519.FeMul(&h, &radix25A, &radix25A) +// } +// } + +// func BenchmarkFeMul51(b *testing.B) { +// var h radix51.FieldElement +// for i := 0; i < b.N; i++ { +// radix51.FeMul(&h, &radix51A, &radix51A) +// } +// } + +// func BenchmarkFeSquare25(b *testing.B) { +// var h edwards25519.FieldElement +// for i := 0; i < b.N; i++ { +// edwards25519.FeSquare(&h, &radix25A) +// } +// } + +// func BenchmarkFeSquare51(b *testing.B) { +// var h radix51.FieldElement +// for i := 0; i < b.N; i++ { +// radix51.FeSquare(&h, &radix51A) +// } +// } diff --git a/internal/group/ge.go b/internal/group/ge.go new file mode 100644 index 0000000..6d2aa92 --- /dev/null +++ b/internal/group/ge.go @@ -0,0 +1,209 @@ +// Implements group logic for the Ed25519 curve. + +package group + +import ( + "math/big" + + field "github.com/gtank/ed25519/internal/radix51" +) + +// From EFD https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html +// An elliptic curve in twisted Edwards form has parameters a, d and coordinates +// x, y satisfying the following equations: +// +// a * x^2 + y^2 = 1 + d * x^2 * y^2 +// +// Extended coordinates assume a = -1 and represent x, y as (X, Y, Z, T) +// satisfying the following equations: +// +// x = X / Z +// y = Y / Z +// x * y = T / Z +// +// This representation was introduced in the HisilWongCarterDawson paper "Twisted +// Edwards curves revisited" (Asiacrypt 2008). +type ExtendedGroupElement struct { + X, Y, Z, T field.FieldElement +} + +// Converts (x,y) to (X:Y:T:Z) extended coordinates, or "P3" in ref10. As +// described in "Twisted Edwards Curves Revisited", Hisil-Wong-Carter-Dawson +// 2008, Section 3.1 (https://eprint.iacr.org/2008/522.pdf) +// See also https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3 +func (v *ExtendedGroupElement) FromAffine(x, y *big.Int) { + field.FeFromBig(&v.X, x) + field.FeFromBig(&v.Y, y) + field.FeMul(&v.T, &v.X, &v.Y) + field.FeOne(&v.Z) +} + +// Extended coordinates are XYZT with x = X/Z, y = Y/Z, or the "P3" +// representation in ref10. Extended->affine is the same operation as moving +// from projective to affine. Per HWCD, it is safe to move from extended to +// projective by simply ignoring T. +func (v *ExtendedGroupElement) ToAffine() (*big.Int, *big.Int) { + var x, y, zinv field.FieldElement + + field.FeInvert(&zinv, &v.Z) + field.FeMul(&x, &v.X, &zinv) + field.FeMul(&y, &v.Y, &zinv) + + return field.FeToBig(&x), field.FeToBig(&y) +} + +// Per HWCD, it is safe to move from extended to projective by simply ignoring T. +func (v *ExtendedGroupElement) ToProjective() *ProjectiveGroupElement { + var p ProjectiveGroupElement + + field.FeCopy(&p.X, &v.X) + field.FeCopy(&p.Y, &v.Y) + field.FeCopy(&p.Z, &v.Z) + + return &p +} + +func (v *ExtendedGroupElement) Zero() *ExtendedGroupElement { + field.FeZero(&v.X) + field.FeOne(&v.Y) + field.FeOne(&v.Z) + field.FeZero(&v.T) + return v +} + +// This is the same addition formula everyone uses, "add-2008-hwcd-3". +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3 +// TODO We know Z1=1 and Z2=1 here, so mmadd-2008-hwcd-3 (6M + 1S + 1*k + 9add) could apply +func (v *ExtendedGroupElement) Add(p1, p2 *ExtendedGroupElement) *ExtendedGroupElement { + var tmp1, tmp2, A, B, C, D, E, F, G, H field.FieldElement + field.FeSub(&tmp1, &p1.Y, &p1.X) // tmp1 <-- Y1-X1 + field.FeSub(&tmp2, &p2.Y, &p2.X) // tmp2 <-- Y2-X2 + field.FeMul(&A, &tmp1, &tmp2) // A <-- tmp1*tmp2 = (Y1-X1)*(Y2-X2) + field.FeAdd(&tmp1, &p1.Y, &p1.X) // tmp1 <-- Y1+X1 + field.FeAdd(&tmp2, &p2.Y, &p2.X) // tmp2 <-- Y2+X2 + field.FeMul(&B, &tmp1, &tmp2) // B <-- tmp1*tmp2 = (Y1+X1)*(Y2+X2) + field.FeMul(&tmp1, &p1.T, &p2.T) // tmp1 <-- T1*T2 + field.FeMul(&C, &tmp1, &field.D2) // C <-- tmp1*2d = T1*2d*T2 + field.FeMul(&tmp1, &p1.Z, &p2.Z) // tmp1 <-- Z1*Z2 + field.FeAdd(&D, &tmp1, &tmp1) // D <-- tmp1 + tmp1 = 2*Z1*Z2 + field.FeSub(&E, &B, &A) // E <-- B-A + field.FeSub(&F, &D, &C) // F <-- D-C + field.FeAdd(&G, &D, &C) // G <-- D+C + field.FeAdd(&H, &B, &A) // H <-- B+A + field.FeMul(&v.X, &E, &F) // X3 <-- E*F + field.FeMul(&v.Y, &G, &H) // Y3 <-- G*H + field.FeMul(&v.T, &E, &H) // T3 <-- E*H + field.FeMul(&v.Z, &F, &G) // Z3 <-- F*G + return v +} + +func (v *ExtendedGroupElement) Double() *ExtendedGroupElement { + var p ProjectiveGroupElement = v.ToProjective() + return p.Double().ToExtended() +} + +// Projective coordinates are XYZ with x = X/Z, y = Y/Z, or the "P2" +// representation in ref10. This representation has a cheaper doubling formula +// than extended coordinates. +type ProjectiveGroupElement struct { + X, Y, Z field.FieldElement +} + +func (v *ProjectiveGroupElement) FromAffine(x, y *big.Int) { + field.FeFromBig(&v.X, x) + field.FeFromBig(&v.Y, y) + field.FeOne(&v.Z) +} + +func (v *ProjectiveGroupElement) ToAffine() (*big.Int, *big.Int) { + var x, y, zinv field.FieldElement + + field.FeInvert(&zinv, &v.Z) + field.FeMul(&x, &v.X, &zinv) + field.FeMul(&y, &v.Y, &zinv) + + return field.FeToBig(&x), field.FeToBig(&y) +} + +func (v *ProjectiveGroupElement) ToExtended() *ExtendedGroupElement { + var r ExtendedGroupElement + var zinv field.FieldElement + + field.FeCopy(&r.X, &v.X) + field.FeCopy(&r.Y, &v.Y) + field.FeCopy(&r.Z, &v.Z) + + field.FeInvert(&zinv, &v.Z) + field.FeMul(&r.T, &v.X, &v.Y) // XY = ZT + field.FeMul(&r.T, &r.T, &zinv) // T = ZT/Z + + return &r +} + +func (v *ProjectiveGroupElement) Zero() *ProjectiveGroupElement { + field.FeZero(&v.X) + field.FeOne(&v.Y) + field.FeOne(&v.Z) + return v +} + +// Because we are often converting from affine, we can use "mdbl-2008-bbjlp" +// which assumes Z1=1. We also assume a = -1. +// +// Assumptions: Z1 = 1. +// Cost: 2M + 4S + 1*a + 7add + 1*2. +// Source: 2008 BernsteinBirknerJoyeLangePeters +// http://eprint.iacr.org/2008/013, plus Z1=1, plus standard simplification. +// Explicit formulas: +// +// B = (X1+Y1)^2 +// C = X1^2 +// D = Y1^2 +// E = a*C +// F = E+D +// X3 = (B-C-D)*(F-2) +// Y3 = F*(E-D) +// Z3 = F^2-2*F +// +// This assumption is one reason why this package is internal. For instance, it +// will not hold during a Montgomery ladder using extended coordinates. +// TODO: Hand off or switch entirely to dbl-2008-bbjlp when package is public. +func (v *ProjectiveGroupElement) DoubleZ1() *ProjectiveGroupElement { + var p, q ProjectiveGroupElement + var t0, t1 field.FieldElement + + p = *v + + // C = X1^2, D = Y1^2 + field.FeSquare(&t0, &p.X) + field.FeSquare(&t1, &p.Y) + field.FeMul(&p.Z, &p.X, &p.Y) + + // B = (X1+Y1)^2 = X1^2 + Y1^2 + 2*(X*Y) + field.FeAdd(&q.X, &t0, &t1) + field.FeAdd(&q.X, &q.X, &p.Z) + field.FeAdd(&q.X, &q.X, &p.Z) + + // E = a*C where a = -1 + field.FeNeg(&q.Z, &t0) + + // F = E + D + field.FeAdd(&p.X, &q.Z, &t1) + + // X3 = (B-C-D)*(F-2) + field.FeSub(&p.Y, &q.X, &t0) + field.FeSub(&p.Y, &p.Y, &t1) + field.FeSub(&p.Z, &p.X, &field.FieldTwo) + field.FeMul(&q.X, &p.Y, &p.Z) + + // Y3 = F*(E-D) + field.FeSub(&p.Y, &q.Z, &t1) + field.FeMul(&q.Y, &p.X, &p.Y) + + // Z3 = F^2 - 2*F + field.FeSquare(&q.Z, &p.X) + field.FeSub(&q.Z, &q.Z, &p.X) + field.FeSub(&q.Z, &q.Z, &p.X) + + return &q +} diff --git a/internal/radix51/const.go b/internal/radix51/const.go index ce3f2ff..fa6e7b1 100644 --- a/internal/radix51/const.go +++ b/internal/radix51/const.go @@ -11,4 +11,8 @@ const ( var ( FieldZero FieldElement = [5]uint64{0, 0, 0, 0, 0} FieldOne FieldElement = [5]uint64{1, 0, 0, 0, 0} + FieldTwo FieldElement = [5]uint64{2, 0, 0, 0, 0} + + // 2*d, used in addition formula + D2 FieldElement = [5]uint64{1859910466990425, 932731440258426, 1072319116312658, 1815898335770999, 633789495995903} ) diff --git a/internal/radix51/fe.go b/internal/radix51/fe.go index ff7e5e1..68719df 100644 --- a/internal/radix51/fe.go +++ b/internal/radix51/fe.go @@ -4,12 +4,14 @@ // public domain amd64-51-30k version of ed25519 from SUPERCOP. package radix51 +import "math/big" + // FieldElement represents an element of the field GF(2^255-19). An element t // represents the integer t[0] + t[1]*2^51 + t[2]*2^102 + t[3]*2^153 + // t[4]*2^204. type FieldElement [5]uint64 -func (v *FieldElement) FeZero() { +func FeZero(v *FieldElement) { v[0] = 0 v[1] = 0 v[2] = 0 @@ -17,7 +19,7 @@ func (v *FieldElement) FeZero() { v[4] = 0 } -func (v *FieldElement) FeOne() { +func FeOne(v *FieldElement) { v[0] = 1 v[1] = 0 v[2] = 0 @@ -26,7 +28,7 @@ func (v *FieldElement) FeOne() { } // SetInt sets the receiving FieldElement to the specified small integer. -func (v *FieldElement) SetInt(x uint64) { +func SetInt(v *FieldElement, x uint64) { v[0] = x v[1] = 0 v[2] = 0 @@ -116,7 +118,7 @@ func FeSub(out, a, b *FieldElement) { // FeNeg sets out = -a func FeNeg(out, a *FieldElement) { var t FieldElement - t.SetInt(0) + FeZero(&t) FeSub(out, &t, a) } @@ -210,12 +212,17 @@ func FeCSwap(a, b *FieldElement, c uint64) { b[4] ^= t[4] } -func FeEqual(a, b *FieldElement) uint64 { +// Returns true if two field elements are equal. +func FeEqual(a, b *FieldElement) bool { var result uint64 for i := 0; i < 5; i++ { result |= a[i] ^ b[i] } - return (result ^ 0) + return result == 0 +} + +func FeCopy(out, in *FieldElement) { + copy(out[:], in[:]) } func FeFromBytes(v *FieldElement, x *[32]byte) { @@ -306,3 +313,24 @@ func FeToBytes(r *[32]byte, v *FieldElement) { r[30] = byte((t[4] >> 36) & 0xff) r[31] = byte((t[4] >> 44)) } + +func FeFromBig(h *FieldElement, in *big.Int) { + tmpBytes := in.Bytes() + var buf, reverse [32]byte + copy(buf[32-len(tmpBytes):], tmpBytes) + for i := 0; i < 32; i++ { + reverse[i] = buf[31-i] + } + FeFromBytes(h, &reverse) +} + +func FeToBig(h *FieldElement) *big.Int { + var buf, reverse [32]byte + FeToBytes(&buf, h) // does inline reduction + for i := 0; i < 32; i++ { + reverse[i] = buf[31-i] + } + out := new(big.Int) + out.SetBytes(reverse[:]) + return out +}