WebCrypto: Implement crypto.subtle.deriveKey (chromium-side).

BUG=437577

Review URL: https://codereview.chromium.org/749183004

Cr-Commit-Position: refs/heads/master@{#308110}
diff --git a/content/child/webcrypto/algorithm_dispatch.cc b/content/child/webcrypto/algorithm_dispatch.cc
index 1a0b247..4badeea9 100644
--- a/content/child/webcrypto/algorithm_dispatch.cc
+++ b/content/child/webcrypto/algorithm_dispatch.cc
@@ -252,7 +252,61 @@
   if (status.IsError())
     return status;
 
-  return impl->DeriveBits(algorithm, base_key, length_bits, derived_bytes);
+  return impl->DeriveBits(algorithm, base_key, true, length_bits,
+                          derived_bytes);
+}
+
+Status DeriveKey(const blink::WebCryptoAlgorithm& algorithm,
+                 const blink::WebCryptoKey& base_key,
+                 const blink::WebCryptoAlgorithm& import_algorithm,
+                 const blink::WebCryptoAlgorithm& key_length_algorithm,
+                 bool extractable,
+                 blink::WebCryptoKeyUsageMask usages,
+                 blink::WebCryptoKey* derived_key) {
+  if (!KeyUsageAllows(base_key, blink::WebCryptoKeyUsageDeriveKey))
+    return Status::ErrorUnexpected();
+
+  if (algorithm.id() != base_key.algorithm().id())
+    return Status::ErrorUnexpected();
+
+  if (import_algorithm.id() != key_length_algorithm.id())
+    return Status::ErrorUnexpected();
+
+  const AlgorithmImplementation* import_impl = NULL;
+  Status status =
+      GetAlgorithmImplementation(import_algorithm.id(), &import_impl);
+  if (status.IsError())
+    return status;
+
+  // Fail fast if the requested key usages are incorect.
+  status = import_impl->VerifyKeyUsagesBeforeImportKey(
+      blink::WebCryptoKeyFormatRaw, usages);
+  if (status.IsError())
+    return status;
+
+  // Determine how many bits long the derived key should be.
+  unsigned int length_bits = 0;
+  bool has_length_bits = false;
+  status = import_impl->GetKeyLength(key_length_algorithm, &has_length_bits,
+                                     &length_bits);
+  if (status.IsError())
+    return status;
+
+  // Derive the key bytes.
+  const AlgorithmImplementation* derive_impl = NULL;
+  status = GetAlgorithmImplementation(algorithm.id(), &derive_impl);
+  if (status.IsError())
+    return status;
+
+  std::vector<uint8_t> derived_bytes;
+  status = derive_impl->DeriveBits(algorithm, base_key, has_length_bits,
+                                   length_bits, &derived_bytes);
+  if (status.IsError())
+    return status;
+
+  // Create the key using the derived bytes.
+  return ImportKey(blink::WebCryptoKeyFormatRaw, CryptoData(derived_bytes),
+                   import_algorithm, extractable, usages, derived_key);
 }
 
 scoped_ptr<blink::WebCryptoDigestor> CreateDigestor(
diff --git a/content/child/webcrypto/algorithm_dispatch.h b/content/child/webcrypto/algorithm_dispatch.h
index 977b792..5249d4f7 100644
--- a/content/child/webcrypto/algorithm_dispatch.h
+++ b/content/child/webcrypto/algorithm_dispatch.h
@@ -92,6 +92,34 @@
                                  unsigned int length_bits,
                                  std::vector<uint8_t>* derived_bytes);
 
+// Derives a key by calling the underlying deriveBits/getKeyLength/importKey
+// operations.
+//
+// Note that whereas the WebCrypto spec uses a single "derivedKeyType"
+// AlgorithmIdentifier in its specification of deriveKey(), here two separate
+// AlgorithmIdentifiers are used:
+//
+//   * |import_algorithm|  -- The parameters required by the derived key's
+//                            "importKey" operation.
+//
+//   * |key_length_algorithm| -- The parameters required by the derived key's
+//                               "get key length" operation.
+//
+// WebCryptoAlgorithm is not a flexible type like AlgorithmIdentifier (it cannot
+// be easily re-interpreted as a different parameter type).
+//
+// Therefore being provided with separate parameter types for the import
+// parameters and the key length parameters simplifies passing the right
+// parameters onto ImportKey() and GetKeyLength() respectively.
+CONTENT_EXPORT Status
+DeriveKey(const blink::WebCryptoAlgorithm& algorithm,
+          const blink::WebCryptoKey& base_key,
+          const blink::WebCryptoAlgorithm& import_algorithm,
+          const blink::WebCryptoAlgorithm& key_length_algorithm,
+          bool extractable,
+          blink::WebCryptoKeyUsageMask usages,
+          blink::WebCryptoKey* derived_key);
+
 CONTENT_EXPORT scoped_ptr<blink::WebCryptoDigestor> CreateDigestor(
     blink::WebCryptoAlgorithmId algorithm);
 
diff --git a/content/child/webcrypto/algorithm_implementation.cc b/content/child/webcrypto/algorithm_implementation.cc
index e67e97c..2bbdcdc3 100644
--- a/content/child/webcrypto/algorithm_implementation.cc
+++ b/content/child/webcrypto/algorithm_implementation.cc
@@ -63,7 +63,8 @@
 Status AlgorithmImplementation::DeriveBits(
     const blink::WebCryptoAlgorithm& algorithm,
     const blink::WebCryptoKey& base_key,
-    unsigned int length_bits,
+    bool has_optional_length_bits,
+    unsigned int optional_length_bits,
     std::vector<uint8_t>* derived_bytes) const {
   return Status::ErrorUnsupported();
 }
diff --git a/content/child/webcrypto/algorithm_implementation.h b/content/child/webcrypto/algorithm_implementation.h
index 7449903..d1fb2823 100644
--- a/content/child/webcrypto/algorithm_implementation.h
+++ b/content/child/webcrypto/algorithm_implementation.h
@@ -81,10 +81,16 @@
                              blink::WebCryptoKeyUsageMask usages,
                              GenerateKeyResult* result) const;
 
-  // This method corresponds to Web Crypto's "derive bits" operation.
+  // This method corresponds to Web Crypto's "derive bits" operation. It is
+  // essentially crypto.subtle.deriveBits() with the exception that the length
+  // can be "null" (|has_length_bits = true|).
+  //
+  // In cases where the length was not specified, an appropriate default for the
+  // algorithm should be used (as described by the spec).
   virtual Status DeriveBits(const blink::WebCryptoAlgorithm& algorithm,
                             const blink::WebCryptoKey& base_key,
-                            unsigned int length_bits,
+                            bool has_optional_length_bits,
+                            unsigned int optional_length_bits,
                             std::vector<uint8_t>* derived_bytes) const;
 
   // This method corresponds with Web Crypto's "Get key length" operation.
diff --git a/content/child/webcrypto/openssl/ecdh_openssl.cc b/content/child/webcrypto/openssl/ecdh_openssl.cc
index d6228cb..342064a 100644
--- a/content/child/webcrypto/openssl/ecdh_openssl.cc
+++ b/content/child/webcrypto/openssl/ecdh_openssl.cc
@@ -49,7 +49,8 @@
 
   Status DeriveBits(const blink::WebCryptoAlgorithm& algorithm,
                     const blink::WebCryptoKey& base_key,
-                    unsigned int length_bits,
+                    bool has_optional_length_bits,
+                    unsigned int optional_length_bits,
                     std::vector<uint8_t>* derived_bytes) const override {
     if (base_key.type() != blink::WebCryptoKeyTypePrivate)
       return Status::ErrorUnexpectedKeyType();
@@ -78,13 +79,6 @@
       return Status::ErrorEcdhCurveMismatch();
     }
 
-    // Handle the empty length case now to avoid calling an undefined
-    // |&derived_bytes->front()| later.
-    if (length_bits == 0) {
-      derived_bytes->clear();
-      return Status::Success();
-    }
-
     crypto::ScopedEC_KEY public_key_ec(
         EVP_PKEY_get1_EC_KEY(AsymKeyOpenSsl::Cast(public_key)->key()));
 
@@ -100,6 +94,18 @@
     int field_size_bytes = NumBitsToBytes(
         EC_GROUP_get_degree(EC_KEY_get0_group(private_key_ec.get())));
 
+    // If a desired key length was not specified, default to the field size
+    // (rounded up to nearest byte).
+    unsigned int length_bits =
+        has_optional_length_bits ? optional_length_bits : field_size_bytes * 8;
+
+    // Handle the empty length case now to avoid calling an undefined
+    // |&derived_bytes->front()| later.
+    if (length_bits == 0) {
+      derived_bytes->clear();
+      return Status::Success();
+    }
+
     if (length_bits > static_cast<unsigned int>(field_size_bytes * 8))
       return Status::ErrorEcdhLengthTooBig(field_size_bytes * 8);
 
diff --git a/content/child/webcrypto/test/ecdh_unittest.cc b/content/child/webcrypto/test/ecdh_unittest.cc
index 56af8b4..b49aaa8 100644
--- a/content/child/webcrypto/test/ecdh_unittest.cc
+++ b/content/child/webcrypto/test/ecdh_unittest.cc
@@ -42,7 +42,41 @@
       new blink::WebCryptoEcdhKeyDeriveParams(public_key));
 }
 
-TEST(WebCryptoEcdhTest, VerifyKnownAnswer) {
+blink::WebCryptoAlgorithm CreateAesGcmDerivedKeyParams(
+    unsigned short length_bits) {
+  return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
+      blink::WebCryptoAlgorithmIdAesGcm,
+      new blink::WebCryptoAesDerivedKeyParams(length_bits));
+}
+
+// Helper that loads a  "public_key" and "private_key" from the test data.
+void ImportKeysFromTest(const base::DictionaryValue* test,
+                        blink::WebCryptoKey* public_key,
+                        blink::WebCryptoKey* private_key) {
+  // Import the public key.
+  const base::DictionaryValue* public_key_json = NULL;
+  EXPECT_TRUE(test->GetDictionary("public_key", &public_key_json));
+  blink::WebCryptoNamedCurve curve =
+      GetCurveNameFromDictionary(public_key_json, "crv");
+  ASSERT_EQ(Status::Success(),
+            ImportKey(blink::WebCryptoKeyFormatJwk,
+                      CryptoData(MakeJsonVector(*public_key_json)),
+                      CreateEcdhImportAlgorithm(curve), true, 0, public_key));
+
+  // Import the private key.
+  const base::DictionaryValue* private_key_json = NULL;
+  EXPECT_TRUE(test->GetDictionary("private_key", &private_key_json));
+  curve = GetCurveNameFromDictionary(private_key_json, "crv");
+  ASSERT_EQ(Status::Success(),
+            ImportKey(blink::WebCryptoKeyFormatJwk,
+                      CryptoData(MakeJsonVector(*private_key_json)),
+                      CreateEcdhImportAlgorithm(curve), true,
+                      blink::WebCryptoKeyUsageDeriveBits |
+                          blink::WebCryptoKeyUsageDeriveKey,
+                      private_key));
+}
+
+TEST(WebCryptoEcdhTest, DeriveBitsKnownAnswer) {
   if (!SupportsEcdh())
     return;
 
@@ -55,28 +89,10 @@
     const base::DictionaryValue* test;
     ASSERT_TRUE(tests->GetDictionary(test_index, &test));
 
-    // Import the public key.
-    const base::DictionaryValue* public_key_json = NULL;
-    EXPECT_TRUE(test->GetDictionary("public_key", &public_key_json));
-    blink::WebCryptoNamedCurve curve =
-        GetCurveNameFromDictionary(public_key_json, "crv");
+    // Import the keys.
     blink::WebCryptoKey public_key;
-    ASSERT_EQ(
-        Status::Success(),
-        ImportKey(blink::WebCryptoKeyFormatJwk,
-                  CryptoData(MakeJsonVector(*public_key_json)),
-                  CreateEcdhImportAlgorithm(curve), true, 0, &public_key));
-
-    // Import the private key.
-    const base::DictionaryValue* private_key_json = NULL;
-    EXPECT_TRUE(test->GetDictionary("private_key", &private_key_json));
-    curve = GetCurveNameFromDictionary(private_key_json, "crv");
     blink::WebCryptoKey private_key;
-    ASSERT_EQ(Status::Success(),
-              ImportKey(blink::WebCryptoKeyFormatJwk,
-                        CryptoData(MakeJsonVector(*private_key_json)),
-                        CreateEcdhImportAlgorithm(curve), true,
-                        blink::WebCryptoKeyUsageDeriveBits, &private_key));
+    ImportKeysFromTest(test, &public_key, &private_key);
 
     // Now try to derive bytes.
     std::vector<uint8_t> derived_bytes;
@@ -100,6 +116,199 @@
   }
 }
 
+// Loads up a test ECDH public and private key for P-521. The keys
+// come from different key pairs, and can be used for key derivation of up to
+// 528 bits.
+void LoadTestKeys(blink::WebCryptoKey* public_key,
+                  blink::WebCryptoKey* private_key) {
+  // Assume that the 7th key in the test data is for P-521.
+  scoped_ptr<base::ListValue> tests;
+  ASSERT_TRUE(ReadJsonTestFileToList("ecdh.json", &tests));
+
+  const base::DictionaryValue* test;
+  ASSERT_TRUE(tests->GetDictionary(6, &test));
+
+  ImportKeysFromTest(test, public_key, private_key);
+
+  ASSERT_EQ(blink::WebCryptoNamedCurveP521,
+            public_key->algorithm().ecParams()->namedCurve());
+}
+
+// Try deriving an AES key of length 129 bits.
+TEST(WebCryptoEcdhTest, DeriveKeyBadAesLength) {
+  if (!SupportsEcdh())
+    return;
+
+  blink::WebCryptoKey public_key;
+  blink::WebCryptoKey base_key;
+  LoadTestKeys(&public_key, &base_key);
+
+  blink::WebCryptoKey derived_key;
+
+  ASSERT_EQ(Status::ErrorGetAesKeyLength(),
+            DeriveKey(CreateEcdhDeriveParams(public_key), base_key,
+                      CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm),
+                      CreateAesGcmDerivedKeyParams(129), true,
+                      blink::WebCryptoKeyUsageEncrypt, &derived_key));
+}
+
+// Try deriving an AES key of length 192 bits.
+TEST(WebCryptoEcdhTest, DeriveKeyUnsupportedAesLength) {
+  if (!SupportsEcdh())
+    return;
+
+  blink::WebCryptoKey public_key;
+  blink::WebCryptoKey base_key;
+  LoadTestKeys(&public_key, &base_key);
+
+  blink::WebCryptoKey derived_key;
+
+  ASSERT_EQ(Status::ErrorAes192BitUnsupported(),
+            DeriveKey(CreateEcdhDeriveParams(public_key), base_key,
+                      CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm),
+                      CreateAesGcmDerivedKeyParams(192), true,
+                      blink::WebCryptoKeyUsageEncrypt, &derived_key));
+}
+
+// Try deriving an HMAC key of length 0 bits.
+TEST(WebCryptoEcdhTest, DeriveKeyZeroLengthHmac) {
+  if (!SupportsEcdh())
+    return;
+
+  blink::WebCryptoKey public_key;
+  blink::WebCryptoKey base_key;
+  LoadTestKeys(&public_key, &base_key);
+
+  blink::WebCryptoKey derived_key;
+
+  const blink::WebCryptoAlgorithm import_algorithm =
+      CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1, 0);
+
+  ASSERT_EQ(Status::ErrorGetHmacKeyLengthZero(),
+            DeriveKey(CreateEcdhDeriveParams(public_key), base_key,
+                      import_algorithm, import_algorithm, true,
+                      blink::WebCryptoKeyUsageSign, &derived_key));
+}
+
+// Derive an HMAC key of length 19 bits.
+TEST(WebCryptoEcdhTest, DeriveKeyHmac19Bits) {
+  if (!SupportsEcdh())
+    return;
+
+  blink::WebCryptoKey public_key;
+  blink::WebCryptoKey base_key;
+  LoadTestKeys(&public_key, &base_key);
+
+  blink::WebCryptoKey derived_key;
+
+  const blink::WebCryptoAlgorithm import_algorithm =
+      CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1, 19);
+
+  ASSERT_EQ(Status::Success(),
+            DeriveKey(CreateEcdhDeriveParams(public_key), base_key,
+                      import_algorithm, import_algorithm, true,
+                      blink::WebCryptoKeyUsageSign, &derived_key));
+
+  ASSERT_EQ(blink::WebCryptoAlgorithmIdHmac, derived_key.algorithm().id());
+  ASSERT_EQ(blink::WebCryptoAlgorithmIdSha1,
+            derived_key.algorithm().hmacParams()->hash().id());
+  ASSERT_EQ(19u, derived_key.algorithm().hmacParams()->lengthBits());
+
+  // Export the key and verify its contents.
+  std::vector<uint8_t> raw_key;
+  EXPECT_EQ(Status::Success(),
+            ExportKey(blink::WebCryptoKeyFormatRaw, derived_key, &raw_key));
+  EXPECT_EQ(3u, raw_key.size());
+  // The last 7 bits of the key should be zero.
+  EXPECT_EQ(0, raw_key[raw_key.size() - 1] & 0x1f);
+}
+
+// Derive an HMAC key with no specified length (just the hash of SHA-256).
+TEST(WebCryptoEcdhTest, DeriveKeyHmacSha256NoLength) {
+  if (!SupportsEcdh())
+    return;
+
+  blink::WebCryptoKey public_key;
+  blink::WebCryptoKey base_key;
+  LoadTestKeys(&public_key, &base_key);
+
+  blink::WebCryptoKey derived_key;
+
+  const blink::WebCryptoAlgorithm import_algorithm =
+      CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha256);
+
+  ASSERT_EQ(Status::Success(),
+            DeriveKey(CreateEcdhDeriveParams(public_key), base_key,
+                      import_algorithm, import_algorithm, true,
+                      blink::WebCryptoKeyUsageSign, &derived_key));
+
+  ASSERT_EQ(blink::WebCryptoAlgorithmIdHmac, derived_key.algorithm().id());
+  ASSERT_EQ(blink::WebCryptoAlgorithmIdSha256,
+            derived_key.algorithm().hmacParams()->hash().id());
+  ASSERT_EQ(512u, derived_key.algorithm().hmacParams()->lengthBits());
+
+  // Export the key and verify its contents.
+  std::vector<uint8_t> raw_key;
+  EXPECT_EQ(Status::Success(),
+            ExportKey(blink::WebCryptoKeyFormatRaw, derived_key, &raw_key));
+  EXPECT_EQ(64u, raw_key.size());
+}
+
+// Derive an HMAC key with no specified length (just the hash of SHA-512).
+//
+// This fails, because ECDH using P-521 can only generate 528 bits, however HMAC
+// SHA-512 requires 1024 bits.
+//
+// In practice, authors won't be directly generating keys from key agreement
+// schemes, as that is frequently insecure, and instead be using KDFs to expand
+// and generate keys. For simplicity of testing, however, test using an HMAC
+// key.
+TEST(WebCryptoEcdhTest, DeriveKeyHmacSha512NoLength) {
+  if (!SupportsEcdh())
+    return;
+
+  blink::WebCryptoKey public_key;
+  blink::WebCryptoKey base_key;
+  LoadTestKeys(&public_key, &base_key);
+
+  blink::WebCryptoKey derived_key;
+
+  const blink::WebCryptoAlgorithm import_algorithm =
+      CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha512);
+
+  ASSERT_EQ(Status::ErrorEcdhLengthTooBig(528),
+            DeriveKey(CreateEcdhDeriveParams(public_key), base_key,
+                      import_algorithm, import_algorithm, true,
+                      blink::WebCryptoKeyUsageSign, &derived_key));
+}
+
+// Try deriving an AES key of length 128 bits.
+TEST(WebCryptoEcdhTest, DeriveKeyAes128) {
+  if (!SupportsEcdh())
+    return;
+
+  blink::WebCryptoKey public_key;
+  blink::WebCryptoKey base_key;
+  LoadTestKeys(&public_key, &base_key);
+
+  blink::WebCryptoKey derived_key;
+
+  ASSERT_EQ(Status::Success(),
+            DeriveKey(CreateEcdhDeriveParams(public_key), base_key,
+                      CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm),
+                      CreateAesGcmDerivedKeyParams(128), true,
+                      blink::WebCryptoKeyUsageEncrypt, &derived_key));
+
+  ASSERT_EQ(blink::WebCryptoAlgorithmIdAesGcm, derived_key.algorithm().id());
+  ASSERT_EQ(128, derived_key.algorithm().aesParams()->lengthBits());
+
+  // Export the key and verify its contents.
+  std::vector<uint8_t> raw_key;
+  EXPECT_EQ(Status::Success(),
+            ExportKey(blink::WebCryptoKeyFormatRaw, derived_key, &raw_key));
+  EXPECT_EQ(16u, raw_key.size());
+}
+
 }  // namespace
 
 }  // namespace webcrypto
diff --git a/content/child/webcrypto/webcrypto_impl.cc b/content/child/webcrypto/webcrypto_impl.cc
index 8efd9de..9a3924b 100644
--- a/content/child/webcrypto/webcrypto_impl.cc
+++ b/content/child/webcrypto/webcrypto_impl.cc
@@ -350,6 +350,32 @@
   std::vector<uint8_t> derived_bytes;
 };
 
+struct DeriveKeyState : public BaseState {
+  DeriveKeyState(const blink::WebCryptoAlgorithm& algorithm,
+                 const blink::WebCryptoKey& base_key,
+                 const blink::WebCryptoAlgorithm& import_algorithm,
+                 const blink::WebCryptoAlgorithm& key_length_algorithm,
+                 bool extractable,
+                 blink::WebCryptoKeyUsageMask usages,
+                 const blink::WebCryptoResult& result)
+      : BaseState(result),
+        algorithm(algorithm),
+        base_key(base_key),
+        import_algorithm(import_algorithm),
+        key_length_algorithm(key_length_algorithm),
+        extractable(extractable),
+        usages(usages) {}
+
+  const blink::WebCryptoAlgorithm algorithm;
+  const blink::WebCryptoKey base_key;
+  const blink::WebCryptoAlgorithm import_algorithm;
+  const blink::WebCryptoAlgorithm key_length_algorithm;
+  bool extractable;
+  blink::WebCryptoKeyUsageMask usages;
+
+  blink::WebCryptoKey derived_key;
+};
+
 // --------------------------------------------------------------------
 // Wrapper functions
 // --------------------------------------------------------------------
@@ -552,6 +578,22 @@
       FROM_HERE, base::Bind(DoDeriveBitsReply, Passed(&passed_state)));
 }
 
+void DoDeriveKeyReply(scoped_ptr<DeriveKeyState> state) {
+  CompleteWithKeyOrError(state->status, state->derived_key, &state->result);
+}
+
+void DoDeriveKey(scoped_ptr<DeriveKeyState> passed_state) {
+  DeriveKeyState* state = passed_state.get();
+  if (state->cancelled())
+    return;
+  state->status = webcrypto::DeriveKey(
+      state->algorithm, state->base_key, state->import_algorithm,
+      state->key_length_algorithm, state->extractable, state->usages,
+      &state->derived_key);
+  state->origin_thread->PostTask(
+      FROM_HERE, base::Bind(DoDeriveKeyReply, Passed(&passed_state)));
+}
+
 }  // namespace
 
 WebCryptoImpl::WebCryptoImpl() {
@@ -715,6 +757,23 @@
   }
 }
 
+void WebCryptoImpl::deriveKey(
+    const blink::WebCryptoAlgorithm& algorithm,
+    const blink::WebCryptoKey& base_key,
+    const blink::WebCryptoAlgorithm& import_algorithm,
+    const blink::WebCryptoAlgorithm& key_length_algorithm,
+    bool extractable,
+    blink::WebCryptoKeyUsageMask usages,
+    blink::WebCryptoResult result) {
+  scoped_ptr<DeriveKeyState> state(
+      new DeriveKeyState(algorithm, base_key, import_algorithm,
+                         key_length_algorithm, extractable, usages, result));
+  if (!CryptoThreadPool::PostTask(FROM_HERE,
+                                  base::Bind(DoDeriveKey, Passed(&state)))) {
+    CompleteWithThreadPoolError(&result);
+  }
+}
+
 blink::WebCryptoDigestor* WebCryptoImpl::createDigestor(
     blink::WebCryptoAlgorithmId algorithm_id) {
   return webcrypto::CreateDigestor(algorithm_id).release();
diff --git a/content/child/webcrypto/webcrypto_impl.h b/content/child/webcrypto/webcrypto_impl.h
index d9afb88..c8462dfa 100644
--- a/content/child/webcrypto/webcrypto_impl.h
+++ b/content/child/webcrypto/webcrypto_impl.h
@@ -89,6 +89,14 @@
                           unsigned int length_bits,
                           blink::WebCryptoResult result);
 
+  virtual void deriveKey(const blink::WebCryptoAlgorithm& algorithm,
+                         const blink::WebCryptoKey& base_key,
+                         const blink::WebCryptoAlgorithm& import_algorithm,
+                         const blink::WebCryptoAlgorithm& key_length_algorithm,
+                         bool extractable,
+                         blink::WebCryptoKeyUsageMask usages,
+                         blink::WebCryptoResult result);
+
   // This method returns a digestor object that can be used to synchronously
   // compute a digest one chunk at a time. Thus, the consume does not need to
   // hold onto a large buffer with all the data to digest. Chunks can be given