Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/boringssl.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteven Valdez <svaldez@google.com>2016-09-15 23:27:05 +0300
committerCQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>2016-09-27 23:12:22 +0300
commitfdd10998e1db111d43dedcd8662ecd5f6287c4ab (patch)
tree85cdba85e7dea0405ee85f9fe91b26edf9894a36
parent2fb2ffb660ff9258188b501bf6eadf3a45bea375 (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.h2
-rw-r--r--include/openssl/tls1.h9
-rw-r--r--ssl/handshake_client.c4
-rw-r--r--ssl/handshake_server.c94
-rw-r--r--ssl/ssl_asn1.c6
-rw-r--r--ssl/ssl_lib.c22
-rw-r--r--ssl/t1_lib.c68
-rw-r--r--ssl/test/runner/common.go37
-rw-r--r--ssl/test/runner/dtls.go8
-rw-r--r--ssl/test/runner/handshake_client.go23
-rw-r--r--ssl/test/runner/handshake_messages.go31
-rw-r--r--ssl/test/runner/handshake_server.go37
-rw-r--r--ssl/test/runner/runner.go111
-rw-r--r--ssl/tls_method.c7
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. */