diff options
Diffstat (limited to 'src/crypto/ecdsa')
-rw-r--r-- | src/crypto/ecdsa/boring.go | 100 | ||||
-rw-r--r-- | src/crypto/ecdsa/ecdsa.go | 45 |
2 files changed, 145 insertions, 0 deletions
diff --git a/src/crypto/ecdsa/boring.go b/src/crypto/ecdsa/boring.go new file mode 100644 index 0000000000..fa15ecb850 --- /dev/null +++ b/src/crypto/ecdsa/boring.go @@ -0,0 +1,100 @@ +// Copyright 2017 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 ecdsa + +import ( + "crypto/internal/boring" + "math/big" + "sync/atomic" + "unsafe" +) + +// Cached conversions from Go PublicKey/PrivateKey to BoringCrypto. +// +// A new 'boring atomic.Value' field in both PublicKey and PrivateKey +// serves as a cache for the most recent conversion. The cache is an +// atomic.Value because code might reasonably set up a key and then +// (thinking it immutable) use it from multiple goroutines simultaneously. +// The first operation initializes the cache; if there are multiple simultaneous +// first operations, they will do redundant work but not step on each other. +// +// We could just assume that once used in a Sign or Verify operation, +// a particular key is never again modified, but that has not been a +// stated assumption before. Just in case there is any existing code that +// does modify the key between operations, we save the original values +// alongside the cached BoringCrypto key and check that the real key +// still matches before using the cached key. The theory is that the real +// operations are significantly more expensive than the comparison. + +type boringPub struct { + key *boring.PublicKeyECDSA + orig PublicKey +} + +func boringPublicKey(pub *PublicKey) (*boring.PublicKeyECDSA, error) { + b := (*boringPub)(atomic.LoadPointer(&pub.boring)) + if b != nil && publicKeyEqual(&b.orig, pub) { + return b.key, nil + } + + b = new(boringPub) + b.orig = copyPublicKey(pub) + key, err := boring.NewPublicKeyECDSA(b.orig.Curve.Params().Name, b.orig.X, b.orig.Y) + if err != nil { + return nil, err + } + b.key = key + atomic.StorePointer(&pub.boring, unsafe.Pointer(b)) + return key, nil +} + +type boringPriv struct { + key *boring.PrivateKeyECDSA + orig PrivateKey +} + +func boringPrivateKey(priv *PrivateKey) (*boring.PrivateKeyECDSA, error) { + b := (*boringPriv)(atomic.LoadPointer(&priv.boring)) + if b != nil && privateKeyEqual(&b.orig, priv) { + return b.key, nil + } + + b = new(boringPriv) + b.orig = copyPrivateKey(priv) + key, err := boring.NewPrivateKeyECDSA(b.orig.Curve.Params().Name, b.orig.X, b.orig.Y, b.orig.D) + if err != nil { + return nil, err + } + b.key = key + atomic.StorePointer(&priv.boring, unsafe.Pointer(b)) + return key, nil +} + +func publicKeyEqual(k1, k2 *PublicKey) bool { + return k1.X != nil && + k1.Curve.Params() == k2.Curve.Params() && + k1.X.Cmp(k2.X) == 0 && + k1.Y.Cmp(k2.Y) == 0 +} + +func privateKeyEqual(k1, k2 *PrivateKey) bool { + return publicKeyEqual(&k1.PublicKey, &k2.PublicKey) && + k1.D.Cmp(k2.D) == 0 +} + +func copyPublicKey(k *PublicKey) PublicKey { + return PublicKey{ + Curve: k.Curve, + X: new(big.Int).Set(k.X), + Y: new(big.Int).Set(k.Y), + } +} + +func copyPrivateKey(k *PrivateKey) PrivateKey { + return PrivateKey{ + PublicKey: copyPublicKey(&k.PublicKey), + D: new(big.Int).Set(k.D), + } +} diff --git a/src/crypto/ecdsa/ecdsa.go b/src/crypto/ecdsa/ecdsa.go index ccce873859..04738cdbd7 100644 --- a/src/crypto/ecdsa/ecdsa.go +++ b/src/crypto/ecdsa/ecdsa.go @@ -41,6 +41,11 @@ import ( "golang.org/x/crypto/cryptobyte/asn1" ) +import ( + "crypto/internal/boring" + "unsafe" +) + // A invertible implements fast inverse mod Curve.Params().N type invertible interface { // Inverse returns the inverse of k in GF(P) @@ -60,6 +65,8 @@ const ( type PublicKey struct { elliptic.Curve X, Y *big.Int + + boring unsafe.Pointer } // Any methods implemented on PublicKey might need to also be implemented on @@ -87,6 +94,8 @@ func (pub *PublicKey) Equal(x crypto.PublicKey) bool { type PrivateKey struct { PublicKey D *big.Int + + boring unsafe.Pointer } // Public returns the public key corresponding to priv. @@ -113,6 +122,15 @@ func (priv *PrivateKey) Equal(x crypto.PrivateKey) bool { // where the private part is kept in, for example, a hardware module. Common // uses should use the Sign function in this package directly. func (priv *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { + if boring.Enabled && rand == boring.RandReader { + b, err := boringPrivateKey(priv) + if err != nil { + return nil, err + } + return boring.SignMarshalECDSA(b, digest) + } + boring.UnreachableExceptTests() + r, s, err := Sign(rand, priv, digest) if err != nil { return nil, err @@ -147,6 +165,15 @@ func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) // GenerateKey generates a public and private key pair. func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) { + if boring.Enabled && rand == boring.RandReader { + x, y, d, err := boring.GenerateKeyECDSA(c.Params().Name) + if err != nil { + return nil, err + } + return &PrivateKey{PublicKey: PublicKey{Curve: c, X: x, Y: y}, D: d}, nil + } + boring.UnreachableExceptTests() + k, err := randFieldElement(c, rand) if err != nil { return nil, err @@ -200,6 +227,15 @@ var errZeroParam = errors.New("zero parameter") func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) { randutil.MaybeReadByte(rand) + if boring.Enabled && rand == boring.RandReader { + b, err := boringPrivateKey(priv) + if err != nil { + return nil, nil, err + } + return boring.SignECDSA(b, hash) + } + boring.UnreachableExceptTests() + // Get min(log2(q) / 2, 256) bits of entropy from rand. entropylen := (priv.Curve.Params().BitSize + 7) / 16 if entropylen > 32 { @@ -289,6 +325,15 @@ func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) { // Verify verifies the signature in r, s of hash using the public key, pub. Its // return value records whether the signature is valid. func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { + if boring.Enabled { + b, err := boringPublicKey(pub) + if err != nil { + return false + } + return boring.VerifyECDSA(b, hash, r, s) + } + boring.UnreachableExceptTests() + // See [NSA] 3.4.2 c := pub.Curve N := c.Params().N |