aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorSam Roberts <vieuxtech@gmail.com>2018-11-28 17:58:08 -0800
committerSam Roberts <vieuxtech@gmail.com>2019-03-20 07:48:25 -0700
commit42dbaed4605f44c393a057aad75a31cac1d0e5f5 (patch)
tree096554b95dfb14cef568bfe898018d9bb874305c /test
parent4306300b5ea8d8c4ff3daf64c7ed5fd64055ec2f (diff)
downloadandroid-node-v8-42dbaed4605f44c393a057aad75a31cac1d0e5f5.tar.gz
android-node-v8-42dbaed4605f44c393a057aad75a31cac1d0e5f5.tar.bz2
android-node-v8-42dbaed4605f44c393a057aad75a31cac1d0e5f5.zip
tls: support TLSv1.3
This introduces TLS1.3 support and makes it the default max protocol, but also supports CLI/NODE_OPTIONS switches to disable it if necessary. TLS1.3 is a major update to the TLS protocol, with many security enhancements. It should be preferred over TLS1.2 whenever possible. TLS1.3 is different enough that even though the OpenSSL APIs are technically API/ABI compatible, that when TLS1.3 is negotiated, the timing of protocol records and of callbacks broke assumptions hard-coded into the 'tls' module. This change introduces no API incompatibilities when TLS1.2 is negotiated. It is the intention that it be backported to current and LTS release lines with the default maximum TLS protocol reset to 'TLSv1.2'. This will allow users of those lines to explicitly enable TLS1.3 if they want. API incompatibilities between TLS1.2 and TLS1.3 are: - Renegotiation is not supported by TLS1.3 protocol, attempts to call `.renegotiate()` will always fail. - Compiling against a system OpenSSL lower than 1.1.1 is no longer supported (OpenSSL-1.1.0 used to be supported with configure flags). - Variations of `conn.write('data'); conn.destroy()` have undefined behaviour according to the streams API. They may or may not send the 'data', and may or may not cause a ERR_STREAM_DESTROYED error to be emitted. This has always been true, but conditions under which the write suceeds is slightly but observably different when TLS1.3 is negotiated vs when TLS1.2 or below is negotiated. - If TLS1.3 is negotiated, and a server calls `conn.end()` in its 'secureConnection' listener without any data being written, the client will not receive session tickets (no 'session' events will be emitted, and `conn.getSession()` will never return a resumable session). - The return value of `conn.getSession()` API may not return a resumable session if called right after the handshake. The effect will be that clients using the legacy `getSession()` API will resume sessions if TLS1.2 is negotiated, but will do full handshakes if TLS1.3 is negotiated. See https://github.com/nodejs/node/pull/25831 for more information. PR-URL: https://github.com/nodejs/node/pull/26209 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rod Vagg <rod@vagg.org>
Diffstat (limited to 'test')
-rw-r--r--test/async-hooks/test-graph.tls-write-12.js11
-rw-r--r--test/async-hooks/test-graph.tls-write.js11
-rw-r--r--test/async-hooks/test-tlswrap.js7
-rw-r--r--test/parallel/test-crypto.js1
-rw-r--r--test/parallel/test-https-agent-additional-options.js2
-rw-r--r--test/parallel/test-https-agent-session-eviction.js2
-rw-r--r--test/parallel/test-https-client-renegotiation-limit.js3
-rw-r--r--test/parallel/test-https-client-resume.js3
-rw-r--r--test/parallel/test-tls-alert-handling.js2
-rw-r--r--test/parallel/test-tls-async-cb-after-socket-end.js5
-rw-r--r--test/parallel/test-tls-basic-validations.js6
-rw-r--r--test/parallel/test-tls-cli-max-version-1.2.js15
-rw-r--r--test/parallel/test-tls-cli-max-version-1.3.js15
-rw-r--r--test/parallel/test-tls-cli-min-version-1.0.js4
-rw-r--r--test/parallel/test-tls-cli-min-version-1.1.js4
-rw-r--r--test/parallel/test-tls-cli-min-version-1.3.js15
-rw-r--r--test/parallel/test-tls-client-auth.js37
-rw-r--r--test/parallel/test-tls-client-getephemeralkeyinfo.js3
-rw-r--r--test/parallel/test-tls-client-reject-12.js13
-rw-r--r--test/parallel/test-tls-client-reject.js10
-rw-r--r--test/parallel/test-tls-client-renegotiation-13.js37
-rw-r--r--test/parallel/test-tls-client-renegotiation-limit.js3
-rw-r--r--test/parallel/test-tls-client-resume-12.js13
-rw-r--r--test/parallel/test-tls-client-resume.js43
-rw-r--r--test/parallel/test-tls-destroy-stream-12.js13
-rw-r--r--test/parallel/test-tls-destroy-stream.js22
-rw-r--r--test/parallel/test-tls-disable-renegotiation.js7
-rw-r--r--test/parallel/test-tls-getcipher.js23
-rw-r--r--test/parallel/test-tls-min-max-version.js55
-rw-r--r--test/parallel/test-tls-net-socket-keepalive-12.js13
-rw-r--r--test/parallel/test-tls-net-socket-keepalive.js6
-rw-r--r--test/parallel/test-tls-server-verify.js2
-rw-r--r--test/parallel/test-tls-set-ciphers-error.js3
-rw-r--r--test/parallel/test-tls-set-ciphers.js133
-rw-r--r--test/parallel/test-tls-ticket-12.js12
-rw-r--r--test/parallel/test-tls-ticket-cluster.js16
-rw-r--r--test/parallel/test-tls-ticket.js42
37 files changed, 508 insertions, 104 deletions
diff --git a/test/async-hooks/test-graph.tls-write-12.js b/test/async-hooks/test-graph.tls-write-12.js
new file mode 100644
index 0000000000..748234402c
--- /dev/null
+++ b/test/async-hooks/test-graph.tls-write-12.js
@@ -0,0 +1,11 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const tls = require('tls');
+
+tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
+
+require('./test-graph.tls-write.js');
diff --git a/test/async-hooks/test-graph.tls-write.js b/test/async-hooks/test-graph.tls-write.js
index 2ea03283e4..580264316d 100644
--- a/test/async-hooks/test-graph.tls-write.js
+++ b/test/async-hooks/test-graph.tls-write.js
@@ -39,8 +39,10 @@ function onlistening() {
function onsecureConnection() {}
function onsecureConnect() {
- // Destroying client socket
- this.destroy();
+ // end() client socket, which causes slightly different hook events than
+ // destroy(), but with TLS1.3 destroy() rips the connection down before the
+ // server completes the handshake.
+ this.end();
// Closing server
server.close(common.mustCall(onserverClosed));
@@ -68,7 +70,8 @@ function onexit() {
{ type: 'WRITEWRAP', id: 'write:2', triggerAsyncId: null },
{ type: 'WRITEWRAP', id: 'write:3', triggerAsyncId: null },
{ type: 'WRITEWRAP', id: 'write:4', triggerAsyncId: null },
- { type: 'Immediate', id: 'immediate:1', triggerAsyncId: 'tcp:1' },
- { type: 'Immediate', id: 'immediate:2', triggerAsyncId: 'tcp:2' } ]
+ { type: 'Immediate', id: 'immediate:1', triggerAsyncId: 'tcp:2' },
+ { type: 'Immediate', id: 'immediate:2', triggerAsyncId: 'tcp:1' },
+ ]
);
}
diff --git a/test/async-hooks/test-tlswrap.js b/test/async-hooks/test-tlswrap.js
index 354cd7ad0c..d6dcd20470 100644
--- a/test/async-hooks/test-tlswrap.js
+++ b/test/async-hooks/test-tlswrap.js
@@ -15,6 +15,10 @@ const { checkInvocations } = require('./hook-checks');
const hooks = initHooks();
hooks.enable();
+// TODO(@sam-github) assumes server handshake completes before client, true for
+// 1.2, not for 1.3. Might need a rewrite for TLS1.3.
+tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
+
//
// Creating server and listening on port
//
@@ -52,6 +56,7 @@ function onsecureConnection() {
//
const as = hooks.activitiesOfTypes('TLSWRAP');
assert.strictEqual(as.length, 2);
+ // TODO(@sam-github) This happens after onsecureConnect, with TLS1.3.
client = as[1];
assert.strictEqual(client.type, 'TLSWRAP');
assert.strictEqual(typeof client.uid, 'number');
@@ -78,7 +83,7 @@ function onsecureConnect() {
//
// Destroying client socket
//
- this.destroy();
+ this.destroy(); // This destroys client before server handshakes, with TLS1.3
checkInvocations(svr, { init: 1, before: 2, after: 1 },
'server: when destroying client');
checkInvocations(client, { init: 1, before: 2, after: 2 },
diff --git a/test/parallel/test-crypto.js b/test/parallel/test-crypto.js
index 73b81ec1b1..005a733c07 100644
--- a/test/parallel/test-crypto.js
+++ b/test/parallel/test-crypto.js
@@ -129,6 +129,7 @@ validateList(cryptoCiphers);
// Assume that we have at least AES256-SHA.
const tlsCiphers = tls.getCiphers();
assert(tls.getCiphers().includes('aes256-sha'));
+assert(tls.getCiphers().includes('tls_aes_128_ccm_8_sha256'));
// There should be no capital letters in any element.
const noCapitals = /^[^A-Z]+$/;
assert(tlsCiphers.every((value) => noCapitals.test(value)));
diff --git a/test/parallel/test-https-agent-additional-options.js b/test/parallel/test-https-agent-additional-options.js
index 1bbff96001..a04ef7461d 100644
--- a/test/parallel/test-https-agent-additional-options.js
+++ b/test/parallel/test-https-agent-additional-options.js
@@ -1,4 +1,4 @@
-// Flags: --tls-v1.1
+// Flags: --tls-min-v1.1
'use strict';
const common = require('../common');
if (!common.hasCrypto)
diff --git a/test/parallel/test-https-agent-session-eviction.js b/test/parallel/test-https-agent-session-eviction.js
index 785f4737bd..8e13b150bb 100644
--- a/test/parallel/test-https-agent-session-eviction.js
+++ b/test/parallel/test-https-agent-session-eviction.js
@@ -1,4 +1,4 @@
-// Flags: --tls-v1.0
+// Flags: --tls-min-v1.0
'use strict';
const common = require('../common');
diff --git a/test/parallel/test-https-client-renegotiation-limit.js b/test/parallel/test-https-client-renegotiation-limit.js
index 1fccc7bb5d..3527c48f4b 100644
--- a/test/parallel/test-https-client-renegotiation-limit.js
+++ b/test/parallel/test-https-client-renegotiation-limit.js
@@ -32,6 +32,9 @@ const tls = require('tls');
const https = require('https');
const fixtures = require('../common/fixtures');
+// Renegotiation as a protocol feature was dropped after TLS1.2.
+tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
+
// renegotiation limits to test
const LIMITS = [0, 1, 2, 3, 5, 10, 16];
diff --git a/test/parallel/test-https-client-resume.js b/test/parallel/test-https-client-resume.js
index cf1bbdf262..8904e24180 100644
--- a/test/parallel/test-https-client-resume.js
+++ b/test/parallel/test-https-client-resume.js
@@ -55,7 +55,8 @@ server.listen(0, common.mustCall(function() {
'\r\n');
}));
- client1.on('session', common.mustCall((session) => {
+ // TLS1.2 servers issue 1 ticket, TLS1.3 issues more, but only use the first.
+ client1.once('session', common.mustCall((session) => {
console.log('session');
const opts = {
diff --git a/test/parallel/test-tls-alert-handling.js b/test/parallel/test-tls-alert-handling.js
index 63b845122f..f9f42e2d51 100644
--- a/test/parallel/test-tls-alert-handling.js
+++ b/test/parallel/test-tls-alert-handling.js
@@ -64,7 +64,7 @@ function sendClient() {
}
client.end();
}, max_iter));
- client.write('a');
+ client.write('a', common.mustCall());
client.on('error', common.mustNotCall());
client.on('close', common.mustCall(function() {
clientClosed = true;
diff --git a/test/parallel/test-tls-async-cb-after-socket-end.js b/test/parallel/test-tls-async-cb-after-socket-end.js
index 5c812c8f04..49ca0cebc9 100644
--- a/test/parallel/test-tls-async-cb-after-socket-end.js
+++ b/test/parallel/test-tls-async-cb-after-socket-end.js
@@ -14,7 +14,6 @@ const tls = require('tls');
// new and resume session events will never be emitted on the server.
const options = {
- maxVersion: 'TLSv1.2',
secureOptions: SSL_OP_NO_TICKET,
key: fixtures.readSync('test_key.pem'),
cert: fixtures.readSync('test_cert.pem')
@@ -38,6 +37,10 @@ server.on('resumeSession', common.mustCall((id, cb) => {
server.listen(0, common.mustCall(() => {
const clientOpts = {
+ // Don't send a TLS1.3/1.2 ClientHello, they contain a fake session_id,
+ // which triggers a 'resumeSession' event for client1. TLS1.2 ClientHello
+ // won't have a session_id until client2, which will have a valid session.
+ maxVersion: 'TLSv1.2',
port: server.address().port,
rejectUnauthorized: false,
session: false
diff --git a/test/parallel/test-tls-basic-validations.js b/test/parallel/test-tls-basic-validations.js
index 8d39b8d45c..9bcc63c454 100644
--- a/test/parallel/test-tls-basic-validations.js
+++ b/test/parallel/test-tls-basic-validations.js
@@ -12,7 +12,8 @@ common.expectsError(
{
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
- message: 'Ciphers must be a string'
+ message: 'The "options.ciphers" property must be of type string.' +
+ ' Received type number'
});
common.expectsError(
@@ -20,7 +21,8 @@ common.expectsError(
{
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
- message: 'Ciphers must be a string'
+ message: 'The "options.ciphers" property must be of type string.' +
+ ' Received type number'
});
common.expectsError(
diff --git a/test/parallel/test-tls-cli-max-version-1.2.js b/test/parallel/test-tls-cli-max-version-1.2.js
new file mode 100644
index 0000000000..9bbc9ff0ec
--- /dev/null
+++ b/test/parallel/test-tls-cli-max-version-1.2.js
@@ -0,0 +1,15 @@
+// Flags: --tls-max-v1.2
+'use strict';
+const common = require('../common');
+if (!common.hasCrypto) common.skip('missing crypto');
+
+// Check that node `--tls-max-v1.2` 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.2');
+
+// 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-max-version-1.3.js b/test/parallel/test-tls-cli-max-version-1.3.js
new file mode 100644
index 0000000000..c04354fe4a
--- /dev/null
+++ b/test/parallel/test-tls-cli-max-version-1.3.js
@@ -0,0 +1,15 @@
+// Flags: --tls-max-v1.3
+'use strict';
+const common = require('../common');
+if (!common.hasCrypto) common.skip('missing crypto');
+
+// Check that node `--tls-max-v1.3` is supported.
+
+const assert = require('assert');
+const tls = require('tls');
+
+assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
+assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.2');
+
+// 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.0.js b/test/parallel/test-tls-cli-min-version-1.0.js
index f2f39ce60f..577562782e 100644
--- a/test/parallel/test-tls-cli-min-version-1.0.js
+++ b/test/parallel/test-tls-cli-min-version-1.0.js
@@ -1,4 +1,4 @@
-// Flags: --tls-v1.0 --tls-v1.1
+// Flags: --tls-min-v1.0 --tls-min-v1.1
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');
@@ -8,7 +8,7 @@ if (!common.hasCrypto) common.skip('missing crypto');
const assert = require('assert');
const tls = require('tls');
-assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.2');
+assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1');
// Check the min-max version protocol versions against these CLI settings.
diff --git a/test/parallel/test-tls-cli-min-version-1.1.js b/test/parallel/test-tls-cli-min-version-1.1.js
index 404ee98ff3..3af2b39546 100644
--- a/test/parallel/test-tls-cli-min-version-1.1.js
+++ b/test/parallel/test-tls-cli-min-version-1.1.js
@@ -1,4 +1,4 @@
-// Flags: --tls-v1.1
+// Flags: --tls-min-v1.1
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');
@@ -8,7 +8,7 @@ if (!common.hasCrypto) common.skip('missing crypto');
const assert = require('assert');
const tls = require('tls');
-assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.2');
+assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.1');
// Check the min-max version protocol versions against these CLI settings.
diff --git a/test/parallel/test-tls-cli-min-version-1.3.js b/test/parallel/test-tls-cli-min-version-1.3.js
new file mode 100644
index 0000000000..1bccc2f6cd
--- /dev/null
+++ b/test/parallel/test-tls-cli-min-version-1.3.js
@@ -0,0 +1,15 @@
+// Flags: --tls-min-v1.3
+'use strict';
+const common = require('../common');
+if (!common.hasCrypto) common.skip('missing crypto');
+
+// Check that node `--tls-min-v1.3` is supported.
+
+const assert = require('assert');
+const tls = require('tls');
+
+assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
+assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.3');
+
+// 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-client-auth.js b/test/parallel/test-tls-client-auth.js
index 1f8c7e6096..4762389619 100644
--- a/test/parallel/test-tls-client-auth.js
+++ b/test/parallel/test-tls-client-auth.js
@@ -1,10 +1,10 @@
'use strict';
-require('../common');
+const common = require('../common');
const fixtures = require('../common/fixtures');
const {
- assert, connect, keys
+ assert, connect, keys, tls
} = require(fixtures.path('tls-connect'));
// Use ec10 and agent10, they are the only identities with intermediate CAs.
@@ -63,9 +63,10 @@ connect({
return cleanup();
});
-// Request cert from client that doesn't have one.
+// Request cert from TLS1.2 client that doesn't have one.
connect({
client: {
+ maxVersion: 'TLSv1.2',
ca: server.ca,
checkServerIdentity,
},
@@ -76,10 +77,38 @@ connect({
requestCert: true,
},
}, function(err, pair, cleanup) {
- assert.strictEqual(err.code, 'ECONNRESET');
+ assert.strictEqual(pair.server.err.code,
+ 'ERR_SSL_PEER_DID_NOT_RETURN_A_CERTIFICATE');
+ assert.strictEqual(pair.client.err.code, 'ECONNRESET');
return cleanup();
});
+// Request cert from TLS1.3 client that doesn't have one.
+if (tls.DEFAULT_MAX_VERSION === 'TLSv1.3') connect({
+ client: {
+ ca: server.ca,
+ checkServerIdentity,
+ },
+ server: {
+ key: server.key,
+ cert: server.cert,
+ ca: client.ca,
+ requestCert: true,
+ },
+}, function(err, pair, cleanup) {
+ assert.strictEqual(pair.server.err.code,
+ 'ERR_SSL_PEER_DID_NOT_RETURN_A_CERTIFICATE');
+
+ // TLS1.3 client completes handshake before server, and its only after the
+ // server handshakes, requests certs, gets back a zero-length list of certs,
+ // and sends a fatal Alert to the client that the client discovers there has
+ // been a fatal error.
+ pair.client.conn.once('error', common.mustCall((err) => {
+ assert.strictEqual(err.code, 'ERR_SSL_TLSV13_ALERT_CERTIFICATE_REQUIRED');
+ cleanup();
+ }));
+});
+
// Typical configuration error, incomplete cert chains sent, we have to know the
// peer's subordinate CAs in order to verify the peer.
connect({
diff --git a/test/parallel/test-tls-client-getephemeralkeyinfo.js b/test/parallel/test-tls-client-getephemeralkeyinfo.js
index 8a9cc65a1c..113b452db6 100644
--- a/test/parallel/test-tls-client-getephemeralkeyinfo.js
+++ b/test/parallel/test-tls-client-getephemeralkeyinfo.js
@@ -10,6 +10,9 @@ const tls = require('tls');
const key = fixtures.readKey('agent2-key.pem');
const cert = fixtures.readKey('agent2-cert.pem');
+// TODO(@sam-github) test works with TLS1.3, rework test to add
+// 'ECDH' with 'TLS_AES_128_GCM_SHA256',
+
function loadDHParam(n) {
return fixtures.readKey(`dh${n}.pem`);
}
diff --git a/test/parallel/test-tls-client-reject-12.js b/test/parallel/test-tls-client-reject-12.js
new file mode 100644
index 0000000000..f77d463f44
--- /dev/null
+++ b/test/parallel/test-tls-client-reject-12.js
@@ -0,0 +1,13 @@
+'use strict';
+
+// test-tls-client-reject specifically for TLS1.2.
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const tls = require('tls');
+
+tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
+
+require('./test-tls-client-reject.js');
diff --git a/test/parallel/test-tls-client-reject.js b/test/parallel/test-tls-client-reject.js
index 9eff6cb9ce..329b78c271 100644
--- a/test/parallel/test-tls-client-reject.js
+++ b/test/parallel/test-tls-client-reject.js
@@ -35,6 +35,7 @@ const options = {
const server = tls.createServer(options, function(socket) {
socket.pipe(socket);
+ // Pipe already ends... but leaving this here tests .end() after .end().
socket.on('end', () => socket.end());
}).listen(0, common.mustCall(function() {
unauthorized();
@@ -47,13 +48,19 @@ function unauthorized() {
servername: 'localhost',
rejectUnauthorized: false
}, common.mustCall(function() {
- console.log('... unauthorized');
+ let _data;
assert(!socket.authorized);
socket.on('data', common.mustCall((data) => {
assert.strictEqual(data.toString(), 'ok');
+ _data = data;
+ }));
+ socket.on('end', common.mustCall(() => {
+ assert(_data, 'data failed to echo!');
}));
socket.on('end', () => rejectUnauthorized());
}));
+ socket.once('session', common.mustCall(() => {
+ }));
socket.on('error', common.mustNotCall());
socket.end('ok');
}
@@ -65,7 +72,6 @@ function rejectUnauthorized() {
}, common.mustNotCall());
socket.on('data', common.mustNotCall());
socket.on('error', common.mustCall(function(err) {
- console.log('... rejected:', err);
authorized();
}));
socket.end('ng');
diff --git a/test/parallel/test-tls-client-renegotiation-13.js b/test/parallel/test-tls-client-renegotiation-13.js
new file mode 100644
index 0000000000..8af63c4f79
--- /dev/null
+++ b/test/parallel/test-tls-client-renegotiation-13.js
@@ -0,0 +1,37 @@
+'use strict';
+
+const common = require('../common');
+const fixtures = require('../common/fixtures');
+
+// Confirm that for TLSv1.3, renegotiate() is disallowed.
+
+const {
+ assert, connect, keys
+} = require(fixtures.path('tls-connect'));
+
+const server = keys.agent10;
+
+connect({
+ client: {
+ ca: server.ca,
+ checkServerIdentity: common.mustCall(),
+ },
+ server: {
+ key: server.key,
+ cert: server.cert,
+ },
+}, function(err, pair, cleanup) {
+ assert.ifError(err);
+
+ const client = pair.client.conn;
+
+ assert.strictEqual(client.getProtocol(), 'TLSv1.3');
+
+ const ok = client.renegotiate({}, common.mustCall((err) => {
+ assert(err.code, 'ERR_TLS_RENEGOTIATE');
+ assert(err.message, 'Attempt to renegotiate TLS session failed');
+ cleanup();
+ }));
+
+ assert.strictEqual(ok, false);
+});
diff --git a/test/parallel/test-tls-client-renegotiation-limit.js b/test/parallel/test-tls-client-renegotiation-limit.js
index daae92eeb4..010fd8596a 100644
--- a/test/parallel/test-tls-client-renegotiation-limit.js
+++ b/test/parallel/test-tls-client-renegotiation-limit.js
@@ -31,6 +31,9 @@ const assert = require('assert');
const tls = require('tls');
const fixtures = require('../common/fixtures');
+// Renegotiation as a protocol feature was dropped after TLS1.2.
+tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
+
// renegotiation limits to test
const LIMITS = [0, 1, 2, 3, 5, 10, 16];
diff --git a/test/parallel/test-tls-client-resume-12.js b/test/parallel/test-tls-client-resume-12.js
new file mode 100644
index 0000000000..7767d3dd2a
--- /dev/null
+++ b/test/parallel/test-tls-client-resume-12.js
@@ -0,0 +1,13 @@
+'use strict';
+
+// test-tls-client-resume specifically for TLS1.2.
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const tls = require('tls');
+
+tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
+
+require('./test-tls-client-resume.js');
diff --git a/test/parallel/test-tls-client-resume.js b/test/parallel/test-tls-client-resume.js
index 9f868fdcdc..0f5c9caa7c 100644
--- a/test/parallel/test-tls-client-resume.js
+++ b/test/parallel/test-tls-client-resume.js
@@ -44,32 +44,59 @@ const server = tls.Server(options, common.mustCall((socket) => {
// start listening
server.listen(0, common.mustCall(function() {
-
- let sessionx = null;
- let session1 = null;
+ let sessionx = null; // From right after connect, invalid for TLS1.3
+ let session1 = null; // Delivered by the session event, always valid.
+ let sessions = 0;
+ let tls13;
const client1 = tls.connect({
port: this.address().port,
rejectUnauthorized: false
}, common.mustCall(() => {
- console.log('connect1');
+ tls13 = client1.getProtocol() === 'TLSv1.3';
assert.strictEqual(client1.isSessionReused(), false);
sessionx = client1.getSession();
+ assert(sessionx);
+
+ if (session1)
+ reconnect();
+ }));
+
+ client1.on('data', common.mustCall((d) => {
}));
client1.once('session', common.mustCall((session) => {
console.log('session1');
session1 = session;
+ assert(session1);
+ if (sessionx)
+ reconnect();
}));
- client1.on('close', common.mustCall(() => {
+ client1.on('session', () => {
+ console.log('client1 session#', ++sessions);
+ });
+
+ client1.on('close', () => {
+ console.log('client1 close');
+ assert.strictEqual(sessions, tls13 ? 2 : 1);
+ });
+
+ function reconnect() {
assert(sessionx);
assert(session1);
- assert.strictEqual(sessionx.compare(session1), 0);
+ if (tls13)
+ // For TLS1.3, the session immediately after handshake is a dummy,
+ // unresumable session. The one delivered later in session event is
+ // resumable.
+ assert.notStrictEqual(sessionx.compare(session1), 0);
+ else
+ // For TLS1.2, they are identical.
+ assert.strictEqual(sessionx.compare(session1), 0);
const opts = {
port: server.address().port,
rejectUnauthorized: false,
- session: session1
+ session: session1,
};
const client2 = tls.connect(opts, common.mustCall(() => {
@@ -83,7 +110,7 @@ server.listen(0, common.mustCall(function() {
}));
client2.resume();
- }));
+ }
client1.resume();
}));
diff --git a/test/parallel/test-tls-destroy-stream-12.js b/test/parallel/test-tls-destroy-stream-12.js
new file mode 100644
index 0000000000..69861868cf
--- /dev/null
+++ b/test/parallel/test-tls-destroy-stream-12.js
@@ -0,0 +1,13 @@
+'use strict';
+
+// test-tls-destroy-stream specifically for TLS1.2.
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const tls = require('tls');
+
+tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
+
+require('./test-tls-destroy-stream.js');
diff --git a/test/parallel/test-tls-destroy-stream.js b/test/parallel/test-tls-destroy-stream.js
index eb7a2ca338..b06d7728dc 100644
--- a/test/parallel/test-tls-destroy-stream.js
+++ b/test/parallel/test-tls-destroy-stream.js
@@ -9,6 +9,8 @@ const net = require('net');
const assert = require('assert');
const tls = require('tls');
+tls.DEFAULT_MAX_VERSION = 'TLSv1.3';
+
// This test ensures that an instance of StreamWrap should emit "end" and
// "close" when the socket on the other side call `destroy()` instead of
// `end()`.
@@ -21,10 +23,17 @@ const tlsServer = tls.createServer(
ca: [fixtures.readSync('test_ca.pem')],
},
(socket) => {
- socket.on('error', common.mustNotCall());
socket.on('close', common.mustCall());
socket.write(CONTENT);
socket.destroy();
+
+ socket.on('error', (err) => {
+ // destroy() is sync, write() is async, whether write completes depends
+ // on the protocol, it is not guaranteed by stream API.
+ if (err.code === 'ERR_STREAM_DESTROYED')
+ return;
+ assert.ifError(err);
+ });
},
);
@@ -57,13 +66,12 @@ const server = net.createServer((conn) => {
server.listen(0, () => {
const port = server.address().port;
const conn = tls.connect({ port, rejectUnauthorized: false }, () => {
- conn.on('data', common.mustCall((data) => {
+ // Whether the server's write() completed before its destroy() is
+ // indeterminate, but if data was written, we should receive it correctly.
+ conn.on('data', (data) => {
assert.strictEqual(data.toString('utf8'), CONTENT);
- }));
+ });
conn.on('error', common.mustNotCall());
- conn.on(
- 'close',
- common.mustCall(() => server.close()),
- );
+ conn.on('close', common.mustCall(() => server.close()));
});
});
diff --git a/test/parallel/test-tls-disable-renegotiation.js b/test/parallel/test-tls-disable-renegotiation.js
index 13e08112e5..3fd6710622 100644
--- a/test/parallel/test-tls-disable-renegotiation.js
+++ b/test/parallel/test-tls-disable-renegotiation.js
@@ -10,6 +10,9 @@ if (!common.hasCrypto)
const tls = require('tls');
+// Renegotiation as a protocol feature was dropped after TLS1.2.
+tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
+
const options = {
key: fixtures.readKey('agent1-key.pem'),
cert: fixtures.readKey('agent1-cert.pem'),
@@ -86,5 +89,9 @@ server.listen(0, common.mustCall(() => {
}));
}));
assert.strictEqual(ok, true);
+ client.on('secureConnect', common.mustCall(() => {
+ }));
+ client.on('secure', common.mustCall(() => {
+ }));
}));
}));
diff --git a/test/parallel/test-tls-getcipher.js b/test/parallel/test-tls-getcipher.js
index f819161f7f..624f8efd24 100644
--- a/test/parallel/test-tls-getcipher.js
+++ b/test/parallel/test-tls-getcipher.js
@@ -60,7 +60,7 @@ server.listen(0, '127.0.0.1', common.mustCall(function() {
tls.connect({
host: '127.0.0.1',
port: this.address().port,
- cipher: 'ECDHE-RSA-AES128-GCM-SHA256',
+ ciphers: 'ECDHE-RSA-AES128-GCM-SHA256',
rejectUnauthorized: false
}, common.mustCall(function() {
const cipher = this.getCipher();
@@ -69,3 +69,24 @@ server.listen(0, '127.0.0.1', common.mustCall(function() {
this.end();
}));
}));
+
+tls.createServer({
+ key: fixtures.readKey('agent2-key.pem'),
+ cert: fixtures.readKey('agent2-cert.pem'),
+ ciphers: 'TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_8_SHA256',
+ maxVersion: 'TLSv1.3',
+}, common.mustCall(function() {
+ this.close();
+})).listen(0, common.mustCall(function() {
+ const client = tls.connect({
+ port: this.address().port,
+ ciphers: 'TLS_AES_128_CCM_8_SHA256',
+ maxVersion: 'TLSv1.3',
+ rejectUnauthorized: false
+ }, common.mustCall(() => {
+ const cipher = client.getCipher();
+ assert.strictEqual(cipher.name, 'TLS_AES_128_CCM_8_SHA256');
+ assert.strictEqual(cipher.version, 'TLSv1.3');
+ client.end();
+ }));
+}));
diff --git a/test/parallel/test-tls-min-max-version.js b/test/parallel/test-tls-min-max-version.js
index 9a8b73c40e..179ae1fa63 100644
--- a/test/parallel/test-tls-min-max-version.js
+++ b/test/parallel/test-tls-min-max-version.js
@@ -13,6 +13,10 @@ const DEFAULT_MAX_VERSION = tls.DEFAULT_MAX_VERSION;
function test(cmin, cmax, cprot, smin, smax, sprot, proto, cerr, serr) {
assert(proto || cerr || serr, 'test missing any expectations');
+ // Report where test was called from. Strip leading garbage from
+ // at Object.<anonymous> (file:line)
+ // from the stack location, we only want the file:line part.
+ const where = (new Error()).stack.split('\n')[2].replace(/[^(]*/, '');
connect({
client: {
checkServerIdentity: (servername, cert) => { },
@@ -32,6 +36,7 @@ function test(cmin, cmax, cprot, smin, smax, sprot, proto, cerr, serr) {
function u(_) { return _ === undefined ? 'U' : _; }
console.log('test:', u(cmin), u(cmax), u(cprot), u(smin), u(smax), u(sprot),
'expect', u(proto), u(cerr), u(serr));
+ console.log(' ', where);
if (!proto) {
console.log('client', pair.client.err ? pair.client.err.code : undefined);
console.log('server', pair.server.err ? pair.server.err.code : undefined);
@@ -64,8 +69,8 @@ function test(cmin, cmax, cprot, smin, smax, sprot, proto, cerr, serr) {
const U = undefined;
-// Default protocol is TLSv1.2.
-test(U, U, U, U, U, U, 'TLSv1.2');
+// Default protocol is the max version.
+test(U, U, U, U, U, U, DEFAULT_MAX_VERSION);
// Insecure or invalid protocols cannot be enabled.
test(U, U, U, U, U, 'SSLv2_method',
@@ -101,7 +106,23 @@ 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.3') {
+ test(U, U, 'TLSv1_2_method', U, U, 'SSLv23_method',
+ U, 'ECONNRESET', 'ERR_SSL_INTERNAL_ERROR');
+} else {
+ test(U, U, 'TLSv1_2_method', U, U, 'SSLv23_method', 'TLSv1.2');
+}
+
+if (DEFAULT_MIN_VERSION === 'TLSv1.3') {
+ test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method',
+ U, 'ECONNRESET', 'ERR_SSL_INTERNAL_ERROR');
+ test(U, U, 'TLSv1_method', U, U, 'SSLv23_method',
+ U, 'ECONNRESET', 'ERR_SSL_INTERNAL_ERROR');
+ test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method',
+ U, 'ERR_SSL_NO_PROTOCOLS_AVAILABLE', 'ERR_SSL_UNEXPECTED_MESSAGE');
+ test(U, U, 'SSLv23_method', U, U, 'TLSv1_method',
+ U, 'ERR_SSL_NO_PROTOCOLS_AVAILABLE', 'ERR_SSL_UNEXPECTED_MESSAGE');
+}
if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method',
@@ -149,7 +170,11 @@ if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
test(U, U, U, U, U, 'TLSv1_method',
U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
} else {
- assert(false, 'unreachable');
+ // TLS1.3 client hellos are are not understood by TLS1.1 or below.
+ test(U, U, U, U, U, 'TLSv1_1_method',
+ U, 'ECONNRESET', 'ERR_SSL_UNSUPPORTED_PROTOCOL');
+ test(U, U, U, U, U, 'TLSv1_method',
+ U, 'ECONNRESET', 'ERR_SSL_UNSUPPORTED_PROTOCOL');
}
}
@@ -164,7 +189,9 @@ if (DEFAULT_MIN_VERSION === 'TLSv1.1') {
test(U, U, U, U, U, 'TLSv1_method',
U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
} else {
- assert(false, 'unreachable');
+ // TLS1.3 client hellos are are not understood by TLS1.1 or below.
+ test(U, U, U, U, U, 'TLSv1_method',
+ U, 'ECONNRESET', 'ERR_SSL_UNSUPPORTED_PROTOCOL');
}
}
@@ -180,14 +207,32 @@ if (DEFAULT_MIN_VERSION === 'TLSv1') {
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('TLSv1', 'TLSv1.2', U, U, U, 'TLS_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.3', U, 'TLSv1.1');
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.3', 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', 'TLSv1.3', 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');
+test('TLSv1', 'TLSv1.2', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.2');
+
+// v-any client can connect to v-specific server
+test('TLSv1', 'TLSv1.3', U, 'TLSv1.3', 'TLSv1.3', U, 'TLSv1.3');
+test('TLSv1', 'TLSv1.3', U, 'TLSv1.2', 'TLSv1.3', U, 'TLSv1.3');
+test('TLSv1', 'TLSv1.3', U, 'TLSv1.2', 'TLSv1.2', U, 'TLSv1.2');
+test('TLSv1', 'TLSv1.3', U, 'TLSv1.1', 'TLSv1.1', U, 'TLSv1.1');
+test('TLSv1', 'TLSv1.3', U, 'TLSv1', 'TLSv1', U, 'TLSv1');
+
+// v-specific client can connect to v-any server
+test('TLSv1.3', 'TLSv1.3', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.3');
+test('TLSv1.2', 'TLSv1.2', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.2');
+test('TLSv1.1', 'TLSv1.1', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.1');
+test('TLSv1', 'TLSv1', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1');
diff --git a/test/parallel/test-tls-net-socket-keepalive-12.js b/test/parallel/test-tls-net-socket-keepalive-12.js
new file mode 100644
index 0000000000..d2fb230796
--- /dev/null
+++ b/test/parallel/test-tls-net-socket-keepalive-12.js
@@ -0,0 +1,13 @@
+'use strict';
+
+// test-tls-net-socket-keepalive specifically for TLS1.2.
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const tls = require('tls');
+
+tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
+
+require('./test-tls-net-socket-keepalive.js');
diff --git a/test/parallel/test-tls-net-socket-keepalive.js b/test/parallel/test-tls-net-socket-keepalive.js
index c184e902b4..4acb4e8022 100644
--- a/test/parallel/test-tls-net-socket-keepalive.js
+++ b/test/parallel/test-tls-net-socket-keepalive.js
@@ -20,8 +20,11 @@ const options = {
};
const server = tls.createServer(options, common.mustCall((conn) => {
- conn.write('hello');
+ conn.write('hello', common.mustCall());
conn.on('data', common.mustCall());
+ conn.on('end', common.mustCall());
+ conn.on('data', common.mustCall());
+ conn.on('close', common.mustCall());
conn.end();
})).listen(0, common.mustCall(() => {
const netSocket = new net.Socket({
@@ -42,6 +45,7 @@ const server = tls.createServer(options, common.mustCall((conn) => {
address,
});
+ socket.on('secureConnect', common.mustCall());
socket.on('end', common.mustCall());
socket.on('data', common.mustCall());
socket.on('close', common.mustCall(() => {
diff --git a/test/parallel/test-tls-server-verify.js b/test/parallel/test-tls-server-verify.js
index 2fd815d627..347dfd985a 100644
--- a/test/parallel/test-tls-server-verify.js
+++ b/test/parallel/test-tls-server-verify.js
@@ -272,6 +272,8 @@ function runTest(port, testIndex) {
if (tcase.renegotiate) {
serverOptions.secureOptions =
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
+ // Renegotiation as a protocol feature was dropped after TLS1.2.
+ serverOptions.maxVersion = 'TLSv1.2';
}
let renegotiated = false;
diff --git a/test/parallel/test-tls-set-ciphers-error.js b/test/parallel/test-tls-set-ciphers-error.js
index 5ef08dda04..f963b414f4 100644
--- a/test/parallel/test-tls-set-ciphers-error.js
+++ b/test/parallel/test-tls-set-ciphers-error.js
@@ -19,4 +19,7 @@ const fixtures = require('../common/fixtures');
options.ciphers = 'FOOBARBAZ';
assert.throws(() => tls.createServer(options, common.mustNotCall()),
/no cipher match/i);
+ options.ciphers = 'TLS_not_a_cipher';
+ assert.throws(() => tls.createServer(options, common.mustNotCall()),
+ /no cipher match/i);
}
diff --git a/test/parallel/test-tls-set-ciphers.js b/test/parallel/test-tls-set-ciphers.js
index ef2c254351..96eb0944f4 100644
--- a/test/parallel/test-tls-set-ciphers.js
+++ b/test/parallel/test-tls-set-ciphers.js
@@ -1,62 +1,93 @@
-// Copyright Joyent, Inc. and other Node contributors.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to permit
-// persons to whom the Software is furnished to do so, subject to the
-// following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-// USE OR OTHER DEALINGS IN THE SOFTWARE.
-
'use strict';
const common = require('../common');
+if (!common.hasCrypto) common.skip('missing crypto');
+const fixtures = require('../common/fixtures');
-if (!common.opensslCli)
- common.skip('node compiled without OpenSSL CLI.');
+// Test cipher: option for TLS.
-if (!common.hasCrypto)
- common.skip('missing crypto');
+const {
+ assert, connect, keys
+} = require(fixtures.path('tls-connect'));
-const assert = require('assert');
-const exec = require('child_process').exec;
-const tls = require('tls');
-const fixtures = require('../common/fixtures');
-const options = {
- key: fixtures.readKey('agent2-key.pem'),
- cert: fixtures.readKey('agent2-cert.pem'),
- ciphers: 'AES256-SHA'
-};
+function test(cciphers, sciphers, cipher, cerr, serr) {
+ assert(cipher || cerr || serr, 'test missing any expectations');
+ const where = (new Error()).stack.split('\n')[2].replace(/[^(]*/, '');
+ connect({
+ client: {
+ checkServerIdentity: (servername, cert) => { },
+ ca: `${keys.agent1.cert}\n${keys.agent6.ca}`,
+ ciphers: cciphers,
+ },
+ server: {
+ cert: keys.agent6.cert,
+ key: keys.agent6.key,
+ ciphers: sciphers,
+ },
+ }, common.mustCall((err, pair, cleanup) => {
+ function u(_) { return _ === undefined ? 'U' : _; }
+ console.log('test:', u(cciphers), u(sciphers),
+ 'expect', u(cipher), u(cerr), u(serr));
+ console.log(' ', where);
+ if (!cipher) {
+ console.log('client', pair.client.err ? pair.client.err.code : undefined);
+ console.log('server', pair.server.err ? pair.server.err.code : undefined);
+ if (cerr) {
+ assert(pair.client.err);
+ assert.strictEqual(pair.client.err.code, cerr);
+ }
+ if (serr) {
+ assert(pair.server.err);
+ assert.strictEqual(pair.server.err.code, serr);
+ }
+ return cleanup();
+ }
-const reply = 'I AM THE WALRUS'; // something recognizable
-let response = '';
+ const reply = 'So long and thanks for all the fish.';
-process.on('exit', function() {
- assert.ok(response.includes(reply));
-});
+ assert.ifError(err);
+ assert.ifError(pair.server.err);
+ assert.ifError(pair.client.err);
+ assert(pair.server.conn);
+ assert(pair.client.conn);
+ assert.strictEqual(pair.client.conn.getCipher().name, cipher);
+ assert.strictEqual(pair.server.conn.getCipher().name, cipher);
-const server = tls.createServer(options, common.mustCall(function(conn) {
- conn.end(reply);
-}));
+ pair.server.conn.write(reply);
-server.listen(0, '127.0.0.1', function() {
- const cmd = `"${common.opensslCli}" s_client -cipher ${
- options.ciphers} -connect 127.0.0.1:${this.address().port}`;
+ pair.client.conn.on('data', common.mustCall((data) => {
+ assert.strictEqual(data.toString(), reply);
+ return cleanup();
+ }));
+ }));
+}
- exec(cmd, function(err, stdout, stderr) {
- assert.ifError(err);
- response = stdout;
- server.close();
- });
-});
+const U = undefined;
+
+// Have shared ciphers.
+test(U, 'AES256-SHA', 'AES256-SHA');
+test('AES256-SHA', U, 'AES256-SHA');
+
+test(U, 'TLS_AES_256_GCM_SHA384', 'TLS_AES_256_GCM_SHA384');
+test('TLS_AES_256_GCM_SHA384', U, 'TLS_AES_256_GCM_SHA384');
+
+// Do not have shared ciphers.
+test('TLS_AES_256_GCM_SHA384', 'TLS_CHACHA20_POLY1305_SHA256',
+ U, 'ECONNRESET', 'ERR_SSL_NO_SHARED_CIPHER');
+
+test('AES128-SHA', 'AES256-SHA', U, 'ECONNRESET', 'ERR_SSL_NO_SHARED_CIPHER');
+test('AES128-SHA:TLS_AES_256_GCM_SHA384',
+ 'TLS_CHACHA20_POLY1305_SHA256:AES256-SHA',
+ U, 'ECONNRESET', 'ERR_SSL_NO_SHARED_CIPHER');
+
+// Cipher order ignored, TLS1.3 chosen before TLS1.2.
+test('AES256-SHA:TLS_AES_256_GCM_SHA384', U, 'TLS_AES_256_GCM_SHA384');
+test(U, 'AES256-SHA:TLS_AES_256_GCM_SHA384', 'TLS_AES_256_GCM_SHA384');
+
+// TLS_AES_128_CCM_8_SHA256 & TLS_AES_128_CCM_SHA256 are not enabled by
+// default, but work.
+test('TLS_AES_128_CCM_8_SHA256', U,
+ U, 'ECONNRESET', 'ERR_SSL_NO_SHARED_CIPHER');
+
+test('TLS_AES_128_CCM_8_SHA256', 'TLS_AES_128_CCM_8_SHA256',
+ 'TLS_AES_128_CCM_8_SHA256');
diff --git a/test/parallel/test-tls-ticket-12.js b/test/parallel/test-tls-ticket-12.js
new file mode 100644
index 0000000000..600c571a03
--- /dev/null
+++ b/test/parallel/test-tls-ticket-12.js
@@ -0,0 +1,12 @@
+'use strict';
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+// Run test-tls-ticket.js with TLS1.2
+
+const tls = require('tls');
+
+tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
+
+require('./test-tls-ticket.js');
diff --git a/test/parallel/test-tls-ticket-cluster.js b/test/parallel/test-tls-ticket-cluster.js
index 98fe533b69..234c1bad09 100644
--- a/test/parallel/test-tls-ticket-cluster.js
+++ b/test/parallel/test-tls-ticket-cluster.js
@@ -40,13 +40,17 @@ if (cluster.isMaster) {
let workerPort = null;
function shoot() {
- console.error('[master] connecting', workerPort);
+ console.error('[master] connecting', workerPort, 'session?', !!lastSession);
const c = tls.connect(workerPort, {
session: lastSession,
rejectUnauthorized: false
}, () => {
c.end();
-
+ }).on('close', () => {
+ // Wait for close to shoot off another connection. We don't want to shoot
+ // until a new session is allocated, if one will be. The new session is
+ // not guaranteed on secureConnect (it depends on TLS1.2 vs TLS1.3), but
+ // it is guaranteed to happen before the connection is closed.
if (++reqCount === expectedReqCount) {
Object.keys(cluster.workers).forEach(function(id) {
cluster.workers[id].send('die');
@@ -55,8 +59,11 @@ if (cluster.isMaster) {
shoot();
}
}).once('session', (session) => {
+ assert(!lastSession);
lastSession = session;
});
+
+ c.resume(); // See close_notify comment in server
}
function fork() {
@@ -93,12 +100,15 @@ const cert = fixtures.readSync('agent.crt');
const options = { key, cert };
const server = tls.createServer(options, (c) => {
+ console.error('[worker] connection reused?', c.isSessionReused());
if (c.isSessionReused()) {
process.send({ msg: 'reused' });
} else {
process.send({ msg: 'not-reused' });
}
- c.end();
+ // Used to just .end(), but that means client gets close_notify before
+ // NewSessionTicket. Send data until that problem is solved.
+ c.end('x');
});
server.listen(0, () => {
diff --git a/test/parallel/test-tls-ticket.js b/test/parallel/test-tls-ticket.js
index d11535dd3a..8d9cd8cdd2 100644
--- a/test/parallel/test-tls-ticket.js
+++ b/test/parallel/test-tls-ticket.js
@@ -34,6 +34,8 @@ const keys = crypto.randomBytes(48);
const serverLog = [];
const ticketLog = [];
+let s;
+
let serverCount = 0;
function createServer() {
const id = serverCount++;
@@ -47,16 +49,37 @@ function createServer() {
ticketKeys: keys
}, function(c) {
serverLog.push(id);
- c.end();
+ // TODO(@sam-github) Triggers close_notify before NewSessionTicket bug.
+ // c.end();
+ c.end('x');
counter++;
// Rotate ticket keys
+ //
+ // Take especial care to account for TLS1.2 and TLS1.3 differences around
+ // when ticket keys are encrypted. In TLS1.2, they are encrypted before the
+ // handshake complete callback, but in TLS1.3, they are encrypted after.
+ // There is no callback or way for us to know when they were sent, so hook
+ // the client's reception of the keys, and use it as proof that the current
+ // keys were used, and its safe to rotate them.
+ //
+ // Rotation can occur right away if the session was reused, the keys were
+ // already decrypted or we wouldn't have a reused session.
+ function setTicketKeys(keys) {
+ if (c.isSessionReused())
+ server.setTicketKeys(keys);
+ else
+ s.once('session', () => {
+ server.setTicketKeys(keys);
+ });
+ }
if (counter === 1) {
previousKey = server.getTicketKeys();
- server.setTicketKeys(crypto.randomBytes(48));
+ assert.strictEqual(previousKey.compare(keys), 0);
+ setTicketKeys(crypto.randomBytes(48));
} else if (counter === 2) {
- server.setTicketKeys(previousKey);
+ setTicketKeys(previousKey);
} else if (counter === 3) {
// Use keys from counter=2
} else {
@@ -95,12 +118,15 @@ function start(callback) {
let left = servers.length;
function connect() {
- const s = tls.connect(shared.address().port, {
+ s = tls.connect(shared.address().port, {
session: sess,
rejectUnauthorized: false
}, function() {
- sess = sess || s.getSession();
- ticketLog.push(s.getTLSTicket().toString('hex'));
+ if (s.isSessionReused())
+ ticketLog.push(s.getTLSTicket().toString('hex'));
+ });
+ s.on('data', () => {
+ s.end();
});
s.on('close', function() {
if (--left === 0)
@@ -108,7 +134,11 @@ function start(callback) {
else
connect();
});
+ s.on('session', (session) => {
+ sess = sess || session;
+ });
s.once('session', (session) => onNewSession(s, session));
+ s.once('session', () => ticketLog.push(s.getTLSTicket().toString('hex')));
}
connect();