recordHeaderLen = 5 // record header length
maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB)
maxUselessRecords = 5 // maximum number of consecutive non-advancing records
-
- minVersion = VersionTLS10
- maxVersion = VersionTLS12
)
// TLS record types.
return s
}
-func (c *Config) minVersion() uint16 {
- if c == nil || c.MinVersion == 0 {
- return minVersion
+var supportedVersions = []uint16{
+ VersionTLS12,
+ VersionTLS11,
+ VersionTLS10,
+ VersionSSL30,
+}
+
+func (c *Config) supportedVersions(isClient bool) []uint16 {
+ versions := make([]uint16, 0, len(supportedVersions))
+ for _, v := range supportedVersions {
+ if c != nil && c.MinVersion != 0 && v < c.MinVersion {
+ continue
+ }
+ if c != nil && c.MaxVersion != 0 && v > c.MaxVersion {
+ continue
+ }
+ // TLS 1.0 is the minimum version supported as a client.
+ if isClient && v < VersionTLS10 {
+ continue
+ }
+ versions = append(versions, v)
}
- return c.MinVersion
+ return versions
}
-func (c *Config) maxVersion() uint16 {
- if c == nil || c.MaxVersion == 0 {
- return maxVersion
+// supportedVersionsFromMax returns a list of supported versions derived from a
+// legacy maximum version value. Note that only versions supported by this
+// library are returned. Any newer peer will use supportedVersions anyway.
+func supportedVersionsFromMax(maxVersion uint16) []uint16 {
+ versions := make([]uint16, 0, len(supportedVersions))
+ for _, v := range supportedVersions {
+ if v > maxVersion {
+ continue
+ }
+ versions = append(versions, v)
}
- return c.MaxVersion
+ return versions
}
var defaultCurvePreferences = []CurveID{X25519, CurveP256, CurveP384, CurveP521}
}
// mutualVersion returns the protocol version to use given the advertised
-// version of the peer.
-func (c *Config) mutualVersion(vers uint16) (uint16, bool) {
- minVersion := c.minVersion()
- maxVersion := c.maxVersion()
-
- if vers < minVersion {
- return 0, false
- }
- if vers > maxVersion {
- vers = maxVersion
+// versions of the peer. Priority is given to the peer preference order.
+func (c *Config) mutualVersion(isClient bool, peerVersions []uint16) (uint16, bool) {
+ supportedVersions := c.supportedVersions(isClient)
+ for _, peerVersion := range peerVersions {
+ for _, v := range supportedVersions {
+ if v == peerVersion {
+ return v, true
+ }
+ }
}
- return vers, true
+ return 0, false
}
// getCertificate returns the best certificate for the given ClientHelloInfo,
nextProtosLength += 1 + l
}
}
-
if nextProtosLength > 0xffff {
return nil, errors.New("tls: NextProtos values too large")
}
+ supportedVersions := config.supportedVersions(true)
+ if len(supportedVersions) == 0 {
+ return nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion")
+ }
+
+ clientHelloVersion := supportedVersions[0]
+ // 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 clientHelloVersion > VersionTLS12 {
+ clientHelloVersion = VersionTLS12
+ }
+
hello := &clientHelloMsg{
- vers: config.maxVersion(),
+ vers: clientHelloVersion,
compressionMethods: []uint8{compressionNone},
random: make([]byte, 32),
ocspStapling: true,
nextProtoNeg: len(config.NextProtos) > 0,
secureRenegotiationSupported: true,
alpnProtocols: config.NextProtos,
+ supportedVersions: supportedVersions,
}
possibleCipherSuites := config.cipherSuites()
hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites))
}
}
- versOk := candidateSession.vers >= c.config.minVersion() &&
- candidateSession.vers <= c.config.maxVersion()
+ versOk := false
+ for _, v := range c.config.supportedVersions(true) {
+ if v == candidateSession.vers {
+ versOk = true
+ break
+ }
+ }
+
if versOk && cipherSuiteOk {
session = candidateSession
}
}
func (hs *clientHandshakeState) pickTLSVersion() error {
- vers, ok := hs.c.config.mutualVersion(hs.serverHello.vers)
- if !ok || vers < VersionTLS10 {
- // TLS 1.0 is the minimum version supported as a client.
+ peerVersion := hs.serverHello.vers
+ if hs.serverHello.supportedVersion != 0 {
+ peerVersion = hs.serverHello.supportedVersion
+ }
+
+ vers, ok := hs.c.config.mutualVersion(true, []uint16{peerVersion})
+ if !ok {
hs.c.sendAlert(alertProtocolVersion)
- return fmt.Errorf("tls: server selected unsupported protocol version %x", hs.serverHello.vers)
+ return fmt.Errorf("tls: server selected unsupported protocol version %x", peerVersion)
}
hs.c.vers = vers
func (test *clientTest) run(t *testing.T, write bool) {
checkOpenSSLVersion(t)
+ // TODO(filippo): regenerate client tests all at once after CL 146217,
+ // RSA-PSS and client-side TLS 1.3 are landed.
+ if !write {
+ t.Skip("recorded client tests are out of date")
+ }
+
var clientConn, serverConn net.Conn
var recordingConn *recordingConn
var childProcess *exec.Cmd
}
}
- c.vers, ok = c.config.mutualVersion(hs.clientHello.vers)
+ clientVersions := hs.clientHello.supportedVersions
+ if len(hs.clientHello.supportedVersions) == 0 {
+ clientVersions = supportedVersionsFromMax(hs.clientHello.vers)
+ }
+ c.vers, ok = c.config.mutualVersion(false, clientVersions)
if !ok {
c.sendAlert(alertProtocolVersion)
- return false, fmt.Errorf("tls: client offered an unsupported, maximum protocol version of %x", hs.clientHello.vers)
+ return false, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions)
}
c.haveVers = true
hs.hello = new(serverHelloMsg)
+ hs.hello.vers = c.vers
supportedCurve := false
preferredCurves := c.config.curvePreferences()
return false, errors.New("tls: client does not support uncompressed connections")
}
- hs.hello.vers = c.vers
hs.hello.random = make([]byte, 32)
_, err = io.ReadFull(c.config.rand(), hs.hello.random)
if err != nil {
for _, id := range hs.clientHello.cipherSuites {
if id == TLS_FALLBACK_SCSV {
// The client is doing a fallback connection.
- if hs.clientHello.vers < c.config.maxVersion() {
+ if hs.clientHello.vers < c.config.supportedVersions(false)[0] {
c.sendAlert(alertInappropriateFallback)
return false, errors.New("tls: client using inappropriate protocol fallback")
}
return false
}
-// suppVersArray is the backing array of ClientHelloInfo.SupportedVersions
-var suppVersArray = [...]uint16{VersionTLS12, VersionTLS11, VersionTLS10, VersionSSL30}
-
func (hs *serverHandshakeState) clientHelloInfo() *ClientHelloInfo {
if hs.cachedClientHelloInfo != nil {
return hs.cachedClientHelloInfo
}
- var supportedVersions []uint16
- if hs.clientHello.vers > VersionTLS12 {
- supportedVersions = suppVersArray[:]
- } else if hs.clientHello.vers >= VersionSSL30 {
- supportedVersions = suppVersArray[VersionTLS12-hs.clientHello.vers:]
+ supportedVersions := hs.clientHello.supportedVersions
+ if len(hs.clientHello.supportedVersions) == 0 {
+ supportedVersions = supportedVersionsFromMax(hs.clientHello.vers)
}
hs.cachedClientHelloInfo = &ClientHelloInfo{
testClientHelloFailure(t, testConfig, &clientHelloMsg{
vers: v,
random: make([]byte, 32),
- }, "unsupported, maximum protocol version")
+ }, "unsupported versions")
}
+ testClientHelloFailure(t, testConfig, &clientHelloMsg{
+ vers: VersionTLS12,
+ supportedVersions: badProtocolVersions,
+ random: make([]byte, 32),
+ }, "unsupported versions")
}
func TestNoSuiteOverlap(t *testing.T) {
func(clientHello *ClientHelloInfo) (*Config, error) {
config := testConfig.Clone()
// Setting a maximum version of TLS 1.1 should cause
- // the handshake to fail.
+ // the handshake to fail, as the client MinVersion is TLS 1.2.
config.MaxVersion = VersionTLS11
return config, nil
},
- "version 301 when expecting version 302",
+ "client offered only unsupported versions",
nil,
},
{