Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto: support multiple ECDH curves and auto #15206

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions doc/api/tls.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ openssl dhparam -outform PEM -out dhparam.pem 2048

If using Perfect Forward Secrecy using `ECDHE`, Diffie-Hellman parameters are
not required and a default ECDHE curve will be used. The `ecdhCurve` property
can be used when creating a TLS Server to specify the name of an alternative
curve to use, see [`tls.createServer()`] for more info.
can be used when creating a TLS Server to specify the list of names of supported
curves to use, see [`tls.createServer()`] for more info.

### ALPN, NPN and SNI

Expand Down Expand Up @@ -984,11 +984,13 @@ changes:
preferences instead of the client's. When `true`, causes
`SSL_OP_CIPHER_SERVER_PREFERENCE` to be set in `secureOptions`, see
[OpenSSL Options][] for more information.
* `ecdhCurve` {string} A string describing a named curve to use for ECDH key
agreement or `false` to disable ECDH. Defaults to
[`tls.DEFAULT_ECDH_CURVE`]. Use [`crypto.getCurves()`][] to obtain a list
of available curve names. On recent releases, `openssl ecparam -list_curves`
will also display the name and description of each available elliptic curve.
* `ecdhCurve` {string} A string describing a named curve or a colon separated
list of curve NIDs or names, for example `P-521:P-384:P-256`, to use for
ECDH key agreement, or `false` to disable ECDH. Set to `auto` to select the
curve automatically. Defaults to [`tls.DEFAULT_ECDH_CURVE`]. Use
[`crypto.getCurves()`][] to obtain a list of available curve names. On
recent releases, `openssl ecparam -list_curves` will also display the name
and description of each available elliptic curve.
* `dhparam` {string|Buffer} Diffie Hellman parameters, required for
[Perfect Forward Secrecy][]. Use `openssl dhparam` to create the parameters.
The key length must be greater than or equal to 1024 bits, otherwise an
Expand Down
18 changes: 6 additions & 12 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -923,20 +923,14 @@ void SecureContext::SetECDHCurve(const FunctionCallbackInfo<Value>& args) {

node::Utf8Value curve(env->isolate(), args[0]);

int nid = OBJ_sn2nid(*curve);

if (nid == NID_undef)
return env->ThrowTypeError("First argument should be a valid curve name");

EC_KEY* ecdh = EC_KEY_new_by_curve_name(nid);

if (ecdh == nullptr)
return env->ThrowTypeError("First argument should be a valid curve name");

SSL_CTX_set_options(sc->ctx_, SSL_OP_SINGLE_ECDH_USE);
SSL_CTX_set_tmp_ecdh(sc->ctx_, ecdh);
SSL_CTX_set_ecdh_auto(sc->ctx_, 1);

if (strcmp(*curve, "auto") == 0)
return;

EC_KEY_free(ecdh);
if (!SSL_CTX_set1_curves_list(sc->ctx_, *curve))
return env->ThrowError("Failed to set ECDH curve");
}


Expand Down
64 changes: 64 additions & 0 deletions test/parallel/test-tls-ecdh-auto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
'use strict';
const common = require('../common');

// This test ensures that the value "auto" on ecdhCurve option is
// supported to enable automatic curve selection in TLS server.

if (!common.hasCrypto)
common.skip('missing crypto');

if (!common.opensslCli)
common.skip('missing openssl-cli');

const assert = require('assert');
const tls = require('tls');
const spawn = require('child_process').spawn;
const fixtures = require('../common/fixtures');

function loadPEM(n) {
return fixtures.readKey(`${n}.pem`);
}

const options = {
key: loadPEM('agent2-key'),
cert: loadPEM('agent2-cert'),
ciphers: '-ALL:ECDHE-RSA-AES128-SHA256',
ecdhCurve: 'auto'
};

const reply = 'I AM THE WALRUS'; // something recognizable

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

let gotReply = false;

server.listen(0, function() {
const args = ['s_client',
'-cipher', `${options.ciphers}`,
'-connect', `127.0.0.1:${this.address().port}`];

// for the performance and stability issue in s_client on Windows
if (common.isWindows)
args.push('-no_rand_screen');

const client = spawn(common.opensslCli, args);

client.stdout.on('data', function(data) {
const message = data.toString();
if (message.includes(reply))
gotReply = true;
});

client.on('exit', function(code) {
assert.strictEqual(0, code);
server.close();
});

client.on('error', assert.ifError);
});

process.on('exit', function() {
assert.ok(gotReply);
});
80 changes: 80 additions & 0 deletions test/parallel/test-tls-ecdh-multiple.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
'use strict';
const common = require('../common');

// This test ensures that ecdhCurve option of TLS server supports colon
// separated ECDH curve names as value.

if (!common.hasCrypto)
common.skip('missing crypto');

if (!common.opensslCli)
common.skip('missing openssl-cli');

const assert = require('assert');
const tls = require('tls');
const spawn = require('child_process').spawn;
const fixtures = require('../common/fixtures');

function loadPEM(n) {
return fixtures.readKey(`${n}.pem`);
}

const options = {
key: loadPEM('agent2-key'),
cert: loadPEM('agent2-cert'),
ciphers: '-ALL:ECDHE-RSA-AES128-SHA256',
ecdhCurve: 'secp256k1:prime256v1:secp521r1'
};

const reply = 'I AM THE WALRUS'; // something recognizable

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

let gotReply = false;

server.listen(0, function() {
const args = ['s_client',
'-cipher', `${options.ciphers}`,
'-connect', `127.0.0.1:${this.address().port}`];

// for the performance and stability issue in s_client on Windows
if (common.isWindows)
args.push('-no_rand_screen');

const client = spawn(common.opensslCli, args);

client.stdout.on('data', function(data) {
const message = data.toString();
if (message.includes(reply))
gotReply = true;
});

client.on('exit', function(code) {
assert.strictEqual(0, code);
server.close();
});

client.on('error', assert.ifError);
});

process.on('exit', function() {
assert.ok(gotReply);

// Some of unsupported curves
const unsupportedCurves = [
'wap-wsg-idm-ecid-wtls1',
'c2pnb163v1',
'prime192v3'
];

// Brainpool is not supported in FIPS mode
if (common.hasFipsCrypto)
unsupportedCurves.push('brainpoolP256r1');

unsupportedCurves.forEach((ecdhCurve) => {
assert.throws(() => tls.createServer({ ecdhCurve: ecdhCurve }),
/Error: Failed to set ECDH curve/);
});
});