Commit 7bacfc64 authored by Vlad Krasnov's avatar Vlad Krasnov Committed by Adam Langley

crypto/elliptic,crypto/ecdsa: P256 amd64 assembly

This is based on the implementation used in OpenSSL, from a
submission by Shay Gueron and myself. Besides using assembly,
this implementation employs several optimizations described in:

    S.Gueron and V.Krasnov, "Fast prime field elliptic-curve
                             cryptography with 256-bit primes"

In addition a new and improved modular inverse modulo N is
implemented here.

The performance measured on a Haswell based Macbook Pro shows 21X
speedup for the sign and 9X for the verify operations.
The operation BaseMult is 30X faster (and the Diffie-Hellman/ECDSA
key generation that use it are sped up as well).

The adaptation to Go with the help of Filippo Valsorda

Updated the submission for faster verify/ecdh, fixed some asm syntax
and API problems and added benchmarks.

Change-Id: I86a33636747d5c92f15e0c8344caa2e7e07e0028
Reviewed-on: https://go-review.googlesource.com/8968
Run-TryBot: Adam Langley <agl@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarAdam Langley <agl@golang.org>
parent 50fa6467
......@@ -27,6 +27,17 @@ import (
"math/big"
)
// A invertible implements fast inverse mod Curve.Params().N
type invertible interface {
// Inverse returns the inverse of k in GF(P)
Inverse(k *big.Int) *big.Int
}
// combinedMult implements fast multiplication S1*g + S2*p (g - generator, p - arbitrary point)
type combinedMult interface {
CombinedMult(bigX, bigY *big.Int, baseScalar, scalar []byte) (x, y *big.Int)
}
const (
aesIV = "IV for ECDSA CTR"
)
......@@ -179,7 +190,12 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
return
}
kInv = fermatInverse(k, N)
if in, ok := priv.Curve.(invertible); ok {
kInv = in.Inverse(k)
} else {
kInv = fermatInverse(k, N)
}
r, _ = priv.Curve.ScalarBaseMult(k.Bytes())
r.Mod(r, N)
if r.Sign() != 0 {
......@@ -214,16 +230,29 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
return false
}
e := hashToInt(hash, c)
w := new(big.Int).ModInverse(s, N)
var w *big.Int
if in, ok := c.(invertible); ok {
w = in.Inverse(s)
} else {
w = new(big.Int).ModInverse(s, N)
}
u1 := e.Mul(e, w)
u1.Mod(u1, N)
u2 := w.Mul(r, w)
u2.Mod(u2, N)
x1, y1 := c.ScalarBaseMult(u1.Bytes())
x2, y2 := c.ScalarMult(pub.X, pub.Y, u2.Bytes())
x, y := c.Add(x1, y1, x2, y2)
// Check if implements S1*g + S2*p
var x, y *big.Int
if opt, ok := c.(combinedMult); ok {
x, y = opt.CombinedMult(pub.X, pub.Y, u1.Bytes(), u2.Bytes())
} else {
x1, y1 := c.ScalarBaseMult(u1.Bytes())
x2, y2 := c.ScalarMult(pub.X, pub.Y, u2.Bytes())
x, y = c.Add(x1, y1, x2, y2)
}
if x.Sign() == 0 && y.Sign() == 0 {
return false
}
......
......@@ -42,6 +42,41 @@ func TestKeyGeneration(t *testing.T) {
testKeyGeneration(t, elliptic.P521(), "p521")
}
func BenchmarkSignP256(b *testing.B) {
b.ResetTimer()
p256 := elliptic.P256()
hashed := []byte("testing")
priv, _ := GenerateKey(p256, rand.Reader)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, _ = Sign(rand.Reader, priv, hashed)
}
}
func BenchmarkVerifyP256(b *testing.B) {
b.ResetTimer()
p256 := elliptic.P256()
hashed := []byte("testing")
priv, _ := GenerateKey(p256, rand.Reader)
r, s, _ := Sign(rand.Reader, priv, hashed)
b.ResetTimer()
for i := 0; i < b.N; i++ {
Verify(&priv.PublicKey, hashed, r, s)
}
}
func BenchmarkKeyGeneration(b *testing.B) {
b.ResetTimer()
p256 := elliptic.P256()
b.ResetTimer()
for i := 0; i < b.N; i++ {
GenerateKey(p256, rand.Reader)
}
}
func testSignAndVerify(t *testing.T, c elliptic.Curve, tag string) {
priv, _ := GenerateKey(c, rand.Reader)
......
......@@ -441,6 +441,18 @@ func BenchmarkBaseMultP256(b *testing.B) {
}
}
func BenchmarkScalarMultP256(b *testing.B) {
b.ResetTimer()
p256 := P256()
_, x, y, _ := GenerateKey(p256, rand.Reader)
priv, _, _, _ := GenerateKey(p256, rand.Reader)
b.StartTimer()
for i := 0; i < b.N; i++ {
p256.ScalarMult(x, y, priv)
}
}
func TestMarshal(t *testing.T) {
p224 := P224()
_, x, y, err := GenerateKey(p224, rand.Reader)
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !amd64
package elliptic
// This file contains a constant-time, 32-bit implementation of P256.
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment