mirror of
https://github.com/golang/go
synced 2024-10-02 22:25:08 +00:00
crypto/elliptic: use complete addition formulas for P-521
Complete formulas don't have exceptions for P = Q or P = 0, which makes them significantly simpler and safer to implement. Notice how the constant time IsZero checks are gone. It's not free, but still well within the performance gains of CL 315271. name old time/op new time/op delta pkg:crypto/elliptic goos:darwin goarch:amd64 ScalarBaseMult/P521-16 1.34ms ± 3% 1.63ms ± 4% +21.78% (p=0.000 n=10+10) ScalarMult/P521-16 1.35ms ± 3% 1.65ms ± 4% +22.58% (p=0.000 n=10+10) pkg:crypto/ecdsa goos:darwin goarch:amd64 Sign/P521-16 1.45ms ± 2% 1.67ms ± 1% +15.00% (p=0.000 n=10+8) Verify/P521-16 2.68ms ± 1% 3.10ms ± 2% +16.02% (p=0.000 n=10+9) GenerateKey/P521-16 1.31ms ± 4% 1.53ms ± 1% +16.89% (p=0.000 n=10+9) Change-Id: Ibd9a961e9865df68a1250aba739c190caf9a54de Reviewed-on: https://go-review.googlesource.com/c/go/+/320071 Trust: Filippo Valsorda <filippo@golang.org> Run-TryBot: Filippo Valsorda <filippo@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Julie Qiu <julie@golang.org>
This commit is contained in:
parent
5d6d9f5610
commit
e39b854a67
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
type p521Curve struct {
|
||||
*CurveParams
|
||||
b *fiat.P521Element
|
||||
}
|
||||
|
||||
var p521 p521Curve
|
||||
|
@ -25,6 +26,7 @@ func initP521() {
|
|||
p521.Gx, _ = new(big.Int).SetString("c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", 16)
|
||||
p521.Gy, _ = new(big.Int).SetString("11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650", 16)
|
||||
p521.BitSize = 521
|
||||
p521.b = bigIntToFiatP521(p521.B)
|
||||
}
|
||||
|
||||
func (curve p521Curve) Params() *CurveParams {
|
||||
|
@ -34,7 +36,6 @@ func (curve p521Curve) Params() *CurveParams {
|
|||
func (curve p521Curve) IsOnCurve(x, y *big.Int) bool {
|
||||
x1 := bigIntToFiatP521(x)
|
||||
y1 := bigIntToFiatP521(y)
|
||||
b := bigIntToFiatP521(curve.B) // TODO: precompute this value.
|
||||
|
||||
// x³ - 3x + b.
|
||||
x3 := new(fiat.P521Element).Square(x1)
|
||||
|
@ -44,7 +45,7 @@ func (curve p521Curve) IsOnCurve(x, y *big.Int) bool {
|
|||
threeX.Add(threeX, x1)
|
||||
|
||||
x3.Sub(x3, threeX)
|
||||
x3.Add(x3, b)
|
||||
x3.Add(x3, curve.b)
|
||||
|
||||
// y² = x³ - 3x + b
|
||||
y2 := new(fiat.P521Element).Square(y1)
|
||||
|
@ -52,10 +53,20 @@ func (curve p521Curve) IsOnCurve(x, y *big.Int) bool {
|
|||
return x3.Equal(y2) == 1
|
||||
}
|
||||
|
||||
// p521Point is a P-521 point in projective coordinates, where x = X/Z, y = Y/Z.
|
||||
type p521Point struct {
|
||||
x, y, z *fiat.P521Element
|
||||
}
|
||||
|
||||
// newP521Point returns a new p521Point representing the identity point.
|
||||
func newP521Point() *p521Point {
|
||||
return &p521Point{
|
||||
x: new(fiat.P521Element),
|
||||
y: new(fiat.P521Element).One(),
|
||||
z: new(fiat.P521Element),
|
||||
}
|
||||
}
|
||||
|
||||
func fiatP521ToBigInt(x *fiat.P521Element) *big.Int {
|
||||
xBytes := x.Bytes()
|
||||
for i := range xBytes[:len(xBytes)/2] {
|
||||
|
@ -64,20 +75,16 @@ func fiatP521ToBigInt(x *fiat.P521Element) *big.Int {
|
|||
return new(big.Int).SetBytes(xBytes)
|
||||
}
|
||||
|
||||
// affineFromJacobian brings a point in Jacobian coordinates back to affine
|
||||
// coordinates, with (0, 0) representing infinity by convention. It also goes
|
||||
// back to big.Int values to match the exposed API.
|
||||
func (curve p521Curve) affineFromJacobian(p *p521Point) (x, y *big.Int) {
|
||||
// Affine returns p in affine coordinates, with (0, 0) representing infinity by
|
||||
// convention. It also goes back to big.Int values to match the exposed API.
|
||||
func (p *p521Point) Affine() (x, y *big.Int) {
|
||||
if p.z.IsZero() == 1 {
|
||||
return new(big.Int), new(big.Int)
|
||||
}
|
||||
|
||||
zinv := new(fiat.P521Element).Invert(p.z)
|
||||
zinvsq := new(fiat.P521Element).Mul(zinv, zinv)
|
||||
|
||||
xx := new(fiat.P521Element).Mul(p.x, zinvsq)
|
||||
zinvsq.Mul(zinvsq, zinv)
|
||||
yy := new(fiat.P521Element).Mul(p.y, zinvsq)
|
||||
xx := new(fiat.P521Element).Mul(p.x, zinv)
|
||||
yy := new(fiat.P521Element).Mul(p.y, zinv)
|
||||
|
||||
return fiatP521ToBigInt(xx), fiatP521ToBigInt(yy)
|
||||
}
|
||||
|
@ -96,18 +103,14 @@ func bigIntToFiatP521(x *big.Int) *fiat.P521Element {
|
|||
return x1
|
||||
}
|
||||
|
||||
// jacobianFromAffine converts (x, y) affine coordinates into (x, y, z) Jacobian
|
||||
// newP521PointFromAffine converts (x, y) affine coordinates into (X, Y, Z) projective
|
||||
// coordinates. It also converts from big.Int to fiat, which is necessarily a
|
||||
// messy and variable-time operation, which we can't avoid due to the exposed API.
|
||||
func (curve p521Curve) jacobianFromAffine(x, y *big.Int) *p521Point {
|
||||
func newP521PointFromAffine(x, y *big.Int) *p521Point {
|
||||
// (0, 0) is by convention the point at infinity, which can't be represented
|
||||
// in affine coordinates, but is (0, 0, 0) in Jacobian.
|
||||
// in affine coordinates, but is (0, 0, 0) in projective coordinates.
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return &p521Point{
|
||||
x: new(fiat.P521Element),
|
||||
y: new(fiat.P521Element),
|
||||
z: new(fiat.P521Element),
|
||||
}
|
||||
return newP521Point()
|
||||
}
|
||||
return &p521Point{
|
||||
x: bigIntToFiatP521(x),
|
||||
|
@ -117,65 +120,59 @@ func (curve p521Curve) jacobianFromAffine(x, y *big.Int) *p521Point {
|
|||
}
|
||||
|
||||
func (curve p521Curve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
|
||||
p1 := curve.jacobianFromAffine(x1, y1)
|
||||
p2 := curve.jacobianFromAffine(x2, y2)
|
||||
return curve.affineFromJacobian(p1.addJacobian(p1, p2))
|
||||
p1 := newP521PointFromAffine(x1, y1)
|
||||
p2 := newP521PointFromAffine(x2, y2)
|
||||
return p1.Add(p1, p2).Affine()
|
||||
}
|
||||
|
||||
// addJacobian sets q = p1 + p2, and returns q. The points may overlap.
|
||||
func (q *p521Point) addJacobian(p1, p2 *p521Point) *p521Point {
|
||||
// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl
|
||||
z1IsZero := p1.z.IsZero()
|
||||
z2IsZero := p2.z.IsZero()
|
||||
// Add sets q = p1 + p2, and returns q. The points may overlap.
|
||||
func (q *p521Point) Add(p1, p2 *p521Point) *p521Point {
|
||||
// Complete addition formula for a = -3 from "Complete addition formulas for
|
||||
// prime order elliptic curves" (https://eprint.iacr.org/2015/1060), §A.2.
|
||||
|
||||
z1z1 := new(fiat.P521Element).Square(p1.z)
|
||||
z2z2 := new(fiat.P521Element).Square(p2.z)
|
||||
|
||||
u1 := new(fiat.P521Element).Mul(p1.x, z2z2)
|
||||
u2 := new(fiat.P521Element).Mul(p2.x, z1z1)
|
||||
h := new(fiat.P521Element).Sub(u2, u1)
|
||||
xEqual := h.IsZero() == 1
|
||||
i := new(fiat.P521Element).Add(h, h)
|
||||
i.Square(i)
|
||||
j := new(fiat.P521Element).Mul(h, i)
|
||||
|
||||
s1 := new(fiat.P521Element).Mul(p1.y, p2.z)
|
||||
s1.Mul(s1, z2z2)
|
||||
s2 := new(fiat.P521Element).Mul(p2.y, p1.z)
|
||||
s2.Mul(s2, z1z1)
|
||||
r := new(fiat.P521Element).Sub(s2, s1)
|
||||
yEqual := r.IsZero() == 1
|
||||
if xEqual && yEqual && z1IsZero == 0 && z2IsZero == 0 {
|
||||
return q.doubleJacobian(p1)
|
||||
}
|
||||
r.Add(r, r)
|
||||
v := new(fiat.P521Element).Mul(u1, i)
|
||||
|
||||
x := new(fiat.P521Element).Set(r)
|
||||
x.Square(x)
|
||||
x.Sub(x, j)
|
||||
x.Sub(x, v)
|
||||
x.Sub(x, v)
|
||||
|
||||
y := new(fiat.P521Element).Set(r)
|
||||
v.Sub(v, x)
|
||||
y.Mul(y, v)
|
||||
s1.Mul(s1, j)
|
||||
s1.Add(s1, s1)
|
||||
y.Sub(y, s1)
|
||||
|
||||
z := new(fiat.P521Element).Add(p1.z, p2.z)
|
||||
z.Square(z)
|
||||
z.Sub(z, z1z1)
|
||||
z.Sub(z, z2z2)
|
||||
z.Mul(z, h)
|
||||
|
||||
x.Select(p2.x, x, z1IsZero)
|
||||
x.Select(p1.x, x, z2IsZero)
|
||||
y.Select(p2.y, y, z1IsZero)
|
||||
y.Select(p1.y, y, z2IsZero)
|
||||
z.Select(p2.z, z, z1IsZero)
|
||||
z.Select(p1.z, z, z2IsZero)
|
||||
t0 := new(fiat.P521Element).Mul(p1.x, p2.x) // t0 := X1 * X2
|
||||
t1 := new(fiat.P521Element).Mul(p1.y, p2.y) // t1 := Y1 * Y2
|
||||
t2 := new(fiat.P521Element).Mul(p1.z, p2.z) // t2 := Z1 * Z2
|
||||
t3 := new(fiat.P521Element).Add(p1.x, p1.y) // t3 := X1 + Y1
|
||||
t4 := new(fiat.P521Element).Add(p2.x, p2.y) // t4 := X2 + Y2
|
||||
t3.Mul(t3, t4) // t3 := t3 * t4
|
||||
t4.Add(t0, t1) // t4 := t0 + t1
|
||||
t3.Sub(t3, t4) // t3 := t3 - t4
|
||||
t4.Add(p1.y, p1.z) // t4 := Y1 + Z1
|
||||
x := new(fiat.P521Element).Add(p2.y, p2.z) // X3 := Y2 + Z2
|
||||
t4.Mul(t4, x) // t4 := t4 * X3
|
||||
x.Add(t1, t2) // X3 := t1 + t2
|
||||
t4.Sub(t4, x) // t4 := t4 - X3
|
||||
x.Add(p1.x, p1.z) // X3 := X1 + Z1
|
||||
y := new(fiat.P521Element).Add(p2.x, p2.z) // Y3 := X2 + Z2
|
||||
x.Mul(x, y) // X3 := X3 * Y3
|
||||
y.Add(t0, t2) // Y3 := t0 + t2
|
||||
y.Sub(x, y) // Y3 := X3 - Y3
|
||||
z := new(fiat.P521Element).Mul(p521.b, t2) // Z3 := b * t2
|
||||
x.Sub(y, z) // X3 := Y3 - Z3
|
||||
z.Add(x, x) // Z3 := X3 + X3
|
||||
x.Add(x, z) // X3 := X3 + Z3
|
||||
z.Sub(t1, x) // Z3 := t1 - X3
|
||||
x.Add(t1, x) // X3 := t1 + X3
|
||||
y.Mul(p521.b, y) // Y3 := b * Y3
|
||||
t1.Add(t2, t2) // t1 := t2 + t2
|
||||
t2.Add(t1, t2) // t2 := t1 + t2
|
||||
y.Sub(y, t2) // Y3 := Y3 - t2
|
||||
y.Sub(y, t0) // Y3 := Y3 - t0
|
||||
t1.Add(y, y) // t1 := Y3 + Y3
|
||||
y.Add(t1, y) // Y3 := t1 + Y3
|
||||
t1.Add(t0, t0) // t1 := t0 + t0
|
||||
t0.Add(t1, t0) // t0 := t1 + t0
|
||||
t0.Sub(t0, t2) // t0 := t0 - t2
|
||||
t1.Mul(t4, y) // t1 := t4 * Y3
|
||||
t2.Mul(t0, y) // t2 := t0 * Y3
|
||||
y.Mul(x, z) // Y3 := X3 * Z3
|
||||
y.Add(y, t2) // Y3 := Y3 + t2
|
||||
x.Mul(t3, x) // X3 := t3 * X3
|
||||
x.Sub(x, t1) // X3 := X3 - t1
|
||||
z.Mul(t4, z) // Z3 := t4 * Z3
|
||||
t1.Mul(t3, t0) // t1 := t3 * t0
|
||||
z.Add(z, t1) // Z3 := Z3 + t1
|
||||
|
||||
q.x.Set(x)
|
||||
q.y.Set(y)
|
||||
|
@ -184,74 +181,78 @@ func (q *p521Point) addJacobian(p1, p2 *p521Point) *p521Point {
|
|||
}
|
||||
|
||||
func (curve p521Curve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
|
||||
p := curve.jacobianFromAffine(x1, y1)
|
||||
return curve.affineFromJacobian(p.doubleJacobian(p))
|
||||
p := newP521PointFromAffine(x1, y1)
|
||||
return p.Double(p).Affine()
|
||||
}
|
||||
|
||||
// doubleJacobian sets q = p + p, and returns q. The points may overlap.
|
||||
func (q *p521Point) doubleJacobian(p *p521Point) *p521Point {
|
||||
// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b
|
||||
delta := new(fiat.P521Element).Square(p.z)
|
||||
gamma := new(fiat.P521Element).Square(p.y)
|
||||
alpha := new(fiat.P521Element).Sub(p.x, delta)
|
||||
alpha2 := new(fiat.P521Element).Add(p.x, delta)
|
||||
alpha.Mul(alpha, alpha2)
|
||||
alpha2.Set(alpha)
|
||||
alpha.Add(alpha, alpha)
|
||||
alpha.Add(alpha, alpha2)
|
||||
// Double sets q = p + p, and returns q. The points may overlap.
|
||||
func (q *p521Point) Double(p *p521Point) *p521Point {
|
||||
// Complete addition formula for a = -3 from "Complete addition formulas for
|
||||
// prime order elliptic curves" (https://eprint.iacr.org/2015/1060), §A.2.
|
||||
|
||||
beta := alpha2.Mul(p.x, gamma)
|
||||
t0 := new(fiat.P521Element).Square(p.x) // t0 := X ^ 2
|
||||
t1 := new(fiat.P521Element).Square(p.y) // t1 := Y ^ 2
|
||||
t2 := new(fiat.P521Element).Square(p.z) // t2 := Z ^ 2
|
||||
t3 := new(fiat.P521Element).Mul(p.x, p.y) // t3 := X * Y
|
||||
t3.Add(t3, t3) // t3 := t3 + t3
|
||||
z := new(fiat.P521Element).Mul(p.x, p.z) // Z3 := X * Z
|
||||
z.Add(z, z) // Z3 := Z3 + Z3
|
||||
y := new(fiat.P521Element).Mul(p521.b, t2) // Y3 := b * t2
|
||||
y.Sub(y, z) // Y3 := Y3 - Z3
|
||||
x := new(fiat.P521Element).Add(y, y) // X3 := Y3 + Y3
|
||||
y.Add(x, y) // Y3 := X3 + Y3
|
||||
x.Sub(t1, y) // X3 := t1 - Y3
|
||||
y.Add(t1, y) // Y3 := t1 + Y3
|
||||
y.Mul(x, y) // Y3 := X3 * Y3
|
||||
x.Mul(x, t3) // X3 := X3 * t3
|
||||
t3.Add(t2, t2) // t3 := t2 + t2
|
||||
t2.Add(t2, t3) // t2 := t2 + t3
|
||||
z.Mul(p521.b, z) // Z3 := b * Z3
|
||||
z.Sub(z, t2) // Z3 := Z3 - t2
|
||||
z.Sub(z, t0) // Z3 := Z3 - t0
|
||||
t3.Add(z, z) // t3 := Z3 + Z3
|
||||
z.Add(z, t3) // Z3 := Z3 + t3
|
||||
t3.Add(t0, t0) // t3 := t0 + t0
|
||||
t0.Add(t3, t0) // t0 := t3 + t0
|
||||
t0.Sub(t0, t2) // t0 := t0 - t2
|
||||
t0.Mul(t0, z) // t0 := t0 * Z3
|
||||
y.Add(y, t0) // Y3 := Y3 + t0
|
||||
t0.Mul(p.y, p.z) // t0 := Y * Z
|
||||
t0.Add(t0, t0) // t0 := t0 + t0
|
||||
z.Mul(t0, z) // Z3 := t0 * Z3
|
||||
x.Sub(x, z) // X3 := X3 - Z3
|
||||
z.Mul(t0, t1) // Z3 := t0 * t1
|
||||
z.Add(z, z) // Z3 := Z3 + Z3
|
||||
z.Add(z, z) // Z3 := Z3 + Z3
|
||||
|
||||
q.x.Square(alpha)
|
||||
beta8 := new(fiat.P521Element).Add(beta, beta)
|
||||
beta8.Add(beta8, beta8)
|
||||
beta8.Add(beta8, beta8)
|
||||
q.x.Sub(q.x, beta8)
|
||||
|
||||
q.z.Add(p.y, p.z)
|
||||
q.z.Square(q.z)
|
||||
q.z.Sub(q.z, gamma)
|
||||
q.z.Sub(q.z, delta)
|
||||
|
||||
beta.Add(beta, beta)
|
||||
beta.Add(beta, beta)
|
||||
beta.Sub(beta, q.x)
|
||||
q.y.Mul(alpha, beta)
|
||||
|
||||
gamma.Square(gamma)
|
||||
gamma.Add(gamma, gamma)
|
||||
gamma.Add(gamma, gamma)
|
||||
gamma.Add(gamma, gamma)
|
||||
|
||||
q.y.Sub(q.y, gamma)
|
||||
q.x.Set(x)
|
||||
q.y.Set(y)
|
||||
q.z.Set(z)
|
||||
return q
|
||||
}
|
||||
|
||||
// Select sets q to p1 if cond == 1, and to p2 if cond == 0.
|
||||
func (q *p521Point) Select(p1, p2 *p521Point, cond int) *p521Point {
|
||||
q.x.Select(p1.x, p2.x, cond)
|
||||
q.y.Select(p1.y, p2.y, cond)
|
||||
q.z.Select(p1.z, p2.z, cond)
|
||||
return q
|
||||
}
|
||||
|
||||
func (curve p521Curve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
|
||||
B := curve.jacobianFromAffine(Bx, By)
|
||||
p, t := &p521Point{
|
||||
x: new(fiat.P521Element),
|
||||
y: new(fiat.P521Element),
|
||||
z: new(fiat.P521Element),
|
||||
}, &p521Point{
|
||||
x: new(fiat.P521Element),
|
||||
y: new(fiat.P521Element),
|
||||
z: new(fiat.P521Element),
|
||||
}
|
||||
B := newP521PointFromAffine(Bx, By)
|
||||
p, t := newP521Point(), newP521Point()
|
||||
|
||||
for _, byte := range scalar {
|
||||
for bitNum := 0; bitNum < 8; bitNum++ {
|
||||
p.doubleJacobian(p)
|
||||
p.Double(p)
|
||||
t.Add(p, B)
|
||||
bit := (byte >> (7 - bitNum)) & 1
|
||||
t.addJacobian(p, B)
|
||||
p.x.Select(t.x, p.x, int(bit))
|
||||
p.y.Select(t.y, p.y, int(bit))
|
||||
p.z.Select(t.z, p.z, int(bit))
|
||||
p.Select(t, p, int(bit))
|
||||
}
|
||||
}
|
||||
|
||||
return curve.affineFromJacobian(p)
|
||||
return p.Affine()
|
||||
}
|
||||
|
||||
func (curve p521Curve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
|
||||
|
|
Loading…
Reference in a new issue