Revise version negotiation logic on the C side.
This is in preparation for upcoming experiments which will require
supporting multiple experimental versions of TLS 1.3 with, on the
server, the ability to enable multiple variants at once. This means the
version <-> wire bijection no longer exists, even when limiting to a
single SSL*. Thus version_to_wire is removed and instead we treat the
wire version as the canonical version value.
There is a mapping from valid wire versions to protocol versions which
describe the high-level handshake protocol in use. This mapping is not
injective, so uses of version_from_wire are rewritten differently.
All the version-munging logic is moved to ssl_versions.c with a master
preference list of all TLS and DTLS versions. The legacy version
negotiation is converted to the new scheme. The version lists and
negotiation are driven by the preference lists and a
ssl_supports_version API.
To simplify the mess around SSL_SESSION and versions, version_from_wire
is now DTLS/TLS-agnostic, with any filtering being done by
ssl_supports_version. This is screwy but allows parsing SSL_SESSIONs to
sanity-check it and reject all bogus versions in SSL_SESSION. This
reduces a mess of error cases.
As part of this, the weird logic where ssl->version is set early when
sending the ClientHello is removed. The one place where we were relying
on this behavior is tweaked to query hs->max_version instead.
Change-Id: Ic91b348481ceba94d9ae06d6781187c11adc15b0
Reviewed-on: https://boringssl-review.googlesource.com/17524
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/ssl/CMakeLists.txt b/ssl/CMakeLists.txt
index 3e9cd40..0c09443 100644
--- a/ssl/CMakeLists.txt
+++ b/ssl/CMakeLists.txt
@@ -29,6 +29,7 @@
ssl_session.c
ssl_stat.c
ssl_transcript.c
+ ssl_versions.c
ssl_x509.c
t1_enc.c
t1_lib.c
diff --git a/ssl/dtls_method.c b/ssl/dtls_method.c
index 6084789..dd8d786 100644
--- a/ssl/dtls_method.c
+++ b/ssl/dtls_method.c
@@ -66,35 +66,6 @@
#include "internal.h"
-static int dtls1_version_from_wire(uint16_t *out_version,
- uint16_t wire_version) {
- switch (wire_version) {
- case DTLS1_VERSION:
- /* DTLS 1.0 maps to TLS 1.1, not TLS 1.0. */
- *out_version = TLS1_1_VERSION;
- return 1;
- case DTLS1_2_VERSION:
- *out_version = TLS1_2_VERSION;
- return 1;
- }
-
- return 0;
-}
-
-static uint16_t dtls1_version_to_wire(uint16_t version) {
- switch (version) {
- case TLS1_1_VERSION:
- /* DTLS 1.0 maps to TLS 1.1, not TLS 1.0. */
- return DTLS1_VERSION;
- case TLS1_2_VERSION:
- return DTLS1_2_VERSION;
- }
-
- /* It is an error to use this function with an invalid version. */
- assert(0);
- return 0;
-}
-
static int dtls1_supports_cipher(const SSL_CIPHER *cipher) {
return cipher->algorithm_enc != SSL_eNULL;
}
@@ -134,10 +105,6 @@
static const SSL_PROTOCOL_METHOD kDTLSProtocolMethod = {
1 /* is_dtls */,
- TLS1_1_VERSION,
- TLS1_2_VERSION,
- dtls1_version_from_wire,
- dtls1_version_to_wire,
dtls1_new,
dtls1_free,
dtls1_get_message,
diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c
index dddf602..c772f77 100644
--- a/ssl/handshake_client.c
+++ b/ssl/handshake_client.c
@@ -717,33 +717,26 @@
return -1;
}
- uint16_t max_wire_version = ssl->method->version_to_wire(hs->max_version);
- assert(hs->state == SSL3_ST_CW_CLNT_HELLO_A);
- if (!ssl->s3->have_version) {
- ssl->version = max_wire_version;
- }
-
/* Always advertise the ClientHello version from the original maximum version,
* even on renegotiation. The static RSA key exchange uses this field, and
* some servers fail when it changes across handshakes. */
- hs->client_version = max_wire_version;
- if (hs->max_version >= TLS1_3_VERSION) {
- hs->client_version = ssl->method->version_to_wire(TLS1_2_VERSION);
+ if (SSL_is_dtls(hs->ssl)) {
+ hs->client_version =
+ hs->max_version >= TLS1_2_VERSION ? DTLS1_2_VERSION : DTLS1_VERSION;
+ } else {
+ hs->client_version =
+ hs->max_version >= TLS1_2_VERSION ? TLS1_2_VERSION : hs->max_version;
}
/* If the configured session has expired or was created at a disabled
* version, drop it. */
if (ssl->session != NULL) {
- uint16_t session_version;
if (ssl->session->is_server ||
- !ssl->method->version_from_wire(&session_version,
- ssl->session->ssl_version) ||
- (session_version < TLS1_3_VERSION &&
- ssl->session->session_id_length == 0) ||
+ !ssl_supports_version(hs, ssl->session->ssl_version) ||
+ (ssl->session->session_id_length == 0 &&
+ ssl->session->tlsext_ticklen == 0) ||
ssl->session->not_resumable ||
- !ssl_session_is_time_valid(ssl, ssl->session) ||
- session_version < hs->min_version ||
- session_version > hs->max_version) {
+ !ssl_session_is_time_valid(ssl, ssl->session)) {
ssl_set_session(ssl, NULL);
}
}
@@ -798,7 +791,7 @@
static int ssl3_get_server_hello(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
CBS server_hello, server_random, session_id;
- uint16_t server_wire_version, cipher_suite;
+ uint16_t server_version, cipher_suite;
uint8_t compression_method;
int ret = ssl->method->ssl_get_message(ssl);
@@ -826,15 +819,13 @@
CBS_init(&server_hello, ssl->init_msg, ssl->init_num);
- if (!CBS_get_u16(&server_hello, &server_wire_version)) {
+ if (!CBS_get_u16(&server_hello, &server_version)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
return -1;
}
- uint16_t server_version;
- if (!ssl->method->version_from_wire(&server_version, server_wire_version) ||
- server_version < hs->min_version || server_version > hs->max_version) {
+ if (!ssl_supports_version(hs, server_version)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_PROTOCOL_VERSION);
return -1;
@@ -842,11 +833,11 @@
assert(ssl->s3->have_version == ssl->s3->initial_handshake_complete);
if (!ssl->s3->have_version) {
- ssl->version = server_wire_version;
+ ssl->version = server_version;
/* At this point, the connection's version is known and ssl->version is
* fixed. Begin enforcing the record-layer version. */
ssl->s3->have_version = 1;
- } else if (server_wire_version != ssl->version) {
+ } else if (server_version != ssl->version) {
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_PROTOCOL_VERSION);
return -1;
diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c
index d591c80..a2c7f92 100644
--- a/ssl/handshake_server.c
+++ b/ssl/handshake_server.c
@@ -471,12 +471,9 @@
const SSL_CLIENT_HELLO *client_hello) {
SSL *const ssl = hs->ssl;
assert(!ssl->s3->have_version);
- uint16_t version = 0;
- /* Check supported_versions extension if it is present. */
- CBS supported_versions;
+ CBS supported_versions, versions;
if (ssl_client_hello_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) {
@@ -484,89 +481,63 @@
*out_alert = SSL_AD_DECODE_ERROR;
return 0;
}
-
- /* Choose the newest commonly-supported version advertised by the client.
- * The client orders the versions according to its preferences, but we're
- * not required to honor the client's preferences. */
- 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 (hs->min_version <= ext_version &&
- ext_version <= hs->max_version &&
- (!found_version || version < ext_version)) {
- version = ext_version;
- found_version = 1;
- }
- }
-
- if (!found_version) {
- goto unsupported_protocol;
- }
} else {
- /* Process ClientHello.version instead. Note that versions beyond (D)TLS 1.2
- * do not use this mechanism. */
+ /* Convert the ClientHello version to an equivalent supported_versions
+ * extension. */
+ static const uint8_t kTLSVersions[] = {
+ 0x03, 0x03, /* TLS 1.2 */
+ 0x03, 0x02, /* TLS 1.1 */
+ 0x03, 0x01, /* TLS 1 */
+ 0x03, 0x00, /* SSL 3 */
+ };
+
+ static const uint8_t kDTLSVersions[] = {
+ 0xfe, 0xfd, /* DTLS 1.2 */
+ 0xfe, 0xff, /* DTLS 1.0 */
+ };
+
+ size_t versions_len = 0;
if (SSL_is_dtls(ssl)) {
if (client_hello->version <= DTLS1_2_VERSION) {
- version = TLS1_2_VERSION;
+ versions_len = 4;
} else if (client_hello->version <= DTLS1_VERSION) {
- version = TLS1_1_VERSION;
- } else {
- goto unsupported_protocol;
+ versions_len = 2;
}
+ CBS_init(&versions, kDTLSVersions + sizeof(kDTLSVersions) - versions_len,
+ versions_len);
} else {
if (client_hello->version >= TLS1_2_VERSION) {
- version = TLS1_2_VERSION;
+ versions_len = 8;
} else if (client_hello->version >= TLS1_1_VERSION) {
- version = TLS1_1_VERSION;
+ versions_len = 6;
} else if (client_hello->version >= TLS1_VERSION) {
- version = TLS1_VERSION;
+ versions_len = 4;
} else if (client_hello->version >= SSL3_VERSION) {
- version = SSL3_VERSION;
- } else {
- goto unsupported_protocol;
+ versions_len = 2;
}
- }
-
- /* Apply our minimum and maximum version. */
- if (version > hs->max_version) {
- version = hs->max_version;
- }
-
- if (version < hs->min_version) {
- goto unsupported_protocol;
+ CBS_init(&versions, kTLSVersions + sizeof(kTLSVersions) - versions_len,
+ versions_len);
}
}
- /* Handle FALLBACK_SCSV. */
- if (ssl_client_cipher_list_contains_cipher(client_hello,
- SSL3_CK_FALLBACK_SCSV & 0xffff) &&
- version < hs->max_version) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_INAPPROPRIATE_FALLBACK);
- *out_alert = SSL3_AD_INAPPROPRIATE_FALLBACK;
+ if (!ssl_negotiate_version(hs, out_alert, &ssl->version, &versions)) {
return 0;
}
- hs->client_version = client_hello->version;
- ssl->version = ssl->method->version_to_wire(version);
-
/* At this point, the connection's version is known and |ssl->version| is
* fixed. Begin enforcing the record-layer version. */
ssl->s3->have_version = 1;
- return 1;
+ /* Handle FALLBACK_SCSV. */
+ if (ssl_client_cipher_list_contains_cipher(client_hello,
+ SSL3_CK_FALLBACK_SCSV & 0xffff) &&
+ ssl3_protocol_version(ssl) < hs->max_version) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INAPPROPRIATE_FALLBACK);
+ *out_alert = SSL3_AD_INAPPROPRIATE_FALLBACK;
+ return 0;
+ }
-unsupported_protocol:
- OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
- *out_alert = SSL_AD_PROTOCOL_VERSION;
- return 0;
+ return 1;
}
static STACK_OF(SSL_CIPHER) *
@@ -759,7 +730,7 @@
return -1;
}
- /* Load the client random. */
+ hs->client_version = client_hello.version;
if (client_hello.random_len != SSL3_RANDOM_SIZE) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return -1;
diff --git a/ssl/internal.h b/ssl/internal.h
index d56c73b..f6cea7a 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -163,6 +163,57 @@
#endif
+typedef struct ssl_handshake_st SSL_HANDSHAKE;
+
+/* Protocol versions.
+ *
+ * Due to DTLS's historical wire version differences and to support multiple
+ * variants of the same protocol during development, we maintain two notions of
+ * version.
+ *
+ * The "version" or "wire version" is the actual 16-bit value that appears on
+ * the wire. It uniquely identifies a version and is also used at API
+ * boundaries. The set of supported versions differs between TLS and DTLS. Wire
+ * versions are opaque values and may not be compared numerically.
+ *
+ * The "protocol version" identifies the high-level handshake variant being
+ * used. DTLS versions map to the corresponding TLS versions. Draft TLS 1.3
+ * variants all map to TLS 1.3. Protocol versions are sequential and may be
+ * compared numerically. */
+
+/* ssl_protocol_version_from_wire sets |*out| to the protocol version
+ * corresponding to wire version |version| and returns one. If |version| is not
+ * a valid TLS or DTLS version, it returns zero.
+ *
+ * Note this simultaneously handles both DTLS and TLS. Use one of the
+ * higher-level functions below for most operations. */
+int ssl_protocol_version_from_wire(uint16_t *out, uint16_t version);
+
+/* ssl_get_version_range sets |*out_min_version| and |*out_max_version| to the
+ * minimum and maximum enabled protocol versions, respectively. */
+int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
+ uint16_t *out_max_version);
+
+/* ssl_supports_version returns one if |hs| supports |version| and zero
+ * otherwise. */
+int ssl_supports_version(SSL_HANDSHAKE *hs, uint16_t version);
+
+/* ssl_add_supported_versions writes the supported versions of |hs| to |cbb|, in
+ * decreasing preference order. */
+int ssl_add_supported_versions(SSL_HANDSHAKE *hs, CBB *cbb);
+
+/* ssl_negotiate_version negotiates a common version based on |hs|'s preferences
+ * and the peer preference list in |peer_versions|. On success, it returns one
+ * and sets |*out_version| to the selected version. Otherwise, it returns zero
+ * and sets |*out_alert| to an alert to send. */
+int ssl_negotiate_version(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+ uint16_t *out_version, const CBS *peer_versions);
+
+/* ssl3_protocol_version returns |ssl|'s protocol version. It is an error to
+ * call this function before the version is determined. */
+uint16_t ssl3_protocol_version(const SSL *ssl);
+
+
/* Cipher suites. */
/* Bits for |algorithm_mkey| (key exchange algorithm). */
@@ -542,8 +593,6 @@
/* Private key operations. */
-typedef struct ssl_handshake_st SSL_HANDSHAKE;
-
/* ssl_has_private_key returns one if |ssl| has a private key
* configured and zero otherwise. */
int ssl_has_private_key(const SSL *ssl);
@@ -1414,17 +1463,6 @@
struct ssl_protocol_method_st {
/* is_dtls is one if the protocol is DTLS and zero otherwise. */
char is_dtls;
- /* min_version is the minimum implemented version. */
- uint16_t min_version;
- /* max_version is the maximum implemented version. */
- uint16_t max_version;
- /* version_from_wire maps |wire_version| to a protocol version. On success, it
- * sets |*out_version| to the result and returns one. If the version is
- * unknown, it returns zero. */
- int (*version_from_wire)(uint16_t *out_version, uint16_t wire_version);
- /* version_to_wire maps |version| to the wire representation. It is an error
- * to call it with an invalid version. */
- uint16_t (*version_to_wire)(uint16_t version);
int (*ssl_new)(SSL *ssl);
void (*ssl_free)(SSL *ssl);
/* ssl_get_message reads the next handshake message. On success, it returns
@@ -1846,7 +1884,7 @@
const SSL_PROTOCOL_METHOD *method;
/* version is the protocol version. */
- int version;
+ uint16_t version;
/* conf_max_version is the maximum acceptable protocol version configured by
* |SSL_set_max_proto_version|. Note this version is normalized in DTLS and is
@@ -2049,10 +2087,12 @@
int ssl_session_is_resumable(const SSL_HANDSHAKE *hs,
const SSL_SESSION *session);
-/* SSL_SESSION_get_digest returns the digest used in |session|. If the digest is
- * invalid, it returns NULL. */
-const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session,
- const SSL *ssl);
+/* SSL_SESSION_protocol_version returns the protocol version associated with
+ * |session|. */
+uint16_t SSL_SESSION_protocol_version(const SSL_SESSION *session);
+
+/* SSL_SESSION_get_digest returns the digest used in |session|. */
+const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session);
void ssl_set_session(SSL *ssl, SSL_SESSION *session);
@@ -2285,15 +2325,6 @@
/* ssl_can_read returns one if |ssl| is allowed to read and zero otherwise. */
int ssl_can_read(const SSL *ssl);
-/* ssl_get_version_range sets |*out_min_version| and |*out_max_version| to the
- * minimum and maximum enabled protocol versions, respectively. */
-int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
- uint16_t *out_max_version);
-
-/* ssl3_protocol_version returns |ssl|'s protocol version. It is an error to
- * call this function before the version is determined. */
-uint16_t ssl3_protocol_version(const SSL *ssl);
-
void ssl_get_current_time(const SSL *ssl, struct OPENSSL_timeval *out_clock);
/* ssl_reset_error_state resets state for |SSL_get_error|. */
diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c
index f368ff2..cc6a559 100644
--- a/ssl/ssl_asn1.c
+++ b/ssl/ssl_asn1.c
@@ -576,10 +576,17 @@
CBS session;
uint64_t version, ssl_version;
+ uint16_t unused;
if (!CBS_get_asn1(cbs, &session, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1_uint64(&session, &version) ||
version != kVersion ||
- !CBS_get_asn1_uint64(&session, &ssl_version)) {
+ !CBS_get_asn1_uint64(&session, &ssl_version) ||
+ /* Require sessions have versions valid in either TLS or DTLS. The session
+ * will not be used by the handshake if not applicable, but, for
+ * simplicity, never parse a session that does not pass
+ * |ssl_protocol_version_from_wire|. */
+ ssl_version > UINT16_MAX ||
+ !ssl_protocol_version_from_wire(&unused, ssl_version)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
goto err;
}
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 1b002a5..109dfd0 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -978,60 +978,6 @@
return SSL_ERROR_SYSCALL;
}
-static int set_min_version(const SSL_PROTOCOL_METHOD *method, uint16_t *out,
- uint16_t version) {
- /* Zero is interpreted as the default minimum version. */
- if (version == 0) {
- *out = method->min_version;
- /* SSL 3.0 is disabled unless explicitly enabled. */
- if (*out < TLS1_VERSION) {
- *out = TLS1_VERSION;
- }
- return 1;
- }
-
- if (version == TLS1_3_VERSION) {
- version = TLS1_3_DRAFT_VERSION;
- }
-
- return method->version_from_wire(out, version);
-}
-
-static int set_max_version(const SSL_PROTOCOL_METHOD *method, uint16_t *out,
- uint16_t version) {
- /* Zero is interpreted as the default maximum version. */
- if (version == 0) {
- *out = method->max_version;
- /* TODO(svaldez): Enable TLS 1.3 by default once fully implemented. */
- if (*out > TLS1_2_VERSION) {
- *out = TLS1_2_VERSION;
- }
- return 1;
- }
-
- if (version == TLS1_3_VERSION) {
- version = TLS1_3_DRAFT_VERSION;
- }
-
- return method->version_from_wire(out, version);
-}
-
-int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, uint16_t version) {
- return set_min_version(ctx->method, &ctx->conf_min_version, version);
-}
-
-int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, uint16_t version) {
- return set_max_version(ctx->method, &ctx->conf_max_version, version);
-}
-
-int SSL_set_min_proto_version(SSL *ssl, uint16_t version) {
- return set_min_version(ssl->method, &ssl->conf_min_version, version);
-}
-
-int SSL_set_max_proto_version(SSL *ssl, uint16_t version) {
- return set_max_version(ssl->method, &ssl->conf_max_version, version);
-}
-
uint32_t SSL_CTX_set_options(SSL_CTX *ctx, uint32_t options) {
ctx->options |= options;
return ctx->options;
@@ -1901,43 +1847,6 @@
}
}
-static const char *ssl_get_version(int version) {
- switch (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:
- return "TLSv1.2";
-
- case TLS1_1_VERSION:
- return "TLSv1.1";
-
- case TLS1_VERSION:
- return "TLSv1";
-
- case SSL3_VERSION:
- return "SSLv3";
-
- case DTLS1_VERSION:
- return "DTLSv1";
-
- case DTLS1_2_VERSION:
- return "DTLSv1.2";
-
- default:
- return "unknown";
- }
-}
-
-const char *SSL_get_version(const SSL *ssl) {
- return ssl_get_version(ssl->version);
-}
-
-const char *SSL_SESSION_get_version(const SSL_SESSION *session) {
- return ssl_get_version(session->ssl_version);
-}
-
EVP_PKEY *SSL_get_privatekey(const SSL *ssl) {
if (ssl->cert != NULL) {
return ssl->cert->privatekey;
@@ -2015,15 +1924,6 @@
return ret;
}
-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; }
SSL_CTX *SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx) {
@@ -2329,102 +2229,6 @@
cipher->algorithm_mac == SSL_AEAD;
}
-const struct {
- uint16_t version;
- uint32_t flag;
-} kVersions[] = {
- {SSL3_VERSION, SSL_OP_NO_SSLv3},
- {TLS1_VERSION, SSL_OP_NO_TLSv1},
- {TLS1_1_VERSION, SSL_OP_NO_TLSv1_1},
- {TLS1_2_VERSION, SSL_OP_NO_TLSv1_2},
- {TLS1_3_VERSION, SSL_OP_NO_TLSv1_3},
-};
-
-static const size_t kVersionsLen = OPENSSL_ARRAY_SIZE(kVersions);
-
-int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
- uint16_t *out_max_version) {
- /* For historical reasons, |SSL_OP_NO_DTLSv1| aliases |SSL_OP_NO_TLSv1|, but
- * DTLS 1.0 should be mapped to TLS 1.1. */
- uint32_t options = ssl->options;
- if (SSL_is_dtls(ssl)) {
- options &= ~SSL_OP_NO_TLSv1_1;
- if (options & SSL_OP_NO_DTLSv1) {
- options |= SSL_OP_NO_TLSv1_1;
- }
- }
-
- uint16_t min_version = ssl->conf_min_version;
- uint16_t max_version = ssl->conf_max_version;
-
- /* Bound the range to only those implemented in this protocol. */
- if (min_version < ssl->method->min_version) {
- min_version = ssl->method->min_version;
- }
- if (max_version > ssl->method->max_version) {
- max_version = ssl->method->max_version;
- }
-
- /* OpenSSL's API for controlling versions entails blacklisting individual
- * protocols. This has two problems. First, on the client, the protocol can
- * only express a contiguous range of versions. Second, a library consumer
- * trying to set a maximum version cannot disable protocol versions that get
- * added in a future version of the library.
- *
- * To account for both of these, OpenSSL interprets the client-side bitmask
- * as a min/max range by picking the lowest contiguous non-empty range of
- * enabled protocols. Note that this means it is impossible to set a maximum
- * version of the higest supported TLS version in a future-proof way. */
- int any_enabled = 0;
- for (size_t i = 0; i < kVersionsLen; i++) {
- /* Only look at the versions already enabled. */
- if (min_version > kVersions[i].version) {
- continue;
- }
- if (max_version < kVersions[i].version) {
- break;
- }
-
- if (!(options & kVersions[i].flag)) {
- /* The minimum version is the first enabled version. */
- if (!any_enabled) {
- any_enabled = 1;
- min_version = kVersions[i].version;
- }
- continue;
- }
-
- /* If there is a disabled version after the first enabled one, all versions
- * after it are implicitly disabled. */
- if (any_enabled) {
- max_version = kVersions[i-1].version;
- break;
- }
- }
-
- if (!any_enabled) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SUPPORTED_VERSIONS_ENABLED);
- return 0;
- }
-
- *out_min_version = min_version;
- *out_max_version = max_version;
- return 1;
-}
-
-uint16_t ssl3_protocol_version(const SSL *ssl) {
- assert(ssl->s3->have_version);
- uint16_t version;
- if (!ssl->method->version_from_wire(&version, ssl->version)) {
- /* TODO(davidben): Use the internal version representation for ssl->version
- * and map to the public API representation at API boundaries. */
- assert(0);
- return 0;
- }
-
- return version;
-}
-
int SSL_is_server(const SSL *ssl) { return ssl->server; }
int SSL_is_dtls(const SSL *ssl) { return ssl->method->is_dtls; }
diff --git a/ssl/ssl_session.c b/ssl/ssl_session.c
index f025364..1670eb7 100644
--- a/ssl/ssl_session.c
+++ b/ssl/ssl_session.c
@@ -505,14 +505,21 @@
return CRYPTO_get_ex_data(&session->ex_data, idx);
}
-const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session,
- const SSL *ssl) {
- uint16_t version;
- if (!ssl->method->version_from_wire(&version, session->ssl_version)) {
- return NULL;
+uint16_t SSL_SESSION_protocol_version(const SSL_SESSION *session) {
+ uint16_t ret;
+ if (!ssl_protocol_version_from_wire(&ret, session->ssl_version)) {
+ /* An |SSL_SESSION| will never have an invalid version. This is enforced by
+ * the parser. */
+ assert(0);
+ return 0;
}
- return ssl_get_handshake_digest(session->cipher->algorithm_prf, version);
+ return ret;
+}
+
+const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session) {
+ return ssl_get_handshake_digest(session->cipher->algorithm_prf,
+ SSL_SESSION_protocol_version(session));
}
int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server) {
diff --git a/ssl/ssl_versions.c b/ssl/ssl_versions.c
new file mode 100644
index 0000000..6565688
--- /dev/null
+++ b/ssl/ssl_versions.c
@@ -0,0 +1,326 @@
+/* Copyright (c) 2017, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/ssl.h>
+
+#include <assert.h>
+
+#include <openssl/bytestring.h>
+#include <openssl/err.h>
+
+#include "internal.h"
+#include "../crypto/internal.h"
+
+
+int ssl_protocol_version_from_wire(uint16_t *out, uint16_t version) {
+ switch (version) {
+ case SSL3_VERSION:
+ case TLS1_VERSION:
+ case TLS1_1_VERSION:
+ case TLS1_2_VERSION:
+ *out = version;
+ return 1;
+
+ case TLS1_3_DRAFT_VERSION:
+ *out = TLS1_3_VERSION;
+ return 1;
+
+ case DTLS1_VERSION:
+ /* DTLS 1.0 is analogous to TLS 1.1, not TLS 1.0. */
+ *out = TLS1_1_VERSION;
+ return 1;
+
+ case DTLS1_2_VERSION:
+ *out = TLS1_2_VERSION;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* The follow arrays are the supported versions for TLS and DTLS, in order of
+ * decreasing preference. */
+
+static const uint16_t kTLSVersions[] = {
+ TLS1_3_DRAFT_VERSION,
+ TLS1_2_VERSION,
+ TLS1_1_VERSION,
+ TLS1_VERSION,
+ SSL3_VERSION,
+};
+
+static const uint16_t kDTLSVersions[] = {
+ DTLS1_2_VERSION,
+ DTLS1_VERSION,
+};
+
+static void get_method_versions(const SSL_PROTOCOL_METHOD *method,
+ const uint16_t **out, size_t *out_num) {
+ if (method->is_dtls) {
+ *out = kDTLSVersions;
+ *out_num = OPENSSL_ARRAY_SIZE(kDTLSVersions);
+ } else {
+ *out = kTLSVersions;
+ *out_num = OPENSSL_ARRAY_SIZE(kTLSVersions);
+ }
+}
+
+static int method_supports_version(const SSL_PROTOCOL_METHOD *method,
+ uint16_t version) {
+ const uint16_t *versions;
+ size_t num_versions;
+ get_method_versions(method, &versions, &num_versions);
+ for (size_t i = 0; i < num_versions; i++) {
+ if (versions[i] == version) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int set_version_bound(const SSL_PROTOCOL_METHOD *method, uint16_t *out,
+ uint16_t version) {
+ /* The public API uses wire versions, except we use |TLS1_3_VERSION|
+ * everywhere to refer to any draft TLS 1.3 versions. In this direction, we
+ * map it to some representative TLS 1.3 draft version. */
+ if (version == TLS1_3_VERSION) {
+ version = TLS1_3_DRAFT_VERSION;
+ }
+
+ return method_supports_version(method, version) &&
+ ssl_protocol_version_from_wire(out, version);
+}
+
+static int set_min_version(const SSL_PROTOCOL_METHOD *method, uint16_t *out,
+ uint16_t version) {
+ /* Zero is interpreted as the default minimum version. */
+ if (version == 0) {
+ /* SSL 3.0 is disabled by default and TLS 1.0 does not exist in DTLS. */
+ *out = method->is_dtls ? TLS1_1_VERSION : TLS1_VERSION;
+ return 1;
+ }
+
+ return set_version_bound(method, out, version);
+}
+
+static int set_max_version(const SSL_PROTOCOL_METHOD *method, uint16_t *out,
+ uint16_t version) {
+ /* Zero is interpreted as the default maximum version. */
+ if (version == 0) {
+ *out = TLS1_2_VERSION;
+ return 1;
+ }
+
+ return set_version_bound(method, out, version);
+}
+
+int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, uint16_t version) {
+ return set_min_version(ctx->method, &ctx->conf_min_version, version);
+}
+
+int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, uint16_t version) {
+ return set_max_version(ctx->method, &ctx->conf_max_version, version);
+}
+
+int SSL_set_min_proto_version(SSL *ssl, uint16_t version) {
+ return set_min_version(ssl->method, &ssl->conf_min_version, version);
+}
+
+int SSL_set_max_proto_version(SSL *ssl, uint16_t version) {
+ return set_max_version(ssl->method, &ssl->conf_max_version, version);
+}
+
+const struct {
+ uint16_t version;
+ uint32_t flag;
+} kProtocolVersions[] = {
+ {SSL3_VERSION, SSL_OP_NO_SSLv3},
+ {TLS1_VERSION, SSL_OP_NO_TLSv1},
+ {TLS1_1_VERSION, SSL_OP_NO_TLSv1_1},
+ {TLS1_2_VERSION, SSL_OP_NO_TLSv1_2},
+ {TLS1_3_VERSION, SSL_OP_NO_TLSv1_3},
+};
+
+int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
+ uint16_t *out_max_version) {
+ /* For historical reasons, |SSL_OP_NO_DTLSv1| aliases |SSL_OP_NO_TLSv1|, but
+ * DTLS 1.0 should be mapped to TLS 1.1. */
+ uint32_t options = ssl->options;
+ if (SSL_is_dtls(ssl)) {
+ options &= ~SSL_OP_NO_TLSv1_1;
+ if (options & SSL_OP_NO_DTLSv1) {
+ options |= SSL_OP_NO_TLSv1_1;
+ }
+ }
+
+ uint16_t min_version = ssl->conf_min_version;
+ uint16_t max_version = ssl->conf_max_version;
+
+ /* OpenSSL's API for controlling versions entails blacklisting individual
+ * protocols. This has two problems. First, on the client, the protocol can
+ * only express a contiguous range of versions. Second, a library consumer
+ * trying to set a maximum version cannot disable protocol versions that get
+ * added in a future version of the library.
+ *
+ * To account for both of these, OpenSSL interprets the client-side bitmask
+ * as a min/max range by picking the lowest contiguous non-empty range of
+ * enabled protocols. Note that this means it is impossible to set a maximum
+ * version of the higest supported TLS version in a future-proof way. */
+ int any_enabled = 0;
+ for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kProtocolVersions); i++) {
+ /* Only look at the versions already enabled. */
+ if (min_version > kProtocolVersions[i].version) {
+ continue;
+ }
+ if (max_version < kProtocolVersions[i].version) {
+ break;
+ }
+
+ if (!(options & kProtocolVersions[i].flag)) {
+ /* The minimum version is the first enabled version. */
+ if (!any_enabled) {
+ any_enabled = 1;
+ min_version = kProtocolVersions[i].version;
+ }
+ continue;
+ }
+
+ /* If there is a disabled version after the first enabled one, all versions
+ * after it are implicitly disabled. */
+ if (any_enabled) {
+ max_version = kProtocolVersions[i-1].version;
+ break;
+ }
+ }
+
+ if (!any_enabled) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SUPPORTED_VERSIONS_ENABLED);
+ return 0;
+ }
+
+ *out_min_version = min_version;
+ *out_max_version = max_version;
+ return 1;
+}
+
+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;
+}
+
+static const char *ssl_get_version(int version) {
+ switch (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:
+ return "TLSv1.2";
+
+ case TLS1_1_VERSION:
+ return "TLSv1.1";
+
+ case TLS1_VERSION:
+ return "TLSv1";
+
+ case SSL3_VERSION:
+ return "SSLv3";
+
+ case DTLS1_VERSION:
+ return "DTLSv1";
+
+ case DTLS1_2_VERSION:
+ return "DTLSv1.2";
+
+ default:
+ return "unknown";
+ }
+}
+
+const char *SSL_get_version(const SSL *ssl) {
+ return ssl_get_version(ssl->version);
+}
+
+const char *SSL_SESSION_get_version(const SSL_SESSION *session) {
+ return ssl_get_version(session->ssl_version);
+}
+
+uint16_t ssl3_protocol_version(const SSL *ssl) {
+ assert(ssl->s3->have_version);
+ uint16_t version;
+ if (!ssl_protocol_version_from_wire(&version, ssl->version)) {
+ /* |ssl->version| will always be set to a valid version. */
+ assert(0);
+ return 0;
+ }
+
+ return version;
+}
+
+int ssl_supports_version(SSL_HANDSHAKE *hs, uint16_t version) {
+ uint16_t protocol_version;
+ return method_supports_version(hs->ssl->method, version) &&
+ ssl_protocol_version_from_wire(&protocol_version, version) &&
+ hs->min_version <= protocol_version &&
+ protocol_version <= hs->max_version;
+}
+
+int ssl_add_supported_versions(SSL_HANDSHAKE *hs, CBB *cbb) {
+ const uint16_t *versions;
+ size_t num_versions;
+ get_method_versions(hs->ssl->method, &versions, &num_versions);
+ for (size_t i = 0; i < num_versions; i++) {
+ if (ssl_supports_version(hs, versions[i]) &&
+ !CBB_add_u16(cbb, versions[i])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int ssl_negotiate_version(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+ uint16_t *out_version, const CBS *peer_versions) {
+ const uint16_t *versions;
+ size_t num_versions;
+ get_method_versions(hs->ssl->method, &versions, &num_versions);
+ for (size_t i = 0; i < num_versions; i++) {
+ if (!ssl_supports_version(hs, versions[i])) {
+ continue;
+ }
+
+ CBS copy = *peer_versions;
+ while (CBS_len(©) != 0) {
+ uint16_t version;
+ if (!CBS_get_u16(©, &version)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+ *out_alert = SSL_AD_DECODE_ERROR;
+ return 0;
+ }
+
+ if (version == versions[i]) {
+ *out_version = version;
+ return 1;
+ }
+ }
+ }
+
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
+ *out_alert = SSL_AD_PROTOCOL_VERSION;
+ return 0;
+}
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 1b14371..02f5a30 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -970,14 +970,11 @@
* advertise the extension to avoid potentially breaking servers which carry
* over the state from the previous handshake, such as OpenSSL servers
* without upstream's 3c3f0259238594d77264a78944d409f2127642c4. */
- uint16_t session_version;
if (!ssl->s3->initial_handshake_complete &&
ssl->session != NULL &&
ssl->session->tlsext_tick != NULL &&
/* Don't send TLS 1.3 session tickets in the ticket extension. */
- ssl->method->version_from_wire(&session_version,
- ssl->session->ssl_version) &&
- session_version < TLS1_3_VERSION) {
+ SSL_SESSION_protocol_version(ssl->session) < TLS1_3_VERSION) {
ticket_data = ssl->session->tlsext_tick;
ticket_len = ssl->session->tlsext_ticklen;
}
@@ -1863,31 +1860,19 @@
static size_t ext_pre_shared_key_clienthello_length(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
- uint16_t session_version;
if (hs->max_version < TLS1_3_VERSION || ssl->session == NULL ||
- !ssl->method->version_from_wire(&session_version,
- ssl->session->ssl_version) ||
- session_version < TLS1_3_VERSION) {
+ SSL_SESSION_protocol_version(ssl->session) < TLS1_3_VERSION) {
return 0;
}
- const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session, ssl);
- if (digest == NULL) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
- }
-
- size_t binder_len = EVP_MD_size(digest);
+ size_t binder_len = EVP_MD_size(SSL_SESSION_get_digest(ssl->session));
return 15 + ssl->session->tlsext_ticklen + binder_len;
}
static int ext_pre_shared_key_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
- uint16_t session_version;
if (hs->max_version < TLS1_3_VERSION || ssl->session == NULL ||
- !ssl->method->version_from_wire(&session_version,
- ssl->session->ssl_version) ||
- session_version < TLS1_3_VERSION) {
+ SSL_SESSION_protocol_version(ssl->session) < TLS1_3_VERSION) {
return 1;
}
@@ -1899,14 +1884,7 @@
/* Fill in a placeholder zero binder of the appropriate length. It will be
* computed and filled in later after length prefixes are computed. */
uint8_t zero_binder[EVP_MAX_MD_SIZE] = {0};
-
- const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session, ssl);
- if (digest == NULL) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
- }
-
- size_t binder_len = EVP_MD_size(digest);
+ size_t binder_len = EVP_MD_size(SSL_SESSION_get_digest(ssl->session));
CBB contents, identity, ticket, binders, binder;
if (!CBB_add_u16(out, TLSEXT_TYPE_pre_shared_key) ||
@@ -2071,11 +2049,8 @@
static int ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
- uint16_t session_version;
if (ssl->session == NULL ||
- !ssl->method->version_from_wire(&session_version,
- ssl->session->ssl_version) ||
- session_version < TLS1_3_VERSION ||
+ SSL_SESSION_protocol_version(ssl->session) < TLS1_3_VERSION ||
ssl->session->ticket_max_early_data == 0 ||
hs->received_hello_retry_request ||
!ssl->cert->enable_early_data) {
@@ -2375,14 +2350,8 @@
return 0;
}
- for (uint16_t version = hs->max_version; version >= hs->min_version;
- version--) {
- if (!CBB_add_u16(&versions, ssl->method->version_to_wire(version))) {
- return 0;
- }
- }
-
- if (!CBB_flush(out)) {
+ if (!ssl_add_supported_versions(hs, &versions) ||
+ !CBB_flush(out)) {
return 0;
}
diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c
index 2137fe2..97f0ed9 100644
--- a/ssl/tls13_enc.c
+++ b/ssl/tls13_enc.c
@@ -54,15 +54,8 @@
int tls13_init_early_key_schedule(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
- uint16_t session_version;
- if (!ssl->method->version_from_wire(&session_version,
- ssl->session->ssl_version) ||
- !init_key_schedule(hs, session_version,
- ssl->session->cipher->algorithm_prf)) {
- return 0;
- }
-
- return 1;
+ return init_key_schedule(hs, SSL_SESSION_protocol_version(ssl->session),
+ ssl->session->cipher->algorithm_prf);
}
int tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in,
@@ -122,11 +115,7 @@
const uint8_t *traffic_secret,
size_t traffic_secret_len) {
const SSL_SESSION *session = SSL_get_session(ssl);
- uint16_t version;
- if (!ssl->method->version_from_wire(&version, session->ssl_version)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
- }
+ uint16_t version = SSL_SESSION_protocol_version(session);
if (traffic_secret_len > 0xff) {
OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
@@ -141,8 +130,7 @@
return 0;
}
- const EVP_MD *digest = ssl_get_handshake_digest(
- session->cipher->algorithm_prf, version);
+ const EVP_MD *digest = SSL_SESSION_get_digest(session);
/* Derive the key. */
size_t key_len = EVP_AEAD_key_length(aead);
@@ -383,11 +371,7 @@
int tls13_write_psk_binder(SSL_HANDSHAKE *hs, uint8_t *msg, size_t len) {
SSL *const ssl = hs->ssl;
- const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session, ssl);
- if (digest == NULL) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
- }
+ const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session);
size_t hash_len = EVP_MD_size(digest);
if (len < hash_len + 3) {
diff --git a/ssl/tls_method.c b/ssl/tls_method.c
index 6144f86..d039b7d 100644
--- a/ssl/tls_method.c
+++ b/ssl/tls_method.c
@@ -65,39 +65,6 @@
#include "internal.h"
-static int ssl3_version_from_wire(uint16_t *out_version,
- uint16_t wire_version) {
- switch (wire_version) {
- case SSL3_VERSION:
- case TLS1_VERSION:
- case TLS1_1_VERSION:
- case TLS1_2_VERSION:
- *out_version = wire_version;
- return 1;
- case TLS1_3_DRAFT_VERSION:
- *out_version = TLS1_3_VERSION;
- return 1;
- }
-
- return 0;
-}
-
-static uint16_t ssl3_version_to_wire(uint16_t version) {
- switch (version) {
- case SSL3_VERSION:
- case TLS1_VERSION:
- case TLS1_1_VERSION:
- case TLS1_2_VERSION:
- return version;
- case TLS1_3_VERSION:
- return TLS1_3_DRAFT_VERSION;
- }
-
- /* It is an error to use this function with an invalid version. */
- assert(0);
- return 0;
-}
-
static int ssl3_supports_cipher(const SSL_CIPHER *cipher) { return 1; }
static void ssl3_expect_flight(SSL *ssl) {}
@@ -130,10 +97,6 @@
static const SSL_PROTOCOL_METHOD kTLSProtocolMethod = {
0 /* is_dtls */,
- SSL3_VERSION,
- TLS1_3_VERSION,
- ssl3_version_from_wire,
- ssl3_version_to_wire,
ssl3_new,
ssl3_free,
ssl3_get_message,
diff --git a/ssl/tls_record.c b/ssl/tls_record.c
index a5bbe93..3bc0b29 100644
--- a/ssl/tls_record.c
+++ b/ssl/tls_record.c
@@ -389,8 +389,10 @@
* ClientHellos should use SSL 3.0 and pre-TLS-1.3 expects the version
* to change after version negotiation. */
uint16_t wire_version = TLS1_VERSION;
- if (ssl->version == SSL3_VERSION ||
- (ssl->s3->have_version && ssl3_protocol_version(ssl) < TLS1_3_VERSION)) {
+ if (ssl->s3->hs != NULL && ssl->s3->hs->max_version == SSL3_VERSION) {
+ wire_version = SSL3_VERSION;
+ }
+ if (ssl->s3->have_version && ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
wire_version = ssl->version;
}