tls: add `getTicketKeys()`/`setTicketKeys()`
authorFedor Indutny <fedor@indutny.com>
Wed, 22 Jul 2015 20:52:23 +0000 (13:52 -0700)
committerFedor Indutny <fedor@indutny.com>
Thu, 23 Jul 2015 18:13:26 +0000 (11:13 -0700)
Introduce two new APIs for getting/settings the TLS Server Ticket Keys.

Fix: #1465
PR-URL: https://github.com/nodejs/io.js/pull/2227
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
doc/api/tls.markdown
lib/_tls_wrap.js
test/parallel/test-tls-ticket.js

index d04d2cb..16af6fe 100644 (file)
@@ -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
index b96e577..f42da43 100644 (file)
@@ -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);
 };
 
 
index 6c3ad01..ed77610 100644 (file)
@@ -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]);
   }
 });