Bug 995385 - Add WebCrypto WebIDL interfaces. r=bz, r=cviecco, r=dkeeler
authorRichard Barnes <rbarnes@mozilla.com>
Thu, 15 May 2014 06:20:00 -0400
changeset 184203 7ea38414bb31b01471b1beebf149c3cb99023fb7
parent 184202 21d19c772468327cc2c9e08a7dfa72ef90bd129b
child 184204 43114cb9948066b7fbcde652e331edf2fe365fee
push id26814
push userryanvm@gmail.com
push dateWed, 21 May 2014 19:50:12 +0000
treeherdermozilla-central@7aee2fa0f655 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, cviecco, dkeeler
bugs995385
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 995385 - Add WebCrypto WebIDL interfaces. r=bz, r=cviecco, r=dkeeler
dom/base/Crypto.cpp
dom/base/Crypto.h
dom/base/DOMException.cpp
dom/base/StructuredCloneTags.h
dom/base/SubtleCrypto.cpp
dom/base/SubtleCrypto.h
dom/base/domerr.msg
dom/base/moz.build
dom/base/nsJSEnvironment.cpp
dom/bindings/Bindings.conf
dom/crypto/AesKeyAlgorithm.cpp
dom/crypto/AesKeyAlgorithm.h
dom/crypto/CryptoBuffer.cpp
dom/crypto/CryptoBuffer.h
dom/crypto/HmacKeyAlgorithm.cpp
dom/crypto/HmacKeyAlgorithm.h
dom/crypto/Key.cpp
dom/crypto/Key.h
dom/crypto/KeyAlgorithm.cpp
dom/crypto/KeyAlgorithm.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/WebCryptoTask.h
dom/crypto/moz.build
dom/crypto/test/mochitest.ini
dom/crypto/test/moz.build
dom/crypto/test/test-array.js
dom/crypto/test/test-vectors.js
dom/crypto/test/test_WebCrypto.css
dom/crypto/test/test_WebCrypto.html
dom/crypto/test/tests.js
dom/crypto/test/util.js
dom/moz.build
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/Crypto.webidl
dom/webidl/SubtleCrypto.webidl
dom/webidl/moz.build
modules/libpref/src/init/all.js
xpcom/base/ErrorList.h
--- a/dom/base/Crypto.cpp
+++ b/dom/base/Crypto.cpp
@@ -24,17 +24,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCrypto)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Crypto)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Crypto)
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Crypto, mWindow)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Crypto, mWindow, mSubtle)
 
 Crypto::Crypto()
 {
   MOZ_COUNT_CTOR(Crypto);
   SetIsDOMBinding();
 }
 
 Crypto::~Crypto()
@@ -112,16 +112,25 @@ Crypto::GetRandomValues(JSContext* aCx, 
 
     memcpy(data, buf, dataLen);
     NS_Free(buf);
   }
 
   return view;
 }
 
+SubtleCrypto*
+Crypto::Subtle()
+{
+  if(!mSubtle) {
+    mSubtle = new SubtleCrypto(GetParentObject());
+  }
+  return mSubtle;
+}
+
 #ifndef MOZ_DISABLE_CRYPTOLEGACY
 // Stub out the legacy nsIDOMCrypto methods. The actual
 // implementations are in security/manager/ssl/src/nsCrypto.{cpp,h}
 
 NS_IMETHODIMP
 Crypto::GetEnableSmartCardEvents(bool *aEnableSmartCardEvents)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
--- a/dom/base/Crypto.h
+++ b/dom/base/Crypto.h
@@ -10,16 +10,17 @@
 #include "nsIDOMCryptoLegacy.h"
 namespace mozilla {
 namespace dom {
 class CRMFObject;
 }
 }
 #endif
 
+#include "mozilla/dom/SubtleCrypto.h"
 #include "nsPIDOMWindow.h"
 
 #include "nsWrapperCache.h"
 #include "mozilla/dom/TypedArray.h"
 #define NS_DOMCRYPTO_CID \
   {0x929d9320, 0x251e, 0x11d4, { 0x8a, 0x7c, 0x00, 0x60, 0x08, 0xc8, 0x44, 0xc3} }
 
 namespace mozilla {
@@ -39,16 +40,19 @@ public:
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Crypto)
 
   JSObject *
   GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray,
 		  ErrorResult& aRv);
 
+  SubtleCrypto*
+  Subtle();
+
 #ifndef MOZ_DISABLE_CRYPTOLEGACY
   virtual bool EnableSmartCardEvents();
   virtual void SetEnableSmartCardEvents(bool aEnable, ErrorResult& aRv);
 
   virtual void GetVersion(nsString& aVersion);
 
   virtual mozilla::dom::CRMFObject*
   GenerateCRMFRequest(JSContext* aContext,
@@ -87,14 +91,15 @@ public:
   virtual JSObject*
   WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   static uint8_t*
   GetRandomValues(uint32_t aLength);
 
 private:
   nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsRefPtr<SubtleCrypto> mSubtle;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Crypto_h
--- a/dom/base/DOMException.cpp
+++ b/dom/base/DOMException.cpp
@@ -65,16 +65,19 @@ enum DOM4ErrorTypeCodeMap {
   ReadOnlyError            = 0,
   VersionError             = 0,
 
   /* File API errors http://dev.w3.org/2006/webapi/FileAPI/#ErrorAndException */
   NotReadableError         = 0,
 
   /* FileHandle API errors */
   LockedFileInactiveError = 0,
+
+  /* WebCrypto errors https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#dfn-DataError */
+  OperationError           = 0,
 };
 
 #define DOM4_MSG_DEF(name, message, nsresult) {(nsresult), name, #name, message},
 #define DOM_MSG_DEF(val, message) {(val), NS_ERROR_GET_CODE(val), #val, message},
 
 static const struct ResultStruct
 {
   nsresult mNSResult;
@@ -479,17 +482,17 @@ Exception::WrapObject(JSContext* cx)
   return ExceptionBinding::Wrap(cx, this);
 }
 
 void
 Exception::GetMessageMoz(nsString& retval)
 {
   nsCString str;
 #ifdef DEBUG
-  DebugOnly<nsresult> rv = 
+  DebugOnly<nsresult> rv =
 #endif
   GetMessageMoz(str);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
   CopyUTF8toUTF16(str, retval);
 }
 
 uint32_t
 Exception::Result() const
--- a/dom/base/StructuredCloneTags.h
+++ b/dom/base/StructuredCloneTags.h
@@ -27,15 +27,18 @@ enum StructuredCloneTags {
   SCTAG_DOM_FILE,
 
   // These tags are used for both main thread and workers.
   SCTAG_DOM_IMAGEDATA,
   SCTAG_DOM_MAP_MESSAGEPORT,
 
   SCTAG_DOM_FUNCTION,
 
+  // This tag is for WebCrypto keys
+  SCTAG_DOM_WEBCRYPTO_KEY,
+
   SCTAG_DOM_MAX
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // StructuredCloneTags_h__
new file mode 100644
--- /dev/null
+++ b/dom/base/SubtleCrypto.cpp
@@ -0,0 +1,139 @@
+/* -*- 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/SubtleCrypto.h"
+
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/SubtleCryptoBinding.h"
+#include "mozilla/dom/WebCryptoTask.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(SubtleCrypto, mWindow)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(SubtleCrypto)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(SubtleCrypto)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SubtleCrypto)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+
+
+SubtleCrypto::SubtleCrypto(nsPIDOMWindow* aWindow)
+  : mWindow(aWindow)
+{
+  SetIsDOMBinding();
+}
+
+JSObject*
+SubtleCrypto::WrapObject(JSContext* aCx)
+{
+  return SubtleCryptoBinding::Wrap(aCx, this);
+}
+
+#define SUBTLECRYPTO_METHOD_BODY(Operation, ...) \
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow); \
+  MOZ_ASSERT(global); \
+  nsRefPtr<Promise> p = new Promise(global); \
+  nsRefPtr<WebCryptoTask> task = WebCryptoTask::Operation ## Task(__VA_ARGS__); \
+  task->DispatchWithPromise(p); \
+  return p.forget();
+
+already_AddRefed<Promise>
+SubtleCrypto::Encrypt(JSContext* cx,
+                      const ObjectOrString& algorithm,
+                      Key& key,
+                      const CryptoOperationData& data)
+{
+  SUBTLECRYPTO_METHOD_BODY(Encrypt, cx, algorithm, key, data)
+}
+
+already_AddRefed<Promise>
+SubtleCrypto::Decrypt(JSContext* cx,
+                      const ObjectOrString& algorithm,
+                      Key& key,
+                      const CryptoOperationData& data)
+{
+  SUBTLECRYPTO_METHOD_BODY(Decrypt, cx, algorithm, key, data)
+}
+
+already_AddRefed<Promise>
+SubtleCrypto::Sign(JSContext* cx,
+                   const ObjectOrString& algorithm,
+                   Key& key,
+                   const CryptoOperationData& data)
+{
+  SUBTLECRYPTO_METHOD_BODY(Sign, cx, algorithm, key, data)
+}
+
+already_AddRefed<Promise>
+SubtleCrypto::Verify(JSContext* cx,
+                     const ObjectOrString& algorithm,
+                     Key& key,
+                     const CryptoOperationData& signature,
+                     const CryptoOperationData& data)
+{
+  SUBTLECRYPTO_METHOD_BODY(Verify, cx, algorithm, key, signature, data)
+}
+
+already_AddRefed<Promise>
+SubtleCrypto::Digest(JSContext* cx,
+                     const ObjectOrString& algorithm,
+                     const CryptoOperationData& data)
+{
+  SUBTLECRYPTO_METHOD_BODY(Digest, cx, algorithm, data)
+}
+
+
+already_AddRefed<Promise>
+SubtleCrypto::ImportKey(JSContext* cx,
+                        const nsAString& format,
+                        const KeyData& keyData,
+                        const ObjectOrString& algorithm,
+                        bool extractable,
+                        const Sequence<nsString>& keyUsages)
+{
+  SUBTLECRYPTO_METHOD_BODY(ImportKey, cx, format, keyData, algorithm,
+                                      extractable, keyUsages)
+}
+
+already_AddRefed<Promise>
+SubtleCrypto::ExportKey(const nsAString& format,
+                        Key& key)
+{
+  SUBTLECRYPTO_METHOD_BODY(ExportKey, format, key)
+}
+
+already_AddRefed<Promise>
+SubtleCrypto::GenerateKey(JSContext* cx, const ObjectOrString& algorithm,
+                          bool extractable, const Sequence<nsString>& keyUsages)
+{
+  SUBTLECRYPTO_METHOD_BODY(GenerateKey, cx, algorithm, extractable, keyUsages)
+}
+
+already_AddRefed<Promise>
+SubtleCrypto::DeriveKey(JSContext* cx,
+                        const ObjectOrString& algorithm,
+                        Key& baseKey,
+                        const ObjectOrString& derivedKeyType,
+                        bool extractable, const Sequence<nsString>& keyUsages)
+{
+  SUBTLECRYPTO_METHOD_BODY(DeriveKey, cx, algorithm, baseKey, derivedKeyType,
+                                      extractable, keyUsages)
+}
+
+already_AddRefed<Promise>
+SubtleCrypto::DeriveBits(JSContext* cx,
+                         const ObjectOrString& algorithm,
+                         Key& baseKey,
+                         uint32_t length)
+{
+  SUBTLECRYPTO_METHOD_BODY(DeriveBits, cx, algorithm, baseKey, length)
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/SubtleCrypto.h
@@ -0,0 +1,100 @@
+/* -*- 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_SubtleCrypto_h
+#define mozilla_dom_SubtleCrypto_h
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+#include "nsPIDOMWindow.h"
+#include "mozilla/dom/UnionTypes.h"
+#include "mozilla/dom/Key.h"
+#include "js/TypeDecls.h"
+
+namespace mozilla {
+namespace dom {
+
+class Promise;
+
+typedef ArrayBufferViewOrArrayBuffer CryptoOperationData;
+typedef ArrayBufferViewOrArrayBuffer KeyData;
+
+class SubtleCrypto MOZ_FINAL : public nsISupports,
+                               public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(SubtleCrypto)
+
+public:
+  SubtleCrypto(nsPIDOMWindow* aWindow);
+
+  nsPIDOMWindow* GetParentObject() const
+  {
+    return mWindow;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  already_AddRefed<Promise> Encrypt(JSContext* cx,
+                                    const ObjectOrString& algorithm,
+                                    Key& key,
+                                    const CryptoOperationData& data);
+
+  already_AddRefed<Promise> Decrypt(JSContext* cx,
+                                    const ObjectOrString& algorithm,
+                                    Key& key,
+                                    const CryptoOperationData& data);
+
+  already_AddRefed<Promise> Sign(JSContext* cx,
+                                 const ObjectOrString& algorithm,
+                                 Key& key,
+                                 const CryptoOperationData& data);
+
+  already_AddRefed<Promise> Verify(JSContext* cx,
+                                   const ObjectOrString& algorithm,
+                                   Key& key,
+                                   const CryptoOperationData& signature,
+                                   const CryptoOperationData& data);
+
+  already_AddRefed<Promise> Digest(JSContext* cx,
+                                   const ObjectOrString& aAlgorithm,
+                                   const CryptoOperationData& aData);
+
+  already_AddRefed<Promise> ImportKey(JSContext* cx,
+                                      const nsAString& format,
+                                      const KeyData& keyData,
+                                      const ObjectOrString& algorithm,
+                                      bool extractable,
+                                      const Sequence<nsString>& keyUsages);
+
+  already_AddRefed<Promise> ExportKey(const nsAString& format, Key& key);
+
+  already_AddRefed<Promise> GenerateKey(JSContext* cx,
+                                        const ObjectOrString& algorithm,
+                                        bool extractable,
+                                        const Sequence<nsString>& keyUsages);
+
+  already_AddRefed<Promise> DeriveKey(JSContext* cx,
+                                      const ObjectOrString& algorithm,
+                                      Key& baseKey,
+                                      const ObjectOrString& derivedKeyType,
+                                      bool extractable,
+                                      const Sequence<nsString>& keyUsages);
+
+  already_AddRefed<Promise> DeriveBits(JSContext* cx,
+                                       const ObjectOrString& algorithm,
+                                       Key& baseKey,
+                                       uint32_t length);
+
+private:
+  nsCOMPtr<nsPIDOMWindow> mWindow;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_SubtleCrypto_h
--- a/dom/base/domerr.msg
+++ b/dom/base/domerr.msg
@@ -36,16 +36,21 @@ DOM4_MSG_DEF(InvalidPointerId, "Invalid 
 DOM4_MSG_DEF(TypeError, "The method parameter is missing or invalid.", NS_ERROR_TYPE_ERR)
 DOM4_MSG_DEF(RangeError, "The method parameter is out of valid range.", NS_ERROR_RANGE_ERR)
 
 /* StringEncoding API errors from http://wiki.whatwg.org/wiki/StringEncoding */
 DOM4_MSG_DEF(EncodingError, "The given encoding is not supported.", NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR)
 DOM4_MSG_DEF(EncodingError, "The encoding must be utf-8, utf-16, or utf-16be.", NS_ERROR_DOM_ENCODING_NOT_UTF_ERR)
 DOM4_MSG_DEF(EncodingError, "The decoder failed to convert.", NS_ERROR_DOM_ENCODING_DECODE_ERR)
 
+/* WebCrypto API errors from http://www.w3.org/TR/WebCryptoAPI/ */
+DOM4_MSG_DEF(UnknownError, "The operation failed for an unknown transient reason", NS_ERROR_DOM_UNKNOWN_ERR)
+DOM4_MSG_DEF(DataError, "Data provided to an operation does not meet requirements", NS_ERROR_DOM_DATA_ERR)
+DOM4_MSG_DEF(OperationError, "The operation failed for an operation-specific reason", NS_ERROR_DOM_OPERATION_ERR)
+
 /* SVG DOM errors from http://www.w3.org/TR/SVG11/svgdom.html */
 
 DOM4_MSG_DEF(TypeError, "Unknown or invalid type", NS_ERROR_DOM_SVG_WRONG_TYPE_ERR)
 DOM4_MSG_DEF(InvalidStateError, "The matrix could not be computed", NS_ERROR_DOM_SVG_MATRIX_NOT_INVERTABLE)
 
 /* XPath errors from http://www.w3.org/TR/DOM-Level-3-XPath/ */
 
 DOM4_MSG_DEF(SyntaxError, "The expression is not a legal expression.", NS_ERROR_DOM_INVALID_EXPRESSION_ERR)
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -60,16 +60,17 @@ EXPORTS.mozilla.dom += [
     'MessagePort.h',
     'MessagePortList.h',
     'Navigator.h',
     'PerformanceEntry.h',
     'PerformanceResourceTiming.h',
     'ScreenOrientation.h',
     'ScriptSettings.h',
     'StructuredCloneTags.h',
+    'SubtleCrypto.h',
     'URL.h',
     'URLSearchParams.h',
 ]
 
 UNIFIED_SOURCES += [
     'BarProps.cpp',
     'CompositionStringSynthesizer.cpp',
     'Console.cpp',
@@ -100,16 +101,17 @@ UNIFIED_SOURCES += [
     'nsScriptNameSpaceManager.cpp',
     'nsStructuredCloneContainer.cpp',
     'nsWindowMemoryReporter.cpp',
     'nsWindowRoot.cpp',
     'nsWrapperCache.cpp',
     'PerformanceEntry.cpp',
     'PerformanceResourceTiming.cpp',
     'ScriptSettings.cpp',
+    'SubtleCrypto.cpp',
     'URL.cpp',
     'URLSearchParams.cpp',
     'WindowNamedPropertiesHandler.cpp',
 ]
 
 # these files couldn't be in UNIFIED_SOURCES for now for reasons given below:
 SOURCES += [
     # this file doesn't like windows.h
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -50,17 +50,19 @@
 #include "prmem.h"
 #include "WrapperFactory.h"
 #include "nsGlobalWindow.h"
 #include "nsScriptNameSpaceManager.h"
 #include "StructuredCloneTags.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/dom/ErrorEvent.h"
 #include "mozilla/dom/ImageData.h"
+#include "mozilla/dom/Key.h"
 #include "mozilla/dom/ImageDataBinding.h"
+#include "mozilla/dom/SubtleCryptoBinding.h"
 #include "nsAXPCNativeCallContext.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 
 #include "nsJSPrincipals.h"
 
 #ifdef XP_MACOSX
 // AssertMacros.h defines 'check' and conflicts with AccessCheck.h
 #undef check
@@ -2815,48 +2817,67 @@ NS_DOMReadStructuredClone(JSContext* cx,
     }
     MOZ_ASSERT(dataArray.isObject());
 
     // Construct the ImageData.
     nsRefPtr<ImageData> imageData = new ImageData(width, height,
                                                   dataArray.toObject());
     // Wrap it in a JS::Value.
     return imageData->WrapObject(cx);
+  } else if (tag == SCTAG_DOM_WEBCRYPTO_KEY) {
+    nsIGlobalObject *global = xpc::GetNativeForGlobal(JS::CurrentGlobalOrNull(cx));
+    if (!global) {
+      return nullptr;
+    }
+
+    nsRefPtr<Key> key = new Key(global);
+    if (!key->ReadStructuredClone(reader)) {
+      return nullptr;
+    }
+
+    return key->WrapObject(cx);
   }
 
   // Don't know what this is. Bail.
   xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
   return nullptr;
 }
 
 bool
 NS_DOMWriteStructuredClone(JSContext* cx,
                            JSStructuredCloneWriter* writer,
                            JS::Handle<JSObject*> obj,
                            void *closure)
 {
+  // Handle ImageData cloning
   ImageData* imageData;
-  nsresult rv = UNWRAP_OBJECT(ImageData, obj, imageData);
-  if (NS_FAILED(rv)) {
-    // Don't know what this is. Bail.
-    xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
-    return false;
+  if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageData, obj, imageData))) {
+    // Prepare the ImageData internals.
+    uint32_t width = imageData->Width();
+    uint32_t height = imageData->Height();
+    JS::Rooted<JSObject*> dataArray(cx, imageData->GetDataObject());
+
+    // Write the internals to the stream.
+    JSAutoCompartment ac(cx, dataArray);
+    JS::Rooted<JS::Value> arrayValue(cx, JS::ObjectValue(*dataArray));
+    return JS_WriteUint32Pair(writer, SCTAG_DOM_IMAGEDATA, 0) &&
+           JS_WriteUint32Pair(writer, width, height) &&
+           JS_WriteTypedArray(writer, arrayValue);
   }
 
-  // Prepare the ImageData internals.
-  uint32_t width = imageData->Width();
-  uint32_t height = imageData->Height();
-  JS::Rooted<JSObject*> dataArray(cx, imageData->GetDataObject());
-
-  // Write the internals to the stream.
-  JSAutoCompartment ac(cx, dataArray);
-  JS::Rooted<JS::Value> arrayValue(cx, JS::ObjectValue(*dataArray));
-  return JS_WriteUint32Pair(writer, SCTAG_DOM_IMAGEDATA, 0) &&
-         JS_WriteUint32Pair(writer, width, height) &&
-         JS_WriteTypedArray(writer, arrayValue);
+  // Handle Key cloning
+  Key* key;
+  if (NS_SUCCEEDED(UNWRAP_OBJECT(Key, obj, key))) {
+    return JS_WriteUint32Pair(writer, SCTAG_DOM_WEBCRYPTO_KEY, 0) &&
+           key->WriteStructuredClone(writer);
+  }
+
+  // Don't know what this is
+  xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
+  return false;
 }
 
 void
 NS_DOMStructuredCloneError(JSContext* cx,
                            uint32_t errorid)
 {
   // We don't currently support any extensions to structured cloning.
   xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -478,16 +478,20 @@ DOMInterfaces = {
     'headerFile': 'nsGeolocation.h'
 },
 
 'History': {
     'headerFile': 'nsHistory.h',
     'nativeType': 'nsHistory'
 },
 
+'HmacKeyAlgorithm': {
+    'resultNotAddRefed': ['hash']
+},
+
 'HTMLAppletElement': {
     'nativeType': 'mozilla::dom::HTMLSharedObjectElement'
 },
 
 'HTMLBaseElement': {
     'nativeType': 'mozilla::dom::HTMLSharedElement'
 },
 
@@ -714,16 +718,20 @@ DOMInterfaces = {
 },
 
 'InstallEvent': {
     'headerFile': 'ServiceWorkerEvents.h',
     'nativeType': 'mozilla::dom::workers::InstallEvent',
     'workers': True
 },
 
+'Key': {
+    'resultNotAddRefed': ['algorithm']
+},
+
 'KeyEvent': {
     'concrete': False
 },
 
 'LocalMediaStream': {
     'headerFile': 'DOMMediaStream.h',
     'nativeType': 'mozilla::DOMLocalMediaStream'
 },
@@ -1006,16 +1014,20 @@ DOMInterfaces = {
     'resultNotAddRefed': [ 'top', 'right', 'bottom', 'left' ]
 },
 
 'RGBColor': {
     'nativeType': 'nsDOMCSSRGBColor',
     'resultNotAddRefed': [ 'alpha', 'blue', 'green', 'red' ]
 },
 
+'RsaHashedKeyAlgorithm': {
+    'resultNotAddRefed': ['hash']
+},
+
 'Screen': {
     'nativeType': 'nsScreen',
 },
 
 'Selection': {
     'resultNotAddRefed': [ 'anchorNode', 'focusNode', 'getRangeAt' ],
 },
 
new file mode 100644
--- /dev/null
+++ b/dom/crypto/AesKeyAlgorithm.cpp
@@ -0,0 +1,44 @@
+/* -*- 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);
+}
+
+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,45 @@
+/* -*- 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/KeyAlgorithm.h"
+#include "js/TypeDecls.h"
+
+namespace mozilla {
+namespace dom {
+
+class AesKeyAlgorithm MOZ_FINAL : public KeyAlgorithm
+{
+public:
+  AesKeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName, uint16_t aLength)
+    : KeyAlgorithm(aGlobal, aName)
+    , mLength(aLength)
+  {}
+
+  ~AesKeyAlgorithm()
+  {}
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  uint16_t Length() const
+  {
+    return mLength;
+  }
+
+  virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE;
+  static KeyAlgorithm* Create(nsIGlobalObject* aGlobal,
+                              JSStructuredCloneReader* aReader);
+
+protected:
+  uint16_t mLength;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_AesKeyAlgorithm_h
new file mode 100644
--- /dev/null
+++ b/dom/crypto/CryptoBuffer.cpp
@@ -0,0 +1,103 @@
+/* -*- 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 "CryptoBuffer.h"
+#include "mozilla/dom/UnionTypes.h"
+
+namespace mozilla {
+namespace dom {
+
+uint8_t*
+CryptoBuffer::Assign(const uint8_t* aData, uint32_t aLength)
+{
+  return ReplaceElementsAt(0, Length(), aData, aLength);
+}
+
+uint8_t*
+CryptoBuffer::Assign(const SECItem* aItem)
+{
+  return Assign(aItem->data, aItem->len);
+}
+
+uint8_t*
+CryptoBuffer::Assign(const ArrayBuffer& aData)
+{
+  return Assign(aData.Data(), aData.Length());
+}
+
+uint8_t*
+CryptoBuffer::Assign(const ArrayBufferView& aData)
+{
+  return Assign(aData.Data(), aData.Length());
+}
+
+uint8_t*
+CryptoBuffer::Assign(const ArrayBufferViewOrArrayBuffer& aData)
+{
+  if (aData.IsArrayBufferView()) {
+    return Assign(aData.GetAsArrayBufferView());
+  } else if (aData.IsArrayBuffer()) {
+    return Assign(aData.GetAsArrayBuffer());
+  }
+
+  // If your union is uninitialized, something's wrong
+  MOZ_ASSERT(false);
+  SetLength(0);
+  return nullptr;
+}
+
+uint8_t*
+CryptoBuffer::Assign(const OwningArrayBufferViewOrArrayBuffer& aData)
+{
+  if (aData.IsArrayBufferView()) {
+    return Assign(aData.GetAsArrayBufferView());
+  } else if (aData.IsArrayBuffer()) {
+    return Assign(aData.GetAsArrayBuffer());
+  }
+
+  // If your union is uninitialized, something's wrong
+  MOZ_ASSERT(false);
+  SetLength(0);
+  return nullptr;
+}
+
+SECItem*
+CryptoBuffer::ToSECItem()
+{
+  uint8_t* data = (uint8_t*) moz_malloc(Length());
+  if (!data) {
+    return nullptr;
+  }
+
+  SECItem* item = new SECItem();
+  item->type = siBuffer;
+  item->data = data;
+  item->len = Length();
+
+  memcpy(item->data, Elements(), Length());
+
+  return item;
+}
+
+// "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;
+  }
+
+  aRetVal = 0;
+  for (size_t i=0; i < Length(); ++i) {
+    aRetVal = (aRetVal << 8) + ElementAt(i);
+  }
+  return true;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/crypto/CryptoBuffer.h
@@ -0,0 +1,56 @@
+/* -*- 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_CryptoBuffer_h
+#define mozilla_dom_CryptoBuffer_h
+
+#include "nsTArray.h"
+#include "seccomon.h"
+#include "mozilla/dom/TypedArray.h"
+
+namespace mozilla {
+namespace dom {
+
+class ArrayBufferViewOrArrayBuffer;
+class OwningArrayBufferViewOrArrayBuffer;
+
+class CryptoBuffer : public FallibleTArray<uint8_t>
+{
+public:
+  CryptoBuffer()
+    : FallibleTArray<uint8_t>()
+  {}
+
+  template<class T>
+  explicit CryptoBuffer(const T& aData)
+    : FallibleTArray<uint8_t>()
+  {
+    Assign(aData);
+  }
+
+  template<class T>
+  CryptoBuffer& operator=(const T& aData)
+  {
+    Assign(aData);
+    return *this;
+  }
+
+  uint8_t* Assign(const uint8_t* aData, uint32_t aLength);
+  uint8_t* Assign(const SECItem* aItem);
+  uint8_t* Assign(const ArrayBuffer& aData);
+  uint8_t* Assign(const ArrayBufferView& aData);
+  uint8_t* Assign(const ArrayBufferViewOrArrayBuffer& aData);
+  uint8_t* Assign(const OwningArrayBufferViewOrArrayBuffer& aData);
+
+  SECItem* ToSECItem();
+
+  bool GetBigIntValue(unsigned long& aRetVal);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_CryptoBuffer_h
new file mode 100644
--- /dev/null
+++ b/dom/crypto/HmacKeyAlgorithm.cpp
@@ -0,0 +1,52 @@
+/* -*- 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);
+}
+
+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,71 @@
+/* -*- 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_SHA224: mMechanism = CKM_SHA224_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;
+    }
+  }
+
+  ~HmacKeyAlgorithm()
+  {}
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  KeyAlgorithm* Hash() const
+  {
+    return mHash;
+  }
+
+  uint32_t Length() const
+  {
+    return mLength;
+  }
+
+  virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE;
+  static KeyAlgorithm* Create(nsIGlobalObject* aGlobal,
+                              JSStructuredCloneReader* aReader);
+
+protected:
+  nsRefPtr<KeyAlgorithm> mHash;
+  uint32_t mLength;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_HmacKeyAlgorithm_h
new file mode 100644
--- /dev/null
+++ b/dom/crypto/Key.cpp
@@ -0,0 +1,409 @@
+/* -*- 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 "pk11pub.h"
+#include "cryptohi.h"
+#include "ScopedNSSTypes.h"
+#include "mozilla/dom/Key.h"
+#include "mozilla/dom/WebCryptoCommon.h"
+#include "mozilla/dom/SubtleCryptoBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Key, mGlobal, mAlgorithm)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(Key)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(Key)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Key)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+Key::Key(nsIGlobalObject* aGlobal)
+  : mGlobal(aGlobal)
+  , mAttributes(0)
+  , mSymKey()
+  , mPrivateKey(nullptr)
+  , mPublicKey(nullptr)
+{
+  SetIsDOMBinding();
+}
+
+Key::~Key()
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return;
+  }
+  destructorSafeDestroyNSSReference();
+  shutdown(calledFromObject);
+}
+
+JSObject*
+Key::WrapObject(JSContext* aCx)
+{
+  return KeyBinding::Wrap(aCx, this);
+}
+
+void
+Key::GetType(nsString& aRetVal) const
+{
+  uint32_t type = mAttributes & TYPE_MASK;
+  switch (type) {
+    case PUBLIC:  aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC); break;
+    case PRIVATE: aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE); break;
+    case SECRET:  aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_SECRET); break;
+  }
+}
+
+bool
+Key::Extractable() const
+{
+  return (mAttributes & EXTRACTABLE);
+}
+
+KeyAlgorithm*
+Key::Algorithm() const
+{
+  return mAlgorithm;
+}
+
+void
+Key::GetUsages(nsTArray<nsString>& aRetVal) const
+{
+  if (mAttributes & ENCRYPT) {
+    aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_ENCRYPT));
+  }
+  if (mAttributes & DECRYPT) {
+    aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DECRYPT));
+  }
+  if (mAttributes & SIGN) {
+    aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_SIGN));
+  }
+  if (mAttributes & VERIFY) {
+    aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_VERIFY));
+  }
+  if (mAttributes & DERIVEKEY) {
+    aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DERIVEKEY));
+  }
+  if (mAttributes & DERIVEBITS) {
+    aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DERIVEBITS));
+  }
+  if (mAttributes & WRAPKEY) {
+    aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_WRAPKEY));
+  }
+  if (mAttributes & UNWRAPKEY) {
+    aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_UNWRAPKEY));
+  }
+}
+
+Key::KeyType
+Key::GetKeyType() const
+{
+  return static_cast<Key::KeyType>(mAttributes & TYPE_MASK);
+}
+
+nsresult
+Key::SetType(const nsString& aType)
+{
+  mAttributes &= CLEAR_TYPE;
+  if (aType.EqualsLiteral(WEBCRYPTO_KEY_TYPE_SECRET)) {
+    mAttributes |= SECRET;
+  } else if (aType.EqualsLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC)) {
+    mAttributes |= PUBLIC;
+  } else if (aType.EqualsLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE)) {
+    mAttributes |= PRIVATE;
+  } else {
+    mAttributes |= UNKNOWN;
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+
+  return NS_OK;
+}
+
+void
+Key::SetType(Key::KeyType aType)
+{
+  mAttributes &= CLEAR_TYPE;
+  mAttributes |= aType;
+}
+
+void
+Key::SetExtractable(bool aExtractable)
+{
+  mAttributes &= CLEAR_EXTRACTABLE;
+  if (aExtractable) {
+    mAttributes |= EXTRACTABLE;
+  }
+}
+
+void
+Key::SetAlgorithm(KeyAlgorithm* aAlgorithm)
+{
+  mAlgorithm = aAlgorithm;
+}
+
+void
+Key::ClearUsages()
+{
+  mAttributes &= CLEAR_USAGES;
+}
+
+nsresult
+Key::AddUsage(const nsString& aUsage)
+{
+  if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_ENCRYPT)) {
+    mAttributes |= ENCRYPT;
+  } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DECRYPT)) {
+    mAttributes |= DECRYPT;
+  } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_SIGN)) {
+    mAttributes |= SIGN;
+  } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_VERIFY)) {
+    mAttributes |= VERIFY;
+  } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DERIVEKEY)) {
+    mAttributes |= DERIVEKEY;
+  } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DERIVEBITS)) {
+    mAttributes |= DERIVEBITS;
+  } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_WRAPKEY)) {
+    mAttributes |= WRAPKEY;
+  } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_UNWRAPKEY)) {
+    mAttributes |= UNWRAPKEY;
+  } else {
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+
+  return NS_OK;
+}
+
+void
+Key::AddUsage(Key::KeyUsage aUsage)
+{
+  mAttributes |= aUsage;
+}
+
+bool
+Key::HasUsage(Key::KeyUsage aUsage)
+{
+  return !!(mAttributes & aUsage);
+}
+
+bool
+Key::HasUsageOtherThan(uint32_t aUsages)
+{
+  return !!(mAttributes & USAGES_MASK & ~aUsages);
+}
+
+void Key::SetSymKey(const CryptoBuffer& aSymKey)
+{
+  mSymKey = aSymKey;
+}
+
+void
+Key::SetPrivateKey(SECKEYPrivateKey* aPrivateKey)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (!aPrivateKey || isAlreadyShutDown()) {
+    mPrivateKey = nullptr;
+    return;
+  }
+  mPrivateKey = SECKEY_CopyPrivateKey(aPrivateKey);
+}
+
+void
+Key::SetPublicKey(SECKEYPublicKey* aPublicKey)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (!aPublicKey || isAlreadyShutDown()) {
+    mPublicKey = nullptr;
+    return;
+  }
+  mPublicKey = SECKEY_CopyPublicKey(aPublicKey);
+}
+
+const CryptoBuffer&
+Key::GetSymKey() const
+{
+  return mSymKey;
+}
+
+SECKEYPrivateKey*
+Key::GetPrivateKey() const
+{
+  nsNSSShutDownPreventionLock locker;
+  if (!mPrivateKey || isAlreadyShutDown()) {
+    return nullptr;
+  }
+  return SECKEY_CopyPrivateKey(mPrivateKey.get());
+}
+
+SECKEYPublicKey*
+Key::GetPublicKey() const
+{
+  nsNSSShutDownPreventionLock locker;
+  if (!mPublicKey || isAlreadyShutDown()) {
+    return nullptr;
+  }
+  return SECKEY_CopyPublicKey(mPublicKey.get());
+}
+
+void Key::virtualDestroyNSSReference()
+{
+  destructorSafeDestroyNSSReference();
+}
+
+void Key::destructorSafeDestroyNSSReference()
+{
+  mPrivateKey.dispose();
+  mPublicKey.dispose();
+}
+
+
+// Serialization and deserialization convenience methods
+
+SECKEYPrivateKey*
+Key::PrivateKeyFromPkcs8(CryptoBuffer& aKeyData,
+                         const nsNSSShutDownPreventionLock& /*proofOfLock*/)
+{
+  SECKEYPrivateKey* privKey;
+  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+  ScopedSECItem pkcs8Item(aKeyData.ToSECItem());
+  if (!pkcs8Item) {
+    return nullptr;
+  }
+
+  // Allow everything, we enforce usage ourselves
+  unsigned int usage = KU_ALL;
+
+  nsresult rv = MapSECStatus(PK11_ImportDERPrivateKeyInfoAndReturnKey(
+                slot.get(), pkcs8Item.get(), nullptr, nullptr, false, false,
+                usage, &privKey, nullptr));
+
+  if (NS_FAILED(rv)) {
+    return nullptr;
+  }
+  return privKey;
+}
+
+SECKEYPublicKey*
+Key::PublicKeyFromSpki(CryptoBuffer& aKeyData,
+                       const nsNSSShutDownPreventionLock& /*proofOfLock*/)
+{
+  ScopedSECItem spkiItem(aKeyData.ToSECItem());
+  if (!spkiItem) {
+    return nullptr;
+  }
+
+  ScopedCERTSubjectPublicKeyInfo spki(SECKEY_DecodeDERSubjectPublicKeyInfo(spkiItem.get()));
+  if (!spki) {
+    return nullptr;
+  }
+
+  return SECKEY_ExtractPublicKey(spki.get());
+}
+
+nsresult
+Key::PrivateKeyToPkcs8(SECKEYPrivateKey* aPrivKey,
+                       CryptoBuffer& aRetVal,
+                       const nsNSSShutDownPreventionLock& /*proofOfLock*/)
+{
+  ScopedSECItem pkcs8Item(PK11_ExportDERPrivateKeyInfo(aPrivKey, nullptr));
+  if (!pkcs8Item.get()) {
+    return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+  }
+  aRetVal.Assign(pkcs8Item.get());
+  return NS_OK;
+}
+
+nsresult
+Key::PublicKeyToSpki(SECKEYPublicKey* aPubKey,
+                     CryptoBuffer& aRetVal,
+                     const nsNSSShutDownPreventionLock& /*proofOfLock*/)
+{
+  ScopedSECItem spkiItem(PK11_DEREncodePublicKey(aPubKey));
+  if (!spkiItem.get()) {
+    return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+  }
+
+  aRetVal.Assign(spkiItem.get());
+  return NS_OK;
+}
+
+bool
+Key::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return false;
+  }
+
+  // Write in five pieces
+  // 1. Attributes
+  // 2. Symmetric key as raw (if present)
+  // 3. Private key as pkcs8 (if present)
+  // 4. Public key as spki (if present)
+  // 5. Algorithm in whatever form it chooses
+  CryptoBuffer priv, pub;
+
+  if (mPrivateKey) {
+    Key::PrivateKeyToPkcs8(mPrivateKey, priv, locker);
+  }
+
+  if (mPublicKey) {
+    Key::PublicKeyToSpki(mPublicKey, pub, locker);
+  }
+
+  return JS_WriteUint32Pair(aWriter, mAttributes, 0) &&
+         WriteBuffer(aWriter, mSymKey) &&
+         WriteBuffer(aWriter, priv) &&
+         WriteBuffer(aWriter, pub) &&
+         mAlgorithm->WriteStructuredClone(aWriter);
+}
+
+bool
+Key::ReadStructuredClone(JSStructuredCloneReader* aReader)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return false;
+  }
+
+  uint32_t zero;
+  CryptoBuffer sym, priv, pub;
+  nsRefPtr<KeyAlgorithm> algorithm;
+
+  bool read = JS_ReadUint32Pair(aReader, &mAttributes, &zero) &&
+              ReadBuffer(aReader, sym) &&
+              ReadBuffer(aReader, priv) &&
+              ReadBuffer(aReader, pub) &&
+              (algorithm = KeyAlgorithm::Create(mGlobal, aReader));
+  if (!read) {
+    return false;
+  }
+
+  if (sym.Length() > 0)  {
+    mSymKey = sym;
+  }
+  if (priv.Length() > 0) {
+    mPrivateKey = Key::PrivateKeyFromPkcs8(priv, locker);
+  }
+  if (pub.Length() > 0)  {
+    mPublicKey = Key::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;
+  }
+
+  return true;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/crypto/Key.h
@@ -0,0 +1,171 @@
+/* -*- 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_Key_h
+#define mozilla_dom_Key_h
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+#include "nsIGlobalObject.h"
+#include "nsNSSShutDown.h"
+#include "pk11pub.h"
+#include "keyhi.h"
+#include "ScopedNSSTypes.h"
+#include "mozilla/dom/KeyAlgorithm.h"
+#include "mozilla/dom/CryptoBuffer.h"
+#include "js/StructuredClone.h"
+#include "js/TypeDecls.h"
+
+class nsIGlobalObject;
+
+namespace mozilla {
+namespace dom {
+
+/*
+
+The internal structure of keys is dictated by the need for cloning.
+We store everything besides the key data itself in a 32-bit bitmask,
+with the following structure (byte-aligned for simplicity, in order
+from least to most significant):
+
+Bits  Usage
+0     Extractable
+1-7   [reserved]
+8-15  KeyType
+16-23 KeyUsage
+24-31 [reserved]
+
+In the order of a hex value for a uint32_t
+
+   3                   2                   1                   0
+ 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|~~~~~~~~~~~~~~~|     Usage     |     Type      |~~~~~~~~~~~~~|E|
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Thus, internally, a key has the following fields:
+* uint32_t - flags for extractable, usage, type
+* KeyAlgorithm - the algorithm (which must serialize/deserialize itself)
+* The actual keys (which the Key must serialize)
+
+*/
+
+class Key MOZ_FINAL : public nsISupports,
+                      public nsWrapperCache,
+                      public nsNSSShutDownObject
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Key)
+
+  static const uint32_t CLEAR_EXTRACTABLE = 0xFFFFFFE;
+  static const uint32_t EXTRACTABLE = 0x00000001;
+
+  static const uint32_t CLEAR_TYPE = 0xFFFF00FF;
+  static const uint32_t TYPE_MASK = 0x0000FF00;
+  enum KeyType {
+    UNKNOWN = 0x00000000,
+    SECRET  = 0x00000100,
+    PUBLIC  = 0x00000200,
+    PRIVATE = 0x00000300
+  };
+
+  static const uint32_t CLEAR_USAGES = 0xFF00FFFF;
+  static const uint32_t USAGES_MASK = 0x00FF0000;
+  enum KeyUsage {
+    ENCRYPT    = 0x00010000,
+    DECRYPT    = 0x00020000,
+    SIGN       = 0x00040000,
+    VERIFY     = 0x00080000,
+    DERIVEKEY  = 0x00100000,
+    DERIVEBITS = 0x00200000,
+    WRAPKEY    = 0x00400000,
+    UNWRAPKEY  = 0x00800000
+  };
+
+  Key(nsIGlobalObject* aWindow);
+
+  ~Key();
+
+  nsIGlobalObject* GetParentObject() const
+  {
+    return mGlobal;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  // WebIDL methods
+  void GetType(nsString& aRetVal) const;
+  bool Extractable() 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
+
+  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);
+  void AddUsage(KeyUsage aUsage);
+  bool HasUsage(KeyUsage aUsage);
+  bool HasUsageOtherThan(uint32_t 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
+  // or SECKEY_DestroyPublicKey.
+  const CryptoBuffer& GetSymKey() const;
+  SECKEYPrivateKey* GetPrivateKey() const;
+  SECKEYPublicKey* GetPublicKey() const;
+
+  // For nsNSSShutDownObject
+  virtual void virtualDestroyNSSReference();
+  void destructorSafeDestroyNSSReference();
+
+  // Serialization and deserialization convenience methods
+  // Note:
+  // 1. The inputs aKeyData are non-const only because the NSS import
+  //    functions lack the const modifier.  They should not be modified.
+  // 2. All of the NSS key objects returned need to be freed by the caller.
+  static SECKEYPrivateKey* PrivateKeyFromPkcs8(CryptoBuffer& aKeyData,
+                                               const nsNSSShutDownPreventionLock& /*proofOfLock*/);
+  static nsresult PrivateKeyToPkcs8(SECKEYPrivateKey* aPrivKey,
+                                    CryptoBuffer& aRetVal,
+                                    const nsNSSShutDownPreventionLock& /*proofOfLock*/);
+
+  static SECKEYPublicKey* PublicKeyFromSpki(CryptoBuffer& aKeyData,
+                                            const nsNSSShutDownPreventionLock& /*proofOfLock*/);
+  static nsresult PublicKeyToSpki(SECKEYPublicKey* aPrivKey,
+                                  CryptoBuffer& aRetVal,
+                                  const nsNSSShutDownPreventionLock& /*proofOfLock*/);
+
+  // Structured clone methods use these to clone keys
+  bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const;
+  bool ReadStructuredClone(JSStructuredCloneReader* aReader);
+
+private:
+  nsRefPtr<nsIGlobalObject> mGlobal;
+  uint32_t mAttributes; // see above
+  nsRefPtr<KeyAlgorithm> mAlgorithm;
+
+  // Only one key handle should be set, according to the KeyType
+  CryptoBuffer mSymKey;
+  ScopedSECKEYPrivateKey mPrivateKey;
+  ScopedSECKEYPublicKey mPublicKey;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_Key_h
new file mode 100644
--- /dev/null
+++ b/dom/crypto/KeyAlgorithm.cpp
@@ -0,0 +1,126 @@
+/* -*- 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/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
+  if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
+    mMechanism = CKM_AES_CBC_PAD;
+  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
+    mMechanism = CKM_AES_CTR;
+  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
+    mMechanism = CKM_AES_GCM;
+  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
+    mMechanism = CKM_SHA_1;
+  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA224)) {
+    mMechanism = CKM_SHA224;
+  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
+    mMechanism = CKM_SHA256;
+  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
+    mMechanism = CKM_SHA384;
+  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
+    mMechanism = CKM_SHA512;
+  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) {
+    mMechanism = CKM_RSA_PKCS;
+  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
+    mMechanism = CKM_RSA_PKCS;
+  } else {
+    mMechanism = UNKNOWN_CK_MECHANISM;
+  }
+  // HMAC not handled here, since it requires extra info
+}
+
+KeyAlgorithm::~KeyAlgorithm()
+{
+}
+
+JSObject*
+KeyAlgorithm::WrapObject(JSContext* aCx)
+{
+  return KeyAlgorithmBinding::Wrap(aCx, this);
+}
+
+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_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,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_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 Key;
+
+enum KeyAlgorithmStructuredCloneTags {
+  SCTAG_KEYALG,
+  SCTAG_AESKEYALG,
+  SCTAG_HMACKEYALG,
+  SCTAG_RSAKEYALG,
+  SCTAG_RSAHASHEDKEYALG
+};
+
+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);
+
+  virtual ~KeyAlgorithm();
+
+  nsIGlobalObject* GetParentObject() const
+  {
+    return mGlobal;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  void GetName(nsString& aRetVal) 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:
+  nsRefPtr<nsIGlobalObject> mGlobal;
+  nsString mName;
+  CK_MECHANISM_TYPE mMechanism;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_KeyAlgorithm_h
new file mode 100644
--- /dev/null
+++ b/dom/crypto/RsaHashedKeyAlgorithm.cpp
@@ -0,0 +1,56 @@
+/* -*- 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);
+}
+
+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,51 @@
+/* -*- 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))
+  {}
+
+  ~RsaHashedKeyAlgorithm() {}
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  KeyAlgorithm* Hash() const
+  {
+    return mHash;
+  }
+
+  virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE;
+  static KeyAlgorithm* Create(nsIGlobalObject* aGlobal,
+                              JSStructuredCloneReader* aReader);
+
+private:
+  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,56 @@
+/* -*- 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/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;
+  }
+
+  JSObject* PublicExponent(JSContext* cx) const
+  {
+    TypedArrayCreator<Uint8Array> creator(mPublicExponent);
+    return creator.Create(cx);
+  }
+
+  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
new file mode 100644
--- /dev/null
+++ b/dom/crypto/WebCryptoCommon.h
@@ -0,0 +1,127 @@
+/* -*- 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_WebCryptoCommon_h
+#define mozilla_dom_WebCryptoCommon_h
+
+#include "pk11pub.h"
+#include "nsString.h"
+#include "mozilla/dom/CryptoBuffer.h"
+#include "js/StructuredClone.h"
+
+// WebCrypto algorithm names
+#define WEBCRYPTO_ALG_AES_CBC       "AES-CBC"
+#define WEBCRYPTO_ALG_AES_CTR       "AES-CTR"
+#define WEBCRYPTO_ALG_AES_GCM       "AES-GCM"
+#define WEBCRYPTO_ALG_SHA1          "SHA-1"
+#define WEBCRYPTO_ALG_SHA256        "SHA-256"
+#define WEBCRYPTO_ALG_SHA224        "SHA-224"
+#define WEBCRYPTO_ALG_SHA384        "SHA-384"
+#define WEBCRYPTO_ALG_SHA512        "SHA-512"
+#define WEBCRYPTO_ALG_HMAC          "HMAC"
+#define WEBCRYPTO_ALG_RSAES_PKCS1   "RSAES-PKCS1-v1_5"
+#define WEBCRYPTO_ALG_RSASSA_PKCS1  "RSASSA-PKCS1-v1_5"
+
+// WebCrypto key formats
+#define WEBCRYPTO_KEY_FORMAT_RAW    "raw"
+#define WEBCRYPTO_KEY_FORMAT_PKCS8  "pkcs8"
+#define WEBCRYPTO_KEY_FORMAT_SPKI   "spki"
+#define WEBCRYPTO_KEY_FORMAT_JWK    "jwk"
+
+// WebCrypto key types
+#define WEBCRYPTO_KEY_TYPE_PUBLIC  "public"
+#define WEBCRYPTO_KEY_TYPE_PRIVATE "private"
+#define WEBCRYPTO_KEY_TYPE_SECRET  "secret"
+
+// WebCrypto key usages
+#define WEBCRYPTO_KEY_USAGE_ENCRYPT     "encrypt"
+#define WEBCRYPTO_KEY_USAGE_DECRYPT     "decrypt"
+#define WEBCRYPTO_KEY_USAGE_SIGN        "sign"
+#define WEBCRYPTO_KEY_USAGE_VERIFY      "verify"
+#define WEBCRYPTO_KEY_USAGE_DERIVEKEY   "deriveKey"
+#define WEBCRYPTO_KEY_USAGE_DERIVEBITS  "deriveBits"
+#define WEBCRYPTO_KEY_USAGE_WRAPKEY     "wrapKey"
+#define WEBCRYPTO_KEY_USAGE_UNWRAPKEY   "unwrapKey"
+
+// JWK key types
+#define JWK_TYPE_SYMMETRIC          "oct"
+#define JWK_TYPE_RSA                "RSA"
+#define JWK_TYPE_EC                 "EC"
+
+// JWK algorithms
+#define JWK_ALG_RS1                 "RS1"
+#define JWK_ALG_RS256               "RS256"
+#define JWK_ALG_RS384               "RS384"
+#define JWK_ALG_RS512               "RS512"
+
+// Define an unknown mechanism type
+#define UNKNOWN_CK_MECHANISM        CKM_VENDOR_DEFINED+1
+
+namespace mozilla {
+namespace dom {
+
+// Helper functions for structured cloning
+inline bool
+ReadString(JSStructuredCloneReader* aReader, nsString& aString)
+{
+  bool read;
+  uint32_t nameLength, zero;
+  read = JS_ReadUint32Pair(aReader, &nameLength, &zero);
+  if (!read) {
+    return false;
+  }
+
+  aString.SetCapacity(nameLength+1);
+  size_t charSize = sizeof(nsString::char_type);
+  read = JS_ReadBytes(aReader, (void*) aString.BeginWriting(), nameLength * charSize);
+  aString.SetCharAt('\0', nameLength);
+  if (!read) {
+    return false;
+  }
+
+  return true;
+}
+
+inline bool
+WriteString(JSStructuredCloneWriter* aWriter, const nsString& aString)
+{
+  size_t charSize = sizeof(nsString::char_type);
+  return JS_WriteUint32Pair(aWriter, aString.Length(), 0) &&
+         JS_WriteBytes(aWriter, aString.get(), aString.Length() * charSize);
+}
+
+inline bool
+ReadBuffer(JSStructuredCloneReader* aReader, CryptoBuffer& aBuffer)
+{
+  uint32_t length, zero;
+  bool ret = JS_ReadUint32Pair(aReader, &length, &zero);
+  if (!ret) {
+    return false;
+  }
+
+  if (length > 0) {
+    if (!aBuffer.SetLength(length)) {
+      return false;
+    }
+    ret = JS_ReadBytes(aReader, aBuffer.Elements(), aBuffer.Length());
+  }
+  return ret;
+}
+
+inline bool
+WriteBuffer(JSStructuredCloneWriter* aWriter, const CryptoBuffer& aBuffer)
+{
+  bool ret = JS_WriteUint32Pair(aWriter, aBuffer.Length(), 0);
+  if (ret && aBuffer.Length() > 0) {
+    ret = JS_WriteBytes(aWriter, aBuffer.Elements(), aBuffer.Length());
+  }
+  return ret;
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_WebCryptoCommon_h
new file mode 100644
--- /dev/null
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -0,0 +1,552 @@
+/* -*- 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 "pk11pub.h"
+#include "cryptohi.h"
+#include "ScopedNSSTypes.h"
+
+#include "mozilla/dom/WebCryptoTask.h"
+#include "mozilla/dom/TypedArray.h"
+#include "mozilla/dom/Key.h"
+#include "mozilla/dom/KeyAlgorithm.h"
+#include "mozilla/dom/AesKeyAlgorithm.h"
+#include "mozilla/dom/HmacKeyAlgorithm.h"
+#include "mozilla/dom/RsaKeyAlgorithm.h"
+#include "mozilla/dom/RsaHashedKeyAlgorithm.h"
+#include "mozilla/dom/CryptoBuffer.h"
+#include "mozilla/dom/WebCryptoCommon.h"
+
+namespace mozilla {
+namespace dom {
+
+// Convenience functions for extracting / converting information
+
+class ClearException
+{
+public:
+  ClearException(JSContext* aCx)
+    : mCx(aCx)
+  {}
+
+  ~ClearException()
+  {
+    JS_ClearPendingException(mCx);
+  }
+
+private:
+  JSContext* mCx;
+};
+
+template<class OOS>
+static nsresult
+GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm, nsString& aName)
+{
+  ClearException ce(aCx);
+
+  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) || !alg.mName.WasPassed()) {
+      return NS_ERROR_DOM_SYNTAX_ERR;
+    }
+
+    aName.Assign(alg.mName.Value());
+  }
+
+  return NS_OK;
+}
+
+template<class T, class OOS>
+static nsresult
+Coerce(JSContext* aCx, T& aTarget, const OOS& aAlgorithm)
+{
+  ClearException ce(aCx);
+
+  if (!aAlgorithm.IsObject()) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+
+  JS::RootedValue value(aCx, JS::ObjectValue(*aAlgorithm.GetAsObject()));
+  if (!aTarget.Init(aCx, value)) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+
+  return NS_OK;
+}
+
+
+// Some generic utility classes
+
+class FailureTask : public WebCryptoTask
+{
+public:
+  FailureTask(nsresult rv) {
+    mEarlyRv = rv;
+  }
+};
+
+class ReturnArrayBufferViewTask : public WebCryptoTask
+{
+protected:
+  CryptoBuffer mResult;
+
+private:
+  // Returns mResult as an ArrayBufferView, or an error
+  virtual void Resolve() MOZ_OVERRIDE
+  {
+    TypedArrayCreator<Uint8Array> ret(mResult);
+    mResultPromise->MaybeResolve(ret);
+  }
+};
+
+class ImportKeyTask : public WebCryptoTask
+{
+public:
+  ImportKeyTask(JSContext* aCx,
+      const nsAString& aFormat, const KeyData& aKeyData,
+      const ObjectOrString& aAlgorithm, bool aExtractable,
+      const Sequence<nsString>& aKeyUsages)
+  {
+    // Get the current global object from the context
+    nsIGlobalObject *global = xpc::GetNativeForGlobal(JS::CurrentGlobalOrNull(aCx));
+    if (!global) {
+      mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
+      return;
+    }
+
+    // This stuff pretty much always happens, so we'll do it here
+    mKey = new Key(global);
+    mKey->SetExtractable(aExtractable);
+    mKey->ClearUsages();
+    for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
+      mEarlyRv = mKey->AddUsage(aKeyUsages[i]);
+      if (NS_FAILED(mEarlyRv)) {
+        return;
+      }
+    }
+
+    mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
+    if (NS_FAILED(mEarlyRv)) {
+      mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+      return;
+    }
+  }
+
+protected:
+  nsRefPtr<Key> mKey;
+  nsString mAlgName;
+
+private:
+  virtual void Resolve() MOZ_OVERRIDE
+  {
+    mResultPromise->MaybeResolve(mKey);
+  }
+
+  virtual void Cleanup() MOZ_OVERRIDE
+  {
+    mKey = nullptr;
+  }
+};
+
+
+class ImportSymmetricKeyTask : public ImportKeyTask
+{
+public:
+  ImportSymmetricKeyTask(JSContext* aCx,
+      const nsAString& aFormat, const KeyData& aKeyData,
+      const ObjectOrString& aAlgorithm, bool aExtractable,
+      const Sequence<nsString>& aKeyUsages)
+    : ImportKeyTask(aCx, aFormat, aKeyData, aAlgorithm, aExtractable, aKeyUsages)
+  {
+    if (NS_FAILED(mEarlyRv)) {
+      return;
+    }
+
+    // Import the key data
+    if (aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
+      if (aKeyData.IsArrayBufferView()) {
+        mKeyData.Assign(aKeyData.GetAsArrayBufferView());
+      } else if (aKeyData.IsArrayBuffer()) {
+        mKeyData.Assign(aKeyData.GetAsArrayBuffer());
+      } else {
+        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+        return;
+      }
+    } else if (aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
+      mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+      return;
+    } else {
+      // Invalid key format
+      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+      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) || !params.mHash.WasPassed()) {
+        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+        return;
+      }
+      mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), mHashName);
+      if (NS_FAILED(mEarlyRv)) {
+        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+        return;
+      }
+    }
+  }
+
+  virtual nsresult BeforeCrypto() MOZ_OVERRIDE
+  {
+    // 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)) {
+      if (mKey->HasUsageOtherThan(Key::ENCRYPT | Key::DECRYPT)) {
+        return NS_ERROR_DOM_DATA_ERR;
+      }
+
+      if ( (length != 128) && (length != 192) && (length != 256) ) {
+        return NS_ERROR_DOM_DATA_ERR;
+      }
+      algorithm = new AesKeyAlgorithm(global, mAlgName, length);
+    } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
+      if (mKey->HasUsageOtherThan(Key::SIGN | Key::VERIFY)) {
+        return NS_ERROR_DOM_DATA_ERR;
+      }
+
+      algorithm = new HmacKeyAlgorithm(global, mAlgName, length, mHashName);
+      if (algorithm->Mechanism() == UNKNOWN_CK_MECHANISM) {
+        return NS_ERROR_DOM_SYNTAX_ERR;
+      }
+    } else {
+      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+    }
+
+    mKey->SetAlgorithm(algorithm);
+    mKey->SetSymKey(mKeyData);
+    mKey->SetType(Key::SECRET);
+    mEarlyComplete = true;
+    return NS_OK;
+  }
+
+private:
+  CryptoBuffer mKeyData;
+  nsString mHashName;
+};
+
+class ImportRsaKeyTask : public ImportKeyTask
+{
+public:
+  ImportRsaKeyTask(JSContext* aCx,
+      const nsAString& aFormat, const KeyData& aKeyData,
+      const ObjectOrString& aAlgorithm, bool aExtractable,
+      const Sequence<nsString>& aKeyUsages)
+    : ImportKeyTask(aCx, aFormat, aKeyData, aAlgorithm, aExtractable, aKeyUsages)
+  {
+    if (NS_FAILED(mEarlyRv)) {
+      return;
+    }
+
+    mFormat = aFormat;
+
+    // Import the key data
+    if (aKeyData.IsArrayBufferView()) {
+      mKeyData.Assign(aKeyData.GetAsArrayBufferView());
+    } else if (aKeyData.IsArrayBuffer()) {
+      mKeyData.Assign(aKeyData.GetAsArrayBuffer());
+    } else {
+      // TODO This will need to be changed for JWK (Bug 1005220)
+      mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+      return;
+    }
+
+    // If this is RSA with a hash, cache the hash name
+    if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
+      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;
+      }
+    }
+  }
+
+private:
+  CryptoBuffer mKeyData;
+  nsString mFormat;
+  nsString mHashName;
+  uint32_t mModulusLength;
+  CryptoBuffer mPublicExponent;
+
+  virtual nsresult DoCrypto() MOZ_OVERRIDE
+  {
+    nsNSSShutDownPreventionLock locker;
+
+    // Import the key data itself
+    ScopedSECKEYPublicKey pubKey;
+    if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
+      ScopedSECKEYPrivateKey privKey(Key::PrivateKeyFromPkcs8(mKeyData, locker));
+      if (!privKey.get()) {
+        return NS_ERROR_DOM_DATA_ERR;
+      }
+
+      mKey->SetPrivateKey(privKey.get());
+      mKey->SetType(Key::PRIVATE);
+      pubKey = SECKEY_ConvertToPublicKey(privKey.get());
+      if (!pubKey) {
+        return NS_ERROR_DOM_UNKNOWN_ERR;
+      }
+    } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
+      pubKey = Key::PublicKeyFromSpki(mKeyData, locker);
+      if (!pubKey.get()) {
+        return NS_ERROR_DOM_DATA_ERR;
+      }
+
+      if (pubKey->keyType != rsaKey) {
+        return NS_ERROR_DOM_DATA_ERR;
+      }
+
+      mKey->SetPublicKey(pubKey.get());
+      mKey->SetType(Key::PUBLIC);
+    } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
+      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+    } else {
+      // Invalid key format
+      return NS_ERROR_DOM_SYNTAX_ERR;
+    }
+
+    // Extract relevant information from the public key
+    mModulusLength = 8 * pubKey->u.rsa.modulus.len;
+    mPublicExponent.Assign(&pubKey->u.rsa.publicExponent);
+
+    return NS_OK;
+  }
+
+  virtual nsresult AfterCrypto() MOZ_OVERRIDE
+  {
+    // Construct an appropriate KeyAlgorithm
+    nsIGlobalObject* global = mKey->GetParentObject();
+    if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) {
+      if ((mKey->GetKeyType() == Key::PUBLIC &&
+           mKey->HasUsageOtherThan(Key::ENCRYPT)) ||
+          (mKey->GetKeyType() == Key::PRIVATE &&
+           mKey->HasUsageOtherThan(Key::DECRYPT))) {
+        return NS_ERROR_DOM_DATA_ERR;
+      }
+
+      mKey->SetAlgorithm(new RsaKeyAlgorithm(global, mAlgName, mModulusLength, mPublicExponent));
+    } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
+      if ((mKey->GetKeyType() == Key::PUBLIC &&
+           mKey->HasUsageOtherThan(Key::VERIFY)) ||
+          (mKey->GetKeyType() == Key::PRIVATE &&
+           mKey->HasUsageOtherThan(Key::SIGN))) {
+        return NS_ERROR_DOM_DATA_ERR;
+      }
+
+      nsRefPtr<RsaHashedKeyAlgorithm> algorithm = new RsaHashedKeyAlgorithm(
+                                                          global,
+                                                          mAlgName,
+                                                          mModulusLength,
+                                                          mPublicExponent,
+                                                          mHashName);
+      if (algorithm->Mechanism() == UNKNOWN_CK_MECHANISM) {
+        return NS_ERROR_DOM_SYNTAX_ERR;
+      }
+      mKey->SetAlgorithm(algorithm);
+    }
+
+    return NS_OK;
+  }
+};
+
+
+class UnifiedExportKeyTask : public ReturnArrayBufferViewTask
+{
+public:
+  UnifiedExportKeyTask(const nsAString& aFormat, Key& aKey)
+    : mFormat(aFormat)
+    , mSymKey(aKey.GetSymKey())
+    , mPrivateKey(aKey.GetPrivateKey())
+    , mPublicKey(aKey.GetPublicKey())
+  {
+    if (!aKey.Extractable()) {
+      mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
+      return;
+    }
+  }
+
+
+private:
+  nsString mFormat;
+  CryptoBuffer mSymKey;
+  ScopedSECKEYPrivateKey mPrivateKey;
+  ScopedSECKEYPublicKey mPublicKey;
+
+  virtual void ReleaseNSSResources() MOZ_OVERRIDE
+  {
+    mPrivateKey.dispose();
+    mPublicKey.dispose();
+  }
+
+  virtual nsresult DoCrypto() MOZ_OVERRIDE
+  {
+    nsNSSShutDownPreventionLock locker;
+
+    if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
+      mResult = mSymKey;
+      if (mResult.Length() == 0) {
+        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+      }
+    } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
+      if (!mPrivateKey) {
+        mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+      }
+
+      switch (mPrivateKey->keyType) {
+        case rsaKey:
+          Key::PrivateKeyToPkcs8(mPrivateKey.get(), mResult, locker);
+          mEarlyRv = NS_OK;
+          break;
+        default:
+          mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+      }
+    } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
+      if (!mPublicKey) {
+        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+      }
+
+      mEarlyRv = Key::PublicKeyToSpki(mPublicKey.get(), mResult, locker);
+    } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
+      mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+    } else {
+      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+    }
+
+    return NS_OK;
+  }
+};
+
+
+// Task creation methods for WebCryptoTask
+
+WebCryptoTask*
+WebCryptoTask::EncryptDecryptTask(JSContext* aCx,
+                                  const ObjectOrString& aAlgorithm,
+                                  Key& aKey,
+                                  const CryptoOperationData& aData,
+                                  bool aEncrypt)
+{
+  return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+}
+
+WebCryptoTask*
+WebCryptoTask::SignVerifyTask(JSContext* aCx,
+                              const ObjectOrString& aAlgorithm,
+                              Key& aKey,
+                              const CryptoOperationData& aSignature,
+                              const CryptoOperationData& aData,
+                              bool aSign)
+{
+  return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+}
+
+WebCryptoTask*
+WebCryptoTask::DigestTask(JSContext* aCx,
+                          const ObjectOrString& aAlgorithm,
+                          const CryptoOperationData& aData)
+{
+  return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+}
+
+WebCryptoTask*
+WebCryptoTask::ImportKeyTask(JSContext* aCx,
+                             const nsAString& aFormat,
+                             const KeyData& aKeyData,
+                             const ObjectOrString& aAlgorithm,
+                             bool aExtractable,
+                             const Sequence<nsString>& aKeyUsages)
+{
+  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) ||
+      algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
+    return new ImportSymmetricKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
+                                      aExtractable, aKeyUsages);
+  } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1) ||
+             algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
+    return new ImportRsaKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
+                                aExtractable, aKeyUsages);
+  } else {
+    return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+  }
+}
+
+WebCryptoTask*
+WebCryptoTask::ExportKeyTask(const nsAString& aFormat,
+                             Key& aKey)
+{
+  if (aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
+    return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+  } else {
+    return new UnifiedExportKeyTask(aFormat, aKey);
+  }
+}
+
+WebCryptoTask*
+WebCryptoTask::GenerateKeyTask(JSContext* aCx,
+                               const ObjectOrString& aAlgorithm,
+                               bool aExtractable,
+                               const Sequence<nsString>& aKeyUsages)
+{
+  return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+}
+
+WebCryptoTask*
+WebCryptoTask::DeriveKeyTask(JSContext* aCx,
+                             const ObjectOrString& aAlgorithm,
+                             Key& aBaseKey,
+                             const ObjectOrString& aDerivedKeyType,
+                             bool aExtractable,
+                             const Sequence<nsString>& aKeyUsages)
+{
+  return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+}
+
+WebCryptoTask*
+WebCryptoTask::DeriveBitsTask(JSContext* aCx,
+                              const ObjectOrString& aAlgorithm,
+                              Key& aKey,
+                              uint32_t aLength)
+{
+  return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+}
+
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/crypto/WebCryptoTask.h
@@ -0,0 +1,238 @@
+/* -*- 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_WebCryptoTask_h
+#define mozilla_dom_WebCryptoTask_h
+
+#include "CryptoTask.h"
+
+#include "nsIGlobalObject.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/SubtleCryptoBinding.h"
+#include "mozilla/dom/Key.h"
+
+namespace mozilla {
+namespace dom {
+
+typedef ArrayBufferViewOrArrayBuffer CryptoOperationData;
+typedef ArrayBufferViewOrArrayBuffer KeyData;
+
+/*
+
+The execution of a WebCryptoTask happens in several phases
+
+1. Constructor
+2. BeforeCrypto
+3. CalculateResult -> DoCrypto
+4. AfterCrypto
+5. Resolve or FailWithError
+6. Cleanup
+
+If any of these steps produces an error (setting mEarlyRv), then
+subsequent steps will not proceed.  If the constructor or BeforeCrypto
+sets mEarlyComplete to true, then we will skip step 3, saving the
+thread overhead.
+
+In general, the constructor should handle any parsing steps that
+require JS context, and otherwise just cache information for later
+steps to use.
+
+All steps besides step 3 occur on the main thread, so they should
+avoid blocking operations.
+
+Only step 3 is guarded to ensure that NSS has not been shutdown,
+so all NSS interactions should occur in DoCrypto
+
+Cleanup should execute regardless of what else happens.
+
+*/
+
+#define MAYBE_EARLY_FAIL(rv) \
+if (NS_FAILED(rv)) { \
+  FailWithError(rv); \
+  Skip(); \
+  return; \
+}
+
+class WebCryptoTask : public CryptoTask
+{
+public:
+  virtual void DispatchWithPromise(Promise* aResultPromise)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    mResultPromise = aResultPromise;
+
+    // Fail if an error was set during the constructor
+    MAYBE_EARLY_FAIL(mEarlyRv)
+
+    // Perform pre-NSS operations, and fail if they fail
+    mEarlyRv = BeforeCrypto();
+    MAYBE_EARLY_FAIL(mEarlyRv)
+
+    // Skip NSS if we're already done, or launch a CryptoTask
+    if (mEarlyComplete) {
+      CallCallback(mEarlyRv);
+      Skip();
+      return;
+    }
+
+     mEarlyRv = Dispatch("SubtleCrypto");
+     MAYBE_EARLY_FAIL(mEarlyRv)
+  }
+
+protected:
+  static WebCryptoTask* EncryptDecryptTask(JSContext* aCx,
+                           const ObjectOrString& aAlgorithm,
+                           Key& aKey,
+                           const CryptoOperationData& aData,
+                           bool aEncrypt);
+
+  static WebCryptoTask* SignVerifyTask(JSContext* aCx,
+                          const ObjectOrString& aAlgorithm,
+                          Key& aKey,
+                          const CryptoOperationData& aSignature,
+                          const CryptoOperationData& aData,
+                          bool aSign);
+
+public:
+  static WebCryptoTask* EncryptTask(JSContext* aCx,
+                          const ObjectOrString& aAlgorithm,
+                          Key& aKey,
+                          const CryptoOperationData& aData)
+  {
+    return EncryptDecryptTask(aCx, aAlgorithm, aKey, aData, true);
+  }
+
+  static WebCryptoTask* DecryptTask(JSContext* aCx,
+                          const ObjectOrString& aAlgorithm,
+                          Key& aKey,
+                          const CryptoOperationData& aData)
+  {
+    return EncryptDecryptTask(aCx, aAlgorithm, aKey, aData, false);
+  }
+
+  static WebCryptoTask* SignTask(JSContext* aCx,
+                          const ObjectOrString& aAlgorithm,
+                          Key& aKey,
+                          const CryptoOperationData& aData)
+  {
+    CryptoOperationData dummy;
+    return SignVerifyTask(aCx, aAlgorithm, aKey, dummy, aData, true);
+  }
+
+  static WebCryptoTask* VerifyTask(JSContext* aCx,
+                          const ObjectOrString& aAlgorithm,
+                          Key& aKey,
+                          const CryptoOperationData& aSignature,
+                          const CryptoOperationData& aData)
+  {
+    return SignVerifyTask(aCx, aAlgorithm, aKey, aSignature, aData, false);
+  }
+
+  static WebCryptoTask* DigestTask(JSContext* aCx,
+                          const ObjectOrString& aAlgorithm,
+                          const CryptoOperationData& aData);
+
+  static WebCryptoTask* ImportKeyTask(JSContext* aCx,
+                          const nsAString& aFormat,
+                          const KeyData& aKeyData,
+                          const ObjectOrString& aAlgorithm,
+                          bool aExtractable,
+                          const Sequence<nsString>& aKeyUsages);
+  static WebCryptoTask* ExportKeyTask(const nsAString& aFormat,
+                          Key& aKey);
+  static WebCryptoTask* GenerateKeyTask(JSContext* aCx,
+                          const ObjectOrString& aAlgorithm,
+                          bool aExtractable,
+                          const Sequence<nsString>& aKeyUsages);
+
+  static WebCryptoTask* DeriveKeyTask(JSContext* aCx,
+                          const ObjectOrString& aAlgorithm,
+                          Key& aBaseKey,
+                          const ObjectOrString& aDerivedKeyType,
+                          bool extractable,
+                          const Sequence<nsString>& aKeyUsages);
+  static WebCryptoTask* DeriveBitsTask(JSContext* aCx,
+                          const ObjectOrString& aAlgorithm,
+                          Key& aKey,
+                          uint32_t aLength);
+
+protected:
+  nsRefPtr<Promise> mResultPromise;
+  nsresult mEarlyRv;
+  bool mEarlyComplete;
+
+  WebCryptoTask()
+    : mEarlyRv(NS_OK)
+    , mEarlyComplete(false)
+  {}
+
+  // For things that need to happen on the main thread
+  // either before or after CalculateResult
+  virtual nsresult BeforeCrypto() { return NS_OK; }
+  virtual nsresult DoCrypto() { return NS_OK; }
+  virtual nsresult AfterCrypto() { return NS_OK; }
+  virtual void Resolve() {}
+  virtual void Cleanup() {}
+
+  void FailWithError(nsresult aRv)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    // Blindly convert nsresult to DOMException
+    // Individual tasks must ensure they pass the right values
+    mResultPromise->MaybeReject(aRv);
+    // Manually release mResultPromise while we're on the main thread
+    mResultPromise = nullptr;
+    Cleanup();
+  }
+
+  // Subclasses should override this method if they keep references to
+  // any NSS objects, e.g., SECKEYPrivateKey or PK11SymKey.
+  virtual void ReleaseNSSResources() MOZ_OVERRIDE {}
+
+  virtual nsresult CalculateResult() MOZ_OVERRIDE MOZ_FINAL
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    if (NS_FAILED(mEarlyRv)) {
+      return mEarlyRv;
+    }
+
+    if (isAlreadyShutDown()) {
+      return NS_ERROR_DOM_UNKNOWN_ERR;
+    }
+
+    return DoCrypto();
+  }
+
+  virtual void CallCallback(nsresult rv) MOZ_OVERRIDE MOZ_FINAL
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    if (NS_FAILED(rv)) {
+      FailWithError(rv);
+      return;
+    }
+
+    nsresult rv2 = AfterCrypto();
+    if (NS_FAILED(rv2)) {
+      FailWithError(rv2);
+      return;
+    }
+
+    Resolve();
+
+    // Manually release mResultPromise while we're on the main thread
+    mResultPromise = nullptr;
+    Cleanup();
+  }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_WebCryptoTask_h
new file mode 100644
--- /dev/null
+++ b/dom/crypto/moz.build
@@ -0,0 +1,41 @@
+# -*- 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/.
+
+TEST_DIRS += ['test']
+
+EXPORTS.mozilla.dom += [
+    'AesKeyAlgorithm.h',
+    'CryptoBuffer.h',
+    'HmacKeyAlgorithm.h',
+    'Key.h',
+    'KeyAlgorithm.h',
+    'RsaHashedKeyAlgorithm.h',
+    'RsaKeyAlgorithm.h',
+    'WebCryptoCommon.h',
+    'WebCryptoTask.h',
+]
+
+UNIFIED_SOURCES += [
+    'AesKeyAlgorithm.cpp',
+    'CryptoBuffer.cpp',
+    'HmacKeyAlgorithm.cpp',
+    'Key.cpp',
+    'KeyAlgorithm.cpp',
+    'RsaHashedKeyAlgorithm.cpp',
+    'RsaKeyAlgorithm.cpp',
+    'WebCryptoTask.cpp',
+]
+
+FAIL_ON_WARNINGS = True
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'gklayout'
+
+LOCAL_INCLUDES += [
+    '/security/manager/ssl/src',
+]
+
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/mochitest.ini
@@ -0,0 +1,11 @@
+[DEFAULT]
+# Bug 1010743 - Re-enable WebCrypto tests on b2g-desktop
+skip-if = (buildapp == 'b2g' && toolkit != 'gonk')
+support-files =
+  test-array.js
+  test-vectors.js
+  test_WebCrypto.css
+  tests.js
+  util.js
+
+[test_WebCrypto.html]
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+MOCHITEST_MANIFESTS += ['mochitest.ini']
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/test-array.js
@@ -0,0 +1,182 @@
+/* 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/. */
+
+var MOCHITEST = false;
+
+function Test(name, test) {
+  this.name = name;
+  this.startTime = null;
+  this.endTime = null;
+  this.result = null;
+  this.row = null;
+
+  this.run = function() {
+    // Note the start time
+    this.startTime = new Date();
+    // Run the test
+    try {
+      test.call(this);
+    } catch (e) {
+      console.log(e);
+      console.log(e.stack);
+      this.complete(false);
+    }
+  };
+
+  this.memcmp_complete = function(x, y) {
+    var passfail = util.memcmp(x, y);
+    if (!passfail) {
+      console.log("expected: " + util.abv2hex(x));
+      console.log("   got: " + util.abv2hex(y));
+    }
+    this.complete(passfail);
+  };
+
+  this.complete = function(result) {
+    if (MOCHITEST) { ok(result, this.name); }
+
+    // Note the end time
+    this.endTime = new Date();
+    // Set result
+    this.result = result;
+    // Re-draw the row
+    this.draw();
+    this.next();
+  };
+
+  this.next = function() {
+    if (this.oncomplete) {
+      this.oncomplete();
+    }
+  };
+
+  this.setRow = function(id) {
+    this.row = document.getElementById(id).getElementsByTagName("td");
+  };
+
+  this.draw = function() {
+    if (!this.row) return;
+
+    // Print the name of the test
+    if (this.name) {
+      this.row[0].innerHTML = this.name;
+      var that = this;
+      this.row[0].onclick = function() { that.run(); }
+    } else {
+      this.row[0] = "";
+    }
+
+    // Print the result of the test
+    if (this.result == true) {
+      this.row[1].className = "pass";
+      this.row[1].innerHTML = "PASS";
+    } else if (this.result == false) {
+      this.row[1].className = "fail";
+      this.row[1].innerHTML = "FAIL";
+    } else {
+      //this.row[1].innerHTML = "";
+      this.row[1].innerHTML = this.result;
+    }
+
+    // Print the elapsed time, if known
+    if (this.startTime &&  this.endTime) {
+      this.row[2].innerHTML = (this.endTime - this.startTime) + " ms";
+    } else {
+      this.row[2].innerHTML = "";
+    }
+  };
+}
+
+var TestArray = {
+  tests: [],
+  table: null,
+  passSpan: null,
+  failSpan: null,
+  pendingSpan: null,
+  pass: 0,
+  fail: 0,
+  pending: 0,
+  currTest: 0,
+
+  addTest: function(name, testFn) {
+    // Give it a reference to the array
+    var test = new Test(name, testFn);
+    test.ta = this;
+    // Add test to tests
+    this.tests.push(test);
+  },
+
+  updateSummary: function() {
+    this.pass = this.fail = this.pending = 0;
+    for (var i=0; i<this.tests.length; ++i) {
+      if (this.tests[i].result == true)  this.pass++;
+      if (this.tests[i].result == false) this.fail++;
+      if (this.tests[i].result == null)  this.pending++;
+    }
+    this.passSpan.innerHTML = this.pass;
+    this.failSpan.innerHTML = this.fail;
+    this.pendingSpan.innerHTML = this.pending;
+  },
+
+  load: function() {
+    // Grab reference to table and summary numbers
+    this.table = document.getElementById("results");
+    this.passSpan = document.getElementById("passN");
+    this.failSpan = document.getElementById("failN");
+    this.pendingSpan = document.getElementById("pendingN");
+
+    // Populate everything initially
+    this.updateSummary();
+    for (var i=0; i<this.tests.length; ++i) {
+      var tr = document.createElement("tr");
+      tr.id = "test" + i;
+      tr.appendChild(document.createElement("td"));
+      tr.appendChild(document.createElement("td"));
+      tr.appendChild(document.createElement("td"));
+      this.table.appendChild(tr);
+      this.tests[i].setRow(tr.id);
+      this.tests[i].draw();
+    }
+  },
+
+  run: function() {
+    this.currTest = 0;
+    this.runNextTest();
+  },
+
+  runNextTest: function() {
+    this.updateSummary();
+    var i = this.currTest++;
+    if (i >= this.tests.length) {
+      if (MOCHITEST) { SimpleTest.finish(); }
+      return;
+    }
+
+    var self = this;
+    this.tests[i].oncomplete = function() {
+      self.runNextTest();
+    }
+    this.tests[i].run();
+  }
+}
+
+if (window.addEventListener) {
+  window.addEventListener("load", function() { TestArray.load(); } );
+} else {
+  window.attachEvent("onload", function() { TestArray.load(); } );
+}
+
+function start() {
+  TestArray.run();
+}
+
+MOCHITEST = ("SimpleTest" in window);
+if (MOCHITEST) {
+  SimpleTest.waitForExplicitFinish();
+  window.addEventListener("load", function() {
+    SimpleTest.waitForFocus(function() {
+      SpecialPowers.pushPrefEnv({'set': [["dom.webcrypto.enabled", true]]}, start);
+    });
+  });
+}
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/test-vectors.js
@@ -0,0 +1,41 @@
+/* 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/. */
+
+tv = {
+  raw: util.hex2abv("f3095c4fe5e299477643c2310b44f0aa"),
+
+  // this key had an inappropriate length (18 octets)
+  negative_raw: util.hex2abv("f3095c4fe5e299477643c2310b44f0aabbcc"),
+
+  // openssl genrsa 512 | openssl pkcs8 -topk8 -nocrypt
+  pkcs8: util.hex2abv(
+    "30820154020100300d06092a864886f70d01010105000482013e3082013a0201" +
+    "00024100a240ceb54e70dc14825b587d2f5dfd463c4b8250b696004a1acaafe4" +
+    "9bcf384a46aa9fb4d9c7ee88e9ef0a315f53868f63680b58347249baedd93415" +
+    "16c4cab70203010001024034e6dc7ed0ec8b55448b73f69d1310196e5f5045f0" +
+    "c247a5e1c664432d6a0af7e7da40b83af047dd01f5e0a90e47c224d7b5133a35" +
+    "4d11aa5003b3e8546c9901022100cdb2d7a7435bcb45e50e86f6c14e97ed781f" +
+    "0956cd26e6f75ed9fc88125f8407022100c9ee30af6cb95ac9c1149ed84b3338" +
+    "481741359409f369c497be177d950fb7d10221008b0ef98d611320639b0b6c20" +
+    "4ae4a7fee8f30a6c3cfaacafd4d6c74af228d26702206b0e1dbf935bbd774327" +
+    "2483b572a53f0b1d2643a2f6eab7305fb6627cf9855102203d2263156b324146" +
+    "4478b713eb854c4f6b3ef052f0463b65d8217daec0099834"
+  ),
+
+  // Truncated form of the PKCS#8 stucture above
+  negative_pkcs8: util.hex2abv("30820154020100300d06092a864886f70d010"),
+
+  // Extracted from a cert via http://lapo.it/asn1js/
+  spki: util.hex2abv(
+    "30819F300D06092A864886F70D010101050003818D0030818902818100895309" +
+    "7086EE6147C5F4D5FFAF1B498A3D11EC5518E964DC52126B2614F743883F64CA" +
+    "51377ABB530DFD20464A48BD67CD27E7B29AEC685C5D10825E605C056E4AB8EE" +
+    "A460FA27E55AA62C498B02D7247A249838A12ECDF37C6011CF4F0EDEA9CEE687" +
+    "C1CB4A51C6AE62B2EFDB000723A01C99D6C23F834880BA8B42D5414E6F020301" +
+    "0001"
+  ),
+
+  // Truncated form of the PKCS#8 stucture above
+  negative_spki: util.hex2abv("30819F300D06092A864886F70D010101050003"),
+}
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/test_WebCrypto.css
@@ -0,0 +1,86 @@
+/* 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/. */
+
+body {
+	font-family: Helvetica Neue, Helvetica, Trebuchet MS, Sans-serif;
+	font-size: 12pt;
+	text-align: center;
+}
+
+a {
+	color: #FF9500;
+	text-decoration: none;
+}
+
+a:hover {
+	text-decoration: underline;
+}
+
+#content {
+	width: 50em;
+	margin-left: auto;
+	margin-right: auto;
+	text-align: left;
+}
+
+#head {
+	font-family: Helvetica Neue, Helvetica, Trebuchet MS, Sans-serif;
+	font-size: 300%;
+	font-weight: lighter;
+	padding: .2ex;
+	padding-bottom: 0ex;
+	margin-bottom: .5ex;
+	border-bottom: 10px solid #FF9500;
+}
+#head b {
+	font-weight: bold;
+	color: #FF9500;
+}
+
+div.content {
+	font-family: Helvetica Neue, Helvetica, Trebuchet MS, Sans-serif;
+	color: #000;
+	margin: 2ex;
+}
+
+#foot {
+	border-bottom: 1ex solid #FF9500;
+	margin-top: 2ex;
+}
+
+/*------------------------------------------*/
+
+#start {
+    background: #FF9500;
+    color: #fff;
+    text-align: center;
+    font-weight: bold;
+    padding: 1em 0 1em 0;
+    width: 50em;
+    cursor: pointer;
+}
+
+
+#results {
+    text-align: left;
+    width: 48em;
+    border: 1px solid black;
+}
+
+.pass {
+    font-weight: bold;
+    color: #00539F;
+}
+
+.fail {
+    font-weight: bold;
+    color: #FF9500;
+}
+
+.pending {
+    font-weight: bold;
+    color: #666;
+}
+
+
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/test_WebCrypto.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+<title>WebCrypto Test Suite</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<link rel="stylesheet" href="./test_WebCrypto.css"/>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<!-- Utilities for manipulating ABVs -->
+<script src="util.js"></script>
+
+<!-- A simple wrapper around IndexedDB -->
+<script src="simpledb.js"></script>
+
+<!-- Test vectors drawn from the literature -->
+<script src="./test-vectors.js"></script>
+
+<!-- General testing framework -->
+<script src="./test-array.js"></script>
+
+<!-- Specific test definitions (ADD YOUR TEST HERE) -->
+<script src="./tests.js"></script>
+</head>
+
+<body>
+
+<div id="content">
+	<div id="head">
+		<b>Web</b>Crypto<br>
+	</div>
+
+    <div id="start" onclick="start();">RUN ALL</div>
+
+    <div id="resultDiv" class="content">
+    Summary:
+    <span class="pass"><span id="passN">0</span> passed, </span>
+    <span class="fail"><span id="failN">0</span> failed, </span>
+    <span class="pending"><span id="pendingN">0</span> pending.</span>
+    <br/>
+    <br/>
+
+    <table id="results">
+        <tr>
+            <th>Test</th>
+            <th>Result</th>
+            <th>Time</th>
+        </tr>
+    </table>
+
+    </div>
+
+    <div id="foot"></div>
+</div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/tests.js
@@ -0,0 +1,310 @@
+/* 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/. */
+
+function exists(x) {
+  return (x !== undefined);
+}
+
+function hasKeyFields(x) {
+  return exists(x.algorithm) &&
+         exists(x.extractable) &&
+         exists(x.type) &&
+         exists(x.usages);
+}
+
+function error(test) {
+  return function(x) {
+    console.log("ERROR :: " + x);
+    test.complete(false);
+    throw x;
+  }
+}
+
+function complete(test, valid) {
+  return function(x) {
+    console.log("COMPLETE")
+    console.log(x);
+    if (valid) {
+      test.complete(valid(x));
+    } else {
+      test.complete(true);
+    }
+  }
+}
+
+function memcmp_complete(test, value) {
+  return function(x) {
+    console.log("COMPLETE")
+    console.log(x);
+    test.memcmp_complete(value, x);
+  }
+}
+
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Test for presence of WebCrypto API methods",
+  function() {
+    var that = this;
+    this.complete(
+      exists(window.crypto.subtle) &&
+      exists(window.crypto.subtle.encrypt) &&
+      exists(window.crypto.subtle.decrypt) &&
+      exists(window.crypto.subtle.sign) &&
+      exists(window.crypto.subtle.verify) &&
+      exists(window.crypto.subtle.digest) &&
+      exists(window.crypto.subtle.importKey) &&
+      exists(window.crypto.subtle.exportKey) &&
+      exists(window.crypto.subtle.generateKey) &&
+      exists(window.crypto.subtle.deriveKey) &&
+      exists(window.crypto.subtle.deriveBits)
+    );
+  }
+);
+
+// -----------------------------------------------------------------------------
+
+TestArray.addTest(
+  "Clean failure on a mal-formed algorithm",
+  function() {
+    var that = this;
+    var alg = {
+      get name() {
+        throw "Oh no, no name!";
+      }
+    };
+
+    crypto.subtle.importKey("raw", tv.raw, alg, true, ["encrypt"])
+      .then(
+        error(that),
+        complete(that, function(x) { return true; })
+      );
+  }
+)
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Import / export round-trip with 'raw'",
+  function() {
+    var that = this;
+    var alg = "AES-GCM";
+
+    function doExport(x) {
+      if (!hasKeyFields(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, error(that))
+      .then(
+        memcmp_complete(that, tv.raw),
+        error(that)
+      );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Import failure with format 'raw'",
+  function() {
+    var that = this;
+    var alg = "AES-GCM";
+
+    crypto.subtle.importKey("raw", tv.negative_raw, alg, true, ["encrypt"])
+      .then(error(that), complete(that));
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Proper handling of an ABV representing part of a buffer",
+  function() {
+    var that = this;
+    var alg = "AES-GCM";
+
+    var u8 = new Uint8Array([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                             0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+                             0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+                             0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f]);
+    var u32 = new Uint32Array(u8.buffer, 8, 4);
+    var out = u8.subarray(8, 24)
+
+    function doExport(x) {
+      return crypto.subtle.exportKey("raw", x);
+    }
+
+    crypto.subtle.importKey("raw", u32, alg, true, ["encrypt"])
+      .then(doExport, error(that))
+      .then(memcmp_complete(that, out), error(that));
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Import / export round-trip with 'pkcs8'",
+  function() {
+    var that = this;
+    var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA1" };
+
+    function doExport(x) {
+      if (!hasKeyFields(x)) {
+        throw "Invalid key; missing field(s)";
+      } else if ((x.algorithm.name != alg.name) ||
+        (x.algorithm.hash.name != alg.hash) ||
+        (x.algorithm.modulusLength != 512) ||
+        (x.algorithm.publicExponent.byteLength != 3) ||
+        (x.type != "private") ||
+        (!x.extractable) ||
+        (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, error(that))
+      .then(
+        memcmp_complete(that, tv.pkcs8),
+        error(that)
+      );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Import failure with format 'pkcs8'",
+  function() {
+    var that = this;
+    var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA1" };
+
+    crypto.subtle.importKey("pkcs8", tv.negative_pkcs8, alg, true, ["encrypt"])
+      .then(error(that), complete(that));
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Import / export round-trip with 'spki'",
+  function() {
+    var that = this;
+    var alg = "RSAES-PKCS1-v1_5";
+
+    function doExport(x) {
+      if (!hasKeyFields(x)) {
+        throw "Invalid key; missing field(s)";
+      } 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] != 'encrypt')){
+        throw "Invalid key: incorrect key data";
+      }
+      return crypto.subtle.exportKey("spki", x);
+    }
+
+    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 = "RSAES-PKCS1-v1_5";
+
+    crypto.subtle.importKey("spki", tv.negative_spki, alg, true, ["encrypt"])
+      .then(error(that), complete(that));
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Refuse to export non-extractable key",
+  function() {
+    var that = this;
+    var alg = "AES-GCM";
+
+    function doExport(x) {
+      return crypto.subtle.exportKey("raw", x);
+    }
+
+    crypto.subtle.importKey("raw", tv.raw, alg, false, ["encrypt"])
+      .then(doExport, error(that))
+      .then(
+        error(that),
+        complete(that)
+      );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "IndexedDB store / retrieve round-trip",
+  function() {
+    var that = this;
+    var alg = "AES-GCM";
+    var importedKey;
+    var dbname = "keyDB";
+    var dbstore = "keystore";
+    var dbversion = 1;
+    var dbkey = 0;
+
+    function doIndexedDB(x) {
+      importedKey = x;
+      var req = indexedDB.deleteDatabase(dbname);
+      req.onerror = error(that);
+      req.onsuccess = doCreateDB;
+    }
+
+    function doCreateDB() {
+      var req = indexedDB.open(dbname, dbversion);
+      req.onerror = error(that);
+      req.onupgradeneeded = function(e) {
+        db = e.target.result;
+        db.createObjectStore(dbstore, {keyPath: "id"});
+      }
+
+      req.onsuccess = doPut;
+    }
+
+    function doPut() {
+      req = db.transaction([dbstore], "readwrite")
+              .objectStore(dbstore)
+              .add({id: dbkey, val: importedKey});
+      req.onerror = error(that);
+      req.onsuccess = doGet;
+    }
+
+    function doGet() {
+      req = db.transaction([dbstore], "readwrite")
+              .objectStore(dbstore)
+              .get(dbkey);
+      req.onerror = error(that);
+      req.onsuccess = complete(that, function(e) {
+        return hasKeyFields(e.target.result.val);
+      });
+    }
+
+    crypto.subtle.importKey("raw", tv.raw, alg, false, ['encrypt'])
+      .then(doIndexedDB, error(that));
+  }
+);
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/util.js
@@ -0,0 +1,45 @@
+/* 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/. */
+
+var util = {
+  // Compare the contents of two ArrayBufferViews
+  memcmp: function util_memcmp(x, y) {
+    if (!x || !y) { return false; }
+
+    if (x.byteLength !== y.byteLength) { return false; }
+
+    var xb = new Uint8Array(x.buffer, x.byteOffset, x.byteLength);
+    var yb = new Uint8Array(y.buffer, y.byteOffset, y.byteLength);
+    for (var i=0; i<xb.byteLength; ++i) {
+      if (xb[i] !== yb[i]) {
+        return false;
+      }
+    }
+    return true;
+  },
+
+  // Convert an ArrayBufferView to a hex string
+  abv2hex: function util_abv2hex(abv) {
+    var b = new Uint8Array(abv.buffer, abv.byteOffset, abv.byteLength);
+    var hex = "";
+    for (var i=0; i <b.length; ++i) {
+      var zeropad = (b[i] < 0x10) ? "0" : "";
+      hex += zeropad + b[i].toString(16);
+    }
+    return hex;
+  },
+
+  // Convert a hex string to an ArrayBufferView
+  hex2abv: function util_hex2abv(hex) {
+    if (hex.length % 2 !== 0) {
+      hex = "0" + hex;
+    }
+
+    var abv = new Uint8Array(hex.length / 2);
+    for (var i=0; i<abv.length; ++i) {
+      abv[i] = parseInt(hex.substr(2*i, 2), 16);
+    }
+    return abv;
+  },
+};
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -40,16 +40,17 @@ PARALLEL_DIRS += [
     'base',
     'activities',
     'archivereader',
     'bindings',
     'battery',
     'bluetooth',
     'browser-element',
     'contacts',
+    'crypto',
     'phonenumberutils',
     'alarm',
     'datastore',
     'devicestorage',
     'encoding',
     'events',
     'filehandle',
     'filesystem',
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -565,16 +565,18 @@ var interfaceNamesInGlobalScope =
     "Image",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ImageData",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "InputEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "InstallTrigger", b2g: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "Key", pref: "dom.webcrypto.enabled"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "KeyEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "KeyboardEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "LocalMediaStream",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Location",
 // IMPORTANT: Do not change this list without review from a DOM peer!
@@ -843,16 +845,18 @@ var interfaceNamesInGlobalScope =
     "Storage",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "StorageEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "StyleSheet",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "StyleSheetList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "SubtleCrypto", pref: "dom.webcrypto.enabled"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "SVGAElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SVGAltGlyphElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SVGAngle",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SVGAnimatedAngle",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/webidl/Crypto.webidl
+++ b/dom/webidl/Crypto.webidl
@@ -11,16 +11,18 @@
 interface RandomSource {
   [Throws]
   ArrayBufferView getRandomValues(ArrayBufferView array);
 };
 
 Crypto implements RandomSource;
 
 interface Crypto {
+  [Pref="dom.webcrypto.enabled"]
+  readonly attribute SubtleCrypto subtle;
 };
 
 #ifndef MOZ_DISABLE_CRYPTOLEGACY
 [NoInterfaceObject]
 interface CryptoLegacy {
   readonly attribute DOMString version;
 
   [SetterThrows]
new file mode 100644
--- /dev/null
+++ b/dom/webidl/SubtleCrypto.webidl
@@ -0,0 +1,153 @@
+/* -*- 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/
+ */
+
+typedef DOMString KeyType;
+typedef DOMString KeyUsage;
+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;
+  readonly attribute BigInteger publicExponent;
+};
+
+[NoInterfaceObject]
+interface RsaHashedKeyAlgorithm : RsaKeyAlgorithm {
+  readonly attribute KeyAlgorithm hash;
+};
+
+
+/***** Algorithm dictionaries *****/
+
+dictionary Algorithm {
+  DOMString name;
+};
+
+dictionary AesCbcParams : Algorithm {
+  CryptoOperationData iv;
+};
+
+dictionary AesCtrParams : Algorithm {
+  CryptoOperationData counter;
+  [EnforceRange] octet length;
+};
+
+dictionary AesGcmParams : Algorithm {
+  CryptoOperationData iv;
+  CryptoOperationData additionalData;
+  [EnforceRange] octet tagLength;
+};
+
+dictionary HmacImportParams : Algorithm {
+  AlgorithmIdentifier hash;
+};
+
+dictionary RsaHashedImportParams {
+  AlgorithmIdentifier hash;
+};
+
+dictionary AesKeyGenParams : Algorithm {
+  [EnforceRange] unsigned short length;
+};
+
+dictionary HmacKeyGenParams : Algorithm {
+  AlgorithmIdentifier hash;
+  [EnforceRange] unsigned long length;
+};
+
+dictionary RsaKeyGenParams : Algorithm {
+  [EnforceRange] unsigned long modulusLength;
+  BigInteger publicExponent;
+};
+
+dictionary RsaHashedKeyGenParams : RsaKeyGenParams {
+  AlgorithmIdentifier hash;
+};
+
+dictionary DhKeyGenParams : Algorithm {
+  BigInteger prime;
+  BigInteger generator;
+};
+
+typedef DOMString NamedCurve;
+dictionary EcKeyGenParams : Algorithm {
+  NamedCurve namedCurve;
+};
+
+/***** The Main API *****/
+
+[Pref="dom.webcrypto.enabled"]
+interface Key {
+  readonly attribute KeyType type;
+  readonly attribute boolean extractable;
+  readonly attribute KeyAlgorithm algorithm;
+  [Cached, Constant, Frozen] readonly attribute sequence<KeyUsage> usages;
+};
+
+typedef DOMString KeyFormat;
+typedef (ArrayBufferView or ArrayBuffer) CryptoOperationData;
+typedef (ArrayBufferView or ArrayBuffer) KeyData;
+typedef (object or DOMString) AlgorithmIdentifier;
+
+[Pref="dom.webcrypto.enabled"]
+interface SubtleCrypto {
+  Promise encrypt(AlgorithmIdentifier algorithm,
+                  Key key,
+                  CryptoOperationData data);
+  Promise decrypt(AlgorithmIdentifier algorithm,
+                  Key key,
+                  CryptoOperationData data);
+  Promise sign(AlgorithmIdentifier algorithm,
+               Key key,
+               CryptoOperationData data);
+  Promise verify(AlgorithmIdentifier algorithm,
+                 Key key,
+                 CryptoOperationData signature,
+                 CryptoOperationData data);
+  Promise digest(AlgorithmIdentifier algorithm,
+                 CryptoOperationData data);
+
+  Promise generateKey(AlgorithmIdentifier algorithm,
+                      boolean extractable,
+                      sequence<KeyUsage> keyUsages );
+  Promise deriveKey(AlgorithmIdentifier algorithm,
+                    Key baseKey,
+                    AlgorithmIdentifier derivedKeyType,
+                    boolean extractable,
+                    sequence<KeyUsage> keyUsages );
+  Promise deriveBits(AlgorithmIdentifier algorithm,
+                     Key baseKey,
+                     unsigned long length);
+
+  Promise importKey(KeyFormat format,
+                    KeyData keyData,
+                    AlgorithmIdentifier algorithm,
+                    boolean extractable,
+                    sequence<KeyUsage> keyUsages );
+  Promise exportKey(KeyFormat format, Key key);
+};
+
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -311,16 +311,17 @@ WEBIDL_FILES = [
     'SharedWorker.webidl',
     'SharedWorkerGlobalScope.webidl',
     'SimpleGestureEvent.webidl',
     'SourceBuffer.webidl',
     'SourceBufferList.webidl',
     'StorageType.webidl',
     'StyleSheet.webidl',
     'StyleSheetList.webidl',
+    'SubtleCrypto.webidl',
     'SVGAElement.webidl',
     'SVGAltGlyphElement.webidl',
     'SVGAngle.webidl',
     'SVGAnimatedAngle.webidl',
     'SVGAnimatedBoolean.webidl',
     'SVGAnimatedEnumeration.webidl',
     'SVGAnimatedInteger.webidl',
     'SVGAnimatedLength.webidl',
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -116,16 +116,23 @@ pref("dom.enable_resource_timing", false
 // Whether the Gamepad API is enabled
 pref("dom.gamepad.enabled", true);
 #ifdef RELEASE_BUILD
 pref("dom.gamepad.non_standard_events.enabled", false);
 #else
 pref("dom.gamepad.non_standard_events.enabled", true);
 #endif
 
+// Whether the WebCrypto API is enabled
+#ifdef RELEASE_BUILD
+pref("dom.webcrypto.enabled", false);
+#else
+pref("dom.webcrypto.enabled", true);
+#endif
+
 // Whether the UndoManager API is enabled
 pref("dom.undo_manager.enabled", false);
 
 // Fastback caching - if this pref is negative, then we calculate the number
 // of content viewers to cache based on the amount of available memory.
 pref("browser.sessionhistory.max_total_viewers", -1);
 
 pref("ui.use_native_colors", true);
--- a/xpcom/base/ErrorList.h
+++ b/xpcom/base/ErrorList.h
@@ -482,16 +482,20 @@
   /* XXX Should be JavaScript native errors */
   ERROR(NS_ERROR_TYPE_ERR,                         FAILURE(26)),
   ERROR(NS_ERROR_RANGE_ERR,                        FAILURE(27)),
   /* StringEncoding API errors from http://wiki.whatwg.org/wiki/StringEncoding */
   ERROR(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR,   FAILURE(28)),
   ERROR(NS_ERROR_DOM_ENCODING_NOT_UTF_ERR,         FAILURE(29)),
   ERROR(NS_ERROR_DOM_ENCODING_DECODE_ERR,          FAILURE(30)),
   ERROR(NS_ERROR_DOM_INVALID_POINTER_ERR,          FAILURE(31)),
+  /* WebCrypto API errors from http://www.w3.org/TR/WebCryptoAPI/ */
+  ERROR(NS_ERROR_DOM_UNKNOWN_ERR,                  FAILURE(32)),
+  ERROR(NS_ERROR_DOM_DATA_ERR,                     FAILURE(33)),
+  ERROR(NS_ERROR_DOM_OPERATION_ERR,                FAILURE(34)),
   /* DOM error codes defined by us */
   ERROR(NS_ERROR_DOM_SECMAN_ERR,                   FAILURE(1001)),
   ERROR(NS_ERROR_DOM_WRONG_TYPE_ERR,               FAILURE(1002)),
   ERROR(NS_ERROR_DOM_NOT_OBJECT_ERR,               FAILURE(1003)),
   ERROR(NS_ERROR_DOM_NOT_XPC_OBJECT_ERR,           FAILURE(1004)),
   ERROR(NS_ERROR_DOM_NOT_NUMBER_ERR,               FAILURE(1005)),
   ERROR(NS_ERROR_DOM_NOT_BOOLEAN_ERR,              FAILURE(1006)),
   ERROR(NS_ERROR_DOM_NOT_FUNCTION_ERR,             FAILURE(1007)),