From 8b887c69a0d566ff808c75d476e12c4df5692289 Mon Sep 17 00:00:00 2001 From: Daniel Bourdrez <3641580+bytemare@users.noreply.github.com> Date: Mon, 17 Apr 2023 18:44:26 +0200 Subject: [PATCH] Post code showcasing in/complete formula differences (#38) * secp256k1: use complete formula exclusively Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com> --- README.md | 2 +- internal/secp256k1/element.go | 213 ++++++++-------------------------- internal/secp256k1/scalar.go | 8 +- tests/element_test.go | 18 +-- 4 files changed, 69 insertions(+), 172 deletions(-) diff --git a/README.md b/README.md index 866cbc4..bdd9f94 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ The following table indexes supported groups with hash-to-curve capability and l | 4 | P-384 | filippo.io/nistec | | 5 | P-521 | filippo.io/nistec | | 6 | Edwards25519 | filippo.io/edwards25519 | -| 7 | Secp256k1 | not yet supported | +| 7 | Secp256k1 | github.com/bytemare/crypto | | 8 | Double-Odd | not yet supported | ## Prime-order group interface diff --git a/internal/secp256k1/element.go b/internal/secp256k1/element.go index 4284dd0..290ae95 100644 --- a/internal/secp256k1/element.go +++ b/internal/secp256k1/element.go @@ -14,15 +14,6 @@ import ( "github.com/bytemare/crypto/internal" ) -type mode uint8 - -const ( - incomplete mode = 1 - complete mode = 2 - - formulaType = incomplete -) - // Element implements the Element interface for the Secp256k1 group element. type Element struct { x, y, z big.Int @@ -43,7 +34,7 @@ func newElementWithAffine(x, y *big.Int) *Element { e.x.Set(x) e.y.Set(y) - e.z.Set(fp.One()) + e.z.Set(scOne) return e } @@ -72,28 +63,23 @@ func assertElement(element internal.Element) *Element { return ec } -func (e *Element) affine() (x, y *big.Int) { +// affine returns the affine (x,y) coordinates from the inner standard projective representation. +func (e *Element) affine() (*big.Int, *big.Int) { if e.z.Sign() == 0 { return fp.Zero(), fp.Zero() } - if fp.AreEqual(&e.z, fp.One()) { + if fp.AreEqual(&e.z, scOne) { return &e.x, &e.y } - var zInv, zInvSq big.Int + var zInv, x, y big.Int fp.Inv(&zInv, &e.z) - fp.Square(&zInvSq, &zInv) + fp.Mul(&x, &e.x, &zInv) + fp.Mul(&y, &e.y, &zInv) - x = new(big.Int) - fp.Mul(x, &e.x, &zInvSq) - fp.Mul(&zInvSq, &zInvSq, &zInv) - - y = new(big.Int) - fp.Mul(y, &e.y, &zInvSq) - - return x, y + return &x, &y } // Base sets the element to the group's base point a.k.a. canonical generator. @@ -130,61 +116,13 @@ func (e *Element) addAffine(element *Element) *Element { e.x.Set(&x) e.y.Set(&y) - e.z.Set(fp.One()) - - return e -} - -// From http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl. -func (e *Element) addJacobianIncomplete(element *Element) *Element { - var u1, u2, h, i, j, s1, s2, r, v, z1z1, z2z2, x3, y3, z3 big.Int - - fp.Square(&z1z1, &e.z) // Z1Z1 = Z12 - fp.Square(&z2z2, &element.z) // Z2Z2 = Z22 - - fp.Mul(&u1, &e.x, &z2z2) // U1 = X1*Z2Z2 - fp.Mul(&u2, &element.x, &z1z1) // U2 = X2*Z1Z1 - fp.Sub(&h, &u2, &u1) // H = U2-U1 - fp.Lsh(&i, &h, 1) // I = (2*H)2 - fp.Square(&i, &i) // - fp.Mul(&j, &h, &i) // J = H*I - - fp.Mul(&s1, &e.y, &element.z) - fp.Mul(&s1, &s1, &z2z2) // S1 = Y1*Z2*Z2Z2 - fp.Mul(&s2, &element.y, &e.z) - fp.Mul(&s2, &s2, &z1z1) // S2 = Y2*Z1*Z1Z1 - fp.Sub(&r, &s2, &s1) // r = 2*(S2-S1) - fp.Lsh(&r, &r, 1) - fp.Mul(&v, &u1, &i) // V = U1*I - - x3.Set(&r) - fp.Square(&x3, &x3) - fp.Sub(&x3, &x3, &j) - fp.Sub(&x3, &x3, &v) - fp.Sub(&x3, &x3, &v) // X3 = r2-J-2*V - - y3.Set(&r) - fp.Sub(&v, &v, &x3) - fp.Mul(&y3, &y3, &v) - fp.Mul(&s1, &s1, &j) // S1 = Y1*Z2*Z2Z2 - fp.Lsh(&s1, &s1, 1) - fp.Sub(&y3, &y3, &s1) // Y3 = r*(V-X3)-2*S1*J - - fp.Add(&z3, &e.z, &element.z) - fp.Square(&z3, &z3) - fp.Sub(&z3, &z3, &z1z1) - fp.Sub(&z3, &z3, &z2z2) - fp.Mul(&z3, &z3, &h) // Z3 = ((Z1+Z2)2-Z1Z1-Z2Z2)*H - - e.x.Set(&x3) - e.y.Set(&y3) - e.z.Set(&z3) + e.z.Set(scOne) return e } // https://eprint.iacr.org/2015/1060.pdf -func (e *Element) addJacobianComplete(element *Element) *Element { +func (e *Element) addProjectiveComplete(element *Element) *Element { var t0, t1, t2, t3, t4, x3, y3, z3 big.Int fp.Mul(&t0, &e.x, &element.x) // t0 := X1 * X2 @@ -231,35 +169,26 @@ func (e *Element) addJacobianComplete(element *Element) *Element { fp.Mul(&z3, &z3, &t4) // Z3 := Z3 * t4 fp.Add(&z3, &z3, &t0) // Z3 := Z3 + t0 - e.x.Set(&x3) - e.y.Set(&y3) - e.z.Set(&z3) + switch { + case element.IsIdentity(): + e.x.Set(&e.x) + e.y.Set(&e.y) + e.z.Set(&e.z) + case e.IsIdentity(): + e.x.Set(&element.x) + e.y.Set(&element.y) + e.z.Set(&element.z) + default: + e.x.Set(&x3) + e.y.Set(&y3) + e.z.Set(&z3) + } return e } func (e *Element) add(element *Element) *Element { - if element.IsIdentity() { - return e - } - - if e.IsIdentity() { - e.set(element) - return e - } - - if e.isEqual(element) == 1 { - return e.double() - } - - switch formulaType { - case incomplete: - return e.addJacobianIncomplete(element) - case complete: - return e.addJacobianComplete(element) - } - - panic("invalid formula type") + return e.addProjectiveComplete(element) } // Add sets the receiver to the sum of the input and the receiver, and returns the receiver. @@ -268,44 +197,8 @@ func (e *Element) Add(element internal.Element) internal.Element { return e.add(q) } -// Double sets the receiver to its double, and returns it. -// From http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l. -func (e *Element) doubleJacobianIncomplete() *Element { - var a, _b, c, d, e2, f, x3, y3, z3 big.Int - - fp.Square(&a, &e.x) - fp.Square(&_b, &e.y) - fp.Square(&c, &_b) - - fp.Add(&d, &e.x, &_b) - fp.Square(&d, &d) - fp.Sub(&d, &d, &a) - fp.Sub(&d, &d, &c) - fp.Lsh(&d, &d, 1) - - fp.Mul(&e2, &a, big.NewInt(3)) - fp.Square(&f, &e2) - - fp.Lsh(&x3, &d, 1) - fp.Sub(&x3, &f, &x3) - - fp.Sub(&y3, &d, &x3) - fp.Mul(&y3, &e2, &y3) - fp.Lsh(&c, &c, 3) - fp.Sub(&y3, &y3, &c) - - fp.Mul(&z3, &e.y, &e.z) - fp.Lsh(&z3, &z3, 1) - - e.x.Set(&x3) - e.y.Set(&y3) - e.z.Set(&z3) - - return e -} - // https://eprint.iacr.org/2015/1060.pdf -func (e *Element) doubleJacobianComplete() *Element { +func (e *Element) doubleProjectiveComplete() *Element { var t0, t1, t2, x3, y3, z3 big.Int fp.Square(&t0, &e.y) // t0 := Y ^2 @@ -339,21 +232,9 @@ func (e *Element) doubleJacobianComplete() *Element { return e } -func (e *Element) double() *Element { - switch formulaType { - case incomplete: - return e.doubleJacobianIncomplete() - case complete: - return e.doubleJacobianComplete() - } - - panic("invalid formula type") -} - // Double sets the receiver to its double, and returns it. -// From https://www.microsoft.com/en-us/research/wp-content/uploads/2016/06/complete-2.pdf. func (e *Element) Double() internal.Element { - return e.double() + return e.doubleProjectiveComplete() } func (e *Element) negate() *Element { @@ -372,34 +253,37 @@ func (e *Element) Negate() internal.Element { // Subtract subtracts the input from the receiver, and returns the receiver. func (e *Element) Subtract(element internal.Element) internal.Element { - q := assertElement(element).negate() - return e.addJacobianIncomplete(q) + q := assertElement(element).copy().negate() + return e.add(q) } -// Multiply sets the receiver to the scalar multiplication of the receiver with the given Scalar, and returns it. -func (e *Element) Multiply(scalar internal.Scalar) internal.Element { - s := assert(scalar) - - if fp.AreEqual(&s.scalar, scOne) { +func (e *Element) multiply(scalar *Scalar) *Element { + if fp.AreEqual(&scalar.scalar, scOne) { return e } r0 := newElement() r1 := e.copy() - for i := s.scalar.BitLen() - 1; i >= 0; i-- { - if s.scalar.Bit(i) == 0 { - r1.add(r0) - r0.double() + for i := scalar.scalar.BitLen() - 1; i >= 0; i-- { + if scalar.scalar.Bit(i) == 0 { + r1.Add(r0) + r0.Double() } else { - r0.add(r1) - r1.double() + r0.Add(r1) + r1.Double() } } return e.set(r0) } +// Multiply sets the receiver to the scalar multiplication of the receiver with the given Scalar, and returns it. +func (e *Element) Multiply(scalar internal.Scalar) internal.Element { + s := assert(scalar) + return e.multiply(s) +} + // Equal returns 1 if the elements are equivalent, and 0 otherwise. func (e *Element) isEqual(element *Element) int { x1, y1 := e.affine() @@ -479,8 +363,7 @@ func secp256Polynomial(y, x *big.Int) { fp.Add(y, y, b) } -// Decode sets the receiver to a decoding of the input data, and returns an error on failure. -func (e *Element) Decode(data []byte) error { +func (e *Element) decode(data []byte) error { /* - check coordinates are in the correct range - check point is on the curve @@ -509,7 +392,7 @@ func (e *Element) Decode(data []byte) error { fp.SquareRoot(&y, &y) - cond := int(y.Bytes()[0]&1) ^ int(data[0]&1) + cond := int(y.Bit(0)&1) ^ int(data[0]&1) fp.CondNeg(&y, &y, cond) // Identity Check @@ -519,10 +402,16 @@ func (e *Element) Decode(data []byte) error { e.x.Set(x) e.y.Set(&y) + e.z.Set(scOne) return nil } +// Decode sets the receiver to a decoding of the input data, and returns an error on failure. +func (e *Element) Decode(data []byte) error { + return e.decode(data) +} + // MarshalBinary returns the compressed byte encoding of the element. func (e *Element) MarshalBinary() (data []byte, err error) { return e.Encode(), nil diff --git a/internal/secp256k1/scalar.go b/internal/secp256k1/scalar.go index 8fa3a31..c3a9951 100644 --- a/internal/secp256k1/scalar.go +++ b/internal/secp256k1/scalar.go @@ -202,8 +202,7 @@ func (s *Scalar) Encode() []byte { return s.scalar.FillBytes(scalar) } -// Decode sets the receiver to a decoding of the input data, and returns an error on failure. -func (s *Scalar) Decode(in []byte) error { +func (s *Scalar) decode(in []byte) error { if len(in) == 0 { return internal.ErrParamNilScalar } @@ -223,6 +222,11 @@ func (s *Scalar) Decode(in []byte) error { return nil } +// Decode sets the receiver to a decoding of the input data, and returns an error on failure. +func (s *Scalar) Decode(in []byte) error { + return s.decode(in) +} + // MarshalBinary returns the compressed byte encoding of the scalar. func (s *Scalar) MarshalBinary() (data []byte, err error) { return s.Encode(), nil diff --git a/tests/element_test.go b/tests/element_test.go index 765abaa..0628e99 100644 --- a/tests/element_test.go +++ b/tests/element_test.go @@ -10,6 +10,7 @@ package group_test import ( "encoding/hex" + "log" "testing" "github.com/bytemare/crypto" @@ -175,22 +176,23 @@ func elementTestAdd(t *testing.T, g crypto.Group) { // Verify whether add yields the same element when given identity base = g.Base() cpy = base.Copy() - if cpy.Add(g.NewElement()).Equal(base) != 1 { + cpy.Add(g.NewElement()) + if cpy.Equal(base) != 1 { t.Fatal(errExpectedEquality) } - // Verify whether add yields the identity given the negative + // Verify whether add yields the same when adding to identity base = g.Base() - negative := g.Base().Negate() identity := g.NewElement() - if base.Add(negative).Equal(identity) != 1 { + if identity.Add(base).Equal(base) != 1 { t.Fatal(errExpectedEquality) } - // Verify whether add yields the same when adding to identity + // Verify whether add yields the identity given the negative base = g.Base() + negative := g.Base().Negate() identity = g.NewElement() - if identity.Add(base).Equal(base) != 1 { + if base.Add(negative).Equal(identity) != 1 { t.Fatal(errExpectedEquality) } @@ -259,7 +261,7 @@ func elementTestDouble(t *testing.T, g crypto.Group) { func elementTestSubstract(t *testing.T, g crypto.Group) { base := g.Base() - // Verify whether subtrating yields the same element when given nil. + // Verify whether subtracting yields the same element when given nil. if base.Subtract(nil).Equal(base) != 1 { t.Fatal(errExpectedEquality) } @@ -316,6 +318,8 @@ func elementTestIdentity(t *testing.T, g crypto.Group) { base := g.Base() if id.Equal(base.Subtract(base)) != 1 { + log.Printf("id : %v", id.Encode()) + log.Printf("ba : %v", base.Encode()) t.Fatal(errExpectedIdentity) }