diff --git a/ecc/bls12-377/twistededwards/point.go b/ecc/bls12-377/twistededwards/point.go index 64ef3cf099..70cd6b24d2 100644 --- a/ecc/bls12-377/twistededwards/point.go +++ b/ecc/bls12-377/twistededwards/point.go @@ -35,6 +35,11 @@ type PointProj struct { X, Y, Z fr.Element } +// PointExtended point in extended coordinates +type PointExtended struct { + X, Y, Z, T fr.Element +} + const ( //following https://tools.ietf.org/html/rfc8032#section-3.1, // an fr element x is negative if its binary encoding is @@ -128,14 +133,6 @@ func (p *PointAffine) Unmarshal(b []byte) error { return err } -// Set sets p to p1 and return it -func (p *PointProj) Set(p1 *PointProj) *PointProj { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - p.Z.Set(&p1.Z) - return p -} - // Set sets p to p1 and return it func (p *PointAffine) Set(p1 *PointAffine) *PointAffine { p.X.Set(&p1.X) @@ -148,16 +145,11 @@ func (p *PointAffine) Equal(p1 *PointAffine) bool { return p.X.Equal(&p1.X) && p.Y.Equal(&p1.Y) } -// Equal returns true if p=p1 false otherwise -// If one point is on the affine chart Z=0 it returns false -func (p *PointProj) Equal(p1 *PointProj) bool { - if p.Z.IsZero() || p1.Z.IsZero() { - return false - } - var pAffine, p1Affine PointAffine - pAffine.FromProj(p) - p1Affine.FromProj(p1) - return pAffine.Equal(&p1Affine) +// IsZero returns true if p=0 false otherwise +func (p *PointAffine) IsZero() bool { + var one fr.Element + one.SetOne() + return p.X.IsZero() && p.Y.Equal(&one) } // NewPointAffine creates a new instance of PointAffine @@ -186,6 +178,13 @@ func (p *PointAffine) IsOnCurve() bool { return lhs.Equal(&rhs) } +// Neg sets p to -p1 and returns it +func (p *PointAffine) Neg(p1 *PointAffine) *PointAffine { + p.Set(p1) + p.X.Neg(&p.X) + return p +} + // Add adds two points (x,y), (u,v) on a twisted Edwards curve with parameters a, d // modifies p func (p *PointAffine) Add(p1, p2 *PointAffine) *PointAffine { @@ -237,60 +236,84 @@ func (p *PointAffine) Double(p1 *PointAffine) *PointAffine { return p } -// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d -// modifies p -func (p *PointProj) Neg(p1 *PointProj) *PointProj { - p.Set(p1) - p.X.Neg(&p.X) +// FromProj sets p in affine from p in projective +func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { + var I fr.Element + I.Inverse(&p1.Z) + p.X.Mul(&p1.X, &I) + p.Y.Mul(&p1.Y, &I) return p } -// FromProj sets p in affine from p in projective -func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { - p.X.Div(&p1.X, &p1.Z) - p.Y.Div(&p1.Y, &p1.Z) +// FromExtended sets p in affine from p in extended coordinates +func (p *PointAffine) FromExtended(p1 *PointExtended) *PointAffine { + var I fr.Element + I.Inverse(&p1.Z) + p.X.Mul(&p1.X, &I) + p.Y.Mul(&p1.Y, &I) return p } -// FromAffine sets p in projective from p in affine -func (p *PointProj) FromAffine(p1 *PointAffine) *PointProj { +// ScalarMul scalar multiplication of a point +// p1 in affine coordinates with a scalar in big.Int +func (p *PointAffine) ScalarMul(p1 *PointAffine, scalar *big.Int) *PointAffine { + + var p1Extended, resExtended PointExtended + p1Extended.FromAffine(p1) + resExtended.ScalarMul(&p1Extended, scalar) + p.FromExtended(&resExtended) + + return p +} + +//-------- Projective coordinates + +// Set sets p to p1 and return it +func (p *PointProj) Set(p1 *PointProj) *PointProj { p.X.Set(&p1.X) p.Y.Set(&p1.Y) - p.Z.SetOne() + p.Z.Set(&p1.Z) return p } -// Add adds points in projective coordinates -// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp -func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { +// setInfinity sets p to O (0:1:1) +func (p *PointProj) setInfinity() *PointProj { + p.X.SetZero() + p.Y.SetOne() + p.Z.SetOne() + return p +} - var res PointProj +// Equal returns true if p=p1 false otherwise +// If one point is on the affine chart Z=0 it returns false +func (p *PointProj) Equal(p1 *PointProj) bool { + if p.Z.IsZero() || p1.Z.IsZero() { + return false + } + var pAffine, p1Affine PointAffine + pAffine.FromProj(p) + p1Affine.FromProj(p1) + return pAffine.Equal(&p1Affine) +} - ecurve := GetEdwardsCurve() +// IsZero returns true if p=0 false otherwise +func (p *PointProj) IsZero() bool { + return p.X.IsZero() && p.Y.Equal(&p.Z) +} - var A, B, C, D, E, F, G, H, I fr.Element - A.Mul(&p1.Z, &p2.Z) - B.Square(&A) - C.Mul(&p1.X, &p2.X) - D.Mul(&p1.Y, &p2.Y) - E.Mul(&ecurve.D, &C).Mul(&E, &D) - F.Sub(&B, &E) - G.Add(&B, &E) - H.Add(&p1.X, &p1.Y) - I.Add(&p2.X, &p2.Y) - res.X.Mul(&H, &I). - Sub(&res.X, &C). - Sub(&res.X, &D). - Mul(&res.X, &A). - Mul(&res.X, &F) - mulByA(&C) - C.Neg(&C) - res.Y.Add(&D, &C). - Mul(&res.Y, &A). - Mul(&res.Y, &G) - res.Z.Mul(&F, &G) +// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d +// modifies p +func (p *PointProj) Neg(p1 *PointProj) *PointProj { + p.Set(p1) + p.X.Neg(&p.X) + return p +} - p.Set(&res) +// FromAffine sets p in projective from p in affine +func (p *PointProj) FromAffine(p1 *PointAffine) *PointProj { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.Z.SetOne() return p } @@ -298,8 +321,6 @@ func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { // cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-madd-2008-bbjlp func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { - var res PointProj - ecurve := GetEdwardsCurve() var B, C, D, E, F, G, H, I fr.Element @@ -311,18 +332,17 @@ func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { G.Add(&B, &E) H.Add(&p1.X, &p1.Y) I.Add(&p2.X, &p2.Y) - res.X.Mul(&H, &I). - Sub(&res.X, &C). - Sub(&res.X, &D). - Mul(&res.X, &p1.Z). - Mul(&res.X, &F) + p.X.Mul(&H, &I). + Sub(&p.X, &C). + Sub(&p.X, &D). + Mul(&p.X, &p1.Z). + Mul(&p.X, &F) mulByA(&C) - res.Y.Sub(&D, &C). - Mul(&res.Y, &p1.Z). - Mul(&res.Y, &G) - res.Z.Mul(&F, &G) + p.Y.Sub(&D, &C). + Mul(&p.Y, &p1.Z). + Mul(&p.Y, &G) + p.Z.Mul(&F, &G) - p.Set(&res) return p } @@ -330,8 +350,6 @@ func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { // cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#doubling-dbl-2008-bbjlp func (p *PointProj) Double(p1 *PointProj) *PointProj { - var res PointProj - var B, C, D, E, F, H, J fr.Element B.Add(&p1.X, &p1.Y).Square(&B) @@ -342,28 +360,43 @@ func (p *PointProj) Double(p1 *PointProj) *PointProj { F.Add(&E, &D) H.Square(&p1.Z) J.Sub(&F, &H).Sub(&J, &H) - res.X.Sub(&B, &C). - Sub(&res.X, &D). - Mul(&res.X, &J) - res.Y.Sub(&E, &D).Mul(&res.Y, &F) - res.Z.Mul(&F, &J) + p.X.Sub(&B, &C). + Sub(&p.X, &D). + Mul(&p.X, &J) + p.Y.Sub(&E, &D).Mul(&p.Y, &F) + p.Z.Mul(&F, &J) - p.Set(&res) return p } -// Neg sets p to -p1 and returns it -func (p *PointAffine) Neg(p1 *PointAffine) *PointAffine { - p.Set(p1) - p.X.Neg(&p.X) - return p -} +// Add adds points in projective coordinates +// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp +func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { + + ecurve := GetEdwardsCurve() + + var A, B, C, D, E, F, G, H, I fr.Element + A.Mul(&p1.Z, &p2.Z) + B.Square(&A) + C.Mul(&p1.X, &p2.X) + D.Mul(&p1.Y, &p2.Y) + E.Mul(&ecurve.D, &C).Mul(&E, &D) + F.Sub(&B, &E) + G.Add(&B, &E) + H.Add(&p1.X, &p1.Y) + I.Add(&p2.X, &p2.Y) + p.X.Mul(&H, &I). + Sub(&p.X, &C). + Sub(&p.X, &D). + Mul(&p.X, &A). + Mul(&p.X, &F) + mulByA(&C) + C.Neg(&C) + p.Y.Add(&D, &C). + Mul(&p.Y, &A). + Mul(&p.Y, &G) + p.Z.Mul(&F, &G) -// setInfinity sets p to O (0:1:1) -func (p *PointProj) setInfinity() *PointProj { - p.X.SetZero() - p.Y.SetOne() - p.Z.SetOne() return p } @@ -398,14 +431,218 @@ func (p *PointProj) ScalarMul(p1 *PointProj, scalar *big.Int) *PointProj { return p } +// ------- Extended coordinates + +// Set sets p to p1 and return it +func (p *PointExtended) Set(p1 *PointExtended) *PointExtended { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.T.Set(&p1.T) + p.Z.Set(&p1.Z) + return p +} + +// IsZero returns true if p=0 false otherwise +func (p *PointExtended) IsZero() bool { + return p.X.IsZero() && p.Y.Equal(&p.Z) && p.T.IsZero() +} + +// Equal returns true if p=p1 false otherwise +// If one point is on the affine chart Z=0 it returns false +func (p *PointExtended) Equal(p1 *PointExtended) bool { + if p.Z.IsZero() || p1.Z.IsZero() { + return false + } + var pAffine, p1Affine PointAffine + pAffine.FromExtended(p) + p1Affine.FromExtended(p1) + return pAffine.Equal(&p1Affine) +} + +// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d +// modifies p +func (p *PointExtended) Neg(p1 *PointExtended) *PointExtended { + p.Set(p1) + p.X.Neg(&p.X) + p.T.Neg(&p.T) + return p +} + +// FromAffine sets p in projective from p in affine +func (p *PointExtended) FromAffine(p1 *PointAffine) *PointExtended { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.Z.SetOne() + p.T.Mul(&p1.X, &p1.Y) + return p +} + +// Add adds points in extended coordinates +// dedicated addition +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4 +func (p *PointExtended) Add(p1, p2 *PointExtended) *PointExtended { + + if p1.Equal(p2) { + p.Double(p1) + return p + } + + var A, B, C, D, E, F, G, H, tmp fr.Element + tmp.Add(&p2.X, &p2.Y) + A.Sub(&p1.Y, &p1.X). + Mul(&A, &tmp) + tmp.Add(&p1.X, &p1.Y) + B.Sub(&p2.Y, &p2.X). + Mul(&B, &tmp) + C.Mul(&p1.Z, &p2.T). + Double(&C) + D.Mul(&p2.Z, &p1.T). + Double(&D) + E.Add(&D, &C) + F.Sub(&B, &A) + G.Add(&B, &A) + H.Sub(&D, &C) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&E, &H) + p.Z.Mul(&F, &G) + + return p +} + +// MixedAdd adds a point in extended coordinates to a point in affine coordinates +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-madd-2008-hwcd-4 +func (p *PointExtended) MixedAdd(p1 *PointExtended, p2 *PointAffine) *PointExtended { + + var A, B, C, D, E, F, G, H, tmp fr.Element + + A.Mul(&p2.X, &p1.Z) + B.Mul(&p2.Y, &p1.Z) + + if p1.X.Equal(&A) && p1.Y.Equal(&B) { + p.MixedDouble(p1) + return p + } + + tmp.Add(&p2.X, &p2.Y) + A.Sub(&p1.Y, &p1.X). + Mul(&A, &tmp) + tmp.Add(&p1.X, &p1.Y) + B.Sub(&p2.Y, &p2.X). + Mul(&B, &tmp) + C.Mul(&p1.Z, &p2.X). + Mul(&C, &p2.Y). + Double(&C) + D.Double(&p1.T) + E.Add(&D, &C) + F.Sub(&B, &A) + G.Add(&B, &A) + H.Sub(&D, &C) + + p.X.Mul(&F, &E) + p.Y.Mul(&G, &H) + p.T.Mul(&E, &H) + p.Z.Mul(&G, &F) + + return p +} + +// Double adds points in extended coordinates +// Dedicated doubling +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd +func (p *PointExtended) Double(p1 *PointExtended) *PointExtended { + + var A, B, C, D, E, F, G, H fr.Element + + A.Square(&p1.X) + B.Square(&p1.Y) + C.Square(&p1.Z). + Double(&C) + D.Set(&A) + mulByA(&D) + E.Add(&p1.X, &p1.Y). + Square(&E). + Sub(&E, &A). + Sub(&E, &B) + G.Add(&D, &B) + F.Sub(&G, &C) + H.Sub(&D, &B) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&H, &E) + p.Z.Mul(&F, &G) + + return p +} + +// MixedDouble adds points in extended coordinates +// Dedicated mixed doubling +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-mdbl-2008-hwcd +func (p *PointExtended) MixedDouble(p1 *PointExtended) *PointExtended { + + var A, B, D, E, G, H, two fr.Element + two.SetUint64(2) + + A.Square(&p1.X) + B.Square(&p1.Y) + D.Set(&A) + mulByA(&D) + E.Add(&p1.X, &p1.Y). + Square(&E). + Sub(&E, &A). + Sub(&E, &B) + G.Add(&D, &B) + H.Sub(&D, &B) + + p.X.Sub(&G, &two). + Mul(&p.X, &E) + p.Y.Mul(&G, &H) + p.T.Mul(&H, &E) + p.Z.Square(&G). + Sub(&p.Z, &G). + Sub(&p.Z, &G) + + return p +} + +// setInfinity sets p to O (0:1:1:0) +func (p *PointExtended) setInfinity() *PointExtended { + p.X.SetZero() + p.Y.SetOne() + p.Z.SetOne() + p.T.SetZero() + return p +} + // ScalarMul scalar multiplication of a point -// p1 in affine coordinates with a scalar in big.Int -func (p *PointAffine) ScalarMul(p1 *PointAffine, scalar *big.Int) *PointAffine { +// p1 in extended coordinates with a scalar in big.Int +func (p *PointExtended) ScalarMul(p1 *PointExtended, scalar *big.Int) *PointExtended { + + var _scalar big.Int + _scalar.Set(scalar) + p.Set(p1) + if _scalar.Sign() == -1 { + _scalar.Neg(&_scalar) + p.Neg(p) + } + var resExtended PointExtended + resExtended.setInfinity() + const wordSize = bits.UintSize + sWords := _scalar.Bits() - var p1Proj, resProj PointProj - p1Proj.FromAffine(p1) - resProj.ScalarMul(&p1Proj, scalar) - p.FromProj(&resProj) + for i := len(sWords) - 1; i >= 0; i-- { + ithWord := sWords[i] + for k := 0; k < wordSize; k++ { + resExtended.Double(&resExtended) + kthBit := (ithWord >> (wordSize - 1 - k)) & 1 + if kthBit == 1 { + resExtended.Add(&resExtended, p) + } + } + } + p.Set(&resExtended) return p } diff --git a/ecc/bls12-377/twistededwards/twistededwards_test.go b/ecc/bls12-377/twistededwards/twistededwards_test.go index 6a23edc463..b6a1cc9365 100644 --- a/ecc/bls12-377/twistededwards/twistededwards_test.go +++ b/ecc/bls12-377/twistededwards/twistededwards_test.go @@ -123,7 +123,7 @@ func TestReceiverIsOperand(t *testing.T) { )) properties.TestingRun(t, gopter.ConsoleReporter(false)) - // proj + // projective properties.Property("Equal projective: having the receiver as operand should output the same result", prop.ForAll( func() bool { params := GetEdwardsCurve() @@ -191,6 +191,74 @@ func TestReceiverIsOperand(t *testing.T) { }, )) + // extended + properties.Property("Equal extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + params := GetEdwardsCurve() + var p1, baseProj PointProj + p1.FromAffine(¶ms.Base) + baseProj.FromAffine(¶ms.Base) + + return p1.Equal(&p1) && p1.Equal(&baseProj) + }, + )) + + properties.Property("Add extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2, p3 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + p3.FromAffine(¶ms.Base) + + res := true + + p3.Add(&p1, &p2) + p1.Add(&p1, &p2) + res = res && p3.Equal(&p1) + + p1.FromAffine(¶ms.Base) + p2.Add(&p1, &p2) + res = res && p2.Equal(&p3) + + return res + }, + )) + + properties.Property("Double extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + + p2.Double(&p1) + p1.Double(&p1) + + return p2.Equal(&p1) + }, + )) + + properties.Property("Neg extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + + p2.Neg(&p1) + p1.Neg(&p1) + + return p2.Equal(&p1) + }, + )) + properties.TestingRun(t, gopter.ConsoleReporter(false)) } @@ -244,7 +312,7 @@ func TestOps(t *testing.T) { var one fr.Element one.SetOne() - return p1.IsOnCurve() && p1.X.IsZero() && p1.Y.Equal(&one) + return p1.IsOnCurve() && p1.IsZero() }, genS1, )) @@ -308,7 +376,7 @@ func TestOps(t *testing.T) { genS1, )) - properties.Property("[5]P=[2][2]P+P", prop.ForAll( + properties.Property("(affine) [5]P=[2][2]P+P", prop.ForAll( func(s1 big.Int) bool { params := GetEdwardsCurve() @@ -325,7 +393,7 @@ func TestOps(t *testing.T) { genS1, )) - // proj + // projective properties.Property("(projective) P+(-P)=O", prop.ForAll( func(s1 big.Int) bool { @@ -338,7 +406,7 @@ func TestOps(t *testing.T) { p.Add(&p1, &p2) - return p.X.IsZero() && p.Y.Equal(&p.Z) + return p.IsZero() }, genS1, )) @@ -361,8 +429,119 @@ func TestOps(t *testing.T) { genS1, )) - // mixed - properties.Property("(mixed) P+(-P)=O", prop.ForAll( + properties.Property("(projective) [5]P=[2][2]P+P", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseProj, p1, p2 PointProj + baseProj.FromAffine(¶ms.Base) + p1.ScalarMul(&baseProj, &s1) + + five := big.NewInt(5) + p2.Double(&p1).Double(&p2).Add(&p2, &p1) + p1.ScalarMul(&p1, five) + + return p2.Equal(&p1) + }, + genS1, + )) + + // extended + properties.Property("(extended) P+(-P)=O", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2, p PointExtended + baseExtended.FromAffine(¶ms.Base) + p1.ScalarMul(&baseExtended, &s1) + p2.Neg(&p1) + + p.Add(&p1, &p2) + + return p.IsZero() + }, + genS1, + )) + + properties.Property("(extended) P+P=2*P", prop.ForAll( + + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2, p PointExtended + baseExtended.FromAffine(¶ms.Base) + p.ScalarMul(&baseExtended, &s) + + p1.Add(&p, &p) + p2.Double(&p) + + return p1.Equal(&p2) + }, + genS1, + )) + + properties.Property("(extended) [5]P=[2][2]P+P", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2 PointExtended + baseExtended.FromAffine(¶ms.Base) + p1.ScalarMul(&baseExtended, &s1) + + five := big.NewInt(5) + p2.Double(&p1).Double(&p2).Add(&p2, &p1) + p1.ScalarMul(&p1, five) + + return p2.Equal(&p1) + }, + genS1, + )) + + // mixed affine+extended + properties.Property("(mixed affine+extended) P+(-P)=O", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, pExtended, p PointExtended + var pAffine PointAffine + baseExtended.FromAffine(¶ms.Base) + pExtended.ScalarMul(&baseExtended, &s) + pAffine.ScalarMul(¶ms.Base, &s) + pAffine.Neg(&pAffine) + + p.MixedAdd(&pExtended, &pAffine) + + return p.IsZero() + }, + genS1, + )) + + properties.Property("(mixed affine+extended) P+P=2*P", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, pExtended, p, p2 PointExtended + var pAffine PointAffine + baseExtended.FromAffine(¶ms.Base) + pExtended.ScalarMul(&baseExtended, &s) + pAffine.ScalarMul(¶ms.Base, &s) + + p.MixedAdd(&pExtended, &pAffine) + p2.MixedDouble(&pExtended) + + return p.Equal(&p2) + }, + genS1, + )) + + // mixed affine+projective + properties.Property("(mixed affine+proj) P+(-P)=O", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() @@ -376,12 +555,12 @@ func TestOps(t *testing.T) { p.MixedAdd(&pProj, &pAffine) - return p.X.IsZero() && p.Y.Equal(&p.Z) + return p.IsZero() }, genS1, )) - properties.Property("(mixed) P+P=2*P", prop.ForAll( + properties.Property("(mixed affine+proj) P+P=2*P", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() @@ -400,6 +579,27 @@ func TestOps(t *testing.T) { genS1, )) + properties.Property("scalar multiplication in Proj vs Ext should be consistant", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseProj PointProj + var baseExt PointExtended + var p1, p2 PointAffine + baseProj.FromAffine(¶ms.Base) + baseProj.ScalarMul(&baseProj, &s) + baseExt.FromAffine(¶ms.Base) + baseExt.ScalarMul(&baseExt, &s) + + p1.FromProj(&baseProj) + p2.FromExtended(&baseExt) + + return p1.Equal(&p2) + }, + genS1, + )) + properties.TestingRun(t, gopter.ConsoleReporter(false)) } @@ -437,7 +637,25 @@ func GenBigInt() gopter.Gen { // ------------------------------------------------------------ // benches -func BenchmarkScalarMul(b *testing.B) { +func BenchmarkScalarMulExtended(b *testing.B) { + params := GetEdwardsCurve() + var a PointExtended + var s big.Int + a.FromAffine(¶ms.Base) + s.SetString("52435875175126190479447705081859658376581184513", 10) + s.Add(&s, ¶ms.Order) + + var doubleAndAdd PointExtended + + b.Run("double and add", func(b *testing.B) { + b.ResetTimer() + for j := 0; j < b.N; j++ { + doubleAndAdd.ScalarMul(&a, &s) + } + }) +} + +func BenchmarkScalarMulProjective(b *testing.B) { params := GetEdwardsCurve() var a PointProj var s big.Int diff --git a/ecc/bls12-381/twistededwards/bandersnatch/point.go b/ecc/bls12-381/twistededwards/bandersnatch/point.go index f9339193c5..0b7d2c176d 100644 --- a/ecc/bls12-381/twistededwards/bandersnatch/point.go +++ b/ecc/bls12-381/twistededwards/bandersnatch/point.go @@ -40,6 +40,11 @@ type PointProj struct { X, Y, Z fr.Element } +// PointExtended point in extended coordinates +type PointExtended struct { + X, Y, Z, T fr.Element +} + const ( //following https://tools.ietf.org/html/rfc8032#section-3.1, // an fr element x is negative if its binary encoding is @@ -133,6 +138,11 @@ func (p *PointAffine) Unmarshal(b []byte) error { return err } +// IsZero returns true if p=0 false otherwise +func (p *PointProj) IsZero() bool { + return p.X.IsZero() && p.Y.Equal(&p.Z) +} + // Set sets p to p1 and return it func (p *PointProj) Set(p1 *PointProj) *PointProj { p.X.Set(&p1.X) @@ -148,6 +158,13 @@ func (p *PointAffine) Set(p1 *PointAffine) *PointAffine { return p } +// IsZero returns true if p=0 false otherwise +func (p *PointAffine) IsZero() bool { + var one fr.Element + one.SetOne() + return p.X.IsZero() && p.Y.Equal(&one) +} + // Equal returns true if p=p1 false otherwise func (p *PointAffine) Equal(p1 *PointAffine) bool { return p.X.Equal(&p1.X) && p.Y.Equal(&p1.Y) @@ -250,6 +267,15 @@ func (p *PointProj) Neg(p1 *PointProj) *PointProj { return p } +// FromExtended sets p in affine from p in extended coordinates +func (p *PointAffine) FromExtended(p1 *PointExtended) *PointAffine { + var I fr.Element + I.Inverse(&p1.Z) + p.X.Mul(&p1.X, &I) + p.Y.Mul(&p1.Y, &I) + return p +} + // FromProj sets p in affine from p in projective func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { p.X.Div(&p1.X, &p1.Z) @@ -505,3 +531,312 @@ func (p *PointAffine) ScalarMul(p1 *PointAffine, scalar *big.Int) *PointAffine { return p } + +// ------- Extended coordinates + +// Set sets p to p1 and return it +func (p *PointExtended) Set(p1 *PointExtended) *PointExtended { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.T.Set(&p1.T) + p.Z.Set(&p1.Z) + return p +} + +// IsZero returns true if p=0 false otherwise +func (p *PointExtended) IsZero() bool { + return p.X.IsZero() && p.Y.Equal(&p.Z) && p.T.IsZero() +} + +// Equal returns true if p=p1 false otherwise +// If one point is on the affine chart Z=0 it returns false +func (p *PointExtended) Equal(p1 *PointExtended) bool { + if p.Z.IsZero() || p1.Z.IsZero() { + return false + } + var pAffine, p1Affine PointAffine + pAffine.FromExtended(p) + p1Affine.FromExtended(p1) + return pAffine.Equal(&p1Affine) +} + +// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d +// modifies p +func (p *PointExtended) Neg(p1 *PointExtended) *PointExtended { + p.Set(p1) + p.X.Neg(&p.X) + p.T.Neg(&p.T) + return p +} + +// FromAffine sets p in projective from p in affine +func (p *PointExtended) FromAffine(p1 *PointAffine) *PointExtended { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.Z.SetOne() + p.T.Mul(&p1.X, &p1.Y) + return p +} + +// Add adds points in extended coordinates +// dedicated addition +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4 +func (p *PointExtended) Add(p1, p2 *PointExtended) *PointExtended { + + if p1.Equal(p2) { + p.Double(p1) + return p + } + + var A, B, C, D, E, F, G, H, tmp fr.Element + A.Mul(&p1.X, &p2.X) + B.Mul(&p1.Y, &p2.Y) + C.Mul(&p1.Z, &p2.T) + D.Mul(&p1.T, &p2.Z) + E.Add(&D, &C) + tmp.Sub(&p1.X, &p1.Y) + F.Add(&p2.X, &p2.Y). + Mul(&F, &tmp). + Add(&F, &B). + Sub(&F, &A) + G.Set(&A) + mulByA(&G) + G.Add(&G, &B) + H.Sub(&D, &C) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&E, &H) + p.Z.Mul(&F, &G) + + return p +} + +// MixedAdd adds a point in extended coordinates to a point in affine coordinates +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-madd-2008-hwcd-4 +func (p *PointExtended) MixedAdd(p1 *PointExtended, p2 *PointAffine) *PointExtended { + + var A, B, C, D, E, F, G, H, tmp fr.Element + + A.Mul(&p2.X, &p1.Z) + B.Mul(&p2.Y, &p1.Z) + + if p1.X.Equal(&A) && p1.Y.Equal(&B) { + p.MixedDouble(p1) + return p + } + + A.Mul(&p1.X, &p2.X) + B.Mul(&p1.Y, &p2.Y) + C.Mul(&p1.Z, &p2.X). + Mul(&C, &p2.Y) + D.Set(&p1.T) + E.Add(&D, &C) + tmp.Sub(&p1.X, &p1.Y) + F.Add(&p2.X, &p2.Y). + Mul(&F, &tmp). + Add(&F, &B). + Sub(&F, &A) + G.Set(&A) + mulByA(&G) + G.Add(&G, &B) + H.Sub(&D, &C) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&E, &H) + p.Z.Mul(&F, &G) + + return p +} + +// Double adds points in extended coordinates +// Dedicated doubling +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd +func (p *PointExtended) Double(p1 *PointExtended) *PointExtended { + + var A, B, C, D, E, F, G, H fr.Element + + A.Square(&p1.X) + B.Square(&p1.Y) + C.Square(&p1.Z). + Double(&C) + D.Set(&A) + mulByA(&D) + E.Add(&p1.X, &p1.Y). + Square(&E). + Sub(&E, &A). + Sub(&E, &B) + G.Add(&D, &B) + F.Sub(&G, &C) + H.Sub(&D, &B) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&H, &E) + p.Z.Mul(&F, &G) + + return p +} + +// MixedDouble adds points in extended coordinates +// Dedicated mixed doubling +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-mdbl-2008-hwcd +func (p *PointExtended) MixedDouble(p1 *PointExtended) *PointExtended { + + var A, B, D, E, G, H, two fr.Element + two.SetUint64(2) + + A.Square(&p1.X) + B.Square(&p1.Y) + D.Set(&A) + mulByA(&D) + E.Add(&p1.X, &p1.Y). + Square(&E). + Sub(&E, &A). + Sub(&E, &B) + G.Add(&D, &B) + H.Sub(&D, &B) + + p.X.Sub(&G, &two). + Mul(&p.X, &E) + p.Y.Mul(&G, &H) + p.T.Mul(&H, &E) + p.Z.Square(&G). + Sub(&p.Z, &G). + Sub(&p.Z, &G) + + return p +} + +// setInfinity sets p to O (0:1:1:0) +func (p *PointExtended) setInfinity() *PointExtended { + p.X.SetZero() + p.Y.SetOne() + p.Z.SetOne() + p.T.SetZero() + return p +} + +// ScalarMulBasic scalar multiplication of a point +// p1 in projective coordinates with a scalar in big.Int +func (p *PointExtended) ScalarMulBasic(p1 *PointExtended, scalar *big.Int) *PointExtended { + + var _scalar big.Int + _scalar.Set(scalar) + p.Set(p1) + if _scalar.Sign() == -1 { + _scalar.Neg(&_scalar) + p.Neg(p) + } + var resExtended PointExtended + resExtended.setInfinity() + const wordSize = bits.UintSize + sWords := _scalar.Bits() + + for i := len(sWords) - 1; i >= 0; i-- { + ithWord := sWords[i] + for k := 0; k < wordSize; k++ { + resExtended.Double(&resExtended) + kthBit := (ithWord >> (wordSize - 1 - k)) & 1 + if kthBit == 1 { + resExtended.Add(&resExtended, p) + } + } + } + + p.Set(&resExtended) + return p +} + +// phi endomorphism sqrt(-2) \in O(-8) +// (x,y,z)->\lambda*(x,y,z) s.t. \lamba^2 = -2 mod Order +func (p *PointExtended) phi(p1 *PointExtended) *PointExtended { + + ecurve := GetEdwardsCurve() + + var zz, yy, xy, f, g, h fr.Element + zz.Square(&p1.Z) + yy.Square(&p1.Y) + xy.Mul(&p1.X, &p1.Y) + f.Sub(&zz, &yy).Mul(&f, &ecurve.endo[1]) + zz.Mul(&zz, &ecurve.endo[0]) + g.Add(&yy, &zz).Mul(&g, &ecurve.endo[0]) + h.Sub(&yy, &zz) + + p.X.Mul(&f, &h) + p.Y.Mul(&g, &xy) + p.Z.Mul(&h, &xy) + p.T.Mul(&f, &g) + + return p +} + +// ScalarMul scalar multiplication (GLV) of a point +// p1 in projective coordinates with a scalar in big.Int +func (p *PointExtended) ScalarMul(p1 *PointExtended, scalar *big.Int) *PointExtended { + + ecurve := GetEdwardsCurve() + + var table [15]PointExtended + var zero big.Int + var res PointExtended + var k1, k2 fr.Element + + res.setInfinity() + + // table[b3b2b1b0-1] = b3b2*phi(p1) + b1b0*p1 + table[0].Set(p1) + table[3].phi(p1) + + // split the scalar, modifies +-p1, phi(p1) accordingly + k := ecc.SplitScalar(scalar, &ecurve.glvBasis) + + if k[0].Cmp(&zero) == -1 { + k[0].Neg(&k[0]) + table[0].Neg(&table[0]) + } + if k[1].Cmp(&zero) == -1 { + k[1].Neg(&k[1]) + table[3].Neg(&table[3]) + } + + // precompute table (2 bits sliding window) + // table[b3b2b1b0-1] = b3b2*phi(p1) + b1b0*p1 if b3b2b1b0 != 0 + table[1].Double(&table[0]) + table[2].Set(&table[1]).Add(&table[2], &table[0]) + table[4].Set(&table[3]).Add(&table[4], &table[0]) + table[5].Set(&table[3]).Add(&table[5], &table[1]) + table[6].Set(&table[3]).Add(&table[6], &table[2]) + table[7].Double(&table[3]) + table[8].Set(&table[7]).Add(&table[8], &table[0]) + table[9].Set(&table[7]).Add(&table[9], &table[1]) + table[10].Set(&table[7]).Add(&table[10], &table[2]) + table[11].Set(&table[7]).Add(&table[11], &table[3]) + table[12].Set(&table[11]).Add(&table[12], &table[0]) + table[13].Set(&table[11]).Add(&table[13], &table[1]) + table[14].Set(&table[11]).Add(&table[14], &table[2]) + + // bounds on the lattice base vectors guarantee that k1, k2 are len(r)/2 bits long max + k1.SetBigInt(&k[0]).FromMont() + k2.SetBigInt(&k[1]).FromMont() + + // loop starts from len(k1)/2 due to the bounds + // fr.Limbs == Order.limbs + for i := int(math.Ceil(fr.Limbs/2. - 1)); i >= 0; i-- { + mask := uint64(3) << 62 + for j := 0; j < 32; j++ { + res.Double(&res).Double(&res) + b1 := (k1[i] & mask) >> (62 - 2*j) + b2 := (k2[i] & mask) >> (62 - 2*j) + if b1|b2 != 0 { + scalar := (b2<<2 | b1) + res.Add(&res, &table[scalar-1]) + } + mask = mask >> 2 + } + } + + p.Set(&res) + return p +} diff --git a/ecc/bls12-381/twistededwards/bandersnatch/twistededwards_test.go b/ecc/bls12-381/twistededwards/bandersnatch/twistededwards_test.go index 62810a998e..59b1c35a7b 100644 --- a/ecc/bls12-381/twistededwards/bandersnatch/twistededwards_test.go +++ b/ecc/bls12-381/twistededwards/bandersnatch/twistededwards_test.go @@ -123,7 +123,7 @@ func TestReceiverIsOperand(t *testing.T) { )) properties.TestingRun(t, gopter.ConsoleReporter(false)) - // proj + // projective properties.Property("Equal projective: having the receiver as operand should output the same result", prop.ForAll( func() bool { params := GetEdwardsCurve() @@ -191,51 +191,72 @@ func TestReceiverIsOperand(t *testing.T) { }, )) - properties.TestingRun(t, gopter.ConsoleReporter(false)) + // extended + properties.Property("Equal extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + params := GetEdwardsCurve() + var p1, baseProj PointProj + p1.FromAffine(¶ms.Base) + baseProj.FromAffine(¶ms.Base) -} + return p1.Equal(&p1) && p1.Equal(&baseProj) + }, + )) -func TestEndomorphism(t *testing.T) { + properties.Property("Add extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { - parameters := gopter.DefaultTestParameters() - parameters.MinSuccessfulTests = 1 + params := GetEdwardsCurve() - properties := gopter.NewProperties(parameters) - genS := GenBigInt() + var p1, p2, p3 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + p3.FromAffine(¶ms.Base) - properties.Property("phi^2+2=0 mod Order", prop.ForAll( - func(s big.Int) bool { + res := true + + p3.Add(&p1, &p2) + p1.Add(&p1, &p2) + res = res && p3.Equal(&p1) + + p1.FromAffine(¶ms.Base) + p2.Add(&p1, &p2) + res = res && p2.Equal(&p3) + + return res + }, + )) + + properties.Property("Double extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { params := GetEdwardsCurve() - var p, p1, p2 PointProj - p.FromAffine(¶ms.Base). - ScalarMul(&p, &s) + var p1, p2 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) - p1.phi(&p). - phi(&p1) - p2.Double(&p).Neg(&p2) + p2.Double(&p1) + p1.Double(&p1) - return p1.Equal(&p2) + return p2.Equal(&p1) }, - genS, )) - properties.Property("(projective) phi(P)=lambda*P", prop.ForAll( - func(s big.Int) bool { + properties.Property("Neg extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { params := GetEdwardsCurve() - var p, p1, p2 PointProj - p.FromAffine(¶ms.Base). - ScalarMul(&p, &s) + var p1, p2 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) - p1.ScalarMul(&p, ¶ms.lambda) - p2.phi(&p) + p2.Neg(&p1) + p1.Neg(&p1) - return p1.Equal(&p2) + return p2.Equal(&p1) }, - genS, )) properties.TestingRun(t, gopter.ConsoleReporter(false)) @@ -291,7 +312,7 @@ func TestOps(t *testing.T) { var one fr.Element one.SetOne() - return p1.IsOnCurve() && p1.X.IsZero() && p1.Y.Equal(&one) + return p1.IsOnCurve() && p1.IsZero() }, genS1, )) @@ -355,7 +376,7 @@ func TestOps(t *testing.T) { genS1, )) - properties.Property("[5]P=[2][2]P+P", prop.ForAll( + properties.Property("(affine) [5]P=[2][2]P+P", prop.ForAll( func(s1 big.Int) bool { params := GetEdwardsCurve() @@ -372,7 +393,7 @@ func TestOps(t *testing.T) { genS1, )) - // proj + // projective properties.Property("(projective) P+(-P)=O", prop.ForAll( func(s1 big.Int) bool { @@ -385,7 +406,7 @@ func TestOps(t *testing.T) { p.Add(&p1, &p2) - return p.X.IsZero() && p.Y.Equal(&p.Z) + return p.IsZero() }, genS1, )) @@ -408,25 +429,119 @@ func TestOps(t *testing.T) { genS1, )) - properties.Property("(projective) GLV should match basic scalar multiplication", prop.ForAll( - - func(s big.Int) bool { + properties.Property("(projective) [5]P=[2][2]P+P", prop.ForAll( + func(s1 big.Int) bool { params := GetEdwardsCurve() var baseProj, p1, p2 PointProj baseProj.FromAffine(¶ms.Base) + p1.ScalarMul(&baseProj, &s1) + + five := big.NewInt(5) + p2.Double(&p1).Double(&p2).Add(&p2, &p1) + p1.ScalarMul(&p1, five) + + return p2.Equal(&p1) + }, + genS1, + )) + + // extended + properties.Property("(extended) P+(-P)=O", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2, p PointExtended + baseExtended.FromAffine(¶ms.Base) + p1.ScalarMul(&baseExtended, &s1) + p2.Neg(&p1) + + p.Add(&p1, &p2) + + return p.IsZero() + }, + genS1, + )) + + properties.Property("(extended) P+P=2*P", prop.ForAll( + + func(s big.Int) bool { + + params := GetEdwardsCurve() - p1.ScalarMulBasic(&baseProj, &s) - p2.ScalarMul(&baseProj, &s) + var baseExtended, p1, p2, p PointExtended + baseExtended.FromAffine(¶ms.Base) + p.ScalarMul(&baseExtended, &s) + + p1.Add(&p, &p) + p2.Double(&p) return p1.Equal(&p2) }, genS1, )) - // mixed - properties.Property("(mixed) P+(-P)=O", prop.ForAll( + properties.Property("(extended) [5]P=[2][2]P+P", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2 PointExtended + baseExtended.FromAffine(¶ms.Base) + p1.ScalarMul(&baseExtended, &s1) + + five := big.NewInt(5) + p2.Double(&p1).Double(&p2).Add(&p2, &p1) + p1.ScalarMul(&p1, five) + + return p2.Equal(&p1) + }, + genS1, + )) + + // mixed affine+extended + properties.Property("(mixed affine+extended) P+(-P)=O", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, pExtended, p PointExtended + var pAffine PointAffine + baseExtended.FromAffine(¶ms.Base) + pExtended.ScalarMul(&baseExtended, &s) + pAffine.ScalarMul(¶ms.Base, &s) + pAffine.Neg(&pAffine) + + p.MixedAdd(&pExtended, &pAffine) + + return p.IsZero() + }, + genS1, + )) + + properties.Property("(mixed affine+extended) P+P=2*P", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, pExtended, p, p2 PointExtended + var pAffine PointAffine + baseExtended.FromAffine(¶ms.Base) + pExtended.ScalarMul(&baseExtended, &s) + pAffine.ScalarMul(¶ms.Base, &s) + + p.MixedAdd(&pExtended, &pAffine) + p2.MixedDouble(&pExtended) + + return p.Equal(&p2) + }, + genS1, + )) + + // mixed affine+projective + properties.Property("(mixed affine+proj) P+(-P)=O", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() @@ -440,12 +555,12 @@ func TestOps(t *testing.T) { p.MixedAdd(&pProj, &pAffine) - return p.X.IsZero() && p.Y.Equal(&p.Z) + return p.IsZero() }, genS1, )) - properties.Property("(mixed) P+P=2*P", prop.ForAll( + properties.Property("(mixed affine+proj) P+P=2*P", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() @@ -464,6 +579,48 @@ func TestOps(t *testing.T) { genS1, )) + properties.Property("Basic scalar multiplication in Proj vs Ext should be consistant", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseProj PointProj + var baseExt PointExtended + var p1, p2 PointAffine + baseProj.FromAffine(¶ms.Base) + baseProj.ScalarMulBasic(&baseProj, &s) + baseExt.FromAffine(¶ms.Base) + baseExt.ScalarMulBasic(&baseExt, &s) + + p1.FromProj(&baseProj) + p2.FromExtended(&baseExt) + + return p1.Equal(&p2) + }, + genS1, + )) + + properties.Property("GLV scalar multiplication in Proj vs Ext should be consistant", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseProj PointProj + var baseExt PointExtended + var p1, p2 PointAffine + baseProj.FromAffine(¶ms.Base) + baseProj.ScalarMul(&baseProj, &s) + baseExt.FromAffine(¶ms.Base) + baseExt.ScalarMul(&baseExt, &s) + + p1.FromProj(&baseProj) + p2.FromExtended(&baseExt) + + return p1.Equal(&p2) + }, + genS1, + )) + properties.TestingRun(t, gopter.ConsoleReporter(false)) } @@ -501,28 +658,38 @@ func GenBigInt() gopter.Gen { // ------------------------------------------------------------ // benches -func BenchmarkScalarMul(b *testing.B) { +func BenchmarkScalarMulExtended(b *testing.B) { params := GetEdwardsCurve() - var a PointProj + var a PointExtended var s big.Int a.FromAffine(¶ms.Base) s.SetString("52435875175126190479447705081859658376581184513", 10) s.Add(&s, ¶ms.Order) - var doubleAndAdd PointProj + var doubleAndAdd PointExtended b.Run("double and add", func(b *testing.B) { b.ResetTimer() for j := 0; j < b.N; j++ { - doubleAndAdd.ScalarMulBasic(&a, &s) + doubleAndAdd.ScalarMul(&a, &s) } }) +} - var glv PointProj - b.Run("GLV", func(b *testing.B) { +func BenchmarkScalarMulProjective(b *testing.B) { + params := GetEdwardsCurve() + var a PointProj + var s big.Int + a.FromAffine(¶ms.Base) + s.SetString("52435875175126190479447705081859658376581184513", 10) + s.Add(&s, ¶ms.Order) + + var doubleAndAdd PointProj + + b.Run("double and add", func(b *testing.B) { b.ResetTimer() for j := 0; j < b.N; j++ { - glv.ScalarMul(&a, &s) + doubleAndAdd.ScalarMul(&a, &s) } }) } diff --git a/ecc/bls12-381/twistededwards/point.go b/ecc/bls12-381/twistededwards/point.go index e9d1da52f9..665c6d7bfb 100644 --- a/ecc/bls12-381/twistededwards/point.go +++ b/ecc/bls12-381/twistededwards/point.go @@ -35,6 +35,11 @@ type PointProj struct { X, Y, Z fr.Element } +// PointExtended point in extended coordinates +type PointExtended struct { + X, Y, Z, T fr.Element +} + const ( //following https://tools.ietf.org/html/rfc8032#section-3.1, // an fr element x is negative if its binary encoding is @@ -128,14 +133,6 @@ func (p *PointAffine) Unmarshal(b []byte) error { return err } -// Set sets p to p1 and return it -func (p *PointProj) Set(p1 *PointProj) *PointProj { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - p.Z.Set(&p1.Z) - return p -} - // Set sets p to p1 and return it func (p *PointAffine) Set(p1 *PointAffine) *PointAffine { p.X.Set(&p1.X) @@ -148,16 +145,11 @@ func (p *PointAffine) Equal(p1 *PointAffine) bool { return p.X.Equal(&p1.X) && p.Y.Equal(&p1.Y) } -// Equal returns true if p=p1 false otherwise -// If one point is on the affine chart Z=0 it returns false -func (p *PointProj) Equal(p1 *PointProj) bool { - if p.Z.IsZero() || p1.Z.IsZero() { - return false - } - var pAffine, p1Affine PointAffine - pAffine.FromProj(p) - p1Affine.FromProj(p1) - return pAffine.Equal(&p1Affine) +// IsZero returns true if p=0 false otherwise +func (p *PointAffine) IsZero() bool { + var one fr.Element + one.SetOne() + return p.X.IsZero() && p.Y.Equal(&one) } // NewPointAffine creates a new instance of PointAffine @@ -186,6 +178,13 @@ func (p *PointAffine) IsOnCurve() bool { return lhs.Equal(&rhs) } +// Neg sets p to -p1 and returns it +func (p *PointAffine) Neg(p1 *PointAffine) *PointAffine { + p.Set(p1) + p.X.Neg(&p.X) + return p +} + // Add adds two points (x,y), (u,v) on a twisted Edwards curve with parameters a, d // modifies p func (p *PointAffine) Add(p1, p2 *PointAffine) *PointAffine { @@ -237,60 +236,84 @@ func (p *PointAffine) Double(p1 *PointAffine) *PointAffine { return p } -// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d -// modifies p -func (p *PointProj) Neg(p1 *PointProj) *PointProj { - p.Set(p1) - p.X.Neg(&p.X) +// FromProj sets p in affine from p in projective +func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { + var I fr.Element + I.Inverse(&p1.Z) + p.X.Mul(&p1.X, &I) + p.Y.Mul(&p1.Y, &I) return p } -// FromProj sets p in affine from p in projective -func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { - p.X.Div(&p1.X, &p1.Z) - p.Y.Div(&p1.Y, &p1.Z) +// FromExtended sets p in affine from p in extended coordinates +func (p *PointAffine) FromExtended(p1 *PointExtended) *PointAffine { + var I fr.Element + I.Inverse(&p1.Z) + p.X.Mul(&p1.X, &I) + p.Y.Mul(&p1.Y, &I) return p } -// FromAffine sets p in projective from p in affine -func (p *PointProj) FromAffine(p1 *PointAffine) *PointProj { +// ScalarMul scalar multiplication of a point +// p1 in affine coordinates with a scalar in big.Int +func (p *PointAffine) ScalarMul(p1 *PointAffine, scalar *big.Int) *PointAffine { + + var p1Extended, resExtended PointExtended + p1Extended.FromAffine(p1) + resExtended.ScalarMul(&p1Extended, scalar) + p.FromExtended(&resExtended) + + return p +} + +//-------- Projective coordinates + +// Set sets p to p1 and return it +func (p *PointProj) Set(p1 *PointProj) *PointProj { p.X.Set(&p1.X) p.Y.Set(&p1.Y) - p.Z.SetOne() + p.Z.Set(&p1.Z) return p } -// Add adds points in projective coordinates -// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp -func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { +// setInfinity sets p to O (0:1:1) +func (p *PointProj) setInfinity() *PointProj { + p.X.SetZero() + p.Y.SetOne() + p.Z.SetOne() + return p +} - var res PointProj +// Equal returns true if p=p1 false otherwise +// If one point is on the affine chart Z=0 it returns false +func (p *PointProj) Equal(p1 *PointProj) bool { + if p.Z.IsZero() || p1.Z.IsZero() { + return false + } + var pAffine, p1Affine PointAffine + pAffine.FromProj(p) + p1Affine.FromProj(p1) + return pAffine.Equal(&p1Affine) +} - ecurve := GetEdwardsCurve() +// IsZero returns true if p=0 false otherwise +func (p *PointProj) IsZero() bool { + return p.X.IsZero() && p.Y.Equal(&p.Z) +} - var A, B, C, D, E, F, G, H, I fr.Element - A.Mul(&p1.Z, &p2.Z) - B.Square(&A) - C.Mul(&p1.X, &p2.X) - D.Mul(&p1.Y, &p2.Y) - E.Mul(&ecurve.D, &C).Mul(&E, &D) - F.Sub(&B, &E) - G.Add(&B, &E) - H.Add(&p1.X, &p1.Y) - I.Add(&p2.X, &p2.Y) - res.X.Mul(&H, &I). - Sub(&res.X, &C). - Sub(&res.X, &D). - Mul(&res.X, &A). - Mul(&res.X, &F) - mulByA(&C) - C.Neg(&C) - res.Y.Add(&D, &C). - Mul(&res.Y, &A). - Mul(&res.Y, &G) - res.Z.Mul(&F, &G) +// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d +// modifies p +func (p *PointProj) Neg(p1 *PointProj) *PointProj { + p.Set(p1) + p.X.Neg(&p.X) + return p +} - p.Set(&res) +// FromAffine sets p in projective from p in affine +func (p *PointProj) FromAffine(p1 *PointAffine) *PointProj { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.Z.SetOne() return p } @@ -298,8 +321,6 @@ func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { // cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-madd-2008-bbjlp func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { - var res PointProj - ecurve := GetEdwardsCurve() var B, C, D, E, F, G, H, I fr.Element @@ -311,18 +332,17 @@ func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { G.Add(&B, &E) H.Add(&p1.X, &p1.Y) I.Add(&p2.X, &p2.Y) - res.X.Mul(&H, &I). - Sub(&res.X, &C). - Sub(&res.X, &D). - Mul(&res.X, &p1.Z). - Mul(&res.X, &F) + p.X.Mul(&H, &I). + Sub(&p.X, &C). + Sub(&p.X, &D). + Mul(&p.X, &p1.Z). + Mul(&p.X, &F) mulByA(&C) - res.Y.Sub(&D, &C). - Mul(&res.Y, &p1.Z). - Mul(&res.Y, &G) - res.Z.Mul(&F, &G) + p.Y.Sub(&D, &C). + Mul(&p.Y, &p1.Z). + Mul(&p.Y, &G) + p.Z.Mul(&F, &G) - p.Set(&res) return p } @@ -330,8 +350,6 @@ func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { // cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#doubling-dbl-2008-bbjlp func (p *PointProj) Double(p1 *PointProj) *PointProj { - var res PointProj - var B, C, D, E, F, H, J fr.Element B.Add(&p1.X, &p1.Y).Square(&B) @@ -342,28 +360,43 @@ func (p *PointProj) Double(p1 *PointProj) *PointProj { F.Add(&E, &D) H.Square(&p1.Z) J.Sub(&F, &H).Sub(&J, &H) - res.X.Sub(&B, &C). - Sub(&res.X, &D). - Mul(&res.X, &J) - res.Y.Sub(&E, &D).Mul(&res.Y, &F) - res.Z.Mul(&F, &J) + p.X.Sub(&B, &C). + Sub(&p.X, &D). + Mul(&p.X, &J) + p.Y.Sub(&E, &D).Mul(&p.Y, &F) + p.Z.Mul(&F, &J) - p.Set(&res) return p } -// Neg sets p to -p1 and returns it -func (p *PointAffine) Neg(p1 *PointAffine) *PointAffine { - p.Set(p1) - p.X.Neg(&p.X) - return p -} +// Add adds points in projective coordinates +// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp +func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { + + ecurve := GetEdwardsCurve() + + var A, B, C, D, E, F, G, H, I fr.Element + A.Mul(&p1.Z, &p2.Z) + B.Square(&A) + C.Mul(&p1.X, &p2.X) + D.Mul(&p1.Y, &p2.Y) + E.Mul(&ecurve.D, &C).Mul(&E, &D) + F.Sub(&B, &E) + G.Add(&B, &E) + H.Add(&p1.X, &p1.Y) + I.Add(&p2.X, &p2.Y) + p.X.Mul(&H, &I). + Sub(&p.X, &C). + Sub(&p.X, &D). + Mul(&p.X, &A). + Mul(&p.X, &F) + mulByA(&C) + C.Neg(&C) + p.Y.Add(&D, &C). + Mul(&p.Y, &A). + Mul(&p.Y, &G) + p.Z.Mul(&F, &G) -// setInfinity sets p to O (0:1:1) -func (p *PointProj) setInfinity() *PointProj { - p.X.SetZero() - p.Y.SetOne() - p.Z.SetOne() return p } @@ -398,14 +431,218 @@ func (p *PointProj) ScalarMul(p1 *PointProj, scalar *big.Int) *PointProj { return p } +// ------- Extended coordinates + +// Set sets p to p1 and return it +func (p *PointExtended) Set(p1 *PointExtended) *PointExtended { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.T.Set(&p1.T) + p.Z.Set(&p1.Z) + return p +} + +// IsZero returns true if p=0 false otherwise +func (p *PointExtended) IsZero() bool { + return p.X.IsZero() && p.Y.Equal(&p.Z) && p.T.IsZero() +} + +// Equal returns true if p=p1 false otherwise +// If one point is on the affine chart Z=0 it returns false +func (p *PointExtended) Equal(p1 *PointExtended) bool { + if p.Z.IsZero() || p1.Z.IsZero() { + return false + } + var pAffine, p1Affine PointAffine + pAffine.FromExtended(p) + p1Affine.FromExtended(p1) + return pAffine.Equal(&p1Affine) +} + +// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d +// modifies p +func (p *PointExtended) Neg(p1 *PointExtended) *PointExtended { + p.Set(p1) + p.X.Neg(&p.X) + p.T.Neg(&p.T) + return p +} + +// FromAffine sets p in projective from p in affine +func (p *PointExtended) FromAffine(p1 *PointAffine) *PointExtended { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.Z.SetOne() + p.T.Mul(&p1.X, &p1.Y) + return p +} + +// Add adds points in extended coordinates +// dedicated addition +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4 +func (p *PointExtended) Add(p1, p2 *PointExtended) *PointExtended { + + if p1.Equal(p2) { + p.Double(p1) + return p + } + + var A, B, C, D, E, F, G, H, tmp fr.Element + tmp.Add(&p2.X, &p2.Y) + A.Sub(&p1.Y, &p1.X). + Mul(&A, &tmp) + tmp.Add(&p1.X, &p1.Y) + B.Sub(&p2.Y, &p2.X). + Mul(&B, &tmp) + C.Mul(&p1.Z, &p2.T). + Double(&C) + D.Mul(&p2.Z, &p1.T). + Double(&D) + E.Add(&D, &C) + F.Sub(&B, &A) + G.Add(&B, &A) + H.Sub(&D, &C) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&E, &H) + p.Z.Mul(&F, &G) + + return p +} + +// MixedAdd adds a point in extended coordinates to a point in affine coordinates +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-madd-2008-hwcd-4 +func (p *PointExtended) MixedAdd(p1 *PointExtended, p2 *PointAffine) *PointExtended { + + var A, B, C, D, E, F, G, H, tmp fr.Element + + A.Mul(&p2.X, &p1.Z) + B.Mul(&p2.Y, &p1.Z) + + if p1.X.Equal(&A) && p1.Y.Equal(&B) { + p.MixedDouble(p1) + return p + } + + tmp.Add(&p2.X, &p2.Y) + A.Sub(&p1.Y, &p1.X). + Mul(&A, &tmp) + tmp.Add(&p1.X, &p1.Y) + B.Sub(&p2.Y, &p2.X). + Mul(&B, &tmp) + C.Mul(&p1.Z, &p2.X). + Mul(&C, &p2.Y). + Double(&C) + D.Double(&p1.T) + E.Add(&D, &C) + F.Sub(&B, &A) + G.Add(&B, &A) + H.Sub(&D, &C) + + p.X.Mul(&F, &E) + p.Y.Mul(&G, &H) + p.T.Mul(&E, &H) + p.Z.Mul(&G, &F) + + return p +} + +// Double adds points in extended coordinates +// Dedicated doubling +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd +func (p *PointExtended) Double(p1 *PointExtended) *PointExtended { + + var A, B, C, D, E, F, G, H fr.Element + + A.Square(&p1.X) + B.Square(&p1.Y) + C.Square(&p1.Z). + Double(&C) + D.Set(&A) + mulByA(&D) + E.Add(&p1.X, &p1.Y). + Square(&E). + Sub(&E, &A). + Sub(&E, &B) + G.Add(&D, &B) + F.Sub(&G, &C) + H.Sub(&D, &B) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&H, &E) + p.Z.Mul(&F, &G) + + return p +} + +// MixedDouble adds points in extended coordinates +// Dedicated mixed doubling +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-mdbl-2008-hwcd +func (p *PointExtended) MixedDouble(p1 *PointExtended) *PointExtended { + + var A, B, D, E, G, H, two fr.Element + two.SetUint64(2) + + A.Square(&p1.X) + B.Square(&p1.Y) + D.Set(&A) + mulByA(&D) + E.Add(&p1.X, &p1.Y). + Square(&E). + Sub(&E, &A). + Sub(&E, &B) + G.Add(&D, &B) + H.Sub(&D, &B) + + p.X.Sub(&G, &two). + Mul(&p.X, &E) + p.Y.Mul(&G, &H) + p.T.Mul(&H, &E) + p.Z.Square(&G). + Sub(&p.Z, &G). + Sub(&p.Z, &G) + + return p +} + +// setInfinity sets p to O (0:1:1:0) +func (p *PointExtended) setInfinity() *PointExtended { + p.X.SetZero() + p.Y.SetOne() + p.Z.SetOne() + p.T.SetZero() + return p +} + // ScalarMul scalar multiplication of a point -// p1 in affine coordinates with a scalar in big.Int -func (p *PointAffine) ScalarMul(p1 *PointAffine, scalar *big.Int) *PointAffine { +// p1 in extended coordinates with a scalar in big.Int +func (p *PointExtended) ScalarMul(p1 *PointExtended, scalar *big.Int) *PointExtended { + + var _scalar big.Int + _scalar.Set(scalar) + p.Set(p1) + if _scalar.Sign() == -1 { + _scalar.Neg(&_scalar) + p.Neg(p) + } + var resExtended PointExtended + resExtended.setInfinity() + const wordSize = bits.UintSize + sWords := _scalar.Bits() - var p1Proj, resProj PointProj - p1Proj.FromAffine(p1) - resProj.ScalarMul(&p1Proj, scalar) - p.FromProj(&resProj) + for i := len(sWords) - 1; i >= 0; i-- { + ithWord := sWords[i] + for k := 0; k < wordSize; k++ { + resExtended.Double(&resExtended) + kthBit := (ithWord >> (wordSize - 1 - k)) & 1 + if kthBit == 1 { + resExtended.Add(&resExtended, p) + } + } + } + p.Set(&resExtended) return p } diff --git a/ecc/bls12-381/twistededwards/twistededwards_test.go b/ecc/bls12-381/twistededwards/twistededwards_test.go index 26120f59a0..1c8899a21e 100644 --- a/ecc/bls12-381/twistededwards/twistededwards_test.go +++ b/ecc/bls12-381/twistededwards/twistededwards_test.go @@ -123,7 +123,7 @@ func TestReceiverIsOperand(t *testing.T) { )) properties.TestingRun(t, gopter.ConsoleReporter(false)) - // proj + // projective properties.Property("Equal projective: having the receiver as operand should output the same result", prop.ForAll( func() bool { params := GetEdwardsCurve() @@ -191,6 +191,74 @@ func TestReceiverIsOperand(t *testing.T) { }, )) + // extended + properties.Property("Equal extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + params := GetEdwardsCurve() + var p1, baseProj PointProj + p1.FromAffine(¶ms.Base) + baseProj.FromAffine(¶ms.Base) + + return p1.Equal(&p1) && p1.Equal(&baseProj) + }, + )) + + properties.Property("Add extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2, p3 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + p3.FromAffine(¶ms.Base) + + res := true + + p3.Add(&p1, &p2) + p1.Add(&p1, &p2) + res = res && p3.Equal(&p1) + + p1.FromAffine(¶ms.Base) + p2.Add(&p1, &p2) + res = res && p2.Equal(&p3) + + return res + }, + )) + + properties.Property("Double extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + + p2.Double(&p1) + p1.Double(&p1) + + return p2.Equal(&p1) + }, + )) + + properties.Property("Neg extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + + p2.Neg(&p1) + p1.Neg(&p1) + + return p2.Equal(&p1) + }, + )) + properties.TestingRun(t, gopter.ConsoleReporter(false)) } @@ -244,7 +312,7 @@ func TestOps(t *testing.T) { var one fr.Element one.SetOne() - return p1.IsOnCurve() && p1.X.IsZero() && p1.Y.Equal(&one) + return p1.IsOnCurve() && p1.IsZero() }, genS1, )) @@ -308,7 +376,7 @@ func TestOps(t *testing.T) { genS1, )) - properties.Property("[5]P=[2][2]P+P", prop.ForAll( + properties.Property("(affine) [5]P=[2][2]P+P", prop.ForAll( func(s1 big.Int) bool { params := GetEdwardsCurve() @@ -325,7 +393,7 @@ func TestOps(t *testing.T) { genS1, )) - // proj + // projective properties.Property("(projective) P+(-P)=O", prop.ForAll( func(s1 big.Int) bool { @@ -338,7 +406,7 @@ func TestOps(t *testing.T) { p.Add(&p1, &p2) - return p.X.IsZero() && p.Y.Equal(&p.Z) + return p.IsZero() }, genS1, )) @@ -361,8 +429,119 @@ func TestOps(t *testing.T) { genS1, )) - // mixed - properties.Property("(mixed) P+(-P)=O", prop.ForAll( + properties.Property("(projective) [5]P=[2][2]P+P", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseProj, p1, p2 PointProj + baseProj.FromAffine(¶ms.Base) + p1.ScalarMul(&baseProj, &s1) + + five := big.NewInt(5) + p2.Double(&p1).Double(&p2).Add(&p2, &p1) + p1.ScalarMul(&p1, five) + + return p2.Equal(&p1) + }, + genS1, + )) + + // extended + properties.Property("(extended) P+(-P)=O", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2, p PointExtended + baseExtended.FromAffine(¶ms.Base) + p1.ScalarMul(&baseExtended, &s1) + p2.Neg(&p1) + + p.Add(&p1, &p2) + + return p.IsZero() + }, + genS1, + )) + + properties.Property("(extended) P+P=2*P", prop.ForAll( + + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2, p PointExtended + baseExtended.FromAffine(¶ms.Base) + p.ScalarMul(&baseExtended, &s) + + p1.Add(&p, &p) + p2.Double(&p) + + return p1.Equal(&p2) + }, + genS1, + )) + + properties.Property("(extended) [5]P=[2][2]P+P", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2 PointExtended + baseExtended.FromAffine(¶ms.Base) + p1.ScalarMul(&baseExtended, &s1) + + five := big.NewInt(5) + p2.Double(&p1).Double(&p2).Add(&p2, &p1) + p1.ScalarMul(&p1, five) + + return p2.Equal(&p1) + }, + genS1, + )) + + // mixed affine+extended + properties.Property("(mixed affine+extended) P+(-P)=O", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, pExtended, p PointExtended + var pAffine PointAffine + baseExtended.FromAffine(¶ms.Base) + pExtended.ScalarMul(&baseExtended, &s) + pAffine.ScalarMul(¶ms.Base, &s) + pAffine.Neg(&pAffine) + + p.MixedAdd(&pExtended, &pAffine) + + return p.IsZero() + }, + genS1, + )) + + properties.Property("(mixed affine+extended) P+P=2*P", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, pExtended, p, p2 PointExtended + var pAffine PointAffine + baseExtended.FromAffine(¶ms.Base) + pExtended.ScalarMul(&baseExtended, &s) + pAffine.ScalarMul(¶ms.Base, &s) + + p.MixedAdd(&pExtended, &pAffine) + p2.MixedDouble(&pExtended) + + return p.Equal(&p2) + }, + genS1, + )) + + // mixed affine+projective + properties.Property("(mixed affine+proj) P+(-P)=O", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() @@ -376,12 +555,12 @@ func TestOps(t *testing.T) { p.MixedAdd(&pProj, &pAffine) - return p.X.IsZero() && p.Y.Equal(&p.Z) + return p.IsZero() }, genS1, )) - properties.Property("(mixed) P+P=2*P", prop.ForAll( + properties.Property("(mixed affine+proj) P+P=2*P", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() @@ -400,6 +579,27 @@ func TestOps(t *testing.T) { genS1, )) + properties.Property("scalar multiplication in Proj vs Ext should be consistant", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseProj PointProj + var baseExt PointExtended + var p1, p2 PointAffine + baseProj.FromAffine(¶ms.Base) + baseProj.ScalarMul(&baseProj, &s) + baseExt.FromAffine(¶ms.Base) + baseExt.ScalarMul(&baseExt, &s) + + p1.FromProj(&baseProj) + p2.FromExtended(&baseExt) + + return p1.Equal(&p2) + }, + genS1, + )) + properties.TestingRun(t, gopter.ConsoleReporter(false)) } @@ -437,7 +637,25 @@ func GenBigInt() gopter.Gen { // ------------------------------------------------------------ // benches -func BenchmarkScalarMul(b *testing.B) { +func BenchmarkScalarMulExtended(b *testing.B) { + params := GetEdwardsCurve() + var a PointExtended + var s big.Int + a.FromAffine(¶ms.Base) + s.SetString("52435875175126190479447705081859658376581184513", 10) + s.Add(&s, ¶ms.Order) + + var doubleAndAdd PointExtended + + b.Run("double and add", func(b *testing.B) { + b.ResetTimer() + for j := 0; j < b.N; j++ { + doubleAndAdd.ScalarMul(&a, &s) + } + }) +} + +func BenchmarkScalarMulProjective(b *testing.B) { params := GetEdwardsCurve() var a PointProj var s big.Int diff --git a/ecc/bls24-315/twistededwards/point.go b/ecc/bls24-315/twistededwards/point.go index 1ffe865f3c..e75da069a3 100644 --- a/ecc/bls24-315/twistededwards/point.go +++ b/ecc/bls24-315/twistededwards/point.go @@ -35,6 +35,11 @@ type PointProj struct { X, Y, Z fr.Element } +// PointExtended point in extended coordinates +type PointExtended struct { + X, Y, Z, T fr.Element +} + const ( //following https://tools.ietf.org/html/rfc8032#section-3.1, // an fr element x is negative if its binary encoding is @@ -128,14 +133,6 @@ func (p *PointAffine) Unmarshal(b []byte) error { return err } -// Set sets p to p1 and return it -func (p *PointProj) Set(p1 *PointProj) *PointProj { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - p.Z.Set(&p1.Z) - return p -} - // Set sets p to p1 and return it func (p *PointAffine) Set(p1 *PointAffine) *PointAffine { p.X.Set(&p1.X) @@ -148,16 +145,11 @@ func (p *PointAffine) Equal(p1 *PointAffine) bool { return p.X.Equal(&p1.X) && p.Y.Equal(&p1.Y) } -// Equal returns true if p=p1 false otherwise -// If one point is on the affine chart Z=0 it returns false -func (p *PointProj) Equal(p1 *PointProj) bool { - if p.Z.IsZero() || p1.Z.IsZero() { - return false - } - var pAffine, p1Affine PointAffine - pAffine.FromProj(p) - p1Affine.FromProj(p1) - return pAffine.Equal(&p1Affine) +// IsZero returns true if p=0 false otherwise +func (p *PointAffine) IsZero() bool { + var one fr.Element + one.SetOne() + return p.X.IsZero() && p.Y.Equal(&one) } // NewPointAffine creates a new instance of PointAffine @@ -186,6 +178,13 @@ func (p *PointAffine) IsOnCurve() bool { return lhs.Equal(&rhs) } +// Neg sets p to -p1 and returns it +func (p *PointAffine) Neg(p1 *PointAffine) *PointAffine { + p.Set(p1) + p.X.Neg(&p.X) + return p +} + // Add adds two points (x,y), (u,v) on a twisted Edwards curve with parameters a, d // modifies p func (p *PointAffine) Add(p1, p2 *PointAffine) *PointAffine { @@ -237,60 +236,84 @@ func (p *PointAffine) Double(p1 *PointAffine) *PointAffine { return p } -// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d -// modifies p -func (p *PointProj) Neg(p1 *PointProj) *PointProj { - p.Set(p1) - p.X.Neg(&p.X) +// FromProj sets p in affine from p in projective +func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { + var I fr.Element + I.Inverse(&p1.Z) + p.X.Mul(&p1.X, &I) + p.Y.Mul(&p1.Y, &I) return p } -// FromProj sets p in affine from p in projective -func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { - p.X.Div(&p1.X, &p1.Z) - p.Y.Div(&p1.Y, &p1.Z) +// FromExtended sets p in affine from p in extended coordinates +func (p *PointAffine) FromExtended(p1 *PointExtended) *PointAffine { + var I fr.Element + I.Inverse(&p1.Z) + p.X.Mul(&p1.X, &I) + p.Y.Mul(&p1.Y, &I) return p } -// FromAffine sets p in projective from p in affine -func (p *PointProj) FromAffine(p1 *PointAffine) *PointProj { +// ScalarMul scalar multiplication of a point +// p1 in affine coordinates with a scalar in big.Int +func (p *PointAffine) ScalarMul(p1 *PointAffine, scalar *big.Int) *PointAffine { + + var p1Extended, resExtended PointExtended + p1Extended.FromAffine(p1) + resExtended.ScalarMul(&p1Extended, scalar) + p.FromExtended(&resExtended) + + return p +} + +//-------- Projective coordinates + +// Set sets p to p1 and return it +func (p *PointProj) Set(p1 *PointProj) *PointProj { p.X.Set(&p1.X) p.Y.Set(&p1.Y) - p.Z.SetOne() + p.Z.Set(&p1.Z) return p } -// Add adds points in projective coordinates -// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp -func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { +// setInfinity sets p to O (0:1:1) +func (p *PointProj) setInfinity() *PointProj { + p.X.SetZero() + p.Y.SetOne() + p.Z.SetOne() + return p +} - var res PointProj +// Equal returns true if p=p1 false otherwise +// If one point is on the affine chart Z=0 it returns false +func (p *PointProj) Equal(p1 *PointProj) bool { + if p.Z.IsZero() || p1.Z.IsZero() { + return false + } + var pAffine, p1Affine PointAffine + pAffine.FromProj(p) + p1Affine.FromProj(p1) + return pAffine.Equal(&p1Affine) +} - ecurve := GetEdwardsCurve() +// IsZero returns true if p=0 false otherwise +func (p *PointProj) IsZero() bool { + return p.X.IsZero() && p.Y.Equal(&p.Z) +} - var A, B, C, D, E, F, G, H, I fr.Element - A.Mul(&p1.Z, &p2.Z) - B.Square(&A) - C.Mul(&p1.X, &p2.X) - D.Mul(&p1.Y, &p2.Y) - E.Mul(&ecurve.D, &C).Mul(&E, &D) - F.Sub(&B, &E) - G.Add(&B, &E) - H.Add(&p1.X, &p1.Y) - I.Add(&p2.X, &p2.Y) - res.X.Mul(&H, &I). - Sub(&res.X, &C). - Sub(&res.X, &D). - Mul(&res.X, &A). - Mul(&res.X, &F) - mulByA(&C) - C.Neg(&C) - res.Y.Add(&D, &C). - Mul(&res.Y, &A). - Mul(&res.Y, &G) - res.Z.Mul(&F, &G) +// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d +// modifies p +func (p *PointProj) Neg(p1 *PointProj) *PointProj { + p.Set(p1) + p.X.Neg(&p.X) + return p +} - p.Set(&res) +// FromAffine sets p in projective from p in affine +func (p *PointProj) FromAffine(p1 *PointAffine) *PointProj { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.Z.SetOne() return p } @@ -298,8 +321,6 @@ func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { // cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-madd-2008-bbjlp func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { - var res PointProj - ecurve := GetEdwardsCurve() var B, C, D, E, F, G, H, I fr.Element @@ -311,18 +332,17 @@ func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { G.Add(&B, &E) H.Add(&p1.X, &p1.Y) I.Add(&p2.X, &p2.Y) - res.X.Mul(&H, &I). - Sub(&res.X, &C). - Sub(&res.X, &D). - Mul(&res.X, &p1.Z). - Mul(&res.X, &F) + p.X.Mul(&H, &I). + Sub(&p.X, &C). + Sub(&p.X, &D). + Mul(&p.X, &p1.Z). + Mul(&p.X, &F) mulByA(&C) - res.Y.Sub(&D, &C). - Mul(&res.Y, &p1.Z). - Mul(&res.Y, &G) - res.Z.Mul(&F, &G) + p.Y.Sub(&D, &C). + Mul(&p.Y, &p1.Z). + Mul(&p.Y, &G) + p.Z.Mul(&F, &G) - p.Set(&res) return p } @@ -330,8 +350,6 @@ func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { // cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#doubling-dbl-2008-bbjlp func (p *PointProj) Double(p1 *PointProj) *PointProj { - var res PointProj - var B, C, D, E, F, H, J fr.Element B.Add(&p1.X, &p1.Y).Square(&B) @@ -342,28 +360,43 @@ func (p *PointProj) Double(p1 *PointProj) *PointProj { F.Add(&E, &D) H.Square(&p1.Z) J.Sub(&F, &H).Sub(&J, &H) - res.X.Sub(&B, &C). - Sub(&res.X, &D). - Mul(&res.X, &J) - res.Y.Sub(&E, &D).Mul(&res.Y, &F) - res.Z.Mul(&F, &J) + p.X.Sub(&B, &C). + Sub(&p.X, &D). + Mul(&p.X, &J) + p.Y.Sub(&E, &D).Mul(&p.Y, &F) + p.Z.Mul(&F, &J) - p.Set(&res) return p } -// Neg sets p to -p1 and returns it -func (p *PointAffine) Neg(p1 *PointAffine) *PointAffine { - p.Set(p1) - p.X.Neg(&p.X) - return p -} +// Add adds points in projective coordinates +// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp +func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { + + ecurve := GetEdwardsCurve() + + var A, B, C, D, E, F, G, H, I fr.Element + A.Mul(&p1.Z, &p2.Z) + B.Square(&A) + C.Mul(&p1.X, &p2.X) + D.Mul(&p1.Y, &p2.Y) + E.Mul(&ecurve.D, &C).Mul(&E, &D) + F.Sub(&B, &E) + G.Add(&B, &E) + H.Add(&p1.X, &p1.Y) + I.Add(&p2.X, &p2.Y) + p.X.Mul(&H, &I). + Sub(&p.X, &C). + Sub(&p.X, &D). + Mul(&p.X, &A). + Mul(&p.X, &F) + mulByA(&C) + C.Neg(&C) + p.Y.Add(&D, &C). + Mul(&p.Y, &A). + Mul(&p.Y, &G) + p.Z.Mul(&F, &G) -// setInfinity sets p to O (0:1:1) -func (p *PointProj) setInfinity() *PointProj { - p.X.SetZero() - p.Y.SetOne() - p.Z.SetOne() return p } @@ -398,14 +431,218 @@ func (p *PointProj) ScalarMul(p1 *PointProj, scalar *big.Int) *PointProj { return p } +// ------- Extended coordinates + +// Set sets p to p1 and return it +func (p *PointExtended) Set(p1 *PointExtended) *PointExtended { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.T.Set(&p1.T) + p.Z.Set(&p1.Z) + return p +} + +// IsZero returns true if p=0 false otherwise +func (p *PointExtended) IsZero() bool { + return p.X.IsZero() && p.Y.Equal(&p.Z) && p.T.IsZero() +} + +// Equal returns true if p=p1 false otherwise +// If one point is on the affine chart Z=0 it returns false +func (p *PointExtended) Equal(p1 *PointExtended) bool { + if p.Z.IsZero() || p1.Z.IsZero() { + return false + } + var pAffine, p1Affine PointAffine + pAffine.FromExtended(p) + p1Affine.FromExtended(p1) + return pAffine.Equal(&p1Affine) +} + +// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d +// modifies p +func (p *PointExtended) Neg(p1 *PointExtended) *PointExtended { + p.Set(p1) + p.X.Neg(&p.X) + p.T.Neg(&p.T) + return p +} + +// FromAffine sets p in projective from p in affine +func (p *PointExtended) FromAffine(p1 *PointAffine) *PointExtended { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.Z.SetOne() + p.T.Mul(&p1.X, &p1.Y) + return p +} + +// Add adds points in extended coordinates +// dedicated addition +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4 +func (p *PointExtended) Add(p1, p2 *PointExtended) *PointExtended { + + if p1.Equal(p2) { + p.Double(p1) + return p + } + + var A, B, C, D, E, F, G, H, tmp fr.Element + tmp.Add(&p2.X, &p2.Y) + A.Sub(&p1.Y, &p1.X). + Mul(&A, &tmp) + tmp.Add(&p1.X, &p1.Y) + B.Sub(&p2.Y, &p2.X). + Mul(&B, &tmp) + C.Mul(&p1.Z, &p2.T). + Double(&C) + D.Mul(&p2.Z, &p1.T). + Double(&D) + E.Add(&D, &C) + F.Sub(&B, &A) + G.Add(&B, &A) + H.Sub(&D, &C) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&E, &H) + p.Z.Mul(&F, &G) + + return p +} + +// MixedAdd adds a point in extended coordinates to a point in affine coordinates +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-madd-2008-hwcd-4 +func (p *PointExtended) MixedAdd(p1 *PointExtended, p2 *PointAffine) *PointExtended { + + var A, B, C, D, E, F, G, H, tmp fr.Element + + A.Mul(&p2.X, &p1.Z) + B.Mul(&p2.Y, &p1.Z) + + if p1.X.Equal(&A) && p1.Y.Equal(&B) { + p.MixedDouble(p1) + return p + } + + tmp.Add(&p2.X, &p2.Y) + A.Sub(&p1.Y, &p1.X). + Mul(&A, &tmp) + tmp.Add(&p1.X, &p1.Y) + B.Sub(&p2.Y, &p2.X). + Mul(&B, &tmp) + C.Mul(&p1.Z, &p2.X). + Mul(&C, &p2.Y). + Double(&C) + D.Double(&p1.T) + E.Add(&D, &C) + F.Sub(&B, &A) + G.Add(&B, &A) + H.Sub(&D, &C) + + p.X.Mul(&F, &E) + p.Y.Mul(&G, &H) + p.T.Mul(&E, &H) + p.Z.Mul(&G, &F) + + return p +} + +// Double adds points in extended coordinates +// Dedicated doubling +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd +func (p *PointExtended) Double(p1 *PointExtended) *PointExtended { + + var A, B, C, D, E, F, G, H fr.Element + + A.Square(&p1.X) + B.Square(&p1.Y) + C.Square(&p1.Z). + Double(&C) + D.Set(&A) + mulByA(&D) + E.Add(&p1.X, &p1.Y). + Square(&E). + Sub(&E, &A). + Sub(&E, &B) + G.Add(&D, &B) + F.Sub(&G, &C) + H.Sub(&D, &B) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&H, &E) + p.Z.Mul(&F, &G) + + return p +} + +// MixedDouble adds points in extended coordinates +// Dedicated mixed doubling +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-mdbl-2008-hwcd +func (p *PointExtended) MixedDouble(p1 *PointExtended) *PointExtended { + + var A, B, D, E, G, H, two fr.Element + two.SetUint64(2) + + A.Square(&p1.X) + B.Square(&p1.Y) + D.Set(&A) + mulByA(&D) + E.Add(&p1.X, &p1.Y). + Square(&E). + Sub(&E, &A). + Sub(&E, &B) + G.Add(&D, &B) + H.Sub(&D, &B) + + p.X.Sub(&G, &two). + Mul(&p.X, &E) + p.Y.Mul(&G, &H) + p.T.Mul(&H, &E) + p.Z.Square(&G). + Sub(&p.Z, &G). + Sub(&p.Z, &G) + + return p +} + +// setInfinity sets p to O (0:1:1:0) +func (p *PointExtended) setInfinity() *PointExtended { + p.X.SetZero() + p.Y.SetOne() + p.Z.SetOne() + p.T.SetZero() + return p +} + // ScalarMul scalar multiplication of a point -// p1 in affine coordinates with a scalar in big.Int -func (p *PointAffine) ScalarMul(p1 *PointAffine, scalar *big.Int) *PointAffine { +// p1 in extended coordinates with a scalar in big.Int +func (p *PointExtended) ScalarMul(p1 *PointExtended, scalar *big.Int) *PointExtended { + + var _scalar big.Int + _scalar.Set(scalar) + p.Set(p1) + if _scalar.Sign() == -1 { + _scalar.Neg(&_scalar) + p.Neg(p) + } + var resExtended PointExtended + resExtended.setInfinity() + const wordSize = bits.UintSize + sWords := _scalar.Bits() - var p1Proj, resProj PointProj - p1Proj.FromAffine(p1) - resProj.ScalarMul(&p1Proj, scalar) - p.FromProj(&resProj) + for i := len(sWords) - 1; i >= 0; i-- { + ithWord := sWords[i] + for k := 0; k < wordSize; k++ { + resExtended.Double(&resExtended) + kthBit := (ithWord >> (wordSize - 1 - k)) & 1 + if kthBit == 1 { + resExtended.Add(&resExtended, p) + } + } + } + p.Set(&resExtended) return p } diff --git a/ecc/bls24-315/twistededwards/twistededwards_test.go b/ecc/bls24-315/twistededwards/twistededwards_test.go index 7ecdbac66a..a4bc261eed 100644 --- a/ecc/bls24-315/twistededwards/twistededwards_test.go +++ b/ecc/bls24-315/twistededwards/twistededwards_test.go @@ -123,7 +123,7 @@ func TestReceiverIsOperand(t *testing.T) { )) properties.TestingRun(t, gopter.ConsoleReporter(false)) - // proj + // projective properties.Property("Equal projective: having the receiver as operand should output the same result", prop.ForAll( func() bool { params := GetEdwardsCurve() @@ -191,6 +191,74 @@ func TestReceiverIsOperand(t *testing.T) { }, )) + // extended + properties.Property("Equal extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + params := GetEdwardsCurve() + var p1, baseProj PointProj + p1.FromAffine(¶ms.Base) + baseProj.FromAffine(¶ms.Base) + + return p1.Equal(&p1) && p1.Equal(&baseProj) + }, + )) + + properties.Property("Add extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2, p3 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + p3.FromAffine(¶ms.Base) + + res := true + + p3.Add(&p1, &p2) + p1.Add(&p1, &p2) + res = res && p3.Equal(&p1) + + p1.FromAffine(¶ms.Base) + p2.Add(&p1, &p2) + res = res && p2.Equal(&p3) + + return res + }, + )) + + properties.Property("Double extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + + p2.Double(&p1) + p1.Double(&p1) + + return p2.Equal(&p1) + }, + )) + + properties.Property("Neg extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + + p2.Neg(&p1) + p1.Neg(&p1) + + return p2.Equal(&p1) + }, + )) + properties.TestingRun(t, gopter.ConsoleReporter(false)) } @@ -244,7 +312,7 @@ func TestOps(t *testing.T) { var one fr.Element one.SetOne() - return p1.IsOnCurve() && p1.X.IsZero() && p1.Y.Equal(&one) + return p1.IsOnCurve() && p1.IsZero() }, genS1, )) @@ -308,7 +376,7 @@ func TestOps(t *testing.T) { genS1, )) - properties.Property("[5]P=[2][2]P+P", prop.ForAll( + properties.Property("(affine) [5]P=[2][2]P+P", prop.ForAll( func(s1 big.Int) bool { params := GetEdwardsCurve() @@ -325,7 +393,7 @@ func TestOps(t *testing.T) { genS1, )) - // proj + // projective properties.Property("(projective) P+(-P)=O", prop.ForAll( func(s1 big.Int) bool { @@ -338,7 +406,7 @@ func TestOps(t *testing.T) { p.Add(&p1, &p2) - return p.X.IsZero() && p.Y.Equal(&p.Z) + return p.IsZero() }, genS1, )) @@ -361,8 +429,119 @@ func TestOps(t *testing.T) { genS1, )) - // mixed - properties.Property("(mixed) P+(-P)=O", prop.ForAll( + properties.Property("(projective) [5]P=[2][2]P+P", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseProj, p1, p2 PointProj + baseProj.FromAffine(¶ms.Base) + p1.ScalarMul(&baseProj, &s1) + + five := big.NewInt(5) + p2.Double(&p1).Double(&p2).Add(&p2, &p1) + p1.ScalarMul(&p1, five) + + return p2.Equal(&p1) + }, + genS1, + )) + + // extended + properties.Property("(extended) P+(-P)=O", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2, p PointExtended + baseExtended.FromAffine(¶ms.Base) + p1.ScalarMul(&baseExtended, &s1) + p2.Neg(&p1) + + p.Add(&p1, &p2) + + return p.IsZero() + }, + genS1, + )) + + properties.Property("(extended) P+P=2*P", prop.ForAll( + + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2, p PointExtended + baseExtended.FromAffine(¶ms.Base) + p.ScalarMul(&baseExtended, &s) + + p1.Add(&p, &p) + p2.Double(&p) + + return p1.Equal(&p2) + }, + genS1, + )) + + properties.Property("(extended) [5]P=[2][2]P+P", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2 PointExtended + baseExtended.FromAffine(¶ms.Base) + p1.ScalarMul(&baseExtended, &s1) + + five := big.NewInt(5) + p2.Double(&p1).Double(&p2).Add(&p2, &p1) + p1.ScalarMul(&p1, five) + + return p2.Equal(&p1) + }, + genS1, + )) + + // mixed affine+extended + properties.Property("(mixed affine+extended) P+(-P)=O", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, pExtended, p PointExtended + var pAffine PointAffine + baseExtended.FromAffine(¶ms.Base) + pExtended.ScalarMul(&baseExtended, &s) + pAffine.ScalarMul(¶ms.Base, &s) + pAffine.Neg(&pAffine) + + p.MixedAdd(&pExtended, &pAffine) + + return p.IsZero() + }, + genS1, + )) + + properties.Property("(mixed affine+extended) P+P=2*P", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, pExtended, p, p2 PointExtended + var pAffine PointAffine + baseExtended.FromAffine(¶ms.Base) + pExtended.ScalarMul(&baseExtended, &s) + pAffine.ScalarMul(¶ms.Base, &s) + + p.MixedAdd(&pExtended, &pAffine) + p2.MixedDouble(&pExtended) + + return p.Equal(&p2) + }, + genS1, + )) + + // mixed affine+projective + properties.Property("(mixed affine+proj) P+(-P)=O", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() @@ -376,12 +555,12 @@ func TestOps(t *testing.T) { p.MixedAdd(&pProj, &pAffine) - return p.X.IsZero() && p.Y.Equal(&p.Z) + return p.IsZero() }, genS1, )) - properties.Property("(mixed) P+P=2*P", prop.ForAll( + properties.Property("(mixed affine+proj) P+P=2*P", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() @@ -400,6 +579,27 @@ func TestOps(t *testing.T) { genS1, )) + properties.Property("scalar multiplication in Proj vs Ext should be consistant", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseProj PointProj + var baseExt PointExtended + var p1, p2 PointAffine + baseProj.FromAffine(¶ms.Base) + baseProj.ScalarMul(&baseProj, &s) + baseExt.FromAffine(¶ms.Base) + baseExt.ScalarMul(&baseExt, &s) + + p1.FromProj(&baseProj) + p2.FromExtended(&baseExt) + + return p1.Equal(&p2) + }, + genS1, + )) + properties.TestingRun(t, gopter.ConsoleReporter(false)) } @@ -437,7 +637,25 @@ func GenBigInt() gopter.Gen { // ------------------------------------------------------------ // benches -func BenchmarkScalarMul(b *testing.B) { +func BenchmarkScalarMulExtended(b *testing.B) { + params := GetEdwardsCurve() + var a PointExtended + var s big.Int + a.FromAffine(¶ms.Base) + s.SetString("52435875175126190479447705081859658376581184513", 10) + s.Add(&s, ¶ms.Order) + + var doubleAndAdd PointExtended + + b.Run("double and add", func(b *testing.B) { + b.ResetTimer() + for j := 0; j < b.N; j++ { + doubleAndAdd.ScalarMul(&a, &s) + } + }) +} + +func BenchmarkScalarMulProjective(b *testing.B) { params := GetEdwardsCurve() var a PointProj var s big.Int diff --git a/ecc/bn254/twistededwards/point.go b/ecc/bn254/twistededwards/point.go index 970a2f0a1d..822f8b3464 100644 --- a/ecc/bn254/twistededwards/point.go +++ b/ecc/bn254/twistededwards/point.go @@ -35,6 +35,11 @@ type PointProj struct { X, Y, Z fr.Element } +// PointExtended point in extended coordinates +type PointExtended struct { + X, Y, Z, T fr.Element +} + const ( //following https://tools.ietf.org/html/rfc8032#section-3.1, // an fr element x is negative if its binary encoding is @@ -128,14 +133,6 @@ func (p *PointAffine) Unmarshal(b []byte) error { return err } -// Set sets p to p1 and return it -func (p *PointProj) Set(p1 *PointProj) *PointProj { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - p.Z.Set(&p1.Z) - return p -} - // Set sets p to p1 and return it func (p *PointAffine) Set(p1 *PointAffine) *PointAffine { p.X.Set(&p1.X) @@ -148,16 +145,11 @@ func (p *PointAffine) Equal(p1 *PointAffine) bool { return p.X.Equal(&p1.X) && p.Y.Equal(&p1.Y) } -// Equal returns true if p=p1 false otherwise -// If one point is on the affine chart Z=0 it returns false -func (p *PointProj) Equal(p1 *PointProj) bool { - if p.Z.IsZero() || p1.Z.IsZero() { - return false - } - var pAffine, p1Affine PointAffine - pAffine.FromProj(p) - p1Affine.FromProj(p1) - return pAffine.Equal(&p1Affine) +// IsZero returns true if p=0 false otherwise +func (p *PointAffine) IsZero() bool { + var one fr.Element + one.SetOne() + return p.X.IsZero() && p.Y.Equal(&one) } // NewPointAffine creates a new instance of PointAffine @@ -186,6 +178,13 @@ func (p *PointAffine) IsOnCurve() bool { return lhs.Equal(&rhs) } +// Neg sets p to -p1 and returns it +func (p *PointAffine) Neg(p1 *PointAffine) *PointAffine { + p.Set(p1) + p.X.Neg(&p.X) + return p +} + // Add adds two points (x,y), (u,v) on a twisted Edwards curve with parameters a, d // modifies p func (p *PointAffine) Add(p1, p2 *PointAffine) *PointAffine { @@ -237,60 +236,84 @@ func (p *PointAffine) Double(p1 *PointAffine) *PointAffine { return p } -// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d -// modifies p -func (p *PointProj) Neg(p1 *PointProj) *PointProj { - p.Set(p1) - p.X.Neg(&p.X) +// FromProj sets p in affine from p in projective +func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { + var I fr.Element + I.Inverse(&p1.Z) + p.X.Mul(&p1.X, &I) + p.Y.Mul(&p1.Y, &I) return p } -// FromProj sets p in affine from p in projective -func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { - p.X.Div(&p1.X, &p1.Z) - p.Y.Div(&p1.Y, &p1.Z) +// FromExtended sets p in affine from p in extended coordinates +func (p *PointAffine) FromExtended(p1 *PointExtended) *PointAffine { + var I fr.Element + I.Inverse(&p1.Z) + p.X.Mul(&p1.X, &I) + p.Y.Mul(&p1.Y, &I) return p } -// FromAffine sets p in projective from p in affine -func (p *PointProj) FromAffine(p1 *PointAffine) *PointProj { +// ScalarMul scalar multiplication of a point +// p1 in affine coordinates with a scalar in big.Int +func (p *PointAffine) ScalarMul(p1 *PointAffine, scalar *big.Int) *PointAffine { + + var p1Extended, resExtended PointExtended + p1Extended.FromAffine(p1) + resExtended.ScalarMul(&p1Extended, scalar) + p.FromExtended(&resExtended) + + return p +} + +//-------- Projective coordinates + +// Set sets p to p1 and return it +func (p *PointProj) Set(p1 *PointProj) *PointProj { p.X.Set(&p1.X) p.Y.Set(&p1.Y) - p.Z.SetOne() + p.Z.Set(&p1.Z) return p } -// Add adds points in projective coordinates -// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp -func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { +// setInfinity sets p to O (0:1:1) +func (p *PointProj) setInfinity() *PointProj { + p.X.SetZero() + p.Y.SetOne() + p.Z.SetOne() + return p +} - var res PointProj +// Equal returns true if p=p1 false otherwise +// If one point is on the affine chart Z=0 it returns false +func (p *PointProj) Equal(p1 *PointProj) bool { + if p.Z.IsZero() || p1.Z.IsZero() { + return false + } + var pAffine, p1Affine PointAffine + pAffine.FromProj(p) + p1Affine.FromProj(p1) + return pAffine.Equal(&p1Affine) +} - ecurve := GetEdwardsCurve() +// IsZero returns true if p=0 false otherwise +func (p *PointProj) IsZero() bool { + return p.X.IsZero() && p.Y.Equal(&p.Z) +} - var A, B, C, D, E, F, G, H, I fr.Element - A.Mul(&p1.Z, &p2.Z) - B.Square(&A) - C.Mul(&p1.X, &p2.X) - D.Mul(&p1.Y, &p2.Y) - E.Mul(&ecurve.D, &C).Mul(&E, &D) - F.Sub(&B, &E) - G.Add(&B, &E) - H.Add(&p1.X, &p1.Y) - I.Add(&p2.X, &p2.Y) - res.X.Mul(&H, &I). - Sub(&res.X, &C). - Sub(&res.X, &D). - Mul(&res.X, &A). - Mul(&res.X, &F) - mulByA(&C) - C.Neg(&C) - res.Y.Add(&D, &C). - Mul(&res.Y, &A). - Mul(&res.Y, &G) - res.Z.Mul(&F, &G) +// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d +// modifies p +func (p *PointProj) Neg(p1 *PointProj) *PointProj { + p.Set(p1) + p.X.Neg(&p.X) + return p +} - p.Set(&res) +// FromAffine sets p in projective from p in affine +func (p *PointProj) FromAffine(p1 *PointAffine) *PointProj { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.Z.SetOne() return p } @@ -298,8 +321,6 @@ func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { // cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-madd-2008-bbjlp func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { - var res PointProj - ecurve := GetEdwardsCurve() var B, C, D, E, F, G, H, I fr.Element @@ -311,18 +332,17 @@ func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { G.Add(&B, &E) H.Add(&p1.X, &p1.Y) I.Add(&p2.X, &p2.Y) - res.X.Mul(&H, &I). - Sub(&res.X, &C). - Sub(&res.X, &D). - Mul(&res.X, &p1.Z). - Mul(&res.X, &F) + p.X.Mul(&H, &I). + Sub(&p.X, &C). + Sub(&p.X, &D). + Mul(&p.X, &p1.Z). + Mul(&p.X, &F) mulByA(&C) - res.Y.Sub(&D, &C). - Mul(&res.Y, &p1.Z). - Mul(&res.Y, &G) - res.Z.Mul(&F, &G) + p.Y.Sub(&D, &C). + Mul(&p.Y, &p1.Z). + Mul(&p.Y, &G) + p.Z.Mul(&F, &G) - p.Set(&res) return p } @@ -330,8 +350,6 @@ func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { // cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#doubling-dbl-2008-bbjlp func (p *PointProj) Double(p1 *PointProj) *PointProj { - var res PointProj - var B, C, D, E, F, H, J fr.Element B.Add(&p1.X, &p1.Y).Square(&B) @@ -342,28 +360,43 @@ func (p *PointProj) Double(p1 *PointProj) *PointProj { F.Add(&E, &D) H.Square(&p1.Z) J.Sub(&F, &H).Sub(&J, &H) - res.X.Sub(&B, &C). - Sub(&res.X, &D). - Mul(&res.X, &J) - res.Y.Sub(&E, &D).Mul(&res.Y, &F) - res.Z.Mul(&F, &J) + p.X.Sub(&B, &C). + Sub(&p.X, &D). + Mul(&p.X, &J) + p.Y.Sub(&E, &D).Mul(&p.Y, &F) + p.Z.Mul(&F, &J) - p.Set(&res) return p } -// Neg sets p to -p1 and returns it -func (p *PointAffine) Neg(p1 *PointAffine) *PointAffine { - p.Set(p1) - p.X.Neg(&p.X) - return p -} +// Add adds points in projective coordinates +// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp +func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { + + ecurve := GetEdwardsCurve() + + var A, B, C, D, E, F, G, H, I fr.Element + A.Mul(&p1.Z, &p2.Z) + B.Square(&A) + C.Mul(&p1.X, &p2.X) + D.Mul(&p1.Y, &p2.Y) + E.Mul(&ecurve.D, &C).Mul(&E, &D) + F.Sub(&B, &E) + G.Add(&B, &E) + H.Add(&p1.X, &p1.Y) + I.Add(&p2.X, &p2.Y) + p.X.Mul(&H, &I). + Sub(&p.X, &C). + Sub(&p.X, &D). + Mul(&p.X, &A). + Mul(&p.X, &F) + mulByA(&C) + C.Neg(&C) + p.Y.Add(&D, &C). + Mul(&p.Y, &A). + Mul(&p.Y, &G) + p.Z.Mul(&F, &G) -// setInfinity sets p to O (0:1:1) -func (p *PointProj) setInfinity() *PointProj { - p.X.SetZero() - p.Y.SetOne() - p.Z.SetOne() return p } @@ -398,14 +431,218 @@ func (p *PointProj) ScalarMul(p1 *PointProj, scalar *big.Int) *PointProj { return p } +// ------- Extended coordinates + +// Set sets p to p1 and return it +func (p *PointExtended) Set(p1 *PointExtended) *PointExtended { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.T.Set(&p1.T) + p.Z.Set(&p1.Z) + return p +} + +// IsZero returns true if p=0 false otherwise +func (p *PointExtended) IsZero() bool { + return p.X.IsZero() && p.Y.Equal(&p.Z) && p.T.IsZero() +} + +// Equal returns true if p=p1 false otherwise +// If one point is on the affine chart Z=0 it returns false +func (p *PointExtended) Equal(p1 *PointExtended) bool { + if p.Z.IsZero() || p1.Z.IsZero() { + return false + } + var pAffine, p1Affine PointAffine + pAffine.FromExtended(p) + p1Affine.FromExtended(p1) + return pAffine.Equal(&p1Affine) +} + +// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d +// modifies p +func (p *PointExtended) Neg(p1 *PointExtended) *PointExtended { + p.Set(p1) + p.X.Neg(&p.X) + p.T.Neg(&p.T) + return p +} + +// FromAffine sets p in projective from p in affine +func (p *PointExtended) FromAffine(p1 *PointAffine) *PointExtended { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.Z.SetOne() + p.T.Mul(&p1.X, &p1.Y) + return p +} + +// Add adds points in extended coordinates +// dedicated addition +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4 +func (p *PointExtended) Add(p1, p2 *PointExtended) *PointExtended { + + if p1.Equal(p2) { + p.Double(p1) + return p + } + + var A, B, C, D, E, F, G, H, tmp fr.Element + tmp.Add(&p2.X, &p2.Y) + A.Sub(&p1.Y, &p1.X). + Mul(&A, &tmp) + tmp.Add(&p1.X, &p1.Y) + B.Sub(&p2.Y, &p2.X). + Mul(&B, &tmp) + C.Mul(&p1.Z, &p2.T). + Double(&C) + D.Mul(&p2.Z, &p1.T). + Double(&D) + E.Add(&D, &C) + F.Sub(&B, &A) + G.Add(&B, &A) + H.Sub(&D, &C) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&E, &H) + p.Z.Mul(&F, &G) + + return p +} + +// MixedAdd adds a point in extended coordinates to a point in affine coordinates +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-madd-2008-hwcd-4 +func (p *PointExtended) MixedAdd(p1 *PointExtended, p2 *PointAffine) *PointExtended { + + var A, B, C, D, E, F, G, H, tmp fr.Element + + A.Mul(&p2.X, &p1.Z) + B.Mul(&p2.Y, &p1.Z) + + if p1.X.Equal(&A) && p1.Y.Equal(&B) { + p.MixedDouble(p1) + return p + } + + tmp.Add(&p2.X, &p2.Y) + A.Sub(&p1.Y, &p1.X). + Mul(&A, &tmp) + tmp.Add(&p1.X, &p1.Y) + B.Sub(&p2.Y, &p2.X). + Mul(&B, &tmp) + C.Mul(&p1.Z, &p2.X). + Mul(&C, &p2.Y). + Double(&C) + D.Double(&p1.T) + E.Add(&D, &C) + F.Sub(&B, &A) + G.Add(&B, &A) + H.Sub(&D, &C) + + p.X.Mul(&F, &E) + p.Y.Mul(&G, &H) + p.T.Mul(&E, &H) + p.Z.Mul(&G, &F) + + return p +} + +// Double adds points in extended coordinates +// Dedicated doubling +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd +func (p *PointExtended) Double(p1 *PointExtended) *PointExtended { + + var A, B, C, D, E, F, G, H fr.Element + + A.Square(&p1.X) + B.Square(&p1.Y) + C.Square(&p1.Z). + Double(&C) + D.Set(&A) + mulByA(&D) + E.Add(&p1.X, &p1.Y). + Square(&E). + Sub(&E, &A). + Sub(&E, &B) + G.Add(&D, &B) + F.Sub(&G, &C) + H.Sub(&D, &B) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&H, &E) + p.Z.Mul(&F, &G) + + return p +} + +// MixedDouble adds points in extended coordinates +// Dedicated mixed doubling +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-mdbl-2008-hwcd +func (p *PointExtended) MixedDouble(p1 *PointExtended) *PointExtended { + + var A, B, D, E, G, H, two fr.Element + two.SetUint64(2) + + A.Square(&p1.X) + B.Square(&p1.Y) + D.Set(&A) + mulByA(&D) + E.Add(&p1.X, &p1.Y). + Square(&E). + Sub(&E, &A). + Sub(&E, &B) + G.Add(&D, &B) + H.Sub(&D, &B) + + p.X.Sub(&G, &two). + Mul(&p.X, &E) + p.Y.Mul(&G, &H) + p.T.Mul(&H, &E) + p.Z.Square(&G). + Sub(&p.Z, &G). + Sub(&p.Z, &G) + + return p +} + +// setInfinity sets p to O (0:1:1:0) +func (p *PointExtended) setInfinity() *PointExtended { + p.X.SetZero() + p.Y.SetOne() + p.Z.SetOne() + p.T.SetZero() + return p +} + // ScalarMul scalar multiplication of a point -// p1 in affine coordinates with a scalar in big.Int -func (p *PointAffine) ScalarMul(p1 *PointAffine, scalar *big.Int) *PointAffine { +// p1 in extended coordinates with a scalar in big.Int +func (p *PointExtended) ScalarMul(p1 *PointExtended, scalar *big.Int) *PointExtended { + + var _scalar big.Int + _scalar.Set(scalar) + p.Set(p1) + if _scalar.Sign() == -1 { + _scalar.Neg(&_scalar) + p.Neg(p) + } + var resExtended PointExtended + resExtended.setInfinity() + const wordSize = bits.UintSize + sWords := _scalar.Bits() - var p1Proj, resProj PointProj - p1Proj.FromAffine(p1) - resProj.ScalarMul(&p1Proj, scalar) - p.FromProj(&resProj) + for i := len(sWords) - 1; i >= 0; i-- { + ithWord := sWords[i] + for k := 0; k < wordSize; k++ { + resExtended.Double(&resExtended) + kthBit := (ithWord >> (wordSize - 1 - k)) & 1 + if kthBit == 1 { + resExtended.Add(&resExtended, p) + } + } + } + p.Set(&resExtended) return p } diff --git a/ecc/bn254/twistededwards/twistededwards_test.go b/ecc/bn254/twistededwards/twistededwards_test.go index 50dbccd458..75c5b5ff4b 100644 --- a/ecc/bn254/twistededwards/twistededwards_test.go +++ b/ecc/bn254/twistededwards/twistededwards_test.go @@ -123,7 +123,7 @@ func TestReceiverIsOperand(t *testing.T) { )) properties.TestingRun(t, gopter.ConsoleReporter(false)) - // proj + // projective properties.Property("Equal projective: having the receiver as operand should output the same result", prop.ForAll( func() bool { params := GetEdwardsCurve() @@ -191,6 +191,74 @@ func TestReceiverIsOperand(t *testing.T) { }, )) + // extended + properties.Property("Equal extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + params := GetEdwardsCurve() + var p1, baseProj PointProj + p1.FromAffine(¶ms.Base) + baseProj.FromAffine(¶ms.Base) + + return p1.Equal(&p1) && p1.Equal(&baseProj) + }, + )) + + properties.Property("Add extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2, p3 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + p3.FromAffine(¶ms.Base) + + res := true + + p3.Add(&p1, &p2) + p1.Add(&p1, &p2) + res = res && p3.Equal(&p1) + + p1.FromAffine(¶ms.Base) + p2.Add(&p1, &p2) + res = res && p2.Equal(&p3) + + return res + }, + )) + + properties.Property("Double extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + + p2.Double(&p1) + p1.Double(&p1) + + return p2.Equal(&p1) + }, + )) + + properties.Property("Neg extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + + p2.Neg(&p1) + p1.Neg(&p1) + + return p2.Equal(&p1) + }, + )) + properties.TestingRun(t, gopter.ConsoleReporter(false)) } @@ -244,7 +312,7 @@ func TestOps(t *testing.T) { var one fr.Element one.SetOne() - return p1.IsOnCurve() && p1.X.IsZero() && p1.Y.Equal(&one) + return p1.IsOnCurve() && p1.IsZero() }, genS1, )) @@ -308,7 +376,7 @@ func TestOps(t *testing.T) { genS1, )) - properties.Property("[5]P=[2][2]P+P", prop.ForAll( + properties.Property("(affine) [5]P=[2][2]P+P", prop.ForAll( func(s1 big.Int) bool { params := GetEdwardsCurve() @@ -325,7 +393,7 @@ func TestOps(t *testing.T) { genS1, )) - // proj + // projective properties.Property("(projective) P+(-P)=O", prop.ForAll( func(s1 big.Int) bool { @@ -338,7 +406,7 @@ func TestOps(t *testing.T) { p.Add(&p1, &p2) - return p.X.IsZero() && p.Y.Equal(&p.Z) + return p.IsZero() }, genS1, )) @@ -361,8 +429,119 @@ func TestOps(t *testing.T) { genS1, )) - // mixed - properties.Property("(mixed) P+(-P)=O", prop.ForAll( + properties.Property("(projective) [5]P=[2][2]P+P", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseProj, p1, p2 PointProj + baseProj.FromAffine(¶ms.Base) + p1.ScalarMul(&baseProj, &s1) + + five := big.NewInt(5) + p2.Double(&p1).Double(&p2).Add(&p2, &p1) + p1.ScalarMul(&p1, five) + + return p2.Equal(&p1) + }, + genS1, + )) + + // extended + properties.Property("(extended) P+(-P)=O", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2, p PointExtended + baseExtended.FromAffine(¶ms.Base) + p1.ScalarMul(&baseExtended, &s1) + p2.Neg(&p1) + + p.Add(&p1, &p2) + + return p.IsZero() + }, + genS1, + )) + + properties.Property("(extended) P+P=2*P", prop.ForAll( + + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2, p PointExtended + baseExtended.FromAffine(¶ms.Base) + p.ScalarMul(&baseExtended, &s) + + p1.Add(&p, &p) + p2.Double(&p) + + return p1.Equal(&p2) + }, + genS1, + )) + + properties.Property("(extended) [5]P=[2][2]P+P", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2 PointExtended + baseExtended.FromAffine(¶ms.Base) + p1.ScalarMul(&baseExtended, &s1) + + five := big.NewInt(5) + p2.Double(&p1).Double(&p2).Add(&p2, &p1) + p1.ScalarMul(&p1, five) + + return p2.Equal(&p1) + }, + genS1, + )) + + // mixed affine+extended + properties.Property("(mixed affine+extended) P+(-P)=O", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, pExtended, p PointExtended + var pAffine PointAffine + baseExtended.FromAffine(¶ms.Base) + pExtended.ScalarMul(&baseExtended, &s) + pAffine.ScalarMul(¶ms.Base, &s) + pAffine.Neg(&pAffine) + + p.MixedAdd(&pExtended, &pAffine) + + return p.IsZero() + }, + genS1, + )) + + properties.Property("(mixed affine+extended) P+P=2*P", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, pExtended, p, p2 PointExtended + var pAffine PointAffine + baseExtended.FromAffine(¶ms.Base) + pExtended.ScalarMul(&baseExtended, &s) + pAffine.ScalarMul(¶ms.Base, &s) + + p.MixedAdd(&pExtended, &pAffine) + p2.MixedDouble(&pExtended) + + return p.Equal(&p2) + }, + genS1, + )) + + // mixed affine+projective + properties.Property("(mixed affine+proj) P+(-P)=O", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() @@ -376,12 +555,12 @@ func TestOps(t *testing.T) { p.MixedAdd(&pProj, &pAffine) - return p.X.IsZero() && p.Y.Equal(&p.Z) + return p.IsZero() }, genS1, )) - properties.Property("(mixed) P+P=2*P", prop.ForAll( + properties.Property("(mixed affine+proj) P+P=2*P", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() @@ -400,6 +579,27 @@ func TestOps(t *testing.T) { genS1, )) + properties.Property("scalar multiplication in Proj vs Ext should be consistant", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseProj PointProj + var baseExt PointExtended + var p1, p2 PointAffine + baseProj.FromAffine(¶ms.Base) + baseProj.ScalarMul(&baseProj, &s) + baseExt.FromAffine(¶ms.Base) + baseExt.ScalarMul(&baseExt, &s) + + p1.FromProj(&baseProj) + p2.FromExtended(&baseExt) + + return p1.Equal(&p2) + }, + genS1, + )) + properties.TestingRun(t, gopter.ConsoleReporter(false)) } @@ -437,7 +637,25 @@ func GenBigInt() gopter.Gen { // ------------------------------------------------------------ // benches -func BenchmarkScalarMul(b *testing.B) { +func BenchmarkScalarMulExtended(b *testing.B) { + params := GetEdwardsCurve() + var a PointExtended + var s big.Int + a.FromAffine(¶ms.Base) + s.SetString("52435875175126190479447705081859658376581184513", 10) + s.Add(&s, ¶ms.Order) + + var doubleAndAdd PointExtended + + b.Run("double and add", func(b *testing.B) { + b.ResetTimer() + for j := 0; j < b.N; j++ { + doubleAndAdd.ScalarMul(&a, &s) + } + }) +} + +func BenchmarkScalarMulProjective(b *testing.B) { params := GetEdwardsCurve() var a PointProj var s big.Int diff --git a/ecc/bw6-633/twistededwards/point.go b/ecc/bw6-633/twistededwards/point.go index fb66194a89..1e77544abb 100644 --- a/ecc/bw6-633/twistededwards/point.go +++ b/ecc/bw6-633/twistededwards/point.go @@ -35,6 +35,11 @@ type PointProj struct { X, Y, Z fr.Element } +// PointExtended point in extended coordinates +type PointExtended struct { + X, Y, Z, T fr.Element +} + const ( //following https://tools.ietf.org/html/rfc8032#section-3.1, // an fr element x is negative if its binary encoding is @@ -128,14 +133,6 @@ func (p *PointAffine) Unmarshal(b []byte) error { return err } -// Set sets p to p1 and return it -func (p *PointProj) Set(p1 *PointProj) *PointProj { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - p.Z.Set(&p1.Z) - return p -} - // Set sets p to p1 and return it func (p *PointAffine) Set(p1 *PointAffine) *PointAffine { p.X.Set(&p1.X) @@ -148,16 +145,11 @@ func (p *PointAffine) Equal(p1 *PointAffine) bool { return p.X.Equal(&p1.X) && p.Y.Equal(&p1.Y) } -// Equal returns true if p=p1 false otherwise -// If one point is on the affine chart Z=0 it returns false -func (p *PointProj) Equal(p1 *PointProj) bool { - if p.Z.IsZero() || p1.Z.IsZero() { - return false - } - var pAffine, p1Affine PointAffine - pAffine.FromProj(p) - p1Affine.FromProj(p1) - return pAffine.Equal(&p1Affine) +// IsZero returns true if p=0 false otherwise +func (p *PointAffine) IsZero() bool { + var one fr.Element + one.SetOne() + return p.X.IsZero() && p.Y.Equal(&one) } // NewPointAffine creates a new instance of PointAffine @@ -186,6 +178,13 @@ func (p *PointAffine) IsOnCurve() bool { return lhs.Equal(&rhs) } +// Neg sets p to -p1 and returns it +func (p *PointAffine) Neg(p1 *PointAffine) *PointAffine { + p.Set(p1) + p.X.Neg(&p.X) + return p +} + // Add adds two points (x,y), (u,v) on a twisted Edwards curve with parameters a, d // modifies p func (p *PointAffine) Add(p1, p2 *PointAffine) *PointAffine { @@ -237,60 +236,84 @@ func (p *PointAffine) Double(p1 *PointAffine) *PointAffine { return p } -// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d -// modifies p -func (p *PointProj) Neg(p1 *PointProj) *PointProj { - p.Set(p1) - p.X.Neg(&p.X) +// FromProj sets p in affine from p in projective +func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { + var I fr.Element + I.Inverse(&p1.Z) + p.X.Mul(&p1.X, &I) + p.Y.Mul(&p1.Y, &I) return p } -// FromProj sets p in affine from p in projective -func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { - p.X.Div(&p1.X, &p1.Z) - p.Y.Div(&p1.Y, &p1.Z) +// FromExtended sets p in affine from p in extended coordinates +func (p *PointAffine) FromExtended(p1 *PointExtended) *PointAffine { + var I fr.Element + I.Inverse(&p1.Z) + p.X.Mul(&p1.X, &I) + p.Y.Mul(&p1.Y, &I) return p } -// FromAffine sets p in projective from p in affine -func (p *PointProj) FromAffine(p1 *PointAffine) *PointProj { +// ScalarMul scalar multiplication of a point +// p1 in affine coordinates with a scalar in big.Int +func (p *PointAffine) ScalarMul(p1 *PointAffine, scalar *big.Int) *PointAffine { + + var p1Extended, resExtended PointExtended + p1Extended.FromAffine(p1) + resExtended.ScalarMul(&p1Extended, scalar) + p.FromExtended(&resExtended) + + return p +} + +//-------- Projective coordinates + +// Set sets p to p1 and return it +func (p *PointProj) Set(p1 *PointProj) *PointProj { p.X.Set(&p1.X) p.Y.Set(&p1.Y) - p.Z.SetOne() + p.Z.Set(&p1.Z) return p } -// Add adds points in projective coordinates -// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp -func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { +// setInfinity sets p to O (0:1:1) +func (p *PointProj) setInfinity() *PointProj { + p.X.SetZero() + p.Y.SetOne() + p.Z.SetOne() + return p +} - var res PointProj +// Equal returns true if p=p1 false otherwise +// If one point is on the affine chart Z=0 it returns false +func (p *PointProj) Equal(p1 *PointProj) bool { + if p.Z.IsZero() || p1.Z.IsZero() { + return false + } + var pAffine, p1Affine PointAffine + pAffine.FromProj(p) + p1Affine.FromProj(p1) + return pAffine.Equal(&p1Affine) +} - ecurve := GetEdwardsCurve() +// IsZero returns true if p=0 false otherwise +func (p *PointProj) IsZero() bool { + return p.X.IsZero() && p.Y.Equal(&p.Z) +} - var A, B, C, D, E, F, G, H, I fr.Element - A.Mul(&p1.Z, &p2.Z) - B.Square(&A) - C.Mul(&p1.X, &p2.X) - D.Mul(&p1.Y, &p2.Y) - E.Mul(&ecurve.D, &C).Mul(&E, &D) - F.Sub(&B, &E) - G.Add(&B, &E) - H.Add(&p1.X, &p1.Y) - I.Add(&p2.X, &p2.Y) - res.X.Mul(&H, &I). - Sub(&res.X, &C). - Sub(&res.X, &D). - Mul(&res.X, &A). - Mul(&res.X, &F) - mulByA(&C) - C.Neg(&C) - res.Y.Add(&D, &C). - Mul(&res.Y, &A). - Mul(&res.Y, &G) - res.Z.Mul(&F, &G) +// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d +// modifies p +func (p *PointProj) Neg(p1 *PointProj) *PointProj { + p.Set(p1) + p.X.Neg(&p.X) + return p +} - p.Set(&res) +// FromAffine sets p in projective from p in affine +func (p *PointProj) FromAffine(p1 *PointAffine) *PointProj { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.Z.SetOne() return p } @@ -298,8 +321,6 @@ func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { // cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-madd-2008-bbjlp func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { - var res PointProj - ecurve := GetEdwardsCurve() var B, C, D, E, F, G, H, I fr.Element @@ -311,18 +332,17 @@ func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { G.Add(&B, &E) H.Add(&p1.X, &p1.Y) I.Add(&p2.X, &p2.Y) - res.X.Mul(&H, &I). - Sub(&res.X, &C). - Sub(&res.X, &D). - Mul(&res.X, &p1.Z). - Mul(&res.X, &F) + p.X.Mul(&H, &I). + Sub(&p.X, &C). + Sub(&p.X, &D). + Mul(&p.X, &p1.Z). + Mul(&p.X, &F) mulByA(&C) - res.Y.Sub(&D, &C). - Mul(&res.Y, &p1.Z). - Mul(&res.Y, &G) - res.Z.Mul(&F, &G) + p.Y.Sub(&D, &C). + Mul(&p.Y, &p1.Z). + Mul(&p.Y, &G) + p.Z.Mul(&F, &G) - p.Set(&res) return p } @@ -330,8 +350,6 @@ func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { // cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#doubling-dbl-2008-bbjlp func (p *PointProj) Double(p1 *PointProj) *PointProj { - var res PointProj - var B, C, D, E, F, H, J fr.Element B.Add(&p1.X, &p1.Y).Square(&B) @@ -342,28 +360,43 @@ func (p *PointProj) Double(p1 *PointProj) *PointProj { F.Add(&E, &D) H.Square(&p1.Z) J.Sub(&F, &H).Sub(&J, &H) - res.X.Sub(&B, &C). - Sub(&res.X, &D). - Mul(&res.X, &J) - res.Y.Sub(&E, &D).Mul(&res.Y, &F) - res.Z.Mul(&F, &J) + p.X.Sub(&B, &C). + Sub(&p.X, &D). + Mul(&p.X, &J) + p.Y.Sub(&E, &D).Mul(&p.Y, &F) + p.Z.Mul(&F, &J) - p.Set(&res) return p } -// Neg sets p to -p1 and returns it -func (p *PointAffine) Neg(p1 *PointAffine) *PointAffine { - p.Set(p1) - p.X.Neg(&p.X) - return p -} +// Add adds points in projective coordinates +// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp +func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { + + ecurve := GetEdwardsCurve() + + var A, B, C, D, E, F, G, H, I fr.Element + A.Mul(&p1.Z, &p2.Z) + B.Square(&A) + C.Mul(&p1.X, &p2.X) + D.Mul(&p1.Y, &p2.Y) + E.Mul(&ecurve.D, &C).Mul(&E, &D) + F.Sub(&B, &E) + G.Add(&B, &E) + H.Add(&p1.X, &p1.Y) + I.Add(&p2.X, &p2.Y) + p.X.Mul(&H, &I). + Sub(&p.X, &C). + Sub(&p.X, &D). + Mul(&p.X, &A). + Mul(&p.X, &F) + mulByA(&C) + C.Neg(&C) + p.Y.Add(&D, &C). + Mul(&p.Y, &A). + Mul(&p.Y, &G) + p.Z.Mul(&F, &G) -// setInfinity sets p to O (0:1:1) -func (p *PointProj) setInfinity() *PointProj { - p.X.SetZero() - p.Y.SetOne() - p.Z.SetOne() return p } @@ -398,14 +431,218 @@ func (p *PointProj) ScalarMul(p1 *PointProj, scalar *big.Int) *PointProj { return p } +// ------- Extended coordinates + +// Set sets p to p1 and return it +func (p *PointExtended) Set(p1 *PointExtended) *PointExtended { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.T.Set(&p1.T) + p.Z.Set(&p1.Z) + return p +} + +// IsZero returns true if p=0 false otherwise +func (p *PointExtended) IsZero() bool { + return p.X.IsZero() && p.Y.Equal(&p.Z) && p.T.IsZero() +} + +// Equal returns true if p=p1 false otherwise +// If one point is on the affine chart Z=0 it returns false +func (p *PointExtended) Equal(p1 *PointExtended) bool { + if p.Z.IsZero() || p1.Z.IsZero() { + return false + } + var pAffine, p1Affine PointAffine + pAffine.FromExtended(p) + p1Affine.FromExtended(p1) + return pAffine.Equal(&p1Affine) +} + +// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d +// modifies p +func (p *PointExtended) Neg(p1 *PointExtended) *PointExtended { + p.Set(p1) + p.X.Neg(&p.X) + p.T.Neg(&p.T) + return p +} + +// FromAffine sets p in projective from p in affine +func (p *PointExtended) FromAffine(p1 *PointAffine) *PointExtended { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.Z.SetOne() + p.T.Mul(&p1.X, &p1.Y) + return p +} + +// Add adds points in extended coordinates +// dedicated addition +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4 +func (p *PointExtended) Add(p1, p2 *PointExtended) *PointExtended { + + if p1.Equal(p2) { + p.Double(p1) + return p + } + + var A, B, C, D, E, F, G, H, tmp fr.Element + tmp.Add(&p2.X, &p2.Y) + A.Sub(&p1.Y, &p1.X). + Mul(&A, &tmp) + tmp.Add(&p1.X, &p1.Y) + B.Sub(&p2.Y, &p2.X). + Mul(&B, &tmp) + C.Mul(&p1.Z, &p2.T). + Double(&C) + D.Mul(&p2.Z, &p1.T). + Double(&D) + E.Add(&D, &C) + F.Sub(&B, &A) + G.Add(&B, &A) + H.Sub(&D, &C) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&E, &H) + p.Z.Mul(&F, &G) + + return p +} + +// MixedAdd adds a point in extended coordinates to a point in affine coordinates +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-madd-2008-hwcd-4 +func (p *PointExtended) MixedAdd(p1 *PointExtended, p2 *PointAffine) *PointExtended { + + var A, B, C, D, E, F, G, H, tmp fr.Element + + A.Mul(&p2.X, &p1.Z) + B.Mul(&p2.Y, &p1.Z) + + if p1.X.Equal(&A) && p1.Y.Equal(&B) { + p.MixedDouble(p1) + return p + } + + tmp.Add(&p2.X, &p2.Y) + A.Sub(&p1.Y, &p1.X). + Mul(&A, &tmp) + tmp.Add(&p1.X, &p1.Y) + B.Sub(&p2.Y, &p2.X). + Mul(&B, &tmp) + C.Mul(&p1.Z, &p2.X). + Mul(&C, &p2.Y). + Double(&C) + D.Double(&p1.T) + E.Add(&D, &C) + F.Sub(&B, &A) + G.Add(&B, &A) + H.Sub(&D, &C) + + p.X.Mul(&F, &E) + p.Y.Mul(&G, &H) + p.T.Mul(&E, &H) + p.Z.Mul(&G, &F) + + return p +} + +// Double adds points in extended coordinates +// Dedicated doubling +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd +func (p *PointExtended) Double(p1 *PointExtended) *PointExtended { + + var A, B, C, D, E, F, G, H fr.Element + + A.Square(&p1.X) + B.Square(&p1.Y) + C.Square(&p1.Z). + Double(&C) + D.Set(&A) + mulByA(&D) + E.Add(&p1.X, &p1.Y). + Square(&E). + Sub(&E, &A). + Sub(&E, &B) + G.Add(&D, &B) + F.Sub(&G, &C) + H.Sub(&D, &B) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&H, &E) + p.Z.Mul(&F, &G) + + return p +} + +// MixedDouble adds points in extended coordinates +// Dedicated mixed doubling +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-mdbl-2008-hwcd +func (p *PointExtended) MixedDouble(p1 *PointExtended) *PointExtended { + + var A, B, D, E, G, H, two fr.Element + two.SetUint64(2) + + A.Square(&p1.X) + B.Square(&p1.Y) + D.Set(&A) + mulByA(&D) + E.Add(&p1.X, &p1.Y). + Square(&E). + Sub(&E, &A). + Sub(&E, &B) + G.Add(&D, &B) + H.Sub(&D, &B) + + p.X.Sub(&G, &two). + Mul(&p.X, &E) + p.Y.Mul(&G, &H) + p.T.Mul(&H, &E) + p.Z.Square(&G). + Sub(&p.Z, &G). + Sub(&p.Z, &G) + + return p +} + +// setInfinity sets p to O (0:1:1:0) +func (p *PointExtended) setInfinity() *PointExtended { + p.X.SetZero() + p.Y.SetOne() + p.Z.SetOne() + p.T.SetZero() + return p +} + // ScalarMul scalar multiplication of a point -// p1 in affine coordinates with a scalar in big.Int -func (p *PointAffine) ScalarMul(p1 *PointAffine, scalar *big.Int) *PointAffine { +// p1 in extended coordinates with a scalar in big.Int +func (p *PointExtended) ScalarMul(p1 *PointExtended, scalar *big.Int) *PointExtended { + + var _scalar big.Int + _scalar.Set(scalar) + p.Set(p1) + if _scalar.Sign() == -1 { + _scalar.Neg(&_scalar) + p.Neg(p) + } + var resExtended PointExtended + resExtended.setInfinity() + const wordSize = bits.UintSize + sWords := _scalar.Bits() - var p1Proj, resProj PointProj - p1Proj.FromAffine(p1) - resProj.ScalarMul(&p1Proj, scalar) - p.FromProj(&resProj) + for i := len(sWords) - 1; i >= 0; i-- { + ithWord := sWords[i] + for k := 0; k < wordSize; k++ { + resExtended.Double(&resExtended) + kthBit := (ithWord >> (wordSize - 1 - k)) & 1 + if kthBit == 1 { + resExtended.Add(&resExtended, p) + } + } + } + p.Set(&resExtended) return p } diff --git a/ecc/bw6-633/twistededwards/twistededwards_test.go b/ecc/bw6-633/twistededwards/twistededwards_test.go index 0c51dd0343..da247758ad 100644 --- a/ecc/bw6-633/twistededwards/twistededwards_test.go +++ b/ecc/bw6-633/twistededwards/twistededwards_test.go @@ -123,7 +123,7 @@ func TestReceiverIsOperand(t *testing.T) { )) properties.TestingRun(t, gopter.ConsoleReporter(false)) - // proj + // projective properties.Property("Equal projective: having the receiver as operand should output the same result", prop.ForAll( func() bool { params := GetEdwardsCurve() @@ -191,6 +191,74 @@ func TestReceiverIsOperand(t *testing.T) { }, )) + // extended + properties.Property("Equal extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + params := GetEdwardsCurve() + var p1, baseProj PointProj + p1.FromAffine(¶ms.Base) + baseProj.FromAffine(¶ms.Base) + + return p1.Equal(&p1) && p1.Equal(&baseProj) + }, + )) + + properties.Property("Add extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2, p3 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + p3.FromAffine(¶ms.Base) + + res := true + + p3.Add(&p1, &p2) + p1.Add(&p1, &p2) + res = res && p3.Equal(&p1) + + p1.FromAffine(¶ms.Base) + p2.Add(&p1, &p2) + res = res && p2.Equal(&p3) + + return res + }, + )) + + properties.Property("Double extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + + p2.Double(&p1) + p1.Double(&p1) + + return p2.Equal(&p1) + }, + )) + + properties.Property("Neg extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + + p2.Neg(&p1) + p1.Neg(&p1) + + return p2.Equal(&p1) + }, + )) + properties.TestingRun(t, gopter.ConsoleReporter(false)) } @@ -244,7 +312,7 @@ func TestOps(t *testing.T) { var one fr.Element one.SetOne() - return p1.IsOnCurve() && p1.X.IsZero() && p1.Y.Equal(&one) + return p1.IsOnCurve() && p1.IsZero() }, genS1, )) @@ -308,7 +376,7 @@ func TestOps(t *testing.T) { genS1, )) - properties.Property("[5]P=[2][2]P+P", prop.ForAll( + properties.Property("(affine) [5]P=[2][2]P+P", prop.ForAll( func(s1 big.Int) bool { params := GetEdwardsCurve() @@ -325,7 +393,7 @@ func TestOps(t *testing.T) { genS1, )) - // proj + // projective properties.Property("(projective) P+(-P)=O", prop.ForAll( func(s1 big.Int) bool { @@ -338,7 +406,7 @@ func TestOps(t *testing.T) { p.Add(&p1, &p2) - return p.X.IsZero() && p.Y.Equal(&p.Z) + return p.IsZero() }, genS1, )) @@ -361,8 +429,119 @@ func TestOps(t *testing.T) { genS1, )) - // mixed - properties.Property("(mixed) P+(-P)=O", prop.ForAll( + properties.Property("(projective) [5]P=[2][2]P+P", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseProj, p1, p2 PointProj + baseProj.FromAffine(¶ms.Base) + p1.ScalarMul(&baseProj, &s1) + + five := big.NewInt(5) + p2.Double(&p1).Double(&p2).Add(&p2, &p1) + p1.ScalarMul(&p1, five) + + return p2.Equal(&p1) + }, + genS1, + )) + + // extended + properties.Property("(extended) P+(-P)=O", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2, p PointExtended + baseExtended.FromAffine(¶ms.Base) + p1.ScalarMul(&baseExtended, &s1) + p2.Neg(&p1) + + p.Add(&p1, &p2) + + return p.IsZero() + }, + genS1, + )) + + properties.Property("(extended) P+P=2*P", prop.ForAll( + + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2, p PointExtended + baseExtended.FromAffine(¶ms.Base) + p.ScalarMul(&baseExtended, &s) + + p1.Add(&p, &p) + p2.Double(&p) + + return p1.Equal(&p2) + }, + genS1, + )) + + properties.Property("(extended) [5]P=[2][2]P+P", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2 PointExtended + baseExtended.FromAffine(¶ms.Base) + p1.ScalarMul(&baseExtended, &s1) + + five := big.NewInt(5) + p2.Double(&p1).Double(&p2).Add(&p2, &p1) + p1.ScalarMul(&p1, five) + + return p2.Equal(&p1) + }, + genS1, + )) + + // mixed affine+extended + properties.Property("(mixed affine+extended) P+(-P)=O", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, pExtended, p PointExtended + var pAffine PointAffine + baseExtended.FromAffine(¶ms.Base) + pExtended.ScalarMul(&baseExtended, &s) + pAffine.ScalarMul(¶ms.Base, &s) + pAffine.Neg(&pAffine) + + p.MixedAdd(&pExtended, &pAffine) + + return p.IsZero() + }, + genS1, + )) + + properties.Property("(mixed affine+extended) P+P=2*P", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, pExtended, p, p2 PointExtended + var pAffine PointAffine + baseExtended.FromAffine(¶ms.Base) + pExtended.ScalarMul(&baseExtended, &s) + pAffine.ScalarMul(¶ms.Base, &s) + + p.MixedAdd(&pExtended, &pAffine) + p2.MixedDouble(&pExtended) + + return p.Equal(&p2) + }, + genS1, + )) + + // mixed affine+projective + properties.Property("(mixed affine+proj) P+(-P)=O", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() @@ -376,12 +555,12 @@ func TestOps(t *testing.T) { p.MixedAdd(&pProj, &pAffine) - return p.X.IsZero() && p.Y.Equal(&p.Z) + return p.IsZero() }, genS1, )) - properties.Property("(mixed) P+P=2*P", prop.ForAll( + properties.Property("(mixed affine+proj) P+P=2*P", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() @@ -400,6 +579,27 @@ func TestOps(t *testing.T) { genS1, )) + properties.Property("scalar multiplication in Proj vs Ext should be consistant", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseProj PointProj + var baseExt PointExtended + var p1, p2 PointAffine + baseProj.FromAffine(¶ms.Base) + baseProj.ScalarMul(&baseProj, &s) + baseExt.FromAffine(¶ms.Base) + baseExt.ScalarMul(&baseExt, &s) + + p1.FromProj(&baseProj) + p2.FromExtended(&baseExt) + + return p1.Equal(&p2) + }, + genS1, + )) + properties.TestingRun(t, gopter.ConsoleReporter(false)) } @@ -437,7 +637,25 @@ func GenBigInt() gopter.Gen { // ------------------------------------------------------------ // benches -func BenchmarkScalarMul(b *testing.B) { +func BenchmarkScalarMulExtended(b *testing.B) { + params := GetEdwardsCurve() + var a PointExtended + var s big.Int + a.FromAffine(¶ms.Base) + s.SetString("52435875175126190479447705081859658376581184513", 10) + s.Add(&s, ¶ms.Order) + + var doubleAndAdd PointExtended + + b.Run("double and add", func(b *testing.B) { + b.ResetTimer() + for j := 0; j < b.N; j++ { + doubleAndAdd.ScalarMul(&a, &s) + } + }) +} + +func BenchmarkScalarMulProjective(b *testing.B) { params := GetEdwardsCurve() var a PointProj var s big.Int diff --git a/ecc/bw6-761/twistededwards/point.go b/ecc/bw6-761/twistededwards/point.go index 15a8613649..5677768433 100644 --- a/ecc/bw6-761/twistededwards/point.go +++ b/ecc/bw6-761/twistededwards/point.go @@ -35,6 +35,11 @@ type PointProj struct { X, Y, Z fr.Element } +// PointExtended point in extended coordinates +type PointExtended struct { + X, Y, Z, T fr.Element +} + const ( //following https://tools.ietf.org/html/rfc8032#section-3.1, // an fr element x is negative if its binary encoding is @@ -128,14 +133,6 @@ func (p *PointAffine) Unmarshal(b []byte) error { return err } -// Set sets p to p1 and return it -func (p *PointProj) Set(p1 *PointProj) *PointProj { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - p.Z.Set(&p1.Z) - return p -} - // Set sets p to p1 and return it func (p *PointAffine) Set(p1 *PointAffine) *PointAffine { p.X.Set(&p1.X) @@ -148,16 +145,11 @@ func (p *PointAffine) Equal(p1 *PointAffine) bool { return p.X.Equal(&p1.X) && p.Y.Equal(&p1.Y) } -// Equal returns true if p=p1 false otherwise -// If one point is on the affine chart Z=0 it returns false -func (p *PointProj) Equal(p1 *PointProj) bool { - if p.Z.IsZero() || p1.Z.IsZero() { - return false - } - var pAffine, p1Affine PointAffine - pAffine.FromProj(p) - p1Affine.FromProj(p1) - return pAffine.Equal(&p1Affine) +// IsZero returns true if p=0 false otherwise +func (p *PointAffine) IsZero() bool { + var one fr.Element + one.SetOne() + return p.X.IsZero() && p.Y.Equal(&one) } // NewPointAffine creates a new instance of PointAffine @@ -186,6 +178,13 @@ func (p *PointAffine) IsOnCurve() bool { return lhs.Equal(&rhs) } +// Neg sets p to -p1 and returns it +func (p *PointAffine) Neg(p1 *PointAffine) *PointAffine { + p.Set(p1) + p.X.Neg(&p.X) + return p +} + // Add adds two points (x,y), (u,v) on a twisted Edwards curve with parameters a, d // modifies p func (p *PointAffine) Add(p1, p2 *PointAffine) *PointAffine { @@ -237,60 +236,84 @@ func (p *PointAffine) Double(p1 *PointAffine) *PointAffine { return p } -// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d -// modifies p -func (p *PointProj) Neg(p1 *PointProj) *PointProj { - p.Set(p1) - p.X.Neg(&p.X) +// FromProj sets p in affine from p in projective +func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { + var I fr.Element + I.Inverse(&p1.Z) + p.X.Mul(&p1.X, &I) + p.Y.Mul(&p1.Y, &I) return p } -// FromProj sets p in affine from p in projective -func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { - p.X.Div(&p1.X, &p1.Z) - p.Y.Div(&p1.Y, &p1.Z) +// FromExtended sets p in affine from p in extended coordinates +func (p *PointAffine) FromExtended(p1 *PointExtended) *PointAffine { + var I fr.Element + I.Inverse(&p1.Z) + p.X.Mul(&p1.X, &I) + p.Y.Mul(&p1.Y, &I) return p } -// FromAffine sets p in projective from p in affine -func (p *PointProj) FromAffine(p1 *PointAffine) *PointProj { +// ScalarMul scalar multiplication of a point +// p1 in affine coordinates with a scalar in big.Int +func (p *PointAffine) ScalarMul(p1 *PointAffine, scalar *big.Int) *PointAffine { + + var p1Extended, resExtended PointExtended + p1Extended.FromAffine(p1) + resExtended.ScalarMul(&p1Extended, scalar) + p.FromExtended(&resExtended) + + return p +} + +//-------- Projective coordinates + +// Set sets p to p1 and return it +func (p *PointProj) Set(p1 *PointProj) *PointProj { p.X.Set(&p1.X) p.Y.Set(&p1.Y) - p.Z.SetOne() + p.Z.Set(&p1.Z) return p } -// Add adds points in projective coordinates -// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp -func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { +// setInfinity sets p to O (0:1:1) +func (p *PointProj) setInfinity() *PointProj { + p.X.SetZero() + p.Y.SetOne() + p.Z.SetOne() + return p +} - var res PointProj +// Equal returns true if p=p1 false otherwise +// If one point is on the affine chart Z=0 it returns false +func (p *PointProj) Equal(p1 *PointProj) bool { + if p.Z.IsZero() || p1.Z.IsZero() { + return false + } + var pAffine, p1Affine PointAffine + pAffine.FromProj(p) + p1Affine.FromProj(p1) + return pAffine.Equal(&p1Affine) +} - ecurve := GetEdwardsCurve() +// IsZero returns true if p=0 false otherwise +func (p *PointProj) IsZero() bool { + return p.X.IsZero() && p.Y.Equal(&p.Z) +} - var A, B, C, D, E, F, G, H, I fr.Element - A.Mul(&p1.Z, &p2.Z) - B.Square(&A) - C.Mul(&p1.X, &p2.X) - D.Mul(&p1.Y, &p2.Y) - E.Mul(&ecurve.D, &C).Mul(&E, &D) - F.Sub(&B, &E) - G.Add(&B, &E) - H.Add(&p1.X, &p1.Y) - I.Add(&p2.X, &p2.Y) - res.X.Mul(&H, &I). - Sub(&res.X, &C). - Sub(&res.X, &D). - Mul(&res.X, &A). - Mul(&res.X, &F) - mulByA(&C) - C.Neg(&C) - res.Y.Add(&D, &C). - Mul(&res.Y, &A). - Mul(&res.Y, &G) - res.Z.Mul(&F, &G) +// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d +// modifies p +func (p *PointProj) Neg(p1 *PointProj) *PointProj { + p.Set(p1) + p.X.Neg(&p.X) + return p +} - p.Set(&res) +// FromAffine sets p in projective from p in affine +func (p *PointProj) FromAffine(p1 *PointAffine) *PointProj { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.Z.SetOne() return p } @@ -298,8 +321,6 @@ func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { // cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-madd-2008-bbjlp func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { - var res PointProj - ecurve := GetEdwardsCurve() var B, C, D, E, F, G, H, I fr.Element @@ -311,18 +332,17 @@ func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { G.Add(&B, &E) H.Add(&p1.X, &p1.Y) I.Add(&p2.X, &p2.Y) - res.X.Mul(&H, &I). - Sub(&res.X, &C). - Sub(&res.X, &D). - Mul(&res.X, &p1.Z). - Mul(&res.X, &F) + p.X.Mul(&H, &I). + Sub(&p.X, &C). + Sub(&p.X, &D). + Mul(&p.X, &p1.Z). + Mul(&p.X, &F) mulByA(&C) - res.Y.Sub(&D, &C). - Mul(&res.Y, &p1.Z). - Mul(&res.Y, &G) - res.Z.Mul(&F, &G) + p.Y.Sub(&D, &C). + Mul(&p.Y, &p1.Z). + Mul(&p.Y, &G) + p.Z.Mul(&F, &G) - p.Set(&res) return p } @@ -330,8 +350,6 @@ func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { // cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#doubling-dbl-2008-bbjlp func (p *PointProj) Double(p1 *PointProj) *PointProj { - var res PointProj - var B, C, D, E, F, H, J fr.Element B.Add(&p1.X, &p1.Y).Square(&B) @@ -342,28 +360,43 @@ func (p *PointProj) Double(p1 *PointProj) *PointProj { F.Add(&E, &D) H.Square(&p1.Z) J.Sub(&F, &H).Sub(&J, &H) - res.X.Sub(&B, &C). - Sub(&res.X, &D). - Mul(&res.X, &J) - res.Y.Sub(&E, &D).Mul(&res.Y, &F) - res.Z.Mul(&F, &J) + p.X.Sub(&B, &C). + Sub(&p.X, &D). + Mul(&p.X, &J) + p.Y.Sub(&E, &D).Mul(&p.Y, &F) + p.Z.Mul(&F, &J) - p.Set(&res) return p } -// Neg sets p to -p1 and returns it -func (p *PointAffine) Neg(p1 *PointAffine) *PointAffine { - p.Set(p1) - p.X.Neg(&p.X) - return p -} +// Add adds points in projective coordinates +// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp +func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { + + ecurve := GetEdwardsCurve() + + var A, B, C, D, E, F, G, H, I fr.Element + A.Mul(&p1.Z, &p2.Z) + B.Square(&A) + C.Mul(&p1.X, &p2.X) + D.Mul(&p1.Y, &p2.Y) + E.Mul(&ecurve.D, &C).Mul(&E, &D) + F.Sub(&B, &E) + G.Add(&B, &E) + H.Add(&p1.X, &p1.Y) + I.Add(&p2.X, &p2.Y) + p.X.Mul(&H, &I). + Sub(&p.X, &C). + Sub(&p.X, &D). + Mul(&p.X, &A). + Mul(&p.X, &F) + mulByA(&C) + C.Neg(&C) + p.Y.Add(&D, &C). + Mul(&p.Y, &A). + Mul(&p.Y, &G) + p.Z.Mul(&F, &G) -// setInfinity sets p to O (0:1:1) -func (p *PointProj) setInfinity() *PointProj { - p.X.SetZero() - p.Y.SetOne() - p.Z.SetOne() return p } @@ -398,14 +431,218 @@ func (p *PointProj) ScalarMul(p1 *PointProj, scalar *big.Int) *PointProj { return p } +// ------- Extended coordinates + +// Set sets p to p1 and return it +func (p *PointExtended) Set(p1 *PointExtended) *PointExtended { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.T.Set(&p1.T) + p.Z.Set(&p1.Z) + return p +} + +// IsZero returns true if p=0 false otherwise +func (p *PointExtended) IsZero() bool { + return p.X.IsZero() && p.Y.Equal(&p.Z) && p.T.IsZero() +} + +// Equal returns true if p=p1 false otherwise +// If one point is on the affine chart Z=0 it returns false +func (p *PointExtended) Equal(p1 *PointExtended) bool { + if p.Z.IsZero() || p1.Z.IsZero() { + return false + } + var pAffine, p1Affine PointAffine + pAffine.FromExtended(p) + p1Affine.FromExtended(p1) + return pAffine.Equal(&p1Affine) +} + +// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d +// modifies p +func (p *PointExtended) Neg(p1 *PointExtended) *PointExtended { + p.Set(p1) + p.X.Neg(&p.X) + p.T.Neg(&p.T) + return p +} + +// FromAffine sets p in projective from p in affine +func (p *PointExtended) FromAffine(p1 *PointAffine) *PointExtended { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.Z.SetOne() + p.T.Mul(&p1.X, &p1.Y) + return p +} + +// Add adds points in extended coordinates +// dedicated addition +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4 +func (p *PointExtended) Add(p1, p2 *PointExtended) *PointExtended { + + if p1.Equal(p2) { + p.Double(p1) + return p + } + + var A, B, C, D, E, F, G, H, tmp fr.Element + tmp.Add(&p2.X, &p2.Y) + A.Sub(&p1.Y, &p1.X). + Mul(&A, &tmp) + tmp.Add(&p1.X, &p1.Y) + B.Sub(&p2.Y, &p2.X). + Mul(&B, &tmp) + C.Mul(&p1.Z, &p2.T). + Double(&C) + D.Mul(&p2.Z, &p1.T). + Double(&D) + E.Add(&D, &C) + F.Sub(&B, &A) + G.Add(&B, &A) + H.Sub(&D, &C) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&E, &H) + p.Z.Mul(&F, &G) + + return p +} + +// MixedAdd adds a point in extended coordinates to a point in affine coordinates +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-madd-2008-hwcd-4 +func (p *PointExtended) MixedAdd(p1 *PointExtended, p2 *PointAffine) *PointExtended { + + var A, B, C, D, E, F, G, H, tmp fr.Element + + A.Mul(&p2.X, &p1.Z) + B.Mul(&p2.Y, &p1.Z) + + if p1.X.Equal(&A) && p1.Y.Equal(&B) { + p.MixedDouble(p1) + return p + } + + tmp.Add(&p2.X, &p2.Y) + A.Sub(&p1.Y, &p1.X). + Mul(&A, &tmp) + tmp.Add(&p1.X, &p1.Y) + B.Sub(&p2.Y, &p2.X). + Mul(&B, &tmp) + C.Mul(&p1.Z, &p2.X). + Mul(&C, &p2.Y). + Double(&C) + D.Double(&p1.T) + E.Add(&D, &C) + F.Sub(&B, &A) + G.Add(&B, &A) + H.Sub(&D, &C) + + p.X.Mul(&F, &E) + p.Y.Mul(&G, &H) + p.T.Mul(&E, &H) + p.Z.Mul(&G, &F) + + return p +} + +// Double adds points in extended coordinates +// Dedicated doubling +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd +func (p *PointExtended) Double(p1 *PointExtended) *PointExtended { + + var A, B, C, D, E, F, G, H fr.Element + + A.Square(&p1.X) + B.Square(&p1.Y) + C.Square(&p1.Z). + Double(&C) + D.Set(&A) + mulByA(&D) + E.Add(&p1.X, &p1.Y). + Square(&E). + Sub(&E, &A). + Sub(&E, &B) + G.Add(&D, &B) + F.Sub(&G, &C) + H.Sub(&D, &B) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&H, &E) + p.Z.Mul(&F, &G) + + return p +} + +// MixedDouble adds points in extended coordinates +// Dedicated mixed doubling +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-mdbl-2008-hwcd +func (p *PointExtended) MixedDouble(p1 *PointExtended) *PointExtended { + + var A, B, D, E, G, H, two fr.Element + two.SetUint64(2) + + A.Square(&p1.X) + B.Square(&p1.Y) + D.Set(&A) + mulByA(&D) + E.Add(&p1.X, &p1.Y). + Square(&E). + Sub(&E, &A). + Sub(&E, &B) + G.Add(&D, &B) + H.Sub(&D, &B) + + p.X.Sub(&G, &two). + Mul(&p.X, &E) + p.Y.Mul(&G, &H) + p.T.Mul(&H, &E) + p.Z.Square(&G). + Sub(&p.Z, &G). + Sub(&p.Z, &G) + + return p +} + +// setInfinity sets p to O (0:1:1:0) +func (p *PointExtended) setInfinity() *PointExtended { + p.X.SetZero() + p.Y.SetOne() + p.Z.SetOne() + p.T.SetZero() + return p +} + // ScalarMul scalar multiplication of a point -// p1 in affine coordinates with a scalar in big.Int -func (p *PointAffine) ScalarMul(p1 *PointAffine, scalar *big.Int) *PointAffine { +// p1 in extended coordinates with a scalar in big.Int +func (p *PointExtended) ScalarMul(p1 *PointExtended, scalar *big.Int) *PointExtended { + + var _scalar big.Int + _scalar.Set(scalar) + p.Set(p1) + if _scalar.Sign() == -1 { + _scalar.Neg(&_scalar) + p.Neg(p) + } + var resExtended PointExtended + resExtended.setInfinity() + const wordSize = bits.UintSize + sWords := _scalar.Bits() - var p1Proj, resProj PointProj - p1Proj.FromAffine(p1) - resProj.ScalarMul(&p1Proj, scalar) - p.FromProj(&resProj) + for i := len(sWords) - 1; i >= 0; i-- { + ithWord := sWords[i] + for k := 0; k < wordSize; k++ { + resExtended.Double(&resExtended) + kthBit := (ithWord >> (wordSize - 1 - k)) & 1 + if kthBit == 1 { + resExtended.Add(&resExtended, p) + } + } + } + p.Set(&resExtended) return p } diff --git a/ecc/bw6-761/twistededwards/twistededwards_test.go b/ecc/bw6-761/twistededwards/twistededwards_test.go index 5318a2b283..17db363a21 100644 --- a/ecc/bw6-761/twistededwards/twistededwards_test.go +++ b/ecc/bw6-761/twistededwards/twistededwards_test.go @@ -123,7 +123,7 @@ func TestReceiverIsOperand(t *testing.T) { )) properties.TestingRun(t, gopter.ConsoleReporter(false)) - // proj + // projective properties.Property("Equal projective: having the receiver as operand should output the same result", prop.ForAll( func() bool { params := GetEdwardsCurve() @@ -191,6 +191,74 @@ func TestReceiverIsOperand(t *testing.T) { }, )) + // extended + properties.Property("Equal extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + params := GetEdwardsCurve() + var p1, baseProj PointProj + p1.FromAffine(¶ms.Base) + baseProj.FromAffine(¶ms.Base) + + return p1.Equal(&p1) && p1.Equal(&baseProj) + }, + )) + + properties.Property("Add extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2, p3 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + p3.FromAffine(¶ms.Base) + + res := true + + p3.Add(&p1, &p2) + p1.Add(&p1, &p2) + res = res && p3.Equal(&p1) + + p1.FromAffine(¶ms.Base) + p2.Add(&p1, &p2) + res = res && p2.Equal(&p3) + + return res + }, + )) + + properties.Property("Double extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + + p2.Double(&p1) + p1.Double(&p1) + + return p2.Equal(&p1) + }, + )) + + properties.Property("Neg extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + + p2.Neg(&p1) + p1.Neg(&p1) + + return p2.Equal(&p1) + }, + )) + properties.TestingRun(t, gopter.ConsoleReporter(false)) } @@ -244,7 +312,7 @@ func TestOps(t *testing.T) { var one fr.Element one.SetOne() - return p1.IsOnCurve() && p1.X.IsZero() && p1.Y.Equal(&one) + return p1.IsOnCurve() && p1.IsZero() }, genS1, )) @@ -308,7 +376,7 @@ func TestOps(t *testing.T) { genS1, )) - properties.Property("[5]P=[2][2]P+P", prop.ForAll( + properties.Property("(affine) [5]P=[2][2]P+P", prop.ForAll( func(s1 big.Int) bool { params := GetEdwardsCurve() @@ -325,7 +393,7 @@ func TestOps(t *testing.T) { genS1, )) - // proj + // projective properties.Property("(projective) P+(-P)=O", prop.ForAll( func(s1 big.Int) bool { @@ -338,7 +406,7 @@ func TestOps(t *testing.T) { p.Add(&p1, &p2) - return p.X.IsZero() && p.Y.Equal(&p.Z) + return p.IsZero() }, genS1, )) @@ -361,8 +429,119 @@ func TestOps(t *testing.T) { genS1, )) - // mixed - properties.Property("(mixed) P+(-P)=O", prop.ForAll( + properties.Property("(projective) [5]P=[2][2]P+P", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseProj, p1, p2 PointProj + baseProj.FromAffine(¶ms.Base) + p1.ScalarMul(&baseProj, &s1) + + five := big.NewInt(5) + p2.Double(&p1).Double(&p2).Add(&p2, &p1) + p1.ScalarMul(&p1, five) + + return p2.Equal(&p1) + }, + genS1, + )) + + // extended + properties.Property("(extended) P+(-P)=O", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2, p PointExtended + baseExtended.FromAffine(¶ms.Base) + p1.ScalarMul(&baseExtended, &s1) + p2.Neg(&p1) + + p.Add(&p1, &p2) + + return p.IsZero() + }, + genS1, + )) + + properties.Property("(extended) P+P=2*P", prop.ForAll( + + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2, p PointExtended + baseExtended.FromAffine(¶ms.Base) + p.ScalarMul(&baseExtended, &s) + + p1.Add(&p, &p) + p2.Double(&p) + + return p1.Equal(&p2) + }, + genS1, + )) + + properties.Property("(extended) [5]P=[2][2]P+P", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2 PointExtended + baseExtended.FromAffine(¶ms.Base) + p1.ScalarMul(&baseExtended, &s1) + + five := big.NewInt(5) + p2.Double(&p1).Double(&p2).Add(&p2, &p1) + p1.ScalarMul(&p1, five) + + return p2.Equal(&p1) + }, + genS1, + )) + + // mixed affine+extended + properties.Property("(mixed affine+extended) P+(-P)=O", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, pExtended, p PointExtended + var pAffine PointAffine + baseExtended.FromAffine(¶ms.Base) + pExtended.ScalarMul(&baseExtended, &s) + pAffine.ScalarMul(¶ms.Base, &s) + pAffine.Neg(&pAffine) + + p.MixedAdd(&pExtended, &pAffine) + + return p.IsZero() + }, + genS1, + )) + + properties.Property("(mixed affine+extended) P+P=2*P", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, pExtended, p, p2 PointExtended + var pAffine PointAffine + baseExtended.FromAffine(¶ms.Base) + pExtended.ScalarMul(&baseExtended, &s) + pAffine.ScalarMul(¶ms.Base, &s) + + p.MixedAdd(&pExtended, &pAffine) + p2.MixedDouble(&pExtended) + + return p.Equal(&p2) + }, + genS1, + )) + + // mixed affine+projective + properties.Property("(mixed affine+proj) P+(-P)=O", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() @@ -376,12 +555,12 @@ func TestOps(t *testing.T) { p.MixedAdd(&pProj, &pAffine) - return p.X.IsZero() && p.Y.Equal(&p.Z) + return p.IsZero() }, genS1, )) - properties.Property("(mixed) P+P=2*P", prop.ForAll( + properties.Property("(mixed affine+proj) P+P=2*P", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() @@ -400,6 +579,27 @@ func TestOps(t *testing.T) { genS1, )) + properties.Property("scalar multiplication in Proj vs Ext should be consistant", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseProj PointProj + var baseExt PointExtended + var p1, p2 PointAffine + baseProj.FromAffine(¶ms.Base) + baseProj.ScalarMul(&baseProj, &s) + baseExt.FromAffine(¶ms.Base) + baseExt.ScalarMul(&baseExt, &s) + + p1.FromProj(&baseProj) + p2.FromExtended(&baseExt) + + return p1.Equal(&p2) + }, + genS1, + )) + properties.TestingRun(t, gopter.ConsoleReporter(false)) } @@ -437,7 +637,25 @@ func GenBigInt() gopter.Gen { // ------------------------------------------------------------ // benches -func BenchmarkScalarMul(b *testing.B) { +func BenchmarkScalarMulExtended(b *testing.B) { + params := GetEdwardsCurve() + var a PointExtended + var s big.Int + a.FromAffine(¶ms.Base) + s.SetString("52435875175126190479447705081859658376581184513", 10) + s.Add(&s, ¶ms.Order) + + var doubleAndAdd PointExtended + + b.Run("double and add", func(b *testing.B) { + b.ResetTimer() + for j := 0; j < b.N; j++ { + doubleAndAdd.ScalarMul(&a, &s) + } + }) +} + +func BenchmarkScalarMulProjective(b *testing.B) { params := GetEdwardsCurve() var a PointProj var s big.Int diff --git a/internal/generator/edwards/template/pointtwistededwards.go.tmpl b/internal/generator/edwards/template/pointtwistededwards.go.tmpl index b1d86d6594..7516de1ca3 100644 --- a/internal/generator/edwards/template/pointtwistededwards.go.tmpl +++ b/internal/generator/edwards/template/pointtwistededwards.go.tmpl @@ -17,6 +17,11 @@ type PointProj struct { X, Y, Z fr.Element } +// PointExtended point in extended coordinates +type PointExtended struct { + X, Y, Z, T fr.Element +} + const ( //following https://tools.ietf.org/html/rfc8032#section-3.1, // an fr element x is negative if its binary encoding is @@ -110,14 +115,6 @@ func (p *PointAffine) Unmarshal(b []byte) error { return err } -// Set sets p to p1 and return it -func (p *PointProj) Set(p1 *PointProj) *PointProj { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - p.Z.Set(&p1.Z) - return p -} - // Set sets p to p1 and return it func (p *PointAffine) Set(p1 *PointAffine) *PointAffine { p.X.Set(&p1.X) @@ -130,16 +127,11 @@ func (p *PointAffine) Equal(p1 *PointAffine) bool { return p.X.Equal(&p1.X) && p.Y.Equal(&p1.Y) } -// Equal returns true if p=p1 false otherwise -// If one point is on the affine chart Z=0 it returns false -func (p *PointProj) Equal(p1 *PointProj) bool { - if p.Z.IsZero() || p1.Z.IsZero() { - return false - } - var pAffine, p1Affine PointAffine - pAffine.FromProj(p) - p1Affine.FromProj(p1) - return pAffine.Equal(&p1Affine) +// IsZero returns true if p=0 false otherwise +func (p *PointAffine) IsZero() bool { + var one fr.Element + one.SetOne() + return p.X.IsZero() && p.Y.Equal(&one) } // NewPointAffine creates a new instance of PointAffine @@ -156,7 +148,7 @@ func (p *PointAffine) IsOnCurve() bool { tmp.Mul(&p.Y, &p.Y) lhs.Mul(&p.X, &p.X) - mulByA(&lhs) + mulByA(&lhs) lhs.Add(&lhs, &tmp) tmp.Mul(&p.X, &p.X). @@ -168,6 +160,13 @@ func (p *PointAffine) IsOnCurve() bool { return lhs.Equal(&rhs) } +// Neg sets p to -p1 and returns it +func (p *PointAffine) Neg(p1 *PointAffine) *PointAffine { + p.Set(p1) + p.X.Neg(&p.X) + return p +} + // Add adds two points (x,y), (u,v) on a twisted Edwards curve with parameters a, d // modifies p func (p *PointAffine) Add(p1, p2 *PointAffine) *PointAffine { @@ -181,7 +180,7 @@ func (p *PointAffine) Add(p1, p2 *PointAffine) *PointAffine { pRes.X.Add(&xv, &yu) xu.Mul(&p1.X, &p2.X) - mulByA(&xu) + mulByA(&xu) yv.Mul(&p1.Y, &p2.Y) pRes.Y.Sub(&yv, &xu) @@ -219,60 +218,84 @@ func (p *PointAffine) Double(p1 *PointAffine) *PointAffine { return p } -// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d -// modifies p -func (p *PointProj) Neg(p1 *PointProj) *PointProj { - p.Set(p1) - p.X.Neg(&p.X) +// FromProj sets p in affine from p in projective +func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { + var I fr.Element + I.Inverse(&p1.Z) + p.X.Mul(&p1.X, &I) + p.Y.Mul(&p1.Y, &I) return p } -// FromProj sets p in affine from p in projective -func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { - p.X.Div(&p1.X, &p1.Z) - p.Y.Div(&p1.Y, &p1.Z) +// FromExtended sets p in affine from p in extended coordinates +func (p *PointAffine) FromExtended(p1 *PointExtended) *PointAffine { + var I fr.Element + I.Inverse(&p1.Z) + p.X.Mul(&p1.X, &I) + p.Y.Mul(&p1.Y, &I) return p } -// FromAffine sets p in projective from p in affine -func (p *PointProj) FromAffine(p1 *PointAffine) *PointProj { +// ScalarMul scalar multiplication of a point +// p1 in affine coordinates with a scalar in big.Int +func (p *PointAffine) ScalarMul(p1 *PointAffine, scalar *big.Int) *PointAffine { + + var p1Extended, resExtended PointExtended + p1Extended.FromAffine(p1) + resExtended.ScalarMul(&p1Extended, scalar) + p.FromExtended(&resExtended) + + return p +} + +//-------- Projective coordinates + +// Set sets p to p1 and return it +func (p *PointProj) Set(p1 *PointProj) *PointProj { p.X.Set(&p1.X) p.Y.Set(&p1.Y) - p.Z.SetOne() + p.Z.Set(&p1.Z) return p } -// Add adds points in projective coordinates -// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp -func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { +// setInfinity sets p to O (0:1:1) +func (p *PointProj) setInfinity() *PointProj { + p.X.SetZero() + p.Y.SetOne() + p.Z.SetOne() + return p +} - var res PointProj +// Equal returns true if p=p1 false otherwise +// If one point is on the affine chart Z=0 it returns false +func (p *PointProj) Equal(p1 *PointProj) bool { + if p.Z.IsZero() || p1.Z.IsZero() { + return false + } + var pAffine, p1Affine PointAffine + pAffine.FromProj(p) + p1Affine.FromProj(p1) + return pAffine.Equal(&p1Affine) +} - ecurve := GetEdwardsCurve() +// IsZero returns true if p=0 false otherwise +func (p *PointProj) IsZero() bool { + return p.X.IsZero() && p.Y.Equal(&p.Z) +} - var A, B, C, D, E, F, G, H, I fr.Element - A.Mul(&p1.Z, &p2.Z) - B.Square(&A) - C.Mul(&p1.X, &p2.X) - D.Mul(&p1.Y, &p2.Y) - E.Mul(&ecurve.D, &C).Mul(&E, &D) - F.Sub(&B, &E) - G.Add(&B, &E) - H.Add(&p1.X, &p1.Y) - I.Add(&p2.X, &p2.Y) - res.X.Mul(&H, &I). - Sub(&res.X, &C). - Sub(&res.X, &D). - Mul(&res.X, &A). - Mul(&res.X, &F) - mulByA(&C) - C.Neg(&C) - res.Y.Add(&D, &C). - Mul(&res.Y, &A). - Mul(&res.Y, &G) - res.Z.Mul(&F, &G) +// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d +// modifies p +func (p *PointProj) Neg(p1 *PointProj) *PointProj { + p.Set(p1) + p.X.Neg(&p.X) + return p +} - p.Set(&res) +// FromAffine sets p in projective from p in affine +func (p *PointProj) FromAffine(p1 *PointAffine) *PointProj { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.Z.SetOne() return p } @@ -280,8 +303,6 @@ func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { // cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-madd-2008-bbjlp func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { - var res PointProj - ecurve := GetEdwardsCurve() var B, C, D, E, F, G, H, I fr.Element @@ -293,18 +314,17 @@ func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { G.Add(&B, &E) H.Add(&p1.X, &p1.Y) I.Add(&p2.X, &p2.Y) - res.X.Mul(&H, &I). - Sub(&res.X, &C). - Sub(&res.X, &D). - Mul(&res.X, &p1.Z). - Mul(&res.X, &F) + p.X.Mul(&H, &I). + Sub(&p.X, &C). + Sub(&p.X, &D). + Mul(&p.X, &p1.Z). + Mul(&p.X, &F) mulByA(&C) - res.Y.Sub(&D, &C). - Mul(&res.Y, &p1.Z). - Mul(&res.Y, &G) - res.Z.Mul(&F, &G) + p.Y.Sub(&D, &C). + Mul(&p.Y, &p1.Z). + Mul(&p.Y, &G) + p.Z.Mul(&F, &G) - p.Set(&res) return p } @@ -312,41 +332,54 @@ func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { // cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#doubling-dbl-2008-bbjlp func (p *PointProj) Double(p1 *PointProj) *PointProj { - var res PointProj - var B, C, D, E, F, H, J fr.Element B.Add(&p1.X, &p1.Y).Square(&B) C.Square(&p1.X) D.Square(&p1.Y) - E.Set(&C) + E.Set(&C) mulByA(&E) F.Add(&E, &D) H.Square(&p1.Z) J.Sub(&F, &H).Sub(&J, &H) - res.X.Sub(&B, &C). - Sub(&res.X, &D). - Mul(&res.X, &J) - res.Y.Sub(&E, &D).Mul(&res.Y, &F) - res.Z.Mul(&F, &J) + p.X.Sub(&B, &C). + Sub(&p.X, &D). + Mul(&p.X, &J) + p.Y.Sub(&E, &D).Mul(&p.Y, &F) + p.Z.Mul(&F, &J) - p.Set(&res) return p } -// Neg sets p to -p1 and returns it -func (p *PointAffine) Neg(p1 *PointAffine) *PointAffine { - p.Set(p1) - p.X.Neg(&p.X) - return p -} +// Add adds points in projective coordinates +// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp +func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { -// setInfinity sets p to O (0:1:1) -func (p *PointProj) setInfinity() *PointProj { - p.X.SetZero() - p.Y.SetOne() - p.Z.SetOne() - return p + ecurve := GetEdwardsCurve() + + var A, B, C, D, E, F, G, H, I fr.Element + A.Mul(&p1.Z, &p2.Z) + B.Square(&A) + C.Mul(&p1.X, &p2.X) + D.Mul(&p1.Y, &p2.Y) + E.Mul(&ecurve.D, &C).Mul(&E, &D) + F.Sub(&B, &E) + G.Add(&B, &E) + H.Add(&p1.X, &p1.Y) + I.Add(&p2.X, &p2.Y) + p.X.Mul(&H, &I). + Sub(&p.X, &C). + Sub(&p.X, &D). + Mul(&p.X, &A). + Mul(&p.X, &F) + mulByA(&C) + C.Neg(&C) + p.Y.Add(&D, &C). + Mul(&p.Y, &A). + Mul(&p.Y, &G) + p.Z.Mul(&F, &G) + + return p } // ScalarMul scalar multiplication of a point @@ -380,14 +413,218 @@ func (p *PointProj) ScalarMul(p1 *PointProj, scalar *big.Int) *PointProj { return p } +// ------- Extended coordinates + +// Set sets p to p1 and return it +func (p *PointExtended) Set(p1 *PointExtended) *PointExtended { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.T.Set(&p1.T) + p.Z.Set(&p1.Z) + return p +} + +// IsZero returns true if p=0 false otherwise +func (p *PointExtended) IsZero() bool { + return p.X.IsZero() && p.Y.Equal(&p.Z) && p.T.IsZero() +} + +// Equal returns true if p=p1 false otherwise +// If one point is on the affine chart Z=0 it returns false +func (p *PointExtended) Equal(p1 *PointExtended) bool { + if p.Z.IsZero() || p1.Z.IsZero() { + return false + } + var pAffine, p1Affine PointAffine + pAffine.FromExtended(p) + p1Affine.FromExtended(p1) + return pAffine.Equal(&p1Affine) +} + +// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d +// modifies p +func (p *PointExtended) Neg(p1 *PointExtended) *PointExtended { + p.Set(p1) + p.X.Neg(&p.X) + p.T.Neg(&p.T) + return p +} + +// FromAffine sets p in projective from p in affine +func (p *PointExtended) FromAffine(p1 *PointAffine) *PointExtended { + p.X.Set(&p1.X) + p.Y.Set(&p1.Y) + p.Z.SetOne() + p.T.Mul(&p1.X, &p1.Y) + return p +} + +// Add adds points in extended coordinates +// dedicated addition +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4 +func (p *PointExtended) Add(p1, p2 *PointExtended) *PointExtended { + + if p1.Equal(p2) { + p.Double(p1) + return p + } + + var A, B, C, D, E, F, G, H, tmp fr.Element + tmp.Add(&p2.X, &p2.Y) + A.Sub(&p1.Y, &p1.X). + Mul(&A, &tmp) + tmp.Add(&p1.X, &p1.Y) + B.Sub(&p2.Y, &p2.X). + Mul(&B, &tmp) + C.Mul(&p1.Z, &p2.T). + Double(&C) + D.Mul(&p2.Z, &p1.T). + Double(&D) + E.Add(&D, &C) + F.Sub(&B, &A) + G.Add(&B, &A) + H.Sub(&D, &C) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&E, &H) + p.Z.Mul(&F, &G) + + return p +} + +// MixedAdd adds a point in extended coordinates to a point in affine coordinates +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-madd-2008-hwcd-4 +func (p *PointExtended) MixedAdd(p1 *PointExtended, p2 *PointAffine) *PointExtended { + + var A, B, C, D, E, F, G, H, tmp fr.Element + + A.Mul(&p2.X, &p1.Z) + B.Mul(&p2.Y, &p1.Z) + + if p1.X.Equal(&A) && p1.Y.Equal(&B) { + p.MixedDouble(p1) + return p + } + + tmp.Add(&p2.X, &p2.Y) + A.Sub(&p1.Y, &p1.X). + Mul(&A, &tmp) + tmp.Add(&p1.X, &p1.Y) + B.Sub(&p2.Y, &p2.X). + Mul(&B, &tmp) + C.Mul(&p1.Z, &p2.X). + Mul(&C, &p2.Y). + Double(&C) + D.Double(&p1.T) + E.Add(&D, &C) + F.Sub(&B, &A) + G.Add(&B, &A) + H.Sub(&D, &C) + + p.X.Mul(&F, &E) + p.Y.Mul(&G, &H) + p.T.Mul(&E, &H) + p.Z.Mul(&G, &F) + + return p +} + +// Double adds points in extended coordinates +// Dedicated doubling +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd +func (p *PointExtended) Double(p1 *PointExtended) *PointExtended { + + var A, B, C, D, E, F, G, H fr.Element + + A.Square(&p1.X) + B.Square(&p1.Y) + C.Square(&p1.Z). + Double(&C) + D.Set(&A) + mulByA(&D) + E.Add(&p1.X, &p1.Y). + Square(&E). + Sub(&E, &A). + Sub(&E, &B) + G.Add(&D, &B) + F.Sub(&G, &C) + H.Sub(&D, &B) + + p.X.Mul(&E, &F) + p.Y.Mul(&G, &H) + p.T.Mul(&H, &E) + p.Z.Mul(&F, &G) + + return p +} + +// MixedDouble adds points in extended coordinates +// Dedicated mixed doubling +// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-mdbl-2008-hwcd +func (p *PointExtended) MixedDouble(p1 *PointExtended) *PointExtended { + + var A, B, D, E, G, H, two fr.Element + two.SetUint64(2) + + A.Square(&p1.X) + B.Square(&p1.Y) + D.Set(&A) + mulByA(&D) + E.Add(&p1.X, &p1.Y). + Square(&E). + Sub(&E, &A). + Sub(&E, &B) + G.Add(&D, &B) + H.Sub(&D, &B) + + p.X.Sub(&G, &two). + Mul(&p.X, &E) + p.Y.Mul(&G, &H) + p.T.Mul(&H, &E) + p.Z.Square(&G). + Sub(&p.Z, &G). + Sub(&p.Z, &G) + + return p +} + +// setInfinity sets p to O (0:1:1:0) +func (p *PointExtended) setInfinity() *PointExtended { + p.X.SetZero() + p.Y.SetOne() + p.Z.SetOne() + p.T.SetZero() + return p +} + // ScalarMul scalar multiplication of a point -// p1 in affine coordinates with a scalar in big.Int -func (p *PointAffine) ScalarMul(p1 *PointAffine, scalar *big.Int) *PointAffine { +// p1 in extended coordinates with a scalar in big.Int +func (p *PointExtended) ScalarMul(p1 *PointExtended, scalar *big.Int) *PointExtended { - var p1Proj, resProj PointProj - p1Proj.FromAffine(p1) - resProj.ScalarMul(&p1Proj, scalar) - p.FromProj(&resProj) + var _scalar big.Int + _scalar.Set(scalar) + p.Set(p1) + if _scalar.Sign() == -1 { + _scalar.Neg(&_scalar) + p.Neg(p) + } + var resExtended PointExtended + resExtended.setInfinity() + const wordSize = bits.UintSize + sWords := _scalar.Bits() + + for i := len(sWords) - 1; i >= 0; i-- { + ithWord := sWords[i] + for k := 0; k < wordSize; k++ { + resExtended.Double(&resExtended) + kthBit := (ithWord >> (wordSize - 1 - k)) & 1 + if kthBit == 1 { + resExtended.Add(&resExtended, p) + } + } + } + p.Set(&resExtended) return p } diff --git a/internal/generator/edwards/template/tests/pointtwistededwards.go.tmpl b/internal/generator/edwards/template/tests/pointtwistededwards.go.tmpl index 78a2f971e2..5c6f0faf5d 100644 --- a/internal/generator/edwards/template/tests/pointtwistededwards.go.tmpl +++ b/internal/generator/edwards/template/tests/pointtwistededwards.go.tmpl @@ -105,7 +105,7 @@ func TestReceiverIsOperand(t *testing.T) { )) properties.TestingRun(t, gopter.ConsoleReporter(false)) - // proj + // projective properties.Property("Equal projective: having the receiver as operand should output the same result", prop.ForAll( func() bool { params := GetEdwardsCurve() @@ -173,6 +173,74 @@ func TestReceiverIsOperand(t *testing.T) { }, )) + // extended + properties.Property("Equal extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + params := GetEdwardsCurve() + var p1, baseProj PointProj + p1.FromAffine(¶ms.Base) + baseProj.FromAffine(¶ms.Base) + + return p1.Equal(&p1) && p1.Equal(&baseProj) + }, + )) + + properties.Property("Add extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2, p3 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + p3.FromAffine(¶ms.Base) + + res := true + + p3.Add(&p1, &p2) + p1.Add(&p1, &p2) + res = res && p3.Equal(&p1) + + p1.FromAffine(¶ms.Base) + p2.Add(&p1, &p2) + res = res && p2.Equal(&p3) + + return res + }, + )) + + properties.Property("Double extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + + p2.Double(&p1) + p1.Double(&p1) + + return p2.Equal(&p1) + }, + )) + + properties.Property("Neg extended: having the receiver as operand should output the same result", prop.ForAll( + func() bool { + + params := GetEdwardsCurve() + + var p1, p2 PointProj + p1.FromAffine(¶ms.Base) + p2.FromAffine(¶ms.Base) + + p2.Neg(&p1) + p1.Neg(&p1) + + return p2.Equal(&p1) + }, + )) + properties.TestingRun(t, gopter.ConsoleReporter(false)) } @@ -184,15 +252,15 @@ func TestField(t *testing.T) { properties := gopter.NewProperties(parameters) genS := GenBigInt() - properties.Property("MulByA(x) should match Mul(x, curve.A)", prop.ForAll( + properties.Property("MulByA(x) should match Mul(x, curve.A)", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() - var z1, z2 fr.Element - z1.SetBigInt(&s) - z2.Mul(&z1, ¶ms.A) - mulByA(&z1) + var z1, z2 fr.Element + z1.SetBigInt(&s) + z2.Mul(&z1, ¶ms.A) + mulByA(&z1) return z1.Equal(&z2) }, @@ -202,7 +270,6 @@ func TestField(t *testing.T) { properties.TestingRun(t, gopter.ConsoleReporter(false)) } - func TestOps(t *testing.T) { parameters := gopter.DefaultTestParameters() @@ -227,7 +294,7 @@ func TestOps(t *testing.T) { var one fr.Element one.SetOne() - return p1.IsOnCurve() && p1.X.IsZero() && p1.Y.Equal(&one) + return p1.IsOnCurve() && p1.IsZero() }, genS1, )) @@ -291,7 +358,7 @@ func TestOps(t *testing.T) { genS1, )) - properties.Property("[5]P=[2][2]P+P", prop.ForAll( + properties.Property("(affine) [5]P=[2][2]P+P", prop.ForAll( func(s1 big.Int) bool { params := GetEdwardsCurve() @@ -308,7 +375,7 @@ func TestOps(t *testing.T) { genS1, )) - // proj + // projective properties.Property("(projective) P+(-P)=O", prop.ForAll( func(s1 big.Int) bool { @@ -321,7 +388,7 @@ func TestOps(t *testing.T) { p.Add(&p1, &p2) - return p.X.IsZero() && p.Y.Equal(&p.Z) + return p.IsZero() }, genS1, )) @@ -344,8 +411,119 @@ func TestOps(t *testing.T) { genS1, )) - // mixed - properties.Property("(mixed) P+(-P)=O", prop.ForAll( + properties.Property("(projective) [5]P=[2][2]P+P", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseProj, p1, p2 PointProj + baseProj.FromAffine(¶ms.Base) + p1.ScalarMul(&baseProj, &s1) + + five := big.NewInt(5) + p2.Double(&p1).Double(&p2).Add(&p2, &p1) + p1.ScalarMul(&p1, five) + + return p2.Equal(&p1) + }, + genS1, + )) + + // extended + properties.Property("(extended) P+(-P)=O", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2, p PointExtended + baseExtended.FromAffine(¶ms.Base) + p1.ScalarMul(&baseExtended, &s1) + p2.Neg(&p1) + + p.Add(&p1, &p2) + + return p.IsZero() + }, + genS1, + )) + + properties.Property("(extended) P+P=2*P", prop.ForAll( + + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2, p PointExtended + baseExtended.FromAffine(¶ms.Base) + p.ScalarMul(&baseExtended, &s) + + p1.Add(&p, &p) + p2.Double(&p) + + return p1.Equal(&p2) + }, + genS1, + )) + + properties.Property("(extended) [5]P=[2][2]P+P", prop.ForAll( + func(s1 big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, p1, p2 PointExtended + baseExtended.FromAffine(¶ms.Base) + p1.ScalarMul(&baseExtended, &s1) + + five := big.NewInt(5) + p2.Double(&p1).Double(&p2).Add(&p2, &p1) + p1.ScalarMul(&p1, five) + + return p2.Equal(&p1) + }, + genS1, + )) + + // mixed affine+extended + properties.Property("(mixed affine+extended) P+(-P)=O", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, pExtended, p PointExtended + var pAffine PointAffine + baseExtended.FromAffine(¶ms.Base) + pExtended.ScalarMul(&baseExtended, &s) + pAffine.ScalarMul(¶ms.Base, &s) + pAffine.Neg(&pAffine) + + p.MixedAdd(&pExtended, &pAffine) + + return p.IsZero() + }, + genS1, + )) + + properties.Property("(mixed affine+extended) P+P=2*P", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseExtended, pExtended, p, p2 PointExtended + var pAffine PointAffine + baseExtended.FromAffine(¶ms.Base) + pExtended.ScalarMul(&baseExtended, &s) + pAffine.ScalarMul(¶ms.Base, &s) + + p.MixedAdd(&pExtended, &pAffine) + p2.MixedDouble(&pExtended) + + return p.Equal(&p2) + }, + genS1, + )) + + // mixed affine+projective + properties.Property("(mixed affine+proj) P+(-P)=O", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() @@ -359,12 +537,12 @@ func TestOps(t *testing.T) { p.MixedAdd(&pProj, &pAffine) - return p.X.IsZero() && p.Y.Equal(&p.Z) + return p.IsZero() }, genS1, )) - properties.Property("(mixed) P+P=2*P", prop.ForAll( + properties.Property("(mixed affine+proj) P+P=2*P", prop.ForAll( func(s big.Int) bool { params := GetEdwardsCurve() @@ -383,6 +561,27 @@ func TestOps(t *testing.T) { genS1, )) + properties.Property("scalar multiplication in Proj vs Ext should be consistant", prop.ForAll( + func(s big.Int) bool { + + params := GetEdwardsCurve() + + var baseProj PointProj + var baseExt PointExtended + var p1, p2 PointAffine + baseProj.FromAffine(¶ms.Base) + baseProj.ScalarMul(&baseProj, &s) + baseExt.FromAffine(¶ms.Base) + baseExt.ScalarMul(&baseExt, &s) + + p1.FromProj(&baseProj) + p2.FromExtended(&baseExt) + + return p1.Equal(&p2) + }, + genS1, + )) + properties.TestingRun(t, gopter.ConsoleReporter(false)) } @@ -420,7 +619,25 @@ func GenBigInt() gopter.Gen { // ------------------------------------------------------------ // benches -func BenchmarkScalarMul(b *testing.B) { +func BenchmarkScalarMulExtended(b *testing.B) { + params := GetEdwardsCurve() + var a PointExtended + var s big.Int + a.FromAffine(¶ms.Base) + s.SetString("52435875175126190479447705081859658376581184513", 10) + s.Add(&s, ¶ms.Order) + + var doubleAndAdd PointExtended + + b.Run("double and add", func(b *testing.B) { + b.ResetTimer() + for j := 0; j < b.N; j++ { + doubleAndAdd.ScalarMul(&a, &s) + } + }) +} + +func BenchmarkScalarMulProjective(b *testing.B) { params := GetEdwardsCurve() var a PointProj var s big.Int