mirror of
https://github.com/golang/go.git
synced 2026-06-27 19:30:52 +00:00
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:
parent
edf006c9a3
commit
ad46b4815e
6 changed files with 73 additions and 21 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1068,7 +1071,6 @@ func (c *Config) initLegacySessionTicketKeyRLocked() {
|
|||
} else if !bytes.HasPrefix(c.SessionTicketKey[:], deprecatedSessionTicketKey) && len(c.sessionTicketKeys) == 0 {
|
||||
c.sessionTicketKeys = []ticketKey{c.ticketKeyFromBytes(c.SessionTicketKey)}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ticketKeys returns the ticketKeys for this connection.
|
||||
|
|
@ -1218,7 +1220,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) {
|
||||
|
|
@ -1236,13 +1238,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
|
||||
}
|
||||
|
|
@ -1294,8 +1299,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
|
||||
|
|
@ -1381,7 +1386,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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,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")
|
||||
}
|
||||
|
|
@ -316,7 +316,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) ||
|
||||
|
|
@ -508,7 +508,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)
|
||||
|
|
|
|||
|
|
@ -188,7 +188,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)
|
||||
|
|
@ -235,7 +235,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)
|
||||
|
|
@ -410,7 +410,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")
|
||||
}
|
||||
|
|
@ -1037,6 +1037,7 @@ func clientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg)
|
|||
Conn: conn,
|
||||
HelloRetryRequest: c.didHRR,
|
||||
config: c.config,
|
||||
isQUIC: c.quic != nil,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,7 +132,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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
return newQUICConn(Server(nil, config.TLSConfig), config)
|
||||
}
|
||||
|
|
@ -221,9 +217,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
|
||||
|
|
|
|||
|
|
@ -215,6 +215,59 @@ func TestQUICConnection(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestQUICVersions(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
clientMin uint16
|
||||
clientMax uint16
|
||||
serverMin uint16
|
||||
serverMax uint16
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "defaults",
|
||||
},
|
||||
{
|
||||
name: "MinVersion TLS 1.2",
|
||||
clientMin: VersionTLS12,
|
||||
serverMin: VersionTLS12,
|
||||
},
|
||||
{
|
||||
name: "MinVersion TLS 1.3",
|
||||
clientMin: VersionTLS13,
|
||||
serverMin: VersionTLS13,
|
||||
},
|
||||
{
|
||||
name: "client MaxVersion TLS 1.2",
|
||||
clientMax: VersionTLS12,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "server MaxVersion TLS 1.2",
|
||||
serverMax: VersionTLS12,
|
||||
wantErr: true,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
client := testConfig.Clone()
|
||||
client.MinVersion = tc.clientMin
|
||||
client.MaxVersion = tc.clientMax
|
||||
server := testConfig.Clone()
|
||||
server.MinVersion = tc.serverMin
|
||||
server.MaxVersion = tc.serverMax
|
||||
|
||||
cli := newTestQUICClient(t, &QUICConfig{TLSConfig: client})
|
||||
cli.conn.SetTransportParameters(nil)
|
||||
srv := newTestQUICServer(t, &QUICConfig{TLSConfig: server})
|
||||
srv.conn.SetTransportParameters(nil)
|
||||
err := runTestQUICConnection(context.Background(), cli, srv, nil)
|
||||
if tc.wantErr == (err == nil) {
|
||||
t.Errorf("got err=%v, wantErr=%v", err, tc.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestQUICSessionResumption(t *testing.T) {
|
||||
clientConfig := &QUICConfig{TLSConfig: testConfigClient.Clone()}
|
||||
clientConfig.TLSConfig.MinVersion = VersionTLS13
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue