diff options
author | Adam Langley <agl@golang.org> | 2016-10-10 18:23:37 -0700 |
---|---|---|
committer | Adam Langley <agl@golang.org> | 2016-10-12 17:04:28 +0000 |
commit | 8a11cb318f5c4c15b67ffe9ff9b2fa772bf1dd85 (patch) | |
tree | 8e06ac858a1d47422a10381498d3db1d19f2f883 /src/crypto/tls/key_agreement.go | |
parent | 8c74139cada0b7da5a0914ee0afd75837b6f682d (diff) | |
download | go-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.go | 156 |
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)) |