From: Fedor Indutny Date: Mon, 3 Feb 2014 21:32:13 +0000 (+0400) Subject: tls: more session configuration options, methods X-Git-Tag: v0.11.12~98 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=528a3ce3ed0df7aaafb02877495c94caa1d77355;p=platform%2Fupstream%2Fnodejs.git tls: more session configuration options, methods Introduce `ticketKeys` server option, `session` client option, `getSession()` and `getTLSTicket()` methods. fix #7032 --- diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown index cf7a87f..2a4c312 100644 --- a/doc/api/tls.markdown +++ b/doc/api/tls.markdown @@ -205,6 +205,12 @@ automatically set as a listener for the [secureConnection][] event. The session identifiers and TLS session tickets created by the server are timed out. See [SSL_CTX_set_timeout] for more details. + - `ticketKeys`: A 48-byte `Buffer` instance consisting of 16-byte prefix, + 16-byte hmac key, 16-byte AES key. You could use it to accept tls session + tickets on multiple instances of tls server. + + NOTE: Automatically shared between `cluster` module workers. + - `sessionIdContext`: A string containing a opaque identifier for session resumption. If `requestCert` is `true`, the default is MD5 hash value generated from command-line. Otherwise, the default is not provided. @@ -314,6 +320,8 @@ Creates a new client connection to the given `port` and `host` (old API) or SSL version 3. The possible values depend on your installation of OpenSSL and are defined in the constant [SSL_METHODS][]. + - `session`: A `Buffer` instance, containing TLS session. + The `callback` parameter will be added as a listener for the ['secureConnect'][] event. @@ -398,6 +406,8 @@ Construct a new TLSSocket object from existing TCP socket. - `SNICallback`: Optional, see [tls.createServer][] + - `session`: Optional, a `Buffer` instance, containing TLS session + ## tls.createSecurePair([credentials], [isServer], [requestCert], [rejectUnauthorized]) Stability: 0 - Deprecated. Use tls.TLSSocket instead. @@ -646,6 +656,18 @@ and their processing can be delayed due to packet loss or reordering. However, smaller fragments add extra TLS framing bytes and CPU overhead, which may decrease overall server throughput. +### tlsSocket.getSession() + +Return ASN.1 encoded TLS session or `undefined` if none was negotiated. Could +be used to speed up handshake establishment when reconnecting to the server. + +### tlsSocket.getTLSTicket() + +NOTE: Works only with client TLS sockets. Useful only for debugging, for +session reuse provide `session` option to `tls.connect`. + +Return TLS session ticket or `undefined` if none was negotiated. + ### tlsSocket.address() Returns the bound address, the address family name and port of the diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 9324130..ff76210 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -232,6 +232,9 @@ TLSSocket.prototype._init = function(socket) { } else { this.ssl.onhandshakestart = function() {}; this.ssl.onhandshakedone = this._finishInit.bind(this); + + if (options.session) + this.ssl.setSession(options.session); } this.ssl.onerror = function(err) { @@ -321,6 +324,10 @@ TLSSocket.prototype.setMaxSendFragment = function setMaxSendFragment(size) { return this.ssl.setMaxSendFragment(size) == 1; }; +TLSSocket.prototype.getTLSTicket = function getTLSTicket() { + return this.ssl.getTLSTicket(); +}; + TLSSocket.prototype._handleTimeout = function() { this._tlsError(new Error('TLS handshake timeout')); }; @@ -620,6 +627,7 @@ Server.prototype.setOptions = function(options) { if (!util.isUndefined(options.ecdhCurve)) this.ecdhCurve = options.ecdhCurve; if (options.sessionTimeout) this.sessionTimeout = options.sessionTimeout; + if (options.ticketKeys) this.ticketKeys = options.ticketKeys; var secureOptions = options.secureOptions || 0; if (options.honorCipherOrder) { secureOptions |= constants.SSL_OP_CIPHER_SERVER_PREFERENCE; @@ -747,6 +755,7 @@ exports.connect = function(/* [port, host], options, cb */) { isServer: false, requestCert: true, rejectUnauthorized: options.rejectUnauthorized, + session: options.session, NPNProtocols: NPN.NPNProtocols }); result = socket; diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 987e2cf..9952b2c 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -856,6 +856,7 @@ void SSLWrap::AddMethods(Handle t) { NODE_SET_PROTOTYPE_METHOD(t, "endParser", EndParser); NODE_SET_PROTOTYPE_METHOD(t, "renegotiate", Renegotiate); NODE_SET_PROTOTYPE_METHOD(t, "shutdown", Shutdown); + NODE_SET_PROTOTYPE_METHOD(t, "getTLSTicket", GetTLSTicket); #ifdef SSL_set_max_send_fragment NODE_SET_PROTOTYPE_METHOD(t, "setMaxSendFragment", SetMaxSendFragment); @@ -1129,7 +1130,7 @@ void SSLWrap::GetSession(const FunctionCallbackInfo& args) { unsigned char* sbuf = new unsigned char[slen]; unsigned char* p = sbuf; i2d_SSL_SESSION(sess, &p); - args.GetReturnValue().Set(Encode(sbuf, slen, BINARY)); + args.GetReturnValue().Set(Encode(sbuf, slen, BUFFER)); delete[] sbuf; } @@ -1247,6 +1248,25 @@ void SSLWrap::Shutdown(const FunctionCallbackInfo& args) { } +template +void SSLWrap::GetTLSTicket(const FunctionCallbackInfo& args) { + HandleScope scope(args.GetIsolate()); + + Base* w = Unwrap(args.This()); + Environment* env = w->ssl_env(); + + SSL_SESSION* sess = SSL_get_session(w->ssl_); + if (sess == NULL || sess->tlsext_tick == NULL) + return; + + Local buf = Buffer::New(env, + reinterpret_cast(sess->tlsext_tick), + sess->tlsext_ticklen); + + args.GetReturnValue().Set(buf); +} + + #ifdef SSL_set_max_send_fragment template void SSLWrap::SetMaxSendFragment( diff --git a/src/node_crypto.h b/src/node_crypto.h index aa670db..8e94950 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -187,6 +187,7 @@ class SSLWrap { static void EndParser(const v8::FunctionCallbackInfo& args); static void Renegotiate(const v8::FunctionCallbackInfo& args); static void Shutdown(const v8::FunctionCallbackInfo& args); + static void GetTLSTicket(const v8::FunctionCallbackInfo& args); #ifdef SSL_set_max_send_fragment static void SetMaxSendFragment( diff --git a/test/simple/test-tls-ticket.js b/test/simple/test-tls-ticket.js new file mode 100644 index 0000000..471d8c3 --- /dev/null +++ b/test/simple/test-tls-ticket.js @@ -0,0 +1,95 @@ +// 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. + +if (!process.versions.openssl) { + console.error('Skipping because node compiled without OpenSSL.'); + process.exit(0); +} + +var assert = require('assert'); +var fs = require('fs'); +var net = require('net'); +var tls = require('tls'); +var crypto = require('crypto'); + +var common = require('../common'); + +var keys = crypto.randomBytes(48); +var serverLog = []; +var ticketLog = []; + +var serverCount = 0; +function createServer() { + var id = serverCount++; + + var server = tls.createServer({ + key: fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'), + cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem'), + ticketKeys: keys + }, function(c) { + serverLog.push(id); + c.end(); + }); + + return server; +} + +var servers = [ createServer(), createServer(), createServer(), createServer(), createServer(), createServer() ]; + +// Create one TCP server and balance sockets to multiple TLS server instances +var shared = net.createServer(function(c) { + servers.shift().emit('connection', c); +}).listen(common.PORT, function() { + start(function() { + shared.close(); + }); +}); + +function start(callback) { + var sess = null; + var left = servers.length; + + function connect() { + var s = tls.connect(common.PORT, { + session: sess, + rejectUnauthorized: false + }, function() { + sess = s.getSession() || sess; + ticketLog.push(s.getTLSTicket().toString('hex')); + }); + s.on('close', function() { + if (--left === 0) + callback(); + else + connect(); + }); + } + + connect(); +} + +process.on('exit', function() { + assert.equal(ticketLog.length, serverLog.length); + for (var i = 0; i < serverLog.length - 1; i++) { + assert.notEqual(serverLog[i], serverLog[i + 1]); + assert.equal(ticketLog[i], ticketLog[i + 1]); + } +});