aboutsummaryrefslogtreecommitdiff
path: root/src/crypto/tls/key_agreement.go
diff options
context:
space:
mode:
authorAdam Langley <agl@golang.org>2016-10-10 18:23:37 -0700
committerAdam Langley <agl@golang.org>2016-10-12 17:04:28 +0000
commit8a11cb318f5c4c15b67ffe9ff9b2fa772bf1dd85 (patch)
tree8e06ac858a1d47422a10381498d3db1d19f2f883 /src/crypto/tls/key_agreement.go
parent8c74139cada0b7da5a0914ee0afd75837b6f682d (diff)
downloadgo-8a11cb318f5c4c15b67ffe9ff9b2fa772bf1dd85.tar.gz
go-8a11cb318f5c4c15b67ffe9ff9b2fa772bf1dd85.zip
crypto/tls: support X25519.
X25519 (RFC 7748) is now commonly used for key agreement in TLS connections, as specified in https://tools.ietf.org/html/draft-ietf-tls-curve25519-01. This change adds support for that in crypto/tls, but does not enabled it by default so that there's less test noise. A future change will enable it by default and will update all the test data at the same time. Change-Id: I91802ecd776d73aae5c65bcb653d12e23c413ed4 Reviewed-on: https://go-review.googlesource.com/30824 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src/crypto/tls/key_agreement.go')
-rw-r--r--src/crypto/tls/key_agreement.go156
1 files changed, 112 insertions, 44 deletions
diff --git a/src/crypto/tls/key_agreement.go b/src/crypto/tls/key_agreement.go
index 467efb2bf5..1b27c049ed 100644
--- a/src/crypto/tls/key_agreement.go
+++ b/src/crypto/tls/key_agreement.go
@@ -16,6 +16,8 @@ import (
"errors"
"io"
"math/big"
+
+ "golang_org/x/crypto/curve25519"
)
var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message")
@@ -177,52 +179,71 @@ type ecdheKeyAgreement struct {
version uint16
sigType uint8
privateKey []byte
- curve elliptic.Curve
- x, y *big.Int
+ curveid CurveID
+
+ // publicKey is used to store the peer's public value when X25519 is
+ // being used.
+ publicKey []byte
+ // x and y are used to store the peer's public value when one of the
+ // NIST curves is being used.
+ x, y *big.Int
}
func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
- var curveid CurveID
preferredCurves := config.curvePreferences()
NextCandidate:
for _, candidate := range preferredCurves {
for _, c := range clientHello.supportedCurves {
if candidate == c {
- curveid = c
+ ka.curveid = c
break NextCandidate
}
}
}
- if curveid == 0 {
+ if ka.curveid == 0 {
return nil, errors.New("tls: no supported elliptic curves offered")
}
- var ok bool
- if ka.curve, ok = curveForCurveID(curveid); !ok {
- return nil, errors.New("tls: preferredCurves includes unsupported curve")
- }
+ var ecdhePublic []byte
- var x, y *big.Int
- var err error
- ka.privateKey, x, y, err = elliptic.GenerateKey(ka.curve, config.rand())
- if err != nil {
- return nil, err
+ if ka.curveid == X25519 {
+ var scalar, public [32]byte
+ if _, err := io.ReadFull(config.rand(), scalar[:]); err != nil {
+ return nil, err
+ }
+
+ curve25519.ScalarBaseMult(&public, &scalar)
+ ka.privateKey = scalar[:]
+ ecdhePublic = public[:]
+ } else {
+ curve, ok := curveForCurveID(ka.curveid)
+ if !ok {
+ return nil, errors.New("tls: preferredCurves includes unsupported curve")
+ }
+
+ var x, y *big.Int
+ var err error
+ ka.privateKey, x, y, err = elliptic.GenerateKey(curve, config.rand())
+ if err != nil {
+ return nil, err
+ }
+ ecdhePublic = elliptic.Marshal(curve, x, y)
}
- ecdhePublic := elliptic.Marshal(ka.curve, x, y)
// http://tools.ietf.org/html/rfc4492#section-5.4
serverECDHParams := make([]byte, 1+2+1+len(ecdhePublic))
serverECDHParams[0] = 3 // named curve
- serverECDHParams[1] = byte(curveid >> 8)
- serverECDHParams[2] = byte(curveid)
+ serverECDHParams[1] = byte(ka.curveid >> 8)
+ serverECDHParams[2] = byte(ka.curveid)
serverECDHParams[3] = byte(len(ecdhePublic))
copy(serverECDHParams[4:], ecdhePublic)
sigAndHash := signatureAndHash{signature: ka.sigType}
if ka.version >= VersionTLS12 {
+ var err error
if sigAndHash.hash, err = pickTLS12HashForSignature(ka.sigType, clientHello.signatureAndHashes); err != nil {
return nil, err
}
@@ -281,15 +302,32 @@ func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Cert
if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
return nil, errClientKeyExchange
}
- x, y := elliptic.Unmarshal(ka.curve, ckx.ciphertext[1:])
+
+ if ka.curveid == X25519 {
+ if len(ckx.ciphertext) != 1+32 {
+ return nil, errClientKeyExchange
+ }
+
+ var theirPublic, sharedKey, scalar [32]byte
+ copy(theirPublic[:], ckx.ciphertext[1:])
+ copy(scalar[:], ka.privateKey)
+ curve25519.ScalarMult(&sharedKey, &scalar, &theirPublic)
+ return sharedKey[:], nil
+ }
+
+ curve, ok := curveForCurveID(ka.curveid)
+ if !ok {
+ panic("internal error")
+ }
+ x, y := elliptic.Unmarshal(curve, ckx.ciphertext[1:])
if x == nil {
return nil, errClientKeyExchange
}
- if !ka.curve.IsOnCurve(x, y) {
+ if !curve.IsOnCurve(x, y) {
return nil, errClientKeyExchange
}
- x, _ = ka.curve.ScalarMult(x, y, ka.privateKey)
- preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3)
+ x, _ = curve.ScalarMult(x, y, ka.privateKey)
+ preMasterSecret := make([]byte, (curve.Params().BitSize+7)>>3)
xBytes := x.Bytes()
copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes)
@@ -303,31 +341,40 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
if skx.key[0] != 3 { // named curve
return errors.New("tls: server selected unsupported curve")
}
- curveid := CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
-
- var ok bool
- if ka.curve, ok = curveForCurveID(curveid); !ok {
- return errors.New("tls: server selected unsupported curve")
- }
+ ka.curveid = CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
publicLen := int(skx.key[3])
if publicLen+4 > len(skx.key) {
return errServerKeyExchange
}
- ka.x, ka.y = elliptic.Unmarshal(ka.curve, skx.key[4:4+publicLen])
- if ka.x == nil {
- return errServerKeyExchange
- }
- if !ka.curve.IsOnCurve(ka.x, ka.y) {
- return errServerKeyExchange
- }
serverECDHParams := skx.key[:4+publicLen]
+ publicKey := serverECDHParams[4:]
sig := skx.key[4+publicLen:]
if len(sig) < 2 {
return errServerKeyExchange
}
+ if ka.curveid == X25519 {
+ if len(publicKey) != 32 {
+ return errors.New("tls: bad X25519 public value")
+ }
+ ka.publicKey = publicKey
+ } else {
+ curve, ok := curveForCurveID(ka.curveid)
+ if !ok {
+ return errors.New("tls: server selected unsupported curve")
+ }
+
+ ka.x, ka.y = elliptic.Unmarshal(curve, publicKey)
+ if ka.x == nil {
+ return errServerKeyExchange
+ }
+ if !curve.IsOnCurve(ka.x, ka.y) {
+ return errServerKeyExchange
+ }
+ }
+
sigAndHash := signatureAndHash{signature: ka.sigType}
if ka.version >= VersionTLS12 {
// handle SignatureAndHashAlgorithm
@@ -382,19 +429,40 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
}
func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
- if ka.curve == nil {
+ if ka.curveid == 0 {
return nil, nil, errors.New("tls: missing ServerKeyExchange message")
}
- priv, mx, my, err := elliptic.GenerateKey(ka.curve, config.rand())
- if err != nil {
- return nil, nil, err
- }
- x, _ := ka.curve.ScalarMult(ka.x, ka.y, priv)
- preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3)
- xBytes := x.Bytes()
- copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes)
- serialized := elliptic.Marshal(ka.curve, mx, my)
+ var serialized, preMasterSecret []byte
+
+ if ka.curveid == X25519 {
+ var ourPublic, theirPublic, sharedKey, scalar [32]byte
+
+ if _, err := io.ReadFull(config.rand(), scalar[:]); err != nil {
+ return nil, nil, err
+ }
+
+ copy(theirPublic[:], ka.publicKey)
+ curve25519.ScalarBaseMult(&ourPublic, &scalar)
+ curve25519.ScalarMult(&sharedKey, &scalar, &theirPublic)
+ serialized = ourPublic[:]
+ preMasterSecret = sharedKey[:]
+ } else {
+ curve, ok := curveForCurveID(ka.curveid)
+ if !ok {
+ panic("internal error")
+ }
+ priv, mx, my, err := elliptic.GenerateKey(curve, config.rand())
+ if err != nil {
+ return nil, nil, err
+ }
+ x, _ := curve.ScalarMult(ka.x, ka.y, priv)
+ preMasterSecret = make([]byte, (curve.Params().BitSize+7)>>3)
+ xBytes := x.Bytes()
+ copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes)
+
+ serialized = elliptic.Marshal(curve, mx, my)
+ }
ckx := new(clientKeyExchangeMsg)
ckx.ciphertext = make([]byte, 1+len(serialized))