In the manual page, there is a statement that ciphersuites contain explicit default settings - all TLS 1.3 ciphersuites enabled. In node, we assume that an empty setting mean no ciphersuites and we disable TLS 1.3. A correct approach to disabling TLS 1.3 is to disable TLS 1.3 and by not override the default ciphersuits with an empty string. So, only override OpenSSL's TLS 1.3 ciphersuites with an explicit list of ciphers. If none are acceptable, the correct approach is to disable TLS 1.3 instead elsewhere. Fixes: https://github.com/nodejs/node/issues/43419 PR-URL: https://github.com/nodejs/node/pull/43427 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: James M Snell <jasnell@gmail.com>
129 lines
4.4 KiB
JavaScript
129 lines
4.4 KiB
JavaScript
'use strict';
|
|
const common = require('../common');
|
|
if (!common.hasCrypto)
|
|
common.skip('missing crypto');
|
|
|
|
const fixtures = require('../common/fixtures');
|
|
const { inspect } = require('util');
|
|
|
|
// Test cipher: option for TLS.
|
|
|
|
const {
|
|
assert, connect, keys
|
|
} = require(fixtures.path('tls-connect'));
|
|
|
|
|
|
function test(cciphers, sciphers, cipher, cerr, serr, options) {
|
|
assert(cipher || cerr || serr, 'test missing any expectations');
|
|
const where = inspect(new Error()).split('\n')[2].replace(/[^(]*/, '');
|
|
|
|
const max_tls_ver = (ciphers, options) => {
|
|
if (options instanceof Object && Object.hasOwn(options, 'maxVersion'))
|
|
return options.maxVersion;
|
|
if ((typeof ciphers === 'string' || ciphers instanceof String) && ciphers.length > 0 && !ciphers.includes('TLS_'))
|
|
return 'TLSv1.2';
|
|
|
|
return 'TLSv1.3';
|
|
};
|
|
|
|
connect({
|
|
client: {
|
|
checkServerIdentity: (servername, cert) => { },
|
|
ca: `${keys.agent1.cert}\n${keys.agent6.ca}`,
|
|
ciphers: cciphers,
|
|
maxVersion: max_tls_ver(cciphers, options),
|
|
},
|
|
server: {
|
|
cert: keys.agent6.cert,
|
|
key: keys.agent6.key,
|
|
ciphers: sciphers,
|
|
maxVersion: max_tls_ver(sciphers, options),
|
|
},
|
|
}, 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 = 'So long and thanks for all the fish.';
|
|
|
|
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);
|
|
|
|
pair.server.conn.write(reply);
|
|
|
|
pair.client.conn.on('data', common.mustCall((data) => {
|
|
assert.strictEqual(data.toString(), reply);
|
|
return cleanup();
|
|
}));
|
|
}));
|
|
}
|
|
|
|
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');
|
|
|
|
// Cipher order ignored, TLS1.3 before TLS1.2 and
|
|
// cipher suites are not disabled if TLS ciphers are set only
|
|
// TODO: maybe these tests should be reworked so maxVersion clamping
|
|
// is done explicitly and not implicitly in the test() function
|
|
test('AES256-SHA', U, 'TLS_AES_256_GCM_SHA384', U, U, { maxVersion: 'TLSv1.3' });
|
|
test(U, 'AES256-SHA', 'TLS_AES_256_GCM_SHA384', U, U, { maxVersion: 'TLSv1.3' });
|
|
|
|
// 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');
|
|
|
|
// Invalid cipher values
|
|
test(9, 'AES256-SHA', U, 'ERR_INVALID_ARG_TYPE', U);
|
|
test('AES256-SHA', 9, U, U, 'ERR_INVALID_ARG_TYPE');
|
|
test(':', 'AES256-SHA', U, 'ERR_INVALID_ARG_VALUE', U);
|
|
test('AES256-SHA', ':', U, U, 'ERR_INVALID_ARG_VALUE');
|
|
|
|
// Using '' is synonymous for "use default ciphers"
|
|
test('TLS_AES_256_GCM_SHA384', '', 'TLS_AES_256_GCM_SHA384');
|
|
test('', 'TLS_AES_256_GCM_SHA384', 'TLS_AES_256_GCM_SHA384');
|
|
|
|
// Using null should be treated the same as undefined.
|
|
test(null, 'AES256-SHA', 'AES256-SHA');
|
|
test('AES256-SHA', null, 'AES256-SHA');
|