1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22'use strict'; 23 24const common = require('../common'); 25 26if (!common.opensslCli) 27 common.skip('node compiled without OpenSSL CLI.'); 28 29if (!common.hasCrypto) 30 common.skip('missing crypto'); 31 32if (common.isWindows) 33 common.skip('test does not work on Windows'); // ...but it should! 34 35const net = require('net'); 36const assert = require('assert'); 37const fixtures = require('../common/fixtures'); 38const tls = require('tls'); 39const spawn = require('child_process').spawn; 40 41const useIPv4 = !common.hasIPv6; 42 43test1(); 44 45// simple/test-tls-securepair-client 46function test1() { 47 test('keys/rsa_private.pem', 'keys/rsa_cert.crt', null, test2); 48} 49 50// simple/test-tls-ext-key-usage 51function test2() { 52 function check(pair) { 53 // "TLS Web Client Authentication" 54 assert.strictEqual(pair.cleartext.getPeerCertificate().ext_key_usage.length, 55 1); 56 assert.strictEqual(pair.cleartext.getPeerCertificate().ext_key_usage[0], 57 '1.3.6.1.5.5.7.3.2'); 58 } 59 test('keys/agent4-key.pem', 'keys/agent4-cert.pem', check); 60} 61 62function test(keyPath, certPath, check, next) { 63 const key = fixtures.readSync(keyPath).toString(); 64 const cert = fixtures.readSync(certPath).toString(); 65 66 const server = spawn(common.opensslCli, ['s_server', 67 '-accept', 0, 68 '-cert', fixtures.path(certPath), 69 '-key', fixtures.path(keyPath), 70 ...(useIPv4 ? ['-4'] : []), 71 ]); 72 server.stdout.pipe(process.stdout); 73 server.stderr.pipe(process.stdout); 74 75 76 let state = 'WAIT-ACCEPT'; 77 78 let serverStdoutBuffer = ''; 79 server.stdout.setEncoding('utf8'); 80 server.stdout.on('data', function(s) { 81 serverStdoutBuffer += s; 82 console.log(state); 83 switch (state) { 84 case 'WAIT-ACCEPT': { 85 const matches = serverStdoutBuffer.match(/ACCEPT .*?:(\d+)/); 86 if (matches) { 87 const port = matches[1]; 88 state = 'WAIT-HELLO'; 89 startClient(port); 90 } 91 break; 92 } 93 case 'WAIT-HELLO': 94 if (/hello/.test(serverStdoutBuffer)) { 95 96 // End the current SSL connection and exit. 97 // See s_server(1ssl). 98 server.stdin.write('Q'); 99 100 state = 'WAIT-SERVER-CLOSE'; 101 } 102 break; 103 104 default: 105 break; 106 } 107 }); 108 109 110 const timeout = setTimeout(function() { 111 server.kill(); 112 process.exit(1); 113 }, 5000); 114 115 let gotWriteCallback = false; 116 let serverExitCode = -1; 117 118 server.on('exit', function(code) { 119 serverExitCode = code; 120 clearTimeout(timeout); 121 if (next) next(); 122 }); 123 124 125 function startClient(port) { 126 const s = new net.Stream(); 127 128 const sslcontext = tls.createSecureContext({ key, cert }); 129 sslcontext.context.setCiphers('RC4-SHA:AES128-SHA:AES256-SHA'); 130 131 const pair = tls.createSecurePair(sslcontext, false); 132 133 assert.ok(pair.encrypted.writable); 134 assert.ok(pair.cleartext.writable); 135 136 pair.encrypted.pipe(s); 137 s.pipe(pair.encrypted); 138 139 s.connect(port); 140 141 s.on('connect', function() { 142 console.log('client connected'); 143 setTimeout(function() { 144 pair.cleartext.write('hello\r\n', function() { 145 gotWriteCallback = true; 146 }); 147 }, 500); 148 }); 149 150 pair.on('secure', function() { 151 console.log('client: connected+secure!'); 152 console.log('client pair.cleartext.getPeerCertificate(): %j', 153 pair.cleartext.getPeerCertificate()); 154 console.log('client pair.cleartext.getCipher(): %j', 155 pair.cleartext.getCipher()); 156 if (check) check(pair); 157 }); 158 159 pair.cleartext.on('data', function(d) { 160 console.log('cleartext: %s', d.toString()); 161 }); 162 163 s.on('close', function() { 164 console.log('client close'); 165 }); 166 167 pair.encrypted.on('error', function(err) { 168 console.log(`encrypted error: ${err}`); 169 }); 170 171 s.on('error', function(err) { 172 console.log(`socket error: ${err}`); 173 }); 174 175 pair.on('error', function(err) { 176 console.log(`secure error: ${err}`); 177 }); 178 } 179 180 181 process.on('exit', function() { 182 assert.strictEqual(serverExitCode, 0); 183 assert.strictEqual(state, 'WAIT-SERVER-CLOSE'); 184 assert.ok(gotWriteCallback); 185 }); 186} 187