Backed out changeset 398bdeea30b0 (bug 1037892) for build bustage
authorWes Kocher <wkocher@mozilla.com>
Fri, 26 Sep 2014 15:35:38 -0700
changeset 230784 d1268000b5d0597fbeecfdb7e807010e6a3c331a
parent 230783 ba2438fdc98e6b26a925e008a6053a3b53e24f13
child 230785 627e848b2bf369668371ac47261bd898cdf8979b
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1037892
milestone35.0a1
backs out398bdeea30b07712656cb016612375ca983ed61b
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
Backed out changeset 398bdeea30b0 (bug 1037892) for build bustage
dom/crypto/AesKeyAlgorithm.cpp
dom/crypto/AesKeyAlgorithm.h
dom/crypto/BasicSymmetricKeyAlgorithm.h
dom/crypto/CryptoBuffer.cpp
dom/crypto/CryptoBuffer.h
dom/crypto/CryptoKey.cpp
dom/crypto/CryptoKey.h
dom/crypto/CryptoKeyPair.cpp
dom/crypto/CryptoKeyPair.h
dom/crypto/EcKeyAlgorithm.cpp
dom/crypto/EcKeyAlgorithm.h
dom/crypto/HmacKeyAlgorithm.cpp
dom/crypto/HmacKeyAlgorithm.h
dom/crypto/KeyAlgorithm.cpp
dom/crypto/KeyAlgorithm.h
dom/crypto/KeyAlgorithmProxy.cpp
dom/crypto/KeyAlgorithmProxy.h
dom/crypto/RsaHashedKeyAlgorithm.cpp
dom/crypto/RsaHashedKeyAlgorithm.h
dom/crypto/RsaKeyAlgorithm.cpp
dom/crypto/RsaKeyAlgorithm.h
dom/crypto/WebCryptoCommon.h
dom/crypto/WebCryptoTask.cpp
dom/crypto/moz.build
dom/crypto/test/test_WebCrypto.html
dom/crypto/test/test_WebCrypto_ECDH.html
dom/crypto/test/test_WebCrypto_JWK.html
dom/crypto/test/test_WebCrypto_PBKDF2.html
dom/crypto/test/test_WebCrypto_RSA_OAEP.html
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/KeyAlgorithm.webidl
dom/webidl/SubtleCrypto.webidl
dom/webidl/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/crypto/AesKeyAlgorithm.cpp
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/AesKeyAlgorithm.h"
+#include "mozilla/dom/SubtleCryptoBinding.h"
+#include "mozilla/dom/WebCryptoCommon.h"
+
+namespace mozilla {
+namespace dom {
+
+JSObject*
+AesKeyAlgorithm::WrapObject(JSContext* aCx)
+{
+  return AesKeyAlgorithmBinding::Wrap(aCx, this);
+}
+
+nsString
+AesKeyAlgorithm::ToJwkAlg() const
+{
+  if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
+    switch (mLength) {
+      case 128: return NS_LITERAL_STRING(JWK_ALG_A128CBC);
+      case 192: return NS_LITERAL_STRING(JWK_ALG_A192CBC);
+      case 256: return NS_LITERAL_STRING(JWK_ALG_A256CBC);
+    }
+  }
+
+  if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
+    switch (mLength) {
+      case 128: return NS_LITERAL_STRING(JWK_ALG_A128CTR);
+      case 192: return NS_LITERAL_STRING(JWK_ALG_A192CTR);
+      case 256: return NS_LITERAL_STRING(JWK_ALG_A256CTR);
+    }
+  }
+
+  if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
+    switch (mLength) {
+      case 128: return NS_LITERAL_STRING(JWK_ALG_A128GCM);
+      case 192: return NS_LITERAL_STRING(JWK_ALG_A192GCM);
+      case 256: return NS_LITERAL_STRING(JWK_ALG_A256GCM);
+    }
+  }
+
+  if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
+    switch (mLength) {
+      case 128: return NS_LITERAL_STRING(JWK_ALG_A128KW);
+      case 192: return NS_LITERAL_STRING(JWK_ALG_A192KW);
+      case 256: return NS_LITERAL_STRING(JWK_ALG_A256KW);
+    }
+  }
+
+  return nsString();
+}
+
+bool
+AesKeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
+{
+  return JS_WriteUint32Pair(aWriter, SCTAG_AESKEYALG, 0) &&
+         JS_WriteUint32Pair(aWriter, mLength, 0) &&
+         WriteString(aWriter, mName);
+}
+
+KeyAlgorithm*
+AesKeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader)
+{
+  uint32_t length, zero;
+  nsString name;
+  bool read = JS_ReadUint32Pair(aReader, &length, &zero) &&
+              ReadString(aReader, name);
+  if (!read) {
+    return nullptr;
+  }
+
+  return new AesKeyAlgorithm(aGlobal, name, length);
+}
+
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/crypto/AesKeyAlgorithm.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_AesKeyAlgorithm_h
+#define mozilla_dom_AesKeyAlgorithm_h
+
+#include "mozilla/dom/BasicSymmetricKeyAlgorithm.h"
+#include "js/TypeDecls.h"
+
+namespace mozilla {
+namespace dom {
+
+class AesKeyAlgorithm MOZ_FINAL : public BasicSymmetricKeyAlgorithm
+{
+public:
+  AesKeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName, uint16_t aLength)
+    : BasicSymmetricKeyAlgorithm(aGlobal, aName, aLength)
+  {}
+
+  ~AesKeyAlgorithm()
+  {}
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  virtual nsString ToJwkAlg() const MOZ_OVERRIDE;
+
+  virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE;
+  static KeyAlgorithm* Create(nsIGlobalObject* aGlobal,
+                              JSStructuredCloneReader* aReader);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_AesKeyAlgorithm_h
new file mode 100644
--- /dev/null
+++ b/dom/crypto/BasicSymmetricKeyAlgorithm.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_BasicSymmetricKeyAlgorithm_h
+#define mozilla_dom_BasicSymmetricKeyAlgorithm_h
+
+#include "mozilla/dom/KeyAlgorithm.h"
+
+namespace mozilla {
+namespace dom {
+
+class BasicSymmetricKeyAlgorithm : public KeyAlgorithm
+{
+public:
+  BasicSymmetricKeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName, uint16_t aLength)
+    : KeyAlgorithm(aGlobal, aName)
+    , mLength(aLength)
+  {}
+
+  ~BasicSymmetricKeyAlgorithm()
+  {}
+
+  uint16_t Length() const
+  {
+    return mLength;
+  }
+
+protected:
+  uint16_t mLength;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_BasicSymmetricKeyAlgorithm_h
--- a/dom/crypto/CryptoBuffer.cpp
+++ b/dom/crypto/CryptoBuffer.cpp
@@ -156,23 +156,16 @@ CryptoBuffer::ToSECItem() const
   item->data = data;
   item->len = Length();
 
   memcpy(item->data, Elements(), Length());
 
   return item;
 }
 
-JSObject*
-CryptoBuffer::ToUint8Array(JSContext* aCx) const
-{
-  return Uint8Array::Create(aCx, Length(), Elements());
-}
-
-
 // "BigInt" comes from the WebCrypto spec
 // ("unsigned long" isn't very "big", of course)
 // Likewise, the spec calls for big-endian ints
 bool
 CryptoBuffer::GetBigIntValue(unsigned long& aRetVal)
 {
   if (Length() > sizeof(aRetVal)) {
     return false;
--- a/dom/crypto/CryptoBuffer.h
+++ b/dom/crypto/CryptoBuffer.h
@@ -35,17 +35,16 @@ public:
   {
     aArray.ComputeLengthAndData();
     return Assign(aArray.Data(), aArray.Length());
   }
 
   nsresult FromJwkBase64(const nsString& aBase64);
   nsresult ToJwkBase64(nsString& aBase64);
   SECItem* ToSECItem() const;
-  JSObject* ToUint8Array(JSContext* aCx) const;
 
   bool GetBigIntValue(unsigned long& aRetVal);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_CryptoBuffer_h
--- a/dom/crypto/CryptoKey.cpp
+++ b/dom/crypto/CryptoKey.cpp
@@ -5,22 +5,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "pk11pub.h"
 #include "cryptohi.h"
 #include "ScopedNSSTypes.h"
 #include "mozilla/dom/CryptoKey.h"
 #include "mozilla/dom/WebCryptoCommon.h"
 #include "mozilla/dom/SubtleCryptoBinding.h"
-#include "mozilla/dom/ToJSValue.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKey, mGlobal)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKey, mGlobal, mAlgorithm)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(CryptoKey)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(CryptoKey)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CryptoKey)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 nsresult
@@ -86,45 +85,20 @@ CryptoKey::GetType(nsString& aRetVal) co
 }
 
 bool
 CryptoKey::Extractable() const
 {
   return (mAttributes & EXTRACTABLE);
 }
 
-void
-CryptoKey::GetAlgorithm(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal,
-                        ErrorResult& aRv) const
+KeyAlgorithm*
+CryptoKey::Algorithm() const
 {
-  bool converted = false;
-  JS::RootedValue val(cx);
-  switch (mAlgorithm.mType) {
-    case KeyAlgorithmProxy::AES:
-      converted = ToJSValue(cx, mAlgorithm.mAes, &val);
-      break;
-    case KeyAlgorithmProxy::HMAC:
-      converted = ToJSValue(cx, mAlgorithm.mHmac, &val);
-      break;
-    case KeyAlgorithmProxy::RSA: {
-      RootedDictionary<RsaHashedKeyAlgorithm> rsa(cx);
-      mAlgorithm.mRsa.ToKeyAlgorithm(cx, rsa);
-      converted = ToJSValue(cx, rsa, &val);
-      break;
-    }
-    case KeyAlgorithmProxy::EC:
-      converted = ToJSValue(cx, mAlgorithm.mEc, &val);
-      break;
-  }
-  if (!converted) {
-    aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
-    return;
-  }
-
-  aRetVal.set(&val.toObject());
+  return mAlgorithm;
 }
 
 void
 CryptoKey::GetUsages(nsTArray<nsString>& aRetVal) const
 {
   if (mAttributes & ENCRYPT) {
     aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_ENCRYPT));
   }
@@ -146,28 +120,16 @@ CryptoKey::GetUsages(nsTArray<nsString>&
   if (mAttributes & WRAPKEY) {
     aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_WRAPKEY));
   }
   if (mAttributes & UNWRAPKEY) {
     aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_UNWRAPKEY));
   }
 }
 
-KeyAlgorithmProxy&
-CryptoKey::Algorithm()
-{
-  return mAlgorithm;
-}
-
-const KeyAlgorithmProxy&
-CryptoKey::Algorithm() const
-{
-  return mAlgorithm;
-}
-
 CryptoKey::KeyType
 CryptoKey::GetKeyType() const
 {
   return static_cast<CryptoKey::KeyType>(mAttributes & TYPE_MASK);
 }
 
 nsresult
 CryptoKey::SetType(const nsString& aType)
@@ -199,16 +161,22 @@ CryptoKey::SetExtractable(bool aExtracta
 {
   mAttributes &= CLEAR_EXTRACTABLE;
   if (aExtractable) {
     mAttributes |= EXTRACTABLE;
   }
 }
 
 void
+CryptoKey::SetAlgorithm(KeyAlgorithm* aAlgorithm)
+{
+  mAlgorithm = aAlgorithm;
+}
+
+void
 CryptoKey::ClearUsages()
 {
   mAttributes &= CLEAR_USAGES;
 }
 
 nsresult
 CryptoKey::AddUsage(const nsString& aUsage)
 {
@@ -233,52 +201,27 @@ CryptoKey::AddUsageIntersecting(const ns
 
 void
 CryptoKey::AddUsage(CryptoKey::KeyUsage aUsage)
 {
   mAttributes |= aUsage;
 }
 
 bool
-CryptoKey::HasAnyUsage()
-{
-  return !!(mAttributes & USAGES_MASK);
-}
-
-bool
 CryptoKey::HasUsage(CryptoKey::KeyUsage aUsage)
 {
   return !!(mAttributes & aUsage);
 }
 
 bool
 CryptoKey::HasUsageOtherThan(uint32_t aUsages)
 {
   return !!(mAttributes & USAGES_MASK & ~aUsages);
 }
 
-bool
-CryptoKey::IsRecognizedUsage(const nsString& aUsage)
-{
-  KeyUsage dummy;
-  nsresult rv = StringToUsage(aUsage, dummy);
-  return NS_SUCCEEDED(rv);
-}
-
-bool
-CryptoKey::AllUsagesRecognized(const Sequence<nsString>& aUsages)
-{
-  for (uint32_t i = 0; i < aUsages.Length(); ++i) {
-    if (!IsRecognizedUsage(aUsages[i])) {
-      return false;
-    }
-  }
-  return true;
-}
-
 void CryptoKey::SetSymKey(const CryptoBuffer& aSymKey)
 {
   mSymKey = aSymKey;
 }
 
 void
 CryptoKey::SetPrivateKey(SECKEYPrivateKey* aPrivateKey)
 {
@@ -493,31 +436,35 @@ PrivateKeyFromPrivateKeyTemplate(SECItem
 
   return SECKEY_CopyPrivateKey(privKey.get());
 }
 
 SECKEYPrivateKey*
 CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk,
                              const nsNSSShutDownPreventionLock& /*proofOfLock*/)
 {
+  if (!aJwk.mKty.WasPassed()) {
+    return nullptr;
+  }
+
   CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY;
   CK_BBOOL falseValue = CK_FALSE;
 
-  if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) {
+  if (aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_EC)) {
     // Verify that all of the required parameters are present
     CryptoBuffer x, y, d;
     if (!aJwk.mCrv.WasPassed() ||
         !aJwk.mX.WasPassed() || NS_FAILED(x.FromJwkBase64(aJwk.mX.Value())) ||
         !aJwk.mY.WasPassed() || NS_FAILED(y.FromJwkBase64(aJwk.mY.Value())) ||
         !aJwk.mD.WasPassed() || NS_FAILED(d.FromJwkBase64(aJwk.mD.Value()))) {
       return nullptr;
     }
 
     nsString namedCurve;
-    if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) {
+    if (!NormalizeNamedCurveValue(aJwk.mCrv.Value(), namedCurve)) {
       return nullptr;
     }
 
     ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
     if (!arena) {
       return nullptr;
     }
 
@@ -552,17 +499,17 @@ CryptoKey::PrivateKeyFromJwk(const JsonW
       { CKA_EC_POINT,         ecPoint->data,        ecPoint->len },
       { CKA_VALUE,            (void*) d.Elements(), d.Length() },
     };
 
     return PrivateKeyFromPrivateKeyTemplate(objID, keyTemplate,
                                             PR_ARRAY_SIZE(keyTemplate));
   }
 
-  if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) {
+  if (aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_RSA)) {
     // Verify that all of the required parameters are present
     CryptoBuffer n, e, d, p, q, dp, dq, qi;
     if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) ||
         !aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value())) ||
         !aJwk.mD.WasPassed() || NS_FAILED(d.FromJwkBase64(aJwk.mD.Value())) ||
         !aJwk.mP.WasPassed() || NS_FAILED(p.FromJwkBase64(aJwk.mP.Value())) ||
         !aJwk.mQ.WasPassed() || NS_FAILED(q.FromJwkBase64(aJwk.mQ.Value())) ||
         !aJwk.mDp.WasPassed() || NS_FAILED(dp.FromJwkBase64(aJwk.mDp.Value())) ||
@@ -691,17 +638,17 @@ ECKeyToJwk(const PK11ObjectType aKeyType
   memcpy(ecPointY->data, aPublicValue->data + 1 + flen, flen);
 
   CryptoBuffer x, y;
   if (!x.Assign(ecPointX) || NS_FAILED(x.ToJwkBase64(aRetVal.mX.Value())) ||
       !y.Assign(ecPointY) || NS_FAILED(y.ToJwkBase64(aRetVal.mY.Value()))) {
     return false;
   }
 
-  aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_EC);
+  aRetVal.mKty.Construct(NS_LITERAL_STRING(JWK_TYPE_EC));
   return true;
 }
 
 nsresult
 CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey* aPrivKey,
                            JsonWebKey& aRetVal,
                            const nsNSSShutDownPreventionLock& /*proofOfLock*/)
 {
@@ -722,17 +669,17 @@ CryptoKey::PrivateKeyToJwk(SECKEYPrivate
           !ReadAndEncodeAttribute(aPrivKey, CKA_PRIME_1, aRetVal.mP) ||
           !ReadAndEncodeAttribute(aPrivKey, CKA_PRIME_2, aRetVal.mQ) ||
           !ReadAndEncodeAttribute(aPrivKey, CKA_EXPONENT_1, aRetVal.mDp) ||
           !ReadAndEncodeAttribute(aPrivKey, CKA_EXPONENT_2, aRetVal.mDq) ||
           !ReadAndEncodeAttribute(aPrivKey, CKA_COEFFICIENT, aRetVal.mQi)) {
         return NS_ERROR_DOM_OPERATION_ERR;
       }
 
-      aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_RSA);
+      aRetVal.mKty.Construct(NS_LITERAL_STRING(JWK_TYPE_RSA));
       return NS_OK;
     }
     case ecKey: {
       // Read EC params.
       ScopedSECItem params(::SECITEM_AllocItem(nullptr, nullptr, 0));
       SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, aPrivKey,
                                            CKA_EC_PARAMS, params);
       if (rv != SECSuccess) {
@@ -764,17 +711,21 @@ CryptoKey::PrivateKeyToJwk(SECKEYPrivate
       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   }
 }
 
 SECKEYPublicKey*
 CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk,
                             const nsNSSShutDownPreventionLock& /*proofOfLock*/)
 {
-  if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) {
+  if (!aJwk.mKty.WasPassed()) {
+    return nullptr;
+  }
+
+  if (aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_RSA)) {
     // Verify that all of the required parameters are present
     CryptoBuffer n, e;
     if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) ||
         !aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value()))) {
       return nullptr;
     }
 
     // Transcode to a DER RSAPublicKey structure
@@ -797,17 +748,17 @@ CryptoKey::PublicKeyFromJwk(const JsonWe
                                            rsaPublicKeyTemplate));
     if (!pkDer.get()) {
       return nullptr;
     }
 
     return SECKEY_ImportDERPublicKey(pkDer.get(), CKK_RSA);
   }
 
-  if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) {
+  if (aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_EC)) {
     // Verify that all of the required parameters are present
     CryptoBuffer x, y;
     if (!aJwk.mCrv.WasPassed() ||
         !aJwk.mX.WasPassed() || NS_FAILED(x.FromJwkBase64(aJwk.mX.Value())) ||
         !aJwk.mY.WasPassed() || NS_FAILED(y.FromJwkBase64(aJwk.mY.Value()))) {
       return nullptr;
     }
 
@@ -821,17 +772,17 @@ CryptoKey::PublicKeyFromJwk(const JsonWe
       return nullptr;
     }
 
     key->keyType = ecKey;
     key->pkcs11Slot = nullptr;
     key->pkcs11ID = CK_INVALID_HANDLE;
 
     nsString namedCurve;
-    if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) {
+    if (!NormalizeNamedCurveValue(aJwk.mCrv.Value(), namedCurve)) {
       return nullptr;
     }
 
     // Create parameters.
     SECItem* params = CreateECParamsForCurve(namedCurve, arena.get());
     if (!params) {
       return nullptr;
     }
@@ -863,17 +814,17 @@ CryptoKey::PublicKeyToJwk(SECKEYPublicKe
 
       if (!n.Assign(&aPubKey->u.rsa.modulus) ||
           !e.Assign(&aPubKey->u.rsa.publicExponent) ||
           NS_FAILED(n.ToJwkBase64(aRetVal.mN.Value())) ||
           NS_FAILED(e.ToJwkBase64(aRetVal.mE.Value()))) {
         return NS_ERROR_DOM_OPERATION_ERR;
       }
 
-      aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_RSA);
+      aRetVal.mKty.Construct(NS_LITERAL_STRING(JWK_TYPE_RSA));
       return NS_OK;
     }
     case ecKey:
       if (!ECKeyToJwk(PK11_TypePubKey, aPubKey, &aPubKey->u.ec.DEREncodedParams,
                       &aPubKey->u.ec.publicValue, aRetVal)) {
         return NS_ERROR_DOM_OPERATION_ERR;
       }
       return NS_OK;
@@ -901,53 +852,54 @@ CryptoKey::WriteStructuredClone(JSStruct
   if (mPrivateKey) {
     CryptoKey::PrivateKeyToPkcs8(mPrivateKey, priv, locker);
   }
 
   if (mPublicKey) {
     CryptoKey::PublicKeyToSpki(mPublicKey, pub, locker);
   }
 
-  return JS_WriteUint32Pair(aWriter, mAttributes, CRYPTOKEY_SC_VERSION) &&
+  return JS_WriteUint32Pair(aWriter, mAttributes, 0) &&
          WriteBuffer(aWriter, mSymKey) &&
          WriteBuffer(aWriter, priv) &&
          WriteBuffer(aWriter, pub) &&
-         mAlgorithm.WriteStructuredClone(aWriter);
+         mAlgorithm->WriteStructuredClone(aWriter);
 }
 
 bool
 CryptoKey::ReadStructuredClone(JSStructuredCloneReader* aReader)
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return false;
   }
 
-  uint32_t version;
+  uint32_t zero;
   CryptoBuffer sym, priv, pub;
+  nsRefPtr<KeyAlgorithm> algorithm;
 
-  bool read = JS_ReadUint32Pair(aReader, &mAttributes, &version) &&
-              (version == CRYPTOKEY_SC_VERSION) &&
+  bool read = JS_ReadUint32Pair(aReader, &mAttributes, &zero) &&
               ReadBuffer(aReader, sym) &&
               ReadBuffer(aReader, priv) &&
               ReadBuffer(aReader, pub) &&
-              mAlgorithm.ReadStructuredClone(aReader);
+              (algorithm = KeyAlgorithm::Create(mGlobal, aReader));
   if (!read) {
     return false;
   }
 
   if (sym.Length() > 0)  {
     mSymKey = sym;
   }
   if (priv.Length() > 0) {
     mPrivateKey = CryptoKey::PrivateKeyFromPkcs8(priv, locker);
   }
   if (pub.Length() > 0)  {
     mPublicKey = CryptoKey::PublicKeyFromSpki(pub, locker);
   }
+  mAlgorithm = algorithm;
 
   // Ensure that what we've read is consistent
   // If the attributes indicate a key type, should have a key of that type
   if (!((GetKeyType() == SECRET  && mSymKey.Length() > 0) ||
         (GetKeyType() == PRIVATE && mPrivateKey) ||
         (GetKeyType() == PUBLIC  && mPublicKey))) {
     return false;
   }
--- a/dom/crypto/CryptoKey.h
+++ b/dom/crypto/CryptoKey.h
@@ -9,24 +9,21 @@
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 #include "nsIGlobalObject.h"
 #include "nsNSSShutDown.h"
 #include "pk11pub.h"
 #include "keyhi.h"
 #include "ScopedNSSTypes.h"
-#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/KeyAlgorithm.h"
 #include "mozilla/dom/CryptoBuffer.h"
-#include "mozilla/dom/KeyAlgorithmProxy.h"
 #include "js/StructuredClone.h"
 #include "js/TypeDecls.h"
 
-#define CRYPTOKEY_SC_VERSION 0x00000001
-
 class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
 /*
 
 The internal structure of keys is dictated by the need for cloning.
@@ -98,38 +95,33 @@ public:
     return mGlobal;
   }
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   // WebIDL methods
   void GetType(nsString& aRetVal) const;
   bool Extractable() const;
-  void GetAlgorithm(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal,
-                    ErrorResult& aRv) const;
+  KeyAlgorithm* Algorithm() const;
   void GetUsages(nsTArray<nsString>& aRetVal) const;
 
   // The below methods are not exposed to JS, but C++ can use
   // them to manipulate the object
 
-  KeyAlgorithmProxy& Algorithm();
-  const KeyAlgorithmProxy& Algorithm() const;
   KeyType GetKeyType() const;
   nsresult SetType(const nsString& aType);
   void SetType(KeyType aType);
   void SetExtractable(bool aExtractable);
+  void SetAlgorithm(KeyAlgorithm* aAlgorithm);
   void ClearUsages();
   nsresult AddUsage(const nsString& aUsage);
   nsresult AddUsageIntersecting(const nsString& aUsage, uint32_t aUsageMask);
   void AddUsage(KeyUsage aUsage);
-  bool HasAnyUsage();
   bool HasUsage(KeyUsage aUsage);
   bool HasUsageOtherThan(uint32_t aUsages);
-  static bool IsRecognizedUsage(const nsString& aUsage);
-  static bool AllUsagesRecognized(const Sequence<nsString>& aUsages);
 
   void SetSymKey(const CryptoBuffer& aSymKey);
   void SetPrivateKey(SECKEYPrivateKey* aPrivateKey);
   void SetPublicKey(SECKEYPublicKey* aPublicKey);
 
   // Accessors for the keys themselves
   // Note: GetPrivateKey and GetPublicKey return copies of the internal
   // key handles, which the caller must free with SECKEY_DestroyPrivateKey
@@ -175,17 +167,17 @@ public:
   bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const;
   bool ReadStructuredClone(JSStructuredCloneReader* aReader);
 
 private:
   ~CryptoKey();
 
   nsRefPtr<nsIGlobalObject> mGlobal;
   uint32_t mAttributes; // see above
-  KeyAlgorithmProxy mAlgorithm;
+  nsRefPtr<KeyAlgorithm> mAlgorithm;
 
   // Only one key handle should be set, according to the KeyType
   CryptoBuffer mSymKey;
   ScopedSECKEYPrivateKey mPrivateKey;
   ScopedSECKEYPublicKey mPublicKey;
 };
 
 } // namespace dom
new file mode 100644
--- /dev/null
+++ b/dom/crypto/CryptoKeyPair.cpp
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/CryptoKeyPair.h"
+#include "mozilla/dom/SubtleCryptoBinding.h"
+#include "nsContentUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKeyPair, mGlobal, mPublicKey, mPrivateKey)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(CryptoKeyPair)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(CryptoKeyPair)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CryptoKeyPair)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+JSObject*
+CryptoKeyPair::WrapObject(JSContext* aCx)
+{
+  return CryptoKeyPairBinding::Wrap(aCx, this);
+}
+
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/crypto/CryptoKeyPair.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_CryptoKeyPair_h
+#define mozilla_dom_CryptoKeyPair_h
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+#include "nsIGlobalObject.h"
+#include "mozilla/dom/CryptoKey.h"
+#include "js/TypeDecls.h"
+
+namespace mozilla {
+namespace dom {
+
+class CryptoKeyPair MOZ_FINAL : public nsISupports,
+                                public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CryptoKeyPair)
+
+public:
+  explicit CryptoKeyPair(nsIGlobalObject* aGlobal)
+    : mGlobal(aGlobal)
+    , mPublicKey(new CryptoKey(aGlobal))
+    , mPrivateKey(new CryptoKey(aGlobal))
+  {
+    SetIsDOMBinding();
+  }
+
+  nsIGlobalObject* GetParentObject() const
+  {
+    return mGlobal;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  CryptoKey* PublicKey() const
+  {
+    return mPublicKey;
+  }
+
+  CryptoKey* PrivateKey() const
+  {
+    return mPrivateKey;
+  }
+
+private:
+  ~CryptoKeyPair() {}
+
+  nsRefPtr<nsIGlobalObject> mGlobal;
+  nsRefPtr<CryptoKey> mPublicKey;
+  nsRefPtr<CryptoKey> mPrivateKey;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_CryptoKeyPair_h
new file mode 100644
--- /dev/null
+++ b/dom/crypto/EcKeyAlgorithm.cpp
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/EcKeyAlgorithm.h"
+#include "mozilla/dom/SubtleCryptoBinding.h"
+#include "mozilla/dom/WebCryptoCommon.h"
+
+namespace mozilla {
+namespace dom {
+
+JSObject*
+EcKeyAlgorithm::WrapObject(JSContext* aCx)
+{
+  return EcKeyAlgorithmBinding::Wrap(aCx, this);
+}
+
+bool
+EcKeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
+{
+  return JS_WriteUint32Pair(aWriter, SCTAG_ECKEYALG, 0) &&
+         WriteString(aWriter, mNamedCurve) &&
+         WriteString(aWriter, mName);
+}
+
+KeyAlgorithm*
+EcKeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader)
+{
+  nsString name;
+  nsString namedCurve;
+  bool read = ReadString(aReader, namedCurve) &&
+              ReadString(aReader, name);
+  if (!read) {
+    return nullptr;
+  }
+
+  return new EcKeyAlgorithm(aGlobal, name, namedCurve);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/crypto/EcKeyAlgorithm.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_EcKeyAlgorithm_h
+#define mozilla_dom_EcKeyAlgorithm_h
+
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/KeyAlgorithm.h"
+#include "js/TypeDecls.h"
+
+namespace mozilla {
+namespace dom {
+
+class EcKeyAlgorithm : public KeyAlgorithm
+{
+public:
+  EcKeyAlgorithm(nsIGlobalObject* aGlobal,
+                 const nsString& aName,
+                 const nsString& aNamedCurve)
+    : KeyAlgorithm(aGlobal, aName)
+    , mNamedCurve(aNamedCurve)
+  {}
+
+  ~EcKeyAlgorithm()
+  {}
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  void GetNamedCurve(nsString& aRetVal) const
+  {
+    aRetVal.Assign(mNamedCurve);
+  }
+
+  virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE;
+  static KeyAlgorithm* Create(nsIGlobalObject* aGlobal,
+                              JSStructuredCloneReader* aReader);
+
+protected:
+  nsString mNamedCurve;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_EcKeyAlgorithm_h
new file mode 100644
--- /dev/null
+++ b/dom/crypto/HmacKeyAlgorithm.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/HmacKeyAlgorithm.h"
+#include "mozilla/dom/SubtleCryptoBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(HmacKeyAlgorithm, KeyAlgorithm, mHash)
+NS_IMPL_ADDREF_INHERITED(HmacKeyAlgorithm, KeyAlgorithm)
+NS_IMPL_RELEASE_INHERITED(HmacKeyAlgorithm, KeyAlgorithm)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HmacKeyAlgorithm)
+NS_INTERFACE_MAP_END_INHERITING(KeyAlgorithm)
+
+JSObject*
+HmacKeyAlgorithm::WrapObject(JSContext* aCx)
+{
+  return HmacKeyAlgorithmBinding::Wrap(aCx, this);
+}
+
+nsString
+HmacKeyAlgorithm::ToJwkAlg() const
+{
+  switch (mMechanism) {
+    case CKM_SHA_1_HMAC:  return NS_LITERAL_STRING(JWK_ALG_HS1);
+    case CKM_SHA256_HMAC: return NS_LITERAL_STRING(JWK_ALG_HS256);
+    case CKM_SHA384_HMAC: return NS_LITERAL_STRING(JWK_ALG_HS384);
+    case CKM_SHA512_HMAC: return NS_LITERAL_STRING(JWK_ALG_HS512);
+  }
+  return nsString();
+}
+
+bool
+HmacKeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
+{
+  nsString hashName;
+  mHash->GetName(hashName);
+  return JS_WriteUint32Pair(aWriter, SCTAG_HMACKEYALG, 0) &&
+         JS_WriteUint32Pair(aWriter, mLength, 0) &&
+         WriteString(aWriter, hashName) &&
+         WriteString(aWriter, mName);
+}
+
+KeyAlgorithm*
+HmacKeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader)
+{
+  uint32_t length, zero;
+  nsString hash, name;
+  bool read = JS_ReadUint32Pair(aReader, &length, &zero) &&
+              ReadString(aReader, hash) &&
+              ReadString(aReader, name);
+  if (!read) {
+    return nullptr;
+  }
+
+  return new HmacKeyAlgorithm(aGlobal, name, length, hash);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/crypto/HmacKeyAlgorithm.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_HmacKeyAlgorithm_h
+#define mozilla_dom_HmacKeyAlgorithm_h
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+#include "nsAutoPtr.h"
+#include "mozilla/dom/KeyAlgorithm.h"
+#include "mozilla/dom/WebCryptoCommon.h"
+#include "js/TypeDecls.h"
+
+namespace mozilla {
+namespace dom {
+
+class HmacKeyAlgorithm MOZ_FINAL : public KeyAlgorithm
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HmacKeyAlgorithm, KeyAlgorithm)
+
+  HmacKeyAlgorithm(nsIGlobalObject* aGlobal,
+                   const nsString& aName,
+                   uint32_t aLength,
+                   const nsString& aHash)
+    : KeyAlgorithm(aGlobal, aName)
+    , mHash(new KeyAlgorithm(aGlobal, aHash))
+    , mLength(aLength)
+  {
+    switch (mHash->Mechanism()) {
+      case CKM_SHA_1: mMechanism = CKM_SHA_1_HMAC; break;
+      case CKM_SHA256: mMechanism = CKM_SHA256_HMAC; break;
+      case CKM_SHA384: mMechanism = CKM_SHA384_HMAC; break;
+      case CKM_SHA512: mMechanism = CKM_SHA512_HMAC; break;
+      default: mMechanism = UNKNOWN_CK_MECHANISM; break;
+    }
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  KeyAlgorithm* Hash() const
+  {
+    return mHash;
+  }
+
+  uint32_t Length() const
+  {
+    return mLength;
+  }
+
+  virtual nsString ToJwkAlg() const MOZ_OVERRIDE;
+
+  virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE;
+  static KeyAlgorithm* Create(nsIGlobalObject* aGlobal,
+                              JSStructuredCloneReader* aReader);
+
+protected:
+  ~HmacKeyAlgorithm()
+  {}
+
+  nsRefPtr<KeyAlgorithm> mHash;
+  uint32_t mLength;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_HmacKeyAlgorithm_h
new file mode 100644
--- /dev/null
+++ b/dom/crypto/KeyAlgorithm.cpp
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/KeyAlgorithm.h"
+#include "mozilla/dom/WebCryptoCommon.h"
+#include "mozilla/dom/AesKeyAlgorithm.h"
+#include "mozilla/dom/EcKeyAlgorithm.h"
+#include "mozilla/dom/HmacKeyAlgorithm.h"
+#include "mozilla/dom/RsaKeyAlgorithm.h"
+#include "mozilla/dom/RsaHashedKeyAlgorithm.h"
+#include "mozilla/dom/SubtleCryptoBinding.h"
+#include "mozilla/dom/WebCryptoCommon.h"
+
+namespace mozilla {
+namespace dom {
+
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(KeyAlgorithm, mGlobal)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(KeyAlgorithm)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(KeyAlgorithm)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(KeyAlgorithm)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+KeyAlgorithm::KeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName)
+  : mGlobal(aGlobal)
+  , mName(aName)
+{
+  SetIsDOMBinding();
+
+  // Set mechanism based on algorithm name
+  mMechanism = MapAlgorithmNameToMechanism(aName);
+
+  // HMAC not handled here, since it requires extra info
+}
+
+KeyAlgorithm::~KeyAlgorithm()
+{
+}
+
+JSObject*
+KeyAlgorithm::WrapObject(JSContext* aCx)
+{
+  return KeyAlgorithmBinding::Wrap(aCx, this);
+}
+
+nsString
+KeyAlgorithm::ToJwkAlg() const
+{
+  return nsString();
+}
+
+void
+KeyAlgorithm::GetName(nsString& aRetVal) const
+{
+  aRetVal.Assign(mName);
+}
+
+bool
+KeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
+{
+  return WriteString(aWriter, mName);
+}
+
+KeyAlgorithm*
+KeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader)
+{
+  uint32_t tag, zero;
+  bool read = JS_ReadUint32Pair( aReader, &tag, &zero );
+  if (!read) {
+    return nullptr;
+  }
+
+  KeyAlgorithm* algorithm = nullptr;
+  switch (tag) {
+    case SCTAG_KEYALG: {
+      nsString name;
+      read = ReadString(aReader, name);
+      if (!read) {
+        return nullptr;
+      }
+      algorithm = new KeyAlgorithm(aGlobal, name);
+      break;
+    }
+    case SCTAG_AESKEYALG: {
+      algorithm = AesKeyAlgorithm::Create(aGlobal, aReader);
+      break;
+    }
+    case SCTAG_ECKEYALG: {
+      algorithm = EcKeyAlgorithm::Create(aGlobal, aReader);
+      break;
+    }
+    case SCTAG_HMACKEYALG: {
+      algorithm = HmacKeyAlgorithm::Create(aGlobal, aReader);
+      break;
+    }
+    case SCTAG_RSAKEYALG: {
+      algorithm = RsaKeyAlgorithm::Create(aGlobal, aReader);
+      break;
+    }
+    case SCTAG_RSAHASHEDKEYALG: {
+      algorithm = RsaHashedKeyAlgorithm::Create(aGlobal, aReader);
+      break;
+    }
+    // No default, algorithm is already nullptr
+  }
+
+  return algorithm;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/crypto/KeyAlgorithm.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_KeyAlgorithm_h
+#define mozilla_dom_KeyAlgorithm_h
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsIGlobalObject.h"
+#include "nsWrapperCache.h"
+#include "pk11pub.h"
+#include "mozilla/dom/CryptoBuffer.h"
+#include "js/StructuredClone.h"
+#include "js/TypeDecls.h"
+
+namespace mozilla {
+namespace dom {
+
+class CryptoKey;
+class KeyAlgorithm;
+
+enum KeyAlgorithmStructuredCloneTags {
+  SCTAG_KEYALG,
+  SCTAG_AESKEYALG,
+  SCTAG_ECKEYALG,
+  SCTAG_HMACKEYALG,
+  SCTAG_RSAKEYALG,
+  SCTAG_RSAHASHEDKEYALG
+};
+
+}
+
+namespace dom {
+
+class KeyAlgorithm : public nsISupports,
+                     public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(KeyAlgorithm)
+
+public:
+  KeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName);
+
+  nsIGlobalObject* GetParentObject() const
+  {
+    return mGlobal;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  void GetName(nsString& aRetVal) const;
+
+  virtual nsString ToJwkAlg() const;
+
+  // Structured clone support methods
+  virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const;
+  static KeyAlgorithm* Create(nsIGlobalObject* aGlobal,
+                              JSStructuredCloneReader* aReader);
+
+  // Helper method to look up NSS methods
+  // Sub-classes should assign mMechanism on constructor
+  CK_MECHANISM_TYPE Mechanism() const {
+    return mMechanism;
+  }
+
+protected:
+  virtual ~KeyAlgorithm();
+
+  nsRefPtr<nsIGlobalObject> mGlobal;
+  nsString mName;
+  CK_MECHANISM_TYPE mMechanism;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_KeyAlgorithm_h
deleted file mode 100644
--- a/dom/crypto/KeyAlgorithmProxy.cpp
+++ /dev/null
@@ -1,215 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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/KeyAlgorithmProxy.h"
-#include "mozilla/dom/WebCryptoCommon.h"
-
-namespace mozilla {
-namespace dom {
-
-bool
-KeyAlgorithmProxy::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
-{
-  if (!WriteString(aWriter, mName) ||
-      !JS_WriteUint32Pair(aWriter, mType, KEY_ALGORITHM_SC_VERSION)) {
-    return false;
-  }
-
-  switch (mType) {
-    case AES:
-      return JS_WriteUint32Pair(aWriter, mAes.mLength, 0);
-    case HMAC:
-      return JS_WriteUint32Pair(aWriter, mHmac.mLength, 0) &&
-             WriteString(aWriter, mHmac.mHash.mName);
-    case RSA: {
-      return JS_WriteUint32Pair(aWriter, mRsa.mModulusLength, 0) &&
-             WriteBuffer(aWriter, mRsa.mPublicExponent) &&
-             WriteString(aWriter, mRsa.mHash.mName);
-    }
-    case EC:
-      return WriteString(aWriter, mEc.mNamedCurve);
-  }
-
-  return false;
-}
-
-bool
-KeyAlgorithmProxy::ReadStructuredClone(JSStructuredCloneReader* aReader)
-{
-  uint32_t type, version, dummy;
-  if (!ReadString(aReader, mName) ||
-      !JS_ReadUint32Pair(aReader, &type, &version)) {
-    return false;
-  }
-
-  if (version != KEY_ALGORITHM_SC_VERSION) {
-    return false;
-  }
-
-  mType = (KeyAlgorithmType) type;
-  switch (mType) {
-    case AES: {
-      uint32_t length;
-      if (!JS_ReadUint32Pair(aReader, &length, &dummy)) {
-        return false;
-      }
-
-      mAes.mLength = length;
-      mAes.mName = mName;
-      return true;
-    }
-    case HMAC: {
-      if (!JS_ReadUint32Pair(aReader, &mHmac.mLength, &dummy) ||
-          !ReadString(aReader, mHmac.mHash.mName)) {
-        return false;
-      }
-
-      mHmac.mName = mName;
-      return true;
-    }
-    case RSA: {
-      uint32_t modulusLength;
-      nsString hashName;
-      if (!JS_ReadUint32Pair(aReader, &modulusLength, &dummy) ||
-          !ReadBuffer(aReader, mRsa.mPublicExponent) ||
-          !ReadString(aReader, mRsa.mHash.mName)) {
-        return false;
-      }
-
-      mRsa.mModulusLength = modulusLength;
-      mRsa.mName = mName;
-      return true;
-    }
-    case EC: {
-      nsString namedCurve;
-      if (!ReadString(aReader, mEc.mNamedCurve)) {
-        return false;
-      }
-
-      mEc.mName = mName;
-      return true;
-    }
-  }
-
-  return false;
-}
-
-CK_MECHANISM_TYPE
-KeyAlgorithmProxy::Mechanism() const
-{
-  if (mType == HMAC) {
-    return GetMechanism(mHmac);
-  }
-  return MapAlgorithmNameToMechanism(mName);
-}
-
-nsString
-KeyAlgorithmProxy::JwkAlg() const
-{
-  if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
-    switch (mAes.mLength) {
-      case 128: return NS_LITERAL_STRING(JWK_ALG_A128CBC);
-      case 192: return NS_LITERAL_STRING(JWK_ALG_A192CBC);
-      case 256: return NS_LITERAL_STRING(JWK_ALG_A256CBC);
-    }
-  }
-
-  if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
-    switch (mAes.mLength) {
-      case 128: return NS_LITERAL_STRING(JWK_ALG_A128CTR);
-      case 192: return NS_LITERAL_STRING(JWK_ALG_A192CTR);
-      case 256: return NS_LITERAL_STRING(JWK_ALG_A256CTR);
-    }
-  }
-
-  if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
-    switch (mAes.mLength) {
-      case 128: return NS_LITERAL_STRING(JWK_ALG_A128GCM);
-      case 192: return NS_LITERAL_STRING(JWK_ALG_A192GCM);
-      case 256: return NS_LITERAL_STRING(JWK_ALG_A256GCM);
-    }
-  }
-
-  if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
-    switch (mAes.mLength) {
-      case 128: return NS_LITERAL_STRING(JWK_ALG_A128KW);
-      case 192: return NS_LITERAL_STRING(JWK_ALG_A192KW);
-      case 256: return NS_LITERAL_STRING(JWK_ALG_A256KW);
-    }
-  }
-
-  if (mName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
-    nsString hashName = mHmac.mHash.mName;
-    if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
-      return NS_LITERAL_STRING(JWK_ALG_HS1);
-    } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
-      return NS_LITERAL_STRING(JWK_ALG_HS256);
-    } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
-      return NS_LITERAL_STRING(JWK_ALG_HS384);
-    } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
-      return NS_LITERAL_STRING(JWK_ALG_HS512);
-    }
-  }
-
-  if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
-    nsString hashName = mRsa.mHash.mName;
-    if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
-      return NS_LITERAL_STRING(JWK_ALG_RS1);
-    } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
-      return NS_LITERAL_STRING(JWK_ALG_RS256);
-    } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
-      return NS_LITERAL_STRING(JWK_ALG_RS384);
-    } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
-      return NS_LITERAL_STRING(JWK_ALG_RS512);
-    }
-  }
-
-  if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
-    nsString hashName = mRsa.mHash.mName;
-    if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
-      return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP);
-    } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
-      return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_256);
-    } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
-      return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_384);
-    } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
-      return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_512);
-    }
-  }
-
-  return nsString();
-}
-
-CK_MECHANISM_TYPE
-KeyAlgorithmProxy::GetMechanism(const KeyAlgorithm& aAlgorithm)
-{
-  // For everything but HMAC, the name determines the mechanism
-  // HMAC is handled by the specialization below
-  return MapAlgorithmNameToMechanism(aAlgorithm.mName);
-}
-
-CK_MECHANISM_TYPE
-KeyAlgorithmProxy::GetMechanism(const HmacKeyAlgorithm& aAlgorithm)
-{
-  // The use of HmacKeyAlgorithm doesn't completely prevent this
-  // method from being called with dictionaries that don't really
-  // represent HMAC key algorithms.
-  MOZ_ASSERT(aAlgorithm.mName.EqualsLiteral(WEBCRYPTO_ALG_HMAC));
-
-  CK_MECHANISM_TYPE hashMech;
-  hashMech = MapAlgorithmNameToMechanism(aAlgorithm.mHash.mName);
-
-  switch (hashMech) {
-    case CKM_SHA_1: return CKM_SHA_1_HMAC;
-    case CKM_SHA256: return CKM_SHA256_HMAC;
-    case CKM_SHA384: return CKM_SHA384_HMAC;
-    case CKM_SHA512: return CKM_SHA512_HMAC;
-  }
-  return UNKNOWN_CK_MECHANISM;
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/crypto/KeyAlgorithmProxy.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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_KeyAlgorithmProxy_h
-#define mozilla_dom_KeyAlgorithmProxy_h
-
-#include "pk11pub.h"
-#include "js/StructuredClone.h"
-#include "mozilla/dom/KeyAlgorithmBinding.h"
-#include "mozilla/dom/WebCryptoCommon.h"
-
-#define KEY_ALGORITHM_SC_VERSION 0x00000001
-
-namespace mozilla {
-namespace dom {
-
-// A heap-safe variant of RsaHashedKeyAlgorithm
-// The only difference is that it uses CryptoBuffer instead of Uint8Array
-struct RsaHashedKeyAlgorithmStorage {
-  nsString mName;
-  KeyAlgorithm mHash;
-  uint16_t mModulusLength;
-  CryptoBuffer mPublicExponent;
-
-  void
-  ToKeyAlgorithm(JSContext* aCx, RsaHashedKeyAlgorithm& aRsa) const
-  {
-    aRsa.mName = mName;
-    aRsa.mModulusLength = mModulusLength;
-    aRsa.mHash.mName = mHash.mName;
-    aRsa.mPublicExponent.Init(mPublicExponent.ToUint8Array(aCx));
-    aRsa.mPublicExponent.ComputeLengthAndData();
-  }
-};
-
-// This class encapuslates a KeyAlgorithm object, and adds several
-// methods that make WebCrypto operations simpler.
-struct KeyAlgorithmProxy
-{
-  enum KeyAlgorithmType {
-    AES,
-    HMAC,
-    RSA,
-    EC
-  };
-  KeyAlgorithmType mType;
-
-  // Plain is always populated with the algorithm name
-  // Others are only populated for the corresponding key type
-  nsString mName;
-  AesKeyAlgorithm mAes;
-  HmacKeyAlgorithm mHmac;
-  RsaHashedKeyAlgorithmStorage mRsa;
-  EcKeyAlgorithm mEc;
-
-  // Structured clone
-  bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const;
-  bool ReadStructuredClone(JSStructuredCloneReader* aReader);
-
-  // Extract various forms of derived information
-  CK_MECHANISM_TYPE Mechanism() const;
-  nsString JwkAlg() const;
-
-  // And in static form for calling on raw KeyAlgorithm dictionaries
-  static CK_MECHANISM_TYPE GetMechanism(const KeyAlgorithm& aAlgorithm);
-  static CK_MECHANISM_TYPE GetMechanism(const HmacKeyAlgorithm& aAlgorithm);
-  static nsString GetJwkAlg(const KeyAlgorithm& aAlgorithm);
-
-  // Construction of the various algorithm types
-  void
-  MakeAes(const nsString& aName, uint32_t aLength)
-  {
-    mType = AES;
-    mName = aName;
-    mAes.mName = aName;
-    mAes.mLength = aLength;
-  }
-
-  void
-  MakeHmac(uint32_t aLength, const nsString& aHashName)
-  {
-    mType = HMAC;
-    mName = NS_LITERAL_STRING(WEBCRYPTO_ALG_HMAC);
-    mHmac.mName = NS_LITERAL_STRING(WEBCRYPTO_ALG_HMAC);
-    mHmac.mLength = aLength;
-    mHmac.mHash.mName = aHashName;
-  }
-
-  void
-  MakeRsa(const nsString& aName, uint32_t aModulusLength,
-         const CryptoBuffer& aPublicExponent, const nsString& aHashName)
-  {
-    mType = RSA;
-    mName = aName;
-    mRsa.mName = aName;
-    mRsa.mModulusLength = aModulusLength;
-    mRsa.mHash.mName = aHashName;
-    mRsa.mPublicExponent.Assign(aPublicExponent);
-  }
-
-  void
-  MakeEc(const nsString& aName, const nsString& aNamedCurve)
-  {
-    mType = EC;
-    mName = aName;
-    mEc.mName = aName;
-    mEc.mNamedCurve = aNamedCurve;
-  }
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_KeyAlgorithmProxy_h
new file mode 100644
--- /dev/null
+++ b/dom/crypto/RsaHashedKeyAlgorithm.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/RsaHashedKeyAlgorithm.h"
+#include "mozilla/dom/SubtleCryptoBinding.h"
+#include "mozilla/dom/WebCryptoCommon.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(RsaHashedKeyAlgorithm, RsaKeyAlgorithm, mHash)
+NS_IMPL_ADDREF_INHERITED(RsaHashedKeyAlgorithm, RsaKeyAlgorithm)
+NS_IMPL_RELEASE_INHERITED(RsaHashedKeyAlgorithm, RsaKeyAlgorithm)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RsaHashedKeyAlgorithm)
+NS_INTERFACE_MAP_END_INHERITING(RsaKeyAlgorithm)
+
+JSObject*
+RsaHashedKeyAlgorithm::WrapObject(JSContext* aCx)
+{
+  return RsaHashedKeyAlgorithmBinding::Wrap(aCx, this);
+}
+
+nsString
+RsaHashedKeyAlgorithm::ToJwkAlg() const
+{
+  if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
+    switch (mHash->Mechanism()) {
+      case CKM_SHA_1:  return NS_LITERAL_STRING(JWK_ALG_RS1);
+      case CKM_SHA256: return NS_LITERAL_STRING(JWK_ALG_RS256);
+      case CKM_SHA384: return NS_LITERAL_STRING(JWK_ALG_RS384);
+      case CKM_SHA512: return NS_LITERAL_STRING(JWK_ALG_RS512);
+    }
+  }
+
+  if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
+    switch(mHash->Mechanism()) {
+      case CKM_SHA_1:  return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP);
+      case CKM_SHA256: return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_256);
+      case CKM_SHA384: return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_256);
+      case CKM_SHA512: return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_512);
+    }
+  }
+
+  return nsString();
+}
+
+bool
+RsaHashedKeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
+{
+  nsString hashName;
+  mHash->GetName(hashName);
+  return JS_WriteUint32Pair(aWriter, SCTAG_RSAHASHEDKEYALG, 0) &&
+         JS_WriteUint32Pair(aWriter, mModulusLength, 0) &&
+         WriteBuffer(aWriter, mPublicExponent) &&
+         WriteString(aWriter, hashName) &&
+         WriteString(aWriter, mName);
+}
+
+KeyAlgorithm*
+RsaHashedKeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader) {
+  uint32_t modulusLength, zero;
+  CryptoBuffer publicExponent;
+  nsString name, hash;
+
+  bool read = JS_ReadUint32Pair(aReader, &modulusLength, &zero) &&
+              ReadBuffer(aReader, publicExponent) &&
+              ReadString(aReader, hash) &&
+              ReadString(aReader, name);
+  if (!read) {
+    return nullptr;
+  }
+
+  return new RsaHashedKeyAlgorithm(aGlobal, name, modulusLength, publicExponent, hash);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/crypto/RsaHashedKeyAlgorithm.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_RsaHashedKeyAlgorithm_h
+#define mozilla_dom_RsaHashedKeyAlgorithm_h
+
+#include "nsAutoPtr.h"
+#include "mozilla/dom/RsaKeyAlgorithm.h"
+
+namespace mozilla {
+namespace dom {
+
+class RsaHashedKeyAlgorithm MOZ_FINAL : public RsaKeyAlgorithm
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(RsaHashedKeyAlgorithm, RsaKeyAlgorithm)
+
+  RsaHashedKeyAlgorithm(nsIGlobalObject* aGlobal,
+                        const nsString& aName,
+                        uint32_t aModulusLength,
+                        const CryptoBuffer& aPublicExponent,
+                        const nsString& aHashName)
+    : RsaKeyAlgorithm(aGlobal, aName, aModulusLength, aPublicExponent)
+    , mHash(new KeyAlgorithm(aGlobal, aHashName))
+  {}
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  virtual nsString ToJwkAlg() const MOZ_OVERRIDE;
+
+  KeyAlgorithm* Hash() const
+  {
+    return mHash;
+  }
+
+  virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE;
+  static KeyAlgorithm* Create(nsIGlobalObject* aGlobal,
+                              JSStructuredCloneReader* aReader);
+
+private:
+  ~RsaHashedKeyAlgorithm() {}
+
+  nsRefPtr<KeyAlgorithm> mHash;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_RsaHashedKeyAlgorithm_h
new file mode 100644
--- /dev/null
+++ b/dom/crypto/RsaKeyAlgorithm.cpp
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/RsaKeyAlgorithm.h"
+#include "mozilla/dom/SubtleCryptoBinding.h"
+#include "mozilla/dom/WebCryptoCommon.h"
+
+namespace mozilla {
+namespace dom {
+
+JSObject*
+RsaKeyAlgorithm::WrapObject(JSContext* aCx)
+{
+  return RsaKeyAlgorithmBinding::Wrap(aCx, this);
+}
+
+bool
+RsaKeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
+{
+  return JS_WriteUint32Pair(aWriter, SCTAG_RSAKEYALG, 0) &&
+         JS_WriteUint32Pair(aWriter, mModulusLength, 0) &&
+         WriteBuffer(aWriter, mPublicExponent) &&
+         WriteString(aWriter, mName);
+}
+
+KeyAlgorithm*
+RsaKeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader)
+{
+  uint32_t modulusLength, zero;
+  CryptoBuffer publicExponent;
+  nsString name;
+  bool read = JS_ReadUint32Pair(aReader, &modulusLength, &zero) &&
+              ReadBuffer(aReader, publicExponent) &&
+              ReadString(aReader, name);
+  if (!read) {
+    return nullptr;
+  }
+
+  return new RsaKeyAlgorithm(aGlobal, name, modulusLength, publicExponent);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/crypto/RsaKeyAlgorithm.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_RsaKeyAlgorithm_h
+#define mozilla_dom_RsaKeyAlgorithm_h
+
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/KeyAlgorithm.h"
+#include "js/TypeDecls.h"
+
+namespace mozilla {
+namespace dom {
+
+class RsaKeyAlgorithm : public KeyAlgorithm
+{
+public:
+  RsaKeyAlgorithm(nsIGlobalObject* aGlobal,
+                  const nsString& aName,
+                  uint32_t aModulusLength,
+                  const CryptoBuffer& aPublicExponent)
+    : KeyAlgorithm(aGlobal, aName)
+    , mModulusLength(aModulusLength)
+    , mPublicExponent(aPublicExponent)
+  {}
+
+  ~RsaKeyAlgorithm()
+  {}
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  uint32_t ModulusLength() const
+  {
+    return mModulusLength;
+  }
+
+  void GetPublicExponent(JSContext* cx, JS::MutableHandle<JSObject*> aRetval,
+                         ErrorResult& aError) const
+  {
+    TypedArrayCreator<Uint8Array> creator(mPublicExponent);
+    JSObject* retval = creator.Create(cx);
+    if (!retval) {
+      aError.Throw(NS_ERROR_OUT_OF_MEMORY);
+    } else {
+      aRetval.set(retval);
+    }
+  }
+
+  virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE;
+  static KeyAlgorithm* Create(nsIGlobalObject* aGlobal,
+                              JSStructuredCloneReader* aReader);
+
+protected:
+  uint32_t mModulusLength;
+  CryptoBuffer mPublicExponent;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_RsaKeyAlgorithm_h
--- a/dom/crypto/WebCryptoCommon.h
+++ b/dom/crypto/WebCryptoCommon.h
@@ -18,16 +18,17 @@
 #define WEBCRYPTO_ALG_AES_GCM       "AES-GCM"
 #define WEBCRYPTO_ALG_AES_KW        "AES-KW"
 #define WEBCRYPTO_ALG_SHA1          "SHA-1"
 #define WEBCRYPTO_ALG_SHA256        "SHA-256"
 #define WEBCRYPTO_ALG_SHA384        "SHA-384"
 #define WEBCRYPTO_ALG_SHA512        "SHA-512"
 #define WEBCRYPTO_ALG_HMAC          "HMAC"
 #define WEBCRYPTO_ALG_PBKDF2        "PBKDF2"
+#define WEBCRYPTO_ALG_RSAES_PKCS1   "RSAES-PKCS1-v1_5"
 #define WEBCRYPTO_ALG_RSASSA_PKCS1  "RSASSA-PKCS1-v1_5"
 #define WEBCRYPTO_ALG_RSA_OAEP      "RSA-OAEP"
 #define WEBCRYPTO_ALG_ECDH          "ECDH"
 
 // WebCrypto key formats
 #define WEBCRYPTO_KEY_FORMAT_RAW    "raw"
 #define WEBCRYPTO_KEY_FORMAT_PKCS8  "pkcs8"
 #define WEBCRYPTO_KEY_FORMAT_SPKI   "spki"
@@ -175,66 +176,37 @@ MapAlgorithmNameToMechanism(const nsStri
   } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
     mechanism = CKM_SHA256;
   } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
     mechanism = CKM_SHA384;
   } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
     mechanism = CKM_SHA512;
   } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
     mechanism = CKM_PKCS5_PBKD2;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) {
+    mechanism = CKM_RSA_PKCS;
   } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
     mechanism = CKM_RSA_PKCS;
   } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
     mechanism = CKM_RSA_PKCS_OAEP;
   } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
     mechanism = CKM_ECDH1_DERIVE;
   }
 
   return mechanism;
 }
 
-#define NORMALIZED_EQUALS(aTest, aConst) \
-        nsContentUtils::EqualsIgnoreASCIICase(aTest, NS_LITERAL_STRING(aConst))
-
 inline bool
-NormalizeToken(const nsString& aName, nsString& aDest)
+NormalizeNamedCurveValue(const nsString& aNamedCurve, nsString& aDest)
 {
-  // Algorithm names
-  if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_AES_CBC)) {
-    aDest.AssignLiteral(WEBCRYPTO_ALG_AES_CBC);
-  } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_AES_CTR)) {
-    aDest.AssignLiteral(WEBCRYPTO_ALG_AES_CTR);
-  } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_AES_GCM)) {
-    aDest.AssignLiteral(WEBCRYPTO_ALG_AES_GCM);
-  } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_AES_KW)) {
-    aDest.AssignLiteral(WEBCRYPTO_ALG_AES_KW);
-  } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_SHA1)) {
-    aDest.AssignLiteral(WEBCRYPTO_ALG_SHA1);
-  } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_SHA256)) {
-    aDest.AssignLiteral(WEBCRYPTO_ALG_SHA256);
-  } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_SHA384)) {
-    aDest.AssignLiteral(WEBCRYPTO_ALG_SHA384);
-  } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_SHA512)) {
-    aDest.AssignLiteral(WEBCRYPTO_ALG_SHA512);
-  } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_HMAC)) {
-    aDest.AssignLiteral(WEBCRYPTO_ALG_HMAC);
-  } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_PBKDF2)) {
-    aDest.AssignLiteral(WEBCRYPTO_ALG_PBKDF2);
-  } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_RSASSA_PKCS1)) {
-    aDest.AssignLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1);
-  } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_RSA_OAEP)) {
-    aDest.AssignLiteral(WEBCRYPTO_ALG_RSA_OAEP);
-  } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_ECDH)) {
-    aDest.AssignLiteral(WEBCRYPTO_ALG_ECDH);
-  // Named curve values
-  } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P256)) {
+  if (aNamedCurve.EqualsIgnoreCase(WEBCRYPTO_NAMED_CURVE_P256)) {
     aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256);
-  } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P384)) {
+  } else if (aNamedCurve.EqualsIgnoreCase(WEBCRYPTO_NAMED_CURVE_P384)) {
     aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384);
-  } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P521)) {
+  } else if (aNamedCurve.EqualsIgnoreCase(WEBCRYPTO_NAMED_CURVE_P521)) {
     aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521);
   } else {
     return false;
   }
 
   return true;
 }
 
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -6,19 +6,26 @@
 
 #include "pk11pub.h"
 #include "cryptohi.h"
 #include "secerr.h"
 #include "ScopedNSSTypes.h"
 
 #include "jsapi.h"
 #include "mozilla/Telemetry.h"
+#include "mozilla/dom/AesKeyAlgorithm.h"
 #include "mozilla/dom/CryptoBuffer.h"
 #include "mozilla/dom/CryptoKey.h"
-#include "mozilla/dom/KeyAlgorithmProxy.h"
+#include "mozilla/dom/CryptoKeyPair.h"
+#include "mozilla/dom/EcKeyAlgorithm.h"
+#include "mozilla/dom/HmacKeyAlgorithm.h"
+#include "mozilla/dom/KeyAlgorithm.h"
+#include "mozilla/dom/RsaHashedKeyAlgorithm.h"
+#include "mozilla/dom/RsaKeyAlgorithm.h"
+#include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/WebCryptoCommon.h"
 #include "mozilla/dom/WebCryptoTask.h"
 
 namespace mozilla {
 namespace dom {
 
 // Pre-defined identifiers for telemetry histograms
@@ -42,17 +49,17 @@ enum TelemetryAlgorithm {
   // Please make additions at the end of the list,
   // to preserve comparability of histograms over time
   TA_UNKNOWN         = 0,
   // encrypt / decrypt
   TA_AES_CBC         = 1,
   TA_AES_CFB         = 2,
   TA_AES_CTR         = 3,
   TA_AES_GCM         = 4,
-  TA_RSAES_PKCS1     = 5, // NB: This algorithm has been removed
+  TA_RSAES_PKCS1     = 5,
   TA_RSA_OAEP        = 6,
   // sign/verify
   TA_RSASSA_PKCS1    = 7,
   TA_RSA_PSS         = 8,
   TA_HMAC_SHA_1      = 9,
   TA_HMAC_SHA_224    = 10,
   TA_HMAC_SHA_256    = 11,
   TA_HMAC_SHA_384    = 12,
@@ -87,17 +94,19 @@ enum TelemetryAlgorithm {
 #define ATTEMPT_BUFFER_ASSIGN(dst, src) \
   if (!dst.Assign(src)) { \
     return NS_ERROR_DOM_UNKNOWN_ERR; \
   }
 
 // Safety check for algorithms that use keys, suitable for constructors
 #define CHECK_KEY_ALGORITHM(keyAlg, algName) \
   { \
-    if (!NORMALIZED_EQUALS(keyAlg.mName, algName)) { \
+    nsString keyAlgName; \
+    keyAlg->GetName(keyAlgName); \
+    if (!keyAlgName.EqualsLiteral(algName)) { \
       mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; \
       return; \
     } \
   }
 
 class ClearException
 {
 public:
@@ -123,25 +132,52 @@ GetAlgorithmName(JSContext* aCx, const O
   if (aAlgorithm.IsString()) {
     // If string, then treat as algorithm name
     aName.Assign(aAlgorithm.GetAsString());
   } else {
     // Coerce to algorithm and extract name
     JS::RootedValue value(aCx, JS::ObjectValue(*aAlgorithm.GetAsObject()));
     Algorithm alg;
 
-    if (!alg.Init(aCx, value)) {
+    if (!alg.Init(aCx, value) || !alg.mName.WasPassed()) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
 
-    aName = alg.mName;
+    aName.Assign(alg.mName.Value());
   }
 
-  if (!NormalizeToken(aName, aName)) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
+  // Normalize algorithm names.
+  if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_AES_CBC)) {
+    aName.AssignLiteral(WEBCRYPTO_ALG_AES_CBC);
+  } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_AES_CTR)) {
+    aName.AssignLiteral(WEBCRYPTO_ALG_AES_CTR);
+  } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_AES_GCM)) {
+    aName.AssignLiteral(WEBCRYPTO_ALG_AES_GCM);
+  } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_AES_KW)) {
+    aName.AssignLiteral(WEBCRYPTO_ALG_AES_KW);
+  } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_SHA1)) {
+    aName.AssignLiteral(WEBCRYPTO_ALG_SHA1);
+  } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_SHA256)) {
+    aName.AssignLiteral(WEBCRYPTO_ALG_SHA256);
+  } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_SHA384)) {
+    aName.AssignLiteral(WEBCRYPTO_ALG_SHA384);
+  } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_SHA512)) {
+    aName.AssignLiteral(WEBCRYPTO_ALG_SHA512);
+  } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_HMAC)) {
+    aName.AssignLiteral(WEBCRYPTO_ALG_HMAC);
+  } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_PBKDF2)) {
+    aName.AssignLiteral(WEBCRYPTO_ALG_PBKDF2);
+  } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_RSAES_PKCS1)) {
+    aName.AssignLiteral(WEBCRYPTO_ALG_RSAES_PKCS1);
+  } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
+    aName.AssignLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1);
+  } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_RSA_OAEP)) {
+    aName.AssignLiteral(WEBCRYPTO_ALG_RSA_OAEP);
+  } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_ECDH)) {
+    aName.AssignLiteral(WEBCRYPTO_ALG_ECDH);
   }
 
   return NS_OK;
 }
 
 template<class T, class OOS>
 static nsresult
 Coerce(JSContext* aCx, T& aTarget, const OOS& aAlgorithm)
@@ -189,46 +225,47 @@ GetKeyLengthForAlgorithm(JSContext* aCx,
   }
 
   // Read AES key length from given algorithm object.
   if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
     RootedDictionary<AesDerivedKeyParams> params(aCx);
-    if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) {
+    if (NS_FAILED(Coerce(aCx, params, aAlgorithm)) ||
+        !params.mLength.WasPassed()) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
 
-    if (params.mLength != 128 &&
-        params.mLength != 192 &&
-        params.mLength != 256) {
+    size_t length = params.mLength.Value();
+    if (length != 128 && length != 192 && length != 256) {
       return NS_ERROR_DOM_DATA_ERR;
     }
 
-    aLength = params.mLength;
+    aLength = length;
     return NS_OK;
   }
 
   // Read HMAC key length from given algorithm object or
   // determine key length as the block size of the given hash.
   if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
     RootedDictionary<HmacDerivedKeyParams> params(aCx);
-    if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) {
+    if (NS_FAILED(Coerce(aCx, params, aAlgorithm)) ||
+        !params.mHash.WasPassed()) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
 
     // Return the passed length, if any.
     if (params.mLength.WasPassed()) {
       aLength = params.mLength.Value();
       return NS_OK;
     }
 
     nsString hashName;
-    if (NS_FAILED(GetAlgorithmName(aCx, params.mHash, hashName))) {
+    if (NS_FAILED(GetAlgorithmName(aCx, params.mHash.Value(), hashName))) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
 
     // Return the given hash algorithm's block size as the key length.
     size_t length = MapHashAlgorithmNameToBlockSize(hashName);
     if (length == 0) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
@@ -276,16 +313,17 @@ CloneData(JSContext* aCx, CryptoBuffer& 
   RootedTypedArray<ArrayBufferView> abv(aCx);
   if (abv.Init(aSrc)) {
     return !!aDst.Assign(abv);
   }
 
   return false;
 }
 
+
 // Implementation of WebCryptoTask methods
 
 void
 WebCryptoTask::FailWithError(nsresult aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, false);
 
@@ -421,58 +459,59 @@ public:
     TelemetryAlgorithm telemetryAlg;
     if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
       CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CBC);
 
       mMechanism = CKM_AES_CBC_PAD;
       telemetryAlg = TA_AES_CBC;
       AesCbcParams params;
       nsresult rv = Coerce(aCx, params, aAlgorithm);
-      if (NS_FAILED(rv)) {
+      if (NS_FAILED(rv) || !params.mIv.WasPassed()) {
         mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
         return;
       }
 
-      ATTEMPT_BUFFER_INIT(mIv, params.mIv)
+      ATTEMPT_BUFFER_INIT(mIv, params.mIv.Value())
       if (mIv.Length() != 16) {
         mEarlyRv = NS_ERROR_DOM_DATA_ERR;
         return;
       }
     } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
       CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CTR);
 
       mMechanism = CKM_AES_CTR;
       telemetryAlg = TA_AES_CTR;
       AesCtrParams params;
       nsresult rv = Coerce(aCx, params, aAlgorithm);
-      if (NS_FAILED(rv)) {
+      if (NS_FAILED(rv) || !params.mCounter.WasPassed() ||
+          !params.mLength.WasPassed()) {
         mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
         return;
       }
 
-      ATTEMPT_BUFFER_INIT(mIv, params.mCounter)
+      ATTEMPT_BUFFER_INIT(mIv, params.mCounter.Value())
       if (mIv.Length() != 16) {
         mEarlyRv = NS_ERROR_DOM_DATA_ERR;
         return;
       }
 
-      mCounterLength = params.mLength;
+      mCounterLength = params.mLength.Value();
     } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
       CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_GCM);
 
       mMechanism = CKM_AES_GCM;
       telemetryAlg = TA_AES_GCM;
       AesGcmParams params;
       nsresult rv = Coerce(aCx, params, aAlgorithm);
-      if (NS_FAILED(rv)) {
+      if (NS_FAILED(rv) || !params.mIv.WasPassed()) {
         mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
         return;
       }
 
-      ATTEMPT_BUFFER_INIT(mIv, params.mIv)
+      ATTEMPT_BUFFER_INIT(mIv, params.mIv.Value())
 
       if (params.mAdditionalData.WasPassed()) {
         ATTEMPT_BUFFER_INIT(mAad, params.mAdditionalData.Value())
       }
 
       // 32, 64, 96, 104, 112, 120 or 128
       mTagLength = 128;
       if (params.mTagLength.WasPassed()) {
@@ -699,16 +738,114 @@ private:
       }
       ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(unwrappedKey));
     }
 
     return rv;
   }
 };
 
+class RsaesPkcs1Task : public ReturnArrayBufferViewTask,
+                       public DeferredData
+{
+public:
+  RsaesPkcs1Task(JSContext* aCx, const ObjectOrString& aAlgorithm,
+                 CryptoKey& aKey, bool aEncrypt)
+    : mPrivKey(aKey.GetPrivateKey())
+    , mPubKey(aKey.GetPublicKey())
+    , mEncrypt(aEncrypt)
+  {
+    Init(aCx, aAlgorithm, aKey, aEncrypt);
+  }
+
+  RsaesPkcs1Task(JSContext* aCx, const ObjectOrString& aAlgorithm,
+                 CryptoKey& aKey, const CryptoOperationData& aData,
+                 bool aEncrypt)
+    : mPrivKey(aKey.GetPrivateKey())
+    , mPubKey(aKey.GetPublicKey())
+    , mEncrypt(aEncrypt)
+  {
+    Init(aCx, aAlgorithm, aKey, aEncrypt);
+    SetData(aData);
+  }
+
+  void Init(JSContext* aCx, const ObjectOrString& aAlgorithm,
+            CryptoKey& aKey, bool aEncrypt)
+  {
+
+    Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSAES_PKCS1);
+
+    CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSAES_PKCS1);
+
+    if (mEncrypt) {
+      if (!mPubKey) {
+        mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
+        return;
+      }
+      mStrength = SECKEY_PublicKeyStrength(mPubKey);
+    } else {
+      if (!mPrivKey) {
+        mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
+        return;
+      }
+      mStrength = PK11_GetPrivateModulusLen(mPrivKey);
+    }
+  }
+
+private:
+  ScopedSECKEYPrivateKey mPrivKey;
+  ScopedSECKEYPublicKey mPubKey;
+  uint32_t mStrength;
+  bool mEncrypt;
+
+  virtual nsresult BeforeCrypto() MOZ_OVERRIDE
+  {
+    if (!mDataIsSet) {
+      return NS_ERROR_DOM_OPERATION_ERR;
+    }
+
+    // Verify that the data input is not too big
+    // (as required by PKCS#1 / RFC 3447, Section 7.2)
+    // http://tools.ietf.org/html/rfc3447#section-7.2
+    if (mEncrypt && mData.Length() > mStrength - 11) {
+      return NS_ERROR_DOM_DATA_ERR;
+    }
+
+    return NS_OK;
+  }
+
+  virtual nsresult DoCrypto() MOZ_OVERRIDE
+  {
+    nsresult rv;
+
+    // Ciphertext is an integer mod the modulus, so it will be
+    // no longer than mStrength octets
+    if (!mResult.SetLength(mStrength)) {
+      return NS_ERROR_DOM_UNKNOWN_ERR;
+    }
+
+    if (mEncrypt) {
+      rv = MapSECStatus(PK11_PubEncryptPKCS1(
+              mPubKey.get(), mResult.Elements(),
+              mData.Elements(), mData.Length(),
+              nullptr));
+    } else {
+      uint32_t outLen;
+      rv = MapSECStatus(PK11_PrivDecryptPKCS1(
+              mPrivKey.get(), mResult.Elements(),
+              &outLen, mResult.Length(),
+              mData.Elements(), mData.Length()));
+      mResult.SetLength(outLen);
+    }
+
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
+    return NS_OK;
+  }
+};
+
 class RsaOaepTask : public ReturnArrayBufferViewTask,
                     public DeferredData
 {
 public:
   RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
               CryptoKey& aKey, bool aEncrypt)
     : mPrivKey(aKey.GetPrivateKey())
     , mPubKey(aKey.GetPublicKey())
@@ -754,27 +891,30 @@ public:
     if (!aAlgorithm.IsString()) {
       RootedDictionary<RsaOaepParams> params(aCx);
       mEarlyRv = Coerce(aCx, params, aAlgorithm);
       if (NS_FAILED(mEarlyRv)) {
         mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
         return;
       }
 
-      if (params.mLabel.WasPassed()) {
-        ATTEMPT_BUFFER_INIT(mLabel, params.mLabel.Value());
+      if (params.mLabel.WasPassed() && !params.mLabel.Value().IsNull()) {
+        ATTEMPT_BUFFER_INIT(mLabel, params.mLabel.Value().Value());
       }
     }
     // Otherwise mLabel remains the empty octet string, as intended
 
     // Look up the MGF based on the KeyAlgorithm.
     // static_cast is safe because we only get here if the algorithm name
     // is RSA-OAEP, and that only happens if we've constructed
     // an RsaHashedKeyAlgorithm.
-    mHashMechanism = KeyAlgorithmProxy::GetMechanism(aKey.Algorithm().mRsa.mHash);
+    // TODO: Add As* methods to KeyAlgorithm (Bug 1036734)
+    nsRefPtr<RsaHashedKeyAlgorithm> rsaAlg =
+      static_cast<RsaHashedKeyAlgorithm*>(aKey.Algorithm());
+    mHashMechanism = rsaAlg->Hash()->Mechanism();
 
     switch (mHashMechanism) {
       case CKM_SHA_1:
         mMgfMechanism = CKG_MGF1_SHA1; break;
       case CKM_SHA256:
         mMgfMechanism = CKG_MGF1_SHA256; break;
       case CKM_SHA384:
         mMgfMechanism = CKG_MGF1_SHA384; break;
@@ -851,17 +991,17 @@ private:
 class HmacTask : public WebCryptoTask
 {
 public:
   HmacTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
            CryptoKey& aKey,
            const CryptoOperationData& aSignature,
            const CryptoOperationData& aData,
            bool aSign)
-    : mMechanism(aKey.Algorithm().Mechanism())
+    : mMechanism(aKey.Algorithm()->Mechanism())
     , mSymKey(aKey.GetSymKey())
     , mSign(aSign)
   {
     CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_HMAC);
 
     ATTEMPT_BUFFER_INIT(mData, aData);
     if (!aSign) {
       ATTEMPT_BUFFER_INIT(mSignature, aSignature);
@@ -975,20 +1115,20 @@ public:
     if (!aSign) {
       ATTEMPT_BUFFER_INIT(mSignature, aSignature);
     }
 
     // Look up the SECOidTag based on the KeyAlgorithm
     // static_cast is safe because we only get here if the algorithm name
     // is RSASSA-PKCS1-v1_5, and that only happens if we've constructed
     // an RsaHashedKeyAlgorithm
-    CK_MECHANISM_TYPE mech;
-    mech = KeyAlgorithmProxy::GetMechanism(aKey.Algorithm().mRsa.mHash);
-
-    switch (mech) {
+    nsRefPtr<RsaHashedKeyAlgorithm> rsaAlg = static_cast<RsaHashedKeyAlgorithm*>(aKey.Algorithm());
+    nsRefPtr<KeyAlgorithm> hashAlg = rsaAlg->Hash();
+
+    switch (hashAlg->Mechanism()) {
       case CKM_SHA_1:
         mOidTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; break;
       case CKM_SHA256:
         mOidTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; break;
       case CKM_SHA384:
         mOidTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; break;
       case CKM_SHA512:
         mOidTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; break;
@@ -1175,17 +1315,17 @@ public:
     // Check 'ext'
     if (aKey->Extractable() &&
         aJwk.mExt.WasPassed() && !aJwk.mExt.Value()) {
       return false;
     }
 
     // Check 'alg'
     if (aJwk.mAlg.WasPassed() &&
-        aJwk.mAlg.Value() != aKey->Algorithm().JwkAlg()) {
+        aJwk.mAlg.Value() != aKey->Algorithm()->ToJwkAlg()) {
       return false;
     }
 
     // Check 'key_ops'
     if (aJwk.mKey_ops.WasPassed()) {
       nsTArray<nsString> usages;
       aKey->GetUsages(usages);
       for (size_t i = 0; i < usages.Length(); ++i) {
@@ -1284,41 +1424,37 @@ public:
       const Sequence<nsString>& aKeyUsages)
   {
     Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
     if (NS_FAILED(mEarlyRv)) {
       return;
     }
 
     SetKeyData(aCx, aKeyData);
-    if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
-      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
-      return;
-    }
   }
 
   void Init(JSContext* aCx,
       const nsAString& aFormat,
       const ObjectOrString& aAlgorithm, bool aExtractable,
       const Sequence<nsString>& aKeyUsages)
   {
     ImportKeyTask::Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
     if (NS_FAILED(mEarlyRv)) {
       return;
     }
 
     // If this is an HMAC key, import the hash name
     if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
       RootedDictionary<HmacImportParams> params(aCx);
       mEarlyRv = Coerce(aCx, params, aAlgorithm);
-      if (NS_FAILED(mEarlyRv)) {
+      if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed()) {
         mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
         return;
       }
-      mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName);
+      mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), mHashName);
       if (NS_FAILED(mEarlyRv)) {
         mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
         return;
       }
     }
   }
 
   virtual nsresult BeforeCrypto() MOZ_OVERRIDE
@@ -1340,16 +1476,18 @@ public:
 
     // Check that we have valid key data.
     if (mKeyData.Length() == 0) {
       return NS_ERROR_DOM_DATA_ERR;
     }
 
     // Construct an appropriate KeyAlorithm,
     // and verify that usages are appropriate
+    nsRefPtr<KeyAlgorithm> algorithm;
+    nsIGlobalObject* global = mKey->GetParentObject();
     uint32_t length = 8 * mKeyData.Length(); // bytes to bits
     if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
         mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
         mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
         mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
       if (mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::DECRYPT |
                                   CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
         return NS_ERROR_DOM_DATA_ERR;
@@ -1358,59 +1496,62 @@ public:
       if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) &&
           mKey->HasUsageOtherThan(CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
         return NS_ERROR_DOM_DATA_ERR;
       }
 
       if ( (length != 128) && (length != 192) && (length != 256) ) {
         return NS_ERROR_DOM_DATA_ERR;
       }
-      mKey->Algorithm().MakeAes(mAlgName, length);
+      algorithm = new AesKeyAlgorithm(global, mAlgName, length);
 
       if (mDataIsJwk && mJwk.mUse.WasPassed() &&
           !mJwk.mUse.Value().EqualsLiteral(JWK_USE_ENC)) {
         return NS_ERROR_DOM_DATA_ERR;
       }
     } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
-      if (mKey->HasUsageOtherThan(CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS)) {
+      if (mKey->HasUsageOtherThan(CryptoKey::DERIVEKEY)) {
         return NS_ERROR_DOM_DATA_ERR;
       }
-      mKey->Algorithm().MakeAes(mAlgName, length);
+      algorithm = new BasicSymmetricKeyAlgorithm(global, mAlgName, length);
 
       if (mDataIsJwk && mJwk.mUse.WasPassed()) {
         // There is not a 'use' value consistent with PBKDF
         return NS_ERROR_DOM_DATA_ERR;
       };
     } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
       if (mKey->HasUsageOtherThan(CryptoKey::SIGN | CryptoKey::VERIFY)) {
         return NS_ERROR_DOM_DATA_ERR;
       }
 
-      mKey->Algorithm().MakeHmac(length, mHashName);
-
-      if (mKey->Algorithm().Mechanism() == UNKNOWN_CK_MECHANISM) {
+      algorithm = new HmacKeyAlgorithm(global, mAlgName, length, mHashName);
+      if (algorithm->Mechanism() == UNKNOWN_CK_MECHANISM) {
         return NS_ERROR_DOM_SYNTAX_ERR;
       }
 
       if (mDataIsJwk && mJwk.mUse.WasPassed() &&
           !mJwk.mUse.Value().EqualsLiteral(JWK_USE_SIG)) {
         return NS_ERROR_DOM_DATA_ERR;
       }
     } else {
       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
     }
 
+    mKey->SetAlgorithm(algorithm);
     mKey->SetSymKey(mKeyData);
     mKey->SetType(CryptoKey::SECRET);
-
+    mEarlyComplete = true;
+    return NS_OK;
+  }
+
+  nsresult AfterCrypto() MOZ_OVERRIDE
+  {
     if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
       return NS_ERROR_DOM_DATA_ERR;
     }
-
-    mEarlyComplete = true;
     return NS_OK;
   }
 
 private:
   nsString mHashName;
 };
 
 class ImportRsaKeyTask : public ImportKeyTask
@@ -1430,20 +1571,16 @@ public:
       const Sequence<nsString>& aKeyUsages)
   {
     Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
     if (NS_FAILED(mEarlyRv)) {
       return;
     }
 
     SetKeyData(aCx, aKeyData);
-    if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
-      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
-      return;
-    }
   }
 
   void Init(JSContext* aCx,
       const nsAString& aFormat,
       const ObjectOrString& aAlgorithm, bool aExtractable,
       const Sequence<nsString>& aKeyUsages)
   {
     ImportKeyTask::Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
@@ -1451,34 +1588,26 @@ public:
       return;
     }
 
     // If this is RSA with a hash, cache the hash name
     if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
         mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
       RootedDictionary<RsaHashedImportParams> params(aCx);
       mEarlyRv = Coerce(aCx, params, aAlgorithm);
+      if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed()) {
+        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+        return;
+      }
+
+      mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), mHashName);
       if (NS_FAILED(mEarlyRv)) {
         mEarlyRv = NS_ERROR_DOM_DATA_ERR;
         return;
       }
-
-      mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName);
-      if (NS_FAILED(mEarlyRv)) {
-        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
-        return;
-      }
-    }
-
-    // Check support for the algorithm and hash names
-    CK_MECHANISM_TYPE mech1 = MapAlgorithmNameToMechanism(mAlgName);
-    CK_MECHANISM_TYPE mech2 = MapAlgorithmNameToMechanism(mHashName);
-    if ((mech1 == UNKNOWN_CK_MECHANISM) || (mech2 == UNKNOWN_CK_MECHANISM)) {
-      mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
-      return;
     }
   }
 
 private:
   nsString mHashName;
   uint32_t mModulusLength;
   CryptoBuffer mPublicExponent;
 
@@ -1535,35 +1664,51 @@ private:
     mPublicExponent.Assign(&pubKey->u.rsa.publicExponent);
 
     return NS_OK;
   }
 
   virtual nsresult AfterCrypto() MOZ_OVERRIDE
   {
     // Check permissions for the requested operation
-    if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
+    nsIGlobalObject* global = mKey->GetParentObject();
+    if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1) ||
+        mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
       if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
            mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::WRAPKEY)) ||
           (mKey->GetKeyType() == CryptoKey::PRIVATE &&
            mKey->HasUsageOtherThan(CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY))) {
         return NS_ERROR_DOM_DATA_ERR;
       }
     } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
       if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
            mKey->HasUsageOtherThan(CryptoKey::VERIFY)) ||
           (mKey->GetKeyType() == CryptoKey::PRIVATE &&
            mKey->HasUsageOtherThan(CryptoKey::SIGN))) {
         return NS_ERROR_DOM_DATA_ERR;
       }
     }
 
-    // Set an appropriate KeyAlgorithm
-    mKey->Algorithm().MakeRsa(mAlgName, mModulusLength,
-                              mPublicExponent, mHashName);
+    // Construct an appropriate KeyAlgorithm
+    if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) {
+      mKey->SetAlgorithm(new RsaKeyAlgorithm(global, mAlgName, mModulusLength, mPublicExponent));
+    } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
+               mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
+      nsRefPtr<RsaHashedKeyAlgorithm> algorithm =
+        new RsaHashedKeyAlgorithm(global, mAlgName,
+                                  mModulusLength, mPublicExponent, mHashName);
+      if (algorithm->Mechanism() == UNKNOWN_CK_MECHANISM) {
+        return NS_ERROR_DOM_SYNTAX_ERR;
+      }
+
+      if (algorithm->Hash()->Mechanism() == UNKNOWN_CK_MECHANISM) {
+        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+      }
+      mKey->SetAlgorithm(algorithm);
+    }
 
     if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
       return NS_ERROR_DOM_DATA_ERR;
     }
 
     return NS_OK;
   }
 };
@@ -1643,33 +1788,34 @@ private:
       mKey->SetPublicKey(pubKey.get());
       mKey->SetType(CryptoKey::PUBLIC);
     } else {
       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
     }
 
     // Extract 'crv' parameter from JWKs.
     if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
-      if (!NormalizeToken(mJwk.mCrv.Value(), mNamedCurve)) {
+      if (!NormalizeNamedCurveValue(mJwk.mCrv.Value(), mNamedCurve)) {
         return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
       }
     }
 
     return NS_OK;
   }
 
   virtual nsresult AfterCrypto() MOZ_OVERRIDE
   {
     // Check permissions for the requested operation
     if (mKey->GetKeyType() == CryptoKey::PRIVATE &&
         mKey->HasUsageOtherThan(CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY)) {
       return NS_ERROR_DOM_DATA_ERR;
     }
 
-    mKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
+    nsIGlobalObject* global = mKey->GetParentObject();
+    mKey->SetAlgorithm(new EcKeyAlgorithm(global, mAlgName, mNamedCurve));
 
     if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
       return NS_ERROR_DOM_DATA_ERR;
     }
 
     return NS_OK;
   }
 };
@@ -1679,18 +1825,23 @@ class ExportKeyTask : public WebCryptoTa
 public:
   ExportKeyTask(const nsAString& aFormat, CryptoKey& aKey)
     : mFormat(aFormat)
     , mSymKey(aKey.GetSymKey())
     , mPrivateKey(aKey.GetPrivateKey())
     , mPublicKey(aKey.GetPublicKey())
     , mKeyType(aKey.GetKeyType())
     , mExtractable(aKey.Extractable())
-    , mAlg(aKey.Algorithm().JwkAlg())
+    , mAlg(aKey.Algorithm()->ToJwkAlg())
   {
+    if (!aKey.Extractable()) {
+      mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
+      return;
+    }
+
     aKey.GetUsages(mKeyUsages);
   }
 
 
 protected:
   nsString mFormat;
   CryptoBuffer mSymKey;
   ScopedSECKEYPrivateKey mPrivateKey;
@@ -1741,17 +1892,17 @@ private:
     } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
       if (mKeyType == CryptoKey::SECRET) {
         nsString k;
         nsresult rv = mSymKey.ToJwkBase64(k);
         if (NS_FAILED(rv)) {
           return NS_ERROR_DOM_OPERATION_ERR;
         }
         mJwk.mK.Construct(k);
-        mJwk.mKty = NS_LITERAL_STRING(JWK_TYPE_SYMMETRIC);
+        mJwk.mKty.Construct(NS_LITERAL_STRING(JWK_TYPE_SYMMETRIC));
       } else if (mKeyType == CryptoKey::PUBLIC) {
         if (!mPublicKey) {
           return NS_ERROR_DOM_UNKNOWN_ERR;
         }
 
         nsresult rv = CryptoKey::PublicKeyToJwk(mPublicKey, mJwk, locker);
         if (NS_FAILED(rv)) {
           return NS_ERROR_DOM_OPERATION_ERR;
@@ -1819,73 +1970,74 @@ public:
     nsString algName;
     mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
     if (NS_FAILED(mEarlyRv)) {
       mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
       return;
     }
 
     // Construct an appropriate KeyAlorithm
+    nsRefPtr<KeyAlgorithm> algorithm;
     uint32_t allowedUsages = 0;
     if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
         algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
         algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
         algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
       mEarlyRv = GetKeyLengthForAlgorithm(aCx, aAlgorithm, mLength);
       if (NS_FAILED(mEarlyRv)) {
         return;
       }
-      mKey->Algorithm().MakeAes(algName, mLength);
-
+      algorithm = new AesKeyAlgorithm(global, algName, mLength);
       allowedUsages = CryptoKey::ENCRYPT | CryptoKey::DECRYPT |
                       CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY;
     } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
       RootedDictionary<HmacKeyGenParams> params(aCx);
       mEarlyRv = Coerce(aCx, params, aAlgorithm);
-      if (NS_FAILED(mEarlyRv)) {
+      if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed()) {
         mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
         return;
       }
 
       nsString hashName;
-      mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
+      mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), hashName);
       if (NS_FAILED(mEarlyRv)) {
         mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
         return;
       }
 
       if (params.mLength.WasPassed()) {
         mLength = params.mLength.Value();
       } else {
         mLength = MapHashAlgorithmNameToBlockSize(hashName);
       }
 
       if (mLength == 0) {
         mEarlyRv = NS_ERROR_DOM_DATA_ERR;
         return;
       }
 
-      mKey->Algorithm().MakeHmac(mLength, hashName);
+      algorithm = new HmacKeyAlgorithm(global, algName, mLength, hashName);
       allowedUsages = CryptoKey::SIGN | CryptoKey::VERIFY;
     } else {
       mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
       return;
     }
 
     // Add key usages
     mKey->ClearUsages();
     for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
       mEarlyRv = mKey->AddUsageIntersecting(aKeyUsages[i], allowedUsages);
       if (NS_FAILED(mEarlyRv)) {
         return;
       }
     }
 
     mLength = mLength >> 3; // bits to bytes
-    mMechanism = mKey->Algorithm().Mechanism();
+    mMechanism = algorithm->Mechanism();
+    mKey->SetAlgorithm(algorithm);
     // SetSymKey done in Resolve, after we've done the keygen
   }
 
 private:
   nsRefPtr<CryptoKey> mKey;
   size_t mLength;
   CK_MECHANISM_TYPE mMechanism;
   CryptoBuffer mKeyData;
@@ -1932,133 +2084,154 @@ public:
   {
     nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
     if (!global) {
       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);
+    mKeyPair = new CryptoKeyPair(global);
 
     // Extract algorithm name
     nsString algName;
     mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
     if (NS_FAILED(mEarlyRv)) {
       mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
       return;
     }
 
     // Construct an appropriate KeyAlorithm
+    KeyAlgorithm* algorithm;
     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)) {
+      if (NS_FAILED(mEarlyRv) || !params.mModulusLength.WasPassed() ||
+          !params.mPublicExponent.WasPassed() ||
+          !params.mHash.WasPassed()) {
         mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
         return;
       }
 
       // Pull relevant info
-      uint32_t modulusLength = params.mModulusLength;
+      uint32_t modulusLength = params.mModulusLength.Value();
       CryptoBuffer publicExponent;
-      ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent);
+      ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent.Value());
       nsString hashName;
-      mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
+      mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), hashName);
       if (NS_FAILED(mEarlyRv)) {
         mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
         return;
       }
 
       // Create algorithm
-      mKeyPair.mPublicKey.get()->Algorithm().MakeRsa(algName,
-                                                     modulusLength,
-                                                     publicExponent,
-                                                     hashName);
-      mKeyPair.mPrivateKey.get()->Algorithm().MakeRsa(algName,
-                                                      modulusLength,
-                                                      publicExponent,
-                                                      hashName);
+      algorithm = new RsaHashedKeyAlgorithm(global, algName, modulusLength,
+                                            publicExponent, hashName);
+      mKeyPair->PublicKey()->SetAlgorithm(algorithm);
+      mKeyPair->PrivateKey()->SetAlgorithm(algorithm);
+      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_RSAES_PKCS1)) {
+      RootedDictionary<RsaKeyGenParams> params(aCx);
+      mEarlyRv = Coerce(aCx, params, aAlgorithm);
+      if (NS_FAILED(mEarlyRv) || !params.mModulusLength.WasPassed() ||
+          !params.mPublicExponent.WasPassed()) {
+        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+        return;
+      }
+
+      // Pull relevant info
+      uint32_t modulusLength = params.mModulusLength.Value();
+      CryptoBuffer publicExponent;
+      ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent.Value());
+
+      // Create algorithm and note the mechanism
+      algorithm = new RsaKeyAlgorithm(global, algName, modulusLength,
+                                      publicExponent);
+      mKeyPair->PublicKey()->SetAlgorithm(algorithm);
+      mKeyPair->PrivateKey()->SetAlgorithm(algorithm);
       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)) {
       RootedDictionary<EcKeyGenParams> params(aCx);
       mEarlyRv = Coerce(aCx, params, aAlgorithm);
-      if (NS_FAILED(mEarlyRv)) {
+      if (NS_FAILED(mEarlyRv) || !params.mNamedCurve.WasPassed()) {
         mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
         return;
       }
 
-      if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) {
+      if (!NormalizeNamedCurveValue(params.mNamedCurve.Value(), 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);
+      algorithm = new EcKeyAlgorithm(global, algName, mNamedCurve);
+      mKeyPair->PublicKey()->SetAlgorithm(algorithm);
+      mKeyPair->PrivateKey()->SetAlgorithm(algorithm);
       mMechanism = CKM_EC_KEY_PAIR_GEN;
     } else {
       mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
       return;
     }
 
     // Set key usages.
     if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
       privateAllowedUsages = CryptoKey::SIGN;
       publicAllowedUsages = CryptoKey::VERIFY;
-    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
+    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1) ||
+               algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
       privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
       publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
     } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
       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();
+    mKeyPair->PrivateKey()->SetExtractable(aExtractable);
+    mKeyPair->PrivateKey()->SetType(CryptoKey::PRIVATE);
+
+    mKeyPair->PublicKey()->SetExtractable(true);
+    mKeyPair->PublicKey()->SetType(CryptoKey::PUBLIC);
+
+    mKeyPair->PrivateKey()->ClearUsages();
+    mKeyPair->PublicKey()->ClearUsages();
     for (uint32_t i=0; i < aKeyUsages.Length(); ++i) {
-      mEarlyRv = mKeyPair.mPrivateKey.get()->AddUsageIntersecting(aKeyUsages[i],
-                                                                  privateAllowedUsages);
+      mEarlyRv = mKeyPair->PrivateKey()->AddUsageIntersecting(aKeyUsages[i],
+                                                              privateAllowedUsages);
       if (NS_FAILED(mEarlyRv)) {
         return;
       }
 
-      mEarlyRv = mKeyPair.mPublicKey.get()->AddUsageIntersecting(aKeyUsages[i],
-                                                                 publicAllowedUsages);
+      mEarlyRv = mKeyPair->PublicKey()->AddUsageIntersecting(aKeyUsages[i],
+                                                             publicAllowedUsages);
       if (NS_FAILED(mEarlyRv)) {
         return;
       }
     }
-
-    // If no usages ended up being allowed, DataError
-    if (!mKeyPair.mPrivateKey.get()->HasAnyUsage() ||
-        !mKeyPair.mPrivateKey.get()->HasAnyUsage()) {
-      mEarlyRv = NS_ERROR_DOM_DATA_ERR;
-      return;
-    }
   }
 
 private:
-  CryptoKeyPair mKeyPair;
+  nsRefPtr<CryptoKeyPair> mKeyPair;
   CK_MECHANISM_TYPE mMechanism;
   PK11RSAGenParams mRsaParams;
   ScopedSECKEYPublicKey mPublicKey;
   ScopedSECKEYPrivateKey mPrivateKey;
   nsString mNamedCurve;
 
   virtual void ReleaseNSSResources() MOZ_OVERRIDE
   {
@@ -2097,25 +2270,30 @@ private:
     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);
+    mKeyPair->PrivateKey()->SetPrivateKey(mPrivateKey);
+    mKeyPair->PublicKey()->SetPublicKey(mPublicKey);
     return NS_OK;
   }
 
   virtual void Resolve() MOZ_OVERRIDE
   {
     mResultPromise->MaybeResolve(mKeyPair);
   }
+
+  virtual void Cleanup() MOZ_OVERRIDE
+  {
+    mKeyPair = nullptr;
+  }
 };
 
 class DerivePbkdfBitsTask : public ReturnArrayBufferViewTask
 {
 public:
   DerivePbkdfBitsTask(JSContext* aCx,
       const ObjectOrString& aAlgorithm, CryptoKey& aKey, uint32_t aLength)
     : mSymKey(aKey.GetSymKey())
@@ -2143,49 +2321,50 @@ public:
     // Check that we got a symmetric key
     if (mSymKey.Length() == 0) {
       mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
       return;
     }
 
     RootedDictionary<Pbkdf2Params> params(aCx);
     mEarlyRv = Coerce(aCx, params, aAlgorithm);
-    if (NS_FAILED(mEarlyRv)) {
+    if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed() ||
+        !params.mIterations.WasPassed() || !params.mSalt.WasPassed()) {
       mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
       return;
     }
 
     // length must be a multiple of 8 bigger than zero.
     if (aLength == 0 || aLength % 8) {
       mEarlyRv = NS_ERROR_DOM_DATA_ERR;
       return;
     }
 
     // Extract the hash algorithm.
     nsString hashName;
-    mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
+    mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), hashName);
     if (NS_FAILED(mEarlyRv)) {
       return;
     }
 
     // Check the given hash algorithm.
     switch (MapAlgorithmNameToMechanism(hashName)) {
       case CKM_SHA_1: mHashOidTag = SEC_OID_HMAC_SHA1; break;
       case CKM_SHA256: mHashOidTag = SEC_OID_HMAC_SHA256; break;
       case CKM_SHA384: mHashOidTag = SEC_OID_HMAC_SHA384; break;
       case CKM_SHA512: mHashOidTag = SEC_OID_HMAC_SHA512; break;
       default: {
         mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
         return;
       }
     }
 
-    ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
+    ATTEMPT_BUFFER_INIT(mSalt, params.mSalt.Value())
     mLength = aLength >> 3; // bits to bytes
-    mIterations = params.mIterations;
+    mIterations = params.mIterations.Value();
   }
 
 private:
   size_t mLength;
   size_t mIterations;
   CryptoBuffer mSalt;
   CryptoBuffer mSymKey;
   SECOidTag mHashOidTag;
@@ -2310,33 +2489,35 @@ public:
       return;
     }
 
     mLength = mLength >> 3; // bits to bytes
 
     // Retrieve the peer's public key.
     RootedDictionary<EcdhKeyDeriveParams> params(aCx);
     mEarlyRv = Coerce(aCx, params, aAlgorithm);
-    if (NS_FAILED(mEarlyRv)) {
+    if (NS_FAILED(mEarlyRv) || !params.mPublic.WasPassed()) {
       mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
       return;
     }
 
-    CryptoKey* publicKey = params.mPublic;
+    CryptoKey* publicKey = params.mPublic.Value();
     mPubKey = publicKey->GetPublicKey();
     if (!mPubKey) {
       mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
       return;
     }
 
-    CHECK_KEY_ALGORITHM(publicKey->Algorithm(), WEBCRYPTO_ALG_ECDH);
+    nsRefPtr<KeyAlgorithm> publicAlgorithm = publicKey->Algorithm();
+    CHECK_KEY_ALGORITHM(publicAlgorithm, WEBCRYPTO_ALG_ECDH);
 
     // Both keys must use the same named curve.
-    nsString curve1 = aKey.Algorithm().mEc.mNamedCurve;
-    nsString curve2 = publicKey->Algorithm().mEc.mNamedCurve;
+    nsString curve1, curve2;
+    static_cast<EcKeyAlgorithm*>(aKey.Algorithm())->GetNamedCurve(curve1);
+    static_cast<EcKeyAlgorithm*>(publicAlgorithm.get())->GetNamedCurve(curve2);
 
     if (!curve1.Equals(curve2)) {
       mEarlyRv = NS_ERROR_DOM_DATA_ERR;
       return;
     }
   }
 
 private:
@@ -2459,268 +2640,182 @@ private:
       mTask->Skip();
     }
     mTask = nullptr;
   }
 };
 
 // Task creation methods for WebCryptoTask
 
-// Note: We do not perform algorithm normalization as a monolithic process,
-// as described in the spec.  Instead:
-// * Each method handles its slice of the supportedAlgorithms structure
-// * Task constructors take care of:
-//    * Coercing the algorithm to the proper concrete type
-//    * Cloning subordinate data items
-//    * Cloning input data as needed
-//
-// Thus, support for different algorithms is determined by the if-statements
-// below, rather than a data structure.
-//
-// This results in algorithm normalization coming after some other checks,
-// and thus slightly more steps being done synchronously than the spec calls
-// for.  But none of these steps is especially time-consuming.
-
 WebCryptoTask*
 WebCryptoTask::CreateEncryptDecryptTask(JSContext* aCx,
-                                        const ObjectOrString& aAlgorithm,
-                                        CryptoKey& aKey,
-                                        const CryptoOperationData& aData,
-                                        bool aEncrypt)
+                                  const ObjectOrString& aAlgorithm,
+                                  CryptoKey& aKey,
+                                  const CryptoOperationData& aData,
+                                  bool aEncrypt)
 {
   TelemetryMethod method = (aEncrypt)? TM_ENCRYPT : TM_DECRYPT;
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method);
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_ENC, aKey.Extractable());
 
+  nsString algName;
+  nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
+  if (NS_FAILED(rv)) {
+    return new FailureTask(rv);
+  }
+
   // Ensure key is usable for this operation
   if ((aEncrypt  && !aKey.HasUsage(CryptoKey::ENCRYPT)) ||
       (!aEncrypt && !aKey.HasUsage(CryptoKey::DECRYPT))) {
     return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   }
 
-  nsString algName;
-  nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
-  if (NS_FAILED(rv)) {
-    return new FailureTask(rv);
-  }
-
   if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
     return new AesTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
+  } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) {
+    return new RsaesPkcs1Task(aCx, aAlgorithm, aKey, aData, aEncrypt);
   } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
     return new RsaOaepTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
   }
 
   return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
 
 WebCryptoTask*
 WebCryptoTask::CreateSignVerifyTask(JSContext* aCx,
-                                    const ObjectOrString& aAlgorithm,
-                                    CryptoKey& aKey,
-                                    const CryptoOperationData& aSignature,
-                                    const CryptoOperationData& aData,
-                                    bool aSign)
+                              const ObjectOrString& aAlgorithm,
+                              CryptoKey& aKey,
+                              const CryptoOperationData& aSignature,
+                              const CryptoOperationData& aData,
+                              bool aSign)
 {
   TelemetryMethod method = (aSign)? TM_SIGN : TM_VERIFY;
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method);
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_SIG, aKey.Extractable());
 
+  nsString algName;
+  nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
+  if (NS_FAILED(rv)) {
+    return new FailureTask(rv);
+  }
+
   // Ensure key is usable for this operation
   if ((aSign  && !aKey.HasUsage(CryptoKey::SIGN)) ||
       (!aSign && !aKey.HasUsage(CryptoKey::VERIFY))) {
     return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   }
 
-  nsString algName;
-  nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
-  if (NS_FAILED(rv)) {
-    return new FailureTask(rv);
-  }
-
   if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
     return new HmacTask(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
   } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
     return new RsassaPkcs1Task(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
   }
 
   return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
 
 WebCryptoTask*
 WebCryptoTask::CreateDigestTask(JSContext* aCx,
-                                const ObjectOrString& aAlgorithm,
-                                const CryptoOperationData& aData)
+                          const ObjectOrString& aAlgorithm,
+                          const CryptoOperationData& aData)
 {
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DIGEST);
+  return new DigestTask(aCx, aAlgorithm, aData);
+}
+
+WebCryptoTask*
+WebCryptoTask::CreateImportKeyTask(JSContext* aCx,
+                             const nsAString& aFormat,
+                             JS::Handle<JSObject*> aKeyData,
+                             const ObjectOrString& aAlgorithm,
+                             bool aExtractable,
+                             const Sequence<nsString>& aKeyUsages)
+{
+  Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_IMPORTKEY);
+  Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_IMPORT, aExtractable);
 
   nsString algName;
   nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
   if (NS_FAILED(rv)) {
     return new FailureTask(rv);
   }
 
-  if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) ||
-      algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256) ||
-      algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) ||
-      algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
-    return new DigestTask(aCx, aAlgorithm, aData);
-  }
-
-  return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-}
-
-WebCryptoTask*
-WebCryptoTask::CreateImportKeyTask(JSContext* aCx,
-                                   const nsAString& aFormat,
-                                   JS::Handle<JSObject*> aKeyData,
-                                   const ObjectOrString& aAlgorithm,
-                                   bool aExtractable,
-                                   const Sequence<nsString>& aKeyUsages)
-{
-  Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_IMPORTKEY);
-  Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_IMPORT, aExtractable);
-
-  // Verify that the format is recognized
-  if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
-      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
-      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
-      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
-    return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
-  }
-
-  // Verify that aKeyUsages does not contain an unrecognized value
-  if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
-    return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
-  }
-
-  nsString algName;
-  nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
-  if (NS_FAILED(rv)) {
-    return new FailureTask(rv);
-  }
-
-  // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
-  // However, the spec should be updated to allow it.
   if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
     return new ImportSymmetricKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
                                       aExtractable, aKeyUsages);
-  } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
+  } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1) ||
+             algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
              algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
     return new ImportRsaKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
                                 aExtractable, aKeyUsages);
   } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
     return new ImportEcKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
                                aExtractable, aKeyUsages);
   } else {
     return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   }
 }
 
 WebCryptoTask*
 WebCryptoTask::CreateExportKeyTask(const nsAString& aFormat,
-                                   CryptoKey& aKey)
+                             CryptoKey& aKey)
 {
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_EXPORTKEY);
 
-  // Verify that the format is recognized
-  if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
-      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
-      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
-      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
-    return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
-  }
-
-  // Verify that the key is extractable
-  if (!aKey.Extractable()) {
-    return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-  }
-
-  // Verify that the algorithm supports export
-  // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
-  // However, the spec should be updated to allow it.
-  nsString algName = aKey.Algorithm().mName;
-  if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
-      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
-      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
-      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) ||
-      algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
-      algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC) ||
-      algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
-      algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
-      algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
-    return new ExportKeyTask(aFormat, aKey);
-  }
-
-  return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+  return new ExportKeyTask(aFormat, aKey);
 }
 
 WebCryptoTask*
 WebCryptoTask::CreateGenerateKeyTask(JSContext* aCx,
-                                     const ObjectOrString& aAlgorithm,
-                                     bool aExtractable,
-                                     const Sequence<nsString>& aKeyUsages)
+                               const ObjectOrString& aAlgorithm,
+                               bool aExtractable,
+                               const Sequence<nsString>& aKeyUsages)
 {
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_GENERATEKEY);
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_GENERATE, aExtractable);
 
-  // Verify that aKeyUsages does not contain an unrecognized value
-  // SPEC-BUG: Spec says that this should be InvalidAccessError, but that
-  // is inconsistent with other analogous points in the spec
-  if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
-    return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
-  }
-
   nsString algName;
   nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
   if (NS_FAILED(rv)) {
     return new FailureTask(rv);
   }
 
   if (algName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
       algName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
       algName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
       algName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
       algName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
     return new GenerateSymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages);
-  } else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
+  } else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSAES_PKCS1) ||
+             algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
              algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
              algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
     return new GenerateAsymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages);
   } else {
     return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   }
 }
 
 WebCryptoTask*
 WebCryptoTask::CreateDeriveKeyTask(JSContext* aCx,
-                                   const ObjectOrString& aAlgorithm,
-                                   CryptoKey& aBaseKey,
-                                   const ObjectOrString& aDerivedKeyType,
-                                   bool aExtractable,
-                                   const Sequence<nsString>& aKeyUsages)
+                             const ObjectOrString& aAlgorithm,
+                             CryptoKey& aBaseKey,
+                             const ObjectOrString& aDerivedKeyType,
+                             bool aExtractable,
+                             const Sequence<nsString>& aKeyUsages)
 {
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEKEY);
 
-  // Ensure baseKey is usable for this operation
-  if (!aBaseKey.HasUsage(CryptoKey::DERIVEKEY)) {
-    return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-  }
-
-  // Verify that aKeyUsages does not contain an unrecognized value
-  if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
-    return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
-  }
-
   nsString algName;
   nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
   if (NS_FAILED(rv)) {
     return new FailureTask(rv);
   }
 
   if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
     return new DeriveKeyTask<DerivePbkdfBitsTask>(aCx, aAlgorithm, aBaseKey,
@@ -2734,27 +2829,22 @@ WebCryptoTask::CreateDeriveKeyTask(JSCon
                                                  aKeyUsages);
   }
 
   return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
 
 WebCryptoTask*
 WebCryptoTask::CreateDeriveBitsTask(JSContext* aCx,
-                                    const ObjectOrString& aAlgorithm,
-                                    CryptoKey& aKey,
-                                    uint32_t aLength)
+                              const ObjectOrString& aAlgorithm,
+                              CryptoKey& aKey,
+                              uint32_t aLength)
 {
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEBITS);
 
-  // Ensure baseKey is usable for this operation
-  if (!aKey.HasUsage(CryptoKey::DERIVEBITS)) {
-    return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-  }
-
   nsString algName;
   nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
   if (NS_FAILED(rv)) {
     return new FailureTask(rv);
   }
 
   if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
     return new DerivePbkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
@@ -2764,101 +2854,87 @@ WebCryptoTask::CreateDeriveBitsTask(JSCo
     return new DeriveEcdhBitsTask(aCx, aAlgorithm, aKey, aLength);
   }
 
   return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
 
 WebCryptoTask*
 WebCryptoTask::CreateWrapKeyTask(JSContext* aCx,
-                                 const nsAString& aFormat,
-                                 CryptoKey& aKey,
-                                 CryptoKey& aWrappingKey,
-                                 const ObjectOrString& aWrapAlgorithm)
+                             const nsAString& aFormat,
+                           CryptoKey& aKey,
+                           CryptoKey& aWrappingKey,
+                           const ObjectOrString& aWrapAlgorithm)
 {
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_WRAPKEY);
 
-  // Verify that the format is recognized
-  if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
-      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
-      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
-      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
-    return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
-  }
-
-  // Ensure wrappingKey is usable for this operation
+  // Ensure key is usable for this operation
   if (!aWrappingKey.HasUsage(CryptoKey::WRAPKEY)) {
     return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   }
 
-  // Ensure key is extractable
-  if (!aKey.Extractable()) {
-    return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-  }
-
   nsString wrapAlgName;
   nsresult rv = GetAlgorithmName(aCx, aWrapAlgorithm, wrapAlgName);
   if (NS_FAILED(rv)) {
     return new FailureTask(rv);
   }
 
   if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
       wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
       wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
     return new WrapKeyTask<AesTask>(aCx, aFormat, aKey,
                                     aWrappingKey, aWrapAlgorithm);
   } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
     return new WrapKeyTask<AesKwTask>(aCx, aFormat, aKey,
                                     aWrappingKey, aWrapAlgorithm);
+  } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) {
+    return new WrapKeyTask<RsaesPkcs1Task>(aCx, aFormat, aKey,
+                                           aWrappingKey, aWrapAlgorithm);
   } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
     return new WrapKeyTask<RsaOaepTask>(aCx, aFormat, aKey,
                                         aWrappingKey, aWrapAlgorithm);
   }
 
   return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
 
 WebCryptoTask*
 WebCryptoTask::CreateUnwrapKeyTask(JSContext* aCx,
-                                   const nsAString& aFormat,
-                                   const ArrayBufferViewOrArrayBuffer& aWrappedKey,
-                                   CryptoKey& aUnwrappingKey,
-                                   const ObjectOrString& aUnwrapAlgorithm,
-                                   const ObjectOrString& aUnwrappedKeyAlgorithm,
-                                   bool aExtractable,
-                                   const Sequence<nsString>& aKeyUsages)
+                             const nsAString& aFormat,
+                             const ArrayBufferViewOrArrayBuffer& aWrappedKey,
+                             CryptoKey& aUnwrappingKey,
+                             const ObjectOrString& aUnwrapAlgorithm,
+                             const ObjectOrString& aUnwrappedKeyAlgorithm,
+                             bool aExtractable,
+                             const Sequence<nsString>& aKeyUsages)
 {
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_UNWRAPKEY);
 
   // Ensure key is usable for this operation
   if (!aUnwrappingKey.HasUsage(CryptoKey::UNWRAPKEY)) {
     return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   }
 
-  // Verify that aKeyUsages does not contain an unrecognized value
-  if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
-    return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
-  }
-
   nsString keyAlgName;
   nsresult rv = GetAlgorithmName(aCx, aUnwrappedKeyAlgorithm, keyAlgName);
   if (NS_FAILED(rv)) {
     return new FailureTask(rv);
   }
 
   CryptoOperationData dummy;
   nsRefPtr<ImportKeyTask> importTask;
   if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
       keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
       keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
       keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
     importTask = new ImportSymmetricKeyTask(aCx, aFormat,
                                             aUnwrappedKeyAlgorithm,
                                             aExtractable, aKeyUsages);
-  } else if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
+  } else if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSAES_PKCS1) ||
+             keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
              keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP)) {
     importTask = new ImportRsaKeyTask(aCx, aFormat,
                                       aUnwrappedKeyAlgorithm,
                                       aExtractable, aKeyUsages);
   } else {
     return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   }
 
@@ -2872,16 +2948,20 @@ WebCryptoTask::CreateUnwrapKeyTask(JSCon
       unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
     return new UnwrapKeyTask<AesTask>(aCx, aWrappedKey,
                                       aUnwrappingKey, aUnwrapAlgorithm,
                                       importTask);
   } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
     return new UnwrapKeyTask<AesKwTask>(aCx, aWrappedKey,
                                       aUnwrappingKey, aUnwrapAlgorithm,
                                       importTask);
+  } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) {
+    return new UnwrapKeyTask<RsaesPkcs1Task>(aCx, aWrappedKey,
+                                      aUnwrappingKey, aUnwrapAlgorithm,
+                                      importTask);
   } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
     return new UnwrapKeyTask<RsaOaepTask>(aCx, aWrappedKey,
                                       aUnwrappingKey, aUnwrapAlgorithm,
                                       importTask);
   }
 
   return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
--- a/dom/crypto/moz.build
+++ b/dom/crypto/moz.build
@@ -1,26 +1,39 @@
 # -*- 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/.
 
 EXPORTS.mozilla.dom += [
+    'AesKeyAlgorithm.h',
+    'BasicSymmetricKeyAlgorithm.h',
     'CryptoBuffer.h',
     'CryptoKey.h',
-    'KeyAlgorithmProxy.h',
+    'CryptoKeyPair.h',
+    'EcKeyAlgorithm.h',
+    'HmacKeyAlgorithm.h',
+    'KeyAlgorithm.h',
+    'RsaHashedKeyAlgorithm.h',
+    'RsaKeyAlgorithm.h',
     'WebCryptoCommon.h',
     'WebCryptoTask.h',
 ]
 
 UNIFIED_SOURCES += [
+    'AesKeyAlgorithm.cpp',
     'CryptoBuffer.cpp',
     'CryptoKey.cpp',
-    'KeyAlgorithmProxy.cpp',
+    'CryptoKeyPair.cpp',
+    'EcKeyAlgorithm.cpp',
+    'HmacKeyAlgorithm.cpp',
+    'KeyAlgorithm.cpp',
+    'RsaHashedKeyAlgorithm.cpp',
+    'RsaKeyAlgorithm.cpp',
     'WebCryptoTask.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/dom/crypto/test/test_WebCrypto.html
+++ b/dom/crypto/test/test_WebCrypto.html
@@ -66,30 +66,31 @@ TestArray.addTest(
 TestArray.addTest(
   "Import / export round-trip with 'raw'",
   function() {
     var that = this;
     var alg = "AES-GCM";
 
     function doExport(x) {
       if (!hasKeyFields(x)) {
+        window.result = x;
         throw "Invalid key; missing field(s)";
       } else if ((x.algorithm.name != alg) ||
         (x.algorithm.length != 8 * tv.raw.length) ||
         (x.type != "secret") ||
         (!x.extractable) ||
         (x.usages.length != 1) ||
         (x.usages[0] != 'encrypt')){
         throw "Invalid key: incorrect key data";
       }
       return crypto.subtle.exportKey("raw", x);
     }
 
     crypto.subtle.importKey("raw", tv.raw, alg, true, ["encrypt"])
-      .then(doExport)
+      .then(doExport, error(that))
       .then(
         memcmp_complete(that, tv.raw),
         error(that)
       );
   }
 );
 
 // -----------------------------------------------------------------------------
@@ -147,17 +148,17 @@ TestArray.addTest(
         (x.usages.length != 1) ||
         (x.usages[0] != 'sign')){
         throw "Invalid key: incorrect key data";
       }
       return crypto.subtle.exportKey("pkcs8", x);
     }
 
     crypto.subtle.importKey("pkcs8", tv.pkcs8, alg, true, ["sign"])
-      .then(doExport)
+      .then(doExport, error(that))
       .then(
         memcmp_complete(that, tv.pkcs8),
         error(that)
       );
   }
 );
 
 // -----------------------------------------------------------------------------
@@ -172,54 +173,48 @@ TestArray.addTest(
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "Import / export round-trip with 'spki'",
   function() {
     var that = this;
-    var alg = {
-      name: "RSASSA-PKCS1-v1_5",
-      hash: "SHA-256"
-    };
+    var alg = "RSAES-PKCS1-v1_5";
 
     function doExport(x) {
       if (!hasKeyFields(x)) {
         throw "Invalid key; missing field(s)";
-      } else if ((x.algorithm.name != alg.name) ||
+      } else if ((x.algorithm.name != alg) ||
         (x.algorithm.modulusLength != 1024) ||
         (x.algorithm.publicExponent.byteLength != 3) ||
         (x.type != "public") ||
         (!x.extractable) ||
         (x.usages.length != 1) ||
-        (x.usages[0] != 'verify')){
+        (x.usages[0] != 'encrypt')){
         throw "Invalid key: incorrect key data";
       }
       return crypto.subtle.exportKey("spki", x);
     }
 
-    crypto.subtle.importKey("spki", tv.spki, alg, true, ["verify"])
+    crypto.subtle.importKey("spki", tv.spki, alg, true, ["encrypt"])
       .then(doExport, error(that))
       .then(
         memcmp_complete(that, tv.spki),
         error(that)
       );
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "Import failure with format 'spki'",
   function() {
     var that = this;
-    var alg = {
-      name: "RSASSA-PKCS1-v1_5",
-      hash: "SHA-256"
-    };
+    var alg = "RSAES-PKCS1-v1_5";
 
     crypto.subtle.importKey("spki", tv.negative_spki, alg, true, ["encrypt"])
       .then(error(that), complete(that));
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
@@ -382,56 +377,54 @@ TestArray.addTest(
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "Generate a 1024-bit RSA key",
   function() {
     var that = this;
     var alg = {
-      name: "RSASSA-PKCS1-v1_5",
-      hash: "SHA-256",
+      name: "RSAES-PKCS1-v1_5",
       modulusLength: 1024,
       publicExponent: new Uint8Array([0x01, 0x00, 0x01])
     };
-    crypto.subtle.generateKey(alg, false, ["sign", "verify"]).then(
+    crypto.subtle.generateKey(alg, false, ["encrypt", "decrypt"]).then(
       complete(that, function(x) {
         return exists(x.publicKey) &&
                (x.publicKey.algorithm.name == alg.name) &&
                (x.publicKey.algorithm.modulusLength == alg.modulusLength) &&
                (x.publicKey.type == "public") &&
                x.publicKey.extractable &&
                (x.publicKey.usages.length == 1) &&
-               (x.publicKey.usages[0] == "verify") &&
+               (x.publicKey.usages[0] == "encrypt") &&
                exists(x.privateKey) &&
                (x.privateKey.algorithm.name == alg.name) &&
                (x.privateKey.algorithm.modulusLength == alg.modulusLength) &&
                (x.privateKey.type == "private") &&
                !x.privateKey.extractable &&
                (x.privateKey.usages.length == 1) &&
-               (x.privateKey.usages[0] == "sign");
+               (x.privateKey.usages[0] == "decrypt");
       }),
       error(that)
     );
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "Fail cleanly when NSS refuses to generate a key pair",
   function() {
     var that = this;
     var alg = {
-      name: "RSASSA-PKCS1-v1_5",
-      hash: "SHA-256",
+      name: "RSAES-PKCS1-v1_5",
       modulusLength: 2299, // NSS does not like this key length
       publicExponent: new Uint8Array([0x01, 0x00, 0x01])
     };
 
-    crypto.subtle.generateKey(alg, false, ["sign"])
+    crypto.subtle.generateKey(alg, false, ["encrypt"])
       .then( error(that), complete(that) );
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "SHA-256 digest",
   function() {
@@ -457,16 +450,17 @@ TestArray.addTest(
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "AES-CBC encrypt",
   function () {
     var that = this;
 
     function doEncrypt(x) {
+      console.log(x);
       return crypto.subtle.encrypt(
         { name: "AES-CBC", iv: tv.aes_cbc_enc.iv },
         x, tv.aes_cbc_enc.data);
     }
 
     crypto.subtle.importKey("raw", tv.aes_cbc_enc.key, "AES-CBC", false, ['encrypt'])
       .then(doEncrypt)
       .then(
@@ -478,16 +472,17 @@ TestArray.addTest(
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "AES-CBC encrypt with wrong IV size",
   function () {
     var that = this;
 
     function encrypt(x, iv) {
+      console.log(x);
       return crypto.subtle.encrypt(
         { name: "AES-CBC", iv: iv },
         x, tv.aes_cbc_enc.data);
     }
 
     function doEncrypt(x) {
       return encrypt(x, new Uint8Array(15))
         .then(
@@ -824,125 +819,187 @@ TestArray.addTest(
         error(that),
         complete(that, function(x) { return true; })
       );
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
+  "RSAES-PKCS#1 encrypt/decrypt round-trip",
+  function () {
+    var that = this;
+    var privKey, pubKey;
+    var alg = {name:"RSAES-PKCS1-v1_5"};
+
+    var privKey, pubKey, data, ct, pt;
+    function setPriv(x) { privKey = x; }
+    function setPub(x) { pubKey = x; }
+    function doEncrypt() {
+      return crypto.subtle.encrypt(alg.name, pubKey, tv.rsaes.data);
+    }
+    function doDecrypt(x) {
+      return crypto.subtle.decrypt(alg.name, privKey, x);
+    }
+
+    function fail() { error(that); }
+
+    Promise.all([
+      crypto.subtle.importKey("pkcs8", tv.rsaes.pkcs8, alg, false, ['decrypt'])
+          .then(setPriv, error(that)),
+      crypto.subtle.importKey("spki", tv.rsaes.spki, alg, false, ['encrypt'])
+          .then(setPub, error(that))
+    ]).then(doEncrypt, error(that))
+      .then(doDecrypt, error(that))
+      .then(
+        memcmp_complete(that, tv.rsaes.data),
+        error(that)
+      );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "RSAES-PKCS#1 decryption known answer",
+  function () {
+    var that = this;
+    var alg = {name:"RSAES-PKCS1-v1_5"};
+
+    function doDecrypt(x) {
+      return crypto.subtle.decrypt(alg.name, x, tv.rsaes.result);
+    }
+    function fail() { error(that); }
+
+    crypto.subtle.importKey("pkcs8", tv.rsaes.pkcs8, alg, false, ['decrypt'])
+      .then( doDecrypt, fail )
+      .then( memcmp_complete(that, tv.rsaes.data), fail );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
   "RSASSA/SHA-1 signature",
   function () {
     var that = this;
     var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-1" };
 
     function doSign(x) {
+      console.log("sign");
+      console.log(x);
       return crypto.subtle.sign(alg.name, x, tv.rsassa.data);
     }
+    function fail() { console.log("fail"); error(that); }
 
     crypto.subtle.importKey("pkcs8", tv.rsassa.pkcs8, alg, false, ['sign'])
-      .then( doSign )
-      .then( memcmp_complete(that, tv.rsassa.sig1), error(that) );
+      .then( doSign, fail )
+      .then( memcmp_complete(that, tv.rsassa.sig1), fail );
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "RSASSA verification (SHA-1)",
   function () {
     var that = this;
     var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-1" };
 
     function doVerify(x) {
       return crypto.subtle.verify(alg.name, x, tv.rsassa.sig1, tv.rsassa.data);
     }
+    function fail(x) { error(that); }
 
     crypto.subtle.importKey("spki", tv.rsassa.spki, alg, false, ['verify'])
-      .then( doVerify )
+      .then( doVerify, fail )
       .then(
         complete(that, function(x) { return x; }),
-        error(that)
+        fail
       );
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "RSASSA verification (SHA-1), failing verification",
   function () {
     var that = this;
     var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-1" };
 
     function doVerify(x) {
       return crypto.subtle.verify(alg.name, x, tv.rsassa.sig_fail, tv.rsassa.data);
     }
+    function fail(x) { error(that); }
 
     crypto.subtle.importKey("spki", tv.rsassa.spki, alg, false, ['verify'])
-      .then( doVerify )
+      .then( doVerify, fail )
       .then(
         complete(that, function(x) { return !x; }),
-        error(that)
+        fail
       );
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "RSASSA/SHA-256 signature",
   function () {
     var that = this;
     var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" };
 
     function doSign(x) {
       return crypto.subtle.sign(alg.name, x, tv.rsassa.data);
     }
+    function fail(x) { console.log(x); error(that); }
 
     crypto.subtle.importKey("pkcs8", tv.rsassa.pkcs8, alg, false, ['sign'])
-      .then( doSign )
-      .then( memcmp_complete(that, tv.rsassa.sig256), error(that) );
+      .then( doSign, fail )
+      .then( memcmp_complete(that, tv.rsassa.sig256), fail );
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "RSASSA verification (SHA-256)",
   function () {
     var that = this;
     var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" };
 
     function doVerify(x) {
       return crypto.subtle.verify(alg.name, x, tv.rsassa.sig256, tv.rsassa.data);
     }
+    function fail(x) { error(that); }
 
     crypto.subtle.importKey("spki", tv.rsassa.spki, alg, false, ['verify'])
-      .then( doVerify )
+      .then( doVerify, fail )
       .then(
         complete(that, function(x) { return x; }),
-        error(that)
+        fail
       );
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "RSASSA verification (SHA-256), failing verification",
   function () {
     var that = this;
     var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" };
     var use = ['sign', 'verify'];
 
     function doVerify(x) {
+      console.log("verifying")
       return crypto.subtle.verify(alg.name, x, tv.rsassa.sig_fail, tv.rsassa.data);
     }
+    function fail(x) { console.log("failing"); error(that)(x); }
 
+    console.log("running")
     crypto.subtle.importKey("spki", tv.rsassa.spki, alg, false, ['verify'])
-      .then( doVerify )
+      .then( doVerify, fail )
       .then(
         complete(that, function(x) { return !x; }),
-        error(that)
+        fail
       );
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "Key wrap known answer, using AES-GCM",
   function () {
@@ -1113,28 +1170,30 @@ TestArray.addTest(
       return crypto.subtle.wrapKey("jwk", originalKey, wrapKey, wrapAlg);
     }
     function doUnwrap(wrappedKey) {
       return crypto.subtle.unwrapKey("jwk", wrappedKey, wrapKey, wrapAlg,
                                      { name: "HMAC", hash: "SHA-384"},
                                      true, ['sign', 'verify']);
     }
 
+    function temperr(x) { return function(y) { console.log("error in "+x); console.log(y); } }
+
     Promise.all([
       crypto.subtle.importKey("jwk", tv.aes_gcm_enc.key_jwk,
                               "AES-GCM", false, ['wrapKey','unwrapKey'])
-        .then(function(x) { wrapKey = x; }),
+        .then(function(x) { console.log("wrapKey"); wrapKey = x; }),
       crypto.subtle.generateKey(genAlg, true, ['sign', 'verify'])
-        .then(function(x) { originalKey = x; return x; })
+        .then(function(x) { console.log("originalKey"); originalKey = x; return x; })
         .then(doExport)
         .then(function(x) { originalKeyJwk = x; })
     ])
-      .then(doWrap)
-      .then(doUnwrap)
-      .then(doExport)
+      .then(doWrap, temperr("initial phase"))
+      .then(doUnwrap, temperr("wrap"))
+      .then(doExport, temperr("unwrap"))
       .then(
         complete(that, function(x) {
           return exists(x.k) && x.k == originalKeyJwk.k;
         }),
         error(that)
       );
   }
 );
@@ -1205,19 +1264,19 @@ TestArray.addTest(
       return crypto.subtle.unwrapKey("raw", wrappedKey, wrapKey,
                                      "AES-KW", { name: "HMAC", hash: "SHA-384"},
                                      true, ['sign', 'verify']);
     }
 
     Promise.all([
       crypto.subtle.importKey("jwk", tv.aes_kw.wrapping_key,
                               "AES-KW", false, ['wrapKey','unwrapKey'])
-        .then(function(x) { wrapKey = x; }),
+        .then(function(x) { console.log("wrapKey"); wrapKey = x; }),
       crypto.subtle.generateKey(genAlg, true, ['sign'])
-        .then(function(x) { originalKey = x; return x; })
+        .then(function(x) { console.log("originalKey"); originalKey = x; return x; })
         .then(doExport)
         .then(function(x) { originalKeyJwk = x; })
     ])
       .then(doWrap)
       .then(doUnwrap)
       .then(doExport)
       .then(
         complete(that, function(x) {
@@ -1268,17 +1327,17 @@ TestArray.addTest(
 
     function doGenerateRsaOaepKey() {
       var alg = {
         name: "rsa-OAEP",
         hash: "sha-1",
         modulusLength: 2048,
         publicExponent: new Uint8Array([0x01, 0x00, 0x01])
       };
-      return crypto.subtle.generateKey(alg, false, ["encrypt", "decrypt"]);
+      return crypto.subtle.generateKey(alg, false, ["encrypt"]);
     }
 
     function doGenerateRsaSsaPkcs1Key() {
       var alg = { name: "RSASSA-pkcs1-V1_5", hash: "SHA-1" };
       return crypto.subtle.importKey("pkcs8", tv.pkcs8, alg, true, ["sign"]);
     }
 
     crypto.subtle.generateKey(alg, false, ["sign"])
@@ -1300,17 +1359,34 @@ TestArray.addTest(
 
       function doSign(x) {
         return crypto.subtle.sign("RSASSA-PKCS1-v1_5", x, new Uint8Array());
       }
 
       return crypto.subtle.generateKey(alg, false, ["sign"]).then(doSign);
     }
 
-    doCheckRSASSA().then(error(that), complete(that));
+    function doCheckRSAES() {
+      var alg = {
+        name: "RSAES-PKCS1-v1_5",
+        modulusLength: 1024,
+        publicExponent: new Uint8Array([0x01, 0x00, 0x01])
+      };
+
+      function doEncrypt(x) {
+        var alg = {name: "RSA-OAEP", hash: "SHA-1"};
+        return crypto.subtle.encrypt(alg, x.publicKey, new Uint8Array());
+      }
+
+      return crypto.subtle.generateKey(alg, false, ["encrypt"]).then(doEncrypt);
+    }
+
+    doCheckRSASSA().then(error(that), function () {
+      doCheckRSAES().then(error(that), complete(that));
+    });
   }
 );
 /*]]>*/</script>
 </head>
 
 <body>
 
 <div id="content">
--- a/dom/crypto/test/test_WebCrypto_ECDH.html
+++ b/dom/crypto/test/test_WebCrypto_ECDH.html
@@ -98,17 +98,17 @@ TestArray.addTest(
 
     function doGenerateRSA() {
       var alg = {
         name: "RSA-OAEP",
         hash: "SHA-256",
         modulusLength: 2048,
         publicExponent: new Uint8Array([0x01, 0x00, 0x01])
       };
-      return crypto.subtle.generateKey(alg, false, ["encrypt", "decrypt"])
+      return crypto.subtle.generateKey(alg, false, ["encrypt"])
     }
 
     function doDerive() {
       var alg = { name: "ECDH", public: pubKey };
       return crypto.subtle.deriveBits(alg, privKey, 16);
     }
 
     doGenerateP256()
@@ -331,22 +331,22 @@ TestArray.addTest(
       var data = crypto.getRandomValues(new Uint8Array(1024));
       return crypto.subtle.sign("HMAC", x, data)
         .then(function (sig) {
           return crypto.subtle.verify("HMAC", x, sig, data);
         });
     }
 
     Promise.all([
-      crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_priv, alg, false, ["deriveKey"])
-        .then(setPriv),
-      crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_pub, alg, false, ["deriveKey"])
-        .then(setPub)
-    ]).then(doDerive)
-      .then(doSignAndVerify)
+      crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_priv, alg, false, ["deriveBits"])
+        .then(setPriv, error(that)),
+      crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_pub, alg, false, ["deriveBits"])
+        .then(setPub, error(that))
+    ]).then(doDerive, error(that))
+      .then(doSignAndVerify, error(that))
       .then(complete(that), error(that));
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "SPKI import/export of public ECDH keys (P-256)",
   function () {
--- a/dom/crypto/test/test_WebCrypto_JWK.html
+++ b/dom/crypto/test/test_WebCrypto_JWK.html
@@ -187,16 +187,18 @@ TestArray.addTest(
     function doExport(k) {
       return crypto.subtle.exportKey("jwk", k);
     }
 
     crypto.subtle.importKey("jwk", jwk, alg, true, ['sign'])
       .then(doExport)
       .then(
         complete(that, function(x) {
+          window.jwk_priv = x;
+          console.log(JSON.stringify(x));
           return hasBaseJwkFields(x) &&
                  hasFields(x, ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi']) &&
                  x.kty == 'RSA' &&
                  x.alg == 'RS256' &&
                  x.ext &&
                  shallowArrayEquals(x.key_ops, ['sign']) &&
                  x.n  == jwk.n  &&
                  x.e  == jwk.e  &&
--- a/dom/crypto/test/test_WebCrypto_PBKDF2.html
+++ b/dom/crypto/test/test_WebCrypto_PBKDF2.html
@@ -41,45 +41,47 @@ TestArray.addTest(
 TestArray.addTest(
   "Import raw PBKDF2 key and derive bits using HMAC-SHA-1",
   function() {
     var that = this;
     var alg = "PBKDF2";
     var key = tv.pbkdf2_sha1.password;
 
     function doDerive(x) {
+      console.log("deriving");
       if (!hasKeyFields(x)) {
         throw "Invalid key; missing field(s)";
       }
 
       var alg = {
         name: "PBKDF2",
         hash: "SHA-1",
         salt: tv.pbkdf2_sha1.salt,
         iterations: tv.pbkdf2_sha1.iterations
       };
       return crypto.subtle.deriveBits(alg, x, tv.pbkdf2_sha1.length);
     }
     function fail(x) { console.log("failing"); error(that)(x); }
 
-    crypto.subtle.importKey("raw", key, alg, false, ["deriveBits"])
+    crypto.subtle.importKey("raw", key, alg, false, ["deriveKey"])
       .then( doDerive, fail )
       .then( memcmp_complete(that, tv.pbkdf2_sha1.derived), fail );
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "Import raw PBKDF2 key and derive a new key using HMAC-SHA-1",
   function() {
     var that = this;
     var alg = "PBKDF2";
     var key = tv.pbkdf2_sha1.password;
 
     function doDerive(x) {
+      console.log("deriving");
       if (!hasKeyFields(x)) {
         throw "Invalid key; missing field(s)";
       }
 
       var alg = {
         name: "PBKDF2",
         hash: "SHA-1",
         salt: tv.pbkdf2_sha1.salt,
@@ -154,16 +156,17 @@ TestArray.addTest(
 /*TestArray.addTest(
   "Import raw PBKDF2 key and derive bits using HMAC-SHA-256",
   function() {
     var that = this;
     var alg = "PBKDF2";
     var key = tv.pbkdf2_sha256.password;
 
     function doDerive(x) {
+      console.log("deriving");
       if (!hasKeyFields(x)) {
         throw "Invalid key; missing field(s)";
       }
 
       var alg = {
         name: "PBKDF2",
         hash: "SHA-256",
         salt: tv.pbkdf2_sha256.salt,
--- a/dom/crypto/test/test_WebCrypto_RSA_OAEP.html
+++ b/dom/crypto/test/test_WebCrypto_RSA_OAEP.html
@@ -115,23 +115,22 @@ TestArray.addTest(
       hash: "SHA-1",
       modulusLength: 2048,
       publicExponent: new Uint8Array([0x01, 0x00, 0x01])
     };
 
     var privKey, pubKey;
     function setKey(x) { pubKey = x.publicKey; privKey = x.privateKey; }
     function doEncrypt(n) {
-      console.log("entered encrypt("+ n +")");
       return function () {
         return crypto.subtle.encrypt(alg, pubKey, new Uint8Array(n));
       }
     }
 
-    crypto.subtle.generateKey(alg, false, ['encrypt', 'decrypt'])
+    crypto.subtle.generateKey(alg, false, ['encrypt'])
       .then(setKey, error(that))
       .then(doEncrypt(214), error(that))
       .then(doEncrypt(215), error(that))
       .then(error(that), complete(that));
   }
 );
 
 // -----------------------------------------------------------------------------
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -240,16 +240,18 @@ var interfaceNamesInGlobalScope =
     "Controllers",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ConvolverNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Crypto",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CryptoKey", pref: "dom.webcrypto.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "CryptoKeyPair", pref: "dom.webcrypto.enabled"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "CSS",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSS2Properties",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSCharsetRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSConditionRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
deleted file mode 100644
--- a/dom/webidl/KeyAlgorithm.webidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*- 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/.
- *
- * The origin of this IDL file is
- * http://www.w3.org/TR/WebCryptoAPI/
- */
-
-dictionary KeyAlgorithm {
-  required DOMString name;
-};
-
-dictionary AesKeyAlgorithm : KeyAlgorithm {
-  required unsigned short length;
-};
-
-dictionary EcKeyAlgorithm : KeyAlgorithm {
-  required DOMString namedCurve;
-};
-
-dictionary HmacKeyAlgorithm : KeyAlgorithm {
-  required KeyAlgorithm hash;
-  required unsigned long length;
-};
-
-dictionary RsaHashedKeyAlgorithm : KeyAlgorithm {
-  required unsigned short modulusLength;
-  required Uint8Array publicExponent;
-  required KeyAlgorithm hash;
-};
-
--- a/dom/webidl/SubtleCrypto.webidl
+++ b/dom/webidl/SubtleCrypto.webidl
@@ -4,107 +4,146 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is
  * http://www.w3.org/TR/WebCryptoAPI/
  */
 
 typedef DOMString KeyType;
 typedef DOMString KeyUsage;
-typedef DOMString NamedCurve;
 typedef Uint8Array BigInteger;
 
+/***** KeyAlgorithm interfaces *****/
+
+[NoInterfaceObject]
+interface KeyAlgorithm {
+  readonly attribute DOMString name;
+};
+
+[NoInterfaceObject]
+interface AesKeyAlgorithm : KeyAlgorithm {
+  readonly attribute unsigned short length;
+};
+
+[NoInterfaceObject]
+interface HmacKeyAlgorithm : KeyAlgorithm {
+  readonly attribute KeyAlgorithm hash;
+  readonly attribute unsigned long length;
+};
+
+[NoInterfaceObject]
+interface RsaKeyAlgorithm : KeyAlgorithm {
+  readonly attribute unsigned long modulusLength;
+  [Throws]
+  readonly attribute BigInteger publicExponent;
+};
+
+[NoInterfaceObject]
+interface RsaHashedKeyAlgorithm : RsaKeyAlgorithm {
+  readonly attribute KeyAlgorithm hash;
+};
+
+[NoInterfaceObject]
+interface EcKeyAlgorithm : KeyAlgorithm {
+  readonly attribute NamedCurve namedCurve;
+};
+
+
 /***** Algorithm dictionaries *****/
 
 dictionary Algorithm {
-  required DOMString name;
+  DOMString name;
 };
 
 dictionary AesCbcParams : Algorithm {
-  required CryptoOperationData iv;
+  CryptoOperationData iv;
 };
 
 dictionary AesCtrParams : Algorithm {
-  required CryptoOperationData counter;
-  [EnforceRange] required octet length;
+  CryptoOperationData counter;
+  [EnforceRange] octet length;
 };
 
 dictionary AesGcmParams : Algorithm {
-  required CryptoOperationData iv;
+  CryptoOperationData iv;
   CryptoOperationData additionalData;
   [EnforceRange] octet tagLength;
 };
 
 dictionary HmacImportParams : Algorithm {
-  required AlgorithmIdentifier hash;
+  AlgorithmIdentifier hash;
 };
 
 dictionary Pbkdf2Params : Algorithm {
-  required CryptoOperationData salt;
-  [EnforceRange] required unsigned long iterations;
-  required AlgorithmIdentifier hash;
+  CryptoOperationData salt;
+  [EnforceRange] unsigned long iterations;
+  AlgorithmIdentifier hash;
 };
 
 dictionary RsaHashedImportParams {
-  required AlgorithmIdentifier hash;
+  AlgorithmIdentifier hash;
 };
 
 dictionary AesKeyGenParams : Algorithm {
-  [EnforceRange] required unsigned short length;
+  [EnforceRange] unsigned short length;
 };
 
 dictionary HmacKeyGenParams : Algorithm {
-  required AlgorithmIdentifier hash;
+  AlgorithmIdentifier hash;
   [EnforceRange] unsigned long length;
 };
 
-dictionary RsaHashedKeyGenParams : Algorithm {
-  [EnforceRange] required unsigned long modulusLength;
-  required BigInteger publicExponent;
-  required AlgorithmIdentifier hash;
+dictionary RsaKeyGenParams : Algorithm {
+  [EnforceRange] unsigned long modulusLength;
+  BigInteger publicExponent;
+};
+
+dictionary RsaHashedKeyGenParams : RsaKeyGenParams {
+  AlgorithmIdentifier hash;
 };
 
 dictionary RsaOaepParams : Algorithm {
-  CryptoOperationData label;
+  CryptoOperationData? label;
 };
 
 dictionary DhKeyGenParams : Algorithm {
-  required BigInteger prime;
-  required BigInteger generator;
+  BigInteger prime;
+  BigInteger generator;
 };
 
+typedef DOMString NamedCurve;
 dictionary EcKeyGenParams : Algorithm {
-  required NamedCurve namedCurve;
+  NamedCurve namedCurve;
 };
 
 dictionary AesDerivedKeyParams : Algorithm {
-  [EnforceRange] required unsigned long length;
+  [EnforceRange] unsigned long length;
 };
 
 dictionary HmacDerivedKeyParams : HmacImportParams {
   [EnforceRange] unsigned long length;
 };
 
 dictionary EcdhKeyDeriveParams : Algorithm {
-  required CryptoKey public;
+  CryptoKey public;
 };
 
 
 /***** JWK *****/
 
 dictionary RsaOtherPrimesInfo {
   // The following fields are defined in Section 6.3.2.7 of JSON Web Algorithms
-  required DOMString r;
-  required DOMString d;
-  required DOMString t;
+  DOMString r;
+  DOMString d;
+  DOMString t;
 };
 
 dictionary JsonWebKey {
   // The following fields are defined in Section 3.1 of JSON Web Key
-  required DOMString kty;
+  DOMString kty;
   DOMString use;
   sequence<DOMString> key_ops;
   DOMString alg;
 
   // The following fields are defined in JSON Web Key Parameters Registration
   boolean ext;
 
   // The following fields are defined in Section 6 of JSON Web Algorithms
@@ -125,23 +164,24 @@ dictionary JsonWebKey {
 
 
 /***** The Main API *****/
 
 [Pref="dom.webcrypto.enabled"]
 interface CryptoKey {
   readonly attribute KeyType type;
   readonly attribute boolean extractable;
-  [Cached, Constant, Throws] readonly attribute object algorithm;
+  readonly attribute KeyAlgorithm algorithm;
   [Cached, Constant, Frozen] readonly attribute sequence<KeyUsage> usages;
 };
 
-dictionary CryptoKeyPair {
-  required CryptoKey publicKey;
-  required CryptoKey privateKey;
+[Pref="dom.webcrypto.enabled"]
+interface CryptoKeyPair {
+  readonly attribute CryptoKey publicKey;
+  readonly attribute CryptoKey privateKey;
 };
 
 typedef DOMString KeyFormat;
 typedef (ArrayBufferView or ArrayBuffer) CryptoOperationData;
 typedef (object or DOMString) AlgorithmIdentifier;
 
 [Pref="dom.webcrypto.enabled"]
 interface SubtleCrypto {
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -233,17 +233,16 @@ WEBIDL_FILES = [
     'InputEvent.webidl',
     'InputMethod.webidl',
     'InspectorUtils.webidl',
     'InstallEvent.webidl',
     'InstallPhaseEvent.webidl',
     'InterAppConnection.webidl',
     'InterAppConnectionRequest.webidl',
     'InterAppMessagePort.webidl',
-    'KeyAlgorithm.webidl',
     'KeyboardEvent.webidl',
     'KeyEvent.webidl',
     'LegacyQueryInterface.webidl',
     'LinkStyle.webidl',
     'LocalMediaStream.webidl',
     'Location.webidl',
     'MediaElementAudioSourceNode.webidl',
     'MediaError.webidl',