Bug 1158489 - Enable signature algorithm configuration, r=wtc,ekr
authorMartin Thomson <martin.thomson@gmail.com>
Mon, 17 Aug 2015 11:30:47 -0700
changeset 11522 276a21813f7c4366d4005b18d7f2b35b77cd6042
parent 11521 0a59f0bce2ea7089bce03a54ca442dc323c6e995
child 11523 ce585b24aefd0c67512cbdef982c51fc0fbc80ab
push id690
push usermartin.thomson@gmail.com
push dateMon, 17 Aug 2015 18:31:19 +0000
reviewerswtc, ekr
bugs1158489
Bug 1158489 - Enable signature algorithm configuration, r=wtc,ekr
external_tests/ssl_gtest/databuffer.h
external_tests/ssl_gtest/ssl_extension_unittest.cc
external_tests/ssl_gtest/ssl_loopback_unittest.cc
external_tests/ssl_gtest/test_io.cc
external_tests/ssl_gtest/test_io.h
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
lib/ssl/SSLerrs.h
lib/ssl/ssl.def
lib/ssl/ssl.h
lib/ssl/ssl3con.c
lib/ssl/ssl3ecc.c
lib/ssl/ssl3ext.c
lib/ssl/ssl3prot.h
lib/ssl/sslerr.h
lib/ssl/sslimpl.h
lib/ssl/sslsock.c
lib/ssl/sslt.h
tests/cert/cert.sh
--- a/external_tests/ssl_gtest/databuffer.h
+++ b/external_tests/ssl_gtest/databuffer.h
@@ -22,37 +22,40 @@ namespace nss_test {
 
 class DataBuffer {
  public:
   DataBuffer() : data_(nullptr), len_(0) {}
   DataBuffer(const uint8_t *data, size_t len) : data_(nullptr), len_(0) {
     Assign(data, len);
   }
   explicit DataBuffer(const DataBuffer& other) : data_(nullptr), len_(0) {
-    Assign(other.data(), other.len());
+    Assign(other);
   }
   ~DataBuffer() { delete[] data_; }
 
   DataBuffer& operator=(const DataBuffer& other) {
     if (&other != this) {
-      Assign(other.data(), other.len());
+      Assign(other);
     }
     return *this;
   }
 
   void Allocate(size_t len) {
     delete[] data_;
     data_ = new uint8_t[len ? len : 1];  // Don't depend on new [0].
     len_ = len;
   }
 
   void Truncate(size_t len) {
     len_ = std::min(len_, len);
   }
 
+  void Assign(const DataBuffer& other) {
+    Assign(other.data(), other.len());
+  }
   void Assign(const uint8_t* data, size_t len) {
     Allocate(len);
     memcpy(static_cast<void *>(data_), static_cast<const void *>(data), len);
   }
 
   // Write will do a new allocation and expand the size of the buffer if needed.
   void Write(size_t index, const uint8_t* val, size_t count) {
     if (index + count > len_) {
@@ -78,16 +81,31 @@ class DataBuffer {
   // Write an integer, also performing host-to-network order conversion.
   void Write(size_t index, uint32_t val, size_t count) {
     assert(count <= sizeof(uint32_t));
     uint32_t nvalue = htonl(val);
     auto* addr = reinterpret_cast<const uint8_t*>(&nvalue);
     Write(index, addr + sizeof(uint32_t) - count, count);
   }
 
+  // This can't use the same trick as Write(), since we might be reading from a
+  // smaller data source.
+  bool Read(size_t index, size_t count, uint32_t* val) const {
+    assert(count < sizeof(uint32_t));
+    assert(val);
+    if ((index > len()) || (count > (len() - index))) {
+      return false;
+    }
+    *val = 0;
+    for (size_t i = 0; i < count; ++i) {
+      *val = (*val << 8) | data()[index + i];
+    }
+    return true;
+  }
+
   // Starting at |index|, remove |remove| bytes and replace them with the
   // contents of |buf|.
   void Splice(const DataBuffer& buf, size_t index, size_t remove = 0) {
     Splice(buf.data(), buf.len(), index, remove);
   }
 
   void Splice(const uint8_t* ins, size_t ins_len, size_t index, size_t remove = 0) {
     uint8_t* old_value = data_;
--- a/external_tests/ssl_gtest/ssl_extension_unittest.cc
+++ b/external_tests/ssl_gtest/ssl_extension_unittest.cc
@@ -191,18 +191,18 @@ class TlsExtensionReplacer : public TlsE
     if (extension_type != extension_) {
       return false;
     }
 
     *output = data_;
     return true;
   }
  private:
-  uint16_t extension_;
-  DataBuffer data_;
+  const uint16_t extension_;
+  const DataBuffer data_;
 };
 
 class TlsExtensionInjector : public TlsHandshakeFilter {
  public:
   TlsExtensionInjector(uint16_t ext, DataBuffer& data)
       : extension_(ext), data_(data) {}
 
   virtual bool FilterHandshake(uint16_t version, uint8_t handshake_type,
@@ -246,17 +246,37 @@ class TlsExtensionInjector : public TlsH
     // Insert the payload.
     output->Splice(data_, offset + 6);
 
     std::cerr << "Aft:" << *output << std::endl;
     return true;
   }
 
  private:
-  uint16_t extension_;
+  const uint16_t extension_;
+  const DataBuffer data_;
+};
+
+class TlsExtensionCapture : public TlsExtensionFilter {
+ public:
+  TlsExtensionCapture(uint16_t ext)
+      : extension_(ext), data_() {}
+
+  virtual bool FilterExtension(uint16_t extension_type,
+                               const DataBuffer& input, DataBuffer* output) {
+    if (extension_type == extension_) {
+      data_.Assign(input);
+    }
+    return false;
+  }
+
+  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) {}
 
@@ -557,16 +577,42 @@ TEST_P(TlsExtensionTestGeneric, Renegoti
 
 // The extension has to contain a length.
 TEST_P(TlsExtensionTestGeneric, RenegotiationInfoExtensionEmpty) {
   DataBuffer extension;
   ClientHelloErrorTest(new TlsExtensionReplacer(ssl_renegotiation_info_xtn,
                                                 extension));
 }
 
+TEST_P(TlsExtensionTest12Plus, SignatureAlgorithmConfiguration) {
+  const SSLSignatureAndHashAlg algorithms[] = {
+    {ssl_hash_sha512, ssl_sign_rsa},
+    {ssl_hash_sha384, ssl_sign_ecdsa}
+  };
+
+  TlsExtensionCapture *capture =
+    new TlsExtensionCapture(ssl_signature_algorithms_xtn);
+  client_->SetSignatureAlgorithms(algorithms, PR_ARRAY_SIZE(algorithms));
+  client_->SetPacketFilter(capture);
+  DisableDheCiphers();
+  Connect();
+
+  const DataBuffer& ext = capture->extension();
+  EXPECT_EQ(2 + PR_ARRAY_SIZE(algorithms) * 2, ext.len());
+  for (size_t i = 0, cursor = 2;
+       i < PR_ARRAY_SIZE(algorithms) && cursor < ext.len();
+       ++i) {
+    uint32_t v;
+    EXPECT_TRUE(ext.Read(cursor++, 1, &v));
+    EXPECT_EQ(algorithms[i].hashAlg, static_cast<SSLHashType>(v));
+    EXPECT_TRUE(ext.Read(cursor++, 1, &v));
+    EXPECT_EQ(algorithms[i].sigAlg, static_cast<SSLSignType>(v));
+  }
+}
+
 INSTANTIATE_TEST_CASE_P(ExtensionTls10, TlsExtensionTestGeneric,
                         ::testing::Combine(
                           TlsConnectTestBase::kTlsModesStream,
                           TlsConnectTestBase::kTlsV10));
 INSTANTIATE_TEST_CASE_P(ExtensionVariants, TlsExtensionTestGeneric,
                         ::testing::Combine(
                           TlsConnectTestBase::kTlsModesAll,
                           TlsConnectTestBase::kTlsV11V12));
--- a/external_tests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/external_tests/ssl_gtest/ssl_loopback_unittest.cc
@@ -177,16 +177,122 @@ TEST_P(TlsConnectGeneric, ResumeWithHigh
   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, ClientAuth) {
+  client_->SetupClientAuth();
+  server_->RequestClientAuth(true);
+  Connect();
+  server_->CheckAuthType(ssl_auth_rsa);
+}
+
+TEST_P(TlsConnectGeneric, ClientAuthEcdsa) {
+  ResetEcdsa();
+  client_->SetupClientAuth();
+  server_->RequestClientAuth(true);
+  Connect();
+  server_->CheckAuthType(ssl_auth_ecdsa);
+}
+
+static const SSLSignatureAndHashAlg SignatureEcdsaSha384[] = {
+  {ssl_hash_sha384, ssl_sign_ecdsa}
+};
+static const SSLSignatureAndHashAlg SignatureEcdsaSha256[] = {
+  {ssl_hash_sha256, ssl_sign_ecdsa}
+};
+static const SSLSignatureAndHashAlg SignatureRsaSha384[] = {
+  {ssl_hash_sha384, ssl_sign_rsa}
+};
+static const SSLSignatureAndHashAlg SignatureRsaSha256[] = {
+  {ssl_hash_sha256, ssl_sign_rsa}
+};
+
+// When signature algorithms match up, this should connect successfully; even
+// for TLS 1.1 and 1.0, where they should be ignored.
+TEST_P(TlsConnectGeneric, SignatureAlgorithmServerAuth) {
+  client_->SetSignatureAlgorithms(SignatureEcdsaSha384,
+                                  PR_ARRAY_SIZE(SignatureEcdsaSha384));
+  server_->SetSignatureAlgorithms(SignatureEcdsaSha384,
+                                  PR_ARRAY_SIZE(SignatureEcdsaSha384));
+  ResetEcdsa();
+  Connect();
+}
+
+// Here the client picks a single option, which should work in all versions.
+// Defaults on the server include the first option.
+TEST_P(TlsConnectGeneric, SignatureAlgorithmClientOnly) {
+  const SSLSignatureAndHashAlg clientAlgorithms[] = {
+    {ssl_hash_sha384, ssl_sign_ecdsa},
+    {ssl_hash_sha384, ssl_sign_rsa}, // supported but unusable
+    {ssl_hash_md5, ssl_sign_ecdsa} // unsupported and ignored
+  };
+  client_->SetSignatureAlgorithms(clientAlgorithms,
+                                  PR_ARRAY_SIZE(clientAlgorithms));
+  ResetEcdsa();
+  Connect();
+}
+
+// Here the server picks a single option, which should work in all versions.
+// Defaults on the client include the provided option.
+TEST_P(TlsConnectGeneric, SignatureAlgorithmServerOnly) {
+  server_->SetSignatureAlgorithms(SignatureEcdsaSha384,
+                                  PR_ARRAY_SIZE(SignatureEcdsaSha384));
+  ResetEcdsa();
+  Connect();
+}
+
+// There is no need for overlap on signatures; since we don't actually use the
+// signatures for static RSA, this should still connect successfully.
+// This should also work in TLS 1.0 and 1.1 where the algorithms aren't used.
+TEST_P(TlsConnectGeneric, SignatureAlgorithmNoOverlapStaticRsa) {
+  client_->SetSignatureAlgorithms(SignatureRsaSha384,
+                                  PR_ARRAY_SIZE(SignatureRsaSha384));
+  server_->SetSignatureAlgorithms(SignatureRsaSha256,
+                                  PR_ARRAY_SIZE(SignatureRsaSha256));
+  DisableDheCiphers();
+  Connect();
+  client_->CheckKEAType(ssl_kea_rsa);
+  client_->CheckAuthType(ssl_auth_rsa);
+}
+
+// Signature algorithms governs both verification and generation of signatures.
+// With ECDSA, we need to at least have a common signature algorithm configured.
+TEST_P(TlsConnectTls12, SignatureAlgorithmNoOverlapEcdsa) {
+  ResetEcdsa();
+  client_->SetSignatureAlgorithms(SignatureEcdsaSha384,
+                                  PR_ARRAY_SIZE(SignatureEcdsaSha384));
+  server_->SetSignatureAlgorithms(SignatureEcdsaSha256,
+                                  PR_ARRAY_SIZE(SignatureEcdsaSha256));
+  ConnectExpectFail();
+}
+
+// Pre 1.2, a mismatch on signature algorithms shouldn't affect anything.
+TEST_P(TlsConnectPre12, SignatureAlgorithmNoOverlapEcdsa) {
+  ResetEcdsa();
+  client_->SetSignatureAlgorithms(SignatureEcdsaSha384,
+                                  PR_ARRAY_SIZE(SignatureEcdsaSha384));
+  server_->SetSignatureAlgorithms(SignatureEcdsaSha256,
+                                  PR_ARRAY_SIZE(SignatureEcdsaSha256));
+  Connect();
+}
+
+// The server requests client auth but doesn't offer a SHA-256 option.
+// This fails because NSS only uses SHA-256 for handshake transcript hashes.
+TEST_P(TlsConnectTls12, RequestClientAuthWithoutSha256) {
+  server_->SetSignatureAlgorithms(SignatureRsaSha384,
+                                  PR_ARRAY_SIZE(SignatureRsaSha384));
+  server_->RequestClientAuth(false);
+  ConnectExpectFail();
+}
+
 TEST_P(TlsConnectGeneric, ConnectAlpn) {
   EnableAlpn();
   Connect();
   client_->CheckAlpn(SSL_NEXT_PROTO_SELECTED, "a");
   server_->CheckAlpn(SSL_NEXT_PROTO_NEGOTIATED, "a");
 }
 
 TEST_P(TlsConnectDatagram, ConnectSrtp) {
@@ -290,18 +396,17 @@ TEST_P(TlsConnectGeneric, ConnectSendRec
 // we provide 1200 bytes, they overrun the read buffer
 // provided by the calling test.
 
 // DTLS should return an error.
 TEST_P(TlsConnectDatagram, ShortRead) {
   Connect();
   client_->SetExpectedReadError(true);
   server_->SendData(1200, 1200);
-  WAIT_(client_->error_code() == SSL_ERROR_RX_SHORT_DTLS_READ,
-        2000);
+  WAIT_(client_->error_code() == SSL_ERROR_RX_SHORT_DTLS_READ, 2000);
   // Don't call CheckErrorCode() because it requires us to being
   // in state ERROR.
   ASSERT_EQ(SSL_ERROR_RX_SHORT_DTLS_READ, client_->error_code());
 
   // Now send and receive another packet.
   client_->SetExpectedReadError(false);
   server_->ResetSentBytes(); // Reset the counter.
   SendReceive();
@@ -329,14 +434,24 @@ INSTANTIATE_TEST_CASE_P(VariantsStream10
                           TlsConnectTestBase::kTlsModesStream,
                           TlsConnectTestBase::kTlsV10));
 INSTANTIATE_TEST_CASE_P(VariantsAll, TlsConnectGeneric,
                         ::testing::Combine(
                           TlsConnectTestBase::kTlsModesAll,
                           TlsConnectTestBase::kTlsV11V12));
 INSTANTIATE_TEST_CASE_P(VersionsDatagram, TlsConnectDatagram,
                         TlsConnectTestBase::kTlsV11V12);
+INSTANTIATE_TEST_CASE_P(Variants12, TlsConnectTls12,
+                        TlsConnectTestBase::kTlsModesAll);
+INSTANTIATE_TEST_CASE_P(Pre12Stream, TlsConnectPre12,
+                        ::testing::Combine(
+                          TlsConnectTestBase::kTlsModesStream,
+                          TlsConnectTestBase::kTlsV10));
+INSTANTIATE_TEST_CASE_P(Pre12All, TlsConnectPre12,
+                        ::testing::Combine(
+                          TlsConnectTestBase::kTlsModesAll,
+                          TlsConnectTestBase::kTlsV11));
 INSTANTIATE_TEST_CASE_P(VersionsStream10, TlsConnectStream,
                         TlsConnectTestBase::kTlsV10);
 INSTANTIATE_TEST_CASE_P(VersionsStream, TlsConnectStream,
                         TlsConnectTestBase::kTlsV11V12);
 
 }  // namespace nspr_test
--- a/external_tests/ssl_gtest/test_io.cc
+++ b/external_tests/ssl_gtest/test_io.cc
@@ -120,18 +120,19 @@ static PRStatus DummyBind(PRFileDesc *f,
 }
 
 static PRStatus DummyListen(PRFileDesc *f, int32_t depth) {
   UNIMPLEMENTED();
   return PR_FAILURE;
 }
 
 static PRStatus DummyShutdown(PRFileDesc *f, int32_t how) {
-  UNIMPLEMENTED();
-  return PR_FAILURE;
+  DummyPrSocket *io = reinterpret_cast<DummyPrSocket *>(f->secret);
+  io->Reset();
+  return PR_SUCCESS;
 }
 
 // This function does not support peek.
 static int32_t DummyRecv(PRFileDesc *f, void *buf, int32_t buflen,
                          int32_t flags, PRIntervalTime to) {
   PR_ASSERT(flags == 0);
   if (flags != 0) {
     PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
@@ -245,17 +246,22 @@ static PRStatus DummyConnectContinue(PRF
 }
 
 static int32_t DummyReserved(PRFileDesc *f) {
   UNIMPLEMENTED();
   return -1;
 }
 
 DummyPrSocket::~DummyPrSocket() {
+  Reset();
+}
+
+void DummyPrSocket::Reset() {
   delete filter_;
+  peer_ = nullptr;
   while (!input_.empty())
   {
     Packet* front = input_.front();
     input_.pop();
     delete front;
   }
 }
 
--- a/external_tests/ssl_gtest/test_io.h
+++ b/external_tests/ssl_gtest/test_io.h
@@ -45,18 +45,19 @@ class DummyPrSocket {
  public:
   ~DummyPrSocket();
 
   static PRFileDesc* CreateFD(const std::string& name,
                               Mode mode);  // Returns an FD.
   static DummyPrSocket* GetAdapter(PRFileDesc* fd);
 
   void SetPeer(DummyPrSocket* peer) { peer_ = peer; }
-
   void SetPacketFilter(PacketFilter* filter) { filter_ = filter; }
+  // Drops peer, packet filter and any outstanding packets.
+  void Reset();
 
   void PacketReceived(const DataBuffer& data);
   int32_t Read(void* data, int32_t len);
   int32_t Recv(void* buf, int32_t buflen);
   int32_t Write(const void* buf, int32_t length);
 
   Mode mode() const { return mode_; }
   bool readable() const { return !input_.empty(); }
--- a/external_tests/ssl_gtest/tls_agent.cc
+++ b/external_tests/ssl_gtest/tls_agent.cc
@@ -111,16 +111,66 @@ bool TlsAgent::EnsureTlsSetup() {
 
   rv = SSL_HandshakeCallback(ssl_fd_, HandshakeCallback, this);
   EXPECT_EQ(SECSuccess, rv);
   if (rv != SECSuccess) return false;
 
   return true;
 }
 
+void TlsAgent::SetupClientAuth() {
+  EXPECT_TRUE(EnsureTlsSetup());
+  ASSERT_EQ(CLIENT, role_);
+
+  EXPECT_EQ(SECSuccess,
+            SSL_GetClientAuthDataHook(ssl_fd_, GetClientAuthDataHook,
+                                      reinterpret_cast<void*>(this)));
+}
+
+bool TlsAgent::GetClientAuthCredentials(CERTCertificate **cert,
+                                        SECKEYPrivateKey **priv) const {
+  *cert = PK11_FindCertFromNickname(name_.c_str(), nullptr);
+  EXPECT_NE(nullptr, *cert);
+  if (!*cert) return false;
+
+  *priv = PK11_FindKeyByAnyCert(*cert, nullptr);
+  EXPECT_NE(nullptr, *priv);
+  if (!*priv) return false; // Leak cert.
+
+  return true;
+}
+
+SECStatus TlsAgent::GetClientAuthDataHook(void* self, PRFileDesc* fd,
+                                          CERTDistNames* caNames,
+                                          CERTCertificate** cert,
+                                          SECKEYPrivateKey** privKey) {
+  TlsAgent* agent = reinterpret_cast<TlsAgent*>(self);
+  if (agent->GetClientAuthCredentials(cert, privKey)) {
+    return SECSuccess;
+  }
+  return SECFailure;
+}
+
+
+void TlsAgent::RequestClientAuth(bool requireAuth) {
+  EXPECT_TRUE(EnsureTlsSetup());
+  ASSERT_EQ(SERVER, role_);
+
+  EXPECT_EQ(SECSuccess,
+            SSL_OptionSet(ssl_fd_, SSL_REQUEST_CERTIFICATE, PR_TRUE));
+  EXPECT_EQ(SECSuccess,
+            SSL_OptionSet(ssl_fd_, SSL_REQUIRE_CERTIFICATE,
+                          requireAuth ? PR_TRUE : PR_FALSE));
+
+  EXPECT_EQ(SECSuccess,
+            SSL_AuthCertificateHook(ssl_fd_, &TlsAgent::ClientAuthenticated,
+                                    this));
+  expect_client_auth_ = true;
+}
+
 void TlsAgent::StartConnect() {
   EXPECT_TRUE(EnsureTlsSetup());
 
   SECStatus rv;
   rv = SSL_ResetHandshake(ssl_fd_, role_ == SERVER ? PR_TRUE : PR_FALSE);
   EXPECT_EQ(SECSuccess, rv);
   SetState(STATE_CONNECTING);
 }
@@ -186,16 +236,57 @@ void TlsAgent::SetVersionRange(uint16_t 
 void TlsAgent::SetExpectedVersion(uint16_t version) {
   expected_version_ = version;
 }
 
 void TlsAgent::SetExpectedReadError(bool err) {
   expected_read_error_ = err;
 }
 
+void TlsAgent::SetSignatureAlgorithms(const SSLSignatureAndHashAlg* algorithms,
+                                      size_t count) {
+  EXPECT_TRUE(EnsureTlsSetup());
+  EXPECT_LE(count, SSL_SignatureMaxCount());
+  EXPECT_EQ(SECSuccess, SSL_SignaturePrefSet(ssl_fd_, algorithms,
+                                             static_cast<unsigned int>(count)));
+  EXPECT_EQ(SECFailure, SSL_SignaturePrefSet(ssl_fd_, algorithms, 0))
+      << "setting no algorithms should fail and do nothing";
+
+  unsigned int configuredCount;
+  SSLSignatureAndHashAlg configuredAlgorithms[count];
+  EXPECT_EQ(SECFailure,
+            SSL_SignaturePrefGet(ssl_fd_, nullptr, &configuredCount, 1))
+      << "get algorithms, algorithms is nullptr";
+  EXPECT_EQ(SECFailure,
+            SSL_SignaturePrefGet(ssl_fd_, configuredAlgorithms,
+                                 &configuredCount, 0))
+      << "get algorithms, too little space";
+  EXPECT_EQ(SECFailure,
+            SSL_SignaturePrefGet(ssl_fd_, configuredAlgorithms, nullptr,
+                                 PR_ARRAY_SIZE(configuredAlgorithms)))
+      << "get algorithms, algCountOut is nullptr";
+
+  EXPECT_EQ(SECSuccess,
+            SSL_SignaturePrefGet(ssl_fd_, configuredAlgorithms,
+                                 &configuredCount,
+                                 PR_ARRAY_SIZE(configuredAlgorithms)));
+  // SignaturePrefSet drops unsupported algorithms silently, so the number that
+  // are configured might be fewer.
+  EXPECT_LE(configuredCount, count);
+  unsigned int i = 0;
+  for (unsigned int j = 0; j < count && i < configuredCount; ++j) {
+    if (i < configuredCount &&
+        algorithms[j].hashAlg == configuredAlgorithms[i].hashAlg &&
+        algorithms[j].sigAlg == configuredAlgorithms[i].sigAlg) {
+      ++i;
+    }
+  }
+  EXPECT_EQ(i, configuredCount) << "algorithms in use were all set";
+}
+
 void TlsAgent::CheckKEAType(SSLKEAType type) const {
   EXPECT_EQ(STATE_CONNECTED, state_);
   EXPECT_EQ(type, csinfo_.keaType);
 }
 
 void TlsAgent::CheckAuthType(SSLAuthType type) const {
   EXPECT_EQ(STATE_CONNECTED, state_);
   EXPECT_EQ(type, csinfo_.authAlgorithm);
@@ -237,17 +328,16 @@ void TlsAgent::CheckAlpn(SSLNextProtoSta
 
 void TlsAgent::EnableSrtp() {
   EXPECT_TRUE(EnsureTlsSetup());
   const uint16_t ciphers[] = {
     SRTP_AES128_CM_HMAC_SHA1_80, SRTP_AES128_CM_HMAC_SHA1_32
   };
   EXPECT_EQ(SECSuccess, SSL_SetSRTPCiphers(ssl_fd_, ciphers,
                                            PR_ARRAY_SIZE(ciphers)));
-
 }
 
 void TlsAgent::CheckSrtp() const {
   uint16_t actual;
   EXPECT_EQ(SECSuccess, SSL_GetSRTPCipher(ssl_fd_, &actual));
   EXPECT_EQ(SRTP_AES128_CM_HMAC_SHA1_80, actual);
 }
 
@@ -337,17 +427,21 @@ void TlsAgent::Handshake() {
       Poller::Instance()->Wait(READABLE_EVENT, adapter_, this,
                                &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);
+      if (IS_SSL_ERROR(err)) {
+        LOG("Handshake failed with SSL error " << err - SSL_ERROR_BASE);
+      } else {
+        LOG("Handshake failed with error " << err);
+      }
       error_code_ = err;
       SetState(STATE_ERROR);
       return;
   }
 }
 
 void TlsAgent::PrepareForRenegotiate() {
   EXPECT_EQ(STATE_CONNECTED, state_);
--- a/external_tests/ssl_gtest/tls_agent.h
+++ b/external_tests/ssl_gtest/tls_agent.h
@@ -61,25 +61,32 @@ class TlsAgent : public PollTarget {
   // Marks the internal state as CONNECTING in anticipation of renegotiation.
   void PrepareForRenegotiate();
   // Prepares for renegotiation, then actually triggers it.
   void StartRenegotiate();
   void EnableSomeEcdheCiphers();
   void DisableDheCiphers();
   bool EnsureTlsSetup();
 
+  void SetupClientAuth();
+  void RequestClientAuth(bool requireAuth);
+  bool GetClientAuthCredentials(CERTCertificate** cert,
+                                SECKEYPrivateKey** priv) const;
+
   void ConfigureSessionCache(SessionResumptionMode mode);
   void SetSessionTicketsEnabled(bool en);
   void SetSessionCacheEnabled(bool en);
   void SetVersionRange(uint16_t minver, uint16_t maxver);
   void CheckPreliminaryInfo();
   void SetExpectedVersion(uint16_t version);
   void SetExpectedReadError(bool err);
   void EnableFalseStart();
   void ExpectResumption();
+  void SetSignatureAlgorithms(const SSLSignatureAndHashAlg* algorithms,
+                              size_t count);
   void EnableAlpn(const uint8_t* val, size_t len);
   void CheckAlpn(SSLNextProtoState expected_state,
                  const std::string& expected) const;
   void EnableSrtp();
   void CheckSrtp() const;
   void CheckErrorCode(int32_t expected) const;
   void SendData(size_t bytes, size_t blocksize = 1024);
   void ReadBytes();
@@ -136,16 +143,30 @@ class TlsAgent : public PollTarget {
   static SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd,
                                        PRBool checksig, PRBool isServer) {
     TlsAgent* agent = reinterpret_cast<TlsAgent*>(arg);
     agent->CheckPreliminaryInfo();
     agent->auth_certificate_hook_called_ = true;
     return SECSuccess;
   }
 
+  // Client auth certificate hook.
+  static SECStatus ClientAuthenticated(void* arg, PRFileDesc* fd,
+                                       PRBool checksig, PRBool isServer) {
+    TlsAgent* agent = reinterpret_cast<TlsAgent*>(arg);
+    EXPECT_TRUE(agent->expect_client_auth_);
+    EXPECT_TRUE(isServer);
+    return SECSuccess;
+  }
+
+  static SECStatus GetClientAuthDataHook(void* self, PRFileDesc* fd,
+                                         CERTDistNames* caNames,
+                                         CERTCertificate** cert,
+                                         SECKEYPrivateKey** privKey);
+
   static void ReadableCallback(PollTarget* self, Event event) {
     TlsAgent* agent = static_cast<TlsAgent*>(self);
     agent->ReadableCallback_int();
   }
 
 
   void ReadableCallback_int() {
     LOG("Readable");
@@ -196,16 +217,17 @@ class TlsAgent : public PollTarget {
   DummyPrSocket* adapter_;
   PRFileDesc* ssl_fd_;
   Role role_;
   State state_;
   bool falsestart_enabled_;
   uint16_t expected_version_;
   uint16_t expected_cipher_suite_;
   bool expect_resumption_;
+  bool expect_client_auth_;
   bool can_falsestart_hook_called_;
   bool sni_hook_called_;
   bool auth_certificate_hook_called_;
   bool handshake_callback_called_;
   SSLChannelInfo info_;
   SSLCipherSuiteInfo csinfo_;
   SSLVersionRange vrange_;
   int32_t error_code_;
--- a/external_tests/ssl_gtest/tls_connect.cc
+++ b/external_tests/ssl_gtest/tls_connect.cc
@@ -19,16 +19,19 @@ static const std::string kTlsModesStream
 ::testing::internal::ParamGenerator<std::string>
   TlsConnectTestBase::kTlsModesStream = ::testing::ValuesIn(kTlsModesStreamArr);
 static const std::string kTlsModesAllArr[] = {"TLS", "DTLS"};
 ::testing::internal::ParamGenerator<std::string>
   TlsConnectTestBase::kTlsModesAll = ::testing::ValuesIn(kTlsModesAllArr);
 static const uint16_t kTlsV10Arr[] = {SSL_LIBRARY_VERSION_TLS_1_0};
 ::testing::internal::ParamGenerator<uint16_t>
   TlsConnectTestBase::kTlsV10 = ::testing::ValuesIn(kTlsV10Arr);
+static const uint16_t kTlsV11Arr[] = {SSL_LIBRARY_VERSION_TLS_1_1};
+::testing::internal::ParamGenerator<uint16_t>
+  TlsConnectTestBase::kTlsV11 = ::testing::ValuesIn(kTlsV11Arr);
 static const uint16_t kTlsV11V12Arr[] = {SSL_LIBRARY_VERSION_TLS_1_1,
                                          SSL_LIBRARY_VERSION_TLS_1_2};
 ::testing::internal::ParamGenerator<uint16_t>
   TlsConnectTestBase::kTlsV11V12 = ::testing::ValuesIn(kTlsV11V12Arr);
 // TODO: add TLS 1.3
 static const uint16_t kTlsV12PlusArr[] = {SSL_LIBRARY_VERSION_TLS_1_2};
 ::testing::internal::ParamGenerator<uint16_t>
   TlsConnectTestBase::kTlsV12Plus = ::testing::ValuesIn(kTlsV12PlusArr);
@@ -244,20 +247,27 @@ void TlsConnectTestBase::EnableSrtp() {
 void TlsConnectTestBase::CheckSrtp() const {
   client_->CheckSrtp();
   server_->CheckSrtp();
 }
 
 void TlsConnectTestBase::SendReceive() {
   client_->SendData(50);
   server_->SendData(50);
-  WAIT_(
-      client_->received_bytes() == 50 &&
-      server_->received_bytes() == 50, 2000);
+  WAIT_(client_->received_bytes() == 50U &&
+        server_->received_bytes() == 50U, 2000);
   ASSERT_EQ(50U, client_->received_bytes());
   ASSERT_EQ(50U, server_->received_bytes());
 }
 
 TlsConnectGeneric::TlsConnectGeneric()
   : TlsConnectTestBase(TlsConnectTestBase::ToMode(std::get<0>(GetParam())),
                        std::get<1>(GetParam())) {}
 
+TlsConnectPre12::TlsConnectPre12()
+  : TlsConnectTestBase(TlsConnectTestBase::ToMode(std::get<0>(GetParam())),
+                       std::get<1>(GetParam())) {}
+
+TlsConnectTls12::TlsConnectTls12()
+  : TlsConnectTestBase(TlsConnectTestBase::ToMode(GetParam()),
+                       SSL_LIBRARY_VERSION_TLS_1_2) {}
+
 } // namespace nss_test
--- a/external_tests/ssl_gtest/tls_connect.h
+++ b/external_tests/ssl_gtest/tls_connect.h
@@ -19,16 +19,17 @@
 namespace nss_test {
 
 // A generic TLS connection test base.
 class TlsConnectTestBase : public ::testing::Test {
  public:
   static ::testing::internal::ParamGenerator<std::string> kTlsModesStream;
   static ::testing::internal::ParamGenerator<std::string> kTlsModesAll;
   static ::testing::internal::ParamGenerator<uint16_t> kTlsV10;
+  static ::testing::internal::ParamGenerator<uint16_t> kTlsV11;
   static ::testing::internal::ParamGenerator<uint16_t> kTlsV11V12;
   static ::testing::internal::ParamGenerator<uint16_t> kTlsV12Plus;
 
   static inline Mode ToMode(const std::string& str) {
     return str == "TLS" ? STREAM : DGRAM;
   }
 
   TlsConnectTestBase(Mode mode, uint16_t version);
@@ -101,11 +102,27 @@ class TlsConnectDatagram : public TlsCon
 // should use TEST_P().
 class TlsConnectGeneric
   : public TlsConnectTestBase,
     public ::testing::WithParamInterface<std::tuple<std::string, uint16_t>> {
  public:
   TlsConnectGeneric();
 };
 
+// A Pre TLS 1.2 generic test.
+class TlsConnectPre12
+  : public TlsConnectTestBase,
+    public ::testing::WithParamInterface<std::tuple<std::string, uint16_t>> {
+ public:
+  TlsConnectPre12();
+};
+
+// A TLS 1.2 only generic test.
+class TlsConnectTls12
+  : public TlsConnectTestBase,
+    public ::testing::WithParamInterface<std::string> {
+ public:
+  TlsConnectTls12();
+};
+
 } // namespace nss_test
 
 #endif
--- a/lib/ssl/SSLerrs.h
+++ b/lib/ssl/SSLerrs.h
@@ -423,8 +423,14 @@ ER3(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALE
 "The server rejected the handshake because the client downgraded to a lower "
 "TLS version than the server supports.")
 
 ER3(SSL_ERROR_WEAK_SERVER_CERT_KEY, (SSL_ERROR_BASE + 132),
 "The server certificate included a public key that was too weak.")
 
 ER3(SSL_ERROR_RX_SHORT_DTLS_READ, (SSL_ERROR_BASE + 133),
 "Not enough room in buffer for DTLS record.")
+
+ER3(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM, (SSL_ERROR_BASE + 134),
+"No supported TLS signature algorithm was configured.")
+
+ER3(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM, (SSL_ERROR_BASE + 135),
+"The peer used an unsupported combination of signature and hash algorithm.")
--- a/lib/ssl/ssl.def
+++ b/lib/ssl/ssl.def
@@ -176,11 +176,14 @@ SSL_SetCanFalseStartCallback;
 SSL_DHEGroupPrefSet;
 SSL_EnableWeakDHEPrimeGroup;
 ;+    local:
 ;+*;
 ;+};
 ;+NSS_3.21 {    # NSS 3.21 release
 ;+    global:
 SSL_GetPreliminaryChannelInfo;
+SSL_SignaturePrefSet;
+SSL_SignaturePrefGet;
+SSL_SignatureMaxCount;
 ;+    local:
 ;+*;
 ;+};
--- a/lib/ssl/ssl.h
+++ b/lib/ssl/ssl.h
@@ -292,16 +292,56 @@ SSL_IMPORT SECStatus SSL_SetPolicy(long 
 /* New function names */
 SSL_IMPORT SECStatus SSL_CipherPrefSet(PRFileDesc *fd, PRInt32 cipher, PRBool enabled);
 SSL_IMPORT SECStatus SSL_CipherPrefGet(PRFileDesc *fd, PRInt32 cipher, PRBool *enabled);
 SSL_IMPORT SECStatus SSL_CipherPrefSetDefault(PRInt32 cipher, PRBool enabled);
 SSL_IMPORT SECStatus SSL_CipherPrefGetDefault(PRInt32 cipher, PRBool *enabled);
 SSL_IMPORT SECStatus SSL_CipherPolicySet(PRInt32 cipher, PRInt32 policy);
 SSL_IMPORT SECStatus SSL_CipherPolicyGet(PRInt32 cipher, PRInt32 *policy);
 
+/*
+** Control for TLS signature algorithms for TLS 1.2 only.
+**
+** This governs what signature algorithms are sent by a client in the
+** signature_algorithms extension.  A client will not accept a signature from a
+** server unless it uses an enabled algorithm.
+**
+** This also governs what the server sends in the supported_signature_algorithms
+** field of a CertificateRequest.  It also changes what the server uses to sign
+** ServerKeyExchange: a server uses the first entry from this list that is
+** compatible with the client's advertised signature_algorithms extension and
+** the selected server certificate.
+**
+** Omitting SHA-256 from this list might be foolish.  Support is mandatory in
+** TLS 1.2 and there might be interoperability issues.  For a server, NSS only
+** supports SHA-256 for verifying a TLS 1.2 CertificateVerify.  This list needs
+** to include SHA-256 if client authentication is requested or required, or
+** creating a CertificateRequest will fail.
+*/
+SSL_IMPORT SECStatus SSL_SignaturePrefSet(
+    PRFileDesc *fd, const SSLSignatureAndHashAlg *algorithms,
+    unsigned int count);
+
+/*
+** Get the currently configured signature algorithms.
+**
+** The algorithms are written to |algorithms| but not if there are more than
+** |maxCount| values configured.  The number of algorithms that are in use are
+** written to |count|.  This fails if |maxCount| is insufficiently large.
+*/
+SSL_IMPORT SECStatus SSL_SignaturePrefGet(
+    PRFileDesc *fd, SSLSignatureAndHashAlg *algorithms, unsigned int *count,
+    unsigned int maxCount);
+
+/*
+** Returns the maximum number of signature algorithms that are supported and
+** can be set or retrieved using SSL_SignaturePrefSet or SSL_SignaturePrefGet.
+*/
+SSL_IMPORT unsigned int SSL_SignatureMaxCount();
+
 /* SSL_DHEGroupPrefSet is used to configure the set of allowed/enabled DHE group
 ** parameters that can be used by NSS for the given server socket.
 ** The first item in the array is used as the default group, if no other
 ** selection criteria can be used by NSS.
 ** The set is provided as an array of identifiers as defined by SSLDHEGroupType.
 ** If more than one group identifier is provided, NSS will select the one to use.
 ** For example, a TLS extension sent by the client might indicate a preference.
 */
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -56,18 +56,18 @@ static SECStatus ssl3_SendCertificateReq
 static SECStatus ssl3_SendNextProto(         sslSocket *ss);
 static SECStatus ssl3_SendFinished(          sslSocket *ss, PRInt32 flags);
 static SECStatus ssl3_SendServerHello(       sslSocket *ss);
 static SECStatus ssl3_SendServerHelloDone(   sslSocket *ss);
 static SECStatus ssl3_SendServerKeyExchange( sslSocket *ss);
 static SECStatus ssl3_UpdateHandshakeHashes( sslSocket *ss,
                                              const unsigned char *b,
                                              unsigned int l);
+static SECOidTag ssl3_TLSHashAlgorithmToOID(SSLHashType hashFunc);
 static SECStatus ssl3_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags);
-static int       ssl3_OIDToTLSHashAlgorithm(SECOidTag oid);
 
 static SECStatus Null_Cipher(void *ctx, unsigned char *output, int *outputLen,
 			     int maxOutputLen, const unsigned char *input,
 			     int inputLen);
 #ifndef NO_PKCS11_BYPASS
 static SECStatus ssl3_AESGCMBypass(ssl3KeyMaterial *keys, PRBool doDecrypt,
 				   unsigned char *out, int *outlen, int maxout,
 				   const unsigned char *in, int inlen,
@@ -171,16 +171,33 @@ static ssl3CipherSuiteCfg cipherSuites[s
  { TLS_ECDH_RSA_WITH_NULL_SHA,              SSL_ALLOWED, PR_FALSE, PR_FALSE},
  { TLS_ECDH_ECDSA_WITH_NULL_SHA,            SSL_ALLOWED, PR_FALSE, PR_FALSE},
 #endif /* NSS_DISABLE_ECC */
  { TLS_RSA_WITH_NULL_SHA,                   SSL_ALLOWED, PR_FALSE, PR_FALSE},
  { TLS_RSA_WITH_NULL_SHA256,                SSL_ALLOWED, PR_FALSE, PR_FALSE},
  { TLS_RSA_WITH_NULL_MD5,                   SSL_ALLOWED, PR_FALSE, PR_FALSE},
 };
 
+static const SSLSignatureAndHashAlg defaultSignatureAlgorithms[] = {
+    {ssl_hash_sha256, ssl_sign_rsa},
+    {ssl_hash_sha384, ssl_sign_rsa},
+    {ssl_hash_sha512, ssl_sign_rsa},
+    {ssl_hash_sha1, ssl_sign_rsa},
+#ifndef NSS_DISABLE_ECC
+    {ssl_hash_sha256, ssl_sign_ecdsa},
+    {ssl_hash_sha384, ssl_sign_ecdsa},
+    {ssl_hash_sha512, ssl_sign_ecdsa},
+    {ssl_hash_sha1, ssl_sign_ecdsa},
+#endif
+    {ssl_hash_sha256, ssl_sign_dsa},
+    {ssl_hash_sha1, ssl_sign_dsa}
+};
+PR_STATIC_ASSERT(PR_ARRAY_SIZE(defaultSignatureAlgorithms) <=
+                 MAX_SIGNATURE_ALGORITHMS);
+
 /* Verify that SSL_ImplementedCiphers and cipherSuites are in consistent order.
  */
 #ifdef DEBUG
 void ssl3_CheckCipherSuiteOrderConsistency()
 {
     unsigned int i;
 
     /* Note that SSL_ImplementedCiphers has more elements than cipherSuites
@@ -231,30 +248,16 @@ compressionEnabled(sslSocket *ss, SSLCom
 static const /*SSL3ClientCertificateType */ PRUint8 certificate_types [] = {
     ct_RSA_sign,
 #ifndef NSS_DISABLE_ECC
     ct_ECDSA_sign,
 #endif /* NSS_DISABLE_ECC */
     ct_DSS_sign,
 };
 
-/* This block is the contents of the supported_signature_algorithms field of
- * our TLS 1.2 CertificateRequest message, in wire format. See
- * https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
- *
- * This block contains only sha256 entries because we only support TLS 1.2
- * CertificateVerify messages that use the handshake hash. */
-static const PRUint8 supported_signature_algorithms[] = {
-    tls_hash_sha256, tls_sig_rsa,
-#ifndef NSS_DISABLE_ECC
-    tls_hash_sha256, tls_sig_ecdsa,
-#endif
-    tls_hash_sha256, tls_sig_dsa,
-};
-
 #define EXPORT_RSA_KEY_LENGTH 64	/* bytes */
 
 
 /* This global item is used only in servers.  It is is initialized by
 ** SSL_ConfigSecureServer(), and is used in ssl3_SendCertificateRequest().
 */
 CERTDistNames *ssl3_server_ca_list = NULL;
 static SSL3Statistics ssl3stats;
@@ -959,61 +962,62 @@ ssl3_SignHashes(SSL3Hashes *hash, SECKEY
 
     switch (key->keyType) {
     case rsaKey:
 	hashItem.data = hash->u.raw;
 	hashItem.len = hash->len;
 	break;
     case dsaKey:
 	doDerEncode = isTLS;
-	/* SEC_OID_UNKNOWN is used to specify the MD5/SHA1 concatenated hash.
+	/* ssl_hash_none is used to specify the MD5/SHA1 concatenated hash.
 	 * In that case, we use just the SHA1 part. */
-	if (hash->hashAlg == SEC_OID_UNKNOWN) {
+	if (hash->hashAlg == ssl_hash_none) {
 	    hashItem.data = hash->u.s.sha;
 	    hashItem.len = sizeof(hash->u.s.sha);
 	} else {
 	    hashItem.data = hash->u.raw;
 	    hashItem.len = hash->len;
 	}
 	break;
 #ifndef NSS_DISABLE_ECC
     case ecKey:
 	doDerEncode = PR_TRUE;
-	/* SEC_OID_UNKNOWN is used to specify the MD5/SHA1 concatenated hash.
+	/* ssl_hash_none is used to specify the MD5/SHA1 concatenated hash.
 	 * In that case, we use just the SHA1 part. */
-	if (hash->hashAlg == SEC_OID_UNKNOWN) {
+	if (hash->hashAlg == ssl_hash_none) {
 	    hashItem.data = hash->u.s.sha;
 	    hashItem.len = sizeof(hash->u.s.sha);
 	} else {
 	    hashItem.data = hash->u.raw;
 	    hashItem.len = hash->len;
 	}
 	break;
 #endif /* NSS_DISABLE_ECC */
     default:
 	PORT_SetError(SEC_ERROR_INVALID_KEY);
 	goto done;
     }
     PRINT_BUF(60, (NULL, "hash(es) to be signed", hashItem.data, hashItem.len));
 
-    if (hash->hashAlg == SEC_OID_UNKNOWN) {
+    if (hash->hashAlg == ssl_hash_none) {
 	signatureLen = PK11_SignatureLen(key);
 	if (signatureLen <= 0) {
 	    PORT_SetError(SEC_ERROR_INVALID_KEY);
 	    goto done;
 	}
 
 	buf->len  = (unsigned)signatureLen;
 	buf->data = (unsigned char *)PORT_Alloc(signatureLen);
 	if (!buf->data)
 	    goto done;  /* error code was set. */
 
 	rv = PK11_Sign(key, buf, &hashItem);
     } else {
-	rv = SGN_Digest(key, hash->hashAlg, buf, &hashItem);
+        SECOidTag hashOID = ssl3_TLSHashAlgorithmToOID(hash->hashAlg);
+        rv = SGN_Digest(key, hashOID, buf, &hashItem);
     }
     if (rv != SECSuccess) {
 	ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE);
     } else if (doDerEncode) {
 	SECItem   derSig	= {siBuffer, NULL, 0};
 
 	/* This also works for an ECDSA signature */
 	rv = DSAU_EncodeDerSigWithLen(&derSig, buf, buf->len);
@@ -1051,28 +1055,28 @@ ssl3_VerifySignedHashes(SSL3Hashes *hash
                   buf->data, buf->len));
 
     key = CERT_ExtractPublicKey(cert);
     if (key == NULL) {
 	ssl_MapLowLevelError(SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE);
     	return SECFailure;
     }
 
-    hashAlg = hash->hashAlg;
+    hashAlg = ssl3_TLSHashAlgorithmToOID(hash->hashAlg);
     switch (key->keyType) {
     case rsaKey:
 	encAlg = SEC_OID_PKCS1_RSA_ENCRYPTION;
 	hashItem.data = hash->u.raw;
 	hashItem.len = hash->len;
 	break;
     case dsaKey:
 	encAlg = SEC_OID_ANSIX9_DSA_SIGNATURE;
-	/* SEC_OID_UNKNOWN is used to specify the MD5/SHA1 concatenated hash.
+	/* ssl_hash_none is used to specify the MD5/SHA1 concatenated hash.
 	 * In that case, we use just the SHA1 part. */
-	if (hash->hashAlg == SEC_OID_UNKNOWN) {
+	if (hash->hashAlg == ssl_hash_none) {
 	    hashItem.data = hash->u.s.sha;
 	    hashItem.len = sizeof(hash->u.s.sha);
 	} else {
 	    hashItem.data = hash->u.raw;
 	    hashItem.len = hash->len;
 	}
 	/* Allow DER encoded DSA signatures in SSL 3.0 */
 	if (isTLS || buf->len != SECKEY_SignatureLen(key)) {
@@ -1083,23 +1087,23 @@ ssl3_VerifySignedHashes(SSL3Hashes *hash
 	    }
 	    buf = signature;
 	}
 	break;
 
 #ifndef NSS_DISABLE_ECC
     case ecKey:
 	encAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
-	/* SEC_OID_UNKNOWN is used to specify the MD5/SHA1 concatenated hash.
+	/* ssl_hash_none is used to specify the MD5/SHA1 concatenated hash.
 	 * In that case, we use just the SHA1 part.
 	 * ECDSA signatures always encode the integers r and s using ASN.1
 	 * (unlike DSA where ASN.1 encoding is used with TLS but not with
 	 * SSL3). So we can use VFY_VerifyDigestDirect for ECDSA.
 	 */
-	if (hash->hashAlg == SEC_OID_UNKNOWN) {
+	if (hash->hashAlg == ssl_hash_none) {
 	    hashAlg = SEC_OID_SHA1;
 	    hashItem.data = hash->u.s.sha;
 	    hashItem.len = sizeof(hash->u.s.sha);
 	} else {
 	    hashItem.data = hash->u.raw;
 	    hashItem.len = hash->len;
 	}
 	break;
@@ -1117,113 +1121,109 @@ ssl3_VerifySignedHashes(SSL3Hashes *hash
     if (hashAlg == SEC_OID_UNKNOWN || key->keyType == dsaKey) {
 	/* VFY_VerifyDigestDirect requires DSA signatures to be DER-encoded.
 	 * DSA signatures are DER-encoded in TLS but not in SSL3 and the code
 	 * above always removes the DER encoding of DSA signatures when
 	 * present. Thus DSA signatures are always verified with PK11_Verify.
 	 */
 	rv = PK11_Verify(key, buf, &hashItem, pwArg);
     } else {
-	rv = VFY_VerifyDigestDirect(&hashItem, key, buf, encAlg, hashAlg,
-				    pwArg);
+        rv = VFY_VerifyDigestDirect(&hashItem, key, buf, encAlg, hashAlg,
+                                    pwArg);
     }
     SECKEY_DestroyPublicKey(key);
     if (signature) {
     	SECITEM_FreeItem(signature, PR_TRUE);
     }
     if (rv != SECSuccess) {
 	ssl_MapLowLevelError(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
     }
     return rv;
 }
 
 
 /* Caller must set hiLevel error code. */
 /* Called from ssl3_ComputeExportRSAKeyHash
  *             ssl3_ComputeDHKeyHash
- * which are called from ssl3_HandleServerKeyExchange. 
+ * which are called from ssl3_HandleServerKeyExchange.
  *
- * hashAlg: either the OID for a hash algorithm or SEC_OID_UNKNOWN to specify
- * the pre-1.2, MD5/SHA1 combination hash.
+ * hashAlg: ssl_hash_none indicates the pre-1.2, MD5/SHA1 combination hash.
  */
 SECStatus
-ssl3_ComputeCommonKeyHash(SECOidTag hashAlg,
-			  PRUint8 * hashBuf, unsigned int bufLen,
-			  SSL3Hashes *hashes, PRBool bypassPKCS11)
-{
-    SECStatus     rv 		= SECSuccess;
+ssl3_ComputeCommonKeyHash(SSLHashType hashAlg,
+                          PRUint8 * hashBuf, unsigned int bufLen,
+                          SSL3Hashes *hashes, PRBool bypassPKCS11)
+{
+    SECStatus rv;
+    SECOidTag hashOID;
 
 #ifndef NO_PKCS11_BYPASS
     if (bypassPKCS11) {
-	if (hashAlg == SEC_OID_UNKNOWN) {
-	    MD5_HashBuf (hashes->u.s.md5, hashBuf, bufLen);
-	    SHA1_HashBuf(hashes->u.s.sha, hashBuf, bufLen);
-	    hashes->len = MD5_LENGTH + SHA1_LENGTH;
-	} else if (hashAlg == SEC_OID_SHA1) {
-	    SHA1_HashBuf(hashes->u.raw, hashBuf, bufLen);
-	    hashes->len = SHA1_LENGTH;
-	} else if (hashAlg == SEC_OID_SHA256) {
-	    SHA256_HashBuf(hashes->u.raw, hashBuf, bufLen);
-	    hashes->len = SHA256_LENGTH;
-	} else if (hashAlg == SEC_OID_SHA384) {
-	    SHA384_HashBuf(hashes->u.raw, hashBuf, bufLen);
-	    hashes->len = SHA384_LENGTH;
-	} else if (hashAlg == SEC_OID_SHA512) {
-	    SHA512_HashBuf(hashes->u.raw, hashBuf, bufLen);
-	    hashes->len = SHA512_LENGTH;
-	} else {
-	    PORT_SetError(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM);
-	    return SECFailure;
-	}
-    } else 
+        if (hashAlg == ssl_hash_none) {
+            MD5_HashBuf (hashes->u.s.md5, hashBuf, bufLen);
+            SHA1_HashBuf(hashes->u.s.sha, hashBuf, bufLen);
+            hashes->len = MD5_LENGTH + SHA1_LENGTH;
+        } else if (hashAlg == ssl_hash_sha1) {
+            SHA1_HashBuf(hashes->u.raw, hashBuf, bufLen);
+            hashes->len = SHA1_LENGTH;
+        } else if (hashAlg == ssl_hash_sha256) {
+            SHA256_HashBuf(hashes->u.raw, hashBuf, bufLen);
+            hashes->len = SHA256_LENGTH;
+        } else if (hashAlg == ssl_hash_sha384) {
+            SHA384_HashBuf(hashes->u.raw, hashBuf, bufLen);
+            hashes->len = SHA384_LENGTH;
+        } else if (hashAlg == ssl_hash_sha512) {
+            SHA512_HashBuf(hashes->u.raw, hashBuf, bufLen);
+            hashes->len = SHA512_LENGTH;
+        } else {
+            PORT_SetError(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM);
+            return SECFailure;
+        }
+    } else
 #endif
     {
-	if (hashAlg == SEC_OID_UNKNOWN) {
-	    rv = PK11_HashBuf(SEC_OID_MD5, hashes->u.s.md5, hashBuf, bufLen);
-	    if (rv != SECSuccess) {
-		ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE);
-		rv = SECFailure;
-		goto done;
-	    }
-
-	    rv = PK11_HashBuf(SEC_OID_SHA1, hashes->u.s.sha, hashBuf, bufLen);
-	    if (rv != SECSuccess) {
-		ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
-		rv = SECFailure;
-	    }
-	    hashes->len = MD5_LENGTH + SHA1_LENGTH;
-	} else {
-	    hashes->len = HASH_ResultLenByOidTag(hashAlg);
-	    if (hashes->len > sizeof(hashes->u.raw)) {
-		ssl_MapLowLevelError(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM);
-		rv = SECFailure;
-		goto done;
-	    }
-	    rv = PK11_HashBuf(hashAlg, hashes->u.raw, hashBuf, bufLen);
-	    if (rv != SECSuccess) {
-		ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
-		rv = SECFailure;
-	    }
-	}
+        if (hashAlg == ssl_hash_none) {
+            rv = PK11_HashBuf(SEC_OID_MD5, hashes->u.s.md5, hashBuf, bufLen);
+            if (rv != SECSuccess) {
+                ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE);
+                return rv;
+            }
+            rv = PK11_HashBuf(SEC_OID_SHA1, hashes->u.s.sha, hashBuf, bufLen);
+            if (rv != SECSuccess) {
+                ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
+                return rv;
+            }
+            hashes->len = MD5_LENGTH + SHA1_LENGTH;
+        } else {
+            hashOID = ssl3_TLSHashAlgorithmToOID(hashAlg);
+            hashes->len = HASH_ResultLenByOidTag(hashOID);
+            if (hashes->len == 0 || hashes->len > sizeof(hashes->u.raw)) {
+                ssl_MapLowLevelError(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM);
+                return SECFailure;
+            }
+            rv = PK11_HashBuf(hashOID, hashes->u.raw, hashBuf, bufLen);
+            if (rv != SECSuccess) {
+                ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
+                return rv;
+            }
+        }
     }
     hashes->hashAlg = hashAlg;
-
-done:
-    return rv;
+    return SECSuccess;
 }
 
 /* Caller must set hiLevel error code. 
 ** Called from ssl3_SendServerKeyExchange and 
 **             ssl3_HandleServerKeyExchange.
 */
 static SECStatus
-ssl3_ComputeExportRSAKeyHash(SECOidTag hashAlg,
-			     SECItem modulus, SECItem publicExponent,
-			     SSL3Random *client_rand, SSL3Random *server_rand,
-			     SSL3Hashes *hashes, PRBool bypassPKCS11)
+ssl3_ComputeExportRSAKeyHash(SSLHashType hashAlg,
+                             SECItem modulus, SECItem publicExponent,
+                             SSL3Random *client_rand, SSL3Random *server_rand,
+                             SSL3Hashes *hashes, PRBool bypassPKCS11)
 {
     PRUint8     * hashBuf;
     PRUint8     * pBuf;
     SECStatus     rv 		= SECSuccess;
     unsigned int  bufLen;
     PRUint8       buf[2*SSL3_RANDOM_LENGTH + 2 + 4096/8 + 2 + 4096/8];
 
     bufLen = 2*SSL3_RANDOM_LENGTH + 2 + modulus.len + 2 + publicExponent.len;
@@ -1251,17 +1251,17 @@ ssl3_ComputeExportRSAKeyHash(SECOidTag h
     memcpy(pBuf, publicExponent.data, publicExponent.len);
     	pBuf += publicExponent.len;
     PORT_Assert((unsigned int)(pBuf - hashBuf) == bufLen);
 
     rv = ssl3_ComputeCommonKeyHash(hashAlg, hashBuf, bufLen, hashes,
 				   bypassPKCS11);
 
     PRINT_BUF(95, (NULL, "RSAkey hash: ", hashBuf, bufLen));
-    if (hashAlg == SEC_OID_UNKNOWN) {
+    if (hashAlg == ssl_hash_none) {
 	PRINT_BUF(95, (NULL, "RSAkey hash: MD5 result",
 		  hashes->u.s.md5, MD5_LENGTH));
 	PRINT_BUF(95, (NULL, "RSAkey hash: SHA1 result",
 		  hashes->u.s.sha, SHA1_LENGTH));
     } else {
 	PRINT_BUF(95, (NULL, "RSAkey hash: result",
 		  hashes->u.raw, hashes->len));
     }
@@ -1269,20 +1269,20 @@ ssl3_ComputeExportRSAKeyHash(SECOidTag h
     if (hashBuf != buf && hashBuf != NULL)
     	PORT_Free(hashBuf);
     return rv;
 }
 
 /* Caller must set hiLevel error code. */
 /* Called from ssl3_HandleServerKeyExchange. */
 static SECStatus
-ssl3_ComputeDHKeyHash(SECOidTag hashAlg,
-		      SECItem dh_p, SECItem dh_g, SECItem dh_Ys,
-		      SSL3Random *client_rand, SSL3Random *server_rand,
-		      SSL3Hashes *hashes, PRBool bypassPKCS11)
+ssl3_ComputeDHKeyHash(SSLHashType hashAlg,
+                      SECItem dh_p, SECItem dh_g, SECItem dh_Ys,
+                      SSL3Random *client_rand, SSL3Random *server_rand,
+                      SSL3Hashes *hashes, PRBool bypassPKCS11)
 {
     PRUint8     * hashBuf;
     PRUint8     * pBuf;
     SECStatus     rv 		= SECSuccess;
     unsigned int  bufLen;
     PRUint8       buf[2*SSL3_RANDOM_LENGTH + 2 + 4096/8 + 2 + 4096/8];
 
     bufLen = 2*SSL3_RANDOM_LENGTH + 2 + dh_p.len + 2 + dh_g.len + 2 + dh_Ys.len;
@@ -1315,17 +1315,17 @@ ssl3_ComputeDHKeyHash(SECOidTag hashAlg,
     memcpy(pBuf, dh_Ys.data, dh_Ys.len);
     	pBuf += dh_Ys.len;
     PORT_Assert((unsigned int)(pBuf - hashBuf) == bufLen);
 
     rv = ssl3_ComputeCommonKeyHash(hashAlg, hashBuf, bufLen, hashes,
 				   bypassPKCS11);
 
     PRINT_BUF(95, (NULL, "DHkey hash: ", hashBuf, bufLen));
-    if (hashAlg == SEC_OID_UNKNOWN) {
+    if (hashAlg == ssl_hash_none) {
 	PRINT_BUF(95, (NULL, "DHkey hash: MD5 result",
 		  hashes->u.s.md5, MD5_LENGTH));
 	PRINT_BUF(95, (NULL, "DHkey hash: SHA1 result",
 		  hashes->u.s.sha, SHA1_LENGTH));
     } else {
 	PRINT_BUF(95, (NULL, "DHkey hash: result",
 		  hashes->u.raw, hashes->len));
     }
@@ -4258,27 +4258,22 @@ ssl3_AppendHandshakeHeader(sslSocket *ss
 
     return rv;		/* error code set by AppendHandshake, if applicable. */
 }
 
 /* ssl3_AppendSignatureAndHashAlgorithm appends the serialisation of
  * |sigAndHash| to the current handshake message. */
 SECStatus
 ssl3_AppendSignatureAndHashAlgorithm(
-	sslSocket *ss, const SSL3SignatureAndHashAlgorithm* sigAndHash)
-{
-    unsigned char serialized[2];
-
-    serialized[0] = ssl3_OIDToTLSHashAlgorithm(sigAndHash->hashAlg);
-    if (serialized[0] == 0) {
-	PORT_SetError(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM);
-	return SECFailure;
-    }
-
-    serialized[1] = sigAndHash->sigAlg;
+    sslSocket *ss, const SSLSignatureAndHashAlg* sigAndHash)
+{
+    PRUint8 serialized[2];
+
+    serialized[0] = (PRUint8)sigAndHash->hashAlg;
+    serialized[1] = (PRUint8)sigAndHash->sigAlg;
 
     return ssl3_AppendHandshake(ss, serialized, sizeof(serialized));
 }
 
 /**************************************************************************
  * Consume Handshake functions.
  *
  * All data used in these functions is protected by two locks,
@@ -4382,151 +4377,181 @@ ssl3_ConsumeHandshakeVariable(sslSocket 
 	*length -= count;
     }
     return SECSuccess;
 }
 
 /* tlsHashOIDMap contains the mapping between TLS hash identifiers and the
  * SECOidTag used internally by NSS. */
 static const struct {
-    int tlsHash;
+    SSLHashType tlsHash;
     SECOidTag oid;
 } tlsHashOIDMap[] = {
-    { tls_hash_md5, SEC_OID_MD5 },
-    { tls_hash_sha1, SEC_OID_SHA1 },
-    { tls_hash_sha224, SEC_OID_SHA224 },
-    { tls_hash_sha256, SEC_OID_SHA256 },
-    { tls_hash_sha384, SEC_OID_SHA384 },
-    { tls_hash_sha512, SEC_OID_SHA512 }
+    { ssl_hash_sha1, SEC_OID_SHA1 },
+    { ssl_hash_sha256, SEC_OID_SHA256 },
+    { ssl_hash_sha384, SEC_OID_SHA384 },
+    { ssl_hash_sha512, SEC_OID_SHA512 }
 };
 
 /* ssl3_TLSHashAlgorithmToOID converts a TLS hash identifier into an OID value.
  * If the hash is not recognised, SEC_OID_UNKNOWN is returned.
  *
  * See https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
 SECOidTag
-ssl3_TLSHashAlgorithmToOID(int hashFunc)
+ssl3_TLSHashAlgorithmToOID(SSLHashType hashFunc)
 {
     unsigned int i;
 
     for (i = 0; i < PR_ARRAY_SIZE(tlsHashOIDMap); i++) {
 	if (hashFunc == tlsHashOIDMap[i].tlsHash) {
 	    return tlsHashOIDMap[i].oid;
 	}
     }
     return SEC_OID_UNKNOWN;
 }
 
-/* ssl3_OIDToTLSHashAlgorithm converts an OID to a TLS hash algorithm
- * identifier. If the hash is not recognised, zero is returned.
- *
- * See https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
-static int
-ssl3_OIDToTLSHashAlgorithm(SECOidTag oid)
-{
-    unsigned int i;
-
-    for (i = 0; i < PR_ARRAY_SIZE(tlsHashOIDMap); i++) {
-	if (oid == tlsHashOIDMap[i].oid) {
-	    return tlsHashOIDMap[i].tlsHash;
-	}
-    }
-    return 0;
-}
-
 /* ssl3_TLSSignatureAlgorithmForKeyType returns the TLS 1.2 signature algorithm
  * identifier for a given KeyType. */
 static SECStatus
-ssl3_TLSSignatureAlgorithmForKeyType(KeyType keyType,
-				     TLSSignatureAlgorithm *out)
+ssl3_TLSSignatureAlgorithmForKeyType(KeyType keyType, SSLSignType *out)
 {
     switch (keyType) {
     case rsaKey:
-	*out = tls_sig_rsa;
-	return SECSuccess;
+        *out = ssl_sign_rsa;
+        return SECSuccess;
     case dsaKey:
-	*out = tls_sig_dsa;
-	return SECSuccess;
+        *out = ssl_sign_dsa;
+        return SECSuccess;
     case ecKey:
-	*out = tls_sig_ecdsa;
-	return SECSuccess;
+        *out = ssl_sign_ecdsa;
+        return SECSuccess;
     default:
-	PORT_SetError(SEC_ERROR_INVALID_KEY);
-	return SECFailure;
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
     }
 }
 
 /* ssl3_TLSSignatureAlgorithmForCertificate returns the TLS 1.2 signature
  * algorithm identifier for the given certificate. */
 static SECStatus
 ssl3_TLSSignatureAlgorithmForCertificate(CERTCertificate *cert,
-					 TLSSignatureAlgorithm *out)
+                                         SSLSignType *out)
 {
     SECKEYPublicKey *key;
     KeyType keyType;
 
     key = CERT_ExtractPublicKey(cert);
     if (key == NULL) {
-	ssl_MapLowLevelError(SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE);
-    	return SECFailure;
+        ssl_MapLowLevelError(SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE);
+        return SECFailure;
     }
 
     keyType = key->keyType;
     SECKEY_DestroyPublicKey(key);
     return ssl3_TLSSignatureAlgorithmForKeyType(keyType, out);
 }
 
 /* ssl3_CheckSignatureAndHashAlgorithmConsistency checks that the signature
  * algorithm identifier in |sigAndHash| is consistent with the public key in
- * |cert|. If so, SECSuccess is returned. Otherwise, PORT_SetError is called
- * and SECFailure is returned. */
+ * |cert|. It also checks the hash algorithm against the configured signature
+ * algorithms.  If all the tests pass, SECSuccess is returned. Otherwise,
+ * PORT_SetError is called and SECFailure is returned. */
 SECStatus
 ssl3_CheckSignatureAndHashAlgorithmConsistency(
-	const SSL3SignatureAndHashAlgorithm *sigAndHash, CERTCertificate* cert)
+    sslSocket *ss, const SSLSignatureAndHashAlg *sigAndHash,
+    CERTCertificate* cert)
 {
     SECStatus rv;
-    TLSSignatureAlgorithm sigAlg;
+    SSLSignType sigAlg;
+    unsigned int i;
 
     rv = ssl3_TLSSignatureAlgorithmForCertificate(cert, &sigAlg);
     if (rv != SECSuccess) {
-	return rv;
+        return rv;
     }
     if (sigAlg != sigAndHash->sigAlg) {
-	PORT_SetError(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM);
-	return SECFailure;
-    }
-    return SECSuccess;
+        PORT_SetError(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM);
+        return SECFailure;
+    }
+
+    for (i = 0; i < ss->ssl3.signatureAlgorithmCount; ++i) {
+        const SSLSignatureAndHashAlg *alg = &ss->ssl3.signatureAlgorithms[i];
+        if (sigAndHash->sigAlg == alg->sigAlg &&
+            sigAndHash->hashAlg == alg->hashAlg) {
+            return SECSuccess;
+        }
+    }
+    PORT_SetError(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
+    return SECFailure;
+}
+
+PRBool
+ssl3_IsSupportedSignatureAlgorithm(const SSLSignatureAndHashAlg *alg)
+{
+    static const SSLHashType supportedHashes[] = {
+        ssl_hash_sha1,
+        ssl_hash_sha256,
+        ssl_hash_sha384,
+        ssl_hash_sha512
+    };
+
+    static const SSLSignType supportedSigAlgs[] = {
+        ssl_sign_rsa,
+#ifndef NSS_DISABLE_ECC
+        ssl_sign_ecdsa,
+#endif
+        ssl_sign_dsa
+    };
+
+    unsigned int i;
+    PRBool hashOK = PR_FALSE;
+    PRBool signOK = PR_FALSE;
+
+    for (i = 0; i < PR_ARRAY_SIZE(supportedHashes); ++i) {
+        if (alg->hashAlg == supportedHashes[i]) {
+            hashOK = PR_TRUE;
+            break;
+        }
+    }
+
+    for (i = 0; i < PR_ARRAY_SIZE(supportedSigAlgs); ++i) {
+        if (alg->sigAlg == supportedSigAlgs[i]) {
+            signOK = PR_TRUE;
+            break;
+        }
+    }
+
+    return hashOK && signOK;
 }
 
 /* ssl3_ConsumeSignatureAndHashAlgorithm reads a SignatureAndHashAlgorithm
  * structure from |b| and puts the resulting value into |out|. |b| and |length|
  * are updated accordingly.
  *
  * See https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
 SECStatus
 ssl3_ConsumeSignatureAndHashAlgorithm(sslSocket *ss,
-				      SSL3Opaque **b,
-				      PRUint32 *length,
-				      SSL3SignatureAndHashAlgorithm *out)
-{
-    unsigned char bytes[2];
+                                      SSL3Opaque **b,
+                                      PRUint32 *length,
+                                      SSLSignatureAndHashAlg *out)
+{
+    PRUint8 bytes[2];
     SECStatus rv;
 
     rv = ssl3_ConsumeHandshake(ss, bytes, sizeof(bytes), b, length);
     if (rv != SECSuccess) {
-	return rv;
-    }
-
-    out->hashAlg = ssl3_TLSHashAlgorithmToOID(bytes[0]);
-    if (out->hashAlg == SEC_OID_UNKNOWN) {
-	PORT_SetError(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM);
-	return SECFailure;
-    }
-
-    out->sigAlg = bytes[1];
+        return rv;
+    }
+
+    out->hashAlg = (SSLHashType)bytes[0];
+    out->sigAlg = (SSLSignType)bytes[1];
+    if (!ssl3_IsSupportedSignatureAlgorithm(out)) {
+        PORT_SetError(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
+        return SECFailure;
+    }
     return SECSuccess;
 }
 
 /**************************************************************************
  * end of Consume Handshake functions.
  **************************************************************************/
 
 /* Extract the hashes of handshake messages to this point.
@@ -4546,17 +4571,17 @@ ssl3_ComputeHandshakeHashes(sslSocket * 
 {
     SECStatus     rv        = SECSuccess;
     PRBool        isTLS     = (PRBool)(spec->version > SSL_LIBRARY_VERSION_3_0);
     unsigned int  outLength;
     SSL3Opaque    md5_inner[MAX_MAC_LENGTH];
     SSL3Opaque    sha_inner[MAX_MAC_LENGTH];
 
     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
-    hashes->hashAlg = SEC_OID_UNKNOWN;
+    hashes->hashAlg = ssl_hash_none;
 
 #ifndef NO_PKCS11_BYPASS
     if (ss->opt.bypassPKCS11 &&
 	ss->ssl3.hs.hashType == handshake_hash_single) {
 	/* compute them without PKCS11 */
 	PRUint64      sha_cx[MAX_MAC_CONTEXT_LLONGS];
 
 	if (!spec->msItem.data) {
@@ -4567,17 +4592,17 @@ ssl3_ComputeHandshakeHashes(sslSocket * 
 	ss->ssl3.hs.sha_clone(sha_cx, ss->ssl3.hs.sha_cx);
 	ss->ssl3.hs.sha_obj->end(sha_cx, hashes->u.raw, &hashes->len,
 				 sizeof(hashes->u.raw));
 
 	PRINT_BUF(60, (NULL, "SHA-256: result", hashes->u.raw, hashes->len));
 
 	/* If we ever support ciphersuites where the PRF hash isn't SHA-256
 	 * then this will need to be updated. */
-	hashes->hashAlg = SEC_OID_SHA256;
+	hashes->hashAlg = ssl_hash_sha256;
 	rv = SECSuccess;
     } else if (ss->opt.bypassPKCS11) {
 	/* compute them without PKCS11 */
 	PRUint64      md5_cx[MAX_MAC_CONTEXT_LLONGS];
 	PRUint64      sha_cx[MAX_MAC_CONTEXT_LLONGS];
 
 #define md5cx ((MD5Context *)md5_cx)
 #define shacx ((SHA1Context *)sha_cx)
@@ -4682,17 +4707,17 @@ ssl3_ComputeHandshakeHashes(sslSocket * 
 			       sizeof(hashes->u.raw));
 	if (rv != SECSuccess) {
 	    ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
 	    rv = SECFailure;
 	    goto tls12_loser;
 	}
 	/* If we ever support ciphersuites where the PRF hash isn't SHA-256
 	 * then this will need to be updated. */
-	hashes->hashAlg = SEC_OID_SHA256;
+	hashes->hashAlg = ssl_hash_sha256;
 	rv = SECSuccess;
 
 tls12_loser:
 	if (stateBuf) {
 	    if (PK11_RestoreContext(h, stateBuf, stateLen) != SECSuccess) {
 		ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
 		rv = SECFailure;
 	    }
@@ -4861,17 +4886,17 @@ ssl3_ComputeBackupHandshakeHashes(sslSoc
 
     rv = PK11_DigestFinal(ss->ssl3.hs.backupHash, hashes->u.raw, &hashes->len,
 			  sizeof(hashes->u.raw));
     if (rv != SECSuccess) {
 	ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
 	rv = SECFailure;
 	goto loser;
     }
-    hashes->hashAlg = SEC_OID_SHA1;
+    hashes->hashAlg = ssl_hash_sha1;
 
 loser:
     PK11_DestroyContext(ss->ssl3.hs.backupHash, PR_TRUE);
     ss->ssl3.hs.backupHash = NULL;
     return rv;
 }
 
 /*
@@ -6189,17 +6214,17 @@ ssl3_SendCertificateVerify(sslSocket *ss
 {
     SECStatus     rv		= SECFailure;
     PRBool        isTLS;
     PRBool        isTLS12;
     SECItem       buf           = {siBuffer, NULL, 0};
     SSL3Hashes    hashes;
     KeyType       keyType;
     unsigned int  len;
-    SSL3SignatureAndHashAlgorithm sigAndHash;
+    SSLSignatureAndHashAlg sigAndHash;
 
     PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     SSL_TRC(3, ("%d: SSL3[%d]: send certificate_verify handshake",
 		SSL_GETPID(), ss->fd));
 
     ssl_GetSpecReadLock(ss);
@@ -6243,21 +6268,21 @@ ssl3_SendCertificateVerify(sslSocket *ss
     len = buf.len + 2 + (isTLS12 ? 2 : 0);
 
     rv = ssl3_AppendHandshakeHeader(ss, certificate_verify, len);
     if (rv != SECSuccess) {
 	goto done;	/* error code set by AppendHandshake */
     }
     if (isTLS12) {
 	rv = ssl3_TLSSignatureAlgorithmForKeyType(keyType,
-						  &sigAndHash.sigAlg);
+                                                  &sigAndHash.sigAlg);
 	if (rv != SECSuccess) {
 	    goto done;
 	}
-	sigAndHash.hashAlg = hashes.hashAlg;
+        sigAndHash.hashAlg = hashes.hashAlg;
 
 	rv = ssl3_AppendSignatureAndHashAlgorithm(ss, &sigAndHash);
 	if (rv != SECSuccess) {
 	    goto done; 	/* err set by AppendHandshake. */
 	}
     }
     rv = ssl3_AppendHandshakeVariable(ss, buf.data, buf.len, 2);
     if (rv != SECSuccess) {
@@ -6655,19 +6680,19 @@ ssl3_HandleServerKeyExchange(sslSocket *
     PLArenaPool *    arena     = NULL;
     SECKEYPublicKey *peerKey   = NULL;
     PRBool           isTLS, isTLS12;
     SECStatus        rv;
     int              errCode   = SSL_ERROR_RX_MALFORMED_SERVER_KEY_EXCH;
     SSL3AlertDescription desc  = illegal_parameter;
     SSL3Hashes       hashes;
     SECItem          signature = {siBuffer, NULL, 0};
-    SSL3SignatureAndHashAlgorithm sigAndHash;
-
-    sigAndHash.hashAlg = SEC_OID_UNKNOWN;
+    SSLSignatureAndHashAlg sigAndHash;
+
+    sigAndHash.hashAlg = ssl_hash_none;
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle server_key_exchange handshake",
 		SSL_GETPID(), ss->fd));
     PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
 
     if (ss->ssl3.hs.ws != wait_server_key) {
         errCode = SSL_ERROR_RX_UNEXPECTED_SERVER_KEY_EXCH;
@@ -6699,17 +6724,17 @@ ssl3_HandleServerKeyExchange(sslSocket *
 	    goto loser;		/* malformed. */
 	}
 	if (isTLS12) {
 	    rv = ssl3_ConsumeSignatureAndHashAlgorithm(ss, &b, &length,
 						       &sigAndHash);
 	    if (rv != SECSuccess) {
 		goto loser;	/* malformed or unsupported. */
 	    }
-	    rv = ssl3_CheckSignatureAndHashAlgorithmConsistency(
+	    rv = ssl3_CheckSignatureAndHashAlgorithmConsistency(ss,
 		    &sigAndHash, ss->sec.peerCert);
 	    if (rv != SECSuccess) {
 		goto loser;
 	    }
 	}
     	rv = ssl3_ConsumeHandshakeVariable(ss, &signature, 2, &b, &length);
     	if (rv != SECSuccess) {
 	    goto loser;		/* malformed. */
@@ -6722,20 +6747,20 @@ ssl3_HandleServerKeyExchange(sslSocket *
 
 	/* failures after this point are not malformed handshakes. */
 	/* TLS: send decrypt_error if signature failed. */
     	desc = isTLS ? decrypt_error : handshake_failure;
 
     	/*
      	 *  check to make sure the hash is signed by right guy
      	 */
-	rv = ssl3_ComputeExportRSAKeyHash(sigAndHash.hashAlg, modulus, exponent,
-					  &ss->ssl3.hs.client_random,
-					  &ss->ssl3.hs.server_random, 
-					  &hashes, ss->opt.bypassPKCS11);
+        rv = ssl3_ComputeExportRSAKeyHash(sigAndHash.hashAlg, modulus, exponent,
+                                          &ss->ssl3.hs.client_random,
+                                          &ss->ssl3.hs.server_random,
+                                          &hashes, ss->opt.bypassPKCS11);
         if (rv != SECSuccess) {
 	    errCode =
 	    	ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
 	    goto alert_loser;
 	}
         rv = ssl3_VerifySignedHashes(&hashes, ss->sec.peerCert, &signature,
 				    isTLS, ss->pkcs11PinArg);
 	if (rv != SECSuccess)  {
@@ -6808,17 +6833,17 @@ ssl3_HandleServerKeyExchange(sslSocket *
         if (dh_Ys_bits > dh_p_bits || dh_Ys_bits <= 1)
 	    goto alert_loser;
 	if (isTLS12) {
 	    rv = ssl3_ConsumeSignatureAndHashAlgorithm(ss, &b, &length,
 						       &sigAndHash);
 	    if (rv != SECSuccess) {
 		goto loser;	/* malformed or unsupported. */
 	    }
-	    rv = ssl3_CheckSignatureAndHashAlgorithmConsistency(
+	    rv = ssl3_CheckSignatureAndHashAlgorithmConsistency(ss,
 		    &sigAndHash, ss->sec.peerCert);
 	    if (rv != SECSuccess) {
 		goto loser;
 	    }
 	}
     	rv = ssl3_ConsumeHandshakeVariable(ss, &signature, 2, &b, &length);
     	if (rv != SECSuccess) {
 	    goto loser;		/* malformed. */
@@ -6835,20 +6860,20 @@ ssl3_HandleServerKeyExchange(sslSocket *
 
 	/* failures after this point are not malformed handshakes. */
 	/* TLS: send decrypt_error if signature failed. */
     	desc = isTLS ? decrypt_error : handshake_failure;
 
     	/*
      	 *  check to make sure the hash is signed by right guy
      	 */
-	rv = ssl3_ComputeDHKeyHash(sigAndHash.hashAlg, dh_p, dh_g, dh_Ys,
-					  &ss->ssl3.hs.client_random,
-					  &ss->ssl3.hs.server_random, 
-					  &hashes, ss->opt.bypassPKCS11);
+        rv = ssl3_ComputeDHKeyHash(sigAndHash.hashAlg, dh_p, dh_g, dh_Ys,
+                                   &ss->ssl3.hs.client_random,
+                                   &ss->ssl3.hs.server_random, 
+                                   &hashes, ss->opt.bypassPKCS11);
         if (rv != SECSuccess) {
 	    errCode =
 	    	ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
 	    goto alert_loser;
 	}
         rv = ssl3_VerifySignedHashes(&hashes, ss->sec.peerCert, &signature,
 				    isTLS, ss->pkcs11PinArg);
 	if (rv != SECSuccess)  {
@@ -6913,17 +6938,17 @@ no_memory:	/* no-memory error has alread
 }
 
 /*
  * Returns the TLS signature algorithm for the client authentication key and
  * whether it is an RSA or DSA key that may be able to sign only SHA-1 hashes.
  */
 static SECStatus
 ssl3_ExtractClientKeyInfo(sslSocket *ss,
-			  TLSSignatureAlgorithm *sigAlg,
+			  SSLSignType *sigAlg,
 			  PRBool *preferSha1)
 {
     SECStatus rv = SECSuccess;
     SECKEYPublicKey *pubk;
 
     pubk = CERT_ExtractPublicKey(ss->ssl3.clientCertificate);
     if (pubk == NULL) {
 	rv = SECFailure;
@@ -6957,17 +6982,17 @@ done:
  * this function selects the hash algorithm for client authentication
  * signatures; ssl3_SendCertificateVerify uses the presence of the backup hash
  * to determine whether to use SHA-1 or SHA-256. */
 static void
 ssl3_DestroyBackupHandshakeHashIfNotNeeded(sslSocket *ss,
 					   const SECItem *algorithms)
 {
     SECStatus rv;
-    TLSSignatureAlgorithm sigAlg;
+    SSLSignType sigAlg;
     PRBool preferSha1;
     PRBool supportsSha1 = PR_FALSE;
     PRBool supportsSha256 = PR_FALSE;
     PRBool needBackupHash = PR_FALSE;
     unsigned int i;
 
 #ifndef NO_PKCS11_BYPASS
     /* Backup handshake hash is not supported in PKCS #11 bypass mode. */
@@ -6982,19 +7007,19 @@ ssl3_DestroyBackupHandshakeHashIfNotNeed
     rv = ssl3_ExtractClientKeyInfo(ss, &sigAlg, &preferSha1);
     if (rv != SECSuccess) {
 	goto done;
     }
 
     /* Determine the server's hash support for that signature algorithm. */
     for (i = 0; i < algorithms->len; i += 2) {
 	if (algorithms->data[i+1] == sigAlg) {
-	    if (algorithms->data[i] == tls_hash_sha1) {
+	    if (algorithms->data[i] == ssl_hash_sha1) {
 		supportsSha1 = PR_TRUE;
-	    } else if (algorithms->data[i] == tls_hash_sha256) {
+	    } else if (algorithms->data[i] == ssl_hash_sha256) {
 		supportsSha256 = PR_TRUE;
 	    }
 	}
     }
 
     /* If either the server does not support SHA-256 or the client key prefers
      * SHA-1, leave the backup hash. */
     if (supportsSha1 && (preferSha1 || !supportsSha256)) {
@@ -8461,18 +8486,19 @@ compression_found:
     }
     ss->sec.ci.sid = sid;
 
     ss->ssl3.hs.isResuming = PR_FALSE;
     ssl_GetXmitBufLock(ss);
     rv = ssl3_SendServerHelloSequence(ss);
     ssl_ReleaseXmitBufLock(ss);
     if (rv != SECSuccess) {
-	errCode = PORT_GetError();
-	goto loser;
+        errCode = PORT_GetError();
+        desc = handshake_failure;
+        goto alert_loser;
     }
 
     if (haveXmitBufLock) {
 	ssl_ReleaseXmitBufLock(ss);
 	haveXmitBufLock = PR_FALSE;
     }
 
     return SECSuccess;
@@ -8812,28 +8838,28 @@ ssl3_SendServerHello(sslSocket *ss)
 	return rv;	/* err set by ssl3_SetupPendingCipherSpec */
     }
 
     return SECSuccess;
 }
 
 static SECStatus
 ssl3_PickSignatureHashAlgorithm(sslSocket *ss,
-				SSL3SignatureAndHashAlgorithm* out);
+				SSLSignatureAndHashAlg* out);
 
 static SECStatus
 ssl3_SendDHServerKeyExchange(sslSocket *ss)
 {
     const ssl3KEADef * kea_def = ss->ssl3.hs.kea_def;
     SECStatus          rv = SECFailure;
     int                length;
     PRBool             isTLS;
     SECItem            signed_hash = {siBuffer, NULL, 0};
     SSL3Hashes         hashes;
-    SSL3SignatureAndHashAlgorithm sigAndHash;
+    SSLSignatureAndHashAlg sigAndHash;
     SECKEYDHParams dhParam;
 
     ssl3KeyPair *keyPair = NULL;
     SECKEYPublicKey *pubKey = NULL; /* Ephemeral DH key */
     SECKEYPrivateKey *privKey = NULL; /* Ephemeral DH key */
     int certIndex = -1;
 
     if (kea_def->kea != kea_dhe_dss && kea_def->kea != kea_dhe_rsa) {
@@ -8965,82 +8991,82 @@ loser:
 
 /* ssl3_PickSignatureHashAlgorithm selects a hash algorithm to use when signing
  * elements of the handshake. (The negotiated cipher suite determines the
  * signature algorithm.) Prior to TLS 1.2, the MD5/SHA1 combination is always
  * used. With TLS 1.2, a client may advertise its support for signature and
  * hash combinations. */
 static SECStatus
 ssl3_PickSignatureHashAlgorithm(sslSocket *ss,
-				SSL3SignatureAndHashAlgorithm* out)
-{
-    TLSSignatureAlgorithm sigAlg;
+                                SSLSignatureAndHashAlg* out)
+{
+    SSLSignType sigAlg;
     unsigned int i, j;
-    /* hashPreference expresses our preferences for hash algorithms, most
-     * preferable first. */
-    static const SECOidTag hashPreference[] = {
-        SEC_OID_SHA256,
-        SEC_OID_SHA384,
-        SEC_OID_SHA512,
-        SEC_OID_SHA1,
-    };
 
     switch (ss->ssl3.hs.kea_def->kea) {
     case kea_rsa:
     case kea_rsa_export:
     case kea_rsa_export_1024:
     case kea_dh_rsa:
     case kea_dh_rsa_export:
     case kea_dhe_rsa:
     case kea_dhe_rsa_export:
     case kea_rsa_fips:
     case kea_ecdh_rsa:
     case kea_ecdhe_rsa:
-	sigAlg = tls_sig_rsa;
-	break;
+        sigAlg = ssl_sign_rsa;
+        break;
     case kea_dh_dss:
     case kea_dh_dss_export:
     case kea_dhe_dss:
     case kea_dhe_dss_export:
-	sigAlg = tls_sig_dsa;
-	break;
+        sigAlg = ssl_sign_dsa;
+        break;
     case kea_ecdh_ecdsa:
     case kea_ecdhe_ecdsa:
-	sigAlg = tls_sig_ecdsa;
-	break;
+        sigAlg = ssl_sign_ecdsa;
+        break;
     default:
-	PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
-	return SECFailure;
+        PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
+        return SECFailure;
     }
     out->sigAlg = sigAlg;
 
     if (ss->version <= SSL_LIBRARY_VERSION_TLS_1_1) {
-	/* SEC_OID_UNKNOWN means the MD5/SHA1 combo hash used in TLS 1.1 and
-	 * prior. */
-	out->hashAlg = SEC_OID_UNKNOWN;
-	return SECSuccess;
+        /* SEC_OID_UNKNOWN means the MD5/SHA1 combo hash used in TLS 1.1 and
+         * prior. */
+        out->hashAlg = ssl_hash_none;
+        return SECSuccess;
     }
 
     if (ss->ssl3.hs.numClientSigAndHash == 0) {
-	/* If the client didn't provide any signature_algorithms extension then
-	 * we can assume that they support SHA-1:
-	 * https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
-	out->hashAlg = SEC_OID_SHA1;
-	return SECSuccess;
-    }
-
-    for (i = 0; i < PR_ARRAY_SIZE(hashPreference); i++) {
-	for (j = 0; j < ss->ssl3.hs.numClientSigAndHash; j++) {
-	    const SSL3SignatureAndHashAlgorithm* sh =
-		&ss->ssl3.hs.clientSigAndHash[j];
-	    if (sh->sigAlg == sigAlg && sh->hashAlg == hashPreference[i]) {
-		out->hashAlg = sh->hashAlg;
-		return SECSuccess;
-	    }
-	}
+        /* If the client didn't provide any signature_algorithms extension then
+         * we can assume that they support SHA-1:
+         * https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
+        out->hashAlg = ssl_hash_sha1;
+        return SECSuccess;
+    }
+
+    /* Here we look for the first server preference that the client has
+     * indicated support for in their signature_algorithms extension. */
+    for (i = 0; i < ss->ssl3.signatureAlgorithmCount; ++i) {
+        const SSLSignatureAndHashAlg *serverPref =
+                &ss->ssl3.signatureAlgorithms[i];
+        if (serverPref->sigAlg != sigAlg) {
+            continue;
+        }
+        for (j = 0; j < ss->ssl3.hs.numClientSigAndHash; j++) {
+            const SSLSignatureAndHashAlg *clientPref =
+                &ss->ssl3.hs.clientSigAndHash[j];
+            if (clientPref->hashAlg == serverPref->hashAlg &&
+                clientPref->sigAlg == sigAlg) {
+                out->hashAlg = serverPref->hashAlg;
+                return SECSuccess;
+            }
+        }
     }
 
     PORT_SetError(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM);
     return SECFailure;
 }
 
 
 static SECStatus
@@ -9048,17 +9074,17 @@ ssl3_SendServerKeyExchange(sslSocket *ss
 {
     const ssl3KEADef * kea_def     = ss->ssl3.hs.kea_def;
     SECStatus          rv          = SECFailure;
     int                length;
     PRBool             isTLS;
     SECItem            signed_hash = {siBuffer, NULL, 0};
     SSL3Hashes         hashes;
     SECKEYPublicKey *  sdPub;	/* public key for step-down */
-    SSL3SignatureAndHashAlgorithm sigAndHash;
+    SSLSignatureAndHashAlg sigAndHash;
 
     SSL_TRC(3, ("%d: SSL3[%d]: send server_key_exchange handshake",
 		SSL_GETPID(), ss->fd));
 
     PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     if (ssl3_PickSignatureHashAlgorithm(ss, &sigAndHash) != SECSuccess) {
@@ -9155,33 +9181,63 @@ ssl3_SendServerKeyExchange(sslSocket *ss
 	break;
     }
 loser:
     if (signed_hash.data != NULL) 
     	PORT_Free(signed_hash.data);
     return SECFailure;
 }
 
+static SECStatus
+ssl3_EncodeCertificateRequestSigAlgs(sslSocket *ss, PRUint8 *buf,
+                                     unsigned maxLen, PRUint32 *len)
+{
+    unsigned int i;
+
+    PORT_Assert(maxLen >= ss->ssl3.signatureAlgorithmCount * 2);
+    if (maxLen < ss->ssl3.signatureAlgorithmCount * 2) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+
+    *len = 0;
+    for (i = 0; i < ss->ssl3.signatureAlgorithmCount; ++i) {
+        const SSLSignatureAndHashAlg *alg = &ss->ssl3.signatureAlgorithms[i];
+        /* Note that we don't support a handshake hash with anything other than
+         * SHA-256, so asking for a signature from clients for something else
+         * would be inviting disaster. */
+        if (alg->hashAlg == ssl_hash_sha256) {
+            buf[(*len)++] = (PRUint8)alg->hashAlg;
+            buf[(*len)++] = (PRUint8)alg->sigAlg;
+        }
+    }
+
+    if (*len == 0) {
+        PORT_SetError(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
+        return SECFailure;
+    }
+    return SECSuccess;
+}
 
 static SECStatus
 ssl3_SendCertificateRequest(sslSocket *ss)
 {
     PRBool         isTLS12;
     SECItem *      name;
     CERTDistNames *ca_list;
     const PRUint8 *certTypes;
-    const PRUint8 *sigAlgs;
     SECItem *      names	= NULL;
     SECStatus      rv;
     int            length;
     int            i;
     int            calen	= 0;
     int            nnames	= 0;
     int            certTypesLength;
-    int            sigAlgsLength;
+    PRUint8        sigAlgs[MAX_SIGNATURE_ALGORITHMS * 2];
+    unsigned int   sigAlgsLength;
 
     SSL_TRC(3, ("%d: SSL3[%d]: send certificate_request handshake",
 		SSL_GETPID(), ss->fd));
 
     PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     isTLS12 = (PRBool)(ss->ssl3.pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2);
@@ -9198,22 +9254,25 @@ ssl3_SendCertificateRequest(sslSocket *s
     }
 
     for (i = 0, name = names; i < nnames; i++, name++) {
 	calen += 2 + name->len;
     }
 
     certTypes       = certificate_types;
     certTypesLength = sizeof certificate_types;
-    sigAlgs         = supported_signature_algorithms;
-    sigAlgsLength   = sizeof supported_signature_algorithms;
 
     length = 1 + certTypesLength + 2 + calen;
     if (isTLS12) {
-	length += 2 + sigAlgsLength;
+        rv = ssl3_EncodeCertificateRequestSigAlgs(ss, sigAlgs, sizeof(sigAlgs),
+                                                  &sigAlgsLength);
+        if (rv != SECSuccess) {
+            return rv;
+        }
+        length += 2 + sigAlgsLength;
     }
 
     rv = ssl3_AppendHandshakeHeader(ss, certificate_request, length);
     if (rv != SECSuccess) {
 	return rv; 		/* err set by AppendHandshake. */
     }
     rv = ssl3_AppendHandshakeVariable(ss, certTypes, certTypesLength, 1);
     if (rv != SECSuccess) {
@@ -9269,17 +9328,17 @@ static SECStatus
 ssl3_HandleCertificateVerify(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
 			     SSL3Hashes *hashes)
 {
     SECItem              signed_hash = {siBuffer, NULL, 0};
     SECStatus            rv;
     int                  errCode     = SSL_ERROR_RX_MALFORMED_CERT_VERIFY;
     SSL3AlertDescription desc        = handshake_failure;
     PRBool               isTLS, isTLS12;
-    SSL3SignatureAndHashAlgorithm sigAndHash;
+    SSLSignatureAndHashAlg sigAndHash;
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle certificate_verify handshake",
 		SSL_GETPID(), ss->fd));
     PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
 
     isTLS = (PRBool)(ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0);
     isTLS12 = (PRBool)(ss->ssl3.prSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2);
@@ -9292,26 +9351,26 @@ ssl3_HandleCertificateVerify(sslSocket *
 
     if (isTLS12) {
 	rv = ssl3_ConsumeSignatureAndHashAlgorithm(ss, &b, &length,
 						   &sigAndHash);
 	if (rv != SECSuccess) {
 	    goto loser;	/* malformed or unsupported. */
 	}
 	rv = ssl3_CheckSignatureAndHashAlgorithmConsistency(
-		&sigAndHash, ss->sec.peerCert);
+            ss, &sigAndHash, ss->sec.peerCert);
 	if (rv != SECSuccess) {
 	    errCode = PORT_GetError();
 	    desc = decrypt_error;
 	    goto alert_loser;
 	}
 
 	/* We only support CertificateVerify messages that use the handshake
 	 * hash. */
-	if (sigAndHash.hashAlg != hashes->hashAlg) {
+        if (sigAndHash.hashAlg != hashes->hashAlg) {
 	    errCode = SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM;
 	    desc = decrypt_error;
 	    goto alert_loser;
 	}
     }
 
     rv = ssl3_ConsumeHandshakeVariable(ss, &signed_hash, 2, &b, &length);
     if (rv != SECSuccess) {
@@ -12327,21 +12386,97 @@ ssl3_CipherPrefGet(sslSocket *ss, ssl3Ci
     } else {
     	pref   = SSL_NOT_ALLOWED;
 	rv     = SECFailure;	/* err code was set by Lookup. */
     }
     *enabled = pref;
     return rv;
 }
 
+SECStatus
+SSL_SignaturePrefSet(PRFileDesc *fd, const SSLSignatureAndHashAlg *algorithms,
+                     unsigned int count)
+{
+    sslSocket *ss;
+    unsigned int i;
+
+    ss = ssl_FindSocket(fd);
+    if (!ss) {
+        SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SignaturePrefSet",
+                 SSL_GETPID(), fd));
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+
+    if (!count || count > MAX_SIGNATURE_ALGORITHMS) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+
+    ss->ssl3.signatureAlgorithmCount = 0;
+    for (i = 0; i < count; ++i) {
+        if (!ssl3_IsSupportedSignatureAlgorithm(&algorithms[i])) {
+            SSL_DBG(("%d: SSL[%d]: invalid signature algorithm set %d/%d",
+                     SSL_GETPID(), fd, algorithms[i].sigAlg,
+                     algorithms[i].hashAlg));
+            continue;
+        }
+
+        ss->ssl3.signatureAlgorithms[ss->ssl3.signatureAlgorithmCount++] =
+                algorithms[i];
+    }
+
+    if (ss->ssl3.signatureAlgorithmCount == 0) {
+        PORT_SetError(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
+        return SECFailure;
+    }
+    return SECSuccess;
+}
+
+SECStatus
+SSL_SignaturePrefGet(PRFileDesc *fd, SSLSignatureAndHashAlg *algorithms,
+                     unsigned int *count, unsigned int maxCount)
+{
+    sslSocket *ss;
+    unsigned int requiredSpace;
+
+    ss = ssl_FindSocket(fd);
+    if (!ss) {
+        SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SignaturePrefGet",
+                 SSL_GETPID(), fd));
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+
+    if (!algorithms || !count ||
+        maxCount < ss->ssl3.signatureAlgorithmCount) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+
+    requiredSpace =
+            ss->ssl3.signatureAlgorithmCount * sizeof(SSLSignatureAndHashAlg);
+    PORT_Memcpy(algorithms, ss->ssl3.signatureAlgorithms, requiredSpace);
+    *count = ss->ssl3.signatureAlgorithmCount;
+    return SECSuccess;
+}
+
+unsigned int
+SSL_SignatureMaxCount() {
+    return MAX_SIGNATURE_ALGORITHMS;
+}
+
 /* copy global default policy into socket. */
 void
 ssl3_InitSocketPolicy(sslSocket *ss)
 {
     PORT_Memcpy(ss->cipherSuites, cipherSuites, sizeof cipherSuites);
+    PORT_Memcpy(ss->ssl3.signatureAlgorithms, defaultSignatureAlgorithms,
+                sizeof(defaultSignatureAlgorithms));
+    ss->ssl3.signatureAlgorithmCount = PR_ARRAY_SIZE(defaultSignatureAlgorithms);
 }
 
 /* ssl3_config_match_init must have already been called by
  * the caller of this function.
  */
 SECStatus
 ssl3_ConstructV2CipherSpecsHack(sslSocket *ss, unsigned char *cs, int *size)
 {
--- a/lib/ssl/ssl3ecc.c
+++ b/lib/ssl/ssl3ecc.c
@@ -203,17 +203,17 @@ params2ecName(SECKEYECParams * params)
             return i;
     }
 
     return ec_noName;
 }
 
 /* Caller must set hiLevel error code. */
 static SECStatus
-ssl3_ComputeECDHKeyHash(SECOidTag hashAlg,
+ssl3_ComputeECDHKeyHash(SSLHashType hashAlg,
                         SECItem ec_params, SECItem server_ecpoint,
                         SSL3Random *client_rand, SSL3Random *server_rand,
                         SSL3Hashes *hashes, PRBool bypassPKCS11)
 {
     PRUint8     * hashBuf;
     PRUint8     * pBuf;
     SECStatus     rv            = SECSuccess;
     unsigned int  bufLen;
@@ -604,19 +604,19 @@ ssl3_HandleECDHServerKeyExchange(sslSock
     int              errCode   = SSL_ERROR_RX_MALFORMED_SERVER_KEY_EXCH;
     SSL3AlertDescription desc  = illegal_parameter;
     SSL3Hashes       hashes;
     SECItem          signature = {siBuffer, NULL, 0};
 
     SECItem          ec_params = {siBuffer, NULL, 0};
     SECItem          ec_point  = {siBuffer, NULL, 0};
     unsigned char    paramBuf[3]; /* only for curve_type == named_curve */
-    SSL3SignatureAndHashAlgorithm sigAndHash;
+    SSLSignatureAndHashAlg sigAndHash;
 
-    sigAndHash.hashAlg = SEC_OID_UNKNOWN;
+    sigAndHash.hashAlg = ssl_hash_none;
 
     isTLS = (PRBool)(ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0);
     isTLS12 = (PRBool)(ss->ssl3.prSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2);
 
     /* XXX This works only for named curves, revisit this when
      * we support generic curves.
      */
     ec_params.len  = sizeof paramBuf;
@@ -648,17 +648,17 @@ ssl3_HandleECDHServerKeyExchange(sslSock
 
     if (isTLS12) {
         rv = ssl3_ConsumeSignatureAndHashAlgorithm(ss, &b, &length,
                                                    &sigAndHash);
         if (rv != SECSuccess) {
             goto loser;         /* malformed or unsupported. */
         }
         rv = ssl3_CheckSignatureAndHashAlgorithmConsistency(
-                &sigAndHash, ss->sec.peerCert);
+            ss, &sigAndHash, ss->sec.peerCert);
         if (rv != SECSuccess) {
             goto loser;
         }
     }
 
     rv = ssl3_ConsumeHandshakeVariable(ss, &signature, 2, &b, &length);
     if (rv != SECSuccess) {
         goto loser;             /* malformed. */
@@ -745,17 +745,17 @@ loser:
 no_memory:      /* no-memory error has already been set. */
     ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
     return SECFailure;
 }
 
 SECStatus
 ssl3_SendECDHServerKeyExchange(
     sslSocket *ss,
-    const SSL3SignatureAndHashAlgorithm *sigAndHash)
+    const SSLSignatureAndHashAlg *sigAndHash)
 {
     const ssl3KEADef * kea_def     = ss->ssl3.hs.kea_def;
     SECStatus          rv          = SECFailure;
     int                length;
     PRBool             isTLS, isTLS12;
     SECItem            signed_hash = {siBuffer, NULL, 0};
     SSL3Hashes         hashes;
 
--- a/lib/ssl/ssl3ext.c
+++ b/lib/ssl/ssl3ext.c
@@ -2311,17 +2311,17 @@ ssl3_ServerHandleUseSRTPXtn(sslSocket * 
  * from a client.
  * See https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
 static SECStatus
 ssl3_ServerHandleSigAlgsXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data)
 {
     SECStatus rv;
     SECItem algorithms;
     const unsigned char *b;
-    unsigned int numAlgorithms, i, j;
+    unsigned int numAlgorithms, i;
 
     /* Ignore this extension if we aren't doing TLS 1.2 or greater. */
     if (ss->version < SSL_LIBRARY_VERSION_TLS_1_2) {
         return SECSuccess;
     }
 
     rv = ssl3_ConsumeHandshakeVariable(ss, &algorithms, 2, &data->data,
                                        &data->len);
@@ -2338,40 +2338,34 @@ ssl3_ServerHandleSigAlgsXtn(sslSocket * 
     numAlgorithms = algorithms.len/2;
 
     /* We don't care to process excessive numbers of algorithms. */
     if (numAlgorithms > 512) {
         numAlgorithms = 512;
     }
 
     ss->ssl3.hs.clientSigAndHash =
-            PORT_NewArray(SSL3SignatureAndHashAlgorithm, numAlgorithms);
+            PORT_NewArray(SSLSignatureAndHashAlg, numAlgorithms);
     if (!ss->ssl3.hs.clientSigAndHash) {
         (void)SSL3_SendAlert(ss, alert_fatal, internal_error);
         PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
         return SECFailure;
     }
     ss->ssl3.hs.numClientSigAndHash = 0;
 
     b = algorithms.data;
-    for (i = j = 0; i < numAlgorithms; i++) {
-        unsigned char tls_hash = *(b++);
-        unsigned char tls_sig = *(b++);
-        SECOidTag hash = ssl3_TLSHashAlgorithmToOID(tls_hash);
-
-        if (hash == SEC_OID_UNKNOWN) {
-            /* We ignore formats that we don't understand. */
-            continue;
+    ss->ssl3.hs.numClientSigAndHash = 0;
+    for (i = 0; i < numAlgorithms; i++) {
+        SSLSignatureAndHashAlg *sigAndHash =
+            &ss->ssl3.hs.clientSigAndHash[ss->ssl3.hs.numClientSigAndHash];
+        sigAndHash->hashAlg = (SSLHashType)*(b++);
+        sigAndHash->sigAlg = (SSLSignType)*(b++);
+        if (ssl3_IsSupportedSignatureAlgorithm(sigAndHash)) {
+            ++ss->ssl3.hs.numClientSigAndHash;
         }
-        /* tls_sig support will be checked later in
-         * ssl3_PickSignatureHashAlgorithm. */
-        ss->ssl3.hs.clientSigAndHash[j].hashAlg = hash;
-        ss->ssl3.hs.clientSigAndHash[j].sigAlg = tls_sig;
-        ++j;
-        ++ss->ssl3.hs.numClientSigAndHash;
     }
 
     if (!ss->ssl3.hs.numClientSigAndHash) {
         /* We didn't understand any of the client's requested signature
          * formats. We'll use the defaults. */
         PORT_Free(ss->ssl3.hs.clientSigAndHash);
         ss->ssl3.hs.clientSigAndHash = NULL;
     }
@@ -2379,71 +2373,62 @@ ssl3_ServerHandleSigAlgsXtn(sslSocket * 
     /* Keep track of negotiated extensions. */
     ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
     return SECSuccess;
 }
 
 /* ssl3_ClientSendSigAlgsXtn sends the signature_algorithm extension for TLS
  * 1.2 ClientHellos. */
 static PRInt32
-ssl3_ClientSendSigAlgsXtn(sslSocket * ss, PRBool append, PRUint32 maxBytes)
+ssl3_ClientSendSigAlgsXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes)
 {
-    static const unsigned char signatureAlgorithms[] = {
-        /* This block is the contents of our signature_algorithms extension, in
-         * wire format. See
-         * https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
-        tls_hash_sha256, tls_sig_rsa,
-        tls_hash_sha384, tls_sig_rsa,
-        tls_hash_sha512, tls_sig_rsa,
-        tls_hash_sha1,   tls_sig_rsa,
-#ifndef NSS_DISABLE_ECC
-        tls_hash_sha256, tls_sig_ecdsa,
-        tls_hash_sha384, tls_sig_ecdsa,
-        tls_hash_sha512, tls_sig_ecdsa,
-        tls_hash_sha1,   tls_sig_ecdsa,
-#endif
-        tls_hash_sha256, tls_sig_dsa,
-        tls_hash_sha1,   tls_sig_dsa,
-    };
     PRInt32 extension_length;
+    unsigned int i;
+    PRUint8 buf[MAX_SIGNATURE_ALGORITHMS * 2];
 
     if (ss->version < SSL_LIBRARY_VERSION_TLS_1_2) {
         return 0;
     }
 
     extension_length =
         2 /* extension type */ +
         2 /* extension length */ +
         2 /* supported_signature_algorithms length */ +
-        sizeof(signatureAlgorithms);
+        ss->ssl3.signatureAlgorithmCount * 2;
 
-    if (maxBytes < (PRUint32)extension_length) {
+    if (maxBytes < extension_length) {
         PORT_Assert(0);
         return 0;
     }
+
     if (append) {
         SECStatus rv;
         rv = ssl3_AppendHandshakeNumber(ss, ssl_signature_algorithms_xtn, 2);
-        if (rv != SECSuccess)
-            goto loser;
+        if (rv != SECSuccess) {
+            return -1;
+        }
         rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2);
-        if (rv != SECSuccess)
-            goto loser;
-        rv = ssl3_AppendHandshakeVariable(ss, signatureAlgorithms,
-                                          sizeof(signatureAlgorithms), 2);
-        if (rv != SECSuccess)
-            goto loser;
+        if (rv != SECSuccess) {
+            return -1;
+        }
+
+        for (i = 0; i < ss->ssl3.signatureAlgorithmCount; ++i) {
+            buf[i * 2] = ss->ssl3.signatureAlgorithms[i].hashAlg;
+            buf[i * 2 + 1] = ss->ssl3.signatureAlgorithms[i].sigAlg;
+        }
+        rv = ssl3_AppendHandshakeVariable(ss, buf, extension_length - 6, 2);
+        if (rv != SECSuccess) {
+            return -1;
+        }
+
         ss->xtnData.advertised[ss->xtnData.numAdvertised++] =
                 ssl_signature_algorithms_xtn;
     }
 
     return extension_length;
-
-loser:
-    return -1;
 }
 
 unsigned int
 ssl3_CalculatePaddingExtensionLength(unsigned int clientHelloLength)
 {
     unsigned int recordLength = 1 /* handshake message type */ +
                                 3 /* handshake message length */ +
                                 clientHelloLength;
--- a/lib/ssl/ssl3prot.h
+++ b/lib/ssl/ssl3prot.h
@@ -212,55 +212,29 @@ typedef struct {
 
 typedef struct {
     union {
         SSL3ServerDHParams dh;
         SSL3ServerRSAParams rsa;
     } u;
 } SSL3ServerParams;
 
-/* This enum reflects HashAlgorithm enum from
- * https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
- *
- * When updating, be sure to also update ssl3_TLSHashAlgorithmToOID. */
-enum {
-    tls_hash_md5 = 1,
-    tls_hash_sha1 = 2,
-    tls_hash_sha224 = 3,
-    tls_hash_sha256 = 4,
-    tls_hash_sha384 = 5,
-    tls_hash_sha512 = 6
-};
-
-/* This enum reflects SignatureAlgorithm enum from
- * https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
-typedef enum {
-    tls_sig_rsa = 1,
-    tls_sig_dsa = 2,
-    tls_sig_ecdsa = 3
-} TLSSignatureAlgorithm;
-
-typedef struct {
-    SECOidTag hashAlg;
-    TLSSignatureAlgorithm sigAlg;
-} SSL3SignatureAndHashAlgorithm;
-
 /* SSL3HashesIndividually contains a combination MD5/SHA1 hash, as used in TLS
  * prior to 1.2. */
 typedef struct {
     PRUint8 md5[16];
     PRUint8 sha[20];
 } SSL3HashesIndividually;
 
 /* SSL3Hashes contains an SSL hash value. The digest is contained in |u.raw|
- * which, if |hashAlg==SEC_OID_UNKNOWN| is also a SSL3HashesIndividually
+ * which, if |hashAlg==ssl_hash_none| is also a SSL3HashesIndividually
  * struct. */
 typedef struct {
     unsigned int len;
-    SECOidTag hashAlg;
+    SSLHashType hashAlg;
     union {
         PRUint8 raw[64];
         SSL3HashesIndividually s;
     } u;
 } SSL3Hashes;
 
 typedef struct {
     union {
--- a/lib/ssl/sslerr.h
+++ b/lib/ssl/sslerr.h
@@ -197,13 +197,16 @@ SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK     
 SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL     = (SSL_ERROR_BASE + 130),
 
 SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT  = (SSL_ERROR_BASE + 131),
 
 SSL_ERROR_WEAK_SERVER_CERT_KEY          = (SSL_ERROR_BASE + 132),
 
 SSL_ERROR_RX_SHORT_DTLS_READ            = (SSL_ERROR_BASE + 133),
 
+SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM = (SSL_ERROR_BASE + 134),
+SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM = (SSL_ERROR_BASE + 135),
+
 SSL_ERROR_END_OF_LIST   /* let the c compiler determine the value of this. */
 } SSLErrorCodes;
 #endif /* NO_SECURITY_ERROR_ENUM */
 
 #endif /* __SSL_ERR_H_ */
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -1,8 +1,9 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /*
  * This file is PRIVATE to SSL and should be the first thing included by
  * any SSL implementation file.
  *
  * 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/. */
 
@@ -301,16 +302,22 @@ typedef struct {
 #ifndef NSS_DISABLE_ECC
 #define ssl_V3_SUITES_IMPLEMENTED 64
 #else
 #define ssl_V3_SUITES_IMPLEMENTED 40
 #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
+ * doesn't allow space for combinations with MD5). */
+#define MAX_SIGNATURE_ALGORITHMS 15
+
+
 typedef struct sslOptionsStr {
     /* If SSL_SetNextProtoNego has been called, then this contains the
      * list of supported protocols. */
     SECItem nextProtoNego;
 
     unsigned int useSecurity		: 1;  /*  1 */
     unsigned int useSocks		: 1;  /*  2 */
     unsigned int requestCertificate	: 1;  /*  3 */
@@ -918,18 +925,18 @@ const ssl3CipherSuiteDef *suite_def;
 
     PRBool                canFalseStart;   /* Can/did we False Start */
     /* Which preliminaryinfo values have been set. */
     PRUint32              preliminaryInfo;
 
     /* clientSigAndHash contains the contents of the signature_algorithms
      * extension (if any) from the client. This is only valid for TLS 1.2
      * or later. */
-    SSL3SignatureAndHashAlgorithm *clientSigAndHash;
-    unsigned int          numClientSigAndHash;
+    SSLSignatureAndHashAlg *clientSigAndHash;
+    unsigned int numClientSigAndHash;
 
     /* This group of values is used for DTLS */
     PRUint16              sendMessageSeq;  /* The sending message sequence
 					    * number */
     PRCList               lastMessageFlight; /* The last message flight we
 					      * sent */
     PRUint16              maxMessageSent;    /* The largest message we sent */
     PRUint16              recvMessageSeq;  /* The receiving message sequence
@@ -999,16 +1006,21 @@ struct ssl3StateStr {
     /* DTLS-SRTP cipher suite preferences (if any) */
     PRUint16             dtlsSRTPCiphers[MAX_DTLS_SRTP_CIPHER_SUITES];
     PRUint16             dtlsSRTPCipherCount;
     PRUint16             dtlsSRTPCipherSuite;	/* 0 if not selected */
     PRBool               fatalAlertSent;
     PRUint16             numDHEGroups;        /* used by server */
     SSLDHEGroupType *    dheGroups;           /* used by server */
     PRBool               dheWeakGroupEnabled; /* used by server */
+
+    /* TLS 1.2 introduces separate signature algorithm negotiation.
+     * This is our preference order. */
+    SSLSignatureAndHashAlg signatureAlgorithms[MAX_SIGNATURE_ALGORITHMS];
+    unsigned int signatureAlgorithmCount;
 };
 
 #define DTLS_MAX_MTU  1500U     /* Ethernet MTU but without subtracting the
 				 * headers, so slightly larger than expected */
 #define IS_DTLS(ss) (ss->protocolVariant == ssl_variant_datagram)
 
 typedef struct {
     SSL3ContentType      type;
@@ -1728,49 +1740,50 @@ extern SECStatus ssl_GetPeerInfo(sslSock
 extern SECStatus ssl3_SendECDHClientKeyExchange(sslSocket * ss, 
 			     SECKEYPublicKey * svrPubKey);
 extern SECStatus ssl3_HandleECDHServerKeyExchange(sslSocket *ss, 
 					SSL3Opaque *b, PRUint32 length);
 extern SECStatus ssl3_HandleECDHClientKeyExchange(sslSocket *ss, 
 				     SSL3Opaque *b, PRUint32 length,
                                      SECKEYPublicKey *srvrPubKey,
                                      SECKEYPrivateKey *srvrPrivKey);
-extern SECStatus ssl3_SendECDHServerKeyExchange(sslSocket *ss,
-			const SSL3SignatureAndHashAlgorithm *sigAndHash);
+extern SECStatus ssl3_SendECDHServerKeyExchange(
+    sslSocket *ss, const SSLSignatureAndHashAlg *sigAndHash);
 #endif
 
-extern SECStatus ssl3_ComputeCommonKeyHash(SECOidTag hashAlg,
+extern SECStatus ssl3_ComputeCommonKeyHash(SSLHashType hashAlg,
 				PRUint8 * hashBuf,
 				unsigned int bufLen, SSL3Hashes *hashes, 
 				PRBool bypassPKCS11);
 extern void ssl3_DestroyCipherSpec(ssl3CipherSpec *spec, PRBool freeSrvName);
 extern SECStatus ssl3_InitPendingCipherSpec(sslSocket *ss, PK11SymKey *pms);
 extern SECStatus ssl3_AppendHandshake(sslSocket *ss, const void *void_src, 
 			PRInt32 bytes);
 extern SECStatus ssl3_AppendHandshakeHeader(sslSocket *ss, 
 			SSL3HandshakeType t, PRUint32 length);
 extern SECStatus ssl3_AppendHandshakeNumber(sslSocket *ss, PRInt32 num, 
 			PRInt32 lenSize);
 extern SECStatus ssl3_AppendHandshakeVariable( sslSocket *ss, 
 			const SSL3Opaque *src, PRInt32 bytes, PRInt32 lenSize);
-extern SECStatus ssl3_AppendSignatureAndHashAlgorithm(sslSocket *ss,
-			const SSL3SignatureAndHashAlgorithm* sigAndHash);
+extern SECStatus ssl3_AppendSignatureAndHashAlgorithm(
+    sslSocket *ss, const SSLSignatureAndHashAlg* sigAndHash);
 extern SECStatus ssl3_ConsumeHandshake(sslSocket *ss, void *v, PRInt32 bytes, 
 			SSL3Opaque **b, PRUint32 *length);
 extern PRInt32   ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRInt32 bytes, 
 			SSL3Opaque **b, PRUint32 *length);
 extern SECStatus ssl3_ConsumeHandshakeVariable(sslSocket *ss, SECItem *i, 
 			PRInt32 bytes, SSL3Opaque **b, PRUint32 *length);
-extern SECOidTag ssl3_TLSHashAlgorithmToOID(int hashFunc);
+extern PRBool ssl3_IsSupportedSignatureAlgorithm(
+    const SSLSignatureAndHashAlg *alg);
 extern SECStatus ssl3_CheckSignatureAndHashAlgorithmConsistency(
-			const SSL3SignatureAndHashAlgorithm *sigAndHash,
-			CERTCertificate* cert);
-extern SECStatus ssl3_ConsumeSignatureAndHashAlgorithm(sslSocket *ss,
-			SSL3Opaque **b, PRUint32 *length,
-			SSL3SignatureAndHashAlgorithm *out);
+    sslSocket *ss, const SSLSignatureAndHashAlg *sigAndHash,
+    CERTCertificate* cert);
+extern SECStatus ssl3_ConsumeSignatureAndHashAlgorithm(
+    sslSocket *ss, SSL3Opaque **b, PRUint32 *length,
+    SSLSignatureAndHashAlg *out);
 extern SECStatus ssl3_SignHashes(SSL3Hashes *hash, SECKEYPrivateKey *key, 
 			SECItem *buf, PRBool isTLS);
 extern SECStatus ssl3_VerifySignedHashes(SSL3Hashes *hash, 
 			CERTCertificate *cert, SECItem *buf, PRBool isTLS, 
 			void *pwArg);
 extern SECStatus ssl3_CacheWrappedMasterSecret(sslSocket *ss,
 			sslSessionID *sid, ssl3CipherSpec *spec,
 			SSL3KEAType effectiveExchKeyType);
--- a/lib/ssl/sslsock.c
+++ b/lib/ssl/sslsock.c
@@ -221,16 +221,20 @@ ssl_DupSocket(sslSocket *os)
         /* copy ssl2&3 policy & prefs, even if it's not selected (yet) */
         ss->allowedByPolicy     = os->allowedByPolicy;
         ss->maybeAllowedByPolicy= os->maybeAllowedByPolicy;
         ss->chosenPreference    = os->chosenPreference;
         PORT_Memcpy(ss->cipherSuites, os->cipherSuites, sizeof os->cipherSuites);
         PORT_Memcpy(ss->ssl3.dtlsSRTPCiphers, os->ssl3.dtlsSRTPCiphers,
                     sizeof(PRUint16) * os->ssl3.dtlsSRTPCipherCount);
         ss->ssl3.dtlsSRTPCipherCount = os->ssl3.dtlsSRTPCipherCount;
+        PORT_Memcpy(ss->ssl3.signatureAlgorithms, os->ssl3.signatureAlgorithms,
+                    sizeof(ss->ssl3.signatureAlgorithms[0]) *
+                    os->ssl3.signatureAlgorithmCount);
+        ss->ssl3.signatureAlgorithmCount = os->ssl3.signatureAlgorithmCount;
 
         ss->ssl3.dheWeakGroupEnabled = os->ssl3.dheWeakGroupEnabled;
         ss->ssl3.numDHEGroups = os->ssl3.numDHEGroups;
         if (os->ssl3.dheGroups) {
             ss->ssl3.dheGroups = PORT_NewArray(SSLDHEGroupType,
                                                os->ssl3.numDHEGroups);
             if (!ss->ssl3.dheGroups) {
                 goto loser;
@@ -1864,16 +1868,20 @@ SSL_ReconfigFD(PRFileDesc *model, PRFile
     }
 
     ss->opt  = sm->opt;
     ss->vrange = sm->vrange;
     PORT_Memcpy(ss->cipherSuites, sm->cipherSuites, sizeof sm->cipherSuites);
     PORT_Memcpy(ss->ssl3.dtlsSRTPCiphers, sm->ssl3.dtlsSRTPCiphers,
                 sizeof(PRUint16) * sm->ssl3.dtlsSRTPCipherCount);
     ss->ssl3.dtlsSRTPCipherCount = sm->ssl3.dtlsSRTPCipherCount;
+    PORT_Memcpy(ss->ssl3.signatureAlgorithms, sm->ssl3.signatureAlgorithms,
+                sizeof(ss->ssl3.signatureAlgorithms[0]) *
+                sm->ssl3.signatureAlgorithmCount);
+    ss->ssl3.signatureAlgorithmCount = sm->ssl3.signatureAlgorithmCount;
 
     if (!ss->opt.useSecurity) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return NULL;
     }
     /* This int should be SSLKEAType, but CC on Irix complains,
      * during the for loop.
      */
--- a/lib/ssl/sslt.h
+++ b/lib/ssl/sslt.h
@@ -1,8 +1,9 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /*
  * This file contains prototypes for the public SSL functions.
  *
  * 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/. */
 
 #ifndef __sslt_h_
@@ -50,23 +51,45 @@ typedef enum {
 */
 #define kt_null   	ssl_kea_null
 #define kt_rsa   	ssl_kea_rsa
 #define kt_dh   	ssl_kea_dh
 #define kt_fortezza	ssl_kea_fortezza       /* deprecated, now unused */
 #define kt_ecdh   	ssl_kea_ecdh
 #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, 
+    ssl_sign_null   = 0, /* "anonymous" in TLS */
     ssl_sign_rsa    = 1,
     ssl_sign_dsa    = 2,
     ssl_sign_ecdsa  = 3
 } 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,
+    ssl_hash_md5 = 1,
+    ssl_hash_sha1 = 2,
+    ssl_hash_sha224 = 3,
+    ssl_hash_sha256 = 4,
+    ssl_hash_sha384 = 5,
+    ssl_hash_sha512 = 6
+} SSLHashType;
+
+typedef struct SSLSignatureAndHashAlgStr {
+    SSLHashType hashAlg;
+    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
 } SSLAuthType;
 
--- a/tests/cert/cert.sh
+++ b/tests/cert/cert.sh
@@ -1208,35 +1208,47 @@ 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 certs for ssl_gtests"
+  echo "$SCRIPTNAME: Creating 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} -g 2048 -d ${SSLGTESTDIR} -n client -s "CN=client" \
+        -t C,C,C -x -m 1 -w -2 -v 120 -Z SHA256 -1 -2 <<CERTSCRIPT
+0
+9
+n
+n
+
+n
+CERTSCRIPT
+  if [ "$RET" -ne 0 ]; then
+     echo "return value is $RET"
+     Exit 6 "Fatal - failed to create RSA client 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