crypto/tls: clamp effective minimum version to TLS 1.3 when using QUIC

Change-Id: Ieec72362bacf1956a2bd5e0b2eb8dad88e624bd1
Reviewed-on: https://go-review.googlesource.com/c/go/+/745980
Reviewed-by: Damien Neil <dneil@google.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
This commit is contained in:
yuhan6665
2026-09-10 22:56:11 -04:00
parent 8ea3928df4
commit 0b8071916e
5 changed files with 20 additions and 20 deletions
+12 -6
View File
@@ -495,6 +495,9 @@ type ClientHelloInfo struct {
// for use with SupportsCertificate.
config *Config
// isQUIC indicates whether the connection is a QUIC connection.
isQUIC bool
// ctx is the context of the handshake that is in progress.
ctx context.Context
}
@@ -1257,7 +1260,7 @@ const roleServer = false
// supportedVersions returns the list of supported TLS versions, sorted from
// highest to lowest (and hence also in preference order).
func (c *Config) supportedVersions(isClient bool) []uint16 {
func (c *Config) supportedVersions(isClient, isQUIC bool) []uint16 {
versions := make([]uint16, 0, len(supportedVersions))
for _, v := range supportedVersions {
if fips140tls.Required() && !slices.Contains(allowedSupportedVersionsFIPS, v) {
@@ -1275,13 +1278,16 @@ func (c *Config) supportedVersions(isClient bool) []uint16 {
if c != nil && c.MaxVersion != 0 && v > c.MaxVersion {
continue
}
if isQUIC && v < VersionTLS13 {
continue
}
versions = append(versions, v)
}
return versions
}
func (c *Config) maxSupportedVersion(isClient bool) uint16 {
supportedVersions := c.supportedVersions(isClient)
func (c *Config) maxSupportedVersion(isClient, isQUIC bool) uint16 {
supportedVersions := c.supportedVersions(isClient, isQUIC)
if len(supportedVersions) == 0 {
return 0
}
@@ -1333,8 +1339,8 @@ func (c *Config) supportsCurve(version uint16, x CurveID) bool {
// mutualVersion returns the protocol version to use given the advertised
// versions of the peer. The highest supported version is preferred.
func (c *Config) mutualVersion(isClient bool, peerVersions []uint16) (uint16, bool) {
supportedVersions := c.supportedVersions(isClient)
func (c *Config) mutualVersion(isClient, isQUIC bool, peerVersions []uint16) (uint16, bool) {
supportedVersions := c.supportedVersions(isClient, isQUIC)
for _, v := range supportedVersions {
if slices.Contains(peerVersions, v) {
return v, true
@@ -1420,7 +1426,7 @@ func (chi *ClientHelloInfo) SupportsCertificate(c *Certificate) error {
if config == nil {
config = &Config{}
}
vers, ok := config.mutualVersion(roleServer, chi.SupportedVersions)
vers, ok := config.mutualVersion(roleServer, chi.isQUIC, chi.SupportedVersions)
if !ok {
return errors.New("no mutually supported protocol versions")
}
+3 -3
View File
@@ -58,7 +58,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCli
return nil, nil, nil, errors.New("tls: NextProtos values too large")
}
supportedVersions := config.supportedVersions(roleClient)
supportedVersions := config.supportedVersions(roleClient, c.quic != nil)
if len(supportedVersions) == 0 {
return nil, nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion")
}
@@ -317,7 +317,7 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) {
// 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)
maxVers := c.config.maxSupportedVersion(roleClient, c.quic != nil)
tls12Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS12
tls11Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS11
if maxVers == VersionTLS13 && c.vers <= VersionTLS12 && (tls12Downgrade || tls11Downgrade) ||
@@ -510,7 +510,7 @@ func (c *Conn) pickTLSVersion(serverHello *serverHelloMsg) error {
peerVersion = serverHello.supportedVersion
}
vers, ok := c.config.mutualVersion(roleClient, []uint16{peerVersion})
vers, ok := c.config.mutualVersion(roleClient, c.quic != nil, []uint16{peerVersion})
if !ok {
c.sendAlert(alertProtocolVersion)
return fmt.Errorf("tls: server selected unsupported protocol version %x", peerVersion)
+4 -3
View File
@@ -189,7 +189,7 @@ func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, *echServer
} else if len(clientVersions) == 0 {
clientVersions = supportedVersionsFromMax(clientHello.vers)
}
c.vers, ok = c.config.mutualVersion(roleServer, clientVersions)
c.vers, ok = c.config.mutualVersion(roleServer, c.quic != nil, clientVersions)
if !ok {
c.sendAlert(alertProtocolVersion)
return nil, nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions)
@@ -236,7 +236,7 @@ func (hs *serverHandshakeState) processClientHello() error {
hs.hello.random = make([]byte, 32)
serverRandom := hs.hello.random
// Downgrade protection canaries. See RFC 8446, Section 4.1.3.
maxVers := c.config.maxSupportedVersion(roleServer)
maxVers := c.config.maxSupportedVersion(roleServer, c.quic != nil)
if maxVers >= VersionTLS12 && c.vers < maxVers || testingOnlyForceDowngradeCanary {
if c.vers == VersionTLS12 {
copy(serverRandom[24:], downgradeCanaryTLS12)
@@ -411,7 +411,7 @@ func (hs *serverHandshakeState) pickCipherSuite() error {
for _, id := range hs.clientHello.cipherSuites {
if id == TLS_FALLBACK_SCSV {
// The client is doing a fallback connection. See RFC 7507.
if hs.clientHello.vers < c.config.maxSupportedVersion(roleServer) {
if hs.clientHello.vers < c.config.maxSupportedVersion(roleServer, c.quic != nil) {
c.sendAlert(alertInappropriateFallback)
return errors.New("tls: client using inappropriate protocol fallback")
}
@@ -1034,6 +1034,7 @@ func clientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg)
Conn: conn,
HelloRetryRequest: c.didHRR,
config: c.config,
isQUIC: c.quic != nil,
ctx: ctx,
}
}
+1 -1
View File
@@ -226,7 +226,7 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error {
if id == TLS_FALLBACK_SCSV {
// Use c.vers instead of max(supported_versions) because an attacker
// could defeat this by adding an arbitrary high version otherwise.
if c.vers < c.config.maxSupportedVersion(roleServer) {
if c.vers < c.config.maxSupportedVersion(roleServer, c.quic != nil) {
c.sendAlert(alertInappropriateFallback)
return errors.New("tls: client using inappropriate protocol fallback")
}
-7
View File
@@ -185,16 +185,12 @@ type quicState struct {
// QUICClient returns a new TLS client side connection using QUICTransport as the
// underlying transport. The config cannot be nil.
//
// The config's MinVersion must be at least TLS 1.3.
func QUICClient(config *QUICConfig) *QUICConn {
return newQUICConn(Client(nil, config.TLSConfig), config)
}
// QUICServer returns a new TLS server side connection using QUICTransport as the
// underlying transport. The config cannot be nil.
//
// The config's MinVersion must be at least TLS 1.3.
func QUICServer(config *QUICConfig) *QUICConn {
c, _ := Server(context.Background(), nil, config.TLSConfig)
return newQUICConn(c, config)
@@ -222,9 +218,6 @@ func (q *QUICConn) Start(ctx context.Context) error {
return quicError(errors.New("tls: Start called more than once"))
}
q.conn.quic.started = true
if q.conn.config.MinVersion < VersionTLS13 {
return quicError(errors.New("tls: Config MinVersion must be at least TLS 1.3"))
}
go q.conn.HandshakeContext(ctx)
if _, ok := <-q.conn.quic.blockedc; !ok {
return q.conn.handshakeErr