Bug 1396525 - put keaGroup and sigScheme in the cache so we can put it in SSLChannelInfo, r=mt
authorFranziskus Kiefer <franziskuskiefer@gmail.com>
Wed, 04 Oct 2017 10:05:29 +0200
changeset 13623 6fb9c5396d52e007c1cdc7330e062589478fdb5e
parent 13622 c1866d7d7f15b3ea9de3659fbd2fdaaf4f2ae273
child 14035 244e8f00c3efa6c7dca02e454e291fc00f748110
child 14037 6c08a77543a053129046acff6e985821bfc8adfe
push id2403
push userfranziskuskiefer@gmail.com
push dateWed, 04 Oct 2017 08:36:46 +0000
reviewersmt
bugs1396525
Bug 1396525 - put keaGroup and sigScheme in the cache so we can put it in SSLChannelInfo, r=mt Summary: This adds originalKeaGroup and resumed fields to the SSLChannelInfo, which provide information about the key exchange group of the original TLS handshake when the session is resumed, and information whether a session is resumed or not. To do this this patch adds the keaGroup and sigScheme to the session cache and the session ticket. Reviewers: mt Reviewed By: mt Bug #: 1396525 Differential Revision: https://phabricator.services.mozilla.com/D29
gtests/ssl_gtest/ssl_dhe_unittest.cc
gtests/ssl_gtest/ssl_resumption_unittest.cc
gtests/ssl_gtest/tls_agent.cc
gtests/ssl_gtest/tls_agent.h
gtests/ssl_gtest/tls_connect.cc
gtests/ssl_gtest/tls_connect.h
lib/ssl/ssl3con.c
lib/ssl/ssl3exthandle.c
lib/ssl/sslimpl.h
lib/ssl/sslinfo.c
lib/ssl/sslsnce.c
lib/ssl/sslt.h
lib/ssl/tls13con.c
--- a/gtests/ssl_gtest/ssl_dhe_unittest.cc
+++ b/gtests/ssl_gtest/ssl_dhe_unittest.cc
@@ -531,30 +531,32 @@ TEST_P(TlsConnectGenericPre13, MismatchD
   client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
 }
 
 TEST_P(TlsConnectTls13, ResumeFfdhe) {
   EnableOnlyDheCiphers();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   Connect();
   SendReceive();  // Need to read so that we absorb the session ticket.
-  CheckKeys(ssl_kea_dh, ssl_auth_rsa_sign);
+  CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign,
+            ssl_sig_rsa_pss_sha256);
 
   Reset();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   EnableOnlyDheCiphers();
   auto clientCapture =
       std::make_shared<TlsExtensionCapture>(ssl_tls13_pre_shared_key_xtn);
   client_->SetPacketFilter(clientCapture);
   auto serverCapture =
       std::make_shared<TlsExtensionCapture>(ssl_tls13_pre_shared_key_xtn);
   server_->SetPacketFilter(serverCapture);
   ExpectResumption(RESUME_TICKET);
   Connect();
-  CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign, ssl_sig_none);
+  CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign,
+            ssl_sig_rsa_pss_sha256);
   ASSERT_LT(0UL, clientCapture->extension().len());
   ASSERT_LT(0UL, serverCapture->extension().len());
 }
 
 class TlsDheSkeChangeSignature : public TlsHandshakeFilter {
  public:
   TlsDheSkeChangeSignature(uint16_t version, const uint8_t* data, size_t len)
       : version_(version), data_(data), len_(len) {}
--- a/gtests/ssl_gtest/ssl_resumption_unittest.cc
+++ b/gtests/ssl_gtest/ssl_resumption_unittest.cc
@@ -391,17 +391,18 @@ TEST_P(TlsConnectTls13, TestTls13ResumeD
   CheckKeys();
 
   Reset();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ExpectResumption(RESUME_TICKET);
   client_->ConfigNamedGroups(kFFDHEGroups);
   server_->ConfigNamedGroups(kFFDHEGroups);
   Connect();
-  CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign, ssl_sig_none);
+  CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign,
+            ssl_sig_rsa_pss_sha256);
 }
 
 // We need to enable different cipher suites at different times in the following
 // tests.  Those cipher suites need to be suited to the version.
 static uint16_t ChooseOneCipher(uint16_t version) {
   if (version >= SSL_LIBRARY_VERSION_TLS_1_3) {
     return TLS_AES_128_GCM_SHA256;
   }
@@ -599,17 +600,17 @@ TEST_F(TlsConnectTest, TestTls13Resumpti
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
   ExpectResumption(RESUME_TICKET);
   auto c1 = std::make_shared<TlsExtensionCapture>(ssl_tls13_pre_shared_key_xtn);
   client_->SetPacketFilter(c1);
   Connect();
   SendReceive();
   CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign,
-            ssl_sig_none);
+            ssl_sig_rsa_pss_sha256);
   // The filter will go away when we reset, so save the captured extension.
   DataBuffer initialTicket(c1->extension());
   ASSERT_LT(0U, initialTicket.len());
 
   ScopedCERTCertificate cert1(SSL_PeerCertificate(client_->ssl_fd()));
   ASSERT_TRUE(!!cert1.get());
 
   Reset();
@@ -617,17 +618,17 @@ TEST_F(TlsConnectTest, TestTls13Resumpti
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
   auto c2 = std::make_shared<TlsExtensionCapture>(ssl_tls13_pre_shared_key_xtn);
   client_->SetPacketFilter(c2);
   ExpectResumption(RESUME_TICKET);
   Connect();
   SendReceive();
   CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign,
-            ssl_sig_none);
+            ssl_sig_rsa_pss_sha256);
   ASSERT_LT(0U, c2->extension().len());
 
   ScopedCERTCertificate cert2(SSL_PeerCertificate(client_->ssl_fd()));
   ASSERT_TRUE(!!cert2.get());
 
   // Check that the cipher suite is reported the same on both sides, though in
   // TLS 1.3 resumption actually negotiates a different cipher suite.
   uint16_t resumed_suite;
@@ -718,9 +719,66 @@ TEST_F(TlsConnectTest, TestTls13Resumpti
 
   client_->ExpectSendAlert(kTlsAlertDecodeError);
   server_->ExpectSendAlert(kTlsAlertBadRecordMac);  // Server can't read
   ConnectExpectFail();
   client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
   server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
 }
 
+TEST_P(TlsConnectGeneric, ReConnectTicket) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+  server_->EnableSingleCipher(ChooseOneCipher(version_));
+  Connect();
+  SendReceive();
+  CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign,
+            ssl_sig_rsa_pss_sha256);
+  // Resume
+  Reset();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+  ExpectResumption(RESUME_TICKET);
+  Connect();
+  // Only the client knows this.
+  CheckKeysResumption(ssl_kea_ecdh, ssl_grp_none, ssl_grp_ec_curve25519,
+                      ssl_auth_rsa_sign, ssl_sig_rsa_pss_sha256);
+}
+
+TEST_P(TlsConnectGenericPre13, ReConnectCache) {
+  ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID);
+  server_->EnableSingleCipher(ChooseOneCipher(version_));
+  Connect();
+  SendReceive();
+  CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign,
+            ssl_sig_rsa_pss_sha256);
+  // Resume
+  Reset();
+  ExpectResumption(RESUME_SESSIONID);
+  Connect();
+  CheckKeysResumption(ssl_kea_ecdh, ssl_grp_none, ssl_grp_ec_curve25519,
+                      ssl_auth_rsa_sign, ssl_sig_rsa_pss_sha256);
+}
+
+TEST_P(TlsConnectGeneric, ReConnectAgainTicket) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+  server_->EnableSingleCipher(ChooseOneCipher(version_));
+  Connect();
+  SendReceive();
+  CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign,
+            ssl_sig_rsa_pss_sha256);
+  // Resume
+  Reset();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+  ExpectResumption(RESUME_TICKET);
+  Connect();
+  // Only the client knows this.
+  CheckKeysResumption(ssl_kea_ecdh, ssl_grp_none, ssl_grp_ec_curve25519,
+                      ssl_auth_rsa_sign, ssl_sig_rsa_pss_sha256);
+  // Resume connection again
+  Reset();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+  ExpectResumption(RESUME_TICKET, 2);
+  Connect();
+  // Only the client knows this.
+  CheckKeysResumption(ssl_kea_ecdh, ssl_grp_none, ssl_grp_ec_curve25519,
+                      ssl_auth_rsa_sign, ssl_sig_rsa_pss_sha256);
+}
+
 }  // namespace nss_test
--- a/gtests/ssl_gtest/tls_agent.cc
+++ b/gtests/ssl_gtest/tls_agent.cc
@@ -490,16 +490,22 @@ void TlsAgent::CheckKEA(SSLKEAType kea_t
     }
   }
   if (kea_group != ssl_grp_ffdhe_custom) {
     EXPECT_EQ(kea_size, info_.keaKeyBits);
     EXPECT_EQ(kea_group, info_.keaGroup);
   }
 }
 
+void TlsAgent::CheckOriginalKEA(SSLNamedGroup kea_group) const {
+  if (kea_group != ssl_grp_ffdhe_custom) {
+    EXPECT_EQ(kea_group, info_.originalKeaGroup);
+  }
+}
+
 void TlsAgent::CheckAuthType(SSLAuthType auth_type,
                              SSLSignatureScheme sig_scheme) const {
   EXPECT_EQ(STATE_CONNECTED, state_);
   EXPECT_EQ(auth_type, info_.authType);
   EXPECT_EQ(server_key_bits_, info_.authKeyBits);
   if (expected_version_ < SSL_LIBRARY_VERSION_TLS_1_2) {
     switch (auth_type) {
       case ssl_auth_rsa_sign:
@@ -715,16 +721,18 @@ void TlsAgent::Connected() {
   LOG("Handshake success");
   CheckPreliminaryInfo();
   CheckCallbacks();
 
   SECStatus rv = SSL_GetChannelInfo(ssl_fd(), &info_, sizeof(info_));
   EXPECT_EQ(SECSuccess, rv);
   EXPECT_EQ(sizeof(info_), info_.length);
 
+  EXPECT_EQ(expect_resumption_, info_.resumed == PR_TRUE);
+
   // Preliminary values are exposed through callbacks during the handshake.
   // If either expected values were set or the callbacks were called, check
   // that the final values are correct.
   EXPECT_EQ(expected_version_, info_.protocolVersion);
   EXPECT_EQ(expected_cipher_suite_, info_.cipherSuite);
 
   rv = SSL_GetCipherSuiteInfo(info_.cipherSuite, &csinfo_, sizeof(csinfo_));
   EXPECT_EQ(SECSuccess, rv);
--- a/gtests/ssl_gtest/tls_agent.h
+++ b/gtests/ssl_gtest/tls_agent.h
@@ -90,16 +90,17 @@ class TlsAgent : public PollTarget {
     adapter_->SetPacketFilter(filter);
   }
 
   void DeletePacketFilter() { adapter_->SetPacketFilter(nullptr); }
 
   void StartConnect(PRFileDesc* model = nullptr);
   void CheckKEA(SSLKEAType kea_type, SSLNamedGroup group,
                 size_t kea_size = 0) const;
+  void CheckOriginalKEA(SSLNamedGroup kea_group) const;
   void CheckAuthType(SSLAuthType auth_type,
                      SSLSignatureScheme sig_scheme) const;
 
   void DisableAllCiphers();
   void EnableCiphersByAuthType(SSLAuthType authType);
   void EnableCiphersByKeyExchange(SSLKEAType kea);
   void EnableGroupsByKeyExchange(SSLKEAType kea);
   void EnableGroupsByAuthType(SSLAuthType authType);
--- a/gtests/ssl_gtest/tls_connect.cc
+++ b/gtests/ssl_gtest/tls_connect.cc
@@ -108,16 +108,17 @@ TlsConnectTestBase::TlsConnectTestBase(S
                                        uint16_t version)
     : variant_(variant),
       client_(new TlsAgent(TlsAgent::kClient, TlsAgent::CLIENT, variant_)),
       server_(new TlsAgent(TlsAgent::kServerRsa, TlsAgent::SERVER, variant_)),
       client_model_(nullptr),
       server_model_(nullptr),
       version_(version),
       expected_resumption_mode_(RESUME_NONE),
+      expected_resumptions_(0),
       session_ids_(),
       expect_extended_master_secret_(false),
       expect_early_data_accepted_(false),
       skip_version_checks_(false) {
   std::string v;
   if (variant_ == ssl_variant_datagram &&
       version_ == SSL_LIBRARY_VERSION_TLS_1_1) {
     v = "1.0";
@@ -215,22 +216,25 @@ void TlsConnectTestBase::Reset(const std
   if (skip_version_checks_) {
     client_->SkipVersionChecks();
     server_->SkipVersionChecks();
   }
 
   Init();
 }
 
-void TlsConnectTestBase::ExpectResumption(SessionResumptionMode expected) {
+void TlsConnectTestBase::ExpectResumption(SessionResumptionMode expected,
+                                          uint8_t num_resumptions) {
   expected_resumption_mode_ = expected;
   if (expected != RESUME_NONE) {
     client_->ExpectResumption();
     server_->ExpectResumption();
+    expected_resumptions_ = num_resumptions;
   }
+  EXPECT_EQ(expected_resumptions_ == 0, expected == RESUME_NONE);
 }
 
 void TlsConnectTestBase::EnsureTlsSetup() {
   EXPECT_TRUE(server_->EnsureTlsSetup(server_model_ ? server_model_->ssl_fd()
                                                     : nullptr));
   EXPECT_TRUE(client_->EnsureTlsSetup(client_model_ ? client_model_->ssl_fd()
                                                     : nullptr));
 }
@@ -310,20 +314,22 @@ void TlsConnectTestBase::CheckConnected(
   CheckResumption(expected_resumption_mode_);
   client_->CheckSecretsDestroyed();
   server_->CheckSecretsDestroyed();
 }
 
 void TlsConnectTestBase::CheckKeys(SSLKEAType kea_type, SSLNamedGroup kea_group,
                                    SSLAuthType auth_type,
                                    SSLSignatureScheme sig_scheme) const {
-  client_->CheckKEA(kea_type, kea_group);
-  server_->CheckKEA(kea_type, kea_group);
+  if (kea_group != ssl_grp_none) {
+    client_->CheckKEA(kea_type, kea_group);
+    server_->CheckKEA(kea_type, kea_group);
+  }
+  server_->CheckAuthType(auth_type, sig_scheme);
   client_->CheckAuthType(auth_type, sig_scheme);
-  server_->CheckAuthType(auth_type, sig_scheme);
 }
 
 void TlsConnectTestBase::CheckKeys(SSLKEAType kea_type,
                                    SSLAuthType auth_type) const {
   SSLNamedGroup group;
   switch (kea_type) {
     case ssl_kea_ecdh:
       group = ssl_grp_ec_curve25519;
@@ -368,16 +374,27 @@ void TlsConnectTestBase::CheckKeys(SSLKE
   }
   CheckKeys(kea_type, group, auth_type, scheme);
 }
 
 void TlsConnectTestBase::CheckKeys() const {
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
 }
 
+void TlsConnectTestBase::CheckKeysResumption(SSLKEAType kea_type,
+                                             SSLNamedGroup kea_group,
+                                             SSLNamedGroup original_kea_group,
+                                             SSLAuthType auth_type,
+                                             SSLSignatureScheme sig_scheme) {
+  CheckKeys(kea_type, kea_group, auth_type, sig_scheme);
+  EXPECT_TRUE(expected_resumption_mode_ != RESUME_NONE);
+  client_->CheckOriginalKEA(original_kea_group);
+  server_->CheckOriginalKEA(original_kea_group);
+}
+
 void TlsConnectTestBase::ConnectExpectFail() {
   server_->StartConnect();
   client_->StartConnect();
   Handshake();
   ASSERT_EQ(TlsAgent::STATE_ERROR, client_->state());
   ASSERT_EQ(TlsAgent::STATE_ERROR, server_->state());
 }
 
@@ -472,31 +489,31 @@ void TlsConnectTestBase::ConfigureSessio
     EXPECT_EQ(SECSuccess,
               SSL_SetSessionTicketKeyPair(pubKey.get(), privKey.get()));
   }
 }
 
 void TlsConnectTestBase::CheckResumption(SessionResumptionMode expected) {
   EXPECT_NE(RESUME_BOTH, expected);
 
-  int resume_count = expected ? 1 : 0;
-  int stateless_count = (expected & RESUME_TICKET) ? 1 : 0;
+  int resume_count = expected ? expected_resumptions_ : 0;
+  int stateless_count = (expected & RESUME_TICKET) ? expected_resumptions_ : 0;
 
   // Note: hch == server counter; hsh == client counter.
   SSL3Statistics* stats = SSL_GetStatistics();
   EXPECT_EQ(resume_count, stats->hch_sid_cache_hits);
   EXPECT_EQ(resume_count, stats->hsh_sid_cache_hits);
 
   EXPECT_EQ(stateless_count, stats->hch_sid_stateless_resumes);
   EXPECT_EQ(stateless_count, stats->hsh_sid_stateless_resumes);
 
   if (expected != RESUME_NONE) {
     if (client_->version() < SSL_LIBRARY_VERSION_TLS_1_3) {
       // Check that the last two session ids match.
-      ASSERT_EQ(2U, session_ids_.size());
+      ASSERT_EQ(1U + expected_resumptions_, session_ids_.size());
       EXPECT_EQ(session_ids_[session_ids_.size() - 1],
                 session_ids_[session_ids_.size() - 2]);
     } else {
       // TLS 1.3 only uses tickets.
       EXPECT_TRUE(expected & RESUME_TICKET);
     }
   }
 }
--- a/gtests/ssl_gtest/tls_connect.h
+++ b/gtests/ssl_gtest/tls_connect.h
@@ -76,25 +76,31 @@ class TlsConnectTestBase : public ::test
   void ConnectWithCipherSuite(uint16_t cipher_suite);
   // Check that the keys used in the handshake match expectations.
   void CheckKeys(SSLKEAType kea_type, SSLNamedGroup kea_group,
                  SSLAuthType auth_type, SSLSignatureScheme sig_scheme) const;
   // This version guesses some of the values.
   void CheckKeys(SSLKEAType kea_type, SSLAuthType auth_type) const;
   // This version assumes defaults.
   void CheckKeys() const;
+  // Check that keys on resumed sessions.
+  void CheckKeysResumption(SSLKEAType kea_type, SSLNamedGroup kea_group,
+                           SSLNamedGroup original_kea_group,
+                           SSLAuthType auth_type,
+                           SSLSignatureScheme sig_scheme);
   void CheckGroups(const DataBuffer& groups,
                    std::function<void(SSLNamedGroup)> check_group);
   void CheckShares(const DataBuffer& shares,
                    std::function<void(SSLNamedGroup)> check_group);
 
   void ConfigureVersion(uint16_t version);
   void SetExpectedVersion(uint16_t version);
   // Expect resumption of a particular type.
-  void ExpectResumption(SessionResumptionMode expected);
+  void ExpectResumption(SessionResumptionMode expected,
+                        uint8_t num_resumed = 1);
   void DisableAllCiphers();
   void EnableOnlyStaticRsaCiphers();
   void EnableOnlyDheCiphers();
   void EnableSomeEcdhCiphers();
   void EnableExtendedMasterSecret();
   void ConfigureSessionCache(SessionResumptionMode client,
                              SessionResumptionMode server);
   void EnableAlpn();
@@ -118,16 +124,17 @@ class TlsConnectTestBase : public ::test
  protected:
   SSLProtocolVariant variant_;
   std::shared_ptr<TlsAgent> client_;
   std::shared_ptr<TlsAgent> server_;
   std::unique_ptr<TlsAgent> client_model_;
   std::unique_ptr<TlsAgent> server_model_;
   uint16_t version_;
   SessionResumptionMode expected_resumption_mode_;
+  uint8_t expected_resumptions_;
   std::vector<std::vector<uint8_t>> session_ids_;
 
   // A simple value of "a", "b".  Note that the preferred value of "a" is placed
   // at the end, because the NSS API follows the now defunct NPN specification,
   // which places the preferred (and default) entry at the end of the list.
   // NSS will move this final entry to the front when used with ALPN.
   const uint8_t alpn_dummy_val_[4] = {0x01, 0x62, 0x01, 0x61};
 
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -6904,16 +6904,18 @@ ssl3_HandleServerHelloPart2(sslSocket *s
                 errCode = SSL_ERROR_MISSING_EXTENDED_MASTER_SECRET;
                 goto alert_loser;
             }
 
             ss->sec.authType = sid->authType;
             ss->sec.authKeyBits = sid->authKeyBits;
             ss->sec.keaType = sid->keaType;
             ss->sec.keaKeyBits = sid->keaKeyBits;
+            ss->sec.originalKeaGroup = ssl_LookupNamedGroup(sid->keaGroup);
+            ss->sec.signatureScheme = sid->sigScheme;
 
             if (sid->u.ssl3.keys.msIsWrapped) {
                 PK11SlotInfo *slot;
                 PK11SymKey *wrapKey; /* wrapping key */
                 CK_FLAGS keyFlags = 0;
 
                 /* unwrap master secret */
                 slot = SECMOD_LookupSlot(sid->u.ssl3.masterModuleID,
@@ -7914,16 +7916,17 @@ ssl3_NewSessionID(sslSocket *ss, PRBool 
     }
     sid->peerID = (ss->peerID == NULL) ? NULL : PORT_Strdup(ss->peerID);
     sid->urlSvrName = (ss->url == NULL) ? NULL : PORT_Strdup(ss->url);
     sid->addr = ss->sec.ci.peer;
     sid->port = ss->sec.ci.port;
     sid->references = 1;
     sid->cached = never_cached;
     sid->version = ss->version;
+    sid->sigScheme = ssl_sig_none;
 
     sid->u.ssl3.keys.resumable = PR_TRUE;
     sid->u.ssl3.policy = SSL_ALLOWED;
     sid->u.ssl3.clientWriteKey = NULL;
     sid->u.ssl3.serverWriteKey = NULL;
     sid->u.ssl3.keys.extendedMasterSecretUsed = PR_FALSE;
 
     if (is_server) {
@@ -8887,16 +8890,18 @@ compression_found:
             if (ss->statelessResume)
                 SSL_AtomicIncrementLong(&ssl3stats.hch_sid_stateless_resumes);
             ss->ssl3.hs.isResuming = PR_TRUE;
 
             ss->sec.authType = sid->authType;
             ss->sec.authKeyBits = sid->authKeyBits;
             ss->sec.keaType = sid->keaType;
             ss->sec.keaKeyBits = sid->keaKeyBits;
+            ss->sec.originalKeaGroup = ssl_LookupNamedGroup(sid->keaGroup);
+            ss->sec.signatureScheme = sid->sigScheme;
 
             ss->sec.localCert =
                 CERT_DupCertificate(ss->sec.serverCert->serverCert);
 
             /* Copy cached name in to pending spec */
             if (sid != NULL &&
                 sid->version > SSL_LIBRARY_VERSION_3_0 &&
                 sid->u.ssl3.srvName.len && sid->u.ssl3.srvName.data) {
@@ -8958,16 +8963,17 @@ compression_found:
         } while (0);
 
     if (haveSpecWriteLock) {
         ssl_ReleaseSpecWriteLock(ss);
         haveSpecWriteLock = PR_FALSE;
     }
 
     if (sid) { /* we had a sid, but it's no longer valid, free it */
+        ss->statelessResume = PR_FALSE;
         SSL_AtomicIncrementLong(&ssl3stats.hch_sid_cache_not_ok);
         ss->sec.uncache(sid);
         ssl_FreeSID(sid);
         sid = NULL;
     }
     SSL_AtomicIncrementLong(&ssl3stats.hch_sid_cache_misses);
 
     /* We only send a session ticket extension if the client supports
@@ -11531,16 +11537,22 @@ ssl3_FillInCachedSID(sslSocket *ss, sslS
     sid->u.ssl3.cipherSuite = ss->ssl3.hs.cipher_suite;
     sid->u.ssl3.compression = ss->ssl3.hs.compression;
     sid->u.ssl3.policy = ss->ssl3.policy;
     sid->version = ss->version;
     sid->authType = ss->sec.authType;
     sid->authKeyBits = ss->sec.authKeyBits;
     sid->keaType = ss->sec.keaType;
     sid->keaKeyBits = ss->sec.keaKeyBits;
+    if (ss->sec.keaGroup) {
+        sid->keaGroup = ss->sec.keaGroup->name;
+    } else {
+        sid->keaGroup = ssl_grp_none;
+    }
+    sid->sigScheme = ss->sec.signatureScheme;
     sid->lastAccessTime = sid->creationTime = ssl_Time();
     sid->expirationTime = sid->creationTime + ssl3_sid_timeout;
     sid->localCert = CERT_DupCertificate(ss->sec.localCert);
     if (ss->sec.isServer) {
         sid->namedCurve = ss->sec.serverCert->namedCurve;
     }
 
     if (ss->xtnData.nextProtoState != SSL_NEXT_PROTO_NO_SUPPORT &&
--- a/lib/ssl/ssl3exthandle.c
+++ b/lib/ssl/ssl3exthandle.c
@@ -797,17 +797,17 @@ ssl3_ClientHandleStatusRequestXtn(const 
     }
 
     /* Keep track of negotiated extensions. */
     xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
     return SECSuccess;
 }
 
 PRUint32 ssl_ticket_lifetime = 2 * 24 * 60 * 60; /* 2 days in seconds */
-#define TLS_EX_SESS_TICKET_VERSION (0x0105)
+#define TLS_EX_SESS_TICKET_VERSION (0x0106)
 
 /*
  * Called from ssl3_SendNewSessionTicket, tls13_SendNewSessionTicket
  */
 SECStatus
 ssl3_EncodeSessionTicket(sslSocket *ss,
                          const NewSessionTicket *ticket,
                          SECItem *ticket_data)
@@ -877,16 +877,17 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
     alpnSelection = &ss->xtnData.nextProto;
 
     plaintext_length =
         sizeof(PRUint16)                       /* ticket version */
         + sizeof(SSL3ProtocolVersion)          /* ssl_version */
         + sizeof(ssl3CipherSuite)              /* ciphersuite */
         + 1                                    /* compression */
         + 10                                   /* cipher spec parameters */
+        + 8                                    /* kea group and sig scheme */
         + 1                                    /* certType arguments */
         + 1                                    /* SessionTicket.ms_is_wrapped */
         + 4                                    /* msWrapMech */
         + 2                                    /* master_secret.length */
         + ms_item.len                          /* master_secret */
         + 1                                    /* client_auth_type */
         + cert_length                          /* cert */
         + 2 + srvName->len                     /* name len + length field */
@@ -932,16 +933,29 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
     if (rv != SECSuccess)
         goto loser;
     rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.keaType, 1);
     if (rv != SECSuccess)
         goto loser;
     rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.keaKeyBits, 4);
     if (rv != SECSuccess)
         goto loser;
+    if (ss->sec.keaGroup) {
+        rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.keaGroup->name, 4);
+        if (rv != SECSuccess)
+            goto loser;
+    } else {
+        /* No kea group. Write 0 as invalid value. */
+        rv = ssl3_AppendNumberToItem(&plaintext, 0, 4);
+        if (rv != SECSuccess)
+            goto loser;
+    }
+    rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.signatureScheme, 4);
+    if (rv != SECSuccess)
+        goto loser;
 
     /* certificate type */
     PORT_Assert(SSL_CERT_IS(ss->sec.serverCert, ss->sec.authType));
     if (SSL_CERT_IS_EC(ss->sec.serverCert)) {
         const sslServerCert *cert = ss->sec.serverCert;
         PORT_Assert(cert->namedCurve);
         /* EC curves only use the second of the two bytes. */
         PORT_Assert(cert->namedCurve->name < 256);
@@ -1160,16 +1174,28 @@ ssl_ParseSessionTicket(sslSocket *ss, co
     }
     parsedTicket->keaType = (SSLKEAType)temp;
     rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
     if (rv != SECSuccess) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
     parsedTicket->keaKeyBits = temp;
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    parsedTicket->originalKeaGroup = temp;
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    parsedTicket->signatureScheme = (SSLSignatureScheme)temp;
 
     /* Read the optional named curve. */
     rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
     if (rv != SECSuccess) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
     if (parsedTicket->authType == ssl_auth_ecdsa ||
@@ -1314,17 +1340,19 @@ ssl_CreateSIDFromTicket(sslSocket *ss, c
     /* Copy over parameters. */
     sid->version = parsedTicket->ssl_version;
     sid->u.ssl3.cipherSuite = parsedTicket->cipher_suite;
     sid->u.ssl3.compression = parsedTicket->compression_method;
     sid->authType = parsedTicket->authType;
     sid->authKeyBits = parsedTicket->authKeyBits;
     sid->keaType = parsedTicket->keaType;
     sid->keaKeyBits = parsedTicket->keaKeyBits;
+    sid->keaGroup = parsedTicket->originalKeaGroup;
     sid->namedCurve = parsedTicket->namedCurve;
+    sid->sigScheme = parsedTicket->signatureScheme;
 
     rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.locked.sessionTicket.ticket,
                           rawTicket);
     if (rv != SECSuccess) {
         goto loser;
     }
     sid->u.ssl3.locked.sessionTicket.flags = parsedTicket->flags;
     sid->u.ssl3.locked.sessionTicket.max_early_data_size =
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -548,16 +548,18 @@ struct sslSessionIDStr {
 
     PRUint32 creationTime;   /* seconds since Jan 1, 1970 */
     PRUint32 expirationTime; /* seconds since Jan 1, 1970 */
 
     SSLAuthType authType;
     PRUint32 authKeyBits;
     SSLKEAType keaType;
     PRUint32 keaKeyBits;
+    SSLNamedGroup keaGroup;
+    SSLSignatureScheme sigScheme;
 
     union {
         struct {
             /* values that are copied into the server's on-disk SID cache. */
             PRUint8 sessionIDLength;
             PRUint8 sessionID[SSL3_SESSIONID_BYTES];
 
             ssl3CipherSuite cipherSuite;
@@ -997,16 +999,18 @@ typedef struct SessionTicketStr {
     PRBool valid;
     SSL3ProtocolVersion ssl_version;
     ssl3CipherSuite cipher_suite;
     SSLCompressionMethod compression_method;
     SSLAuthType authType;
     PRUint32 authKeyBits;
     SSLKEAType keaType;
     PRUint32 keaKeyBits;
+    SSLNamedGroup originalKeaGroup;
+    SSLSignatureScheme signatureScheme;
     const sslNamedGroupDef *namedCurve; /* For certificate lookup. */
 
     /*
      * msWrapMech contains a meaningful value only if ms_is_wrapped is true.
      */
     PRUint8 ms_is_wrapped;
     CK_MECHANISM_TYPE msWrapMech;
     PRUint16 ms_length;
@@ -1063,16 +1067,17 @@ struct sslSecurityInfoStr {
     SECKEYPublicKey *peerKey;
 
     SSLAuthType authType;
     PRUint32 authKeyBits;
     SSLSignatureScheme signatureScheme;
     SSLKEAType keaType;
     PRUint32 keaKeyBits;
     const sslNamedGroupDef *keaGroup;
+    const sslNamedGroupDef *originalKeaGroup;
     /* The selected certificate (for servers only). */
     const sslServerCert *serverCert;
 
     /*
     ** Procs used for SID cache (nonce) management.
     ** Different implementations exist for clients/servers
     ** The lookup proc is only used for servers.  Baloney!
     */
--- a/lib/ssl/sslinfo.c
+++ b/lib/ssl/sslinfo.c
@@ -73,21 +73,32 @@ SSL_GetChannelInfo(PRFileDesc *fd, SSLCh
             if (rv != SECSuccess) {
                 return SECFailure; /* Error code already set. */
             }
             inf.symCipher = cinfo.symCipher;
             inf.macAlgorithm = cinfo.macAlgorithm;
             /* Get these fromm |ss->sec| because that is accurate
              * even with TLS 1.3 disaggregated cipher suites. */
             inf.keaType = ss->sec.keaType;
-            inf.keaGroup = ss->sec.keaGroup ? ss->sec.keaGroup->name : ssl_grp_none;
+            inf.originalKeaGroup = ss->sec.originalKeaGroup
+                                       ? ss->sec.originalKeaGroup->name
+                                       : ssl_grp_none;
+            inf.keaGroup = ss->sec.keaGroup
+                               ? ss->sec.keaGroup->name
+                               : ssl_grp_none;
             inf.keaKeyBits = ss->sec.keaKeyBits;
             inf.authType = ss->sec.authType;
             inf.authKeyBits = ss->sec.authKeyBits;
             inf.signatureScheme = ss->sec.signatureScheme;
+            /* If this is a resumed session, signatureScheme isn't set in ss->sec.
+             * Use the signature scheme from the previous handshake. */
+            if (inf.signatureScheme == ssl_sig_none && sid->sigScheme) {
+                inf.signatureScheme = sid->sigScheme;
+            }
+            inf.resumed = ss->statelessResume || ss->ssl3.hs.isResuming;
         }
         if (sid) {
             unsigned int sidLen;
 
             inf.creationTime = sid->creationTime;
             inf.lastAccessTime = sid->lastAccessTime;
             inf.expirationTime = sid->expirationTime;
             inf.extendedMasterSecretUsed =
--- a/lib/ssl/sslsnce.c
+++ b/lib/ssl/sslsnce.c
@@ -93,17 +93,19 @@ struct sidCacheEntryStr {
     /*  2 */ PRUint16 version;
     /*  1 */ PRUint8 valid;
     /*  1 */ PRUint8 sessionIDLength;
     /* 32 */ PRUint8 sessionID[SSL3_SESSIONID_BYTES];
     /*  2 */ PRUint16 authType;
     /*  2 */ PRUint16 authKeyBits;
     /*  2 */ PRUint16 keaType;
     /*  2 */ PRUint16 keaKeyBits;
-    /* 72  - common header total */
+    /*  4 */ PRUint32 signatureScheme;
+    /*  4 */ PRUint32 keaGroup;
+    /* 80  - common header total */
 
     union {
         struct {
             /*  2 */ ssl3CipherSuite cipherSuite;
             /*  2 */ PRUint16 compression; /* SSLCompressionMethod */
 
             /* 54 */ ssl3SidKeys keys; /* keys, wrapped as needed. */
 
@@ -111,17 +113,17 @@ struct sidCacheEntryStr {
             /*  4 */ PRInt32 certIndex;
             /*  4 */ PRInt32 srvNameIndex;
             /* 32 */ PRUint8 srvNameHash[SHA256_LENGTH]; /* SHA256 name hash */
             /*  2 */ PRUint16 namedCurve;
 /*104 */} ssl3;
 
 /* force sizeof(sidCacheEntry) to be a multiple of cache line size */
 struct {
-    /*120 */ PRUint8 filler[120]; /* 72+120==192, a multiple of 16 */
+    /*112 */ PRUint8 filler[112]; /* 80+112==192, a multiple of 16 */
 } forceSize;
     } u;
 };
 typedef struct sidCacheEntryStr sidCacheEntry;
 
 /* The length of this struct is supposed to be a power of 2, e.g. 4KB */
 struct certCacheEntryStr {
     PRUint16 certLength;                     /*    2 */
@@ -427,16 +429,18 @@ ConvertFromSID(sidCacheEntry *to, sslSes
     to->addr = from->addr;
     to->creationTime = from->creationTime;
     to->lastAccessTime = from->lastAccessTime;
     to->expirationTime = from->expirationTime;
     to->authType = from->authType;
     to->authKeyBits = from->authKeyBits;
     to->keaType = from->keaType;
     to->keaKeyBits = from->keaKeyBits;
+    to->keaGroup = from->keaGroup;
+    to->signatureScheme = from->sigScheme;
 
     to->u.ssl3.cipherSuite = from->u.ssl3.cipherSuite;
     to->u.ssl3.compression = (PRUint16)from->u.ssl3.compression;
     to->u.ssl3.keys = from->u.ssl3.keys;
     to->u.ssl3.masterWrapMech = from->u.ssl3.masterWrapMech;
     to->sessionIDLength = from->u.ssl3.sessionIDLength;
     to->u.ssl3.certIndex = -1;
     to->u.ssl3.srvNameIndex = -1;
@@ -536,16 +540,18 @@ ConvertToSID(sidCacheEntry *from,
     to->expirationTime = from->expirationTime;
     to->cached = in_server_cache;
     to->addr = from->addr;
     to->references = 1;
     to->authType = from->authType;
     to->authKeyBits = from->authKeyBits;
     to->keaType = from->keaType;
     to->keaKeyBits = from->keaKeyBits;
+    to->keaGroup = from->keaGroup;
+    to->sigScheme = from->signatureScheme;
 
     return to;
 
 loser:
     if (to) {
         SECITEM_FreeItem(&to->u.ssl3.srvName, PR_FALSE);
         PORT_Free(to);
     }
--- a/lib/ssl/sslt.h
+++ b/lib/ssl/sslt.h
@@ -270,16 +270,24 @@ typedef struct SSLChannelInfoStr {
     /* These fields have the same meaning as in SSLCipherSuiteInfo. */
     SSLKEAType keaType;
     SSLNamedGroup keaGroup;
     SSLCipherAlgorithm symCipher;
     SSLMACAlgorithm macAlgorithm;
     SSLAuthType authType;
     SSLSignatureScheme signatureScheme;
 
+    /* The following fields were added in NSS 3.34. */
+    /* When the session was resumed this holds the key exchange group of the
+     * original handshake. */
+    SSLNamedGroup originalKeaGroup;
+    /* This field is PR_TRUE when the session is resumed and PR_FALSE
+     * otherwise. */
+    PRBool resumed;
+
     /* When adding new fields to this structure, please document the
      * NSS version in which they were added. */
 } SSLChannelInfo;
 
 /* Preliminary channel info */
 #define ssl_preinfo_version (1U << 0)
 #define ssl_preinfo_cipher_suite (1U << 1)
 #define ssl_preinfo_all (ssl_preinfo_version | ssl_preinfo_cipher_suite)
--- a/lib/ssl/tls13con.c
+++ b/lib/ssl/tls13con.c
@@ -920,16 +920,18 @@ 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.authType = sid->authType;
     ss->sec.authKeyBits = sid->authKeyBits;
+    ss->sec.originalKeaGroup = ssl_LookupNamedGroup(sid->keaGroup);
+    ss->sec.signatureScheme = sid->sigScheme;
 }
 
 /* Check whether resumption-PSK is allowed. */
 static PRBool
 tls13_CanResume(sslSocket *ss, const sslSessionID *sid)
 {
     const sslServerCert *sc;