diff options
author | Steven Valdez <svaldez@google.com> | 2016-09-15 23:27:05 +0300 |
---|---|---|
committer | CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org> | 2016-09-27 23:12:22 +0300 |
commit | fdd10998e1db111d43dedcd8662ecd5f6287c4ab (patch) | |
tree | 85cdba85e7dea0405ee85f9fe91b26edf9894a36 | |
parent | 2fb2ffb660ff9258188b501bf6eadf3a45bea375 (diff) |
Moving TLS 1.3 version negotiation into extension.
Change-Id: I73f9fd64b46f26978b897409d817b34ec9d93afd
Reviewed-on: https://boringssl-review.googlesource.com/11080
Reviewed-by: Steven Valdez <svaldez@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
-rw-r--r-- | include/openssl/ssl.h | 2 | ||||
-rw-r--r-- | include/openssl/tls1.h | 9 | ||||
-rw-r--r-- | ssl/handshake_client.c | 4 | ||||
-rw-r--r-- | ssl/handshake_server.c | 94 | ||||
-rw-r--r-- | ssl/ssl_asn1.c | 6 | ||||
-rw-r--r-- | ssl/ssl_lib.c | 22 | ||||
-rw-r--r-- | ssl/t1_lib.c | 68 | ||||
-rw-r--r-- | ssl/test/runner/common.go | 37 | ||||
-rw-r--r-- | ssl/test/runner/dtls.go | 8 | ||||
-rw-r--r-- | ssl/test/runner/handshake_client.go | 23 | ||||
-rw-r--r-- | ssl/test/runner/handshake_messages.go | 31 | ||||
-rw-r--r-- | ssl/test/runner/handshake_server.go | 37 | ||||
-rw-r--r-- | ssl/test/runner/runner.go | 111 | ||||
-rw-r--r-- | ssl/tls_method.c | 7 |
14 files changed, 339 insertions, 120 deletions
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 3cf4e03b..b0242be1 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -562,7 +562,7 @@ OPENSSL_EXPORT int DTLSv1_handle_timeout(SSL *ssl); #define DTLS1_VERSION 0xfeff #define DTLS1_2_VERSION 0xfefd -#define TLS1_3_DRAFT_VERSION 14 +#define TLS1_3_DRAFT_VERSION 0x7f0e /* SSL_CTX_set_min_proto_version sets the minimum protocol version for |ctx| to * |version|. If |version| is zero, the default minimum version is used. It diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h index 3c97d26a..eca5decd 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -209,16 +209,9 @@ extern "C" { #define TLSEXT_TYPE_key_share 40 #define TLSEXT_TYPE_pre_shared_key 41 #define TLSEXT_TYPE_early_data 42 +#define TLSEXT_TYPE_supported_versions 43 #define TLSEXT_TYPE_cookie 44 -/* TLSEXT_TYPE_draft_version is the extension used to advertise the TLS 1.3 - * draft implemented. - * - * See - * https://github.com/tlswg/tls13-spec/wiki/Implementations#version-negotiation - */ -#define TLSEXT_TYPE_draft_version 0xff02 - /* ExtensionType value from RFC5746 */ #define TLSEXT_TYPE_renegotiate 0xff01 diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c index 584e9ea7..c5b3b1f2 100644 --- a/ssl/handshake_client.c +++ b/ssl/handshake_client.c @@ -731,6 +731,10 @@ static int ssl3_send_client_hello(SSL *ssl) { * key exchange, the ClientHello version is checked in the premaster secret. * Some servers fail when this value changes. */ ssl->client_version = ssl->version; + + if (max_version >= TLS1_3_VERSION) { + ssl->client_version = ssl->method->version_to_wire(TLS1_2_VERSION); + } } /* If the configured session has expired or was created at a disabled diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c index f7975858..fd0223f6 100644 --- a/ssl/handshake_server.c +++ b/ssl/handshake_server.c @@ -564,38 +564,75 @@ static int negotiate_version( return 0; } - /* For TLS versions which use ClientHello.version, convert it to a version we - * are aware of. */ uint16_t version = 0; - if (SSL_is_dtls(ssl)) { - if (client_hello->version <= DTLS1_2_VERSION) { - version = TLS1_2_VERSION; - } else if (client_hello->version <= DTLS1_VERSION) { - version = TLS1_1_VERSION; + /* Check supported_versions extension if it is present. */ + CBS supported_versions; + if (ssl_early_callback_get_extension(client_hello, &supported_versions, + TLSEXT_TYPE_supported_versions)) { + CBS versions; + if (!CBS_get_u8_length_prefixed(&supported_versions, &versions) || + CBS_len(&supported_versions) != 0 || + CBS_len(&versions) == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + int found_version = 0; + while (CBS_len(&versions) != 0) { + uint16_t ext_version; + if (!CBS_get_u16(&versions, &ext_version)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + if (!ssl->method->version_from_wire(&ext_version, ext_version)) { + continue; + } + if (min_version <= ext_version && + ext_version <= max_version) { + version = ext_version; + found_version = 1; + break; + } + } + + if (!found_version) { + goto unsupported_protocol; } } else { - if (client_hello->version >= TLS1_3_VERSION) { - version = TLS1_3_VERSION; - } else if (client_hello->version >= TLS1_2_VERSION) { - version = TLS1_2_VERSION; - } else if (client_hello->version >= TLS1_1_VERSION) { - version = TLS1_1_VERSION; - } else if (client_hello->version >= TLS1_VERSION) { - version = TLS1_VERSION; - } else if (client_hello->version >= SSL3_VERSION) { - version = SSL3_VERSION; + /* Process ClientHello.version instead. Note that versions beyond (D)TLS 1.2 + * do not use this mechanism. */ + if (SSL_is_dtls(ssl)) { + if (client_hello->version <= DTLS1_2_VERSION) { + version = TLS1_2_VERSION; + } else if (client_hello->version <= DTLS1_VERSION) { + version = TLS1_1_VERSION; + } else { + goto unsupported_protocol; + } + } else { + if (client_hello->version >= TLS1_2_VERSION) { + version = TLS1_2_VERSION; + } else if (client_hello->version >= TLS1_1_VERSION) { + version = TLS1_1_VERSION; + } else if (client_hello->version >= TLS1_VERSION) { + version = TLS1_VERSION; + } else if (client_hello->version >= SSL3_VERSION) { + version = SSL3_VERSION; + } else { + goto unsupported_protocol; + } } - } - /* Apply our minimum and maximum version. */ - if (version > max_version) { - version = max_version; - } + /* Apply our minimum and maximum version. */ + if (version > max_version) { + version = max_version; + } - if (version < min_version) { - OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL); - *out_alert = SSL_AD_PROTOCOL_VERSION; - return 0; + if (version < min_version) { + goto unsupported_protocol; + } } /* Handle FALLBACK_SCSV. */ @@ -617,6 +654,11 @@ static int negotiate_version( ssl->s3->have_version = 1; return 1; + +unsupported_protocol: + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL); + *out_alert = SSL_AD_PROTOCOL_VERSION; + return 0; } static int ssl3_get_client_hello(SSL *ssl) { diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c index 6395a005..ba3b10ee 100644 --- a/ssl/ssl_asn1.c +++ b/ssl/ssl_asn1.c @@ -543,12 +543,6 @@ static SSL_SESSION *SSL_SESSION_parse(CBS *cbs) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } - /* Only support SSLv3/TLS and DTLS. */ - if ((ssl_version >> 8) != SSL3_VERSION_MAJOR && - (ssl_version >> 8) != (DTLS1_VERSION >> 8)) { - OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_SSL_VERSION); - goto err; - } ret->ssl_version = ssl_version; CBS cipher; diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 51c16f08..63f72ca7 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -951,6 +951,10 @@ static int set_min_version(const SSL_PROTOCOL_METHOD *method, uint16_t *out, return 1; } + if (version == TLS1_3_VERSION) { + version = TLS1_3_DRAFT_VERSION; + } + return method->version_from_wire(out, version); } @@ -965,6 +969,10 @@ static int set_max_version(const SSL_PROTOCOL_METHOD *method, uint16_t *out, return 1; } + if (version == TLS1_3_VERSION) { + version = TLS1_3_DRAFT_VERSION; + } + return method->version_from_wire(out, version); } @@ -2109,7 +2117,8 @@ void ssl_update_cache(SSL *ssl, int mode) { static const char *ssl_get_version(int version) { switch (version) { - case TLS1_3_VERSION: + /* Report TLS 1.3 draft version as TLS 1.3 in the public API. */ + case TLS1_3_DRAFT_VERSION: return "TLSv1.3"; case TLS1_2_VERSION: @@ -2271,7 +2280,14 @@ int SSL_get_shutdown(const SSL *ssl) { return ret; } -int SSL_version(const SSL *ssl) { return ssl->version; } +int SSL_version(const SSL *ssl) { + /* Report TLS 1.3 draft version as TLS 1.3 in the public API. */ + if (ssl->version == TLS1_3_DRAFT_VERSION) { + return TLS1_3_VERSION; + } + + return ssl->version; +} SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl) { return ssl->ctx; } @@ -2962,7 +2978,7 @@ void ssl_do_msg_callback(SSL *ssl, int is_write, int content_type, version = 0; break; default: - version = ssl->version; + version = SSL_version(ssl); } ssl->msg_callback(is_write, version, content_type, buf, len, ssl, diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index baa2d45e..281fc718 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -1966,27 +1966,6 @@ static int ext_ec_point_add_serverhello(SSL *ssl, CBB *out) { return ext_ec_point_add_extension(ssl, out); } - -/* Draft Version Extension */ - -static int ext_draft_version_add_clienthello(SSL *ssl, CBB *out) { - uint16_t min_version, max_version; - if (!ssl_get_version_range(ssl, &min_version, &max_version) || - max_version < TLS1_3_VERSION) { - return 1; - } - - CBB contents; - if (!CBB_add_u16(out, TLSEXT_TYPE_draft_version) || - !CBB_add_u16_length_prefixed(out, &contents) || - !CBB_add_u16(&contents, TLS1_3_DRAFT_VERSION)) { - return 0; - } - - return CBB_flush(out); -} - - /* Pre Shared Key * * https://tools.ietf.org/html/draft-ietf-tls-tls13-14 */ @@ -2279,6 +2258,41 @@ int ssl_ext_key_share_add_serverhello(SSL *ssl, CBB *out) { } +/* Supported Versions + * + * https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.1 */ + +static int ext_supported_versions_add_clienthello(SSL *ssl, CBB *out) { + uint16_t min_version, max_version; + if (!ssl_get_version_range(ssl, &min_version, &max_version)) { + return 0; + } + + if (max_version <= TLS1_2_VERSION) { + return 1; + } + + CBB contents, versions; + if (!CBB_add_u16(out, TLSEXT_TYPE_supported_versions) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u8_length_prefixed(&contents, &versions)) { + return 0; + } + + for (uint16_t version = max_version; version >= min_version; version--) { + if (!CBB_add_u16(&versions, ssl->method->version_to_wire(version))) { + return 0; + } + } + + if (!CBB_flush(out)) { + return 0; + } + + return 1; +} + + /* Negotiated Groups * * https://tools.ietf.org/html/rfc4492#section-5.1.2 @@ -2476,25 +2490,25 @@ static const struct tls_extension kExtensions[] = { ext_ec_point_add_serverhello, }, { - TLSEXT_TYPE_draft_version, + TLSEXT_TYPE_key_share, NULL, - ext_draft_version_add_clienthello, + ext_key_share_add_clienthello, forbid_parse_serverhello, ignore_parse_clienthello, dont_add_serverhello, }, { - TLSEXT_TYPE_key_share, + TLSEXT_TYPE_pre_shared_key, NULL, - ext_key_share_add_clienthello, + ext_pre_shared_key_add_clienthello, forbid_parse_serverhello, ignore_parse_clienthello, dont_add_serverhello, }, { - TLSEXT_TYPE_pre_shared_key, + TLSEXT_TYPE_supported_versions, NULL, - ext_pre_shared_key_add_clienthello, + ext_supported_versions_add_clienthello, forbid_parse_serverhello, ignore_parse_clienthello, dont_add_serverhello, diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go index e35d2f54..1ef79e67 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go @@ -26,9 +26,8 @@ const ( VersionTLS13 = 0x0304 ) -// The draft version of TLS 1.3 that is implemented here and sent in the draft -// indicator extension. -const tls13DraftVersion = 14 +// A draft version of TLS 1.3 that is sent over the wire for the current draft. +const tls13DraftVersion = 0x7f0e const ( maxPlaintext = 16384 // maximum plaintext payload length @@ -93,11 +92,11 @@ const ( extensionKeyShare uint16 = 40 // draft-ietf-tls-tls13-13 extensionPreSharedKey uint16 = 41 // draft-ietf-tls-tls13-13 extensionEarlyData uint16 = 42 // draft-ietf-tls-tls13-13 + extensionSupportedVersions uint16 = 43 // draft-ietf-tls-tls13-16 extensionCookie uint16 = 44 // draft-ietf-tls-tls13-13 extensionCustom uint16 = 1234 // not IANA assigned extensionNextProtoNeg uint16 = 13172 // not IANA assigned extensionRenegotiationInfo uint16 = 0xff01 - extensionTLS13Draft uint16 = 0xff02 extensionChannelID uint16 = 30032 // not IANA assigned ) @@ -601,6 +600,14 @@ type ProtocolBugs struct { // specified value in the ClientHello version field. SendClientVersion uint16 + // OmitSupportedVersions, if true, causes the client to omit the + // supported versions extension. + OmitSupportedVersions bool + + // SendSupportedVersions, if non-empty, causes the client to send a + // supported versions extension with the values from array. + SendSupportedVersions []uint16 + // NegotiateVersion, if non-zero, causes the server to negotiate the // specifed TLS version rather than the version supported by either // peer. @@ -1188,24 +1195,10 @@ func (c *Config) defaultCurves() map[CurveID]bool { return defaultCurves } -// mutualVersion returns the protocol version to use given the advertised -// version of the peer. -func (c *Config) mutualVersion(vers uint16, isDTLS bool) (uint16, bool) { - // There is no such thing as DTLS 1.1. - if isDTLS && vers == VersionTLS11 { - vers = VersionTLS10 - } - - minVersion := c.minVersion(isDTLS) - maxVersion := c.maxVersion(isDTLS) - - if vers < minVersion { - return 0, false - } - if vers > maxVersion { - vers = maxVersion - } - return vers, true +// isSupportedVersion returns true if the specified protocol version is +// acceptable. +func (c *Config) isSupportedVersion(vers uint16, isDTLS bool) bool { + return c.minVersion(isDTLS) <= vers && vers <= c.maxVersion(isDTLS) } // getCertificateForName returns the best certificate for the given name, diff --git a/ssl/test/runner/dtls.go b/ssl/test/runner/dtls.go index ca7a51d9..e273bc79 100644 --- a/ssl/test/runner/dtls.go +++ b/ssl/test/runner/dtls.go @@ -33,8 +33,10 @@ func versionToWire(vers uint16, isDTLS bool) uint16 { } } else { switch vers { - case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12, VersionTLS13: + case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12: return vers + case VersionTLS13: + return tls13DraftVersion } } @@ -51,8 +53,10 @@ func wireToVersion(vers uint16, isDTLS bool) (uint16, bool) { } } else { switch vers { - case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12, VersionTLS13: + case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12: return vers, true + case tls13DraftVersion: + return VersionTLS13, true } } diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go index cb241536..167fabf3 100644 --- a/ssl/test/runner/handshake_client.go +++ b/ssl/test/runner/handshake_client.go @@ -57,6 +57,7 @@ func (c *Conn) clientHandshake() error { return errors.New("tls: NextProtos values too large") } + minVersion := c.config.minVersion(c.isDTLS) maxVersion := c.config.maxVersion(c.isDTLS) hello := &clientHelloMsg{ isDTLS: c.isDTLS, @@ -74,7 +75,7 @@ func (c *Conn) clientHandshake() error { duplicateExtension: c.config.Bugs.DuplicateExtension, channelIDSupported: c.config.ChannelID != nil, npnLast: c.config.Bugs.SwapNPNAndALPN, - extendedMasterSecret: c.config.maxVersion(c.isDTLS) >= VersionTLS10, + extendedMasterSecret: maxVersion >= VersionTLS10, srtpProtectionProfiles: c.config.SRTPProtectionProfiles, srtpMasterKeyIdentifier: c.config.Bugs.SRTPMasterKeyIdentifer, customExtension: c.config.Bugs.CustomExtension, @@ -235,8 +236,8 @@ NextCipherSuite: } } - versOk := candidateSession.vers >= c.config.minVersion(c.isDTLS) && - candidateSession.vers <= c.config.maxVersion(c.isDTLS) + versOk := candidateSession.vers >= minVersion && + candidateSession.vers <= maxVersion if ticketOk && versOk && cipherSuiteOk { session = candidateSession } @@ -285,6 +286,19 @@ NextCipherSuite: } } + if maxVersion == VersionTLS13 && !c.config.Bugs.OmitSupportedVersions { + if hello.vers >= VersionTLS13 { + hello.vers = VersionTLS12 + } + for version := maxVersion; version >= minVersion; version-- { + hello.supportedVersions = append(hello.supportedVersions, versionToWire(version, c.isDTLS)) + } + } + + if len(c.config.Bugs.SendSupportedVersions) > 0 { + hello.supportedVersions = c.config.Bugs.SendSupportedVersions + } + if c.config.Bugs.SendClientVersion != 0 { hello.vers = c.config.Bugs.SendClientVersion } @@ -366,12 +380,13 @@ NextCipherSuite: serverVersion, ok := wireToVersion(serverWireVersion, c.isDTLS) if ok { - c.vers, ok = c.config.mutualVersion(serverVersion, c.isDTLS) + ok = c.config.isSupportedVersion(serverVersion, c.isDTLS) } if !ok { c.sendAlert(alertProtocolVersion) return fmt.Errorf("tls: server selected unsupported protocol version %x", c.vers) } + c.vers = serverVersion c.haveVers = true helloRetryRequest, haveHelloRetryRequest := msg.(*helloRetryRequestMsg) diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go index 6538dc62..0b45a4df 100644 --- a/ssl/test/runner/handshake_messages.go +++ b/ssl/test/runner/handshake_messages.go @@ -149,6 +149,7 @@ type clientHelloMsg struct { ticketSupported bool sessionTicket []uint8 signatureAlgorithms []signatureAlgorithm + supportedVersions []uint16 secureRenegotiation []byte alpnProtocols []string duplicateExtension bool @@ -190,6 +191,7 @@ func (m *clientHelloMsg) equal(i interface{}) bool { m.ticketSupported == m1.ticketSupported && bytes.Equal(m.sessionTicket, m1.sessionTicket) && eqSignatureAlgorithms(m.signatureAlgorithms, m1.signatureAlgorithms) && + eqUint16s(m.supportedVersions, m1.supportedVersions) && bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) && (m.secureRenegotiation == nil) == (m1.secureRenegotiation == nil) && eqStrings(m.alpnProtocols, m1.alpnProtocols) && @@ -340,6 +342,14 @@ func (m *clientHelloMsg) marshal() []byte { signatureAlgorithms.addU16(uint16(sigAlg)) } } + if len(m.supportedVersions) > 0 { + extensions.addU16(extensionSupportedVersions) + supportedVersionsExtension := extensions.addU16LengthPrefixed() + supportedVersions := supportedVersionsExtension.addU8LengthPrefixed() + for _, version := range m.supportedVersions { + supportedVersions.addU16(uint16(version)) + } + } if m.secureRenegotiation != nil { extensions.addU16(extensionRenegotiationInfo) secureRenegoExt := extensions.addU16LengthPrefixed() @@ -400,11 +410,6 @@ func (m *clientHelloMsg) marshal() []byte { customExt := extensions.addU16LengthPrefixed() customExt.addBytes([]byte(m.customExtension)) } - if m.vers == VersionTLS13 { - extensions.addU16(extensionTLS13Draft) - extValue := extensions.addU16LengthPrefixed() - extValue.addU16(tls13DraftVersion) - } if extensions.len() == 0 { hello.discardChild() @@ -477,6 +482,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { m.ticketSupported = false m.sessionTicket = nil m.signatureAlgorithms = nil + m.supportedVersions = nil m.alpnProtocols = nil m.extendedMasterSecret = false m.customExtension = "" @@ -645,6 +651,21 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { m.signatureAlgorithms[i] = signatureAlgorithm(d[0])<<8 | signatureAlgorithm(d[1]) d = d[2:] } + case extensionSupportedVersions: + if length < 1+2 { + return false + } + l := int(data[0]) + if l != length-1 || l%2 == 1 || l < 2 { + return false + } + n := l / 2 + d := data[1:] + m.supportedVersions = make([]uint16, n) + for i := range m.supportedVersions { + m.supportedVersions[i] = uint16(d[0])<<8 | uint16(d[1]) + d = d[2:] + } case extensionRenegotiationInfo: if length < 1 || length != int(data[0])+1 { return false diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go index 85d4ca85..3f166ecd 100644 --- a/ssl/test/runner/handshake_server.go +++ b/ssl/test/runner/handshake_server.go @@ -204,7 +204,10 @@ func (hs *serverHandshakeState) readClientHello() error { return fmt.Errorf("tls: client offered different version on renego") } } + c.clientVersion = hs.clientHello.vers + + // Convert the ClientHello wire version to a protocol version. var clientVersion uint16 if c.isDTLS { if hs.clientHello.vers <= 0xfefd { @@ -213,9 +216,7 @@ func (hs *serverHandshakeState) readClientHello() error { clientVersion = VersionTLS10 } } else { - if hs.clientHello.vers >= VersionTLS13 { - clientVersion = VersionTLS13 - } else if hs.clientHello.vers >= VersionTLS12 { + if hs.clientHello.vers >= VersionTLS12 { clientVersion = VersionTLS12 } else if hs.clientHello.vers >= VersionTLS11 { clientVersion = VersionTLS11 @@ -230,12 +231,34 @@ func (hs *serverHandshakeState) readClientHello() error { c.vers = config.Bugs.NegotiateVersion } else if c.haveVers && config.Bugs.NegotiateVersionOnRenego != 0 { c.vers = config.Bugs.NegotiateVersionOnRenego - } else { - c.vers, ok = config.mutualVersion(clientVersion, c.isDTLS) - if !ok { + } else if len(hs.clientHello.supportedVersions) > 0 { + // Use the versions extension if supplied. + var foundVersion bool + for _, extVersion := range hs.clientHello.supportedVersions { + extVersion, ok = wireToVersion(extVersion, c.isDTLS) + if !ok { + continue + } + if config.isSupportedVersion(extVersion, c.isDTLS) { + c.vers = extVersion + foundVersion = true + break + } + } + if !foundVersion { c.sendAlert(alertProtocolVersion) + return errors.New("tls: client did not offer any supported protocol versions") + } + } else { + // Otherwise, use the legacy ClientHello version. + version := clientVersion + if maxVersion := config.maxVersion(c.isDTLS); version > maxVersion { + version = maxVersion + } + if version == 0 || !config.isSupportedVersion(version, c.isDTLS) { return fmt.Errorf("tls: client offered an unsupported, maximum protocol version of %x", hs.clientHello.vers) } + c.vers = version } c.haveVers = true @@ -500,7 +523,7 @@ Curves: ResendHelloRetryRequest: // Send HelloRetryRequest. helloRetryRequestMsg := helloRetryRequestMsg{ - vers: c.vers, + vers: versionToWire(c.vers, c.isDTLS), cipherSuite: hs.hello.cipherSuite, selectedGroup: selectedCurve, } diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index 19f67b1e..ec20947a 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go @@ -4084,6 +4084,7 @@ func addVersionNegotiationTests() { flags = append(flags, vers.flag) } + // Test configuring the runner's maximum version. for _, runnerVers := range tlsVersions { protocols := []protocol{tls} if runnerVers.hasDTLS && shimVers.hasDTLS { @@ -4171,34 +4172,117 @@ func addVersionNegotiationTests() { } } + // Test the version extension at all versions. + for _, vers := range tlsVersions { + protocols := []protocol{tls} + if vers.hasDTLS { + protocols = append(protocols, dtls) + } + for _, protocol := range protocols { + suffix := vers.name + if protocol == dtls { + suffix += "-DTLS" + } + + wireVersion := versionToWire(vers.version, protocol == dtls) + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "VersionNegotiationExtension-" + suffix, + config: Config{ + Bugs: ProtocolBugs{ + SendSupportedVersions: []uint16{0x1111, wireVersion, 0x2222}, + }, + }, + expectedVersion: vers.version, + }) + } + + } + + // If all versions are unknown, negotiation fails. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "NoSupportedVersions", + config: Config{ + Bugs: ProtocolBugs{ + SendSupportedVersions: []uint16{0x1111}, + }, + }, + shouldFail: true, + expectedError: ":UNSUPPORTED_PROTOCOL:", + }) + testCases = append(testCases, testCase{ + protocol: dtls, + testType: serverTest, + name: "NoSupportedVersions-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendSupportedVersions: []uint16{0x1111}, + }, + }, + shouldFail: true, + expectedError: ":UNSUPPORTED_PROTOCOL:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ClientHelloVersionTooHigh", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendClientVersion: 0x0304, + OmitSupportedVersions: true, + }, + }, + expectedVersion: VersionTLS12, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ConflictingVersionNegotiation", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendClientVersion: 0x0304, + SendSupportedVersions: []uint16{0x0303}, + }, + }, + expectedVersion: VersionTLS12, + }) + // Test for version tolerance. testCases = append(testCases, testCase{ testType: serverTest, name: "MinorVersionTolerance", config: Config{ Bugs: ProtocolBugs{ - SendClientVersion: 0x03ff, + SendClientVersion: 0x03ff, + OmitSupportedVersions: true, }, }, - expectedVersion: VersionTLS13, + expectedVersion: VersionTLS12, }) testCases = append(testCases, testCase{ testType: serverTest, name: "MajorVersionTolerance", config: Config{ Bugs: ProtocolBugs{ - SendClientVersion: 0x0400, + SendClientVersion: 0x0400, + OmitSupportedVersions: true, }, }, - expectedVersion: VersionTLS13, + expectedVersion: VersionTLS12, }) + testCases = append(testCases, testCase{ protocol: dtls, testType: serverTest, name: "MinorVersionTolerance-DTLS", config: Config{ Bugs: ProtocolBugs{ - SendClientVersion: 0xfe00, + SendClientVersion: 0xfe00, + OmitSupportedVersions: true, }, }, expectedVersion: VersionTLS12, @@ -4209,7 +4293,8 @@ func addVersionNegotiationTests() { name: "MajorVersionTolerance-DTLS", config: Config{ Bugs: ProtocolBugs{ - SendClientVersion: 0xfdff, + SendClientVersion: 0xfdff, + OmitSupportedVersions: true, }, }, expectedVersion: VersionTLS12, @@ -4221,7 +4306,8 @@ func addVersionNegotiationTests() { name: "VersionTooLow", config: Config{ Bugs: ProtocolBugs{ - SendClientVersion: 0x0200, + SendClientVersion: 0x0200, + OmitSupportedVersions: true, }, }, shouldFail: true, @@ -4317,12 +4403,20 @@ func addMinimumVersionTests() { } } + // Test the client enforces minimum + // versions. Use the NegotiateVersion bug to + // ensure the server does not decline to select + // a version first. This may occur if the + // versions extension is used. testCases = append(testCases, testCase{ protocol: protocol, testType: clientTest, name: "MinimumVersion-Client-" + suffix, config: Config{ MaxVersion: runnerVers.version, + Bugs: ProtocolBugs{ + NegotiateVersion: runnerVers.version, + }, }, flags: flags, expectedVersion: expectedVersion, @@ -4336,6 +4430,9 @@ func addMinimumVersionTests() { name: "MinimumVersion-Client2-" + suffix, config: Config{ MaxVersion: runnerVers.version, + Bugs: ProtocolBugs{ + NegotiateVersion: runnerVers.version, + }, }, flags: []string{"-min-version", shimVersFlag}, expectedVersion: expectedVersion, diff --git a/ssl/tls_method.c b/ssl/tls_method.c index 155d09a2..8bcdf8f6 100644 --- a/ssl/tls_method.c +++ b/ssl/tls_method.c @@ -71,9 +71,11 @@ static int ssl3_version_from_wire(uint16_t *out_version, case TLS1_VERSION: case TLS1_1_VERSION: case TLS1_2_VERSION: - case TLS1_3_VERSION: *out_version = wire_version; return 1; + case TLS1_3_DRAFT_VERSION: + *out_version = TLS1_3_VERSION; + return 1; } return 0; @@ -85,8 +87,9 @@ static uint16_t ssl3_version_to_wire(uint16_t version) { case TLS1_VERSION: case TLS1_1_VERSION: case TLS1_2_VERSION: - case TLS1_3_VERSION: return version; + case TLS1_3_VERSION: + return TLS1_3_DRAFT_VERSION; } /* It is an error to use this function with an invalid version. */ |