Commit 20bf4c3c authored by eroman's avatar eroman Committed by Commit bot

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

BUG=437577

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

Cr-Commit-Position: refs/heads/master@{#308110}
parent bd52a8a1
...@@ -252,7 +252,61 @@ Status DeriveBits(const blink::WebCryptoAlgorithm& algorithm, ...@@ -252,7 +252,61 @@ Status DeriveBits(const blink::WebCryptoAlgorithm& algorithm,
if (status.IsError()) if (status.IsError())
return status; 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( scoped_ptr<blink::WebCryptoDigestor> CreateDigestor(
......
...@@ -92,6 +92,34 @@ CONTENT_EXPORT Status DeriveBits(const blink::WebCryptoAlgorithm& algorithm, ...@@ -92,6 +92,34 @@ CONTENT_EXPORT Status DeriveBits(const blink::WebCryptoAlgorithm& algorithm,
unsigned int length_bits, unsigned int length_bits,
std::vector<uint8_t>* derived_bytes); 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( CONTENT_EXPORT scoped_ptr<blink::WebCryptoDigestor> CreateDigestor(
blink::WebCryptoAlgorithmId algorithm); blink::WebCryptoAlgorithmId algorithm);
......
...@@ -63,7 +63,8 @@ Status AlgorithmImplementation::GenerateKey( ...@@ -63,7 +63,8 @@ Status AlgorithmImplementation::GenerateKey(
Status AlgorithmImplementation::DeriveBits( Status AlgorithmImplementation::DeriveBits(
const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& base_key, 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 { std::vector<uint8_t>* derived_bytes) const {
return Status::ErrorUnsupported(); return Status::ErrorUnsupported();
} }
......
...@@ -81,10 +81,16 @@ class AlgorithmImplementation { ...@@ -81,10 +81,16 @@ class AlgorithmImplementation {
blink::WebCryptoKeyUsageMask usages, blink::WebCryptoKeyUsageMask usages,
GenerateKeyResult* result) const; 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, virtual Status DeriveBits(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& base_key, 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; std::vector<uint8_t>* derived_bytes) const;
// This method corresponds with Web Crypto's "Get key length" operation. // This method corresponds with Web Crypto's "Get key length" operation.
......
...@@ -49,7 +49,8 @@ class EcdhImplementation : public EcAlgorithm { ...@@ -49,7 +49,8 @@ class EcdhImplementation : public EcAlgorithm {
Status DeriveBits(const blink::WebCryptoAlgorithm& algorithm, Status DeriveBits(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& base_key, 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 { std::vector<uint8_t>* derived_bytes) const override {
if (base_key.type() != blink::WebCryptoKeyTypePrivate) if (base_key.type() != blink::WebCryptoKeyTypePrivate)
return Status::ErrorUnexpectedKeyType(); return Status::ErrorUnexpectedKeyType();
...@@ -78,13 +79,6 @@ class EcdhImplementation : public EcAlgorithm { ...@@ -78,13 +79,6 @@ class EcdhImplementation : public EcAlgorithm {
return Status::ErrorEcdhCurveMismatch(); 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( crypto::ScopedEC_KEY public_key_ec(
EVP_PKEY_get1_EC_KEY(AsymKeyOpenSsl::Cast(public_key)->key())); EVP_PKEY_get1_EC_KEY(AsymKeyOpenSsl::Cast(public_key)->key()));
...@@ -100,6 +94,18 @@ class EcdhImplementation : public EcAlgorithm { ...@@ -100,6 +94,18 @@ class EcdhImplementation : public EcAlgorithm {
int field_size_bytes = NumBitsToBytes( int field_size_bytes = NumBitsToBytes(
EC_GROUP_get_degree(EC_KEY_get0_group(private_key_ec.get()))); 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)) if (length_bits > static_cast<unsigned int>(field_size_bytes * 8))
return Status::ErrorEcdhLengthTooBig(field_size_bytes * 8); return Status::ErrorEcdhLengthTooBig(field_size_bytes * 8);
......
...@@ -350,6 +350,32 @@ struct DeriveBitsState : public BaseState { ...@@ -350,6 +350,32 @@ struct DeriveBitsState : public BaseState {
std::vector<uint8_t> derived_bytes; 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 // Wrapper functions
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -552,6 +578,22 @@ void DoDeriveBits(scoped_ptr<DeriveBitsState> passed_state) { ...@@ -552,6 +578,22 @@ void DoDeriveBits(scoped_ptr<DeriveBitsState> passed_state) {
FROM_HERE, base::Bind(DoDeriveBitsReply, Passed(&passed_state))); 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 } // namespace
WebCryptoImpl::WebCryptoImpl() { WebCryptoImpl::WebCryptoImpl() {
...@@ -715,6 +757,23 @@ void WebCryptoImpl::deriveBits(const blink::WebCryptoAlgorithm& algorithm, ...@@ -715,6 +757,23 @@ void WebCryptoImpl::deriveBits(const blink::WebCryptoAlgorithm& algorithm,
} }
} }
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::WebCryptoDigestor* WebCryptoImpl::createDigestor(
blink::WebCryptoAlgorithmId algorithm_id) { blink::WebCryptoAlgorithmId algorithm_id) {
return webcrypto::CreateDigestor(algorithm_id).release(); return webcrypto::CreateDigestor(algorithm_id).release();
......
...@@ -89,6 +89,14 @@ class WebCryptoImpl : public blink::WebCrypto { ...@@ -89,6 +89,14 @@ class WebCryptoImpl : public blink::WebCrypto {
unsigned int length_bits, unsigned int length_bits,
blink::WebCryptoResult result); 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 // 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 // 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 // hold onto a large buffer with all the data to digest. Chunks can be given
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment