crypto/ecdh: new package

We use crypto/internal/edwards25519/field to implement X25519 directly,
so that golang.org/x/crypto/curve25519 can be dropped from the src
module dependencies, and eventually replaced with a crypto/ecdh wrapper,
removing the need to keep golang.org/x/crypto/curve25519/internal/field
in sync with crypto/internal/edwards25519/field.

In crypto/internal/nistec, we add BytesX to serialize only the x
coordinate, which we'll need for the horrible ECDSA x-coord-to-scalar
operation, too.

In crypto/tls, we replace the ECDHE implementation with crypto/ecdh,
dropping the X25519 special cases and related scaffolding.

Finally, FINALLY, we deprecate the ~white whale~ big.Int-based APIs of
the crypto/elliptic package.   •_•)   ( •_•)>⌐■-■   (⌐■_■)

Fixes #52182
Fixes #34648
Fixes #52221

Change-Id: Iccdda210319cc892e96bb28a0e7b7123551982c7
Reviewed-on: https://go-review.googlesource.com/c/go/+/398914
Reviewed-by: Fernando Lobato Meeser <felobato@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Filippo Valsorda 2022-04-07 15:15:31 -04:00
parent 8011ffeccb
commit f80ca9c941
6 changed files with 85 additions and 121 deletions

View file

@ -5,15 +5,13 @@
package tls
import (
"crypto/elliptic"
"crypto/ecdh"
"crypto/hmac"
"errors"
"hash"
"io"
"math/big"
"golang.org/x/crypto/cryptobyte"
"golang.org/x/crypto/curve25519"
"golang.org/x/crypto/hkdf"
)
@ -101,99 +99,43 @@ func (c *cipherSuiteTLS13) exportKeyingMaterial(masterSecret []byte, transcript
}
}
// ecdheParameters implements Diffie-Hellman with either NIST curves or X25519,
// generateECDHEParameters returns a PrivateKey that implements Diffie-Hellman
// according to RFC 8446, Section 4.2.8.2.
type ecdheParameters interface {
CurveID() CurveID
PublicKey() []byte
SharedKey(peerPublicKey []byte) []byte
}
func generateECDHEParameters(rand io.Reader, curveID CurveID) (ecdheParameters, error) {
if curveID == X25519 {
privateKey := make([]byte, curve25519.ScalarSize)
if _, err := io.ReadFull(rand, privateKey); err != nil {
return nil, err
}
publicKey, err := curve25519.X25519(privateKey, curve25519.Basepoint)
if err != nil {
return nil, err
}
return &x25519Parameters{privateKey: privateKey, publicKey: publicKey}, nil
}
func generateECDHEKey(rand io.Reader, curveID CurveID) (*ecdh.PrivateKey, error) {
curve, ok := curveForCurveID(curveID)
if !ok {
return nil, errors.New("tls: internal error: unsupported curve")
}
p := &nistParameters{curveID: curveID}
var err error
p.privateKey, p.x, p.y, err = elliptic.GenerateKey(curve, rand)
if err != nil {
return nil, err
}
return p, nil
return curve.GenerateKey(rand)
}
func curveForCurveID(id CurveID) (elliptic.Curve, bool) {
func curveForCurveID(id CurveID) (ecdh.Curve, bool) {
switch id {
case X25519:
return ecdh.X25519(), true
case CurveP256:
return elliptic.P256(), true
return ecdh.P256(), true
case CurveP384:
return elliptic.P384(), true
return ecdh.P384(), true
case CurveP521:
return elliptic.P521(), true
return ecdh.P521(), true
default:
return nil, false
}
}
type nistParameters struct {
privateKey []byte
x, y *big.Int // public key
curveID CurveID
}
func (p *nistParameters) CurveID() CurveID {
return p.curveID
}
func (p *nistParameters) PublicKey() []byte {
curve, _ := curveForCurveID(p.curveID)
return elliptic.Marshal(curve, p.x, p.y)
}
func (p *nistParameters) SharedKey(peerPublicKey []byte) []byte {
curve, _ := curveForCurveID(p.curveID)
// Unmarshal also checks whether the given point is on the curve.
x, y := elliptic.Unmarshal(curve, peerPublicKey)
if x == nil {
return nil
func curveIDForCurve(curve ecdh.Curve) (CurveID, bool) {
switch curve {
case ecdh.X25519():
return X25519, true
case ecdh.P256():
return CurveP256, true
case ecdh.P384():
return CurveP384, true
case ecdh.P521():
return CurveP521, true
default:
return 0, false
}
xShared, _ := curve.ScalarMult(x, y, p.privateKey)
sharedKey := make([]byte, (curve.Params().BitSize+7)/8)
return xShared.FillBytes(sharedKey)
}
type x25519Parameters struct {
privateKey []byte
publicKey []byte
}
func (p *x25519Parameters) CurveID() CurveID {
return X25519
}
func (p *x25519Parameters) PublicKey() []byte {
return p.publicKey[:]
}
func (p *x25519Parameters) SharedKey(peerPublicKey []byte) []byte {
sharedKey, err := curve25519.X25519(p.privateKey, peerPublicKey)
if err != nil {
return nil
}
return sharedKey
}