Bug 1257891 - TLS 1.3: Implement resumption-PSK. r=mt NSS_3_24_BETA1
authorEKR <ekr@rtfm.com>
Sun, 07 Feb 2016 16:21:26 -0800
changeset 12026 250e4280aaa157a248d9786d02b421d6f025eff6
parent 12025 06054c8176875b87963747d7fde01120d8400c0a
child 12028 688eca2f1f7e5aed684daee4c66be7704f2d3526
push id1092
push userekr@mozilla.com
push dateFri, 18 Mar 2016 18:20:09 +0000
reviewersmt
bugs1257891
Bug 1257891 - TLS 1.3: Implement resumption-PSK. r=mt
external_tests/ssl_gtest/libssl_internals.c
external_tests/ssl_gtest/libssl_internals.h
external_tests/ssl_gtest/ssl_extension_unittest.cc
external_tests/ssl_gtest/ssl_loopback_unittest.cc
external_tests/ssl_gtest/tls_agent.cc
external_tests/ssl_gtest/tls_agent.h
external_tests/ssl_gtest/tls_connect.cc
external_tests/ssl_gtest/tls_connect.h
external_tests/ssl_gtest/tls_filter.cc
external_tests/ssl_gtest/tls_filter.h
external_tests/ssl_gtest/tls_parser.h
lib/ssl/SSLerrs.h
lib/ssl/ssl3con.c
lib/ssl/ssl3ext.c
lib/ssl/ssl3prot.h
lib/ssl/sslenum.c
lib/ssl/sslerr.h
lib/ssl/sslimpl.h
lib/ssl/sslinfo.c
lib/ssl/sslproto.h
lib/ssl/sslt.h
lib/ssl/tls13con.c
lib/ssl/tls13con.h
lib/ssl/tls13hkdf.c
lib/util/secoid.c
lib/util/secoidt.h
--- a/external_tests/ssl_gtest/libssl_internals.c
+++ b/external_tests/ssl_gtest/libssl_internals.c
@@ -2,16 +2,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* This file contains functions for frobbing the internals of libssl */
 #include "libssl_internals.h"
 
+#include "nss.h"
 #include "seccomon.h"
 #include "ssl.h"
 #include "sslimpl.h"
 
 SECStatus
 SSLInt_IncrementClientHandshakeVersion(PRFileDesc *fd)
 {
     sslSocket *ss = ssl_FindSocket(fd);
@@ -79,8 +80,15 @@ SSLInt_UpdateSSLv2ClientRandom(PRFileDes
 }
 
 PRBool
 SSLInt_ExtensionNegotiated(PRFileDesc *fd, PRUint16 ext)
 {
     sslSocket *ss = ssl_FindSocket(fd);
     return (PRBool)(ss && ssl3_ExtensionNegotiated(ss, ext));
 }
+
+void
+SSLInt_ClearSessionTicketKey()
+{
+  ssl3_SessionTicketShutdown(NULL, NULL);
+  NSS_UnregisterShutdown(ssl3_SessionTicketShutdown, NULL);
+}
--- a/external_tests/ssl_gtest/libssl_internals.h
+++ b/external_tests/ssl_gtest/libssl_internals.h
@@ -18,12 +18,12 @@ SECStatus SSLInt_IncrementClientHandshak
 PRUint32 SSLInt_DetermineKEABits(PRUint16 serverKeyBits,
                                  SSLAuthType authAlgorithm);
 
 SECStatus SSLInt_UpdateSSLv2ClientRandom(PRFileDesc *fd,
                                          uint8_t *rnd, size_t rnd_len,
                                          uint8_t *msg, size_t msg_len);
 
 PRBool SSLInt_ExtensionNegotiated(PRFileDesc *fd, PRUint16 ext);
-
+void SSLInt_ClearSessionTicketKey();
 #endif
 
 
--- a/external_tests/ssl_gtest/ssl_extension_unittest.cc
+++ b/external_tests/ssl_gtest/ssl_extension_unittest.cc
@@ -10,148 +10,16 @@
 #include <memory>
 
 #include "tls_parser.h"
 #include "tls_filter.h"
 #include "tls_connect.h"
 
 namespace nss_test {
 
-class TlsExtensionFilter : public TlsHandshakeFilter {
- protected:
-  virtual PacketFilter::Action FilterHandshake(
-      const HandshakeHeader& header,
-      const DataBuffer& input, DataBuffer* output) {
-    if (header.handshake_type() == kTlsHandshakeClientHello) {
-      TlsParser parser(input);
-      if (!FindClientHelloExtensions(&parser, header)) {
-        return KEEP;
-      }
-      return FilterExtensions(&parser, input, output);
-    }
-    if (header.handshake_type() == kTlsHandshakeServerHello) {
-      TlsParser parser(input);
-      if (!FindServerHelloExtensions(&parser, header.version())) {
-        return KEEP;
-      }
-      return FilterExtensions(&parser, input, output);
-    }
-    return KEEP;
-  }
-
-  virtual PacketFilter::Action FilterExtension(uint16_t extension_type,
-                                               const DataBuffer& input,
-                                               DataBuffer* output) = 0;
-
- public:
-  static bool FindClientHelloExtensions(TlsParser* parser, const Versioned& header) {
-    if (!parser->Skip(2 + 32)) { // version + random
-      return false;
-    }
-    if (!parser->SkipVariable(1)) { // session ID
-      return false;
-    }
-    if (header.is_dtls() && !parser->SkipVariable(1)) { // DTLS cookie
-      return false;
-    }
-    if (!parser->SkipVariable(2)) { // cipher suites
-      return false;
-    }
-    if (!parser->SkipVariable(1)) { // compression methods
-      return false;
-    }
-    return true;
-  }
-
-  static bool FindServerHelloExtensions(TlsParser* parser, uint16_t version) {
-    if (!parser->Skip(2 + 32)) { // version + random
-      return false;
-    }
-    if (!parser->SkipVariable(1)) { // session ID
-      return false;
-    }
-    if (!parser->Skip(2)) { // cipher suite
-      return false;
-    }
-    if (NormalizeTlsVersion(version) <= SSL_LIBRARY_VERSION_TLS_1_2) {
-      if (!parser->Skip(1)) { // compression method
-        return false;
-      }
-    }
-    return true;
-  }
-
- private:
-  PacketFilter::Action FilterExtensions(TlsParser* parser,
-                                        const DataBuffer& input,
-                                        DataBuffer* output) {
-    size_t length_offset = parser->consumed();
-    uint32_t all_extensions;
-    if (!parser->Read(&all_extensions, 2)) {
-      return KEEP; // no extensions, odd but OK
-    }
-    if (all_extensions != parser->remaining()) {
-      return KEEP; // malformed
-    }
-
-    bool changed = false;
-
-    // Write out the start of the message.
-    output->Allocate(input.len());
-    size_t offset = output->Write(0, input.data(), parser->consumed());
-
-    while (parser->remaining()) {
-      uint32_t extension_type;
-      if (!parser->Read(&extension_type, 2)) {
-        return KEEP; // malformed
-      }
-
-      DataBuffer extension;
-      if (!parser->ReadVariable(&extension, 2)) {
-        return KEEP; // malformed
-      }
-
-      DataBuffer filtered;
-      PacketFilter::Action action = FilterExtension(extension_type, extension,
-                                                    &filtered);
-      if (action == DROP) {
-        changed = true;
-        std::cerr << "extension drop: " << extension << std::endl;
-        continue;
-      }
-
-      const DataBuffer* source = &extension;
-      if (action == CHANGE) {
-        EXPECT_GT(0x10000U, filtered.len());
-        changed = true;
-        std::cerr << "extension old: " << extension << std::endl;
-        std::cerr << "extension new: " << filtered << std::endl;
-        source = &filtered;
-      }
-
-      // Write out extension.
-      offset = output->Write(offset, extension_type, 2);
-      offset = output->Write(offset, source->len(), 2);
-      offset = output->Write(offset, *source);
-    }
-    output->Truncate(offset);
-
-    if (changed) {
-      size_t newlen = output->len() - length_offset - 2;
-      EXPECT_GT(0x10000U, newlen);
-      if (newlen >= 0x10000) {
-        return KEEP; // bad: size increased too much
-      }
-      output->Write(length_offset, newlen, 2);
-      return CHANGE;
-    }
-    return KEEP;
-  }
-};
-
 class TlsExtensionTruncator : public TlsExtensionFilter {
  public:
   TlsExtensionTruncator(uint16_t extension, size_t length)
       : extension_(extension), length_(length) {}
   virtual PacketFilter::Action FilterExtension(
       uint16_t extension_type, const DataBuffer& input, DataBuffer* output) {
     if (extension_type != extension_) {
       return KEEP;
@@ -249,36 +117,16 @@ class TlsExtensionInjector : public TlsH
     return CHANGE;
   }
 
  private:
   const uint16_t extension_;
   const DataBuffer data_;
 };
 
-class TlsExtensionCapture : public TlsExtensionFilter {
- public:
-  TlsExtensionCapture(uint16_t ext)
-      : extension_(ext), data_() {}
-
-  virtual PacketFilter::Action FilterExtension(
-      uint16_t extension_type, const DataBuffer& input, DataBuffer* output) {
-    if (extension_type == extension_) {
-      data_.Assign(input);
-    }
-    return KEEP;
-  }
-
-  const DataBuffer& extension() const { return data_; }
-
- private:
-  const uint16_t extension_;
-  DataBuffer data_;
-};
-
 class TlsExtensionTestBase : public TlsConnectTestBase {
  protected:
   TlsExtensionTestBase(Mode mode, uint16_t version)
     : TlsConnectTestBase(mode, version) {}
 
   void ClientHelloErrorTest(PacketFilter* filter,
                             uint8_t alert = kTlsAlertDecodeError) {
     auto alert_recorder = new TlsAlertRecorder();
--- a/external_tests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/external_tests/ssl_gtest/ssl_loopback_unittest.cc
@@ -156,104 +156,125 @@ TEST_P(TlsConnectGenericPre13, ConnectRe
   ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID);
   Connect();
 
   ResetRsa();
   ExpectResumption(RESUME_SESSIONID);
   Connect();
 }
 
-TEST_P(TlsConnectGenericPre13, ConnectClientCacheDisabled) {
+TEST_P(TlsConnectGeneric, ConnectClientCacheDisabled) {
   ConfigureSessionCache(RESUME_NONE, RESUME_SESSIONID);
   Connect();
+  SendReceive();
+
   ResetRsa();
   ExpectResumption(RESUME_NONE);
   Connect();
+  SendReceive();
 }
 
-TEST_P(TlsConnectGenericPre13, ConnectServerCacheDisabled) {
+TEST_P(TlsConnectGeneric, ConnectServerCacheDisabled) {
   ConfigureSessionCache(RESUME_SESSIONID, RESUME_NONE);
   Connect();
+  SendReceive();
+
   ResetRsa();
   ExpectResumption(RESUME_NONE);
   Connect();
+  SendReceive();
 }
 
-TEST_P(TlsConnectGenericPre13, ConnectSessionCacheDisabled) {
+TEST_P(TlsConnectGeneric, ConnectSessionCacheDisabled) {
   ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
   Connect();
+  SendReceive();
+
   ResetRsa();
   ExpectResumption(RESUME_NONE);
   Connect();
+  SendReceive();
 }
 
-TEST_P(TlsConnectGenericPre13, ConnectResumeSupportBoth) {
+TEST_P(TlsConnectGeneric, ConnectResumeSupportBoth) {
   // This prefers tickets.
   ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
   Connect();
+  SendReceive();
 
   ResetRsa();
   ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
   ExpectResumption(RESUME_TICKET);
   Connect();
+  SendReceive();
 }
 
-TEST_P(TlsConnectGenericPre13, ConnectResumeClientTicketServerBoth) {
+TEST_P(TlsConnectGeneric, ConnectResumeClientTicketServerBoth) {
   // This causes no resumption because the client needs the
   // session cache to resume even with tickets.
   ConfigureSessionCache(RESUME_TICKET, RESUME_BOTH);
   Connect();
+  SendReceive();
 
   ResetRsa();
   ConfigureSessionCache(RESUME_TICKET, RESUME_BOTH);
   ExpectResumption(RESUME_NONE);
   Connect();
+  SendReceive();
 }
 
-TEST_P(TlsConnectGenericPre13, ConnectResumeClientBothTicketServerTicket) {
+TEST_P(TlsConnectGeneric, ConnectResumeClientBothTicketServerTicket) {
   // This causes a ticket resumption.
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   Connect();
+  SendReceive();
 
   ResetRsa();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ExpectResumption(RESUME_TICKET);
   Connect();
+  SendReceive();
 }
 
 TEST_P(TlsConnectGenericPre13, ConnectResumeClientServerTicketOnly) {
   // This causes no resumption because the client needs the
   // session cache to resume even with tickets.
   ConfigureSessionCache(RESUME_TICKET, RESUME_TICKET);
   Connect();
+  SendReceive();
 
   ResetRsa();
   ConfigureSessionCache(RESUME_TICKET, RESUME_TICKET);
   ExpectResumption(RESUME_NONE);
   Connect();
+  SendReceive();
 }
 
 TEST_P(TlsConnectGenericPre13, ConnectResumeClientBothServerNone) {
   ConfigureSessionCache(RESUME_BOTH, RESUME_NONE);
   Connect();
+  SendReceive();
 
   ResetRsa();
   ConfigureSessionCache(RESUME_BOTH, RESUME_NONE);
   ExpectResumption(RESUME_NONE);
   Connect();
+  SendReceive();
 }
 
 TEST_P(TlsConnectGenericPre13, ConnectResumeClientNoneServerBoth) {
   ConfigureSessionCache(RESUME_NONE, RESUME_BOTH);
   Connect();
+  SendReceive();
 
   ResetRsa();
   ConfigureSessionCache(RESUME_NONE, RESUME_BOTH);
   ExpectResumption(RESUME_NONE);
   Connect();
+  SendReceive();
 }
 
 TEST_P(TlsConnectGenericPre13, ConnectResumeWithHigherVersion) {
   EnsureTlsSetup();
   SetExpectedVersion(SSL_LIBRARY_VERSION_TLS_1_1);
   ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID);
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_1);
@@ -267,16 +288,30 @@ TEST_P(TlsConnectGenericPre13, ConnectRe
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_2);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_2);
   ExpectResumption(RESUME_NONE);
   Connect();
 }
 
+TEST_P(TlsConnectGeneric, ConnectResumeClientBothTicketServerTicketForget) {
+  // This causes a ticket resumption.
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  Connect();
+  SendReceive();
+
+  ResetRsa();
+  ClearServerCache();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  ExpectResumption(RESUME_NONE);
+  Connect();
+  SendReceive();
+}
+
 TEST_P(TlsConnectGeneric, ClientAuth) {
   client_->SetupClientAuth();
   server_->RequestClientAuth(true);
   Connect();
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa);
 }
 
 // In TLS 1.3, the client sends its cert rejection on the
@@ -897,16 +932,64 @@ TEST_F(TlsConnectTest, TestFallbackFromT
   client_->SetDowngradeCheckVersion(SSL_LIBRARY_VERSION_TLS_1_3);
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                            SSL_LIBRARY_VERSION_TLS_1_2);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   ConnectExpectFail();
   ASSERT_EQ(SSL_ERROR_RX_MALFORMED_SERVER_HELLO, client_->error_code());
 }
+
+// Test that two TLS resumptions work and produce the same ticket.
+// This will change after bug 1257047 is fixed.
+TEST_F(TlsConnectTest, TestTls13ResumptionTwice) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  Connect();
+  SendReceive(); // Need to read so that we absorb the session ticket.
+  CheckKeys(ssl_kea_ecdh, ssl_auth_rsa);
+
+  ResetRsa();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  TlsExtensionCapture *c1 =
+      new TlsExtensionCapture(kTlsExtensionPreSharedKey);
+  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  ExpectResumption(RESUME_TICKET);
+  Connect();
+  SendReceive();
+  CheckKeys(ssl_kea_ecdh, ssl_auth_rsa);
+  DataBuffer psk1(c1->extension());
+  ASSERT_GE(psk1.len(), 0UL);
+
+  ResetRsa();
+  ClearStats();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  TlsExtensionCapture *c2 =
+      new TlsExtensionCapture(kTlsExtensionPreSharedKey);
+  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  ExpectResumption(RESUME_TICKET);
+  Connect();
+  SendReceive();
+  CheckKeys(ssl_kea_ecdh, ssl_auth_rsa);
+  DataBuffer psk2(c2->extension());
+  ASSERT_GE(psk2.len(), 0UL);
+
+  // TODO(ekr@rtfm.com): This will change when we fix bug 1257047.
+  ASSERT_EQ(psk1, psk2);
+}
+
 #endif
 
 class BeforeFinished : public TlsRecordFilter {
  private:
   enum HandshakeState {
     BEFORE_CCS,
     AFTER_CCS,
     DONE
--- a/external_tests/ssl_gtest/tls_agent.cc
+++ b/external_tests/ssl_gtest/tls_agent.cc
@@ -413,20 +413,21 @@ void TlsAgent::CheckPreliminaryInfo() {
 // Check that all the expected callbacks have been called.
 void TlsAgent::CheckCallbacks() const {
   // If false start happens, the handshake is reported as being complete at the
   // point that false start happens.
   if (expect_resumption_ || !falsestart_enabled_) {
     EXPECT_TRUE(handshake_callback_called_);
   }
 
-  // These callbacks shouldn't fire if we are resuming.
+  // These callbacks shouldn't fire if we are resuming, except on TLS 1.3.
   if (role_ == SERVER) {
     PRBool have_sni = SSLInt_ExtensionNegotiated(ssl_fd_, ssl_server_name_xtn);
-    EXPECT_EQ(!expect_resumption_ && have_sni, sni_hook_called_);
+    EXPECT_EQ(((!expect_resumption_ && have_sni) ||
+               expected_version_ >= SSL_LIBRARY_VERSION_TLS_1_3), sni_hook_called_);
   } else {
     EXPECT_EQ(!expect_resumption_, auth_certificate_hook_called_);
     // Note that this isn't unconditionally called, even with false start on.
     // But the callback is only skipped if a cipher that is ridiculously weak
     // (80 bits) is chosen.  Don't test that: plan to remove bad ciphers.
     EXPECT_EQ(falsestart_enabled_ && !expect_resumption_,
               can_falsestart_hook_called_);
   }
--- a/external_tests/ssl_gtest/tls_agent.h
+++ b/external_tests/ssl_gtest/tls_agent.h
@@ -233,17 +233,18 @@ class TlsAgent : public PollTarget {
   }
 
   static PRInt32 SniHook(PRFileDesc *fd, const SECItem *srvNameArr,
                          PRUint32 srvNameArrSize,
                          void *arg) {
     TlsAgent* agent = reinterpret_cast<TlsAgent*>(arg);
     agent->CheckPreliminaryInfo();
     agent->sni_hook_called_ = true;
-    return SSL_SNI_CURRENT_CONFIG_IS_USED;
+    EXPECT_EQ(1UL, srvNameArrSize);
+    return 0; // First configuration.
   }
 
   static SECStatus CanFalseStartCallback(PRFileDesc *fd, void *arg,
                                          PRBool *canFalseStart) {
     TlsAgent* agent = reinterpret_cast<TlsAgent*>(arg);
     agent->CheckPreliminaryInfo();
     EXPECT_TRUE(agent->falsestart_enabled_);
     EXPECT_FALSE(agent->can_falsestart_hook_called_);
--- a/external_tests/ssl_gtest/tls_connect.cc
+++ b/external_tests/ssl_gtest/tls_connect.cc
@@ -1,15 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "tls_connect.h"
+extern "C" {
+#include "libssl_internals.h"
+}
 
 #include <iostream>
 
 #include "sslproto.h"
 #include "gtest_utils.h"
 
 extern std::string g_working_dir_path;
 
@@ -115,32 +118,41 @@ TlsConnectTestBase::TlsConnectTestBase(M
     v = VersionString(version_);
   }
   std::cerr << "Version: " << mode_ << " " << v << std::endl;
 }
 
 TlsConnectTestBase::~TlsConnectTestBase() {
 }
 
-void TlsConnectTestBase::SetUp() {
-  // Configure a fresh session cache.
-  SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
-
+void TlsConnectTestBase::ClearStats() {
   // Clear statistics.
   SSL3Statistics* stats = SSL_GetStatistics();
   memset(stats, 0, sizeof(*stats));
+}
 
+void TlsConnectTestBase::ClearServerCache() {
+  SSL_ShutdownServerSessionIDCache();
+  SSLInt_ClearSessionTicketKey();
+  SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
+}
+
+void TlsConnectTestBase::SetUp() {
+  SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
+  SSLInt_ClearSessionTicketKey();
+  ClearStats();
   Init();
 }
 
 void TlsConnectTestBase::TearDown() {
   delete client_;
   delete server_;
 
   SSL_ClearSessionCache();
+  SSLInt_ClearSessionTicketKey();
   SSL_ShutdownServerSessionIDCache();
 }
 
 void TlsConnectTestBase::Init() {
   EXPECT_TRUE(client_->Init());
   EXPECT_TRUE(server_->Init());
 
   client_->SetPeer(server_);
@@ -293,18 +305,21 @@ void TlsConnectTestBase::CheckResumption
 
   SSL3Statistics* stats = SSL_GetStatistics();
   EXPECT_EQ(resume_ct, stats->hch_sid_cache_hits);
   EXPECT_EQ(resume_ct, stats->hsh_sid_cache_hits);
 
   EXPECT_EQ(stateless_ct, stats->hch_sid_stateless_resumes);
   EXPECT_EQ(stateless_ct, stats->hsh_sid_stateless_resumes);
 
-  if (resume_ct) {
+  if (resume_ct &&
+      client_->version() < SSL_LIBRARY_VERSION_TLS_1_3) {
     // Check that the last two session ids match.
+    // TLS 1.3 doesn't do session id-based resumption. It's all
+    // tickets.
     EXPECT_EQ(2U, session_ids_.size());
     EXPECT_EQ(session_ids_[session_ids_.size()-1],
               session_ids_[session_ids_.size()-2]);
   }
 }
 
 void TlsConnectTestBase::EnableAlpn() {
   // A simple value of "a", "b".  Note that the preferred value of "a" is placed
--- a/external_tests/ssl_gtest/tls_connect.h
+++ b/external_tests/ssl_gtest/tls_connect.h
@@ -41,16 +41,20 @@ class TlsConnectTestBase : public ::test
   TlsConnectTestBase(Mode mode, uint16_t version);
   virtual ~TlsConnectTestBase();
 
   void SetUp();
   void TearDown();
 
   // Initialize client and server.
   void Init();
+  // Clear the statistics.
+  void ClearStats();
+  // Clear the server session cache.
+  void ClearServerCache();
   // Re-initialize client and server with the default RSA cert.
   void ResetRsa();
   // Re-initialize client and server with an ECDSA cert on the server
   // and some ECDHE suites.
   void ResetEcdsa();
   // Make sure TLS is configured for a connection.
   void EnsureTlsSetup();
 
--- a/external_tests/ssl_gtest/tls_filter.cc
+++ b/external_tests/ssl_gtest/tls_filter.cc
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "tls_filter.h"
+#include "sslproto.h"
 
 #include <iostream>
 #include "gtest_utils.h"
 
 namespace nss_test {
 
 PacketFilter::Action TlsRecordFilter::Filter(const DataBuffer& input, DataBuffer* output) {
   bool changed = false;
@@ -266,9 +267,141 @@ PacketFilter::Action ChainedPacketFilter
     if (action == CHANGE) {
       in = *output;
       changed = true;
     }
   }
   return changed ? CHANGE : KEEP;
 }
 
+PacketFilter::Action TlsExtensionFilter::FilterHandshake(
+    const HandshakeHeader& header,
+    const DataBuffer& input, DataBuffer* output) {
+  if (header.handshake_type() == kTlsHandshakeClientHello) {
+    TlsParser parser(input);
+    if (!FindClientHelloExtensions(&parser, header)) {
+      return KEEP;
+    }
+    return FilterExtensions(&parser, input, output);
+  }
+  if (header.handshake_type() == kTlsHandshakeServerHello) {
+    TlsParser parser(input);
+    if (!FindServerHelloExtensions(&parser, header.version())) {
+      return KEEP;
+    }
+    return FilterExtensions(&parser, input, output);
+  }
+  return KEEP;
+}
+
+bool TlsExtensionFilter::FindClientHelloExtensions(TlsParser* parser,
+                                                   const Versioned& header) {
+  if (!parser->Skip(2 + 32)) { // version + random
+    return false;
+  }
+  if (!parser->SkipVariable(1)) { // session ID
+    return false;
+  }
+  if (header.is_dtls() && !parser->SkipVariable(1)) { // DTLS cookie
+    return false;
+  }
+  if (!parser->SkipVariable(2)) { // cipher suites
+    return false;
+  }
+  if (!parser->SkipVariable(1)) { // compression methods
+    return false;
+  }
+  return true;
+}
+
+bool TlsExtensionFilter::FindServerHelloExtensions(TlsParser* parser,
+                                                   uint16_t version) {
+  if (!parser->Skip(2 + 32)) { // version + random
+    return false;
+  }
+  if (!parser->SkipVariable(1)) { // session ID
+    return false;
+  }
+  if (!parser->Skip(2)) { // cipher suite
+    return false;
+  }
+  if (NormalizeTlsVersion(version) <= SSL_LIBRARY_VERSION_TLS_1_2) {
+    if (!parser->Skip(1)) { // compression method
+      return false;
+    }
+  }
+  return true;
+}
+
+PacketFilter::Action TlsExtensionFilter::FilterExtensions(
+    TlsParser* parser, const DataBuffer& input, DataBuffer* output) {
+  size_t length_offset = parser->consumed();
+  uint32_t all_extensions;
+  if (!parser->Read(&all_extensions, 2)) {
+    return KEEP; // no extensions, odd but OK
+  }
+  if (all_extensions != parser->remaining()) {
+    return KEEP; // malformed
+  }
+
+  bool changed = false;
+
+  // Write out the start of the message.
+  output->Allocate(input.len());
+  size_t offset = output->Write(0, input.data(), parser->consumed());
+
+  while (parser->remaining()) {
+    uint32_t extension_type;
+    if (!parser->Read(&extension_type, 2)) {
+      return KEEP; // malformed
+    }
+
+    DataBuffer extension;
+    if (!parser->ReadVariable(&extension, 2)) {
+      return KEEP; // malformed
+    }
+
+    DataBuffer filtered;
+    PacketFilter::Action action = FilterExtension(extension_type, extension,
+                                                  &filtered);
+    if (action == DROP) {
+      changed = true;
+      std::cerr << "extension drop: " << extension << std::endl;
+      continue;
+    }
+
+    const DataBuffer* source = &extension;
+    if (action == CHANGE) {
+      EXPECT_GT(0x10000U, filtered.len());
+      changed = true;
+      std::cerr << "extension old: " << extension << std::endl;
+      std::cerr << "extension new: " << filtered << std::endl;
+      source = &filtered;
+    }
+
+    // Write out extension.
+    offset = output->Write(offset, extension_type, 2);
+    offset = output->Write(offset, source->len(), 2);
+    offset = output->Write(offset, *source);
+  }
+  output->Truncate(offset);
+
+  if (changed) {
+    size_t newlen = output->len() - length_offset - 2;
+    EXPECT_GT(0x10000U, newlen);
+    if (newlen >= 0x10000) {
+      return KEEP; // bad: size increased too much
+    }
+    output->Write(length_offset, newlen, 2);
+    return CHANGE;
+  }
+  return KEEP;
+}
+
+PacketFilter::Action TlsExtensionCapture::FilterExtension(
+    uint16_t extension_type, const DataBuffer& input, DataBuffer* output) {
+  if (extension_type == extension_) {
+    data_.Assign(input);
+  }
+  return KEEP;
+}
+
 }  // namespace nss_test
--- a/external_tests/ssl_gtest/tls_filter.h
+++ b/external_tests/ssl_gtest/tls_filter.h
@@ -176,11 +176,46 @@ class ChainedPacketFilter : public Packe
   void Add(PacketFilter* filter) {
     filters_.push_back(filter);
   }
 
  private:
   std::vector<PacketFilter*> filters_;
 };
 
+class TlsExtensionFilter : public TlsHandshakeFilter {
+ protected:
+  virtual PacketFilter::Action FilterHandshake(
+      const HandshakeHeader& header,
+      const DataBuffer& input, DataBuffer* output);
+
+  virtual PacketFilter::Action FilterExtension(uint16_t extension_type,
+                                               const DataBuffer& input,
+                                               DataBuffer* output) = 0;
+
+ public:
+  static bool FindClientHelloExtensions(TlsParser* parser,
+                                        const Versioned& header);
+  static bool FindServerHelloExtensions(TlsParser* parser, uint16_t version);
+
+ private:
+  PacketFilter::Action FilterExtensions(TlsParser* parser,
+                                        const DataBuffer& input,
+                                        DataBuffer* output);
+};
+
+class TlsExtensionCapture : public TlsExtensionFilter {
+ public:
+  TlsExtensionCapture(uint16_t ext)
+      : extension_(ext), data_() {}
+
+  virtual PacketFilter::Action FilterExtension(
+      uint16_t extension_type, const DataBuffer& input, DataBuffer* output);
+  const DataBuffer& extension() const { return data_; }
+
+ private:
+  const uint16_t extension_;
+  DataBuffer data_;
+};
+
 }  // namespace nss_test
 
 #endif
--- a/external_tests/ssl_gtest/tls_parser.h
+++ b/external_tests/ssl_gtest/tls_parser.h
@@ -37,16 +37,18 @@ const uint8_t kTlsAlertFatal = 2;
 const uint8_t kTlsAlertUnexpectedMessage = 10;
 const uint8_t kTlsAlertBadRecordMac = 20;
 const uint8_t kTlsAlertHandshakeFailure = 40;
 const uint8_t kTlsAlertIllegalParameter = 47;
 const uint8_t kTlsAlertDecodeError = 50;
 const uint8_t kTlsAlertUnsupportedExtension = 110;
 const uint8_t kTlsAlertNoApplicationProtocol = 120;
 
+const uint8_t kTlsExtensionPreSharedKey = 41;
+
 const uint8_t kTlsFakeChangeCipherSpec[] = {
     kTlsChangeCipherSpecType,        // Type
     0xfe,                     0xff,  // Version
     0x00,                     0x00, 0x00, 0x00,
     0x00,                     0x00, 0x00, 0x10,  // Fictitious sequence #
     0x00,                     0x01,              // Length
     0x01                                         // Value
 };
--- a/lib/ssl/SSLerrs.h
+++ b/lib/ssl/SSLerrs.h
@@ -461,8 +461,12 @@ ER3(SSL_ERROR_MISSING_EXTENSION_ALERT, (
 ER3(SSL_ERROR_KEY_EXCHANGE_FAILURE, (SSL_ERROR_BASE + 144),
     "SSL had an error performing key exchange.")
 
 ER3(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION, (SSL_ERROR_BASE + 145),
     "SSL received an extension that is not permitted for this version.")
 
 ER3(SSL_ERROR_RX_MALFORMED_ENCRYPTED_EXTENSIONS, (SSL_ERROR_BASE + 146),
     "SSL received a malformed Encrypted Extensions handshake message.")
+
+ER3(SSL_ERROR_RX_MALFORMED_PRE_SHARED_KEY, (SSL_ERROR_BASE + 147),
+    "SSL received an invalid PreSharedKey extension.")
+
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -53,16 +53,24 @@ static SECStatus ssl3_DeriveMasterSecret
 static SECStatus ssl3_DeriveConnectionKeysPKCS11(sslSocket *ss);
 static SECStatus ssl3_HandshakeFailure(sslSocket *ss);
 
 static SECStatus ssl3_SendCertificateRequest(sslSocket *ss);
 static SECStatus ssl3_SendNextProto(sslSocket *ss);
 static SECStatus ssl3_SendFinished(sslSocket *ss, PRInt32 flags);
 static SECStatus ssl3_SendServerHelloDone(sslSocket *ss);
 static SECStatus ssl3_SendServerKeyExchange(sslSocket *ss);
+static SECStatus ssl3_HandleClientHelloPart2(sslSocket *ss,
+                                             SECItem *suites,
+                                             SECItem *comps,
+                                             sslSessionID *sid,
+                                             PRBool canOfferSessionTicket);
+static SECStatus ssl3_HandleServerHelloPart2(sslSocket *ss,
+                                             const SECItem *sidBytes,
+                                             int *retErrCode);
 static SECStatus ssl3_HandlePostHelloHandshakeMessage(sslSocket *ss,
                                                       SSL3Opaque *b,
                                                       PRUint32 length,
                                                       SSL3Hashes *hashesPtr);
 static SECStatus ssl3_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags);
 
 static SECStatus Null_Cipher(void *ctx, unsigned char *output, int *outputLen,
                              int maxOutputLen, const unsigned char *input,
@@ -85,16 +93,21 @@ static SECStatus ssl3_AESGCMBypass(ssl3K
  *
  * Important: See bug 946147 before enabling, reordering, or adding any cipher
  * suites to this list.
  */
 /* clang-format off */
 static ssl3CipherSuiteCfg cipherSuites[ssl_V3_SUITES_IMPLEMENTED] = {
    /*      cipher_suite                     policy       enabled   isPresent */
 
+ /* ECDHE-PSK from [draft-mattsson-tls-ecdhe-psk-aead]. Only enabled if
+  * we are doing TLS 1.3 PSK-resumption.
+  */
+ { TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256,   SSL_ALLOWED, PR_TRUE,  PR_FALSE},
+
 #ifndef NSS_DISABLE_ECC
  { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
  { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,   SSL_ALLOWED, PR_TRUE, PR_FALSE},
  { TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
  { TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,   SSL_ALLOWED, PR_TRUE, PR_FALSE},
    /* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA is out of order to work around
     * bug 946147.
     */
@@ -313,16 +326,17 @@ static const ssl3KEADef kea_defs[] =
     {kea_dh_anon_export, kt_dh,   ssl_sign_null,  PR_TRUE,  512, PR_FALSE, PR_TRUE,  SEC_OID_TLS_DH_ANON_EXPORT},
     {kea_rsa_fips,       kt_rsa,  ssl_sign_rsa,   PR_FALSE,   0, PR_TRUE,  PR_FALSE, SEC_OID_TLS_RSA},
 #ifndef NSS_DISABLE_ECC
     {kea_ecdh_ecdsa,     kt_ecdh, ssl_sign_ecdsa, PR_FALSE,   0, PR_FALSE, PR_FALSE, SEC_OID_TLS_ECDH_ECDSA},
     {kea_ecdhe_ecdsa,    kt_ecdh, ssl_sign_ecdsa, PR_FALSE,   0, PR_FALSE, PR_TRUE,  SEC_OID_TLS_ECDHE_ECDSA},
     {kea_ecdh_rsa,       kt_ecdh, ssl_sign_rsa,   PR_FALSE,   0, PR_FALSE, PR_FALSE, SEC_OID_TLS_ECDH_RSA},
     {kea_ecdhe_rsa,      kt_ecdh, ssl_sign_rsa,   PR_FALSE,   0, PR_FALSE, PR_TRUE,  SEC_OID_TLS_ECDHE_RSA},
     {kea_ecdh_anon,      kt_ecdh, ssl_sign_null,  PR_FALSE,   0, PR_FALSE, PR_TRUE,  SEC_OID_TLS_ECDH_ANON},
+    {kea_ecdhe_psk,      kt_ecdh, ssl_sign_psk,   PR_FALSE,   0, PR_FALSE, PR_TRUE,  SEC_OID_TLS_ECDHE_PSK}
 #endif /* NSS_DISABLE_ECC */
 };
 
 /* must use ssl_LookupCipherSuiteDef to access */
 static const ssl3CipherSuiteDef cipher_suite_defs[] =
 {
 /*  cipher_suite                    bulk_cipher_alg mac_alg key_exchange_alg */
 
@@ -455,16 +469,17 @@ static const ssl3CipherSuiteDef cipher_s
 
 #if 0
     {TLS_ECDH_anon_WITH_NULL_SHA,         cipher_null,    mac_sha, kea_ecdh_anon},
     {TLS_ECDH_anon_WITH_RC4_128_SHA,      cipher_rc4,     mac_sha, kea_ecdh_anon},
     {TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, cipher_3des,    mac_sha, kea_ecdh_anon},
     {TLS_ECDH_anon_WITH_AES_128_CBC_SHA,  cipher_aes_128, mac_sha, kea_ecdh_anon},
     {TLS_ECDH_anon_WITH_AES_256_CBC_SHA,  cipher_aes_256, mac_sha, kea_ecdh_anon},
 #endif
+    {TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_ecdhe_psk},
 #endif /* NSS_DISABLE_ECC */
 };
 /* clang-format on */
 
 static const CK_MECHANISM_TYPE kea_alg_defs[] = {
     0x80000000L,
     CKM_RSA_PKCS,
     CKM_DH_PKCS_DERIVE,
@@ -589,16 +604,19 @@ ssl3_DecodeHandshakeType(int msgType)
             rv = "client_hello  (1)";
             break;
         case server_hello:
             rv = "server_hello  (2)";
             break;
         case hello_verify_request:
             rv = "hello_verify_request (3)";
             break;
+        case new_session_ticket:
+            rv = "session_ticket (4)";
+            break;
         case encrypted_extensions:
             rv = "encrypted_extensions (8)";
             break;
         case certificate:
             rv = "certificate  (11)";
             break;
         case server_key_exchange:
             rv = "server_key_exchange (12)";
@@ -741,24 +759,27 @@ ssl3_CipherSuiteAllowedForVersionRange(
         case TLS_ECDHE_RSA_WITH_NULL_SHA:
         case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
         case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
         case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
         case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
             return vrange->max >= SSL_LIBRARY_VERSION_TLS_1_0 &&
                    vrange->min < SSL_LIBRARY_VERSION_TLS_1_3;
 
+        case TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256:
+            return vrange->max >= SSL_LIBRARY_VERSION_TLS_1_3;
+
         default:
             return vrange->min < SSL_LIBRARY_VERSION_TLS_1_3;
     }
 }
 
 /* return pointer to ssl3CipherSuiteDef for suite, or NULL */
 /* XXX This does a linear search.  A binary search would be better. */
-static const ssl3CipherSuiteDef *
+const ssl3CipherSuiteDef *
 ssl_LookupCipherSuiteDef(ssl3CipherSuite suite)
 {
     int cipher_suite_def_len =
         sizeof(cipher_suite_defs) / sizeof(cipher_suite_defs[0]);
     int i;
 
     for (i = 0; i < cipher_suite_def_len; i++) {
         if (cipher_suite_defs[i].cipher_suite == suite)
@@ -902,20 +923,30 @@ config_match(ssl3CipherSuiteCfg *suite, 
     cipher_def = ssl_LookupCipherSuiteDef(suite->cipher_suite);
     PORT_Assert(cipher_def != NULL);
 
     PORT_Assert(ss != NULL);
     if (ss->sec.isServer && !ss->opt.enableServerDhe &&
         kea_defs[cipher_def->key_exchange_alg].exchKeyType == ssl_kea_dh)
         return PR_FALSE;
 
-    return (PRBool)(suite->enabled &&
-                    suite->isPresent &&
-                    suite->policy != SSL_NOT_ALLOWED &&
-                    suite->policy <= policy &&
+    if (!suite->enabled)
+        return PR_FALSE;
+
+    if ((suite->policy == SSL_NOT_ALLOWED) ||
+        (suite->policy > policy))
+        return PR_FALSE;
+
+    /* We only allow PSK for TLS 1.3 and only if there is resumption. */
+    if (kea_defs[cipher_def->key_exchange_alg].signKeyType ==
+        ssl_sign_psk) {
+        return tls13_AllowPskCipher(ss, cipher_def);
+    }
+
+    return (PRBool)(suite->isPresent &&
                     ssl3_CipherSuiteAllowedForVersionRange(
                         suite->cipher_suite, vrange));
 }
 
 /* return number of cipher suites that match policy, enabled state and are
  * applicable for the configured protocol version range. */
 /* called from ssl3_SendClientHello */
 static int
@@ -5538,19 +5569,20 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
     /* count compression methods */
     numCompressionMethods = 0;
     for (i = 0; i < compressionMethodsCount; i++) {
         if (compressionEnabled(ss, compressions[i]))
             numCompressionMethods++;
     }
 
     length = sizeof(SSL3ProtocolVersion) + SSL3_RANDOM_LENGTH +
-             1 + ((sid == NULL) ? 0 : sid->u.ssl3.sessionIDLength) +
-             2 + num_suites * sizeof(ssl3CipherSuite) +
-             1 + numCompressionMethods + total_exten_len;
+            1 + (((sid == NULL) || sid->version >= SSL_LIBRARY_VERSION_TLS_1_3)
+            ? 0 : sid->u.ssl3.sessionIDLength) +
+            2 + num_suites * sizeof(ssl3CipherSuite) +
+            1 + numCompressionMethods + total_exten_len;
     if (IS_DTLS(ss)) {
         length += 1 + ss->ssl3.hs.cookieLen;
     }
 
     /* A padding extension may be included to ensure that the record containing
      * the ClientHello doesn't have a length between 256 and 511 bytes
      * (inclusive). Initial, ClientHello records with such lengths trigger bugs
      * in F5 devices.
@@ -5606,17 +5638,17 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
                               SSL3_RANDOM_LENGTH);
     if (rv != SECSuccess) {
         if (sid->u.ssl3.lock) {
             PR_RWLock_Unlock(sid->u.ssl3.lock);
         }
         return rv; /* err set by ssl3_AppendHandshake* */
     }
 
-    if (sid)
+    if (sid && sid->version < SSL_LIBRARY_VERSION_TLS_1_3)
         rv = ssl3_AppendHandshakeVariable(
             ss, sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength, 1);
     else
         rv = ssl3_AppendHandshakeNumber(ss, 0, 1);
     if (rv != SECSuccess) {
         if (sid->u.ssl3.lock) {
             PR_RWLock_Unlock(sid->u.ssl3.lock);
         }
@@ -6016,18 +6048,18 @@ ssl_InitSymWrapKeysLock(void)
     return symWrapKeysLock ? SECSuccess : SECFailure;
 }
 
 /* Try to get wrapping key for mechanism from in-memory array.
  * If that fails, look for one on disk.
  * If that fails, generate a new one, put the new one on disk,
  * Put the new key in the in-memory array.
  */
-static PK11SymKey *
-getWrappingKey(sslSocket *ss,
+PK11SymKey *
+ssl3_GetWrappingKey(sslSocket *ss,
                PK11SlotInfo *masterSecretSlot,
                SSL3KEAType exchKeyType,
                CK_MECHANISM_TYPE masterWrapMech,
                void *pwArg)
 {
     SECKEYPrivateKey *svrPrivKey;
     SECKEYPublicKey *svrPubKey = NULL;
     PK11SymKey *unwrappedWrappingKey = NULL;
@@ -6675,24 +6707,22 @@ done:
 
 /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
  * ssl3 ServerHello message.
  * Caller must hold Handshake and RecvBuf locks.
  */
 static SECStatus
 ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
-    sslSessionID *sid = ss->sec.ci.sid;
     PRInt32 temp; /* allow for consume number failure */
     PRBool suite_found = PR_FALSE;
     int i;
     int errCode = SSL_ERROR_RX_MALFORMED_SERVER_HELLO;
     SECStatus rv;
     SECItem sidBytes = { siBuffer, NULL, 0 };
-    PRBool sid_match;
     PRBool isTLS = PR_FALSE;
     SSL3AlertDescription desc = illegal_parameter;
     SSL3ProtocolVersion version;
     SSL3ProtocolVersion downgradeCheckVersion;
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle server_hello handshake",
                 SSL_GETPID(), ss->fd));
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
@@ -6890,16 +6920,52 @@ ssl3_HandleServerHello(sslSocket *ss, SS
                 goto alert_loser;
         } else {
             rv = ssl3_HandleHelloExtensions(ss, &extensions.data,
                                             &extensions.len, server_hello);
             if (rv != SECSuccess)
                 goto alert_loser;
         }
     }
+
+    if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+        rv = tls13_HandleServerHelloPart2(ss);
+        if (rv != SECSuccess) {
+            errCode = PORT_GetError();
+            goto loser;
+        }
+    } else {
+        rv = ssl3_HandleServerHelloPart2(ss, &sidBytes, &errCode);
+        if (rv != SECSuccess)
+            goto loser;
+    }
+
+    return SECSuccess;
+
+alert_loser:
+    (void)SSL3_SendAlert(ss, alert_fatal, desc);
+
+loser:
+    /* Clean up the temporary pointer to the handshake buffer. */
+    ss->xtnData.signedCertTimestamps.data = NULL;
+    ss->xtnData.signedCertTimestamps.len = 0;
+    ssl_MapLowLevelError(errCode);
+    return SECFailure;
+}
+
+static SECStatus
+ssl3_HandleServerHelloPart2(sslSocket *ss, const SECItem *sidBytes,
+                            int *retErrCode)
+{
+    SSL3AlertDescription desc = handshake_failure;
+    int errCode = SSL_ERROR_RX_MALFORMED_SERVER_HELLO;
+    SECStatus rv;
+    PRBool sid_match;
+    sslSessionID *sid = ss->sec.ci.sid;
+
     if ((ss->opt.requireSafeNegotiation ||
          (ss->firstHsDone && (ss->peerRequestedProtection ||
                               ss->opt.enableRenegotiation ==
                                   SSL_RENEGOTIATE_REQUIRES_XTN))) &&
         !ssl3_ExtensionNegotiated(ss, ssl_renegotiation_info_xtn)) {
         desc = handshake_failure;
         errCode = ss->firstHsDone ? SSL_ERROR_RENEGOTIATION_NOT_ALLOWED
                                   : SSL_ERROR_UNSAFE_NEGOTIATION;
@@ -6916,20 +6982,21 @@ ssl3_HandleServerHello(sslSocket *ss, SS
         goto alert_loser; /* error code is set. */
     }
 
     /* We may or may not have sent a session id, we may get one back or
      * not and if so it may match the one we sent.
      * Attempt to restore the master secret to see if this is so...
      * Don't consider failure to find a matching SID an error.
      */
-    sid_match = (PRBool)(sidBytes.len > 0 &&
-                         sidBytes.len ==
+    sid_match = (PRBool)(sidBytes->len > 0 &&
+                         sidBytes->len ==
                              sid->u.ssl3.sessionIDLength &&
-                         !PORT_Memcmp(sid->u.ssl3.sessionID, sidBytes.data, sidBytes.len));
+                         !PORT_Memcmp(sid->u.ssl3.sessionID,
+                                      sidBytes->data, sidBytes->len));
 
     if (sid_match &&
         sid->version == ss->version &&
         sid->u.ssl3.cipherSuite == ss->ssl3.hs.cipher_suite)
         do {
             ssl3CipherSpec *pwSpec = ss->ssl3.pwSpec;
 
             SECItem wrappedMS; /* wrapped master secret. */
@@ -7083,60 +7150,52 @@ ssl3_HandleServerHello(sslSocket *ss, SS
 
     /* get a new sid */
     ss->sec.ci.sid = sid = ssl3_NewSessionID(ss, PR_FALSE);
     if (sid == NULL) {
         goto alert_loser; /* memory error is set. */
     }
 
     sid->version = ss->version;
-    sid->u.ssl3.sessionIDLength = sidBytes.len;
-    PORT_Memcpy(sid->u.ssl3.sessionID, sidBytes.data, sidBytes.len);
+    sid->u.ssl3.sessionIDLength = sidBytes->len;
+    PORT_Memcpy(sid->u.ssl3.sessionID, sidBytes->data, sidBytes->len);
 
     sid->u.ssl3.keys.extendedMasterSecretUsed =
         ssl3_ExtensionNegotiated(ss, ssl_extended_master_secret_xtn);
 
     /* Copy Signed Certificate Timestamps, if any. */
     if (ss->xtnData.signedCertTimestamps.data) {
         rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.signedCertTimestamps,
                               &ss->xtnData.signedCertTimestamps);
         if (rv != SECSuccess)
             goto loser;
         /* Clean up the temporary pointer to the handshake buffer. */
         ss->xtnData.signedCertTimestamps.data = NULL;
         ss->xtnData.signedCertTimestamps.len = 0;
     }
 
     ss->ssl3.hs.isResuming = PR_FALSE;
-    if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
-        rv = tls13_HandleServerKeyShare(ss);
-        if (rv != SECSuccess)
-            goto alert_loser;
-        TLS13_SET_HS_STATE(ss, wait_encrypted_extensions);
-    } else if (ss->ssl3.hs.kea_def->signKeyType != ssl_sign_null) {
+    if (ss->ssl3.hs.kea_def->signKeyType != ssl_sign_null) {
         /* All current cipher suites other than those with ssl_sign_null (i.e.,
          * (EC)DH_anon_* suites) require a certificate, so use that signal. */
         ss->ssl3.hs.ws = wait_server_cert;
     } else {
         /* All the remaining cipher suites must be (EC)DH_anon_* and so
          * must be ephemeral. Note, if we ever add PSK this might
          * change. */
         PORT_Assert(ss->ssl3.hs.kea_def->ephemeral);
         ss->ssl3.hs.ws = wait_server_key;
     }
     return SECSuccess;
 
 alert_loser:
     (void)SSL3_SendAlert(ss, alert_fatal, desc);
 
 loser:
-    /* Clean up the temporary pointer to the handshake buffer. */
-    ss->xtnData.signedCertTimestamps.data = NULL;
-    ss->xtnData.signedCertTimestamps.len = 0;
-    ssl_MapLowLevelError(errCode);
+    *retErrCode = errCode;
     return SECFailure;
 }
 
 /* Called from ssl3_HandlePostHelloHandshakeMessage() when it has deciphered a
  * complete ssl3 ServerKeyExchange message.
  * Caller must hold Handshake and RecvBuf locks.
  */
 static SECStatus
@@ -8217,40 +8276,248 @@ ssl3_KEAAllowsSessionTicket(SSL3KeyExcha
             /* TODO: Fix session tickets for DSS. The server code rejects the
              * session ticket received from the client. Bug 1174677 */
             return PR_FALSE;
         default:
             return PR_TRUE;
     };
 }
 
+/* Select a cipher suite.
+**
+** NOTE: This suite selection algorithm should be the same as the one in
+** ssl3_HandleV2ClientHello().
+**
+** If TLS 1.0 is enabled, we could handle the case where the client
+** offered TLS 1.1 but offered only export cipher suites by choosing TLS
+** 1.0 and selecting one of those export cipher suites. However, a secure
+** TLS 1.1 client should not have export cipher suites enabled at all,
+** and a TLS 1.1 client should definitely not be offering *only* export
+** cipher suites. Therefore, we refuse to negotiate export cipher suites
+** with any client that indicates support for TLS 1.1 or higher when we
+** (the server) have TLS 1.1 support enabled.
+*/
+SECStatus
+ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites)
+{
+    ssl3CipherSuiteCfg *chosenSuite = NULL;
+    int j;
+    int i;
+
+    for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
+        ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
+        SSLVersionRange vrange = {ss->version, ss->version};
+        if (!config_match(suite, ss->ssl3.policy, PR_TRUE, &vrange, ss)) {
+            continue;
+        }
+        for (i = 0; i + 1 < suites->len; i += 2) {
+            PRUint16 suite_i = (suites->data[i] << 8) | suites->data[i + 1];
+            if (suite_i == suite->cipher_suite) {
+                chosenSuite = suite;
+                ss->ssl3.hs.cipher_suite = chosenSuite->cipher_suite;
+                ss->ssl3.hs.suite_def =
+                        ssl_LookupCipherSuiteDef(ss->ssl3.hs.cipher_suite);
+                ss->ssl3.hs.kea_def =
+                        &kea_defs[ss->ssl3.hs.suite_def->key_exchange_alg];
+                ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_cipher_suite;
+                return SECSuccess;
+            }
+        }
+    }
+    return SECFailure;
+}
+
+/*
+ * Call the SNI config hook.
+ *
+ * Called from:
+ *   ssl3_HandleClientHello
+ *   tls13_HandleClientHelloPart2
+ */
+SECStatus
+ssl3_ServerCallSNICallback(sslSocket *ss)
+{
+    int errCode = SSL_ERROR_RX_MALFORMED_CLIENT_HELLO;
+    SSL3AlertDescription desc  = illegal_parameter;
+    int ret = 0;
+
+    if (!ssl3_ExtensionNegotiated(ss, ssl_server_name_xtn)) {
+#ifndef SSL_SNI_ALLOW_NAME_CHANGE_2HS
+        if (ss->firstHsDone) {
+            /* Check that we don't have the name is current spec
+             * if this extension was not negotiated on the 2d hs. */
+            PRBool passed = PR_TRUE;
+            ssl_GetSpecReadLock(ss);  /*******************************/
+            if (ss->ssl3.cwSpec->srvVirtName.data) {
+                passed = PR_FALSE;
+            }
+            ssl_ReleaseSpecReadLock(ss);  /***************************/
+            if (!passed) {
+                errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
+                desc = handshake_failure;
+                goto alert_loser;
+            }
+        }
+#endif
+        return SECSuccess;
+    }
+
+    if (ss->sniSocketConfig) do { /* not a loop */
+            PORT_Assert((ss->ssl3.hs.preliminaryInfo & ssl_preinfo_all) ==
+                        ssl_preinfo_all);
+
+            ret = SSL_SNI_SEND_ALERT;
+            /* If extension is negotiated, the len of names should > 0. */
+            if (ss->xtnData.sniNameArrSize) {
+                /* Calling client callback to reconfigure the socket. */
+                ret = (SECStatus)(*ss->sniSocketConfig)(ss->fd,
+                                                        ss->xtnData.sniNameArr,
+                                                        ss->xtnData.sniNameArrSize,
+                                                        ss->sniSocketConfigArg);
+            }
+            if (ret <= SSL_SNI_SEND_ALERT) {
+                /* Application does not know the name or was not able to
+                 * properly reconfigure the socket. */
+                errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
+                desc = unrecognized_name;
+                break;
+            } else if (ret == SSL_SNI_CURRENT_CONFIG_IS_USED) {
+                SECStatus       rv = SECSuccess;
+                SECItem *       cwsName, *pwsName;
+
+                ssl_GetSpecWriteLock(ss);  /*******************************/
+                pwsName = &ss->ssl3.pwSpec->srvVirtName;
+                cwsName = &ss->ssl3.cwSpec->srvVirtName;
+#ifndef SSL_SNI_ALLOW_NAME_CHANGE_2HS
+                /* not allow name change on the 2d HS */
+                if (ss->firstHsDone) {
+                    if (ssl3_ServerNameCompare(pwsName, cwsName)) {
+                        ssl_ReleaseSpecWriteLock(ss);  /******************/
+                        errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
+                        desc = handshake_failure;
+                        ret = SSL_SNI_SEND_ALERT;
+                        break;
+                    }
+                }
+#endif
+                if (pwsName->data) {
+                    SECITEM_FreeItem(pwsName, PR_FALSE);
+                }
+                if (cwsName->data) {
+                    rv = SECITEM_CopyItem(NULL, pwsName, cwsName);
+                }
+                ssl_ReleaseSpecWriteLock(ss);  /**************************/
+                if (rv != SECSuccess) {
+                    errCode = SSL_ERROR_INTERNAL_ERROR_ALERT;
+                    desc = internal_error;
+                    ret = SSL_SNI_SEND_ALERT;
+                    break;
+                }
+            } else if ((unsigned int)ret < ss->xtnData.sniNameArrSize) {
+                /* Application has configured new socket info. Lets check it
+                 * and save the name. */
+                SECStatus       rv;
+                SECItem *       name = &ss->xtnData.sniNameArr[ret];
+                int             configedCiphers;
+                SECItem *       pwsName;
+
+                /* get rid of the old name and save the newly picked. */
+                /* This code is protected by ssl3HandshakeLock. */
+                ssl_GetSpecWriteLock(ss);  /*******************************/
+#ifndef SSL_SNI_ALLOW_NAME_CHANGE_2HS
+                /* not allow name change on the 2d HS */
+                if (ss->firstHsDone) {
+                    SECItem *cwsName = &ss->ssl3.cwSpec->srvVirtName;
+                    if (ssl3_ServerNameCompare(name, cwsName)) {
+                        ssl_ReleaseSpecWriteLock(ss);  /******************/
+                        errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
+                        desc = handshake_failure;
+                        ret = SSL_SNI_SEND_ALERT;
+                        break;
+                    }
+                }
+#endif
+                pwsName = &ss->ssl3.pwSpec->srvVirtName;
+                if (pwsName->data) {
+                    SECITEM_FreeItem(pwsName, PR_FALSE);
+                }
+                rv = SECITEM_CopyItem(NULL, pwsName, name);
+                ssl_ReleaseSpecWriteLock(ss);  /***************************/
+                if (rv != SECSuccess) {
+                    errCode = SSL_ERROR_INTERNAL_ERROR_ALERT;
+                    desc = internal_error;
+                    ret = SSL_SNI_SEND_ALERT;
+                    break;
+                }
+                configedCiphers = ssl3_config_match_init(ss);
+                if (configedCiphers <= 0) {
+                    /* no ciphers are working/supported */
+                    errCode = PORT_GetError();
+                    desc = handshake_failure;
+                    ret = SSL_SNI_SEND_ALERT;
+                    break;
+                }
+                /* Need to tell the client that application has picked
+                 * the name from the offered list and reconfigured the socket.
+                 */
+                ssl3_RegisterServerHelloExtensionSender(ss, ssl_server_name_xtn,
+                                                        ssl3_SendServerNameXtn);
+            } else {
+                /* Callback returned index outside of the boundary. */
+                PORT_Assert((unsigned int)ret < ss->xtnData.sniNameArrSize);
+                errCode = SSL_ERROR_INTERNAL_ERROR_ALERT;
+                desc = internal_error;
+                ret = SSL_SNI_SEND_ALERT;
+                break;
+            }
+        } while (0);
+    /* Free sniNameArr. The data that each SECItem in the array
+     * points into is the data from the input buffer "b". It will
+     * not be available outside the scope of this function or
+     * the callers (*HandleClientHelloPart2) and the callers
+     must not use it after this point. */
+    if (ss->xtnData.sniNameArr) {
+        PORT_Free(ss->xtnData.sniNameArr);
+        ss->xtnData.sniNameArr = NULL;
+        ss->xtnData.sniNameArrSize = 0;
+    }
+    if (ret <= SSL_SNI_SEND_ALERT) {
+        /* desc and errCode should be set. */
+        goto alert_loser;
+    }
+
+    return SECSuccess;
+
+alert_loser:
+    (void)SSL3_SendAlert(ss, alert_fatal, desc);
+    PORT_SetError(errCode);
+    return SECFailure;
+}
+
 /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
  * ssl3 Client Hello message.
  * Caller must hold Handshake and RecvBuf locks.
  */
 static SECStatus
 ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     sslSessionID *sid = NULL;
     PRInt32 tmp;
     unsigned int i;
-    int j;
     SECStatus rv;
     int errCode = SSL_ERROR_RX_MALFORMED_CLIENT_HELLO;
     SSL3AlertDescription desc = illegal_parameter;
     SSL3AlertLevel level = alert_fatal;
     SSL3ProtocolVersion version;
     SECItem sidBytes = { siBuffer, NULL, 0 };
     SECItem cookieBytes = { siBuffer, NULL, 0 };
     SECItem suites = { siBuffer, NULL, 0 };
     SECItem comps = { siBuffer, NULL, 0 };
-    PRBool haveSpecWriteLock = PR_FALSE;
-    PRBool haveXmitBufLock = PR_FALSE;
     PRBool canOfferSessionTicket = PR_FALSE;
-    PRBool isTLS13 = PR_FALSE;
+    PRBool isTLS13;
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle client_hello handshake",
                 SSL_GETPID(), ss->fd));
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
     PORT_Assert(ss->ssl3.initialized);
     ss->ssl3.hs.preliminaryInfo = 0;
@@ -8373,16 +8640,21 @@ ssl3_HandleClientHello(sslSocket *ss, SS
     }
 
     /* grab the client's SID, if present. */
     rv = ssl3_ConsumeHandshakeVariable(ss, &sidBytes, 1, &b, &length);
     if (rv != SECSuccess) {
         goto loser; /* malformed */
     }
 
+    /* You can't resume TLS 1.3 like this, */
+    if (isTLS13 && sidBytes.len) {
+        goto alert_loser;
+    }
+
     /* grab the client's cookie, if present. */
     if (IS_DTLS(ss)) {
         rv = ssl3_ConsumeHandshakeVariable(ss, &cookieBytes, 1, &b, &length);
         if (rv != SECSuccess) {
             goto loser; /* malformed */
         }
     }
 
@@ -8409,17 +8681,17 @@ ssl3_HandleClientHello(sslSocket *ss, SS
     rv = ssl3_ConsumeHandshakeVariable(ss, &comps, 1, &b, &length);
     if (rv != SECSuccess) {
         goto loser; /* malformed */
     }
 
     /* TLS 1.3 requires that compression be empty */
     if (isTLS13) {
         if (comps.len != 1 || comps.data[0] != ssl_compression_null) {
-            goto loser;
+            goto alert_loser;
         }
     }
 
     /* Handle TLS hello extensions for SSL3 & TLS. We do not know if
      * we are restarting a previous session until extensions have been
      * parsed, since we might have received a SessionTicket extension.
      * Note: we allow extensions even when negotiating SSL3 for the sake
      * of interoperability (and backwards compatibility).
@@ -8436,16 +8708,17 @@ ssl3_HandleClientHello(sslSocket *ss, SS
             ssl3_DecodeError(ss); /* send alert */
             goto loser;
         }
         rv = ssl3_HandleHelloExtensions(ss, &b, &length, client_hello);
         if (rv != SECSuccess) {
             goto loser; /* malformed */
         }
     }
+
     if (!ssl3_ExtensionNegotiated(ss, ssl_renegotiation_info_xtn)) {
         /* If we didn't receive an RI extension, look for the SCSV,
          * and if found, treat it just like an empty RI extension
          * by processing a local copy of an empty RI extension.
          */
         for (i = 0; i + 1 < suites.len; i += 2) {
             PRUint16 suite_i = (suites.data[i] << 8) | suites.data[i + 1];
             if (suite_i == TLS_EMPTY_RENEGOTIATION_INFO_SCSV) {
@@ -8468,23 +8741,25 @@ ssl3_HandleClientHello(sslSocket *ss, SS
     if ((ss->opt.requireSafeNegotiation ||
          (ss->firstHsDone && ss->peerRequestedProtection)) &&
         !ssl3_ExtensionNegotiated(ss, ssl_renegotiation_info_xtn)) {
         desc = handshake_failure;
         errCode = SSL_ERROR_UNSAFE_NEGOTIATION;
         goto alert_loser;
     }
 
-    /* We do stateful resumes only if either of the following
-     * conditions are satisfied: (1) the client does not support the
-     * session ticket extension, or (2) the client support the session
-     * ticket extension, but sent an empty ticket.
+    /* We do stateful resumes only if we are in TLS < 1.3 and
+     * either of the following conditions are satisfied:
+     * (1) the client does not support the session ticket extension, or
+     * (2) the client support the session ticket extension, but sent an
+     * empty ticket.
      */
-    if (!ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn) ||
-        ss->xtnData.emptySessionTicket) {
+    if ((ss->version < SSL_LIBRARY_VERSION_TLS_1_3) &&
+        (!ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn) ||
+         ss->xtnData.emptySessionTicket)) {
         if (sidBytes.len > 0 && !ss->opt.noCache) {
             SSL_TRC(7, ("%d: SSL3[%d]: server, lookup client session-id for 0x%08x%08x%08x%08x",
                         SSL_GETPID(), ss->fd, ss->sec.ci.peer.pr_s6_addr32[0],
                         ss->sec.ci.peer.pr_s6_addr32[1],
                         ss->sec.ci.peer.pr_s6_addr32[2],
                         ss->sec.ci.peer.pr_s6_addr32[3]));
             if (ssl_sid_lookup) {
                 sid = (*ssl_sid_lookup)(&ss->sec.ci.peer, sidBytes.data,
@@ -8492,16 +8767,17 @@ ssl3_HandleClientHello(sslSocket *ss, SS
             } else {
                 errCode = SSL_ERROR_SERVER_CACHE_NOT_CONFIGURED;
                 goto loser;
             }
         }
     } else if (ss->statelessResume) {
         /* Fill in the client's session ID if doing a stateless resume.
          * (When doing stateless resumes, server echos client's SessionID.)
+         * This branch also handles TLS 1.3 resumption-PSK.
          */
         sid = ss->sec.ci.sid;
         PORT_Assert(sid != NULL); /* Should have already been filled in.*/
 
         if (sidBytes.len > 0 && sidBytes.len <= SSL3_SESSIONID_BYTES) {
             sid->u.ssl3.sessionIDLength = sidBytes.len;
             PORT_Memcpy(sid->u.ssl3.sessionID, sidBytes.data,
                         sidBytes.len);
@@ -8560,37 +8836,70 @@ ssl3_HandleClientHello(sslSocket *ss, SS
     /* Look for a matching cipher suite. */
     j = ssl3_config_match_init(ss);
     if (j <= 0) {                  /* no ciphers are working/supported by PK11 */
         errCode = PORT_GetError(); /* error code is already set. */
         goto alert_loser;
     }
 #endif
 
+    if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+        rv = tls13_HandleClientHelloPart2(ss, &suites, sid);
+    } else {
+        rv = ssl3_HandleClientHelloPart2(ss, &suites, &comps, sid,
+                                         canOfferSessionTicket);
+    }
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    return SECSuccess;
+
+alert_loser:
+    (void)SSL3_SendAlert(ss, level, desc);
+    /* FALLTHRU */
+loser:
+    PORT_SetError(errCode);
+    return SECFailure;
+}
+
+static SECStatus ssl3_HandleClientHelloPart2(sslSocket *ss,
+                                             SECItem *suites,
+                                             SECItem *comps,
+                                             sslSessionID *sid,
+                                             PRBool canOfferSessionTicket)
+{
+    PRBool haveSpecWriteLock = PR_FALSE;
+    PRBool haveXmitBufLock = PR_FALSE;
+    int errCode = SSL_ERROR_RX_MALFORMED_CLIENT_HELLO;
+    SSL3AlertDescription desc = illegal_parameter;
+    SECStatus rv;
+    unsigned int i;
+    int j;
+
     /* If we already have a session for this client, be sure to pick the
     ** same cipher suite and compression method we picked before.
     ** This is not a loop, despite appearances.
     */
     if (sid)
         do {
             ssl3CipherSuiteCfg *suite;
 #ifdef PARANOID
             SSLVersionRange vrange = { ss->version, ss->version };
 #endif
 
             /* Check that the cached compression method is still enabled. */
             if (!compressionEnabled(ss, sid->u.ssl3.compression))
                 break;
 
             /* Check that the cached compression method is in the client's list */
-            for (i = 0; i < comps.len; i++) {
-                if (comps.data[i] == sid->u.ssl3.compression)
+            for (i = 0; i < comps->len; i++) {
+                if (comps->data[i] == sid->u.ssl3.compression)
                     break;
             }
-            if (i == comps.len)
+            if (i == comps->len)
                 break;
 
             suite = ss->cipherSuites;
             /* Find the entry for the cipher suite used in the cached session. */
             for (j = ssl_V3_SUITES_IMPLEMENTED; j > 0; --j, ++suite) {
                 if (suite->cipher_suite == sid->u.ssl3.cipherSuite)
                     break;
             }
@@ -8605,112 +8914,80 @@ ssl3_HandleClientHello(sslSocket *ss, SS
              */
             if (!config_match(suite, ss->ssl3.policy, PR_TRUE, &vrange, ss))
                 break;
 #else
             if (!suite->enabled)
                 break;
 #endif
             /* Double check that the cached cipher suite is in the client's list */
-            for (i = 0; i + 1 < suites.len; i += 2) {
-                PRUint16 suite_i = (suites.data[i] << 8) | suites.data[i + 1];
+            for (i = 0; i + 1 < suites->len; i += 2) {
+                PRUint16 suite_i = (suites->data[i] << 8) | suites->data[i + 1];
                 if (suite_i == suite->cipher_suite) {
                     ss->ssl3.hs.cipher_suite =
                         suite->cipher_suite;
                     ss->ssl3.hs.suite_def =
                         ssl_LookupCipherSuiteDef(ss->ssl3.hs.cipher_suite);
                     ss->ssl3.hs.kea_def =
                         &kea_defs[ss->ssl3.hs.suite_def->key_exchange_alg];
                     ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_cipher_suite;
 
                     /* Use the cached compression method. */
                     ss->ssl3.hs.compression =
                         sid->u.ssl3.compression;
                     goto compression_found;
                 }
             }
         } while (0);
-
 /* START A NEW SESSION */
 
 #ifndef PARANOID
     /* Look for a matching cipher suite. */
     j = ssl3_config_match_init(ss);
     if (j <= 0) {                  /* no ciphers are working/supported by PK11 */
         desc = internal_error;
         errCode = PORT_GetError(); /* error code is already set. */
         goto alert_loser;
     }
 #endif
 
-    /* Select a cipher suite.
-    **
-    ** NOTE: This suite selection algorithm should be the same as the one in
-    ** ssl3_HandleV2ClientHello().
-    **
-    ** If TLS 1.0 is enabled, we could handle the case where the client
-    ** offered TLS 1.1 but offered only export cipher suites by choosing TLS
-    ** 1.0 and selecting one of those export cipher suites. However, a secure
-    ** TLS 1.1 client should not have export cipher suites enabled at all,
-    ** and a TLS 1.1 client should definitely not be offering *only* export
-    ** cipher suites. Therefore, we refuse to negotiate export cipher suites
-    ** with any client that indicates support for TLS 1.1 or higher when we
-    ** (the server) have TLS 1.1 support enabled.
-    */
-    for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
-        ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
-        SSLVersionRange vrange = { ss->version, ss->version };
-        if (!config_match(suite, ss->ssl3.policy, PR_TRUE, &vrange, ss)) {
-            continue;
-        }
-        for (i = 0; i + 1 < suites.len; i += 2) {
-            PRUint16 suite_i = (suites.data[i] << 8) | suites.data[i + 1];
-            if (suite_i == suite->cipher_suite) {
-                ss->ssl3.hs.cipher_suite = suite->cipher_suite;
-                ss->ssl3.hs.suite_def =
-                    ssl_LookupCipherSuiteDef(ss->ssl3.hs.cipher_suite);
-                ss->ssl3.hs.kea_def =
-                    &kea_defs[ss->ssl3.hs.suite_def->key_exchange_alg];
-                ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_cipher_suite;
-                goto suite_found;
-            }
-        }
-    }
-    errCode = SSL_ERROR_NO_CYPHER_OVERLAP;
-    goto alert_loser;
-
-suite_found:
+    rv = ssl3_NegotiateCipherSuite(ss, suites);
+    if (rv != SECSuccess) {
+        errCode = SSL_ERROR_NO_CYPHER_OVERLAP;
+        goto alert_loser;
+    }
+
     if (canOfferSessionTicket)
         canOfferSessionTicket = ssl3_KEAAllowsSessionTicket(
             ss->ssl3.hs.suite_def->key_exchange_alg);
 
     if (canOfferSessionTicket) {
         ssl3_RegisterServerHelloExtensionSender(ss,
                                                 ssl_session_ticket_xtn, ssl3_SendSessionTicketXtn);
     }
 
     /* Select a compression algorithm. */
-    for (i = 0; i < comps.len; i++) {
-        if (!compressionEnabled(ss, comps.data[i]))
+    for (i = 0; i < comps->len; i++) {
+        if (!compressionEnabled(ss, comps->data[i]))
             continue;
         for (j = 0; j < compressionMethodsCount; j++) {
-            if (comps.data[i] == compressions[j]) {
+            if (comps->data[i] == compressions[j]) {
                 ss->ssl3.hs.compression =
                     (SSLCompressionMethod)compressions[j];
                 goto compression_found;
             }
         }
     }
     errCode = SSL_ERROR_NO_COMPRESSION_OVERLAP;
     /* null compression must be supported */
     goto alert_loser;
 
 compression_found:
-    suites.data = NULL;
-    comps.data = NULL;
+    suites->data = NULL;
+    comps->data = NULL;
 
     /* If there are any failures while processing the old sid,
      * we don't consider them to be errors.  Instead, We just behave
      * as if the client had sent us no sid to begin with, and make a new one.
      * The exception here is attempts to resume extended_master_secret
      * sessions without the extension, which causes an alert.
      */
     if (sid != NULL)
@@ -8769,19 +9046,19 @@ compression_found:
                 if (ss->opt.bypassPKCS11) {
                     /* we cannot restart a non-bypass session in a
                     ** bypass socket.
                     */
                     break;
                 }
 #endif
 
-                wrapKey = getWrappingKey(ss, NULL, sid->u.ssl3.exchKeyType,
-                                         sid->u.ssl3.masterWrapMech,
-                                         ss->pkcs11PinArg);
+                wrapKey = ssl3_GetWrappingKey(ss, NULL, sid->u.ssl3.exchKeyType,
+                                              sid->u.ssl3.masterWrapMech,
+                                              ss->pkcs11PinArg);
                 if (!wrapKey) {
                     /* we have a SID cache entry, but no wrapping key for it??? */
                     break;
                 }
 
                 if (ss->version > SSL_LIBRARY_VERSION_3_0) { /* isTLS */
                     keyFlags =
                         CKF_SIGN | CKF_VERIFY;
@@ -8926,188 +9203,36 @@ compression_found:
         SSL_AtomicIncrementLong(&ssl3stats.hch_sid_cache_not_ok);
         if (ss->sec.uncache)
             ss->sec.uncache(sid);
         ssl_FreeSID(sid);
         sid = NULL;
     }
     SSL_AtomicIncrementLong(&ssl3stats.hch_sid_cache_misses);
 
-    if (ssl3_ExtensionNegotiated(ss, ssl_server_name_xtn)) {
-        int ret = 0;
-        if (ss->sniSocketConfig)
-            do { /* not a loop */
-                PORT_Assert((ss->ssl3.hs.preliminaryInfo & ssl_preinfo_all) ==
-                            ssl_preinfo_all);
-
-                ret = SSL_SNI_SEND_ALERT;
-                /* If extension is negotiated, the len of names should > 0. */
-                if (ss->xtnData.sniNameArrSize) {
-                    /* Calling client callback to reconfigure the socket. */
-                    ret = (SECStatus)(*ss->sniSocketConfig)(ss->fd,
-                                                            ss->xtnData.sniNameArr,
-                                                            ss->xtnData.sniNameArrSize,
-                                                            ss->sniSocketConfigArg);
-                }
-                if (ret <= SSL_SNI_SEND_ALERT) {
-                    /* Application does not know the name or was not able to
-                     * properly reconfigure the socket. */
-                    errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
-                    desc = unrecognized_name;
-                    break;
-                } else if (ret == SSL_SNI_CURRENT_CONFIG_IS_USED) {
-                    SECStatus rv = SECSuccess;
-                    SECItem *cwsName, *pwsName;
-
-                    ssl_GetSpecWriteLock(ss); /*******************************/
-                    pwsName = &ss->ssl3.pwSpec->srvVirtName;
-                    cwsName = &ss->ssl3.cwSpec->srvVirtName;
-#ifndef SSL_SNI_ALLOW_NAME_CHANGE_2HS
-                    /* not allow name change on the 2d HS */
-                    if (ss->firstHsDone) {
-                        if (ssl3_ServerNameCompare(pwsName, cwsName)) {
-                            ssl_ReleaseSpecWriteLock(ss); /******************/
-                            errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
-                            desc = handshake_failure;
-                            ret = SSL_SNI_SEND_ALERT;
-                            break;
-                        }
-                    }
-#endif
-                    if (pwsName->data) {
-                        SECITEM_FreeItem(pwsName, PR_FALSE);
-                    }
-                    if (cwsName->data) {
-                        rv = SECITEM_CopyItem(NULL, pwsName, cwsName);
-                    }
-                    ssl_ReleaseSpecWriteLock(ss); /**************************/
-                    if (rv != SECSuccess) {
-                        errCode = SSL_ERROR_INTERNAL_ERROR_ALERT;
-                        desc = internal_error;
-                        ret = SSL_SNI_SEND_ALERT;
-                        break;
-                    }
-                } else if ((unsigned int)ret < ss->xtnData.sniNameArrSize) {
-                    /* Application has configured new socket info. Lets check it
-                     * and save the name. */
-                    SECStatus rv;
-                    SECItem *name = &ss->xtnData.sniNameArr[ret];
-                    int configedCiphers;
-                    SECItem *pwsName;
-
-                    /* get rid of the old name and save the newly picked. */
-                    /* This code is protected by ssl3HandshakeLock. */
-                    ssl_GetSpecWriteLock(ss); /*******************************/
-#ifndef SSL_SNI_ALLOW_NAME_CHANGE_2HS
-                    /* not allow name change on the 2d HS */
-                    if (ss->firstHsDone) {
-                        SECItem *cwsName = &ss->ssl3.cwSpec->srvVirtName;
-                        if (ssl3_ServerNameCompare(name, cwsName)) {
-                            ssl_ReleaseSpecWriteLock(ss); /******************/
-                            errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
-                            desc = handshake_failure;
-                            ret = SSL_SNI_SEND_ALERT;
-                            break;
-                        }
-                    }
-#endif
-                    pwsName = &ss->ssl3.pwSpec->srvVirtName;
-                    if (pwsName->data) {
-                        SECITEM_FreeItem(pwsName, PR_FALSE);
-                    }
-                    rv = SECITEM_CopyItem(NULL, pwsName, name);
-                    ssl_ReleaseSpecWriteLock(ss); /***************************/
-                    if (rv != SECSuccess) {
-                        errCode = SSL_ERROR_INTERNAL_ERROR_ALERT;
-                        desc = internal_error;
-                        ret = SSL_SNI_SEND_ALERT;
-                        break;
-                    }
-                    configedCiphers = ssl3_config_match_init(ss);
-                    if (configedCiphers <= 0) {
-                        /* no ciphers are working/supported */
-                        errCode = PORT_GetError();
-                        desc = handshake_failure;
-                        ret = SSL_SNI_SEND_ALERT;
-                        break;
-                    }
-                    /* Need to tell the client that application has picked
-                     * the name from the offered list and reconfigured the socket.
-                     */
-                    ssl3_RegisterServerHelloExtensionSender(ss, ssl_server_name_xtn,
-                                                            ssl3_SendServerNameXtn);
-                } else {
-                    /* Callback returned index outside of the boundary. */
-                    PORT_Assert((unsigned int)ret < ss->xtnData.sniNameArrSize);
-                    errCode = SSL_ERROR_INTERNAL_ERROR_ALERT;
-                    desc = internal_error;
-                    ret = SSL_SNI_SEND_ALERT;
-                    break;
-                }
-            } while (0);
-        /* Free sniNameArr. The data that each SECItem in the array
-         * points into is the data from the input buffer "b". It will
-         * not be available outside the scope of this or it's child
-         * functions.*/
-        if (ss->xtnData.sniNameArr) {
-            PORT_Free(ss->xtnData.sniNameArr);
-            ss->xtnData.sniNameArr = NULL;
-            ss->xtnData.sniNameArrSize = 0;
-        }
-        if (ret <= SSL_SNI_SEND_ALERT) {
-            /* desc and errCode should be set. */
-            goto alert_loser;
-        }
-    }
-#ifndef SSL_SNI_ALLOW_NAME_CHANGE_2HS
-    else if (ss->firstHsDone) {
-        /* Check that we don't have the name is current spec
-         * if this extension was not negotiated on the 2d hs. */
-        PRBool passed = PR_TRUE;
-        ssl_GetSpecReadLock(ss); /*******************************/
-        if (ss->ssl3.cwSpec->srvVirtName.data) {
-            passed = PR_FALSE;
-        }
-        ssl_ReleaseSpecReadLock(ss); /***************************/
-        if (!passed) {
-            errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
-            desc = handshake_failure;
-            goto alert_loser;
-        }
-    }
-#endif
-
-    /* If this is TLS 1.3 we are expecting a ClientKeyShare
-     * extension. Missing/absent extension cause failure
-     * below. */
-    if (isTLS13) {
-        rv = tls13_HandleClientKeyShare(ss);
-        if (rv != SECSuccess) {
-            errCode = PORT_GetError();
-            goto alert_loser;
-        }
+    rv = ssl3_ServerCallSNICallback(ss);
+    if (rv != SECSuccess) {
+        /* The alert has already been sent. */
+        errCode = PORT_GetError();
+        goto loser;
     }
 
     sid = ssl3_NewSessionID(ss, PR_TRUE);
     if (sid == NULL) {
         errCode = PORT_GetError();
         goto loser; /* memory error is set. */
     }
     ss->sec.ci.sid = sid;
 
     sid->u.ssl3.keys.extendedMasterSecretUsed =
         ssl3_ExtensionNegotiated(ss, ssl_extended_master_secret_xtn);
     ss->ssl3.hs.isResuming = PR_FALSE;
 
     ssl_GetXmitBufLock(ss);
-    if (isTLS13) {
-        rv = tls13_SendServerHelloSequence(ss);
-    } else {
-        rv = ssl3_SendServerHelloSequence(ss);
-    }
+    rv = ssl3_SendServerHelloSequence(ss);
     ssl_ReleaseXmitBufLock(ss);
     if (rv != SECSuccess) {
         errCode = PORT_GetError();
         desc = handshake_failure;
         goto alert_loser;
     }
 
     if (haveXmitBufLock) {
@@ -9116,18 +9241,18 @@ compression_found:
 
     return SECSuccess;
 
 alert_loser:
     if (haveSpecWriteLock) {
         ssl_ReleaseSpecWriteLock(ss);
         haveSpecWriteLock = PR_FALSE;
     }
-    (void)SSL3_SendAlert(ss, level, desc);
-/* FALLTHRU */
+    (void)SSL3_SendAlert(ss, alert_fatal, desc);
+    /* FALLTHRU */
 loser:
     if (haveSpecWriteLock) {
         ssl_ReleaseSpecWriteLock(ss);
     }
 
     if (haveXmitBufLock) {
         ssl_ReleaseXmitBufLock(ss);
     }
@@ -11705,18 +11830,18 @@ ssl3_CacheWrappedMasterSecret(sslSocket 
                 PK11_SetWrapKey(symKeySlot, wrapKeyIndex, wrappingKey);
             }
         }
     } else {
         /* server socket using session cache. */
         mechanism = PK11_GetBestWrapMechanism(symKeySlot);
         if (mechanism != CKM_INVALID_MECHANISM) {
             wrappingKey =
-                getWrappingKey(ss, symKeySlot, effectiveExchKeyType,
-                               mechanism, pwArg);
+                ssl3_GetWrappingKey(ss, symKeySlot, effectiveExchKeyType,
+                                    mechanism, pwArg);
             if (wrappingKey) {
                 mechanism = PK11_GetMechanism(wrappingKey); /* can't fail. */
             }
         }
     }
 
     sid->u.ssl3.masterWrapMech = mechanism;
     PK11_FreeSlot(symKeySlot);
@@ -11875,51 +12000,17 @@ xmit_loser:
     if (ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa ||
         ss->ssl3.hs.kea_def->kea == kea_dhe_rsa) {
         effectiveExchKeyType = kt_rsa;
     } else {
         effectiveExchKeyType = ss->ssl3.hs.kea_def->exchKeyType;
     }
 
     if (sid->cached == never_cached && !ss->opt.noCache && ss->sec.cache) {
-        /* fill in the sid */
-        sid->u.ssl3.cipherSuite = ss->ssl3.hs.cipher_suite;
-        sid->u.ssl3.compression = ss->ssl3.hs.compression;
-        sid->u.ssl3.policy = ss->ssl3.policy;
-#ifndef NSS_DISABLE_ECC
-        sid->u.ssl3.negotiatedECCurves = ss->ssl3.hs.negotiatedECCurves;
-#endif
-        sid->u.ssl3.exchKeyType = effectiveExchKeyType;
-        sid->version = ss->version;
-        sid->authAlgorithm = ss->sec.authAlgorithm;
-        sid->authKeyBits = ss->sec.authKeyBits;
-        sid->keaType = ss->sec.keaType;
-        sid->keaKeyBits = ss->sec.keaKeyBits;
-        sid->lastAccessTime = sid->creationTime = ssl_Time();
-        sid->expirationTime = sid->creationTime + ssl3_sid_timeout;
-        sid->localCert = CERT_DupCertificate(ss->sec.localCert);
-
-        ssl_GetSpecReadLock(ss); /*************************************/
-
-        /* Copy the master secret (wrapped or unwrapped) into the sid */
-        if (ss->ssl3.crSpec->msItem.len && ss->ssl3.crSpec->msItem.data) {
-            sid->u.ssl3.keys.wrapped_master_secret_len =
-                ss->ssl3.crSpec->msItem.len;
-            memcpy(sid->u.ssl3.keys.wrapped_master_secret,
-                   ss->ssl3.crSpec->msItem.data, ss->ssl3.crSpec->msItem.len);
-            sid->u.ssl3.masterValid = PR_TRUE;
-            sid->u.ssl3.keys.msIsWrapped = PR_FALSE;
-            rv = SECSuccess;
-        } else {
-            rv = ssl3_CacheWrappedMasterSecret(ss, ss->sec.ci.sid,
-                                               ss->ssl3.crSpec,
-                                               effectiveExchKeyType);
-            sid->u.ssl3.keys.msIsWrapped = PR_TRUE;
-        }
-        ssl_ReleaseSpecReadLock(ss); /*************************************/
+        rv = ssl3_FillInCachedSID(ss, sid, effectiveExchKeyType);
 
         /* If the wrap failed, we don't cache the sid.
          * The connection continues normally however.
          */
         ss->ssl3.hs.cacheSID = rv == SECSuccess;
     }
 
     if (ss->ssl3.hs.authCertificatePending) {
@@ -11932,16 +12023,63 @@ xmit_loser:
         ss->ssl3.hs.restartTarget = ssl3_FinishHandshake;
         return SECWouldBlock;
     }
 
     rv = ssl3_FinishHandshake(ss);
     return rv;
 }
 
+SECStatus
+ssl3_FillInCachedSID(sslSocket *ss, sslSessionID *sid,
+                     SSL3KEAType effectiveExchKeyType)
+{
+    SECStatus rv;
+
+    /* fill in the sid */
+    sid->u.ssl3.cipherSuite =
+            ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 ?
+            ss->ssl3.hs.origCipherSuite : ss->ssl3.hs.cipher_suite;
+    sid->u.ssl3.compression = ss->ssl3.hs.compression;
+    sid->u.ssl3.policy = ss->ssl3.policy;
+#ifndef NSS_DISABLE_ECC
+    sid->u.ssl3.negotiatedECCurves = ss->ssl3.hs.negotiatedECCurves;
+#endif
+    sid->u.ssl3.exchKeyType = effectiveExchKeyType;
+    sid->version = ss->version;
+    sid->authAlgorithm = ss->sec.authAlgorithm;
+    sid->authKeyBits = ss->sec.authKeyBits;
+    sid->keaType = ss->sec.keaType;
+    sid->keaKeyBits = ss->sec.keaKeyBits;
+    sid->lastAccessTime = sid->creationTime = ssl_Time();
+    sid->expirationTime = sid->creationTime + ssl3_sid_timeout;
+    sid->localCert = CERT_DupCertificate(ss->sec.localCert);
+
+    ssl_GetSpecReadLock(ss); /*************************************/
+
+    /* Copy the master secret (wrapped or unwrapped) into the sid */
+    if (ss->ssl3.crSpec->msItem.len && ss->ssl3.crSpec->msItem.data) {
+        sid->u.ssl3.keys.wrapped_master_secret_len =
+                ss->ssl3.crSpec->msItem.len;
+        memcpy(sid->u.ssl3.keys.wrapped_master_secret,
+               ss->ssl3.crSpec->msItem.data, ss->ssl3.crSpec->msItem.len);
+        sid->u.ssl3.masterValid = PR_TRUE;
+        sid->u.ssl3.keys.msIsWrapped = PR_FALSE;
+        rv = SECSuccess;
+    } else {
+        rv = ssl3_CacheWrappedMasterSecret(ss, ss->sec.ci.sid,
+                                           ss->ssl3.crSpec,
+                                           effectiveExchKeyType);
+        sid->u.ssl3.keys.msIsWrapped = PR_TRUE;
+    }
+    ssl_ReleaseSpecReadLock(ss); /*************************************/
+
+    return rv;
+}
+
 /* The return type is SECStatus instead of void because this function needs
  * to have type sslRestartTarget.
  */
 SECStatus
 ssl3_FinishHandshake(sslSocket *ss)
 {
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
--- a/lib/ssl/ssl3ext.c
+++ b/lib/ssl/ssl3ext.c
@@ -111,16 +111,25 @@ static SECStatus ssl3_HandleExtendedMast
 static PRInt32 tls13_ClientSendKeyShareXtn(sslSocket *ss, PRBool append,
                                            PRUint32 maxBytes);
 static SECStatus tls13_ClientHandleKeyShareXtn(sslSocket *ss,
                                                PRUint16 ex_type,
                                                SECItem *data);
 static SECStatus tls13_ServerHandleKeyShareXtn(sslSocket *ss,
                                                PRUint16 ex_type,
                                                SECItem *data);
+static PRInt32 tls13_ClientSendPreSharedKeyXtn(sslSocket *ss, PRBool append,
+                                               PRUint32 maxBytes);
+static SECStatus tls13_ServerHandlePreSharedKeyXtn(sslSocket *ss,
+                                                   PRUint16 ex_type,
+                                                   SECItem *data);
+static SECStatus tls13_ClientHandlePreSharedKeyXtn(sslSocket *ss,
+                                                   PRUint16 ex_type,
+                                                   SECItem *data);
+
 
 /*
  * Write bytes.  Using this function means the SECItem structure
  * cannot be freed.  The caller is expected to call this function
  * on a shallow copy of the structure.
  */
 static SECStatus
 ssl3_AppendToItem(SECItem *item, const unsigned char *buf, PRUint32 bytes)
@@ -155,17 +164,17 @@ ssl3_AppendNumberToItem(SECItem *item, P
             *p++ = (PRUint8)(num >> 8);
         case 1:
             *p = (PRUint8)num;
     }
     rv = ssl3_AppendToItem(item, &b[0], lenSize);
     return rv;
 }
 
-static SECStatus
+SECStatus
 ssl3_SessionTicketShutdown(void *appData, void *nssData)
 {
     if (session_ticket_enc_key_pkcs11) {
         PK11_FreeSymKey(session_ticket_enc_key_pkcs11);
         session_ticket_enc_key_pkcs11 = NULL;
     }
     if (session_ticket_mac_key_pkcs11) {
         PK11_FreeSymKey(session_ticket_mac_key_pkcs11);
@@ -280,16 +289,17 @@ static const ssl3HelloExtensionHandler c
     { ssl_app_layer_protocol_xtn, &ssl3_ServerHandleAppProtoXtn },
     { ssl_use_srtp_xtn, &ssl3_ServerHandleUseSRTPXtn },
     { ssl_cert_status_xtn, &ssl3_ServerHandleStatusRequestXtn },
     { ssl_signature_algorithms_xtn, &ssl3_ServerHandleSigAlgsXtn },
     { ssl_tls13_draft_version_xtn, &ssl3_ServerHandleDraftVersionXtn },
     { ssl_extended_master_secret_xtn, &ssl3_HandleExtendedMasterSecretXtn },
     { ssl_signed_cert_timestamp_xtn, &ssl3_ServerHandleSignedCertTimestampXtn },
     { ssl_tls13_key_share_xtn, &tls13_ServerHandleKeyShareXtn },
+    { ssl_tls13_pre_shared_key_xtn, &tls13_ServerHandlePreSharedKeyXtn },
     { -1, NULL }
 };
 
 /* These two tables are used by the client, to handle server hello
  * extensions. */
 static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = {
     { ssl_server_name_xtn, &ssl3_HandleServerNameXtn },
     /* TODO: add a handler for ssl_ec_point_formats_xtn */
@@ -297,33 +307,30 @@ static const ssl3HelloExtensionHandler s
     { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
     { ssl_next_proto_nego_xtn, &ssl3_ClientHandleNextProtoNegoXtn },
     { ssl_app_layer_protocol_xtn, &ssl3_ClientHandleAppProtoXtn },
     { ssl_use_srtp_xtn, &ssl3_ClientHandleUseSRTPXtn },
     { ssl_cert_status_xtn, &ssl3_ClientHandleStatusRequestXtn },
     { ssl_extended_master_secret_xtn, &ssl3_HandleExtendedMasterSecretXtn },
     { ssl_signed_cert_timestamp_xtn, &ssl3_ClientHandleSignedCertTimestampXtn },
     { ssl_tls13_key_share_xtn, &tls13_ClientHandleKeyShareXtn },
+    { ssl_tls13_pre_shared_key_xtn, &tls13_ClientHandlePreSharedKeyXtn },
     { -1, NULL }
 };
 
 static const ssl3HelloExtensionHandler serverHelloHandlersSSL3[] = {
     { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
     { -1, NULL }
 };
 
 /* Tables of functions to format TLS hello extensions, one function per
  * extension.
  * These static tables are for the formatting of client hello extensions.
  * The server's table of hello senders is dynamic, in the socket struct,
  * and sender functions are registered there.
- * NB: the order of these extensions can have an impact on compatibility. Some
- * servers (e.g. Tomcat) will terminate the connection if the last extension in
- * the client hello is empty (for example, the extended master secret
- * extension, if it were listed last). See bug 1243641.
  */
 static const ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] =
     {
       { ssl_server_name_xtn, &ssl3_SendServerNameXtn },
       { ssl_extended_master_secret_xtn, &ssl3_SendExtendedMasterSecretXtn },
       { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn },
 #ifndef NSS_DISABLE_ECC
       { ssl_elliptic_curves_xtn, &ssl3_SendSupportedCurvesXtn },
@@ -333,24 +340,25 @@ static const ssl3HelloExtensionSender cl
       { ssl_next_proto_nego_xtn, &ssl3_ClientSendNextProtoNegoXtn },
       { ssl_app_layer_protocol_xtn, &ssl3_ClientSendAppProtoXtn },
       { ssl_use_srtp_xtn, &ssl3_ClientSendUseSRTPXtn },
       { ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn },
       { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn },
       { ssl_tls13_draft_version_xtn, &ssl3_ClientSendDraftVersionXtn },
       { ssl_signed_cert_timestamp_xtn, &ssl3_ClientSendSignedCertTimestampXtn },
       { ssl_tls13_key_share_xtn, &tls13_ClientSendKeyShareXtn },
+      { ssl_tls13_pre_shared_key_xtn, &tls13_ClientSendPreSharedKeyXtn }
       /* any extra entries will appear as { 0, NULL }    */
     };
 
-static const ssl3HelloExtensionSender clientHelloSendersSSL3[SSL_MAX_EXTENSIONS] =
-    {
-      { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn }
-      /* any extra entries will appear as { 0, NULL }    */
-    };
+static const
+ssl3HelloExtensionSender clientHelloSendersSSL3[SSL_MAX_EXTENSIONS] = {
+    { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn }
+    /* any extra entries will appear as { 0, NULL }    */
+};
 
 static PRBool
 arrayContainsExtension(const PRUint16 *array, PRUint32 len, PRUint16 ex_type)
 {
     unsigned int i;
     for (i = 0; i < len; i++) {
         if (ex_type == array[i])
             return PR_TRUE;
@@ -361,17 +369,17 @@ arrayContainsExtension(const PRUint16 *a
 PRBool
 ssl3_ExtensionNegotiated(sslSocket *ss, PRUint16 ex_type)
 {
     TLSExtensionData *xtnData = &ss->xtnData;
     return arrayContainsExtension(xtnData->negotiated,
                                   xtnData->numNegotiated, ex_type);
 }
 
-static PRBool
+PRBool
 ssl3_ClientExtensionAdvertised(sslSocket *ss, PRUint16 ex_type)
 {
     TLSExtensionData *xtnData = &ss->xtnData;
     return arrayContainsExtension(xtnData->advertised,
                                   xtnData->numAdvertised, ex_type);
 }
 
 /* Format an SNI extension, using the name from the socket's URL,
@@ -543,16 +551,23 @@ ssl3_SendSessionTicketXtn(
     sslSocket *ss,
     PRBool append,
     PRUint32 maxBytes)
 {
     PRInt32 extension_length;
     NewSessionTicket *session_ticket = NULL;
     sslSessionID *sid = ss->sec.ci.sid;
 
+    /* Never send an extension with a ticket for TLS 1.3, but
+     * OK to send the empty one in case the server does 1.2. */
+    if (sid->cached == in_client_cache &&
+        sid->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+        return 0;
+    }
+
     /* Ignore the SessionTicket extension if processing is disabled. */
     if (!ss->opt.enableSessionTickets)
         return 0;
 
     /* Empty extension length = extension_type (2-bytes) +
      * length(extension_data) (2-bytes)
      */
     extension_length = 4;
@@ -1142,16 +1157,18 @@ ssl3_SendNewSessionTicket(sslSocket *ss)
     unsigned char computed_mac[TLS_EX_SESS_TICKET_MAC_LENGTH];
     unsigned int computed_mac_length;
     unsigned char iv[AES_BLOCK_SIZE];
     SECItem ivItem;
     SECItem *srvName = NULL;
     PRUint32 srvNameLen = 0;
     CK_MECHANISM_TYPE msWrapMech = 0; /* dummy default value,
                                           * must be >= 0 */
+    ssl3CipherSpec *spec = ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 ?
+            ss->ssl3.cwSpec : ss->ssl3.pwSpec;
 
     SSL_TRC(3, ("%d: SSL3[%d]: send session_ticket handshake",
                 SSL_GETPID(), ss->fd));
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     ticket.ticket_lifetime_hint = TLS_EX_SESS_TICKET_LIFETIME_HINT;
@@ -1174,51 +1191,51 @@ ssl3_SendNewSessionTicket(sslSocket *ss)
 #endif
     {
         rv = ssl3_GetSessionTicketKeysPKCS11(ss, &aes_key_pkcs11,
                                              &mac_key_pkcs11);
     }
     if (rv != SECSuccess)
         goto loser;
 
-    if (ss->ssl3.pwSpec->msItem.len && ss->ssl3.pwSpec->msItem.data) {
+    if (spec->msItem.len && spec->msItem.data) {
         /* The master secret is available unwrapped. */
-        ms_item.data = ss->ssl3.pwSpec->msItem.data;
-        ms_item.len = ss->ssl3.pwSpec->msItem.len;
+        ms_item.data = spec->msItem.data;
+        ms_item.len = spec->msItem.len;
         ms_is_wrapped = PR_FALSE;
     } else {
         /* Extract the master secret wrapped. */
         sslSessionID sid;
         PORT_Memset(&sid, 0, sizeof(sslSessionID));
 
         if (ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa ||
             ss->ssl3.hs.kea_def->kea == kea_dhe_rsa) {
             effectiveExchKeyType = kt_rsa;
         } else {
             effectiveExchKeyType = ss->ssl3.hs.kea_def->exchKeyType;
         }
 
-        rv = ssl3_CacheWrappedMasterSecret(ss, &sid, ss->ssl3.pwSpec,
+        rv = ssl3_CacheWrappedMasterSecret(ss, &sid, spec,
                                            effectiveExchKeyType);
         if (rv == SECSuccess) {
             if (sid.u.ssl3.keys.wrapped_master_secret_len > sizeof(wrapped_ms))
                 goto loser;
             memcpy(wrapped_ms, sid.u.ssl3.keys.wrapped_master_secret,
                    sid.u.ssl3.keys.wrapped_master_secret_len);
             ms_item.data = wrapped_ms;
             ms_item.len = sid.u.ssl3.keys.wrapped_master_secret_len;
             msWrapMech = sid.u.ssl3.masterWrapMech;
         } else {
             /* TODO: else send an empty ticket. */
             goto loser;
         }
         ms_is_wrapped = PR_TRUE;
     }
     /* Prep to send negotiated name */
-    srvName = &ss->ssl3.pwSpec->srvVirtName;
+    srvName = &ss->sec.ci.sid->u.ssl3.srvName;
     if (srvName->data && srvName->len) {
         srvNameLen = 2 + srvName->len; /* len bytes + name len */
     }
 
     ciphertext_length =
         sizeof(PRUint16)              /* ticket_version */
         + sizeof(SSL3ProtocolVersion) /* ssl_version */
         + sizeof(ssl3CipherSuite)     /* ciphersuite */
@@ -1368,31 +1385,31 @@ ssl3_SendNewSessionTicket(sslSocket *ss)
         rv = SECFailure;
         goto loser;
     }
 
 /* Generate encrypted portion of ticket. */
 #ifndef NO_PKCS11_BYPASS
     if (ss->opt.bypassPKCS11) {
         PORT_Assert(aes_key);
+
         aes_ctx = (AESContext *)aes_ctx_buf;
         rv = AES_InitContext(aes_ctx, aes_key, aes_key_length, iv,
                              NSS_AES_CBC, 1, AES_BLOCK_SIZE);
         if (rv != SECSuccess)
             goto loser;
 
         rv = AES_Encrypt(aes_ctx, ciphertext.data, &ciphertext.len,
                          ciphertext.len, plaintext_item.data,
                          plaintext_item.len);
         if (rv != SECSuccess)
             goto loser;
     } else
 #endif
     {
-        PORT_Assert(aes_key_pkcs11);
         aes_ctx_pkcs11 = PK11_CreateContextBySymKey(cipherMech,
                                                     CKA_ENCRYPT, aes_key_pkcs11, &ivItem);
         if (!aes_ctx_pkcs11)
             goto loser;
 
         rv = PK11_CipherOp(aes_ctx_pkcs11, ciphertext.data,
                            (int *)&ciphertext.len, ciphertext.len,
                            plaintext_item.data, plaintext_item.len);
@@ -1404,18 +1421,16 @@ ssl3_SendNewSessionTicket(sslSocket *ss)
 
     /* Convert ciphertext length to network order. */
     length_buf[0] = (ciphertext.len >> 8) & 0xff;
     length_buf[1] = (ciphertext.len) & 0xff;
 
 /* Compute MAC. */
 #ifndef NO_PKCS11_BYPASS
     if (ss->opt.bypassPKCS11) {
-        PORT_Assert(mac_key);
-
         hmac_ctx = (HMACContext *)hmac_ctx_buf;
         hashObj = HASH_GetRawHashObject(HASH_AlgSHA256);
         if (HMAC_Init(hmac_ctx, hashObj, mac_key,
                       mac_key_length, PR_FALSE) != SECSuccess)
             goto loser;
 
         HMAC_Begin(hmac_ctx);
         HMAC_Update(hmac_ctx, key_name, SESS_TICKET_KEY_NAME_LEN);
@@ -1423,17 +1438,16 @@ ssl3_SendNewSessionTicket(sslSocket *ss)
         HMAC_Update(hmac_ctx, (unsigned char *)length_buf, 2);
         HMAC_Update(hmac_ctx, ciphertext.data, ciphertext.len);
         HMAC_Finish(hmac_ctx, computed_mac, &computed_mac_length,
                     sizeof(computed_mac));
     } else
 #endif
     {
         SECItem macParam;
-        PORT_Assert(mac_key_pkcs11);
         macParam.data = NULL;
         macParam.len = 0;
         hmac_ctx_pkcs11 = PK11_CreateContextBySymKey(macMech,
                                                      CKA_SIGN, mac_key_pkcs11, &macParam);
         if (!hmac_ctx_pkcs11)
             goto loser;
 
         rv = PK11_DigestBegin(hmac_ctx_pkcs11);
@@ -1512,451 +1526,438 @@ ssl3_ClientHandleSessionTicketXtn(sslSoc
         return SECSuccess; /* Ignore the extension. */
     }
 
     /* Keep track of negotiated extensions. */
     ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
     return SECSuccess;
 }
 
-SECStatus
-ssl3_ServerHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type,
-                                  SECItem *data)
+/* Generic ticket processing code, common to TLS 1.0-1.2 and
+ * TLS 1.3. */
+static SECStatus
+ssl3_ProcessSessionTicketCommon(sslSocket *ss, SECItem *data)
 {
     SECStatus rv;
     SECItem *decrypted_state = NULL;
     SessionTicket *parsed_session_ticket = NULL;
     sslSessionID *sid = NULL;
     SSL3Statistics *ssl3stats;
-
-    /* Ignore the SessionTicket extension if processing is disabled. */
-    if (!ss->opt.enableSessionTickets) {
-        return SECSuccess;
-    }
-
-    /* Keep track of negotiated extensions. */
-    ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
-
-    /* Parse the received ticket sent in by the client.  We are
-     * lenient about some parse errors, falling back to a fullshake
-     * instead of terminating the current connection.
+    PRUint32 i;
+    SECItem extension_data;
+    EncryptedSessionTicket enc_session_ticket;
+    unsigned char computed_mac[TLS_EX_SESS_TICKET_MAC_LENGTH];
+    unsigned int computed_mac_length;
+#ifndef NO_PKCS11_BYPASS
+    const SECHashObject *hashObj;
+    const unsigned char *aes_key = NULL;
+    const unsigned char *mac_key = NULL;
+    PRUint32 aes_key_length;
+    PRUint32 mac_key_length;
+    PRUint64 hmac_ctx_buf[MAX_MAC_CONTEXT_LLONGS];
+    HMACContext *hmac_ctx;
+    PRUint64 aes_ctx_buf[MAX_CIPHER_CONTEXT_LLONGS];
+    AESContext *aes_ctx;
+#endif
+    PK11SymKey *aes_key_pkcs11 = NULL;
+    PK11SymKey *mac_key_pkcs11 = NULL;
+    PK11Context *hmac_ctx_pkcs11;
+    CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC;
+    PK11Context *aes_ctx_pkcs11;
+    CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC;
+    unsigned char *padding;
+    PRUint32 padding_length;
+    unsigned char *buffer;
+    unsigned int buffer_len;
+    PRInt32 temp;
+    SECItem cert_item;
+    PRInt8 nameType = TLS_STE_NO_SERVER_NAME;
+
+    /* Turn off stateless session resumption if the client sends a
+     * SessionTicket extension, even if the extension turns out to be
+     * malformed (ss->sec.ci.sid is non-NULL when doing session
+     * renegotiation.)
      */
-    if (data->len == 0) {
-        ss->xtnData.emptySessionTicket = PR_TRUE;
-    } else {
-        PRUint32 i;
-        SECItem extension_data;
-        EncryptedSessionTicket enc_session_ticket;
-        unsigned char computed_mac[TLS_EX_SESS_TICKET_MAC_LENGTH];
-        unsigned int computed_mac_length;
+    if (ss->sec.ci.sid != NULL) {
+        if (ss->sec.uncache)
+            ss->sec.uncache(ss->sec.ci.sid);
+        ssl_FreeSID(ss->sec.ci.sid);
+        ss->sec.ci.sid = NULL;
+    }
+
+    extension_data.data = data->data; /* Keep a copy for future use. */
+    extension_data.len = data->len;
+
+    if (ssl3_ParseEncryptedSessionTicket(ss, data, &enc_session_ticket) !=
+        SECSuccess) {
+        return SECSuccess; /* Pretend it isn't there */
+    }
+
+    /* Get session ticket keys. */
 #ifndef NO_PKCS11_BYPASS
-        const SECHashObject *hashObj;
-        const unsigned char *aes_key = NULL;
-        const unsigned char *mac_key = NULL;
-        PRUint32 aes_key_length;
-        PRUint32 mac_key_length;
-        PRUint64 hmac_ctx_buf[MAX_MAC_CONTEXT_LLONGS];
-        HMACContext *hmac_ctx;
-        PRUint64 aes_ctx_buf[MAX_CIPHER_CONTEXT_LLONGS];
-        AESContext *aes_ctx;
+    if (ss->opt.bypassPKCS11) {
+        rv = ssl3_GetSessionTicketKeys(&aes_key, &aes_key_length,
+                                       &mac_key, &mac_key_length);
+    } else
 #endif
-        PK11SymKey *aes_key_pkcs11 = NULL;
-        PK11SymKey *mac_key_pkcs11 = NULL;
-        PK11Context *hmac_ctx_pkcs11;
-        CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC;
-        PK11Context *aes_ctx_pkcs11;
-        CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC;
-        unsigned char *padding;
-        PRUint32 padding_length;
-        unsigned char *buffer;
-        unsigned int buffer_len;
-        PRInt32 temp;
-        SECItem cert_item;
-        PRInt8 nameType = TLS_STE_NO_SERVER_NAME;
-
-        /* Turn off stateless session resumption if the client sends a
-         * SessionTicket extension, even if the extension turns out to be
-         * malformed (ss->sec.ci.sid is non-NULL when doing session
-         * renegotiation.)
-         */
-        if (ss->sec.ci.sid != NULL) {
-            if (ss->sec.uncache)
-                ss->sec.uncache(ss->sec.ci.sid);
-            ssl_FreeSID(ss->sec.ci.sid);
-            ss->sec.ci.sid = NULL;
+    {
+        rv = ssl3_GetSessionTicketKeysPKCS11(ss, &aes_key_pkcs11,
+                                             &mac_key_pkcs11);
+    }
+    if (rv != SECSuccess) {
+        SSL_DBG(("%d: SSL[%d]: Unable to get/generate session ticket keys.",
+                 SSL_GETPID(), ss->fd));
+        goto loser;
+    }
+
+    /* If the ticket sent by the client was generated under a key different
+     * from the one we have, bypass ticket processing.
+     */
+    if (PORT_Memcmp(enc_session_ticket.key_name, key_name,
+                    SESS_TICKET_KEY_NAME_LEN) != 0) {
+        SSL_DBG(("%d: SSL[%d]: Session ticket key_name sent mismatch.",
+                 SSL_GETPID(), ss->fd));
+        goto no_ticket;
+    }
+
+    /* Verify the MAC on the ticket.  MAC verification may also
+     * fail if the MAC key has been recently refreshed.
+     */
+#ifndef NO_PKCS11_BYPASS
+    if (ss->opt.bypassPKCS11) {
+        PORT_Assert(mac_key);
+        hmac_ctx = (HMACContext *)hmac_ctx_buf;
+        hashObj = HASH_GetRawHashObject(HASH_AlgSHA256);
+        if (HMAC_Init(hmac_ctx, hashObj, mac_key,
+                      sizeof(session_ticket_mac_key), PR_FALSE) != SECSuccess)
+            goto no_ticket;
+        HMAC_Begin(hmac_ctx);
+        HMAC_Update(hmac_ctx, extension_data.data,
+                    extension_data.len - TLS_EX_SESS_TICKET_MAC_LENGTH);
+        if (HMAC_Finish(hmac_ctx, computed_mac, &computed_mac_length,
+                        sizeof(computed_mac)) != SECSuccess)
+            goto no_ticket;
+    } else
+#endif
+    {
+        SECItem macParam;
+        PORT_Assert(mac_key_pkcs11);
+        macParam.data = NULL;
+        macParam.len = 0;
+        hmac_ctx_pkcs11 = PK11_CreateContextBySymKey(macMech,
+                                                     CKA_SIGN, mac_key_pkcs11, &macParam);
+        if (!hmac_ctx_pkcs11) {
+            SSL_DBG(("%d: SSL[%d]: Unable to create HMAC context: %d.",
+                     SSL_GETPID(), ss->fd, PORT_GetError()));
+            goto no_ticket;
+        } else {
+            SSL_DBG(("%d: SSL[%d]: Successfully created HMAC context.",
+                     SSL_GETPID(), ss->fd));
         }
-
-        extension_data.data = data->data; /* Keep a copy for future use. */
-        extension_data.len = data->len;
-
-        if (ssl3_ParseEncryptedSessionTicket(ss, data, &enc_session_ticket) !=
-            SECSuccess) {
-            return SECSuccess; /* Pretend it isn't there */
+        rv = PK11_DigestBegin(hmac_ctx_pkcs11);
+        if (rv != SECSuccess) {
+            PK11_DestroyContext(hmac_ctx_pkcs11, PR_TRUE);
+            goto no_ticket;
+        }
+        rv = PK11_DigestOp(hmac_ctx_pkcs11, extension_data.data,
+                           extension_data.len -
+                           TLS_EX_SESS_TICKET_MAC_LENGTH);
+        if (rv != SECSuccess) {
+            PK11_DestroyContext(hmac_ctx_pkcs11, PR_TRUE);
+            goto no_ticket;
         }
-
-/* Get session ticket keys. */
+        rv = PK11_DigestFinal(hmac_ctx_pkcs11, computed_mac,
+                              &computed_mac_length, sizeof(computed_mac));
+        PK11_DestroyContext(hmac_ctx_pkcs11, PR_TRUE);
+        if (rv != SECSuccess)
+            goto no_ticket;
+    }
+    if (NSS_SecureMemcmp(computed_mac, enc_session_ticket.mac,
+                         computed_mac_length) !=
+        0) {
+        SSL_DBG(("%d: SSL[%d]: Session ticket MAC mismatch.",
+                 SSL_GETPID(), ss->fd));
+        goto no_ticket;
+    }
+
+    /* We ignore key_name for now.
+     * This is ok as MAC verification succeeded.
+     */
+
+    /* Decrypt the ticket. */
+
+    /* Plaintext is shorter than the ciphertext due to padding. */
+    decrypted_state = SECITEM_AllocItem(NULL, NULL,
+                                        enc_session_ticket.encrypted_state.len);
+
 #ifndef NO_PKCS11_BYPASS
-        if (ss->opt.bypassPKCS11) {
-            rv = ssl3_GetSessionTicketKeys(&aes_key, &aes_key_length,
-                                           &mac_key, &mac_key_length);
-        } else
-#endif
-        {
-            rv = ssl3_GetSessionTicketKeysPKCS11(ss, &aes_key_pkcs11,
-                                                 &mac_key_pkcs11);
-        }
+    if (ss->opt.bypassPKCS11) {
+        PORT_Assert(aes_key);
+        aes_ctx = (AESContext *)aes_ctx_buf;
+        rv = AES_InitContext(aes_ctx, aes_key,
+                             sizeof(session_ticket_enc_key), enc_session_ticket.iv,
+                             NSS_AES_CBC, 0, AES_BLOCK_SIZE);
         if (rv != SECSuccess) {
-            SSL_DBG(("%d: SSL[%d]: Unable to get/generate session ticket keys.",
-                     SSL_GETPID(), ss->fd));
-            goto loser;
-        }
-
-        /* If the ticket sent by the client was generated under a key different
-         * from the one we have, bypass ticket processing.
-         */
-        if (PORT_Memcmp(enc_session_ticket.key_name, key_name,
-                        SESS_TICKET_KEY_NAME_LEN) != 0) {
-            SSL_DBG(("%d: SSL[%d]: Session ticket key_name sent mismatch.",
+            SSL_DBG(("%d: SSL[%d]: Unable to create AES context.",
                      SSL_GETPID(), ss->fd));
             goto no_ticket;
         }
 
-        /* Verify the MAC on the ticket.  MAC verification may also
-         * fail if the MAC key has been recently refreshed.
-         */
-#ifndef NO_PKCS11_BYPASS
-        if (ss->opt.bypassPKCS11) {
-            PORT_Assert(mac_key);
-            hmac_ctx = (HMACContext *)hmac_ctx_buf;
-            hashObj = HASH_GetRawHashObject(HASH_AlgSHA256);
-            if (HMAC_Init(hmac_ctx, hashObj, mac_key,
-                          sizeof(session_ticket_mac_key), PR_FALSE) != SECSuccess)
-                goto no_ticket;
-            HMAC_Begin(hmac_ctx);
-            HMAC_Update(hmac_ctx, extension_data.data,
-                        extension_data.len - TLS_EX_SESS_TICKET_MAC_LENGTH);
-            if (HMAC_Finish(hmac_ctx, computed_mac, &computed_mac_length,
-                            sizeof(computed_mac)) != SECSuccess)
-                goto no_ticket;
-        } else
+        rv = AES_Decrypt(aes_ctx, decrypted_state->data,
+                         &decrypted_state->len, decrypted_state->len,
+                         enc_session_ticket.encrypted_state.data,
+                         enc_session_ticket.encrypted_state.len);
+        if (rv != SECSuccess)
+            goto no_ticket;
+    } else
 #endif
-        {
-            SECItem macParam;
-            PORT_Assert(mac_key_pkcs11);
-            macParam.data = NULL;
-            macParam.len = 0;
-            hmac_ctx_pkcs11 = PK11_CreateContextBySymKey(macMech,
-                                                         CKA_SIGN, mac_key_pkcs11, &macParam);
-            if (!hmac_ctx_pkcs11) {
-                SSL_DBG(("%d: SSL[%d]: Unable to create HMAC context: %d.",
-                         SSL_GETPID(), ss->fd, PORT_GetError()));
-                goto no_ticket;
-            } else {
-                SSL_DBG(("%d: SSL[%d]: Successfully created HMAC context.",
-                         SSL_GETPID(), ss->fd));
-            }
-            rv = PK11_DigestBegin(hmac_ctx_pkcs11);
-            if (rv != SECSuccess) {
-                PK11_DestroyContext(hmac_ctx_pkcs11, PR_TRUE);
-                goto no_ticket;
-            }
-            rv = PK11_DigestOp(hmac_ctx_pkcs11, extension_data.data,
-                               extension_data.len -
-                                   TLS_EX_SESS_TICKET_MAC_LENGTH);
-            if (rv != SECSuccess) {
-                PK11_DestroyContext(hmac_ctx_pkcs11, PR_TRUE);
-                goto no_ticket;
-            }
-            rv = PK11_DigestFinal(hmac_ctx_pkcs11, computed_mac,
-                                  &computed_mac_length, sizeof(computed_mac));
-            PK11_DestroyContext(hmac_ctx_pkcs11, PR_TRUE);
-            if (rv != SECSuccess)
-                goto no_ticket;
-        }
-        if (NSS_SecureMemcmp(computed_mac, enc_session_ticket.mac,
-                             computed_mac_length) !=
-            0) {
-            SSL_DBG(("%d: SSL[%d]: Session ticket MAC mismatch.",
+    {
+        SECItem ivItem;
+        PORT_Assert(aes_key_pkcs11);
+        ivItem.data = enc_session_ticket.iv;
+        ivItem.len = AES_BLOCK_SIZE;
+        aes_ctx_pkcs11 = PK11_CreateContextBySymKey(cipherMech,
+                                                    CKA_DECRYPT, aes_key_pkcs11, &ivItem);
+        if (!aes_ctx_pkcs11) {
+            SSL_DBG(("%d: SSL[%d]: Unable to create AES context.",
                      SSL_GETPID(), ss->fd));
             goto no_ticket;
         }
 
-        /* We ignore key_name for now.
-         * This is ok as MAC verification succeeded.
-         */
-
-        /* Decrypt the ticket. */
-
-        /* Plaintext is shorter than the ciphertext due to padding. */
-        decrypted_state = SECITEM_AllocItem(NULL, NULL,
-                                            enc_session_ticket.encrypted_state.len);
-
-#ifndef NO_PKCS11_BYPASS
-        if (ss->opt.bypassPKCS11) {
-            PORT_Assert(aes_key);
-            aes_ctx = (AESContext *)aes_ctx_buf;
-            rv = AES_InitContext(aes_ctx, aes_key,
-                                 sizeof(session_ticket_enc_key), enc_session_ticket.iv,
-                                 NSS_AES_CBC, 0, AES_BLOCK_SIZE);
-            if (rv != SECSuccess) {
-                SSL_DBG(("%d: SSL[%d]: Unable to create AES context.",
-                         SSL_GETPID(), ss->fd));
-                goto no_ticket;
-            }
-
-            rv = AES_Decrypt(aes_ctx, decrypted_state->data,
-                             &decrypted_state->len, decrypted_state->len,
-                             enc_session_ticket.encrypted_state.data,
-                             enc_session_ticket.encrypted_state.len);
+        rv = PK11_CipherOp(aes_ctx_pkcs11, decrypted_state->data,
+                           (int *)&decrypted_state->len, decrypted_state->len,
+                           enc_session_ticket.encrypted_state.data,
+                           enc_session_ticket.encrypted_state.len);
+        PK11_Finalize(aes_ctx_pkcs11);
+        PK11_DestroyContext(aes_ctx_pkcs11, PR_TRUE);
+        if (rv != SECSuccess)
+            goto no_ticket;
+    }
+
+    /* Check padding. */
+    padding_length =
+            (PRUint32)decrypted_state->data[decrypted_state->len - 1];
+    if (padding_length == 0 || padding_length > AES_BLOCK_SIZE)
+        goto no_ticket;
+
+    padding = &decrypted_state->data[decrypted_state->len - padding_length];
+    for (i = 0; i < padding_length; i++, padding++) {
+        if (padding_length != (PRUint32)*padding)
+            goto no_ticket;
+    }
+
+    /* Deserialize session state. */
+    buffer = decrypted_state->data;
+    buffer_len = decrypted_state->len;
+
+    parsed_session_ticket = PORT_ZAlloc(sizeof(SessionTicket));
+    if (parsed_session_ticket == NULL) {
+        rv = SECFailure;
+        goto loser;
+    }
+
+    /* Read ticket_version and reject if the version is wrong */
+    temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
+    if (temp != TLS_EX_SESS_TICKET_VERSION)
+        goto no_ticket;
+
+    parsed_session_ticket->ticket_version = (SSL3ProtocolVersion)temp;
+
+    /* Read SSLVersion. */
+    temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
+    if (temp < 0)
+        goto no_ticket;
+    parsed_session_ticket->ssl_version = (SSL3ProtocolVersion)temp;
+
+    /* Read cipher_suite. */
+    temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
+    if (temp < 0)
+        goto no_ticket;
+    parsed_session_ticket->cipher_suite = (ssl3CipherSuite)temp;
+
+    /* Read compression_method. */
+    temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+    if (temp < 0)
+        goto no_ticket;
+    parsed_session_ticket->compression_method = (SSLCompressionMethod)temp;
+
+    /* Read cipher spec parameters. */
+    temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+    if (temp < 0)
+        goto no_ticket;
+    parsed_session_ticket->authAlgorithm = (SSLSignType)temp;
+    temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
+    if (temp < 0)
+        goto no_ticket;
+    parsed_session_ticket->authKeyBits = (PRUint32)temp;
+    temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+    if (temp < 0)
+        goto no_ticket;
+    parsed_session_ticket->keaType = (SSLKEAType)temp;
+    temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
+    if (temp < 0)
+        goto no_ticket;
+    parsed_session_ticket->keaKeyBits = (PRUint32)temp;
+
+    /* Read wrapped master_secret. */
+    temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+    if (temp < 0)
+        goto no_ticket;
+    parsed_session_ticket->ms_is_wrapped = (PRBool)temp;
+
+    temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+    if (temp < 0)
+        goto no_ticket;
+    parsed_session_ticket->exchKeyType = (SSL3KEAType)temp;
+
+    temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
+    if (temp < 0)
+        goto no_ticket;
+    parsed_session_ticket->msWrapMech = (CK_MECHANISM_TYPE)temp;
+
+    temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
+    if (temp < 0)
+        goto no_ticket;
+    parsed_session_ticket->ms_length = (PRUint16)temp;
+    if (parsed_session_ticket->ms_length == 0 || /* sanity check MS. */
+        parsed_session_ticket->ms_length >
+        sizeof(parsed_session_ticket->master_secret))
+        goto no_ticket;
+
+    /* Allow for the wrapped master secret to be longer. */
+    if (buffer_len < parsed_session_ticket->ms_length)
+        goto no_ticket;
+    PORT_Memcpy(parsed_session_ticket->master_secret, buffer,
+                parsed_session_ticket->ms_length);
+    buffer += parsed_session_ticket->ms_length;
+    buffer_len -= parsed_session_ticket->ms_length;
+
+    /* Read client_identity */
+    temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+    if (temp < 0)
+        goto no_ticket;
+    parsed_session_ticket->client_identity.client_auth_type =
+            (ClientAuthenticationType)temp;
+    switch (parsed_session_ticket->client_identity.client_auth_type) {
+        case CLIENT_AUTH_ANONYMOUS:
+            break;
+        case CLIENT_AUTH_CERTIFICATE:
+            rv = ssl3_ConsumeHandshakeVariable(ss, &cert_item, 3,
+                                               &buffer, &buffer_len);
             if (rv != SECSuccess)
                 goto no_ticket;
-        } else
-#endif
-        {
-            SECItem ivItem;
-            PORT_Assert(aes_key_pkcs11);
-            ivItem.data = enc_session_ticket.iv;
-            ivItem.len = AES_BLOCK_SIZE;
-            aes_ctx_pkcs11 = PK11_CreateContextBySymKey(cipherMech,
-                                                        CKA_DECRYPT, aes_key_pkcs11, &ivItem);
-            if (!aes_ctx_pkcs11) {
-                SSL_DBG(("%d: SSL[%d]: Unable to create AES context.",
-                         SSL_GETPID(), ss->fd));
-                goto no_ticket;
-            }
-
-            rv = PK11_CipherOp(aes_ctx_pkcs11, decrypted_state->data,
-                               (int *)&decrypted_state->len, decrypted_state->len,
-                               enc_session_ticket.encrypted_state.data,
-                               enc_session_ticket.encrypted_state.len);
-            PK11_Finalize(aes_ctx_pkcs11);
-            PK11_DestroyContext(aes_ctx_pkcs11, PR_TRUE);
+            rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->peer_cert,
+                                  &cert_item);
             if (rv != SECSuccess)
                 goto no_ticket;
-        }
-
-        /* Check padding. */
-        padding_length =
-            (PRUint32)decrypted_state->data[decrypted_state->len - 1];
-        if (padding_length == 0 || padding_length > AES_BLOCK_SIZE)
+            break;
+        default:
+            goto no_ticket;
+    }
+    /* Read timestamp. */
+    temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
+    if (temp < 0)
+        goto no_ticket;
+    parsed_session_ticket->timestamp = (PRUint32)temp;
+
+    /* Read server name */
+    nameType =
+            ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+    if (nameType != TLS_STE_NO_SERVER_NAME) {
+        SECItem name_item;
+        rv = ssl3_ConsumeHandshakeVariable(ss, &name_item, 2, &buffer,
+                                           &buffer_len);
+        if (rv != SECSuccess)
+            goto no_ticket;
+        rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->srvName,
+                              &name_item);
+        if (rv != SECSuccess)
             goto no_ticket;
-
-        padding = &decrypted_state->data[decrypted_state->len - padding_length];
-        for (i = 0; i < padding_length; i++, padding++) {
-            if (padding_length != (PRUint32)*padding)
-                goto no_ticket;
-        }
-
-        /* Deserialize session state. */
-        buffer = decrypted_state->data;
-        buffer_len = decrypted_state->len;
-
-        parsed_session_ticket = PORT_ZAlloc(sizeof(SessionTicket));
-        if (parsed_session_ticket == NULL) {
+        parsed_session_ticket->srvName.type = nameType;
+    }
+
+    /* Read extendedMasterSecretUsed */
+    temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+    if (temp < 0)
+        goto no_ticket;
+    PORT_Assert(temp == PR_TRUE || temp == PR_FALSE);
+    parsed_session_ticket->extendedMasterSecretUsed = (PRBool)temp;
+
+    /* Done parsing.  Check that all bytes have been consumed. */
+    if (buffer_len != padding_length)
+        goto no_ticket;
+
+    /* Use the ticket if it has not expired, otherwise free the allocated
+     * memory since the ticket is of no use.
+     */
+    if (parsed_session_ticket->timestamp != 0 &&
+        parsed_session_ticket->timestamp +
+        TLS_EX_SESS_TICKET_LIFETIME_HINT >
+        ssl_Time()) {
+
+        sid = ssl3_NewSessionID(ss, PR_TRUE);
+        if (sid == NULL) {
             rv = SECFailure;
             goto loser;
         }
 
-        /* Read ticket_version and reject if the version is wrong */
-        temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
-        if (temp != TLS_EX_SESS_TICKET_VERSION)
-            goto no_ticket;
-
-        parsed_session_ticket->ticket_version = (SSL3ProtocolVersion)temp;
-
-        /* Read SSLVersion. */
-        temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
-        if (temp < 0)
-            goto no_ticket;
-        parsed_session_ticket->ssl_version = (SSL3ProtocolVersion)temp;
-
-        /* Read cipher_suite. */
-        temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
-        if (temp < 0)
-            goto no_ticket;
-        parsed_session_ticket->cipher_suite = (ssl3CipherSuite)temp;
-
-        /* Read compression_method. */
-        temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-        if (temp < 0)
-            goto no_ticket;
-        parsed_session_ticket->compression_method = (SSLCompressionMethod)temp;
-
-        /* Read cipher spec parameters. */
-        temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-        if (temp < 0)
-            goto no_ticket;
-        parsed_session_ticket->authAlgorithm = (SSLSignType)temp;
-        temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
-        if (temp < 0)
+        /* Copy over parameters. */
+        sid->version = parsed_session_ticket->ssl_version;
+        sid->u.ssl3.cipherSuite = parsed_session_ticket->cipher_suite;
+        sid->u.ssl3.compression = parsed_session_ticket->compression_method;
+        sid->authAlgorithm = parsed_session_ticket->authAlgorithm;
+        sid->authKeyBits = parsed_session_ticket->authKeyBits;
+        sid->keaType = parsed_session_ticket->keaType;
+        sid->keaKeyBits = parsed_session_ticket->keaKeyBits;
+       if (SECITEM_CopyItem(NULL, &sid->u.ssl3.locked.sessionTicket.ticket,
+                            &extension_data) != SECSuccess)
+           goto no_ticket;
+
+       /* Copy master secret. */
+#ifndef NO_PKCS11_BYPASS
+        if (ss->opt.bypassPKCS11 &&
+            parsed_session_ticket->ms_is_wrapped)
             goto no_ticket;
-        parsed_session_ticket->authKeyBits = (PRUint32)temp;
-        temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-        if (temp < 0)
-            goto no_ticket;
-        parsed_session_ticket->keaType = (SSLKEAType)temp;
-        temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
-        if (temp < 0)
-            goto no_ticket;
-        parsed_session_ticket->keaKeyBits = (PRUint32)temp;
-
-        /* Read wrapped master_secret. */
-        temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-        if (temp < 0)
-            goto no_ticket;
-        parsed_session_ticket->ms_is_wrapped = (PRBool)temp;
-
-        temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-        if (temp < 0)
-            goto no_ticket;
-        parsed_session_ticket->exchKeyType = (SSL3KEAType)temp;
-
-        temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
-        if (temp < 0)
-            goto no_ticket;
-        parsed_session_ticket->msWrapMech = (CK_MECHANISM_TYPE)temp;
-
-        temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
-        if (temp < 0)
-            goto no_ticket;
-        parsed_session_ticket->ms_length = (PRUint16)temp;
-        if (parsed_session_ticket->ms_length == 0 || /* sanity check MS. */
-            parsed_session_ticket->ms_length >
-                sizeof(parsed_session_ticket->master_secret))
-            goto no_ticket;
-
-        /* Allow for the wrapped master secret to be longer. */
-        if (buffer_len < parsed_session_ticket->ms_length)
+#endif
+        if (parsed_session_ticket->ms_length >
+            sizeof(sid->u.ssl3.keys.wrapped_master_secret))
             goto no_ticket;
-        PORT_Memcpy(parsed_session_ticket->master_secret, buffer,
+        PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret,
+                    parsed_session_ticket->master_secret,
                     parsed_session_ticket->ms_length);
-        buffer += parsed_session_ticket->ms_length;
-        buffer_len -= parsed_session_ticket->ms_length;
-
-        /* Read client_identity */
-        temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-        if (temp < 0)
-            goto no_ticket;
-        parsed_session_ticket->client_identity.client_auth_type =
-            (ClientAuthenticationType)temp;
-        switch (parsed_session_ticket->client_identity.client_auth_type) {
-            case CLIENT_AUTH_ANONYMOUS:
-                break;
-            case CLIENT_AUTH_CERTIFICATE:
-                rv = ssl3_ConsumeHandshakeVariable(ss, &cert_item, 3,
-                                                   &buffer, &buffer_len);
-                if (rv != SECSuccess)
-                    goto no_ticket;
-                rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->peer_cert,
-                                      &cert_item);
-                if (rv != SECSuccess)
-                    goto no_ticket;
-                break;
-            default:
-                goto no_ticket;
-        }
-        /* Read timestamp. */
-        temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
-        if (temp < 0)
-            goto no_ticket;
-        parsed_session_ticket->timestamp = (PRUint32)temp;
-
-        /* Read server name */
-        nameType =
-            ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-        if (nameType != TLS_STE_NO_SERVER_NAME) {
-            SECItem name_item;
-            rv = ssl3_ConsumeHandshakeVariable(ss, &name_item, 2, &buffer,
-                                               &buffer_len);
-            if (rv != SECSuccess)
-                goto no_ticket;
-            rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->srvName,
-                                  &name_item);
-            if (rv != SECSuccess)
-                goto no_ticket;
-            parsed_session_ticket->srvName.type = nameType;
-        }
-
-        /* Read extendedMasterSecretUsed */
-        temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-        if (temp < 0)
-            goto no_ticket;
-        PORT_Assert(temp == PR_TRUE || temp == PR_FALSE);
-        parsed_session_ticket->extendedMasterSecretUsed = (PRBool)temp;
-
-        /* Done parsing.  Check that all bytes have been consumed. */
-        if (buffer_len != padding_length)
-            goto no_ticket;
-
-        /* Use the ticket if it has not expired, otherwise free the allocated
-         * memory since the ticket is of no use.
-         */
-        if (parsed_session_ticket->timestamp != 0 &&
-            parsed_session_ticket->timestamp +
-                    TLS_EX_SESS_TICKET_LIFETIME_HINT >
-                ssl_Time()) {
-
-            sid = ssl3_NewSessionID(ss, PR_TRUE);
-            if (sid == NULL) {
+        sid->u.ssl3.keys.wrapped_master_secret_len =
+                parsed_session_ticket->ms_length;
+        sid->u.ssl3.exchKeyType = parsed_session_ticket->exchKeyType;
+        sid->u.ssl3.masterWrapMech = parsed_session_ticket->msWrapMech;
+        sid->u.ssl3.keys.msIsWrapped =
+                parsed_session_ticket->ms_is_wrapped;
+        sid->u.ssl3.masterValid = PR_TRUE;
+        sid->u.ssl3.keys.resumable = PR_TRUE;
+        sid->u.ssl3.keys.extendedMasterSecretUsed = parsed_session_ticket->extendedMasterSecretUsed;
+
+        /* Copy over client cert from session ticket if there is one. */
+        if (parsed_session_ticket->peer_cert.data != NULL) {
+            if (sid->peerCert != NULL)
+                CERT_DestroyCertificate(sid->peerCert);
+            sid->peerCert = CERT_NewTempCertificate(ss->dbHandle,
+                                                    &parsed_session_ticket->peer_cert, NULL, PR_FALSE, PR_TRUE);
+            if (sid->peerCert == NULL) {
                 rv = SECFailure;
                 goto loser;
             }
-
-            /* Copy over parameters. */
-            sid->version = parsed_session_ticket->ssl_version;
-            sid->u.ssl3.cipherSuite = parsed_session_ticket->cipher_suite;
-            sid->u.ssl3.compression = parsed_session_ticket->compression_method;
-            sid->authAlgorithm = parsed_session_ticket->authAlgorithm;
-            sid->authKeyBits = parsed_session_ticket->authKeyBits;
-            sid->keaType = parsed_session_ticket->keaType;
-            sid->keaKeyBits = parsed_session_ticket->keaKeyBits;
-
-/* Copy master secret. */
-#ifndef NO_PKCS11_BYPASS
-            if (ss->opt.bypassPKCS11 &&
-                parsed_session_ticket->ms_is_wrapped)
-                goto no_ticket;
-#endif
-            if (parsed_session_ticket->ms_length >
-                sizeof(sid->u.ssl3.keys.wrapped_master_secret))
-                goto no_ticket;
-            PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret,
-                        parsed_session_ticket->master_secret,
-                        parsed_session_ticket->ms_length);
-            sid->u.ssl3.keys.wrapped_master_secret_len =
-                parsed_session_ticket->ms_length;
-            sid->u.ssl3.exchKeyType = parsed_session_ticket->exchKeyType;
-            sid->u.ssl3.masterWrapMech = parsed_session_ticket->msWrapMech;
-            sid->u.ssl3.keys.msIsWrapped =
-                parsed_session_ticket->ms_is_wrapped;
-            sid->u.ssl3.masterValid = PR_TRUE;
-            sid->u.ssl3.keys.resumable = PR_TRUE;
-            sid->u.ssl3.keys.extendedMasterSecretUsed = parsed_session_ticket->extendedMasterSecretUsed;
-
-            /* Copy over client cert from session ticket if there is one. */
-            if (parsed_session_ticket->peer_cert.data != NULL) {
-                if (sid->peerCert != NULL)
-                    CERT_DestroyCertificate(sid->peerCert);
-                sid->peerCert = CERT_NewTempCertificate(ss->dbHandle,
-                                                        &parsed_session_ticket->peer_cert, NULL, PR_FALSE, PR_TRUE);
-                if (sid->peerCert == NULL) {
-                    rv = SECFailure;
-                    goto loser;
-                }
-            }
-            if (parsed_session_ticket->srvName.data != NULL) {
-                sid->u.ssl3.srvName = parsed_session_ticket->srvName;
-            }
-            ss->statelessResume = PR_TRUE;
-            ss->sec.ci.sid = sid;
         }
+        if (parsed_session_ticket->srvName.data != NULL) {
+            sid->u.ssl3.srvName = parsed_session_ticket->srvName;
+        }
+        ss->statelessResume = PR_TRUE;
+        ss->sec.ci.sid = sid;
     }
 
     if (0) {
-    no_ticket:
+  no_ticket:
         SSL_DBG(("%d: SSL[%d]: Session ticket parsing failed.",
                  SSL_GETPID(), ss->fd));
         ssl3stats = SSL_GetStatistics();
         SSL_AtomicIncrementLong(&ssl3stats->hch_sid_ticket_parse_failures);
     }
     rv = SECSuccess;
 
 loser:
@@ -1977,16 +1978,46 @@ loser:
             SECITEM_FreeItem(&parsed_session_ticket->peer_cert, PR_FALSE);
         }
         PORT_ZFree(parsed_session_ticket, sizeof(SessionTicket));
     }
 
     return rv;
 }
 
+SECStatus
+ssl3_ServerHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type,
+                                  SECItem *data)
+{
+
+    /* Ignore the SessionTicket extension if processing is disabled. */
+    if (!ss->opt.enableSessionTickets) {
+        return SECSuccess;
+    }
+
+    /* If we are doing TLS 1.3, then ignore this. */
+    if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+        return SECSuccess;
+    }
+
+    /* Keep track of negotiated extensions. */
+    ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
+
+    /* Parse the received ticket sent in by the client.  We are
+     * lenient about some parse errors, falling back to a fullshake
+     * instead of terminating the current connection.
+     */
+    if (data->len == 0) {
+        ss->xtnData.emptySessionTicket = PR_TRUE;
+        return SECSuccess;
+    }
+
+    return ssl3_ProcessSessionTicketCommon(ss, data);
+}
+
 /*
  * Read bytes.  Using this function means the SECItem structure
  * cannot be freed.  The caller is expected to call this function
  * on a shallow copy of the structure.
  */
 static SECStatus
 ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf, PRUint32 bytes)
 {
@@ -3235,8 +3266,225 @@ tls13_ServerSendKeyShareXtn(sslSocket *s
             goto loser;
     }
 
     return extension_length;
 
 loser:
     return -1;
 }
+
+/* Called by clients.
+ *
+ *   opaque psk_identity<0..2^16-1>;
+ *
+ *   struct {
+ *        select (Role) {
+ *            case client:
+ *                psk_identity identities<2..2^16-1>;
+ *
+ *            case server:
+ *                psk_identity identity;
+ *        }
+ *   } PreSharedKeyExtension;
+ *
+ * Presently the only way to get a PSK is by resumption, so this is
+ * really a ticket label and there wll be at most one.
+ */
+static PRInt32
+tls13_ClientSendPreSharedKeyXtn(sslSocket * ss,
+                                PRBool      append,
+                                PRUint32    maxBytes)
+{
+    PRInt32 extension_length;
+    NewSessionTicket *session_ticket = NULL;
+    sslSessionID *sid = ss->sec.ci.sid;
+
+    if (sid->cached == never_cached ||
+        sid->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+        return 0;
+    }
+
+    /* The caller must be holding sid->u.ssl3.lock for reading. We cannot
+     * just acquire and release the lock within this function because the
+     * caller will call this function twice, and we need the inputs to be
+     * consistent between the two calls. Note that currently the caller
+     * will only be holding the lock when we are the client and when we're
+     * attempting to resume an existing session.
+     */
+    session_ticket = &sid->u.ssl3.locked.sessionTicket;
+    PORT_Assert(session_ticket && session_ticket->ticket.data);
+
+    /* In our first pass through, set the ticket to be verified if
+     * it is still valid. */
+    if (!append && (session_ticket->ticket_lifetime_hint == 0 ||
+        (session_ticket->ticket_lifetime_hint +
+         session_ticket->received_timestamp > ssl_Time()))) {
+        ss->xtnData.ticketTimestampVerified = PR_TRUE;
+    }
+
+    /* Ticket out of date so don't send PSK. */
+    if (!ss->xtnData.ticketTimestampVerified) {
+        return 0;
+    }
+
+    /* Type + length + vector length + identity length + ticket. */
+    extension_length = 2 + 2 + 2 + 2 +
+            session_ticket->ticket.len;
+
+    if (maxBytes < (PRUint32)extension_length) {
+        PORT_Assert(0);
+        return 0;
+    }
+
+    if (append) {
+        SECStatus rv;
+        /* extension_type */
+        rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_pre_shared_key_xtn, 2);
+        if (rv != SECSuccess)
+            goto loser;
+        rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2);
+        if (rv != SECSuccess)
+            goto loser;
+        rv = ssl3_AppendHandshakeNumber(ss, session_ticket->ticket.len + 2, 2);
+        if (rv != SECSuccess)
+            goto loser;
+        rv = ssl3_AppendHandshakeVariable(ss, session_ticket->ticket.data,
+                                          session_ticket->ticket.len, 2);
+        PRINT_BUF(50, (ss, "Sending PreSharedKey value",
+                       session_ticket->ticket.data,
+                       session_ticket->ticket.len));
+        ss->xtnData.ticketTimestampVerified = PR_FALSE;
+        ss->xtnData.sentSessionTicketInClientHello = PR_TRUE;
+        if (rv != SECSuccess)
+            goto loser;
+
+        ss->xtnData.advertised[ss->xtnData.numAdvertised++] =
+                ssl_tls13_pre_shared_key_xtn;
+    }
+    return extension_length;
+
+ loser:
+    ss->xtnData.ticketTimestampVerified = PR_FALSE;
+    return -1;
+}
+
+/* Handle a TLS 1.3 PreSharedKey Extension. We only accept PSKs
+ * that contain session tickets. */
+static SECStatus
+tls13_ServerHandlePreSharedKeyXtn(sslSocket *ss, PRUint16 ex_type,
+                                  SECItem *data)
+{
+    SECItem label;
+    PRInt32 len;
+    PRBool first = PR_TRUE;
+    SECStatus rv;
+
+    /* If we are doing < TLS 1.3, then ignore this. */
+    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+        return SECSuccess;
+    }
+
+    len = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
+    if (len < 0)
+        return SECFailure;
+
+    if (len != data->len) {
+        PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
+        return SECFailure;
+    }
+
+    while (data->len) {
+        rv = ssl3_ConsumeHandshakeVariable(ss, &label, 2,
+                                           &data->data, &data->len);
+        if (rv != SECSuccess)
+            return rv;
+
+        if (first) {
+            first = PR_FALSE; /* Continue to read through the extension to check
+                               * the format. */
+
+            PRINT_BUF(50, (ss, "Handling PreSharedKey value",
+                           label.data, label.len));
+
+            rv = ssl3_ProcessSessionTicketCommon(ss, &label);
+            /* This only happens if we have an internal error, not
+             * a malformed ticket. Bogus tickets just don't resume
+             * and return SECSuccess. */
+            if (rv != SECSuccess)
+                return rv;
+        }
+    }
+
+    /* Keep track of negotiated extensions. Note that this does not
+     * mean we are resuming. */
+    ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
+
+    return SECSuccess;
+}
+
+PRInt32
+tls13_ServerSendPreSharedKeyXtn(sslSocket * ss,
+                                PRBool      append,
+                                PRUint32    maxBytes)
+{
+    SECItem *session_ticket =
+            &ss->sec.ci.sid->u.ssl3.locked.sessionTicket.ticket;
+    PRInt32 extension_length =
+            2 + 2 + 2 + session_ticket->len; /* type + len +
+                                                inner_len + data */
+    SECStatus rv;
+
+    PORT_Assert(session_ticket->len);
+
+    if (append) {
+        rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_pre_shared_key_xtn, 2);
+        if (rv != SECSuccess)
+            return -1;
+
+        rv = ssl3_AppendHandshakeNumber(ss, session_ticket->len + 2, 2);
+        if (rv != SECSuccess)
+            return -1;
+
+        rv = ssl3_AppendHandshakeVariable(ss, session_ticket->data,
+                                          session_ticket->len, 2);
+        if (rv != SECSuccess)
+            return -1;
+    }
+
+    return extension_length;
+}
+
+/* Handle a TLS 1.3 PreSharedKey Extension. We only accept PSKs
+ * that contain session tickets. */
+static SECStatus
+tls13_ClientHandlePreSharedKeyXtn(sslSocket *ss, PRUint16 ex_type,
+                                  SECItem *data)
+{
+    PRInt32 len;
+
+    /* If we are doing < TLS 1.3, then ignore this. */
+    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+        return SECSuccess;
+    }
+
+    len = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
+    if (len < 0)
+        return SECFailure;
+
+    if (len != data->len) {
+        PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
+        return SECFailure;
+    }
+
+    /* Just check for equality since we only sent one PSK label. */
+    if (SECITEM_CompareItem(
+            &ss->sec.ci.sid->u.ssl3.locked.sessionTicket.ticket,
+                             data) != SECEqual) {
+        PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
+        return SECFailure;
+    }
+
+    /* Keep track of negotiated extensions. */
+    ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
+
+    return SECSuccess;
+}
--- a/lib/ssl/ssl3prot.h
+++ b/lib/ssl/ssl3prot.h
@@ -195,17 +195,18 @@ typedef enum {
     kea_dhe_rsa_export,
     kea_dh_anon,
     kea_dh_anon_export,
     kea_rsa_fips,
     kea_ecdh_ecdsa,
     kea_ecdhe_ecdsa,
     kea_ecdh_rsa,
     kea_ecdhe_rsa,
-    kea_ecdh_anon
+    kea_ecdh_anon,
+    kea_ecdhe_psk,
 } SSL3KeyExchangeAlgorithm;
 
 typedef struct {
     SECItem modulus;
     SECItem exponent;
 } SSL3ServerRSAParams;
 
 typedef struct {
--- a/lib/ssl/sslenum.c
+++ b/lib/ssl/sslenum.c
@@ -29,29 +29,35 @@
  *      Camellia also has wide international support across standards
  *      organizations. SEED is only recommended by the Korean government. 3DES
  *      only provides 112 bits of security. RC4 is now deprecated or forbidden
  *      by many standards organizations.
  *    * Within symmetric algorithm sections, order by message authentication
  *      algorithm: GCM, then HMAC-SHA1, then HMAC-SHA256, then HMAC-MD5.
  *    * Within message authentication algorithm sections, order by asymmetric
  *      signature algorithm: ECDSA, then RSA, then DSS.
+ *    * As a special case, the PSK ciphers, which are only enabled when
+ *      TLS 1.3 PSK-resumption is in use, come first.
  *
  * Exception: Because some servers ignore the high-order byte of the cipher
  * suite ID, we must be careful about adding cipher suites with IDs larger
  * than 0x00ff; see bug 946147. For these broken servers, the first four cipher
  * suites, with the MSB zeroed, look like:
  *      TLS_KRB5_EXPORT_WITH_RC4_40_MD5 { 0x00,0x2B }
  *      TLS_RSA_WITH_AES_128_CBC_SHA { 0x00,0x2F }
  *      TLS_RSA_WITH_3DES_EDE_CBC_SHA { 0x00,0x0A }
  *      TLS_RSA_WITH_DES_CBC_SHA { 0x00,0x09 }
  * The broken server only supports the third and fourth ones and will select
  * the third one.
  */
 const PRUint16 SSL_ImplementedCiphers[] = {
+    /* ECDHE-PSK from [draft-mattsson-tls-ecdhe-psk-aead]. */
+    TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256,
+
+
 #ifndef NSS_DISABLE_ECC
     TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
     TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
     TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
     TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
     /* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA must appear before
      * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA to work around bug 946147.
      */
--- a/lib/ssl/sslerr.h
+++ b/lib/ssl/sslerr.h
@@ -225,15 +225,16 @@ typedef enum {
     SSL_ERROR_RX_MALFORMED_DHE_KEY_SHARE    = (SSL_ERROR_BASE + 141),
 
     SSL_ERROR_RX_UNEXPECTED_ENCRYPTED_EXTENSIONS = (SSL_ERROR_BASE + 142),
     SSL_ERROR_MISSING_EXTENSION_ALERT       = (SSL_ERROR_BASE + 143),
 
     SSL_ERROR_KEY_EXCHANGE_FAILURE          = (SSL_ERROR_BASE + 144),
     SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION = (SSL_ERROR_BASE + 145),
     SSL_ERROR_RX_MALFORMED_ENCRYPTED_EXTENSIONS = (SSL_ERROR_BASE + 146),
+    SSL_ERROR_MALFORMED_PRE_SHARED_KEY = (SSL_ERROR_BASE + 147),
     SSL_ERROR_END_OF_LIST   /* let the c compiler determine the value of this. */
 } SSLErrorCodes;
 #endif /* NO_SECURITY_ERROR_ENUM */
 
 /* clang-format on */
 
 #endif /* __SSL_ERR_H_ */
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -271,17 +271,17 @@ typedef struct {
     ssl3CipherSuite cipher_suite;
     PRUint8 policy;
     unsigned char enabled : 1;
     unsigned char isPresent : 1;
 #endif
 } ssl3CipherSuiteCfg;
 
 #ifndef NSS_DISABLE_ECC
-#define ssl_V3_SUITES_IMPLEMENTED 67
+#define ssl_V3_SUITES_IMPLEMENTED 68
 #else
 #define ssl_V3_SUITES_IMPLEMENTED 41
 #endif /* NSS_DISABLE_ECC */
 
 #define MAX_DTLS_SRTP_CIPHER_SUITES 4
 
 /* MAX_SIGNATURE_ALGORITHMS allows for a large number of combinations of
  * SSLSignType and SSLHashType, but not all combinations (specifically, this
@@ -944,16 +944,18 @@ typedef struct SSL3HandshakeStateStr {
     PK11SymKey *trafficSecret;         /* The source key to use to generate
                                         * traffic keys */
     PK11SymKey *clientFinishedSecret;  /* Used for client Finished */
     PK11SymKey *serverFinishedSecret;  /* Used for server Finished */
     unsigned char certReqContext[255]; /* Ties CertificateRequest
                                         * to Certificate */
     PRUint8 certReqContextLen;         /* Length of the context
                                         * cannot be greater than 255. */
+    ssl3CipherSuite origCipherSuite;   /* The cipher suite from the original
+                                        * connection if we are resuming. */
 } SSL3HandshakeState;
 
 /*
 ** This is the "ssl3" struct, as in "ss->ssl3".
 ** note:
 ** usually,   crSpec == cwSpec and prSpec == pwSpec.
 ** Sometimes, crSpec == pwSpec and prSpec == cwSpec.
 ** But there are never more than 2 actual specs.
@@ -1808,16 +1810,17 @@ extern void ssl3_SetSIDSessionTicket(ssl
                                      /*in/out*/ NewSessionTicket *session_ticket);
 extern SECStatus ssl3_SendNewSessionTicket(sslSocket *ss);
 extern PRBool ssl_GetSessionTicketKeys(unsigned char *keyName,
                                        unsigned char *encKey, unsigned char *macKey);
 extern PRBool ssl_GetSessionTicketKeysPKCS11(SECKEYPrivateKey *svrPrivKey,
                                              SECKEYPublicKey *svrPubKey, void *pwArg,
                                              unsigned char *keyName, PK11SymKey **aesKey,
                                              PK11SymKey **macKey);
+extern SECStatus ssl3_SessionTicketShutdown(void *appData, void *nssData);
 
 /* Tell clients to consider tickets valid for this long. */
 #define TLS_EX_SESS_TICKET_LIFETIME_HINT (2 * 24 * 60 * 60) /* 2 days */
 #define TLS_EX_SESS_TICKET_VERSION (0x0101)
 
 extern SECStatus ssl3_ValidateNextProtoNego(const unsigned char *data,
                                             unsigned int length);
 
@@ -1900,16 +1903,18 @@ dtls_TLSVersionToDTLSVersion(SSL3Protoco
 extern SSL3ProtocolVersion
 dtls_DTLSVersionToTLSVersion(SSL3ProtocolVersion dtlsv);
 extern PRBool dtls_IsRelevant(sslSocket *ss, const ssl3CipherSpec *crSpec,
                               const SSL3Ciphertext *cText, PRUint64 *seqNum);
 extern SECStatus dtls_MaybeRetransmitHandshake(sslSocket *ss,
                                                const SSL3Ciphertext *cText);
 
 CK_MECHANISM_TYPE ssl3_Alg2Mech(SSLCipherAlgorithm calg);
+SECStatus ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites);
+SECStatus ssl3_ServerCallSNICallback(sslSocket *ss);
 SECStatus ssl3_SetupPendingCipherSpec(sslSocket *ss);
 SECStatus ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags);
 SECStatus ssl3_SendCertificate(sslSocket *ss);
 SECStatus ssl3_CompleteHandleCertificate(sslSocket *ss,
                                          SSL3Opaque *b, PRUint32 length);
 SECStatus ssl3_SendEmptyCertificate(sslSocket *ss);
 SECStatus ssl3_SendCertificateStatus(sslSocket *ss);
 SECStatus ssl3_CompleteHandleCertificateStatus(sslSocket *ss, SSL3Opaque *b,
@@ -1937,16 +1942,29 @@ PRInt32 tls13_ServerSendKeyShareXtn(sslS
                                     PRUint32 maxBytes);
 #ifndef NSS_DISABLE_ECC
 SECStatus ssl3_CreateECDHEphemeralKeyPair(ECName ec_curve,
                                           ssl3KeyPair **keyPair);
 PK11SymKey *tls13_ComputeECDHSharedKey(sslSocket *ss,
                                        SECKEYPrivateKey *myPrivKey,
                                        SECKEYPublicKey *peerKey);
 #endif
+SECStatus ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags);
+PK11SymKey *ssl3_GetWrappingKey(sslSocket *ss,
+                                PK11SlotInfo *masterSecretSlot,
+                                SSL3KEAType exchKeyType,
+                                CK_MECHANISM_TYPE masterWrapMech,
+                                void *pwArg);
+PRInt32 tls13_ServerSendPreSharedKeyXtn(sslSocket * ss,
+                                        PRBool      append,
+                                        PRUint32    maxBytes);
+PRBool ssl3_ClientExtensionAdvertised(sslSocket *ss, PRUint16 ex_type);
+SECStatus ssl3_FillInCachedSID(sslSocket *ss, sslSessionID *sid,
+                               SSL3KEAType effectiveExchKeyType);
+const ssl3CipherSuiteDef *ssl_LookupCipherSuiteDef(ssl3CipherSuite suite);
 
 /* Pull in TLS 1.3 functions */
 #include "tls13con.h"
 
 /********************** misc calls *********************/
 
 #ifdef DEBUG
 extern void ssl3_CheckCipherSuiteOrderConsistency();
--- a/lib/ssl/sslinfo.c
+++ b/lib/ssl/sslinfo.c
@@ -51,17 +51,24 @@ SSL_GetChannelInfo(PRFileDesc *fd, SSLCh
         inf.authKeyBits = ss->sec.authKeyBits;
         inf.keaKeyBits = ss->sec.keaKeyBits;
         if (ss->ssl3.initialized) {
             ssl_GetSpecReadLock(ss);
             /* XXX  The cipher suite should be in the specs and this
              * function should get it from cwSpec rather than from the "hs".
              * See bug 275744 comment 69 and bug 766137.
              */
-            inf.cipherSuite = ss->ssl3.hs.cipher_suite;
+            /* For TLS 1.3, we return the cipher suite of the original
+             * connection if there was one rather than the PSK cipher
+             * suite. This matches the original interface for resumption
+             * and is safe because we only enable the corresponding PSK
+             * cipher suite.
+             */
+            inf.cipherSuite = ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 ?
+                    ss->ssl3.hs.origCipherSuite : ss->ssl3.hs.cipher_suite;
             inf.compressionMethod = ss->ssl3.cwSpec->compression_method;
             ssl_ReleaseSpecReadLock(ss);
             inf.compressionMethodName =
                 ssl_GetCompressionMethodName(inf.compressionMethod);
         }
         if (sid) {
             unsigned int sidLen;
 
@@ -109,35 +116,44 @@ SSL_GetPreliminaryChannelInfo(PRFileDesc
         return SECFailure;
     }
 
     memset(&inf, 0, sizeof(inf));
     inf.length = PR_MIN(sizeof(inf), len);
 
     inf.valuesSet = ss->ssl3.hs.preliminaryInfo;
     inf.protocolVersion = ss->version;
-    inf.cipherSuite = ss->ssl3.hs.cipher_suite;
+    /* For TLS 1.3, we return the cipher suite of the original
+     * connection if there was one rather than the PSK cipher
+     * suite. This matches the original interface for resumption
+     * and is safe because we only enable the corresponding PSK
+     * cipher suite.
+     */
+    inf.cipherSuite = ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 ?
+            ss->ssl3.hs.origCipherSuite : ss->ssl3.hs.cipher_suite;
 
     memcpy(info, &inf, inf.length);
     return SECSuccess;
 }
 
 #define CS(x) x, #x
 #define CK(x) x | 0xff00, #x
 
 #define S_DSA "DSA", ssl_auth_dsa
 #define S_RSA "RSA", ssl_auth_rsa
 #define S_KEA "KEA", ssl_auth_kea
 #define S_ECDSA "ECDSA", ssl_auth_ecdsa
+#define S_PSK   "PSK", ssl_auth_psk
 
 #define K_DHE "DHE", kt_dh
 #define K_RSA "RSA", kt_rsa
 #define K_KEA "KEA", kt_kea
 #define K_ECDH "ECDH", kt_ecdh
 #define K_ECDHE "ECDHE", kt_ecdh
+#define K_ECDHE_PSK "ECDHE-PSK", kt_ecdh
 
 #define C_SEED "SEED", calg_seed
 #define C_CAMELLIA "CAMELLIA", calg_camellia
 #define C_AES "AES", calg_aes
 #define C_RC4 "RC4", calg_rc4
 #define C_RC2 "RC2", calg_rc2
 #define C_DES "DES", calg_des
 #define C_3DES "3DES", calg_3des
@@ -210,16 +226,17 @@ static const SSLCipherSuiteInfo suiteInf
     {0,CS(TLS_RSA_WITH_NULL_SHA256),              S_RSA, K_RSA, C_NULL,B_0,   M_SHA256, 0, 1, 0 },
     {0,CS(TLS_RSA_WITH_NULL_SHA),                 S_RSA, K_RSA, C_NULL,B_0,   M_SHA, 0, 1, 0 },
     {0,CS(TLS_RSA_WITH_NULL_MD5),                 S_RSA, K_RSA, C_NULL,B_0,   M_MD5, 0, 1, 0 },
 
 #ifndef NSS_DISABLE_ECC
     /* ECC cipher suites */
     {0,CS(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_ECDHE, C_AESGCM, B_128, M_AEAD_128, 1, 0, 0 },
     {0,CS(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256), S_ECDSA, K_ECDHE, C_AESGCM, B_128, M_AEAD_128, 1, 0, 0 },
+    {0,CS(TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256), S_PSK,   K_ECDHE_PSK, C_AESGCM, B_128, M_AEAD_128, 1, 0, 0 },
 
     {0,CS(TLS_ECDH_ECDSA_WITH_NULL_SHA),          S_ECDSA, K_ECDH, C_NULL, B_0, M_SHA, 0, 0, 0 },
     {0,CS(TLS_ECDH_ECDSA_WITH_RC4_128_SHA),       S_ECDSA, K_ECDH, C_RC4, B_128, M_SHA, 0, 0, 0 },
     {0,CS(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA),  S_ECDSA, K_ECDH, C_3DES, B_3DES, M_SHA, 1, 0, 0 },
     {0,CS(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA),   S_ECDSA, K_ECDH, C_AES, B_128, M_SHA, 1, 0, 0 },
     {0,CS(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA),   S_ECDSA, K_ECDH, C_AES, B_256, M_SHA, 1, 0, 0 },
 
     {0,CS(TLS_ECDHE_ECDSA_WITH_NULL_SHA),         S_ECDSA, K_ECDHE, C_NULL, B_0, M_SHA, 0, 0, 0 },
--- a/lib/ssl/sslproto.h
+++ b/lib/ssl/sslproto.h
@@ -218,16 +218,19 @@
 #define TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256  0xC02D
 #define TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256   0xC02F
 #define TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256    0xC031
 
 #define TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256   0xCCA8
 #define TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA9
 #define TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256     0xCCAA
 
+/* Experimental PSK support for [draft-mattsson-tls-ecdhe-psk-aead] */
+#define TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256   0xD001
+
 /* Netscape "experimental" cipher suites. */
 #define SSL_RSA_OLDFIPS_WITH_3DES_EDE_CBC_SHA   0xffe0
 #define SSL_RSA_OLDFIPS_WITH_DES_CBC_SHA        0xffe1
 
 /* New non-experimental openly spec'ed versions of those cipher suites. */
 #define SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA      0xfeff
 #define SSL_RSA_FIPS_WITH_DES_CBC_SHA           0xfefe
 
--- a/lib/ssl/sslt.h
+++ b/lib/ssl/sslt.h
@@ -57,17 +57,18 @@ typedef enum {
 #define kt_kea_size ssl_kea_size
 
 /* Values of this enum match the SignatureAlgorithm enum from
  * https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
 typedef enum {
     ssl_sign_null = 0, /* "anonymous" in TLS */
     ssl_sign_rsa = 1,
     ssl_sign_dsa = 2,
-    ssl_sign_ecdsa = 3
+    ssl_sign_ecdsa = 3,
+    ssl_sign_psk = 4
 } SSLSignType;
 
 /* Values of this enum match the HashAlgorithm enum from
  * https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
 typedef enum {
     /* ssl_hash_none is used internally to mean the pre-1.2 combination of MD5
      * and SHA1. The other values are only used in TLS 1.2. */
     ssl_hash_none = 0,
@@ -84,17 +85,18 @@ typedef struct SSLSignatureAndHashAlgStr
     SSLSignType sigAlg;
 } SSLSignatureAndHashAlg;
 
 typedef enum {
     ssl_auth_null = 0,
     ssl_auth_rsa = 1,
     ssl_auth_dsa = 2,
     ssl_auth_kea = 3,
-    ssl_auth_ecdsa = 4
+    ssl_auth_ecdsa = 4,
+    ssl_auth_psk = 5         /* Used for both PSK and (EC)DHE-PSK */
 } SSLAuthType;
 
 typedef enum {
     ssl_calg_null = 0,
     ssl_calg_rc4 = 1,
     ssl_calg_rc2 = 2,
     ssl_calg_des = 3,
     ssl_calg_3des = 4,
@@ -243,22 +245,23 @@ typedef enum {
     ssl_use_srtp_xtn = 14,
     ssl_app_layer_protocol_xtn = 16,
     /* signed_certificate_timestamp extension, RFC 6962 */
     ssl_signed_cert_timestamp_xtn = 18,
     ssl_padding_xtn = 21,
     ssl_extended_master_secret_xtn = 23,
     ssl_session_ticket_xtn = 35,
     ssl_tls13_key_share_xtn = 40, /* unofficial TODO(ekr) */
+    ssl_tls13_pre_shared_key_xtn = 41, /* unofficial TODO(ekr) */
     ssl_next_proto_nego_xtn = 13172,
     ssl_renegotiation_info_xtn = 0xff01,
     ssl_tls13_draft_version_xtn = 0xff02 /* experimental number */
 } SSLExtensionType;
 
-#define SSL_MAX_EXTENSIONS 14 /* doesn't include ssl_padding_xtn. */
+#define SSL_MAX_EXTENSIONS 15 /* doesn't include ssl_padding_xtn. */
 
 typedef enum {
     ssl_dhe_group_none = 0,
     ssl_ff_dhe_2048_group = 1,
     ssl_ff_dhe_3072_group = 2,
     ssl_ff_dhe_4096_group = 3,
     ssl_ff_dhe_6144_group = 4,
     ssl_ff_dhe_8192_group = 5,
--- a/lib/ssl/tls13con.c
+++ b/lib/ssl/tls13con.c
@@ -7,16 +7,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "stdarg.h"
 #include "cert.h"
 #include "ssl.h"
 #include "keyhi.h"
 #include "pk11func.h"
 #include "secitem.h"
+#include "secmod.h"
 #include "sslimpl.h"
 #include "sslproto.h"
 #include "sslerr.h"
 #include "tls13hkdf.h"
 #include "tls13con.h"
 
 typedef enum {
     TrafficKeyEarlyData,
@@ -60,16 +61,17 @@ static SECStatus tls13_HandleCertificate
 static SECStatus tls13_HkdfExtractSharedKey(sslSocket *ss, PK11SymKey *key,
                                             SharedSecretType keyType);
 static SECStatus tls13_SendFinished(sslSocket *ss);
 static SECStatus tls13_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
                                       const SSL3Hashes *hashes);
 static SECStatus tls13_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b,
                                               PRUint32 length);
 static SECStatus tls13_ComputeSecrets1(sslSocket *ss);
+static SECStatus tls13_ComputeSecrets2(sslSocket *ss);
 static SECStatus tls13_ComputeFinished(
     sslSocket *ss, const SSL3Hashes *hashes,
     PRBool sending,
     PRUint8 *output, unsigned int *outputLen,
     unsigned int maxOutputLen);
 static SECStatus tls13_SendClientSecondRound(sslSocket *ss);
 static SECStatus tls13_FinishHandshake(sslSocket *ss);
 
@@ -90,16 +92,20 @@ const char kHkdfPurposeServerWriteKey[] 
 const char kHkdfPurposeClientWriteIv[] = "client write iv";
 const char kHkdfPurposeServerWriteIv[] = "server write iv";
 const char kClientFinishedLabel[] = "client finished";
 const char kServerFinishedLabel[] = "server finished";
 
 const SSL3ProtocolVersion kTlsRecordVersion = SSL_LIBRARY_VERSION_TLS_1_0;
 const SSL3ProtocolVersion kDtlsRecordVersion = SSL_LIBRARY_VERSION_TLS_1_1;
 
+/* Belt and suspenders in case we ever add a TLS 1.4. */
+PR_STATIC_ASSERT(SSL_LIBRARY_VERSION_MAX_SUPPORTED <=
+                 SSL_LIBRARY_VERSION_TLS_1_3);
+
 #define FATAL_ERROR(ss, prError, desc)                                             \
     do {                                                                           \
         SSL_TRC(3, ("%d: TLS13[%d]: fatal error %d in %s (%s:%d)",                 \
                     SSL_GETPID(), ss->fd, prError, __func__, __FILE__, __LINE__)); \
         tls13_FatalError(ss, prError, desc);                                       \
     } while (0)
 
 #define UNIMPLEMENTED()                                                   \
@@ -325,17 +331,293 @@ tls13_HandlePostHelloHandshakeMessage(ss
             FATAL_ERROR(ss, SSL_ERROR_RX_UNKNOWN_HANDSHAKE, unexpected_message);
             return SECFailure;
     }
 
     PORT_Assert(0); /* Unreached */
     return SECFailure;
 }
 
-/* Called from ssl3_HandleClientHello.
+static SECStatus
+tls13_RecoverWrappedSharedSecret(sslSocket *ss, sslSessionID *sid)
+{
+    PK11SymKey *wrapKey;  /* wrapping key */
+    PK11SymKey *SS = NULL;
+    SECItem wrappedMS = {siBuffer, NULL, 0};
+    SECStatus rv;
+    PK11SlotInfo *slot = NULL;
+    SSL_TRC(3, ("%d: TLS13[%d]: recovering static secret (%s)",
+                SSL_GETPID(), ss->fd,
+                ss->sec.isServer ? "server" : "client"));
+    if (!sid->u.ssl3.keys.msIsWrapped) {
+        PORT_Assert(0); /* I think this can't happen. */
+        return SECFailure;
+    }
+
+    /* If we are the server, we compute the wrapping key, but if we
+     * are the client, it's coordinates are stored with the ticket. */
+    if (ss->sec.isServer) {
+        wrapKey = ssl3_GetWrappingKey(ss, NULL,
+                                      sid->u.ssl3.exchKeyType,
+                                      sid->u.ssl3.masterWrapMech,
+                                      ss->pkcs11PinArg);
+    } else {
+        slot = SECMOD_LookupSlot(sid->u.ssl3.masterModuleID,
+                                 sid->u.ssl3.masterSlotID);
+        if (!slot)
+            return SECFailure;
+
+        wrapKey = PK11_GetWrapKey(slot,
+                                  sid->u.ssl3.masterWrapIndex,
+                                  sid->u.ssl3.masterWrapMech,
+                                  sid->u.ssl3.masterWrapSeries,
+                                  ss->pkcs11PinArg);
+    }
+    if (!wrapKey) {
+        return SECFailure;
+    }
+
+    wrappedMS.data = sid->u.ssl3.keys.wrapped_master_secret;
+    wrappedMS.len  = sid->u.ssl3.keys.wrapped_master_secret_len;
+
+    /* unwrap the "master secret" which becomes SS. */
+    PORT_Assert(tls13_GetHash(ss) == ssl_hash_sha256);
+    SS = PK11_UnwrapSymKeyWithFlags(wrapKey, sid->u.ssl3.masterWrapMech,
+                                    NULL, &wrappedMS,
+                                    CKM_SSL3_MASTER_KEY_DERIVE,
+                                    CKA_DERIVE, 32,
+                                    CKF_SIGN | CKF_VERIFY);
+    PK11_FreeSymKey(wrapKey);
+    if (!SS) {
+        return SECFailure;
+    }
+    PRINT_KEY(50, (ss, "Recovered static secret", SS));
+    rv = tls13_HkdfExtractSharedKey(ss, SS, StaticSharedSecret);
+    PK11_FreeSymKey(SS);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    return SECSuccess;
+}
+
+static void
+tls13_RestoreCipherInfo(sslSocket *ss, sslSessionID *sid)
+{
+    /* Set these to match the cached value.
+     * TODO(ekr@rtfm.com): Make a version with the "true" values.
+     * Bug 1256137.
+     */
+    ss->sec.authAlgorithm = sid->authAlgorithm;
+    ss->sec.authKeyBits   = sid->authKeyBits;
+    ss->sec.keaType       = sid->keaType;
+    ss->sec.keaKeyBits    = sid->keaKeyBits;
+    ss->ssl3.hs.origCipherSuite = sid->u.ssl3.cipherSuite;
+}
+
+PRBool
+tls13_AllowPskCipher(const sslSocket *ss, const ssl3CipherSuiteDef *cipher_def)
+{
+    if (ss->sec.isServer) {
+        if (!ss->statelessResume)
+            return PR_FALSE;
+    } else {
+        sslSessionID *sid = ss->sec.ci.sid;
+        const ssl3CipherSuiteDef *cached_cipher_def;
+
+        /* This is zero when called from ssl3_ConstructV2CipherSpecsHack.
+         * TODO(ekr@rtfm.com): remove when SSLv2 is removed. Bug 1228555.
+         */
+        if (!sid)
+            return PR_FALSE;
+
+        /* Verify that this was cached. */
+        if (sid->cached == never_cached)
+            return PR_FALSE;
+
+        /* Don't offer this if the session version < TLS 1.3 */
+        if (sid->version < SSL_LIBRARY_VERSION_TLS_1_3)
+            return PR_FALSE;
+        cached_cipher_def = ssl_LookupCipherSuiteDef(
+            sid->u.ssl3.cipherSuite);
+        PORT_Assert(cached_cipher_def);
+
+        /* Only offer a PSK cipher with the same symmetric parameters
+         * as we negotiated before. */
+        if (cached_cipher_def->bulk_cipher_alg !=
+            cipher_def->bulk_cipher_alg)
+            return PR_FALSE;
+    }
+    /* TODO(ekr@rtfm.com): Check the KDF code whenever we have
+     * adjustable KDFs. */
+    SSL_TRC(3, ("%d: TLS 1.3[%d]: Enabling cipher suite suite 0x%04x",
+                SSL_GETPID(), ss->fd,
+                cipher_def->cipher_suite));
+
+    return PR_TRUE;
+}
+
+/* Called from ssl3_HandleClientHello after we have parsed the
+ * ClientHello and are sure that we are going to do TLS 1.3
+ * or fail. */
+SECStatus
+tls13_HandleClientHelloPart2(sslSocket *ss,
+                             const SECItem *suites,
+                             sslSessionID *sid)
+{
+    PRBool haveSpecWriteLock = PR_FALSE;
+    PRBool haveXmitBufLock = PR_FALSE;
+    SECStatus rv;
+    SSL3Statistics *ssl3stats = SSL_GetStatistics();
+    int j;
+
+    /* Sanity check whether resumption-PSK is allowed. */
+    if (sid != NULL) {
+        PRBool resumeOK = PR_FALSE;
+
+        do {
+            if (sid->version != ss->version) {
+                break;
+            }
+            resumeOK = PR_TRUE;
+        } while(0);
+
+        if (!resumeOK) {
+            SSL_AtomicIncrementLong(& ssl3stats->hch_sid_cache_not_ok);
+            if (ss->sec.uncache)
+                ss->sec.uncache(sid);
+            ssl_FreeSID(sid);
+            sid = NULL;
+            ss->statelessResume = PR_FALSE;
+        }
+    }
+
+#ifndef PARANOID
+    /* Look for a matching cipher suite. */
+    j = ssl3_config_match_init(ss);
+    if (j <= 0) {                  /* no ciphers are working/supported by PK11 */
+        FATAL_ERROR(ss, PORT_GetError(), internal_error);
+        goto loser;
+    }
+#endif
+
+    rv = ssl3_NegotiateCipherSuite(ss, suites);
+    if (rv != SECSuccess) {
+        FATAL_ERROR(ss, SSL_ERROR_NO_CYPHER_OVERLAP, handshake_failure);
+        goto loser;
+    }
+
+    /* TODO(ekr@rtfm.com): Update this when we have pure PSK. */
+    if (ss->ssl3.hs.suite_def->key_exchange_alg != kea_ecdhe_psk) {
+        /* TODO(ekr@rtfm.com): Free resumeSID. */
+        ss->statelessResume = PR_FALSE;
+    }
+
+    if (ss->statelessResume) {
+        PORT_Assert(sid);
+
+        rv = tls13_RecoverWrappedSharedSecret(ss, sid);
+        if (rv != SECSuccess) {
+            FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+            goto loser;
+        }
+
+        SSL_AtomicIncrementLong(& ssl3stats->hch_sid_cache_hits);
+        SSL_AtomicIncrementLong(& ssl3stats->hch_sid_stateless_resumes);
+        ss->ssl3.hs.isResuming = PR_TRUE;
+
+        tls13_RestoreCipherInfo(ss, sid);
+
+        /* server sids don't remember the server cert we previously sent,
+        ** but they do remember the kea type we originally used, so we
+        ** can locate it again, provided that the current ssl socket
+        ** has had its server certs configured the same as the previous one.
+        */
+        ss->sec.localCert     =
+                CERT_DupCertificate(ss->serverCerts[sid->keaType].serverCert);
+
+        if (sid->peerCert != NULL) {
+            ss->sec.peerCert = CERT_DupCertificate(sid->peerCert);
+        }
+        ssl3_RegisterServerHelloExtensionSender(
+            ss, ssl_tls13_pre_shared_key_xtn, tls13_ServerSendPreSharedKeyXtn);
+        ss->sec.ci.sid = sid;
+    } else {
+        if (sid) { /* we had a sid, but it's no longer valid, free it */
+            SSL_AtomicIncrementLong(& ssl3stats->hch_sid_cache_not_ok);
+            if (ss->sec.uncache)
+                ss->sec.uncache(sid);
+            ssl_FreeSID(sid);
+            sid = NULL;
+        }
+        ss->ssl3.hs.origCipherSuite = ss->ssl3.hs.cipher_suite;
+        SSL_AtomicIncrementLong(& ssl3stats->hch_sid_cache_misses);
+    }
+
+    rv = ssl3_ServerCallSNICallback(ss);
+    if (rv != SECSuccess) {
+        goto loser;  /* An alert has already been sent. */
+    }
+
+    if (sid) {
+        /* Check that the negotiated SID and the cached SID match. */
+        if (SECITEM_CompareItem(&sid->u.ssl3.srvName,
+                                &ss->ssl3.pwSpec->srvVirtName) !=
+            SECEqual) {
+            FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO,
+                        handshake_failure);
+            goto loser;
+        }
+    }
+
+    rv = ssl3_SetupPendingCipherSpec(ss);
+    if (rv != SECSuccess) {
+        FATAL_ERROR(ss, PORT_GetError(), internal_error);
+        goto loser;
+    }
+
+    /* If this is TLS 1.3 we are expecting a ClientKeyShare
+     * extension. Missing/absent extension cause failure
+     * below. */
+    rv = tls13_HandleClientKeyShare(ss);
+    if (rv != SECSuccess) {
+        goto loser;  /* An alert was sent already. */
+    }
+
+    if (!sid) {
+        sid = ssl3_NewSessionID(ss, PR_TRUE);
+        if (sid == NULL) {
+            FATAL_ERROR(ss, PORT_GetError(), internal_error);
+            goto loser;
+        }
+        ss->sec.ci.sid = sid;
+        ss->ssl3.hs.isResuming = PR_FALSE;
+    }
+
+    ssl_GetXmitBufLock(ss);
+    rv = tls13_SendServerHelloSequence(ss);
+    ssl_ReleaseXmitBufLock(ss);
+    if (rv != SECSuccess) {
+        FATAL_ERROR(ss, PORT_GetError(), handshake_failure);
+        goto loser;
+    }
+
+    return SECSuccess;
+
+loser:
+    if (haveSpecWriteLock) {
+        ssl_ReleaseSpecWriteLock(ss);
+    }
+    if (haveXmitBufLock) {
+        ssl_ReleaseXmitBufLock(ss);
+    }
+    return SECFailure;
+}
+
+/* Called from tls13_HandleClientHello.
  *
  * Caller must hold Handshake and RecvBuf locks.
  */
 SECStatus
 tls13_HandleClientKeyShare(sslSocket *ss)
 {
     ECName expectedGroup;
     SECStatus rv;
@@ -343,20 +625,16 @@ tls13_HandleClientKeyShare(sslSocket *ss
     PRCList *cur_p;
 
     SSL_TRC(3, ("%d: TLS13[%d]: handle client_key_share handshake",
                 SSL_GETPID(), ss->fd));
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
-    rv = ssl3_SetupPendingCipherSpec(ss);
-    if (rv != SECSuccess)
-        return SECFailure; /* Error code set below */
-
     /* Figure out what group we expect */
     switch (ss->ssl3.hs.kea_def->exchKeyType) {
 #ifndef NSS_DISABLE_ECC
         case ssl_kea_ecdh:
             expectedGroup = ssl3_GetCurveNameForServerSocket(ss);
             if (!expectedGroup) {
                 FATAL_ERROR(ss, SSL_ERROR_NO_CYPHER_OVERLAP,
                             handshake_failure);
@@ -575,22 +853,24 @@ loser:
     return SECFailure;
 }
 
 static SECStatus
 tls13_InitializeHandshakeEncryption(sslSocket *ss)
 {
     SECStatus rv;
 
-    /* For all present cipher suites, SS = ES.
-     * TODO(ekr@rtfm.com): Revisit for 0-RTT. */
-    ss->ssl3.hs.xSS = PK11_ReferenceSymKey(ss->ssl3.hs.xES);
+    PORT_Assert(!!ss->ssl3.hs.xSS ==
+                (ss->ssl3.hs.kea_def->signKeyType == ssl_sign_psk));
     if (!ss->ssl3.hs.xSS) {
-        FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
-        return SECFailure;
+        ss->ssl3.hs.xSS = PK11_ReferenceSymKey(ss->ssl3.hs.xES);
+        if (!ss->ssl3.hs.xSS) {
+            FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+            return SECFailure;
+        }
     }
 
     /* Here we destroy the old cipher spec immediately; in DTLS, we have to
      * avoid running the holddown timer at this point. Retransmission of old
      * packets will use the static nullCipherSpec spec. */
     rv = tls13_InitCipherSpec(ss, TrafficKeyHandshake,
                               InstallCipherSpecBoth, dtls_oldkey_release);
     if (rv != SECSuccess) {
@@ -630,40 +910,42 @@ tls13_SendServerHelloSequence(sslSocket 
     }
 
     if (ss->opt.requestCertificate) {
         rv = tls13_SendCertificateRequest(ss);
         if (rv != SECSuccess) {
             return SECFailure; /* error code is set. */
         }
     }
-    rv = ssl3_SendCertificate(ss);
-    if (rv != SECSuccess) {
-        return SECFailure; /* error code is set. */
-    }
-    rv = ssl3_SendCertificateStatus(ss);
-    if (rv != SECSuccess) {
-        return SECFailure; /* error code is set. */
-    }
+    if (ss->ssl3.hs.kea_def->signKeyType != ssl_sign_psk) {
+        rv = ssl3_SendCertificate(ss);
+        if (rv != SECSuccess) {
+            return SECFailure; /* error code is set. */
+        }
+        rv = ssl3_SendCertificateStatus(ss);
+        if (rv != SECSuccess) {
+            return SECFailure; /* error code is set. */
+        }
 
-    /* This was copied from: ssl3_SendCertificate.
-     * TODO(ekr@rtfm.com): Verify that this selection logic is correct.
-     * Bug 1237514.
-    */
-    if ((ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa) ||
-        (ss->ssl3.hs.kea_def->kea == kea_dhe_rsa)) {
-        certIndex = kt_rsa;
-    } else {
-        certIndex = ss->ssl3.hs.kea_def->exchKeyType;
+        /* This was copied from: ssl3_SendCertificate.
+         * TODO(ekr@rtfm.com): Verify that this selection logic is correct.
+         * Bug 1237514.
+         */
+        if ((ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa) ||
+            (ss->ssl3.hs.kea_def->kea == kea_dhe_rsa)) {
+            certIndex = kt_rsa;
+        } else {
+            certIndex = ss->ssl3.hs.kea_def->exchKeyType;
+        }
+        rv = ssl3_SendCertificateVerify(ss,
+                                        ss->serverCerts[certIndex].SERVERKEY);
+        if (rv != SECSuccess) {
+            return rv; /* err code is set. */
+        }
     }
-    rv = ssl3_SendCertificateVerify(ss, ss->serverCerts[certIndex].SERVERKEY);
-    if (rv != SECSuccess) {
-        return rv; /* err code is set. */
-    }
-
     /* Compute the rest of the secrets except for the resumption
      * and exporter secret. */
     rv = tls13_ComputeSecrets1(ss);
     if (rv != SECSuccess) {
         FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
         return SECFailure;
     }
 
@@ -673,16 +955,111 @@ tls13_SendServerHelloSequence(sslSocket 
     }
 
     TLS13_SET_HS_STATE(ss, ss->opt.requestCertificate ? wait_client_cert
                                                       : wait_finished);
 
     return SECSuccess;
 }
 
+SECStatus
+tls13_HandleServerHelloPart2(sslSocket *ss)
+{
+    SECStatus rv;
+    PRBool isPSK = ssl3_ExtensionNegotiated(ss, ssl_tls13_pre_shared_key_xtn);
+    sslSessionID *sid = ss->sec.ci.sid;
+    SSL3Statistics *ssl3stats = SSL_GetStatistics();
+
+    /* we need to call ssl3_SetupPendingCipherSpec here so we can check the
+     * key exchange algorithm. */
+    rv = ssl3_SetupPendingCipherSpec(ss);
+    if (rv != SECSuccess) {
+        FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+        return SECFailure;
+    }
+
+    if (isPSK) {
+        PRBool cacheOK = PR_FALSE;
+        do {
+            if (ss->ssl3.hs.kea_def->signKeyType != ssl_sign_psk) {
+                FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_SERVER_HELLO,
+                            illegal_parameter);
+                break;
+            }
+            rv = tls13_RecoverWrappedSharedSecret(ss, sid);
+            if (rv != SECSuccess) {
+                FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+                break;
+            }
+            cacheOK = PR_TRUE;
+        } while (0);
+
+        if (!cacheOK) {
+            SSL_AtomicIncrementLong(&ssl3stats->hsh_sid_cache_not_ok);
+            if (ss->sec.uncache)
+                ss->sec.uncache(sid);
+            return SECFailure;
+        }
+
+        tls13_RestoreCipherInfo(ss, sid);
+
+        SSL_AtomicIncrementLong(&ssl3stats->hsh_sid_cache_hits);
+        SSL_AtomicIncrementLong(&ssl3stats->hsh_sid_stateless_resumes);
+    } else {
+        /* No PSK negotiated.*/
+        if (ss->ssl3.hs.kea_def->signKeyType == ssl_sign_psk) {
+            FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_SERVER_HELLO,
+                            illegal_parameter);
+            return SECFailure;
+        }
+        if (ssl3_ClientExtensionAdvertised(ss, ssl_tls13_pre_shared_key_xtn)) {
+            SSL_AtomicIncrementLong(&ssl3stats->hsh_sid_cache_misses);
+        }
+
+        /* Copy Signed Certificate Timestamps, if any. */
+        if (ss->xtnData.signedCertTimestamps.data) {
+            rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.signedCertTimestamps,
+                                  &ss->xtnData.signedCertTimestamps);
+            if (rv != SECSuccess) {
+                FATAL_ERROR(ss, SEC_ERROR_NO_MEMORY, internal_error);
+                return SECFailure;
+            }
+            /* Clean up the temporary pointer to the handshake buffer. */
+            ss->xtnData.signedCertTimestamps.data = NULL;
+            ss->xtnData.signedCertTimestamps.len = 0;
+        }
+        ss->ssl3.hs.origCipherSuite = ss->ssl3.hs.cipher_suite;
+
+        if (sid->cached == in_client_cache && (ss->sec.uncache)) {
+            /* If we tried to resume and failed, let's not try again. */
+            ss->sec.uncache(sid);
+        }
+    }
+
+
+    /* Discard current SID and make a new one, though it may eventually
+     * end up looking a lot like the old one.
+     */
+    ssl_FreeSID(sid);
+    ss->sec.ci.sid = sid = ssl3_NewSessionID(ss, PR_FALSE);
+    if (sid == NULL) {
+        FATAL_ERROR(ss, PORT_GetError(), internal_error);
+        return SECFailure;
+    }
+    sid->version = ss->version;
+    rv = tls13_HandleServerKeyShare(ss);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+    TLS13_SET_HS_STATE(ss, wait_encrypted_extensions);
+
+    return SECSuccess;
+}
+
+
 /*
  * Called from ssl3_HandleServerHello.
  *
  * Caller must hold Handshake and RecvBuf locks.
  */
 SECStatus
 tls13_HandleServerKeyShare(sslSocket *ss)
 {
@@ -899,27 +1276,27 @@ tls13_AddContextToHashes(sslSocket *ss, 
     if (!ctx) {
         PORT_SetError(SEC_ERROR_NO_MEMORY);
         goto loser;
     }
 
     PORT_Assert(SECFailure);
     PORT_Assert(!SECSuccess);
 
-    PRINT_BUF(90, (ss, "TLS 1.3 hash without context", hashes->u.raw, hashes->len));
-    PRINT_BUF(90, (ss, "Context string", context_string, strlen(context_string)));
+    PRINT_BUF(50, (ss, "TLS 1.3 hash without context", hashes->u.raw, hashes->len));
+    PRINT_BUF(50, (ss, "Context string", context_string, strlen(context_string)));
     rv |= PK11_DigestBegin(ctx);
     rv |= PK11_DigestOp(ctx, context_padding, sizeof(context_padding));
     rv |= PK11_DigestOp(ctx, (unsigned char *)context_string,
                         strlen(context_string) + 1); /* +1 includes the terminating 0 */
     rv |= PK11_DigestOp(ctx, hashes->u.raw, hashes->len);
     /* Update the hash in-place */
     rv |= PK11_DigestFinal(ctx, hashes->u.raw, &hashlength, sizeof(hashes->u.raw));
     PK11_DestroyContext(ctx, PR_TRUE);
-    PRINT_BUF(90, (NULL, "TLS 1.3 hash with context", hashes->u.raw, hashlength));
+    PRINT_BUF(50, (ss, "TLS 1.3 hash with context", hashes->u.raw, hashlength));
 
     hashes->len = hashlength;
     hashes->hashAlg = algorithm;
 
     if (rv) {
         ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
         goto loser;
     }
@@ -1011,17 +1388,17 @@ tls13_DeriveTrafficKeys(sslSocket *ss, s
     PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
 
     rv = ssl3_ComputeHandshakeHashes(ss, pwSpec, &hashes, 0);
     if (rv != SECSuccess) {
         PORT_Assert(0); /* Should never fail */
         ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE);
         return SECFailure;
     }
-    PRINT_BUF(60, (ss, "Deriving traffic keys. Session hash=", hashes.u.raw,
+    PRINT_BUF(50, (ss, "Deriving traffic keys. Session hash=", hashes.u.raw,
                    hashes.len));
 
     switch (type) {
         case TrafficKeyHandshake:
             phase = kHkdfPhaseHandshakeKeys;
             prk = ss->ssl3.hs.xES;
             break;
         case TrafficKeyApplicationData:
@@ -1140,25 +1517,25 @@ loser:
 }
 
 static SECStatus
 tls13_ComputeSecrets1(sslSocket *ss)
 {
     SECStatus rv;
     PK11SymKey *mSS = NULL;
     PK11SymKey *mES = NULL;
-    PK11SymKey *masterSecret = NULL;
     SSL3Hashes hashes;
+    ssl3CipherSpec *pwSpec = ss->ssl3.pwSpec;
 
     rv = ssl3_SetupPendingCipherSpec(ss);
     if (rv != SECSuccess) {
         return rv; /* error code set below. */
     }
 
-    rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.pwSpec, &hashes, 0);
+    rv = ssl3_ComputeHandshakeHashes(ss, pwSpec, &hashes, 0);
     if (rv != SECSuccess) {
         PORT_Assert(0); /* Should never fail */
         ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE);
         return SECFailure;
     }
 
     PORT_Assert(ss->ssl3.hs.xSS);
     PORT_Assert(ss->ssl3.hs.xES);
@@ -1182,45 +1559,45 @@ tls13_ComputeSecrets1(sslSocket *ss)
                                tls13_GetHkdfMechanism(ss),
                                hashes.len, &mES);
     if (rv != SECSuccess) {
         goto loser;
     }
 
     rv = tls13_HkdfExtract(mSS, mES,
                            tls13_GetHash(ss),
-                           &masterSecret);
+                           &pwSpec->master_secret);
 
     if (rv != SECSuccess) {
         goto loser;
     }
 
-    rv = tls13_HkdfExpandLabel(masterSecret,
+    rv = tls13_HkdfExpandLabel(pwSpec->master_secret,
                                tls13_GetHash(ss),
                                hashes.u.raw, hashes.len,
                                kHkdfLabelTrafficSecret,
                                strlen(kHkdfLabelTrafficSecret),
                                tls13_GetHkdfMechanism(ss),
                                hashes.len, &ss->ssl3.hs.trafficSecret);
     if (rv != SECSuccess) {
         goto loser;
     }
 
-    rv = tls13_HkdfExpandLabel(masterSecret,
+    rv = tls13_HkdfExpandLabel(pwSpec->master_secret,
                                tls13_GetHash(ss),
                                NULL, 0,
                                kHkdfLabelClientFinishedSecret,
                                strlen(kHkdfLabelClientFinishedSecret),
                                tls13_GetHmacMechanism(ss),
                                hashes.len, &ss->ssl3.hs.clientFinishedSecret);
     if (rv != SECSuccess) {
         goto loser;
     }
 
-    rv = tls13_HkdfExpandLabel(masterSecret,
+    rv = tls13_HkdfExpandLabel(pwSpec->master_secret,
                                tls13_GetHash(ss),
                                NULL, 0,
                                kHkdfLabelServerFinishedSecret,
                                strlen(kHkdfLabelServerFinishedSecret),
                                tls13_GetHmacMechanism(ss),
                                hashes.len, &ss->ssl3.hs.serverFinishedSecret);
     if (rv != SECSuccess) {
         goto loser;
@@ -1233,20 +1610,55 @@ loser:
     ss->ssl3.hs.xES = NULL;
 
     if (mSS) {
         PK11_FreeSymKey(mSS);
     }
     if (mES) {
         PK11_FreeSymKey(mES);
     }
-    if (masterSecret) {
-        PK11_FreeSymKey(masterSecret);
+
+    return rv;
+}
+
+static SECStatus
+tls13_ComputeSecrets2(sslSocket *ss)
+{
+    SECStatus rv;
+    SSL3Hashes hashes;
+    ssl3CipherSpec *cwSpec = ss->ssl3.cwSpec;
+    PK11SymKey *resumptionMasterSecret = NULL;
+
+    rv = ssl3_ComputeHandshakeHashes(ss, cwSpec, &hashes, 0);
+    if (rv != SECSuccess) {
+        PORT_Assert(0);  /* Should never fail */
+        ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE);
+        return SECFailure;
     }
 
+    rv = tls13_HkdfExpandLabel(cwSpec->master_secret,
+                               tls13_GetHash(ss),
+                               hashes.u.raw, hashes.len,
+                               kHkdfLabelResumptionMasterSecret,
+                               strlen(kHkdfLabelResumptionMasterSecret),
+                               tls13_GetHkdfMechanism(ss),
+                               hashes.len, &resumptionMasterSecret);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    /* This is pretty gross. TLS 1.3 uses a number of master secrets.
+     * the master secret to generate the keys and then the resumption
+     * master secret for future connections. To make this work without
+     * refactoring too much of the SSLv3 code, we replace
+     * |pwSpec->master_secret| with the resumption master secret.
+     */
+    PK11_FreeSymKey(cwSpec->master_secret);
+    cwSpec->master_secret = resumptionMasterSecret;
+
+loser:
     return rv;
 }
 
 void
 tls13_DestroyKeyShareEntry(TLS13KeyShareEntry *offer)
 {
     SECITEM_ZfreeItem(&offer->key_exchange, PR_FALSE);
     PORT_ZFree(offer, sizeof(*offer));
@@ -1365,17 +1777,31 @@ tls13_HandleEncryptedExtensions(sslSocke
         return SECFailure;
     }
 
     rv = ssl3_HandleHelloExtensions(ss, &b, &length, encrypted_extensions);
     if (rv != SECSuccess) {
         return SECFailure; /* Error code set below */
     }
 
-    TLS13_SET_HS_STATE(ss, wait_cert_request);
+    PORT_Assert(!ss->sec.isServer);
+
+    if (ss->ssl3.hs.kea_def->signKeyType == ssl_sign_psk) {
+        /* Compute the rest of the secrets except for the resumption
+         * and exporter secret. */
+        rv = tls13_ComputeSecrets1(ss);
+        if (rv != SECSuccess) {
+            FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+            return SECFailure;
+        }
+        TLS13_SET_HS_STATE(ss, wait_finished);
+    } else {
+        TLS13_SET_HS_STATE(ss, wait_cert_request);
+    }
+
     return SECSuccess;
 }
 
 static SECStatus
 tls13_SendEncryptedExtensions(sslSocket *ss)
 {
     SECStatus rv;
     PRInt32 extensions_len = 0;
@@ -1516,17 +1942,17 @@ tls13_ComputeFinished(sslSocket *ss, con
     PK11Context *hmacCtx = NULL;
     CK_MECHANISM_TYPE macAlg = tls13_GetHmacMechanism(ss);
     SECItem param = { siBuffer, NULL, 0 };
     unsigned int outputLenUint;
     PK11SymKey *secret = (ss->sec.isServer ^ sending) ? ss->ssl3.hs.clientFinishedSecret
                                                       : ss->ssl3.hs.serverFinishedSecret;
 
     PORT_Assert(secret);
-    PRINT_BUF(90, (NULL, "Handshake hash", hashes->u.raw, hashes->len));
+    PRINT_BUF(50, (NULL, "Handshake hash", hashes->u.raw, hashes->len));
 
     hmacCtx = PK11_CreateContextBySymKey(macAlg, CKA_SIGN,
                                          secret, &param);
     if (!hmacCtx) {
         goto abort;
     }
 
     rv = PK11_DigestBegin(hmacCtx);
@@ -1669,59 +2095,78 @@ tls13_HandleFinished(sslSocket *ss, SSL3
         FATAL_ERROR(ss, SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE,
                     decrypt_error);
         return SECFailure;
     }
 
     /* Server is now finished.
      * Client sends second flight
      */
-    /* TODO(ekr@rtfm.com): Send NewSession Ticket if server. */
     if (ss->sec.isServer) {
         /* Once we've receive the client's Finished, there is no need for
          * retransmission; the retransmission timer was stopped when we received
          * the client's Finished message.  Installing the new cipher spec causes
          * the old cipher spec to be destroyed. */
         rv = tls13_InstallCipherSpec(ss, InstallCipherSpecRead,
                                      dtls_oldkey_release);
         if (rv != SECSuccess) {
             FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
             return SECFailure;
         }
 
         rv = tls13_FinishHandshake(ss);
+        if (rv != SECSuccess) {
+            return SECFailure;  /* Error code and alerts handled below */
+        }
+        ssl_GetXmitBufLock(ss);
+        if (ss->opt.enableSessionTickets &&
+            ss->ssl3.hs.kea_def->signKeyType != ssl_sign_psk) {
+            /* TODO(ekr@rtfm.com): Add support for new tickets in PSK. */
+            rv = ssl3_SendNewSessionTicket(ss);
+            if (rv != SECSuccess) {
+                ssl_ReleaseXmitBufLock(ss);
+                return SECFailure;  /* Error code and alerts handled below */
+            }
+            rv = ssl3_FlushHandshake(ss, 0);
+        }
+        ssl_ReleaseXmitBufLock(ss);
+
     } else {
         if (ss->ssl3.hs.authCertificatePending) {
             /* TODO(ekr@rtfm.com): Handle pending auth */
             FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
             PORT_Assert(0);
             return SECFailure;
         }
         rv = tls13_InitCipherSpec(ss, TrafficKeyApplicationData,
                                   InstallCipherSpecRead, dtls_oldkey_release);
         if (rv != SECSuccess) {
             FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
             return SECFailure;
         }
 
         rv = tls13_SendClientSecondRound(ss);
-        if (rv != SECSuccess)
-            return SECFailure; /* Error code and alerts handled below */
     }
 
     return rv;
 }
 
 static SECStatus
 tls13_FinishHandshake(sslSocket *ss)
 {
+    SECStatus rv;
+
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
     PORT_Assert(ss->ssl3.hs.restartTarget == NULL);
 
+    rv = tls13_ComputeSecrets2(ss);
+    if (rv != SECSuccess)
+        return SECFailure;
+
     /* The first handshake is now completed. */
     ss->handshake = NULL;
 
     TLS13_SET_HS_STATE(ss, idle_handshake);
 
     ssl_FinishHandshake(ss);
 
     return SECSuccess;
@@ -1795,26 +2240,89 @@ loser:
     ssl_ReleaseXmitBufLock(ss); /*******************************/
     return SECFailure;
 }
 
 static SECStatus
 tls13_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     SECStatus rv;
+    PRInt32 tmp;
+    NewSessionTicket ticket;
+    SECItem data;
+
+    SSL_TRC(3, ("%d: TLS13[%d]: handle new session ticket message",
+                SSL_GETPID(), ss->fd));
 
     rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET,
                               idle_handshake);
     if (rv != SECSuccess) {
         return SECFailure;
     }
+    if (!ss->firstHsDone || ss->sec.isServer) {
+        FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET,
+                    unexpected_message);
+        return SECFailure;
+    }
 
-    UNIMPLEMENTED();
+    ticket.received_timestamp = ssl_Time();
+    tmp = ssl3_ConsumeHandshakeNumber(ss, 4, &b, &length);
+    if (tmp < 0) {
+        FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET,
+                    decode_error);
+        return SECFailure;
+    }
+    ticket.ticket_lifetime_hint = (PRUint32)tmp;
+    ticket.ticket.type = siBuffer;
+    rv = ssl3_ConsumeHandshakeVariable(ss, &data, 2, &b, &length);
+    if (rv != SECSuccess || length != 0 || !data.len) {
+        FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET,
+                    decode_error);
+        return SECFailure;
+    }
+
+    /* TODO(ekr@rtfm.com): Re-enable new tickets when PSK mode is
+     * in use. I believe this works, but I can't test it until the
+     * server side supports it. Bug 1257047.
+     */
+    if (!ss->opt.noCache && ss->sec.cache &&
+        ss->ssl3.hs.kea_def->signKeyType != ssl_sign_psk) {
+        SSL3KEAType effectiveExchKeyType;
+
+        /* Uncache so that we replace. */
+        (*ss->sec.uncache)(ss->sec.ci.sid);
 
-    /* Ignore */
+        rv = SECITEM_CopyItem(NULL, &ticket.ticket, &data);
+        if (rv != SECSuccess) {
+            FATAL_ERROR(ss, SEC_ERROR_NO_MEMORY, internal_error);
+            return SECFailure;
+        }
+        PRINT_BUF(50, (ss, "Caching session ticket",
+                       ticket.ticket.data,
+                       ticket.ticket.len));
+
+        ssl3_SetSIDSessionTicket(ss->sec.ci.sid, &ticket);
+        PORT_Assert(!ticket.ticket.data);
+
+        if (ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa ||
+            ss->ssl3.hs.kea_def->kea == kea_dhe_rsa) {
+            effectiveExchKeyType = kt_rsa;
+        } else {
+            effectiveExchKeyType = ss->ssl3.hs.kea_def->exchKeyType;
+        }
+
+        rv = ssl3_FillInCachedSID(ss, ss->sec.ci.sid, effectiveExchKeyType);
+        if (rv != SECSuccess)
+            return SECFailure;
+
+        /* Cache the session. */
+        ss->sec.ci.sid->cached = never_cached;
+        (*ss->sec.cache)(ss->sec.ci.sid);
+    }
+
     return SECSuccess;
 }
 
 typedef enum {
     ExtensionNotUsed,
     ExtensionClientOnly,
     ExtensionSendClear,
     ExtensionSendEncrypted,
@@ -1845,16 +2353,18 @@ static const struct {
     { ssl_padding_xtn,
       ExtensionNotUsed },
     { ssl_extended_master_secret_xtn,
       ExtensionNotUsed },
     { ssl_session_ticket_xtn,
       ExtensionClientOnly },
     { ssl_tls13_key_share_xtn,
       ExtensionSendClear },
+    { ssl_tls13_pre_shared_key_xtn,
+      ExtensionSendClear },
     { ssl_next_proto_nego_xtn,
       ExtensionNotUsed },
     { ssl_renegotiation_info_xtn,
       ExtensionNotUsed },
     { ssl_tls13_draft_version_xtn,
       ExtensionClientOnly }
 };
 
--- a/lib/ssl/tls13con.h
+++ b/lib/ssl/tls13con.h
@@ -36,16 +36,22 @@ PRBool tls13_InHsState(sslSocket *ss, ..
 #define TLS13_IN_HS_STATE(ss, ...) \
     tls13_InHsState(ss, __VA_ARGS__, wait_invalid)
 
 SSLHashType tls13_GetHash(sslSocket *ss);
 CK_MECHANISM_TYPE tls13_GetHkdfMechanism(sslSocket *ss);
 void tls13_FatalError(sslSocket *ss, PRErrorCode prError,
                       SSL3AlertDescription desc);
 SECStatus tls13_SetupClientHello(sslSocket *ss);
+PRBool tls13_AllowPskCipher(const sslSocket *ss,
+                            const ssl3CipherSuiteDef *cipher_def);
+SECStatus tls13_HandleClientHelloPart2(sslSocket *ss,
+                                       const SECItem *suites,
+                                       sslSessionID *sid);
+SECStatus tls13_HandleServerHelloPart2(sslSocket *ss);
 SECStatus tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, SSL3Opaque *b,
                                                 PRUint32 length,
                                                 SSL3Hashes *hashesPtr);
 SECStatus tls13_HandleClientKeyShare(sslSocket *ss);
 SECStatus tls13_SendServerHelloSequence(sslSocket *ss);
 SECStatus tls13_HandleServerKeyShare(sslSocket *ss);
 SECStatus tls13_AddContextToHashes(sslSocket *ss,
                                    SSL3Hashes *hashes /* IN/OUT */,
--- a/lib/ssl/tls13hkdf.c
+++ b/lib/ssl/tls13hkdf.c
@@ -77,17 +77,17 @@ tls13_HkdfExtract(PK11SymKey *ikm1, PK11
     PORT_Assert(kTlsHkdfInfo[baseHash].pkcs11Mech);
     PORT_Assert(kTlsHkdfInfo[baseHash].hashSize);
     PORT_Assert(kTlsHkdfInfo[baseHash].hash == baseHash);
     prk = PK11_Derive(ikm2, kTlsHkdfInfo[baseHash].pkcs11Mech,
                       &paramsi, kTlsHkdfInfo[baseHash].pkcs11Mech,
                       CKA_DERIVE, kTlsHkdfInfo[baseHash].hashSize);
     if (!prk)
         return SECFailure;
-    PRINT_KEY(60, (NULL, "HKDF Extract", prk));
+    PRINT_KEY(50, (NULL, "HKDF Extract", prk));
     *prkp = prk;
 
     return SECSuccess;
 }
 
 SECStatus
 tls13_HkdfExpandLabel(PK11SymKey *prk, SSLHashType baseHash,
                       const PRUint8 *handshakeHash, unsigned int handshakeHashLen,
@@ -166,23 +166,23 @@ tls13_HkdfExpandLabel(PK11SymKey *prk, S
     *keyp = derived;
 
 #ifdef TRACE
     if (ssl_trace >= 10) {
         /* Make sure the label is null terminated. */
         char labelStr[100];
         PORT_Memcpy(labelStr, label, labelLen);
         labelStr[labelLen] = 0;
-        SSL_TRC(60, ("HKDF Expand: label=[TLS 1.3, ] + '%s',requested length=%d",
+        SSL_TRC(50, ("HKDF Expand: label=[TLS 1.3, ] + '%s',requested length=%d",
                      labelStr, keySize));
     }
-    PRINT_KEY(60, (NULL, "PRK", prk));
-    PRINT_BUF(60, (NULL, "Hash", handshakeHash, handshakeHashLen));
-    PRINT_BUF(60, (NULL, "Info", info, infoLen));
-    PRINT_KEY(60, (NULL, "Derived key", derived));
+    PRINT_KEY(50, (NULL, "PRK", prk));
+    PRINT_BUF(50, (NULL, "Hash", handshakeHash, handshakeHashLen));
+    PRINT_BUF(50, (NULL, "Info", info, infoLen));
+    PRINT_KEY(50, (NULL, "Derived key", derived));
 #endif
 
     return SECSuccess;
 
 abort:
     PORT_SetError(SSL_ERROR_SYM_KEY_CONTEXT_FAILURE);
     return SECFailure;
 }
--- a/lib/util/secoid.c
+++ b/lib/util/secoid.c
@@ -1707,16 +1707,19 @@ const static SECOidData oids[SEC_OID_TOT
 	"TLS DH-DSS-EXPORT key exchange", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION ),
     ODE( SEC_OID_TLS_DH_ANON_EXPORT,
 	"TLS DH-ANON-EXPORT key exchange", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION ),
     ODE( SEC_OID_APPLY_SSL_POLICY,
 	"Apply SSL policy (pseudo-OID)", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION ),
     ODE( SEC_OID_CHACHA20_POLY1305,
 	"ChaCha20-Poly1305", CKM_NSS_CHACHA20_POLY1305, INVALID_CERT_EXTENSION ),
 
+    ODE( SEC_OID_TLS_ECDHE_PSK,
+         "TLS ECHDE-PSK key exchange", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION ),
+
 };
 
 /* PRIVATE EXTENDED SECOID Table
  * This table is private. Its structure is opaque to the outside.
  * It is indexed by the same SECOidTag as the oids table above.
  * Every member of this struct must have accessor functions (set, get)
  * and those functions must operate by value, not by reference.
  * The addresses of the contents of this table must not be exposed 
--- a/lib/util/secoidt.h
+++ b/lib/util/secoidt.h
@@ -476,16 +476,18 @@ typedef enum {
     SEC_OID_TLS_DHE_DSS_EXPORT         = 341,
     SEC_OID_TLS_DH_RSA_EXPORT          = 342,
     SEC_OID_TLS_DH_DSS_EXPORT          = 343,
     SEC_OID_TLS_DH_ANON_EXPORT         = 344,
     SEC_OID_APPLY_SSL_POLICY           = 345,
 
     SEC_OID_CHACHA20_POLY1305          = 346,
 
+    SEC_OID_TLS_ECDHE_PSK              = 347,
+
     SEC_OID_TOTAL
 } SECOidTag;
 
 #define SEC_OID_SECG_EC_SECP192R1 SEC_OID_ANSIX962_EC_PRIME192V1
 #define SEC_OID_SECG_EC_SECP256R1 SEC_OID_ANSIX962_EC_PRIME256V1
 #define SEC_OID_PKCS12_KEY_USAGE  SEC_OID_X509_KEY_USAGE
 
 /* fake OID for DSS sign/verify */