AtlatestRepositorysigil-tls

sigil-tls / tree / test / fixturestls-matrix.cjs

1const fs = require('fs');
2const tls = require('tls');
3const cp = require('child_process');
4const path = require('path');
5const os = require('os');
6
7const binary = process.argv[2];
8if (!binary) {
9 throw new Error('Usage: node test/fixtures/tls-matrix.cjs /path/to/tls-client');
11const root = fs.mkdtempSync(path.join(os.tmpdir(), 'sigil-tls-matrix-'));
12let failures = 0;
13process.on('exit', () => fs.rmSync(root, { recursive: true, force: true }));
14cp.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' });
20function 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 });
44async 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 }
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});