bug 1083118 - backout removal of unsafe, non-standardized legacy window.crypto functions r=bz a=lmandel ba=lmandel
authorDavid Keeler <dkeeler@mozilla.com>
Wed, 05 Nov 2014 09:37:25 -0800
changeset 226018 fea4ac1165f9
parent 226017 1e8f0a8c4474
child 226019 87fd4f56cfed
push id4108
push userdkeeler@mozilla.com
push date2014-11-10 21:44 +0000
treeherdermozilla-beta@87fd4f56cfed [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, lmandel
bugs1083118
milestone34.0
bug 1083118 - backout removal of unsafe, non-standardized legacy window.crypto functions r=bz a=lmandel ba=lmandel
b2g/confvars.sh
configure.in
content/base/test/csp/file_CSP_evalscript_main_allowed_getCRMFRequest.html
content/base/test/csp/file_CSP_evalscript_main_allowed_getCRMFRequest.html^headers^
content/base/test/csp/file_CSP_evalscript_main_allowed_getCRMFRequest.js
content/base/test/csp/file_CSP_evalscript_main_getCRMFRequest.html
content/base/test/csp/file_CSP_evalscript_main_getCRMFRequest.html^headers^
content/base/test/csp/file_CSP_evalscript_main_getCRMFRequest.js
content/base/test/csp/file_CSP_evalscript_no_CSP_at_all.html
content/base/test/csp/file_CSP_evalscript_no_CSP_at_all.html^headers^
content/base/test/csp/file_CSP_evalscript_no_CSP_at_all.js
content/base/test/csp/mochitest.ini
content/base/test/csp/test_CSP_evalscript_getCRMFRequest.html
dom/base/Crypto.cpp
dom/base/Crypto.h
dom/base/nsGlobalWindow.cpp
dom/bindings/Bindings.conf
dom/events/test/test_all_synthetic_events.html
dom/events/test/test_eventctors.html
dom/interfaces/base/moz.build
dom/interfaces/base/nsIDOMCryptoLegacy.idl
dom/tests/mochitest/crypto/mochitest-legacy.ini
dom/tests/mochitest/crypto/mochitest.ini
dom/tests/mochitest/crypto/test_legacy.html
dom/tests/mochitest/crypto/test_no_legacy.html
dom/tests/mochitest/general/test_interfaces.html
dom/tests/moz.build
dom/webidl/CRMFObject.webidl
dom/webidl/Crypto.webidl
dom/webidl/SmartCardEvent.webidl
dom/webidl/moz.build
mobile/android/confvars.sh
security/certverifier/CertVerifier.cpp
security/manager/pki/resources/content/certManager.js
security/manager/pki/resources/content/certManager.xul
security/manager/pki/resources/content/device_manager.js
security/manager/pki/resources/content/device_manager.xul
security/manager/pki/resources/content/formsigning.js
security/manager/pki/resources/content/formsigning.xul
security/manager/pki/resources/jar.mn
security/manager/pki/src/moz.build
security/manager/pki/src/nsFormSigningDialog.cpp
security/manager/pki/src/nsFormSigningDialog.h
security/manager/pki/src/nsPKIModule.cpp
security/manager/ssl/crashtests/327524-1.html
security/manager/ssl/crashtests/crashtests.list
security/manager/ssl/public/moz.build
security/manager/ssl/public/nsIFormSigningDialog.idl
security/manager/ssl/src/moz.build
security/manager/ssl/src/nsCrypto.cpp
security/manager/ssl/src/nsCrypto.h
security/manager/ssl/src/nsNSSComponent.cpp
security/manager/ssl/src/nsNSSComponent.h
security/manager/ssl/src/nsNSSModule.cpp
security/manager/ssl/src/nsSmartCardMonitor.cpp
security/manager/ssl/tests/mochitest/bugs/mochitest-legacy.ini
security/manager/ssl/tests/mochitest/bugs/moz.build
security/manager/ssl/tests/mochitest/bugs/test_generateCRMFRequest.html
security/manager/ssl/tests/moz.build
security/manager/ssl/tests/unit/moz.build
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -18,17 +18,17 @@ MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/bran
 # MOZ_APP_DISPLAYNAME is set by branding/configure.sh
 
 MOZ_SAFE_BROWSING=
 MOZ_SERVICES_COMMON=1
 MOZ_SERVICES_METRICS=1
 MOZ_CAPTIVEDETECT=1
 
 MOZ_WEBSMS_BACKEND=1
-MOZ_NO_SMART_CARDS=1
+MOZ_DISABLE_CRYPTOLEGACY=1
 MOZ_APP_STATIC_INI=1
 NSS_NO_LIBPKIX=1
 NSS_DISABLE_DBM=1
 MOZ_NO_EV_CERTS=1
 MOZ_DISABLE_EXPORT_JS=1
 
 if test "$OS_TARGET" = "Android"; then
 MOZ_CAPTURE=1
--- a/configure.in
+++ b/configure.in
@@ -3813,17 +3813,17 @@ MOZ_ANDROID_APZ=
 MOZ_TOOLKIT_SEARCH=1
 MOZ_UI_LOCALE=en-US
 MOZ_UNIVERSALCHARDET=1
 MOZ_URL_CLASSIFIER=
 MOZ_XUL=1
 MOZ_ZIPWRITER=1
 NS_PRINTING=1
 MOZ_PDF_PRINTING=
-MOZ_NO_SMART_CARDS=
+MOZ_DISABLE_CRYPTOLEGACY=
 NSS_DISABLE_DBM=
 NECKO_COOKIES=1
 NECKO_PROTOCOLS_DEFAULT="about app data file ftp http res viewsource websocket wyciwyg device"
 if test -n "$MOZ_RTSP"; then
   NECKO_PROTOCOLS_DEFAULT="$NECKO_PROTOCOLS_DEFAULT rtsp"
 fi
 USE_ARM_KUSER=
 BUILD_CTYPES=1
@@ -6386,22 +6386,22 @@ MOZ_ARG_DISABLE_BOOL(parental-controls,
    MOZ_DISABLE_PARENTAL_CONTROLS=)
 if test -n "$MOZ_DISABLE_PARENTAL_CONTROLS"; then
     AC_DEFINE(MOZ_DISABLE_PARENTAL_CONTROLS)
 fi
 
 AC_SUBST(MOZ_DISABLE_PARENTAL_CONTROLS)
 
 dnl ========================================================
-dnl = Disable smartcard support
-dnl ========================================================
-if test -n "$MOZ_NO_SMART_CARDS"; then
-    AC_DEFINE(MOZ_NO_SMART_CARDS)
-fi
-AC_SUBST(MOZ_NO_SMART_CARDS)
+dnl = Disable DOMCrypto
+dnl ========================================================
+if test -n "$MOZ_DISABLE_CRYPTOLEGACY"; then
+    AC_DEFINE(MOZ_DISABLE_CRYPTOLEGACY)
+fi
+AC_SUBST(MOZ_DISABLE_CRYPTOLEGACY)
 
 dnl ========================================================
 dnl = Disable EV certificate verification
 dnl ========================================================
 if test -n "$MOZ_NO_EV_CERTS"; then
     AC_DEFINE(MOZ_NO_EV_CERTS)
 fi
 AC_SUBST(MOZ_NO_EV_CERTS)
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_CSP_evalscript_main_allowed_getCRMFRequest.html
@@ -0,0 +1,12 @@
+<html>
+  <head>
+    <title>CSP eval script tests</title>
+    <script type="application/javascript"
+             src="file_CSP_evalscript_main_allowed_getCRMFRequest.js"></script>
+  </head>
+  <body>
+
+    Foo.
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_CSP_evalscript_main_allowed_getCRMFRequest.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+Content-Security-Policy: default-src 'self' ; script-src 'self' 'unsafe-eval'
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_CSP_evalscript_main_allowed_getCRMFRequest.js
@@ -0,0 +1,42 @@
+// some javascript for the CSP eval() tests
+// all of these evals should succeed, as the document loading this script
+// has script-src 'self' 'unsafe-eval'
+
+function logResult(str, passed) {
+  var elt = document.createElement('div');
+  var color = passed ? "#cfc;" : "#fcc";
+  elt.setAttribute('style', 'background-color:' + color + '; width:100%; border:1px solid black; padding:3px; margin:4px;');
+  elt.innerHTML = str;
+  document.body.appendChild(elt);
+}
+
+// callback for when stuff is allowed by CSP
+var onevalexecuted = (function(window) {
+    return function(shouldrun, what, data) {
+      window.parent.scriptRan(shouldrun, what, data);
+      logResult((shouldrun ? "PASS: " : "FAIL: ") + what + " : " + data, shouldrun);
+    };})(window);
+
+// callback for when stuff is blocked
+var onevalblocked = (function(window) {
+    return function(shouldrun, what, data) {
+      window.parent.scriptBlocked(shouldrun, what, data);
+      logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
+    };})(window);
+
+
+// Defer until document is loaded so that we can write the pretty result boxes
+// out.
+addEventListener('load', function() {
+  // test that allows crypto.generateCRMFRequest eval to run
+  try {
+      var script =
+        'console.log("dynamic script passed to crypto.generateCRMFRequest should execute")';
+      crypto.generateCRMFRequest('CN=0', 0, 0, null, script, 384, null, 'rsa-dual-use');
+      onevalexecuted(true, "eval(script) inside crypto.generateCRMFRequest",
+                     "eval executed during crypto.generateCRMFRequest");
+  } catch (e) {
+    onevalblocked(true, "eval(script) inside crypto.generateCRMFRequest",
+                  "eval was blocked during crypto.generateCRMFRequest");
+  }
+}, false);
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_CSP_evalscript_main_getCRMFRequest.html
@@ -0,0 +1,12 @@
+<html>
+  <head>
+    <title>CSP eval script tests</title>
+    <script type="application/javascript"
+             src="file_CSP_evalscript_main_getCRMFRequest.js"></script>
+  </head>
+  <body>
+
+    Foo.
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_CSP_evalscript_main_getCRMFRequest.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+Content-Security-Policy: default-src 'self'
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_CSP_evalscript_main_getCRMFRequest.js
@@ -0,0 +1,48 @@
+// some javascript for the CSP eval() tests
+
+function logResult(str, passed) {
+  var elt = document.createElement('div');
+  var color = passed ? "#cfc;" : "#fcc";
+  elt.setAttribute('style', 'background-color:' + color + '; width:100%; border:1px solid black; padding:3px; margin:4px;');
+  elt.innerHTML = str;
+  document.body.appendChild(elt);
+}
+
+window._testResults = {};
+
+// callback for when stuff is allowed by CSP
+var onevalexecuted = (function(window) {
+    return function(shouldrun, what, data) {
+      window._testResults[what] = "ran";
+      window.parent.scriptRan(shouldrun, what, data);
+      logResult((shouldrun ? "PASS: " : "FAIL: ") + what + " : " + data, shouldrun);
+    };})(window);
+
+// callback for when stuff is blocked
+var onevalblocked = (function(window) {
+    return function(shouldrun, what, data) {
+      window._testResults[what] = "blocked";
+      window.parent.scriptBlocked(shouldrun, what, data);
+      logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
+    };})(window);
+
+
+// Defer until document is loaded so that we can write the pretty result boxes
+// out.
+addEventListener('load', function() {
+  // generateCRMFRequest test -- make sure we cannot eval the callback if CSP is in effect
+  try {
+    var script = 'console.log("dynamic script eval\'d in crypto.generateCRMFRequest should be disallowed")';
+    crypto.generateCRMFRequest('CN=0', 0, 0, null, script, 384, null, 'rsa-dual-use');
+    onevalexecuted(false, "crypto.generateCRMFRequest()",
+                   "crypto.generateCRMFRequest() should not run!");
+  } catch (e) {
+    onevalblocked(false, "eval(script) inside crypto.generateCRMFRequest",
+                  "eval was blocked during crypto.generateCRMFRequest");
+  }
+
+
+}, false);
+
+
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_CSP_evalscript_no_CSP_at_all.html
@@ -0,0 +1,12 @@
+<html>
+  <head>
+    <title>CSP eval script tests: no CSP specified</title>
+    <script type="application/javascript"
+             src="file_CSP_evalscript_no_CSP_at_all.js"></script>
+  </head>
+  <body>
+
+    Foo. See bug 824652
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_CSP_evalscript_no_CSP_at_all.html^headers^
@@ -0,0 +1,1 @@
+Cache-Control: no-cache
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_CSP_evalscript_no_CSP_at_all.js
@@ -0,0 +1,42 @@
+// some javascript for the CSP eval() tests
+// all of these evals should succeed, as the document loading this script
+// has script-src 'self' 'unsafe-eval'
+
+function logResult(str, passed) {
+  var elt = document.createElement('div');
+  var color = passed ? "#cfc;" : "#fcc";
+  elt.setAttribute('style', 'background-color:' + color + '; width:100%; border:1px solid black; padding:3px; margin:4px;');
+  elt.innerHTML = str;
+  document.body.appendChild(elt);
+}
+
+// callback for when stuff is allowed by CSP
+var onevalexecuted = (function(window) {
+    return function(shouldrun, what, data) {
+      window.parent.scriptRan(shouldrun, what, data);
+      logResult((shouldrun ? "PASS: " : "FAIL: ") + what + " : " + data, shouldrun);
+    };})(window);
+
+// callback for when stuff is blocked
+var onevalblocked = (function(window) {
+    return function(shouldrun, what, data) {
+      window.parent.scriptBlocked(shouldrun, what, data);
+      logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
+    };})(window);
+
+
+// Defer until document is loaded so that we can write the pretty result boxes
+// out.
+addEventListener('load', function() {
+  // test that allows crypto.generateCRMFRequest eval to run when there is no CSP at all in place
+  try {
+      var script =
+        'console.log("dynamic script passed to crypto.generateCRMFRequest should execute")';
+      crypto.generateCRMFRequest('CN=0', 0, 0, null, script, 384, null, 'rsa-dual-use');
+      onevalexecuted(true, "eval(script) inside crypto.generateCRMFRequest: no CSP at all",
+                     "eval executed during crypto.generateCRMFRequest where no CSP is set at all");
+  } catch (e) {
+    onevalblocked(true, "eval(script) inside crypto.generateCRMFRequest",
+                  "eval was blocked during crypto.generateCRMFRequest");
+  }
+}, false);
--- a/content/base/test/csp/mochitest.ini
+++ b/content/base/test/csp/mochitest.ini
@@ -15,20 +15,29 @@ support-files =
   file_CSP_bug885433_allows.html
   file_CSP_bug885433_allows.html^headers^
   file_CSP_bug885433_blocks.html
   file_CSP_bug885433_blocks.html^headers^
   file_CSP_bug888172.html
   file_CSP_bug888172.sjs
   file_CSP_evalscript_main.js
   file_CSP_evalscript_main_allowed.js
+  file_CSP_evalscript_main_allowed_getCRMFRequest.js
+  file_CSP_evalscript_main_getCRMFRequest.js
   file_CSP_evalscript_main.html
   file_CSP_evalscript_main.html^headers^
   file_CSP_evalscript_main_allowed.html
   file_CSP_evalscript_main_allowed.html^headers^
+  file_CSP_evalscript_main_allowed_getCRMFRequest.html
+  file_CSP_evalscript_main_allowed_getCRMFRequest.html^headers^
+  file_CSP_evalscript_main_getCRMFRequest.html
+  file_CSP_evalscript_main_getCRMFRequest.html^headers^
+  file_CSP_evalscript_no_CSP_at_all.html
+  file_CSP_evalscript_no_CSP_at_all.html^headers^
+  file_CSP_evalscript_no_CSP_at_all.js
   file_CSP_frameancestors_main.html
   file_CSP_frameancestors_main.js
   file_CSP_frameancestors.sjs
   file_CSP_inlinescript_main.html
   file_CSP_inlinescript_main.html^headers^
   file_CSP_inlinescript_main_allowed.html
   file_CSP_inlinescript_main_allowed.html^headers^
   file_CSP_inlinestyle_main.html
@@ -98,16 +107,18 @@ support-files =
 
 [test_connect-src.html]
 [test_CSP.html]
 [test_CSP_bug663567.html]
 [test_CSP_bug802872.html]
 [test_CSP_bug885433.html]
 [test_CSP_bug888172.html]
 [test_CSP_evalscript.html]
+[test_CSP_evalscript_getCRMFRequest.html]
+skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # no (deprecated) window.crypto support in multiprocess (bug 824652)
 [test_CSP_frameancestors.html]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || toolkit == 'android' # Times out, not sure why (bug 1008445)
 [test_CSP_inlinescript.html]
 [test_CSP_inlinestyle.html]
 [test_bug836922_npolicies.html]
 [test_bug886164.html]
 [test_csp_redirects.html]
 [test_CSP_bug910139.html]
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/test_CSP_evalscript_getCRMFRequest.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Content Security Policy "no eval" in crypto.getCRMFRequest()</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<iframe style="width:100%;height:300px;" id='cspframe'></iframe>
+<iframe style="width:100%;height:300px;" id='cspframe2'></iframe>
+<iframe style="width:100%;height:300px;" id='cspframe3'></iframe>
+<script class="testbody" type="text/javascript">
+
+var path = "/tests/content/base/test/csp/";
+
+var evalScriptsThatRan = 0;
+var evalScriptsBlocked = 0;
+var evalScriptsTotal = 3;
+
+// called by scripts that run
+var scriptRan = function(shouldrun, testname, data) {
+  evalScriptsThatRan++;
+  ok(shouldrun, 'EVAL SCRIPT RAN: ' + testname + '(' + data + ')');
+  checkTestResults();
+}
+
+// called when a script is blocked
+var scriptBlocked = function(shouldrun, testname, data) {
+  evalScriptsBlocked++;
+  ok(!shouldrun, 'EVAL SCRIPT BLOCKED: ' + testname + '(' + data + ')');
+  checkTestResults();
+}
+
+// Check to see if all the tests have run
+var checkTestResults = function() {
+  // if any test is incomplete, keep waiting
+  if (evalScriptsTotal - evalScriptsBlocked - evalScriptsThatRan > 0)
+    return;
+
+  // ... otherwise, finish
+  SimpleTest.finish();
+}
+
+function loadElements() {
+  // save this for last so that our listeners are registered.
+  // ... this loads the testbed of good and bad requests.
+  document.getElementById('cspframe').src = 'file_CSP_evalscript_main_getCRMFRequest.html';
+  document.getElementById('cspframe2').src = 'file_CSP_evalscript_main_allowed_getCRMFRequest.html';
+  document.getElementById('cspframe3').src = 'file_CSP_evalscript_no_CSP_at_all.html';
+}
+
+//////////////////////////////////////////////////////////////////////
+// set up and go
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [["dom.unsafe_legacy_crypto.enabled", true]]},
+                          loadElements);
+</script>
+</pre>
+</body>
+</html>
--- a/dom/base/Crypto.cpp
+++ b/dom/base/Crypto.cpp
@@ -122,16 +122,92 @@ SubtleCrypto*
 Crypto::Subtle()
 {
   if(!mSubtle) {
     mSubtle = new SubtleCrypto(GetParentObject());
   }
   return mSubtle;
 }
 
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
+// Stub out the legacy nsIDOMCrypto methods. The actual
+// implementations are in security/manager/ssl/src/nsCrypto.{cpp,h}
+
+NS_IMETHODIMP
+Crypto::GetEnableSmartCardEvents(bool *aEnableSmartCardEvents)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+Crypto::SetEnableSmartCardEvents(bool aEnableSmartCardEvents)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+bool
+Crypto::EnableSmartCardEvents()
+{
+  return false;
+}
+
+void
+Crypto::SetEnableSmartCardEvents(bool aEnable, ErrorResult& aRv)
+{
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+}
+
+void
+Crypto::GetVersion(nsString& aVersion)
+{
+}
+
+mozilla::dom::CRMFObject*
+Crypto::GenerateCRMFRequest(JSContext* aContext,
+                            const nsCString& aReqDN,
+                            const nsCString& aRegToken,
+                            const nsCString& aAuthenticator,
+                            const nsCString& aEaCert,
+                            const nsCString& aJsCallback,
+                            const Sequence<JS::Value>& aArgs,
+                            ErrorResult& aRv)
+{
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+  return nullptr;
+}
+
+void
+Crypto::ImportUserCertificates(const nsAString& aNickname,
+                               const nsAString& aCmmfResponse,
+                               bool aDoForcedBackup,
+                               nsAString& aReturn,
+                               ErrorResult& aRv)
+{
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+}
+
+void
+Crypto::SignText(JSContext* aContext,
+                 const nsAString& aStringToSign,
+                 const nsAString& aCaOption,
+                 const Sequence<nsCString>& aArgs,
+                 nsAString& aReturn)
+
+{
+  aReturn.AssignLiteral("error:internalError");
+}
+
+void
+Crypto::Logout(ErrorResult& aRv)
+{
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+}
+
+#endif
+
 /* static */ uint8_t*
 Crypto::GetRandomValues(uint32_t aLength)
 {
   nsCOMPtr<nsIRandomGenerator> randomGenerator;
   nsresult rv;
   randomGenerator = do_GetService("@mozilla.org/security/random-generator;1");
   NS_ENSURE_TRUE(randomGenerator, nullptr);
 
--- a/dom/base/Crypto.h
+++ b/dom/base/Crypto.h
@@ -1,15 +1,25 @@
 /* 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 mozilla_dom_Crypto_h
 #define mozilla_dom_Crypto_h
 
+#ifdef MOZ_DISABLE_CRYPTOLEGACY
 #include "nsIDOMCrypto.h"
+#else
+#include "nsIDOMCryptoLegacy.h"
+namespace mozilla {
+namespace dom {
+class CRMFObject;
+}
+}
+#endif
+
 #include "mozilla/dom/SubtleCrypto.h"
 #include "nsPIDOMWindow.h"
 
 #include "nsWrapperCache.h"
 #include "mozilla/dom/TypedArray.h"
 #define NS_DOMCRYPTO_CID \
   {0x929d9320, 0x251e, 0x11d4, { 0x8a, 0x7c, 0x00, 0x60, 0x08, 0xc8, 0x44, 0xc3} }
 
@@ -36,16 +46,48 @@ public:
   void
   GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray,
 		  JS::MutableHandle<JSObject*> aRetval,
 		  ErrorResult& aRv);
 
   SubtleCrypto*
   Subtle();
 
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
+  virtual bool EnableSmartCardEvents();
+  virtual void SetEnableSmartCardEvents(bool aEnable, ErrorResult& aRv);
+
+  virtual void GetVersion(nsString& aVersion);
+
+  virtual mozilla::dom::CRMFObject*
+  GenerateCRMFRequest(JSContext* aContext,
+                      const nsCString& aReqDN,
+                      const nsCString& aRegToken,
+                      const nsCString& aAuthenticator,
+                      const nsCString& aEaCert,
+                      const nsCString& aJsCallback,
+                      const Sequence<JS::Value>& aArgs,
+                      ErrorResult& aRv);
+
+  virtual void ImportUserCertificates(const nsAString& aNickname,
+                                      const nsAString& aCmmfResponse,
+                                      bool aDoForcedBackup,
+                                      nsAString& aReturn,
+                                      ErrorResult& aRv);
+
+  virtual void SignText(JSContext* aContext,
+                        const nsAString& aStringToSign,
+                        const nsAString& aCaOption,
+                        const Sequence<nsCString>& aArgs,
+                        nsAString& aReturn);
+
+  virtual void Logout(ErrorResult& aRv);
+
+#endif
+
   // WebIDL
 
   nsPIDOMWindow*
   GetParentObject() const
   {
     return mWindow;
   }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -80,16 +80,19 @@
 #include "nsIWidgetListener.h"
 #include "nsIBaseWindow.h"
 #include "nsIDeviceSensors.h"
 #include "nsIContent.h"
 #include "nsIDocShell.h"
 #include "nsIDocCharset.h"
 #include "nsIDocument.h"
 #include "Crypto.h"
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
+#include "nsIDOMCryptoLegacy.h"
+#endif
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMOfflineResourceList.h"
 #include "nsDOMString.h"
 #include "nsIEmbeddingSiteWindow.h"
 #include "nsThreadUtils.h"
 #include "nsILoadContext.h"
@@ -2363,16 +2366,24 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       mDoc &&
       mDoc->NodePrincipal() != aDocument->NodePrincipal()) {
     NS_ERROR("Attempted forced inner window reuse while changing principal");
     return NS_ERROR_UNEXPECTED;
   }
 
   nsCOMPtr<nsIDocument> oldDoc = mDoc;
 
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
+  // clear smartcard events, our document has gone away.
+  if (mCrypto && XRE_GetProcessType() != GeckoProcessType_Content) {
+    nsresult rv = mCrypto->SetEnableSmartCardEvents(false);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+#endif
+
   AutoJSAPI jsapi;
   jsapi.Init();
   JSContext *cx = jsapi.cx();
 
   if (!mDoc) {
     // First document load.
 
     // Get our private root. If it is equal to us, then we need to
@@ -4449,17 +4460,30 @@ nsGlobalWindow::GetApplicationCache(nsID
 }
 
 nsIDOMCrypto*
 nsGlobalWindow::GetCrypto(ErrorResult& aError)
 {
   FORWARD_TO_INNER_OR_THROW(GetCrypto, (aError), aError, nullptr);
 
   if (!mCrypto) {
-    mCrypto = new Crypto();
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
+    if (XRE_GetProcessType() != GeckoProcessType_Content) {
+      nsresult rv;
+      mCrypto = do_CreateInstance(NS_CRYPTO_CONTRACTID, &rv);
+      if (NS_FAILED(rv)) {
+        aError.Throw(rv);
+        return nullptr;
+      }
+    } else
+#endif
+    {
+      mCrypto = new Crypto();
+    }
+
     mCrypto->Init(this);
   }
   return mCrypto;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetCrypto(nsIDOMCrypto** aCrypto)
 {
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -246,17 +246,24 @@ DOMInterfaces = {
     'implicitJSContext': [ 'buffer' ],
     'resultNotAddRefed': [ 'buffer' ],
 },
 
 'Coordinates': {
     'headerFile': 'nsGeoPosition.h'
 },
 
+'CRMFObject': {
+    'headerFile': 'nsCrypto.h',
+    'nativeOwnership': 'owned',
+    'wrapperCache': False,
+},
+
 'Crypto' : {
+    'implicitJSContext': [ 'generateCRMFRequest', 'signText' ],
     'headerFile': 'Crypto.h'
 },
 
 'CSS': {
     'concrete': False,
 },
 
 'CSS2Properties': {
--- a/dom/events/test/test_all_synthetic_events.html
+++ b/dom/events/test/test_all_synthetic_events.html
@@ -378,16 +378,20 @@ const kEventConstructors = {
                                                                                   aProps.clientX, aProps.clientY,
                                                                                   aProps.ctrlKey, aProps.altKey, aProps.shiftKey, aProps.metaKey,
                                                                                   aProps.button, aProps.relatedTarget,
                                                                                   aProps.allowedDirections, aProps.direction, aProps.delta || 0.0,
                                                                                   aProps.clickCount);
                                                          return e;
                                                        },
                                              },
+  SmartCardEvent:                            { create: function (aName, aProps) {
+                                                         return new SmartCardEvent(aName, aProps);
+                                                       },
+                                             },
   SpeechRecognitionEvent:                    { create: function (aName, aProps) {
                                                          return new SpeechRecognitionEvent(aName, aProps);
                                                        },
                                              },
   SpeechSynthesisEvent:                      { create: function (aName, aProps) {
                                                          return new SpeechSynthesisEvent(aName, aProps);
                                                        },
                                              },
--- a/dom/events/test/test_eventctors.html
+++ b/dom/events/test/test_eventctors.html
@@ -743,16 +743,39 @@ e = new PopupBlockedEvent("hello",
                           { requestingWindow: window,
                             popupWindowFeatures: "features",
                             popupWindowName: "name"
                           });
 is(e.requestingWindow, window);
 is(e.popupWindowFeatures, "features");
 is(e.popupWindowName, "name");
 
+
+// SmartCardEvent
+
+try {
+  e = new SmartCardEvent();
+} catch(exp) {
+  ex = true;
+}
+ok(ex, "SmartCardEvent: First parameter is required!");
+ex = false;
+
+e = new SmartCardEvent("hello");
+is(e.type, "hello", "SmartCardEvent: Wrong event type!");
+ok(!e.isTrusted, "SmartCardEvent: Event shouldn't be trusted!");
+ok(!e.bubbles, "SmartCardEvent: Event shouldn't bubble!");
+ok(!e.cancelable, "SmartCardEvent: Event shouldn't be cancelable!");
+is(e.tokenName, "");
+document.dispatchEvent(e);
+is(receivedEvent, e, "SmartCardEvent: Wrong event!");
+
+e = new SmartCardEvent("hello", { tokenName: "foo" });
+is(e.tokenName, "foo");
+
 // WheelEvent
 
 try {
   e = new WheelEvent();
 } catch(exp) {
   ex = true;
 }
 ok(ex, "WheelEvent: First parameter is required!");
--- a/dom/interfaces/base/moz.build
+++ b/dom/interfaces/base/moz.build
@@ -11,17 +11,16 @@ XPIDL_SOURCES += [
     'nsIContentPermissionPrompt.idl',
     'nsIContentPrefService.idl',
     'nsIContentPrefService2.idl',
     'nsIContentURIGrouper.idl',
     'nsIDOMChromeWindow.idl',
     'nsIDOMClientRect.idl',
     'nsIDOMClientRectList.idl',
     'nsIDOMConstructor.idl',
-    'nsIDOMCrypto.idl',
     'nsIDOMGlobalObjectConstructor.idl',
     'nsIDOMGlobalPropertyInitializer.idl',
     'nsIDOMHistory.idl',
     'nsIDOMJSWindow.idl',
     'nsIDOMLocation.idl',
     'nsIDOMModalContentWindow.idl',
     'nsIDOMNavigator.idl',
     'nsIDOMScreen.idl',
@@ -33,16 +32,25 @@ XPIDL_SOURCES += [
     'nsIIdleObserver.idl',
     'nsIQueryContentEventResult.idl',
     'nsIServiceWorkerManager.idl',
     'nsIStructuredCloneContainer.idl',
     'nsITabChild.idl',
     'nsITabParent.idl',
 ]
 
+if CONFIG['MOZ_DISABLE_CRYPTOLEGACY']:
+    XPIDL_SOURCES += [
+        'nsIDOMCrypto.idl',
+    ]
+else:
+    XPIDL_SOURCES += [
+        'nsIDOMCryptoLegacy.idl',
+    ]
+
 if CONFIG['MOZ_B2G']:
     XPIDL_SOURCES += [
         'nsIDOMWindowB2G.idl',
     ]
 
 if CONFIG['MOZ_WEBSPEECH']:
     XPIDL_SOURCES += [
         'nsISpeechSynthesisGetter.idl'
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/base/nsIDOMCryptoLegacy.idl
@@ -0,0 +1,15 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "domstubs.idl"
+
+interface nsIDOMWindow;
+
+[uuid(c25ecf08-3f46-4420-bee4-8505792fd63a)]
+interface nsIDOMCrypto : nsISupports
+{
+  [notxpcom] void init(in nsIDOMWindow window);
+  attribute boolean         enableSmartCardEvents;
+};
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/crypto/mochitest-legacy.ini
@@ -0,0 +1,2 @@
+[test_legacy.html]
+skip-if = e10s
--- a/dom/tests/mochitest/crypto/mochitest.ini
+++ b/dom/tests/mochitest/crypto/mochitest.ini
@@ -1,4 +1,5 @@
 [DEFAULT]
 skip-if = e10s
 
 [test_getRandomValues.html]
+[test_no_legacy.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/crypto/test_legacy.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test presence of legacy window.crypto features when
+         MOZ_DISABLE_CRYPTOLEGACY is NOT set and dom.unsafe_legacy_crypto.enabled is true</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+
+function test_unsafe_legacy_crypto_enabled() {
+  ok("crypto" in window, "crypto in window");
+  ok("version" in window.crypto, "version in window.crypto");
+  ok("enableSmartCardEvents" in window.crypto,
+     "enableSmartCardEvents in window.crypto");
+  ok("generateCRMFRequest" in window.crypto,
+     "generateCRMFRequest in window.crypto");
+  ok("importUserCertificates" in window.crypto,
+     "importUserCertificates in window.crypto");
+  ok("signText" in window.crypto, "signText in window.crypto");
+
+  function jsCallback () {
+  }
+
+  try {
+    window.crypto.generateCRMFRequest(null, null, null, null, jsCallback.toString());
+    ok(false, "window.crypto.generateCRMFRequest failed, should throw error");
+  } catch (e) {
+    ok(e.toString().search(/Failure/) > -1,
+       "Expected error: ReqDN cannot be null");
+  }
+
+  try {
+    window.crypto.generateCRMFRequest(document.documentElement, null, null, null,
+                                      null);
+    ok(false, "window.crypto.generateCRMFRequest failed, should throw error");
+  } catch (e) {
+    ok(e.toString().search(/Failure/) > -1,
+       "Expected error: jsCallback cannot be null");
+  }
+
+  try {
+    window.crypto.generateCRMFRequest(document.documentElement, null, null, null,
+                                      jsCallback.toString(), 1024);
+    ok(false, "window.crypto.generateCRMFRequest failed, should throw error");
+  } catch (e) {
+    ok(e.toString().search(/TypeError/) > -1,
+       "Expected error: Not enough arguments");
+  }
+
+  SimpleTest.finish();
+}
+
+SpecialPowers.pushPrefEnv({"set": [["dom.unsafe_legacy_crypto.enabled", true]]},
+                          test_unsafe_legacy_crypto_enabled);
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/crypto/test_no_legacy.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test lack of legacy window.crypto features when
+         MOZ_DISABLE_CRYPTOLEGACY is set or dom.unsafe_legacy_crypto.enabled is false</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+
+ok("crypto" in window, "crypto in window");
+ok(!("version" in window.crypto), "version not in window.crypto");
+ok(!("enableSmartCardEvents" in window.crypto),
+   "enableSmartCardEvents not in window.crypto");
+ok(!("generateCRMFRequest" in window.crypto),
+   "generateCRMFRequest not in window.crypto");
+ok(!("importUserCertificates" in window.crypto),
+   "importUserCertificates not in window.crypto");
+ok(!("signText" in window.crypto), "signText not in window.crypto");
+
+</script>
+</body></html>
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -859,16 +859,18 @@ var interfaceNamesInGlobalScope =
     {name: "ShadowRoot", pref: "dom.webcomponents.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SharedWorker", pref: "dom.workers.sharedWorkers.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SimpleGestureEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SimpleTest", xbl: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "SmartCardEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SpeechSynthesisEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SpeechSynthesis", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SpeechSynthesisUtterance", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SpeechSynthesisVoice", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/tests/moz.build
+++ b/dom/tests/moz.build
@@ -43,15 +43,20 @@ MOCHITEST_CHROME_MANIFESTS += [
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gtk2':
     # Bug 788164.
     MOCHITEST_MANIFESTS += [
         'mochitest/pointerlock/mochitest.ini',
     ]
 
+if not CONFIG['MOZ_DISABLE_CRYPTOLEGACY']:
+    MOCHITEST_MANIFESTS += [
+        'mochitest/crypto/mochitest-legacy.ini',
+    ]
+
 if CONFIG['MOZ_GAMEPAD']:
     MOCHITEST_MANIFESTS += [
         'mochitest/gamepad/mochitest.ini',
     ]
 
 XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
 BROWSER_CHROME_MANIFESTS += ['browser/browser.ini']
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CRMFObject.webidl
@@ -0,0 +1,10 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+[NoInterfaceObject]
+interface CRMFObject {
+  readonly attribute DOMString request;
+};
--- a/dom/webidl/Crypto.webidl
+++ b/dom/webidl/Crypto.webidl
@@ -14,8 +14,43 @@ interface RandomSource {
 };
 
 Crypto implements RandomSource;
 
 interface Crypto {
   [Pref="dom.webcrypto.enabled"]
   readonly attribute SubtleCrypto subtle;
 };
+
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
+[NoInterfaceObject]
+interface CryptoLegacy {
+  [Pref="dom.unsafe_legacy_crypto.enabled"]
+  readonly attribute DOMString version;
+
+  [SetterThrows,Pref="dom.unsafe_legacy_crypto.enabled"]
+  attribute boolean enableSmartCardEvents;
+
+  [Throws,NewObject,Pref="dom.unsafe_legacy_crypto.enabled"]
+  CRMFObject? generateCRMFRequest(ByteString? reqDN,
+                                  ByteString? regToken,
+                                  ByteString? authenticator,
+                                  ByteString? eaCert,
+                                  ByteString? jsCallback,
+                                  any... args);
+
+  [Throws,Pref="dom.unsafe_legacy_crypto.enabled"]
+  DOMString importUserCertificates(DOMString nickname,
+                                   DOMString cmmfResponse,
+                                   boolean doForcedBackup);
+
+  [Pref="dom.unsafe_legacy_crypto.enabled"]
+  DOMString signText(DOMString stringToSign,
+                     DOMString caOption,
+                     ByteString... args);
+
+  [Throws,Pref="dom.unsafe_legacy_crypto.enabled"]
+  void logout();
+};
+
+Crypto implements CryptoLegacy;
+#endif // !MOZ_DISABLE_CRYPTOLEGACY
+
new file mode 100644
--- /dev/null
+++ b/dom/webidl/SmartCardEvent.webidl
@@ -0,0 +1,16 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+[Constructor(DOMString type, optional SmartCardEventInit eventInitDict)]
+interface SmartCardEvent : Event
+{
+  readonly attribute DOMString? tokenName;
+};
+
+dictionary SmartCardEventInit : EventInit
+{
+  DOMString tokenName = "";
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -557,16 +557,17 @@ WEBIDL_FILES += [
     'HashChangeEvent.webidl',
     'MozApplicationEvent.webidl',
     'MozSettingsEvent.webidl',
     'PageTransitionEvent.webidl',
     'PopStateEvent.webidl',
     'PopupBlockedEvent.webidl',
     'ProgressEvent.webidl',
     'RecordErrorEvent.webidl',
+    'SmartCardEvent.webidl',
     'StyleRuleChangeEvent.webidl',
     'StyleSheetApplicableStateChangeEvent.webidl',
     'StyleSheetChangeEvent.webidl',
 ]
 
 # We only expose our prefable test interfaces in debug builds, just to be on
 # the safe side.
 if CONFIG['MOZ_DEBUG']:
@@ -631,16 +632,21 @@ else:
         'InstallTrigger.webidl',
     ]
 
 if CONFIG['MOZ_B2G_FM']:
     WEBIDL_FILES += [
         'FMRadio.webidl',
     ]
 
+if not CONFIG['MOZ_DISABLE_CRYPTOLEGACY']:
+    WEBIDL_FILES += [
+        'CRMFObject.webidl',
+    ]
+
 GENERATED_EVENTS_WEBIDL_FILES = [
     'AutocompleteErrorEvent.webidl',
     'BlobEvent.webidl',
     'CallEvent.webidl',
     'CallGroupErrorEvent.webidl',
     'CFStateChangeEvent.webidl',
     'CloseEvent.webidl',
     'DataErrorEvent.webidl',
@@ -673,16 +679,17 @@ GENERATED_EVENTS_WEBIDL_FILES = [
     'PopupBlockedEvent.webidl',
     'ProgressEvent.webidl',
     'RecordErrorEvent.webidl',
     'RTCDataChannelEvent.webidl',
     'RTCPeerConnectionIceEvent.webidl',
     'RTCPeerConnectionIdentityErrorEvent.webidl',
     'RTCPeerConnectionIdentityEvent.webidl',
     'SelectionChangeEvent.webidl',
+    'SmartCardEvent.webidl',
     'StyleRuleChangeEvent.webidl',
     'StyleSheetApplicableStateChangeEvent.webidl',
     'StyleSheetChangeEvent.webidl',
     'TrackEvent.webidl',
     'UDPMessageEvent.webidl',
     'UserProximityEvent.webidl',
     'USSDReceivedEvent.webidl',
 ]
--- a/mobile/android/confvars.sh
+++ b/mobile/android/confvars.sh
@@ -13,17 +13,17 @@ MOZ_OFFICIAL_BRANDING_DIRECTORY=mobile/a
 # MOZ_APP_DISPLAYNAME is set by branding/configure.sh
 
 # We support Android SDK version 9 and up by default.
 # See the --enable-android-min-sdk and --enable-android-max-sdk arguments in configure.in.
 MOZ_ANDROID_MIN_SDK_VERSION=9
 
 MOZ_SAFE_BROWSING=1
 
-MOZ_NO_SMART_CARDS=1
+MOZ_DISABLE_CRYPTOLEGACY=1
 
 # Enable getUserMedia
 MOZ_MEDIA_NAVIGATOR=1
 
 # Enable NFC permission
 MOZ_ANDROID_BEAM=1
 
 if test "$LIBXUL_SDK"; then
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -367,17 +367,18 @@ CertVerifier::VerifyCert(CERTCertificate
                           KeyPurposeId::id_kp_codeSigning,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
       break;
     }
 
     case certificateUsageVerifyCA:
     case certificateUsageStatusResponder: {
       // XXX This is a pretty useless way to verify a certificate. It is used
-      // by the certificate viewer UI. Because we don't know what trust bit is
+      // by the implementation of window.crypto.importCertificates and in the
+      // certificate viewer UI. Because we don't know what trust bit is
       // interesting, we just try them all.
       mozilla::pkix::EndEntityOrCA endEntityOrCA;
       mozilla::pkix::KeyUsage keyUsage;
       KeyPurposeId eku;
       if (usage == certificateUsageVerifyCA) {
         endEntityOrCA = EndEntityOrCA::MustBeCA;
         keyUsage = KeyUsage::keyCertSign;
         eku = KeyPurposeId::anyExtendedKeyUsage;
--- a/security/manager/pki/resources/content/certManager.js
+++ b/security/manager/pki/resources/content/certManager.js
@@ -27,32 +27,21 @@ var selected_index = [];
 var certdb;
 
 var caTreeView;
 var serverTreeView;
 var emailTreeView;
 var userTreeView;
 var orphanTreeView;
 
-var smartCardObserver = {
-  observe: function() {
-    onSmartCardChange();
-  }
-};
-
-function DeregisterSmartCardObservers()
-{
-  Services.obs.removeObserver(smartCardObserver, "smartcard-insert");
-  Services.obs.removeObserver(smartCardObserver, "smartcard-remove");
-}
-
 function LoadCerts()
 {
-  Services.obs.addObserver(smartCardObserver, "smartcard-insert", false);
-  Services.obs.addObserver(smartCardObserver, "smartcard-remove", false);
+  window.crypto.enableSmartCardEvents = true;
+  document.addEventListener("smartcard-insert", onSmartCardChange, false);
+  document.addEventListener("smartcard-remove", onSmartCardChange, false);
 
   certdb = Components.classes[nsX509CertDB].getService(nsIX509CertDB);
   var certcache = Components.classes[nsNSSCertCache].createInstance(nsINSSCertCache);
   
   certcache.cacheAllCerts();
 
   caTreeView = Components.classes[nsCertTree]
                     .createInstance(nsICertTree);
@@ -619,8 +608,9 @@ function addException()
                     'chrome,centerscreen,modal');
   var certcache = Components.classes[nsNSSCertCache].createInstance(nsINSSCertCache);
   certcache.cacheAllCerts();
   serverTreeView.loadCertsFromCache(certcache, nsIX509Cert.SERVER_CERT);
   serverTreeView.selection.clearSelection();
   orphanTreeView.loadCertsFromCache(certcache, nsIX509Cert.UNKNOWN_CERT);
   orphanTreeView.selection.clearSelection();
 }
+
--- a/security/manager/pki/resources/content/certManager.xul
+++ b/security/manager/pki/resources/content/certManager.xul
@@ -13,17 +13,16 @@
 
 <!DOCTYPE dialog SYSTEM "chrome://pippki/locale/certManager.dtd">
 
 <dialog id="certmanager" 
 	windowtype="mozilla:certmanager"
 	xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 
         title="&certmgr.title;"
         onload="LoadCerts();"
-        onunload="DeregisterSmartCardObservers();"
         buttons="accept"
         style="width: 48em; height: 32em;"
         persist="screenX screenY width height">
 
   <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
 
   <script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
   <script type="application/javascript" src="chrome://pippki/content/certManager.js"/>
--- a/security/manager/pki/resources/content/device_manager.js
+++ b/security/manager/pki/resources/content/device_manager.js
@@ -11,41 +11,28 @@ const nsIPKCS11ModuleDB = Components.int
 const nsIPK11Token = Components.interfaces.nsIPK11Token;
 const nsPK11TokenDB = "@mozilla.org/security/pk11tokendb;1";
 const nsIPK11TokenDB = Components.interfaces.nsIPK11TokenDB;
 const nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock;
 const nsDialogParamBlock = "@mozilla.org/embedcomp/dialogparam;1";
 const nsIPKCS11 = Components.interfaces.nsIPKCS11;
 const nsPKCS11ContractID = "@mozilla.org/security/pkcs11;1";
 
-let { Services } = Components.utils.import("resource://gre/modules/Services.jsm", {});
-
 var bundle;
 var secmoddb;
 var skip_enable_buttons = false;
 
-var smartCardObserver = {
-  observe: function() {
-    onSmartCardChange();
-  }
-};
-
-function DeregisterSmartCardObservers()
-{
-  Services.obs.removeObserver(smartCardObserver, "smartcard-insert");
-  Services.obs.removeObserver(smartCardObserver, "smartcard-remove");
-}
-
 /* Do the initial load of all PKCS# modules and list them. */
 function LoadModules()
 {
   bundle = document.getElementById("pippki_bundle");
   secmoddb = Components.classes[nsPKCS11ModuleDB].getService(nsIPKCS11ModuleDB);
-  Services.obs.addObserver(smartCardObserver, "smartcard-insert", false);
-  Services.obs.addObserver(smartCardObserver, "smartcard-remove", false);
+  window.crypto.enableSmartCardEvents = true;
+  document.addEventListener("smartcard-insert", onSmartCardChange, false);
+  document.addEventListener("smartcard-remove", onSmartCardChange, false);
 
   RefreshDeviceList();
 }
 
 function getPKCS11()
 {
   return Components.classes[nsPKCS11ContractID].getService(nsIPKCS11);
 }
--- a/security/manager/pki/resources/content/device_manager.xul
+++ b/security/manager/pki/resources/content/device_manager.xul
@@ -14,17 +14,16 @@
 
 <dialog id="devicemanager"
 	windowtype="mozilla:devicemanager"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 
         title="&devmgr.title;"
         style="&devmgr.style;"
         persist="screenX screenY width height"
         onload="LoadModules();"
-        onunload="DeregisterSmartCardObservers();"
         buttons="accept">
 
 <stringbundleset id="stringbundleset">
   <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
   <stringbundle id="pipnss_bundle" src="chrome://pipnss/locale/pipnss.properties"/>
 </stringbundleset>
 
 <script type="application/javascript" src="chrome://pippki/content/device_manager.js"/>
new file mode 100644
--- /dev/null
+++ b/security/manager/pki/resources/content/formsigning.js
@@ -0,0 +1,67 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+
+const nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock;
+
+var dialogParams;
+var itemCount = 0;
+
+function onLoad()
+{
+  dialogParams = window.arguments[0].QueryInterface(nsIDialogParamBlock);
+
+  var hostname = dialogParams.GetString(0);
+  var bundle = document.getElementById("pippki_bundle");
+  var intro = "The site '" + hostname + "' has requested that you sign the following text message:";
+  setText("sign.intro", intro);
+
+  document.getElementById("sign.text").value = dialogParams.GetString(1);
+
+  var selectElement = document.getElementById("nicknames");
+  itemCount = dialogParams.GetInt(0);
+  for (var index = 0; index < itemCount; ++index) {
+    var menuItemNode = document.createElement("menuitem");
+    var nick = dialogParams.GetString(2 + 2 * index);
+    menuItemNode.setAttribute("value", index);
+    menuItemNode.setAttribute("label", nick); // this is displayed
+    selectElement.firstChild.appendChild(menuItemNode);
+    if (index == 0) {
+      selectElement.selectedItem = menuItemNode;
+    }
+  }
+  setDetails();
+  document.getElementById("pw").focus();
+}
+
+function setDetails()
+{
+  var index = parseInt(document.getElementById("nicknames").value);
+  if (index == "NaN")
+    return;
+  
+  var details = dialogParams.GetString(2 + (2 * index + 1));
+  document.getElementById("certdetails").value = details;
+}
+
+function onCertSelected()
+{
+  setDetails();
+}
+
+function doOK()
+{
+  dialogParams.SetInt(0, 1);
+  var index = parseInt(document.getElementById("nicknames").value);
+  dialogParams.SetInt(1, index);
+  var password = document.getElementById("pw").value;
+  dialogParams.SetString(0, password);
+  return true;
+}
+
+function doCancel()
+{
+  dialogParams.SetInt(0, 0);
+  return true;
+}
new file mode 100644
--- /dev/null
+++ b/security/manager/pki/resources/content/formsigning.xul
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % pippkiDTD SYSTEM "chrome://pippki/locale/pippki.dtd" >
+%pippkiDTD;
+]>
+
+<dialog id="formsigning" title="Text Signing Request"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+  buttons="accept,cancel"
+  ondialogaccept="return doOK();"
+  ondialogcancel="return doCancel();"
+  onload="onLoad();">
+
+<stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
+
+<script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
+<script type="application/javascript" src="chrome://pippki/content/formsigning.js"/>
+
+  <description id="sign.intro" style="max-width: 50em;"/>
+  <textbox readonly="true" id="sign.text" multiline="true"
+    style="height: 10em;" wrap="off"/>
+  <separator class="thin"/>
+  <groupbox>
+    <caption label="Signing Certificate"/>
+    <broadcaster id="certSelected" oncommand="onCertSelected();"/>
+    <menulist id="nicknames" observes="certSelected">
+      <!-- The items in this menulist must never be sorted,
+           but remain in the order filled by the application
+      -->
+      <menupopup/>
+    </menulist>
+    <textbox readonly="true" id="certdetails" multiline="true"
+      style="height: 10em;" wrap="off"/>
+    <separator/>
+  </groupbox>
+  <separator class="thin"/>
+  <description style="max-width: 30em;">To confirm you agree to sign this text message using your selected certificate, please confirm by entering the master password:</description>
+  <textbox id="pw" type="password"/>
+</dialog>
--- a/security/manager/pki/resources/jar.mn
+++ b/security/manager/pki/resources/jar.mn
@@ -42,8 +42,10 @@ pippki.jar:
     content/pippki/choosetoken.xul           (content/choosetoken.xul)
     content/pippki/choosetoken.js            (content/choosetoken.js)
     content/pippki/escrowWarn.xul            (content/escrowWarn.xul)
     content/pippki/escrowWarn.js             (content/escrowWarn.js)
     content/pippki/createCertInfo.xul        (content/createCertInfo.xul)
     content/pippki/createCertInfo.js         (content/createCertInfo.js)
     content/pippki/protectedAuth.xul         (content/protectedAuth.xul)
     content/pippki/protectedAuth.js          (content/protectedAuth.js)
+    content/pippki/formsigning.xul           (content/formsigning.xul)
+    content/pippki/formsigning.js            (content/formsigning.js)
--- a/security/manager/pki/src/moz.build
+++ b/security/manager/pki/src/moz.build
@@ -1,16 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 UNIFIED_SOURCES += [
     'nsASN1Tree.cpp',
+    'nsFormSigningDialog.cpp',
     'nsNSSDialogHelper.cpp',
     'nsNSSDialogs.cpp',
     'nsPKIModule.cpp',
     'nsPKIParamBlock.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
new file mode 100644
--- /dev/null
+++ b/security/manager/pki/src/nsFormSigningDialog.cpp
@@ -0,0 +1,91 @@
+/* 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 "nsFormSigningDialog.h"
+#include "nsNSSDialogHelper.h"
+#include "nsCOMPtr.h"
+#include "nsIDialogParamBlock.h"
+#include "nsIComponentManager.h"
+#include "nsIServiceManager.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsLiteralString.h"
+#include "nsXPIDLString.h"
+
+nsFormSigningDialog::nsFormSigningDialog()
+{
+}
+
+nsFormSigningDialog::~nsFormSigningDialog()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsFormSigningDialog, nsIFormSigningDialog)
+
+NS_IMETHODIMP
+nsFormSigningDialog::ConfirmSignText(nsIInterfaceRequestor *aContext, 
+                                     const nsAString &aHost,
+                                     const nsAString &aSignText,
+                                     const char16_t **aCertNickList,
+                                     const char16_t **aCertDetailsList,
+                                     uint32_t aCount, int32_t *aSelectedIndex,
+                                     nsAString &aPassword, bool *aCanceled) 
+{
+  *aCanceled = true;
+
+  // Get the parent window for the dialog
+  nsCOMPtr<nsIDOMWindow> parent = do_GetInterface(aContext);
+
+  nsresult rv;
+  nsCOMPtr<nsIDialogParamBlock> block =
+    do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  block->SetNumberStrings(3 + aCount * 2);
+
+  rv = block->SetString(0, PromiseFlatString(aHost).get());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = block->SetString(1, PromiseFlatString(aSignText).get());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uint32_t i;
+  for (i = 0; i < aCount; ++i) {
+    rv = block->SetString(2 + 2 * i, aCertNickList[i]);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = block->SetString(2 + (2 * i + 1), aCertDetailsList[i]);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  rv = block->SetInt(0, aCount);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = nsNSSDialogHelper::openDialog(parent,
+                                     "chrome://pippki/content/formsigning.xul",
+                                     block);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  int32_t status;
+  rv = block->GetInt(0, &status);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (status == 0) {
+    *aCanceled = true;
+  }
+  else {
+    *aCanceled = false;
+
+    rv = block->GetInt(1, aSelectedIndex);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsXPIDLString pw;
+    rv = block->GetString(0, getter_Copies(pw));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    aPassword = pw;
+  }
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/security/manager/pki/src/nsFormSigningDialog.h
@@ -0,0 +1,26 @@
+/* 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 __NS_NSFORMSIGNINGDIALOG_H__
+#define __NS_NSFORMSIGNINGDIALOG_H__
+
+#include "nsIFormSigningDialog.h"
+#include "mozilla/Attributes.h"
+
+#define NS_FORMSIGNINGDIALOG_CID \
+  { 0xa4bd2161, 0x7892, 0x4389, \
+    { 0x8d, 0x5a, 0x31, 0x11, 0xa6, 0xd1, 0x7e, 0xc7 }}
+
+class nsFormSigningDialog MOZ_FINAL : public nsIFormSigningDialog
+{
+private:
+  ~nsFormSigningDialog();
+public:
+  nsFormSigningDialog();
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIFORMSIGNINGDIALOG
+};
+
+#endif
--- a/security/manager/pki/src/nsPKIModule.cpp
+++ b/security/manager/pki/src/nsPKIModule.cpp
@@ -4,43 +4,48 @@
  * 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 "mozilla/ModuleUtils.h"
 
 #include "nsNSSDialogs.h"
 #include "nsPKIParamBlock.h"
 #include "nsASN1Tree.h"
+#include "nsFormSigningDialog.h"
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsNSSDialogs, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPKIParamBlock, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsNSSASN1Tree)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsFormSigningDialog)
 
 NS_DEFINE_NAMED_CID(NS_NSSDIALOGS_CID);
 NS_DEFINE_NAMED_CID(NS_NSSASN1OUTINER_CID);
 NS_DEFINE_NAMED_CID(NS_PKIPARAMBLOCK_CID);
+NS_DEFINE_NAMED_CID(NS_FORMSIGNINGDIALOG_CID);
 
 
 static const mozilla::Module::CIDEntry kPKICIDs[] = {
   { &kNS_NSSDIALOGS_CID, false, nullptr, nsNSSDialogsConstructor },
   { &kNS_NSSASN1OUTINER_CID, false, nullptr, nsNSSASN1TreeConstructor },
   { &kNS_PKIPARAMBLOCK_CID, false, nullptr, nsPKIParamBlockConstructor },
+  { &kNS_FORMSIGNINGDIALOG_CID, false, nullptr, nsFormSigningDialogConstructor },
   { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kPKIContracts[] = {
   { NS_TOKENPASSWORDSDIALOG_CONTRACTID, &kNS_NSSDIALOGS_CID },
   { NS_CERTIFICATEDIALOGS_CONTRACTID, &kNS_NSSDIALOGS_CID },
   { NS_CLIENTAUTHDIALOGS_CONTRACTID, &kNS_NSSDIALOGS_CID },
   { NS_CERTPICKDIALOGS_CONTRACTID, &kNS_NSSDIALOGS_CID },
   { NS_TOKENDIALOGS_CONTRACTID, &kNS_NSSDIALOGS_CID },
   { NS_DOMCRYPTODIALOGS_CONTRACTID, &kNS_NSSDIALOGS_CID },
   { NS_GENERATINGKEYPAIRINFODIALOGS_CONTRACTID, &kNS_NSSDIALOGS_CID },
   { NS_ASN1TREE_CONTRACTID, &kNS_NSSASN1OUTINER_CID },
   { NS_PKIPARAMBLOCK_CONTRACTID, &kNS_PKIPARAMBLOCK_CID },
+  { NS_FORMSIGNINGDIALOG_CONTRACTID, &kNS_FORMSIGNINGDIALOG_CID },
   { nullptr }
 };
 
 static const mozilla::Module kPKIModule = {
   mozilla::Module::kVersion,
   kPKICIDs,
   kPKIContracts
 };
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/crashtests/327524-1.html
@@ -0,0 +1,10 @@
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+
+<title>Testcase bug 327524 - Crash when using crypto.generateCRMFRequest(document.documentElement);</title>
+</head><body>
+This should not crash Mozilla
+<script>
+crypto.generateCRMFRequest(document.documentElement);
+</script>
+</body></html>
\ No newline at end of file
--- a/security/manager/ssl/crashtests/crashtests.list
+++ b/security/manager/ssl/crashtests/crashtests.list
@@ -1,1 +1,2 @@
+skip-if(browserIsRemote) asserts-if(browserIsRemote,0-1) load 327524-1.html # bug 582297, bug 918119
 asserts-if(browserIsRemote,1) load 398665-1.html # bug 582297
--- a/security/manager/ssl/public/moz.build
+++ b/security/manager/ssl/public/moz.build
@@ -12,16 +12,17 @@ XPIDL_SOURCES += [
     'nsIBadCertListener2.idl',
     'nsICertificateDialogs.idl',
     'nsICertificatePrincipal.idl',
     'nsICertOverrideService.idl',
     'nsICertPickDialogs.idl',
     'nsIClientAuthDialogs.idl',
     'nsIDataSignatureVerifier.idl',
     'nsIDOMCryptoDialogs.idl',
+    'nsIFormSigningDialog.idl',
     'nsIGenKeypairInfoDlg.idl',
     'nsIIdentityInfo.idl',
     'nsIKeygenThread.idl',
     'nsIKeyModule.idl',
     'nsINSSCertCache.idl',
     'nsINSSVersion.idl',
     'nsIPK11Token.idl',
     'nsIPK11TokenDB.idl',
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/public/nsIFormSigningDialog.idl
@@ -0,0 +1,39 @@
+/* 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 "nsISupports.idl"
+
+interface nsIInterfaceRequestor;
+
+/**
+ * nsIFormSigningDialog
+ * Provides UI for form signing.
+ */
+[scriptable, uuid(4fe04d6d-4b66-4023-a0bc-b43ce68b3e15)]
+interface nsIFormSigningDialog : nsISupports
+{
+  /**
+   *  confirmSignText
+   *    UI shown when a web site calls crypto.signText,
+   *    asking the user to confirm the confirm the signing request.
+   *
+   *  returns true if the user confirmed, false on cancel
+   */
+  boolean confirmSignText(in nsIInterfaceRequestor ctxt,
+                          in AString host,
+                          in AString signText,
+                          [array, size_is(count)] in wstring certNickList,
+                          [array, size_is(count)] in wstring certDetailsList,
+                          in uint32_t count,
+                          out int32_t selectedIndex,
+                          out AString password);
+};
+
+/**
+ * NS_FORMSIGNINGDIALOG_CONTRACTID - contract id to obtain an instance
+ *   that implements nsIFormSigningDialog.
+ */
+%{C++
+#define NS_FORMSIGNINGDIALOG_CONTRACTID "@mozilla.org/nsFormSigningDialog;1"
+%}
--- a/security/manager/ssl/src/moz.build
+++ b/security/manager/ssl/src/moz.build
@@ -68,17 +68,17 @@ SOURCES += [
 SOURCES += [
     'nsCryptoHash.cpp',
     'nsNSSCertificateDB.cpp',
     'nsNSSComponent.cpp',
     'nsNSSVersion.cpp',
     'PSMContentListener.cpp',
 ]
 
-if not CONFIG['MOZ_NO_SMART_CARDS']:
+if not CONFIG['MOZ_DISABLE_CRYPTOLEGACY']:
     SOURCES += [
         'nsSmartCardMonitor.cpp',
     ]
 
 if CONFIG['MOZ_XUL']:
     SOURCES += [
         'nsCertTree.cpp',
     ]
--- a/security/manager/ssl/src/nsCrypto.cpp
+++ b/security/manager/ssl/src/nsCrypto.cpp
@@ -9,25 +9,2817 @@
 
 #include "nsReadableUtils.h"
 #include "nsCRT.h"
 #include "nsXPIDLString.h"
 #include "nsISaveAsCharset.h"
 #include "nsNativeCharsetUtils.h"
 #include "nsServiceManagerUtils.h"
 
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
+#include "mozilla/dom/ScriptSettings.h"
+#include "nsKeygenHandler.h"
+#include "nsKeygenThread.h"
+#include "nsNSSCertificate.h"
+#include "nsNSSCertificateDB.h"
+#include "nsPKCS12Blob.h"
+#include "nsPK11TokenDB.h"
+#include "nsThreadUtils.h"
+#include "nsIServiceManager.h"
+#include "nsIMemory.h"
+#include "nsAlgorithm.h"
+#include "prprf.h"
+#include "nsDOMCID.h"
+#include "nsIDOMWindow.h"
+#include "nsIDOMClassInfo.h"
+#include "nsIDOMDocument.h"
+#include "nsIDocument.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "nsIScriptContext.h"
+#include "nsIGlobalObject.h"
+#include "nsContentUtils.h"
+#include "nsDOMJSUtils.h"
+#include "nsJSUtils.h"
+#include "nsIXPConnect.h"
+#include "nsIRunnable.h"
+#include "nsIWindowWatcher.h"
+#include "nsIPrompt.h"
+#include "nsIFilePicker.h"
+#include "nsJSPrincipals.h"
+#include "nsJSUtils.h"
+#include "nsIPrincipal.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIGenKeypairInfoDlg.h"
+#include "nsIDOMCryptoDialogs.h"
+#include "nsIFormSigningDialog.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsIURI.h"
+#include "jsapi.h"
+#include "js/OldDebugAPI.h"
+#include <ctype.h>
+#include "pk11func.h"
+#include "keyhi.h"
+#include "cryptohi.h"
+#include "seccomon.h"
+#include "secerr.h"
+#include "sechash.h"
+#include "crmf.h"
+#include "pk11pqg.h"
+#include "cmmf.h"
+#include "nssb64.h"
+#include "base64.h"
+#include "cert.h"
+#include "certdb.h"
+#include "secmod.h"
+#include "ScopedNSSTypes.h"
+#include "pkix/pkixtypes.h"
+
+#include "ssl.h" // For SSL_ClearSessionCache
+
+#include "nsNSSCleaner.h"
+
+#include "nsNSSCertHelper.h"
+#include <algorithm>
+#include "nsWrapperCacheInlines.h"
+#endif
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
+#include "mozilla/dom/CRMFObjectBinding.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+/*
+ * These are the most common error strings that are returned
+ * by the JavaScript methods in case of error.
+ */
+
+#define JS_ERROR       "error:"
+#define JS_ERROR_INTERNAL  JS_ERROR"internalError"
+
+#undef REPORT_INCORRECT_NUM_ARGS
+
+#define JS_OK_ADD_MOD                      3
+#define JS_OK_DEL_EXTERNAL_MOD             2
+#define JS_OK_DEL_INTERNAL_MOD             1
+
+#define JS_ERR_INTERNAL                   -1
+#define JS_ERR_USER_CANCEL_ACTION         -2
+#define JS_ERR_INCORRECT_NUM_OF_ARGUMENTS -3
+#define JS_ERR_DEL_MOD                    -4
+#define JS_ERR_ADD_MOD                    -5
+#define JS_ERR_BAD_MODULE_NAME            -6
+#define JS_ERR_BAD_DLL_NAME               -7
+#define JS_ERR_BAD_MECHANISM_FLAGS        -8
+#define JS_ERR_BAD_CIPHER_ENABLE_FLAGS    -9
+#define JS_ERR_ADD_DUPLICATE_MOD          -10
+
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
+
+NSSCleanupAutoPtrClass_WithParam(PK11Context, PK11_DestroyContext, TrueParam, true)
+
+/*
+ * This structure is used to store information for one key generation.
+ * The nsCrypto::GenerateCRMFRequest method parses the inputs and then
+ * stores one of these structures for every key generation that happens.
+ * The information stored in this structure is then used to set some
+ * values in the CRMF request.
+ */
+typedef enum {
+  rsaEnc, rsaDualUse, rsaSign, rsaNonrepudiation, rsaSignNonrepudiation,
+  ecEnc, ecDualUse, ecSign, ecNonrepudiation, ecSignNonrepudiation,
+  dhEx, dsaSignNonrepudiation, dsaSign, dsaNonrepudiation, invalidKeyGen
+} nsKeyGenType;
+
+bool isECKeyGenType(nsKeyGenType kgt)
+{
+  switch (kgt)
+  {
+    case ecEnc:
+    case ecDualUse:
+    case ecSign:
+    case ecNonrepudiation:
+    case ecSignNonrepudiation:
+      return true;
+    
+    default:
+      break;
+  }
+
+  return false;
+}
+
+typedef struct nsKeyPairInfoStr {
+  SECKEYPublicKey  *pubKey;     /* The putlic key associated with gen'd 
+                                   priv key. */
+  SECKEYPrivateKey *privKey;    /* The private key we generated */ 
+  nsKeyGenType      keyGenType; /* What type of key gen are we doing.*/
+
+  CERTCertificate *ecPopCert;
+                   /* null: use signing for pop
+                      other than null: a cert that defines EC keygen params
+                                       and will be used for dhMac PoP. */
+
+  SECKEYPublicKey  *ecPopPubKey;
+                   /* extracted public key from ecPopCert */
+} nsKeyPairInfo;
+
+
+//This class is just used to pass arguments
+//to the nsCryptoRunnable event.
+class nsCryptoRunArgs : public nsISupports {
+public:
+  nsCryptoRunArgs();
+  nsCOMPtr<nsIGlobalObject> m_globalObject;
+  nsXPIDLCString m_jsCallback;
+  NS_DECL_ISUPPORTS
+protected:
+  virtual ~nsCryptoRunArgs();
+};
+
+//This class is used to run the callback code
+//passed to crypto.generateCRMFRequest
+//We have to do that for backwards compatibility
+//reasons w/ PSM 1.x and Communciator 4.x
+class nsCryptoRunnable : public nsIRunnable {
+public:
+  nsCryptoRunnable(nsCryptoRunArgs *args);
+
+  NS_IMETHOD Run ();
+  NS_DECL_ISUPPORTS
+private:
+  virtual ~nsCryptoRunnable();
+
+  nsCryptoRunArgs *m_args;
+};
+
+//We're going to inherit the memory passed
+//into us.
+//This class backs up an array of certificates
+//as an event.
+class nsP12Runnable : public nsIRunnable {
+public:
+  nsP12Runnable(nsIX509Cert **certArr, int32_t numCerts, nsIPK11Token *token);
+
+  NS_IMETHOD Run();
+  NS_DECL_ISUPPORTS
+protected:
+  virtual ~nsP12Runnable();
+private:
+  nsCOMPtr<nsIPK11Token> mToken;
+  nsIX509Cert **mCertArr;
+  int32_t       mNumCerts;
+};
+
+// QueryInterface implementation for nsCrypto
+NS_INTERFACE_MAP_BEGIN(nsCrypto)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMCrypto)
+NS_INTERFACE_MAP_END_INHERITING(mozilla::dom::Crypto)
+
+NS_IMPL_ADDREF_INHERITED(nsCrypto, mozilla::dom::Crypto)
+NS_IMPL_RELEASE_INHERITED(nsCrypto, mozilla::dom::Crypto)
+
 // QueryInterface implementation for nsPkcs11
+#endif // MOZ_DISABLE_CRYPTOLEGACY
+
 NS_INTERFACE_MAP_BEGIN(nsPkcs11)
   NS_INTERFACE_MAP_ENTRY(nsIPKCS11)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsPkcs11)
 NS_IMPL_RELEASE(nsPkcs11)
 
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
+
+// ISupports implementation for nsCryptoRunnable
+NS_IMPL_ISUPPORTS(nsCryptoRunnable, nsIRunnable)
+
+// ISupports implementation for nsP12Runnable
+NS_IMPL_ISUPPORTS(nsP12Runnable, nsIRunnable)
+
+// ISupports implementation for nsCryptoRunArgs
+NS_IMPL_ISUPPORTS0(nsCryptoRunArgs)
+
+nsCrypto::nsCrypto() :
+  mEnableSmartCardEvents(false)
+{
+}
+
+nsCrypto::~nsCrypto()
+{
+}
+
+void
+nsCrypto::Init(nsIDOMWindow* aWindow)
+{
+  mozilla::dom::Crypto::Init(aWindow);
+}
+
+void
+nsCrypto::SetEnableSmartCardEvents(bool aEnable, ErrorResult& aRv)
+{
+  NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
+
+  nsresult rv = NS_OK;
+
+  // this has the side effect of starting the nssComponent (and initializing
+  // NSS) even if it isn't already going. Starting the nssComponent is a
+  // prerequisite for getting smartCard events.
+  if (aEnable) {
+    nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
+  }
+
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  mEnableSmartCardEvents = aEnable;
+}
+
+NS_IMETHODIMP
+nsCrypto::SetEnableSmartCardEvents(bool aEnable)
+{
+  ErrorResult rv;
+  SetEnableSmartCardEvents(aEnable, rv);
+  return rv.ErrorCode();
+}
+
+bool
+nsCrypto::EnableSmartCardEvents()
+{
+  return mEnableSmartCardEvents;
+}
+
+NS_IMETHODIMP
+nsCrypto::GetEnableSmartCardEvents(bool *aEnable)
+{
+  *aEnable = EnableSmartCardEvents();
+  return NS_OK;
+}
+
+//A quick function to let us know if the key we're trying to generate
+//can be escrowed.
+static bool
+ns_can_escrow(nsKeyGenType keyGenType)
+{
+  /* For now, we only escrow rsa-encryption and ec-encryption keys. */
+  return (bool)(keyGenType == rsaEnc || keyGenType == ecEnc);
+}
+
+//Retrieve crypto.version so that callers know what
+//version of PSM this is.
+void
+nsCrypto::GetVersion(nsString& aVersion)
+{
+  aVersion.Assign(NS_LITERAL_STRING(PSM_VERSION_STRING));
+}
+
+/*
+ * Given an nsKeyGenType, return the PKCS11 mechanism that will
+ * perform the correct key generation.
+ */
+static uint32_t
+cryptojs_convert_to_mechanism(nsKeyGenType keyGenType)
+{
+  uint32_t retMech;
+
+  switch (keyGenType) {
+  case rsaEnc:
+  case rsaDualUse:
+  case rsaSign:
+  case rsaNonrepudiation:
+  case rsaSignNonrepudiation:
+    retMech = CKM_RSA_PKCS_KEY_PAIR_GEN;
+    break;
+  case ecEnc:
+  case ecDualUse:
+  case ecSign:
+  case ecNonrepudiation:
+  case ecSignNonrepudiation:
+    retMech = CKM_EC_KEY_PAIR_GEN;
+    break;
+  case dhEx:
+    retMech = CKM_DH_PKCS_KEY_PAIR_GEN;
+    break;
+  case dsaSign:
+  case dsaSignNonrepudiation:
+  case dsaNonrepudiation:
+    retMech = CKM_DSA_KEY_PAIR_GEN;
+    break;
+  default:
+    retMech = CKM_INVALID_MECHANISM;
+  }
+  return retMech;
+}
+
+/*
+ * This function takes a string read through JavaScript parameters
+ * and translates it to the internal enumeration representing the
+ * key gen type. Leading and trailing whitespace must be already removed.
+ */
+static nsKeyGenType
+cryptojs_interpret_key_gen_type(const nsAString& keyAlg)
+{
+  if (keyAlg.EqualsLiteral("rsa-ex")) {
+    return rsaEnc;
+  }
+  if (keyAlg.EqualsLiteral("rsa-dual-use")) {
+    return rsaDualUse;
+  }
+  if (keyAlg.EqualsLiteral("rsa-sign")) {
+    return rsaSign;
+  }
+  if (keyAlg.EqualsLiteral("rsa-sign-nonrepudiation")) {
+    return rsaSignNonrepudiation;
+  }
+  if (keyAlg.EqualsLiteral("rsa-nonrepudiation")) {
+    return rsaNonrepudiation;
+  }
+  if (keyAlg.EqualsLiteral("ec-ex")) {
+    return ecEnc;
+  }
+  if (keyAlg.EqualsLiteral("ec-dual-use")) {
+    return ecDualUse;
+  }
+  if (keyAlg.EqualsLiteral("ec-sign")) {
+    return ecSign;
+  }
+  if (keyAlg.EqualsLiteral("ec-sign-nonrepudiation")) {
+    return ecSignNonrepudiation;
+  }
+  if (keyAlg.EqualsLiteral("ec-nonrepudiation")) {
+    return ecNonrepudiation;
+  }
+  if (keyAlg.EqualsLiteral("dsa-sign-nonrepudiation")) {
+    return dsaSignNonrepudiation;
+  }
+  if (keyAlg.EqualsLiteral("dsa-sign")) {
+    return dsaSign;
+  }
+  if (keyAlg.EqualsLiteral("dsa-nonrepudiation")) {
+    return dsaNonrepudiation;
+  }
+  if (keyAlg.EqualsLiteral("dh-ex")) {
+    return dhEx;
+  }
+  return invalidKeyGen;
+}
+
+/* 
+ * input: null terminated char* pointing to (the remainder of) an
+ * EC key param string.
+ *
+ * bool return value, false means "no more name=value pair found",
+ *                    true means "found, see out params"
+ * 
+ * out param name: char * pointing to name (not zero terminated)
+ * out param name_len: length of found name
+ * out param value: char * pointing to value (not zero terminated)
+ * out param value_len: length of found value
+ * out param next_pair: to be used for a follow up call to this function
+ */
+
+bool getNextNameValueFromECKeygenParamString(char *input,
+                                               char *&name,
+                                               int &name_len,
+                                               char *&value,
+                                               int &value_len,
+                                               char *&next_call)
+{
+  if (!input || !*input)
+    return false;
+
+  // we allow leading ; and leading space in front of each name value pair
+
+  while (*input && *input == ';')
+    ++input;
+
+  while (*input && *input == ' ')
+    ++input;
+
+  name = input;
+
+  while (*input && *input != '=')
+    ++input;
+
+  if (*input != '=')
+    return false;
+
+  name_len = input - name;
+  ++input;
+
+  value = input;
+
+  while (*input && *input != ';')
+    ++input;
+
+  value_len = input - value;
+  next_call = input;
+
+  return true;
+}
+
+//Take the string passed into us via crypto.generateCRMFRequest
+//as the keygen type parameter and convert it to parameters 
+//we can actually pass to the PKCS#11 layer.
+static void*
+nsConvertToActualKeyGenParams(uint32_t keyGenMech, char *params,
+                              uint32_t paramLen, int32_t keySize,
+                              nsKeyPairInfo *keyPairInfo)
+{
+  void *returnParams = nullptr;
+
+
+  switch (keyGenMech) {
+  case CKM_RSA_PKCS_KEY_PAIR_GEN:
+  {
+    // For RSA, we don't support passing in key generation arguments from
+    // the JS code just yet.
+    if (params)
+      return nullptr;
+
+    PK11RSAGenParams *rsaParams;
+    rsaParams = static_cast<PK11RSAGenParams*>
+                           (nsMemory::Alloc(sizeof(PK11RSAGenParams)));
+                              
+    if (!rsaParams) {
+      return nullptr;
+    }
+    /* I'm just taking the same parameters used in 
+     * certdlgs.c:GenKey
+     */
+    if (keySize > 0) {
+      rsaParams->keySizeInBits = keySize;
+    } else {
+      rsaParams->keySizeInBits = 1024;
+    }
+    rsaParams->pe = DEFAULT_RSA_KEYGEN_PE;
+    returnParams = rsaParams;
+    break;
+  }
+  case CKM_EC_KEY_PAIR_GEN:
+  {
+    /*
+     * keygen params for generating EC keys must be composed of name=value pairs,
+     * multiple pairs allowed, separated using semicolon ;
+     *
+     * Either param "curve" or param "popcert" must be specified.
+     * curve=name-of-curve
+     * popcert=base64-encoded-cert
+     *
+     * When both params are specified, popcert will be used.
+     * If no popcert param is given, or if popcert can not be decoded,
+     * we will fall back to the curve param.
+     *
+     * Additional name=value pairs may be defined in the future.
+     *
+     * If param popcert is present and valid, the given certificate will be used
+     * to determine the key generation params. In addition the certificate
+     * will be used to produce a dhMac based Proof of Posession,
+     * using the cert's public key, subject and issuer names,
+     * as specified in RFC 2511 section 4.3 paragraph 2 and Appendix A.
+     *
+     * If neither param popcert nor param curve could be used,
+     * tse a curve based on the keysize param.
+     * NOTE: Here keysize is used only as an indication of
+     * High/Medium/Low strength; elliptic curve
+     * cryptography uses smaller keys than RSA to provide
+     * equivalent security.
+     */
+
+    char *curve = nullptr;
+
+    {
+      // extract components of name=value list
+
+      char *next_input = params;
+      char *name = nullptr;
+      char *value = nullptr;
+      int name_len = 0;
+      int value_len = 0;
+  
+      while (getNextNameValueFromECKeygenParamString(
+              next_input, name, name_len, value, value_len,
+              next_input))
+      {
+        // use only the first specified curve
+        if (!curve && PL_strncmp(name, "curve", std::min(name_len, 5)) == 0)
+        {
+          curve = PL_strndup(value, value_len);
+        }
+        // use only the first specified popcert
+        else if (!keyPairInfo->ecPopCert &&
+                 PL_strncmp(name, "popcert", std::min(name_len, 7)) == 0)
+        {
+          char *certstr = PL_strndup(value, value_len);
+          if (certstr) {
+            keyPairInfo->ecPopCert = CERT_ConvertAndDecodeCertificate(certstr);
+            PL_strfree(certstr);
+
+            if (keyPairInfo->ecPopCert)
+            {
+              keyPairInfo->ecPopPubKey = CERT_ExtractPublicKey(keyPairInfo->ecPopCert);
+            }
+          }
+        }
+      }
+    }
+
+    // first try to use the params of the provided CA cert
+    if (keyPairInfo->ecPopPubKey && keyPairInfo->ecPopPubKey->keyType == ecKey)
+    {
+      returnParams = SECITEM_DupItem(&keyPairInfo->ecPopPubKey->u.ec.DEREncodedParams);
+    }
+
+    // if we did not yet find good params, do we have a curve name?
+    if (!returnParams && curve)
+    {
+      returnParams = decode_ec_params(curve);
+    }
+
+    // if we did not yet find good params, do something based on keysize
+    if (!returnParams)
+    {
+      switch (keySize) {
+      case 512:
+      case 1024:
+          returnParams = decode_ec_params("secp256r1");
+          break;
+      case 2048:
+      default:
+          returnParams = decode_ec_params("secp384r1");
+          break;
+      }
+    }
+
+    if (curve)
+      PL_strfree(curve);
+
+    break;
+  }
+  case CKM_DSA_KEY_PAIR_GEN:
+  {
+    // For DSA, we don't support passing in key generation arguments from
+    // the JS code just yet.
+    if (params)
+      return nullptr;
+
+    PQGParams *pqgParams = nullptr;
+    PQGVerify *vfy = nullptr;
+    SECStatus  rv;
+    int        index;
+       
+    index = PQG_PBITS_TO_INDEX(keySize);
+    if (index == -1) {
+      returnParams = nullptr;
+      break;
+    }
+    rv = PK11_PQG_ParamGen(0, &pqgParams, &vfy);
+    if (vfy) {
+      PK11_PQG_DestroyVerify(vfy);
+    }
+    if (rv != SECSuccess) {
+      if (pqgParams) {
+        PK11_PQG_DestroyParams(pqgParams);
+      }
+      return nullptr;
+    }
+    returnParams = pqgParams;
+    break;
+  }
+  default:
+    returnParams = nullptr;
+  }
+  return returnParams;
+}
+
+//We need to choose which PKCS11 slot we're going to generate
+//the key on.  Calls the default implementation provided by
+//nsKeygenHandler.cpp
+static PK11SlotInfo*
+nsGetSlotForKeyGen(nsKeyGenType keyGenType, nsIInterfaceRequestor *ctx)
+{
+  nsNSSShutDownPreventionLock locker;
+  uint32_t mechanism = cryptojs_convert_to_mechanism(keyGenType);
+  PK11SlotInfo *slot = nullptr;
+  nsresult rv = GetSlotWithMechanism(mechanism,ctx, &slot);
+  if (NS_FAILED(rv)) {
+    if (slot)
+      PK11_FreeSlot(slot);
+    slot = nullptr;
+  }
+  return slot;
+}
+
+//Free the parameters that were passed into PK11_GenerateKeyPair
+//depending on the mechanism type used.
+static void
+nsFreeKeyGenParams(CK_MECHANISM_TYPE keyGenMechanism, void *params)
+{
+  switch (keyGenMechanism) {
+  case CKM_RSA_PKCS_KEY_PAIR_GEN:
+    nsMemory::Free(params);
+    break;
+  case CKM_EC_KEY_PAIR_GEN:
+    SECITEM_FreeItem(reinterpret_cast<SECItem*>(params), true);
+    break;
+  case CKM_DSA_KEY_PAIR_GEN:
+    PK11_PQG_DestroyParams(static_cast<PQGParams*>(params));
+    break;
+  }
+}
+
+//Function that is used to generate a single key pair.
+//Once all the arguments have been parsed and processed, this
+//function gets called and takes care of actually generating
+//the key pair passing the appopriate parameters to the NSS
+//functions.
+static nsresult
+cryptojs_generateOneKeyPair(JSContext *cx, nsKeyPairInfo *keyPairInfo, 
+                            int32_t keySize, char *params, 
+                            nsIInterfaceRequestor *uiCxt,
+                            PK11SlotInfo *slot, bool willEscrow)
+                            
+{
+  const PK11AttrFlags sensitiveFlags = (PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE);
+  const PK11AttrFlags temporarySessionFlags = PK11_ATTR_SESSION;
+  const PK11AttrFlags permanentTokenFlags = PK11_ATTR_TOKEN;
+  const PK11AttrFlags extractableFlags = PK11_ATTR_EXTRACTABLE;
+  
+  nsIGeneratingKeypairInfoDialogs * dialogs;
+  nsKeygenThread *KeygenRunnable = 0;
+  nsCOMPtr<nsIKeygenThread> runnable;
+
+  uint32_t mechanism = cryptojs_convert_to_mechanism(keyPairInfo->keyGenType);
+  void *keyGenParams = nsConvertToActualKeyGenParams(mechanism, params, 
+                                                     (params) ? strlen(params):0, 
+                                                     keySize, keyPairInfo);
+
+  if (!keyGenParams || !slot) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  // Make sure the token has password already set on it before trying
+  // to generate the key.
+
+  nsresult rv = setPassword(slot, uiCxt);
+  if (NS_FAILED(rv))
+    return rv;
+
+  if (PK11_Authenticate(slot, true, uiCxt) != SECSuccess)
+    return NS_ERROR_FAILURE;
+ 
+  // Smart cards will not let you extract a private key once 
+  // it is on the smart card.  If we've been told to escrow
+  // a private key that will be stored on a smart card,
+  // then we'll use the following strategy to ensure we can escrow it.
+  // We'll attempt to generate the key on our internal token,
+  // because this is expected to avoid some problems.
+  // If it works, we'll escrow, move the key to the smartcard, done.
+  // If it didn't work (or the internal key doesn't support the desired
+  // mechanism), then we'll attempt to generate the key on
+  // the destination token, with the EXTRACTABLE flag set.
+  // If it works, we'll extract, escrow, done.
+  // If it failed, then we're unable to escrow and return failure.
+  // NOTE: We call PK11_GetInternalSlot instead of PK11_GetInternalKeySlot
+  //       so that the key has zero chance of being store in the
+  //       user's key3.db file.  Which the slot returned by
+  //       PK11_GetInternalKeySlot has access to and PK11_GetInternalSlot
+  //       does not.
+  ScopedPK11SlotInfo intSlot;
+  
+  if (willEscrow && !PK11_IsInternal(slot)) {
+    intSlot = PK11_GetInternalSlot();
+    NS_ASSERTION(intSlot,"Couldn't get the internal slot");
+    
+    if (!PK11_DoesMechanism(intSlot, mechanism)) {
+      // Set to null, and the subsequent code will not attempt to use it.
+      intSlot = nullptr;
+    }
+  }
+
+  rv = getNSSDialogs((void**)&dialogs,
+                     NS_GET_IID(nsIGeneratingKeypairInfoDialogs),
+                     NS_GENERATINGKEYPAIRINFODIALOGS_CONTRACTID);
+
+  if (NS_SUCCEEDED(rv)) {
+    KeygenRunnable = new nsKeygenThread();
+    if (KeygenRunnable) {
+      NS_ADDREF(KeygenRunnable);
+    }
+  }
+  
+  // "firstAttemptSlot" and "secondAttemptSlot" are alternative names
+  // for better code readability, we don't increase the reference counts.
+  
+  PK11SlotInfo *firstAttemptSlot = nullptr;
+  PK11AttrFlags firstAttemptFlags = 0;
+
+  PK11SlotInfo *secondAttemptSlot = slot;
+  PK11AttrFlags secondAttemptFlags = sensitiveFlags | permanentTokenFlags;
+  
+  if (willEscrow) {
+    secondAttemptFlags |= extractableFlags;
+  }
+  
+  if (!intSlot || PK11_IsInternal(slot)) {
+    // if we cannot use the internal slot, then there is only one attempt
+    // if the destination slot is the internal slot, then there is only one attempt
+    firstAttemptSlot = secondAttemptSlot;
+    firstAttemptFlags = secondAttemptFlags;
+    secondAttemptSlot = nullptr;
+    secondAttemptFlags = 0;
+  }
+  else {
+    firstAttemptSlot = intSlot;
+    firstAttemptFlags = sensitiveFlags | temporarySessionFlags;
+    
+    // We always need the extractable flag on the first attempt,
+    // because we want to move the key to another slot - ### is this correct?
+    firstAttemptFlags |= extractableFlags;
+  }
+
+  bool mustMoveKey = false;
+  
+  if (NS_FAILED(rv) || !KeygenRunnable) {
+    /* execute key generation on this thread */
+    rv = NS_OK;
+    
+    keyPairInfo->privKey = 
+      PK11_GenerateKeyPairWithFlags(firstAttemptSlot, mechanism,
+                                    keyGenParams, &keyPairInfo->pubKey,
+                                    firstAttemptFlags, uiCxt);
+    
+    if (keyPairInfo->privKey) {
+      // success on first attempt
+      if (secondAttemptSlot) {
+        mustMoveKey = true;
+      }
+    }
+    else {
+      keyPairInfo->privKey = 
+        PK11_GenerateKeyPairWithFlags(secondAttemptSlot, mechanism,
+                                      keyGenParams, &keyPairInfo->pubKey,
+                                      secondAttemptFlags, uiCxt);
+    }
+    
+  } else {
+    /* execute key generation on separate thread */
+    KeygenRunnable->SetParams( firstAttemptSlot, firstAttemptFlags,
+                               secondAttemptSlot, secondAttemptFlags,
+                               mechanism, keyGenParams, uiCxt );
+
+    runnable = do_QueryInterface(KeygenRunnable);
+
+    if (runnable) {
+      {
+        nsPSMUITracker tracker;
+        if (tracker.isUIForbidden()) {
+          rv = NS_ERROR_NOT_AVAILABLE;
+        }
+        else {
+          rv = dialogs->DisplayGeneratingKeypairInfo(uiCxt, runnable);
+          // We call join on the thread, 
+          // so we can be sure that no simultaneous access to the passed parameters will happen.
+          KeygenRunnable->Join();
+        }
+      }
+
+      NS_RELEASE(dialogs);
+      if (NS_SUCCEEDED(rv)) {
+        PK11SlotInfo *used_slot = nullptr;
+        rv = KeygenRunnable->ConsumeResult(&used_slot, 
+                                           &keyPairInfo->privKey, &keyPairInfo->pubKey);
+
+        if (NS_SUCCEEDED(rv)) {
+          if ((used_slot == firstAttemptSlot) && secondAttemptSlot) {
+            mustMoveKey = true;
+          }
+        
+          if (used_slot) {
+            PK11_FreeSlot(used_slot);
+          }
+        }
+      }
+    }
+  }
+
+  firstAttemptSlot = nullptr;
+  secondAttemptSlot = nullptr;
+  
+  nsFreeKeyGenParams(mechanism, keyGenParams);
+
+  if (KeygenRunnable) {
+    NS_RELEASE(KeygenRunnable);
+  }
+
+  if (!keyPairInfo->privKey || !keyPairInfo->pubKey) {
+    return NS_ERROR_FAILURE;
+  }
+ 
+  //If we generated the key pair on the internal slot because the
+  // keys were going to be escrowed, move the keys over right now.
+  if (mustMoveKey) {
+    ScopedSECKEYPrivateKey newPrivKey(PK11_LoadPrivKey(slot,
+                                                    keyPairInfo->privKey,
+                                                    keyPairInfo->pubKey,
+                                                    true, true));
+    if (!newPrivKey)
+      return NS_ERROR_FAILURE;
+
+    // The private key is stored on the selected slot now, and the copy we
+    // ultimately use for escrowing when the time comes lives 
+    // in the internal slot.  We will delete it from that slot
+    // after the requests are made.
+  }  
+
+  return NS_OK;
+}
+
+/*
+ * FUNCTION: cryptojs_ReadArgsAndGenerateKey
+ * -------------------------------------
+ * INPUTS:
+ *  cx
+ *    The JSContext associated with the execution of the corresponging
+ *    crypto.generateCRMFRequest call
+ *  argv
+ *    A pointer to an array of JavaScript parameters passed to the
+ *    method crypto.generateCRMFRequest.  The array should have the
+ *    3 arguments keySize, "keyParams", and "keyGenAlg" mentioned in
+ *    the definition of crypto.generateCRMFRequest at the following
+ *    document http://docs.iplanet.com/docs/manuals/psm/11/cmcjavascriptapi.html 
+ *  keyGenType
+ *    A structure used to store the information about the newly created
+ *    key pair.
+ *  uiCxt
+ *    An interface requestor that would be used to get an nsIPrompt
+ *    if we need to ask the user for a password.
+ *  slotToUse
+ *    The PKCS11 slot to use for generating the key pair. If nullptr, then
+ *    this function should select a slot that can do the key generation 
+ *    from the keytype associted with the keyPairInfo, and pass it back to
+ *    the caller so that subsequence key generations can use the same slot. 
+ *  willEscrow
+ *    If true, then that means we will try to escrow the generated
+ *    private key when building the CRMF request.  If false, then
+ *    we will not try to escrow the private key.
+ *
+ * NOTES:
+ * This function takes care of reading a set of 3 parameters that define
+ * one key generation.  The argv pointer should be one that originates
+ * from the argv parameter passed in to the method nsCrypto::GenerateCRMFRequest.
+ * The function interprets the argument in the first index as an integer and
+ * passes that as the key size for the key generation-this parameter is
+ * mandatory.  The second parameter is read in as a string.  This value can
+ * be null in JavaScript world and everything will still work.  The third
+ * parameter is a mandatory string that indicates what kind of key to generate.
+ * There should always be 1-to-1 correspondence between the strings compared
+ * in the function cryptojs_interpret_key_gen_type and the strings listed in
+ * document at http://docs.iplanet.com/docs/manuals/psm/11/cmcjavascriptapi.html 
+ * under the definition of the method generateCRMFRequest, for the parameter
+ * "keyGenAlgN".  After reading the parameters, the function then 
+ * generates the key pairs passing the parameters parsed from the JavaScript i
+ * routine.  
+ *
+ * RETURN:
+ * NS_OK if creating the Key was successful.  Any other return value
+ * indicates an error.
+ */
+
+static nsresult
+cryptojs_ReadArgsAndGenerateKey(JSContext *cx,
+                                JS::Value *argv,
+                                nsKeyPairInfo *keyGenType,
+                                nsIInterfaceRequestor *uiCxt,
+                                PK11SlotInfo **slot, bool willEscrow)
+{
+  JSString  *jsString;
+  JSAutoByteString params;
+  int    keySize;
+  nsresult  rv;
+
+  if (!argv[0].isInt32()) {
+    JS_ReportError(cx, "%s%s", JS_ERROR,
+                   "passed in non-integer for key size");
+    return NS_ERROR_FAILURE;
+  }
+  keySize = argv[0].toInt32();
+  if (!argv[1].isNull()) {
+    JS::Rooted<JS::Value> v(cx, argv[1]);
+    jsString = JS::ToString(cx, v);
+    NS_ENSURE_TRUE(jsString, NS_ERROR_OUT_OF_MEMORY);
+    argv[1] = STRING_TO_JSVAL(jsString);
+    params.encodeLatin1(cx, jsString);
+    NS_ENSURE_TRUE(!!params, NS_ERROR_OUT_OF_MEMORY);
+  }
+
+  if (argv[2].isNull()) {
+    JS_ReportError(cx,"%s%s", JS_ERROR,
+             "key generation type not specified");
+    return NS_ERROR_FAILURE;
+  }
+  JS::Rooted<JS::Value> v(cx, argv[2]);
+  jsString = JS::ToString(cx, v);
+  NS_ENSURE_TRUE(jsString, NS_ERROR_OUT_OF_MEMORY);
+  argv[2] = STRING_TO_JSVAL(jsString);
+  nsAutoJSString autoJSKeyGenAlg;
+  NS_ENSURE_TRUE(autoJSKeyGenAlg.init(cx, jsString), NS_ERROR_UNEXPECTED);
+  nsAutoString keyGenAlg(autoJSKeyGenAlg);
+  keyGenAlg.Trim("\r\n\t ");
+  keyGenType->keyGenType = cryptojs_interpret_key_gen_type(keyGenAlg);
+  if (keyGenType->keyGenType == invalidKeyGen) {
+    NS_LossyConvertUTF16toASCII keyGenAlgNarrow(autoJSKeyGenAlg);
+    JS_ReportError(cx, "%s%s%s", JS_ERROR,
+                   "invalid key generation argument:",
+                   keyGenAlgNarrow.get());
+    goto loser;
+  }
+  if (!*slot) {
+    *slot = nsGetSlotForKeyGen(keyGenType->keyGenType, uiCxt);
+    if (!*slot)
+      goto loser;
+  }
+
+  rv = cryptojs_generateOneKeyPair(cx,keyGenType,keySize,params.ptr(),uiCxt,
+                                   *slot,willEscrow);
+
+  if (rv != NS_OK) {
+    NS_LossyConvertUTF16toASCII keyGenAlgNarrow(autoJSKeyGenAlg);
+    JS_ReportError(cx,"%s%s%s", JS_ERROR,
+                   "could not generate the key for algorithm ",
+                   keyGenAlgNarrow.get());
+    goto loser;
+  }
+  return NS_OK;
+loser:
+  return NS_ERROR_FAILURE;
+}
+
+//Utility funciton to free up the memory used by nsKeyPairInfo
+//arrays.
+static void
+nsFreeKeyPairInfo(nsKeyPairInfo *keyids, int numIDs)
+{
+  NS_ASSERTION(keyids, "NULL pointer passed to nsFreeKeyPairInfo");
+  if (!keyids)
+    return;
+  int i;
+  for (i=0; i<numIDs; i++) {
+    if (keyids[i].pubKey)
+      SECKEY_DestroyPublicKey(keyids[i].pubKey);
+    if (keyids[i].privKey)
+      SECKEY_DestroyPrivateKey(keyids[i].privKey);
+    if (keyids[i].ecPopCert)
+      CERT_DestroyCertificate(keyids[i].ecPopCert);
+    if (keyids[i].ecPopPubKey)
+      SECKEY_DestroyPublicKey(keyids[i].ecPopPubKey);
+  }
+  delete []keyids;
+}
+
+//Utility funciton used to free the genertaed cert request messages
+static void
+nsFreeCertReqMessages(CRMFCertReqMsg **certReqMsgs, int32_t numMessages)
+{
+  int32_t i;
+  for (i=0; i<numMessages && certReqMsgs[i]; i++) {
+    CRMF_DestroyCertReqMsg(certReqMsgs[i]);
+  }
+  delete []certReqMsgs;
+}
+
+//If the form called for escrowing the private key we just generated,
+//this function adds all the correct elements to the request.
+//That consists of adding CRMFEncryptedKey to the reques as part
+//of the CRMFPKIArchiveOptions Control.
+static nsresult
+nsSetEscrowAuthority(CRMFCertRequest *certReq, nsKeyPairInfo *keyInfo,
+                     nsNSSCertificate *wrappingCert)
+{
+  if (!wrappingCert ||
+      CRMF_CertRequestIsControlPresent(certReq, crmfPKIArchiveOptionsControl)){
+    return NS_ERROR_FAILURE;
+  }
+  ScopedCERTCertificate cert(wrappingCert->GetCert());
+  if (!cert)
+    return NS_ERROR_FAILURE;
+
+  CRMFEncryptedKey *encrKey = 
+      CRMF_CreateEncryptedKeyWithEncryptedValue(keyInfo->privKey, cert.get());
+  if (!encrKey)
+    return NS_ERROR_FAILURE;
+
+  CRMFPKIArchiveOptions *archOpt = 
+      CRMF_CreatePKIArchiveOptions(crmfEncryptedPrivateKey, encrKey);
+  if (!archOpt) {
+    CRMF_DestroyEncryptedKey(encrKey);
+    return NS_ERROR_FAILURE;
+  }
+  SECStatus srv = CRMF_CertRequestSetPKIArchiveOptions(certReq, archOpt);
+  CRMF_DestroyEncryptedKey(encrKey);
+  CRMF_DestroyPKIArchiveOptions(archOpt);
+  if (srv != SECSuccess)
+    return NS_ERROR_FAILURE;
+
+  return NS_OK;
+}
+
+//Set the Distinguished Name (Subject Name) for the cert
+//being requested.
+static nsresult
+nsSetDNForRequest(CRMFCertRequest *certReq, char *reqDN)
+{
+  if (!reqDN || CRMF_CertRequestIsFieldPresent(certReq, crmfSubject)) {
+    return NS_ERROR_FAILURE;
+  }
+  ScopedCERTName subjectName(CERT_AsciiToName(reqDN));
+  if (!subjectName) {
+    return NS_ERROR_FAILURE;
+  }
+  SECStatus srv = CRMF_CertRequestSetTemplateField(certReq, crmfSubject,
+                                                   static_cast<void*>
+                                                              (subjectName));
+  return (srv == SECSuccess) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+//Set Registration Token Control on the request.
+static nsresult
+nsSetRegToken(CRMFCertRequest *certReq, char *regToken)
+{
+  // this should never happen, but might as well add this.
+  NS_ASSERTION(certReq, "A bogus certReq passed to nsSetRegToken");
+  if (regToken){
+    if (CRMF_CertRequestIsControlPresent(certReq, crmfRegTokenControl))
+      return NS_ERROR_FAILURE;
+  
+    SECItem src;
+    src.data = (unsigned char*)regToken;
+    src.len  = strlen(regToken);
+    SECItem *derEncoded = SEC_ASN1EncodeItem(nullptr, nullptr, &src, 
+                                        SEC_ASN1_GET(SEC_UTF8StringTemplate));
+
+    if (!derEncoded)
+      return NS_ERROR_FAILURE;
+
+    SECStatus srv = CRMF_CertRequestSetRegTokenControl(certReq, derEncoded);
+    SECITEM_FreeItem(derEncoded,true);
+    if (srv != SECSuccess)
+      return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+//Set the Authenticator control on the cert reuest.  It's just
+//a string that gets passed along.
+static nsresult
+nsSetAuthenticator(CRMFCertRequest *certReq, char *authenticator)
+{
+  //This should never happen, but might as well check.
+  NS_ASSERTION(certReq, "Bogus certReq passed to nsSetAuthenticator");
+  if (authenticator) {
+    if (CRMF_CertRequestIsControlPresent(certReq, crmfAuthenticatorControl))
+      return NS_ERROR_FAILURE;
+    
+    SECItem src;
+    src.data = (unsigned char*)authenticator;
+    src.len  = strlen(authenticator);
+    SECItem *derEncoded = SEC_ASN1EncodeItem(nullptr, nullptr, &src,
+                                     SEC_ASN1_GET(SEC_UTF8StringTemplate));
+    if (!derEncoded)
+      return NS_ERROR_FAILURE;
+
+    SECStatus srv = CRMF_CertRequestSetAuthenticatorControl(certReq, 
+                                                            derEncoded);
+    SECITEM_FreeItem(derEncoded, true);
+    if (srv != SECSuccess)
+      return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+// ASN1 DER encoding rules say that when encoding a BIT string,
+// the length in the header for the bit string is the number 
+// of "useful" bits in the BIT STRING.  So the function finds
+// it and sets accordingly for the returned item.
+static void
+nsPrepareBitStringForEncoding (SECItem *bitsmap, SECItem *value)
+{
+  unsigned char onebyte;
+  unsigned int i, len = 0;
+
+  /* to prevent warning on some platform at compile time */
+  onebyte = '\0';
+  /* Get the position of the right-most turn-on bit */
+  for (i = 0; i < (value->len ) * 8; ++i) {
+    if (i % 8 == 0)
+      onebyte = value->data[i/8];
+    if (onebyte & 0x80)
+      len = i;
+    onebyte <<= 1;
+  }
+
+  bitsmap->data = value->data;
+  /* Add one here since we work with base 1 */
+  bitsmap->len = len + 1;
+}
+
+//This next section defines all the functions that sets the 
+//keyUsageExtension for all the different types of key gens
+//we handle.  The keyUsageExtension is just a bit flag extension
+//that we set in wrapper functions that call straight into
+//nsSetKeyUsageExtension.  There is one wrapper funciton for each
+//keyGenType.  The correct function will eventually be called 
+//by going through a switch statement based on the nsKeyGenType
+//in the nsKeyPairInfo struct.
+static nsresult
+nsSetKeyUsageExtension(CRMFCertRequest *crmfReq,
+                       unsigned char   keyUsage)
+{
+  SECItem                 *encodedExt= nullptr;
+  SECItem                  keyUsageValue = { (SECItemType) 0, nullptr, 0 };
+  SECItem                  bitsmap = { (SECItemType) 0, nullptr, 0 };
+  SECStatus                srv;
+  CRMFCertExtension       *ext = nullptr;
+  CRMFCertExtCreationInfo  extAddParams;
+  SEC_ASN1Template         bitStrTemplate = {SEC_ASN1_BIT_STRING, 0, nullptr,
+                                             sizeof(SECItem)};
+
+  keyUsageValue.data = &keyUsage;
+  keyUsageValue.len  = 1;
+  nsPrepareBitStringForEncoding(&bitsmap, &keyUsageValue);
+
+  encodedExt = SEC_ASN1EncodeItem(nullptr, nullptr, &bitsmap,&bitStrTemplate);
+  if (!encodedExt) {
+    goto loser;
+  }
+  ext = CRMF_CreateCertExtension(SEC_OID_X509_KEY_USAGE, true, encodedExt);
+  if (!ext) {
+      goto loser;
+  }
+  extAddParams.numExtensions = 1;
+  extAddParams.extensions = &ext;
+  srv = CRMF_CertRequestSetTemplateField(crmfReq, crmfExtension,
+                                         &extAddParams);
+  if (srv != SECSuccess) {
+      goto loser;
+  }
+  CRMF_DestroyCertExtension(ext);
+  SECITEM_FreeItem(encodedExt, true);
+  return NS_OK;
+ loser:
+  if (ext) {
+    CRMF_DestroyCertExtension(ext);
+  }
+  if (encodedExt) {
+      SECITEM_FreeItem(encodedExt, true);
+  }
+  return NS_ERROR_FAILURE;
+}
+
+static nsresult
+nsSetRSADualUse(CRMFCertRequest *crmfReq)
+{
+  unsigned char keyUsage =   KU_DIGITAL_SIGNATURE
+                           | KU_NON_REPUDIATION
+                           | KU_KEY_ENCIPHERMENT;
+
+  return nsSetKeyUsageExtension(crmfReq, keyUsage);
+}
+
+static nsresult
+nsSetRSAKeyEx(CRMFCertRequest *crmfReq)
+{
+  unsigned char keyUsage = KU_KEY_ENCIPHERMENT;
+
+  return nsSetKeyUsageExtension(crmfReq, keyUsage);
+}
+
+static nsresult
+nsSetRSASign(CRMFCertRequest *crmfReq)
+{
+  unsigned char keyUsage = KU_DIGITAL_SIGNATURE;
+
+
+  return nsSetKeyUsageExtension(crmfReq, keyUsage);
+}
+
+static nsresult
+nsSetRSANonRepudiation(CRMFCertRequest *crmfReq)
+{
+  unsigned char keyUsage = KU_NON_REPUDIATION;
+
+  return nsSetKeyUsageExtension(crmfReq, keyUsage);
+}
+
+static nsresult
+nsSetRSASignNonRepudiation(CRMFCertRequest *crmfReq)
+{
+  unsigned char keyUsage = KU_DIGITAL_SIGNATURE |
+                           KU_NON_REPUDIATION;
+
+  return nsSetKeyUsageExtension(crmfReq, keyUsage);
+}
+
+static nsresult
+nsSetECDualUse(CRMFCertRequest *crmfReq)
+{
+  unsigned char keyUsage =   KU_DIGITAL_SIGNATURE
+                           | KU_NON_REPUDIATION
+                           | KU_KEY_AGREEMENT;
+
+  return nsSetKeyUsageExtension(crmfReq, keyUsage);
+}
+
+static nsresult
+nsSetECKeyEx(CRMFCertRequest *crmfReq)
+{
+  unsigned char keyUsage = KU_KEY_AGREEMENT;
+
+  return nsSetKeyUsageExtension(crmfReq, keyUsage);
+}
+
+static nsresult
+nsSetECSign(CRMFCertRequest *crmfReq)
+{
+  unsigned char keyUsage = KU_DIGITAL_SIGNATURE;
+
+
+  return nsSetKeyUsageExtension(crmfReq, keyUsage);
+}
+
+static nsresult
+nsSetECNonRepudiation(CRMFCertRequest *crmfReq)
+{
+  unsigned char keyUsage = KU_NON_REPUDIATION;
+
+  return nsSetKeyUsageExtension(crmfReq, keyUsage);
+}
+
+static nsresult
+nsSetECSignNonRepudiation(CRMFCertRequest *crmfReq)
+{
+  unsigned char keyUsage = KU_DIGITAL_SIGNATURE |
+                           KU_NON_REPUDIATION;
+
+  return nsSetKeyUsageExtension(crmfReq, keyUsage);
+}
+
+static nsresult
+nsSetDH(CRMFCertRequest *crmfReq)
+{
+  unsigned char keyUsage = KU_KEY_AGREEMENT;
+
+  return nsSetKeyUsageExtension(crmfReq, keyUsage);
+}
+
+static nsresult
+nsSetDSASign(CRMFCertRequest *crmfReq)
+{
+  unsigned char keyUsage = KU_DIGITAL_SIGNATURE;
+
+  return nsSetKeyUsageExtension(crmfReq, keyUsage);
+}
+
+static nsresult
+nsSetDSANonRepudiation(CRMFCertRequest *crmfReq)
+{
+  unsigned char keyUsage = KU_NON_REPUDIATION;
+
+  return nsSetKeyUsageExtension(crmfReq, keyUsage);
+}
+
+static nsresult
+nsSetDSASignNonRepudiation(CRMFCertRequest *crmfReq)
+{
+  unsigned char keyUsage = KU_DIGITAL_SIGNATURE |
+                           KU_NON_REPUDIATION;
+
+  return nsSetKeyUsageExtension(crmfReq, keyUsage);
+}
+
+static nsresult
+nsSetKeyUsageExtension(CRMFCertRequest *crmfReq, nsKeyGenType keyGenType)
+{
+  nsresult rv;
+
+  switch (keyGenType) {
+  case rsaDualUse:
+    rv = nsSetRSADualUse(crmfReq);
+    break;
+  case rsaEnc:
+    rv = nsSetRSAKeyEx(crmfReq);
+    break;
+  case rsaSign:
+    rv = nsSetRSASign(crmfReq);
+    break;
+  case rsaNonrepudiation:
+    rv = nsSetRSANonRepudiation(crmfReq);
+    break;
+  case rsaSignNonrepudiation:
+    rv = nsSetRSASignNonRepudiation(crmfReq);
+    break;
+  case ecDualUse:
+    rv = nsSetECDualUse(crmfReq);
+    break;
+  case ecEnc:
+    rv = nsSetECKeyEx(crmfReq);
+    break;
+  case ecSign:
+    rv = nsSetECSign(crmfReq);
+    break;
+  case ecNonrepudiation:
+    rv = nsSetECNonRepudiation(crmfReq);
+    break;
+  case ecSignNonrepudiation:
+    rv = nsSetECSignNonRepudiation(crmfReq);
+    break;
+  case dhEx:
+    rv = nsSetDH(crmfReq);
+    break;
+  case dsaSign:
+    rv = nsSetDSASign(crmfReq);
+    break;
+  case dsaNonrepudiation:
+    rv = nsSetDSANonRepudiation(crmfReq);
+    break;
+  case dsaSignNonrepudiation:
+    rv = nsSetDSASignNonRepudiation(crmfReq);
+    break;
+  default:
+    rv = NS_ERROR_FAILURE;
+    break;
+  }
+  return rv;
+}
+
+//Create a single CRMFCertRequest with all of the necessary parts 
+//already installed.  The request returned by this function will
+//have all the parts necessary and can just be added to a 
+//Certificate Request Message.
+static CRMFCertRequest*
+nsCreateSingleCertReq(nsKeyPairInfo *keyInfo, char *reqDN, char *regToken, 
+                      char *authenticator, nsNSSCertificate *wrappingCert)
+{
+  uint32_t reqID;
+  nsresult rv;
+
+  //The draft says the ID of the request should be a random
+  //number.  We don't have a way of tracking this number
+  //to compare when the reply actually comes back,though.
+  PK11_GenerateRandom((unsigned char*)&reqID, sizeof(reqID));
+  CRMFCertRequest *certReq = CRMF_CreateCertRequest(reqID);
+  if (!certReq)
+    return nullptr;
+
+  long version = SEC_CERTIFICATE_VERSION_3;
+  SECStatus srv;
+  CERTSubjectPublicKeyInfo *spki = nullptr;
+  srv = CRMF_CertRequestSetTemplateField(certReq, crmfVersion, &version);
+  if (srv != SECSuccess)
+    goto loser;
+  
+  spki = SECKEY_CreateSubjectPublicKeyInfo(keyInfo->pubKey);
+  if (!spki)
+    goto loser;
+
+  srv = CRMF_CertRequestSetTemplateField(certReq, crmfPublicKey, spki);
+  SECKEY_DestroySubjectPublicKeyInfo(spki);
+  if (srv != SECSuccess)
+    goto loser;
+
+  if (wrappingCert && ns_can_escrow(keyInfo->keyGenType)) {
+    rv = nsSetEscrowAuthority(certReq, keyInfo, wrappingCert);
+    if (NS_FAILED(rv))
+      goto loser;
+  }
+  rv = nsSetDNForRequest(certReq, reqDN);
+  if (NS_FAILED(rv))
+    goto loser;
+
+  rv = nsSetRegToken(certReq, regToken);
+  if (NS_FAILED(rv))
+    goto loser;
+
+  rv = nsSetAuthenticator(certReq, authenticator);
+  if (NS_FAILED(rv))
+    goto loser;
+
+ rv = nsSetKeyUsageExtension(certReq, keyInfo->keyGenType); 
+  if (NS_FAILED(rv))
+    goto loser;
+
+  return certReq;
+loser:
+  if (certReq) {
+    CRMF_DestroyCertRequest(certReq);
+  }
+  return nullptr;
+}
+
+/*
+ * This function will set the Proof Of Possession (POP) for a request
+ * associated with a key pair intended to do Key Encipherment.  Currently
+ * this means encryption only keys.
+ */
+static nsresult
+nsSetKeyEnciphermentPOP(CRMFCertReqMsg *certReqMsg, bool isEscrowed)
+{
+  SECItem       bitString;
+  unsigned char der[2];
+  SECStatus     srv;
+
+  if (isEscrowed) {
+    /* For proof of possession on escrowed keys, we use the
+     * this Message option of POPOPrivKey and include a zero
+     * length bit string in the POP field.  This is OK because the encrypted
+     * private key already exists as part of the PKIArchiveOptions
+     * Control and that for all intents and purposes proves that
+     * we do own the private key.
+     */
+    der[0] = 0x03; /*We've got a bit string          */
+    der[1] = 0x00; /*We've got a 0 length bit string */
+    bitString.data = der;
+    bitString.len  = 2;
+    srv = CRMF_CertReqMsgSetKeyEnciphermentPOP(certReqMsg, crmfThisMessage,
+                                              crmfNoSubseqMess, &bitString);
+  } else {
+    /* If the encryption key is not being escrowed, then we set the 
+     * Proof Of Possession to be a Challenge Response mechanism.
+     */
+    srv = CRMF_CertReqMsgSetKeyEnciphermentPOP(certReqMsg,
+                                              crmfSubsequentMessage,
+                                              crmfChallengeResp, nullptr);
+  }
+  return (srv == SECSuccess) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+static void
+nsCRMFEncoderItemCount(void *arg, const char *buf, unsigned long len);
+
+static void
+nsCRMFEncoderItemStore(void *arg, const char *buf, unsigned long len);
+
+static nsresult
+nsSet_EC_DHMAC_ProofOfPossession(CRMFCertReqMsg *certReqMsg, 
+                                 nsKeyPairInfo  *keyInfo,
+                                 CRMFCertRequest *certReq)
+{
+  // RFC 2511 Appendix A section 2 a) defines,
+  // the "text" input for HMAC shall be the DER encoded version of
+  // of the single cert request.
+  // We'll produce that encoding and destroy it afterwards,
+  // because when sending the complete package to the CA,
+  // we'll use a different encoding, one that includes POP and
+  // allows multiple requests to be sent in one step.
+
+  unsigned long der_request_len = 0;
+  ScopedSECItem der_request;
+
+  if (SECSuccess != CRMF_EncodeCertRequest(certReq, 
+                                           nsCRMFEncoderItemCount, 
+                                           &der_request_len))
+    return NS_ERROR_FAILURE;
+
+  der_request = SECITEM_AllocItem(nullptr, nullptr, der_request_len);
+  if (!der_request)
+    return NS_ERROR_FAILURE;
+
+  // set len in returned SECItem back to zero, because it will
+  // be used as the destination offset inside the 
+  // nsCRMFEncoderItemStore callback.
+
+  der_request->len = 0;
+
+  if (SECSuccess != CRMF_EncodeCertRequest(certReq, 
+                                           nsCRMFEncoderItemStore, 
+                                           der_request))
+    return NS_ERROR_FAILURE;
+
+  // RFC 2511 Appendix A section 2 c):
+  // "A key K is derived from the shared secret Kec and the subject and
+  //  issuer names in the CA's certificate as follows:
+  //  K = SHA1(DER-encoded-subjectName | Kec | DER-encoded-issuerName)"
+
+  ScopedPK11SymKey shared_secret;
+  ScopedPK11SymKey subject_and_secret;
+  ScopedPK11SymKey subject_and_secret_and_issuer;
+  ScopedPK11SymKey sha1_of_subject_and_secret_and_issuer;
+
+  shared_secret = 
+    PK11_PubDeriveWithKDF(keyInfo->privKey, // SECKEYPrivateKey *privKey
+                          keyInfo->ecPopPubKey,  // SECKEYPublicKey *pubKey
+                          false, // bool isSender
+                          nullptr, // SECItem *randomA
+                          nullptr, // SECItem *randomB
+                          CKM_ECDH1_DERIVE, // CK_MECHANISM_TYPE derive
+                          CKM_CONCATENATE_DATA_AND_BASE, // CK_MECHANISM_TYPE target
+                          CKA_DERIVE, // CK_ATTRIBUTE_TYPE operation
+                          0, // int keySize
+                          CKD_NULL, // CK_ULONG kdf
+                          nullptr, // SECItem *sharedData
+                          nullptr); // void *wincx
+
+  if (!shared_secret)
+    return NS_ERROR_FAILURE;
+
+  CK_KEY_DERIVATION_STRING_DATA concat_data_base;
+  concat_data_base.pData = keyInfo->ecPopCert->derSubject.data;
+  concat_data_base.ulLen = keyInfo->ecPopCert->derSubject.len;
+  SECItem concat_data_base_item;
+  concat_data_base_item.data = (unsigned char*)&concat_data_base;
+  concat_data_base_item.len = sizeof(CK_KEY_DERIVATION_STRING_DATA);
+
+  subject_and_secret =
+    PK11_Derive(shared_secret, // PK11SymKey *baseKey
+                CKM_CONCATENATE_DATA_AND_BASE, // CK_MECHANISM_TYPE mechanism
+                &concat_data_base_item, // SECItem *param
+                CKM_CONCATENATE_BASE_AND_DATA, // CK_MECHANISM_TYPE target
+                CKA_DERIVE, // CK_ATTRIBUTE_TYPE operation
+                0); // int keySize
+
+  if (!subject_and_secret)
+    return NS_ERROR_FAILURE;
+
+  CK_KEY_DERIVATION_STRING_DATA concat_base_data;
+  concat_base_data.pData = keyInfo->ecPopCert->derSubject.data;
+  concat_base_data.ulLen = keyInfo->ecPopCert->derSubject.len;
+  SECItem concat_base_data_item;
+  concat_base_data_item.data = (unsigned char*)&concat_base_data;
+  concat_base_data_item.len = sizeof(CK_KEY_DERIVATION_STRING_DATA);
+
+  subject_and_secret_and_issuer =
+    PK11_Derive(subject_and_secret, // PK11SymKey *baseKey
+                CKM_CONCATENATE_BASE_AND_DATA, // CK_MECHANISM_TYPE mechanism
+                &concat_base_data_item, // SECItem *param
+                CKM_SHA1_KEY_DERIVATION, // CK_MECHANISM_TYPE target
+                CKA_DERIVE, // CK_ATTRIBUTE_TYPE operation
+                0); // int keySize
+
+  if (!subject_and_secret_and_issuer)
+    return NS_ERROR_FAILURE;
+
+  sha1_of_subject_and_secret_and_issuer =
+    PK11_Derive(subject_and_secret_and_issuer, // PK11SymKey *baseKey
+                CKM_SHA1_KEY_DERIVATION, // CK_MECHANISM_TYPE mechanism
+                nullptr, // SECItem *param
+                CKM_SHA_1_HMAC, // CK_MECHANISM_TYPE target
+                CKA_SIGN, // CK_ATTRIBUTE_TYPE operation
+                0); // int keySize
+
+  if (!sha1_of_subject_and_secret_and_issuer)
+    return NS_ERROR_FAILURE;
+
+  PK11Context *context = nullptr;
+  PK11ContextCleanerTrueParam context_cleaner(context);
+
+  SECItem ignore;
+  ignore.data = 0;
+  ignore.len = 0;
+
+  context = 
+    PK11_CreateContextBySymKey(CKM_SHA_1_HMAC, // CK_MECHANISM_TYPE type
+                               CKA_SIGN, // CK_ATTRIBUTE_TYPE operation
+                               sha1_of_subject_and_secret_and_issuer, // PK11SymKey *symKey
+                               &ignore); // SECItem *param
+
+  if (!context)
+    return NS_ERROR_FAILURE;
+
+  if (SECSuccess != PK11_DigestBegin(context))
+    return NS_ERROR_FAILURE;
+
+  if (SECSuccess != 
+      PK11_DigestOp(context, der_request->data, der_request->len))
+    return NS_ERROR_FAILURE;
+
+  ScopedAutoSECItem result_hmac_sha1_item(SHA1_LENGTH);
+
+  if (SECSuccess !=
+      PK11_DigestFinal(context, 
+                       result_hmac_sha1_item.data, 
+                       &result_hmac_sha1_item.len, 
+                       SHA1_LENGTH))
+    return NS_ERROR_FAILURE;
+
+  if (SECSuccess !=
+      CRMF_CertReqMsgSetKeyAgreementPOP(certReqMsg, crmfDHMAC,
+                                        crmfNoSubseqMess, &result_hmac_sha1_item))
+    return NS_ERROR_FAILURE;
+
+  return NS_OK;
+}
+
+static nsresult
+nsSetProofOfPossession(CRMFCertReqMsg *certReqMsg, 
+                       nsKeyPairInfo  *keyInfo,
+                       CRMFCertRequest *certReq)
+{
+  // Depending on the type of cert request we'll try
+  // POP mechanisms in different order,
+  // and add the result to the cert request message.
+  //
+  // For any signing or dual use cert,
+  //   try signing first,
+  //   fall back to DHMAC if we can
+  //     (EC cert requests that provide keygen param "popcert"),
+  //   otherwise fail.
+  //
+  // For encryption only certs that get escrowed, this is sufficient.
+  //
+  // For encryption only certs, that are not being escrowed, 
+  //   try DHMAC if we can 
+  //     (EC cert requests that provide keygen param "popcert"),
+  //   otherwise we'll indicate challenge response should be used.
+  
+  bool isEncryptionOnlyCertRequest = false;
+  bool escrowEncryptionOnlyCert = false;
+  
+  switch (keyInfo->keyGenType)
+  {
+    case rsaEnc:
+    case ecEnc:
+      isEncryptionOnlyCertRequest = true;
+      break;
+    
+    case rsaSign:
+    case rsaDualUse:
+    case rsaNonrepudiation:
+    case rsaSignNonrepudiation:
+    case ecSign:
+    case ecDualUse:
+    case ecNonrepudiation:
+    case ecSignNonrepudiation:
+    case dsaSign:
+    case dsaNonrepudiation:
+    case dsaSignNonrepudiation:
+      break;
+    
+    case dhEx:
+    /* This case may be supported in the future, but for now, we just fall 
+      * though to the default case and return an error for diffie-hellman keys.
+    */
+    default:
+      return NS_ERROR_FAILURE;
+  };
+    
+  if (isEncryptionOnlyCertRequest)
+  {
+    escrowEncryptionOnlyCert = 
+      CRMF_CertRequestIsControlPresent(certReq,crmfPKIArchiveOptionsControl);
+  }
+    
+  bool gotDHMACParameters = false;
+  
+  if (isECKeyGenType(keyInfo->keyGenType) && 
+      keyInfo->ecPopCert && 
+      keyInfo->ecPopPubKey)
+  {
+    gotDHMACParameters = true;
+  }
+  
+  if (isEncryptionOnlyCertRequest)
+  {
+    if (escrowEncryptionOnlyCert)
+      return nsSetKeyEnciphermentPOP(certReqMsg, true); // escrowed
+    
+    if (gotDHMACParameters)
+      return nsSet_EC_DHMAC_ProofOfPossession(certReqMsg, keyInfo, certReq);
+    
+    return nsSetKeyEnciphermentPOP(certReqMsg, false); // not escrowed
+  }
+  
+  // !isEncryptionOnlyCertRequest
+  
+  SECStatus srv = CRMF_CertReqMsgSetSignaturePOP(certReqMsg,
+                                                 keyInfo->privKey,
+                                                 keyInfo->pubKey, nullptr,
+                                                 nullptr, nullptr);
+
+  if (srv == SECSuccess)
+    return NS_OK;
+  
+  if (!gotDHMACParameters)
+    return NS_ERROR_FAILURE;
+  
+  return nsSet_EC_DHMAC_ProofOfPossession(certReqMsg, keyInfo, certReq);
+}
+
+static void
+nsCRMFEncoderItemCount(void *arg, const char *buf, unsigned long len)
+{
+  unsigned long *count = (unsigned long *)arg;
+  *count += len;
+}
+
+static void
+nsCRMFEncoderItemStore(void *arg, const char *buf, unsigned long len)
+{
+  SECItem *dest = (SECItem *)arg;
+  memcpy(dest->data + dest->len, buf, len);
+  dest->len += len;
+}
+
+static SECItem*
+nsEncodeCertReqMessages(CRMFCertReqMsg **certReqMsgs)
+{
+  unsigned long len = 0;
+  if (CRMF_EncodeCertReqMessages(certReqMsgs, nsCRMFEncoderItemCount, &len)
+      != SECSuccess) {
+    return nullptr;
+  }
+  SECItem *dest = (SECItem *)PORT_Alloc(sizeof(SECItem));
+  if (!dest) {
+    return nullptr;
+  }
+  dest->type = siBuffer;
+  dest->data = (unsigned char *)PORT_Alloc(len);
+  if (!dest->data) {
+    PORT_Free(dest);
+    return nullptr;
+  }
+  dest->len = 0;
+
+  if (CRMF_EncodeCertReqMessages(certReqMsgs, nsCRMFEncoderItemStore, dest)
+      != SECSuccess) {
+    SECITEM_FreeItem(dest, true);
+    return nullptr;
+  }
+  return dest;
+}
+
+//Create a Base64 encoded CRMFCertReqMsg that can be sent to a CA
+//requesting one or more certificates to be issued.  This function
+//creates a single cert request per key pair and then appends it to
+//a message that is ultimately sent off to a CA.
+static char*
+nsCreateReqFromKeyPairs(nsKeyPairInfo *keyids, int32_t numRequests,
+                        char *reqDN, char *regToken, char *authenticator,
+                        nsNSSCertificate *wrappingCert) 
+{
+  // We'use the goto notation for clean-up purposes in this function
+  // that calls the C API of NSS.
+  int32_t i;
+  // The ASN1 encoder in NSS wants the last entry in the array to be
+  // nullptr so that it knows when the last element is.
+  CRMFCertReqMsg **certReqMsgs = new CRMFCertReqMsg*[numRequests+1];
+  CRMFCertRequest *certReq;
+  if (!certReqMsgs)
+    return nullptr;
+  memset(certReqMsgs, 0, sizeof(CRMFCertReqMsg*)*(1+numRequests));
+  SECStatus srv;
+  nsresult rv;
+  SECItem *encodedReq;
+  char *retString;
+  for (i=0; i<numRequests; i++) {
+    certReq = nsCreateSingleCertReq(&keyids[i], reqDN, regToken, authenticator,
+                                    wrappingCert);
+    if (!certReq)
+      goto loser;
+
+    certReqMsgs[i] = CRMF_CreateCertReqMsg();
+    if (!certReqMsgs[i])
+      goto loser;
+    srv = CRMF_CertReqMsgSetCertRequest(certReqMsgs[i], certReq);
+    if (srv != SECSuccess)
+      goto loser;
+
+    rv = nsSetProofOfPossession(certReqMsgs[i], &keyids[i], certReq);
+    if (NS_FAILED(rv))
+      goto loser;
+    CRMF_DestroyCertRequest(certReq);
+  }
+  encodedReq = nsEncodeCertReqMessages(certReqMsgs);
+  nsFreeCertReqMessages(certReqMsgs, numRequests);
+
+  retString = NSSBase64_EncodeItem (nullptr, nullptr, 0, encodedReq);
+  SECITEM_FreeItem(encodedReq, true);
+  return retString;
+loser:
+  nsFreeCertReqMessages(certReqMsgs,numRequests);
+  return nullptr;
+}
+
+//The top level method which is a member of nsIDOMCrypto
+//for generate a base64 encoded CRMF request.
+CRMFObject*
+nsCrypto::GenerateCRMFRequest(JSContext* aContext,
+                              const nsCString& aReqDN,
+                              const nsCString& aRegToken,
+                              const nsCString& aAuthenticator,
+                              const nsCString& aEaCert,
+                              const nsCString& aJsCallback,
+                              const Sequence<JS::Value>& aArgs,
+                              ErrorResult& aRv)
+{
+  nsNSSShutDownPreventionLock locker;
+  nsresult nrv;
+
+  uint32_t argc = aArgs.Length();
+
+  /*
+   * Get all of the parameters.
+   */
+  if (argc % 3 != 0) {
+    aRv.ThrowNotEnoughArgsError();
+    return nullptr;
+  }
+
+  if (aReqDN.IsVoid()) {
+    NS_WARNING("no DN specified");
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  if (aJsCallback.IsVoid()) {
+    NS_WARNING("no completion function specified");
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(GetParentObject());
+  if (MOZ_UNLIKELY(!globalObject)) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIContentSecurityPolicy> csp;
+  if (!nsContentUtils::GetContentSecurityPolicy(getter_AddRefs(csp))) {
+    NS_ERROR("Error: failed to get CSP");
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  bool evalAllowed = true;
+  bool reportEvalViolations = false;
+  if (csp && NS_FAILED(csp->GetAllowsEval(&reportEvalViolations, &evalAllowed))) {
+    NS_WARNING("CSP: failed to get allowsEval");
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  if (reportEvalViolations) {
+    NS_NAMED_LITERAL_STRING(scriptSample, "window.crypto.generateCRMFRequest: call to eval() or related function blocked by CSP");
+
+    const char *fileName;
+    uint32_t lineNum;
+    nsJSUtils::GetCallingLocation(aContext, &fileName, &lineNum);
+    csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
+                             NS_ConvertASCIItoUTF16(fileName),
+                             scriptSample,
+                             lineNum,
+                             EmptyString(),
+                             EmptyString());
+  }
+
+  if (!evalAllowed) {
+    NS_WARNING("eval() not allowed by Content Security Policy");
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  //Put up some UI warning that someone is trying to
+  //escrow the private key.
+  //Don't addref this copy.  That way ths reference goes away
+  //at the same the nsIX09Cert ref goes away.
+  nsNSSCertificate *escrowCert = nullptr;
+  nsCOMPtr<nsIX509Cert> nssCert;
+  bool willEscrow = false;
+  if (!aEaCert.IsVoid()) {
+    SECItem certDer = {siBuffer, nullptr, 0};
+    SECStatus srv = ATOB_ConvertAsciiToItem(&certDer, aEaCert.get());
+    if (srv != SECSuccess) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return nullptr;
+    }
+    ScopedCERTCertificate cert(
+      CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
+                              &certDer, nullptr, false, true));
+    if (!cert) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return nullptr;
+    }
+
+    escrowCert = nsNSSCertificate::Create(cert.get());
+    nssCert = escrowCert;
+    if (!nssCert) {
+      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return nullptr;
+    }
+
+    nsCOMPtr<nsIDOMCryptoDialogs> dialogs;
+    nsresult rv = getNSSDialogs(getter_AddRefs(dialogs),
+                                NS_GET_IID(nsIDOMCryptoDialogs),
+                                NS_DOMCRYPTODIALOGS_CONTRACTID);
+    if (NS_FAILED(rv)) {
+      aRv.Throw(rv);
+      return nullptr;
+    }
+
+    bool okay=false;
+    {
+      nsPSMUITracker tracker;
+      if (tracker.isUIForbidden()) {
+        okay = false;
+      }
+      else {
+        dialogs->ConfirmKeyEscrow(nssCert, &okay);
+      }
+    }
+    if (!okay) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return nullptr;
+    }
+    willEscrow = true;
+  }
+  nsCOMPtr<nsIInterfaceRequestor> uiCxt = new PipUIContext;
+  int32_t numRequests = argc / 3;
+  nsKeyPairInfo *keyids = new nsKeyPairInfo[numRequests];
+  memset(keyids, 0, sizeof(nsKeyPairInfo)*numRequests);
+  int keyInfoIndex;
+  uint32_t i;
+  PK11SlotInfo *slot = nullptr;
+  // Go through all of the arguments and generate the appropriate key pairs.
+  for (i=0,keyInfoIndex=0; i<argc; i+=3,keyInfoIndex++) {
+    nrv = cryptojs_ReadArgsAndGenerateKey(aContext,
+                                          const_cast<JS::Value*>(&aArgs[i]),
+                                          &keyids[keyInfoIndex],
+                                          uiCxt, &slot, willEscrow);
+
+    if (NS_FAILED(nrv)) {
+      if (slot)
+        PK11_FreeSlot(slot);
+      nsFreeKeyPairInfo(keyids,numRequests);
+      aRv.Throw(nrv);
+      return nullptr;
+    }
+  }
+  // By this time we'd better have a slot for the key gen.
+  NS_ASSERTION(slot, "There was no slot selected for key generation");
+  if (slot)
+    PK11_FreeSlot(slot);
+
+  char *encodedRequest = nsCreateReqFromKeyPairs(keyids,numRequests,
+                                                 const_cast<char*>(aReqDN.get()),
+                                                 const_cast<char*>(aRegToken.get()),
+                                                 const_cast<char*>(aAuthenticator.get()),
+                                                 escrowCert);
+  if (!encodedRequest) {
+    nsFreeKeyPairInfo(keyids, numRequests);
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+  CRMFObject* newObject = new CRMFObject();
+  newObject->SetCRMFRequest(encodedRequest);
+  PORT_Free(encodedRequest);
+  nsFreeKeyPairInfo(keyids, numRequests);
+
+  // Post an event on the UI queue so that the JS gets called after
+  // we return control to the JS layer.  Why do we have to this?
+  // Because when this API was implemented for PSM 1.x w/ Communicator,
+  // the only way to make this method work was to have a callback
+  // in the JS layer that got called after key generation had happened.
+  // So for backwards compatibility, we return control and then just post
+  // an event to call the JS the script provides as the code to execute
+  // when the request has been generated.
+  //
+
+  MOZ_ASSERT(nsContentUtils::GetCurrentJSContext());
+  nsCryptoRunArgs *args = new nsCryptoRunArgs();
+
+  args->m_globalObject = globalObject;
+  if (!aJsCallback.IsVoid()) {
+    args->m_jsCallback = aJsCallback;
+  }
+
+  nsRefPtr<nsCryptoRunnable> cryptoRunnable(new nsCryptoRunnable(args));
+
+  nsresult rv = NS_DispatchToMainThread(cryptoRunnable);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
+
+  return newObject;
+}
+
+// Reminder that we inherit the memory passed into us here.
+// An implementation to let us back up certs as an event.
+nsP12Runnable::nsP12Runnable(nsIX509Cert **certArr, int32_t numCerts,
+                             nsIPK11Token *token)
+{
+  mCertArr  = certArr;
+  mNumCerts = numCerts;
+  mToken = token;
+}
+
+nsP12Runnable::~nsP12Runnable()
+{
+  int32_t i;
+  for (i=0; i<mNumCerts; i++) {
+      NS_IF_RELEASE(mCertArr[i]);
+  }
+  delete []mCertArr;
+}
+
+
+//Implementation that backs cert(s) into a PKCS12 file
+NS_IMETHODIMP
+nsP12Runnable::Run()
+{
+  NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
+
+  NS_ASSERTION(NS_IsMainThread(), "nsP12Runnable dispatched to the wrong thread");
+
+  nsNSSShutDownPreventionLock locker;
+  NS_ASSERTION(mCertArr, "certArr is NULL while trying to back up");
+
+  nsString final;
+  nsString temp;
+  nsresult rv;
+
+  nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
+  if (NS_FAILED(rv))
+    return rv;
+
+  //Build up the message that let's the user know we're trying to 
+  //make PKCS12 backups of the new certs.
+  nssComponent->GetPIPNSSBundleString("ForcedBackup1", final);
+  final.Append(MOZ_UTF16("\n\n"));
+  nssComponent->GetPIPNSSBundleString("ForcedBackup2", temp);
+  final.Append(temp.get());
+  final.Append(MOZ_UTF16("\n\n"));
+
+  nssComponent->GetPIPNSSBundleString("ForcedBackup3", temp);
+
+  final.Append(temp.get());
+  nsNSSComponent::ShowAlertWithConstructedString(final);
+
+  nsCOMPtr<nsIFilePicker> filePicker = 
+                        do_CreateInstance("@mozilla.org/filepicker;1", &rv);
+  if (!filePicker) {
+    NS_ERROR("Could not create a file picker when backing up certs.");
+    return rv;
+  }
+
+  nsCOMPtr<nsIWindowWatcher> wwatch =
+    (do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIDOMWindow> window;
+  wwatch->GetActiveWindow(getter_AddRefs(window));
+
+  nsString filePickMessage;
+  nssComponent->GetPIPNSSBundleString("chooseP12BackupFileDialog",
+                                      filePickMessage);
+  rv = filePicker->Init(window, filePickMessage, nsIFilePicker::modeSave);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  filePicker->AppendFilter(NS_LITERAL_STRING("PKCS12"),
+                           NS_LITERAL_STRING("*.p12"));
+  filePicker->AppendFilters(nsIFilePicker::filterAll);
+
+  int16_t dialogReturn;
+  filePicker->Show(&dialogReturn);
+  if (dialogReturn == nsIFilePicker::returnCancel)
+    return NS_OK;  //User canceled.  It'd be nice if they couldn't, 
+                   //but oh well.
+
+  nsCOMPtr<nsIFile> localFile;
+  rv = filePicker->GetFile(getter_AddRefs(localFile));
+  if (NS_FAILED(rv))
+    return NS_ERROR_FAILURE;
+
+  nsPKCS12Blob p12Cxt;
+  
+  p12Cxt.SetToken(mToken);
+  p12Cxt.ExportToFile(localFile, mCertArr, mNumCerts);
+  return NS_OK;
+}
+
+nsCryptoRunArgs::nsCryptoRunArgs() {}
+
+nsCryptoRunArgs::~nsCryptoRunArgs() {}
+
+nsCryptoRunnable::nsCryptoRunnable(nsCryptoRunArgs *args)
+{
+  nsNSSShutDownPreventionLock locker;
+  NS_ASSERTION(args,"Passed nullptr to nsCryptoRunnable constructor.");
+  m_args = args;
+  NS_IF_ADDREF(m_args);
+}
+
+nsCryptoRunnable::~nsCryptoRunnable()
+{
+  nsNSSShutDownPreventionLock locker;
+  NS_IF_RELEASE(m_args);
+}
+
+//Implementation that runs the callback passed to 
+//crypto.generateCRMFRequest as an event.
+NS_IMETHODIMP
+nsCryptoRunnable::Run()
+{
+  nsNSSShutDownPreventionLock locker;
+
+  // We're going to run script via JS_EvaluateScript, so we need an
+  // AutoEntryScript. This is Gecko specific and not on a standards track.
+  AutoEntryScript aes(m_args->m_globalObject);
+  JSContext* cx = aes.cx();
+  JS::RootedObject scope(cx, JS::CurrentGlobalOrNull(cx));
+
+  bool ok =
+    JS_EvaluateScript(cx, scope, m_args->m_jsCallback,
+                      strlen(m_args->m_jsCallback), nullptr, 0);
+  return ok ? NS_OK : NS_ERROR_FAILURE;
+}
+
+//Quick helper function to check if a newly issued cert
+//already exists in the user's database.
+static bool
+nsCertAlreadyExists(SECItem *derCert)
+{
+  CERTCertDBHandle *handle = CERT_GetDefaultCertDB();
+  bool retVal = false;
+
+  ScopedCERTCertificate cert(CERT_FindCertByDERCert(handle, derCert));
+  if (cert) {
+    if (cert->isperm && !cert->nickname && !cert->emailAddr) {
+      //If the cert doesn't have a nickname or email addr, it is
+      //bogus cruft, so delete it.
+      SEC_DeletePermCertificate(cert.get());
+    } else if (cert->isperm) {
+      retVal = true;
+    }
+  }
+  return retVal;
+}
+
+static int32_t
+nsCertListCount(CERTCertList *certList)
+{
+  int32_t numCerts = 0;
+  CERTCertListNode *node;
+
+  node = CERT_LIST_HEAD(certList);
+  while (!CERT_LIST_END(node, certList)) {
+    numCerts++;
+    node = CERT_LIST_NEXT(node);
+  }
+  return numCerts;
+}
+
+//Import user certificates that arrive as a CMMF base64 encoded
+//string.
+void
+nsCrypto::ImportUserCertificates(const nsAString& aNickname,
+                                 const nsAString& aCmmfResponse,
+                                 bool aDoForcedBackup,
+                                 nsAString& aReturn,
+                                 ErrorResult& aRv)
+{
+  nsNSSShutDownPreventionLock locker;
+  char *nickname=nullptr, *cmmfResponse=nullptr;
+  CMMFCertRepContent *certRepContent = nullptr;
+  int numResponses = 0;
+  nsIX509Cert **certArr = nullptr;
+  int i;
+  CMMFCertResponse *currResponse;
+  CMMFPKIStatus reqStatus;
+  CERTCertificate *currCert;
+  PK11SlotInfo *slot;
+  nsAutoCString localNick;
+  nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
+  nsresult rv = NS_OK;
+  nsCOMPtr<nsIPK11Token> token;
+
+  nickname = ToNewCString(aNickname);
+  cmmfResponse = ToNewCString(aCmmfResponse);
+  if (nsCRT::strcmp("null", nickname) == 0) {
+    nsMemory::Free(nickname);
+    nickname = nullptr;
+  }
+
+  SECItem cmmfDer = {siBuffer, nullptr, 0};
+  SECStatus srv = ATOB_ConvertAsciiToItem(&cmmfDer, cmmfResponse);
+
+  if (srv != SECSuccess) {
+    rv = NS_ERROR_FAILURE;
+    goto loser;
+  }
+
+  certRepContent = CMMF_CreateCertRepContentFromDER(CERT_GetDefaultCertDB(),
+                                                    (const char*)cmmfDer.data,
+                                                    cmmfDer.len);
+  if (!certRepContent) {
+    rv = NS_ERROR_FAILURE;
+    goto loser;
+  }
+
+  numResponses = CMMF_CertRepContentGetNumResponses(certRepContent);
+
+  if (aDoForcedBackup) {
+    //We've been asked to force the user to back up these
+    //certificates.  Let's keep an array of them around which
+    //we pass along to the nsP12Runnable to use.
+    certArr = new nsIX509Cert*[numResponses];
+    // If this is nullptr, chances are we're gonna fail really
+    // soon, but let's try to keep going just in case.
+    if (!certArr)
+      aDoForcedBackup = false;
+
+    memset(certArr, 0, sizeof(nsIX509Cert*)*numResponses);
+  }
+  for (i=0; i<numResponses; i++) {
+    currResponse = CMMF_CertRepContentGetResponseAtIndex(certRepContent,i);
+    if (!currResponse) {
+      rv = NS_ERROR_FAILURE;
+      goto loser;
+    }
+    reqStatus = CMMF_CertResponseGetPKIStatusInfoStatus(currResponse);
+    if (!(reqStatus == cmmfGranted || reqStatus == cmmfGrantedWithMods)) {
+      // The CA didn't give us the cert we requested.
+      rv = NS_ERROR_FAILURE;
+      goto loser;
+    }
+    currCert = CMMF_CertResponseGetCertificate(currResponse, 
+                                               CERT_GetDefaultCertDB());
+    if (!currCert) {
+      rv = NS_ERROR_FAILURE;
+      goto loser;
+    }
+
+    if (nsCertAlreadyExists(&currCert->derCert)) {
+      if (aDoForcedBackup) {
+        certArr[i] = nsNSSCertificate::Create(currCert);
+        if (!certArr[i])
+          goto loser;
+        NS_ADDREF(certArr[i]);
+      }
+      CERT_DestroyCertificate(currCert);
+      CMMF_DestroyCertResponse(currResponse);
+      continue;
+    }
+    // Let's figure out which nickname to give the cert.  If 
+    // a certificate with the same subject name already exists,
+    // then just use that one, otherwise, get the default nickname.
+    if (currCert->nickname) {
+      localNick = currCert->nickname;
+    }
+    else if (!nickname || nickname[0] == '\0') {
+      nsNSSCertificateDB::get_default_nickname(currCert, ctx, localNick, locker);
+    } else {
+      //This is the case where we're getting a brand new
+      //cert that doesn't have the same subjectName as a cert
+      //that already exists in our db and the CA page has 
+      //designated a nickname to use for the newly issued cert.
+      localNick = nickname;
+    }
+    {
+      char *cast_const_away = const_cast<char*>(localNick.get());
+      slot = PK11_ImportCertForKey(currCert, cast_const_away, ctx);
+    }
+    if (!slot) {
+      rv = NS_ERROR_FAILURE;
+      goto loser;
+    }
+    if (aDoForcedBackup) {
+      certArr[i] = nsNSSCertificate::Create(currCert);
+      if (!certArr[i])
+        goto loser;
+      NS_ADDREF(certArr[i]);
+    }
+    CERT_DestroyCertificate(currCert);
+
+    if (!token)
+      token = new nsPK11Token(slot);
+
+    PK11_FreeSlot(slot);
+    CMMF_DestroyCertResponse(currResponse);
+  }
+  //Let the loser: label take care of freeing up our reference to
+  //nickname (This way we don't free it twice and avoid crashing.
+  //That would be a good thing.
+
+  //Import the root chain into the cert db.
+ {
+  ScopedCERTCertList caPubs(CMMF_CertRepContentGetCAPubs(certRepContent));
+  if (caPubs) {
+    int32_t numCAs = nsCertListCount(caPubs.get());
+    
+    NS_ASSERTION(numCAs > 0, "Invalid number of CA's");
+    if (numCAs > 0) {
+      CERTCertListNode *node;
+      SECItem *derCerts;
+
+      derCerts = static_cast<SECItem*>
+                            (nsMemory::Alloc(sizeof(SECItem)*numCAs));
+      if (!derCerts) {
+        rv = NS_ERROR_OUT_OF_MEMORY;
+        goto loser;
+      }
+      for (node = CERT_LIST_HEAD(caPubs), i=0; 
+           !CERT_LIST_END(node, caPubs);
+           node = CERT_LIST_NEXT(node), i++) {
+        derCerts[i] = node->cert->derCert;
+      }
+      nsNSSCertificateDB::ImportValidCACerts(numCAs, derCerts, ctx, locker);
+      nsMemory::Free(derCerts);
+    }
+  }
+ }
+
+  if (aDoForcedBackup) {
+    // I can't pop up a file picker from the depths of JavaScript,
+    // so I'll just post an event on the UI queue to do the backups
+    // later.
+    nsCOMPtr<nsIRunnable> p12Runnable = new nsP12Runnable(certArr, numResponses,
+                                                          token);
+    if (!p12Runnable) {
+      rv = NS_ERROR_FAILURE;
+      goto loser;
+    }
+
+    // null out the certArr pointer which has now been inherited by
+    // the nsP12Runnable instance so that we don't free up the
+    // memory on the way out.
+    certArr = nullptr;
+
+    rv = NS_DispatchToMainThread(p12Runnable);
+    if (NS_FAILED(rv))
+      goto loser;
+  }
+
+ loser:
+  if (certArr) {
+    for (i=0; i<numResponses; i++) {
+      NS_IF_RELEASE(certArr[i]);
+    }
+    delete []certArr;
+  }
+  aReturn.Assign(EmptyString());
+  if (nickname) {
+    NS_Free(nickname);
+  }
+  if (cmmfResponse) {
+    NS_Free(cmmfResponse);
+  }
+  if (certRepContent) {
+    CMMF_DestroyCertRepContent(certRepContent);
+  }
+
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
+}
+
+static void
+GetDocumentFromContext(JSContext *cx, nsIDocument **aDocument)
+{
+  // Get the script context.
+  nsIScriptContext* scriptContext = GetScriptContextFromJSContext(cx);
+  if (!scriptContext) {
+    return;
+  }
+
+  nsCOMPtr<nsIDOMWindow> domWindow = 
+    do_QueryInterface(scriptContext->GetGlobalObject());
+  if (!domWindow) {
+    return;
+  }
+
+  nsCOMPtr<nsIDOMDocument> domDocument;
+  domWindow->GetDocument(getter_AddRefs(domDocument));
+  if (!domDocument) {
+    return;
+  }
+
+  CallQueryInterface(domDocument, aDocument);
+
+  return;
+}
+
+void signTextOutputCallback(void *arg, const char *buf, unsigned long len)
+{
+  ((nsCString*)arg)->Append(buf, len);
+}
+
+void
+nsCrypto::SignText(JSContext* aContext,
+                   const nsAString& aStringToSign,
+                   const nsAString& aCaOption,
+                   const Sequence<nsCString>& aArgs,
+                   nsAString& aReturn)
+{
+  // XXX This code should return error codes, but we're keeping this
+  //     backwards compatible with NS4.x and so we can't throw exceptions.
+  NS_NAMED_LITERAL_STRING(internalError, "error:internalError");
+
+  aReturn.Truncate();
+
+  uint32_t argc = aArgs.Length();
+
+  if (!aCaOption.EqualsLiteral("auto") &&
+      !aCaOption.EqualsLiteral("ask")) {
+    NS_WARNING("caOption argument must be ask or auto");
+    aReturn.Append(internalError);
+
+    return;
+  }
+
+  // It was decided to always behave as if "ask" were specified.
+  // XXX Should we warn in the JS Console for auto?
+
+  nsCOMPtr<nsIInterfaceRequestor> uiContext = new PipUIContext;
+  if (!uiContext) {
+    aReturn.Append(internalError);
+
+    return;
+  }
+
+  bool bestOnly = true;
+  bool validOnly = true;
+  CERTCertList* certList =
+    CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), certUsageEmailSigner,
+                              bestOnly, validOnly, uiContext);
+
+  uint32_t numCAs = argc;
+  if (numCAs > 0) {
+    nsAutoArrayPtr<char*> caNames(new char*[numCAs]);
+    if (!caNames) {
+      aReturn.Append(internalError);
+      return;
+    }
+
+    uint32_t i;
+    for (i = 0; i < numCAs; ++i)
+      caNames[i] = const_cast<char*>(aArgs[i].get());
+
+    if (certList &&
+        CERT_FilterCertListByCANames(certList, numCAs, caNames,
+                                     certUsageEmailSigner) != SECSuccess) {
+      aReturn.Append(internalError);
+
+      return;
+    }
+  }
+
+  if (!certList || CERT_LIST_EMPTY(certList)) {
+    aReturn.AppendLiteral("error:noMatchingCert");
+
+    return;
+  }
+
+  nsCOMPtr<nsIFormSigningDialog> fsd =
+    do_CreateInstance(NS_FORMSIGNINGDIALOG_CONTRACTID);
+  if (!fsd) {
+    aReturn.Append(internalError);
+
+    return;
+  }
+
+  nsCOMPtr<nsIDocument> document;
+  GetDocumentFromContext(aContext, getter_AddRefs(document));
+  if (!document) {
+    aReturn.Append(internalError);
+
+    return;
+  }
+
+  // Get the hostname from the URL of the document.
+  nsIURI* uri = document->GetDocumentURI();
+  if (!uri) {
+    aReturn.Append(internalError);
+
+    return;
+  }
+
+  nsresult rv;
+
+  nsCString host;
+  rv = uri->GetHost(host);
+  if (NS_FAILED(rv)) {
+    aReturn.Append(internalError);
+
+    return;
+  }
+
+  int32_t numberOfCerts = 0;
+  CERTCertListNode* node;
+  for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
+       node = CERT_LIST_NEXT(node)) {
+    ++numberOfCerts;
+  }
+
+  ScopedCERTCertNicknames nicknames(getNSSCertNicknamesFromCertList(certList));
+
+  if (!nicknames) {
+    aReturn.Append(internalError);
+
+    return;
+  }
+
+  NS_ASSERTION(nicknames->numnicknames == numberOfCerts,
+               "nicknames->numnicknames != numberOfCerts");
+
+  nsAutoArrayPtr<char16_t*> certNicknameList(new char16_t*[nicknames->numnicknames * 2]);
+  if (!certNicknameList) {
+    aReturn.Append(internalError);
+
+    return;
+  }
+
+  char16_t** certDetailsList = certNicknameList.get() + nicknames->numnicknames;
+
+  int32_t certsToUse;
+  for (node = CERT_LIST_HEAD(certList), certsToUse = 0;
+       !CERT_LIST_END(node, certList) && certsToUse < nicknames->numnicknames;
+       node = CERT_LIST_NEXT(node)) {
+    RefPtr<nsNSSCertificate> tempCert(nsNSSCertificate::Create(node->cert));
+    if (tempCert) {
+      nsAutoString nickWithSerial, details;
+      rv = tempCert->FormatUIStrings(NS_ConvertUTF8toUTF16(nicknames->nicknames[certsToUse]),
+                                     nickWithSerial, details);
+      if (NS_SUCCEEDED(rv)) {
+        certNicknameList[certsToUse] = ToNewUnicode(nickWithSerial);
+        if (certNicknameList[certsToUse]) {
+          certDetailsList[certsToUse] = ToNewUnicode(details);
+          if (!certDetailsList[certsToUse]) {
+            nsMemory::Free(certNicknameList[certsToUse]);
+            continue;
+          }
+          ++certsToUse;
+        }
+      }
+    }
+  }
+
+  if (certsToUse == 0) {
+    aReturn.Append(internalError);
+
+    return;
+  }
+
+  NS_ConvertUTF8toUTF16 utf16Host(host);
+
+  CERTCertificate *signingCert = nullptr;
+  bool tryAgain, canceled;
+  nsAutoString password;
+  do {
+    // Throw up the form signing confirmation dialog and get back the index
+    // of the selected cert.
+    int32_t selectedIndex = -1;
+    rv = fsd->ConfirmSignText(uiContext, utf16Host, aStringToSign,
+                              const_cast<const char16_t**>(certNicknameList.get()),
+                              const_cast<const char16_t**>(certDetailsList),
+                              certsToUse, &selectedIndex, password,
+                              &canceled);
+    if (NS_FAILED(rv) || canceled) {
+      break; // out of tryAgain loop
+    }
+
+    int32_t j = 0;
+    for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
+         node = CERT_LIST_NEXT(node)) {
+      if (j == selectedIndex) {
+        signingCert = CERT_DupCertificate(node->cert);
+        break; // out of cert list iteration loop
+      }
+      ++j;
+    }
+
+    if (!signingCert) {
+      rv = NS_ERROR_FAILURE;
+      break; // out of tryAgain loop
+    }
+
+    NS_ConvertUTF16toUTF8 pwUtf8(password);
+
+    tryAgain =
+      PK11_CheckUserPassword(signingCert->slot,
+                             const_cast<char *>(pwUtf8.get())) != SECSuccess;
+    // XXX we should show an error dialog before retrying
+  } while (tryAgain);
+
+  int32_t k;
+  for (k = 0; k < certsToUse; ++k) {
+    nsMemory::Free(certNicknameList[k]);
+    nsMemory::Free(certDetailsList[k]);
+  }
+
+  if (NS_FAILED(rv)) { // something went wrong inside the tryAgain loop
+    aReturn.Append(internalError);
+
+    return;
+  }
+
+  if (canceled) {
+    aReturn.AppendLiteral("error:userCancel");
+
+    return;
+  }
+
+  SECKEYPrivateKey* privKey = PK11_FindKeyByAnyCert(signingCert, uiContext);
+  if (!privKey) {
+    aReturn.Append(internalError);
+
+    return;
+  }
+
+  nsAutoCString charset(document->GetDocumentCharacterSet());
+
+  // XXX Doing what nsFormSubmission::GetEncoder does (see
+  //     http://bugzilla.mozilla.org/show_bug.cgi?id=81203).
+  if (charset.EqualsLiteral("ISO-8859-1")) {
+    charset.AssignLiteral("windows-1252");
+  }
+
+  nsCOMPtr<nsISaveAsCharset> encoder =
+    do_CreateInstance(NS_SAVEASCHARSET_CONTRACTID);
+  if (encoder) {
+    rv = encoder->Init(charset.get(),
+                       (nsISaveAsCharset::attr_EntityAfterCharsetConv + 
+                       nsISaveAsCharset::attr_FallbackDecimalNCR),
+                       0);
+  }
+
+  nsXPIDLCString buffer;
+  if (aStringToSign.Length() > 0) {
+    if (encoder && NS_SUCCEEDED(rv)) {
+      rv = encoder->Convert(PromiseFlatString(aStringToSign).get(),
+                            getter_Copies(buffer));
+      if (NS_FAILED(rv)) {
+        aReturn.Append(internalError);
+
+        return;
+      }
+    }
+    else {
+      AppendUTF16toUTF8(aStringToSign, buffer);
+    }
+  }
+
+  HASHContext *hc = HASH_Create(HASH_AlgSHA1);
+  if (!hc) {
+    aReturn.Append(internalError);
+
+    return;
+  }
+
+  unsigned char hash[SHA1_LENGTH];
+
+  SECItem digest;
+  digest.data = hash;
+
+  HASH_Begin(hc);
+  HASH_Update(hc, reinterpret_cast<const unsigned char*>(buffer.get()),
+              buffer.Length());
+  HASH_End(hc, digest.data, &digest.len, SHA1_LENGTH);
+  HASH_Destroy(hc);
+
+  nsCString p7;
+  SECStatus srv = SECFailure;
+
+  SEC_PKCS7ContentInfo *ci = SEC_PKCS7CreateSignedData(signingCert,
+                                                       certUsageEmailSigner,
+                                                       nullptr, SEC_OID_SHA1,
+                                                       &digest, nullptr, uiContext);
+  if (ci) {
+    srv = SEC_PKCS7IncludeCertChain(ci, nullptr);
+    if (srv == SECSuccess) {
+      srv = SEC_PKCS7AddSigningTime(ci);
+      if (srv == SECSuccess) {
+        srv = SEC_PKCS7Encode(ci, signTextOutputCallback, &p7, nullptr, nullptr,
+                              uiContext);
+      }
+    }
+
+    SEC_PKCS7DestroyContentInfo(ci);
+  }
+
+  if (srv != SECSuccess) {
+    aReturn.Append(internalError);
+
+    return;
+  }
+
+  SECItem binary_item;
+  binary_item.data = reinterpret_cast<unsigned char*>
+                                     (const_cast<char*>(p7.get()));
+  binary_item.len = p7.Length();
+
+  char *result = NSSBase64_EncodeItem(nullptr, nullptr, 0, &binary_item);
+  if (result) {
+    AppendASCIItoUTF16(result, aReturn);
+  }
+  else {
+    aReturn.Append(internalError);
+  }
+
+  PORT_Free(result);
+
+  return;
+}
+
+void
+nsCrypto::Logout(ErrorResult& aRv)
+{
+  NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
+
+  nsresult rv;
+  nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  {
+    nsNSSShutDownPreventionLock locker;
+    PK11_LogoutAll();
+    SSL_ClearSessionCache();
+  }
+
+  rv = nssComponent->LogoutAuthenticatedPK11();
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
+}
+
+CRMFObject::CRMFObject()
+{
+  MOZ_COUNT_CTOR(CRMFObject);
+}
+
+CRMFObject::~CRMFObject()
+{
+  MOZ_COUNT_DTOR(CRMFObject);
+}
+
+JSObject*
+CRMFObject::WrapObject(JSContext *aCx, bool* aTookOwnership)
+{
+  return CRMFObjectBinding::Wrap(aCx, this, aTookOwnership);
+}
+
+void
+CRMFObject::GetRequest(nsAString& aRequest)
+{
+  aRequest.Assign(mBase64Request);
+}
+
+nsresult
+CRMFObject::SetCRMFRequest(char *inRequest)
+{
+  mBase64Request.AssignWithConversion(inRequest);  
+  return NS_OK;
+}
+
+#endif // MOZ_DISABLE_CRYPTOLEGACY
+
 nsPkcs11::nsPkcs11()
 {
 }
 
 nsPkcs11::~nsPkcs11()
 {
 }
 
@@ -50,17 +2842,17 @@ nsPkcs11::DeleteModule(const nsAString& 
   }
   
   NS_ConvertUTF16toUTF8 modName(aModuleName);
   int32_t modType;
   SECStatus srv = SECMOD_DeleteModule(modName.get(), &modType);
   if (srv == SECSuccess) {
     SECMODModule *module = SECMOD_FindModule(modName.get());
     if (module) {
-#ifndef MOZ_NO_SMART_CARDS
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
       nssComponent->ShutdownSmartCardThread(module);
 #endif
       SECMOD_DestroyModule(module);
     }
     rv = NS_OK;
   } else {
     rv = NS_ERROR_FAILURE;
   }
@@ -86,17 +2878,17 @@ nsPkcs11::AddModule(const nsAString& aMo
   NS_CopyUnicodeToNative(aLibraryFullPath, fullPath);
   uint32_t mechFlags = SECMOD_PubMechFlagstoInternal(aCryptoMechanismFlags);
   uint32_t cipherFlags = SECMOD_PubCipherFlagstoInternal(aCipherFlags);
   SECStatus srv = SECMOD_AddNewModule(moduleName.get(), fullPath.get(), 
                                       mechFlags, cipherFlags);
   if (srv == SECSuccess) {
     SECMODModule *module = SECMOD_FindModule(moduleName.get());
     if (module) {
-#ifndef MOZ_NO_SMART_CARDS
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
       nssComponent->LaunchSmartCardThread(module);
 #endif
       SECMOD_DestroyModule(module);
     }
   }
 
   // The error message we report to the user depends directly on 
   // what the return value for SEDMOD_AddNewModule is
@@ -106,8 +2898,9 @@ nsPkcs11::AddModule(const nsAString& aMo
   case SECFailure:
     return NS_ERROR_FAILURE;
   case -2:
     return NS_ERROR_ILLEGAL_VALUE;
   }
   NS_ERROR("Bogus return value, this should never happen");
   return NS_ERROR_FAILURE;
 }
+
--- a/security/manager/ssl/src/nsCrypto.h
+++ b/security/manager/ssl/src/nsCrypto.h
@@ -1,16 +1,107 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * 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 _nsCrypto_h_
 #define _nsCrypto_h_
 
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/ErrorResult.h"
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
+#include "mozilla/dom/NonRefcountedDOMObject.h"
+#include "Crypto.h"
+#include "nsCOMPtr.h"
+#include "nsIDOMCryptoLegacy.h"
+#include "nsIRunnable.h"
+#include "nsString.h"
+#include "nsIPrincipal.h"
+
+#define NS_CRYPTO_CID \
+  {0x929d9320, 0x251e, 0x11d4, { 0x8a, 0x7c, 0x00, 0x60, 0x08, 0xc8, 0x44, 0xc3} }
+#define PSM_VERSION_STRING "2.4"
+
+class nsIPSMComponent;
+class nsIDOMScriptObjectFactory;
+
+namespace mozilla {
+namespace dom {
+
+class CRMFObject : public NonRefcountedDOMObject
+{
+public:
+  CRMFObject();
+  virtual ~CRMFObject();
+
+  nsresult SetCRMFRequest(char *inRequest);
+
+  JSObject* WrapObject(JSContext *aCx, bool* aTookOwnership);
+
+  void GetRequest(nsAString& aRequest);
+
+private:
+  nsString mBase64Request;
+};
+
+}
+}
+
+class nsCrypto: public mozilla::dom::Crypto
+{
+public:
+  nsCrypto();
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // If legacy DOM crypto is enabled this is the class that actually
+  // implements the legacy methods.
+  NS_DECL_NSIDOMCRYPTO
+
+  virtual bool EnableSmartCardEvents() MOZ_OVERRIDE;
+  virtual void SetEnableSmartCardEvents(bool aEnable,
+                                        mozilla::ErrorResult& aRv) MOZ_OVERRIDE;
+
+  virtual void GetVersion(nsString& aVersion) MOZ_OVERRIDE;
+
+  virtual mozilla::dom::CRMFObject*
+  GenerateCRMFRequest(JSContext* aContext,
+                      const nsCString& aReqDN,
+                      const nsCString& aRegToken,
+                      const nsCString& aAuthenticator,
+                      const nsCString& aEaCert,
+                      const nsCString& aJsCallback,
+                      const mozilla::dom::Sequence<JS::Value>& aArgs,
+                      mozilla::ErrorResult& aRv) MOZ_OVERRIDE;
+
+  virtual void ImportUserCertificates(const nsAString& aNickname,
+                                      const nsAString& aCmmfResponse,
+                                      bool aDoForcedBackup,
+                                      nsAString& aReturn,
+                                      mozilla::ErrorResult& aRv) MOZ_OVERRIDE;
+
+  virtual void SignText(JSContext* aContext,
+                        const nsAString& aStringToSign,
+                        const nsAString& aCaOption,
+                        const mozilla::dom::Sequence<nsCString>& aArgs,
+                        nsAString& aReturn) MOZ_OVERRIDE;
+
+  virtual void Logout(mozilla::ErrorResult& aRv) MOZ_OVERRIDE;
+
+protected:
+  virtual ~nsCrypto();
+
+private:
+  static already_AddRefed<nsIPrincipal> GetScriptPrincipal(JSContext *cx);
+
+  bool mEnableSmartCardEvents;
+};
+#endif // MOZ_DISABLE_CRYPTOLEGACY
+
 #include "nsIPKCS11.h"
 
 #define NS_PKCS11_CID \
   {0x74b7a390, 0x3b41, 0x11d4, { 0x8a, 0x80, 0x00, 0x60, 0x08, 0xc8, 0x44, 0xc3} }
 
 class nsPkcs11 : public nsIPKCS11
 {
 public:
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -18,17 +18,17 @@
 #include "nsComponentManagerUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsICertOverrideService.h"
 #include "mozilla/Preferences.h"
 #include "nsThreadUtils.h"
 #include "mozilla/PublicSSL.h"
 #include "mozilla/StaticPtr.h"
 
-#ifndef MOZ_NO_SMART_CARDS
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
 #include "nsSmartCardMonitor.h"
 #endif
 
 #include "nsCRT.h"
 #include "nsNTLMAuthModule.h"
 #include "nsIFile.h"
 #include "nsIProperties.h"
 #include "nsIWindowWatcher.h"
@@ -208,17 +208,17 @@ GetOCSPBehaviorFromPrefs(/*out*/ CertVer
        : CertVerifier::ocsp_get_disabled;
 
   SSL_ClearSessionCache();
 }
 
 nsNSSComponent::nsNSSComponent()
   :mutex("nsNSSComponent.mutex"),
    mNSSInitialized(false),
-#ifndef MOZ_NO_SMART_CARDS
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
    mThreadList(nullptr),
 #endif
    mCertVerificationThread(nullptr)
 {
 #ifdef PR_LOGGING
   if (!gPIPNSSLog)
     gPIPNSSLog = PR_NewLogModule("pipnss");
 #endif
@@ -352,17 +352,17 @@ nsNSSComponent::GetNSSBundleString(const
       outString = result;
       rv = NS_OK;
     }
   }
 
   return rv;
 }
 
-#ifndef MOZ_NO_SMART_CARDS
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
 void
 nsNSSComponent::LaunchSmartCardThreads()
 {
   nsNSSShutDownPreventionLock locker;
   {
     SECMODModuleList* list;
     SECMODListLock* lock = SECMOD_GetDefaultModuleListLock();
     if (!lock) {
@@ -408,17 +408,17 @@ nsNSSComponent::ShutdownSmartCardThread(
 }
 
 void
 nsNSSComponent::ShutdownSmartCardThreads()
 {
   delete mThreadList;
   mThreadList = nullptr;
 }
-#endif // MOZ_NO_SMART_CARDS
+#endif // MOZ_DISABLE_CRYPTOLEGACY
 
 void
 nsNSSComponent::LoadLoadableRoots()
 {
   nsNSSShutDownPreventionLock locker;
   SECMODModule* RootsModule = nullptr;
 
   // In the past we used SECMOD_AddNewModule to load our module containing
@@ -1028,17 +1028,17 @@ nsNSSComponent::InitializeNSS()
     return NS_ERROR_FAILURE;
   }
 
   // dynamic options from prefs
   setValidationOptions(true, lock);
 
   mHttpForNSS.initTable();
 
-#ifndef MOZ_NO_SMART_CARDS
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
   LaunchSmartCardThreads();
 #endif
 
   mozilla::pkix::RegisterErrorTable();
 
   // Initialize the site security service
   nsCOMPtr<nsISiteSecurityService> sssService =
     do_GetService(NS_SSSERVICE_CONTRACTID);
@@ -1067,17 +1067,17 @@ nsNSSComponent::ShutdownNSS()
 
     PK11_SetPasswordFunc((PK11PasswordFunc)nullptr);
 
     Preferences::RemoveObserver(this, "security.");
     if (NS_FAILED(CipherSuiteChangeObserver::StopObserve())) {
       PR_LOG(gPIPNSSLog, PR_LOG_ERROR, ("nsNSSComponent::ShutdownNSS cannot stop observing cipher suite change\n"));
     }
 
-#ifndef MOZ_NO_SMART_CARDS
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
     ShutdownSmartCardThreads();
 #endif
     SSL_ClearSessionCache();
     UnloadLoadableRoots();
 #ifndef MOZ_NO_EV_CERTS
     CleanupIdentityInfo();
 #endif
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("evaporating psm resources\n"));
--- a/security/manager/ssl/src/nsNSSComponent.h
+++ b/security/manager/ssl/src/nsNSSComponent.h
@@ -9,16 +9,18 @@
 
 #include "mozilla/Mutex.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIEntropyCollector.h"
 #include "nsIStringBundle.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
+#endif
 #include "nsINSSErrorsService.h"
 #include "nsNSSCallbacks.h"
 #include "SharedCertVerifier.h"
 #include "nsNSSHelper.h"
 #include "nsClientAuthRemember.h"
 #include "prerror.h"
 
 class nsIDOMWindow;
@@ -80,17 +82,17 @@ class NS_NO_VTABLE nsINSSComponent : pub
                                 nsAString& outString) = 0;
   NS_IMETHOD NSSBundleFormatStringFromName(const char* name,
                                            const char16_t** params,
                                            uint32_t numParams,
                                            nsAString& outString) = 0;
 
   NS_IMETHOD LogoutAuthenticatedPK11() = 0;
 
-#ifndef MOZ_NO_SMART_CARDS
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
   NS_IMETHOD LaunchSmartCardThread(SECMODModule* module) = 0;
 
   NS_IMETHOD ShutdownSmartCardThread(SECMODModule* module) = 0;
 #endif
 
   NS_IMETHOD IsNSSInitialized(bool* initialized) = 0;
 
   virtual ::mozilla::TemporaryRef<mozilla::psm::SharedCertVerifier>
@@ -133,17 +135,17 @@ public:
                                            nsAString& outString);
   NS_IMETHOD GetNSSBundleString(const char* name, nsAString& outString);
   NS_IMETHOD NSSBundleFormatStringFromName(const char* name,
                                            const char16_t** params,
                                            uint32_t numParams,
                                            nsAString& outString);
   NS_IMETHOD LogoutAuthenticatedPK11();
 
-#ifndef MOZ_NO_SMART_CARDS
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
   NS_IMETHOD LaunchSmartCardThread(SECMODModule* module);
   NS_IMETHOD ShutdownSmartCardThread(SECMODModule* module);
   void LaunchSmartCardThreads();
   void ShutdownSmartCardThreads();
   nsresult DispatchEventToWindow(nsIDOMWindow* domWin,
                                  const nsAString& eventType,
                                  const nsAString& token);
 #endif
@@ -180,17 +182,17 @@ private:
   Mutex mutex;
 
   nsCOMPtr<nsIStringBundle> mPIPNSSBundle;
   nsCOMPtr<nsIStringBundle> mNSSErrorsBundle;
   bool mNSSInitialized;
   bool mObserversRegistered;
   static int mInstanceCount;
   nsNSSShutDownList* mShutdownObjectList;
-#ifndef MOZ_NO_SMART_CARDS
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
   SmartCardThreadList* mThreadList;
 #endif
   bool mIsNetworkDown;
 
   void deleteBackgroundThreads();
   void createBackgroundThreads();
   nsCertVerificationThread* mCertVerificationThread;
 
--- a/security/manager/ssl/src/nsNSSModule.cpp
+++ b/security/manager/ssl/src/nsNSSModule.cpp
@@ -185,16 +185,19 @@ NS_NSS_GENERIC_FACTORY_CONSTRUCTOR_BYPRO
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsNSSCertificateDB)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsNSSCertCache)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR_BYPROCESS(nssEnsureOnChromeOnly,
                                              nsNSSCertList,
                                              nsNSSCertListFakeTransport)
 #ifdef MOZ_XUL
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsCertTree)
 #endif
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
+NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsCrypto)
+#endif
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsPkcs11)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsCertPicker)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nssEnsure, nsNTLMAuthModule, InitTest)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsCryptoHash)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsCryptoHMAC)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsStreamCipher)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsKeyObject)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsKeyObjectFactory)
@@ -219,16 +222,19 @@ NS_DEFINE_NAMED_CID(NS_X509CERT_CID);
 NS_DEFINE_NAMED_CID(NS_X509CERTDB_CID);
 NS_DEFINE_NAMED_CID(NS_X509CERTLIST_CID);
 NS_DEFINE_NAMED_CID(NS_NSSCERTCACHE_CID);
 NS_DEFINE_NAMED_CID(NS_FORMPROCESSOR_CID);
 #ifdef MOZ_XUL
 NS_DEFINE_NAMED_CID(NS_CERTTREE_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_PKCS11_CID);
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
+NS_DEFINE_NAMED_CID(NS_CRYPTO_CID);
+#endif
 NS_DEFINE_NAMED_CID(NS_CRYPTO_HASH_CID);
 NS_DEFINE_NAMED_CID(NS_CRYPTO_HMAC_CID);
 NS_DEFINE_NAMED_CID(NS_CERT_PICKER_CID);
 NS_DEFINE_NAMED_CID(NS_NTLMAUTHMODULE_CID);
 NS_DEFINE_NAMED_CID(NS_STREAMCIPHER_CID);
 NS_DEFINE_NAMED_CID(NS_KEYMODULEOBJECT_CID);
 NS_DEFINE_NAMED_CID(NS_KEYMODULEOBJECTFACTORY_CID);
 NS_DEFINE_NAMED_CID(NS_DATASIGNATUREVERIFIER_CID);
@@ -251,16 +257,19 @@ static const mozilla::Module::CIDEntry k
   { &kNS_X509CERTDB_CID, false, nullptr, nsNSSCertificateDBConstructor },
   { &kNS_X509CERTLIST_CID, false, nullptr, nsNSSCertListConstructor },
   { &kNS_NSSCERTCACHE_CID, false, nullptr, nsNSSCertCacheConstructor },
   { &kNS_FORMPROCESSOR_CID, false, nullptr, nsKeygenFormProcessor::Create },
 #ifdef MOZ_XUL
   { &kNS_CERTTREE_CID, false, nullptr, nsCertTreeConstructor },
 #endif
   { &kNS_PKCS11_CID, false, nullptr, nsPkcs11Constructor },
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
+  { &kNS_CRYPTO_CID, false, nullptr, nsCryptoConstructor },
+#endif
   { &kNS_CRYPTO_HASH_CID, false, nullptr, nsCryptoHashConstructor },
   { &kNS_CRYPTO_HMAC_CID, false, nullptr, nsCryptoHMACConstructor },
   { &kNS_CERT_PICKER_CID, false, nullptr, nsCertPickerConstructor },
   { &kNS_NTLMAUTHMODULE_CID, false, nullptr, nsNTLMAuthModuleConstructor },
   { &kNS_STREAMCIPHER_CID, false, nullptr, nsStreamCipherConstructor },
   { &kNS_KEYMODULEOBJECT_CID, false, nullptr, nsKeyObjectConstructor },
   { &kNS_KEYMODULEOBJECTFACTORY_CID, false, nullptr, nsKeyObjectFactoryConstructor },
   { &kNS_DATASIGNATUREVERIFIER_CID, false, nullptr, nsDataSignatureVerifierConstructor },
@@ -286,16 +295,19 @@ static const mozilla::Module::ContractID
   { NS_X509CERTDB_CONTRACTID, &kNS_X509CERTDB_CID },
   { NS_X509CERTLIST_CONTRACTID, &kNS_X509CERTLIST_CID },
   { NS_NSSCERTCACHE_CONTRACTID, &kNS_NSSCERTCACHE_CID },
   { NS_FORMPROCESSOR_CONTRACTID, &kNS_FORMPROCESSOR_CID },
 #ifdef MOZ_XUL
   { NS_CERTTREE_CONTRACTID, &kNS_CERTTREE_CID },
 #endif
   { NS_PKCS11_CONTRACTID, &kNS_PKCS11_CID },
+#ifndef MOZ_DISABLE_CRYPTOLEGACY
+  { NS_CRYPTO_CONTRACTID, &kNS_CRYPTO_CID },
+#endif
   { NS_CRYPTO_HASH_CONTRACTID, &kNS_CRYPTO_HASH_CID },
   { NS_CRYPTO_HMAC_CONTRACTID, &kNS_CRYPTO_HMAC_CID },
   { NS_CERT_PICKER_CONTRACTID, &kNS_CERT_PICKER_CID },
   { "@mozilla.org/uriloader/psm-external-content-listener;1", &kNS_PSMCONTENTLISTEN_CID },
   { NS_CRYPTO_FIPSINFO_SERVICE_CONTRACTID, &kNS_PKCS11MODULEDB_CID },
   { NS_NTLMAUTHMODULE_CONTRACTID, &kNS_NTLMAUTHMODULE_CID },
   { NS_STREAMCIPHER_CONTRACTID, &kNS_STREAMCIPHER_CID },
   { NS_KEYMODULEOBJECT_CONTRACTID, &kNS_KEYMODULEOBJECT_CID },
--- a/security/manager/ssl/src/nsSmartCardMonitor.cpp
+++ b/security/manager/ssl/src/nsSmartCardMonitor.cpp
@@ -1,22 +1,29 @@
 /* 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 "nspr.h"
 
+#include "mozilla/dom/SmartCardEvent.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
+#include "nsIDOMCryptoLegacy.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMWindow.h"
+#include "nsIDOMWindowCollection.h"
 #include "nsIObserverService.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIWindowWatcher.h"
 #include "nsServiceManagerUtils.h"
 #include "nsSmartCardMonitor.h"
-#include "nsThreadUtils.h"
 #include "pk11func.h"
 
 using namespace mozilla;
+using namespace mozilla::dom;
 
 //
 // The SmartCard monitoring thread should start up for each module we load
 // that has removable tokens. This code calls an NSS function which waits
 // until there is a change in the token state. NSS uses the
 // C_WaitForSlotEvent() call in PKCS #11 if the module implements the call,
 // otherwise NSS will poll the token in a loop with a delay of 'latency'
 // between polls. Note that the C_WaitForSlotEvent() may wake up on any type
@@ -38,16 +45,17 @@ public:
   {
   }
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
 
 private:
   virtual ~nsTokenEventRunnable() {}
+  nsresult DispatchEventToWindow(nsIDOMWindow* domWin);
 
   nsString mType;
   nsString mTokenName;
 };
 
 NS_IMPL_ISUPPORTS(nsTokenEventRunnable, nsIRunnable)
 
 NS_IMETHODIMP
@@ -58,18 +66,131 @@ nsTokenEventRunnable::Run()
   nsCOMPtr<nsIObserverService> observerService =
     mozilla::services::GetObserverService();
   if (!observerService) {
     return NS_ERROR_FAILURE;
   }
   // This conversion is safe because mType can only be "smartcard-insert"
   // or "smartcard-remove".
   NS_ConvertUTF16toUTF8 eventTypeUTF8(mType);
-  return observerService->NotifyObservers(nullptr, eventTypeUTF8.get(),
-                                          mTokenName.get());
+  nsresult rv = observerService->NotifyObservers(nullptr, eventTypeUTF8.get(),
+                                                 mTokenName.get());
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // 'Dispatch' the event to all the windows. 'DispatchEventToWindow()' will
+  // first check to see if a given window has requested crypto events.
+  nsCOMPtr<nsIWindowWatcher> windowWatcher =
+    do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsCOMPtr<nsISimpleEnumerator> enumerator;
+  rv = windowWatcher->GetWindowEnumerator(getter_AddRefs(enumerator));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  for (;;) {
+    bool hasMoreWindows;
+    rv = enumerator->HasMoreElements(&hasMoreWindows);
+    if (NS_FAILED(rv) || !hasMoreWindows) {
+      return rv;
+    }
+    nsCOMPtr<nsISupports> supports;
+    enumerator->GetNext(getter_AddRefs(supports));
+    nsCOMPtr<nsIDOMWindow> domWin(do_QueryInterface(supports));
+    if (domWin) {
+      rv = DispatchEventToWindow(domWin);
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
+    }
+  }
+  return rv;
+}
+
+nsresult
+nsTokenEventRunnable::DispatchEventToWindow(nsIDOMWindow* domWin)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(domWin);
+
+  // first walk the children and dispatch their events
+  nsCOMPtr<nsIDOMWindowCollection> frames;
+  nsresult rv = domWin->GetFrames(getter_AddRefs(frames));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  uint32_t length;
+  rv = frames->GetLength(&length);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  for (uint32_t i = 0; i < length; i++) {
+    nsCOMPtr<nsIDOMWindow> childWin;
+    rv = frames->Item(i, getter_AddRefs(childWin));
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    if (domWin) {
+      rv = DispatchEventToWindow(childWin);
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
+    }
+  }
+
+  // check if we've enabled smart card events on this window
+  // NOTE: it's not an error to say that we aren't going to dispatch
+  // the event.
+  nsCOMPtr<nsIDOMCrypto> crypto;
+  rv = domWin->GetCrypto(getter_AddRefs(crypto));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  if (!crypto) {
+    return NS_OK; // nope, it doesn't have a crypto property
+  }
+
+  bool boolrv;
+  rv = crypto->GetEnableSmartCardEvents(&boolrv);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  if (!boolrv) {
+    return NS_OK; // nope, it's not enabled.
+  }
+
+  // dispatch the event ...
+
+  // find the document
+  nsCOMPtr<nsIDOMDocument> doc;
+  rv = domWin->GetDocument(getter_AddRefs(doc));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  if (!doc) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<EventTarget> d = do_QueryInterface(doc);
+
+  SmartCardEventInit init;
+  init.mBubbles = false;
+  init.mCancelable = true;
+  init.mTokenName = mTokenName;
+
+  nsRefPtr<SmartCardEvent> event(SmartCardEvent::Constructor(d, mType, init));
+  event->SetTrusted(true);
+
+  return d->DispatchEvent(event, &boolrv);
 }
 
 // self linking and removing double linked entry
 // adopts the thread it is passed.
 class SmartCardThreadEntry
 {
 public:
   friend class SmartCardThreadList;
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/bugs/mochitest-legacy.ini
@@ -0,0 +1,2 @@
+[test_generateCRMFRequest.html]
+skip-if = e10s
\ No newline at end of file
--- a/security/manager/ssl/tests/mochitest/bugs/moz.build
+++ b/security/manager/ssl/tests/mochitest/bugs/moz.build
@@ -1,9 +1,14 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 MOCHITEST_MANIFESTS += ['mochitest.ini']
+# test_generateCRMFRequest.html tests crypto.generateCRMFRequest, which isn't
+# available if legacy crypto has been disabled.
+if not CONFIG['MOZ_DISABLE_CRYPTOLEGACY']:
+    MOCHITEST_MANIFESTS += ['mochitest-legacy.ini']
+
 MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
 
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/bugs/test_generateCRMFRequest.html
@@ -0,0 +1,146 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>crypto.generateCRMFRequest bugs</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="onWindowLoad()">
+<script class="testbody" type="text/javascript">
+
+  SimpleTest.waitForExplicitFinish();
+
+  function onWindowLoad() {
+    SpecialPowers.pushPrefEnv({"set": [["dom.unsafe_legacy_crypto.enabled", true]]},
+                              runTest);
+  }
+
+  function runTest() {
+    // Does it work at all?
+    try {
+      var crmfObject = crypto.generateCRMFRequest("CN=undefined", "regToken",
+                                                  "authenticator", null, "",
+                                                  512, null, "  rsa-ex   ",
+                                                  1024, null, "\r\n\t rsa-sign\t");
+      ok(true, "no exception thrown in generateCRMFRequest");
+    } catch (e) {
+      ok(false, "unexpected exception: " + e);
+    }
+
+    // bug 849553
+    // This should fail because 8 is too small of a key size.
+    try {
+      var crmfObject = crypto.generateCRMFRequest("CN=undefined", "regToken",
+                                                  "authenticator", null, "",
+                                                  8, null, "rsa-ex",
+                                                  1024, null, "rsa-sign");
+      ok(false, "execution should not reach this line");
+    } catch (e) {
+      is(e.toString(), "Error: error:could not generate the key for algorithm rsa-ex", "expected exception");
+    }
+    // This should fail because 65536 is too large of a key size.
+    try {
+      var crmfObject = crypto.generateCRMFRequest("CN=undefined", "regToken",
+                                                  "authenticator", null, "",
+                                                  65536, null, "rsa-ex",
+                                                  1024, null, "rsa-sign");
+      ok(false, "execution should not reach this line");
+    } catch (e) {
+      is(e.toString(), "Error: error:could not generate the key for algorithm rsa-ex", "expected exception");
+    }
+
+    // bug 882865
+    var o200 = document.documentElement;
+    var o1 = crypto;
+    try {
+      o1.generateCRMFRequest("undefined", o200, 'X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X', null, o1, 1404343237, Math.PI, []);
+      ok(false, "execution should not reach this line");
+    } catch (e) {
+      // The 'key generation argument' in this case was an empty array,
+      // which gets interpreted as an empty string.
+      is(e.toString(), "Error: error:invalid key generation argument:", "expected exception");
+    }
+
+    // Test that an rsa certificate isn't used to generate an ec key.
+    try {
+      var crmfObject = crypto.generateCRMFRequest("CN=a", "a", "a", null, "",
+                         1024, "popcert=MIIBjzCB+aADAgECAgUAnVC3BjANBgkqhkiG9w0BAQUFADAMMQowCAYDVQQDEwFhMB4XDTEzMTEwNjE3NDU1NFoXDTIzMTEwNjE3NDU1NFowDDEKMAgGA1UEAxMBYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3G2mwjE8IGVwv6H1NGZFSKE3UrTsgez2DtNIYb5zdi0P0w9SbmL2GWfveu9DZhRebhVz7QSMPKLagI4aoIzoP5BxRl7a8wR5wbU0z8qXnAvy9p3Ex5oN5vX47TWB7cnItoWpi6A81GSn5X1CFFHhVCEwnQsHuWXrEvLD5hrfdmcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCNo+yLKfAd2NBI5DUpwgHSFBA+59pdNtHY7E2KZjyc9tXN6PHkPp8nScVCtk0g60j4aiiZQm8maPQPLo7Hipgpk83iYqquHRvcJVX4fWJpS/7vX+qTNT0hRiKRhVlI6S4Ttp2J2W6uxy2xxeqC6nBbU98QmDj3UQAY31LyejbecQ==", "ec-dual-use");
+      ok(crmfObject, "generateCRMFRequest succeeded");
+      var request = crmfObject.request;
+      var bytes = atob(request.replace(/\r\n/g, ""));
+
+      // rsaEncryption oid encoded in the request (as ASN1)
+      var badIdentifier = [ 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
+                            0x01, 0x01, 0x01 ];
+      ok(!findIdentifierInString(badIdentifier, bytes),
+         "didn't find bad identifier in request");
+
+      // secp256r1 encoded in the request (as ASN1) (this is the default for
+      // a "1024-bit" ec key)
+      var goodIdentifier = [ 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03,
+                             0x01, 0x07 ];
+      ok(findIdentifierInString(goodIdentifier, bytes),
+         "found good identifier in request");
+    } catch (e) {
+      ok(false, "unexpected exception: " + e);
+    }
+
+    // Test that only the first of repeated keygen parameters are used.
+    try {
+      var curveCrmfObject = crypto.generateCRMFRequest("CN=a", "a", "a", null,
+                              "", 1024, "curve=secp521r1;curve=nistp384",
+                              "ec-dual-use");
+      ok(curveCrmfObject, "generateCRMFRequest succeeded");
+      var curveRequest = curveCrmfObject.request;
+      var curveBytes = atob(curveRequest.replace(/\r\n/g, ""));
+
+      // nistp384 encoded in the request (as ASN1)
+      var badIdentifier = [ 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22 ];
+      ok(!findIdentifierInString(badIdentifier, curveBytes),
+         "didn't find bad identifier in curve request");
+
+      // secp512r1 encoded in the request (as ASN1)
+      var goodIdentifier = [ 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23 ];
+      ok(findIdentifierInString(goodIdentifier, curveBytes),
+         "found good identifier in curve request");
+
+      // The popcert=MII... values are base-64 encodings of self-signed
+      // certificates. The key of the first one is a secp521r1 key, whereas
+      // the second is nistp384.
+      var popcertCrmfObject = crypto.generateCRMFRequest("CN=a", "a", "a",
+                                null, "", 1024, "popcert=MIIBjjCB8aADAgECAgUAnVEmVTAJBgcqhkjOPQQBMAwxCjAIBgNVBAMTAWkwHhcNMTMxMTA2MjE1NDUxWhcNMTQwMjA2MjE1NDUxWjAMMQowCAYDVQQDEwFpMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQA5juM0J3Od2Ih4s0ZiTkzVkh96J4yO12/L71df3GWy/Ex3LaGcew/EucY5ZITEqNHn22+3pKf3pwL6GJ/zA/foJwBspiE42ITo2BHHXl2Uuf1QK70UH5gzaqf1Cc4lPv3ibOf3EAmHx0a8sdDxRlUN2+V38iYWnMmV1qf4jM15fsYEDMwCQYHKoZIzj0EAQOBjAAwgYgCQgDO34oQkVDNkwtto1OAEbFZgq1xP9aqc+Nt7vnOuEGTISdFXCjlhon7SysTejFhO8d8wG500NrlJEmaie9FJbmWpQJCARcVtnC0K+ilxz307GyuANTaLEd7vBJxTAho8l6xkibNoxY1D9+hcc6ECbK7PCtAaysZoAVx5XhJlsnFdJdDj3wG;popcert=MIIBRDCBy6ADAgECAgUAnVEotzAJBgcqhkjOPQQBMAwxCjAIBgNVBAMTAWkwHhcNMTMxMTA2MjIwMDExWhcNMTQwMjA2MjIwMDExWjAMMQowCAYDVQQDEwFpMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEXjFpZ9bodzikeN4C8p2mVj1Ia1t+8zIndSavQHmxaD3+kvhkt18+P20ZagfBOaVEQZdArZ6KxBeW9oYZqaNpqHLveGlKYi6u9z5FyozAx4MXzyLdfu+bzOLIsryKRnLFMAkGByqGSM49BAEDaQAwZgIxAJDawIJLQ5iZsJVC3vV1YEKsI2aNEicdZ3YTMp/zUy+64Z2/cjyyfa7d5m1xKLDBogIxANHOQoy/7DioCyWNDDzx5QK0M24dOURVWRXsxjAjrg4vDmV/fkVzwpUzIr5fMgXEyQ==", "ec-dual-use");
+      ok(popcertCrmfObject, "generateCRMFRequest succeeded");
+      var popcertRequest = popcertCrmfObject.request;
+      var popcertBytes = atob(popcertRequest.replace(/\r\n/g, ""));
+      ok(!findIdentifierInString(badIdentifier, popcertBytes),
+         "didn't find bad identifier in popcert request");
+
+      ok(findIdentifierInString(goodIdentifier, popcertBytes),
+         "found good identifier in popcert request");
+    } catch (e) {
+      ok(false, "unexpected exception: " + e);
+    }
+    SimpleTest.finish();
+  }
+
+  function findIdentifierInString(identifier, str) {
+    var matches = 0;
+    for (var i = 0; i < str.length - identifier.length;
+         i += (matches != 0 ? matches : 1)) {
+      matches = 0;
+      for (var j = 0; j < identifier.length; j++) {
+        if (identifier[j] == str.charCodeAt(i + j)) {
+          matches++;
+        } else {
+          break;
+        }
+      }
+      if (matches == identifier.length) {
+        return true;
+      }
+    }
+    return false;
+  }
+</script>
+</body>
+</html>
--- a/security/manager/ssl/tests/moz.build
+++ b/security/manager/ssl/tests/moz.build
@@ -12,10 +12,10 @@ TEST_DIRS += [
 ]
 
 TEST_DIRS += [
   'compiled',
 ]
 
 XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
 
-if not CONFIG['MOZ_NO_SMART_CARDS']:
+if not CONFIG['MOZ_DISABLE_CRYPTOLEGACY']:
     XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell-smartcards.ini']
--- a/security/manager/ssl/tests/unit/moz.build
+++ b/security/manager/ssl/tests/unit/moz.build
@@ -1,10 +1,10 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 DIRS += ['tlsserver']
 
-if not CONFIG['MOZ_NO_SMART_CARDS']:
+if not CONFIG['MOZ_DISABLE_CRYPTOLEGACY']:
     DIRS += ['pkcs11testmodule']