Skip to content
This repository has been archived by the owner on Oct 3, 2024. It is now read-only.

Commit

Permalink
Post code showcasing in/complete formula differences (#38)
Browse files Browse the repository at this point in the history
* secp256k1: use complete formula exclusively

Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com>
  • Loading branch information
bytemare authored Apr 17, 2023
1 parent 7fe5a68 commit 8b887c6
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 172 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
213 changes: 51 additions & 162 deletions internal/secp256k1/element.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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()
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
8 changes: 6 additions & 2 deletions internal/secp256k1/scalar.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
Expand Down
Loading

0 comments on commit 8b887c6

Please sign in to comment.