diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown index d04d2cba47..16af6fe74f 100644 --- a/doc/api/tls.markdown +++ b/doc/api/tls.markdown @@ -630,6 +630,21 @@ Returns the bound address, the address family name and port of the server as reported by the operating system. See [net.Server.address()][] for more information. +### server.getTicketKeys() + +Returns `Buffer` instance holding the keys currently used for +encryption/decryption of the [TLS Session Tickets][] + +### server.setTicketKeys(keys) + +Updates the keys for encryption/decryption of the [TLS Session Tickets][]. + +NOTE: the buffer should be 48 bytes long. See server `ticketKeys` option for +more information oh how it is going to be used. + +NOTE: the change is effective only for the future server connections. Existing +or currently pending server connections will use previous keys. + ### server.addContext(hostname, context) Add secure context that will be used if client request's SNI hostname is @@ -835,3 +850,4 @@ The numeric representation of the local port. [asn1.js]: http://npmjs.org/package/asn1.js [OCSP request]: http://en.wikipedia.org/wiki/OCSP_stapling [TLS recommendations]: https://wiki.mozilla.org/Security/Server_Side_TLS +[TLS Session Tickets]: https://www.ietf.org/rfc/rfc5077.txt diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index b96e577a76..f42da43ddd 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -812,13 +812,23 @@ exports.createServer = function(options, listener) { Server.prototype._getServerData = function() { return { - ticketKeys: this._sharedCreds.context.getTicketKeys().toString('hex') + ticketKeys: this.getTicketKeys().toString('hex') }; }; Server.prototype._setServerData = function(data) { - this._sharedCreds.context.setTicketKeys(new Buffer(data.ticketKeys, 'hex')); + this.setTicketKeys(new Buffer(data.ticketKeys, 'hex')); +}; + + +Server.prototype.getTicketKeys = function getTicketKeys(keys) { + return this._sharedCreds.context.getTicketKeys(keys); +}; + + +Server.prototype.setTicketKeys = function setTicketKeys(keys) { + this._sharedCreds.context.setTicketKeys(keys); }; diff --git a/test/parallel/test-tls-ticket.js b/test/parallel/test-tls-ticket.js index 6c3ad01fa1..ed77610002 100644 --- a/test/parallel/test-tls-ticket.js +++ b/test/parallel/test-tls-ticket.js @@ -20,6 +20,9 @@ var serverCount = 0; function createServer() { var id = serverCount++; + var counter = 0; + var previousKey = null; + var server = tls.createServer({ key: fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'), cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem'), @@ -27,14 +30,29 @@ function createServer() { }, function(c) { serverLog.push(id); c.end(); + + counter++; + + // Rotate ticket keys + if (counter === 1) { + previousKey = server.getTicketKeys(); + server.setTicketKeys(crypto.randomBytes(48)); + } else if (counter === 2) { + server.setTicketKeys(previousKey); + } else if (counter === 3) { + // Use keys from counter=2 + } else { + throw new Error('UNREACHABLE'); + } }); return server; } -var servers = [ createServer(), createServer(), - createServer(), createServer(), - createServer(), createServer() ]; +var naturalServers = [ createServer(), createServer(), createServer() ]; + +// 3x servers +var servers = naturalServers.concat(naturalServers).concat(naturalServers); // Create one TCP server and balance sockets to multiple TLS server instances var shared = net.createServer(function(c) { @@ -54,7 +72,7 @@ function start(callback) { session: sess, rejectUnauthorized: false }, function() { - sess = s.getSession() || sess; + sess = sess || s.getSession(); ticketLog.push(s.getTLSTicket().toString('hex')); }); s.on('close', function() { @@ -70,8 +88,14 @@ function start(callback) { process.on('exit', function() { assert.equal(ticketLog.length, serverLog.length); - for (var i = 0; i < serverLog.length - 1; i++) { + for (var i = 0; i < naturalServers.length - 1; i++) { assert.notEqual(serverLog[i], serverLog[i + 1]); assert.equal(ticketLog[i], ticketLog[i + 1]); + + // 2nd connection should have different ticket + assert.notEqual(ticketLog[i], ticketLog[i + naturalServers.length]); + + // 3rd connection should have the same ticket + assert.equal(ticketLog[i], ticketLog[i + naturalServers.length * 2]); } });