Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Commit

Permalink
tls, crypto: add DHE support
Browse files Browse the repository at this point in the history
This also changes the default cipher list as `DHE-RSA-AES128-SHA256`
is added and '!EDH' is explicitly written to
`!EDH-RSA-DES:!EDH-DSS-DES`. In case of an invalid DH parameter file,
it is sliently discarded. To use auto DH parameter in a server and DHE
key length check in a client, we need to wait for the next release of
OpenSSL-1.0.2.
  • Loading branch information
Shigeki Ohtsu committed Aug 27, 2014
1 parent 6adf3ec commit 4dcb018
Show file tree
Hide file tree
Showing 12 changed files with 197 additions and 9 deletions.
20 changes: 13 additions & 7 deletions doc/api/tls.markdown
Expand Up @@ -142,14 +142,16 @@ automatically set as a listener for the [secureConnection][] event. The
conjunction with the `honorCipherOrder` option described below to
prioritize the non-CBC cipher.

Defaults to `ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH`.
Consult the [OpenSSL cipher list format documentation] for details on the
format.
Defaults to
`ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH-RSA-DES:!EDH-DSS-DES`.
Consult the [OpenSSL cipher list format documentation] for details
on the format.

`ECDHE-RSA-AES128-SHA256` and `AES128-GCM-SHA256` are TLS v1.2 ciphers and
used when node.js is linked against OpenSSL 1.0.1 or newer, such as the
bundled version of OpenSSL. Note that it is still possible for a TLS v1.2
client to negotiate a weaker cipher unless `honorCipherOrder` is enabled.
`ECDHE-RSA-AES128-SHA256`, `DHE-RSA-AES128-SHA256` and
`AES128-GCM-SHA256` are TLS v1.2 ciphers and used when node.js is
linked against OpenSSL 1.0.1 or newer, such as the bundled version
of OpenSSL. Note that it is still possible for a TLS v1.2 client
to negotiate a weaker cipher unless `honorCipherOrder` is enabled.

`RC4` is used as a fallback for clients that speak on older version of
the TLS protocol. `RC4` has in recent years come under suspicion and
Expand All @@ -165,6 +167,10 @@ automatically set as a listener for the [secureConnection][] event. The

Defaults to `prime256v1`. Consult [RFC 4492] for more details.

- `dhparam`: DH parameter file to use for DHE key agreement. Use
`openssl dhparam` command to create it. If the file is invalid to
load, it is silently discarded.

- `handshakeTimeout`: Abort the connection if the SSL/TLS handshake does not
finish in this many milliseconds. The default is 120 seconds.

Expand Down
2 changes: 2 additions & 0 deletions lib/_tls_common.js
Expand Up @@ -97,6 +97,8 @@ exports.createSecureContext = function createSecureContext(options, context) {
else if (options.ecdhCurve)
c.context.setECDHCurve(options.ecdhCurve);

if (options.dhparam) c.context.setDHParam(options.dhparam);

if (options.crl) {
if (util.isArray(options.crl)) {
for (var i = 0, len = options.crl.length; i < len; i++) {
Expand Down
2 changes: 2 additions & 0 deletions lib/_tls_wrap.js
Expand Up @@ -600,6 +600,7 @@ function Server(/* [options], listener */) {
ca: self.ca,
ciphers: self.ciphers,
ecdhCurve: self.ecdhCurve,
dhparam: self.dhparam,
secureProtocol: self.secureProtocol,
secureOptions: self.secureOptions,
honorCipherOrder: self.honorCipherOrder,
Expand Down Expand Up @@ -718,6 +719,7 @@ Server.prototype.setOptions = function(options) {
if (options.ciphers) this.ciphers = options.ciphers;
if (!util.isUndefined(options.ecdhCurve))
this.ecdhCurve = options.ecdhCurve;
if (options.dhparam) this.dhparam = options.dhparam;
if (options.sessionTimeout) this.sessionTimeout = options.sessionTimeout;
if (options.ticketKeys) this.ticketKeys = options.ticketKeys;
var secureOptions = options.secureOptions || 0;
Expand Down
6 changes: 4 additions & 2 deletions lib/tls.js
Expand Up @@ -33,8 +33,10 @@ exports.CLIENT_RENEG_WINDOW = 600;
exports.SLAB_BUFFER_SIZE = 10 * 1024 * 1024;

exports.DEFAULT_CIPHERS =
'ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:' + // TLS 1.2
'RC4:HIGH:!MD5:!aNULL:!EDH'; // TLS 1.0
// TLS 1.2
'ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:AES128-GCM-SHA256:' +
// TLS 1.0
'RC4:HIGH:!MD5:!aNULL:!EDH-RSA-DES:!EDH-DSS-DES';

exports.DEFAULT_ECDH_CURVE = 'prime256v1';

Expand Down
33 changes: 33 additions & 0 deletions src/node_crypto.cc
Expand Up @@ -270,6 +270,7 @@ void SecureContext::Initialize(Environment* env, Handle<Object> target) {
NODE_SET_PROTOTYPE_METHOD(t, "addRootCerts", SecureContext::AddRootCerts);
NODE_SET_PROTOTYPE_METHOD(t, "setCiphers", SecureContext::SetCiphers);
NODE_SET_PROTOTYPE_METHOD(t, "setECDHCurve", SecureContext::SetECDHCurve);
NODE_SET_PROTOTYPE_METHOD(t, "setDHParam", SecureContext::SetDHParam);
NODE_SET_PROTOTYPE_METHOD(t, "setOptions", SecureContext::SetOptions);
NODE_SET_PROTOTYPE_METHOD(t, "setSessionIdContext",
SecureContext::SetSessionIdContext);
Expand Down Expand Up @@ -746,6 +747,38 @@ void SecureContext::SetECDHCurve(const FunctionCallbackInfo<Value>& args) {
}


void SecureContext::SetDHParam(const FunctionCallbackInfo<Value>& args) {
HandleScope scope(args.GetIsolate());

SecureContext* sc = Unwrap<SecureContext>(args.This());
Environment* env = sc->env();

// Auto DH is not supported in openssl 1.0.1, so dhparam needs
// to be specifed explicitly
if (args.Length() != 1) {
return env->ThrowTypeError("Bad parameter");
}

// Invalid dhparam is silently discarded and DHE is no longer used.
BIO *bio = LoadBIO(env, args[0]);
if (!bio)
return;

DH *dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);

if(dh == NULL) {
goto exit;
}

SSL_CTX_set_options(sc->ctx_, SSL_OP_SINGLE_DH_USE);
SSL_CTX_set_tmp_dh(sc->ctx_, dh);

exit:
DH_free(dh);
BIO_free(bio);
}


void SecureContext::SetOptions(const FunctionCallbackInfo<Value>& args) {
HandleScope scope(args.GetIsolate());

Expand Down
1 change: 1 addition & 0 deletions src/node_crypto.h
Expand Up @@ -93,6 +93,7 @@ class SecureContext : public BaseObject {
static void AddRootCerts(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetCiphers(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetECDHCurve(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetDHParam(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetOptions(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetSessionIdContext(
const v8::FunctionCallbackInfo<v8::Value>& args);
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/test_dh1024.pem
@@ -0,0 +1,5 @@
-----BEGIN DH PARAMETERS-----
MIGHAoGBALDYoJasveNejxRyF3d9p7+OF6ALf9MzJD4FJqEO1YDEfrfE4I75HTcU
y+h72WZXgGyI8ySUPvRs8t7W2N+0SRTKJmQnd2uKVyp0pFx5DG7TTy8DK+MFRMeh
8Tz7TptcSHktZNmLo4Cxda7IsfaKUagNhku3Ks6HS2Gg6FHkdb4zAgEC
-----END DH PARAMETERS-----
8 changes: 8 additions & 0 deletions test/fixtures/test_dh2048.pem
@@ -0,0 +1,8 @@
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEAxhRvhunS9qXXGuT1OqTfiFzDgqfyzFWt4uCN38tDODkf6z57MyZJ
6WsKExHeuNpfxS6XI0GOEWnYJqZP1+cMm8Gg2VNX0pI634tt/861ZnoWXQvOf2xa
KOBYMW/e8DmiIfQ9szqECCuY2FBG7CJC9RqI4FTYEkbR3MGAfVqxm5/4nRdUwQIj
V6jakbZ8EpKkk44iA4LBuLiR6BaLCbawK0rvdjaTTXWItMtRx5iMcSxrqaqBgA0v
Vp7EMqmKKDGJVzq5+Rbir9HUwZxBo95SolpL5D3scJoVTqYf2oTSHxqkdqtzadVl
Lq0OvBEfhX1oTPnJBH6GW+zmh462dfmlGwIBAg==
-----END DH PARAMETERS-----
13 changes: 13 additions & 0 deletions test/fixtures/test_dh4096.pem
@@ -0,0 +1,13 @@
-----BEGIN DH PARAMETERS-----
MIICCAKCAgEA1pNgDdQsDa8NTlIkV91Ye7whOPdDrd5wHFSIzTSfJn+N6u+BBsYX
T+RMMHjDg4aWjauzIS7ElF9krh1qMF5KoI+u1+a7PHPvgGzZKCLZYVq6DwrTnyXV
TBAoKHGIoTI0f3VbXjr6vOWdlMszu6fiI43XRIlVtsdoJY4rPWYJ2NQRQpOC6RAD
LOp92Y15no3PHz8ur+3syqYaIDqRmOCZis/NTp3NTvcN0TH/CrHlKxgguijNZ4CA
3kV6vACuztaefFMHUy7NKhOP2aXqydloJjAk4slXJn71SKhl0Me8kyDhALjYblqF
HtMD3p6J4FVQzRmnNmWpKjfjZ87Xn3RPdrZc8AipFM7AHLYo/V6J3iFIh0T0lq0I
QfCzeQUjxeLDaRQ7VRVGGY+P33lYk1zQbFsG+gGEkwQyRm3WfHRHJWC9pfsTWGuH
tYlZqRgQ4o3t3S6NDFajkB8HoyU90pR4gFpcsxMO/kccmIpz84rhJSedoEv6Rnnk
8sVkglWXtLvhgsw3/yGkiOKtA4MkAf6rOKh0JZfGwNXk8vymWGOxxk4thv0JDQrw
EZvyvVWZfy5EjLRJ3UdLp9AgVCY2A5HlqeOLWTYQeJ1l9RGEL+6UBN/Mu8fdh3+S
N42BBE0WUudU52hsCbZ8/TLTU6i6dco/FX5XQ4BDq1Tpbxm5k/iDXnMCAQI=
-----END DH PARAMETERS-----
4 changes: 4 additions & 0 deletions test/fixtures/test_dh512.pem
@@ -0,0 +1,4 @@
-----BEGIN DH PARAMETERS-----
MEYCQQDGhoPGQ171hbuBJoGSPxqcmJbBt0ZaVL3ZjZy79/9vlkQHtXrXbxKhj2vg
SLNu/Sfav5RTZjJpj21cKBWYDZB7AgEC
-----END DH PARAMETERS-----
3 changes: 3 additions & 0 deletions test/fixtures/test_dherror.pem
@@ -0,0 +1,3 @@
-----BEGIN DH PARAMETERS-----
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-----END DH PARAMETERS-----
109 changes: 109 additions & 0 deletions test/simple/test-tls-dhe.js
@@ -0,0 +1,109 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

var common = require('../common');

if (!common.opensslCli) {
console.error('Skipping because node compiled without OpenSSL CLI.');
process.exit(0);
}

var assert = require('assert');
var spawn = require('child_process').spawn;
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 ntests = 0;
var ciphers = 'DHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';

function loadDHParam(n) {
return fs.readFileSync(common.fixturesDir + '/test_dh' + n + '.pem');
}

function test(keylen, expectedCipher, cb) {
var options = {
key: key,
cert: cert,
dhparam: loadDHParam(keylen)
};

var server = tls.createServer(options, function(conn) {
conn.end();
});

server.on('close', function(err) {
assert(!err);
if (cb) cb();
});

server.listen(common.PORT, '127.0.0.1', function() {
var args = ['s_client', '-connect', '127.0.0.1:' + common.PORT,
'-cipher', ciphers];
var client = spawn(common.opensslCli, args);
var out = '';
client.stdout.setEncoding('utf8');
client.stdout.on('data', function(d) {
out += d;
});
client.stdout.on('end', function() {
// DHE key length can be checked -brief option in s_client but it
// is only supported in openssl 1.0.2 so we cannot check it.
var reg = new RegExp('Cipher : ' + expectedCipher);
if (reg.test(out)) {
nsuccess++;
server.close();
}
});
});
}

function test512() {
test(512, 'DHE-RSA-AES128-SHA256', test1024);
ntests++;
}

function test1024() {
test(1024, 'DHE-RSA-AES128-SHA256', test2048);
ntests++;
}

function test2048() {
test(2048, 'DHE-RSA-AES128-SHA256', test4096);
ntests++;
}

function test4096() {
test(4096, 'DHE-RSA-AES128-SHA256', testError);
ntests++;
}

function testError() {
test('error', 'ECDHE-RSA-AES128-SHA256', null);
ntests++;
}

test512();

process.on('exit', function() {
assert.equal(ntests, nsuccess);
});

0 comments on commit 4dcb018

Please sign in to comment.