diff --git a/doc/api/errors.md b/doc/api/errors.md index 500fb7801b61fd..96ff284c0a4054 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1655,6 +1655,17 @@ recommended to use 2048 bits or larger for stronger security. A TLS/SSL handshake timed out. In this case, the server must also abort the connection. + +### ERR_TLS_INVALID_PROTOCOL_VERSION + +Valid TLS protocol versions are `'TLSv1'`, `'TLSv1.1'`, or `'TLSv1.2'`. + + +### ERR_TLS_PROTOCOL_VERSION_CONFLICT + +Attempting to set a TLS protocol `minVersion` or `maxVersion` conflicts with an +attempt to set the `secureProtocol` explicitly. Use one mechanism or the other. + ### ERR_TLS_RENEGOTIATE diff --git a/doc/api/tls.md b/doc/api/tls.md index fe3282f97efc39..45d7fbfa2baa9f 100644 --- a/doc/api/tls.md +++ b/doc/api/tls.md @@ -1070,6 +1070,10 @@ changes: pr-url: /~https://github.com/nodejs/node/pull/4099 description: The `ca` option can now be a single string containing multiple CA certificates. + - version: REPLACEME + pr-url: REPLACEME + description: The `minVersion` and `maxVersion` can be used to restrict + the allowed TLS protocol versions. --> * `options` {Object} @@ -1130,6 +1134,16 @@ changes: passphrase: ]}`. The object form can only occur in an array. `object.passphrase` is optional. Encrypted keys will be decrypted with `object.passphrase` if provided, or `options.passphrase` if it is not. + * `maxVersion` {string} Optionally set the maximum TLS version to allow. One + of `TLSv1.2'`, `'TLSv1.1'`, or `'TLSv1'`. Cannot be specified along with the + `secureProtocol` option, use one or the other. **Default:** `'TLSv1.2'`. + * `minVersion` {string} Optionally set the minimum TLS version to allow. One + of `TLSv1.2'`, `'TLSv1.1'`, or `'TLSv1'`. Cannot be specified along with the + `secureProtocol` option, use one or the other. It is not recommended to use + less than TLSv1.2, but it may be required for interoperability. + **Default:** `'TLSv1.2'`, unless changed using CLI options. Using + `--tls-v1.0` changes the default to `'TLSv1'`. Using `--tls-v1.1` changes + the default to `'TLSv1.1'`. * `passphrase` {string} Shared passphrase used for a single private key and/or a PFX. * `pfx` {string|string[]|Buffer|Buffer[]|Object[]} PFX or PKCS12 encoded @@ -1150,10 +1164,7 @@ changes: example, use `'TLSv1_1_method'` to force TLS version 1.1, or `'TLS_method'` to allow any TLS protocol version. It is not recommended to use TLS versions less than 1.2, but it may be required for interoperability. **Default:** - `'TLSv1_2_method'`, unless changed using CLI options. Using the `--tlsv1.0` - CLI option is like `'TLS_method'` except protocols earlier than TLSv1.0 are - not allowed, and using the `--tlsv1.1` CLI option is like `'TLS_method'` - except that protocols earlier than TLSv1.1 are not allowed. + none, see `minVersion`. * `sessionIdContext` {string} Opaque identifier used by servers to ensure session state is not shared between applications. Unused by clients. diff --git a/lib/_tls_common.js b/lib/_tls_common.js index 4028b02be28e17..7ddb0d4757eb80 100644 --- a/lib/_tls_common.js +++ b/lib/_tls_common.js @@ -26,22 +26,46 @@ const { isArrayBufferView } = require('internal/util/types'); const tls = require('tls'); const { ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED, - ERR_INVALID_ARG_TYPE + ERR_INVALID_ARG_TYPE, + ERR_TLS_INVALID_PROTOCOL_VERSION, + ERR_TLS_PROTOCOL_VERSION_CONFLICT, } = require('internal/errors').codes; - -const { SSL_OP_CIPHER_SERVER_PREFERENCE } = internalBinding('constants').crypto; +const { + SSL_OP_CIPHER_SERVER_PREFERENCE, + TLS1_VERSION, + TLS1_1_VERSION, + TLS1_2_VERSION, +} = internalBinding('constants').crypto; // Lazily loaded from internal/crypto/util. let toBuf = null; +function toV(which, v, def) { + if (v == null) v = def; + if (v === 'TLSv1') return TLS1_VERSION; + if (v === 'TLSv1.1') return TLS1_1_VERSION; + if (v === 'TLSv1.2') return TLS1_2_VERSION; + throw new ERR_TLS_INVALID_PROTOCOL_VERSION(v, which); +} + const { SecureContext: NativeSecureContext } = internalBinding('crypto'); -function SecureContext(secureProtocol, secureOptions) { +function SecureContext(secureProtocol, secureOptions, minVersion, maxVersion) { if (!(this instanceof SecureContext)) { - return new SecureContext(secureProtocol, secureOptions); + return new SecureContext(secureProtocol, secureOptions, minVersion, + maxVersion); + } + + if (secureProtocol) { + if (minVersion != null) + throw new ERR_TLS_PROTOCOL_VERSION_CONFLICT(minVersion, secureProtocol); + if (maxVersion != null) + throw new ERR_TLS_PROTOCOL_VERSION_CONFLICT(maxVersion, secureProtocol); } this.context = new NativeSecureContext(); - this.context.init(secureProtocol); + this.context.init(secureProtocol, + toV('minimum', minVersion, tls.DEFAULT_MIN_VERSION), + toV('maximum', maxVersion, tls.DEFAULT_MAX_VERSION)); if (secureOptions) this.context.setOptions(secureOptions); } @@ -66,7 +90,8 @@ exports.createSecureContext = function createSecureContext(options) { if (options.honorCipherOrder) secureOptions |= SSL_OP_CIPHER_SERVER_PREFERENCE; - const c = new SecureContext(options.secureProtocol, secureOptions); + const c = new SecureContext(options.secureProtocol, secureOptions, + options.minVersion, options.maxVersion); var i; var val; diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 9931691464ec14..1d6ccbb8a9cbaf 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -918,6 +918,16 @@ Server.prototype.setSecureContext = function(options) { else this.ca = undefined; + if (options.minVersion) + this.minVersion = options.minVersion; + else + this.minVersion = undefined; + + if (options.maxVersion) + this.maxVersion = options.maxVersion; + else + this.maxVersion = undefined; + if (options.secureProtocol) this.secureProtocol = options.secureProtocol; else @@ -974,6 +984,8 @@ Server.prototype.setSecureContext = function(options) { ciphers: this.ciphers, ecdhCurve: this.ecdhCurve, dhparam: this.dhparam, + minVersion: this.minVersion, + maxVersion: this.maxVersion, secureProtocol: this.secureProtocol, secureOptions: this.secureOptions, honorCipherOrder: this.honorCipherOrder, @@ -1026,6 +1038,8 @@ Server.prototype.setOptions = util.deprecate(function(options) { if (options.clientCertEngine) this.clientCertEngine = options.clientCertEngine; if (options.ca) this.ca = options.ca; + if (options.minVersion) this.minVersion = options.minVersion; + if (options.maxVersion) this.maxVersion = options.maxVersion; if (options.secureProtocol) this.secureProtocol = options.secureProtocol; if (options.crl) this.crl = options.crl; if (options.ciphers) this.ciphers = options.ciphers; diff --git a/lib/https.js b/lib/https.js index 12db2f452c17fa..66e76c1f050935 100644 --- a/lib/https.js +++ b/lib/https.js @@ -186,6 +186,14 @@ Agent.prototype.getName = function getName(options) { if (options.servername && options.servername !== options.host) name += options.servername; + name += ':'; + if (options.minVersion) + name += options.minVersion; + + name += ':'; + if (options.maxVersion) + name += options.maxVersion; + name += ':'; if (options.secureProtocol) name += options.secureProtocol; diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 5fec67d6c721af..0ed23a943805a8 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -861,6 +861,10 @@ E('ERR_TLS_CERT_ALTNAME_INVALID', 'Hostname/IP does not match certificate\'s altnames: %s', Error); E('ERR_TLS_DH_PARAM_SIZE', 'DH parameter size %s is less than 2048', Error); E('ERR_TLS_HANDSHAKE_TIMEOUT', 'TLS handshake timeout', Error); +E('ERR_TLS_INVALID_PROTOCOL_VERSION', + '%j is not a valid %s TLS protocol version', TypeError); +E('ERR_TLS_PROTOCOL_VERSION_CONFLICT', + 'TLS protocol version %j conflicts with secureProtocol %j', TypeError); E('ERR_TLS_RENEGOTIATE', 'Attempt to renegotiate TLS session failed', Error); E('ERR_TLS_RENEGOTIATION_DISABLED', 'TLS session renegotiation disabled for this socket', Error); diff --git a/lib/tls.js b/lib/tls.js index d6b86a410374b7..898e204c539de8 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -31,6 +31,7 @@ internalUtil.assertCrypto(); const { isArrayBufferView } = require('internal/util/types'); const net = require('net'); +const { getOptionValue } = require('internal/options'); const url = require('url'); const binding = internalBinding('crypto'); const { Buffer } = require('buffer'); @@ -53,6 +54,15 @@ exports.DEFAULT_CIPHERS = exports.DEFAULT_ECDH_CURVE = 'auto'; +exports.DEFAULT_MAX_VERSION = 'TLSv1.2'; + +if (getOptionValue('--tls-v1.0')) + exports.DEFAULT_MIN_VERSION = 'TLSv1'; +else if (getOptionValue('--tls-v1.1')) + exports.DEFAULT_MIN_VERSION = 'TLSv1.1'; +else + exports.DEFAULT_MIN_VERSION = 'TLSv1.2'; + exports.getCiphers = internalUtil.cachedResult( () => internalUtil.filterDuplicateStrings(binding.getSSLCiphers(), true) ); diff --git a/src/node_constants.cc b/src/node_constants.cc index 3b028b52aa75cc..7530d6d0a34780 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -1237,6 +1237,10 @@ void DefineCryptoConstants(Local target) { NODE_DEFINE_STRING_CONSTANT(target, "defaultCipherList", per_process_opts->tls_cipher_list.c_str()); + + NODE_DEFINE_CONSTANT(target, TLS1_VERSION); + NODE_DEFINE_CONSTANT(target, TLS1_1_VERSION); + NODE_DEFINE_CONSTANT(target, TLS1_2_VERSION); #endif NODE_DEFINE_CONSTANT(target, INT_MAX); } diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 7e168c6fa4272b..16d1951ff72eaa 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -405,14 +405,15 @@ void SecureContext::Init(const FunctionCallbackInfo& args) { ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder()); Environment* env = sc->env(); - int min_version = TLS1_2_VERSION; - int max_version = 0; - const SSL_METHOD* method = TLS_method(); + CHECK_EQ(args.Length(), 3); + CHECK(args[1]->IsInt32()); + CHECK(args[2]->IsInt32()); - if (env->options()->tls_v1_1) min_version = TLS1_1_VERSION; - if (env->options()->tls_v1_0) min_version = TLS1_VERSION; + int min_version = args[1].As()->Value(); + int max_version = args[2].As()->Value(); + const SSL_METHOD* method = TLS_method(); - if (args.Length() == 1 && args[0]->IsString()) { + if (args[0]->IsString()) { const node::Utf8Value sslmethod(env->isolate(), args[0]); // Note that SSLv2 and SSLv3 are disallowed but SSLv23_method and friends @@ -509,6 +510,7 @@ void SecureContext::Init(const FunctionCallbackInfo& args) { SSL_CTX_set_min_proto_version(sc->ctx_.get(), min_version); SSL_CTX_set_max_proto_version(sc->ctx_.get(), max_version); + // OpenSSL 1.1.0 changed the ticket key size, but the OpenSSL 1.0.x size was // exposed in the public API. To retain compatibility, install a callback // which restores the old algorithm. diff --git a/test/fixtures/tls-connect.js b/test/fixtures/tls-connect.js index 43c3e6f0fd9fa0..303061adc3223e 100644 --- a/test/fixtures/tls-connect.js +++ b/test/fixtures/tls-connect.js @@ -46,28 +46,45 @@ exports.connect = function connect(options, callback) { const client = {}; const pair = { server, client }; - tls.createServer(options.server, function(conn) { - server.conn = conn; - conn.pipe(conn); - maybeCallback() - }).listen(0, function() { - server.server = this; + try { + tls.createServer(options.server, function(conn) { + server.conn = conn; + conn.pipe(conn); + maybeCallback() + }).listen(0, function() { + server.server = this; - const optClient = util._extend({ - port: this.address().port, - }, options.client); + const optClient = util._extend({ + port: this.address().port, + }, options.client); - tls.connect(optClient) - .on('secureConnect', function() { - client.conn = this; - maybeCallback(); - }) - .on('error', function(err) { + try { + tls.connect(optClient) + .on('secureConnect', function() { + client.conn = this; + maybeCallback(); + }) + .on('error', function(err) { + client.err = err; + client.conn = this; + maybeCallback(); + }); + } catch (err) { client.err = err; - client.conn = this; - maybeCallback(); - }); - }); + // The server won't get a connection, we are done. + callback(err, pair, cleanup); + callback = null; + } + }).on('tlsClientError', function(err, sock) { + server.conn = sock; + server.err = err; + maybeCallback(); + }); + } catch (err) { + // Invalid options can throw, report the error. + pair.server.err = err; + callback(err, pair, () => {}); + } function maybeCallback() { if (!callback) @@ -76,13 +93,13 @@ exports.connect = function connect(options, callback) { const err = pair.client.err || pair.server.err; callback(err, pair, cleanup); callback = null; - - function cleanup() { - if (server.server) - server.server.close(); - if (client.conn) - client.conn.end(); - } } } + + function cleanup() { + if (server.server) + server.server.close(); + if (client.conn) + client.conn.end(); + } } diff --git a/test/parallel/test-https-agent-getname.js b/test/parallel/test-https-agent-getname.js index b68850f21d57ca..5c95da549b20b5 100644 --- a/test/parallel/test-https-agent-getname.js +++ b/test/parallel/test-https-agent-getname.js @@ -12,7 +12,7 @@ const agent = new https.Agent(); // empty options assert.strictEqual( agent.getName({}), - 'localhost:::::::::::::::::' + 'localhost:::::::::::::::::::' ); // pass all options arguments @@ -40,5 +40,5 @@ const options = { assert.strictEqual( agent.getName(options), '0.0.0.0:443:192.168.1.1:ca:cert:dynamic:ciphers:key:pfx:false:localhost:' + - 'secureProtocol:c,r,l:false:ecdhCurve:dhparam:0:sessionIdContext' + '::secureProtocol:c,r,l:false:ecdhCurve:dhparam:0:sessionIdContext' ); diff --git a/test/parallel/test-tls-cli-min-version-1.0.js b/test/parallel/test-tls-cli-min-version-1.0.js new file mode 100644 index 00000000000000..f2f39ce60f6624 --- /dev/null +++ b/test/parallel/test-tls-cli-min-version-1.0.js @@ -0,0 +1,15 @@ +// Flags: --tls-v1.0 --tls-v1.1 +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +// Check that `node --tls-v1.0` is supported, and overrides --tls-v1.1. + +const assert = require('assert'); +const tls = require('tls'); + +assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.2'); +assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1'); + +// Check the min-max version protocol versions against these CLI settings. +require('./test-tls-min-max-version.js'); diff --git a/test/parallel/test-tls-cli-min-version-1.1.js b/test/parallel/test-tls-cli-min-version-1.1.js new file mode 100644 index 00000000000000..404ee98ff326d9 --- /dev/null +++ b/test/parallel/test-tls-cli-min-version-1.1.js @@ -0,0 +1,15 @@ +// Flags: --tls-v1.1 +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +// Check that node `--tls-v1.1` is supported. + +const assert = require('assert'); +const tls = require('tls'); + +assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.2'); +assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.1'); + +// Check the min-max version protocol versions against these CLI settings. +require('./test-tls-min-max-version.js'); diff --git a/test/parallel/test-tls-min-max-version.js b/test/parallel/test-tls-min-max-version.js new file mode 100644 index 00000000000000..3a3dab33d566c6 --- /dev/null +++ b/test/parallel/test-tls-min-max-version.js @@ -0,0 +1,146 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +// Check min/max protocol versions. + +const { + assert, connect, keys, tls +} = require(fixtures.path('tls-connect')); +const DEFAULT_MIN_VERSION = tls.DEFAULT_MIN_VERSION; + +function test(cmin, cmax, cprot, smin, smax, sprot, expect) { + connect({ + client: { + checkServerIdentity: (servername, cert) => { }, + ca: `${keys.agent1.cert}\n${keys.agent6.ca}`, + minVersion: cmin, + maxVersion: cmax, + secureProtocol: cprot, + }, + server: { + cert: keys.agent6.cert, + key: keys.agent6.key, + minVersion: smin, + maxVersion: smax, + secureProtocol: sprot, + }, + }, common.mustCall((err, pair, cleanup) => { + if (expect && !expect.match(/^TLS/)) { + assert(err.message.match(expect)); + return cleanup(); + } + + if (expect) { + assert.ifError(pair.server.err); + assert.ifError(pair.client.err); + assert(pair.server.conn); + assert(pair.client.conn); + assert.strictEqual(pair.client.conn.getProtocol(), expect); + assert.strictEqual(pair.server.conn.getProtocol(), expect); + return cleanup(); + } + + assert(pair.server.err); + assert(pair.client.err); + return cleanup(); + })); +} + +const U = undefined; + +// Default protocol is TLSv1.2. +test(U, U, U, U, U, U, 'TLSv1.2'); + +// Insecure or invalid protocols cannot be enabled. +test(U, U, U, U, U, 'SSLv2_method', 'SSLv2 methods disabled'); +test(U, U, U, U, U, 'SSLv3_method', 'SSLv3 methods disabled'); +test(U, U, 'SSLv2_method', U, U, U, 'SSLv2 methods disabled'); +test(U, U, 'SSLv3_method', U, U, U, 'SSLv3 methods disabled'); +test(U, U, 'hokey-pokey', U, U, U, 'Unknown method'); +test(U, U, U, U, U, 'hokey-pokey', 'Unknown method'); + +// Cannot use secureProtocol and min/max versions simultaneously. +test(U, U, U, U, 'TLSv1.2', 'TLS1_2_method', 'conflicts with secureProtocol'); +test(U, U, U, 'TLSv1.2', U, 'TLS1_2_method', 'conflicts with secureProtocol'); +test(U, 'TLSv1.2', 'TLS1_2_method', U, U, U, 'conflicts with secureProtocol'); +test('TLSv1.2', U, 'TLS1_2_method', U, U, U, 'conflicts with secureProtocol'); + +// TLS_method means "any supported protocol". +test(U, U, 'TLSv1_2_method', U, U, 'TLS_method', 'TLSv1.2'); +test(U, U, 'TLSv1_1_method', U, U, 'TLS_method', 'TLSv1.1'); +test(U, U, 'TLSv1_method', U, U, 'TLS_method', 'TLSv1'); +test(U, U, 'TLS_method', U, U, 'TLSv1_2_method', 'TLSv1.2'); +test(U, U, 'TLS_method', U, U, 'TLSv1_1_method', 'TLSv1.1'); +test(U, U, 'TLS_method', U, U, 'TLSv1_method', 'TLSv1'); + +// SSLv23 also means "any supported protocol" greater than the default +// minimum (which is configurable via command line). +test(U, U, 'TLSv1_2_method', U, U, 'SSLv23_method', 'TLSv1.2'); + +if (DEFAULT_MIN_VERSION === 'TLSv1.2') { + test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method', null); + test(U, U, 'TLSv1_method', U, U, 'SSLv23_method', null); + test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method', null); + test(U, U, 'SSLv23_method', U, U, 'TLSv1_method', null); +} + +if (DEFAULT_MIN_VERSION === 'TLSv1.1') { + test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method', 'TLSv1.1'); + test(U, U, 'TLSv1_method', U, U, 'SSLv23_method', null); + test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method', 'TLSv1.1'); + test(U, U, 'SSLv23_method', U, U, 'TLSv1_method', null); +} + +if (DEFAULT_MIN_VERSION === 'TLSv1') { + test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method', 'TLSv1.1'); + test(U, U, 'TLSv1_method', U, U, 'SSLv23_method', 'TLSv1'); + test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method', 'TLSv1.1'); + test(U, U, 'SSLv23_method', U, U, 'TLSv1_method', 'TLSv1'); +} + +// TLSv1 thru TLSv1.2 are only supported with explicit configuration with API or +// CLI (--tls-v1.0 and --tls-v1.1). +test(U, U, 'TLSv1_2_method', U, U, 'TLSv1_2_method', 'TLSv1.2'); +test(U, U, 'TLSv1_1_method', U, U, 'TLSv1_1_method', 'TLSv1.1'); +test(U, U, 'TLSv1_method', U, U, 'TLSv1_method', 'TLSv1'); + +// The default default. +if (DEFAULT_MIN_VERSION === 'TLSv1.2') { + test(U, U, 'TLSv1_1_method', U, U, U, null); + test(U, U, 'TLSv1_method', U, U, U, null); + test(U, U, U, U, U, 'TLSv1_1_method', null); + test(U, U, U, U, U, 'TLSv1_method', null); +} + +// The default with --tls-v1.1. +if (DEFAULT_MIN_VERSION === 'TLSv1.1') { + test(U, U, 'TLSv1_1_method', U, U, U, 'TLSv1.1'); + test(U, U, 'TLSv1_method', U, U, U, null); + test(U, U, U, U, U, 'TLSv1_1_method', 'TLSv1.1'); + test(U, U, U, U, U, 'TLSv1_method', null); +} + +// The default with --tls-v1.0. +if (DEFAULT_MIN_VERSION === 'TLSv1') { + test(U, U, 'TLSv1_1_method', U, U, U, 'TLSv1.1'); + test(U, U, 'TLSv1_method', U, U, U, 'TLSv1'); + test(U, U, U, U, U, 'TLSv1_1_method', 'TLSv1.1'); + test(U, U, U, U, U, 'TLSv1_method', 'TLSv1'); +} + +// TLS min/max are respected when set with no secureProtocol. +test('TLSv1', 'TLSv1.2', U, U, U, 'TLSv1_method', 'TLSv1'); +test('TLSv1', 'TLSv1.2', U, U, U, 'TLSv1_1_method', 'TLSv1.1'); +test('TLSv1', 'TLSv1.2', U, U, U, 'TLSv1_2_method', 'TLSv1.2'); + +test(U, U, 'TLSv1_method', 'TLSv1', 'TLSv1.2', U, 'TLSv1'); +test(U, U, 'TLSv1_1_method', 'TLSv1', 'TLSv1.2', U, 'TLSv1.1'); +test(U, U, 'TLSv1_2_method', 'TLSv1', 'TLSv1.2', U, 'TLSv1.2'); + +test('TLSv1', 'TLSv1.1', U, 'TLSv1', 'TLSv1.2', U, 'TLSv1.1'); +test('TLSv1', 'TLSv1.2', U, 'TLSv1', 'TLSv1.1', U, 'TLSv1.1'); +test('TLSv1', 'TLSv1', U, 'TLSv1', 'TLSv1.1', U, 'TLSv1'); +test('TLSv1', 'TLSv1.2', U, 'TLSv1', 'TLSv1', U, 'TLSv1'); +test('TLSv1.1', 'TLSv1.1', U, 'TLSv1', 'TLSv1.2', U, 'TLSv1.1'); +test('TLSv1', 'TLSv1.2', U, 'TLSv1.1', 'TLSv1.1', U, 'TLSv1.1');