diff --git a/lib/modules/SSL.pmod/ClientConnection.pike b/lib/modules/SSL.pmod/ClientConnection.pike
index 579c0efb303ebd5ba1128d339706feecfa17fad9..5388539bbdb04f156bedec4fa571be63f9025b7d 100644
--- a/lib/modules/SSL.pmod/ClientConnection.pike
+++ b/lib/modules/SSL.pmod/ClientConnection.pike
@@ -9,6 +9,12 @@
 #define SSL3_DEBUG_MSG(X ...)
 #endif
 
+#define COND_FATAL(X,Y,Z) if(X) {          \
+    send_packet(alert(ALERT_fatal, Y, Z)); \
+    return -1;                             \
+  }
+
+
 import ".";
 import Constants;
 inherit Connection;
@@ -149,6 +155,23 @@ Packet client_hello(string(8bit)|void server_name)
     return extension;
   };
 
+  ext (EXTENSION_session_ticket_tls, 1)
+  {
+    ADT.struct extension = ADT.struct();
+    // RFC 4507 and RFC 5077.
+    if (session->ticket_expiry_time < time(1)) {
+      session->ticket = UNDEFINED;
+      session->ticket_expiry_time = 0;
+    }
+    SSL3_DEBUG_MSG("SSL.ClientConnection: Sending ticket %O.\n",
+		   session->ticket);
+    // NB: RFC 4507 and RFC 5077 differ in encoding here.
+    //     Apparently no implementations actually followed
+    //     the RFC 4507 encoding.
+    extension->add_data(session->ticket || "");
+    return extension;
+  };
+
   ext (EXTENSION_application_layer_protocol_negotiation,
        !!(context->advertised_protocols))
   {
@@ -254,6 +277,32 @@ void send_renegotiate()
   send_packet(client_hello(session->server_name), PRI_application);
 }
 
+protected int(-1..1) got_new_session_ticket(ADT.struct input)
+{
+  COND_FATAL(!tickets_enabled, ALERT_handshake_failure,
+	     "Unexpected session ticket.\n");
+  // Make sure that we only get one ticket.
+  tickets_enabled = 3;
+
+  int lifetime_hint = input->read_int(4);
+  string(8bit) ticket = input->read_hstring(2);
+
+  SSL3_DEBUG_MSG("SSL.ClientConnection: Got ticket %O (%d seconds).\n",
+		 ticket, lifetime_hint);
+
+  COND_FATAL(!sizeof(ticket), ALERT_handshake_failure,
+	     "Empty ticket.\n");
+
+  if (!lifetime_hint) {
+    // Unspecified lifetime. Handle as one hour.
+    lifetime_hint = 3600;
+  }
+
+  session->ticket = ticket;
+  session->ticket_expiry_time = lifetime_hint + time(1);
+  return 0;
+}
+
 //! Do handshake processing. Type is one of HANDSHAKE_*, data is the
 //! contents of the packet, and raw is the raw packet received (needed
 //! for supporting SSLv2 hello messages).
@@ -424,6 +473,13 @@ int(-1..1) handle_handshake(int type, string(8bit) data, string(8bit) raw)
 	    application_protocol = selected_prot;
 	    break;
 
+	  case EXTENSION_session_ticket_tls:
+	    COND_FATAL(sizeof(extension_data), ALERT_handshake_failure,
+		       "Invalid server session ticket extension.\n");
+	    SSL3_DEBUG_MSG("SSL.ClientConnection: Server supports tickets.\n");
+	    tickets_enabled = 1;
+	    break;
+
 	  case EXTENSION_heartbeat:
 	    {
 	      int hb_mode;
@@ -495,20 +551,54 @@ int(-1..1) handle_handshake(int type, string(8bit) data, string(8bit) raw)
 	return -1;
       }
 
+      if (session->ticket && !tickets_enabled) {
+	// The server has stopped supporting session tickets?
+	// Make sure not to compare the server-generated
+	// session id with the one that we may have generated.
+	id = "";
+      }
+
+      // RFC 5746 3.5:
+      // When a ServerHello is received, the client MUST verify that the
+      // "renegotiation_info" extension is present; if it is not, the
+      // client MUST abort the handshake.
+      COND_FATAL(missing_secure_renegotiation, ALERT_handshake_failure,
+                 "Missing secure renegotiation extension.\n");
+
       if ((id == session->identity) && sizeof(id)) {
+        array(State) new_states;
+
 	SSL3_DEBUG_MSG("Resuming session %O.\n", id);
-	array(State) res =
-	  session->new_client_states(this, client_random, server_random,
-				     version);
-	pending_read_state = res[0];
-	pending_write_state = res[1];
 
-	handshake_state = STATE_wait_for_finish;
-	expect_change_cipher = 1;
+        new_states = session->new_client_states(this, client_random,
+                                                server_random, version);
+        [pending_read_state, pending_write_state] = new_states;
+
+        send_packet(change_cipher_packet());
+
+	if (tickets_enabled) {
+	  handshake_state = STATE_wait_for_ticket;
+	  // Expect the ticket before the CC.
+	  expect_change_cipher--;
+	} else {
+	  handshake_state = STATE_wait_for_finish;
+	}
 	reuse = 1;
 	break;
       }
 
+      if ((id == "") && tickets_enabled) {
+	// Generate a session identifier.
+	// NB: We currently do NOT support resumption based on an
+	//     empty session id and a HANDSHAKE_new_session_ticket.
+	//     We thus need a non-empty session id when we use
+	//     this new session for resumption. We don't care much
+	//     about the value (as long as it is non-empty), as
+	//     a compliant server will return either the value we
+	//     provided, or the empty string.
+	id = "RESUMPTION_TICKET";
+      }
+
       session->identity = id;
       handshake_state = STATE_wait_for_peer;
       break;
@@ -725,12 +815,32 @@ int(-1..1) handle_handshake(int type, string(8bit) data, string(8bit) raw)
 	send_packet(heartbleed_packet());
       }
 
-      handshake_state = STATE_wait_for_finish;
-      expect_change_cipher = 1;
+      if (tickets_enabled) {
+	handshake_state = STATE_wait_for_ticket;
+	// Expect the ticket before the CC.
+	expect_change_cipher--;
+      } else {
+	handshake_state = STATE_wait_for_finish;
+      }
       break;
     }
     break;
 
+  case STATE_wait_for_ticket:
+    {
+      COND_FATAL(type != HANDSHAKE_new_session_ticket, ALERT_unexpected_message,
+                 "Expected new session ticket\n");
+
+      SSL3_DEBUG_MSG("SSL.ClientConnection: NEW_SESSION_TICKET\n");
+      handshake_messages += raw;
+
+      // Expect CC.
+      expect_change_cipher++;
+      handshake_state = STATE_wait_for_finish;
+      return got_new_session_ticket(input);
+    }
+    break;
+
   case STATE_wait_for_finish:
     {
       if(type != HANDSHAKE_finished)
@@ -762,8 +872,6 @@ int(-1..1) handle_handshake(int type, string(8bit) data, string(8bit) raw)
       handshake_state = STATE_handshake_finished;
 
       if (reuse) {
-	send_packet(change_cipher_packet());
-
 	handshake_messages += raw; /* Second hash includes this message,
 				    * the first doesn't */
 
diff --git a/lib/modules/SSL.pmod/Connection.pike b/lib/modules/SSL.pmod/Connection.pike
index 4d1605fa4e9725cc9742852ca926a44fc5f4e4d7..5a4986ff55374f6f0b2e6f280d5375ea5e0d9b5b 100644
--- a/lib/modules/SSL.pmod/Connection.pike
+++ b/lib/modules/SSL.pmod/Connection.pike
@@ -141,6 +141,7 @@ Packet change_cipher_packet()
   Packet packet = Packet(version);
   packet->content_type = PACKET_change_cipher_spec;
   packet->fragment = "\001";
+  expect_change_cipher++;
   return packet;
 }
 
@@ -642,7 +643,7 @@ int handle_change_cipher(int c)
   else
   {
     current_read_state = pending_read_state;
-    expect_change_cipher = 0;
+    expect_change_cipher--;
     return 0;
   }
 }
diff --git a/lib/modules/SSL.pmod/Constants.pmod b/lib/modules/SSL.pmod/Constants.pmod
index 6d1481e718e789a4f500f7f1c26fd26c72760dc0..2e596ee8f6373d5114a438cc07e4ad0b03d837a3 100644
--- a/lib/modules/SSL.pmod/Constants.pmod
+++ b/lib/modules/SSL.pmod/Constants.pmod
@@ -39,8 +39,9 @@ constant PACKET_MAX_SIZE = 0x4000;	// 2^14.
 constant STATE_wait_for_hello		= 0;
 constant STATE_wait_for_peer		= 1;
 constant STATE_wait_for_verify		= 2;
-constant STATE_wait_for_finish		= 3;
-constant STATE_handshake_finished	= 4;
+constant STATE_wait_for_ticket		= 3;
+constant STATE_wait_for_finish		= 4;
+constant STATE_handshake_finished	= 5;
 
 //! Connection states.
 //!
diff --git a/lib/modules/SSL.pmod/Context.pike b/lib/modules/SSL.pmod/Context.pike
index cfbc12477cde95152be246468c2cbfe5f208ca7a..81579e56c6a95d2a3290ae2dd7a9006caf7232fe 100644
--- a/lib/modules/SSL.pmod/Context.pike
+++ b/lib/modules/SSL.pmod/Context.pike
@@ -116,6 +116,7 @@ Alert alert_factory(object con,
   return Alert(level, description, version, message);
 }
 
+
 //
 // --- Cryptography
 //
diff --git a/lib/modules/SSL.pmod/ServerConnection.pike b/lib/modules/SSL.pmod/ServerConnection.pike
index 004b668f5788a96b27927072e93497912afff340..860ef6d877c59dd397a5746445f5cf5c524e09de 100644
--- a/lib/modules/SSL.pmod/ServerConnection.pike
+++ b/lib/modules/SSL.pmod/ServerConnection.pike
@@ -825,7 +825,6 @@ int(-1..1) handle_handshake(int type, string(8bit) data, string(8bit) raw)
 	    send_packet(heartbleed_packet());
 	  }
 
-	  expect_change_cipher++;
 	  reuse = 1;
 
 	  handshake_state = STATE_wait_for_finish;