Bug 1172785 - RTCCertificate implementation, r=rbarnes
authorMartin Thomson <martin.thomson@gmail.com>
Mon, 06 Jul 2015 10:40:04 -0700
changeset 283242 d9bd3e512cd05066ca26895bd2386b50977db2e6
parent 283241 0ab638279d6b3921abed81a441304564dad80512
child 283243 9c2fb609e5d8b6775ee681cc7057151c0429c5b6
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrbarnes
bugs1172785
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1172785 - RTCCertificate implementation, r=rbarnes
config/external/nss/nss.def
dom/base/StructuredCloneTags.h
dom/base/nsJSEnvironment.cpp
dom/crypto/WebCryptoCommon.h
dom/crypto/WebCryptoTask.cpp
dom/crypto/WebCryptoTask.h
dom/media/webrtc/RTCCertificate.cpp
dom/media/webrtc/RTCCertificate.h
--- a/config/external/nss/nss.def
+++ b/config/external/nss/nss.def
@@ -18,16 +18,17 @@ CERT_AddCertToListTail
 CERT_AddExtension
 CERT_AddExtensionByOID
 __CERT_AddTempCertToPerm
 CERT_AsciiToName
 CERT_CacheOCSPResponseFromSideChannel
 CERT_CertChainFromCert
 CERT_CertificateRequestTemplate DATA
 CERT_CertificateTemplate DATA
+CERT_CertListFromCert
 CERT_ChangeCertTrust
 CERT_CheckCertUsage
 CERT_CheckCertValidTimes
 CERT_CheckNameSpace
 CERT_ClearOCSPCache
 CERT_CompareCerts
 CERT_CompareName
 CERT_ConvertAndDecodeCertificate
--- a/dom/base/StructuredCloneTags.h
+++ b/dom/base/StructuredCloneTags.h
@@ -38,15 +38,17 @@ enum StructuredCloneTags {
   SCTAG_DOM_WEBCRYPTO_KEY,
 
   SCTAG_DOM_NULL_PRINCIPAL,
   SCTAG_DOM_SYSTEM_PRINCIPAL,
   SCTAG_DOM_CONTENT_PRINCIPAL,
 
   SCTAG_DOM_NFC_NDEF,
 
+  SCTAG_DOM_RTC_CERTIFICATE,
+
   SCTAG_DOM_MAX
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // StructuredCloneTags_h__
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -56,16 +56,18 @@
 #include "mozilla/AutoRestore.h"
 #include "mozilla/dom/CryptoKey.h"
 #include "mozilla/dom/ErrorEvent.h"
 #include "mozilla/dom/ImageDataBinding.h"
 #include "mozilla/dom/ImageData.h"
 #ifdef MOZ_NFC
 #include "mozilla/dom/MozNDEFRecord.h"
 #endif // MOZ_NFC
+#include "mozilla/dom/RTCCertificate.h"
+#include "mozilla/dom/RTCCertificateBinding.h"
 #include "mozilla/dom/StructuredClone.h"
 #include "mozilla/dom/SubtleCryptoBinding.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "nsAXPCNativeCallContext.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 
 #include "nsJSPrincipals.h"
@@ -2542,16 +2544,35 @@ NS_DOMReadStructuredClone(JSContext* cx,
                ndefRecord->WrapObject(cx, nullptr) : nullptr;
     }
     return result;
 #else
     return nullptr;
 #endif
   }
 
+  if (tag == SCTAG_DOM_RTC_CERTIFICATE) {
+    nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
+    if (!global) {
+      return nullptr;
+    }
+
+    // Prevent the return value from being trashed by a GC during ~nsRefPtr.
+    JS::Rooted<JSObject*> result(cx);
+    {
+      nsRefPtr<RTCCertificate> cert = new RTCCertificate(global);
+      if (!cert->ReadStructuredClone(reader)) {
+        result = nullptr;
+      } else {
+        result = cert->WrapObject(cx, nullptr);
+      }
+    }
+    return result;
+  }
+
   // Don't know what this is. Bail.
   xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
   return nullptr;
 }
 
 bool
 NS_DOMWriteStructuredClone(JSContext* cx,
                            JSStructuredCloneWriter* writer,
@@ -2566,16 +2587,23 @@ NS_DOMWriteStructuredClone(JSContext* cx
 
   // Handle Key cloning
   CryptoKey* key;
   if (NS_SUCCEEDED(UNWRAP_OBJECT(CryptoKey, obj, key))) {
     return JS_WriteUint32Pair(writer, SCTAG_DOM_WEBCRYPTO_KEY, 0) &&
            key->WriteStructuredClone(writer);
   }
 
+  // Handle WebRTC Certificate cloning
+  RTCCertificate* cert;
+  if (NS_SUCCEEDED(UNWRAP_OBJECT(RTCCertificate, obj, cert))) {
+    return JS_WriteUint32Pair(writer, SCTAG_DOM_RTC_CERTIFICATE, 0) &&
+           cert->WriteStructuredClone(writer);
+  }
+
   if (xpc::IsReflector(obj)) {
     nsCOMPtr<nsISupports> base = xpc::UnwrapReflectorToISupports(obj);
     nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(base);
     if (principal) {
       mozilla::ipc::PrincipalInfo info;
       if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(principal, &info)))) {
         xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
         return false;
--- a/dom/crypto/WebCryptoCommon.h
+++ b/dom/crypto/WebCryptoCommon.h
@@ -156,23 +156,29 @@ ReadBuffer(JSStructuredCloneReader* aRea
       return false;
     }
     ret = JS_ReadBytes(aReader, aBuffer.Elements(), aBuffer.Length());
   }
   return ret;
 }
 
 inline bool
+WriteBuffer(JSStructuredCloneWriter* aWriter, const uint8_t* aBuffer, size_t aLength)
+{
+  bool ret = JS_WriteUint32Pair(aWriter, aLength, 0);
+  if (ret && aLength > 0) {
+    ret = JS_WriteBytes(aWriter, aBuffer, aLength);
+  }
+  return ret;
+}
+
+inline bool
 WriteBuffer(JSStructuredCloneWriter* aWriter, const CryptoBuffer& aBuffer)
 {
-  bool ret = JS_WriteUint32Pair(aWriter, aBuffer.Length(), 0);
-  if (ret && aBuffer.Length() > 0) {
-    ret = JS_WriteBytes(aWriter, aBuffer.Elements(), aBuffer.Length());
-  }
-  return ret;
+  return WriteBuffer(aWriter, aBuffer.Elements(), aBuffer.Length());
 }
 
 inline CK_MECHANISM_TYPE
 MapAlgorithmNameToMechanism(const nsString& aName)
 {
   CK_MECHANISM_TYPE mechanism(UNKNOWN_CK_MECHANISM);
 
   // Set mechanism based on algorithm name
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -2158,261 +2158,249 @@ private:
   }
 
   virtual void Cleanup() override
   {
     mKey = nullptr;
   }
 };
 
-class GenerateAsymmetricKeyTask : public WebCryptoTask
+GenerateAsymmetricKeyTask::GenerateAsymmetricKeyTask(
+    JSContext* aCx, const ObjectOrString& aAlgorithm, bool aExtractable,
+    const Sequence<nsString>& aKeyUsages)
 {
-public:
-  GenerateAsymmetricKeyTask(JSContext* aCx,
-      const ObjectOrString& aAlgorithm, bool aExtractable,
-      const Sequence<nsString>& aKeyUsages)
-  {
-    nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
-    if (!global) {
-      mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
+  nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
+  if (!global) {
+    mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
+    return;
+  }
+
+  mArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+  if (!mArena) {
+    mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
+    return;
+  }
+
+  // Create an empty key and set easy attributes
+  mKeyPair.mPrivateKey = new CryptoKey(global);
+  mKeyPair.mPublicKey  = new CryptoKey(global);
+
+  // Extract algorithm name
+  mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
+  if (NS_FAILED(mEarlyRv)) {
+    mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+    return;
+  }
+
+  // Construct an appropriate KeyAlorithm
+  uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
+  if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
+      mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
+    RootedDictionary<RsaHashedKeyGenParams> params(aCx);
+    mEarlyRv = Coerce(aCx, params, aAlgorithm);
+    if (NS_FAILED(mEarlyRv)) {
+      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
       return;
     }
 
-    mArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-    if (!mArena) {
-      mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
+    // Pull relevant info
+    uint32_t modulusLength = params.mModulusLength;
+    CryptoBuffer publicExponent;
+    ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent);
+    nsString hashName;
+    mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
+    if (NS_FAILED(mEarlyRv)) {
+      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+      return;
+    }
+
+    // Create algorithm
+    if (!mKeyPair.mPublicKey.get()->Algorithm().MakeRsa(mAlgName,
+                                                        modulusLength,
+                                                        publicExponent,
+                                                        hashName)) {
+      mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
+      return;
+    }
+    if (!mKeyPair.mPrivateKey.get()->Algorithm().MakeRsa(mAlgName,
+                                                         modulusLength,
+                                                         publicExponent,
+                                                         hashName)) {
+      mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
       return;
     }
-
-    // Create an empty key and set easy attributes
-    mKeyPair.mPrivateKey = new CryptoKey(global);
-    mKeyPair.mPublicKey  = new CryptoKey(global);
-
-    // Extract algorithm name
-    nsString algName;
-    mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
+    mMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
+
+    // Set up params struct
+    mRsaParams.keySizeInBits = modulusLength;
+    bool converted = publicExponent.GetBigIntValue(mRsaParams.pe);
+    if (!converted) {
+      mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
+      return;
+    }
+  } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
+             mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
+    RootedDictionary<EcKeyGenParams> params(aCx);
+    mEarlyRv = Coerce(aCx, params, aAlgorithm);
+    if (NS_FAILED(mEarlyRv)) {
+      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+      return;
+    }
+
+    if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) {
+      mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+      return;
+    }
+
+    // Create algorithm.
+    mKeyPair.mPublicKey.get()->Algorithm().MakeEc(mAlgName, mNamedCurve);
+    mKeyPair.mPrivateKey.get()->Algorithm().MakeEc(mAlgName, mNamedCurve);
+    mMechanism = CKM_EC_KEY_PAIR_GEN;
+  } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_DH)) {
+    RootedDictionary<DhKeyGenParams> params(aCx);
+    mEarlyRv = Coerce(aCx, params, aAlgorithm);
     if (NS_FAILED(mEarlyRv)) {
       mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
       return;
     }
 
-    // Construct an appropriate KeyAlorithm
-    uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
-    if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
-        algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
-      RootedDictionary<RsaHashedKeyGenParams> params(aCx);
-      mEarlyRv = Coerce(aCx, params, aAlgorithm);
-      if (NS_FAILED(mEarlyRv)) {
-        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
-        return;
-      }
-
-      // Pull relevant info
-      uint32_t modulusLength = params.mModulusLength;
-      CryptoBuffer publicExponent;
-      ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent);
-      nsString hashName;
-      mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
-      if (NS_FAILED(mEarlyRv)) {
-        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
-        return;
-      }
-
-      // Create algorithm
-      if (!mKeyPair.mPublicKey.get()->Algorithm().MakeRsa(algName,
-                                                          modulusLength,
-                                                          publicExponent,
-                                                          hashName)) {
-        mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
-        return;
-      }
-      if (!mKeyPair.mPrivateKey.get()->Algorithm().MakeRsa(algName,
-                                                           modulusLength,
-                                                           publicExponent,
-                                                           hashName)) {
-        mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
-        return;
-      }
-      mMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
-
-      // Set up params struct
-      mRsaParams.keySizeInBits = modulusLength;
-      bool converted = publicExponent.GetBigIntValue(mRsaParams.pe);
-      if (!converted) {
-        mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
-        return;
-      }
-    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
-               algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
-      RootedDictionary<EcKeyGenParams> params(aCx);
-      mEarlyRv = Coerce(aCx, params, aAlgorithm);
-      if (NS_FAILED(mEarlyRv)) {
-        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
-        return;
-      }
-
-      if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) {
-        mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
-        return;
-      }
-
-      // Create algorithm.
-      mKeyPair.mPublicKey.get()->Algorithm().MakeEc(algName, mNamedCurve);
-      mKeyPair.mPrivateKey.get()->Algorithm().MakeEc(algName, mNamedCurve);
-      mMechanism = CKM_EC_KEY_PAIR_GEN;
-    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_DH)) {
-      RootedDictionary<DhKeyGenParams> params(aCx);
-      mEarlyRv = Coerce(aCx, params, aAlgorithm);
-      if (NS_FAILED(mEarlyRv)) {
-        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
-        return;
-      }
-
-      CryptoBuffer prime;
-      ATTEMPT_BUFFER_INIT(prime, params.mPrime);
-
-      CryptoBuffer generator;
-      ATTEMPT_BUFFER_INIT(generator, params.mGenerator);
-
-      // Set up params.
-      if (!prime.ToSECItem(mArena, &mDhParams.prime) ||
-          !generator.ToSECItem(mArena, &mDhParams.base)) {
-        mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
-        return;
-      }
-
-      // Create algorithm.
-      if (!mKeyPair.mPublicKey.get()->Algorithm().MakeDh(algName,
-                                                         prime,
-                                                         generator)) {
-        mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
-        return;
-      }
-      if (!mKeyPair.mPrivateKey.get()->Algorithm().MakeDh(algName,
-                                                          prime,
-                                                          generator)) {
-        mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
-        return;
-      }
-      mMechanism = CKM_DH_PKCS_KEY_PAIR_GEN;
-    } else {
-      mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+    CryptoBuffer prime;
+    ATTEMPT_BUFFER_INIT(prime, params.mPrime);
+
+    CryptoBuffer generator;
+    ATTEMPT_BUFFER_INIT(generator, params.mGenerator);
+
+    // Set up params.
+    if (!prime.ToSECItem(mArena, &mDhParams.prime) ||
+        !generator.ToSECItem(mArena, &mDhParams.base)) {
+      mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
+      return;
+    }
+
+    // Create algorithm.
+    if (!mKeyPair.mPublicKey.get()->Algorithm().MakeDh(mAlgName,
+                                                       prime,
+                                                       generator)) {
+      mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
+      return;
+    }
+    if (!mKeyPair.mPrivateKey.get()->Algorithm().MakeDh(mAlgName,
+                                                        prime,
+                                                        generator)) {
+      mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
       return;
     }
-
-    // Set key usages.
-    if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
-        algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
-      privateAllowedUsages = CryptoKey::SIGN;
-      publicAllowedUsages = CryptoKey::VERIFY;
-    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
-      privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
-      publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
-    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
-               algName.EqualsLiteral(WEBCRYPTO_ALG_DH)) {
-      privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
-      publicAllowedUsages = 0;
+    mMechanism = CKM_DH_PKCS_KEY_PAIR_GEN;
+  } else {
+    mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+    return;
+  }
+
+  // Set key usages.
+  if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
+      mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
+    privateAllowedUsages = CryptoKey::SIGN;
+    publicAllowedUsages = CryptoKey::VERIFY;
+  } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
+    privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
+    publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
+  } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
+             mAlgName.EqualsLiteral(WEBCRYPTO_ALG_DH)) {
+    privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
+    publicAllowedUsages = 0;
+  }
+
+  mKeyPair.mPrivateKey.get()->SetExtractable(aExtractable);
+  mKeyPair.mPrivateKey.get()->SetType(CryptoKey::PRIVATE);
+
+  mKeyPair.mPublicKey.get()->SetExtractable(true);
+  mKeyPair.mPublicKey.get()->SetType(CryptoKey::PUBLIC);
+
+  mKeyPair.mPrivateKey.get()->ClearUsages();
+  mKeyPair.mPublicKey.get()->ClearUsages();
+  for (uint32_t i=0; i < aKeyUsages.Length(); ++i) {
+    mEarlyRv = mKeyPair.mPrivateKey.get()->AddUsageIntersecting(aKeyUsages[i],
+                                                                privateAllowedUsages);
+    if (NS_FAILED(mEarlyRv)) {
+      return;
     }
 
-    mKeyPair.mPrivateKey.get()->SetExtractable(aExtractable);
-    mKeyPair.mPrivateKey.get()->SetType(CryptoKey::PRIVATE);
-
-    mKeyPair.mPublicKey.get()->SetExtractable(true);
-    mKeyPair.mPublicKey.get()->SetType(CryptoKey::PUBLIC);
-
-    mKeyPair.mPrivateKey.get()->ClearUsages();
-    mKeyPair.mPublicKey.get()->ClearUsages();
-    for (uint32_t i=0; i < aKeyUsages.Length(); ++i) {
-      mEarlyRv = mKeyPair.mPrivateKey.get()->AddUsageIntersecting(aKeyUsages[i],
-                                                                  privateAllowedUsages);
-      if (NS_FAILED(mEarlyRv)) {
-        return;
-      }
-
-      mEarlyRv = mKeyPair.mPublicKey.get()->AddUsageIntersecting(aKeyUsages[i],
-                                                                 publicAllowedUsages);
-      if (NS_FAILED(mEarlyRv)) {
-        return;
-      }
-    }
-
-    // If no usages ended up being allowed, DataError
-    if (!mKeyPair.mPublicKey.get()->HasAnyUsage() &&
-        !mKeyPair.mPrivateKey.get()->HasAnyUsage()) {
-      mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+    mEarlyRv = mKeyPair.mPublicKey.get()->AddUsageIntersecting(aKeyUsages[i],
+                                                               publicAllowedUsages);
+    if (NS_FAILED(mEarlyRv)) {
       return;
     }
   }
 
-private:
-  ScopedPLArenaPool mArena;
-  CryptoKeyPair mKeyPair;
-  CK_MECHANISM_TYPE mMechanism;
-  PK11RSAGenParams mRsaParams;
-  SECKEYDHParams mDhParams;
-  ScopedSECKEYPublicKey mPublicKey;
-  ScopedSECKEYPrivateKey mPrivateKey;
-  nsString mNamedCurve;
-
-  virtual void ReleaseNSSResources() override
-  {
-    mPublicKey.dispose();
-    mPrivateKey.dispose();
+  // If no usages ended up being allowed, DataError
+  if (!mKeyPair.mPublicKey.get()->HasAnyUsage() &&
+      !mKeyPair.mPrivateKey.get()->HasAnyUsage()) {
+    mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+    return;
   }
-
-  virtual nsresult DoCrypto() override
-  {
-    ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
-    MOZ_ASSERT(slot.get());
-
-    void* param;
-    switch (mMechanism) {
-      case CKM_RSA_PKCS_KEY_PAIR_GEN:
-        param = &mRsaParams;
-        break;
-      case CKM_DH_PKCS_KEY_PAIR_GEN:
-        param = &mDhParams;
-        break;
-      case CKM_EC_KEY_PAIR_GEN: {
-        param = CreateECParamsForCurve(mNamedCurve, mArena);
-        if (!param) {
-          return NS_ERROR_DOM_UNKNOWN_ERR;
-        }
-        break;
+}
+
+void
+GenerateAsymmetricKeyTask::ReleaseNSSResources()
+{
+  mPublicKey.dispose();
+  mPrivateKey.dispose();
+}
+
+nsresult
+GenerateAsymmetricKeyTask::DoCrypto()
+{
+  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+  MOZ_ASSERT(slot.get());
+
+  void* param;
+  switch (mMechanism) {
+    case CKM_RSA_PKCS_KEY_PAIR_GEN:
+      param = &mRsaParams;
+      break;
+    case CKM_DH_PKCS_KEY_PAIR_GEN:
+      param = &mDhParams;
+      break;
+    case CKM_EC_KEY_PAIR_GEN: {
+      param = CreateECParamsForCurve(mNamedCurve, mArena);
+      if (!param) {
+        return NS_ERROR_DOM_UNKNOWN_ERR;
       }
-      default:
-        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+      break;
     }
-
-    SECKEYPublicKey* pubKey = nullptr;
-    mPrivateKey = PK11_GenerateKeyPair(slot.get(), mMechanism, param, &pubKey,
-                                       PR_FALSE, PR_FALSE, nullptr);
-    mPublicKey = pubKey;
-    if (!mPrivateKey.get() || !mPublicKey.get()) {
-      return NS_ERROR_DOM_UNKNOWN_ERR;
-    }
-
-    mKeyPair.mPrivateKey.get()->SetPrivateKey(mPrivateKey);
-    mKeyPair.mPublicKey.get()->SetPublicKey(mPublicKey);
-
-    // PK11_GenerateKeyPair() does not set a CKA_EC_POINT attribute on the
-    // private key, we need this later when exporting to PKCS8 and JWK though.
-    if (mMechanism == CKM_EC_KEY_PAIR_GEN) {
-      nsresult rv = mKeyPair.mPrivateKey->AddPublicKeyData(mPublicKey);
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
-    }
-
-    return NS_OK;
+    default:
+      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+  }
+
+  SECKEYPublicKey* pubKey = nullptr;
+  mPrivateKey = PK11_GenerateKeyPair(slot.get(), mMechanism, param, &pubKey,
+                                     PR_FALSE, PR_FALSE, nullptr);
+  mPublicKey = pubKey;
+  if (!mPrivateKey.get() || !mPublicKey.get()) {
+    return NS_ERROR_DOM_UNKNOWN_ERR;
   }
 
-  virtual void Resolve() override
-  {
-    mResultPromise->MaybeResolve(mKeyPair);
+  mKeyPair.mPrivateKey.get()->SetPrivateKey(mPrivateKey);
+  mKeyPair.mPublicKey.get()->SetPublicKey(mPublicKey);
+
+  // PK11_GenerateKeyPair() does not set a CKA_EC_POINT attribute on the
+  // private key, we need this later when exporting to PKCS8 and JWK though.
+  if (mMechanism == CKM_EC_KEY_PAIR_GEN) {
+    nsresult rv = mKeyPair.mPrivateKey->AddPublicKeyData(mPublicKey);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
   }
-};
+
+  return NS_OK;
+}
+
+void
+GenerateAsymmetricKeyTask::Resolve()
+{
+  mResultPromise->MaybeResolve(mKeyPair);
+}
 
 class DerivePbkdfBitsTask : public ReturnArrayBufferViewTask
 {
 public:
   DerivePbkdfBitsTask(JSContext* aCx,
       const ObjectOrString& aAlgorithm, CryptoKey& aKey, uint32_t aLength)
     : mSymKey(aKey.GetSymKey())
   {
--- a/dom/crypto/WebCryptoTask.h
+++ b/dom/crypto/WebCryptoTask.h
@@ -200,12 +200,37 @@ protected:
   // any NSS objects, e.g., SECKEYPrivateKey or PK11SymKey.
   virtual void ReleaseNSSResources() override {}
 
   virtual nsresult CalculateResult() override final;
 
   virtual void CallCallback(nsresult rv) override final;
 };
 
+// XXX This class is declared here (unlike others) to enable reuse by WebRTC.
+class GenerateAsymmetricKeyTask : public WebCryptoTask
+{
+public:
+  GenerateAsymmetricKeyTask(JSContext* aCx,
+                            const ObjectOrString& aAlgorithm, bool aExtractable,
+                            const Sequence<nsString>& aKeyUsages);
+protected:
+  ScopedPLArenaPool mArena;
+  CryptoKeyPair mKeyPair;
+  nsString mAlgName;
+  CK_MECHANISM_TYPE mMechanism;
+  PK11RSAGenParams mRsaParams;
+  SECKEYDHParams mDhParams;
+  nsString mNamedCurve;
+
+  virtual void ReleaseNSSResources() override;
+  virtual nsresult DoCrypto() override;
+  virtual void Resolve() override;
+
+private:
+  ScopedSECKEYPublicKey mPublicKey;
+  ScopedSECKEYPrivateKey mPrivateKey;
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_WebCryptoTask_h
new file mode 100644
--- /dev/null
+++ b/dom/media/webrtc/RTCCertificate.cpp
@@ -0,0 +1,443 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/RTCCertificate.h"
+
+#include <cmath>
+#include "cert.h"
+#include "jsapi.h"
+#include "mozilla/dom/CryptoKey.h"
+#include "mozilla/dom/RTCCertificateBinding.h"
+#include "mozilla/dom/WebCryptoCommon.h"
+#include "mozilla/dom/WebCryptoTask.h"
+
+#include <cstdio>
+
+namespace mozilla {
+namespace dom {
+
+#define RTCCERTIFICATE_SC_VERSION 0x00000001
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(RTCCertificate, mGlobal)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(RTCCertificate)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(RTCCertificate)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RTCCertificate)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+// Note: explicit casts necessary to avoid
+//       warning C4307: '*' : integral constant overflow
+#define ONE_DAY PRTime(PR_USEC_PER_SEC) * PRTime(60) /*sec*/ \
+  * PRTime(60) /*min*/ * PRTime(24) /*hours*/
+#define EXPIRATION_DEFAULT ONE_DAY * PRTime(30)
+#define EXPIRATION_SLACK ONE_DAY
+#define EXPIRATION_MAX ONE_DAY * PRTime(365) /*year*/
+
+const size_t RTCCertificateCommonNameLength = 16;
+const size_t RTCCertificateMinRsaSize = 1024;
+
+class GenerateRTCCertificateTask : public GenerateAsymmetricKeyTask
+{
+public:
+  GenerateRTCCertificateTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
+                     const Sequence<nsString>& aKeyUsages)
+      : GenerateAsymmetricKeyTask(aCx, aAlgorithm, true, aKeyUsages),
+        mExpires(0),
+        mAuthType(ssl_kea_null),
+        mCertificate(nullptr),
+        mSignatureAlg(SEC_OID_UNKNOWN)
+  {
+    // Expiry is 30 days after by default.
+    // This is a sort of arbitrary range designed to be valid
+    // now with some slack in case the other side expects
+    // some before expiry.
+    //
+
+    mExpires = EXPIRATION_DEFAULT;
+    if (!aAlgorithm.IsObject()) {
+      return;
+    }
+
+    // Load the "expires" attribute from the algorithm dictionary.  This is
+    // (currently) non-standard; it exists to support testing of certificate
+    // expiration, since one month is too long to wait for a test to run.
+    JS::Rooted<JS::Value> exp(aCx, JS::UndefinedValue());
+    JS::Rooted<JSObject*> jsval(aCx, aAlgorithm.GetAsObject());
+    bool ok = JS_GetProperty(aCx, jsval, "expires", &exp);
+    int64_t expval;
+    if (ok) {
+      ok = JS::ToInt64(aCx, exp, &expval);
+    }
+    if (ok && expval > 0) {
+      mExpires = std::min(expval, EXPIRATION_MAX);
+    }
+  }
+
+private:
+  PRTime mExpires;
+  SSLKEAType mAuthType;
+  ScopedCERTCertificate mCertificate;
+  SECOidTag mSignatureAlg;
+
+  static CERTName* GenerateRandomName(PK11SlotInfo* aSlot)
+  {
+    uint8_t randomName[RTCCertificateCommonNameLength];
+    SECStatus rv = PK11_GenerateRandomOnSlot(aSlot, randomName,
+                                             sizeof(randomName));
+    if (rv != SECSuccess) {
+      return nullptr;
+    }
+
+    char buf[sizeof(randomName) * 2 + 4];
+    PL_strncpy(buf, "CN=", 3);
+    for (size_t i = 0; i < sizeof(randomName); ++i) {
+      PR_snprintf(&buf[i * 2 + 3], 2, "%.2x", randomName[i]);
+    }
+    buf[sizeof(buf) - 1] = '\0';
+
+    return CERT_AsciiToName(buf);
+  }
+
+  nsresult GenerateCertificate()
+  {
+    ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+    MOZ_ASSERT(slot.get());
+
+    ScopedCERTName subjectName(GenerateRandomName(slot.get()));
+    if (!subjectName) {
+      return NS_ERROR_DOM_UNKNOWN_ERR;
+    }
+
+    ScopedSECKEYPublicKey publicKey(mKeyPair.mPublicKey.get()->GetPublicKey());
+    ScopedCERTSubjectPublicKeyInfo spki(
+        SECKEY_CreateSubjectPublicKeyInfo(publicKey));
+    if (!spki) {
+      return NS_ERROR_DOM_UNKNOWN_ERR;
+    }
+
+    ScopedCERTCertificateRequest certreq(
+        CERT_CreateCertificateRequest(subjectName, spki, nullptr));
+    if (!certreq) {
+      return NS_ERROR_DOM_UNKNOWN_ERR;
+    }
+
+    PRTime now = PR_Now();
+    PRTime notBefore = now - EXPIRATION_SLACK;
+    mExpires += now;
+
+    ScopedCERTValidity validity(CERT_CreateValidity(notBefore, mExpires));
+    if (!validity) {
+      return NS_ERROR_DOM_UNKNOWN_ERR;
+    }
+
+    unsigned long serial;
+    // Note: This serial in principle could collide, but it's unlikely, and we
+    // don't expect anyone to be validating certificates anyway.
+    SECStatus rv =
+        PK11_GenerateRandomOnSlot(slot,
+                                  reinterpret_cast<unsigned char *>(&serial),
+                                  sizeof(serial));
+    if (rv != SECSuccess) {
+      return NS_ERROR_DOM_UNKNOWN_ERR;
+    }
+
+    CERTCertificate* cert = CERT_CreateCertificate(serial, subjectName,
+                                                   validity, certreq);
+    if (!cert) {
+      return NS_ERROR_DOM_UNKNOWN_ERR;
+    }
+    mCertificate.reset(cert);
+    return NS_OK;
+  }
+
+  nsresult SignCertificate()
+  {
+    MOZ_ASSERT(mSignatureAlg != SEC_OID_UNKNOWN);
+    PLArenaPool *arena = mCertificate->arena;
+
+    SECStatus rv = SECOID_SetAlgorithmID(arena, &mCertificate->signature,
+                                         mSignatureAlg, nullptr);
+    if (rv != SECSuccess) {
+      return NS_ERROR_DOM_UNKNOWN_ERR;
+    }
+
+    // Set version to X509v3.
+    *(mCertificate->version.data) = SEC_CERTIFICATE_VERSION_3;
+    mCertificate->version.len = 1;
+
+    SECItem innerDER = { siBuffer, nullptr, 0 };
+    if (!SEC_ASN1EncodeItem(arena, &innerDER, mCertificate,
+                            SEC_ASN1_GET(CERT_CertificateTemplate))) {
+      return NS_ERROR_DOM_UNKNOWN_ERR;
+    }
+
+    SECItem *signedCert = PORT_ArenaZNew(arena, SECItem);
+    if (!signedCert) {
+      return NS_ERROR_DOM_UNKNOWN_ERR;
+    }
+
+    ScopedSECKEYPrivateKey privateKey(mKeyPair.mPrivateKey.get()->GetPrivateKey());
+    rv = SEC_DerSignData(arena, signedCert, innerDER.data, innerDER.len,
+                         privateKey, mSignatureAlg);
+    if (rv != SECSuccess) {
+      return NS_ERROR_DOM_UNKNOWN_ERR;
+    }
+    mCertificate->derCert = *signedCert;
+    return NS_OK;
+  }
+
+  nsresult BeforeCrypto() override
+  {
+    if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
+      // Double check that size is OK.
+      auto sz = static_cast<size_t>(mRsaParams.keySizeInBits);
+      if (sz < RTCCertificateMinRsaSize) {
+        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+      }
+
+      mSignatureAlg = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
+      mAuthType = ssl_kea_rsa;
+
+    } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
+      // We only support good curves in WebCrypto.
+      // If that ever changes, check that a good one was chosen.
+
+      mSignatureAlg = SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE;
+      mAuthType = ssl_kea_ecdh;
+    } else {
+      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+    }
+    return NS_OK;
+  }
+
+  nsresult DoCrypto() override
+  {
+    nsresult rv = GenerateAsymmetricKeyTask::DoCrypto();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = GenerateCertificate();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = SignCertificate();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+  virtual void Resolve() override
+  {
+    // Make copies of the private key and certificate, otherwise, when this
+    // object is deleted, the structures they reference will be deleted too.
+    SECKEYPrivateKey* key = mKeyPair.mPrivateKey.get()->GetPrivateKey();
+    CERTCertificate* cert = CERT_DupCertificate(mCertificate);
+    nsRefPtr<RTCCertificate> result =
+        new RTCCertificate(mResultPromise->GetParentObject(),
+                           key, cert, mAuthType, mExpires);
+    mResultPromise->MaybeResolve(result);
+  }
+};
+
+already_AddRefed<Promise>
+RTCCertificate::GenerateCertificate(
+    const GlobalObject& aGlobal, const ObjectOrString& aKeygenAlgorithm,
+    ErrorResult& aRv, JSCompartment* aCompartment)
+{
+  nsIGlobalObject* global = xpc::NativeGlobal(aGlobal.Get());
+  nsRefPtr<Promise> p = Promise::Create(global, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+  Sequence<nsString> usages;
+  if (!usages.AppendElement(NS_LITERAL_STRING("sign"), fallible)) {
+    return nullptr;
+  }
+  nsRefPtr<WebCryptoTask> task =
+      new GenerateRTCCertificateTask(aGlobal.Context(),
+                                     aKeygenAlgorithm, usages);
+  task->DispatchWithPromise(p);
+  return p.forget();
+}
+
+RTCCertificate::RTCCertificate(nsIGlobalObject* aGlobal)
+    : mGlobal(aGlobal),
+      mPrivateKey(nullptr),
+      mCertificate(nullptr),
+      mAuthType(ssl_kea_null),
+      mExpires(0)
+{
+}
+
+RTCCertificate::RTCCertificate(nsIGlobalObject* aGlobal,
+                               SECKEYPrivateKey* aPrivateKey,
+                               CERTCertificate* aCertificate,
+                               SSLKEAType aAuthType,
+                               PRTime aExpires)
+    : mGlobal(aGlobal),
+      mPrivateKey(aPrivateKey),
+      mCertificate(aCertificate),
+      mAuthType(aAuthType),
+      mExpires(aExpires)
+{
+}
+
+RTCCertificate::~RTCCertificate()
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return;
+  }
+  destructorSafeDestroyNSSReference();
+  shutdown(calledFromObject);
+}
+
+// This creates some interesting lifecycle consequences, since the DtlsIdentity
+// holds NSS objects, but does not implement nsNSSShutDownObject.
+
+// Unfortunately, the code that uses DtlsIdentity cannot always use that lock
+// due to external linkage requirements.  Therefore, the lock is held on this
+// object instead.  Consequently, the DtlsIdentity that this method returns must
+// have a lifetime that is strictly shorter than the RTCCertificate.
+//
+// RTCPeerConnection provides this guarantee by holding a strong reference to
+// the RTCCertificate.  It will cleanup any DtlsIdentity instances that it
+// creates before the RTCCertificate reference is released.
+RefPtr<DtlsIdentity>
+RTCCertificate::CreateDtlsIdentity() const
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown() || !mPrivateKey || !mCertificate) {
+    return nullptr;
+  }
+  SECKEYPrivateKey* key = SECKEY_CopyPrivateKey(mPrivateKey);
+  CERTCertificate* cert = CERT_DupCertificate(mCertificate);
+  RefPtr<DtlsIdentity> id = new DtlsIdentity(key, cert, mAuthType);
+  return id;
+}
+
+JSObject*
+RTCCertificate::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return RTCCertificateBinding::Wrap(aCx, this, aGivenProto);
+}
+
+void
+RTCCertificate::virtualDestroyNSSReference()
+{
+  destructorSafeDestroyNSSReference();
+}
+
+void
+RTCCertificate::destructorSafeDestroyNSSReference()
+{
+  mPrivateKey.dispose();
+  mCertificate.dispose();
+}
+
+bool
+RTCCertificate::WritePrivateKey(JSStructuredCloneWriter* aWriter,
+                                const nsNSSShutDownPreventionLock& aLockProof) const
+{
+  JsonWebKey jwk;
+  nsresult rv = CryptoKey::PrivateKeyToJwk(mPrivateKey, jwk, aLockProof);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+  nsString json;
+  if (!jwk.ToJSON(json)) {
+    return false;
+  }
+  return WriteString(aWriter, json);
+}
+
+bool
+RTCCertificate::WriteCertificate(JSStructuredCloneWriter* aWriter,
+                                 const nsNSSShutDownPreventionLock& /*proof*/) const
+{
+  ScopedCERTCertificateList certs(CERT_CertListFromCert(mCertificate.get()));
+  if (!certs || certs->len <= 0) {
+    return false;
+  }
+  if (!JS_WriteUint32Pair(aWriter, certs->certs[0].len, 0)) {
+    return false;
+  }
+  return JS_WriteBytes(aWriter, certs->certs[0].data, certs->certs[0].len);
+}
+
+bool
+RTCCertificate::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown() || !mPrivateKey || !mCertificate) {
+    return false;
+  }
+
+  return JS_WriteUint32Pair(aWriter, RTCCERTIFICATE_SC_VERSION, mAuthType) &&
+      JS_WriteUint32Pair(aWriter, (mExpires >> 32) & 0xffffffff,
+                         mExpires & 0xffffffff) &&
+      WritePrivateKey(aWriter, locker) &&
+      WriteCertificate(aWriter, locker);
+}
+
+bool
+RTCCertificate::ReadPrivateKey(JSStructuredCloneReader* aReader,
+                               const nsNSSShutDownPreventionLock& aLockProof)
+{
+  nsString json;
+  if (!ReadString(aReader, json)) {
+    return false;
+  }
+  JsonWebKey jwk;
+  if (!jwk.Init(json)) {
+    return false;
+  }
+  mPrivateKey = CryptoKey::PrivateKeyFromJwk(jwk, aLockProof);
+  return !!mPrivateKey;
+}
+
+bool
+RTCCertificate::ReadCertificate(JSStructuredCloneReader* aReader,
+                                const nsNSSShutDownPreventionLock& /*proof*/)
+{
+  CryptoBuffer cert;
+  if (!ReadBuffer(aReader, cert) || cert.Length() == 0) {
+    return false;
+  }
+
+  SECItem der = { siBuffer, cert.Elements(),
+                  static_cast<unsigned int>(cert.Length()) };
+  mCertificate = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
+                                         &der, nullptr, true, true);
+  return !!mCertificate;
+}
+
+bool
+RTCCertificate::ReadStructuredClone(JSStructuredCloneReader* aReader)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return false;
+  }
+
+  uint32_t version, authType;
+  if (!JS_ReadUint32Pair(aReader, &version, &authType) ||
+      version != RTCCERTIFICATE_SC_VERSION) {
+    return false;
+  }
+  mAuthType = static_cast<SSLKEAType>(authType);
+
+  uint32_t high, low;
+  if (!JS_ReadUint32Pair(aReader, &high, &low)) {
+    return false;
+  }
+  mExpires = static_cast<PRTime>(high) << 32 | low;
+
+  return ReadPrivateKey(aReader, locker) &&
+      ReadCertificate(aReader, locker);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/webrtc/RTCCertificate.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_RTCCertificate_h
+#define mozilla_dom_RTCCertificate_h
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+#include "nsIGlobalObject.h"
+#include "nsNSSShutDown.h"
+#include "prtime.h"
+#include "sslt.h"
+#include "ScopedNSSTypes.h"
+
+#include "mozilla/ErrorResult.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/dom/Date.h"
+#include "mozilla/dom/CryptoKey.h"
+#include "mtransport/dtlsidentity.h"
+#include "js/StructuredClone.h"
+#include "js/TypeDecls.h"
+
+namespace mozilla {
+namespace dom {
+
+class ObjectOrString;
+
+class RTCCertificate final
+    : public nsISupports,
+      public nsWrapperCache,
+      public nsNSSShutDownObject
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(RTCCertificate)
+
+  // WebIDL method that implements RTCPeerConnection.generateCertificate.
+  static already_AddRefed<Promise> GenerateCertificate(
+      const GlobalObject& global, const ObjectOrString& keygenAlgorithm,
+      ErrorResult& aRv, JSCompartment* aCompartment = nullptr);
+
+  explicit RTCCertificate(nsIGlobalObject* aGlobal);
+  RTCCertificate(nsIGlobalObject* aGlobal, SECKEYPrivateKey* aPrivateKey,
+                 CERTCertificate* aCertificate, SSLKEAType aAuthType,
+                 PRTime aExpires);
+
+  nsIGlobalObject* GetParentObject() const { return mGlobal; }
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  // WebIDL expires attribute.  Note: JS dates are milliseconds since epoch;
+  // NSPR PRTime is in microseconds since the same epoch.
+  int64_t Expires() const { return mExpires / PR_USEC_PER_MSEC; }
+
+  // Accessors for use by PeerConnectionImpl.
+  RefPtr<DtlsIdentity> CreateDtlsIdentity() const;
+  CERTCertificate* Certificate() const { return mCertificate; }
+
+  // For nsNSSShutDownObject
+  virtual void virtualDestroyNSSReference() override;
+  void destructorSafeDestroyNSSReference();
+
+  // Structured clone methods
+  bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const;
+  bool ReadStructuredClone(JSStructuredCloneReader* aReader);
+
+private:
+  ~RTCCertificate();
+  void operator=(const RTCCertificate&) = delete;
+  RTCCertificate(const RTCCertificate&) = delete;
+
+  bool ReadCertificate(JSStructuredCloneReader* aReader,
+                       const nsNSSShutDownPreventionLock& /*lockproof*/);
+  bool ReadPrivateKey(JSStructuredCloneReader* aReader,
+                      const nsNSSShutDownPreventionLock& aLockProof);
+  bool WriteCertificate(JSStructuredCloneWriter* aWriter,
+                        const nsNSSShutDownPreventionLock& /*lockproof*/) const;
+  bool WritePrivateKey(JSStructuredCloneWriter* aWriter,
+                       const nsNSSShutDownPreventionLock& aLockProof) const;
+
+  nsRefPtr<nsIGlobalObject> mGlobal;
+  ScopedSECKEYPrivateKey mPrivateKey;
+  ScopedCERTCertificate mCertificate;
+  SSLKEAType mAuthType;
+  PRTime mExpires;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_RTCCertificate_h