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.
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.
- `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.
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
} 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) {
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'));
};
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;
isServer: false,
requestCert: true,
rejectUnauthorized: options.rejectUnauthorized,
+ session: options.session,
NPNProtocols: NPN.NPNProtocols
});
result = socket;
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);
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;
}
}
+template <class Base>
+void SSLWrap<Base>::GetTLSTicket(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(args.GetIsolate());
+
+ Base* w = Unwrap<Base>(args.This());
+ Environment* env = w->ssl_env();
+
+ SSL_SESSION* sess = SSL_get_session(w->ssl_);
+ if (sess == NULL || sess->tlsext_tick == NULL)
+ return;
+
+ Local<Object> buf = Buffer::New(env,
+ reinterpret_cast<char*>(sess->tlsext_tick),
+ sess->tlsext_ticklen);
+
+ args.GetReturnValue().Set(buf);
+}
+
+
#ifdef SSL_set_max_send_fragment
template <class Base>
void SSLWrap<Base>::SetMaxSendFragment(
static void EndParser(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Renegotiate(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Shutdown(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetTLSTicket(const v8::FunctionCallbackInfo<v8::Value>& args);
#ifdef SSL_set_max_send_fragment
static void SetMaxSendFragment(
--- /dev/null
+// 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]);
+ }
+});