Skip to content

Commit

Permalink
[dev.boringcrypto] crypto/ecdsa: use BoringCrypto
Browse files Browse the repository at this point in the history
Change-Id: I108e0a527bddd673b16582d206e0697341d0a0ea
Reviewed-on: https://go-review.googlesource.com/55478
Run-TryBot: Russ Cox <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Adam Langley <[email protected]>
  • Loading branch information
rsc committed Aug 17, 2017
1 parent 2efded1 commit b1f201e
Show file tree
Hide file tree
Showing 5 changed files with 374 additions and 0 deletions.
109 changes: 109 additions & 0 deletions src/crypto/ecdsa/boring.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// 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/elliptic"
"crypto/internal/boring"
"math/big"
)

// 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
}

// copy of PublicKey without the atomic.Value field, to placate vet.
type publicKey struct {
elliptic.Curve
X, Y *big.Int
}

func boringPublicKey(pub *PublicKey) (*boring.PublicKeyECDSA, error) {
b, _ := pub.boring.Load().(boringPub)
if publicKeyEqual(&b.orig, pub) {
return b.key, nil
}

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
pub.boring.Store(b)
return key, nil
}

type boringPriv struct {
key *boring.PrivateKeyECDSA
orig privateKey
}

// copy of PrivateKey without the atomic.Value field, to placate vet.
type privateKey struct {
publicKey
D *big.Int
}

func boringPrivateKey(priv *PrivateKey) (*boring.PrivateKeyECDSA, error) {
b, _ := priv.boring.Load().(boringPriv)
if privateKeyEqual(&b.orig, priv) {
return b.key, nil
}

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
priv.boring.Store(b)
return key, nil
}

func publicKeyEqual(k1 *publicKey, 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 *privateKey, 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),
}
}
42 changes: 42 additions & 0 deletions src/crypto/ecdsa/ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ import (
"crypto/aes"
"crypto/cipher"
"crypto/elliptic"
"crypto/internal/boring"
"crypto/sha512"
"encoding/asn1"
"errors"
"io"
"math/big"
"sync/atomic"
)

// A invertible implements fast inverse mod Curve.Params().N
Expand All @@ -47,12 +49,16 @@ const (
type PublicKey struct {
elliptic.Curve
X, Y *big.Int

boring atomic.Value
}

// PrivateKey represents a ECDSA private key.
type PrivateKey struct {
PublicKey
D *big.Int

boring atomic.Value
}

type ecdsaSignature struct {
Expand All @@ -69,6 +75,15 @@ func (priv *PrivateKey) Public() crypto.PublicKey {
// hardware module. Common uses should use the Sign function in this package
// directly.
func (priv *PrivateKey) Sign(rand io.Reader, msg []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, msg)
}
boring.UnreachableExceptTests()

r, s, err := Sign(rand, priv, msg)
if err != nil {
return nil, err
Expand Down Expand Up @@ -98,6 +113,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
Expand Down Expand Up @@ -149,6 +173,15 @@ var errZeroParam = errors.New("zero parameter")
// returns the signature as a pair of integers. The security of the private key
// depends on the entropy of rand.
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
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 {
Expand Down Expand Up @@ -225,6 +258,15 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
// 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
Expand Down
12 changes: 12 additions & 0 deletions src/crypto/internal/boring/boring.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package boring

// #include "goboringcrypto.h"
import "C"
import "math/big"

const available = true

Expand Down Expand Up @@ -41,3 +42,14 @@ func UnreachableExceptTests() {
type fail string

func (e fail) Error() string { return "boringcrypto: " + string(e) + " failed" }

func bigToBN(x *big.Int) *C.GO_BIGNUM {
raw := x.Bytes()
return C._goboringcrypto_BN_bin2bn(base(raw), C.size_t(len(raw)), nil)
}

func bnToBig(bn *C.GO_BIGNUM) *big.Int {
raw := make([]byte, C._goboringcrypto_BN_num_bytes(bn))
n := C._goboringcrypto_BN_bn2bin(bn, base(raw))
return new(big.Int).SetBytes(raw[:n])
}
Loading

0 comments on commit b1f201e

Please sign in to comment.