diff options
author | Filippo Valsorda <filippo@golang.org> | 2018-11-01 01:01:09 -0400 |
---|---|---|
committer | Filippo Valsorda <filippo@golang.org> | 2018-11-02 22:07:02 +0000 |
commit | 4caa1276a1a184edf0ba8027619ecfe71e8c0804 (patch) | |
tree | 1dd3c2fe76b041cfae88c288fb399276e995cd66 /src/crypto/tls/key_agreement.go | |
parent | ee7e443389f7c8aabc58d49e42880b96f0955361 (diff) | |
download | go-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.go | 176 |
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 } |