Bug 1334127 - land NSS 0a7ba014dbb3, r=me
authorFranziskus Kiefer <franziskuskiefer@gmail.com>
Fri, 03 Feb 2017 06:00:56 +0100
changeset 332328 ef2f2b1d477388a54be99288cf0fb3e0490f44a0
parent 332327 2a51c94218f262a01e95ff10323598c206626947
child 332346 a53486ff86c0a036b4deea08d07bbd3fa94d5e28
push id86508
push userfranziskuskiefer@gmail.com
push dateFri, 03 Feb 2017 05:20:15 +0000
treeherdermozilla-inbound@ef2f2b1d4773 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersme
bugs1334127
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1334127 - land NSS 0a7ba014dbb3, r=me
security/nss/TAG-INFO
security/nss/automation/taskcluster/docker-fuzz/setup.sh
security/nss/automation/taskcluster/graph/src/extend.js
security/nss/automation/taskcluster/graph/src/queue.js
security/nss/coreconf/coreconf.dep
security/nss/gtests/common/gtest.gypi
security/nss/gtests/nss_bogo_shim/nss_bogo_shim.cc
security/nss/gtests/nss_bogo_shim/nsskeys.cc
security/nss/gtests/pk11_gtest/manifest.mn
security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc
security/nss/gtests/pk11_gtest/pk11_ecdsa_vectors.h
security/nss/gtests/pk11_gtest/pk11_gtest.gyp
security/nss/gtests/pk11_gtest/pk11_rsapss_unittest.cc
security/nss/gtests/pk11_gtest/pk11_signature_test.h
security/nss/lib/cryptohi/seckey.c
security/nss/lib/pk11wrap/pk11pk12.c
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-650e5f6cb617
+0a7ba014dbb3
--- a/security/nss/automation/taskcluster/docker-fuzz/setup.sh
+++ b/security/nss/automation/taskcluster/docker-fuzz/setup.sh
@@ -31,16 +31,17 @@ apt-get install -y --no-install-recommen
 
 # Install LLVM/clang-4.0.
 mkdir clang-tmp
 git clone -n --depth 1 https://chromium.googlesource.com/chromium/src/tools/clang clang-tmp/clang
 git -C clang-tmp/clang checkout HEAD scripts/update.py
 clang-tmp/clang/scripts/update.py
 rm -fr clang-tmp
 
+# Generate locales.
 locale-gen en_US.UTF-8
 dpkg-reconfigure locales
 
 # Cleanup.
 rm -rf ~/.ccache ~/.cache
 apt-get autoremove -y
 apt-get clean
 apt-get autoclean
--- a/security/nss/automation/taskcluster/graph/src/extend.js
+++ b/security/nss/automation/taskcluster/graph/src/extend.js
@@ -273,16 +273,17 @@ async function scheduleFuzzing() {
     env: {
       ASAN_OPTIONS: "allocator_may_return_null=1",
       UBSAN_OPTIONS: "print_stacktrace=1",
       NSS_DISABLE_ARENA_FREE_LIST: "1",
       NSS_DISABLE_UNLOAD: "1",
       CC: "clang",
       CCC: "clang++"
     },
+    features: ["allowPtrace"],
     platform: "linux64",
     collection: "fuzz",
     image: FUZZ_IMAGE
   };
 
   // Build base definition.
   let build_base = merge({
     command: [
@@ -327,71 +328,55 @@ async function scheduleFuzzing() {
     parent: task_build,
     name: "Hash",
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && nss/automation/taskcluster/scripts/fuzz.sh " +
         "hash nss/fuzz/corpus/hash -max_total_time=300 -max_len=4096"
     ],
-    // Need a privileged docker container to remove detect_leaks=0.
-    env: {
-      ASAN_OPTIONS: "allocator_may_return_null=1:detect_leaks=0",
-    },
     symbol: "Hash",
     kind: "test"
   }));
 
   queue.scheduleTask(merge(base, {
     parent: task_build,
     name: "QuickDER",
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && nss/automation/taskcluster/scripts/fuzz.sh " +
         "quickder nss/fuzz/corpus/quickder -max_total_time=300 -max_len=10000"
     ],
-    // Need a privileged docker container to remove detect_leaks=0.
-    env: {
-      ASAN_OPTIONS: "allocator_may_return_null=1:detect_leaks=0",
-    },
     symbol: "QuickDER",
     kind: "test"
   }));
 
   queue.scheduleTask(merge(base, {
     parent: task_build,
     name: "MPI",
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && nss/automation/taskcluster/scripts/fuzz.sh " +
         "mpi nss/fuzz/corpus/mpi -max_total_time=300 -max_len=2048"
     ],
-    // Need a privileged docker container to remove detect_leaks=0.
-    env: {
-      ASAN_OPTIONS: "allocator_may_return_null=1:detect_leaks=0",
-    },
     symbol: "MPI",
     kind: "test"
   }));
 
   queue.scheduleTask(merge(base, {
     parent: task_build,
     name: "CertDN",
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && nss/automation/taskcluster/scripts/fuzz.sh " +
         "certDN nss/fuzz/corpus/certDN -max_total_time=300 -max_len=4096"
     ],
-    // Need a privileged docker container to remove detect_leaks=0.
-    env: {
-      ASAN_OPTIONS: "allocator_may_return_null=1:detect_leaks=0",
-    },
     symbol: "CertDN",
     kind: "test"
   }));
 
   return queue.submit();
 }
 
 /*****************************************************************************/
--- a/security/nss/automation/taskcluster/graph/src/queue.js
+++ b/security/nss/automation/taskcluster/graph/src/queue.js
@@ -75,16 +75,17 @@ function parseTreeherder(def) {
   if (def.tier) {
     treeherder.tier = def.tier;
   }
 
   return treeherder;
 }
 
 function convertTask(def) {
+  let scopes = [];
   let dependencies = [];
 
   let env = merge({
     NSS_HEAD_REPOSITORY: process.env.NSS_HEAD_REPOSITORY,
     NSS_HEAD_REVISION: process.env.NSS_HEAD_REVISION
   }, def.env || {});
 
   if (def.parent) {
@@ -105,29 +106,34 @@ function convertTask(def) {
     command: def.command,
     maxRunTime: def.maxRunTime || 3600
   };
 
   if (def.image) {
     payload.image = def.image;
   }
 
+  if (def.artifacts) {
+    payload.artifacts = parseArtifacts(def.artifacts);
+  }
+
   if (def.features) {
     payload.features = parseFeatures(def.features);
-  }
 
-  if (def.artifacts) {
-    payload.artifacts = parseArtifacts(def.artifacts);
+    if (payload.features.allowPtrace) {
+      scopes.push("docker-worker:feature:allowPtrace");
+    }
   }
 
   return {
     provisionerId: def.provisioner || "aws-provisioner-v1",
     workerType: def.workerType || "hg-worker",
     schedulerId: "task-graph-scheduler",
 
+    scopes,
     created: fromNow(0),
     deadline: fromNow(24),
 
     dependencies,
     routes: parseRoutes(def.routes || []),
 
     metadata: {
       name: def.name,
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,8 +5,9 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
+
--- a/security/nss/gtests/common/gtest.gypi
+++ b/security/nss/gtests/common/gtest.gypi
@@ -1,13 +1,21 @@
 # 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/.
 {
   'target_defaults': {
+    'cflags': [
+      '-Wsign-compare',
+    ],
+    'xcode_settings': {
+      'OTHER_CFLAGS': [
+        '-Wsign-compare',
+      ],
+    },
     'conditions': [
       ['OS=="win"', {
         'libraries': [
           '-lws2_32',
         ],
       }],
       ['OS=="android"', {
         'libraries': [
--- a/security/nss/gtests/nss_bogo_shim/nss_bogo_shim.cc
+++ b/security/nss/gtests/nss_bogo_shim/nss_bogo_shim.cc
@@ -111,21 +111,17 @@ class TestAgent {
     return true;
   }
 
   bool SetupKeys() {
     SECStatus rv;
 
     if (cfg_.get<std::string>("key-file") != "") {
       key_ = ReadPrivateKey(cfg_.get<std::string>("key-file"));
-      if (!key_) {
-        // Temporary to handle our inability to handle ECDSA.
-        exitCodeUnimplemented = true;
-        return false;
-      }
+      if (!key_) return false;
     }
     if (cfg_.get<std::string>("cert-file") != "") {
       cert_ = ReadCertificate(cfg_.get<std::string>("cert-file"));
       if (!cert_) return false;
     }
     if (cfg_.get<bool>("server")) {
       // Server
       rv = SSL_ConfigServerCert(ssl_fd_, cert_, key_, nullptr, 0);
--- a/security/nss/gtests/nss_bogo_shim/nsskeys.cc
+++ b/security/nss/gtests/nss_bogo_shim/nsskeys.cc
@@ -58,17 +58,16 @@ SECKEYPrivateKey* ReadPrivateKey(const s
   PK11SlotInfo* slot = PK11_GetInternalSlot();
   SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
       slot, &item, nullptr, nullptr, PR_FALSE, PR_FALSE,
       KU_KEY_ENCIPHERMENT | KU_DATA_ENCIPHERMENT | KU_DIGITAL_SIGNATURE,
       &privkey, nullptr);
   PK11_FreeSlot(slot);
   SECITEM_FreeItem(&item, PR_FALSE);
   if (rv != SECSuccess) {
-    // This is probably due to this being an ECDSA key (Bug 1295121).
     std::cerr << "Couldn't import key " << PORT_ErrorToString(PORT_GetError())
               << "\n";
     return nullptr;
   }
 
   return privkey;
 }
 
--- a/security/nss/gtests/pk11_gtest/manifest.mn
+++ b/security/nss/gtests/pk11_gtest/manifest.mn
@@ -4,16 +4,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 CORE_DEPTH = ../..
 DEPTH      = ../..
 MODULE = nss
 
 CPPSRCS = \
       pk11_aeskeywrap_unittest.cc \
       pk11_chacha20poly1305_unittest.cc \
+      pk11_ecdsa_unittest.cc \
       pk11_export_unittest.cc \
       pk11_pbkdf2_unittest.cc \
       pk11_prf_unittest.cc \
       pk11_prng_unittest.cc \
       pk11_rsapss_unittest.cc \
       $(NULL)
 
 INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc
@@ -0,0 +1,156 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <memory>
+#include "nss.h"
+#include "pk11pub.h"
+#include "sechash.h"
+
+#include "gtest/gtest.h"
+#include "scoped_ptrs.h"
+
+#include "pk11_ecdsa_vectors.h"
+#include "pk11_signature_test.h"
+
+namespace nss_test {
+
+class Pkcs11EcdsaTest : public Pk11SignatureTest {
+ protected:
+  CK_MECHANISM_TYPE mechanism() { return CKM_ECDSA; }
+  SECItem* parameters() { return nullptr; }
+};
+
+class Pkcs11EcdsaSha256Test : public Pkcs11EcdsaTest {
+ protected:
+  SECOidTag hashOID() { return SEC_OID_SHA256; }
+};
+
+class Pkcs11EcdsaSha384Test : public Pkcs11EcdsaTest {
+ protected:
+  SECOidTag hashOID() { return SEC_OID_SHA384; }
+};
+
+class Pkcs11EcdsaSha512Test : public Pkcs11EcdsaTest {
+ protected:
+  SECOidTag hashOID() { return SEC_OID_SHA512; }
+};
+
+TEST_F(Pkcs11EcdsaSha256Test, VerifyP256) {
+  SIG_TEST_VECTOR_VERIFY(kP256Spki, kP256Data, kP256Signature)
+}
+TEST_F(Pkcs11EcdsaSha256Test, SignAndVerifyP256) {
+  SIG_TEST_VECTOR_SIGN_VERIFY(kP256Pkcs8, kP256Spki, kP256Data)
+}
+
+TEST_F(Pkcs11EcdsaSha384Test, VerifyP384) {
+  SIG_TEST_VECTOR_VERIFY(kP384Spki, kP384Data, kP384Signature)
+}
+TEST_F(Pkcs11EcdsaSha384Test, SignAndVerifyP384) {
+  SIG_TEST_VECTOR_SIGN_VERIFY(kP384Pkcs8, kP384Spki, kP384Data)
+}
+
+TEST_F(Pkcs11EcdsaSha512Test, VerifyP521) {
+  SIG_TEST_VECTOR_VERIFY(kP521Spki, kP521Data, kP521Signature)
+}
+TEST_F(Pkcs11EcdsaSha512Test, SignAndVerifyP521) {
+  SIG_TEST_VECTOR_SIGN_VERIFY(kP521Pkcs8, kP521Spki, kP521Data)
+}
+
+// Importing a private key in PKCS#8 format must fail when the outer AlgID
+// struct contains neither id-ecPublicKey nor a namedCurve parameter.
+TEST_F(Pkcs11EcdsaSha256Test, ImportNoCurveOIDOrAlgorithmParams) {
+  EXPECT_FALSE(ImportPrivateKey(kP256Pkcs8NoCurveOIDOrAlgorithmParams,
+                                sizeof(kP256Pkcs8NoCurveOIDOrAlgorithmParams)));
+};
+
+// Importing a private key in PKCS#8 format must succeed when only the outer
+// AlgID struct contains the namedCurve parameters.
+TEST_F(Pkcs11EcdsaSha256Test, ImportOnlyAlgorithmParams) {
+  EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(
+      kP256Pkcs8OnlyAlgorithmParams, sizeof(kP256Pkcs8OnlyAlgorithmParams),
+      kP256Data, sizeof(kP256Data)));
+};
+
+// Importing a private key in PKCS#8 format must succeed when the outer AlgID
+// struct and the inner ECPrivateKey contain the same namedCurve parameters.
+// The inner curveOID is always ignored, so only the outer one will be used.
+TEST_F(Pkcs11EcdsaSha256Test, ImportMatchingCurveOIDAndAlgorithmParams) {
+  EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(
+      kP256Pkcs8MatchingCurveOIDAndAlgorithmParams,
+      sizeof(kP256Pkcs8MatchingCurveOIDAndAlgorithmParams), kP256Data,
+      sizeof(kP256Data)));
+};
+
+// Importing a private key in PKCS#8 format must succeed when the outer AlgID
+// struct and the inner ECPrivateKey contain dissimilar namedCurve parameters.
+// The inner curveOID is always ignored, so only the outer one will be used.
+TEST_F(Pkcs11EcdsaSha256Test, ImportDissimilarCurveOIDAndAlgorithmParams) {
+  EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(
+      kP256Pkcs8DissimilarCurveOIDAndAlgorithmParams,
+      sizeof(kP256Pkcs8DissimilarCurveOIDAndAlgorithmParams), kP256Data,
+      sizeof(kP256Data)));
+};
+
+// Importing a private key in PKCS#8 format must fail when the outer ASN.1
+// AlgorithmID struct contains only id-ecPublicKey but no namedCurve parameter.
+TEST_F(Pkcs11EcdsaSha256Test, ImportNoAlgorithmParams) {
+  EXPECT_FALSE(ImportPrivateKey(kP256Pkcs8NoAlgorithmParams,
+                                sizeof(kP256Pkcs8NoAlgorithmParams)));
+};
+
+// Importing a private key in PKCS#8 format must fail when id-ecPublicKey is
+// given (so we know it's an EC key) but the namedCurve parameter is unknown.
+TEST_F(Pkcs11EcdsaSha256Test, ImportInvalidAlgorithmParams) {
+  EXPECT_FALSE(ImportPrivateKey(kP256Pkcs8InvalidAlgorithmParams,
+                                sizeof(kP256Pkcs8InvalidAlgorithmParams)));
+};
+
+// Importing a private key in PKCS#8 format with a point not on the curve will
+// succeed. Using the contained public key however will fail when trying to
+// import it before using it for any operation.
+TEST_F(Pkcs11EcdsaSha256Test, ImportPointNotOnCurve) {
+  ScopedSECKEYPrivateKey privKey(ImportPrivateKey(
+      kP256Pkcs8PointNotOnCurve, sizeof(kP256Pkcs8PointNotOnCurve)));
+  ASSERT_TRUE(privKey);
+
+  ScopedSECKEYPublicKey pubKey(SECKEY_ConvertToPublicKey(privKey.get()));
+  ASSERT_TRUE(pubKey);
+
+  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+  ASSERT_TRUE(slot);
+
+  auto handle = PK11_ImportPublicKey(slot.get(), pubKey.get(), false);
+  EXPECT_EQ(handle, static_cast<decltype(handle)>(CK_INVALID_HANDLE));
+};
+
+// Importing a private key in PKCS#8 format must fail when no point is given.
+// PK11 currently offers no APIs to derive raw public keys from private values.
+TEST_F(Pkcs11EcdsaSha256Test, ImportNoPublicKey) {
+  EXPECT_FALSE(
+      ImportPrivateKey(kP256Pkcs8NoPublicKey, sizeof(kP256Pkcs8NoPublicKey)));
+};
+
+// Importing a public key in SPKI format must fail when id-ecPublicKey is
+// given (so we know it's an EC key) but the namedCurve parameter is missing.
+TEST_F(Pkcs11EcdsaSha256Test, ImportSpkiNoAlgorithmParams) {
+  EXPECT_FALSE(ImportPublicKey(kP256SpkiNoAlgorithmParams,
+                               sizeof(kP256SpkiNoAlgorithmParams)));
+}
+
+// Importing a public key in SPKI format with a point not on the curve will
+// succeed. Using the public key however will fail when trying to import
+// it before using it for any operation.
+TEST_F(Pkcs11EcdsaSha256Test, ImportSpkiPointNotOnCurve) {
+  ScopedSECKEYPublicKey pubKey(ImportPublicKey(
+      kP256SpkiPointNotOnCurve, sizeof(kP256SpkiPointNotOnCurve)));
+  ASSERT_TRUE(pubKey);
+
+  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+  ASSERT_TRUE(slot);
+
+  auto handle = PK11_ImportPublicKey(slot.get(), pubKey.get(), false);
+  EXPECT_EQ(handle, static_cast<decltype(handle)>(CK_INVALID_HANDLE));
+}
+
+}  // namespace nss_test
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/pk11_gtest/pk11_ecdsa_vectors.h
@@ -0,0 +1,251 @@
+/* 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/. */
+
+namespace nss_test {
+
+// ECDSA test vector, A.2.5. ECDSA, 256 Bits (Prime Field), SHA-256
+// <https://tools.ietf.org/html/rfc6979#appendix-A.2.5>
+const uint8_t kP256Pkcs8[] = {
+    0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
+    0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
+    0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20,
+    0xc9, 0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16, 0x6b, 0x5c, 0x21, 0x57,
+    0x67, 0xb1, 0xd6, 0x93, 0x4e, 0x50, 0xc3, 0xdb, 0x36, 0xe8, 0x9b, 0x12,
+    0x7b, 0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21, 0xa1, 0x44, 0x03, 0x42,
+    0x00, 0x04, 0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31, 0xc9, 0x61,
+    0xeb, 0x74, 0xc6, 0x35, 0x6d, 0x68, 0xc0, 0x49, 0xb8, 0x92, 0x3b, 0x61,
+    0xfa, 0x6c, 0xe6, 0x69, 0x62, 0x2e, 0x60, 0xf2, 0x9f, 0xb6, 0x79, 0x03,
+    0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99, 0xa4, 0x1a, 0xe9, 0xe9, 0x56, 0x28,
+    0xbc, 0x64, 0xf2, 0xf1, 0xb2, 0x0c, 0x2d, 0x7e, 0x9f, 0x51, 0x77, 0xa3,
+    0xc2, 0x94, 0xd4, 0x46, 0x22, 0x99};
+const uint8_t kP256Spki[] = {
+    0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
+    0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
+    0x42, 0x00, 0x04, 0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31, 0xc9,
+    0x61, 0xeb, 0x74, 0xc6, 0x35, 0x6d, 0x68, 0xc0, 0x49, 0xb8, 0x92, 0x3b,
+    0x61, 0xfa, 0x6c, 0xe6, 0x69, 0x62, 0x2e, 0x60, 0xf2, 0x9f, 0xb6, 0x79,
+    0x03, 0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99, 0xa4, 0x1a, 0xe9, 0xe9, 0x56,
+    0x28, 0xbc, 0x64, 0xf2, 0xf1, 0xb2, 0x0c, 0x2d, 0x7e, 0x9f, 0x51, 0x77,
+    0xa3, 0xc2, 0x94, 0xd4, 0x46, 0x22, 0x99};
+const uint8_t kP256Data[] = {'s', 'a', 'm', 'p', 'l', 'e'};
+const uint8_t kP256Signature[] = {
+    0xef, 0xd4, 0x8b, 0x2a, 0xac, 0xb6, 0xa8, 0xfd, 0x11, 0x40, 0xdd,
+    0x9c, 0xd4, 0x5e, 0x81, 0xd6, 0x9d, 0x2c, 0x87, 0x7b, 0x56, 0xaa,
+    0xf9, 0x91, 0xc3, 0x4d, 0x0e, 0xa8, 0x4e, 0xaf, 0x37, 0x16, 0xf7,
+    0xcb, 0x1c, 0x94, 0x2d, 0x65, 0x7c, 0x41, 0xd4, 0x36, 0xc7, 0xa1,
+    0xb6, 0xe2, 0x9f, 0x65, 0xf3, 0xe9, 0x00, 0xdb, 0xb9, 0xaf, 0xf4,
+    0x06, 0x4d, 0xc4, 0xab, 0x2f, 0x84, 0x3a, 0xcd, 0xa8};
+
+// ECDSA test vector, A.2.6. ECDSA, 384 Bits (Prime Field), SHA-384
+// <https://tools.ietf.org/html/rfc6979#appendix-A.2.6>
+const uint8_t kP384Pkcs8[] = {
+    0x30, 0x81, 0xb6, 0x02, 0x01, 0x00, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86,
+    0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22,
+    0x04, 0x81, 0x9e, 0x30, 0x81, 0x9b, 0x02, 0x01, 0x01, 0x04, 0x30, 0x6b,
+    0x9d, 0x3d, 0xad, 0x2e, 0x1b, 0x8c, 0x1c, 0x05, 0xb1, 0x98, 0x75, 0xb6,
+    0x65, 0x9f, 0x4d, 0xe2, 0x3c, 0x3b, 0x66, 0x7b, 0xf2, 0x97, 0xba, 0x9a,
+    0xa4, 0x77, 0x40, 0x78, 0x71, 0x37, 0xd8, 0x96, 0xd5, 0x72, 0x4e, 0x4c,
+    0x70, 0xa8, 0x25, 0xf8, 0x72, 0xc9, 0xea, 0x60, 0xd2, 0xed, 0xf5, 0xa1,
+    0x64, 0x03, 0x62, 0x00, 0x04, 0xec, 0x3a, 0x4e, 0x41, 0x5b, 0x4e, 0x19,
+    0xa4, 0x56, 0x86, 0x18, 0x02, 0x9f, 0x42, 0x7f, 0xa5, 0xda, 0x9a, 0x8b,
+    0xc4, 0xae, 0x92, 0xe0, 0x2e, 0x06, 0xaa, 0xe5, 0x28, 0x6b, 0x30, 0x0c,
+    0x64, 0xde, 0xf8, 0xf0, 0xea, 0x90, 0x55, 0x86, 0x60, 0x64, 0xa2, 0x54,
+    0x51, 0x54, 0x80, 0xbc, 0x13, 0x80, 0x15, 0xd9, 0xb7, 0x2d, 0x7d, 0x57,
+    0x24, 0x4e, 0xa8, 0xef, 0x9a, 0xc0, 0xc6, 0x21, 0x89, 0x67, 0x08, 0xa5,
+    0x93, 0x67, 0xf9, 0xdf, 0xb9, 0xf5, 0x4c, 0xa8, 0x4b, 0x3f, 0x1c, 0x9d,
+    0xb1, 0x28, 0x8b, 0x23, 0x1c, 0x3a, 0xe0, 0xd4, 0xfe, 0x73, 0x44, 0xfd,
+    0x25, 0x33, 0x26, 0x47, 0x20};
+const uint8_t kP384Spki[] = {
+    0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
+    0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04,
+    0xec, 0x3a, 0x4e, 0x41, 0x5b, 0x4e, 0x19, 0xa4, 0x56, 0x86, 0x18, 0x02,
+    0x9f, 0x42, 0x7f, 0xa5, 0xda, 0x9a, 0x8b, 0xc4, 0xae, 0x92, 0xe0, 0x2e,
+    0x06, 0xaa, 0xe5, 0x28, 0x6b, 0x30, 0x0c, 0x64, 0xde, 0xf8, 0xf0, 0xea,
+    0x90, 0x55, 0x86, 0x60, 0x64, 0xa2, 0x54, 0x51, 0x54, 0x80, 0xbc, 0x13,
+    0x80, 0x15, 0xd9, 0xb7, 0x2d, 0x7d, 0x57, 0x24, 0x4e, 0xa8, 0xef, 0x9a,
+    0xc0, 0xc6, 0x21, 0x89, 0x67, 0x08, 0xa5, 0x93, 0x67, 0xf9, 0xdf, 0xb9,
+    0xf5, 0x4c, 0xa8, 0x4b, 0x3f, 0x1c, 0x9d, 0xb1, 0x28, 0x8b, 0x23, 0x1c,
+    0x3a, 0xe0, 0xd4, 0xfe, 0x73, 0x44, 0xfd, 0x25, 0x33, 0x26, 0x47, 0x20};
+const uint8_t kP384Data[] = {'s', 'a', 'm', 'p', 'l', 'e'};
+const uint8_t kP384Signature[] = {
+    0x94, 0xed, 0xbb, 0x92, 0xa5, 0xec, 0xb8, 0xaa, 0xd4, 0x73, 0x6e, 0x56,
+    0xc6, 0x91, 0x91, 0x6b, 0x3f, 0x88, 0x14, 0x06, 0x66, 0xce, 0x9f, 0xa7,
+    0x3d, 0x64, 0xc4, 0xea, 0x95, 0xad, 0x13, 0x3c, 0x81, 0xa6, 0x48, 0x15,
+    0x2e, 0x44, 0xac, 0xf9, 0x6e, 0x36, 0xdd, 0x1e, 0x80, 0xfa, 0xbe, 0x46,
+    0x99, 0xef, 0x4a, 0xeb, 0x15, 0xf1, 0x78, 0xce, 0xa1, 0xfe, 0x40, 0xdb,
+    0x26, 0x03, 0x13, 0x8f, 0x13, 0x0e, 0x74, 0x0a, 0x19, 0x62, 0x45, 0x26,
+    0x20, 0x3b, 0x63, 0x51, 0xd0, 0xa3, 0xa9, 0x4f, 0xa3, 0x29, 0xc1, 0x45,
+    0x78, 0x6e, 0x67, 0x9e, 0x7b, 0x82, 0xc7, 0x1a, 0x38, 0x62, 0x8a, 0xc8};
+
+// ECDSA test vector, A.2.7. ECDSA, 521 Bits (Prime Field), SHA-512
+// <https://tools.ietf.org/html/rfc6979#appendix-A.2.7>
+const uint8_t kP521Pkcs8[] = {
+    0x30, 0x81, 0xed, 0x02, 0x01, 0x00, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86,
+    0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23,
+    0x04, 0x81, 0xd5, 0x30, 0x81, 0xd2, 0x02, 0x01, 0x01, 0x04, 0x42, 0x00,
+    0xfa, 0xd0, 0x6d, 0xaa, 0x62, 0xba, 0x3b, 0x25, 0xd2, 0xfb, 0x40, 0x13,
+    0x3d, 0xa7, 0x57, 0x20, 0x5d, 0xe6, 0x7f, 0x5b, 0xb0, 0x01, 0x8f, 0xee,
+    0x8c, 0x86, 0xe1, 0xb6, 0x8c, 0x7e, 0x75, 0xca, 0xa8, 0x96, 0xeb, 0x32,
+    0xf1, 0xf4, 0x7c, 0x70, 0x85, 0x58, 0x36, 0xa6, 0xd1, 0x6f, 0xcc, 0x14,
+    0x66, 0xf6, 0xd8, 0xfb, 0xec, 0x67, 0xdb, 0x89, 0xec, 0x0c, 0x08, 0xb0,
+    0xe9, 0x96, 0xb8, 0x35, 0x38, 0xa1, 0x81, 0x88, 0x03, 0x81, 0x85, 0x00,
+    0x04, 0x18, 0x94, 0x55, 0x0d, 0x07, 0x85, 0x93, 0x2e, 0x00, 0xea, 0xa2,
+    0x3b, 0x69, 0x4f, 0x21, 0x3f, 0x8c, 0x31, 0x21, 0xf8, 0x6d, 0xc9, 0x7a,
+    0x04, 0xe5, 0xa7, 0x16, 0x7d, 0xb4, 0xe5, 0xbc, 0xd3, 0x71, 0x12, 0x3d,
+    0x46, 0xe4, 0x5d, 0xb6, 0xb5, 0xd5, 0x37, 0x0a, 0x7f, 0x20, 0xfb, 0x63,
+    0x31, 0x55, 0xd3, 0x8f, 0xfa, 0x16, 0xd2, 0xbd, 0x76, 0x1d, 0xca, 0xc4,
+    0x74, 0xb9, 0xa2, 0xf5, 0x02, 0x3a, 0x40, 0x49, 0x31, 0x01, 0xc9, 0x62,
+    0xcd, 0x4d, 0x2f, 0xdd, 0xf7, 0x82, 0x28, 0x5e, 0x64, 0x58, 0x41, 0x39,
+    0xc2, 0xf9, 0x1b, 0x47, 0xf8, 0x7f, 0xf8, 0x23, 0x54, 0xd6, 0x63, 0x0f,
+    0x74, 0x6a, 0x28, 0xa0, 0xdb, 0x25, 0x74, 0x1b, 0x5b, 0x34, 0xa8, 0x28,
+    0x00, 0x8b, 0x22, 0xac, 0xc2, 0x3f, 0x92, 0x4f, 0xaa, 0xfb, 0xd4, 0xd3,
+    0x3f, 0x81, 0xea, 0x66, 0x95, 0x6d, 0xfe, 0xaa, 0x2b, 0xfd, 0xfc, 0xf5};
+const uint8_t kP521Spki[] = {
+    0x30, 0x81, 0x9b, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d,
+    0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23, 0x03, 0x81, 0x86,
+    0x00, 0x04, 0x01, 0x89, 0x45, 0x50, 0xd0, 0x78, 0x59, 0x32, 0xe0, 0x0e,
+    0xaa, 0x23, 0xb6, 0x94, 0xf2, 0x13, 0xf8, 0xc3, 0x12, 0x1f, 0x86, 0xdc,
+    0x97, 0xa0, 0x4e, 0x5a, 0x71, 0x67, 0xdb, 0x4e, 0x5b, 0xcd, 0x37, 0x11,
+    0x23, 0xd4, 0x6e, 0x45, 0xdb, 0x6b, 0x5d, 0x53, 0x70, 0xa7, 0xf2, 0x0f,
+    0xb6, 0x33, 0x15, 0x5d, 0x38, 0xff, 0xa1, 0x6d, 0x2b, 0xd7, 0x61, 0xdc,
+    0xac, 0x47, 0x4b, 0x9a, 0x2f, 0x50, 0x23, 0xa4, 0x00, 0x49, 0x31, 0x01,
+    0xc9, 0x62, 0xcd, 0x4d, 0x2f, 0xdd, 0xf7, 0x82, 0x28, 0x5e, 0x64, 0x58,
+    0x41, 0x39, 0xc2, 0xf9, 0x1b, 0x47, 0xf8, 0x7f, 0xf8, 0x23, 0x54, 0xd6,
+    0x63, 0x0f, 0x74, 0x6a, 0x28, 0xa0, 0xdb, 0x25, 0x74, 0x1b, 0x5b, 0x34,
+    0xa8, 0x28, 0x00, 0x8b, 0x22, 0xac, 0xc2, 0x3f, 0x92, 0x4f, 0xaa, 0xfb,
+    0xd4, 0xd3, 0x3f, 0x81, 0xea, 0x66, 0x95, 0x6d, 0xfe, 0xaa, 0x2b, 0xfd,
+    0xfc, 0xf5};
+const uint8_t kP521Data[] = {'s', 'a', 'm', 'p', 'l', 'e'};
+const uint8_t kP521Signature[] = {
+    0x00, 0xc3, 0x28, 0xfa, 0xfc, 0xbd, 0x79, 0xdd, 0x77, 0x85, 0x03, 0x70,
+    0xc4, 0x63, 0x25, 0xd9, 0x87, 0xcb, 0x52, 0x55, 0x69, 0xfb, 0x63, 0xc5,
+    0xd3, 0xbc, 0x53, 0x95, 0x0e, 0x6d, 0x4c, 0x5f, 0x17, 0x4e, 0x25, 0xa1,
+    0xee, 0x90, 0x17, 0xb5, 0xd4, 0x50, 0x60, 0x6a, 0xdd, 0x15, 0x2b, 0x53,
+    0x49, 0x31, 0xd7, 0xd4, 0xe8, 0x45, 0x5c, 0xc9, 0x1f, 0x9b, 0x15, 0xbf,
+    0x05, 0xec, 0x36, 0xe3, 0x77, 0xfa, 0x00, 0x61, 0x7c, 0xce, 0x7c, 0xf5,
+    0x06, 0x48, 0x06, 0xc4, 0x67, 0xf6, 0x78, 0xd3, 0xb4, 0x08, 0x0d, 0x6f,
+    0x1c, 0xc5, 0x0a, 0xf2, 0x6c, 0xa2, 0x09, 0x41, 0x73, 0x08, 0x28, 0x1b,
+    0x68, 0xaf, 0x28, 0x26, 0x23, 0xea, 0xa6, 0x3e, 0x5b, 0x5c, 0x07, 0x23,
+    0xd8, 0xb8, 0xc3, 0x7f, 0xf0, 0x77, 0x7b, 0x1a, 0x20, 0xf8, 0xcc, 0xb1,
+    0xdc, 0xcc, 0x43, 0x99, 0x7f, 0x1e, 0xe0, 0xe4, 0x4d, 0xa4, 0xa6, 0x7a};
+
+// ECDSA test vectors, SPKI and PKCS#8 edge cases.
+const uint8_t kP256Pkcs8NoCurveOIDOrAlgorithmParams[] = {
+    0x30, 0x7d, 0x02, 0x01, 0x00, 0x30, 0x09, 0x06, 0x07, 0x2a, 0x86, 0x48,
+    0xce, 0x3d, 0x02, 0x01, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04,
+    0x20, 0xc9, 0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16, 0x6b, 0x5c, 0x21,
+    0x57, 0x67, 0xb1, 0xd6, 0x93, 0x4e, 0x50, 0xc3, 0xdb, 0x36, 0xe8, 0x9b,
+    0x12, 0x7b, 0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21, 0xa1, 0x44, 0x03,
+    0x42, 0x00, 0x04, 0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31, 0xc9,
+    0x61, 0xeb, 0x74, 0xc6, 0x35, 0x6d, 0x68, 0xc0, 0x49, 0xb8, 0x92, 0x3b,
+    0x61, 0xfa, 0x6c, 0xe6, 0x69, 0x62, 0x2e, 0x60, 0xf2, 0x9f, 0xb6, 0x79,
+    0x03, 0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99, 0xa4, 0x1a, 0xe9, 0xe9, 0x56,
+    0x28, 0xbc, 0x64, 0xf2, 0xf1, 0xb2, 0x0c, 0x2d, 0x7e, 0x9f, 0x51, 0x77,
+    0xa3, 0xc2, 0x94, 0xd4, 0x46, 0x22, 0x99};
+const uint8_t kP256Pkcs8OnlyAlgorithmParams[] = {
+    0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
+    0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
+    0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20,
+    0xc9, 0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16, 0x6b, 0x5c, 0x21, 0x57,
+    0x67, 0xb1, 0xd6, 0x93, 0x4e, 0x50, 0xc3, 0xdb, 0x36, 0xe8, 0x9b, 0x12,
+    0x7b, 0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21, 0xa1, 0x44, 0x03, 0x42,
+    0x00, 0x04, 0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31, 0xc9, 0x61,
+    0xeb, 0x74, 0xc6, 0x35, 0x6d, 0x68, 0xc0, 0x49, 0xb8, 0x92, 0x3b, 0x61,
+    0xfa, 0x6c, 0xe6, 0x69, 0x62, 0x2e, 0x60, 0xf2, 0x9f, 0xb6, 0x79, 0x03,
+    0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99, 0xa4, 0x1a, 0xe9, 0xe9, 0x56, 0x28,
+    0xbc, 0x64, 0xf2, 0xf1, 0xb2, 0x0c, 0x2d, 0x7e, 0x9f, 0x51, 0x77, 0xa3,
+    0xc2, 0x94, 0xd4, 0x46, 0x22, 0x99};
+const uint8_t kP256Pkcs8NoAlgorithmParams[] = {
+    0x30, 0x81, 0x89, 0x02, 0x01, 0x00, 0x30, 0x09, 0x06, 0x07, 0x2a, 0x86,
+    0x48, 0xce, 0x3d, 0x02, 0x01, 0x04, 0x79, 0x30, 0x77, 0x02, 0x01, 0x01,
+    0x04, 0x20, 0xc9, 0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16, 0x6b, 0x5c,
+    0x21, 0x57, 0x67, 0xb1, 0xd6, 0x93, 0x4e, 0x50, 0xc3, 0xdb, 0x36, 0xe8,
+    0x9b, 0x12, 0x7b, 0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21, 0xa0, 0x0a,
+    0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0xa1, 0x44,
+    0x03, 0x42, 0x00, 0x04, 0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31,
+    0xc9, 0x61, 0xeb, 0x74, 0xc6, 0x35, 0x6d, 0x68, 0xc0, 0x49, 0xb8, 0x92,
+    0x3b, 0x61, 0xfa, 0x6c, 0xe6, 0x69, 0x62, 0x2e, 0x60, 0xf2, 0x9f, 0xb6,
+    0x79, 0x03, 0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99, 0xa4, 0x1a, 0xe9, 0xe9,
+    0x56, 0x28, 0xbc, 0x64, 0xf2, 0xf1, 0xb2, 0x0c, 0x2d, 0x7e, 0x9f, 0x51,
+    0x77, 0xa3, 0xc2, 0x94, 0xd4, 0x46, 0x22, 0x99};
+const uint8_t kP256Pkcs8MatchingCurveOIDAndAlgorithmParams[] = {
+    0x30, 0x81, 0x93, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
+    0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
+    0x03, 0x01, 0x07, 0x04, 0x79, 0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20,
+    0xc9, 0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16, 0x6b, 0x5c, 0x21, 0x57,
+    0x67, 0xb1, 0xd6, 0x93, 0x4e, 0x50, 0xc3, 0xdb, 0x36, 0xe8, 0x9b, 0x12,
+    0x7b, 0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21, 0xa0, 0x0a, 0x06, 0x08,
+    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42,
+    0x00, 0x04, 0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31, 0xc9, 0x61,
+    0xeb, 0x74, 0xc6, 0x35, 0x6d, 0x68, 0xc0, 0x49, 0xb8, 0x92, 0x3b, 0x61,
+    0xfa, 0x6c, 0xe6, 0x69, 0x62, 0x2e, 0x60, 0xf2, 0x9f, 0xb6, 0x79, 0x03,
+    0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99, 0xa4, 0x1a, 0xe9, 0xe9, 0x56, 0x28,
+    0xbc, 0x64, 0xf2, 0xf1, 0xb2, 0x0c, 0x2d, 0x7e, 0x9f, 0x51, 0x77, 0xa3,
+    0xc2, 0x94, 0xd4, 0x46, 0x22, 0x99};
+const uint8_t kP256Pkcs8DissimilarCurveOIDAndAlgorithmParams[] = {
+    0x30, 0x81, 0x90, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
+    0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
+    0x03, 0x01, 0x07, 0x04, 0x76, 0x30, 0x74, 0x02, 0x01, 0x01, 0x04, 0x20,
+    0xc9, 0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16, 0x6b, 0x5c, 0x21, 0x57,
+    0x67, 0xb1, 0xd6, 0x93, 0x4e, 0x50, 0xc3, 0xdb, 0x36, 0xe8, 0x9b, 0x12,
+    0x7b, 0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21, 0xa0, 0x07, 0x06, 0x05,
+    0x2b, 0x81, 0x04, 0x00, 0x22, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0x60,
+    0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31, 0xc9, 0x61, 0xeb, 0x74, 0xc6,
+    0x35, 0x6d, 0x68, 0xc0, 0x49, 0xb8, 0x92, 0x3b, 0x61, 0xfa, 0x6c, 0xe6,
+    0x69, 0x62, 0x2e, 0x60, 0xf2, 0x9f, 0xb6, 0x79, 0x03, 0xfe, 0x10, 0x08,
+    0xb8, 0xbc, 0x99, 0xa4, 0x1a, 0xe9, 0xe9, 0x56, 0x28, 0xbc, 0x64, 0xf2,
+    0xf1, 0xb2, 0x0c, 0x2d, 0x7e, 0x9f, 0x51, 0x77, 0xa3, 0xc2, 0x94, 0xd4,
+    0x46, 0x22, 0x99};
+const uint8_t kP256Pkcs8InvalidAlgorithmParams[] = {
+    0x30, 0x81, 0x82, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x07, 0x2a, 0x86,
+    0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x03, 0x2a, 0x03, 0x04, 0x04, 0x6d,
+    0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20, 0xc9, 0xaf, 0xa9, 0xd8, 0x45,
+    0xba, 0x75, 0x16, 0x6b, 0x5c, 0x21, 0x57, 0x67, 0xb1, 0xd6, 0x93, 0x4e,
+    0x50, 0xc3, 0xdb, 0x36, 0xe8, 0x9b, 0x12, 0x7b, 0x8a, 0x62, 0x2b, 0x12,
+    0x0f, 0x67, 0x21, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0x60, 0xfe, 0xd4,
+    0xba, 0x25, 0x5a, 0x9d, 0x31, 0xc9, 0x61, 0xeb, 0x74, 0xc6, 0x35, 0x6d,
+    0x68, 0xc0, 0x49, 0xb8, 0x92, 0x3b, 0x61, 0xfa, 0x6c, 0xe6, 0x69, 0x62,
+    0x2e, 0x60, 0xf2, 0x9f, 0xb6, 0x79, 0x03, 0xfe, 0x10, 0x08, 0xb8, 0xbc,
+    0x99, 0xa4, 0x1a, 0xe9, 0xe9, 0x56, 0x28, 0xbc, 0x64, 0xf2, 0xf1, 0xb2,
+    0x0c, 0x2d, 0x7e, 0x9f, 0x51, 0x77, 0xa3, 0xc2, 0x94, 0xd4, 0x46, 0x22,
+    0x99};
+const uint8_t kP256Pkcs8PointNotOnCurve[] = {
+    0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
+    0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
+    0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20,
+    0xc9, 0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16, 0x6b, 0x5c, 0x21, 0x57,
+    0x67, 0xb1, 0xd6, 0x93, 0x4e, 0x50, 0xc3, 0xdb, 0x36, 0xe8, 0x9b, 0x12,
+    0x7b, 0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21, 0xa1, 0x44, 0x03, 0x42,
+    0x00, 0x04, 0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31, 0xc9, 0x61,
+    0xeb, 0x74, 0xc6, 0x35, 0x6d, 0x68, 0xc0, 0x49, 0xb8, 0x92, 0x3b, 0x61,
+    0xfa, 0x6c, 0xe6, 0x69, 0x62, 0x2e, 0x60, 0xf2, 0x9f, 0xb6, 0x79, 0x03,
+    0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99, 0xa4, 0x1a, 0xe9, 0xe9, 0x56, 0x28,
+    0xbc, 0x64, 0xf2, 0xf1, 0xb2, 0x0c, 0x2d, 0x7e, 0x9f, 0x51, 0x77, 0xa3,
+    0xc2, 0x94, 0xd4, 0x33, 0x11, 0x77};
+const uint8_t kP256Pkcs8NoPublicKey[] = {
+    0x30, 0x41, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48,
+    0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03,
+    0x01, 0x07, 0x04, 0x27, 0x30, 0x25, 0x02, 0x01, 0x01, 0x04, 0x20, 0xc9,
+    0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16, 0x6b, 0x5c, 0x21, 0x57, 0x67,
+    0xb1, 0xd6, 0x93, 0x4e, 0x50, 0xc3, 0xdb, 0x36, 0xe8, 0x9b, 0x12, 0x7b,
+    0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21};
+const uint8_t kP256SpkiNoAlgorithmParams[] = {
+    0x30, 0x4f, 0x30, 0x09, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
+    0x01, 0x03, 0x42, 0x00, 0x04, 0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d,
+    0x31, 0xc9, 0x61, 0xeb, 0x74, 0xc6, 0x35, 0x6d, 0x68, 0xc0, 0x49, 0xb8,
+    0x92, 0x3b, 0x61, 0xfa, 0x6c, 0xe6, 0x69, 0x62, 0x2e, 0x60, 0xf2, 0x9f,
+    0xb6, 0x79, 0x03, 0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99, 0xa4, 0x1a, 0xe9,
+    0xe9, 0x56, 0x28, 0xbc, 0x64, 0xf2, 0xf1, 0xb2, 0x0c, 0x2d, 0x7e, 0x9f,
+    0x51, 0x77, 0xa3, 0xc2, 0x94, 0xd4, 0x46, 0x22, 0x99};
+const uint8_t kP256SpkiPointNotOnCurve[] = {
+    0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
+    0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
+    0x42, 0x00, 0x04, 0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31, 0xc9,
+    0x61, 0xeb, 0x74, 0xc6, 0x35, 0x6d, 0x68, 0xc0, 0x49, 0xb8, 0x92, 0x3b,
+    0x61, 0xfa, 0x6c, 0xe6, 0x69, 0x62, 0x2e, 0x60, 0xf2, 0x9f, 0xb6, 0x79,
+    0x03, 0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99, 0xa4, 0x1a, 0xe9, 0xe9, 0x56,
+    0x28, 0xbc, 0x64, 0xf2, 0xf1, 0xb2, 0x0c, 0x2d, 0x7e, 0x9f, 0x51, 0x77,
+    0xa3, 0xc2, 0x94, 0x00, 0x33, 0x11, 0x77};
+
+}  // namespace nss_test
--- a/security/nss/gtests/pk11_gtest/pk11_gtest.gyp
+++ b/security/nss/gtests/pk11_gtest/pk11_gtest.gyp
@@ -8,16 +8,17 @@
   ],
   'targets': [
     {
       'target_name': 'pk11_gtest',
       'type': 'executable',
       'sources': [
         'pk11_aeskeywrap_unittest.cc',
         'pk11_chacha20poly1305_unittest.cc',
+        'pk11_ecdsa_unittest.cc',
         'pk11_pbkdf2_unittest.cc',
         'pk11_prf_unittest.cc',
         'pk11_prng_unittest.cc',
         'pk11_rsapss_unittest.cc',
         '<(DEPTH)/gtests/common/gtests.cc'
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
--- a/security/nss/gtests/pk11_gtest/pk11_rsapss_unittest.cc
+++ b/security/nss/gtests/pk11_gtest/pk11_rsapss_unittest.cc
@@ -8,120 +8,43 @@
 #include "nss.h"
 #include "pk11pub.h"
 #include "sechash.h"
 
 #include "gtest/gtest.h"
 #include "scoped_ptrs.h"
 
 #include "pk11_rsapss_vectors.h"
+#include "pk11_signature_test.h"
 
 namespace nss_test {
 
-static unsigned char* toUcharPtr(const uint8_t* v) {
-  return const_cast<unsigned char*>(static_cast<const unsigned char*>(v));
-}
-
-class Pkcs11RsaPssTest : public ::testing::Test {};
-
-class Pkcs11RsaPssVectorTest : public Pkcs11RsaPssTest {
+class Pkcs11RsaPssVectorTest : public Pk11SignatureTest {
  public:
-  void Verify(const uint8_t* spki, size_t spki_len, const uint8_t* data,
-              size_t data_len, const uint8_t* sig, size_t sig_len) {
-    // Verify data signed with PSS/SHA-1.
-    SECOidTag hashOid = SEC_OID_SHA1;
-    CK_MECHANISM_TYPE hashMech = CKM_SHA_1;
-    CK_RSA_PKCS_MGF_TYPE mgf = CKG_MGF1_SHA1;
-
-    // Set up PSS parameters.
-    unsigned int hLen = HASH_ResultLenByOidTag(hashOid);
-    CK_RSA_PKCS_PSS_PARAMS rsaPssParams = {hashMech, mgf, hLen};
-    SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&rsaPssParams),
-                      sizeof(rsaPssParams)};
+  Pkcs11RsaPssVectorTest() {
+    rsaPssParams_.hashAlg = CKM_SHA_1;
+    rsaPssParams_.mgf = CKG_MGF1_SHA1;
+    rsaPssParams_.sLen = HASH_ResultLenByOidTag(SEC_OID_SHA1);
 
-    // Import public key.
-    SECItem spkiItem = {siBuffer, toUcharPtr(spki),
-                        static_cast<unsigned int>(spki_len)};
-    ScopedCERTSubjectPublicKeyInfo certSpki(
-        SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem));
-    ScopedSECKEYPublicKey pubKey(SECKEY_ExtractPublicKey(certSpki.get()));
-
-    // Hash the data.
-    std::vector<uint8_t> hashBuf(hLen);
-    SECItem hash = {siBuffer, hashBuf.data(),
-                    static_cast<unsigned int>(hashBuf.size())};
-    SECStatus rv = PK11_HashBuf(hashOid, hash.data, toUcharPtr(data), data_len);
-    EXPECT_EQ(rv, SECSuccess);
-
-    // Verify.
-    CK_MECHANISM_TYPE mech = CKM_RSA_PKCS_PSS;
-    SECItem sigItem = {siBuffer, toUcharPtr(sig),
-                       static_cast<unsigned int>(sig_len)};
-    rv = PK11_VerifyWithMechanism(pubKey.get(), mech, &params, &sigItem, &hash,
-                                  nullptr);
-    EXPECT_EQ(rv, SECSuccess);
+    params_.type = siBuffer;
+    params_.data = reinterpret_cast<unsigned char*>(&rsaPssParams_);
+    params_.len = sizeof(rsaPssParams_);
   }
 
-  void SignAndVerify(const uint8_t* pkcs8, size_t pkcs8_len,
-                     const uint8_t* spki, size_t spki_len, const uint8_t* data,
-                     size_t data_len) {
-    // Sign with PSS/SHA-1.
-    SECOidTag hashOid = SEC_OID_SHA1;
-    CK_MECHANISM_TYPE hashMech = CKM_SHA_1;
-    CK_RSA_PKCS_MGF_TYPE mgf = CKG_MGF1_SHA1;
-
-    ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
-    ASSERT_TRUE(slot);
-
-    SECItem pkcs8Item = {siBuffer, toUcharPtr(pkcs8),
-                         static_cast<unsigned int>(pkcs8_len)};
-
-    // Import PKCS #8.
-    SECKEYPrivateKey* key = nullptr;
-    SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
-        slot.get(), &pkcs8Item, nullptr, nullptr, false, false, KU_ALL, &key,
-        nullptr);
-    EXPECT_TRUE(rv == SECSuccess && !!key);
-    ScopedSECKEYPrivateKey privKey(key);
+ protected:
+  CK_MECHANISM_TYPE mechanism() { return CKM_RSA_PKCS_PSS; }
+  SECItem* parameters() { return &params_; }
+  SECOidTag hashOID() { return SEC_OID_SHA1; }
 
-    // Set up PSS parameters.
-    unsigned int hLen = HASH_ResultLenByOidTag(hashOid);
-    CK_RSA_PKCS_PSS_PARAMS rsaPssParams = {hashMech, mgf, hLen};
-    SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&rsaPssParams),
-                      sizeof(rsaPssParams)};
-
-    // Hash the data.
-    std::vector<uint8_t> hashBuf(hLen);
-    SECItem hash = {siBuffer, hashBuf.data(),
-                    static_cast<unsigned int>(hashBuf.size())};
-    rv = PK11_HashBuf(hashOid, hash.data, toUcharPtr(data), data_len);
-    EXPECT_EQ(rv, SECSuccess);
-
-    // Prepare signature buffer.
-    uint32_t len = PK11_SignatureLen(privKey.get());
-    std::vector<uint8_t> sigBuf(len);
-    SECItem sig = {siBuffer, sigBuf.data(),
-                   static_cast<unsigned int>(sigBuf.size())};
-
-    CK_MECHANISM_TYPE mech = CKM_RSA_PKCS_PSS;
-    rv = PK11_SignWithMechanism(privKey.get(), mech, &params, &sig, &hash);
-    EXPECT_EQ(rv, SECSuccess);
-
-    // Verify.
-    Verify(spki, spki_len, data, data_len, sig.data, sig.len);
-  }
+ private:
+  CK_RSA_PKCS_PSS_PARAMS rsaPssParams_;
+  SECItem params_;
 };
 
-#define PSS_TEST_VECTOR_VERIFY(spki, data, sig) \
-  Verify(spki, sizeof(spki), data, sizeof(data), sig, sizeof(sig));
-
-#define PSS_TEST_VECTOR_SIGN_VERIFY(pkcs8, spki, data) \
-  SignAndVerify(pkcs8, sizeof(pkcs8), spki, sizeof(spki), data, sizeof(data));
-
-TEST_F(Pkcs11RsaPssTest, GenerateAndSignAndVerify) {
+TEST_F(Pkcs11RsaPssVectorTest, GenerateAndSignAndVerify) {
   // Sign data with a 1024-bit RSA key, using PSS/SHA-256.
   SECOidTag hashOid = SEC_OID_SHA256;
   CK_MECHANISM_TYPE hashMech = CKM_SHA256;
   CK_RSA_PKCS_MGF_TYPE mgf = CKG_MGF1_SHA256;
   PK11RSAGenParams rsaGenParams = {1024, 0x10001};
 
   // Generate RSA key pair.
   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
@@ -145,133 +68,132 @@ TEST_F(Pkcs11RsaPssTest, GenerateAndSign
                  static_cast<unsigned int>(sigBuf.size())};
 
   // Set up PSS parameters.
   CK_RSA_PKCS_PSS_PARAMS rsaPssParams = {hashMech, mgf, hLen};
   SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&rsaPssParams),
                     sizeof(rsaPssParams)};
 
   // Sign.
-  CK_MECHANISM_TYPE mech = CKM_RSA_PKCS_PSS;
-  rv = PK11_SignWithMechanism(privKey.get(), mech, &params, &sig, &data);
+  rv = PK11_SignWithMechanism(privKey.get(), mechanism(), &params, &sig, &data);
   EXPECT_EQ(rv, SECSuccess);
 
   // Verify.
-  rv = PK11_VerifyWithMechanism(pubKey.get(), mech, &params, &sig, &data,
+  rv = PK11_VerifyWithMechanism(pubKey.get(), mechanism(), &params, &sig, &data,
                                 nullptr);
   EXPECT_EQ(rv, SECSuccess);
 
   // Verification with modified data must fail.
   data.data[0] ^= 0xff;
-  rv = PK11_VerifyWithMechanism(pubKey.get(), mech, &params, &sig, &data,
+  rv = PK11_VerifyWithMechanism(pubKey.get(), mechanism(), &params, &sig, &data,
                                 nullptr);
   EXPECT_EQ(rv, SECFailure);
 
   // Verification with original data but the wrong signature must fail.
   data.data[0] ^= 0xff;  // Revert previous changes.
   sig.data[0] ^= 0xff;
-  rv = PK11_VerifyWithMechanism(pubKey.get(), mech, &params, &sig, &data,
+  rv = PK11_VerifyWithMechanism(pubKey.get(), mechanism(), &params, &sig, &data,
                                 nullptr);
   EXPECT_EQ(rv, SECFailure);
 }
 
 // RSA-PSS test vectors, pss-vect.txt, Example 1.1: A 1024-bit RSA Key Pair
 // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
 TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature1) {
-  PSS_TEST_VECTOR_VERIFY(kTestVector1Spki, kTestVector1Data, kTestVector1Sig);
+  SIG_TEST_VECTOR_VERIFY(kTestVector1Spki, kTestVector1Data, kTestVector1Sig);
 }
 TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify1) {
-  PSS_TEST_VECTOR_SIGN_VERIFY(kTestVector1Pkcs8, kTestVector1Spki,
+  SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector1Pkcs8, kTestVector1Spki,
                               kTestVector1Data);
 }
 
 // RSA-PSS test vectors, pss-vect.txt, Example 2.1: A 1025-bit RSA Key Pair
 // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
 TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature2) {
-  PSS_TEST_VECTOR_VERIFY(kTestVector2Spki, kTestVector2Data, kTestVector2Sig);
+  SIG_TEST_VECTOR_VERIFY(kTestVector2Spki, kTestVector2Data, kTestVector2Sig);
 }
 TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify2) {
-  PSS_TEST_VECTOR_SIGN_VERIFY(kTestVector2Pkcs8, kTestVector2Spki,
+  SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector2Pkcs8, kTestVector2Spki,
                               kTestVector2Data);
 }
 
 // RSA-PSS test vectors, pss-vect.txt, Example 3.1: A 1026-bit RSA Key Pair
 // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
 TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature3) {
-  PSS_TEST_VECTOR_VERIFY(kTestVector3Spki, kTestVector3Data, kTestVector3Sig);
+  SIG_TEST_VECTOR_VERIFY(kTestVector3Spki, kTestVector3Data, kTestVector3Sig);
 }
 TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify3) {
-  PSS_TEST_VECTOR_SIGN_VERIFY(kTestVector3Pkcs8, kTestVector3Spki,
+  SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector3Pkcs8, kTestVector3Spki,
                               kTestVector3Data);
 }
 
 // RSA-PSS test vectors, pss-vect.txt, Example 4.1: A 1027-bit RSA Key Pair
 // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
 TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature4) {
-  PSS_TEST_VECTOR_VERIFY(kTestVector4Spki, kTestVector4Data, kTestVector4Sig);
+  SIG_TEST_VECTOR_VERIFY(kTestVector4Spki, kTestVector4Data, kTestVector4Sig);
 }
 TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify4) {
-  PSS_TEST_VECTOR_SIGN_VERIFY(kTestVector4Pkcs8, kTestVector4Spki,
+  SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector4Pkcs8, kTestVector4Spki,
                               kTestVector4Data);
 }
 
 // RSA-PSS test vectors, pss-vect.txt, Example 5.1: A 1028-bit RSA Key Pair
 // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
 TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature5) {
-  PSS_TEST_VECTOR_VERIFY(kTestVector5Spki, kTestVector5Data, kTestVector5Sig);
+  SIG_TEST_VECTOR_VERIFY(kTestVector5Spki, kTestVector5Data, kTestVector5Sig);
 }
 TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify5) {
-  PSS_TEST_VECTOR_SIGN_VERIFY(kTestVector5Pkcs8, kTestVector5Spki,
+  SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector5Pkcs8, kTestVector5Spki,
                               kTestVector5Data);
 }
 
 // RSA-PSS test vectors, pss-vect.txt, Example 6.1: A 1029-bit RSA Key Pair
 // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
 TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature6) {
-  PSS_TEST_VECTOR_VERIFY(kTestVector6Spki, kTestVector6Data, kTestVector6Sig);
+  SIG_TEST_VECTOR_VERIFY(kTestVector6Spki, kTestVector6Data, kTestVector6Sig);
 }
 TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify6) {
-  PSS_TEST_VECTOR_SIGN_VERIFY(kTestVector6Pkcs8, kTestVector6Spki,
+  SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector6Pkcs8, kTestVector6Spki,
                               kTestVector6Data);
 }
 
 // RSA-PSS test vectors, pss-vect.txt, Example 7.1: A 1030-bit RSA Key Pair
 // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
 TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature7) {
-  PSS_TEST_VECTOR_VERIFY(kTestVector7Spki, kTestVector7Data, kTestVector7Sig);
+  SIG_TEST_VECTOR_VERIFY(kTestVector7Spki, kTestVector7Data, kTestVector7Sig);
 }
 TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify7) {
-  PSS_TEST_VECTOR_SIGN_VERIFY(kTestVector7Pkcs8, kTestVector7Spki,
+  SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector7Pkcs8, kTestVector7Spki,
                               kTestVector7Data);
 }
 
 // RSA-PSS test vectors, pss-vect.txt, Example 8.1: A 1031-bit RSA Key Pair
 // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
 TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature8) {
-  PSS_TEST_VECTOR_VERIFY(kTestVector8Spki, kTestVector8Data, kTestVector8Sig);
+  SIG_TEST_VECTOR_VERIFY(kTestVector8Spki, kTestVector8Data, kTestVector8Sig);
 }
 TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify8) {
-  PSS_TEST_VECTOR_SIGN_VERIFY(kTestVector8Pkcs8, kTestVector8Spki,
+  SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector8Pkcs8, kTestVector8Spki,
                               kTestVector8Data);
 }
 
 // RSA-PSS test vectors, pss-vect.txt, Example 9.1: A 1536-bit RSA Key Pair
 // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
 TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature9) {
-  PSS_TEST_VECTOR_VERIFY(kTestVector9Spki, kTestVector9Data, kTestVector9Sig);
+  SIG_TEST_VECTOR_VERIFY(kTestVector9Spki, kTestVector9Data, kTestVector9Sig);
 }
 TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify9) {
-  PSS_TEST_VECTOR_SIGN_VERIFY(kTestVector9Pkcs8, kTestVector9Spki,
+  SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector9Pkcs8, kTestVector9Spki,
                               kTestVector9Data);
 }
 
 // RSA-PSS test vectors, pss-vect.txt, Example 10.1: A 2048-bit RSA Key Pair
 // <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
 TEST_F(Pkcs11RsaPssVectorTest, VerifyKnownSignature10) {
-  PSS_TEST_VECTOR_VERIFY(kTestVector10Spki, kTestVector10Data,
+  SIG_TEST_VECTOR_VERIFY(kTestVector10Spki, kTestVector10Data,
                          kTestVector10Sig);
 }
 TEST_F(Pkcs11RsaPssVectorTest, SignAndVerify10) {
-  PSS_TEST_VECTOR_SIGN_VERIFY(kTestVector10Pkcs8, kTestVector10Spki,
+  SIG_TEST_VECTOR_SIGN_VERIFY(kTestVector10Pkcs8, kTestVector10Spki,
                               kTestVector10Data);
 }
 
 }  // namespace nss_test
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/pk11_gtest/pk11_signature_test.h
@@ -0,0 +1,140 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <memory>
+#include "nss.h"
+#include "pk11pub.h"
+#include "sechash.h"
+
+#include "gtest/gtest.h"
+#include "scoped_ptrs.h"
+
+namespace nss_test {
+
+static unsigned char* toUcharPtr(const uint8_t* v) {
+  return const_cast<unsigned char*>(static_cast<const unsigned char*>(v));
+}
+
+class Pk11SignatureTest : public ::testing::Test {
+ protected:
+  virtual CK_MECHANISM_TYPE mechanism() = 0;
+  virtual SECItem* parameters() = 0;
+  virtual SECOidTag hashOID() = 0;
+
+  ScopedSECKEYPrivateKey ImportPrivateKey(const uint8_t* pkcs8,
+                                          size_t pkcs8_len) {
+    ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+    if (!slot) {
+      return nullptr;
+    }
+
+    SECItem pkcs8Item = {siBuffer, toUcharPtr(pkcs8),
+                         static_cast<unsigned int>(pkcs8_len)};
+
+    SECKEYPrivateKey* key = nullptr;
+    SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
+        slot.get(), &pkcs8Item, nullptr, nullptr, false, false, KU_ALL, &key,
+        nullptr);
+
+    if (rv != SECSuccess) {
+      return nullptr;
+    }
+
+    return ScopedSECKEYPrivateKey(key);
+  }
+
+  ScopedSECKEYPublicKey ImportPublicKey(const uint8_t* spki, size_t spki_len) {
+    SECItem spkiItem = {siBuffer, toUcharPtr(spki),
+                        static_cast<unsigned int>(spki_len)};
+
+    ScopedCERTSubjectPublicKeyInfo certSpki(
+        SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem));
+
+    return ScopedSECKEYPublicKey(SECKEY_ExtractPublicKey(certSpki.get()));
+  }
+
+  ScopedSECItem ComputeHash(const uint8_t* data, size_t len) {
+    unsigned int hLen = HASH_ResultLenByOidTag(hashOID());
+    ScopedSECItem hash(SECITEM_AllocItem(nullptr, nullptr, hLen));
+    if (!hash) {
+      return nullptr;
+    }
+
+    SECStatus rv = PK11_HashBuf(hashOID(), hash->data, data, len);
+    if (rv != SECSuccess) {
+      return nullptr;
+    }
+
+    return hash;
+  }
+
+  ScopedSECItem SignHashedData(ScopedSECKEYPrivateKey& privKey,
+                               ScopedSECItem& hash) {
+    unsigned int sLen = PK11_SignatureLen(privKey.get());
+    ScopedSECItem sig(SECITEM_AllocItem(nullptr, nullptr, sLen));
+    if (!sig) {
+      return nullptr;
+    }
+
+    SECStatus rv = PK11_SignWithMechanism(privKey.get(), mechanism(),
+                                          parameters(), sig.get(), hash.get());
+    if (rv != SECSuccess) {
+      return nullptr;
+    }
+
+    return sig;
+  }
+
+  ScopedSECItem ImportPrivateKeyAndSignHashedData(const uint8_t* pkcs8,
+                                                  size_t pkcs8_len,
+                                                  const uint8_t* data,
+                                                  size_t data_len) {
+    ScopedSECKEYPrivateKey privKey(ImportPrivateKey(pkcs8, pkcs8_len));
+    if (!privKey) {
+      return nullptr;
+    }
+
+    ScopedSECItem hash(ComputeHash(data, data_len));
+    if (!hash) {
+      return nullptr;
+    }
+
+    return ScopedSECItem(SignHashedData(privKey, hash));
+  }
+
+  void Verify(const uint8_t* spki, size_t spki_len, const uint8_t* data,
+              size_t data_len, const uint8_t* sig, size_t sig_len) {
+    ScopedSECKEYPublicKey pubKey(ImportPublicKey(spki, spki_len));
+    ASSERT_TRUE(pubKey);
+
+    ScopedSECItem hash(ComputeHash(data, data_len));
+    ASSERT_TRUE(hash);
+
+    SECItem sigItem = {siBuffer, toUcharPtr(sig),
+                       static_cast<unsigned int>(sig_len)};
+
+    // Verify.
+    SECStatus rv = PK11_VerifyWithMechanism(
+        pubKey.get(), mechanism(), parameters(), &sigItem, hash.get(), nullptr);
+    EXPECT_EQ(rv, SECSuccess);
+  }
+
+  void SignAndVerify(const uint8_t* pkcs8, size_t pkcs8_len,
+                     const uint8_t* spki, size_t spki_len, const uint8_t* data,
+                     size_t data_len) {
+    ScopedSECItem sig(
+        ImportPrivateKeyAndSignHashedData(pkcs8, pkcs8_len, data, data_len));
+    ASSERT_TRUE(sig);
+
+    Verify(spki, spki_len, data, data_len, sig->data, sig->len);
+  }
+};
+
+#define SIG_TEST_VECTOR_VERIFY(spki, data, sig) \
+  Verify(spki, sizeof(spki), data, sizeof(data), sig, sizeof(sig));
+
+#define SIG_TEST_VECTOR_SIGN_VERIFY(pkcs8, spki, data) \
+  SignAndVerify(pkcs8, sizeof(pkcs8), spki, sizeof(spki), data, sizeof(data));
+
+}  // namespace nss_test
--- a/security/nss/lib/cryptohi/seckey.c
+++ b/security/nss/lib/cryptohi/seckey.c
@@ -1237,16 +1237,32 @@ SECKEY_ConvertToPublicKey(SECKEYPrivateK
             if (rv != SECSuccess)
                 break;
             rv = PK11_ReadAttribute(privk->pkcs11Slot, privk->pkcs11ID,
                                     CKA_PUBLIC_EXPONENT, arena, &pubk->u.rsa.publicExponent);
             if (rv != SECSuccess)
                 break;
             return pubk;
             break;
+        case ecKey:
+            rv = PK11_ReadAttribute(privk->pkcs11Slot, privk->pkcs11ID,
+                                    CKA_EC_PARAMS, arena, &pubk->u.ec.DEREncodedParams);
+            if (rv != SECSuccess) {
+                break;
+            }
+            rv = PK11_ReadAttribute(privk->pkcs11Slot, privk->pkcs11ID,
+                                    CKA_EC_POINT, arena, &pubk->u.ec.publicValue);
+            if (rv != SECSuccess || pubk->u.ec.publicValue.len == 0) {
+                break;
+            }
+            rv = seckey_SetPointEncoding(arena, pubk);
+            if (rv != SECSuccess) {
+                break;
+            }
+            return pubk;
         default:
             break;
     }
 
     PORT_FreeArena(arena, PR_FALSE);
     return NULL;
 }
 
--- a/security/nss/lib/pk11wrap/pk11pk12.c
+++ b/security/nss/lib/pk11wrap/pk11pk12.c
@@ -60,25 +60,39 @@ struct SECKEYDHPrivateKeyStr {
     PLArenaPool *arena;
     SECItem prime;
     SECItem base;
     SECItem privateValue;
 };
 typedef struct SECKEYDHPrivateKeyStr SECKEYDHPrivateKey;
 
 /*
+** Elliptic Curve Private Key structures
+** <https://tools.ietf.org/html/rfc5915#section-3>
+*/
+struct SECKEYECPrivateKeyStr {
+    PLArenaPool *arena;
+    SECItem version;
+    SECItem curveOID;    /* optional/ignored */
+    SECItem publicValue; /* required (for now) */
+    SECItem privateValue;
+};
+typedef struct SECKEYECPrivateKeyStr SECKEYECPrivateKey;
+
+/*
 ** raw private key object
 */
 struct SECKEYRawPrivateKeyStr {
     PLArenaPool *arena;
     KeyType keyType;
     union {
         SECKEYRSAPrivateKey rsa;
         SECKEYDSAPrivateKey dsa;
         SECKEYDHPrivateKey dh;
+        SECKEYECPrivateKey ec;
     } u;
 };
 typedef struct SECKEYRawPrivateKeyStr SECKEYRawPrivateKey;
 
 SEC_ASN1_MKSUB(SEC_AnyTemplate)
 SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
 
 /* ASN1 Templates for new decoder/encoder */
@@ -134,16 +148,43 @@ const SEC_ASN1Template SECKEY_DSAPrivate
 };
 
 const SEC_ASN1Template SECKEY_DHPrivateKeyExportTemplate[] = {
     { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.dh.privateValue) },
     { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.dh.base) },
     { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.dh.prime) },
 };
 
+#ifndef NSS_DISABLE_ECC
+SEC_ASN1_MKSUB(SEC_BitStringTemplate)
+SEC_ASN1_MKSUB(SEC_ObjectIDTemplate)
+
+const SEC_ASN1Template SECKEY_ECPrivateKeyExportTemplate[] = {
+    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYRawPrivateKey) },
+    { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.ec.version) },
+    { SEC_ASN1_OCTET_STRING,
+      offsetof(SECKEYRawPrivateKey, u.ec.privateValue) },
+    /* This value will always be ignored. u.ec.curveOID will always be
+     * overriden with the outer AlgorithmID.parameters. */
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+          SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC |
+          SEC_ASN1_XTRN | 0,
+      offsetof(SECKEYRawPrivateKey, u.ec.curveOID),
+      SEC_ASN1_SUB(SEC_ObjectIDTemplate) },
+    /* The public value is optional per RFC, but required in NSS. We
+     * can't do scalar mult on ECs to get a raw point with PK11 APIs. */
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+          SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC |
+          SEC_ASN1_XTRN | 1,
+      offsetof(SECKEYRawPrivateKey, u.ec.publicValue),
+      SEC_ASN1_SUB(SEC_BitStringTemplate) },
+    { 0 }
+};
+#endif /* NSS_DISABLE_ECC */
+
 const SEC_ASN1Template SECKEY_EncryptedPrivateKeyInfoTemplate[] = {
     { SEC_ASN1_SEQUENCE,
       0, NULL, sizeof(SECKEYEncryptedPrivateKeyInfo) },
     { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
       offsetof(SECKEYEncryptedPrivateKeyInfo, algorithm),
       SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
     { SEC_ASN1_OCTET_STRING,
       offsetof(SECKEYEncryptedPrivateKeyInfo, encryptedData) },
@@ -193,16 +234,25 @@ prepare_dsa_priv_key_export_for_asn1(SEC
 static void
 prepare_dh_priv_key_export_for_asn1(SECKEYRawPrivateKey *key)
 {
     key->u.dh.privateValue.type = siUnsignedInteger;
     key->u.dh.prime.type = siUnsignedInteger;
     key->u.dh.base.type = siUnsignedInteger;
 }
 
+static void
+prepare_ec_priv_key_export_for_asn1(SECKEYRawPrivateKey *key)
+{
+    key->u.ec.version.type = siUnsignedInteger;
+    key->u.ec.curveOID.type = siUnsignedInteger;
+    key->u.ec.privateValue.type = siUnsignedInteger;
+    key->u.ec.publicValue.type = siUnsignedInteger;
+}
+
 SECStatus
 PK11_ImportDERPrivateKeyInfo(PK11SlotInfo *slot, SECItem *derPKI,
                              SECItem *nickname, SECItem *publicValue, PRBool isPerm,
                              PRBool isPrivate, unsigned int keyUsage, void *wincx)
 {
     return PK11_ImportDERPrivateKeyInfoAndReturnKey(slot, derPKI,
                                                     nickname, publicValue,
                                                     isPerm, isPrivate, keyUsage,
@@ -427,17 +477,60 @@ PK11_ImportAndReturnPrivateKey(PK11SlotI
             attrs++;
             PK11_SETATTRS(attrs, CKA_BASE, lpk->u.dh.base.data,
                           lpk->u.dh.base.len);
             attrs++;
             PK11_SETATTRS(attrs, CKA_VALUE, lpk->u.dh.privateValue.data,
                           lpk->u.dh.privateValue.len);
             attrs++;
             break;
-        /* what about fortezza??? */
+#ifndef NSS_DISABLE_ECC
+        case ecKey:
+            keyType = CKK_EC;
+            if (lpk->u.ec.publicValue.len == 0) {
+                goto loser;
+            }
+            if (PK11_IsInternal(slot)) {
+                PK11_SETATTRS(attrs, CKA_NETSCAPE_DB,
+                              lpk->u.ec.publicValue.data,
+                              lpk->u.ec.publicValue.len);
+                attrs++;
+            }
+            PK11_SETATTRS(attrs, CKA_SIGN, (keyUsage & KU_DIGITAL_SIGNATURE) ? &cktrue
+                                                                             : &ckfalse,
+                          sizeof(CK_BBOOL));
+            attrs++;
+            PK11_SETATTRS(attrs, CKA_SIGN_RECOVER,
+                          (keyUsage & KU_DIGITAL_SIGNATURE) ? &cktrue
+                                                            : &ckfalse,
+                          sizeof(CK_BBOOL));
+            attrs++;
+            PK11_SETATTRS(attrs, CKA_DERIVE, (keyUsage & KU_KEY_AGREEMENT) ? &cktrue
+                                                                           : &ckfalse,
+                          sizeof(CK_BBOOL));
+            attrs++;
+            ck_id = PK11_MakeIDFromPubKey(&lpk->u.ec.publicValue);
+            if (ck_id == NULL) {
+                goto loser;
+            }
+            PK11_SETATTRS(attrs, CKA_ID, ck_id->data, ck_id->len);
+            attrs++;
+            signedattr = attrs;
+            /* curveOID always is a copy of AlgorithmID.parameters. */
+            PK11_SETATTRS(attrs, CKA_EC_PARAMS, lpk->u.ec.curveOID.data,
+                          lpk->u.ec.curveOID.len);
+            attrs++;
+            PK11_SETATTRS(attrs, CKA_VALUE, lpk->u.ec.privateValue.data,
+                          lpk->u.ec.privateValue.len);
+            attrs++;
+            PK11_SETATTRS(attrs, CKA_EC_POINT, lpk->u.ec.publicValue.data,
+                          lpk->u.ec.publicValue.len);
+            attrs++;
+            break;
+#endif /* NSS_DISABLE_ECC */
         default:
             PORT_SetError(SEC_ERROR_BAD_KEY);
             goto loser;
     }
     templateCount = attrs - theTemplate;
     PORT_Assert(templateCount <= sizeof(theTemplate) / sizeof(CK_ATTRIBUTE));
     PORT_Assert(signedattr != NULL);
     signedcount = attrs - signedattr;
@@ -508,33 +601,57 @@ PK11_ImportPrivateKeyInfoAndReturnKey(PK
                 goto loser;
             }
             prepare_dh_priv_key_export_for_asn1(lpk);
             keyTemplate = SECKEY_DHPrivateKeyExportTemplate;
             paramTemplate = NULL;
             paramDest = NULL;
             lpk->keyType = dhKey;
             break;
+#ifndef NSS_DISABLE_ECC
+        case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
+            prepare_ec_priv_key_export_for_asn1(lpk);
+            keyTemplate = SECKEY_ECPrivateKeyExportTemplate;
+            paramTemplate = NULL;
+            paramDest = NULL;
+            lpk->keyType = ecKey;
+            break;
+#endif /* NSS_DISABLE_ECC */
 
         default:
             keyTemplate = NULL;
             paramTemplate = NULL;
             paramDest = NULL;
             break;
     }
 
     if (!keyTemplate) {
         goto loser;
     }
 
     /* decode the private key and any algorithm parameters */
-    rv = SEC_ASN1DecodeItem(arena, lpk, keyTemplate, &pki->privateKey);
+    rv = SEC_QuickDERDecodeItem(arena, lpk, keyTemplate, &pki->privateKey);
     if (rv != SECSuccess) {
         goto loser;
     }
+
+#ifndef NSS_DISABLE_ECC
+    if (lpk->keyType == ecKey) {
+        /* Convert length in bits to length in bytes. */
+        lpk->u.ec.publicValue.len >>= 3;
+
+        /* Always override curveOID, we're ignoring any given value. */
+        rv = SECITEM_CopyItem(arena, &lpk->u.ec.curveOID,
+                              &pki->algorithm.parameters);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
+    }
+#endif /* NSS_DISABLE_ECC */
+
     if (paramDest && paramTemplate) {
         rv = SEC_ASN1DecodeItem(arena, paramDest, paramTemplate,
                                 &(pki->algorithm.parameters));
         if (rv != SECSuccess) {
             goto loser;
         }
     }