AtlatestRepositorysigil-tls
1
const fs = require('fs');2
const tls = require('tls');3
const cp = require('child_process');4
const path = require('path');5
const os = require('os');7
const binary = process.argv[2];8
if (!binary) {9
throw new Error('Usage: node test/fixtures/tls-matrix.cjs /path/to/tls-client');10
}11
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'sigil-tls-matrix-'));12
let failures = 0;13
process.on('exit', () => fs.rmSync(root, { recursive: true, force: true }));14
cp.execFileSync(process.env.OPENSSL || 'openssl', [15
'req', '-x509', '-newkey', 'rsa:2048', '-nodes',16
'-keyout', root + '/key.pem', '-out', root + '/cert.pem',17
'-days', '1', '-subj', '/CN=localhost', '-addext', 'subjectAltName=DNS:localhost',18
], { stdio: 'ignore' });20
function probe(port, version, label, host, trust, extra, expect) {21
return new Promise((resolve, reject) => {22
const args = [host, String(port), ...(trust ? [root + '/cert.pem'] : [])];23
const env = { ...process.env, ...extra };24
delete env.SIGIL_TLS_INSECURE;25
const child = cp.spawn(binary, args, { env });26
let output = '';27
const timer = setTimeout(() => child.kill('SIGKILL'), 15000);28
child.stdout.on('data', data => { output += data; });29
child.stderr.on('data', data => { output += data; });30
child.on('error', error => {31
clearTimeout(timer);32
reject(error);33
});34
child.on('exit', (code, signal) => {35
clearTimeout(timer);36
const pass = !signal && expect(code, output);37
console.log(JSON.stringify({ version, label, code, signal, pass, output }));38
if (!pass) failures++;39
resolve();40
});41
});42
}44
async function checkVersion(version) {45
const sockets = new Set();46
const server = tls.createServer({47
key: fs.readFileSync(root + '/key.pem'),48
cert: fs.readFileSync(root + '/cert.pem'),49
minVersion: version,50
maxVersion: version,51
}, socket => {52
socket.on('data', data => socket.write(data));53
});54
server.on('connection', socket => {55
sockets.add(socket);56
socket.on('error', () => {});57
socket.on('close', () => sockets.delete(socket));58
});59
// Certificate-negative arms deliberately terminate their TLS handshakes.60
server.on('tlsClientError', () => {});61
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));62
const port = server.address().port;63
try {64
await probe(port, version, 'verified-repeat-40', 'localhost', true,65
{ PROBE_ECHO: '1', PROBE_REPEAT: '40' }, (code, output) => {66
const attempts = output.split('\n').filter(line => line.startsWith('attempt='));67
return code === 0 && attempts.length === 40 &&68
attempts.every(line => line.includes('protocol=' + version)) &&69
(output.match(/echo=ok/g) || []).length === 40;70
});71
await probe(port, version, 'wrong-host', '127.0.0.1', true, {},72
(code, output) => code === 1 && output.includes('error=-9984') &&73
output.includes('verify=4'));74
await probe(port, version, 'untrusted', 'localhost', false, {},75
(code, output) => code === 1 && output.includes('error=-9984'));76
await probe(port, version, 'recover-after-rejection', 'localhost', true,77
{ PROBE_ECHO: '1', PROBE_REPEAT: '2', PROBE_FIRST_BAD_HOST: '1' },78
(code, output) => code === 0 && output.includes('error=-9984') &&79
output.includes('attempt=1 status=connected') && output.includes('echo=ok'));80
} finally {81
for (const socket of sockets) socket.destroy();82
await new Promise(resolve => server.close(resolve));83
}84
}86
(async () => {87
await checkVersion('TLSv1.2');88
await checkVersion('TLSv1.3');89
process.exitCode = failures ? 1 : 0;90
})().catch(error => {91
console.error(error);92
process.exitCode = 1;93
});