go/src/crypto/tls/handshake_client.go

1321 lines
40 KiB
Go
Raw Normal View History

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tls
import (
"bytes"
"context"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/internal/fips140/mlkem"
"crypto/internal/fips140/tls13"
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
"crypto/internal/hpke"
"crypto/rsa"
"crypto/subtle"
"crypto/tls/internal/fips140tls"
"crypto/x509"
"errors"
"fmt"
"hash"
"internal/byteorder"
"internal/godebug"
"io"
"net"
"slices"
"strconv"
"strings"
"time"
)
type clientHandshakeState struct {
c *Conn
ctx context.Context
serverHello *serverHelloMsg
hello *clientHelloMsg
suite *cipherSuite
finishedHash finishedHash
masterSecret []byte
session *SessionState // the session being resumed
ticket []byte // a fresh ticket received during this handshake
}
var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme
func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echClientContext, error) {
config := c.config
if len(config.ServerName) == 0 && !config.InsecureSkipVerify {
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
return nil, nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config")
}
nextProtosLength := 0
for _, proto := range config.NextProtos {
if l := len(proto); l == 0 || l > 255 {
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
return nil, nil, nil, errors.New("tls: invalid NextProtos value")
} else {
nextProtosLength += 1 + l
}
}
if nextProtosLength > 0xffff {
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
return nil, nil, nil, errors.New("tls: NextProtos values too large")
}
supportedVersions := config.supportedVersions(roleClient)
if len(supportedVersions) == 0 {
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
return nil, nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion")
}
maxVersion := config.maxSupportedVersion(roleClient)
hello := &clientHelloMsg{
vers: maxVersion,
compressionMethods: []uint8{compressionNone},
random: make([]byte, 32),
extendedMasterSecret: true,
ocspStapling: true,
scts: true,
serverName: hostnameInSNI(config.ServerName),
supportedCurves: config.curvePreferences(maxVersion),
supportedPoints: []uint8{pointFormatUncompressed},
secureRenegotiationSupported: true,
alpnProtocols: config.NextProtos,
supportedVersions: supportedVersions,
}
// The version at the beginning of the ClientHello was capped at TLS 1.2
// for compatibility reasons. The supported_versions extension is used
// to negotiate versions now. See RFC 8446, Section 4.2.1.
if hello.vers > VersionTLS12 {
hello.vers = VersionTLS12
}
if c.handshakes > 0 {
hello.secureRenegotiation = c.clientFinished[:]
}
hello.cipherSuites = config.cipherSuites(hasAESGCMHardwareSupport)
// Don't advertise TLS 1.2-only cipher suites unless we're attempting TLS 1.2.
if maxVersion < VersionTLS12 {
hello.cipherSuites = slices.DeleteFunc(hello.cipherSuites, func(id uint16) bool {
return cipherSuiteByID(id).flags&suiteTLS12 != 0
})
}
_, err := io.ReadFull(config.rand(), hello.random)
if err != nil {
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
return nil, nil, nil, errors.New("tls: short read from Rand: " + err.Error())
}
// A random session ID is used to detect when the server accepted a ticket
// and is resuming a session (see RFC 5077). In TLS 1.3, it's always set as
// a compatibility measure (see RFC 8446, Section 4.1.2).
//
// The session ID is not set for QUIC connections (see RFC 9001, Section 8.4).
if c.quic == nil {
hello.sessionId = make([]byte, 32)
if _, err := io.ReadFull(config.rand(), hello.sessionId); err != nil {
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
return nil, nil, nil, errors.New("tls: short read from Rand: " + err.Error())
}
}
if maxVersion >= VersionTLS12 {
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
}
if testingOnlyForceClientHelloSignatureAlgorithms != nil {
hello.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms
}
var keyShareKeys *keySharePrivateKeys
if hello.supportedVersions[0] == VersionTLS13 {
// Reset the list of ciphers when the client only supports TLS 1.3.
if len(hello.supportedVersions) == 1 {
hello.cipherSuites = nil
}
if fips140tls.Required() {
hello.cipherSuites = append(hello.cipherSuites, allowedCipherSuitesTLS13FIPS...)
} else if hasAESGCMHardwareSupport {
crypto/tls: make cipher suite preference ordering automatic We now have a (well, two, depending on AES hardware support) universal cipher suite preference order, based on their security and performance. Peer and application lists are now treated as filters (and AES hardware support hints) that are applied to this universal order. This removes a complex and nuanced decision from the application's responsibilities, one which we are better equipped to make and which applications usually don't need to have an opinion about. It also lets us worry less about what suites we support or enable, because we can be confident that bad ones won't be selected over good ones. This also moves 3DES suites to InsecureCipherSuites(), even if they are not disabled by default. Just because we can keep them as a last resort it doesn't mean they are secure. Thankfully we had not promised that Insecure means disabled by default. Notable test changes: - TestCipherSuiteCertPreferenceECDSA was testing that we'd pick the right certificate regardless of CipherSuite ordering, which is now completely ignored, as tested by TestCipherSuitePreference. Removed. - The openssl command of TestHandshakeServerExportKeyingMaterial was broken for TLS 1.0 in CL 262857, but its golden file was not regenerated, so the test kept passing. It now broke because the selected suite from the ones in the golden file changed. - In TestAESCipherReordering, "server strongly prefers AES-GCM" is removed because there is no way for a server to express a strong preference anymore; "client prefers AES-GCM and AES-CBC over ChaCha" switched to ChaCha20 when the server lacks AES hardware; and finally "client supports multiple AES-GCM" changed to always prefer AES-128 per the universal preference list. * this is going back on an explicit decision from CL 262857, and while that client order is weird and does suggest a strong dislike for ChaCha20, we have a strong dislike for software AES, so it didn't feel worth making the logic more complex - All Client-* golden files had to be regenerated because the ClientHello cipher suites have changed. (Even when Config.CipherSuites was limited to one suite, the TLS 1.3 default order changed.) Fixes #45430 Fixes #41476 (as 3DES is now always the last resort) Change-Id: If5f5d356c0f8d1f1c7542fb06644a478d6bad1e5 Reviewed-on: https://go-review.googlesource.com/c/go/+/314609 Run-TryBot: Filippo Valsorda <filippo@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Roland Shoemaker <roland@golang.org> Trust: Filippo Valsorda <filippo@golang.org>
2021-04-28 01:37:09 -04:00
hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13...)
} else {
hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13NoAES...)
}
if len(hello.supportedCurves) == 0 {
return nil, nil, nil, errors.New("tls: no supported elliptic curves for ECDHE")
}
curveID := hello.supportedCurves[0]
keyShareKeys = &keySharePrivateKeys{curveID: curveID}
// Note that if X25519MLKEM768 is supported, it will be first because
// the preference order is fixed.
if curveID == X25519MLKEM768 {
keyShareKeys.ecdhe, err = generateECDHEKey(config.rand(), X25519)
if err != nil {
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
return nil, nil, nil, err
}
seed := make([]byte, mlkem.SeedSize)
if _, err := io.ReadFull(config.rand(), seed); err != nil {
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
return nil, nil, nil, err
}
keyShareKeys.mlkem, err = mlkem.NewDecapsulationKey768(seed)
if err != nil {
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
return nil, nil, nil, err
}
mlkemEncapsulationKey := keyShareKeys.mlkem.EncapsulationKey().Bytes()
x25519EphemeralKey := keyShareKeys.ecdhe.PublicKey().Bytes()
hello.keyShares = []keyShare{
{group: X25519MLKEM768, data: append(mlkemEncapsulationKey, x25519EphemeralKey...)},
}
// If both X25519MLKEM768 and X25519 are supported, we send both key
// shares (as a fallback) and we reuse the same X25519 ephemeral
// key, as allowed by draft-ietf-tls-hybrid-design-09, Section 3.2.
if slices.Contains(hello.supportedCurves, X25519) {
hello.keyShares = append(hello.keyShares, keyShare{group: X25519, data: x25519EphemeralKey})
}
} else {
if _, ok := curveForCurveID(curveID); !ok {
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
return nil, nil, nil, errors.New("tls: CurvePreferences includes unsupported curve")
}
keyShareKeys.ecdhe, err = generateECDHEKey(config.rand(), curveID)
if err != nil {
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
return nil, nil, nil, err
}
hello.keyShares = []keyShare{{group: curveID, data: keyShareKeys.ecdhe.PublicKey().Bytes()}}
}
}
if c.quic != nil {
p, err := c.quicGetTransportParameters()
if err != nil {
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
return nil, nil, nil, err
}
if p == nil {
p = []byte{}
}
hello.quicTransportParameters = p
}
var ech *echClientContext
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
if c.config.EncryptedClientHelloConfigList != nil {
if c.config.MinVersion != 0 && c.config.MinVersion < VersionTLS13 {
return nil, nil, nil, errors.New("tls: MinVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated")
}
if c.config.MaxVersion != 0 && c.config.MaxVersion <= VersionTLS12 {
return nil, nil, nil, errors.New("tls: MaxVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated")
}
echConfigs, err := parseECHConfigList(c.config.EncryptedClientHelloConfigList)
if err != nil {
return nil, nil, nil, err
}
echConfig := pickECHConfig(echConfigs)
if echConfig == nil {
return nil, nil, nil, errors.New("tls: EncryptedClientHelloConfigList contains no valid configs")
}
ech = &echClientContext{config: echConfig}
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
hello.encryptedClientHello = []byte{1} // indicate inner hello
// We need to explicitly set these 1.2 fields to nil, as we do not
// marshal them when encoding the inner hello, otherwise transcripts
// will later mismatch.
hello.supportedPoints = nil
hello.ticketSupported = false
hello.secureRenegotiationSupported = false
hello.extendedMasterSecret = false
echPK, err := hpke.ParseHPKEPublicKey(ech.config.KemID, ech.config.PublicKey)
if err != nil {
return nil, nil, nil, err
}
suite, err := pickECHCipherSuite(ech.config.SymmetricCipherSuite)
if err != nil {
return nil, nil, nil, err
}
ech.kdfID, ech.aeadID = suite.KDFID, suite.AEADID
info := append([]byte("tls ech\x00"), ech.config.raw...)
ech.encapsulatedKey, ech.hpkeContext, err = hpke.SetupSender(ech.config.KemID, suite.KDFID, suite.AEADID, echPK, info)
if err != nil {
return nil, nil, nil, err
}
}
return hello, keyShareKeys, ech, nil
}
type echClientContext struct {
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
config *echConfig
hpkeContext *hpke.Sender
encapsulatedKey []byte
innerHello *clientHelloMsg
innerTranscript hash.Hash
kdfID uint16
aeadID uint16
echRejected bool
retryConfigs []byte
}
func (c *Conn) clientHandshake(ctx context.Context) (err error) {
if c.config == nil {
c.config = defaultConfig()
}
// This may be a renegotiation handshake, in which case some fields
// need to be reset.
c.didResume = false
c.curveID = 0
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
hello, keyShareKeys, ech, err := c.makeClientHello()
if err != nil {
return err
}
session, earlySecret, binderKey, err := c.loadSession(hello)
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
if err != nil {
return err
}
if session != nil {
defer func() {
// If we got a handshake failure when resuming a session, throw away
// the session ticket. See RFC 5077, Section 3.2.
//
// RFC 8446 makes no mention of dropping tickets on failure, but it
// does require servers to abort on invalid binders, so we need to
// delete tickets to recover from a corrupted PSK.
if err != nil {
if cacheKey := c.clientSessionCacheKey(); cacheKey != "" {
c.config.ClientSessionCache.Put(cacheKey, nil)
}
}
}()
}
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
if ech != nil {
// Split hello into inner and outer
ech.innerHello = hello.clone()
// Overwrite the server name in the outer hello with the public facing
// name.
hello.serverName = string(ech.config.PublicName)
// Generate a new random for the outer hello.
hello.random = make([]byte, 32)
_, err = io.ReadFull(c.config.rand(), hello.random)
if err != nil {
return errors.New("tls: short read from Rand: " + err.Error())
}
// NOTE: we don't do PSK GREASE, in line with boringssl, it's meant to
// work around _possibly_ broken middleboxes, but there is little-to-no
// evidence that this is actually a problem.
if err := computeAndUpdateOuterECHExtension(hello, ech.innerHello, ech, true); err != nil {
return err
}
}
c.serverName = hello.serverName
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
if _, err := c.writeHandshakeRecord(hello, nil); err != nil {
return err
}
if hello.earlyData {
suite := cipherSuiteTLS13ByID(session.cipherSuite)
transcript := suite.hash.New()
if err := transcriptMsg(hello, transcript); err != nil {
return err
}
earlyTrafficSecret := earlySecret.ClientEarlyTrafficSecret(transcript)
c.quicSetWriteSecret(QUICEncryptionLevelEarly, suite.id, earlyTrafficSecret)
}
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
// serverHelloMsg is not included in the transcript
msg, err := c.readHandshake(nil)
if err != nil {
return err
}
serverHello, ok := msg.(*serverHelloMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(serverHello, msg)
}
if err := c.pickTLSVersion(serverHello); err != nil {
return err
}
// If we are negotiating a protocol version that's lower than what we
// support, check for the server downgrade canaries.
// See RFC 8446, Section 4.1.3.
maxVers := c.config.maxSupportedVersion(roleClient)
tls12Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS12
tls11Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS11
if maxVers == VersionTLS13 && c.vers <= VersionTLS12 && (tls12Downgrade || tls11Downgrade) ||
maxVers == VersionTLS12 && c.vers <= VersionTLS11 && tls11Downgrade {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: downgrade attempt detected, possibly due to a MitM attack or a broken middlebox")
}
if c.vers == VersionTLS13 {
hs := &clientHandshakeStateTLS13{
c: c,
ctx: ctx,
serverHello: serverHello,
hello: hello,
keyShareKeys: keyShareKeys,
session: session,
earlySecret: earlySecret,
binderKey: binderKey,
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
echContext: ech,
}
return hs.handshake()
}
hs := &clientHandshakeState{
c: c,
ctx: ctx,
serverHello: serverHello,
hello: hello,
session: session,
}
return hs.handshake()
}
func (c *Conn) loadSession(hello *clientHelloMsg) (
session *SessionState, earlySecret *tls13.EarlySecret, binderKey []byte, err error) {
if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil {
return nil, nil, nil, nil
}
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
echInner := bytes.Equal(hello.encryptedClientHello, []byte{1})
// ticketSupported is a TLS 1.2 extension (as TLS 1.3 replaced tickets with PSK
// identities) and ECH requires and forces TLS 1.3.
hello.ticketSupported = true && !echInner
if hello.supportedVersions[0] == VersionTLS13 {
// Require DHE on resumption as it guarantees forward secrecy against
// compromise of the session ticket key. See RFC 8446, Section 4.2.9.
hello.pskModes = []uint8{pskModeDHE}
}
// Session resumption is not allowed if renegotiating because
// renegotiation is primarily used to allow a client to send a client
// certificate, which would be skipped if session resumption occurred.
if c.handshakes != 0 {
return nil, nil, nil, nil
}
// Try to resume a previously negotiated TLS session, if available.
cacheKey := c.clientSessionCacheKey()
if cacheKey == "" {
return nil, nil, nil, nil
}
cs, ok := c.config.ClientSessionCache.Get(cacheKey)
if !ok || cs == nil {
return nil, nil, nil, nil
}
session = cs.session
// Check that version used for the previous session is still valid.
versOk := false
for _, v := range hello.supportedVersions {
if v == session.version {
versOk = true
break
}
}
if !versOk {
return nil, nil, nil, nil
}
// Check that the cached server certificate is not expired, and that it's
// valid for the ServerName. This should be ensured by the cache key, but
// protect the application from a faulty ClientSessionCache implementation.
if c.config.time().After(session.peerCertificates[0].NotAfter) {
// Expired certificate, delete the entry.
c.config.ClientSessionCache.Put(cacheKey, nil)
return nil, nil, nil, nil
}
if !c.config.InsecureSkipVerify {
if len(session.verifiedChains) == 0 {
// The original connection had InsecureSkipVerify, while this doesn't.
return nil, nil, nil, nil
}
if err := session.peerCertificates[0].VerifyHostname(c.config.ServerName); err != nil {
return nil, nil, nil, nil
}
}
if session.version != VersionTLS13 {
// In TLS 1.2 the cipher suite must match the resumed session. Ensure we
// are still offering it.
if mutualCipherSuite(hello.cipherSuites, session.cipherSuite) == nil {
return nil, nil, nil, nil
}
// FIPS 140-3 requires the use of Extended Master Secret.
if !session.extMasterSecret && fips140tls.Required() {
return nil, nil, nil, nil
}
hello.sessionTicket = session.ticket
return
}
// Check that the session ticket is not expired.
if c.config.time().After(time.Unix(int64(session.useBy), 0)) {
c.config.ClientSessionCache.Put(cacheKey, nil)
return nil, nil, nil, nil
}
// In TLS 1.3 the KDF hash must match the resumed session. Ensure we
// offer at least one cipher suite with that hash.
cipherSuite := cipherSuiteTLS13ByID(session.cipherSuite)
if cipherSuite == nil {
return nil, nil, nil, nil
}
cipherSuiteOk := false
for _, offeredID := range hello.cipherSuites {
offeredSuite := cipherSuiteTLS13ByID(offeredID)
if offeredSuite != nil && offeredSuite.hash == cipherSuite.hash {
cipherSuiteOk = true
break
}
}
if !cipherSuiteOk {
return nil, nil, nil, nil
}
if c.quic != nil {
if c.quic.enableSessionEvents {
c.quicResumeSession(session)
}
// For 0-RTT, the cipher suite has to match exactly, and we need to be
// offering the same ALPN.
if session.EarlyData && mutualCipherSuiteTLS13(hello.cipherSuites, session.cipherSuite) != nil {
for _, alpn := range hello.alpnProtocols {
if alpn == session.alpnProtocol {
hello.earlyData = true
break
}
}
}
}
// Set the pre_shared_key extension. See RFC 8446, Section 4.2.11.1.
ticketAge := c.config.time().Sub(time.Unix(int64(session.createdAt), 0))
identity := pskIdentity{
label: session.ticket,
obfuscatedTicketAge: uint32(ticketAge/time.Millisecond) + session.ageAdd,
}
hello.pskIdentities = []pskIdentity{identity}
hello.pskBinders = [][]byte{make([]byte, cipherSuite.hash.Size())}
// Compute the PSK binders. See RFC 8446, Section 4.2.11.2.
earlySecret = tls13.NewEarlySecret(cipherSuite.hash.New, session.secret)
binderKey = earlySecret.ResumptionBinderKey()
transcript := cipherSuite.hash.New()
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
if err := computeAndUpdatePSK(hello, binderKey, transcript, cipherSuite.finishedHash); err != nil {
return nil, nil, nil, err
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
}
return
}
func (c *Conn) pickTLSVersion(serverHello *serverHelloMsg) error {
peerVersion := serverHello.vers
if serverHello.supportedVersion != 0 {
peerVersion = serverHello.supportedVersion
}
vers, ok := c.config.mutualVersion(roleClient, []uint16{peerVersion})
if !ok {
c.sendAlert(alertProtocolVersion)
return fmt.Errorf("tls: server selected unsupported protocol version %x", peerVersion)
}
c.vers = vers
c.haveVers = true
c.in.version = vers
c.out.version = vers
return nil
}
// Does the handshake, either a full one or resumes old session. Requires hs.c,
// hs.hello, hs.serverHello, and, optionally, hs.session to be set.
func (hs *clientHandshakeState) handshake() error {
c := hs.c
// If we did not load a session (hs.session == nil), but we did set a
// session ID in the transmitted client hello (hs.hello.sessionId != nil),
// it means we tried to negotiate TLS 1.3 and sent a random session ID as a
// compatibility measure (see RFC 8446, Section 4.1.2).
//
// Since we're now handshaking for TLS 1.2, if the server echoed the
// transmitted ID back to us, we know mischief is afoot: the session ID
// was random and can't possibly be recognized by the server.
if hs.session == nil && hs.hello.sessionId != nil && bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server echoed TLS 1.3 compatibility session ID in TLS 1.2")
}
isResume, err := hs.processServerHello()
if err != nil {
return err
}
hs.finishedHash = newFinishedHash(c.vers, hs.suite)
crypto/tls: decouple handshake signatures from the handshake hash. Prior to TLS 1.2, the handshake had a pleasing property that one could incrementally hash it and, from that, get the needed hashes for both the CertificateVerify and Finished messages. TLS 1.2 introduced negotiation for the signature and hash and it became possible for the handshake hash to be, say, SHA-384, but for the CertificateVerify to sign the handshake with SHA-1. The problem is that one doesn't know in advance which hashes will be needed and thus the handshake needs to be buffered. Go ignored this, always kept a single handshake hash, and any signatures over the handshake had to use that hash. However, there are a set of servers that inspect the client's offered signature hash functions and will abort the handshake if one of the server's certificates is signed with a hash function outside of that set. https://robertsspaceindustries.com/ is an example of such a server. Clearly not a lot of thought happened when that server code was written, but its out there and we have to deal with it. This change decouples the handshake hash from the CertificateVerify hash. This lays the groundwork for advertising support for SHA-384 but doesn't actually make that change in the interests of reviewability. Updating the advertised hash functions will cause changes in many of the testdata/ files and some errors might get lost in the noise. This change only needs to update four testdata/ files: one because a SHA-384-based handshake is now being signed with SHA-256 and the others because the TLS 1.2 CertificateRequest message now includes SHA-1. This change also has the effect of adding support for client-certificates in SSLv3 servers. However, SSLv3 is now disabled by default so this should be moot. It would be possible to avoid much of this change and just support SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces and SKX params (a design mistake in TLS). However, that would leave Go in the odd situation where it advertised support for SHA-384, but would only use the handshake hash when signing client certificates. I fear that'll just cause problems in the future. Much of this code was written by davidben@ for the purposes of testing BoringSSL. Partly addresses #9757 Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485 Reviewed-on: https://go-review.googlesource.com/9415 Run-TryBot: Adam Langley <agl@golang.org> Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 09:13:38 -07:00
// No signatures of the handshake are needed in a resumption.
// Otherwise, in a full handshake, if we don't have any certificates
// configured then we will never send a CertificateVerify message and
// thus no signatures are needed in that case either.
if isResume || (len(c.config.Certificates) == 0 && c.config.GetClientCertificate == nil) {
crypto/tls: decouple handshake signatures from the handshake hash. Prior to TLS 1.2, the handshake had a pleasing property that one could incrementally hash it and, from that, get the needed hashes for both the CertificateVerify and Finished messages. TLS 1.2 introduced negotiation for the signature and hash and it became possible for the handshake hash to be, say, SHA-384, but for the CertificateVerify to sign the handshake with SHA-1. The problem is that one doesn't know in advance which hashes will be needed and thus the handshake needs to be buffered. Go ignored this, always kept a single handshake hash, and any signatures over the handshake had to use that hash. However, there are a set of servers that inspect the client's offered signature hash functions and will abort the handshake if one of the server's certificates is signed with a hash function outside of that set. https://robertsspaceindustries.com/ is an example of such a server. Clearly not a lot of thought happened when that server code was written, but its out there and we have to deal with it. This change decouples the handshake hash from the CertificateVerify hash. This lays the groundwork for advertising support for SHA-384 but doesn't actually make that change in the interests of reviewability. Updating the advertised hash functions will cause changes in many of the testdata/ files and some errors might get lost in the noise. This change only needs to update four testdata/ files: one because a SHA-384-based handshake is now being signed with SHA-256 and the others because the TLS 1.2 CertificateRequest message now includes SHA-1. This change also has the effect of adding support for client-certificates in SSLv3 servers. However, SSLv3 is now disabled by default so this should be moot. It would be possible to avoid much of this change and just support SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces and SKX params (a design mistake in TLS). However, that would leave Go in the odd situation where it advertised support for SHA-384, but would only use the handshake hash when signing client certificates. I fear that'll just cause problems in the future. Much of this code was written by davidben@ for the purposes of testing BoringSSL. Partly addresses #9757 Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485 Reviewed-on: https://go-review.googlesource.com/9415 Run-TryBot: Adam Langley <agl@golang.org> Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 09:13:38 -07:00
hs.finishedHash.discardHandshakeBuffer()
}
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
if err := transcriptMsg(hs.hello, &hs.finishedHash); err != nil {
return err
}
if err := transcriptMsg(hs.serverHello, &hs.finishedHash); err != nil {
return err
}
crypto/tls: decouple handshake signatures from the handshake hash. Prior to TLS 1.2, the handshake had a pleasing property that one could incrementally hash it and, from that, get the needed hashes for both the CertificateVerify and Finished messages. TLS 1.2 introduced negotiation for the signature and hash and it became possible for the handshake hash to be, say, SHA-384, but for the CertificateVerify to sign the handshake with SHA-1. The problem is that one doesn't know in advance which hashes will be needed and thus the handshake needs to be buffered. Go ignored this, always kept a single handshake hash, and any signatures over the handshake had to use that hash. However, there are a set of servers that inspect the client's offered signature hash functions and will abort the handshake if one of the server's certificates is signed with a hash function outside of that set. https://robertsspaceindustries.com/ is an example of such a server. Clearly not a lot of thought happened when that server code was written, but its out there and we have to deal with it. This change decouples the handshake hash from the CertificateVerify hash. This lays the groundwork for advertising support for SHA-384 but doesn't actually make that change in the interests of reviewability. Updating the advertised hash functions will cause changes in many of the testdata/ files and some errors might get lost in the noise. This change only needs to update four testdata/ files: one because a SHA-384-based handshake is now being signed with SHA-256 and the others because the TLS 1.2 CertificateRequest message now includes SHA-1. This change also has the effect of adding support for client-certificates in SSLv3 servers. However, SSLv3 is now disabled by default so this should be moot. It would be possible to avoid much of this change and just support SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces and SKX params (a design mistake in TLS). However, that would leave Go in the odd situation where it advertised support for SHA-384, but would only use the handshake hash when signing client certificates. I fear that'll just cause problems in the future. Much of this code was written by davidben@ for the purposes of testing BoringSSL. Partly addresses #9757 Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485 Reviewed-on: https://go-review.googlesource.com/9415 Run-TryBot: Adam Langley <agl@golang.org> Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 09:13:38 -07:00
c.buffering = true
c.didResume = isResume
if isResume {
if err := hs.establishKeys(); err != nil {
return err
}
if err := hs.readSessionTicket(); err != nil {
return err
}
if err := hs.readFinished(c.serverFinished[:]); err != nil {
return err
}
c.clientFinishedIsFirst = false
// Make sure the connection is still being verified whether or not this
// is a resumption. Resumptions currently don't reverify certificates so
// they don't call verifyServerCertificate. See Issue 31641.
if c.config.VerifyConnection != nil {
if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
c.sendAlert(alertBadCertificate)
return err
}
}
if err := hs.sendFinished(c.clientFinished[:]); err != nil {
return err
}
if _, err := c.flush(); err != nil {
return err
}
} else {
if err := hs.doFullHandshake(); err != nil {
return err
}
if err := hs.establishKeys(); err != nil {
return err
}
if err := hs.sendFinished(c.clientFinished[:]); err != nil {
return err
}
if _, err := c.flush(); err != nil {
return err
}
c.clientFinishedIsFirst = true
if err := hs.readSessionTicket(); err != nil {
return err
}
if err := hs.readFinished(c.serverFinished[:]); err != nil {
return err
}
}
if err := hs.saveSessionTicket(); err != nil {
return err
}
c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random)
c.isHandshakeComplete.Store(true)
return nil
}
func (hs *clientHandshakeState) pickCipherSuite() error {
if hs.suite = mutualCipherSuite(hs.hello.cipherSuites, hs.serverHello.cipherSuite); hs.suite == nil {
hs.c.sendAlert(alertHandshakeFailure)
return errors.New("tls: server chose an unconfigured cipher suite")
}
if hs.c.config.CipherSuites == nil && !fips140tls.Required() && rsaKexCiphers[hs.suite.id] {
tlsrsakex.Value() // ensure godebug is initialized
tlsrsakex.IncNonDefault()
}
if hs.c.config.CipherSuites == nil && !fips140tls.Required() && tdesCiphers[hs.suite.id] {
tls3des.Value() // ensure godebug is initialized
tls3des.IncNonDefault()
}
hs.c.cipherSuite = hs.suite.id
return nil
}
func (hs *clientHandshakeState) doFullHandshake() error {
c := hs.c
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
msg, err := c.readHandshake(&hs.finishedHash)
if err != nil {
return err
}
certMsg, ok := msg.(*certificateMsg)
if !ok || len(certMsg.certificates) == 0 {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(certMsg, msg)
}
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
msg, err = c.readHandshake(&hs.finishedHash)
if err != nil {
return err
}
cs, ok := msg.(*certificateStatusMsg)
if ok {
// RFC4366 on Certificate Status Request:
// The server MAY return a "certificate_status" message.
if !hs.serverHello.ocspStapling {
// If a server returns a "CertificateStatus" message, then the
// server MUST have included an extension of type "status_request"
// with empty "extension_data" in the extended server hello.
c.sendAlert(alertUnexpectedMessage)
return errors.New("tls: received unexpected CertificateStatus message")
}
c.ocspResponse = cs.response
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
msg, err = c.readHandshake(&hs.finishedHash)
if err != nil {
return err
}
}
if c.handshakes == 0 {
// If this is the first handshake on a connection, process and
// (optionally) verify the server's certificates.
if err := c.verifyServerCertificate(certMsg.certificates); err != nil {
return err
}
} else {
// This is a renegotiation handshake. We require that the
// server's identity (i.e. leaf certificate) is unchanged and
// thus any previous trust decision is still valid.
//
// See https://mitls.org/pages/attacks/3SHAKE for the
// motivation behind this requirement.
if !bytes.Equal(c.peerCertificates[0].Raw, certMsg.certificates[0]) {
c.sendAlert(alertBadCertificate)
return errors.New("tls: server's identity changed during renegotiation")
}
}
keyAgreement := hs.suite.ka(c.vers)
skx, ok := msg.(*serverKeyExchangeMsg)
if ok {
err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, c.peerCertificates[0], skx)
if err != nil {
c.sendAlert(alertIllegalParameter)
return err
}
if len(skx.key) >= 3 && skx.key[0] == 3 /* named curve */ {
c.curveID = CurveID(byteorder.BEUint16(skx.key[1:]))
}
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
msg, err = c.readHandshake(&hs.finishedHash)
if err != nil {
return err
}
}
var chainToSend *Certificate
var certRequested bool
certReq, ok := msg.(*certificateRequestMsg)
if ok {
certRequested = true
cri := certificateRequestInfoFromMsg(hs.ctx, c.vers, certReq)
if chainToSend, err = c.getClientCertificate(cri); err != nil {
c.sendAlert(alertInternalError)
return err
}
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
msg, err = c.readHandshake(&hs.finishedHash)
if err != nil {
return err
}
}
shd, ok := msg.(*serverHelloDoneMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(shd, msg)
}
// If the server requested a certificate then we have to send a
// Certificate message, even if it's empty because we don't have a
// certificate to send.
if certRequested {
certMsg = new(certificateMsg)
certMsg.certificates = chainToSend.Certificate
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
if _, err := hs.c.writeHandshakeRecord(certMsg, &hs.finishedHash); err != nil {
return err
}
}
preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, c.peerCertificates[0])
if err != nil {
c.sendAlert(alertInternalError)
return err
}
if ckx != nil {
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
if _, err := hs.c.writeHandshakeRecord(ckx, &hs.finishedHash); err != nil {
return err
}
}
if hs.serverHello.extendedMasterSecret {
c.extMasterSecret = true
hs.masterSecret = extMasterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret,
hs.finishedHash.Sum())
} else {
if fips140tls.Required() {
c.sendAlert(alertHandshakeFailure)
return errors.New("tls: FIPS 140-3 requires the use of Extended Master Secret")
}
hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret,
hs.hello.random, hs.serverHello.random)
}
if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.hello.random, hs.masterSecret); err != nil {
c.sendAlert(alertInternalError)
return errors.New("tls: failed to write to key log: " + err.Error())
}
if chainToSend != nil && len(chainToSend.Certificate) > 0 {
crypto/tls: refactor certificate and signature algorithm logic This refactors a lot of the certificate support logic to make it cleaner and reusable where possible. These changes will make the following CLs much simpler. In particular, the heavily overloaded pickSignatureAlgorithm is gone. That function used to cover both signing and verifying side, would work both for pre-signature_algorithms TLS 1.0/1.1 and TLS 1.2, and returned sigalg, type and hash. Now, TLS 1.0/1.1 and 1.2 are differentiated at the caller, as they have effectively completely different logic. TLS 1.0/1.1 simply use legacyTypeAndHashFromPublicKey as they employ a fixed hash function and signature algorithm for each public key type. TLS 1.2 is instead routed through selectSignatureScheme (on the signing side) or isSupportedSignatureAlgorithm (on the verifying side) and typeAndHashFromSignatureScheme, like TLS 1.3. On the signing side, signatureSchemesForCertificate was already version aware (for PKCS#1 v1.5 vs PSS support), so selectSignatureScheme just had to learn the Section 7.4.1.4.1 defaults for a missing signature_algorithms to replace pickSignatureAlgorithm. On the verifying side, pickSignatureAlgorithm was also checking the public key type, while isSupportedSignatureAlgorithm + typeAndHashFromSignatureScheme are not, but that check was redundant with the one in verifyHandshakeSignature. There should be no major change in behavior so far. A few minor changes came from the refactor: we now correctly require signature_algorithms in TLS 1.3 when using a certificate; we won't use Ed25519 in TLS 1.2 if the client didn't send signature_algorithms; and we don't send ec_points_format in the ServerHello (a compatibility measure) if we are not doing ECDHE anyway because there are no mutually supported curves. The tests also got simpler because they test simpler functions. The caller logic switching between TLS 1.0/1.1 and 1.2 is tested by the transcript tests. Updates #32426 Change-Id: Ice9dcaea78d204718f661f8d60efdb408ba41577 Reviewed-on: https://go-review.googlesource.com/c/go/+/205061 Reviewed-by: Katie Hockman <katie@golang.org>
2019-11-01 19:00:33 -04:00
certVerify := &certificateVerifyMsg{}
key, ok := chainToSend.PrivateKey.(crypto.Signer)
if !ok {
c.sendAlert(alertInternalError)
return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", chainToSend.PrivateKey)
}
crypto/tls: decouple handshake signatures from the handshake hash. Prior to TLS 1.2, the handshake had a pleasing property that one could incrementally hash it and, from that, get the needed hashes for both the CertificateVerify and Finished messages. TLS 1.2 introduced negotiation for the signature and hash and it became possible for the handshake hash to be, say, SHA-384, but for the CertificateVerify to sign the handshake with SHA-1. The problem is that one doesn't know in advance which hashes will be needed and thus the handshake needs to be buffered. Go ignored this, always kept a single handshake hash, and any signatures over the handshake had to use that hash. However, there are a set of servers that inspect the client's offered signature hash functions and will abort the handshake if one of the server's certificates is signed with a hash function outside of that set. https://robertsspaceindustries.com/ is an example of such a server. Clearly not a lot of thought happened when that server code was written, but its out there and we have to deal with it. This change decouples the handshake hash from the CertificateVerify hash. This lays the groundwork for advertising support for SHA-384 but doesn't actually make that change in the interests of reviewability. Updating the advertised hash functions will cause changes in many of the testdata/ files and some errors might get lost in the noise. This change only needs to update four testdata/ files: one because a SHA-384-based handshake is now being signed with SHA-256 and the others because the TLS 1.2 CertificateRequest message now includes SHA-1. This change also has the effect of adding support for client-certificates in SSLv3 servers. However, SSLv3 is now disabled by default so this should be moot. It would be possible to avoid much of this change and just support SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces and SKX params (a design mistake in TLS). However, that would leave Go in the odd situation where it advertised support for SHA-384, but would only use the handshake hash when signing client certificates. I fear that'll just cause problems in the future. Much of this code was written by davidben@ for the purposes of testing BoringSSL. Partly addresses #9757 Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485 Reviewed-on: https://go-review.googlesource.com/9415 Run-TryBot: Adam Langley <agl@golang.org> Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 09:13:38 -07:00
crypto/tls: refactor certificate and signature algorithm logic This refactors a lot of the certificate support logic to make it cleaner and reusable where possible. These changes will make the following CLs much simpler. In particular, the heavily overloaded pickSignatureAlgorithm is gone. That function used to cover both signing and verifying side, would work both for pre-signature_algorithms TLS 1.0/1.1 and TLS 1.2, and returned sigalg, type and hash. Now, TLS 1.0/1.1 and 1.2 are differentiated at the caller, as they have effectively completely different logic. TLS 1.0/1.1 simply use legacyTypeAndHashFromPublicKey as they employ a fixed hash function and signature algorithm for each public key type. TLS 1.2 is instead routed through selectSignatureScheme (on the signing side) or isSupportedSignatureAlgorithm (on the verifying side) and typeAndHashFromSignatureScheme, like TLS 1.3. On the signing side, signatureSchemesForCertificate was already version aware (for PKCS#1 v1.5 vs PSS support), so selectSignatureScheme just had to learn the Section 7.4.1.4.1 defaults for a missing signature_algorithms to replace pickSignatureAlgorithm. On the verifying side, pickSignatureAlgorithm was also checking the public key type, while isSupportedSignatureAlgorithm + typeAndHashFromSignatureScheme are not, but that check was redundant with the one in verifyHandshakeSignature. There should be no major change in behavior so far. A few minor changes came from the refactor: we now correctly require signature_algorithms in TLS 1.3 when using a certificate; we won't use Ed25519 in TLS 1.2 if the client didn't send signature_algorithms; and we don't send ec_points_format in the ServerHello (a compatibility measure) if we are not doing ECDHE anyway because there are no mutually supported curves. The tests also got simpler because they test simpler functions. The caller logic switching between TLS 1.0/1.1 and 1.2 is tested by the transcript tests. Updates #32426 Change-Id: Ice9dcaea78d204718f661f8d60efdb408ba41577 Reviewed-on: https://go-review.googlesource.com/c/go/+/205061 Reviewed-by: Katie Hockman <katie@golang.org>
2019-11-01 19:00:33 -04:00
var sigType uint8
var sigHash crypto.Hash
if c.vers >= VersionTLS12 {
signatureAlgorithm, err := selectSignatureScheme(c.vers, chainToSend, certReq.supportedSignatureAlgorithms)
if err != nil {
c.sendAlert(alertIllegalParameter)
return err
}
sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
if err != nil {
return c.sendAlert(alertInternalError)
}
certVerify.hasSignatureAlgorithm = true
certVerify.signatureAlgorithm = signatureAlgorithm
crypto/tls: refactor certificate and signature algorithm logic This refactors a lot of the certificate support logic to make it cleaner and reusable where possible. These changes will make the following CLs much simpler. In particular, the heavily overloaded pickSignatureAlgorithm is gone. That function used to cover both signing and verifying side, would work both for pre-signature_algorithms TLS 1.0/1.1 and TLS 1.2, and returned sigalg, type and hash. Now, TLS 1.0/1.1 and 1.2 are differentiated at the caller, as they have effectively completely different logic. TLS 1.0/1.1 simply use legacyTypeAndHashFromPublicKey as they employ a fixed hash function and signature algorithm for each public key type. TLS 1.2 is instead routed through selectSignatureScheme (on the signing side) or isSupportedSignatureAlgorithm (on the verifying side) and typeAndHashFromSignatureScheme, like TLS 1.3. On the signing side, signatureSchemesForCertificate was already version aware (for PKCS#1 v1.5 vs PSS support), so selectSignatureScheme just had to learn the Section 7.4.1.4.1 defaults for a missing signature_algorithms to replace pickSignatureAlgorithm. On the verifying side, pickSignatureAlgorithm was also checking the public key type, while isSupportedSignatureAlgorithm + typeAndHashFromSignatureScheme are not, but that check was redundant with the one in verifyHandshakeSignature. There should be no major change in behavior so far. A few minor changes came from the refactor: we now correctly require signature_algorithms in TLS 1.3 when using a certificate; we won't use Ed25519 in TLS 1.2 if the client didn't send signature_algorithms; and we don't send ec_points_format in the ServerHello (a compatibility measure) if we are not doing ECDHE anyway because there are no mutually supported curves. The tests also got simpler because they test simpler functions. The caller logic switching between TLS 1.0/1.1 and 1.2 is tested by the transcript tests. Updates #32426 Change-Id: Ice9dcaea78d204718f661f8d60efdb408ba41577 Reviewed-on: https://go-review.googlesource.com/c/go/+/205061 Reviewed-by: Katie Hockman <katie@golang.org>
2019-11-01 19:00:33 -04:00
} else {
sigType, sigHash, err = legacyTypeAndHashFromPublicKey(key.Public())
if err != nil {
c.sendAlert(alertIllegalParameter)
return err
}
}
crypto/tls: refactor certificate and signature algorithm logic This refactors a lot of the certificate support logic to make it cleaner and reusable where possible. These changes will make the following CLs much simpler. In particular, the heavily overloaded pickSignatureAlgorithm is gone. That function used to cover both signing and verifying side, would work both for pre-signature_algorithms TLS 1.0/1.1 and TLS 1.2, and returned sigalg, type and hash. Now, TLS 1.0/1.1 and 1.2 are differentiated at the caller, as they have effectively completely different logic. TLS 1.0/1.1 simply use legacyTypeAndHashFromPublicKey as they employ a fixed hash function and signature algorithm for each public key type. TLS 1.2 is instead routed through selectSignatureScheme (on the signing side) or isSupportedSignatureAlgorithm (on the verifying side) and typeAndHashFromSignatureScheme, like TLS 1.3. On the signing side, signatureSchemesForCertificate was already version aware (for PKCS#1 v1.5 vs PSS support), so selectSignatureScheme just had to learn the Section 7.4.1.4.1 defaults for a missing signature_algorithms to replace pickSignatureAlgorithm. On the verifying side, pickSignatureAlgorithm was also checking the public key type, while isSupportedSignatureAlgorithm + typeAndHashFromSignatureScheme are not, but that check was redundant with the one in verifyHandshakeSignature. There should be no major change in behavior so far. A few minor changes came from the refactor: we now correctly require signature_algorithms in TLS 1.3 when using a certificate; we won't use Ed25519 in TLS 1.2 if the client didn't send signature_algorithms; and we don't send ec_points_format in the ServerHello (a compatibility measure) if we are not doing ECDHE anyway because there are no mutually supported curves. The tests also got simpler because they test simpler functions. The caller logic switching between TLS 1.0/1.1 and 1.2 is tested by the transcript tests. Updates #32426 Change-Id: Ice9dcaea78d204718f661f8d60efdb408ba41577 Reviewed-on: https://go-review.googlesource.com/c/go/+/205061 Reviewed-by: Katie Hockman <katie@golang.org>
2019-11-01 19:00:33 -04:00
signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash)
crypto/tls: refactor certificate and signature algorithm logic This refactors a lot of the certificate support logic to make it cleaner and reusable where possible. These changes will make the following CLs much simpler. In particular, the heavily overloaded pickSignatureAlgorithm is gone. That function used to cover both signing and verifying side, would work both for pre-signature_algorithms TLS 1.0/1.1 and TLS 1.2, and returned sigalg, type and hash. Now, TLS 1.0/1.1 and 1.2 are differentiated at the caller, as they have effectively completely different logic. TLS 1.0/1.1 simply use legacyTypeAndHashFromPublicKey as they employ a fixed hash function and signature algorithm for each public key type. TLS 1.2 is instead routed through selectSignatureScheme (on the signing side) or isSupportedSignatureAlgorithm (on the verifying side) and typeAndHashFromSignatureScheme, like TLS 1.3. On the signing side, signatureSchemesForCertificate was already version aware (for PKCS#1 v1.5 vs PSS support), so selectSignatureScheme just had to learn the Section 7.4.1.4.1 defaults for a missing signature_algorithms to replace pickSignatureAlgorithm. On the verifying side, pickSignatureAlgorithm was also checking the public key type, while isSupportedSignatureAlgorithm + typeAndHashFromSignatureScheme are not, but that check was redundant with the one in verifyHandshakeSignature. There should be no major change in behavior so far. A few minor changes came from the refactor: we now correctly require signature_algorithms in TLS 1.3 when using a certificate; we won't use Ed25519 in TLS 1.2 if the client didn't send signature_algorithms; and we don't send ec_points_format in the ServerHello (a compatibility measure) if we are not doing ECDHE anyway because there are no mutually supported curves. The tests also got simpler because they test simpler functions. The caller logic switching between TLS 1.0/1.1 and 1.2 is tested by the transcript tests. Updates #32426 Change-Id: Ice9dcaea78d204718f661f8d60efdb408ba41577 Reviewed-on: https://go-review.googlesource.com/c/go/+/205061 Reviewed-by: Katie Hockman <katie@golang.org>
2019-11-01 19:00:33 -04:00
signOpts := crypto.SignerOpts(sigHash)
if sigType == signatureRSAPSS {
crypto/tls: refactor certificate and signature algorithm logic This refactors a lot of the certificate support logic to make it cleaner and reusable where possible. These changes will make the following CLs much simpler. In particular, the heavily overloaded pickSignatureAlgorithm is gone. That function used to cover both signing and verifying side, would work both for pre-signature_algorithms TLS 1.0/1.1 and TLS 1.2, and returned sigalg, type and hash. Now, TLS 1.0/1.1 and 1.2 are differentiated at the caller, as they have effectively completely different logic. TLS 1.0/1.1 simply use legacyTypeAndHashFromPublicKey as they employ a fixed hash function and signature algorithm for each public key type. TLS 1.2 is instead routed through selectSignatureScheme (on the signing side) or isSupportedSignatureAlgorithm (on the verifying side) and typeAndHashFromSignatureScheme, like TLS 1.3. On the signing side, signatureSchemesForCertificate was already version aware (for PKCS#1 v1.5 vs PSS support), so selectSignatureScheme just had to learn the Section 7.4.1.4.1 defaults for a missing signature_algorithms to replace pickSignatureAlgorithm. On the verifying side, pickSignatureAlgorithm was also checking the public key type, while isSupportedSignatureAlgorithm + typeAndHashFromSignatureScheme are not, but that check was redundant with the one in verifyHandshakeSignature. There should be no major change in behavior so far. A few minor changes came from the refactor: we now correctly require signature_algorithms in TLS 1.3 when using a certificate; we won't use Ed25519 in TLS 1.2 if the client didn't send signature_algorithms; and we don't send ec_points_format in the ServerHello (a compatibility measure) if we are not doing ECDHE anyway because there are no mutually supported curves. The tests also got simpler because they test simpler functions. The caller logic switching between TLS 1.0/1.1 and 1.2 is tested by the transcript tests. Updates #32426 Change-Id: Ice9dcaea78d204718f661f8d60efdb408ba41577 Reviewed-on: https://go-review.googlesource.com/c/go/+/205061 Reviewed-by: Katie Hockman <katie@golang.org>
2019-11-01 19:00:33 -04:00
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
}
certVerify.signature, err = key.Sign(c.config.rand(), signed, signOpts)
crypto/tls: decouple handshake signatures from the handshake hash. Prior to TLS 1.2, the handshake had a pleasing property that one could incrementally hash it and, from that, get the needed hashes for both the CertificateVerify and Finished messages. TLS 1.2 introduced negotiation for the signature and hash and it became possible for the handshake hash to be, say, SHA-384, but for the CertificateVerify to sign the handshake with SHA-1. The problem is that one doesn't know in advance which hashes will be needed and thus the handshake needs to be buffered. Go ignored this, always kept a single handshake hash, and any signatures over the handshake had to use that hash. However, there are a set of servers that inspect the client's offered signature hash functions and will abort the handshake if one of the server's certificates is signed with a hash function outside of that set. https://robertsspaceindustries.com/ is an example of such a server. Clearly not a lot of thought happened when that server code was written, but its out there and we have to deal with it. This change decouples the handshake hash from the CertificateVerify hash. This lays the groundwork for advertising support for SHA-384 but doesn't actually make that change in the interests of reviewability. Updating the advertised hash functions will cause changes in many of the testdata/ files and some errors might get lost in the noise. This change only needs to update four testdata/ files: one because a SHA-384-based handshake is now being signed with SHA-256 and the others because the TLS 1.2 CertificateRequest message now includes SHA-1. This change also has the effect of adding support for client-certificates in SSLv3 servers. However, SSLv3 is now disabled by default so this should be moot. It would be possible to avoid much of this change and just support SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces and SKX params (a design mistake in TLS). However, that would leave Go in the odd situation where it advertised support for SHA-384, but would only use the handshake hash when signing client certificates. I fear that'll just cause problems in the future. Much of this code was written by davidben@ for the purposes of testing BoringSSL. Partly addresses #9757 Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485 Reviewed-on: https://go-review.googlesource.com/9415 Run-TryBot: Adam Langley <agl@golang.org> Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 09:13:38 -07:00
if err != nil {
c.sendAlert(alertInternalError)
return err
}
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
if _, err := hs.c.writeHandshakeRecord(certVerify, &hs.finishedHash); err != nil {
return err
}
}
crypto/tls: decouple handshake signatures from the handshake hash. Prior to TLS 1.2, the handshake had a pleasing property that one could incrementally hash it and, from that, get the needed hashes for both the CertificateVerify and Finished messages. TLS 1.2 introduced negotiation for the signature and hash and it became possible for the handshake hash to be, say, SHA-384, but for the CertificateVerify to sign the handshake with SHA-1. The problem is that one doesn't know in advance which hashes will be needed and thus the handshake needs to be buffered. Go ignored this, always kept a single handshake hash, and any signatures over the handshake had to use that hash. However, there are a set of servers that inspect the client's offered signature hash functions and will abort the handshake if one of the server's certificates is signed with a hash function outside of that set. https://robertsspaceindustries.com/ is an example of such a server. Clearly not a lot of thought happened when that server code was written, but its out there and we have to deal with it. This change decouples the handshake hash from the CertificateVerify hash. This lays the groundwork for advertising support for SHA-384 but doesn't actually make that change in the interests of reviewability. Updating the advertised hash functions will cause changes in many of the testdata/ files and some errors might get lost in the noise. This change only needs to update four testdata/ files: one because a SHA-384-based handshake is now being signed with SHA-256 and the others because the TLS 1.2 CertificateRequest message now includes SHA-1. This change also has the effect of adding support for client-certificates in SSLv3 servers. However, SSLv3 is now disabled by default so this should be moot. It would be possible to avoid much of this change and just support SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces and SKX params (a design mistake in TLS). However, that would leave Go in the odd situation where it advertised support for SHA-384, but would only use the handshake hash when signing client certificates. I fear that'll just cause problems in the future. Much of this code was written by davidben@ for the purposes of testing BoringSSL. Partly addresses #9757 Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485 Reviewed-on: https://go-review.googlesource.com/9415 Run-TryBot: Adam Langley <agl@golang.org> Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 09:13:38 -07:00
hs.finishedHash.discardHandshakeBuffer()
return nil
}
func (hs *clientHandshakeState) establishKeys() error {
c := hs.c
clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
crypto/tls: decouple handshake signatures from the handshake hash. Prior to TLS 1.2, the handshake had a pleasing property that one could incrementally hash it and, from that, get the needed hashes for both the CertificateVerify and Finished messages. TLS 1.2 introduced negotiation for the signature and hash and it became possible for the handshake hash to be, say, SHA-384, but for the CertificateVerify to sign the handshake with SHA-1. The problem is that one doesn't know in advance which hashes will be needed and thus the handshake needs to be buffered. Go ignored this, always kept a single handshake hash, and any signatures over the handshake had to use that hash. However, there are a set of servers that inspect the client's offered signature hash functions and will abort the handshake if one of the server's certificates is signed with a hash function outside of that set. https://robertsspaceindustries.com/ is an example of such a server. Clearly not a lot of thought happened when that server code was written, but its out there and we have to deal with it. This change decouples the handshake hash from the CertificateVerify hash. This lays the groundwork for advertising support for SHA-384 but doesn't actually make that change in the interests of reviewability. Updating the advertised hash functions will cause changes in many of the testdata/ files and some errors might get lost in the noise. This change only needs to update four testdata/ files: one because a SHA-384-based handshake is now being signed with SHA-256 and the others because the TLS 1.2 CertificateRequest message now includes SHA-1. This change also has the effect of adding support for client-certificates in SSLv3 servers. However, SSLv3 is now disabled by default so this should be moot. It would be possible to avoid much of this change and just support SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces and SKX params (a design mistake in TLS). However, that would leave Go in the odd situation where it advertised support for SHA-384, but would only use the handshake hash when signing client certificates. I fear that'll just cause problems in the future. Much of this code was written by davidben@ for the purposes of testing BoringSSL. Partly addresses #9757 Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485 Reviewed-on: https://go-review.googlesource.com/9415 Run-TryBot: Adam Langley <agl@golang.org> Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 09:13:38 -07:00
keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
var clientCipher, serverCipher any
var clientHash, serverHash hash.Hash
if hs.suite.cipher != nil {
clientCipher = hs.suite.cipher(clientKey, clientIV, false /* not for reading */)
clientHash = hs.suite.mac(clientMAC)
serverCipher = hs.suite.cipher(serverKey, serverIV, true /* for reading */)
serverHash = hs.suite.mac(serverMAC)
} else {
clientCipher = hs.suite.aead(clientKey, clientIV)
serverCipher = hs.suite.aead(serverKey, serverIV)
}
c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
c.out.prepareCipherSpec(c.vers, clientCipher, clientHash)
return nil
}
func (hs *clientHandshakeState) serverResumedSession() bool {
// If the server responded with the same sessionId then it means the
// sessionTicket is being used to resume a TLS session.
return hs.session != nil && hs.hello.sessionId != nil &&
bytes.Equal(hs.serverHello.sessionId, hs.hello.sessionId)
}
func (hs *clientHandshakeState) processServerHello() (bool, error) {
c := hs.c
if err := hs.pickCipherSuite(); err != nil {
return false, err
}
if hs.serverHello.compressionMethod != compressionNone {
c.sendAlert(alertUnexpectedMessage)
return false, errors.New("tls: server selected unsupported compression format")
}
if c.handshakes == 0 && hs.serverHello.secureRenegotiationSupported {
c.secureRenegotiation = true
if len(hs.serverHello.secureRenegotiation) != 0 {
c.sendAlert(alertHandshakeFailure)
return false, errors.New("tls: initial handshake had non-empty renegotiation extension")
}
}
if c.handshakes > 0 && c.secureRenegotiation {
var expectedSecureRenegotiation [24]byte
copy(expectedSecureRenegotiation[:], c.clientFinished[:])
copy(expectedSecureRenegotiation[12:], c.serverFinished[:])
if !bytes.Equal(hs.serverHello.secureRenegotiation, expectedSecureRenegotiation[:]) {
c.sendAlert(alertHandshakeFailure)
return false, errors.New("tls: incorrect renegotiation extension contents")
}
}
if err := checkALPN(hs.hello.alpnProtocols, hs.serverHello.alpnProtocol, false); err != nil {
c.sendAlert(alertUnsupportedExtension)
return false, err
}
c.clientProtocol = hs.serverHello.alpnProtocol
c.scts = hs.serverHello.scts
if !hs.serverResumedSession() {
return false, nil
}
if hs.session.version != c.vers {
c.sendAlert(alertHandshakeFailure)
return false, errors.New("tls: server resumed a session with a different version")
}
if hs.session.cipherSuite != hs.suite.id {
c.sendAlert(alertHandshakeFailure)
return false, errors.New("tls: server resumed a session with a different cipher suite")
}
// RFC 7627, Section 5.3
if hs.session.extMasterSecret != hs.serverHello.extendedMasterSecret {
c.sendAlert(alertHandshakeFailure)
return false, errors.New("tls: server resumed a session with a different EMS extension")
}
// Restore master secret and certificates from previous state
hs.masterSecret = hs.session.secret
c.extMasterSecret = hs.session.extMasterSecret
c.peerCertificates = hs.session.peerCertificates
c.activeCertHandles = hs.c.activeCertHandles
c.verifiedChains = hs.session.verifiedChains
c.ocspResponse = hs.session.ocspResponse
// Let the ServerHello SCTs override the session SCTs from the original
// connection, if any are provided.
if len(c.scts) == 0 && len(hs.session.scts) != 0 {
c.scts = hs.session.scts
}
c.curveID = hs.session.curveID
return true, nil
}
// checkALPN ensure that the server's choice of ALPN protocol is compatible with
// the protocols that we advertised in the ClientHello.
func checkALPN(clientProtos []string, serverProto string, quic bool) error {
if serverProto == "" {
if quic && len(clientProtos) > 0 {
// RFC 9001, Section 8.1
return errors.New("tls: server did not select an ALPN protocol")
}
return nil
}
if len(clientProtos) == 0 {
return errors.New("tls: server advertised unrequested ALPN extension")
}
for _, proto := range clientProtos {
if proto == serverProto {
return nil
}
}
return errors.New("tls: server selected unadvertised ALPN protocol")
}
func (hs *clientHandshakeState) readFinished(out []byte) error {
c := hs.c
if err := c.readChangeCipherSpec(); err != nil {
return err
}
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
// finishedMsg is included in the transcript, but not until after we
// check the client version, since the state before this message was
// sent is used during verification.
msg, err := c.readHandshake(nil)
if err != nil {
return err
}
serverFinished, ok := msg.(*finishedMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(serverFinished, msg)
}
verify := hs.finishedHash.serverSum(hs.masterSecret)
if len(verify) != len(serverFinished.verifyData) ||
subtle.ConstantTimeCompare(verify, serverFinished.verifyData) != 1 {
c.sendAlert(alertHandshakeFailure)
return errors.New("tls: server's Finished message was incorrect")
}
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
if err := transcriptMsg(serverFinished, &hs.finishedHash); err != nil {
return err
}
copy(out, verify)
return nil
}
func (hs *clientHandshakeState) readSessionTicket() error {
if !hs.serverHello.ticketSupported {
return nil
}
c := hs.c
if !hs.hello.ticketSupported {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server sent unrequested session ticket")
}
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
msg, err := c.readHandshake(&hs.finishedHash)
if err != nil {
return err
}
sessionTicketMsg, ok := msg.(*newSessionTicketMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(sessionTicketMsg, msg)
}
hs.ticket = sessionTicketMsg.ticket
return nil
}
func (hs *clientHandshakeState) saveSessionTicket() error {
if hs.ticket == nil {
return nil
}
c := hs.c
cacheKey := c.clientSessionCacheKey()
if cacheKey == "" {
return nil
}
session := c.sessionState()
session.secret = hs.masterSecret
session.ticket = hs.ticket
cs := &ClientSessionState{session: session}
c.config.ClientSessionCache.Put(cacheKey, cs)
return nil
}
func (hs *clientHandshakeState) sendFinished(out []byte) error {
c := hs.c
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
if err := c.writeChangeCipherRecord(); err != nil {
return err
}
finished := new(finishedMsg)
finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret)
crypto/tls: replace all usages of BytesOrPanic Message marshalling makes use of BytesOrPanic a lot, under the assumption that it will never panic. This assumption was incorrect, and specifically crafted handshakes could trigger panics. Rather than just surgically replacing the usages of BytesOrPanic in paths that could panic, replace all usages of it with proper error returns in case there are other ways of triggering panics which we didn't find. In one specific case, the tree routed by expandLabel, we replace the usage of BytesOrPanic, but retain a panic. This function already explicitly panicked elsewhere, and returning an error from it becomes rather painful because it requires changing a large number of APIs. The marshalling is unlikely to ever panic, as the inputs are all either fixed length, or already limited to the sizes required. If it were to panic, it'd likely only be during development. A close inspection shows no paths for a user to cause a panic currently. This patches ends up being rather large, since it requires routing errors back through functions which previously had no error returns. Where possible I've tried to use helpers that reduce the verbosity of frequently repeated stanzas, and to make the diffs as minimal as possible. Thanks to Marten Seemann for reporting this issue. Fixes #58001 Fixes CVE-2022-41724 Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 Reviewed-by: Julie Qiu <julieqiu@google.com> TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> Run-TryBot: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/468125 Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com>
2022-12-14 09:43:16 -08:00
if _, err := hs.c.writeHandshakeRecord(finished, &hs.finishedHash); err != nil {
return err
}
copy(out, finished.verifyData)
return nil
}
// defaultMaxRSAKeySize is the maximum RSA key size in bits that we are willing
// to verify the signatures of during a TLS handshake.
const defaultMaxRSAKeySize = 8192
var tlsmaxrsasize = godebug.New("tlsmaxrsasize")
func checkKeySize(n int) (max int, ok bool) {
if v := tlsmaxrsasize.Value(); v != "" {
if max, err := strconv.Atoi(v); err == nil {
if (n <= max) != (n <= defaultMaxRSAKeySize) {
tlsmaxrsasize.IncNonDefault()
}
return max, n <= max
}
}
return defaultMaxRSAKeySize, n <= defaultMaxRSAKeySize
}
// verifyServerCertificate parses and verifies the provided chain, setting
// c.verifiedChains and c.peerCertificates or sending the appropriate alert.
func (c *Conn) verifyServerCertificate(certificates [][]byte) error {
activeHandles := make([]*activeCert, len(certificates))
certs := make([]*x509.Certificate, len(certificates))
for i, asn1Data := range certificates {
cert, err := globalCertCache.newCert(asn1Data)
if err != nil {
c.sendAlert(alertDecodeError)
return errors.New("tls: failed to parse certificate from server: " + err.Error())
}
if cert.cert.PublicKeyAlgorithm == x509.RSA {
n := cert.cert.PublicKey.(*rsa.PublicKey).N.BitLen()
if max, ok := checkKeySize(n); !ok {
c.sendAlert(alertBadCertificate)
return fmt.Errorf("tls: server sent certificate containing RSA key larger than %d bits", max)
}
}
activeHandles[i] = cert
certs[i] = cert.cert
}
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
echRejected := c.config.EncryptedClientHelloConfigList != nil && !c.echAccepted
if echRejected {
if c.config.EncryptedClientHelloRejectionVerify != nil {
if err := c.config.EncryptedClientHelloRejectionVerify(c.connectionStateLocked()); err != nil {
c.sendAlert(alertBadCertificate)
return err
}
} else {
opts := x509.VerifyOptions{
Roots: c.config.RootCAs,
CurrentTime: c.config.time(),
DNSName: c.serverName,
Intermediates: x509.NewCertPool(),
}
for _, cert := range certs[1:] {
opts.Intermediates.AddCert(cert)
}
chains, err := certs[0].Verify(opts)
if err != nil {
c.sendAlert(alertBadCertificate)
return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err}
}
c.verifiedChains, err = fipsAllowedChains(chains)
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
if err != nil {
c.sendAlert(alertBadCertificate)
return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err}
}
}
} else if !c.config.InsecureSkipVerify {
opts := x509.VerifyOptions{
Roots: c.config.RootCAs,
CurrentTime: c.config.time(),
DNSName: c.config.ServerName,
Intermediates: x509.NewCertPool(),
}
for _, cert := range certs[1:] {
opts.Intermediates.AddCert(cert)
}
chains, err := certs[0].Verify(opts)
if err != nil {
c.sendAlert(alertBadCertificate)
return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err}
}
c.verifiedChains, err = fipsAllowedChains(chains)
if err != nil {
c.sendAlert(alertBadCertificate)
return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err}
}
}
switch certs[0].PublicKey.(type) {
case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
break
default:
c.sendAlert(alertUnsupportedCertificate)
return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey)
}
c.activeCertHandles = activeHandles
c.peerCertificates = certs
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
if c.config.VerifyPeerCertificate != nil && !echRejected {
if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil {
c.sendAlert(alertBadCertificate)
return err
}
}
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
if c.config.VerifyConnection != nil && !echRejected {
if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
c.sendAlert(alertBadCertificate)
return err
}
}
return nil
}
// certificateRequestInfoFromMsg generates a CertificateRequestInfo from a TLS
// <= 1.2 CertificateRequest, making an effort to fill in missing information.
func certificateRequestInfoFromMsg(ctx context.Context, vers uint16, certReq *certificateRequestMsg) *CertificateRequestInfo {
cri := &CertificateRequestInfo{
AcceptableCAs: certReq.certificateAuthorities,
Version: vers,
ctx: ctx,
}
var rsaAvail, ecAvail bool
for _, certType := range certReq.certificateTypes {
switch certType {
case certTypeRSASign:
rsaAvail = true
case certTypeECDSASign:
ecAvail = true
}
}
if !certReq.hasSignatureAlgorithm {
// Prior to TLS 1.2, signature schemes did not exist. In this case we
// make up a list based on the acceptable certificate types, to help
// GetClientCertificate and SupportsCertificate select the right certificate.
// The hash part of the SignatureScheme is a lie here, because
// TLS 1.0 and 1.1 always use MD5+SHA1 for RSA and SHA1 for ECDSA.
switch {
case rsaAvail && ecAvail:
cri.SignatureSchemes = []SignatureScheme{
ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512,
PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1,
}
case rsaAvail:
cri.SignatureSchemes = []SignatureScheme{
PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1,
}
case ecAvail:
cri.SignatureSchemes = []SignatureScheme{
ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512,
}
}
return cri
}
crypto/tls: disable RSA-PSS in TLS 1.2 again Signing with RSA-PSS can uncover faulty crypto.Signer implementations, and it can fail for (broken) small keys. We'll have to take that breakage eventually, but it would be nice for it to be opt-out at first. TLS 1.3 requires RSA-PSS and is opt-out in Go 1.13. Instead of making a TLS 1.3 opt-out influence a TLS 1.2 behavior, let's wait to add RSA-PSS to TLS 1.2 until TLS 1.3 is on without opt-out. Note that since the Client Hello is sent before a protocol version is selected, we have to advertise RSA-PSS there to support TLS 1.3. That means that we still support RSA-PSS on the client in TLS 1.2 for verifying server certificates, which is fine, as all issues arise on the signing side. We have to be careful not to pick (or consider available) RSA-PSS on the client for client certificates, though. We'd expect tests to change only in TLS 1.2: * the server won't pick PSS to sign the key exchange (Server-TLSv12-* w/ RSA, TestHandshakeServerRSAPSS); * the server won't advertise PSS in CertificateRequest (Server-TLSv12-ClientAuthRequested*, TestClientAuth); * and the client won't pick PSS for its CertificateVerify (Client-TLSv12-ClientCert-RSA-*, TestHandshakeClientCertRSAPSS, Client-TLSv12-Renegotiate* because "R" requests a client cert). Client-TLSv13-ClientCert-RSA-RSAPSS was updated because of a fix in the test. This effectively reverts 88343530720a52c96b21f2bd5488c8fb607605d7. Testing was made more complex by the undocumented semantics of OpenSSL's -[client_]sigalgs (see openssl/openssl#9172). Updates #32425 Change-Id: Iaddeb2df1f5c75cd090cc8321df2ac8e8e7db349 Reviewed-on: https://go-review.googlesource.com/c/go/+/182339 Reviewed-by: Adam Langley <agl@golang.org>
2019-06-13 18:33:33 -04:00
// Filter the signature schemes based on the certificate types.
// See RFC 5246, Section 7.4.4 (where it calls this "somewhat complicated").
cri.SignatureSchemes = make([]SignatureScheme, 0, len(certReq.supportedSignatureAlgorithms))
for _, sigScheme := range certReq.supportedSignatureAlgorithms {
sigType, _, err := typeAndHashFromSignatureScheme(sigScheme)
if err != nil {
continue
}
switch sigType {
case signatureECDSA, signatureEd25519:
if ecAvail {
cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme)
}
case signatureRSAPSS, signaturePKCS1v15:
if rsaAvail {
cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme)
}
}
}
return cri
}
func (c *Conn) getClientCertificate(cri *CertificateRequestInfo) (*Certificate, error) {
if c.config.GetClientCertificate != nil {
return c.config.GetClientCertificate(cri)
}
for _, chain := range c.config.Certificates {
if err := cri.SupportsCertificate(&chain); err != nil {
continue
}
return &chain, nil
}
// No acceptable certificate found. Don't send a certificate.
return new(Certificate), nil
}
// clientSessionCacheKey returns a key used to cache sessionTickets that could
// be used to resume previously negotiated TLS sessions with a server.
func (c *Conn) clientSessionCacheKey() string {
if len(c.config.ServerName) > 0 {
return c.config.ServerName
}
if c.conn != nil {
return c.conn.RemoteAddr().String()
}
return ""
}
// hostnameInSNI converts name into an appropriate hostname for SNI.
// Literal IP addresses and absolute FQDNs are not permitted as SNI values.
// See RFC 6066, Section 3.
func hostnameInSNI(name string) string {
host := name
if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' {
host = host[1 : len(host)-1]
}
if i := strings.LastIndex(host, "%"); i > 0 {
host = host[:i]
}
if net.ParseIP(host) != nil {
return ""
}
for len(name) > 0 && name[len(name)-1] == '.' {
name = name[:len(name)-1]
}
return name
}
crypto/tls: add ech client support This CL adds a (very opinionated) client-side ECH implementation. In particular, if a user configures a ECHConfigList, by setting the Config.EncryptedClientHelloConfigList, but we determine that none of the configs are appropriate, we will not fallback to plaintext SNI, and will instead return an error. It is then up to the user to decide if they wish to fallback to plaintext themselves (by removing the config list). Additionally if Config.EncryptedClientHelloConfigList is provided, we will not offer TLS support lower than 1.3, since negotiating any other version, while offering ECH, is a hard error anyway. Similarly, if a user wishes to fallback to plaintext SNI by using 1.2, they may do so by removing the config list. With regard to PSK GREASE, we match the boringssl behavior, which does not include PSK identities/binders in the outer hello when doing ECH. If the server rejects ECH, we will return a ECHRejectionError error, which, if provided by the server, will contain a ECHConfigList in the RetryConfigList field containing configs that should be used if the user wishes to retry. It is up to the user to replace their existing Config.EncryptedClientHelloConfigList with the retry config list. Fixes #63369 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: I9bc373c044064221a647a388ac61624efd6bbdbf Reviewed-on: https://go-review.googlesource.com/c/go/+/578575 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-04-11 08:50:36 -07:00
func computeAndUpdatePSK(m *clientHelloMsg, binderKey []byte, transcript hash.Hash, finishedHash func([]byte, hash.Hash) []byte) error {
helloBytes, err := m.marshalWithoutBinders()
if err != nil {
return err
}
transcript.Write(helloBytes)
pskBinders := [][]byte{finishedHash(binderKey, transcript)}
return m.updateBinders(pskBinders)
}