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