diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown index 27cb8d84468f94..60ef51d5aeb933 100644 --- a/doc/api/tls.markdown +++ b/doc/api/tls.markdown @@ -347,6 +347,11 @@ Creates a new client connection to the given `port` and `host` (old API) or - `session`: A `Buffer` instance, containing TLS session. + - `minDHSize`: Minimum size of DH parameter in bits to accept a TLS + connection. When a server offers DH parameter with a size less + than this, the TLS connection is destroyed and throws an + error. Default: 1024. + The `callback` parameter will be added as a listener for the ['secureConnect'][] event. diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 073ae36df70d3d..925e8c4f45edcc 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -880,7 +880,8 @@ exports.connect = function(/* [port, host], options, cb */) { var defaults = { rejectUnauthorized: '0' !== process.env.NODE_TLS_REJECT_UNAUTHORIZED, ciphers: tls.DEFAULT_CIPHERS, - checkServerIdentity: tls.checkServerIdentity + checkServerIdentity: tls.checkServerIdentity, + minDHSize: 1024 }; options = util._extend(defaults, options || {}); @@ -888,6 +889,8 @@ exports.connect = function(/* [port, host], options, cb */) { options.singleUse = true; assert(typeof options.checkServerIdentity === 'function'); + assert(typeof options.minDHSize === 'number'); + assert(options.minDHSize > 0); var hostname = options.servername || options.host || @@ -939,6 +942,17 @@ exports.connect = function(/* [port, host], options, cb */) { socket._start(); socket.on('secure', function() { + // Check the size of DHE parameter above minimum requirement + // specified in options. + var ekeyinfo = socket.getEphemeralKeyInfo(); + if (ekeyinfo.type === 'DH' && ekeyinfo.size < options.minDHSize) { + var err = new Error('DH parameter size ' + ekeyinfo.size + + ' is less than ' + options.minDHSize); + socket.emit('error', err); + socket.destroy(); + return; + } + var verifyError = socket._handle.verifyError(); // Verify that server's identity matches it's certificate's names diff --git a/test/parallel/test-tls-client-mindhsize.js b/test/parallel/test-tls-client-mindhsize.js new file mode 100644 index 00000000000000..fde3de512cdc26 --- /dev/null +++ b/test/parallel/test-tls-client-mindhsize.js @@ -0,0 +1,81 @@ +'use strict'; +var common = require('../common'); +var assert = require('assert'); + +if (!common.hasCrypto) { + console.log('1..0 # Skipped: missing crypto'); + process.exit(); +} +var tls = require('tls'); + +var fs = require('fs'); +var key = fs.readFileSync(common.fixturesDir + '/keys/agent2-key.pem'); +var cert = fs.readFileSync(common.fixturesDir + '/keys/agent2-cert.pem'); + +var nsuccess = 0; +var nerror = 0; + +function loadDHParam(n) { + var path = common.fixturesDir; + if (n !== 'error') path += '/keys'; + return fs.readFileSync(path + '/dh' + n + '.pem'); +} + +function test(size, err, next) { + var options = { + key: key, + cert: cert, + dhparam: loadDHParam(size), + ciphers: 'DHE-RSA-AES128-GCM-SHA256' + }; + + var server = tls.createServer(options, function(conn) { + conn.end(); + }); + + server.on('close', function(isException) { + assert(!isException); + if (next) next(); + }); + + server.listen(common.PORT, '127.0.0.1', function() { + // client set minimum DH parameter size to 2048 bits so that + // it fails when it make a connection to the tls server where + // dhparams is 1024 bits + var client = tls.connect({ + minDHSize: 2048, + port: common.PORT, + rejectUnauthorized: false + }, function() { + nsuccess++; + server.close(); + }); + if (err) { + client.on('error', function(e) { + nerror++; + assert.strictEqual(e.message, 'DH parameter size 1024 is less' + + ' than 2048'); + server.close(); + }); + } + }); +} + +// A client connection fails with an error when a client has an +// 2048 bits minDHSize option and a server has 1024 bits dhparam +function testDHE1024() { + test(1024, true, testDHE2048); +} + +// A client connection successes when a client has an +// 2048 bits minDHSize option and a server has 2048 bits dhparam +function testDHE2048() { + test(2048, false, null); +} + +testDHE1024(); + +process.on('exit', function() { + assert.equal(nsuccess, 1); + assert.equal(nerror, 1); +});