Bug 1086145 - Improving handshake test coverage, r=wtc
authorMartin Thomson <martin.thomson@gmail.com>
Fri, 20 Mar 2015 14:33:51 -0700
changeset 11431 1865635f5df5b827623371aae1b1d7c4cea52657
parent 11430 6b4770c76bc86fd7cb68e2383fd8c51951a4f2f0
child 11432 657dbfcff53f26773d008f2f1da71e30cab6eba6
push id615
push usermartin.thomson@gmail.com
push dateThu, 16 Apr 2015 18:37:45 +0000
reviewerswtc
bugs1086145
Bug 1086145 - Improving handshake test coverage, r=wtc
external_tests/README
external_tests/ssl_gtest/manifest.mn
external_tests/ssl_gtest/ssl_extension_unittest.cc
external_tests/ssl_gtest/ssl_loopback_unittest.cc
external_tests/ssl_gtest/ssl_skip_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
tests/cert/cert.sh
--- a/external_tests/README
+++ b/external_tests/README
@@ -16,16 +16,16 @@ work do:
 
 This will run the certutil tests (generating a test db) and
 will finalize with a call to the ssl_gtest
 
 You should be able to run the unit tests manually as:
 
   ssl_gtest -d ${SSLGTESTDIR}
 
-Where $SSLGTESTDIR the directory created by ./all.sh or a manually
-created directory with a database containing a certificate called
-server (with its private keys)
+Where $SSLGTESTDIR is a directory with a database containing:
+ - an RSA certificate called server (with its private key)
+ - an ECDSA certificate called ecdsa (with its private key)
 
+A directory like this is created by ./all.sh and can be found
+in a directory named something like
 
-There is a very trivial set of tests that demonstrate some
-of the features.
-
+  tests_results/security/${hostname}.${NUMBER}/ssl_gtests
--- a/external_tests/ssl_gtest/manifest.mn
+++ b/external_tests/ssl_gtest/manifest.mn
@@ -4,16 +4,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 CORE_DEPTH = ../..
 DEPTH      = ../..
 MODULE = nss
 
 CPPSRCS = \
       ssl_loopback_unittest.cc \
       ssl_extension_unittest.cc \
+      ssl_skip_unittest.cc \
       ssl_gtest.cc \
       test_io.cc \
       tls_agent.cc \
       tls_connect.cc \
       tls_filter.cc \
       tls_parser.cc \
       $(NULL)
 
--- a/external_tests/ssl_gtest/ssl_extension_unittest.cc
+++ b/external_tests/ssl_gtest/ssl_extension_unittest.cc
@@ -263,30 +263,30 @@ class TlsExtensionTestBase : public TlsC
   void ClientHelloErrorTest(PacketFilter* filter,
                             uint8_t alert = kTlsAlertDecodeError) {
     auto alert_recorder = new TlsAlertRecorder();
     server_->SetPacketFilter(alert_recorder);
     if (filter) {
       client_->SetPacketFilter(filter);
     }
     ConnectExpectFail();
-    ASSERT_EQ(kTlsAlertFatal, alert_recorder->level());
-    ASSERT_EQ(alert, alert_recorder->description());
+    EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
+    EXPECT_EQ(alert, alert_recorder->description());
   }
 
   void ServerHelloErrorTest(PacketFilter* filter,
                             uint8_t alert = kTlsAlertDecodeError) {
     auto alert_recorder = new TlsAlertRecorder();
     client_->SetPacketFilter(alert_recorder);
     if (filter) {
       server_->SetPacketFilter(filter);
     }
     ConnectExpectFail();
-    ASSERT_EQ(kTlsAlertFatal, alert_recorder->level());
-    ASSERT_EQ(alert, alert_recorder->description());
+    EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
+    EXPECT_EQ(alert, alert_recorder->description());
   }
 
   static void InitSimpleSni(DataBuffer* extension) {
     const char* name = "host.name";
     const size_t namelen = PL_strlen(name);
     extension->Allocate(namelen + 5);
     extension->Write(0, namelen + 3, 2);
     extension->Write(2, static_cast<uint32_t>(0), 1); // 0 == hostname
@@ -489,57 +489,57 @@ TEST_P(TlsExtensionTest12Plus, Signature
 TEST_P(TlsExtensionTest12Plus, DISABLED_SignatureAlgorithmsSigUnsupported) {
   const uint8_t val[] = { 0x00, 0x02, 0x04, 0x99 };
   DataBuffer extension(val, sizeof(val));
   ClientHelloErrorTest(new TlsExtensionReplacer(ssl_signature_algorithms_xtn,
                                                 extension));
 }
 
 TEST_P(TlsExtensionTestGeneric, SupportedCurvesShort) {
-  EnableSomeECDHECiphers();
+  EnableSomeEcdheCiphers();
   const uint8_t val[] = { 0x00, 0x01, 0x00 };
   DataBuffer extension(val, sizeof(val));
   ClientHelloErrorTest(new TlsExtensionReplacer(ssl_elliptic_curves_xtn,
                                                 extension));
 }
 
 TEST_P(TlsExtensionTestGeneric, SupportedCurvesBadLength) {
-  EnableSomeECDHECiphers();
+  EnableSomeEcdheCiphers();
   const uint8_t val[] = { 0x09, 0x99, 0x00, 0x00 };
   DataBuffer extension(val, sizeof(val));
   ClientHelloErrorTest(new TlsExtensionReplacer(ssl_elliptic_curves_xtn,
                                                 extension));
 }
 
 TEST_P(TlsExtensionTestGeneric, SupportedCurvesTrailingData) {
-  EnableSomeECDHECiphers();
+  EnableSomeEcdheCiphers();
   const uint8_t val[] = { 0x00, 0x02, 0x00, 0x00, 0x00 };
   DataBuffer extension(val, sizeof(val));
   ClientHelloErrorTest(new TlsExtensionReplacer(ssl_elliptic_curves_xtn,
                                                 extension));
 }
 
 TEST_P(TlsExtensionTestGeneric, SupportedPointsEmpty) {
-  EnableSomeECDHECiphers();
+  EnableSomeEcdheCiphers();
   const uint8_t val[] = { 0x00 };
   DataBuffer extension(val, sizeof(val));
   ClientHelloErrorTest(new TlsExtensionReplacer(ssl_ec_point_formats_xtn,
                                                 extension));
 }
 
 TEST_P(TlsExtensionTestGeneric, SupportedPointsBadLength) {
-  EnableSomeECDHECiphers();
+  EnableSomeEcdheCiphers();
   const uint8_t val[] = { 0x99, 0x00, 0x00 };
   DataBuffer extension(val, sizeof(val));
   ClientHelloErrorTest(new TlsExtensionReplacer(ssl_ec_point_formats_xtn,
                                                 extension));
 }
 
 TEST_P(TlsExtensionTestGeneric, SupportedPointsTrailingData) {
-  EnableSomeECDHECiphers();
+  EnableSomeEcdheCiphers();
   const uint8_t val[] = { 0x01, 0x00, 0x00 };
   DataBuffer extension(val, sizeof(val));
   ClientHelloErrorTest(new TlsExtensionReplacer(ssl_ec_point_formats_xtn,
                                                 extension));
 }
 
 TEST_P(TlsExtensionTestGeneric, RenegotiationInfoBadLength) {
   const uint8_t val[] = { 0x99 };
--- a/external_tests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/external_tests/ssl_gtest/ssl_loopback_unittest.cc
@@ -10,17 +10,17 @@
 #include <memory>
 
 #include "tls_parser.h"
 #include "tls_filter.h"
 #include "tls_connect.h"
 
 namespace nss_test {
 
-class TlsServerKeyExchangeECDHE {
+class TlsServerKeyExchangeEcdhe {
  public:
   bool Parse(const DataBuffer& buffer) {
     TlsParser parser(buffer);
 
     uint8_t curve_type;
     if (!parser.Read(&curve_type)) {
       return false;
     }
@@ -40,150 +40,128 @@ class TlsServerKeyExchangeECDHE {
   DataBuffer public_key_;
 };
 
 TEST_P(TlsConnectGeneric, SetupOnly) {}
 
 TEST_P(TlsConnectGeneric, Connect) {
   Connect();
   client_->CheckVersion(std::get<1>(GetParam()));
+  client_->CheckAuthType(ssl_auth_rsa);
 }
 
 TEST_P(TlsConnectGeneric, ConnectResumed) {
   ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID);
   Connect();
 
-  Reset();
+  ResetRsa();
   Connect();
   CheckResumption(RESUME_SESSIONID);
 }
 
 TEST_P(TlsConnectGeneric, ConnectClientCacheDisabled) {
   ConfigureSessionCache(RESUME_NONE, RESUME_SESSIONID);
   Connect();
-  Reset();
+  ResetRsa();
   Connect();
   CheckResumption(RESUME_NONE);
 }
 
 TEST_P(TlsConnectGeneric, ConnectServerCacheDisabled) {
   ConfigureSessionCache(RESUME_SESSIONID, RESUME_NONE);
   Connect();
-  Reset();
+  ResetRsa();
   Connect();
   CheckResumption(RESUME_NONE);
 }
 
 TEST_P(TlsConnectGeneric, ConnectSessionCacheDisabled) {
   ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
   Connect();
-  Reset();
+  ResetRsa();
   Connect();
   CheckResumption(RESUME_NONE);
 }
 
 TEST_P(TlsConnectGeneric, ConnectResumeSupportBoth) {
   // This prefers tickets.
   ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
   Connect();
 
-  Reset();
+  ResetRsa();
   ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
   Connect();
   CheckResumption(RESUME_TICKET);
 }
 
 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();
 
-  Reset();
+  ResetRsa();
   ConfigureSessionCache(RESUME_TICKET, RESUME_BOTH);
   Connect();
   CheckResumption(RESUME_NONE);
 }
 
 TEST_P(TlsConnectGeneric, ConnectResumeClientBothTicketServerTicket) {
   // This causes a ticket resumption.
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   Connect();
 
-  Reset();
+  ResetRsa();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   Connect();
   CheckResumption(RESUME_TICKET);
 }
 
 TEST_P(TlsConnectGeneric, ConnectClientServerTicketOnly) {
   // This causes no resumption because the client needs the
   // session cache to resume even with tickets.
   ConfigureSessionCache(RESUME_TICKET, RESUME_TICKET);
   Connect();
 
-  Reset();
+  ResetRsa();
   ConfigureSessionCache(RESUME_TICKET, RESUME_TICKET);
   Connect();
   CheckResumption(RESUME_NONE);
 }
 
 TEST_P(TlsConnectGeneric, ConnectClientBothServerNone) {
   ConfigureSessionCache(RESUME_BOTH, RESUME_NONE);
   Connect();
 
-  Reset();
+  ResetRsa();
   ConfigureSessionCache(RESUME_BOTH, RESUME_NONE);
   Connect();
   CheckResumption(RESUME_NONE);
 }
 
 TEST_P(TlsConnectGeneric, ConnectClientNoneServerBoth) {
   ConfigureSessionCache(RESUME_NONE, RESUME_BOTH);
   Connect();
 
-  Reset();
+  ResetRsa();
   ConfigureSessionCache(RESUME_NONE, RESUME_BOTH);
   Connect();
   CheckResumption(RESUME_NONE);
 }
 
-TEST_P(TlsConnectGeneric, ConnectTLS_1_1_Only) {
-  EnsureTlsSetup();
-  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
-                           SSL_LIBRARY_VERSION_TLS_1_1);
-
-  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
-                           SSL_LIBRARY_VERSION_TLS_1_1);
-
-  Connect();
-
-  client_->CheckVersion(SSL_LIBRARY_VERSION_TLS_1_1);
-}
-
-TEST_P(TlsConnectGeneric, ConnectTLS_1_2_Only) {
-  EnsureTlsSetup();
-  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
-                           SSL_LIBRARY_VERSION_TLS_1_2);
-  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
-                           SSL_LIBRARY_VERSION_TLS_1_2);
-  Connect();
-  client_->CheckVersion(SSL_LIBRARY_VERSION_TLS_1_2);
-}
-
 TEST_P(TlsConnectGeneric, ResumeWithHigherVersion) {
   EnsureTlsSetup();
   ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID);
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_1);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_1);
   Connect();
 
-  Reset();
+  ResetRsa();
   EnsureTlsSetup();
   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);
   Connect();
   CheckResumption(RESUME_NONE);
   client_->CheckVersion(SSL_LIBRARY_VERSION_TLS_1_2);
@@ -191,87 +169,94 @@ TEST_P(TlsConnectGeneric, ResumeWithHigh
 
 TEST_P(TlsConnectGeneric, ConnectAlpn) {
   EnableAlpn();
   Connect();
   client_->CheckAlpn(SSL_NEXT_PROTO_SELECTED, "a");
   server_->CheckAlpn(SSL_NEXT_PROTO_NEGOTIATED, "a");
 }
 
+TEST_P(TlsConnectGeneric, ConnectEcdsa) {
+  ResetEcdsa();
+  Connect();
+  client_->CheckVersion(std::get<1>(GetParam()));
+  client_->CheckAuthType(ssl_auth_ecdsa);
+}
+
 TEST_P(TlsConnectDatagram, ConnectSrtp) {
   EnableSrtp();
   Connect();
   CheckSrtp();
 }
 
-TEST_P(TlsConnectStream, ConnectECDHE) {
-  EnableSomeECDHECiphers();
+TEST_P(TlsConnectStream, ConnectEcdhe) {
+  EnableSomeEcdheCiphers();
   Connect();
   client_->CheckKEAType(ssl_kea_ecdh);
 }
 
-TEST_P(TlsConnectStream, ConnectECDHETwiceReuseKey) {
-  EnableSomeECDHECiphers();
+TEST_P(TlsConnectStream, ConnectEcdheTwiceReuseKey) {
+  EnableSomeEcdheCiphers();
   TlsInspectorRecordHandshakeMessage* i1 =
       new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange);
   server_->SetPacketFilter(i1);
   Connect();
   client_->CheckKEAType(ssl_kea_ecdh);
-  TlsServerKeyExchangeECDHE dhe1;
-  ASSERT_TRUE(dhe1.Parse(i1->buffer()));
+  TlsServerKeyExchangeEcdhe dhe1;
+  EXPECT_TRUE(dhe1.Parse(i1->buffer()));
 
   // Restart
-  Reset();
+  ResetRsa();
   TlsInspectorRecordHandshakeMessage* i2 =
       new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange);
   server_->SetPacketFilter(i2);
-  EnableSomeECDHECiphers();
+  EnableSomeEcdheCiphers();
   ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
   Connect();
   client_->CheckKEAType(ssl_kea_ecdh);
 
-  TlsServerKeyExchangeECDHE dhe2;
-  ASSERT_TRUE(dhe2.Parse(i2->buffer()));
+  TlsServerKeyExchangeEcdhe dhe2;
+  EXPECT_TRUE(dhe2.Parse(i2->buffer()));
 
   // Make sure they are the same.
-  ASSERT_EQ(dhe1.public_key_.len(), dhe2.public_key_.len());
-  ASSERT_TRUE(!memcmp(dhe1.public_key_.data(), dhe2.public_key_.data(),
+  EXPECT_EQ(dhe1.public_key_.len(), dhe2.public_key_.len());
+  EXPECT_TRUE(!memcmp(dhe1.public_key_.data(), dhe2.public_key_.data(),
                       dhe1.public_key_.len()));
 }
 
-TEST_P(TlsConnectStream, ConnectECDHETwiceNewKey) {
-  EnableSomeECDHECiphers();
+TEST_P(TlsConnectStream, ConnectEcdheTwiceNewKey) {
+  EnableSomeEcdheCiphers();
   SECStatus rv =
       SSL_OptionSet(server_->ssl_fd(), SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
-  ASSERT_EQ(SECSuccess, rv);
+  EXPECT_EQ(SECSuccess, rv);
   TlsInspectorRecordHandshakeMessage* i1 =
       new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange);
   server_->SetPacketFilter(i1);
   Connect();
   client_->CheckKEAType(ssl_kea_ecdh);
-  TlsServerKeyExchangeECDHE dhe1;
-  ASSERT_TRUE(dhe1.Parse(i1->buffer()));
+  TlsServerKeyExchangeEcdhe dhe1;
+  EXPECT_TRUE(dhe1.Parse(i1->buffer()));
 
   // Restart
-  Reset();
-  EnableSomeECDHECiphers();
+  ResetRsa();
+  EnableSomeEcdheCiphers();
   rv = SSL_OptionSet(server_->ssl_fd(), SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
-  ASSERT_EQ(SECSuccess, rv);
+  EXPECT_EQ(SECSuccess, rv);
   TlsInspectorRecordHandshakeMessage* i2 =
       new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange);
   server_->SetPacketFilter(i2);
   ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
   Connect();
   client_->CheckKEAType(ssl_kea_ecdh);
 
-  TlsServerKeyExchangeECDHE dhe2;
-  ASSERT_TRUE(dhe2.Parse(i2->buffer()));
+  TlsServerKeyExchangeEcdhe dhe2;
+  EXPECT_TRUE(dhe2.Parse(i2->buffer()));
 
   // Make sure they are different.
-  ASSERT_FALSE((dhe1.public_key_.len() == dhe2.public_key_.len()) &&
+  EXPECT_FALSE((dhe1.public_key_.len() == dhe2.public_key_.len()) &&
                (!memcmp(dhe1.public_key_.data(), dhe2.public_key_.data(),
                         dhe1.public_key_.len())));
 }
 
 INSTANTIATE_TEST_CASE_P(VariantsStream10, TlsConnectGeneric,
                         ::testing::Combine(
                           TlsConnectTestBase::kTlsModesStream,
                           TlsConnectTestBase::kTlsV10));
new file mode 100644
--- /dev/null
+++ b/external_tests/ssl_gtest/ssl_skip_unittest.cc
@@ -0,0 +1,167 @@
+/* -*- 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 "sslerr.h"
+
+#include "tls_parser.h"
+#include "tls_filter.h"
+#include "tls_connect.h"
+
+/*
+ * The tests in this file test that the TLS state machine is robust against
+ * attacks that alter the order of handshake messages.
+ *
+ * See <https://www.smacktls.com/smack.pdf> for a description of the problems
+ * that this sort of attack can enable.
+ */
+namespace nss_test {
+
+class TlsHandshakeSkipFilter : public TlsRecordFilter {
+ public:
+  // A TLS record filter that skips handshake messages of the identified type.
+  TlsHandshakeSkipFilter(uint8_t handshake_type)
+      : handshake_type_(handshake_type),
+        skipped_(false) {}
+
+ protected:
+  // Takes a record; if it is a handshake record, it removes the first handshake
+  // message that is of handshake_type_ type.
+  virtual bool FilterRecord(uint8_t content_type, uint16_t version,
+                            const DataBuffer& input, DataBuffer* output) {
+    if (content_type != kTlsHandshakeType) {
+      return false;
+    }
+
+    size_t output_offset = 0U;
+    output->Allocate(input.len());
+
+    TlsParser parser(input);
+    while (parser.remaining()) {
+      size_t start = parser.consumed();
+      uint8_t handshake_type;
+      if (!parser.Read(&handshake_type)) {
+        return false;
+      }
+      uint32_t length;
+      if (!TlsHandshakeFilter::ReadLength(&parser, version, &length)) {
+        return false;
+      }
+
+      if (!parser.Skip(length)) {
+        return false;
+      }
+
+      if (skipped_ || handshake_type != handshake_type_) {
+        size_t entire_length = parser.consumed() - start;
+        output->Write(output_offset, input.data() + start,
+                      entire_length);
+        // DTLS sequence numbers need to be rewritten
+        if (skipped_ && IsDtls(version)) {
+          output->data()[start + 5] -= 1;
+        }
+        output_offset += entire_length;
+      } else {
+        std::cerr << "Dropping handshake: "
+                  << static_cast<unsigned>(handshake_type_) << std::endl;
+        // We only need to report that the output contains changed data if we
+        // drop a handshake message.  But once we've skipped one message, we
+        // have to modify all subsequent handshake messages so that they include
+        // the correct DTLS sequence numbers.
+        skipped_ = true;
+      }
+    }
+    output->Truncate(output_offset);
+    return skipped_;
+  }
+
+ private:
+  // The type of handshake message to drop.
+  uint8_t handshake_type_;
+  // Whether this filter has ever skipped a handshake message.  Track this so
+  // that sequence numbers on DTLS handshake messages can be rewritten in
+  // subsequent calls.
+  bool skipped_;
+};
+
+class TlsSkipTest
+  : public TlsConnectTestBase,
+    public ::testing::WithParamInterface<std::tuple<std::string, uint16_t>> {
+
+ protected:
+  TlsSkipTest()
+    : TlsConnectTestBase(TlsConnectTestBase::ToMode(std::get<0>(GetParam())),
+                         std::get<1>(GetParam())) {}
+
+  void ServerSkipTest(PacketFilter* filter,
+                      uint8_t alert = kTlsAlertUnexpectedMessage) {
+    auto alert_recorder = new TlsAlertRecorder();
+    client_->SetPacketFilter(alert_recorder);
+    if (filter) {
+      server_->SetPacketFilter(filter);
+    }
+    ConnectExpectFail();
+    EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
+    EXPECT_EQ(alert, alert_recorder->description());
+  }
+};
+
+TEST_P(TlsSkipTest, SkipCertificate) {
+  ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate));
+  client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
+}
+
+TEST_P(TlsSkipTest, SkipCertificateEcdhe) {
+  EnableSomeEcdheCiphers();
+  ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate));
+  client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_SERVER_KEY_EXCH);
+}
+
+TEST_P(TlsSkipTest, SkipCertificateEcdsa) {
+  ResetEcdsa();
+  ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate));
+  client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_SERVER_KEY_EXCH);
+}
+
+TEST_P(TlsSkipTest, SkipServerKeyExchange) {
+  // Have to enable some ephemeral suites, or ServerKeyExchange doesn't appear.
+  EnableSomeEcdheCiphers();
+  ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeServerKeyExchange));
+  client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
+}
+
+TEST_P(TlsSkipTest, SkipServerKeyExchangeEcdsa) {
+  ResetEcdsa();
+  ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeServerKeyExchange));
+  client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
+}
+
+TEST_P(TlsSkipTest, SkipCertAndKeyExch) {
+  auto chain = new ChainedPacketFilter();
+  chain->Add(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate));
+  chain->Add(new TlsHandshakeSkipFilter(kTlsHandshakeServerKeyExchange));
+  ServerSkipTest(chain);
+  client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
+}
+
+TEST_P(TlsSkipTest, SkipCertAndKeyExchEcdsa) {
+  ResetEcdsa();
+  auto chain = new ChainedPacketFilter();
+  chain->Add(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate));
+  chain->Add(new TlsHandshakeSkipFilter(kTlsHandshakeServerKeyExchange));
+  ServerSkipTest(chain);
+  client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
+}
+
+INSTANTIATE_TEST_CASE_P(SkipTls10, TlsSkipTest,
+                        ::testing::Combine(
+                          TlsConnectTestBase::kTlsModesStream,
+                          TlsConnectTestBase::kTlsV10));
+INSTANTIATE_TEST_CASE_P(SkipVariants, TlsSkipTest,
+                        ::testing::Combine(
+                          TlsConnectTestBase::kTlsModesAll,
+                          TlsConnectTestBase::kTlsV11V12));
+
+}  // namespace nss_test
--- a/external_tests/ssl_gtest/tls_agent.cc
+++ b/external_tests/ssl_gtest/tls_agent.cc
@@ -37,17 +37,17 @@ bool TlsAgent::EnsureTlsSetup() {
     CERTCertificate* cert = PK11_FindCertFromNickname(name_.c_str(), nullptr);
     EXPECT_NE(nullptr, cert);
     if (!cert) return false;
 
     SECKEYPrivateKey* priv = PK11_FindKeyByAnyCert(cert, nullptr);
     EXPECT_NE(nullptr, priv);
     if (!priv) return false;  // Leak cert.
 
-    SECStatus rv = SSL_ConfigSecureServer(ssl_fd_, cert, priv, kt_rsa);
+    SECStatus rv = SSL_ConfigSecureServer(ssl_fd_, cert, priv, kea_);
     EXPECT_EQ(SECSuccess, rv);
     if (rv != SECSuccess) return false;  // Leak cert and key.
 
     SECKEY_DestroyPrivateKey(priv);
     CERT_DestroyCertificate(cert);
 
     rv = SSL_SNISocketConfigHook(ssl_fd_, SniHook,
                                  reinterpret_cast<void*>(this));
@@ -66,118 +66,129 @@ bool TlsAgent::EnsureTlsSetup() {
                                reinterpret_cast<void*>(this));
   EXPECT_EQ(SECSuccess, rv);
   if (rv != SECSuccess) return false;
 
   return true;
 }
 
 void TlsAgent::StartConnect() {
-  ASSERT_TRUE(EnsureTlsSetup());
+  EXPECT_TRUE(EnsureTlsSetup());
 
   SECStatus rv;
   rv = SSL_ResetHandshake(ssl_fd_, role_ == SERVER ? PR_TRUE : PR_FALSE);
-  ASSERT_EQ(SECSuccess, rv);
+  EXPECT_EQ(SECSuccess, rv);
   SetState(CONNECTING);
 }
 
-void TlsAgent::EnableSomeECDHECiphers() {
-  ASSERT_TRUE(EnsureTlsSetup());
+void TlsAgent::EnableSomeEcdheCiphers() {
+  EXPECT_TRUE(EnsureTlsSetup());
 
-  const uint32_t EnabledCiphers[] = {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
-                                     TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA};
+  const uint32_t EcdheCiphers[] = {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+                                   TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+                                   TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+                                   TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA};
 
-  for (size_t i = 0; i < PR_ARRAY_SIZE(EnabledCiphers); ++i) {
-    SECStatus rv = SSL_CipherPrefSet(ssl_fd_, EnabledCiphers[i], PR_TRUE);
-    ASSERT_EQ(SECSuccess, rv);
+  for (size_t i = 0; i < PR_ARRAY_SIZE(EcdheCiphers); ++i) {
+    SECStatus rv = SSL_CipherPrefSet(ssl_fd_, EcdheCiphers[i], PR_TRUE);
+    EXPECT_EQ(SECSuccess, rv);
   }
 }
 
 void TlsAgent::SetSessionTicketsEnabled(bool en) {
-  ASSERT_TRUE(EnsureTlsSetup());
+  EXPECT_TRUE(EnsureTlsSetup());
 
   SECStatus rv = SSL_OptionSet(ssl_fd_, SSL_ENABLE_SESSION_TICKETS,
                                en ? PR_TRUE : PR_FALSE);
-  ASSERT_EQ(SECSuccess, rv);
+  EXPECT_EQ(SECSuccess, rv);
 }
 
 void TlsAgent::SetSessionCacheEnabled(bool en) {
-  ASSERT_TRUE(EnsureTlsSetup());
+  EXPECT_TRUE(EnsureTlsSetup());
 
   SECStatus rv = SSL_OptionSet(ssl_fd_, SSL_NO_CACHE,
                                en ? PR_FALSE : PR_TRUE);
-  ASSERT_EQ(SECSuccess, rv);
+  EXPECT_EQ(SECSuccess, rv);
 }
 
 void TlsAgent::SetVersionRange(uint16_t minver, uint16_t maxver) {
    vrange_.min = minver;
    vrange_.max = maxver;
 
    if (ssl_fd_) {
      SECStatus rv = SSL_VersionRangeSet(ssl_fd_, &vrange_);
-     ASSERT_EQ(SECSuccess, rv);
+     EXPECT_EQ(SECSuccess, rv);
    }
 }
 
 void TlsAgent::CheckKEAType(SSLKEAType type) const {
-  ASSERT_EQ(CONNECTED, state_);
-  ASSERT_EQ(type, csinfo_.keaType);
+  EXPECT_EQ(CONNECTED, state_);
+  EXPECT_EQ(type, csinfo_.keaType);
+}
+
+void TlsAgent::CheckAuthType(SSLAuthType type) const {
+  EXPECT_EQ(CONNECTED, state_);
+  EXPECT_EQ(type, csinfo_.authAlgorithm);
 }
 
 void TlsAgent::CheckVersion(uint16_t version) const {
-  ASSERT_EQ(CONNECTED, state_);
-  ASSERT_EQ(version, info_.protocolVersion);
+  EXPECT_EQ(CONNECTED, state_);
+  EXPECT_EQ(version, info_.protocolVersion);
 }
 
 void TlsAgent::EnableAlpn(const uint8_t* val, size_t len) {
-  ASSERT_TRUE(EnsureTlsSetup());
+  EXPECT_TRUE(EnsureTlsSetup());
 
-  ASSERT_EQ(SECSuccess, SSL_OptionSet(ssl_fd_, SSL_ENABLE_ALPN, PR_TRUE));
-  ASSERT_EQ(SECSuccess, SSL_SetNextProtoNego(ssl_fd_, val, len));
+  EXPECT_EQ(SECSuccess, SSL_OptionSet(ssl_fd_, SSL_ENABLE_ALPN, PR_TRUE));
+  EXPECT_EQ(SECSuccess, SSL_SetNextProtoNego(ssl_fd_, val, len));
 }
 
 void TlsAgent::CheckAlpn(SSLNextProtoState expected_state,
                          const std::string& expected) {
   SSLNextProtoState state;
   char chosen[10];
   unsigned int chosen_len;
   SECStatus rv = SSL_GetNextProto(ssl_fd_, &state,
                                   reinterpret_cast<unsigned char*>(chosen),
                                   &chosen_len, sizeof(chosen));
-  ASSERT_EQ(SECSuccess, rv);
-  ASSERT_EQ(expected_state, state);
-  ASSERT_EQ(expected, std::string(chosen, chosen_len));
+  EXPECT_EQ(SECSuccess, rv);
+  EXPECT_EQ(expected_state, state);
+  EXPECT_EQ(expected, std::string(chosen, chosen_len));
 }
 
 void TlsAgent::EnableSrtp() {
-  ASSERT_TRUE(EnsureTlsSetup());
+  EXPECT_TRUE(EnsureTlsSetup());
   const uint16_t ciphers[] = {
     SRTP_AES128_CM_HMAC_SHA1_80, SRTP_AES128_CM_HMAC_SHA1_32
   };
-  ASSERT_EQ(SECSuccess, SSL_SetSRTPCiphers(ssl_fd_, ciphers,
+  EXPECT_EQ(SECSuccess, SSL_SetSRTPCiphers(ssl_fd_, ciphers,
                                            PR_ARRAY_SIZE(ciphers)));
 
 }
 
 void TlsAgent::CheckSrtp() {
   uint16_t actual;
-  ASSERT_EQ(SECSuccess, SSL_GetSRTPCipher(ssl_fd_, &actual));
-  ASSERT_EQ(SRTP_AES128_CM_HMAC_SHA1_80, actual);
+  EXPECT_EQ(SECSuccess, SSL_GetSRTPCipher(ssl_fd_, &actual));
+  EXPECT_EQ(SRTP_AES128_CM_HMAC_SHA1_80, actual);
 }
 
+void TlsAgent::CheckErrorCode(int32_t expected) const {
+  EXPECT_EQ(ERROR, state_);
+  EXPECT_EQ(expected, error_code_);
+}
 
 void TlsAgent::Handshake() {
   SECStatus rv = SSL_ForceHandshake(ssl_fd_);
   if (rv == SECSuccess) {
     LOG("Handshake success");
     SECStatus rv = SSL_GetChannelInfo(ssl_fd_, &info_, sizeof(info_));
-    ASSERT_EQ(SECSuccess, rv);
+    EXPECT_EQ(SECSuccess, rv);
 
     rv = SSL_GetCipherSuiteInfo(info_.cipherSuite, &csinfo_, sizeof(csinfo_));
-    ASSERT_EQ(SECSuccess, rv);
+    EXPECT_EQ(SECSuccess, rv);
 
     SetState(CONNECTED);
     return;
   }
 
   int32_t err = PR_GetError();
   switch (err) {
     case PR_WOULD_BLOCK_ERROR:
@@ -187,31 +198,32 @@ void TlsAgent::Handshake() {
                                &TlsAgent::ReadableCallback);
       return;
       break;
 
       // TODO(ekr@rtfm.com): needs special case for DTLS
     case SSL_ERROR_RX_MALFORMED_HANDSHAKE:
     default:
       LOG("Handshake failed with error " << err);
+      error_code_ = err;
       SetState(ERROR);
       return;
   }
 }
 
 void TlsAgent::ConfigureSessionCache(SessionResumptionMode mode) {
-  ASSERT_TRUE(EnsureTlsSetup());
+  EXPECT_TRUE(EnsureTlsSetup());
 
   SECStatus rv = SSL_OptionSet(ssl_fd_,
                                SSL_NO_CACHE,
                                mode & RESUME_SESSIONID ?
                                PR_FALSE : PR_TRUE);
-  ASSERT_EQ(SECSuccess, rv);
+  EXPECT_EQ(SECSuccess, rv);
 
   rv = SSL_OptionSet(ssl_fd_,
                      SSL_ENABLE_SESSION_TICKETS,
                      mode & RESUME_TICKET ?
                      PR_TRUE : PR_FALSE);
-  ASSERT_EQ(SECSuccess, rv);
+  EXPECT_EQ(SECSuccess, rv);
 }
 
 
 } // namespace nss_test
--- a/external_tests/ssl_gtest/tls_agent.h
+++ b/external_tests/ssl_gtest/tls_agent.h
@@ -28,24 +28,26 @@ enum SessionResumptionMode {
   RESUME_BOTH = RESUME_SESSIONID | RESUME_TICKET
 };
 
 class TlsAgent : public PollTarget {
  public:
   enum Role { CLIENT, SERVER };
   enum State { INIT, CONNECTING, CONNECTED, ERROR };
 
-  TlsAgent(const std::string& name, Role role, Mode mode)
+  TlsAgent(const std::string& name, Role role, Mode mode, SSLKEAType kea)
       : name_(name),
         mode_(mode),
+        kea_(kea),
         pr_fd_(nullptr),
         adapter_(nullptr),
         ssl_fd_(nullptr),
         role_(role),
-        state_(INIT) {
+        state_(INIT),
+        error_code_(0) {
       memset(&info_, 0, sizeof(info_));
       memset(&csinfo_, 0, sizeof(csinfo_));
       SECStatus rv = SSL_VersionRangeGetDefault(mode_ == STREAM ?
                                                 ssl_variant_stream : ssl_variant_datagram,
                                                 &vrange_);
       EXPECT_EQ(SECSuccess, rv);
   }
 
@@ -73,31 +75,33 @@ class TlsAgent : public PollTarget {
 
   void SetPacketFilter(PacketFilter* filter) {
     adapter_->SetPacketFilter(filter);
   }
 
 
   void StartConnect();
   void CheckKEAType(SSLKEAType type) const;
+  void CheckAuthType(SSLAuthType type) const;
   void CheckVersion(uint16_t version) const;
 
   void Handshake();
-  void EnableSomeECDHECiphers();
+  void EnableSomeEcdheCiphers();
   bool EnsureTlsSetup();
 
   void ConfigureSessionCache(SessionResumptionMode mode);
   void SetSessionTicketsEnabled(bool en);
   void SetSessionCacheEnabled(bool en);
   void SetVersionRange(uint16_t minver, uint16_t maxver);
   void EnableAlpn(const uint8_t* val, size_t len);
   void CheckAlpn(SSLNextProtoState expected_state,
                  const std::string& expected);
   void EnableSrtp();
   void CheckSrtp();
+  void CheckErrorCode(int32_t expected) const;
 
   State state() const { return state_; }
 
   const char* state_str() const { return state_str(state()); }
 
   const char* state_str(State state) const { return states[state]; }
 
   PRFileDesc* ssl_fd() { return ssl_fd_; }
@@ -167,21 +171,23 @@ class TlsAgent : public PollTarget {
   static PRInt32 SniHook(PRFileDesc *fd, const SECItem *srvNameArr,
                          PRUint32 srvNameArrSize,
                          void *arg) {
     return SSL_SNI_CURRENT_CONFIG_IS_USED;
   }
 
   const std::string name_;
   Mode mode_;
+  SSLKEAType kea_;
   PRFileDesc* pr_fd_;
   DummyPrSocket* adapter_;
   PRFileDesc* ssl_fd_;
   Role role_;
   State state_;
   SSLChannelInfo info_;
   SSLCipherSuiteInfo csinfo_;
   SSLVersionRange vrange_;
+  int32_t error_code_;
 };
 
 }  // namespace nss_test
 
 #endif
--- a/external_tests/ssl_gtest/tls_connect.cc
+++ b/external_tests/ssl_gtest/tls_connect.cc
@@ -47,18 +47,18 @@ static std::string VersionString(uint16_
     std::cerr << "Invalid version: " << version << std::endl;
     EXPECT_TRUE(false);
     return "";
   }
 }
 
 TlsConnectTestBase::TlsConnectTestBase(Mode mode, uint16_t version)
       : mode_(mode),
-        client_(new TlsAgent("client", TlsAgent::CLIENT, mode_)),
-        server_(new TlsAgent("server", TlsAgent::SERVER, mode_)),
+        client_(new TlsAgent("client", TlsAgent::CLIENT, mode_, ssl_kea_rsa)),
+        server_(new TlsAgent("server", TlsAgent::SERVER, mode_, ssl_kea_rsa)),
         version_(version),
         session_ids_() {
   std::cerr << "Version: " << mode_ << " " << VersionString(version_) << std::endl;
 }
 
 TlsConnectTestBase::~TlsConnectTestBase() {
   delete client_;
   delete server_;
@@ -79,41 +79,50 @@ void TlsConnectTestBase::TearDown() {
   client_ = nullptr;
   server_ = nullptr;
 
   SSL_ClearSessionCache();
   SSL_ShutdownServerSessionIDCache();
 }
 
 void TlsConnectTestBase::Init() {
-  ASSERT_TRUE(client_->Init());
-  ASSERT_TRUE(server_->Init());
+  EXPECT_TRUE(client_->Init());
+  EXPECT_TRUE(server_->Init());
 
   client_->SetPeer(server_);
   server_->SetPeer(client_);
 
   if (version_) {
     client_->SetVersionRange(version_, version_);
     server_->SetVersionRange(version_, version_);
   }
 }
 
-void TlsConnectTestBase::Reset() {
+void TlsConnectTestBase::Reset(const std::string& server_name, SSLKEAType kea) {
   delete client_;
   delete server_;
 
-  client_ = new TlsAgent("client", TlsAgent::CLIENT, mode_);
-  server_ = new TlsAgent("server", TlsAgent::SERVER, mode_);
+  client_ = new TlsAgent("client", TlsAgent::CLIENT, mode_, kea);
+  server_ = new TlsAgent(server_name, TlsAgent::SERVER, mode_, kea);
 
   Init();
 }
 
+void TlsConnectTestBase::ResetRsa() {
+  Reset("server", ssl_kea_rsa);
+}
+
+void TlsConnectTestBase::ResetEcdsa() {
+  Reset("ecdsa", ssl_kea_ecdh);
+  EnableSomeEcdheCiphers();
+}
+
 void TlsConnectTestBase::EnsureTlsSetup() {
-  ASSERT_TRUE(client_->EnsureTlsSetup());
-  ASSERT_TRUE(server_->EnsureTlsSetup());
+  EXPECT_TRUE(client_->EnsureTlsSetup());
+  EXPECT_TRUE(server_->EnsureTlsSetup());
 }
 
 void TlsConnectTestBase::Handshake() {
   server_->StartConnect();
   client_->StartConnect();
   client_->Handshake();
   server_->Handshake();
 
@@ -122,80 +131,80 @@ void TlsConnectTestBase::Handshake() {
                    5000);
 
 }
 
 void TlsConnectTestBase::Connect() {
   Handshake();
 
   // Check the version is as expected
-  ASSERT_EQ(client_->version(), server_->version());
-  ASSERT_EQ(std::min(client_->max_version(),
+  EXPECT_EQ(client_->version(), server_->version());
+  EXPECT_EQ(std::min(client_->max_version(),
                      server_->max_version()),
             client_->version());
 
-  ASSERT_EQ(TlsAgent::CONNECTED, client_->state());
-  ASSERT_EQ(TlsAgent::CONNECTED, server_->state());
+  EXPECT_EQ(TlsAgent::CONNECTED, client_->state());
+  EXPECT_EQ(TlsAgent::CONNECTED, server_->state());
 
   int16_t cipher_suite1, cipher_suite2;
   bool ret = client_->cipher_suite(&cipher_suite1);
-  ASSERT_TRUE(ret);
+  EXPECT_TRUE(ret);
   ret = server_->cipher_suite(&cipher_suite2);
-  ASSERT_TRUE(ret);
-  ASSERT_EQ(cipher_suite1, cipher_suite2);
+  EXPECT_TRUE(ret);
+  EXPECT_EQ(cipher_suite1, cipher_suite2);
 
   std::cerr << "Connected with version " << client_->version()
             << " cipher suite " << client_->cipher_suite_name()
             << std::endl;
 
   // Check and store session ids.
   std::vector<uint8_t> sid_c1 = client_->session_id();
-  ASSERT_EQ(32U, sid_c1.size());
+  EXPECT_EQ(32U, sid_c1.size());
   std::vector<uint8_t> sid_s1 = server_->session_id();
-  ASSERT_EQ(32U, sid_s1.size());
-  ASSERT_EQ(sid_c1, sid_s1);
+  EXPECT_EQ(32U, sid_s1.size());
+  EXPECT_EQ(sid_c1, sid_s1);
   session_ids_.push_back(sid_c1);
 }
 
 void TlsConnectTestBase::ConnectExpectFail() {
   Handshake();
 
   ASSERT_EQ(TlsAgent::ERROR, client_->state());
   ASSERT_EQ(TlsAgent::ERROR, server_->state());
 }
 
-void TlsConnectTestBase::EnableSomeECDHECiphers() {
-  client_->EnableSomeECDHECiphers();
-  server_->EnableSomeECDHECiphers();
+void TlsConnectTestBase::EnableSomeEcdheCiphers() {
+  client_->EnableSomeEcdheCiphers();
+  server_->EnableSomeEcdheCiphers();
 }
 
 
 void TlsConnectTestBase::ConfigureSessionCache(SessionResumptionMode client,
                                                SessionResumptionMode server) {
   client_->ConfigureSessionCache(client);
   server_->ConfigureSessionCache(server);
 }
 
 void TlsConnectTestBase::CheckResumption(SessionResumptionMode expected) {
-  ASSERT_NE(RESUME_BOTH, expected);
+  EXPECT_NE(RESUME_BOTH, expected);
 
   int resume_ct = expected ? 1 : 0;
   int stateless_ct = (expected & RESUME_TICKET) ? 1 : 0;
 
   SSL3Statistics* stats = SSL_GetStatistics();
-  ASSERT_EQ(resume_ct, stats->hch_sid_cache_hits);
-  ASSERT_EQ(resume_ct, stats->hsh_sid_cache_hits);
+  EXPECT_EQ(resume_ct, stats->hch_sid_cache_hits);
+  EXPECT_EQ(resume_ct, stats->hsh_sid_cache_hits);
 
-  ASSERT_EQ(stateless_ct, stats->hch_sid_stateless_resumes);
-  ASSERT_EQ(stateless_ct, stats->hsh_sid_stateless_resumes);
+  EXPECT_EQ(stateless_ct, stats->hch_sid_stateless_resumes);
+  EXPECT_EQ(stateless_ct, stats->hsh_sid_stateless_resumes);
 
   if (resume_ct) {
     // Check that the last two session ids match.
-    ASSERT_GE(2U, session_ids_.size());
-    ASSERT_EQ(session_ids_[session_ids_.size()-1],
+    EXPECT_GE(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
   // 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.
--- a/external_tests/ssl_gtest/tls_connect.h
+++ b/external_tests/ssl_gtest/tls_connect.h
@@ -34,42 +34,48 @@ class TlsConnectTestBase : public ::test
   TlsConnectTestBase(Mode mode, uint16_t version);
   virtual ~TlsConnectTestBase();
 
   void SetUp();
   void TearDown();
 
   // Initialize client and server.
   void Init();
-  // Re-initialize client and server.
-  void Reset();
+  // 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();
 
   // Run the handshake.
   void Handshake();
   // Connect and check that it works.
   void Connect();
   // Connect and expect it to fail.
   void ConnectExpectFail();
 
-  void EnableSomeECDHECiphers();
+  void EnableSomeEcdheCiphers();
   void ConfigureSessionCache(SessionResumptionMode client,
                              SessionResumptionMode server);
   void CheckResumption(SessionResumptionMode expected);
   void EnableAlpn();
   void EnableSrtp();
   void CheckSrtp();
  protected:
 
   Mode mode_;
   TlsAgent* client_;
   TlsAgent* server_;
   uint16_t version_;
   std::vector<std::vector<uint8_t>> session_ids_;
+
+ private:
+  void Reset(const std::string& server_name, SSLKEAType kea);
 };
 
 // A TLS-only test base.
 class TlsConnectStream : public TlsConnectTestBase,
                          public ::testing::WithParamInterface<uint16_t> {
  public:
   TlsConnectStream() : TlsConnectTestBase(STREAM, GetParam()) {}
 };
--- a/external_tests/ssl_gtest/tls_filter.cc
+++ b/external_tests/ssl_gtest/tls_filter.cc
@@ -62,17 +62,17 @@ size_t TlsRecordFilter::ApplyFilter(uint
                                     DataBuffer* output,
                                     size_t offset, bool* changed) {
   const DataBuffer* source = &record;
   DataBuffer filtered;
   if (FilterRecord(content_type, version, record, &filtered) &&
       filtered.len() < 0x10000) {
     *changed = true;
     std::cerr << "record old: " << record << std::endl;
-    std::cerr << "record old: " << filtered << std::endl;
+    std::cerr << "record new: " << filtered << std::endl;
     source = &filtered;
   }
 
   output->Write(offset, source->len(), 2);
   output->Write(offset + 2, *source);
   return offset + 2 + source->len();
 }
 
@@ -91,21 +91,17 @@ bool TlsHandshakeFilter::FilterRecord(ui
   TlsParser parser(input);
   while (parser.remaining()) {
     size_t start = parser.consumed();
     uint8_t handshake_type;
     if (!parser.Read(&handshake_type)) {
       return false; // malformed
     }
     uint32_t length;
-    if (!parser.Read(&length, 3)) {
-      return false; // malformed
-    }
-
-    if (IsDtls(version) && !CheckDtls(parser, length)) {
+    if (!ReadLength(&parser, version, &length)) {
       return false;
     }
 
     size_t header_len = parser.consumed() - start;
     output->Write(output_offset, input.data() + start, header_len);
 
     DataBuffer handshake;
     if (!parser.Read(&handshake, length)) {
@@ -120,34 +116,42 @@ bool TlsHandshakeFilter::FilterRecord(ui
                                 output, output_offset + 1,
                                 output_offset + header_len,
                                 &changed);
   }
   output->Truncate(output_offset);
   return changed;
 }
 
-bool TlsHandshakeFilter::CheckDtls(TlsParser& parser, size_t length) {
+bool TlsHandshakeFilter::ReadLength(TlsParser* parser, uint16_t version, uint32_t *length) {
+  if (!parser->Read(length, 3)) {
+    return false; // malformed
+  }
+
+  if (!IsDtls(version)) {
+    return true; // nothing left to do
+  }
+
   // Read and check DTLS parameters
-  if (!parser.Skip(2)) { // sequence number
+  if (!parser->Skip(2)) { // sequence number
     return false;
   }
 
   uint32_t fragment_offset;
-  if (!parser.Read(&fragment_offset, 3)) {
+  if (!parser->Read(&fragment_offset, 3)) {
     return false;
   }
 
   uint32_t fragment_length;
-  if (!parser.Read(&fragment_length, 3)) {
+  if (!parser->Read(&fragment_length, 3)) {
     return false;
   }
 
   // All current tests where we are using this code don't fragment.
-  return (fragment_offset == 0 && fragment_length == length);
+  return (fragment_offset == 0 && fragment_length == *length);
 }
 
 size_t TlsHandshakeFilter::ApplyFilter(
     uint16_t version, uint8_t handshake_type, const DataBuffer& handshake,
     DataBuffer* output, size_t length_offset, size_t value_offset,
     bool* changed) {
   const DataBuffer* source = &handshake;
   DataBuffer filtered;
--- a/external_tests/ssl_gtest/tls_filter.h
+++ b/external_tests/ssl_gtest/tls_filter.h
@@ -38,24 +38,27 @@ class TlsRecordFilter : public PacketFil
 
 // Abstract filter that operates on handshake messages rather than records.
 // This assumes that the handshake messages are written in a block as entire
 // records and that they don't span records or anything crazy like that.
 class TlsHandshakeFilter : public TlsRecordFilter {
  public:
   TlsHandshakeFilter() {}
 
+  // Reads the length from the record header.
+  // This also reads the DTLS fragment information and checks it.
+  static bool ReadLength(TlsParser* parser, uint16_t version, uint32_t *length);
+
  protected:
   virtual bool FilterRecord(uint8_t content_type, uint16_t version,
                             const DataBuffer& input, DataBuffer* output);
   virtual bool FilterHandshake(uint16_t version, uint8_t handshake_type,
                                const DataBuffer& input, DataBuffer* output) = 0;
 
  private:
-  bool CheckDtls(TlsParser& parser, size_t length);
   size_t ApplyFilter(uint16_t version, uint8_t handshake_type,
                      const DataBuffer& record, DataBuffer* output,
                      size_t length_offset, size_t value_offset, bool* changed);
 };
 
 // Make a copy of the first instance of a handshake message.
 class TlsInspectorRecordHandshakeMessage : public TlsHandshakeFilter {
  public:
--- a/external_tests/ssl_gtest/tls_parser.h
+++ b/external_tests/ssl_gtest/tls_parser.h
@@ -10,33 +10,34 @@
 #include <memory>
 #include <cstdint>
 #include <cstring>
 #include <arpa/inet.h>
 #include "databuffer.h"
 
 namespace nss_test {
 
-const uint8_t kTlsChangeCipherSpecType = 0x14;
-const uint8_t kTlsAlertType = 0x15;
-const uint8_t kTlsHandshakeType = 0x16;
+const uint8_t kTlsChangeCipherSpecType = 20;
+const uint8_t kTlsAlertType = 21;
+const uint8_t kTlsHandshakeType = 22;
 
-const uint8_t kTlsHandshakeClientHello = 0x01;
-const uint8_t kTlsHandshakeServerHello = 0x02;
-const uint8_t kTlsHandshakeCertificate = 0x0b;
-const uint8_t kTlsHandshakeServerKeyExchange = 0x0c;
+const uint8_t kTlsHandshakeClientHello = 1;
+const uint8_t kTlsHandshakeServerHello = 2;
+const uint8_t kTlsHandshakeCertificate = 11;
+const uint8_t kTlsHandshakeServerKeyExchange = 12;
 
 const uint8_t kTlsAlertWarning = 1;
 const uint8_t kTlsAlertFatal = 2;
 
-const uint8_t kTlsAlertHandshakeFailure = 0x28;
-const uint8_t kTlsAlertIllegalParameter = 0x2f;
-const uint8_t kTlsAlertDecodeError = 0x32;
-const uint8_t kTlsAlertUnsupportedExtension = 0x6e;
-const uint8_t kTlsAlertNoApplicationProtocol = 0x78;
+const uint8_t kTlsAlertUnexpectedMessage = 10;
+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 kTlsFakeChangeCipherSpec[] = {
     kTlsChangeCipherSpecType,        // Type
     0xfe,                     0xff,  // Version
     0x00,                     0x00, 0x00, 0x00,
     0x00,                     0x00, 0x00, 0x10,  // Fictitious sequence #
     0x00,                     0x01,              // Length
     0x01                                         // Value
--- a/tests/cert/cert.sh
+++ b/tests/cert/cert.sh
@@ -953,30 +953,45 @@ cert_ssl_gtests()
 {
   CERTFAILED=0
   echo "$SCRIPTNAME: Creating ssl_gtest DB dir"
   cert_init_cert ${SSLGTESTDIR} "server" 1 ${D_EXT_SERVER}
   echo "$SCRIPTNAME: Creating database for ssl_gtests"
   certu -N -d "${SSLGTESTDIR}" --empty-password 2>&1
   # the ssl server used here is special: is a self-signed server
   # certificate with name server.
-  echo "$SCRIPTNAME: Creating server cert for ssl_gtests"
-  certu -S -z ${R_NOISE_FILE} -g 2048 -d ${SSLGTESTDIR} -n server -s "CN=server" -t C,C,C -x -m 1 -w -2 -v 120 -Z SHA256 -1 -2 <<CERTSCRIPT
+  echo "$SCRIPTNAME: Creating server certs for ssl_gtests"
+  certu -S -z ${R_NOISE_FILE} -g 2048 -d ${SSLGTESTDIR} -n server -s "CN=server" \
+        -t C,C,C -x -m 1 -w -2 -v 120 -Z SHA256 -1 -2 <<CERTSCRIPT
+0
+2
+9
+n
+n
+
+n
+CERTSCRIPT
+  if [ "$RET" -ne 0 ]; then
+     echo "return value is $RET"
+     Exit 6 "Fatal - failed to create RSA server cert for ssl_gtests"
+  fi
+  certu -S -z ${R_NOISE_FILE} -k ec -q nistp256 -d ${SSLGTESTDIR} -n ecdsa -s CN=ecdsa \
+        -t C,C,C -x -m 1 -w -2 -v 120 -Z SHA256 -1 -2 <<CERTSCRIPT
 0
 2
 9
 n
 n
 
 n
 CERTSCRIPT
 
   if [ "$RET" -ne 0 ]; then
      echo "return value is $RET"
-     Exit 6 "Fatal - failed to create server cert for ssl_gtests"
+     Exit 6 "Fatal - failed to create ECDSA server cert for ssl_gtests"
   fi
 }
 
 ############################## cert_stresscerts ################################
 # local shell function to create client certs for SSL stresstest
 ########################################################################
 cert_stresscerts()
 {