Bug 1034856 - Implement generateKey() for DH. r=rbarnes, a=lsblakk
authorTim Taubert <ttaubert@mozilla.com>
Sun, 20 Jul 2014 06:38:44 +0200
changeset 225798 5ec58576edf397e74f67df24f0a73ee2cbb01058
parent 225797 af5fea2deedb7fef5b3d7e59dc03c40368c28e54
child 225799 4db14b0721a27be97e216f2f00a2b642b71e2c1c
push id7187
push userryanvm@gmail.com
push dateFri, 31 Oct 2014 15:59:42 +0000
treeherdermozilla-aurora@272d21e92203 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrbarnes, lsblakk
bugs1034856
milestone35.0a2
Bug 1034856 - Implement generateKey() for DH. r=rbarnes, a=lsblakk
dom/crypto/CryptoBuffer.cpp
dom/crypto/CryptoBuffer.h
dom/crypto/WebCryptoTask.cpp
dom/crypto/test/mochitest.ini
dom/crypto/test/test-vectors.js
dom/crypto/test/test_WebCrypto_DH.html
--- a/dom/crypto/CryptoBuffer.cpp
+++ b/dom/crypto/CryptoBuffer.cpp
@@ -156,16 +156,30 @@ CryptoBuffer::ToSECItem() const
   item->data = data;
   item->len = Length();
 
   memcpy(item->data, Elements(), Length());
 
   return item;
 }
 
+bool
+CryptoBuffer::ToSECItem(PLArenaPool *aArena, SECItem* aItem) const
+{
+  aItem->type = siBuffer;
+  aItem->data = nullptr;
+
+  if (!::SECITEM_AllocItem(aArena, aItem, Length())) {
+    return false;
+  }
+
+  memcpy(aItem->data, Elements(), Length());
+  return true;
+}
+
 JSObject*
 CryptoBuffer::ToUint8Array(JSContext* aCx) const
 {
   return Uint8Array::Create(aCx, Length(), Elements());
 }
 
 
 // "BigInt" comes from the WebCrypto spec
--- a/dom/crypto/CryptoBuffer.h
+++ b/dom/crypto/CryptoBuffer.h
@@ -35,16 +35,17 @@ public:
   {
     aArray.ComputeLengthAndData();
     return Assign(aArray.Data(), aArray.Length());
   }
 
   nsresult FromJwkBase64(const nsString& aBase64);
   nsresult ToJwkBase64(nsString& aBase64);
   SECItem* ToSECItem() const;
+  bool ToSECItem(PLArenaPool* aArena, SECItem* aItem) const;
   JSObject* ToUint8Array(JSContext* aCx) const;
 
   bool GetBigIntValue(unsigned long& aRetVal);
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -2003,16 +2003,22 @@ public:
       const Sequence<nsString>& aKeyUsages)
   {
     nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
     if (!global) {
       mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
       return;
     }
 
+    mArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    if (!mArena) {
+      mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
+      return;
+    }
+
     // Create an empty key and set easy attributes
     mKeyPair.mPrivateKey = new CryptoKey(global);
     mKeyPair.mPublicKey  = new CryptoKey(global);
 
     // Extract algorithm name
     nsString algName;
     mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
     if (NS_FAILED(mEarlyRv)) {
@@ -2073,30 +2079,56 @@ public:
         mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
         return;
       }
 
       // Create algorithm.
       mKeyPair.mPublicKey.get()->Algorithm().MakeEc(algName, mNamedCurve);
       mKeyPair.mPrivateKey.get()->Algorithm().MakeEc(algName, mNamedCurve);
       mMechanism = CKM_EC_KEY_PAIR_GEN;
+    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_DH)) {
+      RootedDictionary<DhKeyGenParams> params(aCx);
+      mEarlyRv = Coerce(aCx, params, aAlgorithm);
+      if (NS_FAILED(mEarlyRv)) {
+        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+        return;
+      }
+
+      CryptoBuffer prime;
+      ATTEMPT_BUFFER_INIT(prime, params.mPrime);
+
+      CryptoBuffer generator;
+      ATTEMPT_BUFFER_INIT(generator, params.mGenerator);
+
+      // Set up params.
+      if (!prime.ToSECItem(mArena, &mDhParams.prime) ||
+          !generator.ToSECItem(mArena, &mDhParams.base)) {
+        mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
+        return;
+      }
+
+      // Create algorithm.
+      mKeyPair.mPublicKey.get()->Algorithm().MakeDh(algName, prime, generator);
+      mKeyPair.mPrivateKey.get()->Algorithm().MakeDh(algName, prime, generator);
+      mMechanism = CKM_DH_PKCS_KEY_PAIR_GEN;
     } else {
       mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
       return;
     }
 
     // Set key usages.
     if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
         algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
       privateAllowedUsages = CryptoKey::SIGN;
       publicAllowedUsages = CryptoKey::VERIFY;
     } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
       privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
       publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
-    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
+    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
+               algName.EqualsLiteral(WEBCRYPTO_ALG_DH)) {
       privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
       publicAllowedUsages = 0;
     }
 
     mKeyPair.mPrivateKey.get()->SetExtractable(aExtractable);
     mKeyPair.mPrivateKey.get()->SetType(CryptoKey::PRIVATE);
 
     mKeyPair.mPublicKey.get()->SetExtractable(true);
@@ -2122,48 +2154,46 @@ public:
     if (!mKeyPair.mPublicKey.get()->HasAnyUsage() &&
         !mKeyPair.mPrivateKey.get()->HasAnyUsage()) {
       mEarlyRv = NS_ERROR_DOM_DATA_ERR;
       return;
     }
   }
 
 private:
+  ScopedPLArenaPool mArena;
   CryptoKeyPair mKeyPair;
   CK_MECHANISM_TYPE mMechanism;
   PK11RSAGenParams mRsaParams;
+  SECKEYDHParams mDhParams;
   ScopedSECKEYPublicKey mPublicKey;
   ScopedSECKEYPrivateKey mPrivateKey;
   nsString mNamedCurve;
 
   virtual void ReleaseNSSResources() MOZ_OVERRIDE
   {
     mPublicKey.dispose();
     mPrivateKey.dispose();
   }
 
   virtual nsresult DoCrypto() MOZ_OVERRIDE
   {
     ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
     MOZ_ASSERT(slot.get());
 
     void* param;
-    ScopedPLArenaPool arena;
-
     switch (mMechanism) {
       case CKM_RSA_PKCS_KEY_PAIR_GEN:
         param = &mRsaParams;
         break;
+      case CKM_DH_PKCS_KEY_PAIR_GEN:
+        param = &mDhParams;
+        break;
       case CKM_EC_KEY_PAIR_GEN: {
-        arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-        if (!arena) {
-          return NS_ERROR_DOM_UNKNOWN_ERR;
-        }
-
-        param = CreateECParamsForCurve(mNamedCurve, arena.get());
+        param = CreateECParamsForCurve(mNamedCurve, mArena);
         if (!param) {
           return NS_ERROR_DOM_UNKNOWN_ERR;
         }
         break;
       }
       default:
         return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
     }
@@ -2763,17 +2793,18 @@ WebCryptoTask::CreateGenerateKeyTask(JSC
       algName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
       algName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
       algName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
       algName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
     return new GenerateSymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages);
   } else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
              algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
              algName.EqualsASCII(WEBCRYPTO_ALG_ECDH) ||
-             algName.EqualsASCII(WEBCRYPTO_ALG_ECDSA)) {
+             algName.EqualsASCII(WEBCRYPTO_ALG_ECDSA) ||
+             algName.EqualsASCII(WEBCRYPTO_ALG_DH)) {
     return new GenerateAsymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages);
   } else {
     return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   }
 }
 
 WebCryptoTask*
 WebCryptoTask::CreateDeriveKeyTask(JSContext* aCx,
--- a/dom/crypto/test/mochitest.ini
+++ b/dom/crypto/test/mochitest.ini
@@ -3,12 +3,13 @@
 skip-if = (buildapp == 'b2g')
 support-files =
   test-array.js
   test-vectors.js
   test_WebCrypto.css
   util.js
 
 [test_WebCrypto.html]
+[test_WebCrypto_DH.html]
 [test_WebCrypto_ECDH.html]
 [test_WebCrypto_JWK.html]
 [test_WebCrypto_PBKDF2.html]
 [test_WebCrypto_RSA_OAEP.html]
--- a/dom/crypto/test/test-vectors.js
+++ b/dom/crypto/test/test-vectors.js
@@ -652,10 +652,19 @@ tv = {
     pub_jwk: {
       "kty": "EC",
       "crv": "P-521",
       "x": "BhOH_WuVkU6IX5Eu37tfsnRlUCfyFsQJHKg-GTNnQP2B" +
            "rt_gR_UbQr32gWESEBPg1VsRehTkMD-SbI3rt3p_2q0B",
       "y": "AUNouOdGgHsraPNhXNeNdhpGTd15GPyN9R0iWWL98ePc" +
            "JD4mUQD/DsEzNZ4zLkTdSa/Y5fOP6GEzVzQy0zwC+goD"
     }
-  }
+  },
+
+  // RFC 2409 <http://tools.ietf.org/html/rfc2409#section-6.1>
+  dh: {
+    prime: util.hex2abv(
+      "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74" +
+      "020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f1437" +
+      "4fe1356d6d51c245e485b576625e7ec6f44c42e9a63a3620ffffffffffffffff"
+    )
+  },
 }
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/test_WebCrypto_DH.html
@@ -0,0 +1,92 @@
+<!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>
+
+<script>/*<![CDATA[*/
+"use strict";
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Generate a DH key",
+  function() {
+    var that = this;
+    var alg = {
+      name: "DH",
+      prime: tv.dh.prime,
+      generator: new Uint8Array([0x02])
+    };
+    crypto.subtle.generateKey(alg, false, ["deriveKey", "deriveBits"]).then(
+      complete(that, function(x) {
+        return exists(x.publicKey) &&
+               (x.publicKey.algorithm.name == alg.name) &&
+               util.memcmp(x.publicKey.algorithm.prime, alg.prime) &&
+               util.memcmp(x.publicKey.algorithm.generator, alg.generator) &&
+               (x.publicKey.type == "public") &&
+               x.publicKey.extractable &&
+               (x.publicKey.usages.length == 0) &&
+               exists(x.privateKey) &&
+               (x.privateKey.algorithm.name == alg.name) &&
+               util.memcmp(x.privateKey.algorithm.prime, alg.prime) &&
+               util.memcmp(x.privateKey.algorithm.generator, alg.generator) &&
+               (x.privateKey.type == "private") &&
+               !x.privateKey.extractable &&
+               (x.privateKey.usages.length == 2) &&
+               (x.privateKey.usages[0] == "deriveKey") &&
+               (x.privateKey.usages[1] == "deriveBits");
+      }),
+      error(that)
+    );
+  }
+);
+/*]]>*/</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>