Bug 1034855 - Implement deriveKey() for ECDH r=rbarnes
authorTim Taubert <ttaubert@mozilla.com>
Tue, 29 Jul 2014 12:37:06 +0200
changeset 219797 46fd409366ca5fecbb0f337ecb0606a23f84b062
parent 219796 9e38ab4b2b00208dfe64299644fe979e95c35029
child 219798 d0c7741f70b6b4b78908b1357dc2aa2d1f3e1acf
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrbarnes
bugs1034855
milestone34.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 1034855 - Implement deriveKey() for ECDH r=rbarnes
dom/crypto/WebCryptoTask.cpp
dom/crypto/test/tests.js
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -2322,43 +2322,44 @@ private:
     // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
     // just refers to a buffer managed by symKey. The assignment copies the
     // data, so mResult manages one copy, while symKey manages another.
     ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey));
     return NS_OK;
   }
 };
 
-class DerivePbkdfKeyTask : public DerivePbkdfBitsTask
+template<class DeriveBitsTask>
+class DeriveKeyTask : public DeriveBitsTask
 {
 public:
-  DerivePbkdfKeyTask(JSContext* aCx,
-                     const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey,
-                     const ObjectOrString& aDerivedKeyType, bool aExtractable,
-                     const Sequence<nsString>& aKeyUsages)
-    : DerivePbkdfBitsTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType)
+  DeriveKeyTask(JSContext* aCx,
+                const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey,
+                const ObjectOrString& aDerivedKeyType, bool aExtractable,
+                const Sequence<nsString>& aKeyUsages)
+    : DeriveBitsTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType)
     , mResolved(false)
   {
-    if (NS_FAILED(mEarlyRv)) {
+    if (NS_FAILED(this->mEarlyRv)) {
       return;
     }
 
     NS_NAMED_LITERAL_STRING(format, WEBCRYPTO_KEY_FORMAT_RAW);
     mTask = new ImportSymmetricKeyTask(aCx, format, aDerivedKeyType,
                                        aExtractable, aKeyUsages);
   }
 
 protected:
   nsRefPtr<ImportSymmetricKeyTask> mTask;
   bool mResolved;
 
 private:
   virtual void Resolve() MOZ_OVERRIDE {
-    mTask->SetKeyData(mResult);
-    mTask->DispatchWithPromise(mResultPromise);
+    mTask->SetKeyData(this->mResult);
+    mTask->DispatchWithPromise(this->mResultPromise);
     mResolved = true;
   }
 
   virtual void Cleanup() MOZ_OVERRIDE
   {
     if (mTask && !mResolved) {
       mTask->Skip();
     }
@@ -2732,18 +2733,25 @@ WebCryptoTask::CreateDeriveKeyTask(JSCon
 
   nsString algName;
   nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
   if (NS_FAILED(rv)) {
     return new FailureTask(rv);
   }
 
   if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
-    return new DerivePbkdfKeyTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType,
-                                  aExtractable, aKeyUsages);
+    return new DeriveKeyTask<DerivePbkdfBitsTask>(aCx, aAlgorithm, aBaseKey,
+                                                  aDerivedKeyType, aExtractable,
+                                                  aKeyUsages);
+  }
+
+  if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
+    return new DeriveKeyTask<DeriveEcdhBitsTask>(aCx, aAlgorithm, aBaseKey,
+                                                 aDerivedKeyType, aExtractable,
+                                                 aKeyUsages);
   }
 
   return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
 
 WebCryptoTask*
 WebCryptoTask::CreateDeriveBitsTask(JSContext* aCx,
                               const ObjectOrString& aAlgorithm,
--- a/dom/crypto/test/tests.js
+++ b/dom/crypto/test/tests.js
@@ -2138,8 +2138,56 @@ TestArray.addTest(
 
     doTryImport(tvs.jwk_bad_crv)()
       .then(error(that), doTryImport(tvs.jwk_missing_crv))
       .then(error(that), doTryImport(tvs.jwk_missing_x))
       .then(error(that), doTryImport(tvs.jwk_missing_y))
       .then(error(that), complete(that));
   }
 );
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Derive an HMAC key from two ECDH keys and test sign/verify",
+  function() {
+    var that = this;
+    var alg = { name: "ECDH" };
+    var algDerived = { name: "HMAC", hash: {name: "SHA-1"} };
+
+    var pubKey, privKey;
+    function setPub(x) { pubKey = x; }
+    function setPriv(x) { privKey = x; }
+
+    function doDerive() {
+      var alg = { name: "ECDH", public: pubKey };
+      return crypto.subtle.deriveKey(alg, privKey, algDerived, false, ["sign", "verify"])
+        .then(function (x) {
+          if (!hasKeyFields(x)) {
+            throw "Invalid key; missing field(s)";
+          }
+
+          // 512 bit is the default for HMAC-SHA1.
+          if (x.algorithm.length != 512) {
+            throw "Invalid key; incorrect length";
+          }
+
+          return x;
+        });
+    }
+
+    function doSignAndVerify(x) {
+      var data = crypto.getRandomValues(new Uint8Array(1024));
+      return crypto.subtle.sign("HMAC", x, data)
+        .then(function (sig) {
+          return crypto.subtle.verify("HMAC", x, sig, data);
+        });
+    }
+
+    Promise.all([
+      crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_priv, alg, false, ["deriveBits"])
+        .then(setPriv, error(that)),
+      crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_pub, alg, false, ["deriveBits"])
+        .then(setPub, error(that))
+    ]).then(doDerive, error(that))
+      .then(doSignAndVerify, error(that))
+      .then(complete(that), error(that));
+  }
+);