Skip to content

Commit

Permalink
Refactor WebCrypto code.
Browse files Browse the repository at this point in the history
Split up the monstrously large platform_crypto_nss.cc, platform_crypto_openssl.cc into multiple files.

-----------
Overview:
-----------

* algorithm_implementation.h:

This defines a base class AlgorithmImplementation, which has virtual methods for synchronous encrypt/decrypt/generatekey. All of the information about an algorithm is now encapsulated by an AlgorithmImplementation. So for instance the JWK specific knowledge, key usages for each key type are pulled into this interface.

* algorithm_registry.cc:

Contains a mapping from WebCryptoAlgorithmID --> AlgorithmImplementation, stored by a singleton.

* algorithm_dispatch.cc:

Given parameters from Blink, looks up the appropriate AlgorithmImplementation in the registry and dispatches the operation. Also implements wrap/unwrap in terms of encrypt/decrypt.

* structured_clone.cc:

Contains the code related to structured cloning (which still needs some cleanup, and is implemented in terms of import/export).

* nss/*, openssl/*:

Contains the AlgorithmImplementation concrete classes for each algorithm. 

This reorganization also unintentionally fixes a few bugs.

  * ExportKey() for spki/pkcs8/raw uses the already serialized key data rather than re-exporting
  * Some exception codes were fixed.

BUG=389325,389342,389327,374912

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284192 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
eroman@chromium.org committed Jul 18, 2014
1 parent b31dbfc commit d008566
Show file tree
Hide file tree
Showing 54 changed files with 5,867 additions and 4,665 deletions.
1 change: 0 additions & 1 deletion content/child/blink_platform_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -933,7 +933,6 @@ void BlinkPlatformImpl::didStopWorkerThread(blink::WebThread* thread) {
}

blink::WebCrypto* BlinkPlatformImpl::crypto() {
WebCryptoImpl::EnsureInit();
return &web_crypto_;
}

Expand Down
293 changes: 293 additions & 0 deletions content/child/webcrypto/algorithm_dispatch.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/child/webcrypto/algorithm_dispatch.h"

#include "base/logging.h"
#include "content/child/webcrypto/algorithm_implementation.h"
#include "content/child/webcrypto/algorithm_registry.h"
#include "content/child/webcrypto/crypto_data.h"
#include "content/child/webcrypto/platform_crypto.h"
#include "content/child/webcrypto/status.h"
#include "content/child/webcrypto/webcrypto_util.h"
#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"

namespace content {

namespace webcrypto {

namespace {

Status DecryptDontCheckKeyUsage(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
const CryptoData& data,
std::vector<uint8>* buffer) {
if (algorithm.id() != key.algorithm().id())
return Status::ErrorUnexpected();

const AlgorithmImplementation* impl = NULL;
Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
if (status.IsError())
return status;

return impl->Decrypt(algorithm, key, data, buffer);
}

Status EncryptDontCheckUsage(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
const CryptoData& data,
std::vector<uint8>* buffer) {
if (algorithm.id() != key.algorithm().id())
return Status::ErrorUnexpected();

const AlgorithmImplementation* impl = NULL;
Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
if (status.IsError())
return status;

return impl->Encrypt(algorithm, key, data, buffer);
}

Status ExportKeyDontCheckExtractability(blink::WebCryptoKeyFormat format,
const blink::WebCryptoKey& key,
std::vector<uint8>* buffer) {
const AlgorithmImplementation* impl = NULL;
Status status = GetAlgorithmImplementation(key.algorithm().id(), &impl);
if (status.IsError())
return status;

switch (format) {
case blink::WebCryptoKeyFormatRaw:
return impl->ExportKeyRaw(key, buffer);
case blink::WebCryptoKeyFormatSpki:
return impl->ExportKeySpki(key, buffer);
case blink::WebCryptoKeyFormatPkcs8:
return impl->ExportKeyPkcs8(key, buffer);
case blink::WebCryptoKeyFormatJwk:
return impl->ExportKeyJwk(key, buffer);
default:
return Status::ErrorUnsupported();
}
}

} // namespace

Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
const CryptoData& data,
std::vector<uint8>* buffer) {
if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageEncrypt))
return Status::ErrorUnexpected();
return EncryptDontCheckUsage(algorithm, key, data, buffer);
}

Status Decrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
const CryptoData& data,
std::vector<uint8>* buffer) {
if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageDecrypt))
return Status::ErrorUnexpected();
return DecryptDontCheckKeyUsage(algorithm, key, data, buffer);
}

Status Digest(const blink::WebCryptoAlgorithm& algorithm,
const CryptoData& data,
std::vector<uint8>* buffer) {
const AlgorithmImplementation* impl = NULL;
Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
if (status.IsError())
return status;

return impl->Digest(algorithm, data, buffer);
}

Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usage_mask,
blink::WebCryptoKey* key) {
const AlgorithmImplementation* impl = NULL;
Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
if (status.IsError())
return status;

status = impl->VerifyKeyUsagesBeforeGenerateKey(usage_mask);
if (status.IsError())
return status;

return impl->GenerateSecretKey(algorithm, extractable, usage_mask, key);
}

Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask combined_usage_mask,
blink::WebCryptoKey* public_key,
blink::WebCryptoKey* private_key) {
const AlgorithmImplementation* impl = NULL;
Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
if (status.IsError())
return status;

blink::WebCryptoKeyUsageMask public_usage_mask;
blink::WebCryptoKeyUsageMask private_usage_mask;
status = impl->VerifyKeyUsagesBeforeGenerateKeyPair(
combined_usage_mask, &public_usage_mask, &private_usage_mask);
if (status.IsError())
return status;

return impl->GenerateKeyPair(algorithm,
extractable,
public_usage_mask,
private_usage_mask,
public_key,
private_key);
}

// Note that this function may be called from the target Blink thread.
Status ImportKey(blink::WebCryptoKeyFormat format,
const CryptoData& key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usage_mask,
blink::WebCryptoKey* key) {
const AlgorithmImplementation* impl = NULL;
Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
if (status.IsError())
return status;

status = impl->VerifyKeyUsagesBeforeImportKey(format, usage_mask);
if (status.IsError())
return status;

switch (format) {
case blink::WebCryptoKeyFormatRaw:
return impl->ImportKeyRaw(
key_data, algorithm, extractable, usage_mask, key);
case blink::WebCryptoKeyFormatSpki:
return impl->ImportKeySpki(
key_data, algorithm, extractable, usage_mask, key);
case blink::WebCryptoKeyFormatPkcs8:
return impl->ImportKeyPkcs8(
key_data, algorithm, extractable, usage_mask, key);
case blink::WebCryptoKeyFormatJwk:
return impl->ImportKeyJwk(
key_data, algorithm, extractable, usage_mask, key);
default:
return Status::ErrorUnsupported();
}
}

Status ExportKey(blink::WebCryptoKeyFormat format,
const blink::WebCryptoKey& key,
std::vector<uint8>* buffer) {
if (!key.extractable())
return Status::ErrorKeyNotExtractable();
return ExportKeyDontCheckExtractability(format, key, buffer);
}

Status Sign(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
const CryptoData& data,
std::vector<uint8>* buffer) {
if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageSign))
return Status::ErrorUnexpected();
if (algorithm.id() != key.algorithm().id())
return Status::ErrorUnexpected();

const AlgorithmImplementation* impl = NULL;
Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
if (status.IsError())
return status;

return impl->Sign(algorithm, key, data, buffer);
}

Status Verify(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
const CryptoData& signature,
const CryptoData& data,
bool* signature_match) {
if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageVerify))
return Status::ErrorUnexpected();
if (algorithm.id() != key.algorithm().id())
return Status::ErrorUnexpected();

// TODO(eroman): Move this into implementation which need it instead.
if (!signature.byte_length()) {
// None of the algorithms generate valid zero-length signatures so this
// will necessarily fail verification. Early return to protect
// implementations from dealing with a NULL signature pointer.
*signature_match = false;
return Status::Success();
}

const AlgorithmImplementation* impl = NULL;
Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
if (status.IsError())
return status;

return impl->Verify(algorithm, key, signature, data, signature_match);
}

Status WrapKey(blink::WebCryptoKeyFormat format,
const blink::WebCryptoKey& key_to_wrap,
const blink::WebCryptoKey& wrapping_key,
const blink::WebCryptoAlgorithm& wrapping_algorithm,
std::vector<uint8>* buffer) {
if (!KeyUsageAllows(wrapping_key, blink::WebCryptoKeyUsageWrapKey))
return Status::ErrorUnexpected();

std::vector<uint8> exported_data;
Status status = ExportKey(format, key_to_wrap, &exported_data);
if (status.IsError())
return status;
return EncryptDontCheckUsage(
wrapping_algorithm, wrapping_key, CryptoData(exported_data), buffer);
}

Status UnwrapKey(blink::WebCryptoKeyFormat format,
const CryptoData& wrapped_key_data,
const blink::WebCryptoKey& wrapping_key,
const blink::WebCryptoAlgorithm& wrapping_algorithm,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usage_mask,
blink::WebCryptoKey* key) {
if (!KeyUsageAllows(wrapping_key, blink::WebCryptoKeyUsageUnwrapKey))
return Status::ErrorUnexpected();
if (wrapping_algorithm.id() != wrapping_key.algorithm().id())
return Status::ErrorUnexpected();

// Fail fast if the import is doomed to fail.
const AlgorithmImplementation* import_impl = NULL;
Status status = GetAlgorithmImplementation(algorithm.id(), &import_impl);
if (status.IsError())
return status;

status = import_impl->VerifyKeyUsagesBeforeImportKey(format, usage_mask);
if (status.IsError())
return status;

std::vector<uint8> buffer;
status = DecryptDontCheckKeyUsage(
wrapping_algorithm, wrapping_key, wrapped_key_data, &buffer);
if (status.IsError())
return status;

// NOTE that returning the details of ImportKey() failures may leak
// information about the plaintext of the encrypted key (for instance the JWK
// key_ops). As long as the ImportKey error messages don't describe actual
// key bytes however this should be OK. For more discussion see
// http://crubg.com/372040
return ImportKey(
format, CryptoData(buffer), algorithm, extractable, usage_mask, key);
}

scoped_ptr<blink::WebCryptoDigestor> CreateDigestor(
blink::WebCryptoAlgorithmId algorithm) {
return CreatePlatformDigestor(algorithm);
}

} // namespace webcrypto

} // namespace content
Loading

0 comments on commit d008566

Please sign in to comment.