Browse Source

tls, crypto: add DHE support

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.

Reviewed-By: Fedor Indutny <fedor@indutny.com>
v0.11.14-release
Shigeki Ohtsu 10 years ago
committed by Fedor Indutny
parent
commit
0dfedb7127
  1. 4
      doc/api/tls.markdown
  2. 2
      lib/_tls_common.js
  3. 2
      lib/_tls_wrap.js
  4. 32
      src/node_crypto.cc
  5. 1
      src/node_crypto.h
  6. 3
      test/fixtures/dherror.pem
  7. 11
      test/fixtures/keys/Makefile
  8. 5
      test/fixtures/keys/dh1024.pem
  9. 8
      test/fixtures/keys/dh2048.pem
  10. 4
      test/fixtures/keys/dh512.pem
  11. 107
      test/simple/test-tls-dhe.js

4
doc/api/tls.markdown

@ -165,6 +165,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.

2
lib/_tls_common.js

@ -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++) {

2
lib/_tls_wrap.js

@ -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,
@ -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;

32
src/node_crypto.cc

@ -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);
@ -746,6 +747,37 @@ 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);
BIO_free_all(bio);
if (dh == NULL)
return;
SSL_CTX_set_options(sc->ctx_, SSL_OP_SINGLE_DH_USE);
int r = SSL_CTX_set_tmp_dh(sc->ctx_, dh);
DH_free(dh);
if (!r)
return env->ThrowTypeError("Error setting temp DH parameter");
}
void SecureContext::SetOptions(const FunctionCallbackInfo<Value>& args) {
HandleScope scope(args.GetIsolate());

1
src/node_crypto.h

@ -95,6 +95,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);

3
test/fixtures/dherror.pem

@ -0,0 +1,3 @@
-----BEGIN DH PARAMETERS-----
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-----END DH PARAMETERS-----

11
test/fixtures/keys/Makefile

@ -1,4 +1,4 @@
all: agent1-cert.pem agent2-cert.pem agent3-cert.pem agent4-cert.pem ca2-crl.pem ec-cert.pem
all: agent1-cert.pem agent2-cert.pem agent3-cert.pem agent4-cert.pem ca2-crl.pem ec-cert.pem dh512.pem dh1024.pem dh2048.pem
#
@ -145,6 +145,15 @@ ec-cert.pem: ec-csr.pem ec-key.pem
-signkey ec-key.pem \
-out ec-cert.pem
dh512.pem:
openssl dhparam -out dh512.pem 512
dh1024.pem:
openssl dhparam -out dh1024.pem 1024
dh2048.pem:
openssl dhparam -out dh2048.pem 2048
clean:
rm -f *.pem *.srl ca2-database.txt ca2-serial

5
test/fixtures/keys/dh1024.pem

@ -0,0 +1,5 @@
-----BEGIN DH PARAMETERS-----
MIGHAoGBAO2Ij3VgqTKsxLZiK0uW0xWVszBzNbdjgLkriicMcZuUj1fQOSM6CPwv
5kdrRV8kHCK9q8dU1Dpf2dgh3l4fFFLpjIuUmUx3b7Q+GfHZ1UepNxr1NHSJaCl+
wA0gwSDYhy8xRAsZ3bFVsLfDCuxuzPNC0yjtS5CVqci//vq0NTM7AgEC
-----END DH PARAMETERS-----

8
test/fixtures/keys/dh2048.pem

@ -0,0 +1,8 @@
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEAg3ytm4B8bqS4KmLTw7eeqrzp8Y4Ew65weXKL9eY2FmudR0VUkoti
fs7/fKDsxMVgLyL+9UpbTs18CNslwWgMCSkrXe/NtXWlQQBLgXhEHOaeK/6j9zyt
S6rlSvGK68NF0/e7o6jZXOSktIeflJQXoUyeds65+la/l5O2iuX0tgnGfNjB2Pdt
RnoAPNJW+SBvyfcmiWjfd5Lh67SBgckqFMiH+CPiw54U2CeDPB343DUEPpTcnLJB
aJs4uuxDnskz/0ZVidNBpUBs1wPQ8ruVNZx3hG2+PIqNPvOfYUPXIgn1ABj3mGAR
sgtN63KUBX322zkTVPJnt30mrWp/F62GSwIBAg==
-----END DH PARAMETERS-----

4
test/fixtures/keys/dh512.pem

@ -0,0 +1,4 @@
-----BEGIN DH PARAMETERS-----
MEYCQQDpl3okBAjG92NSOaQEsIyqzvJRN06yHuGXunxYVIqxg7TnU8DBZW0ZYyiJ
rJLRA/9b9dCk5DXpq1pFGoAkYLoDAgEC
-----END DH PARAMETERS-----

107
test/simple/test-tls-dhe.js

@ -0,0 +1,107 @@
// 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) {
var path = common.fixturesDir;
if (n !== 'error') path += '/keys';
return fs.readFileSync(path + '/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', testError);
ntests++;
}
function testError() {
test('error', 'ECDHE-RSA-AES128-SHA256', null);
ntests++;
}
test512();
process.on('exit', function() {
assert.equal(ntests, nsuccess);
});
Loading…
Cancel
Save