aboutsummaryrefslogtreecommitdiff
path: root/src/crypto/tls/key_agreement.go
diff options
context:
space:
mode:
authorFilippo Valsorda <filippo@golang.org>2018-11-01 01:01:09 -0400
committerFilippo Valsorda <filippo@golang.org>2018-11-02 22:07:02 +0000
commit4caa1276a1a184edf0ba8027619ecfe71e8c0804 (patch)
tree1dd3c2fe76b041cfae88c288fb399276e995cd66 /src/crypto/tls/key_agreement.go
parentee7e443389f7c8aabc58d49e42880b96f0955361 (diff)
downloadgo-4caa1276a1a184edf0ba8027619ecfe71e8c0804.tar.gz
go-4caa1276a1a184edf0ba8027619ecfe71e8c0804.zip
crypto/tls: implement TLS 1.3 client handshake (base)
Implement a basic TLS 1.3 client handshake, only enabled if explicitly requested with MaxVersion. This CL intentionally leaves for future CLs: - PSK modes and resumption - client authentication - post-handshake messages - downgrade protection - KeyLogWriter support Updates #9671 Change-Id: Ieb6130fb6f25aea4f0d39e3a2448dfc942e1de7a Reviewed-on: https://go-review.googlesource.com/c/146559 Run-TryBot: Filippo Valsorda <filippo@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Adam Langley <agl@golang.org>
Diffstat (limited to 'src/crypto/tls/key_agreement.go')
-rw-r--r--src/crypto/tls/key_agreement.go176
1 files changed, 45 insertions, 131 deletions
diff --git a/src/crypto/tls/key_agreement.go b/src/crypto/tls/key_agreement.go
index 1baa901ec7..628e578e48 100644
--- a/src/crypto/tls/key_agreement.go
+++ b/src/crypto/tls/key_agreement.go
@@ -6,15 +6,12 @@ package tls
import (
"crypto"
- "crypto/elliptic"
"crypto/md5"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"errors"
- "golang_org/x/crypto/curve25519"
"io"
- "math/big"
)
var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message")
@@ -124,86 +121,54 @@ func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint1
return md5SHA1Hash(slices), nil
}
-func curveForCurveID(id CurveID) (elliptic.Curve, bool) {
- switch id {
- case CurveP256:
- return elliptic.P256(), true
- case CurveP384:
- return elliptic.P384(), true
- case CurveP521:
- return elliptic.P521(), true
- default:
- return nil, false
- }
-
-}
-
// ecdheKeyAgreement implements a TLS key agreement where the server
// generates an ephemeral EC public/private key pair and signs it. The
// pre-master secret is then calculated using ECDH. The signature may
// either be ECDSA or RSA.
type ecdheKeyAgreement struct {
- version uint16
- isRSA bool
- privateKey []byte
- 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
+ version uint16
+ isRSA bool
+ params ecdheParameters
+
+ // ckx and preMasterSecret are generated in processServerKeyExchange
+ // and returned in generateClientKeyExchange.
+ ckx *clientKeyExchangeMsg
+ preMasterSecret []byte
}
func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
preferredCurves := config.curvePreferences()
+ var curveID CurveID
NextCandidate:
for _, candidate := range preferredCurves {
for _, c := range clientHello.supportedCurves {
if candidate == c {
- ka.curveid = c
+ curveID = c
break NextCandidate
}
}
}
- if ka.curveid == 0 {
+ if curveID == 0 {
return nil, errors.New("tls: no supported elliptic curves offered")
}
+ if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
+ return nil, errors.New("tls: CurvePreferences includes unsupported curve")
+ }
- var ecdhePublic []byte
-
- 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)
+ params, err := generateECDHEParameters(config.rand(), curveID)
+ if err != nil {
+ return nil, err
}
+ ka.params = params
// See RFC 4492, Section 5.4.
+ ecdhePublic := params.PublicKey()
serverECDHParams := make([]byte, 1+2+1+len(ecdhePublic))
serverECDHParams[0] = 3 // named curve
- serverECDHParams[1] = byte(ka.curveid >> 8)
- serverECDHParams[2] = byte(ka.curveid)
+ serverECDHParams[1] = byte(curveID >> 8)
+ serverECDHParams[2] = byte(curveID)
serverECDHParams[3] = byte(len(ecdhePublic))
copy(serverECDHParams[4:], ecdhePublic)
@@ -259,30 +224,10 @@ func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Cert
return nil, errClientKeyExchange
}
- 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:]) // Unmarshal also checks whether the given point is on the curve
- if x == nil {
+ preMasterSecret := ka.params.SharedKey(ckx.ciphertext[1:])
+ if preMasterSecret == nil {
return nil, errClientKeyExchange
}
- 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)
return preMasterSecret, nil
}
@@ -294,7 +239,7 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
if skx.key[0] != 3 { // named curve
return errors.New("tls: server selected unsupported curve")
}
- ka.curveid = CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
+ curveID := CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
publicLen := int(skx.key[3])
if publicLen+4 > len(skx.key) {
@@ -308,21 +253,26 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
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) // Unmarshal also checks whether the given point is on the curve
- if ka.x == nil {
- return errServerKeyExchange
- }
+ if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
+ return errors.New("tls: server selected unsupported curve")
+ }
+
+ params, err := generateECDHEParameters(config.rand(), curveID)
+ if err != nil {
+ return err
}
+ ka.params = params
+
+ ka.preMasterSecret = params.SharedKey(publicKey)
+ if ka.preMasterSecret == nil {
+ return errServerKeyExchange
+ }
+
+ ourPublicKey := params.PublicKey()
+ ka.ckx = new(clientKeyExchangeMsg)
+ ka.ckx.ciphertext = make([]byte, 1+len(ourPublicKey))
+ ka.ckx.ciphertext[0] = byte(len(ourPublicKey))
+ copy(ka.ckx.ciphertext[1:], ourPublicKey)
var signatureAlgorithm SignatureScheme
if ka.version >= VersionTLS12 {
@@ -355,45 +305,9 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
}
func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
- if ka.curveid == 0 {
+ if ka.ckx == nil {
return nil, nil, errors.New("tls: missing ServerKeyExchange message")
}
- 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))
- ckx.ciphertext[0] = byte(len(serialized))
- copy(ckx.ciphertext[1:], serialized)
-
- return preMasterSecret, ckx, nil
+ return ka.preMasterSecret, ka.ckx, nil
}