diff --git a/internal/edwards25519/edwards25519.go b/internal/edwards25519/edwards25519.go index 6f06378..12be253 100644 --- a/internal/edwards25519/edwards25519.go +++ b/internal/edwards25519/edwards25519.go @@ -12,14 +12,13 @@ package edwards25519 import ( - "math/big" - "github.com/gtank/ristretto255/internal/radix51" ) // D is a constant in the curve equation. var D = &radix51.FieldElement{929955233495203, 466365720129213, 1662059464998953, 2033849074728123, 1442794654840575} +var twoD = new(radix51.FieldElement).Add(D, D) // Point types. @@ -275,328 +274,3 @@ func (v *ProjP3) Equal(u *ProjP3) int { return t1.Equal(&t2) & t3.Equal(&t4) } - -// 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 radix51.FieldElement -} - -func (v *ExtendedGroupElement) Set(u *ExtendedGroupElement) *ExtendedGroupElement { - *v = *u - return v -} - -// 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) *ExtendedGroupElement { - v.X.FromBig(x) - v.Y.FromBig(y) - v.T.Mul(&v.X, &v.Y) - v.Z.One() - return v -} - -// 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 radix51.FieldElement - - zinv.Invert(&v.Z) - x.Mul(&v.X, &zinv) - y.Mul(&v.Y, &zinv) - - return x.ToBig(), y.ToBig() -} - -// Per HWCD, it is safe to move from extended to projective by simply ignoring T. -func (v *ExtendedGroupElement) ToProjective(p *ProjectiveGroupElement) { - p.X.Set(&v.X) - p.Y.Set(&v.Y) - p.Z.Set(&v.Z) -} - -func (v *ExtendedGroupElement) Zero() *ExtendedGroupElement { - v.X.Zero() - v.Y.One() - v.Z.One() - v.T.Zero() - return v -} - -var twoD = new(radix51.FieldElement).Add(D, D) - -// 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 radix51.FieldElement - tmp1.Sub(&p1.Y, &p1.X) // tmp1 <-- Y1-X1 - tmp2.Sub(&p2.Y, &p2.X) // tmp2 <-- Y2-X2 - A.Mul(&tmp1, &tmp2) // A <-- tmp1*tmp2 = (Y1-X1)*(Y2-X2) - tmp1.Add(&p1.Y, &p1.X) // tmp1 <-- Y1+X1 - tmp2.Add(&p2.Y, &p2.X) // tmp2 <-- Y2+X2 - B.Mul(&tmp1, &tmp2) // B <-- tmp1*tmp2 = (Y1+X1)*(Y2+X2) - tmp1.Mul(&p1.T, &p2.T) // tmp1 <-- T1*T2 - C.Mul(&tmp1, twoD) // C <-- tmp1*2d = T1*2*d*T2 - tmp1.Mul(&p1.Z, &p2.Z) // tmp1 <-- Z1*Z2 - D.Add(&tmp1, &tmp1) // D <-- tmp1 + tmp1 = 2*Z1*Z2 - E.Sub(&B, &A) // E <-- B-A - F.Sub(&D, &C) // F <-- D-C - G.Add(&D, &C) // G <-- D+C - H.Add(&B, &A) // H <-- B+A - v.X.Mul(&E, &F) // X3 <-- E*F - v.Y.Mul(&G, &H) // Y3 <-- G*H - v.T.Mul(&E, &H) // T3 <-- E*H - v.Z.Mul(&F, &G) // Z3 <-- F*G - return v -} - -func (v *ExtendedGroupElement) Sub(p1, p2 *ExtendedGroupElement) *ExtendedGroupElement { - // This is the same function as above, but with X2, T2 negated to X2'=-X2, T2'=-T2 - var tmp1, tmp2, A, B, C, D, E, F, G, H radix51.FieldElement - tmp1.Sub(&p1.Y, &p1.X) // tmp1 <-- Y1-X1 - tmp2.Add(&p2.Y, &p2.X) // tmp2 <-- Y2+X2 = Y2-X2' - A.Mul(&tmp1, &tmp2) // A <-- tmp1*tmp2 = (Y1-X1)*(Y2-X2') - tmp1.Add(&p1.Y, &p1.X) // tmp1 <-- Y1+X1 - tmp2.Sub(&p2.Y, &p2.X) // tmp2 <-- Y2-X2 = Y2+X2' - B.Mul(&tmp1, &tmp2) // B <-- tmp1*tmp2 = (Y1+X1)*(Y2+X2) - tmp1.Mul(&p1.T, &p2.T) // tmp1 <-- -T1*T2' = T1*T2 - C.Mul(&tmp1, twoD) // C' <-- tmp1*2d = -T1*2*d*T2' = T1*2*d*T2 - tmp1.Mul(&p1.Z, &p2.Z) // tmp1 <-- Z1*Z2 - D.Add(&tmp1, &tmp1) // D <-- tmp1 + tmp1 = 2*Z1*Z2 - E.Sub(&B, &A) // E <-- B-A - F.Add(&D, &C) // F <-- D+C' = D-C - G.Sub(&D, &C) // G <-- D-C' = D+C - H.Add(&B, &A) // H <-- B+A - v.X.Mul(&E, &F) // X3 <-- E*F - v.Y.Mul(&G, &H) // Y3 <-- G*H - v.T.Mul(&E, &H) // T3 <-- E*H - v.Z.Mul(&F, &G) // Z3 <-- F*G - return v -} - -func (v *ExtendedGroupElement) Neg(p *ExtendedGroupElement) *ExtendedGroupElement { - v.X.Neg(&p.X) - v.Y.Set(&p.Y) - v.Z.Set(&p.Z) - v.T.Neg(&p.T) - return v -} - -// by @ebfull -// https://github.com/dalek-cryptography/curve25519-dalek/pull/226/files -func (v *ExtendedGroupElement) Equal(u *ExtendedGroupElement) int { - var t1, t2, t3, t4 radix51.FieldElement - t1.Mul(&v.X, &u.Z) - t2.Mul(&u.X, &v.Z) - t3.Mul(&v.Y, &u.Z) - t4.Mul(&u.Y, &v.Z) - - return t1.Equal(&t2) & t3.Equal(&t4) -} - -// This implements the explicit formulas from HWCD Section 3.3, "Dedicated -// Doubling in [extended coordinates]". -// -// Explicit formula is as follows. Cost is 4M + 4S + 1D. For Ed25519, a = -1: -// -// A ← X1^2 -// B ← Y1^2 -// C ← 2*Z1^2 -// D ← a*A -// E ← (X1+Y1)^2 − A − B -// G ← D+B -// F ← G−C -// H ← D−B -// X3 ← E*F -// Y3 ← G*H -// T3 ← E*H -// Z3 ← F*G -// -// In ref10/donna/dalek etc, this is instead handled by a faster -// mixed-coordinate doubling that results in a "Completed" group element -// instead of another point in extended coordinates. I have implemented it -// this way to see if more straightforward code is worth the (hopefully small) -// performance tradeoff. -func (v *ExtendedGroupElement) Double(u *ExtendedGroupElement) *ExtendedGroupElement { - // TODO: Convert to projective coordinates? Section 4.3 mixed doubling? - - var A, B, C, D, E, F, G, H radix51.FieldElement - - // A ← X1^2, B ← Y1^2 - A.Square(&u.X) - B.Square(&u.Y) - - // C ← 2*Z1^2 - C.Square(&u.Z) - C.Add(&C, &C) // TODO should probably implement FeSquare2 - - // D ← -1*A - D.Neg(&A) // implemented as subtraction - - // E ← (X1+Y1)^2 − A − B - var t0 radix51.FieldElement - t0.Add(&u.X, &u.Y) - t0.Square(&t0) - E.Sub(&t0, &A) - E.Sub(&E, &B) - - G.Add(&D, &B) // G ← D+B - F.Sub(&G, &C) // F ← G−C - H.Sub(&D, &B) // H ← D−B - v.X.Mul(&E, &F) // X3 ← E*F - v.Y.Mul(&G, &H) // Y3 ← G*H - v.T.Mul(&E, &H) // T3 ← E*H - v.Z.Mul(&F, &G) // Z3 ← F*G - - return v -} - -// ScalarMult sets v = k*u where k is a reduced scalar field element in -// little-endian form. Note: this function is not constant-time. -func (v *ExtendedGroupElement) ScalarMult(u *ExtendedGroupElement, k *[32]byte) *ExtendedGroupElement { - // Montgomery ladder init: - // R_0 = O, R_1 = P - r1 := new(ExtendedGroupElement).Set(u) - r0 := v.Zero() - - // Montgomery ladder step: - // R_{1-b} = R_{1-b} + R_{b} - // R_{b} = 2*R_{b} - for i := 255; i >= 0; i-- { - var b = int32((k[i/8] >> uint(i&7)) & 1) - if b == 0 { - r1.Add(r0, r1) - r0.Double(r0) - } else { - r0.Add(r0, r1) - r1.Double(r1) - } - } - - return r0 -} - -// 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 radix51.FieldElement -} - -func (v *ProjectiveGroupElement) FromAffine(x, y *big.Int) *ProjectiveGroupElement { - v.X.FromBig(x) - v.Y.FromBig(y) - v.Z.One() - return v -} - -func (v *ProjectiveGroupElement) ToAffine() (*big.Int, *big.Int) { - var x, y, zinv radix51.FieldElement - - zinv.Invert(&v.Z) - x.Mul(&v.X, &zinv) - y.Mul(&v.Y, &zinv) - - return x.ToBig(), y.ToBig() -} - -// HWCD Section 3: "Given (X : Y : Z) in [projective coordinates] passing to -// [extended coordinates, (X : Y : T : Z)] can be performed in 3M+1S by computing -// (XZ, YZ, XY, Z^2)" -func (v *ProjectiveGroupElement) ToExtended(r *ExtendedGroupElement) { - r.X.Mul(&v.X, &v.Z) - r.Y.Mul(&v.Y, &v.Z) - r.T.Mul(&v.X, &v.Y) - r.Z.Square(&v.Z) -} - -func (v *ProjectiveGroupElement) Zero() *ProjectiveGroupElement { - v.X.Zero() - v.Y.One() - v.Z.One() - 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 throughout a Montgomery ladder, when we convert to projective -// from possibly arbitrary extended coordinates. -func (v *ProjectiveGroupElement) DoubleZ1(u *ProjectiveGroupElement) *ProjectiveGroupElement { - var B, C, D, E, F radix51.FieldElement - - if u.Z.Equal(radix51.One) != 1 { - panic("ed25519: DoubleZ1 called with Z != 1") - } - - B.Square(B.Add(&u.X, &u.Y)) // B = (X1+Y1)^2 - C.Square(&u.X) // C = X1^2 - D.Square(&u.Y) // D = Y1^2 - E.Neg(&C) // E = a*C where a = -1 - F.Add(&E, &D) // F = E + D - - // X3 = (B-C-D)*(F-2) - v.Y.Sub(v.Y.Sub(&B, &C), &D) - v.X.Mul(&v.Y, v.X.Sub(&F, radix51.Two)) - - // Y3 = F*(E-D) - v.Y.Mul(&F, v.Y.Sub(&E, &D)) - - // Z3 = F^2 - 2*F - v.Z.Square(&F) - v.Z.Sub(&v.Z, &F) - v.Z.Sub(&v.Z, &F) - - return v -} - -// IsOnCurve reports whether the given affine coordinate (x,y) lies on the curve -// by checking that -x^2 + y^2 - 1 - dx^2y^2 = 0 (mod p). -func IsOnCurve(x, y *radix51.FieldElement) bool { - var lh, y2, rh radix51.FieldElement - lh.Square(x) // x^2 - y2.Square(y) // y^2 - rh.Mul(&lh, &y2) // x^2*y^2 - rh.Mul(&rh, D) // d*x^2*y^2 - rh.Add(&rh, radix51.One) // 1 + d*x^2*y^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 - - return lh.Equal(radix51.Zero) == 1 -} diff --git a/internal/edwards25519/edwards25519_test.go b/internal/edwards25519/edwards25519_test.go index 5422ea3..2d91797 100644 --- a/internal/edwards25519/edwards25519_test.go +++ b/internal/edwards25519/edwards25519_test.go @@ -6,39 +6,6 @@ import ( ) func TestAddSubNegOnBasePoint(t *testing.T) { - - basepoint := ExtendedGroupElement{ - X: radix51.FieldElement([5]uint64{426475514619346, 2063872706840040, 14628272888959, 107677749330612, 288339085807592}), - Y: radix51.FieldElement([5]uint64{1934594822876571, 2049809580636559, 1991994783322914, 1758681962032007, 380046701118659}), - Z: radix51.FieldElement([5]uint64{1, 0, 0, 0, 0}), - T: radix51.FieldElement([5]uint64{410445769351754, 2235400917701188, 1495825632738689, 1351628537510093, 430502003771208}), - } - - negBasepoint := ExtendedGroupElement{} - - negBasepoint.Neg(&basepoint) - - check1 := ExtendedGroupElement{} - check1.Zero() - check2 := ExtendedGroupElement{} - check2.Zero() - zero := ExtendedGroupElement{} - zero.Zero() - - check1.Add(&basepoint, &negBasepoint) - check2.Sub(&basepoint, &basepoint) - - if check1.Equal(&check2) != 1 { - t.Error("B + (-B) != B - B") - } - - if check1.Equal(&zero) != 1 { - t.Error("B + (-B) != 0") - } -} - -func TestAddSubNegOnBasePoint2(t *testing.T) { - var B, Bneg ProjP3 var tmpP2 ProjP2 var tmpP1xP1 ProjP1xP1