Commit 247799ce authored by Adam Langley's avatar Adam Langley

crypto/elliptic: add constant-time P224.

(Sending to r because of the API change.)

This change alters the API for crypto/elliptic to permit different
implementations in the future. This will allow us to add faster,
constant-time implementations of the standard curves without any more
API changes.

As a demonstration, it also adds a constant-time implementation of
P224. Since it's only 32-bit, it's actually only about 40% the speed
of the generic code on a 64-bit system.

R=r, rsc
CC=golang-dev
https://golang.org/cl/5528088
parent f2f00593
...@@ -592,6 +592,25 @@ the correct function or method for the old functionality, but ...@@ -592,6 +592,25 @@ the correct function or method for the old functionality, but
may have the wrong type or require further analysis. may have the wrong type or require further analysis.
</p> </p>
<h3 id="hash">The crypto/elliptic package</h3>
<p>
In Go 1, <a href="/pkg/crypto/elliptic/#Curve"><code>elliptic.Curve</code></a>
has been made an interface to permit alternative implementations. The curve
parameters have been moved to the
<a href="/pkg/crypto/elliptic/#CurveParams"><code>elliptic.CurveParams</code></a>
structure.
</p>
<p>
<em>Updating</em>:
Existing users of <code>*elliptic.Curve</code> will need to change to
simply <code>elliptic.Curve</code>. Calls to <code>Marshal</code>,
<code>Unmarshal</code> and <code>GenerateKey</code> are now functions
in <code>crypto.elliptic</code> that take an <code>elliptic.Curve</code>
as their first argument.
</p>
<h3 id="hash">The hash package</h3> <h3 id="hash">The hash package</h3>
<p> <p>
......
...@@ -20,7 +20,7 @@ import ( ...@@ -20,7 +20,7 @@ import (
// PublicKey represents an ECDSA public key. // PublicKey represents an ECDSA public key.
type PublicKey struct { type PublicKey struct {
*elliptic.Curve elliptic.Curve
X, Y *big.Int X, Y *big.Int
} }
...@@ -34,22 +34,23 @@ var one = new(big.Int).SetInt64(1) ...@@ -34,22 +34,23 @@ var one = new(big.Int).SetInt64(1)
// randFieldElement returns a random element of the field underlying the given // randFieldElement returns a random element of the field underlying the given
// curve using the procedure given in [NSA] A.2.1. // curve using the procedure given in [NSA] A.2.1.
func randFieldElement(c *elliptic.Curve, rand io.Reader) (k *big.Int, err error) { func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) {
b := make([]byte, c.BitSize/8+8) params := c.Params()
b := make([]byte, params.BitSize/8+8)
_, err = io.ReadFull(rand, b) _, err = io.ReadFull(rand, b)
if err != nil { if err != nil {
return return
} }
k = new(big.Int).SetBytes(b) k = new(big.Int).SetBytes(b)
n := new(big.Int).Sub(c.N, one) n := new(big.Int).Sub(params.N, one)
k.Mod(k, n) k.Mod(k, n)
k.Add(k, one) k.Add(k, one)
return return
} }
// GenerateKey generates a public&private key pair. // GenerateKey generates a public&private key pair.
func GenerateKey(c *elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error) { func GenerateKey(c elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error) {
k, err := randFieldElement(c, rand) k, err := randFieldElement(c, rand)
if err != nil { if err != nil {
return return
...@@ -66,8 +67,8 @@ func GenerateKey(c *elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error ...@@ -66,8 +67,8 @@ func GenerateKey(c *elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error
// about how this is done. [NSA] suggests that this is done in the obvious // about how this is done. [NSA] suggests that this is done in the obvious
// manner, but [SECG] truncates the hash to the bit-length of the curve order // manner, but [SECG] truncates the hash to the bit-length of the curve order
// first. We follow [SECG] because that's what OpenSSL does. // first. We follow [SECG] because that's what OpenSSL does.
func hashToInt(hash []byte, c *elliptic.Curve) *big.Int { func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
orderBits := c.N.BitLen() orderBits := c.Params().N.BitLen()
orderBytes := (orderBits + 7) / 8 orderBytes := (orderBits + 7) / 8
if len(hash) > orderBytes { if len(hash) > orderBytes {
hash = hash[:orderBytes] hash = hash[:orderBytes]
...@@ -88,6 +89,7 @@ func hashToInt(hash []byte, c *elliptic.Curve) *big.Int { ...@@ -88,6 +89,7 @@ func hashToInt(hash []byte, c *elliptic.Curve) *big.Int {
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) { func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
// See [NSA] 3.4.1 // See [NSA] 3.4.1
c := priv.PublicKey.Curve c := priv.PublicKey.Curve
N := c.Params().N
var k, kInv *big.Int var k, kInv *big.Int
for { for {
...@@ -98,9 +100,9 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err ...@@ -98,9 +100,9 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
return return
} }
kInv = new(big.Int).ModInverse(k, c.N) kInv = new(big.Int).ModInverse(k, N)
r, _ = priv.Curve.ScalarBaseMult(k.Bytes()) r, _ = priv.Curve.ScalarBaseMult(k.Bytes())
r.Mod(r, priv.Curve.N) r.Mod(r, N)
if r.Sign() != 0 { if r.Sign() != 0 {
break break
} }
...@@ -110,7 +112,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err ...@@ -110,7 +112,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
s = new(big.Int).Mul(priv.D, r) s = new(big.Int).Mul(priv.D, r)
s.Add(s, e) s.Add(s, e)
s.Mul(s, kInv) s.Mul(s, kInv)
s.Mod(s, priv.PublicKey.Curve.N) s.Mod(s, N)
if s.Sign() != 0 { if s.Sign() != 0 {
break break
} }
...@@ -124,15 +126,16 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err ...@@ -124,15 +126,16 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
// See [NSA] 3.4.2 // See [NSA] 3.4.2
c := pub.Curve c := pub.Curve
N := c.Params().N
if r.Sign() == 0 || s.Sign() == 0 { if r.Sign() == 0 || s.Sign() == 0 {
return false return false
} }
if r.Cmp(c.N) >= 0 || s.Cmp(c.N) >= 0 { if r.Cmp(N) >= 0 || s.Cmp(N) >= 0 {
return false return false
} }
e := hashToInt(hash, c) e := hashToInt(hash, c)
w := new(big.Int).ModInverse(s, c.N) w := new(big.Int).ModInverse(s, N)
u1 := e.Mul(e, w) u1 := e.Mul(e, w)
u2 := w.Mul(r, w) u2 := w.Mul(r, w)
...@@ -143,6 +146,6 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { ...@@ -143,6 +146,6 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
return false return false
} }
x, _ := c.Add(x1, y1, x2, y2) x, _ := c.Add(x1, y1, x2, y2)
x.Mod(x, c.N) x.Mod(x, N)
return x.Cmp(r) == 0 return x.Cmp(r) == 0
} }
...@@ -13,7 +13,7 @@ import ( ...@@ -13,7 +13,7 @@ import (
"testing" "testing"
) )
func testKeyGeneration(t *testing.T, c *elliptic.Curve, tag string) { func testKeyGeneration(t *testing.T, c elliptic.Curve, tag string) {
priv, err := GenerateKey(c, rand.Reader) priv, err := GenerateKey(c, rand.Reader)
if err != nil { if err != nil {
t.Errorf("%s: error: %s", tag, err) t.Errorf("%s: error: %s", tag, err)
...@@ -34,7 +34,7 @@ func TestKeyGeneration(t *testing.T) { ...@@ -34,7 +34,7 @@ func TestKeyGeneration(t *testing.T) {
testKeyGeneration(t, elliptic.P521(), "p521") testKeyGeneration(t, elliptic.P521(), "p521")
} }
func testSignAndVerify(t *testing.T, c *elliptic.Curve, tag string) { func testSignAndVerify(t *testing.T, c elliptic.Curve, tag string) {
priv, _ := GenerateKey(c, rand.Reader) priv, _ := GenerateKey(c, rand.Reader)
hashed := []byte("testing") hashed := []byte("testing")
......
...@@ -7,5 +7,6 @@ include ../../../Make.inc ...@@ -7,5 +7,6 @@ include ../../../Make.inc
TARG=crypto/elliptic TARG=crypto/elliptic
GOFILES=\ GOFILES=\
elliptic.go\ elliptic.go\
p224.go\
include ../../../Make.pkg include ../../../Make.pkg
This diff is collapsed.
...@@ -13,7 +13,7 @@ import ( ...@@ -13,7 +13,7 @@ import (
func TestOnCurve(t *testing.T) { func TestOnCurve(t *testing.T) {
p224 := P224() p224 := P224()
if !p224.IsOnCurve(p224.Gx, p224.Gy) { if !p224.IsOnCurve(p224.Params().Gx, p224.Params().Gy) {
t.Errorf("FAIL") t.Errorf("FAIL")
} }
} }
...@@ -295,7 +295,25 @@ func TestBaseMult(t *testing.T) { ...@@ -295,7 +295,25 @@ func TestBaseMult(t *testing.T) {
} }
x, y := p224.ScalarBaseMult(k.Bytes()) x, y := p224.ScalarBaseMult(k.Bytes())
if fmt.Sprintf("%x", x) != e.x || fmt.Sprintf("%x", y) != e.y { if fmt.Sprintf("%x", x) != e.x || fmt.Sprintf("%x", y) != e.y {
t.Errorf("%d: bad output for k=%s: got (%x, %s), want (%x, %s)", i, e.k, x, y, e.x, e.y) t.Errorf("%d: bad output for k=%s: got (%x, %x), want (%s, %s)", i, e.k, x, y, e.x, e.y)
}
if testing.Short() && i > 5 {
break
}
}
}
func TestGenericBaseMult(t *testing.T) {
// We use the P224 CurveParams directly in order to test the generic implementation.
p224 := P224().Params()
for i, e := range p224BaseMultTests {
k, ok := new(big.Int).SetString(e.k, 10)
if !ok {
t.Errorf("%d: bad value for k: %s", i, e.k)
}
x, y := p224.ScalarBaseMult(k.Bytes())
if fmt.Sprintf("%x", x) != e.x || fmt.Sprintf("%x", y) != e.y {
t.Errorf("%d: bad output for k=%s: got (%x, %x), want (%s, %s)", i, e.k, x, y, e.x, e.y)
} }
if testing.Short() && i > 5 { if testing.Short() && i > 5 {
break break
...@@ -316,13 +334,13 @@ func BenchmarkBaseMult(b *testing.B) { ...@@ -316,13 +334,13 @@ func BenchmarkBaseMult(b *testing.B) {
func TestMarshal(t *testing.T) { func TestMarshal(t *testing.T) {
p224 := P224() p224 := P224()
_, x, y, err := p224.GenerateKey(rand.Reader) _, x, y, err := GenerateKey(p224, rand.Reader)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
} }
serialized := p224.Marshal(x, y) serialized := Marshal(p224, x, y)
xx, yy := p224.Unmarshal(serialized) xx, yy := Unmarshal(p224, serialized)
if xx == nil { if xx == nil {
t.Error("failed to unmarshal") t.Error("failed to unmarshal")
return return
......
This diff is collapsed.
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package elliptic
import (
"math/big"
"testing"
)
var toFromBigTests = []string{
"0",
"1",
"23",
"b70e0cb46bb4bf7f321390b94a03c1d356c01122343280d6105c1d21",
"706a46d476dcb76798e6046d89474788d164c18032d268fd10704fa6",
}
func p224AlternativeToBig(in *p224FieldElement) *big.Int {
ret := new(big.Int)
tmp := new(big.Int)
for i := uint(0); i < 8; i++ {
tmp.SetInt64(int64(in[i]))
tmp.Lsh(tmp, 28*i)
ret.Add(ret, tmp)
}
ret.Mod(ret, p224.P)
return ret
}
func TestToFromBig(t *testing.T) {
for i, test := range toFromBigTests {
n, _ := new(big.Int).SetString(test, 16)
var x p224FieldElement
p224FromBig(&x, n)
m := p224ToBig(&x)
if n.Cmp(m) != 0 {
t.Errorf("#%d: %x != %x", i, n, m)
}
q := p224AlternativeToBig(&x)
if n.Cmp(q) != 0 {
t.Errorf("#%d: %x != %x (alternative)", i, n, m)
}
}
}
...@@ -105,7 +105,7 @@ func md5SHA1Hash(slices ...[]byte) []byte { ...@@ -105,7 +105,7 @@ func md5SHA1Hash(slices ...[]byte) []byte {
// pre-master secret is then calculated using ECDH. // pre-master secret is then calculated using ECDH.
type ecdheRSAKeyAgreement struct { type ecdheRSAKeyAgreement struct {
privateKey []byte privateKey []byte
curve *elliptic.Curve curve elliptic.Curve
x, y *big.Int x, y *big.Int
} }
...@@ -132,11 +132,11 @@ Curve: ...@@ -132,11 +132,11 @@ Curve:
var x, y *big.Int var x, y *big.Int
var err error var err error
ka.privateKey, x, y, err = ka.curve.GenerateKey(config.rand()) ka.privateKey, x, y, err = elliptic.GenerateKey(ka.curve, config.rand())
if err != nil { if err != nil {
return nil, err return nil, err
} }
ecdhePublic := ka.curve.Marshal(x, y) ecdhePublic := elliptic.Marshal(ka.curve, x, y)
// http://tools.ietf.org/html/rfc4492#section-5.4 // http://tools.ietf.org/html/rfc4492#section-5.4
serverECDHParams := make([]byte, 1+2+1+len(ecdhePublic)) serverECDHParams := make([]byte, 1+2+1+len(ecdhePublic))
...@@ -167,12 +167,12 @@ func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *cl ...@@ -167,12 +167,12 @@ func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *cl
if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 { if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
return nil, errors.New("bad ClientKeyExchange") return nil, errors.New("bad ClientKeyExchange")
} }
x, y := ka.curve.Unmarshal(ckx.ciphertext[1:]) x, y := elliptic.Unmarshal(ka.curve, ckx.ciphertext[1:])
if x == nil { if x == nil {
return nil, errors.New("bad ClientKeyExchange") return nil, errors.New("bad ClientKeyExchange")
} }
x, _ = ka.curve.ScalarMult(x, y, ka.privateKey) x, _ = ka.curve.ScalarMult(x, y, ka.privateKey)
preMasterSecret := make([]byte, (ka.curve.BitSize+7)>>3) preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3)
xBytes := x.Bytes() xBytes := x.Bytes()
copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes) copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes)
...@@ -205,7 +205,7 @@ func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientH ...@@ -205,7 +205,7 @@ func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientH
if publicLen+4 > len(skx.key) { if publicLen+4 > len(skx.key) {
return errServerKeyExchange return errServerKeyExchange
} }
ka.x, ka.y = ka.curve.Unmarshal(skx.key[4 : 4+publicLen]) ka.x, ka.y = elliptic.Unmarshal(ka.curve, skx.key[4:4+publicLen])
if ka.x == nil { if ka.x == nil {
return errServerKeyExchange return errServerKeyExchange
} }
...@@ -229,16 +229,16 @@ func (ka *ecdheRSAKeyAgreement) generateClientKeyExchange(config *Config, client ...@@ -229,16 +229,16 @@ func (ka *ecdheRSAKeyAgreement) generateClientKeyExchange(config *Config, client
if ka.curve == nil { if ka.curve == nil {
return nil, nil, errors.New("missing ServerKeyExchange message") return nil, nil, errors.New("missing ServerKeyExchange message")
} }
priv, mx, my, err := ka.curve.GenerateKey(config.rand()) priv, mx, my, err := elliptic.GenerateKey(ka.curve, config.rand())
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
x, _ := ka.curve.ScalarMult(ka.x, ka.y, priv) x, _ := ka.curve.ScalarMult(ka.x, ka.y, priv)
preMasterSecret := make([]byte, (ka.curve.BitSize+7)>>3) preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3)
xBytes := x.Bytes() xBytes := x.Bytes()
copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes) copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes)
serialized := ka.curve.Marshal(mx, my) serialized := elliptic.Marshal(ka.curve, mx, my)
ckx := new(clientKeyExchangeMsg) ckx := new(clientKeyExchangeMsg)
ckx.ciphertext = make([]byte, 1+len(serialized)) ckx.ciphertext = make([]byte, 1+len(serialized))
......
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