diff options
author | eroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-18 20:19:34 +0000 |
---|---|---|
committer | eroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-18 20:19:34 +0000 |
commit | d008566b720e4d41fb0fdc83d2b7f3b74ce39c02 (patch) | |
tree | 816ecde55aaf3c3ec81b3a4839f6dac5fcfef53c | |
parent | b31dbfcc5310f4056950e874efbfc513955c47bf (diff) | |
download | chromium_src-d008566b720e4d41fb0fdc83d2b7f3b74ce39c02.zip chromium_src-d008566b720e4d41fb0fdc83d2b7f3b74ce39c02.tar.gz chromium_src-d008566b720e4d41fb0fdc83d2b7f3b74ce39c02.tar.bz2 |
Refactor WebCrypto code.
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
54 files changed, 5867 insertions, 4665 deletions
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc index 629b90e..e4d681b 100644 --- a/content/child/blink_platform_impl.cc +++ b/content/child/blink_platform_impl.cc @@ -933,7 +933,6 @@ void BlinkPlatformImpl::didStopWorkerThread(blink::WebThread* thread) { } blink::WebCrypto* BlinkPlatformImpl::crypto() { - WebCryptoImpl::EnsureInit(); return &web_crypto_; } diff --git a/content/child/webcrypto/algorithm_dispatch.cc b/content/child/webcrypto/algorithm_dispatch.cc new file mode 100644 index 0000000..b2ccfaf --- /dev/null +++ b/content/child/webcrypto/algorithm_dispatch.cc @@ -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 diff --git a/content/child/webcrypto/algorithm_dispatch.h b/content/child/webcrypto/algorithm_dispatch.h new file mode 100644 index 0000000..a8b282c --- /dev/null +++ b/content/child/webcrypto/algorithm_dispatch.h @@ -0,0 +1,104 @@ +// 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. + +#ifndef CONTENT_CHILD_WEBCRYPTO_ALGORITHM_DISPATCH_H_ +#define CONTENT_CHILD_WEBCRYPTO_ALGORITHM_DISPATCH_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "content/common/content_export.h" +#include "third_party/WebKit/public/platform/WebCrypto.h" + +namespace content { + +namespace webcrypto { + +class AlgorithmImplementation; +class CryptoData; +class Status; + +// These functions provide an entry point for synchronous webcrypto operations. +// +// The inputs to these methods come from Blink, and hence the validations done +// by Blink can be assumed: +// +// * The algorithm parameters are consistent with the algorithm +// * The key contains the required usage for the operation + +CONTENT_EXPORT Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer); + +CONTENT_EXPORT Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer); + +CONTENT_EXPORT Status Digest(const blink::WebCryptoAlgorithm& algorithm, + const CryptoData& data, + std::vector<uint8>* buffer); + +CONTENT_EXPORT Status + GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key); + +CONTENT_EXPORT Status + GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* public_key, + blink::WebCryptoKey* private_key); + +CONTENT_EXPORT Status ImportKey(blink::WebCryptoKeyFormat format, + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key); + +CONTENT_EXPORT Status ExportKey(blink::WebCryptoKeyFormat format, + const blink::WebCryptoKey& key, + std::vector<uint8>* buffer); + +CONTENT_EXPORT Status Sign(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer); + +CONTENT_EXPORT Status Verify(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& signature, + const CryptoData& data, + bool* signature_match); + +CONTENT_EXPORT 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); + +CONTENT_EXPORT 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); + +CONTENT_EXPORT scoped_ptr<blink::WebCryptoDigestor> CreateDigestor( + blink::WebCryptoAlgorithmId algorithm); + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_ALGORITHM_DISPATCH_H_ diff --git a/content/child/webcrypto/algorithm_implementation.cc b/content/child/webcrypto/algorithm_implementation.cc new file mode 100644 index 0000000..321a5c3 --- /dev/null +++ b/content/child/webcrypto/algorithm_implementation.cc @@ -0,0 +1,152 @@ +// 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_implementation.h" + +#include "content/child/webcrypto/status.h" + +namespace content { + +namespace webcrypto { + +AlgorithmImplementation::~AlgorithmImplementation() { +} + +Status AlgorithmImplementation::Encrypt( + const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const { + return Status::ErrorUnsupported(); +} + +Status AlgorithmImplementation::Decrypt( + const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const { + return Status::ErrorUnsupported(); +} + +Status AlgorithmImplementation::Sign(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const { + return Status::ErrorUnsupported(); +} + +Status AlgorithmImplementation::Verify( + const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& signature, + const CryptoData& data, + bool* signature_match) const { + return Status::ErrorUnsupported(); +} + +Status AlgorithmImplementation::Digest( + const blink::WebCryptoAlgorithm& algorithm, + const CryptoData& data, + std::vector<uint8>* buffer) const { + return Status::ErrorUnsupported(); +} + +Status AlgorithmImplementation::VerifyKeyUsagesBeforeGenerateKey( + blink::WebCryptoKeyUsageMask usage_mask) const { + return Status::ErrorUnsupported(); +} + +Status AlgorithmImplementation::VerifyKeyUsagesBeforeGenerateKeyPair( + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKeyUsageMask* public_usage_mask, + blink::WebCryptoKeyUsageMask* private_usage_mask) const { + *public_usage_mask = *private_usage_mask = 0; + return Status::ErrorUnsupported(); +} + +Status AlgorithmImplementation::GenerateSecretKey( + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const { + return Status::ErrorUnsupported(); +} + +Status AlgorithmImplementation::GenerateKeyPair( + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask public_usage_mask, + blink::WebCryptoKeyUsageMask private_usage_mask, + blink::WebCryptoKey* public_key, + blink::WebCryptoKey* private_key) const { + return Status::ErrorUnsupported(); +} + +Status AlgorithmImplementation::VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usage_mask) const { + return Status::ErrorUnsupportedImportKeyFormat(); +} + +Status AlgorithmImplementation::ImportKeyRaw( + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const { + return Status::ErrorUnsupportedImportKeyFormat(); +} + +Status AlgorithmImplementation::ImportKeyPkcs8( + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const { + return Status::ErrorUnsupportedImportKeyFormat(); +} + +Status AlgorithmImplementation::ImportKeySpki( + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const { + return Status::ErrorUnsupportedImportKeyFormat(); +} + +Status AlgorithmImplementation::ImportKeyJwk( + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const { + return Status::ErrorUnsupportedImportKeyFormat(); +} + +Status AlgorithmImplementation::ExportKeyRaw(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const { + return Status::ErrorUnsupportedExportKeyFormat(); +} + +Status AlgorithmImplementation::ExportKeyPkcs8( + const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const { + return Status::ErrorUnsupportedExportKeyFormat(); +} + +Status AlgorithmImplementation::ExportKeySpki( + const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const { + return Status::ErrorUnsupportedExportKeyFormat(); +} + +Status AlgorithmImplementation::ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const { + return Status::ErrorUnsupportedExportKeyFormat(); +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/algorithm_implementation.h b/content/child/webcrypto/algorithm_implementation.h new file mode 100644 index 0000000..9982ae8 --- /dev/null +++ b/content/child/webcrypto/algorithm_implementation.h @@ -0,0 +1,173 @@ +// 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. + +#ifndef CONTENT_CHILD_WEBCRYPTO_CRYPTO_ALGORITHM_IMPLEMENTATION_H_ +#define CONTENT_CHILD_WEBCRYPTO_CRYPTO_ALGORITHM_IMPLEMENTATION_H_ + +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "third_party/WebKit/public/platform/WebCrypto.h" + +namespace content { + +namespace webcrypto { + +class CryptoData; +class Status; + +// AlgorithmImplementation is a base class for *executing* the operations of an +// algorithm (generating keys, encrypting, signing, etc.). +// +// This is in contrast to blink::WebCryptoAlgorithm which instead *describes* +// the operation and its parameters. +// +// AlgorithmImplementation has reasonable default implementations for all +// methods which behave as if the operation is it is unsupported, so +// implementations need only override the applicable methods. +// +// Unless stated otherwise methods of AlgorithmImplementation are responsible +// for sanitizing their inputs. The following can be assumed: +// +// * |algorithm.id()| and |key.algorithm.id()| matches the algorithm under +// which the implementation was registerd. +// * |algorithm| has the correct parameters type for the operation. +// * The key usages have already been verified. In fact in the case of calls +// to Encrypt()/Decrypt() the corresponding key usages may not be present +// (when wrapping/unwrapping). +class AlgorithmImplementation { + public: + virtual ~AlgorithmImplementation(); + + // This method corresponds to Web Crypto's crypto.subtle.encrypt(). + virtual Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const; + + // This method corresponds to Web Crypto's crypto.subtle.decrypt(). + virtual Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const; + + // This method corresponds to Web Crypto's crypto.subtle.sign(). + virtual Status Sign(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const; + + // This method corresponds to Web Crypto's crypto.subtle.verify(). + virtual Status Verify(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& signature, + const CryptoData& data, + bool* signature_match) const; + + // This method corresponds to Web Crypto's crypto.subtle.digest(). + virtual Status Digest(const blink::WebCryptoAlgorithm& algorithm, + const CryptoData& data, + std::vector<uint8>* buffer) const; + + // VerifyKeyUsagesBeforeGenerateKey() must be called prior to + // GenerateSecretKey() to validate the requested key usages. + virtual Status VerifyKeyUsagesBeforeGenerateKey( + blink::WebCryptoKeyUsageMask usage_mask) const; + + // This method corresponds to Web Crypto's crypto.subtle.generateKey(). + virtual Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const; + + // VerifyKeyUsagesBeforeGenerateKeyPair() must be called prior to + // GenerateKeyPair() to validate the requested key usages. + virtual Status VerifyKeyUsagesBeforeGenerateKeyPair( + blink::WebCryptoKeyUsageMask combined_usage_mask, + blink::WebCryptoKeyUsageMask* public_usage_mask, + blink::WebCryptoKeyUsageMask* private_usage_mask) const; + + // This method corresponds to Web Crypto's crypto.subtle.generateKey(). + virtual Status GenerateKeyPair( + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask public_usage_mask, + blink::WebCryptoKeyUsageMask private_usage_mask, + blink::WebCryptoKey* public_key, + blink::WebCryptoKey* private_key) const; + + // ----------------------------------------------- + // Key import + // ----------------------------------------------- + + // VerifyKeyUsagesBeforeImportKey() must be called before either + // importing a key, or unwrapping a key. + // + // Implementations should return an error if the requested usages are invalid + // when importing for the specified format. + // + // For instance, importing an RSA-SSA key with 'spki' format and Sign usage + // is invalid. The 'spki' format implies it will be a public key, and public + // keys do not support signing. + // + // When called with format=JWK the key type may be unknown. The + // ImportKeyJwk() must do the final usage check. + virtual Status VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usage_mask) const; + + // This method corresponds to Web Crypto's + // crypto.subtle.importKey(format='raw'). + virtual Status ImportKeyRaw(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const; + + // This method corresponds to Web Crypto's + // crypto.subtle.importKey(format='pkcs8'). + virtual Status ImportKeyPkcs8(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const; + + // This method corresponds to Web Crypto's + // crypto.subtle.importKey(format='spki'). + virtual Status ImportKeySpki(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const; + + // This method corresponds to Web Crypto's + // crypto.subtle.importKey(format='jwk'). + virtual Status ImportKeyJwk(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const; + + // ----------------------------------------------- + // Key export + // ----------------------------------------------- + + virtual Status ExportKeyRaw(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const; + + virtual Status ExportKeyPkcs8(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const; + + virtual Status ExportKeySpki(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const; + + virtual Status ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const; +}; + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_CRYPTO_ALGORITHM_IMPLEMENTATION_H_ diff --git a/content/child/webcrypto/algorithm_registry.cc b/content/child/webcrypto/algorithm_registry.cc new file mode 100644 index 0000000..eda5ba2 --- /dev/null +++ b/content/child/webcrypto/algorithm_registry.cc @@ -0,0 +1,81 @@ +// 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_registry.h" + +#include "base/lazy_instance.h" +#include "content/child/webcrypto/algorithm_implementation.h" +#include "content/child/webcrypto/platform_crypto.h" +#include "content/child/webcrypto/status.h" + +namespace content { + +namespace webcrypto { + +namespace { + +class AlgorithmRegistry { + public: + AlgorithmRegistry() + : sha_(CreatePlatformShaImplementation()), + aes_gcm_(CreatePlatformAesGcmImplementation()), + aes_cbc_(CreatePlatformAesCbcImplementation()), + aes_kw_(CreatePlatformAesKwImplementation()), + hmac_(CreatePlatformHmacImplementation()), + rsa_ssa_(CreatePlatformRsaSsaImplementation()), + rsa_oaep_(CreatePlatformRsaOaepImplementation()) { + PlatformInit(); + } + + const AlgorithmImplementation* GetAlgorithm( + blink::WebCryptoAlgorithmId id) const { + switch (id) { + case blink::WebCryptoAlgorithmIdSha1: + case blink::WebCryptoAlgorithmIdSha256: + case blink::WebCryptoAlgorithmIdSha384: + case blink::WebCryptoAlgorithmIdSha512: + return sha_.get(); + case blink::WebCryptoAlgorithmIdAesGcm: + return aes_gcm_.get(); + case blink::WebCryptoAlgorithmIdAesCbc: + return aes_cbc_.get(); + case blink::WebCryptoAlgorithmIdAesKw: + return aes_kw_.get(); + case blink::WebCryptoAlgorithmIdHmac: + return hmac_.get(); + case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: + return rsa_ssa_.get(); + case blink::WebCryptoAlgorithmIdRsaOaep: + return rsa_oaep_.get(); + default: + return NULL; + } + } + + private: + scoped_ptr<AlgorithmImplementation> sha_; + scoped_ptr<AlgorithmImplementation> aes_gcm_; + scoped_ptr<AlgorithmImplementation> aes_cbc_; + scoped_ptr<AlgorithmImplementation> aes_kw_; + scoped_ptr<AlgorithmImplementation> hmac_; + scoped_ptr<AlgorithmImplementation> rsa_ssa_; + scoped_ptr<AlgorithmImplementation> rsa_oaep_; +}; + +} // namespace + +base::LazyInstance<AlgorithmRegistry>::Leaky g_algorithm_registry = + LAZY_INSTANCE_INITIALIZER; + +Status GetAlgorithmImplementation(blink::WebCryptoAlgorithmId id, + const AlgorithmImplementation** impl) { + *impl = g_algorithm_registry.Get().GetAlgorithm(id); + if (*impl) + return Status::Success(); + return Status::ErrorUnsupported(); +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/algorithm_registry.h b/content/child/webcrypto/algorithm_registry.h new file mode 100644 index 0000000..6db5f5f --- /dev/null +++ b/content/child/webcrypto/algorithm_registry.h @@ -0,0 +1,32 @@ +// 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. + +#ifndef CONTENT_CHILD_WEBCRYPTO_ALGORITHM_REGISTRY_H_ +#define CONTENT_CHILD_WEBCRYPTO_ALGORITHM_REGISTRY_H_ + +#include "third_party/WebKit/public/platform/WebCrypto.h" + +namespace content { + +namespace webcrypto { + +class AlgorithmImplementation; +class Status; + +// Retrieves the AlgorithmImplementation applicable for |id|. +// +// If there is no available implementation, then an error is returned, and +// *impl is set to NULL. +// +// Otherwise Success is returned and *impl is set to a non-NULL value. The +// AlgorithmImplementation pointer will remain valid until the program's +// termination. +Status GetAlgorithmImplementation(blink::WebCryptoAlgorithmId id, + const AlgorithmImplementation** impl); + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_ALGORITHM_REGISTRY_H_ diff --git a/content/child/webcrypto/jwk.cc b/content/child/webcrypto/jwk.cc index 24683aa..b6e8731 100644 --- a/content/child/webcrypto/jwk.cc +++ b/content/child/webcrypto/jwk.cc @@ -10,15 +10,15 @@ #include "base/json/json_reader.h" #include "base/json/json_writer.h" -#include "base/lazy_instance.h" #include "base/strings/string_piece.h" -#include "base/strings/stringprintf.h" #include "content/child/webcrypto/crypto_data.h" -#include "content/child/webcrypto/platform_crypto.h" -#include "content/child/webcrypto/shared_crypto.h" #include "content/child/webcrypto/status.h" #include "content/child/webcrypto/webcrypto_util.h" -#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +// TODO(eroman): The algorithm-specific logic in this file for AES and RSA +// should be moved into the corresponding AlgorithmImplementation file. It +// exists in this file to avoid duplication between OpenSSL and NSS +// implementations. // JSON Web Key Format (JWK) // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-21 @@ -209,22 +209,6 @@ namespace webcrypto { namespace { -// Creates an RSASSA-PKCS1-v1_5 algorithm. It is an error to call this with a -// hash_id that is not a SHA*. -blink::WebCryptoAlgorithm CreateRsaSsaImportAlgorithm( - blink::WebCryptoAlgorithmId hash_id) { - return CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, hash_id); -} - -// Creates an RSA-OAEP algorithm. It is an error to call this with a hash_id -// that is not a SHA*. -blink::WebCryptoAlgorithm CreateRsaOaepImportAlgorithm( - blink::WebCryptoAlgorithmId hash_id) { - return CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep, - hash_id); -} - // Web Crypto equivalent usage mask for JWK 'use' = 'enc'. const blink::WebCryptoKeyUsageMask kJwkEncUsage = blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | @@ -234,165 +218,39 @@ const blink::WebCryptoKeyUsageMask kJwkEncUsage = const blink::WebCryptoKeyUsageMask kJwkSigUsage = blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; -typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)(); - -class JwkAlgorithmInfo { +class JwkWriter { public: - JwkAlgorithmInfo() - : creation_func_(NULL), - required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {} - - explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func) - : creation_func_(algorithm_creation_func), - required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {} - - JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func, - unsigned int required_key_length_bits) - : creation_func_(algorithm_creation_func), - required_key_length_bytes_(required_key_length_bits / 8) { - DCHECK_EQ(0u, required_key_length_bits % 8); - } - - bool CreateImportAlgorithm(blink::WebCryptoAlgorithm* algorithm) const { - *algorithm = creation_func_(); - return !algorithm->isNull(); + JwkWriter(const std::string& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + const std::string& kty) { + dict_.SetString("alg", algorithm); + dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usage_mask)); + dict_.SetBoolean("ext", extractable); + dict_.SetString("kty", kty); } - bool IsInvalidKeyByteLength(size_t byte_length) const { - if (required_key_length_bytes_ == NO_KEY_SIZE_REQUIREMENT) - return false; - return required_key_length_bytes_ != byte_length; + void Set(const std::string& key, const std::string& value) { + dict_.SetString(key, value); } - private: - static const unsigned int NO_KEY_SIZE_REQUIREMENT = UINT_MAX; - - AlgorithmCreationFunc creation_func_; - - // The expected key size for the algorithm or NO_KEY_SIZE_REQUIREMENT. - unsigned int required_key_length_bytes_; -}; - -typedef std::map<std::string, JwkAlgorithmInfo> JwkAlgorithmInfoMap; - -class JwkAlgorithmRegistry { - public: - JwkAlgorithmRegistry() { - // TODO(eroman): - // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-20 - // says HMAC with SHA-2 should have a key size at least as large as the - // hash output. - alg_to_info_["HS1"] = - JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, - blink::WebCryptoAlgorithmIdSha1>); - alg_to_info_["HS256"] = - JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, - blink::WebCryptoAlgorithmIdSha256>); - alg_to_info_["HS384"] = - JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, - blink::WebCryptoAlgorithmIdSha384>); - alg_to_info_["HS512"] = - JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, - blink::WebCryptoAlgorithmIdSha512>); - alg_to_info_["RS1"] = - JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, - blink::WebCryptoAlgorithmIdSha1>); - alg_to_info_["RS256"] = - JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, - blink::WebCryptoAlgorithmIdSha256>); - alg_to_info_["RS384"] = - JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, - blink::WebCryptoAlgorithmIdSha384>); - alg_to_info_["RS512"] = - JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, - blink::WebCryptoAlgorithmIdSha512>); - alg_to_info_["RSA-OAEP"] = - JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm, - blink::WebCryptoAlgorithmIdSha1>); - alg_to_info_["RSA-OAEP-256"] = - JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm, - blink::WebCryptoAlgorithmIdSha256>); - alg_to_info_["RSA-OAEP-384"] = - JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm, - blink::WebCryptoAlgorithmIdSha384>); - alg_to_info_["RSA-OAEP-512"] = - JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm, - blink::WebCryptoAlgorithmIdSha512>); - alg_to_info_["A128KW"] = JwkAlgorithmInfo( - &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>, - 128); - alg_to_info_["A192KW"] = JwkAlgorithmInfo( - &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>, - 192); - alg_to_info_["A256KW"] = JwkAlgorithmInfo( - &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>, - 256); - alg_to_info_["A128GCM"] = JwkAlgorithmInfo( - &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>, - 128); - alg_to_info_["A192GCM"] = JwkAlgorithmInfo( - &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>, - 192); - alg_to_info_["A256GCM"] = JwkAlgorithmInfo( - &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>, - 256); - alg_to_info_["A128CBC"] = JwkAlgorithmInfo( - &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>, - 128); - alg_to_info_["A192CBC"] = JwkAlgorithmInfo( - &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>, - 192); - alg_to_info_["A256CBC"] = JwkAlgorithmInfo( - &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>, - 256); + void SetBase64Encoded(const std::string& key, const CryptoData& value) { + dict_.SetString(key, + Base64EncodeUrlSafe(base::StringPiece( + reinterpret_cast<const char*>(value.bytes()), + value.byte_length()))); } - // Returns NULL if the algorithm name was not registered. - const JwkAlgorithmInfo* GetAlgorithmInfo(const std::string& jwk_alg) const { - const JwkAlgorithmInfoMap::const_iterator pos = alg_to_info_.find(jwk_alg); - if (pos == alg_to_info_.end()) - return NULL; - return &pos->second; + void ToBytes(std::vector<uint8>* utf8_bytes) { + std::string json; + base::JSONWriter::Write(&dict_, &json); + utf8_bytes->assign(json.begin(), json.end()); } private: - // Binds a WebCryptoAlgorithmId value to a compatible factory function. - typedef blink::WebCryptoAlgorithm (*FuncWithWebCryptoAlgIdArg)( - blink::WebCryptoAlgorithmId); - template <FuncWithWebCryptoAlgIdArg func, - blink::WebCryptoAlgorithmId algorithm_id> - static blink::WebCryptoAlgorithm BindAlgorithmId() { - return func(algorithm_id); - } - - JwkAlgorithmInfoMap alg_to_info_; + base::DictionaryValue dict_; }; -base::LazyInstance<JwkAlgorithmRegistry> jwk_alg_registry = - LAZY_INSTANCE_INITIALIZER; - -bool ImportAlgorithmsConsistent(const blink::WebCryptoAlgorithm& alg1, - const blink::WebCryptoAlgorithm& alg2) { - DCHECK(!alg1.isNull()); - DCHECK(!alg2.isNull()); - if (alg1.id() != alg2.id()) - return false; - if (alg1.paramsType() != alg2.paramsType()) - return false; - switch (alg1.paramsType()) { - case blink::WebCryptoAlgorithmParamsTypeNone: - return true; - case blink::WebCryptoAlgorithmParamsTypeRsaHashedImportParams: - return ImportAlgorithmsConsistent(alg1.rsaHashedImportParams()->hash(), - alg2.rsaHashedImportParams()->hash()); - case blink::WebCryptoAlgorithmParamsTypeHmacImportParams: - return ImportAlgorithmsConsistent(alg1.hmacImportParams()->hash(), - alg2.hmacImportParams()->hash()); - default: - return false; - } -} - // Extracts the required string property with key |path| from |dict| and saves // the result to |*result|. If the property does not exist or is not a string, // returns an error. @@ -509,508 +367,353 @@ Status GetOptionalJwkBool(base::DictionaryValue* dict, return Status::Success(); } -// Writes a secret/symmetric key to a JWK dictionary. -void WriteSecretKey(const std::vector<uint8>& raw_key, - base::DictionaryValue* jwk_dict) { - DCHECK(jwk_dict); - jwk_dict->SetString("kty", "oct"); - // For a secret/symmetric key, the only extra JWK field is 'k', containing the - // base64url encoding of the raw key. - const base::StringPiece key_str( - reinterpret_cast<const char*>(Uint8VectorStart(raw_key)), raw_key.size()); - jwk_dict->SetString("k", Base64EncodeUrlSafe(key_str)); -} - -// Writes an RSA public key to a JWK dictionary -void WriteRsaPublicKey(const std::vector<uint8>& modulus, - const std::vector<uint8>& public_exponent, - base::DictionaryValue* jwk_dict) { - DCHECK(jwk_dict); - DCHECK(modulus.size()); - DCHECK(public_exponent.size()); - jwk_dict->SetString("kty", "RSA"); - jwk_dict->SetString("n", Base64EncodeUrlSafe(modulus)); - jwk_dict->SetString("e", Base64EncodeUrlSafe(public_exponent)); +Status VerifyExt(base::DictionaryValue* dict, bool expected_extractable) { + // JWK "ext" (optional) --> extractable parameter + bool jwk_ext_value = false; + bool has_jwk_ext; + Status status = GetOptionalJwkBool(dict, "ext", &jwk_ext_value, &has_jwk_ext); + if (status.IsError()) + return status; + if (has_jwk_ext && expected_extractable && !jwk_ext_value) + return Status::ErrorJwkExtInconsistent(); + return Status::Success(); } -// Writes an RSA private key to a JWK dictionary -Status ExportRsaPrivateKeyJwk(const blink::WebCryptoKey& key, - base::DictionaryValue* jwk_dict) { - platform::PrivateKey* private_key; - Status status = ToPlatformPrivateKey(key, &private_key); +Status VerifyUsages(base::DictionaryValue* dict, + blink::WebCryptoKeyUsageMask expected_usage_mask) { + // JWK "key_ops" (optional) --> usage_mask parameter + base::ListValue* jwk_key_ops_value = NULL; + bool has_jwk_key_ops; + Status status = + GetOptionalJwkList(dict, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops); if (status.IsError()) return status; + blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0; + if (has_jwk_key_ops) { + status = + GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask); + if (status.IsError()) + return status; + // The input usage_mask must be a subset of jwk_key_ops_mask. + if (!ContainsKeyUsages(jwk_key_ops_mask, expected_usage_mask)) + return Status::ErrorJwkKeyopsInconsistent(); + } - // TODO(eroman): Copying the key properties to temporary vectors is - // inefficient. Once there aren't two implementations of platform_crypto this - // and other code will be easier to streamline. - std::vector<uint8> modulus; - std::vector<uint8> public_exponent; - std::vector<uint8> private_exponent; - std::vector<uint8> prime1; - std::vector<uint8> prime2; - std::vector<uint8> exponent1; - std::vector<uint8> exponent2; - std::vector<uint8> coefficient; - - status = platform::ExportRsaPrivateKey(private_key, - &modulus, - &public_exponent, - &private_exponent, - &prime1, - &prime2, - &exponent1, - &exponent2, - &coefficient); + // JWK "use" (optional) --> usage_mask parameter + std::string jwk_use_value; + bool has_jwk_use; + status = GetOptionalJwkString(dict, "use", &jwk_use_value, &has_jwk_use); if (status.IsError()) return status; + blink::WebCryptoKeyUsageMask jwk_use_mask = 0; + if (has_jwk_use) { + if (jwk_use_value == "enc") + jwk_use_mask = kJwkEncUsage; + else if (jwk_use_value == "sig") + jwk_use_mask = kJwkSigUsage; + else + return Status::ErrorJwkUnrecognizedUse(); + // The input usage_mask must be a subset of jwk_use_mask. + if (!ContainsKeyUsages(jwk_use_mask, expected_usage_mask)) + return Status::ErrorJwkUseInconsistent(); + } - jwk_dict->SetString("kty", "RSA"); - jwk_dict->SetString("n", Base64EncodeUrlSafe(modulus)); - jwk_dict->SetString("e", Base64EncodeUrlSafe(public_exponent)); - jwk_dict->SetString("d", Base64EncodeUrlSafe(private_exponent)); - // Although these are "optional" in the JWA, WebCrypto spec requires them to - // be emitted. - jwk_dict->SetString("p", Base64EncodeUrlSafe(prime1)); - jwk_dict->SetString("q", Base64EncodeUrlSafe(prime2)); - jwk_dict->SetString("dp", Base64EncodeUrlSafe(exponent1)); - jwk_dict->SetString("dq", Base64EncodeUrlSafe(exponent2)); - jwk_dict->SetString("qi", Base64EncodeUrlSafe(coefficient)); + // If both 'key_ops' and 'use' are present, ensure they are consistent. + if (has_jwk_key_ops && has_jwk_use && + !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask)) + return Status::ErrorJwkUseAndKeyopsInconsistent(); return Status::Success(); } -// Writes a Web Crypto usage mask to a JWK dictionary. -void WriteKeyOps(blink::WebCryptoKeyUsageMask key_usages, - base::DictionaryValue* jwk_dict) { - jwk_dict->Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(key_usages)); -} +Status VerifyAlg(base::DictionaryValue* dict, + const std::string& expected_algorithm) { + // JWK "alg" --> algorithm parameter + bool has_jwk_alg; + std::string jwk_alg_value; + Status status = + GetOptionalJwkString(dict, "alg", &jwk_alg_value, &has_jwk_alg); + if (status.IsError()) + return status; -// Writes a Web Crypto extractable value to a JWK dictionary. -void WriteExt(bool extractable, base::DictionaryValue* jwk_dict) { - jwk_dict->SetBoolean("ext", extractable); -} + if (has_jwk_alg && jwk_alg_value != expected_algorithm) + return Status::ErrorJwkAlgorithmInconsistent(); -// Writes a Web Crypto algorithm to a JWK dictionary. -Status WriteAlg(const blink::WebCryptoKeyAlgorithm& algorithm, - base::DictionaryValue* jwk_dict) { - switch (algorithm.paramsType()) { - case blink::WebCryptoKeyAlgorithmParamsTypeAes: { - DCHECK(algorithm.aesParams()); - const char* aes_prefix = ""; - switch (algorithm.aesParams()->lengthBits()) { - case 128: - aes_prefix = "A128"; - break; - case 192: - aes_prefix = "A192"; - break; - case 256: - aes_prefix = "A256"; - break; - default: - NOTREACHED(); // bad key length means algorithm was built improperly - return Status::ErrorUnexpected(); - } - const char* aes_suffix = ""; - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdAesCbc: - aes_suffix = "CBC"; - break; - case blink::WebCryptoAlgorithmIdAesCtr: - aes_suffix = "CTR"; - break; - case blink::WebCryptoAlgorithmIdAesGcm: - aes_suffix = "GCM"; - break; - case blink::WebCryptoAlgorithmIdAesKw: - aes_suffix = "KW"; - break; - default: - return Status::ErrorUnsupported(); - } - jwk_dict->SetString("alg", - base::StringPrintf("%s%s", aes_prefix, aes_suffix)); - break; - } - case blink::WebCryptoKeyAlgorithmParamsTypeHmac: { - DCHECK(algorithm.hmacParams()); - switch (algorithm.hmacParams()->hash().id()) { - case blink::WebCryptoAlgorithmIdSha1: - jwk_dict->SetString("alg", "HS1"); - break; - case blink::WebCryptoAlgorithmIdSha256: - jwk_dict->SetString("alg", "HS256"); - break; - case blink::WebCryptoAlgorithmIdSha384: - jwk_dict->SetString("alg", "HS384"); - break; - case blink::WebCryptoAlgorithmIdSha512: - jwk_dict->SetString("alg", "HS512"); - break; - default: - NOTREACHED(); - return Status::ErrorUnexpected(); - } - break; - } - case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed: - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: { - switch (algorithm.rsaHashedParams()->hash().id()) { - case blink::WebCryptoAlgorithmIdSha1: - jwk_dict->SetString("alg", "RS1"); - break; - case blink::WebCryptoAlgorithmIdSha256: - jwk_dict->SetString("alg", "RS256"); - break; - case blink::WebCryptoAlgorithmIdSha384: - jwk_dict->SetString("alg", "RS384"); - break; - case blink::WebCryptoAlgorithmIdSha512: - jwk_dict->SetString("alg", "RS512"); - break; - default: - NOTREACHED(); - return Status::ErrorUnexpected(); - } - break; - } - case blink::WebCryptoAlgorithmIdRsaOaep: { - switch (algorithm.rsaHashedParams()->hash().id()) { - case blink::WebCryptoAlgorithmIdSha1: - jwk_dict->SetString("alg", "RSA-OAEP"); - break; - case blink::WebCryptoAlgorithmIdSha256: - jwk_dict->SetString("alg", "RSA-OAEP-256"); - break; - case blink::WebCryptoAlgorithmIdSha384: - jwk_dict->SetString("alg", "RSA-OAEP-384"); - break; - case blink::WebCryptoAlgorithmIdSha512: - jwk_dict->SetString("alg", "RSA-OAEP-512"); - break; - default: - NOTREACHED(); - return Status::ErrorUnexpected(); - } - break; - } - default: - NOTREACHED(); - return Status::ErrorUnexpected(); - } - break; - default: - return Status::ErrorUnsupported(); - } return Status::Success(); } -bool IsRsaKey(const blink::WebCryptoKey& key) { - return IsAlgorithmRsa(key.algorithm().id()); -} +Status ParseJwkCommon(const CryptoData& bytes, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usage_mask, + std::string* kty, + scoped_ptr<base::DictionaryValue>* dict) { + // Parse the incoming JWK JSON. + base::StringPiece json_string(reinterpret_cast<const char*>(bytes.bytes()), + bytes.byte_length()); -Status ImportRsaKey(base::DictionaryValue* dict, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key) { - // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry - // in the JWK, while an RSA private key must have those, plus at least a "d" - // (private exponent) entry. - // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, - // section 6.3. - std::string jwk_n_value; - Status status = GetJwkBytes(dict, "n", &jwk_n_value); - if (status.IsError()) - return status; - std::string jwk_e_value; - status = GetJwkBytes(dict, "e", &jwk_e_value); - if (status.IsError()) - return status; + scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); + base::DictionaryValue* dict_value = NULL; - bool is_public_key = !dict->HasKey("d"); + if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value) + return Status::ErrorJwkNotDictionary(); - // Now that the key type is known, do an additional check on the usages to - // make sure they are all applicable for this algorithm + key type. - status = CheckKeyUsages(algorithm.id(), - is_public_key ? blink::WebCryptoKeyTypePublic - : blink::WebCryptoKeyTypePrivate, - usage_mask); + // Release |value|, as ownership will be transferred to |dict| via + // |dict_value|, which points to the same object as |value|. + ignore_result(value.release()); + dict->reset(dict_value); + // JWK "kty". Exit early if this required JWK parameter is missing. + Status status = GetJwkString(dict_value, "kty", kty); if (status.IsError()) return status; - if (is_public_key) { - return platform::ImportRsaPublicKey(algorithm, - extractable, - usage_mask, - CryptoData(jwk_n_value), - CryptoData(jwk_e_value), - key); - } + status = VerifyExt(dict_value, expected_extractable); + if (status.IsError()) + return status; - std::string jwk_d_value; - status = GetJwkBytes(dict, "d", &jwk_d_value); + status = VerifyUsages(dict_value, expected_usage_mask); if (status.IsError()) return status; - // The "p", "q", "dp", "dq", and "qi" properties are optional. Treat these - // properties the same if they are unspecified, as if they were specified-but - // empty, since ImportRsaPrivateKey() doesn't do validation checks anyway. + return Status::Success(); +} - std::string jwk_p_value; - bool has_p; - status = GetOptionalJwkBytes(dict, "p", &jwk_p_value, &has_p); +Status ReadSecretKeyNoExpectedAlg( + const CryptoData& key_data, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usage_mask, + std::vector<uint8>* raw_key_data, + scoped_ptr<base::DictionaryValue>* dict) { + if (!key_data.byte_length()) + return Status::ErrorImportEmptyKeyData(); + + std::string kty; + Status status = ParseJwkCommon( + key_data, expected_extractable, expected_usage_mask, &kty, dict); if (status.IsError()) return status; - std::string jwk_q_value; - bool has_q; - status = GetOptionalJwkBytes(dict, "q", &jwk_q_value, &has_q); + if (kty != "oct") + return Status::ErrorJwkUnexpectedKty("oct"); + + std::string jwk_k_value; + status = GetJwkBytes(dict->get(), "k", &jwk_k_value); if (status.IsError()) return status; + raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end()); - std::string jwk_dp_value; - bool has_dp; - status = GetOptionalJwkBytes(dict, "dp", &jwk_dp_value, &has_dp); + return Status::Success(); +} + +} // namespace + +void WriteSecretKeyJwk(const CryptoData& raw_key_data, + const std::string& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + std::vector<uint8>* jwk_key_data) { + JwkWriter writer(algorithm, extractable, usage_mask, "oct"); + writer.SetBase64Encoded("k", raw_key_data); + writer.ToBytes(jwk_key_data); +} + +Status ReadSecretKeyJwk(const CryptoData& key_data, + const std::string& expected_algorithm, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usage_mask, + std::vector<uint8>* raw_key_data) { + scoped_ptr<base::DictionaryValue> dict; + Status status = ReadSecretKeyNoExpectedAlg( + key_data, expected_extractable, expected_usage_mask, raw_key_data, &dict); if (status.IsError()) return status; + return VerifyAlg(dict.get(), expected_algorithm); +} - std::string jwk_dq_value; - bool has_dq; - status = GetOptionalJwkBytes(dict, "dq", &jwk_dq_value, &has_dq); +std::string MakeJwkAesAlgorithmName(const std::string& suffix, + unsigned int keylen_bytes) { + if (keylen_bytes == 16) + return std::string("A128") + suffix; + if (keylen_bytes == 24) + return std::string("A192") + suffix; + if (keylen_bytes == 32) + return std::string("A256") + suffix; + return std::string(); +} + +Status ReadAesSecretKeyJwk(const CryptoData& key_data, + const std::string& algorithm_name_suffix, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usage_mask, + std::vector<uint8>* raw_key_data) { + scoped_ptr<base::DictionaryValue> dict; + Status status = ReadSecretKeyNoExpectedAlg( + key_data, expected_extractable, expected_usage_mask, raw_key_data, &dict); if (status.IsError()) return status; - std::string jwk_qi_value; - bool has_qi; - status = GetOptionalJwkBytes(dict, "qi", &jwk_qi_value, &has_qi); + bool has_jwk_alg; + std::string jwk_alg; + status = GetOptionalJwkString(dict.get(), "alg", &jwk_alg, &has_jwk_alg); if (status.IsError()) return status; - int num_optional_properties = has_p + has_q + has_dp + has_dq + has_qi; - if (num_optional_properties != 0 && num_optional_properties != 5) - return Status::ErrorJwkIncompleteOptionalRsaPrivateKey(); + if (has_jwk_alg) { + std::string expected_algorithm_name = + MakeJwkAesAlgorithmName(algorithm_name_suffix, raw_key_data->size()); + + if (jwk_alg != expected_algorithm_name) { + // Give a different error message if the key length was wrong. + if (jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 16) || + jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 24) || + jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 32)) { + return Status::ErrorJwkIncorrectKeyLength(); + } + return Status::ErrorJwkAlgorithmInconsistent(); + } + } - return platform::ImportRsaPrivateKey( - algorithm, - extractable, - usage_mask, - CryptoData(jwk_n_value), // modulus - CryptoData(jwk_e_value), // public_exponent - CryptoData(jwk_d_value), // private_exponent - CryptoData(jwk_p_value), // prime1 - CryptoData(jwk_q_value), // prime2 - CryptoData(jwk_dp_value), // exponent1 - CryptoData(jwk_dq_value), // exponent2 - CryptoData(jwk_qi_value), // coefficient - key); + return Status::Success(); } -} // namespace +// Writes an RSA public key to a JWK dictionary +void WriteRsaPublicKeyJwk(const CryptoData& n, + const CryptoData& e, + const std::string& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + std::vector<uint8>* jwk_key_data) { + JwkWriter writer(algorithm, extractable, usage_mask, "RSA"); + writer.SetBase64Encoded("n", n); + writer.SetBase64Encoded("e", e); + writer.ToBytes(jwk_key_data); +} + +// Writes an RSA private key to a JWK dictionary +void WriteRsaPrivateKeyJwk(const CryptoData& n, + const CryptoData& e, + const CryptoData& d, + const CryptoData& p, + const CryptoData& q, + const CryptoData& dp, + const CryptoData& dq, + const CryptoData& qi, + const std::string& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + std::vector<uint8>* jwk_key_data) { + JwkWriter writer(algorithm, extractable, usage_mask, "RSA"); + + writer.SetBase64Encoded("n", n); + writer.SetBase64Encoded("e", e); + writer.SetBase64Encoded("d", d); + // Although these are "optional" in the JWA, WebCrypto spec requires them to + // be emitted. + writer.SetBase64Encoded("p", p); + writer.SetBase64Encoded("q", q); + writer.SetBase64Encoded("dp", dp); + writer.SetBase64Encoded("dq", dq); + writer.SetBase64Encoded("qi", qi); + writer.ToBytes(jwk_key_data); +} + +JwkRsaInfo::JwkRsaInfo() : is_private_key(false) { +} -// TODO(eroman): Split this up into smaller functions. -Status ImportKeyJwk(const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key) { +JwkRsaInfo::~JwkRsaInfo() { +} + +Status ReadRsaKeyJwk(const CryptoData& key_data, + const std::string& expected_algorithm, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usage_mask, + JwkRsaInfo* result) { if (!key_data.byte_length()) return Status::ErrorImportEmptyKeyData(); - DCHECK(key); - // Parse the incoming JWK JSON. - base::StringPiece json_string(reinterpret_cast<const char*>(key_data.bytes()), - key_data.byte_length()); - scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); - // Note, bare pointer dict_value is ok since it points into scoped value. - base::DictionaryValue* dict_value = NULL; - if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value) - return Status::ErrorJwkNotDictionary(); + scoped_ptr<base::DictionaryValue> dict; + std::string kty; + Status status = ParseJwkCommon( + key_data, expected_extractable, expected_usage_mask, &kty, &dict); + if (status.IsError()) + return status; - // JWK "kty". Exit early if this required JWK parameter is missing. - std::string jwk_kty_value; - Status status = GetJwkString(dict_value, "kty", &jwk_kty_value); + status = VerifyAlg(dict.get(), expected_algorithm); if (status.IsError()) return status; - // JWK "ext" (optional) --> extractable parameter - { - bool jwk_ext_value = false; - bool has_jwk_ext; - status = - GetOptionalJwkBool(dict_value, "ext", &jwk_ext_value, &has_jwk_ext); - if (status.IsError()) - return status; - if (has_jwk_ext && !jwk_ext_value && extractable) - return Status::ErrorJwkExtInconsistent(); - } + if (kty != "RSA") + return Status::ErrorJwkUnexpectedKty("RSA"); - // JWK "alg" --> algorithm parameter - // 1. JWK alg present but unrecognized: error - // 2. JWK alg valid and inconsistent with input algorithm: error - // 3. JWK alg valid and consistent with input algorithm: use input value - // 4. JWK alg is missing: use input value - const JwkAlgorithmInfo* algorithm_info = NULL; - std::string jwk_alg_value; - bool has_jwk_alg; - status = - GetOptionalJwkString(dict_value, "alg", &jwk_alg_value, &has_jwk_alg); + // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry + // in the JWK, while an RSA private key must have those, plus at least a "d" + // (private exponent) entry. + // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, + // section 6.3. + status = GetJwkBytes(dict.get(), "n", &result->n); + if (status.IsError()) + return status; + status = GetJwkBytes(dict.get(), "e", &result->e); if (status.IsError()) return status; - if (has_jwk_alg) { - // JWK alg present - - // TODO(padolph): Validate alg vs kty. For example kty="RSA" implies alg can - // only be from the RSA family. + result->is_private_key = dict->HasKey("d"); + if (!result->is_private_key) + return Status::Success(); - blink::WebCryptoAlgorithm jwk_algorithm = - blink::WebCryptoAlgorithm::createNull(); - algorithm_info = jwk_alg_registry.Get().GetAlgorithmInfo(jwk_alg_value); - if (!algorithm_info || - !algorithm_info->CreateImportAlgorithm(&jwk_algorithm)) - return Status::ErrorJwkUnrecognizedAlgorithm(); + status = GetJwkBytes(dict.get(), "d", &result->d); + if (status.IsError()) + return status; - if (!ImportAlgorithmsConsistent(jwk_algorithm, algorithm)) - return Status::ErrorJwkAlgorithmInconsistent(); - } - DCHECK(!algorithm.isNull()); + // The "p", "q", "dp", "dq", and "qi" properties are optional. Treat these + // properties the same if they are unspecified, as if they were specified-but + // empty, since ImportRsaPrivateKey() doesn't do validation checks anyway. - // JWK "key_ops" (optional) --> usage_mask parameter - base::ListValue* jwk_key_ops_value = NULL; - bool has_jwk_key_ops; - status = GetOptionalJwkList( - dict_value, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops); + bool has_p; + status = GetOptionalJwkBytes(dict.get(), "p", &result->p, &has_p); if (status.IsError()) return status; - blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0; - if (has_jwk_key_ops) { - status = - GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask); - if (status.IsError()) - return status; - // The input usage_mask must be a subset of jwk_key_ops_mask. - if (!ContainsKeyUsages(jwk_key_ops_mask, usage_mask)) - return Status::ErrorJwkKeyopsInconsistent(); - } - // JWK "use" (optional) --> usage_mask parameter - std::string jwk_use_value; - bool has_jwk_use; - status = - GetOptionalJwkString(dict_value, "use", &jwk_use_value, &has_jwk_use); + bool has_q; + status = GetOptionalJwkBytes(dict.get(), "q", &result->q, &has_q); if (status.IsError()) return status; - blink::WebCryptoKeyUsageMask jwk_use_mask = 0; - if (has_jwk_use) { - if (jwk_use_value == "enc") - jwk_use_mask = kJwkEncUsage; - else if (jwk_use_value == "sig") - jwk_use_mask = kJwkSigUsage; - else - return Status::ErrorJwkUnrecognizedUse(); - // The input usage_mask must be a subset of jwk_use_mask. - if (!ContainsKeyUsages(jwk_use_mask, usage_mask)) - return Status::ErrorJwkUseInconsistent(); - } - // If both 'key_ops' and 'use' are present, ensure they are consistent. - if (has_jwk_key_ops && has_jwk_use && - !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask)) - return Status::ErrorJwkUseAndKeyopsInconsistent(); - - // JWK keying material --> ImportKeyInternal() - if (jwk_kty_value == "oct") { - std::string jwk_k_value; - status = GetJwkBytes(dict_value, "k", &jwk_k_value); - if (status.IsError()) - return status; + bool has_dp; + status = GetOptionalJwkBytes(dict.get(), "dp", &result->dp, &has_dp); + if (status.IsError()) + return status; - // Some JWK alg ID's embed information about the key length in the alg ID - // string. For example "A128CBC" implies the JWK carries 128 bits - // of key material. For such keys validate that enough bytes were provided. - // If this validation is not done, then it would be possible to select a - // different algorithm by passing a different lengthed key, since that is - // how WebCrypto interprets things. - if (algorithm_info && - algorithm_info->IsInvalidKeyByteLength(jwk_k_value.size())) { - return Status::ErrorJwkIncorrectKeyLength(); - } + bool has_dq; + status = GetOptionalJwkBytes(dict.get(), "dq", &result->dq, &has_dq); + if (status.IsError()) + return status; - return ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(jwk_k_value), - algorithm, - extractable, - usage_mask, - key); - } + bool has_qi; + status = GetOptionalJwkBytes(dict.get(), "qi", &result->qi, &has_qi); + if (status.IsError()) + return status; - if (jwk_kty_value == "RSA") - return ImportRsaKey(dict_value, algorithm, extractable, usage_mask, key); + int num_optional_properties = has_p + has_q + has_dp + has_dq + has_qi; + if (num_optional_properties != 0 && num_optional_properties != 5) + return Status::ErrorJwkIncompleteOptionalRsaPrivateKey(); - return Status::ErrorJwkUnrecognizedKty(); + return Status::Success(); } -Status ExportKeyJwk(const blink::WebCryptoKey& key, - std::vector<uint8>* buffer) { - DCHECK(key.extractable()); - base::DictionaryValue jwk_dict; - Status status = Status::OperationError(); - - switch (key.type()) { - case blink::WebCryptoKeyTypeSecret: { - std::vector<uint8> exported_key; - status = ExportKey(blink::WebCryptoKeyFormatRaw, key, &exported_key); - if (status.IsError()) - return status; - WriteSecretKey(exported_key, &jwk_dict); - break; - } - case blink::WebCryptoKeyTypePublic: { - // TODO(eroman): Update when there are asymmetric keys other than RSA. - if (!IsRsaKey(key)) - return Status::ErrorUnsupported(); - platform::PublicKey* public_key; - status = ToPlatformPublicKey(key, &public_key); - if (status.IsError()) - return status; - std::vector<uint8> modulus; - std::vector<uint8> public_exponent; - status = - platform::ExportRsaPublicKey(public_key, &modulus, &public_exponent); - if (status.IsError()) - return status; - WriteRsaPublicKey(modulus, public_exponent, &jwk_dict); - break; - } - case blink::WebCryptoKeyTypePrivate: { - // TODO(eroman): Update when there are asymmetric keys other than RSA. - if (!IsRsaKey(key)) - return Status::ErrorUnsupported(); - - status = ExportRsaPrivateKeyJwk(key, &jwk_dict); - if (status.IsError()) - return status; - break; - } - +const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash) { + switch (hash) { + case blink::WebCryptoAlgorithmIdSha1: + return "HS1"; + case blink::WebCryptoAlgorithmIdSha256: + return "HS256"; + case blink::WebCryptoAlgorithmIdSha384: + return "HS384"; + case blink::WebCryptoAlgorithmIdSha512: + return "HS512"; default: - return Status::ErrorUnsupported(); + return NULL; } - - WriteKeyOps(key.usages(), &jwk_dict); - WriteExt(key.extractable(), &jwk_dict); - status = WriteAlg(key.algorithm(), &jwk_dict); - if (status.IsError()) - return status; - - std::string json; - base::JSONWriter::Write(&jwk_dict, &json); - buffer->assign(json.data(), json.data() + json.size()); - return Status::Success(); } } // namespace webcrypto diff --git a/content/child/webcrypto/jwk.h b/content/child/webcrypto/jwk.h index c919188..71d875d 100644 --- a/content/child/webcrypto/jwk.h +++ b/content/child/webcrypto/jwk.h @@ -8,6 +8,7 @@ #include <vector> #include "base/basictypes.h" +#include "base/values.h" #include "third_party/WebKit/public/platform/WebArrayBuffer.h" #include "third_party/WebKit/public/platform/WebCrypto.h" #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" @@ -19,13 +20,105 @@ namespace webcrypto { class CryptoData; class Status; -Status ImportKeyJwk(const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key); +// Writes a JWK-formatted symmetric key to |jwk_key_data|. +// * raw_key_data: The actual key data +// * algorithm: The JWK algorithm name (i.e. "alg") +// * extractable: The JWK extractability (i.e. "ext") +// * usage_mask: The JWK usages (i.e. "key_ops") +void WriteSecretKeyJwk(const CryptoData& raw_key_data, + const std::string& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + std::vector<uint8>* jwk_key_data); -Status ExportKeyJwk(const blink::WebCryptoKey& key, std::vector<uint8>* buffer); +// Parses a UTF-8 encoded JWK (key_data), and extracts the key material to +// |*raw_key_data|. Returns Status::Success() on success, otherwise an error. +// In order for this to succeed: +// * expected_algorithm must match the JWK's "alg", if present. +// * expected_extractable must be consistent with the JWK's "ext", if +// present. +// * expected_usage_mask must be a subset of the JWK's "key_ops" if present. +Status ReadSecretKeyJwk(const CryptoData& key_data, + const std::string& expected_algorithm, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usage_mask, + std::vector<uint8>* raw_key_data); + +// Creates an AES algorithm name for the given key size (in bytes). For +// instance "A128CBC" is the result of suffix="CBC", keylen_bytes=16. +std::string MakeJwkAesAlgorithmName(const std::string& suffix, + unsigned int keylen_bytes); + +// This is very similar to ReadSecretKeyJwk(), except instead of specifying an +// absolut "expected_algorithm", the suffix for an AES algorithm name is given +// (See MakeJwkAesAlgorithmName() for an explanation of what the suffix is). +// +// This is because the algorithm name for AES keys is dependent on the length +// of the key. This function expects key lengths to be either 128, 192, or 256 +// bits. +Status ReadAesSecretKeyJwk(const CryptoData& key_data, + const std::string& algorithm_name_suffix, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usage_mask, + std::vector<uint8>* raw_key_data); + +// Writes a JWK-formated RSA public key and saves the result to +// |*jwk_key_data|. +void WriteRsaPublicKeyJwk(const CryptoData& n, + const CryptoData& e, + const std::string& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + std::vector<uint8>* jwk_key_data); + +// Writes a JWK-formated RSA private key and saves the result to +// |*jwk_key_data|. +void WriteRsaPrivateKeyJwk(const CryptoData& n, + const CryptoData& e, + const CryptoData& d, + const CryptoData& p, + const CryptoData& q, + const CryptoData& dp, + const CryptoData& dq, + const CryptoData& qi, + const std::string& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + std::vector<uint8>* jwk_key_data); + +// Describes the RSA components for a parsed key. The names of the properties +// correspond with those from the JWK spec. Note that Chromium's WebCrypto +// implementation does not support multi-primes, so there is no parsed field +// for othinfo. +struct JwkRsaInfo { + JwkRsaInfo(); + ~JwkRsaInfo(); + + bool is_private_key; + std::string n; + std::string e; + std::string d; + std::string p; + std::string q; + std::string dp; + std::string dq; + std::string qi; +}; + +// Parses a UTF-8 encoded JWK (key_data), and extracts the RSA components to +// |*result|. Returns Status::Success() on success, otherwise an error. +// In order for this to succeed: +// * expected_algorithm must match the JWK's "alg", if present. +// * expected_extractable must be consistent with the JWK's "ext", if +// present. +// * expected_usage_mask must be a subset of the JWK's "key_ops" if present. +Status ReadRsaKeyJwk(const CryptoData& key_data, + const std::string& expected_algorithm, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usage_mask, + JwkRsaInfo* result); + +const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash); } // namespace webcrypto diff --git a/content/child/webcrypto/nss/aes_cbc_nss.cc b/content/child/webcrypto/nss/aes_cbc_nss.cc new file mode 100644 index 0000000..bac7c96 --- /dev/null +++ b/content/child/webcrypto/nss/aes_cbc_nss.cc @@ -0,0 +1,126 @@ +// 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 <cryptohi.h> + +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/nss/aes_key_nss.h" +#include "content/child/webcrypto/nss/key_nss.h" +#include "content/child/webcrypto/nss/util_nss.h" +#include "content/child/webcrypto/status.h" +#include "content/child/webcrypto/webcrypto_util.h" +#include "crypto/scoped_nss_types.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" + +namespace content { + +namespace webcrypto { + +namespace { + +Status AesCbcEncryptDecrypt(EncryptOrDecrypt mode, + const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) { + const blink::WebCryptoAesCbcParams* params = algorithm.aesCbcParams(); + if (!params) + return Status::ErrorUnexpected(); + + CryptoData iv(params->iv().data(), params->iv().size()); + if (iv.byte_length() != 16) + return Status::ErrorIncorrectSizeAesCbcIv(); + + PK11SymKey* sym_key = SymKeyNss::Cast(key)->key(); + + CK_ATTRIBUTE_TYPE operation = (mode == ENCRYPT) ? CKA_ENCRYPT : CKA_DECRYPT; + + SECItem iv_item = MakeSECItemForBuffer(iv); + + crypto::ScopedSECItem param(PK11_ParamFromIV(CKM_AES_CBC_PAD, &iv_item)); + if (!param) + return Status::OperationError(); + + crypto::ScopedPK11Context context(PK11_CreateContextBySymKey( + CKM_AES_CBC_PAD, operation, sym_key, param.get())); + + if (!context.get()) + return Status::OperationError(); + + // Oddly PK11_CipherOp takes input and output lengths as "int" rather than + // "unsigned int". Do some checks now to avoid integer overflowing. + if (data.byte_length() >= INT_MAX - AES_BLOCK_SIZE) { + // TODO(eroman): Handle this by chunking the input fed into NSS. Right now + // it doesn't make much difference since the one-shot API would end up + // blowing out the memory and crashing anyway. + return Status::ErrorDataTooLarge(); + } + + // PK11_CipherOp does an invalid memory access when given empty decryption + // input, or input which is not a multiple of the block size. See also + // https://bugzilla.mozilla.com/show_bug.cgi?id=921687. + if (operation == CKA_DECRYPT && + (data.byte_length() == 0 || (data.byte_length() % AES_BLOCK_SIZE != 0))) { + return Status::OperationError(); + } + + // TODO(eroman): Refine the output buffer size. It can be computed exactly for + // encryption, and can be smaller for decryption. + unsigned int output_max_len = data.byte_length() + AES_BLOCK_SIZE; + CHECK_GT(output_max_len, data.byte_length()); + + buffer->resize(output_max_len); + + unsigned char* buffer_data = Uint8VectorStart(buffer); + + int output_len; + if (SECSuccess != PK11_CipherOp(context.get(), + buffer_data, + &output_len, + buffer->size(), + data.bytes(), + data.byte_length())) { + return Status::OperationError(); + } + + unsigned int final_output_chunk_len; + if (SECSuccess != PK11_DigestFinal(context.get(), + buffer_data + output_len, + &final_output_chunk_len, + output_max_len - output_len)) { + return Status::OperationError(); + } + + buffer->resize(final_output_chunk_len + output_len); + return Status::Success(); +} + +class AesCbcImplementation : public AesAlgorithm { + public: + AesCbcImplementation() : AesAlgorithm(CKM_AES_CBC, "CBC") {} + + virtual Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + return AesCbcEncryptDecrypt(ENCRYPT, algorithm, key, data, buffer); + } + + virtual Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + return AesCbcEncryptDecrypt(DECRYPT, algorithm, key, data, buffer); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformAesCbcImplementation() { + return new AesCbcImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/nss/aes_gcm_nss.cc b/content/child/webcrypto/nss/aes_gcm_nss.cc new file mode 100644 index 0000000..a745556 --- /dev/null +++ b/content/child/webcrypto/nss/aes_gcm_nss.cc @@ -0,0 +1,188 @@ +// 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/crypto_data.h" +#include "content/child/webcrypto/nss/aes_key_nss.h" +#include "content/child/webcrypto/nss/key_nss.h" +#include "content/child/webcrypto/nss/util_nss.h" +#include "content/child/webcrypto/status.h" +#include "content/child/webcrypto/webcrypto_util.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" + +// At the time of this writing: +// * Windows and Mac builds ship with their own copy of NSS (3.15+) +// * Linux builds use the system's libnss, which is 3.14 on Debian (but 3.15+ +// on other distros). +// +// Since NSS provides AES-GCM support starting in version 3.15, it may be +// unavailable for Linux Chrome users. +// +// * !defined(CKM_AES_GCM) +// +// This means that at build time, the NSS header pkcs11t.h is older than +// 3.15. However at runtime support may be present. +// +// TODO(eroman): Simplify this once 3.15+ is required by Linux builds. +#if !defined(CKM_AES_GCM) +#define CKM_AES_GCM 0x00001087 + +struct CK_GCM_PARAMS { + CK_BYTE_PTR pIv; + CK_ULONG ulIvLen; + CK_BYTE_PTR pAAD; + CK_ULONG ulAADLen; + CK_ULONG ulTagBits; +}; +#endif // !defined(CKM_AES_GCM) + +namespace content { + +namespace webcrypto { + +namespace { + +Status NssSupportsAesGcm() { + if (NssRuntimeSupport::Get()->IsAesGcmSupported()) + return Status::Success(); + return Status::ErrorUnsupported( + "NSS version doesn't support AES-GCM. Try using version 3.15 or later"); +} + +// Helper to either encrypt or decrypt for AES-GCM. The result of encryption is +// the concatenation of the ciphertext and the authentication tag. Similarly, +// this is the expectation for the input to decryption. +Status AesGcmEncryptDecrypt(EncryptOrDecrypt mode, + const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) { + Status status = NssSupportsAesGcm(); + if (status.IsError()) + return status; + + PK11SymKey* sym_key = SymKeyNss::Cast(key)->key(); + const blink::WebCryptoAesGcmParams* params = algorithm.aesGcmParams(); + if (!params) + return Status::ErrorUnexpected(); + + unsigned int tag_length_bits; + status = GetAesGcmTagLengthInBits(params, &tag_length_bits); + if (status.IsError()) + return status; + unsigned int tag_length_bytes = tag_length_bits / 8; + + CryptoData iv(params->iv()); + CryptoData additional_data(params->optionalAdditionalData()); + + CK_GCM_PARAMS gcm_params = {0}; + gcm_params.pIv = const_cast<unsigned char*>(iv.bytes()); + gcm_params.ulIvLen = iv.byte_length(); + + gcm_params.pAAD = const_cast<unsigned char*>(additional_data.bytes()); + gcm_params.ulAADLen = additional_data.byte_length(); + + gcm_params.ulTagBits = tag_length_bits; + + SECItem param; + param.type = siBuffer; + param.data = reinterpret_cast<unsigned char*>(&gcm_params); + param.len = sizeof(gcm_params); + + unsigned int buffer_size = 0; + + // Calculate the output buffer size. + if (mode == ENCRYPT) { + // TODO(eroman): This is ugly, abstract away the safe integer arithmetic. + if (data.byte_length() > (UINT_MAX - tag_length_bytes)) + return Status::ErrorDataTooLarge(); + buffer_size = data.byte_length() + tag_length_bytes; + } else { + // TODO(eroman): In theory the buffer allocated for the plain text should be + // sized as |data.byte_length() - tag_length_bytes|. + // + // However NSS has a bug whereby it will fail if the output buffer size is + // not at least as large as the ciphertext: + // + // https://bugzilla.mozilla.org/show_bug.cgi?id=%20853674 + // + // From the analysis of that bug it looks like it might be safe to pass a + // correctly sized buffer but lie about its size. Since resizing the + // WebCryptoArrayBuffer is expensive that hack may be worth looking into. + buffer_size = data.byte_length(); + } + + buffer->resize(buffer_size); + unsigned char* buffer_data = Uint8VectorStart(buffer); + + PK11_EncryptDecryptFunction encrypt_or_decrypt_func = + (mode == ENCRYPT) ? NssRuntimeSupport::Get()->pk11_encrypt_func() + : NssRuntimeSupport::Get()->pk11_decrypt_func(); + + unsigned int output_len = 0; + SECStatus result = encrypt_or_decrypt_func(sym_key, + CKM_AES_GCM, + ¶m, + buffer_data, + &output_len, + buffer->size(), + data.bytes(), + data.byte_length()); + + if (result != SECSuccess) + return Status::OperationError(); + + // Unfortunately the buffer needs to be shrunk for decryption (see the NSS bug + // above). + buffer->resize(output_len); + + return Status::Success(); +} + +class AesGcmImplementation : public AesAlgorithm { + public: + AesGcmImplementation() : AesAlgorithm(CKM_AES_GCM, "GCM") {} + + virtual Status VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE { + // Prevent importing AES-GCM keys if it is unavailable. + Status status = NssSupportsAesGcm(); + if (status.IsError()) + return status; + return AesAlgorithm::VerifyKeyUsagesBeforeImportKey(format, usage_mask); + } + + virtual Status VerifyKeyUsagesBeforeGenerateKey( + blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE { + // Prevent generating AES-GCM keys if it is unavailable. + Status status = NssSupportsAesGcm(); + if (status.IsError()) + return status; + return AesAlgorithm::VerifyKeyUsagesBeforeGenerateKey(usage_mask); + } + + virtual Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + return AesGcmEncryptDecrypt(ENCRYPT, algorithm, key, data, buffer); + } + + virtual Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + return AesGcmEncryptDecrypt(DECRYPT, algorithm, key, data, buffer); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformAesGcmImplementation() { + return new AesGcmImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/nss/aes_key_nss.cc b/content/child/webcrypto/nss/aes_key_nss.cc new file mode 100644 index 0000000..a5ee933 --- /dev/null +++ b/content/child/webcrypto/nss/aes_key_nss.cc @@ -0,0 +1,137 @@ +// 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/nss/aes_key_nss.h" + +#include "base/logging.h" +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/jwk.h" +#include "content/child/webcrypto/nss/key_nss.h" +#include "content/child/webcrypto/nss/sym_key_nss.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 { + +AesAlgorithm::AesAlgorithm(CK_MECHANISM_TYPE import_mechanism, + CK_FLAGS import_flags, + blink::WebCryptoKeyUsageMask all_key_usages, + const std::string& jwk_suffix) + : import_mechanism_(import_mechanism), + import_flags_(import_flags), + all_key_usages_(all_key_usages), + jwk_suffix_(jwk_suffix) { +} + +AesAlgorithm::AesAlgorithm(CK_MECHANISM_TYPE import_mechanism, + const std::string& jwk_suffix) + : import_mechanism_(import_mechanism), + import_flags_(CKF_ENCRYPT | CKF_DECRYPT), + all_key_usages_(blink::WebCryptoKeyUsageEncrypt | + blink::WebCryptoKeyUsageDecrypt | + blink::WebCryptoKeyUsageWrapKey | + blink::WebCryptoKeyUsageUnwrapKey), + jwk_suffix_(jwk_suffix) { +} + +Status AesAlgorithm::VerifyKeyUsagesBeforeGenerateKey( + blink::WebCryptoKeyUsageMask usage_mask) const { + return CheckKeyCreationUsages(all_key_usages_, usage_mask); +} + +Status AesAlgorithm::GenerateSecretKey( + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const { + unsigned int keylen_bits; + Status status = + GetAesKeyGenLengthInBits(algorithm.aesKeyGenParams(), &keylen_bits); + if (status.IsError()) + return status; + + return GenerateSecretKeyNss( + blink::WebCryptoKeyAlgorithm::createAes(algorithm.id(), keylen_bits), + extractable, + usage_mask, + keylen_bits / 8, + CKM_AES_KEY_GEN, + key); +} + +Status AesAlgorithm::VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usage_mask) const { + switch (format) { + case blink::WebCryptoKeyFormatRaw: + case blink::WebCryptoKeyFormatJwk: + return CheckKeyCreationUsages(all_key_usages_, usage_mask); + default: + return Status::ErrorUnsupportedImportKeyFormat(); + } +} +Status AesAlgorithm::ImportKeyRaw(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const { + const unsigned int keylen_bytes = key_data.byte_length(); + Status status = VerifyAesKeyLengthForImport(keylen_bytes); + if (status.IsError()) + return status; + + // No possibility of overflow. + unsigned int keylen_bits = keylen_bytes * 8; + + return ImportKeyRawNss( + key_data, + blink::WebCryptoKeyAlgorithm::createAes(algorithm.id(), keylen_bits), + extractable, + usage_mask, + import_mechanism_, + import_flags_, + key); +} + +Status AesAlgorithm::ImportKeyJwk(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const { + std::vector<uint8> raw_data; + Status status = ReadAesSecretKeyJwk( + key_data, jwk_suffix_, extractable, usage_mask, &raw_data); + if (status.IsError()) + return status; + + return ImportKeyRaw( + CryptoData(raw_data), algorithm, extractable, usage_mask, key); +} + +Status AesAlgorithm::ExportKeyRaw(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const { + *buffer = SymKeyNss::Cast(key)->raw_key_data(); + return Status::Success(); +} + +Status AesAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const { + SymKeyNss* sym_key = SymKeyNss::Cast(key); + const std::vector<uint8>& raw_data = sym_key->raw_key_data(); + + WriteSecretKeyJwk(CryptoData(raw_data), + MakeJwkAesAlgorithmName(jwk_suffix_, raw_data.size()), + key.extractable(), + key.usages(), + buffer); + + return Status::Success(); +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/nss/aes_key_nss.h b/content/child/webcrypto/nss/aes_key_nss.h new file mode 100644 index 0000000..614d909 --- /dev/null +++ b/content/child/webcrypto/nss/aes_key_nss.h @@ -0,0 +1,80 @@ +// 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. + +#ifndef CONTENT_CHILD_WEBCRYPTO_NSS_AES_NSS_H_ +#define CONTENT_CHILD_WEBCRYPTO_NSS_AES_NSS_H_ + +#include <pkcs11t.h> + +#include "content/child/webcrypto/algorithm_implementation.h" + +namespace content { + +namespace webcrypto { + +// Base class for AES algorithms that provides the implementation for key +// creation and export. +class AesAlgorithm : public AlgorithmImplementation { + public: + // Constructs an AES algorithm whose keys will be imported using the NSS + // mechanism |import_mechanism| and NSS flags |import_flags|. + // |all_key_usages| is the set of all WebCrypto key usages that are + // allowed for imported or generated keys. |jwk_suffix| is the suffix + // used when constructing JWK names for the algorithm. For instance A128CBC + // is the JWK name for 128-bit AES-CBC. The |jwk_suffix| in this case would + // be "CBC". + AesAlgorithm(CK_MECHANISM_TYPE import_mechanism, + CK_FLAGS import_flags, + blink::WebCryptoKeyUsageMask all_key_usages, + const std::string& jwk_suffix); + + // This is the same as the other AesAlgorithm constructor, however + // |import_flags| and |all_key_usages| are pre-filled to values for + // encryption/decryption algorithms (supports usages for: encrypt, decrypt, + // wrap, unwrap). + AesAlgorithm(CK_MECHANISM_TYPE import_mechanism, + const std::string& jwk_suffix); + + virtual Status VerifyKeyUsagesBeforeGenerateKey( + blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE; + + virtual Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const OVERRIDE; + + virtual Status VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE; + + virtual Status ImportKeyRaw(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const OVERRIDE; + + virtual Status ImportKeyJwk(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const OVERRIDE; + + virtual Status ExportKeyRaw(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const OVERRIDE; + + virtual Status ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const OVERRIDE; + + private: + const CK_MECHANISM_TYPE import_mechanism_; + const CK_FLAGS import_flags_; + const blink::WebCryptoKeyUsageMask all_key_usages_; + const std::string jwk_suffix_; +}; + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_NSS_AES_NSS_H_ diff --git a/content/child/webcrypto/nss/aes_kw_nss.cc b/content/child/webcrypto/nss/aes_kw_nss.cc new file mode 100644 index 0000000..4e2796f --- /dev/null +++ b/content/child/webcrypto/nss/aes_kw_nss.cc @@ -0,0 +1,203 @@ +// 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 <secerr.h> + +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/nss/aes_key_nss.h" +#include "content/child/webcrypto/nss/key_nss.h" +#include "content/child/webcrypto/nss/sym_key_nss.h" +#include "content/child/webcrypto/nss/util_nss.h" +#include "content/child/webcrypto/status.h" +#include "content/child/webcrypto/webcrypto_util.h" +#include "crypto/scoped_nss_types.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace content { + +namespace webcrypto { + +namespace { + +// The Default IV for AES-KW. See http://www.ietf.org/rfc/rfc3394.txt +// Section 2.2.3.1. +const unsigned char kAesIv[] = {0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6}; + +// The result of unwrapping is a SymKey rather than a buffer. This is a +// consequence of how NSS exposes AES-KW. Subsequent code can extract the value +// of the sym key to interpret it as key bytes in another format. +Status DoUnwrapSymKeyAesKw(const CryptoData& wrapped_key_data, + PK11SymKey* wrapping_key, + CK_MECHANISM_TYPE mechanism, + CK_FLAGS flags, + crypto::ScopedPK11SymKey* unwrapped_key) { + DCHECK_GE(wrapped_key_data.byte_length(), 24u); + DCHECK_EQ(wrapped_key_data.byte_length() % 8, 0u); + + SECItem iv_item = MakeSECItemForBuffer(CryptoData(kAesIv, sizeof(kAesIv))); + crypto::ScopedSECItem param_item( + PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP, &iv_item)); + if (!param_item) + return Status::ErrorUnexpected(); + + SECItem cipher_text = MakeSECItemForBuffer(wrapped_key_data); + + // The plaintext length is always 64 bits less than the data size. + const unsigned int plaintext_length = wrapped_key_data.byte_length() - 8; + +#if defined(USE_NSS) + // Part of workaround for + // https://bugzilla.mozilla.org/show_bug.cgi?id=981170. See the explanation + // later in this function. + PORT_SetError(0); +#endif + + crypto::ScopedPK11SymKey new_key( + PK11_UnwrapSymKeyWithFlags(wrapping_key, + CKM_NSS_AES_KEY_WRAP, + param_item.get(), + &cipher_text, + mechanism, + CKA_FLAGS_ONLY, + plaintext_length, + flags)); + + // TODO(padolph): Use NSS PORT_GetError() and friends to report a more + // accurate error, providing if doesn't leak any information to web pages + // about other web crypto users, key details, etc. + if (!new_key) + return Status::OperationError(); + +#if defined(USE_NSS) + // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=981170 + // which was fixed in NSS 3.16.0. + // If unwrap fails, NSS nevertheless returns a valid-looking PK11SymKey, + // with a reasonable length but with key data pointing to uninitialized + // memory. + // To understand this workaround see the fix for 981170: + // https://hg.mozilla.org/projects/nss/rev/753bb69e543c + if (!NSS_VersionCheck("3.16") && PORT_GetError() == SEC_ERROR_BAD_DATA) + return Status::OperationError(); +#endif + + *unwrapped_key = new_key.Pass(); + return Status::Success(); +} + +Status WrapSymKeyAesKw(PK11SymKey* key, + PK11SymKey* wrapping_key, + std::vector<uint8>* buffer) { + // The data size must be at least 16 bytes and a multiple of 8 bytes. + // RFC 3394 does not specify a maximum allowed data length, but since only + // keys are being wrapped in this application (which are small), a reasonable + // max limit is whatever will fit into an unsigned. For the max size test, + // note that AES Key Wrap always adds 8 bytes to the input data size. + const unsigned int input_length = PK11_GetKeyLength(key); + DCHECK_GE(input_length, 16u); + DCHECK((input_length % 8) == 0); + if (input_length > UINT_MAX - 8) + return Status::ErrorDataTooLarge(); + + SECItem iv_item = MakeSECItemForBuffer(CryptoData(kAesIv, sizeof(kAesIv))); + crypto::ScopedSECItem param_item( + PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP, &iv_item)); + if (!param_item) + return Status::ErrorUnexpected(); + + const unsigned int output_length = input_length + 8; + buffer->resize(output_length); + SECItem wrapped_key_item = MakeSECItemForBuffer(CryptoData(*buffer)); + + if (SECSuccess != PK11_WrapSymKey(CKM_NSS_AES_KEY_WRAP, + param_item.get(), + wrapping_key, + key, + &wrapped_key_item)) { + return Status::OperationError(); + } + if (output_length != wrapped_key_item.len) + return Status::ErrorUnexpected(); + + return Status::Success(); +} + +class AesKwCryptoAlgorithmNss : public AesAlgorithm { + public: + AesKwCryptoAlgorithmNss() + : AesAlgorithm( + CKM_NSS_AES_KEY_WRAP, + CKF_WRAP | CKF_WRAP, + blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, + "KW") {} + + virtual Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& wrapping_key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + if (data.byte_length() < 16) + return Status::ErrorDataTooSmall(); + if (data.byte_length() % 8) + return Status::ErrorInvalidAesKwDataLength(); + + // Due to limitations in the NSS API for the AES-KW algorithm, |data| must + // be temporarily viewed as a symmetric key to be wrapped (encrypted). + SECItem data_item = MakeSECItemForBuffer(data); + crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); + crypto::ScopedPK11SymKey data_as_sym_key( + PK11_ImportSymKey(slot.get(), + CKK_GENERIC_SECRET, + PK11_OriginUnwrap, + CKA_SIGN, + &data_item, + NULL)); + if (!data_as_sym_key) + return Status::OperationError(); + + return WrapSymKeyAesKw( + data_as_sym_key.get(), SymKeyNss::Cast(wrapping_key)->key(), buffer); + } + + virtual Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& wrapping_key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + if (data.byte_length() < 24) + return Status::ErrorDataTooSmall(); + if (data.byte_length() % 8) + return Status::ErrorInvalidAesKwDataLength(); + + // Due to limitations in the NSS API for the AES-KW algorithm, |data| must + // be temporarily viewed as a symmetric key to be unwrapped (decrypted). + crypto::ScopedPK11SymKey decrypted; + Status status = DoUnwrapSymKeyAesKw(data, + SymKeyNss::Cast(wrapping_key)->key(), + CKK_GENERIC_SECRET, + 0, + &decrypted); + if (status.IsError()) + return status; + + // Once the decrypt is complete, extract the resultant raw bytes from NSS + // and return them to the caller. + if (PK11_ExtractKeyValue(decrypted.get()) != SECSuccess) + return Status::OperationError(); + const SECItem* const key_data = PK11_GetKeyData(decrypted.get()); + if (!key_data) + return Status::OperationError(); + buffer->assign(key_data->data, key_data->data + key_data->len); + + return Status::Success(); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformAesKwImplementation() { + return new AesKwCryptoAlgorithmNss; +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/nss/hmac_nss.cc b/content/child/webcrypto/nss/hmac_nss.cc new file mode 100644 index 0000000..2eff7d0 --- /dev/null +++ b/content/child/webcrypto/nss/hmac_nss.cc @@ -0,0 +1,234 @@ +// 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 <cryptohi.h> +#include <pk11pub.h> +#include <secerr.h> +#include <sechash.h> + +#include "base/logging.h" +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/jwk.h" +#include "content/child/webcrypto/nss/key_nss.h" +#include "content/child/webcrypto/nss/sym_key_nss.h" +#include "content/child/webcrypto/nss/util_nss.h" +#include "content/child/webcrypto/status.h" +#include "content/child/webcrypto/webcrypto_util.h" +#include "crypto/secure_util.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace content { + +namespace webcrypto { + +namespace { + +const blink::WebCryptoKeyUsageMask kAllKeyUsages = + blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; + +bool WebCryptoHashToHMACMechanism(const blink::WebCryptoAlgorithm& algorithm, + CK_MECHANISM_TYPE* mechanism) { + switch (algorithm.id()) { + case blink::WebCryptoAlgorithmIdSha1: + *mechanism = CKM_SHA_1_HMAC; + return true; + case blink::WebCryptoAlgorithmIdSha256: + *mechanism = CKM_SHA256_HMAC; + return true; + case blink::WebCryptoAlgorithmIdSha384: + *mechanism = CKM_SHA384_HMAC; + return true; + case blink::WebCryptoAlgorithmIdSha512: + *mechanism = CKM_SHA512_HMAC; + return true; + default: + return false; + } +} + +class HmacImplementation : public AlgorithmImplementation { + public: + HmacImplementation() {} + + virtual Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const OVERRIDE { + const blink::WebCryptoHmacKeyGenParams* params = + algorithm.hmacKeyGenParams(); + + const blink::WebCryptoAlgorithm& hash = params->hash(); + CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; + if (!WebCryptoHashToHMACMechanism(hash, &mechanism)) + return Status::ErrorUnsupported(); + + unsigned int keylen_bits = 0; + Status status = GetHmacKeyGenLengthInBits(params, &keylen_bits); + if (status.IsError()) + return status; + + return GenerateSecretKeyNss( + blink::WebCryptoKeyAlgorithm::createHmac(hash.id(), keylen_bits), + extractable, + usage_mask, + keylen_bits / 8, + mechanism, + key); + } + + virtual Status VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE { + switch (format) { + case blink::WebCryptoKeyFormatRaw: + case blink::WebCryptoKeyFormatJwk: + return CheckKeyCreationUsages(kAllKeyUsages, usage_mask); + default: + return Status::ErrorUnsupportedImportKeyFormat(); + } + } + + virtual Status VerifyKeyUsagesBeforeGenerateKey( + blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE { + return CheckKeyCreationUsages(kAllKeyUsages, usage_mask); + } + + virtual Status ImportKeyRaw(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const OVERRIDE { + const blink::WebCryptoAlgorithm& hash = + algorithm.hmacImportParams()->hash(); + + CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; + if (!WebCryptoHashToHMACMechanism(hash, &mechanism)) + return Status::ErrorUnsupported(); + + // TODO(eroman): check for overflow. + unsigned int keylen_bits = key_data.byte_length() * 8; + return ImportKeyRawNss( + key_data, + blink::WebCryptoKeyAlgorithm::createHmac(hash.id(), keylen_bits), + extractable, + usage_mask, + mechanism, + CKF_SIGN | CKF_VERIFY, + key); + } + + virtual Status ImportKeyJwk(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const OVERRIDE { + const char* algorithm_name = + GetJwkHmacAlgorithmName(algorithm.hmacImportParams()->hash().id()); + if (!algorithm_name) + return Status::ErrorUnexpected(); + + std::vector<uint8> raw_data; + Status status = ReadSecretKeyJwk( + key_data, algorithm_name, extractable, usage_mask, &raw_data); + if (status.IsError()) + return status; + + return ImportKeyRaw( + CryptoData(raw_data), algorithm, extractable, usage_mask, key); + } + + virtual Status ExportKeyRaw(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const OVERRIDE { + *buffer = SymKeyNss::Cast(key)->raw_key_data(); + return Status::Success(); + } + + virtual Status ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const OVERRIDE { + SymKeyNss* sym_key = SymKeyNss::Cast(key); + const std::vector<uint8>& raw_data = sym_key->raw_key_data(); + + const char* algorithm_name = + GetJwkHmacAlgorithmName(key.algorithm().hmacParams()->hash().id()); + if (!algorithm_name) + return Status::ErrorUnexpected(); + + WriteSecretKeyJwk(CryptoData(raw_data), + algorithm_name, + key.extractable(), + key.usages(), + buffer); + + return Status::Success(); + } + + virtual Status Sign(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + const blink::WebCryptoAlgorithm& hash = + key.algorithm().hmacParams()->hash(); + PK11SymKey* sym_key = SymKeyNss::Cast(key)->key(); + + CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; + if (!WebCryptoHashToHMACMechanism(hash, &mechanism)) + return Status::ErrorUnexpected(); + + SECItem param_item = {siBuffer, NULL, 0}; + SECItem data_item = MakeSECItemForBuffer(data); + // First call is to figure out the length. + SECItem signature_item = {siBuffer, NULL, 0}; + + if (PK11_SignWithSymKey( + sym_key, mechanism, ¶m_item, &signature_item, &data_item) != + SECSuccess) { + return Status::OperationError(); + } + + DCHECK_NE(0u, signature_item.len); + + buffer->resize(signature_item.len); + signature_item.data = Uint8VectorStart(buffer); + + if (PK11_SignWithSymKey( + sym_key, mechanism, ¶m_item, &signature_item, &data_item) != + SECSuccess) { + return Status::OperationError(); + } + + CHECK_EQ(buffer->size(), signature_item.len); + return Status::Success(); + } + + virtual Status Verify(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& signature, + const CryptoData& data, + bool* signature_match) const OVERRIDE { + std::vector<uint8> result; + Status status = Sign(algorithm, key, data, &result); + + if (status.IsError()) + return status; + + // Do not allow verification of truncated MACs. + *signature_match = result.size() == signature.byte_length() && + crypto::SecureMemEqual(Uint8VectorStart(result), + signature.bytes(), + signature.byte_length()); + + return Status::Success(); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformHmacImplementation() { + return new HmacImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/nss/key_nss.cc b/content/child/webcrypto/nss/key_nss.cc new file mode 100644 index 0000000..0e6114d --- /dev/null +++ b/content/child/webcrypto/nss/key_nss.cc @@ -0,0 +1,96 @@ +// 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/nss/key_nss.h" + +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/status.h" +#include "content/child/webcrypto/webcrypto_util.h" + +namespace content { + +namespace webcrypto { + +KeyNss::KeyNss(const CryptoData& serialized_key_data) + : serialized_key_data_( + serialized_key_data.bytes(), + serialized_key_data.bytes() + serialized_key_data.byte_length()) { +} + +KeyNss::~KeyNss() { +} + +SymKeyNss* KeyNss::AsSymKey() { + return NULL; +} + +PublicKeyNss* KeyNss::AsPublicKey() { + return NULL; +} + +PrivateKeyNss* KeyNss::AsPrivateKey() { + return NULL; +} + +SymKeyNss::~SymKeyNss() { +} + +SymKeyNss* SymKeyNss::Cast(const blink::WebCryptoKey& key) { + KeyNss* platform_key = reinterpret_cast<KeyNss*>(key.handle()); + return platform_key->AsSymKey(); +} + +SymKeyNss* SymKeyNss::AsSymKey() { + return this; +} + +SymKeyNss::SymKeyNss(crypto::ScopedPK11SymKey key, + const CryptoData& raw_key_data) + : KeyNss(raw_key_data), key_(key.Pass()) { +} + +PublicKeyNss::~PublicKeyNss() { +} + +PublicKeyNss* PublicKeyNss::Cast(const blink::WebCryptoKey& key) { + KeyNss* platform_key = reinterpret_cast<KeyNss*>(key.handle()); + return platform_key->AsPublicKey(); +} + +PublicKeyNss* PublicKeyNss::AsPublicKey() { + return this; +} + +PublicKeyNss::PublicKeyNss(crypto::ScopedSECKEYPublicKey key, + const CryptoData& spki_data) + : KeyNss(spki_data), key_(key.Pass()) { +} + +PrivateKeyNss::~PrivateKeyNss() { +} + +PrivateKeyNss* PrivateKeyNss::Cast(const blink::WebCryptoKey& key) { + KeyNss* platform_key = reinterpret_cast<KeyNss*>(key.handle()); + return platform_key->AsPrivateKey(); +} + +PrivateKeyNss* PrivateKeyNss::AsPrivateKey() { + return this; +} + +PrivateKeyNss::PrivateKeyNss(crypto::ScopedSECKEYPrivateKey key, + const CryptoData& pkcs8_data) + : KeyNss(pkcs8_data), key_(key.Pass()) { +} + +bool PlatformSerializeKeyForClone(const blink::WebCryptoKey& key, + blink::WebVector<uint8>* key_data) { + const KeyNss* nss_key = static_cast<KeyNss*>(key.handle()); + *key_data = nss_key->serialized_key_data(); + return true; +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/nss/key_nss.h b/content/child/webcrypto/nss/key_nss.h new file mode 100644 index 0000000..f1980e2 --- /dev/null +++ b/content/child/webcrypto/nss/key_nss.h @@ -0,0 +1,102 @@ +// 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. + +#ifndef CONTENT_CHILD_WEBCRYPTO_NSS_KEY_NSS_H_ +#define CONTENT_CHILD_WEBCRYPTO_NSS_KEY_NSS_H_ + +#include "content/child/webcrypto/algorithm_implementation.h" + +#include "crypto/scoped_nss_types.h" + +namespace content { + +namespace webcrypto { + +class PrivateKeyNss; +class PublicKeyNss; +class SymKeyNss; + +// Base key class for all NSS keys, used to safely cast between types. Each key +// maintains a copy of its serialized form in either 'raw', 'pkcs8', or 'spki' +// format. This is to allow structured cloning of keys synchronously from the +// target Blink thread without having to lock access to the key. +class KeyNss : public blink::WebCryptoKeyHandle { + public: + explicit KeyNss(const CryptoData& serialized_key_data); + virtual ~KeyNss(); + + virtual SymKeyNss* AsSymKey(); + virtual PublicKeyNss* AsPublicKey(); + virtual PrivateKeyNss* AsPrivateKey(); + + const std::vector<uint8>& serialized_key_data() const { + return serialized_key_data_; + } + + private: + const std::vector<uint8> serialized_key_data_; +}; + +class SymKeyNss : public KeyNss { + public: + virtual ~SymKeyNss(); + SymKeyNss(crypto::ScopedPK11SymKey key, const CryptoData& raw_key_data); + + static SymKeyNss* Cast(const blink::WebCryptoKey& key); + + PK11SymKey* key() { return key_.get(); } + virtual SymKeyNss* AsSymKey() OVERRIDE; + + const std::vector<uint8>& raw_key_data() const { + return serialized_key_data(); + } + + private: + crypto::ScopedPK11SymKey key_; + + DISALLOW_COPY_AND_ASSIGN(SymKeyNss); +}; + +class PublicKeyNss : public KeyNss { + public: + virtual ~PublicKeyNss(); + PublicKeyNss(crypto::ScopedSECKEYPublicKey key, const CryptoData& spki_data); + + static PublicKeyNss* Cast(const blink::WebCryptoKey& key); + + SECKEYPublicKey* key() { return key_.get(); } + virtual PublicKeyNss* AsPublicKey() OVERRIDE; + + const std::vector<uint8>& spki_data() const { return serialized_key_data(); } + + private: + crypto::ScopedSECKEYPublicKey key_; + + DISALLOW_COPY_AND_ASSIGN(PublicKeyNss); +}; + +class PrivateKeyNss : public KeyNss { + public: + virtual ~PrivateKeyNss(); + PrivateKeyNss(crypto::ScopedSECKEYPrivateKey key, + const CryptoData& pkcs8_data); + + static PrivateKeyNss* Cast(const blink::WebCryptoKey& key); + + SECKEYPrivateKey* key() { return key_.get(); } + virtual PrivateKeyNss* AsPrivateKey() OVERRIDE; + + const std::vector<uint8>& pkcs8_data() const { return serialized_key_data(); } + + private: + crypto::ScopedSECKEYPrivateKey key_; + + DISALLOW_COPY_AND_ASSIGN(PrivateKeyNss); +}; + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_NSS_KEY_NSS_H_ diff --git a/content/child/webcrypto/nss/rsa_key_nss.cc b/content/child/webcrypto/nss/rsa_key_nss.cc new file mode 100644 index 0000000..ec16300 --- /dev/null +++ b/content/child/webcrypto/nss/rsa_key_nss.cc @@ -0,0 +1,897 @@ +// 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/nss/rsa_key_nss.h" + +#include "base/logging.h" +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/jwk.h" +#include "content/child/webcrypto/nss/key_nss.h" +#include "content/child/webcrypto/nss/util_nss.h" +#include "content/child/webcrypto/status.h" +#include "content/child/webcrypto/webcrypto_util.h" +#include "crypto/scoped_nss_types.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace content { + +namespace webcrypto { + +namespace { + +// Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros, +// to unsigned long. +bool BigIntegerToLong(const uint8* data, + unsigned int data_size, + unsigned long* result) { + // TODO(eroman): Fix handling of empty biginteger. http://crubg.com/373552 + if (data_size == 0) + return false; + + *result = 0; + for (size_t i = 0; i < data_size; ++i) { + size_t reverse_i = data_size - i - 1; + + if (reverse_i >= sizeof(unsigned long) && data[i]) + return false; // Too large for a long. + + *result |= data[i] << 8 * reverse_i; + } + return true; +} + +bool CreatePublicKeyAlgorithm(const blink::WebCryptoAlgorithm& algorithm, + SECKEYPublicKey* key, + blink::WebCryptoKeyAlgorithm* key_algorithm) { + // TODO(eroman): What about other key types rsaPss, rsaOaep. + if (!key || key->keyType != rsaKey) + return false; + + unsigned int modulus_length_bits = SECKEY_PublicKeyStrength(key) * 8; + CryptoData public_exponent(key->u.rsa.publicExponent.data, + key->u.rsa.publicExponent.len); + + switch (algorithm.paramsType()) { + case blink::WebCryptoAlgorithmParamsTypeRsaHashedImportParams: + case blink::WebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams: + *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed( + algorithm.id(), + modulus_length_bits, + public_exponent.bytes(), + public_exponent.byte_length(), + GetInnerHashAlgorithm(algorithm).id()); + return true; + default: + return false; + } +} + +bool CreatePrivateKeyAlgorithm(const blink::WebCryptoAlgorithm& algorithm, + SECKEYPrivateKey* key, + blink::WebCryptoKeyAlgorithm* key_algorithm) { + crypto::ScopedSECKEYPublicKey public_key(SECKEY_ConvertToPublicKey(key)); + return CreatePublicKeyAlgorithm(algorithm, public_key.get(), key_algorithm); +} + +#if defined(USE_NSS) && !defined(OS_CHROMEOS) +Status ErrorRsaKeyImportNotSupported() { + return Status::ErrorUnsupported( + "NSS version must be at least 3.16.2 for RSA key import. See " + "http://crbug.com/380424"); +} + +// Prior to NSS 3.16.2 RSA key parameters were not validated. This is +// a security problem for RSA private key import from JWK which uses a +// CKA_ID based on the public modulus to retrieve the private key. +Status NssSupportsRsaKeyImport() { + if (!NSS_VersionCheck("3.16.2")) + return ErrorRsaKeyImportNotSupported(); + + // Also ensure that the version of Softoken is 3.16.2 or later. + crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); + CK_SLOT_INFO info = {}; + if (PK11_GetSlotInfo(slot.get(), &info) != SECSuccess) + return ErrorRsaKeyImportNotSupported(); + + // CK_SLOT_INFO.hardwareVersion contains the major.minor + // version info for Softoken in the corresponding .major/.minor + // fields, and .firmwareVersion contains the patch.build + // version info (in the .major/.minor fields) + if ((info.hardwareVersion.major > 3) || + (info.hardwareVersion.major == 3 && + (info.hardwareVersion.minor > 16 || + (info.hardwareVersion.minor == 16 && + info.firmwareVersion.major >= 2)))) { + return Status::Success(); + } + + return ErrorRsaKeyImportNotSupported(); +} +#else +Status NssSupportsRsaKeyImport() { + return Status::Success(); +} +#endif + +bool CreateRsaHashedPublicKeyAlgorithm( + const blink::WebCryptoAlgorithm& algorithm, + SECKEYPublicKey* key, + blink::WebCryptoKeyAlgorithm* key_algorithm) { + // TODO(eroman): What about other key types rsaPss, rsaOaep. + if (!key || key->keyType != rsaKey) + return false; + + unsigned int modulus_length_bits = SECKEY_PublicKeyStrength(key) * 8; + CryptoData public_exponent(key->u.rsa.publicExponent.data, + key->u.rsa.publicExponent.len); + + switch (algorithm.paramsType()) { + case blink::WebCryptoAlgorithmParamsTypeRsaHashedImportParams: + case blink::WebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams: + *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed( + algorithm.id(), + modulus_length_bits, + public_exponent.bytes(), + public_exponent.byte_length(), + GetInnerHashAlgorithm(algorithm).id()); + return true; + default: + return false; + } +} + +bool CreateRsaHashedPrivateKeyAlgorithm( + const blink::WebCryptoAlgorithm& algorithm, + SECKEYPrivateKey* key, + blink::WebCryptoKeyAlgorithm* key_algorithm) { + crypto::ScopedSECKEYPublicKey public_key(SECKEY_ConvertToPublicKey(key)); + if (!public_key) + return false; + return CreateRsaHashedPublicKeyAlgorithm( + algorithm, public_key.get(), key_algorithm); +} + +// From PKCS#1 [http://tools.ietf.org/html/rfc3447]: +// +// RSAPrivateKey ::= SEQUENCE { +// version Version, +// modulus INTEGER, -- n +// publicExponent INTEGER, -- e +// privateExponent INTEGER, -- d +// prime1 INTEGER, -- p +// prime2 INTEGER, -- q +// exponent1 INTEGER, -- d mod (p-1) +// exponent2 INTEGER, -- d mod (q-1) +// coefficient INTEGER, -- (inverse of q) mod p +// otherPrimeInfos OtherPrimeInfos OPTIONAL +// } +// +// Note that otherPrimeInfos is only applicable for version=1. Since NSS +// doesn't use multi-prime can safely use version=0. +struct RSAPrivateKey { + SECItem version; + SECItem modulus; + SECItem public_exponent; + SECItem private_exponent; + SECItem prime1; + SECItem prime2; + SECItem exponent1; + SECItem exponent2; + SECItem coefficient; +}; + +// The system NSS library doesn't have the new PK11_ExportDERPrivateKeyInfo +// function yet (https://bugzilla.mozilla.org/show_bug.cgi?id=519255). So we +// provide a fallback implementation. +#if defined(USE_NSS) +const SEC_ASN1Template RSAPrivateKeyTemplate[] = { + {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(RSAPrivateKey)}, + {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, version)}, + {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, modulus)}, + {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, public_exponent)}, + {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, private_exponent)}, + {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, prime1)}, + {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, prime2)}, + {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, exponent1)}, + {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, exponent2)}, + {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, coefficient)}, + {0}}; +#endif // defined(USE_NSS) + +// On success |value| will be filled with data which must be freed by +// SECITEM_FreeItem(value, PR_FALSE); +bool ReadUint(SECKEYPrivateKey* key, + CK_ATTRIBUTE_TYPE attribute, + SECItem* value) { + SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, attribute, value); + + // PK11_ReadRawAttribute() returns items of type siBuffer. However in order + // for the ASN.1 encoding to be correct, the items must be of type + // siUnsignedInteger. + value->type = siUnsignedInteger; + + return rv == SECSuccess; +} + +// Fills |out| with the RSA private key properties. Returns true on success. +// Regardless of the return value, the caller must invoke FreeRSAPrivateKey() +// to free up any allocated memory. +// +// The passed in RSAPrivateKey must be zero-initialized. +bool InitRSAPrivateKey(SECKEYPrivateKey* key, RSAPrivateKey* out) { + if (key->keyType != rsaKey) + return false; + + // Everything should be zero-ed out. These are just some spot checks. + DCHECK(!out->version.data); + DCHECK(!out->version.len); + DCHECK(!out->modulus.data); + DCHECK(!out->modulus.len); + + // Always use version=0 since not using multi-prime. + if (!SEC_ASN1EncodeInteger(NULL, &out->version, 0)) + return false; + + if (!ReadUint(key, CKA_MODULUS, &out->modulus)) + return false; + if (!ReadUint(key, CKA_PUBLIC_EXPONENT, &out->public_exponent)) + return false; + if (!ReadUint(key, CKA_PRIVATE_EXPONENT, &out->private_exponent)) + return false; + if (!ReadUint(key, CKA_PRIME_1, &out->prime1)) + return false; + if (!ReadUint(key, CKA_PRIME_2, &out->prime2)) + return false; + if (!ReadUint(key, CKA_EXPONENT_1, &out->exponent1)) + return false; + if (!ReadUint(key, CKA_EXPONENT_2, &out->exponent2)) + return false; + if (!ReadUint(key, CKA_COEFFICIENT, &out->coefficient)) + return false; + + return true; +} + +struct FreeRsaPrivateKey { + void operator()(RSAPrivateKey* out) { + SECITEM_FreeItem(&out->version, PR_FALSE); + SECITEM_FreeItem(&out->modulus, PR_FALSE); + SECITEM_FreeItem(&out->public_exponent, PR_FALSE); + SECITEM_FreeItem(&out->private_exponent, PR_FALSE); + SECITEM_FreeItem(&out->prime1, PR_FALSE); + SECITEM_FreeItem(&out->prime2, PR_FALSE); + SECITEM_FreeItem(&out->exponent1, PR_FALSE); + SECITEM_FreeItem(&out->exponent2, PR_FALSE); + SECITEM_FreeItem(&out->coefficient, PR_FALSE); + } +}; + +typedef scoped_ptr<CERTSubjectPublicKeyInfo, + crypto::NSSDestroyer<CERTSubjectPublicKeyInfo, + SECKEY_DestroySubjectPublicKeyInfo> > + ScopedCERTSubjectPublicKeyInfo; + +struct DestroyGenericObject { + void operator()(PK11GenericObject* o) const { + if (o) + PK11_DestroyGenericObject(o); + } +}; + +typedef scoped_ptr<PK11GenericObject, DestroyGenericObject> + ScopedPK11GenericObject; + +// Helper to add an attribute to a template. +void AddAttribute(CK_ATTRIBUTE_TYPE type, + void* value, + unsigned long length, + std::vector<CK_ATTRIBUTE>* templ) { + CK_ATTRIBUTE attribute = {type, value, length}; + templ->push_back(attribute); +} + +// Helper to optionally add an attribute to a template, if the provided data is +// non-empty. +void AddOptionalAttribute(CK_ATTRIBUTE_TYPE type, + const CryptoData& data, + std::vector<CK_ATTRIBUTE>* templ) { + if (!data.byte_length()) + return; + CK_ATTRIBUTE attribute = {type, const_cast<unsigned char*>(data.bytes()), + data.byte_length()}; + templ->push_back(attribute); +} + +void AddOptionalAttribute(CK_ATTRIBUTE_TYPE type, + const std::string& data, + std::vector<CK_ATTRIBUTE>* templ) { + AddOptionalAttribute(type, CryptoData(data), templ); +} + +Status ExportKeyPkcs8Nss(SECKEYPrivateKey* key, std::vector<uint8>* buffer) { + if (key->keyType != rsaKey) + return Status::ErrorUnsupported(); + +// TODO(rsleevi): Implement OAEP support according to the spec. + +#if defined(USE_NSS) + // PK11_ExportDERPrivateKeyInfo isn't available. Use our fallback code. + const SECOidTag algorithm = SEC_OID_PKCS1_RSA_ENCRYPTION; + const int kPrivateKeyInfoVersion = 0; + + SECKEYPrivateKeyInfo private_key_info = {}; + RSAPrivateKey rsa_private_key = {}; + scoped_ptr<RSAPrivateKey, FreeRsaPrivateKey> free_private_key( + &rsa_private_key); + + // http://crbug.com/366427: the spec does not define any other failures for + // exporting, so none of the subsequent errors are spec compliant. + if (!InitRSAPrivateKey(key, &rsa_private_key)) + return Status::OperationError(); + + crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + if (!arena.get()) + return Status::OperationError(); + + if (!SEC_ASN1EncodeItem(arena.get(), + &private_key_info.privateKey, + &rsa_private_key, + RSAPrivateKeyTemplate)) + return Status::OperationError(); + + if (SECSuccess != + SECOID_SetAlgorithmID( + arena.get(), &private_key_info.algorithm, algorithm, NULL)) + return Status::OperationError(); + + if (!SEC_ASN1EncodeInteger( + arena.get(), &private_key_info.version, kPrivateKeyInfoVersion)) + return Status::OperationError(); + + crypto::ScopedSECItem encoded_key( + SEC_ASN1EncodeItem(NULL, + NULL, + &private_key_info, + SEC_ASN1_GET(SECKEY_PrivateKeyInfoTemplate))); +#else // defined(USE_NSS) + crypto::ScopedSECItem encoded_key(PK11_ExportDERPrivateKeyInfo(key, NULL)); +#endif // defined(USE_NSS) + + if (!encoded_key.get()) + return Status::OperationError(); + + buffer->assign(encoded_key->data, encoded_key->data + encoded_key->len); + return Status::Success(); +} + +Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + const JwkRsaInfo& params, + blink::WebCryptoKey* key) { + Status status = NssSupportsRsaKeyImport(); + if (status.IsError()) + return status; + + CK_OBJECT_CLASS obj_class = CKO_PRIVATE_KEY; + CK_KEY_TYPE key_type = CKK_RSA; + CK_BBOOL ck_false = CK_FALSE; + + std::vector<CK_ATTRIBUTE> key_template; + + AddAttribute(CKA_CLASS, &obj_class, sizeof(obj_class), &key_template); + AddAttribute(CKA_KEY_TYPE, &key_type, sizeof(key_type), &key_template); + AddAttribute(CKA_TOKEN, &ck_false, sizeof(ck_false), &key_template); + AddAttribute(CKA_SENSITIVE, &ck_false, sizeof(ck_false), &key_template); + AddAttribute(CKA_PRIVATE, &ck_false, sizeof(ck_false), &key_template); + + // Required properties. + AddOptionalAttribute(CKA_MODULUS, params.n, &key_template); + AddOptionalAttribute(CKA_PUBLIC_EXPONENT, params.e, &key_template); + AddOptionalAttribute(CKA_PRIVATE_EXPONENT, params.d, &key_template); + + // Manufacture a CKA_ID so the created key can be retrieved later as a + // SECKEYPrivateKey using FindKeyByKeyID(). Unfortunately there isn't a more + // direct way to do this in NSS. + // + // For consistency with other NSS key creation methods, set the CKA_ID to + // PK11_MakeIDFromPubKey(). There are some problems with + // this approach: + // + // (1) Prior to NSS 3.16.2, there is no parameter validation when creating + // private keys. It is therefore possible to construct a key using the + // known public modulus, and where all the other parameters are bogus. + // FindKeyByKeyID() returns the first key matching the ID. So this would + // effectively allow an attacker to retrieve a private key of their + // choice. + // + // (2) The ID space is shared by different key types. So theoretically + // possible to retrieve a key of the wrong type which has a matching + // CKA_ID. In practice I am told this is not likely except for small key + // sizes, since would require constructing keys with the same public + // data. + // + // (3) FindKeyByKeyID() doesn't necessarily return the object that was just + // created by CreateGenericObject. If the pre-existing key was + // provisioned with flags incompatible with WebCrypto (for instance + // marked sensitive) then this will break things. + SECItem modulus_item = MakeSECItemForBuffer(CryptoData(params.n)); + crypto::ScopedSECItem object_id(PK11_MakeIDFromPubKey(&modulus_item)); + AddOptionalAttribute( + CKA_ID, CryptoData(object_id->data, object_id->len), &key_template); + + // Optional properties (all of these will have been specified or none). + AddOptionalAttribute(CKA_PRIME_1, params.p, &key_template); + AddOptionalAttribute(CKA_PRIME_2, params.q, &key_template); + AddOptionalAttribute(CKA_EXPONENT_1, params.dp, &key_template); + AddOptionalAttribute(CKA_EXPONENT_2, params.dq, &key_template); + AddOptionalAttribute(CKA_COEFFICIENT, params.qi, &key_template); + + crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); + + ScopedPK11GenericObject key_object(PK11_CreateGenericObject( + slot.get(), &key_template[0], key_template.size(), PR_FALSE)); + + if (!key_object) + return Status::OperationError(); + + crypto::ScopedSECKEYPrivateKey private_key_tmp( + PK11_FindKeyByKeyID(slot.get(), object_id.get(), NULL)); + + // PK11_FindKeyByKeyID() may return a handle to an existing key, rather than + // the object created by PK11_CreateGenericObject(). + crypto::ScopedSECKEYPrivateKey private_key( + SECKEY_CopyPrivateKey(private_key_tmp.get())); + + if (!private_key) + return Status::OperationError(); + + blink::WebCryptoKeyAlgorithm key_algorithm; + if (!CreatePrivateKeyAlgorithm(algorithm, private_key.get(), &key_algorithm)) + return Status::ErrorUnexpected(); + + std::vector<uint8> pkcs8_data; + status = ExportKeyPkcs8Nss(private_key.get(), &pkcs8_data); + if (status.IsError()) + return status; + + scoped_ptr<PrivateKeyNss> key_handle( + new PrivateKeyNss(private_key.Pass(), CryptoData(pkcs8_data))); + + *key = blink::WebCryptoKey::create(key_handle.release(), + blink::WebCryptoKeyTypePrivate, + extractable, + key_algorithm, + usage_mask); + return Status::Success(); +} + +Status ExportKeySpkiNss(SECKEYPublicKey* key, std::vector<uint8>* buffer) { + const crypto::ScopedSECItem spki_der( + SECKEY_EncodeDERSubjectPublicKeyInfo(key)); + if (!spki_der) + return Status::OperationError(); + + buffer->assign(spki_der->data, spki_der->data + spki_der->len); + return Status::Success(); +} + +Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + const CryptoData& modulus_data, + const CryptoData& exponent_data, + blink::WebCryptoKey* key) { + if (!modulus_data.byte_length()) + return Status::ErrorImportRsaEmptyModulus(); + + if (!exponent_data.byte_length()) + return Status::ErrorImportRsaEmptyExponent(); + + DCHECK(modulus_data.bytes()); + DCHECK(exponent_data.bytes()); + + // NSS does not provide a way to create an RSA public key directly from the + // modulus and exponent values, but it can import an DER-encoded ASN.1 blob + // with these values and create the public key from that. The code below + // follows the recommendation described in + // https://developer.mozilla.org/en-US/docs/NSS/NSS_Tech_Notes/nss_tech_note7 + + // Pack the input values into a struct compatible with NSS ASN.1 encoding, and + // set up an ASN.1 encoder template for it. + struct RsaPublicKeyData { + SECItem modulus; + SECItem exponent; + }; + const RsaPublicKeyData pubkey_in = { + {siUnsignedInteger, const_cast<unsigned char*>(modulus_data.bytes()), + modulus_data.byte_length()}, + {siUnsignedInteger, const_cast<unsigned char*>(exponent_data.bytes()), + exponent_data.byte_length()}}; + const SEC_ASN1Template rsa_public_key_template[] = { + {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(RsaPublicKeyData)}, + { + SEC_ASN1_INTEGER, offsetof(RsaPublicKeyData, modulus), + }, + { + SEC_ASN1_INTEGER, offsetof(RsaPublicKeyData, exponent), + }, + { + 0, + }}; + + // DER-encode the public key. + crypto::ScopedSECItem pubkey_der( + SEC_ASN1EncodeItem(NULL, NULL, &pubkey_in, rsa_public_key_template)); + if (!pubkey_der) + return Status::OperationError(); + + // Import the DER-encoded public key to create an RSA SECKEYPublicKey. + crypto::ScopedSECKEYPublicKey pubkey( + SECKEY_ImportDERPublicKey(pubkey_der.get(), CKK_RSA)); + if (!pubkey) + return Status::OperationError(); + + blink::WebCryptoKeyAlgorithm key_algorithm; + if (!CreatePublicKeyAlgorithm(algorithm, pubkey.get(), &key_algorithm)) + return Status::ErrorUnexpected(); + + std::vector<uint8> spki_data; + Status status = ExportKeySpkiNss(pubkey.get(), &spki_data); + if (status.IsError()) + return status; + + scoped_ptr<PublicKeyNss> key_handle( + new PublicKeyNss(pubkey.Pass(), CryptoData(spki_data))); + + *key = blink::WebCryptoKey::create(key_handle.release(), + blink::WebCryptoKeyTypePublic, + extractable, + key_algorithm, + usage_mask); + return Status::Success(); +} + +} // namespace + +Status RsaHashedAlgorithm::VerifyKeyUsagesBeforeGenerateKeyPair( + blink::WebCryptoKeyUsageMask combined_usage_mask, + blink::WebCryptoKeyUsageMask* public_usage_mask, + blink::WebCryptoKeyUsageMask* private_usage_mask) const { + Status status = CheckKeyCreationUsages( + all_public_key_usages_ | all_private_key_usages_, combined_usage_mask); + if (status.IsError()) + return status; + + *public_usage_mask = combined_usage_mask & all_public_key_usages_; + *private_usage_mask = combined_usage_mask & all_private_key_usages_; + + return Status::Success(); +} + +Status RsaHashedAlgorithm::GenerateKeyPair( + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask public_usage_mask, + blink::WebCryptoKeyUsageMask private_usage_mask, + blink::WebCryptoKey* public_key, + blink::WebCryptoKey* private_key) const { + const blink::WebCryptoRsaHashedKeyGenParams* params = + algorithm.rsaHashedKeyGenParams(); + + if (!params->modulusLengthBits()) + return Status::ErrorGenerateRsaZeroModulus(); + + unsigned long public_exponent = 0; + if (!BigIntegerToLong(params->publicExponent().data(), + params->publicExponent().size(), + &public_exponent) || + (public_exponent != 3 && public_exponent != 65537)) { + return Status::ErrorGenerateKeyPublicExponent(); + } + + crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); + if (!slot) + return Status::OperationError(); + + PK11RSAGenParams rsa_gen_params; + // keySizeInBits is a signed type, don't pass in a negative value. + if (params->modulusLengthBits() > INT_MAX) + return Status::OperationError(); + rsa_gen_params.keySizeInBits = params->modulusLengthBits(); + rsa_gen_params.pe = public_exponent; + + const CK_FLAGS operation_flags_mask = + CKF_ENCRYPT | CKF_DECRYPT | CKF_SIGN | CKF_VERIFY | CKF_WRAP | CKF_UNWRAP; + + // The private key must be marked as insensitive and extractable, otherwise it + // cannot later be exported in unencrypted form or structured-cloned. + const PK11AttrFlags attribute_flags = + PK11_ATTR_INSENSITIVE | PK11_ATTR_EXTRACTABLE; + + // Note: NSS does not generate an sec_public_key if the call below fails, + // so there is no danger of a leaked sec_public_key. + SECKEYPublicKey* sec_public_key; + crypto::ScopedSECKEYPrivateKey scoped_sec_private_key( + PK11_GenerateKeyPairWithOpFlags(slot.get(), + CKM_RSA_PKCS_KEY_PAIR_GEN, + &rsa_gen_params, + &sec_public_key, + attribute_flags, + generate_flags_, + operation_flags_mask, + NULL)); + if (!scoped_sec_private_key) + return Status::OperationError(); + + blink::WebCryptoKeyAlgorithm key_algorithm; + if (!CreatePublicKeyAlgorithm(algorithm, sec_public_key, &key_algorithm)) + return Status::ErrorUnexpected(); + + std::vector<uint8> spki_data; + Status status = ExportKeySpkiNss(sec_public_key, &spki_data); + if (status.IsError()) + return status; + + scoped_ptr<PublicKeyNss> public_key_handle(new PublicKeyNss( + crypto::ScopedSECKEYPublicKey(sec_public_key), CryptoData(spki_data))); + + std::vector<uint8> pkcs8_data; + status = ExportKeyPkcs8Nss(scoped_sec_private_key.get(), &pkcs8_data); + if (status.IsError()) + return status; + + scoped_ptr<PrivateKeyNss> private_key_handle( + new PrivateKeyNss(scoped_sec_private_key.Pass(), CryptoData(pkcs8_data))); + + *public_key = blink::WebCryptoKey::create(public_key_handle.release(), + blink::WebCryptoKeyTypePublic, + true, + key_algorithm, + public_usage_mask); + *private_key = blink::WebCryptoKey::create(private_key_handle.release(), + blink::WebCryptoKeyTypePrivate, + extractable, + key_algorithm, + private_usage_mask); + + return Status::Success(); +} + +Status RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usage_mask) const { + switch (format) { + case blink::WebCryptoKeyFormatSpki: + return CheckKeyCreationUsages(all_public_key_usages_, usage_mask); + case blink::WebCryptoKeyFormatPkcs8: + return CheckKeyCreationUsages(all_private_key_usages_, usage_mask); + case blink::WebCryptoKeyFormatJwk: + return CheckKeyCreationUsages( + all_public_key_usages_ | all_private_key_usages_, usage_mask); + default: + return Status::ErrorUnsupportedImportKeyFormat(); + } +} + +Status RsaHashedAlgorithm::ImportKeyPkcs8( + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const { + Status status = NssSupportsRsaKeyImport(); + if (status.IsError()) + return status; + + if (!key_data.byte_length()) + return Status::ErrorImportEmptyKeyData(); + + // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 PKCS#8 + // private key info object. + SECItem pki_der = MakeSECItemForBuffer(key_data); + + SECKEYPrivateKey* seckey_private_key = NULL; + crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); + if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot.get(), + &pki_der, + NULL, // nickname + NULL, // publicValue + false, // isPerm + false, // isPrivate + KU_ALL, // usage + &seckey_private_key, + NULL) != SECSuccess) { + return Status::DataError(); + } + DCHECK(seckey_private_key); + crypto::ScopedSECKEYPrivateKey private_key(seckey_private_key); + + const KeyType sec_key_type = SECKEY_GetPrivateKeyType(private_key.get()); + if (sec_key_type != rsaKey) + return Status::DataError(); + + blink::WebCryptoKeyAlgorithm key_algorithm; + if (!CreateRsaHashedPrivateKeyAlgorithm( + algorithm, private_key.get(), &key_algorithm)) + return Status::ErrorUnexpected(); + + // TODO(eroman): This is probably going to be the same as the input. + std::vector<uint8> pkcs8_data; + status = ExportKeyPkcs8Nss(private_key.get(), &pkcs8_data); + if (status.IsError()) + return status; + + scoped_ptr<PrivateKeyNss> key_handle( + new PrivateKeyNss(private_key.Pass(), CryptoData(pkcs8_data))); + + *key = blink::WebCryptoKey::create(key_handle.release(), + blink::WebCryptoKeyTypePrivate, + extractable, + key_algorithm, + usage_mask); + + return Status::Success(); +} + +Status RsaHashedAlgorithm::ImportKeySpki( + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const { + Status status = NssSupportsRsaKeyImport(); + if (status.IsError()) + return status; + + if (!key_data.byte_length()) + return Status::ErrorImportEmptyKeyData(); + + // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 Subject + // Public Key Info. Decode this to a CERTSubjectPublicKeyInfo. + SECItem spki_item = MakeSECItemForBuffer(key_data); + const ScopedCERTSubjectPublicKeyInfo spki( + SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); + if (!spki) + return Status::DataError(); + + crypto::ScopedSECKEYPublicKey sec_public_key( + SECKEY_ExtractPublicKey(spki.get())); + if (!sec_public_key) + return Status::DataError(); + + const KeyType sec_key_type = SECKEY_GetPublicKeyType(sec_public_key.get()); + if (sec_key_type != rsaKey) + return Status::DataError(); + + blink::WebCryptoKeyAlgorithm key_algorithm; + if (!CreateRsaHashedPublicKeyAlgorithm( + algorithm, sec_public_key.get(), &key_algorithm)) + return Status::ErrorUnexpected(); + + // TODO(eroman): This is probably going to be the same as the input. + std::vector<uint8> spki_data; + status = ExportKeySpkiNss(sec_public_key.get(), &spki_data); + if (status.IsError()) + return status; + + scoped_ptr<PublicKeyNss> key_handle( + new PublicKeyNss(sec_public_key.Pass(), CryptoData(spki_data))); + + *key = blink::WebCryptoKey::create(key_handle.release(), + blink::WebCryptoKeyTypePublic, + extractable, + key_algorithm, + usage_mask); + + return Status::Success(); +} + +Status RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const { + if (key.type() != blink::WebCryptoKeyTypePrivate) + return Status::ErrorUnexpectedKeyType(); + *buffer = PrivateKeyNss::Cast(key)->pkcs8_data(); + return Status::Success(); +} + +Status RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const { + if (key.type() != blink::WebCryptoKeyTypePublic) + return Status::ErrorUnexpectedKeyType(); + *buffer = PublicKeyNss::Cast(key)->spki_data(); + return Status::Success(); +} + +Status RsaHashedAlgorithm::ImportKeyJwk( + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const { + const char* jwk_algorithm = + GetJwkAlgorithm(algorithm.rsaHashedImportParams()->hash().id()); + + if (!jwk_algorithm) + return Status::ErrorUnexpected(); + + JwkRsaInfo jwk; + Status status = + ReadRsaKeyJwk(key_data, jwk_algorithm, extractable, usage_mask, &jwk); + if (status.IsError()) + return status; + + // Once the key type is known, verify the usages. + status = CheckKeyCreationUsages( + jwk.is_private_key ? all_private_key_usages_ : all_public_key_usages_, + usage_mask); + if (status.IsError()) + return Status::ErrorCreateKeyBadUsages(); + + return jwk.is_private_key + ? ImportRsaPrivateKey(algorithm, extractable, usage_mask, jwk, key) + : ImportRsaPublicKey(algorithm, + extractable, + usage_mask, + CryptoData(jwk.n), + CryptoData(jwk.e), + key); +} + +Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const { + const char* jwk_algorithm = + GetJwkAlgorithm(key.algorithm().rsaHashedParams()->hash().id()); + + if (!jwk_algorithm) + return Status::ErrorUnexpected(); + + switch (key.type()) { + case blink::WebCryptoKeyTypePublic: { + SECKEYPublicKey* nss_key = PublicKeyNss::Cast(key)->key(); + if (nss_key->keyType != rsaKey) + return Status::ErrorUnsupported(); + + WriteRsaPublicKeyJwk(SECItemToCryptoData(nss_key->u.rsa.modulus), + SECItemToCryptoData(nss_key->u.rsa.publicExponent), + jwk_algorithm, + key.extractable(), + key.usages(), + buffer); + + return Status::Success(); + } + + case blink::WebCryptoKeyTypePrivate: { + SECKEYPrivateKey* nss_key = PrivateKeyNss::Cast(key)->key(); + RSAPrivateKey key_props = {}; + scoped_ptr<RSAPrivateKey, FreeRsaPrivateKey> free_private_key(&key_props); + + if (!InitRSAPrivateKey(nss_key, &key_props)) + return Status::OperationError(); + + WriteRsaPrivateKeyJwk(SECItemToCryptoData(key_props.modulus), + SECItemToCryptoData(key_props.public_exponent), + SECItemToCryptoData(key_props.private_exponent), + SECItemToCryptoData(key_props.prime1), + SECItemToCryptoData(key_props.prime2), + SECItemToCryptoData(key_props.exponent1), + SECItemToCryptoData(key_props.exponent2), + SECItemToCryptoData(key_props.coefficient), + jwk_algorithm, + key.extractable(), + key.usages(), + buffer); + + return Status::Success(); + } + default: + return Status::ErrorUnexpected(); + } +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/nss/rsa_key_nss.h b/content/child/webcrypto/nss/rsa_key_nss.h new file mode 100644 index 0000000..06466aa --- /dev/null +++ b/content/child/webcrypto/nss/rsa_key_nss.h @@ -0,0 +1,100 @@ +// 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. + +#ifndef CONTENT_CHILD_WEBCRYPTO_NSS_RSA_KEY_NSS_H_ +#define CONTENT_CHILD_WEBCRYPTO_NSS_RSA_KEY_NSS_H_ + +#include <pkcs11t.h> + +#include "content/child/webcrypto/algorithm_implementation.h" + +namespace content { + +namespace webcrypto { + +class PublicKeyNss; +class PrivateKeyNss; + +// Base class for an RSA algorithm whose keys additionaly have a hash parameter +// bound to them. Provides functionality for generating, importing, and +// exporting keys. +class RsaHashedAlgorithm : public AlgorithmImplementation { + public: + // Constructs an RSA algorithm which will use the NSS flags |generate_flags| + // when generating keys. |all_public_key_usages| and |all_private_key_usages| + // are the set of WebCrypto key usages that are valid for created keys + // (public and private respectively). + // + // For instance if public keys support encryption and wrapping, and private + // keys support decryption and unwrapping callers should set: + // all_public_key_usages = UsageEncrypt | UsageWrap + // all_private_key_usages = UsageDecrypt | UsageUnwrap + // This information is used when importing or generating keys, to enforce + // that valid key usages are allowed. + RsaHashedAlgorithm(CK_FLAGS generate_flags, + blink::WebCryptoKeyUsageMask all_public_key_usages, + blink::WebCryptoKeyUsageMask all_private_key_usages) + : generate_flags_(generate_flags), + all_public_key_usages_(all_public_key_usages), + all_private_key_usages_(all_private_key_usages) {} + + // For instance "RSA-OAEP-256". + virtual const char* GetJwkAlgorithm( + const blink::WebCryptoAlgorithmId hash) const = 0; + + virtual Status VerifyKeyUsagesBeforeGenerateKeyPair( + blink::WebCryptoKeyUsageMask combined_usage_mask, + blink::WebCryptoKeyUsageMask* public_usage_mask, + blink::WebCryptoKeyUsageMask* private_usage_mask) const OVERRIDE; + + virtual Status GenerateKeyPair( + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask public_usage_mask, + blink::WebCryptoKeyUsageMask private_usage_mask, + blink::WebCryptoKey* public_key, + blink::WebCryptoKey* private_key) const OVERRIDE; + + virtual Status VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE; + + virtual Status ImportKeyPkcs8(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const OVERRIDE; + + virtual Status ImportKeySpki(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const OVERRIDE; + + virtual Status ExportKeyPkcs8(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const OVERRIDE; + + virtual Status ExportKeySpki(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const OVERRIDE; + + virtual Status ImportKeyJwk(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const OVERRIDE; + + virtual Status ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const OVERRIDE; + + private: + CK_FLAGS generate_flags_; + blink::WebCryptoKeyUsageMask all_public_key_usages_; + blink::WebCryptoKeyUsageMask all_private_key_usages_; +}; + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_NSS_RSA_KEY_NSS_H_ diff --git a/content/child/webcrypto/nss/rsa_oaep_nss.cc b/content/child/webcrypto/nss/rsa_oaep_nss.cc new file mode 100644 index 0000000..c9fd77f --- /dev/null +++ b/content/child/webcrypto/nss/rsa_oaep_nss.cc @@ -0,0 +1,246 @@ +// 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 <cryptohi.h> +#include <keyhi.h> +#include <pk11pub.h> +#include <secerr.h> +#include <sechash.h> + +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/nss/key_nss.h" +#include "content/child/webcrypto/nss/rsa_key_nss.h" +#include "content/child/webcrypto/nss/util_nss.h" +#include "content/child/webcrypto/status.h" +#include "content/child/webcrypto/webcrypto_util.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace content { + +namespace webcrypto { + +namespace { + +Status NssSupportsRsaOaep() { + if (NssRuntimeSupport::Get()->IsRsaOaepSupported()) + return Status::Success(); + return Status::ErrorUnsupported( + "NSS version doesn't support RSA-OAEP. Try using version 3.16.2 or " + "later"); +} + +CK_MECHANISM_TYPE WebCryptoHashToMGFMechanism( + const blink::WebCryptoAlgorithm& algorithm) { + switch (algorithm.id()) { + case blink::WebCryptoAlgorithmIdSha1: + return CKG_MGF1_SHA1; + case blink::WebCryptoAlgorithmIdSha256: + return CKG_MGF1_SHA256; + case blink::WebCryptoAlgorithmIdSha384: + return CKG_MGF1_SHA384; + case blink::WebCryptoAlgorithmIdSha512: + return CKG_MGF1_SHA512; + default: + return CKM_INVALID_MECHANISM; + } +} + +CK_MECHANISM_TYPE WebCryptoHashToDigestMechanism( + const blink::WebCryptoAlgorithm& algorithm) { + switch (algorithm.id()) { + case blink::WebCryptoAlgorithmIdSha1: + return CKM_SHA_1; + case blink::WebCryptoAlgorithmIdSha256: + return CKM_SHA256; + case blink::WebCryptoAlgorithmIdSha384: + return CKM_SHA384; + case blink::WebCryptoAlgorithmIdSha512: + return CKM_SHA512; + default: + // Not a supported algorithm. + return CKM_INVALID_MECHANISM; + } +} + +bool InitializeRsaOaepParams(const blink::WebCryptoAlgorithm& hash, + const CryptoData& label, + CK_RSA_PKCS_OAEP_PARAMS* oaep_params) { + oaep_params->source = CKZ_DATA_SPECIFIED; + oaep_params->pSourceData = const_cast<unsigned char*>(label.bytes()); + oaep_params->ulSourceDataLen = label.byte_length(); + oaep_params->mgf = WebCryptoHashToMGFMechanism(hash); + oaep_params->hashAlg = WebCryptoHashToDigestMechanism(hash); + + if (oaep_params->mgf == CKM_INVALID_MECHANISM || + oaep_params->hashAlg == CKM_INVALID_MECHANISM) { + return false; + } + + return true; +} + +Status EncryptRsaOaep(SECKEYPublicKey* key, + const blink::WebCryptoAlgorithm& hash, + const CryptoData& label, + const CryptoData& data, + std::vector<uint8>* buffer) { + CK_RSA_PKCS_OAEP_PARAMS oaep_params = {0}; + if (!InitializeRsaOaepParams(hash, label, &oaep_params)) + return Status::ErrorUnsupported(); + + SECItem param; + param.type = siBuffer; + param.data = reinterpret_cast<unsigned char*>(&oaep_params); + param.len = sizeof(oaep_params); + + buffer->resize(SECKEY_PublicKeyStrength(key)); + unsigned char* buffer_data = Uint8VectorStart(buffer); + unsigned int output_len; + if (NssRuntimeSupport::Get()->pk11_pub_encrypt_func()(key, + CKM_RSA_PKCS_OAEP, + ¶m, + buffer_data, + &output_len, + buffer->size(), + data.bytes(), + data.byte_length(), + NULL) != SECSuccess) { + return Status::OperationError(); + } + + CHECK_LE(output_len, buffer->size()); + buffer->resize(output_len); + return Status::Success(); +} + +Status DecryptRsaOaep(SECKEYPrivateKey* key, + const blink::WebCryptoAlgorithm& hash, + const CryptoData& label, + const CryptoData& data, + std::vector<uint8>* buffer) { + Status status = NssSupportsRsaOaep(); + if (status.IsError()) + return status; + + CK_RSA_PKCS_OAEP_PARAMS oaep_params = {0}; + if (!InitializeRsaOaepParams(hash, label, &oaep_params)) + return Status::ErrorUnsupported(); + + SECItem param; + param.type = siBuffer; + param.data = reinterpret_cast<unsigned char*>(&oaep_params); + param.len = sizeof(oaep_params); + + const int modulus_length_bytes = PK11_GetPrivateModulusLen(key); + if (modulus_length_bytes <= 0) + return Status::ErrorUnexpected(); + + buffer->resize(modulus_length_bytes); + + unsigned char* buffer_data = Uint8VectorStart(buffer); + unsigned int output_len; + if (NssRuntimeSupport::Get()->pk11_priv_decrypt_func()(key, + CKM_RSA_PKCS_OAEP, + ¶m, + buffer_data, + &output_len, + buffer->size(), + data.bytes(), + data.byte_length()) != + SECSuccess) { + return Status::OperationError(); + } + + CHECK_LE(output_len, buffer->size()); + buffer->resize(output_len); + return Status::Success(); +} + +class RsaOaepImplementation : public RsaHashedAlgorithm { + public: + RsaOaepImplementation() + : RsaHashedAlgorithm( + CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP, + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageWrapKey, + blink::WebCryptoKeyUsageDecrypt | + blink::WebCryptoKeyUsageUnwrapKey) {} + + virtual Status VerifyKeyUsagesBeforeGenerateKeyPair( + blink::WebCryptoKeyUsageMask combined_usage_mask, + blink::WebCryptoKeyUsageMask* public_usage_mask, + blink::WebCryptoKeyUsageMask* private_usage_mask) const OVERRIDE { + Status status = NssSupportsRsaOaep(); + if (status.IsError()) + return status; + return RsaHashedAlgorithm::VerifyKeyUsagesBeforeGenerateKeyPair( + combined_usage_mask, public_usage_mask, private_usage_mask); + } + + virtual Status VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE { + Status status = NssSupportsRsaOaep(); + if (status.IsError()) + return status; + return RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey(format, + usage_mask); + } + + virtual const char* GetJwkAlgorithm( + const blink::WebCryptoAlgorithmId hash) const OVERRIDE { + switch (hash) { + case blink::WebCryptoAlgorithmIdSha1: + return "RSA-OAEP"; + case blink::WebCryptoAlgorithmIdSha256: + return "RSA-OAEP-256"; + case blink::WebCryptoAlgorithmIdSha384: + return "RSA-OAEP-384"; + case blink::WebCryptoAlgorithmIdSha512: + return "RSA-OAEP-512"; + default: + return NULL; + } + } + + virtual Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + if (key.type() != blink::WebCryptoKeyTypePublic) + return Status::ErrorUnexpectedKeyType(); + + return EncryptRsaOaep( + PublicKeyNss::Cast(key)->key(), + key.algorithm().rsaHashedParams()->hash(), + CryptoData(algorithm.rsaOaepParams()->optionalLabel()), + data, + buffer); + } + + virtual Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + if (key.type() != blink::WebCryptoKeyTypePrivate) + return Status::ErrorUnexpectedKeyType(); + + return DecryptRsaOaep( + PrivateKeyNss::Cast(key)->key(), + key.algorithm().rsaHashedParams()->hash(), + CryptoData(algorithm.rsaOaepParams()->optionalLabel()), + data, + buffer); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformRsaOaepImplementation() { + return new RsaOaepImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/nss/rsa_ssa_nss.cc b/content/child/webcrypto/nss/rsa_ssa_nss.cc new file mode 100644 index 0000000..97d1b4e --- /dev/null +++ b/content/child/webcrypto/nss/rsa_ssa_nss.cc @@ -0,0 +1,144 @@ +// 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 <cryptohi.h> + +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/nss/key_nss.h" +#include "content/child/webcrypto/nss/rsa_key_nss.h" +#include "content/child/webcrypto/nss/util_nss.h" +#include "content/child/webcrypto/status.h" +#include "crypto/scoped_nss_types.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace content { + +namespace webcrypto { + +namespace { + +class RsaSsaImplementation : public RsaHashedAlgorithm { + public: + RsaSsaImplementation() + : RsaHashedAlgorithm(CKF_SIGN | CKF_VERIFY, + blink::WebCryptoKeyUsageVerify, + blink::WebCryptoKeyUsageSign) {} + + virtual const char* GetJwkAlgorithm( + const blink::WebCryptoAlgorithmId hash) const OVERRIDE { + switch (hash) { + case blink::WebCryptoAlgorithmIdSha1: + return "RS1"; + case blink::WebCryptoAlgorithmIdSha256: + return "RS256"; + case blink::WebCryptoAlgorithmIdSha384: + return "RS384"; + case blink::WebCryptoAlgorithmIdSha512: + return "RS512"; + default: + return NULL; + } + } + + virtual Status Sign(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + if (key.type() != blink::WebCryptoKeyTypePrivate) + return Status::ErrorUnexpectedKeyType(); + + SECKEYPrivateKey* private_key = PrivateKeyNss::Cast(key)->key(); + + const blink::WebCryptoAlgorithm& hash = + key.algorithm().rsaHashedParams()->hash(); + + // Pick the NSS signing algorithm by combining RSA-SSA (RSA PKCS1) and the + // inner hash of the input Web Crypto algorithm. + SECOidTag sign_alg_tag; + switch (hash.id()) { + case blink::WebCryptoAlgorithmIdSha1: + sign_alg_tag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; + break; + case blink::WebCryptoAlgorithmIdSha256: + sign_alg_tag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; + break; + case blink::WebCryptoAlgorithmIdSha384: + sign_alg_tag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; + break; + case blink::WebCryptoAlgorithmIdSha512: + sign_alg_tag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; + break; + default: + return Status::ErrorUnsupported(); + } + + crypto::ScopedSECItem signature_item(SECITEM_AllocItem(NULL, NULL, 0)); + if (SEC_SignData(signature_item.get(), + data.bytes(), + data.byte_length(), + private_key, + sign_alg_tag) != SECSuccess) { + return Status::OperationError(); + } + + buffer->assign(signature_item->data, + signature_item->data + signature_item->len); + return Status::Success(); + } + + virtual Status Verify(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& signature, + const CryptoData& data, + bool* signature_match) const OVERRIDE { + if (key.type() != blink::WebCryptoKeyTypePublic) + return Status::ErrorUnexpectedKeyType(); + + SECKEYPublicKey* public_key = PublicKeyNss::Cast(key)->key(); + + const blink::WebCryptoAlgorithm& hash = + key.algorithm().rsaHashedParams()->hash(); + + const SECItem signature_item = MakeSECItemForBuffer(signature); + + SECOidTag hash_alg_tag; + switch (hash.id()) { + case blink::WebCryptoAlgorithmIdSha1: + hash_alg_tag = SEC_OID_SHA1; + break; + case blink::WebCryptoAlgorithmIdSha256: + hash_alg_tag = SEC_OID_SHA256; + break; + case blink::WebCryptoAlgorithmIdSha384: + hash_alg_tag = SEC_OID_SHA384; + break; + case blink::WebCryptoAlgorithmIdSha512: + hash_alg_tag = SEC_OID_SHA512; + break; + default: + return Status::ErrorUnsupported(); + } + + *signature_match = + SECSuccess == VFY_VerifyDataDirect(data.bytes(), + data.byte_length(), + public_key, + &signature_item, + SEC_OID_PKCS1_RSA_ENCRYPTION, + hash_alg_tag, + NULL, + NULL); + return Status::Success(); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformRsaSsaImplementation() { + return new RsaSsaImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/nss/sha_nss.cc b/content/child/webcrypto/nss/sha_nss.cc new file mode 100644 index 0000000..ced07ca --- /dev/null +++ b/content/child/webcrypto/nss/sha_nss.cc @@ -0,0 +1,159 @@ +// 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 <sechash.h> +#include <vector> + +#include "content/child/webcrypto/algorithm_implementation.h" +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/nss/util_nss.h" +#include "content/child/webcrypto/status.h" +#include "content/child/webcrypto/webcrypto_util.h" +#include "crypto/nss_util.h" +#include "crypto/scoped_nss_types.h" + +namespace content { + +namespace webcrypto { + +namespace { + +HASH_HashType WebCryptoAlgorithmToNSSHashType( + blink::WebCryptoAlgorithmId algorithm) { + switch (algorithm) { + case blink::WebCryptoAlgorithmIdSha1: + return HASH_AlgSHA1; + case blink::WebCryptoAlgorithmIdSha256: + return HASH_AlgSHA256; + case blink::WebCryptoAlgorithmIdSha384: + return HASH_AlgSHA384; + case blink::WebCryptoAlgorithmIdSha512: + return HASH_AlgSHA512; + default: + // Not a digest algorithm. + return HASH_AlgNULL; + } +} + +// Implementation of blink::WebCryptoDigester, an internal Blink detail not +// part of WebCrypto, that allows chunks of data to be streamed in before +// computing a SHA-* digest (as opposed to ShaImplementation, which computes +// digests over complete messages) +class DigestorNSS : public blink::WebCryptoDigestor { + public: + explicit DigestorNSS(blink::WebCryptoAlgorithmId algorithm_id) + : hash_context_(NULL), algorithm_id_(algorithm_id) {} + + virtual ~DigestorNSS() { + if (!hash_context_) + return; + + HASH_Destroy(hash_context_); + hash_context_ = NULL; + } + + virtual bool consume(const unsigned char* data, unsigned int size) { + return ConsumeWithStatus(data, size).IsSuccess(); + } + + Status ConsumeWithStatus(const unsigned char* data, unsigned int size) { + // Initialize everything if the object hasn't been initialized yet. + if (!hash_context_) { + Status error = Init(); + if (!error.IsSuccess()) + return error; + } + + HASH_Update(hash_context_, data, size); + + return Status::Success(); + } + + virtual bool finish(unsigned char*& result_data, + unsigned int& result_data_size) { + Status error = FinishInternal(result_, &result_data_size); + if (!error.IsSuccess()) + return false; + result_data = result_; + return true; + } + + Status FinishWithVectorAndStatus(std::vector<uint8>* result) { + if (!hash_context_) + return Status::ErrorUnexpected(); + + unsigned int result_length = HASH_ResultLenContext(hash_context_); + result->resize(result_length); + unsigned char* digest = Uint8VectorStart(result); + unsigned int digest_size; // ignored + return FinishInternal(digest, &digest_size); + } + + private: + Status Init() { + HASH_HashType hash_type = WebCryptoAlgorithmToNSSHashType(algorithm_id_); + + if (hash_type == HASH_AlgNULL) + return Status::ErrorUnsupported(); + + hash_context_ = HASH_Create(hash_type); + if (!hash_context_) + return Status::OperationError(); + + HASH_Begin(hash_context_); + + return Status::Success(); + } + + Status FinishInternal(unsigned char* result, unsigned int* result_size) { + if (!hash_context_) { + Status error = Init(); + if (!error.IsSuccess()) + return error; + } + + unsigned int hash_result_length = HASH_ResultLenContext(hash_context_); + DCHECK_LE(hash_result_length, static_cast<size_t>(HASH_LENGTH_MAX)); + + HASH_End(hash_context_, result, result_size, hash_result_length); + + if (*result_size != hash_result_length) + return Status::ErrorUnexpected(); + return Status::Success(); + } + + HASHContext* hash_context_; + blink::WebCryptoAlgorithmId algorithm_id_; + unsigned char result_[HASH_LENGTH_MAX]; +}; + +class ShaImplementation : public AlgorithmImplementation { + public: + virtual Status Digest(const blink::WebCryptoAlgorithm& algorithm, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + DigestorNSS digestor(algorithm.id()); + Status error = digestor.ConsumeWithStatus(data.bytes(), data.byte_length()); + // http://crbug.com/366427: the spec does not define any other failures for + // digest, so none of the subsequent errors are spec compliant. + if (!error.IsSuccess()) + return error; + return digestor.FinishWithVectorAndStatus(buffer); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformShaImplementation() { + return new ShaImplementation(); +} + +scoped_ptr<blink::WebCryptoDigestor> CreatePlatformDigestor( + blink::WebCryptoAlgorithmId algorithm) { + return scoped_ptr<blink::WebCryptoDigestor>(new DigestorNSS(algorithm)); +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/nss/sym_key_nss.cc b/content/child/webcrypto/nss/sym_key_nss.cc new file mode 100644 index 0000000..08eef28 --- /dev/null +++ b/content/child/webcrypto/nss/sym_key_nss.cc @@ -0,0 +1,91 @@ +// 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/nss/sym_key_nss.h" + +#include "base/logging.h" +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/nss/key_nss.h" +#include "content/child/webcrypto/nss/util_nss.h" +#include "content/child/webcrypto/status.h" +#include "content/child/webcrypto/webcrypto_util.h" +#include "crypto/scoped_nss_types.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace content { + +namespace webcrypto { + +Status GenerateSecretKeyNss(const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + unsigned keylen_bytes, + CK_MECHANISM_TYPE mechanism, + blink::WebCryptoKey* key) { + DCHECK_NE(CKM_INVALID_MECHANISM, mechanism); + + crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); + if (!slot) + return Status::OperationError(); + + crypto::ScopedPK11SymKey pk11_key( + PK11_KeyGen(slot.get(), mechanism, NULL, keylen_bytes, NULL)); + + if (!pk11_key) + return Status::OperationError(); + + if (PK11_ExtractKeyValue(pk11_key.get()) != SECSuccess) + return Status::OperationError(); + + const SECItem* key_data = PK11_GetKeyData(pk11_key.get()); + if (!key_data) + return Status::OperationError(); + + scoped_ptr<SymKeyNss> handle(new SymKeyNss( + pk11_key.Pass(), CryptoData(key_data->data, key_data->len))); + + *key = blink::WebCryptoKey::create(handle.release(), + blink::WebCryptoKeyTypeSecret, + extractable, + algorithm, + usage_mask); + return Status::Success(); +} + +Status ImportKeyRawNss(const CryptoData& key_data, + const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + CK_MECHANISM_TYPE mechanism, + CK_FLAGS flags, + blink::WebCryptoKey* key) { + DCHECK(!algorithm.isNull()); + SECItem key_item = MakeSECItemForBuffer(key_data); + + crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); + crypto::ScopedPK11SymKey pk11_sym_key( + PK11_ImportSymKeyWithFlags(slot.get(), + mechanism, + PK11_OriginUnwrap, + CKA_FLAGS_ONLY, + &key_item, + flags, + false, + NULL)); + if (!pk11_sym_key.get()) + return Status::OperationError(); + + scoped_ptr<SymKeyNss> handle(new SymKeyNss(pk11_sym_key.Pass(), key_data)); + + *key = blink::WebCryptoKey::create(handle.release(), + blink::WebCryptoKeyTypeSecret, + extractable, + algorithm, + usage_mask); + return Status::Success(); +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/nss/sym_key_nss.h b/content/child/webcrypto/nss/sym_key_nss.h new file mode 100644 index 0000000..d6c1fe4 --- /dev/null +++ b/content/child/webcrypto/nss/sym_key_nss.h @@ -0,0 +1,38 @@ +// 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. + +#ifndef CONTENT_CHILD_WEBCRYPTO_NSS_SYM_KEY_NSS_H_ +#define CONTENT_CHILD_WEBCRYPTO_NSS_SYM_KEY_NSS_H_ + +#include <pkcs11t.h> + +#include "third_party/WebKit/public/platform/WebCrypto.h" + +namespace content { + +namespace webcrypto { + +class CryptoData; +class Status; + +Status GenerateSecretKeyNss(const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + unsigned keylen_bytes, + CK_MECHANISM_TYPE mechanism, + blink::WebCryptoKey* key); + +Status ImportKeyRawNss(const CryptoData& key_data, + const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + CK_MECHANISM_TYPE mechanism, + CK_FLAGS flags, + blink::WebCryptoKey* key); + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_NSS_SYM_KEY_NSS_H_ diff --git a/content/child/webcrypto/nss/util_nss.cc b/content/child/webcrypto/nss/util_nss.cc new file mode 100644 index 0000000..dd0ef3c --- /dev/null +++ b/content/child/webcrypto/nss/util_nss.cc @@ -0,0 +1,84 @@ +// 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/nss/util_nss.h" + +#include "base/lazy_instance.h" +#include "content/child/webcrypto/crypto_data.h" +#include "crypto/nss_util.h" +#include "crypto/scoped_nss_types.h" + +#if defined(USE_NSS) +#include <dlfcn.h> +#include <secoid.h> +#endif + +namespace content { + +namespace webcrypto { + +namespace { +base::LazyInstance<NssRuntimeSupport>::Leaky g_nss_runtime_support = + LAZY_INSTANCE_INITIALIZER; +} // namespace + +// Creates a SECItem for the data in |buffer|. This does NOT make a copy, so +// |buffer| should outlive the SECItem. +SECItem MakeSECItemForBuffer(const CryptoData& buffer) { + SECItem item = { + siBuffer, + // NSS requires non-const data even though it is just for input. + const_cast<unsigned char*>(buffer.bytes()), buffer.byte_length()}; + return item; +} + +CryptoData SECItemToCryptoData(const SECItem& item) { + return CryptoData(item.data, item.len); +} + +NssRuntimeSupport* NssRuntimeSupport::Get() { + return &g_nss_runtime_support.Get(); +} + +NssRuntimeSupport::NssRuntimeSupport() : internal_slot_does_oaep_(false) { +#if !defined(USE_NSS) + // Using a bundled version of NSS that is guaranteed to have this symbol. + pk11_encrypt_func_ = PK11_Encrypt; + pk11_decrypt_func_ = PK11_Decrypt; + pk11_pub_encrypt_func_ = PK11_PubEncrypt; + pk11_priv_decrypt_func_ = PK11_PrivDecrypt; + internal_slot_does_oaep_ = true; +#else + // Using system NSS libraries and PCKS #11 modules, which may not have the + // necessary function (PK11_Encrypt) or mechanism support (CKM_AES_GCM). + + // If PK11_Encrypt() was successfully resolved, then NSS will support + // AES-GCM directly. This was introduced in NSS 3.15. + pk11_encrypt_func_ = reinterpret_cast<PK11_EncryptDecryptFunction>( + dlsym(RTLD_DEFAULT, "PK11_Encrypt")); + pk11_decrypt_func_ = reinterpret_cast<PK11_EncryptDecryptFunction>( + dlsym(RTLD_DEFAULT, "PK11_Decrypt")); + + // Even though NSS's pk11wrap layer may support + // PK11_PubEncrypt/PK11_PubDecrypt (introduced in NSS 3.16.2), it may have + // loaded a softoken that does not include OAEP support. + pk11_pub_encrypt_func_ = reinterpret_cast<PK11_PubEncryptFunction>( + dlsym(RTLD_DEFAULT, "PK11_PubEncrypt")); + pk11_priv_decrypt_func_ = reinterpret_cast<PK11_PrivDecryptFunction>( + dlsym(RTLD_DEFAULT, "PK11_PrivDecrypt")); + if (pk11_priv_decrypt_func_ && pk11_pub_encrypt_func_) { + crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); + internal_slot_does_oaep_ = + !!PK11_DoesMechanism(slot.get(), CKM_RSA_PKCS_OAEP); + } +#endif +} + +void PlatformInit() { + crypto::EnsureNSSInit(); +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/nss/util_nss.h b/content/child/webcrypto/nss/util_nss.h new file mode 100644 index 0000000..0b50178 --- /dev/null +++ b/content/child/webcrypto/nss/util_nss.h @@ -0,0 +1,113 @@ +// 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. + +#ifndef CONTENT_CHILD_WEBCRYPTO_NSS_UTIL_NSS_H_ +#define CONTENT_CHILD_WEBCRYPTO_NSS_UTIL_NSS_H_ + +#include <keythi.h> +#include <pkcs11t.h> +#include <seccomon.h> +#include <secmodt.h> + +#include "base/lazy_instance.h" + +namespace content { + +namespace webcrypto { + +class CryptoData; + +SECItem MakeSECItemForBuffer(const CryptoData& buffer); +enum EncryptOrDecrypt { ENCRYPT, DECRYPT }; + +CryptoData SECItemToCryptoData(const SECItem& item); + +// Signature for PK11_Encrypt and PK11_Decrypt. +typedef SECStatus (*PK11_EncryptDecryptFunction)(PK11SymKey*, + CK_MECHANISM_TYPE, + SECItem*, + unsigned char*, + unsigned int*, + unsigned int, + const unsigned char*, + unsigned int); + +// Signature for PK11_PubEncrypt +typedef SECStatus (*PK11_PubEncryptFunction)(SECKEYPublicKey*, + CK_MECHANISM_TYPE, + SECItem*, + unsigned char*, + unsigned int*, + unsigned int, + const unsigned char*, + unsigned int, + void*); + +// Signature for PK11_PrivDecrypt +typedef SECStatus (*PK11_PrivDecryptFunction)(SECKEYPrivateKey*, + CK_MECHANISM_TYPE, + SECItem*, + unsigned char*, + unsigned int*, + unsigned int, + const unsigned char*, + unsigned int); + +// Singleton that detects whether or not AES-GCM and +// RSA-OAEP are supported by the version of NSS being used. +// On non-Linux platforms, Chromium embedders ship with a +// fixed version of NSS, and these are always available. +// However, on Linux (and ChromeOS), NSS is provided by the +// system, and thus not all algorithms may be available +// or be safe to use. +class NssRuntimeSupport { + public: + bool IsAesGcmSupported() const { + return pk11_encrypt_func_ && pk11_decrypt_func_; + } + + bool IsRsaOaepSupported() const { + return pk11_pub_encrypt_func_ && pk11_priv_decrypt_func_ && + internal_slot_does_oaep_; + } + + // Returns NULL if unsupported. + PK11_EncryptDecryptFunction pk11_encrypt_func() const { + return pk11_encrypt_func_; + } + + // Returns NULL if unsupported. + PK11_EncryptDecryptFunction pk11_decrypt_func() const { + return pk11_decrypt_func_; + } + + // Returns NULL if unsupported. + PK11_PubEncryptFunction pk11_pub_encrypt_func() const { + return pk11_pub_encrypt_func_; + } + + // Returns NULL if unsupported. + PK11_PrivDecryptFunction pk11_priv_decrypt_func() const { + return pk11_priv_decrypt_func_; + } + + static NssRuntimeSupport* Get(); + + private: + friend struct base::DefaultLazyInstanceTraits<NssRuntimeSupport>; + + NssRuntimeSupport(); + + PK11_EncryptDecryptFunction pk11_encrypt_func_; + PK11_EncryptDecryptFunction pk11_decrypt_func_; + PK11_PubEncryptFunction pk11_pub_encrypt_func_; + PK11_PrivDecryptFunction pk11_priv_decrypt_func_; + bool internal_slot_does_oaep_; +}; + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_NSS_UTIL_NSS_H_ diff --git a/content/child/webcrypto/openssl/aes_cbc_openssl.cc b/content/child/webcrypto/openssl/aes_cbc_openssl.cc new file mode 100644 index 0000000..aaf0fc1 --- /dev/null +++ b/content/child/webcrypto/openssl/aes_cbc_openssl.cc @@ -0,0 +1,138 @@ +// 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 <openssl/aes.h> +#include <openssl/evp.h> + +#include "base/logging.h" +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/openssl/aes_key_openssl.h" +#include "content/child/webcrypto/openssl/key_openssl.h" +#include "content/child/webcrypto/status.h" +#include "content/child/webcrypto/webcrypto_util.h" +#include "crypto/scoped_openssl_types.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" + +namespace content { + +namespace webcrypto { + +namespace { + +const EVP_CIPHER* GetAESCipherByKeyLength(unsigned int key_length_bytes) { + // BoringSSL does not support 192-bit AES keys. + switch (key_length_bytes) { + case 16: + return EVP_aes_128_cbc(); + case 32: + return EVP_aes_256_cbc(); + default: + return NULL; + } +} + +// OpenSSL constants for EVP_CipherInit_ex(), do not change +enum CipherOperation { kDoDecrypt = 0, kDoEncrypt = 1 }; + +Status AesCbcEncryptDecrypt(CipherOperation cipher_operation, + const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) { + const blink::WebCryptoAesCbcParams* params = algorithm.aesCbcParams(); + const std::vector<uint8>& raw_key = SymKeyOpenSsl::Cast(key)->raw_key_data(); + + if (params->iv().size() != 16) + return Status::ErrorIncorrectSizeAesCbcIv(); + + if (data.byte_length() >= INT_MAX - AES_BLOCK_SIZE) { + // TODO(padolph): Handle this by chunking the input fed into OpenSSL. Right + // now it doesn't make much difference since the one-shot API would end up + // blowing out the memory and crashing anyway. + return Status::ErrorDataTooLarge(); + } + + // Note: PKCS padding is enabled by default + crypto::ScopedOpenSSL<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free>::Type context( + EVP_CIPHER_CTX_new()); + + if (!context.get()) + return Status::OperationError(); + + const EVP_CIPHER* const cipher = GetAESCipherByKeyLength(raw_key.size()); + DCHECK(cipher); + + if (!EVP_CipherInit_ex(context.get(), + cipher, + NULL, + &raw_key[0], + params->iv().data(), + cipher_operation)) { + return Status::OperationError(); + } + + // According to the openssl docs, the amount of data written may be as large + // as (data_size + cipher_block_size - 1), constrained to a multiple of + // cipher_block_size. + unsigned int output_max_len = data.byte_length() + AES_BLOCK_SIZE - 1; + const unsigned remainder = output_max_len % AES_BLOCK_SIZE; + if (remainder != 0) + output_max_len += AES_BLOCK_SIZE - remainder; + DCHECK_GT(output_max_len, data.byte_length()); + + buffer->resize(output_max_len); + + unsigned char* const buffer_data = Uint8VectorStart(buffer); + + int output_len = 0; + if (!EVP_CipherUpdate(context.get(), + buffer_data, + &output_len, + data.bytes(), + data.byte_length())) + return Status::OperationError(); + int final_output_chunk_len = 0; + if (!EVP_CipherFinal_ex( + context.get(), buffer_data + output_len, &final_output_chunk_len)) { + return Status::OperationError(); + } + + const unsigned int final_output_len = + static_cast<unsigned int>(output_len) + + static_cast<unsigned int>(final_output_chunk_len); + DCHECK_LE(final_output_len, output_max_len); + + buffer->resize(final_output_len); + + return Status::Success(); +} + +class AesCbcImplementation : public AesAlgorithm { + public: + AesCbcImplementation() : AesAlgorithm("CBC") {} + + virtual Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + return AesCbcEncryptDecrypt(kDoEncrypt, algorithm, key, data, buffer); + } + + virtual Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + return AesCbcEncryptDecrypt(kDoDecrypt, algorithm, key, data, buffer); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformAesCbcImplementation() { + return new AesCbcImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/openssl/aes_gcm_openssl.cc b/content/child/webcrypto/openssl/aes_gcm_openssl.cc new file mode 100644 index 0000000..b29ad90 --- /dev/null +++ b/content/child/webcrypto/openssl/aes_gcm_openssl.cc @@ -0,0 +1,142 @@ +// 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 <vector> +#include <openssl/evp.h> + +#include "base/logging.h" +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/openssl/aes_key_openssl.h" +#include "content/child/webcrypto/openssl/key_openssl.h" +#include "content/child/webcrypto/openssl/util_openssl.h" +#include "content/child/webcrypto/status.h" +#include "content/child/webcrypto/webcrypto_util.h" +#include "crypto/openssl_util.h" +#include "crypto/scoped_openssl_types.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" + +namespace content { + +namespace webcrypto { + +namespace { + +const EVP_AEAD* GetAesGcmAlgorithmFromKeySize(unsigned int key_size_bytes) { + switch (key_size_bytes) { + case 16: + return EVP_aead_aes_128_gcm(); + // TODO(eroman): Hook up 256-bit support when it is available. + default: + return NULL; + } +} + +Status AesGcmEncryptDecrypt(EncryptOrDecrypt mode, + const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) { + const std::vector<uint8>& raw_key = SymKeyOpenSsl::Cast(key)->raw_key_data(); + const blink::WebCryptoAesGcmParams* params = algorithm.aesGcmParams(); + + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + + unsigned int tag_length_bits; + Status status = GetAesGcmTagLengthInBits(params, &tag_length_bits); + if (status.IsError()) + return status; + unsigned int tag_length_bytes = tag_length_bits / 8; + + CryptoData iv(params->iv()); + CryptoData additional_data(params->optionalAdditionalData()); + + EVP_AEAD_CTX ctx; + + const EVP_AEAD* const aead_alg = + GetAesGcmAlgorithmFromKeySize(raw_key.size()); + if (!aead_alg) + return Status::ErrorUnexpected(); + + if (!EVP_AEAD_CTX_init(&ctx, + aead_alg, + Uint8VectorStart(raw_key), + raw_key.size(), + tag_length_bytes, + NULL)) { + return Status::OperationError(); + } + + crypto::ScopedOpenSSL<EVP_AEAD_CTX, EVP_AEAD_CTX_cleanup>::Type ctx_cleanup( + &ctx); + + size_t len; + int ok; + + if (mode == DECRYPT) { + if (data.byte_length() < tag_length_bytes) + return Status::ErrorDataTooSmall(); + + buffer->resize(data.byte_length() - tag_length_bytes); + + ok = EVP_AEAD_CTX_open(&ctx, + Uint8VectorStart(buffer), + &len, + buffer->size(), + iv.bytes(), + iv.byte_length(), + data.bytes(), + data.byte_length(), + additional_data.bytes(), + additional_data.byte_length()); + } else { + // No need to check for unsigned integer overflow here (seal fails if + // the output buffer is too small). + buffer->resize(data.byte_length() + tag_length_bytes); + + ok = EVP_AEAD_CTX_seal(&ctx, + Uint8VectorStart(buffer), + &len, + buffer->size(), + iv.bytes(), + iv.byte_length(), + data.bytes(), + data.byte_length(), + additional_data.bytes(), + additional_data.byte_length()); + } + + if (!ok) + return Status::OperationError(); + buffer->resize(len); + return Status::Success(); +} + +class AesGcmImplementation : public AesAlgorithm { + public: + AesGcmImplementation() : AesAlgorithm("GCM") {} + + virtual Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + return AesGcmEncryptDecrypt(ENCRYPT, algorithm, key, data, buffer); + } + + virtual Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + return AesGcmEncryptDecrypt(DECRYPT, algorithm, key, data, buffer); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformAesGcmImplementation() { + return new AesGcmImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/openssl/aes_key_openssl.cc b/content/child/webcrypto/openssl/aes_key_openssl.cc new file mode 100644 index 0000000..f6fda3a --- /dev/null +++ b/content/child/webcrypto/openssl/aes_key_openssl.cc @@ -0,0 +1,126 @@ +// 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/openssl/aes_key_openssl.h" + +#include "base/logging.h" +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/jwk.h" +#include "content/child/webcrypto/openssl/key_openssl.h" +#include "content/child/webcrypto/openssl/sym_key_openssl.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 { + +AesAlgorithm::AesAlgorithm(blink::WebCryptoKeyUsageMask all_key_usages, + const std::string& jwk_suffix) + : all_key_usages_(all_key_usages), jwk_suffix_(jwk_suffix) { +} + +AesAlgorithm::AesAlgorithm(const std::string& jwk_suffix) + : all_key_usages_(blink::WebCryptoKeyUsageEncrypt | + blink::WebCryptoKeyUsageDecrypt | + blink::WebCryptoKeyUsageWrapKey | + blink::WebCryptoKeyUsageUnwrapKey), + jwk_suffix_(jwk_suffix) { +} + +Status AesAlgorithm::VerifyKeyUsagesBeforeGenerateKey( + blink::WebCryptoKeyUsageMask usage_mask) const { + return CheckKeyCreationUsages(all_key_usages_, usage_mask); +} + +Status AesAlgorithm::GenerateSecretKey( + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const { + unsigned int keylen_bits; + Status status = + GetAesKeyGenLengthInBits(algorithm.aesKeyGenParams(), &keylen_bits); + if (status.IsError()) + return status; + + return GenerateSecretKeyOpenSsl( + blink::WebCryptoKeyAlgorithm::createAes(algorithm.id(), keylen_bits), + extractable, + usage_mask, + keylen_bits / 8, + key); +} + +Status AesAlgorithm::VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usage_mask) const { + switch (format) { + case blink::WebCryptoKeyFormatRaw: + case blink::WebCryptoKeyFormatJwk: + return CheckKeyCreationUsages(all_key_usages_, usage_mask); + default: + return Status::ErrorUnsupportedImportKeyFormat(); + } +} + +Status AesAlgorithm::ImportKeyRaw(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const { + const unsigned int keylen_bytes = key_data.byte_length(); + Status status = VerifyAesKeyLengthForImport(keylen_bytes); + if (status.IsError()) + return status; + + // No possibility of overflow. + unsigned int keylen_bits = keylen_bytes * 8; + + return ImportKeyRawOpenSsl( + key_data, + blink::WebCryptoKeyAlgorithm::createAes(algorithm.id(), keylen_bits), + extractable, + usage_mask, + key); +} + +Status AesAlgorithm::ImportKeyJwk(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const { + std::vector<uint8> raw_data; + Status status = ReadAesSecretKeyJwk( + key_data, jwk_suffix_, extractable, usage_mask, &raw_data); + if (status.IsError()) + return status; + + return ImportKeyRaw( + CryptoData(raw_data), algorithm, extractable, usage_mask, key); +} + +Status AesAlgorithm::ExportKeyRaw(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const { + *buffer = SymKeyOpenSsl::Cast(key)->raw_key_data(); + return Status::Success(); +} + +Status AesAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const { + const std::vector<uint8>& raw_data = SymKeyOpenSsl::Cast(key)->raw_key_data(); + + WriteSecretKeyJwk(CryptoData(raw_data), + MakeJwkAesAlgorithmName(jwk_suffix_, raw_data.size()), + key.extractable(), + key.usages(), + buffer); + + return Status::Success(); +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/openssl/aes_key_openssl.h b/content/child/webcrypto/openssl/aes_key_openssl.h new file mode 100644 index 0000000..d5a74ff --- /dev/null +++ b/content/child/webcrypto/openssl/aes_key_openssl.h @@ -0,0 +1,70 @@ +// 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. + +#ifndef CONTENT_CHILD_WEBCRYPTO_OPENSSL_AES_OPENSSL_H_ +#define CONTENT_CHILD_WEBCRYPTO_OPENSSL_AES_OPENSSL_H_ + +#include "content/child/webcrypto/algorithm_implementation.h" + +namespace content { + +namespace webcrypto { + +// Base class for AES algorithms that provides the implementation for key +// creation and export. +class AesAlgorithm : public AlgorithmImplementation { + public: + // |all_key_usages| is the set of all WebCrypto key usages that are + // allowed for imported or generated keys. |jwk_suffix| is the suffix + // used when constructing JWK names for the algorithm. For instance A128CBC + // is the JWK name for 128-bit AES-CBC. The |jwk_suffix| in this case would + // be "CBC". + AesAlgorithm(blink::WebCryptoKeyUsageMask all_key_usages, + const std::string& jwk_suffix); + + // This is the same as the other AesAlgorithm constructor where + // |all_key_usages| is pre-filled to values for encryption/decryption + // algorithms (supports usages for: encrypt, decrypt, wrap, unwrap). + explicit AesAlgorithm(const std::string& jwk_suffix); + + virtual Status VerifyKeyUsagesBeforeGenerateKey( + blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE; + + virtual Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const OVERRIDE; + + virtual Status VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE; + + virtual Status ImportKeyRaw(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const OVERRIDE; + + virtual Status ImportKeyJwk(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const OVERRIDE; + + virtual Status ExportKeyRaw(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const OVERRIDE; + + virtual Status ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const OVERRIDE; + + private: + const blink::WebCryptoKeyUsageMask all_key_usages_; + const std::string jwk_suffix_; +}; + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_OPENSSL_AES_OPENSSL_H_ diff --git a/content/child/webcrypto/openssl/aes_kw_openssl.cc b/content/child/webcrypto/openssl/aes_kw_openssl.cc new file mode 100644 index 0000000..d2bb10f --- /dev/null +++ b/content/child/webcrypto/openssl/aes_kw_openssl.cc @@ -0,0 +1,44 @@ +// 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 "base/logging.h" +#include "content/child/webcrypto/openssl/aes_key_openssl.h" +#include "content/child/webcrypto/status.h" + +namespace content { + +namespace webcrypto { + +namespace { + +class AesKwImplementation : public AesAlgorithm { + public: + AesKwImplementation() : AesAlgorithm("KW") {} + + virtual Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + // TODO(eroman): + return Status::ErrorUnsupported(); + } + + virtual Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + // TODO(eroman): + return Status::ErrorUnsupported(); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformAesKwImplementation() { + return new AesKwImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/openssl/hmac_openssl.cc b/content/child/webcrypto/openssl/hmac_openssl.cc new file mode 100644 index 0000000..a40bfd9 --- /dev/null +++ b/content/child/webcrypto/openssl/hmac_openssl.cc @@ -0,0 +1,211 @@ +// 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 <openssl/hmac.h> + +#include "base/logging.h" +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/jwk.h" +#include "content/child/webcrypto/openssl/key_openssl.h" +#include "content/child/webcrypto/openssl/sym_key_openssl.h" +#include "content/child/webcrypto/openssl/util_openssl.h" +#include "content/child/webcrypto/status.h" +#include "content/child/webcrypto/webcrypto_util.h" +#include "crypto/openssl_util.h" +#include "crypto/secure_util.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace content { + +namespace webcrypto { + +namespace { + +const blink::WebCryptoKeyUsageMask kAllKeyUsages = + blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; + +Status SignHmac(const std::vector<uint8>& raw_key, + const blink::WebCryptoAlgorithm& hash, + const CryptoData& data, + std::vector<uint8>* buffer) { + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + + const EVP_MD* digest_algorithm = GetDigest(hash.id()); + if (!digest_algorithm) + return Status::ErrorUnsupported(); + unsigned int hmac_expected_length = EVP_MD_size(digest_algorithm); + + // OpenSSL wierdness here. + // First, HMAC() needs a void* for the key data, so make one up front as a + // cosmetic to avoid a cast. Second, OpenSSL does not like a NULL key, + // which will result if the raw_key vector is empty; an entirely valid + // case. Handle this specific case by pointing to an empty array. + const unsigned char null_key[] = {}; + const void* const raw_key_voidp = raw_key.size() ? &raw_key[0] : null_key; + + buffer->resize(hmac_expected_length); + crypto::ScopedOpenSSLSafeSizeBuffer<EVP_MAX_MD_SIZE> hmac_result( + Uint8VectorStart(buffer), hmac_expected_length); + + unsigned int hmac_actual_length; + unsigned char* const success = HMAC(digest_algorithm, + raw_key_voidp, + raw_key.size(), + data.bytes(), + data.byte_length(), + hmac_result.safe_buffer(), + &hmac_actual_length); + if (!success || hmac_actual_length != hmac_expected_length) + return Status::OperationError(); + + return Status::Success(); +} + +class HmacImplementation : public AlgorithmImplementation { + public: + HmacImplementation() {} + + virtual Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const OVERRIDE { + const blink::WebCryptoHmacKeyGenParams* params = + algorithm.hmacKeyGenParams(); + + unsigned int keylen_bits = 0; + Status status = GetHmacKeyGenLengthInBits(params, &keylen_bits); + if (status.IsError()) + return status; + + return GenerateSecretKeyOpenSsl(blink::WebCryptoKeyAlgorithm::createHmac( + params->hash().id(), keylen_bits), + extractable, + usage_mask, + keylen_bits / 8, + key); + } + + virtual Status VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE { + switch (format) { + case blink::WebCryptoKeyFormatRaw: + case blink::WebCryptoKeyFormatJwk: + return CheckKeyCreationUsages(kAllKeyUsages, usage_mask); + default: + return Status::ErrorUnsupportedImportKeyFormat(); + } + } + + virtual Status VerifyKeyUsagesBeforeGenerateKey( + blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE { + return CheckKeyCreationUsages(kAllKeyUsages, usage_mask); + } + + virtual Status ImportKeyRaw(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const OVERRIDE { + const blink::WebCryptoAlgorithm& hash = + algorithm.hmacImportParams()->hash(); + + // TODO(eroman): check for overflow. + unsigned int keylen_bits = key_data.byte_length() * 8; + + return ImportKeyRawOpenSsl( + key_data, + blink::WebCryptoKeyAlgorithm::createHmac(hash.id(), keylen_bits), + extractable, + usage_mask, + key); + } + + virtual Status ImportKeyJwk(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) const OVERRIDE { + const char* algorithm_name = + GetJwkHmacAlgorithmName(algorithm.hmacImportParams()->hash().id()); + if (!algorithm_name) + return Status::ErrorUnexpected(); + + std::vector<uint8> raw_data; + Status status = ReadSecretKeyJwk( + key_data, algorithm_name, extractable, usage_mask, &raw_data); + if (status.IsError()) + return status; + + return ImportKeyRaw( + CryptoData(raw_data), algorithm, extractable, usage_mask, key); + } + + virtual Status ExportKeyRaw(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const OVERRIDE { + *buffer = SymKeyOpenSsl::Cast(key)->raw_key_data(); + return Status::Success(); + } + + virtual Status ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8>* buffer) const OVERRIDE { + SymKeyOpenSsl* sym_key = SymKeyOpenSsl::Cast(key); + const std::vector<uint8>& raw_data = sym_key->raw_key_data(); + + const char* algorithm_name = + GetJwkHmacAlgorithmName(key.algorithm().hmacParams()->hash().id()); + if (!algorithm_name) + return Status::ErrorUnexpected(); + + WriteSecretKeyJwk(CryptoData(raw_data), + algorithm_name, + key.extractable(), + key.usages(), + buffer); + + return Status::Success(); + } + + virtual Status Sign(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + const blink::WebCryptoAlgorithm& hash = + key.algorithm().hmacParams()->hash(); + + return SignHmac( + SymKeyOpenSsl::Cast(key)->raw_key_data(), hash, data, buffer); + } + + virtual Status Verify(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& signature, + const CryptoData& data, + bool* signature_match) const OVERRIDE { + std::vector<uint8> result; + Status status = Sign(algorithm, key, data, &result); + + if (status.IsError()) + return status; + + // Do not allow verification of truncated MACs. + *signature_match = result.size() == signature.byte_length() && + crypto::SecureMemEqual(Uint8VectorStart(result), + signature.bytes(), + signature.byte_length()); + + return Status::Success(); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformHmacImplementation() { + return new HmacImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/openssl/key_openssl.cc b/content/child/webcrypto/openssl/key_openssl.cc new file mode 100644 index 0000000..ebd45e2 --- /dev/null +++ b/content/child/webcrypto/openssl/key_openssl.cc @@ -0,0 +1,53 @@ +// 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/openssl/key_openssl.h" + +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/status.h" +#include "content/child/webcrypto/webcrypto_util.h" + +namespace content { + +namespace webcrypto { + +KeyOpenSsl::KeyOpenSsl(const CryptoData& serialized_key_data) + : serialized_key_data_( + serialized_key_data.bytes(), + serialized_key_data.bytes() + serialized_key_data.byte_length()) { +} + +KeyOpenSsl::~KeyOpenSsl() { +} + +SymKeyOpenSsl* KeyOpenSsl::AsSymKey() { + return NULL; +} + +SymKeyOpenSsl::~SymKeyOpenSsl() { +} + +SymKeyOpenSsl* SymKeyOpenSsl::Cast(const blink::WebCryptoKey& key) { + KeyOpenSsl* platform_key = reinterpret_cast<KeyOpenSsl*>(key.handle()); + return platform_key->AsSymKey(); +} + +SymKeyOpenSsl* SymKeyOpenSsl::AsSymKey() { + return this; +} + +SymKeyOpenSsl::SymKeyOpenSsl(const CryptoData& raw_key_data) + : KeyOpenSsl(raw_key_data) { +} + +bool PlatformSerializeKeyForClone(const blink::WebCryptoKey& key, + blink::WebVector<uint8>* key_data) { + const KeyOpenSsl* openssl_key = static_cast<KeyOpenSsl*>(key.handle()); + *key_data = openssl_key->serialized_key_data(); + return true; +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/openssl/key_openssl.h b/content/child/webcrypto/openssl/key_openssl.h new file mode 100644 index 0000000..ef32438 --- /dev/null +++ b/content/child/webcrypto/openssl/key_openssl.h @@ -0,0 +1,56 @@ +// 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. + +#ifndef CONTENT_CHILD_WEBCRYPTO_OPENSSL_KEY_OPENSSL_H_ +#define CONTENT_CHILD_WEBCRYPTO_OPENSSL_KEY_OPENSSL_H_ + +#include "content/child/webcrypto/algorithm_implementation.h" + +namespace content { + +namespace webcrypto { + +class SymKeyOpenSsl; + +// Base key class for all OpenSSL keys, used to safely cast between types. Each +// key maintains a copy of its serialized form in either 'raw', 'pkcs8', or +// 'spki' format. This is to allow structured cloning of keys synchronously from +// the target Blink thread without having to lock access to the key. +class KeyOpenSsl : public blink::WebCryptoKeyHandle { + public: + explicit KeyOpenSsl(const CryptoData& serialized_key_data); + virtual ~KeyOpenSsl(); + + virtual SymKeyOpenSsl* AsSymKey(); + + const std::vector<uint8>& serialized_key_data() const { + return serialized_key_data_; + } + + private: + const std::vector<uint8> serialized_key_data_; +}; + +class SymKeyOpenSsl : public KeyOpenSsl { + public: + virtual ~SymKeyOpenSsl(); + explicit SymKeyOpenSsl(const CryptoData& raw_key_data); + + static SymKeyOpenSsl* Cast(const blink::WebCryptoKey& key); + + virtual SymKeyOpenSsl* AsSymKey() OVERRIDE; + + const std::vector<uint8>& raw_key_data() const { + return serialized_key_data(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(SymKeyOpenSsl); +}; + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_OPENSSL_KEY_OPENSSL_H_ diff --git a/content/child/webcrypto/openssl/sha_openssl.cc b/content/child/webcrypto/openssl/sha_openssl.cc new file mode 100644 index 0000000..f9382b5 --- /dev/null +++ b/content/child/webcrypto/openssl/sha_openssl.cc @@ -0,0 +1,139 @@ +// 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 <vector> +#include <openssl/evp.h> +#include <openssl/sha.h> + +#include "base/logging.h" +#include "content/child/webcrypto/algorithm_implementation.h" +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/openssl/util_openssl.h" +#include "content/child/webcrypto/status.h" +#include "content/child/webcrypto/webcrypto_util.h" +#include "crypto/openssl_util.h" +#include "crypto/scoped_openssl_types.h" + +namespace content { + +namespace webcrypto { + +namespace { + +// Implementation of blink::WebCryptoDigester, an internal Blink detail not +// part of WebCrypto, that allows chunks of data to be streamed in before +// computing a SHA-* digest (as opposed to ShaImplementation, which computes +// digests over complete messages) +class DigestorOpenSsl : public blink::WebCryptoDigestor { + public: + explicit DigestorOpenSsl(blink::WebCryptoAlgorithmId algorithm_id) + : initialized_(false), + digest_context_(EVP_MD_CTX_create()), + algorithm_id_(algorithm_id) {} + + virtual bool consume(const unsigned char* data, unsigned int size) { + return ConsumeWithStatus(data, size).IsSuccess(); + } + + Status ConsumeWithStatus(const unsigned char* data, unsigned int size) { + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + Status error = Init(); + if (!error.IsSuccess()) + return error; + + if (!EVP_DigestUpdate(digest_context_.get(), data, size)) + return Status::OperationError(); + + return Status::Success(); + } + + virtual bool finish(unsigned char*& result_data, + unsigned int& result_data_size) { + Status error = FinishInternal(result_, &result_data_size); + if (!error.IsSuccess()) + return false; + result_data = result_; + return true; + } + + Status FinishWithVectorAndStatus(std::vector<uint8>* result) { + const int hash_expected_size = EVP_MD_CTX_size(digest_context_.get()); + result->resize(hash_expected_size); + unsigned char* const hash_buffer = Uint8VectorStart(result); + unsigned int hash_buffer_size; // ignored + return FinishInternal(hash_buffer, &hash_buffer_size); + } + + private: + Status Init() { + if (initialized_) + return Status::Success(); + + const EVP_MD* digest_algorithm = GetDigest(algorithm_id_); + if (!digest_algorithm) + return Status::ErrorUnexpected(); + + if (!digest_context_.get()) + return Status::OperationError(); + + if (!EVP_DigestInit_ex(digest_context_.get(), digest_algorithm, NULL)) + return Status::OperationError(); + + initialized_ = true; + return Status::Success(); + } + + Status FinishInternal(unsigned char* result, unsigned int* result_size) { + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + Status error = Init(); + if (!error.IsSuccess()) + return error; + + const int hash_expected_size = EVP_MD_CTX_size(digest_context_.get()); + if (hash_expected_size <= 0) + return Status::ErrorUnexpected(); + DCHECK_LE(hash_expected_size, EVP_MAX_MD_SIZE); + + if (!EVP_DigestFinal_ex(digest_context_.get(), result, result_size) || + static_cast<int>(*result_size) != hash_expected_size) + return Status::OperationError(); + + return Status::Success(); + } + + bool initialized_; + crypto::ScopedEVP_MD_CTX digest_context_; + blink::WebCryptoAlgorithmId algorithm_id_; + unsigned char result_[EVP_MAX_MD_SIZE]; +}; + +class ShaImplementation : public AlgorithmImplementation { + public: + virtual Status Digest(const blink::WebCryptoAlgorithm& algorithm, + const CryptoData& data, + std::vector<uint8>* buffer) const OVERRIDE { + DigestorOpenSsl digestor(algorithm.id()); + Status error = digestor.ConsumeWithStatus(data.bytes(), data.byte_length()); + // http://crbug.com/366427: the spec does not define any other failures for + // digest, so none of the subsequent errors are spec compliant. + if (!error.IsSuccess()) + return error; + return digestor.FinishWithVectorAndStatus(buffer); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformShaImplementation() { + return new ShaImplementation(); +} + +scoped_ptr<blink::WebCryptoDigestor> CreatePlatformDigestor( + blink::WebCryptoAlgorithmId algorithm) { + return scoped_ptr<blink::WebCryptoDigestor>(new DigestorOpenSsl(algorithm)); +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/openssl/sym_key_openssl.cc b/content/child/webcrypto/openssl/sym_key_openssl.cc new file mode 100644 index 0000000..e07db69 --- /dev/null +++ b/content/child/webcrypto/openssl/sym_key_openssl.cc @@ -0,0 +1,58 @@ +// 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/openssl/sym_key_openssl.h" + +#include <vector> +#include <openssl/rand.h> + +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/openssl/key_openssl.h" +#include "content/child/webcrypto/status.h" +#include "crypto/openssl_util.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace content { + +namespace webcrypto { + +Status GenerateSecretKeyOpenSsl(const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + unsigned keylen_bytes, + blink::WebCryptoKey* key) { + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + + std::vector<unsigned char> random_bytes(keylen_bytes, 0); + + if (keylen_bytes > 0) { + if (!(RAND_bytes(&random_bytes[0], keylen_bytes))) + return Status::OperationError(); + } + + *key = + blink::WebCryptoKey::create(new SymKeyOpenSsl(CryptoData(random_bytes)), + blink::WebCryptoKeyTypeSecret, + extractable, + algorithm, + usage_mask); + return Status::Success(); +} + +Status ImportKeyRawOpenSsl(const CryptoData& key_data, + const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key) { + *key = blink::WebCryptoKey::create(new SymKeyOpenSsl(key_data), + blink::WebCryptoKeyTypeSecret, + extractable, + algorithm, + usage_mask); + return Status::Success(); +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/openssl/sym_key_openssl.h b/content/child/webcrypto/openssl/sym_key_openssl.h new file mode 100644 index 0000000..e952dee --- /dev/null +++ b/content/child/webcrypto/openssl/sym_key_openssl.h @@ -0,0 +1,33 @@ +// 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. + +#ifndef CONTENT_CHILD_WEBCRYPTO_OPENSSL_SYM_KEY_OPENSSL_H_ +#define CONTENT_CHILD_WEBCRYPTO_OPENSSL_SYM_KEY_OPENSSL_H_ + +#include "third_party/WebKit/public/platform/WebCrypto.h" + +namespace content { + +namespace webcrypto { + +class CryptoData; +class Status; + +Status GenerateSecretKeyOpenSsl(const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + unsigned keylen_bytes, + blink::WebCryptoKey* key); + +Status ImportKeyRawOpenSsl(const CryptoData& key_data, + const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKey* key); + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_OPENSSL_SYM_KEY_OPENSSL_H_ diff --git a/content/child/webcrypto/openssl/util_openssl.cc b/content/child/webcrypto/openssl/util_openssl.cc new file mode 100644 index 0000000..110057e --- /dev/null +++ b/content/child/webcrypto/openssl/util_openssl.cc @@ -0,0 +1,48 @@ +// 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/openssl/util_openssl.h" + +#include <openssl/evp.h> + +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/platform_crypto.h" +#include "crypto/openssl_util.h" + +namespace content { + +namespace webcrypto { + +void PlatformInit() { + crypto::EnsureOpenSSLInit(); +} + +const EVP_MD* GetDigest(blink::WebCryptoAlgorithmId id) { + switch (id) { + case blink::WebCryptoAlgorithmIdSha1: + return EVP_sha1(); + case blink::WebCryptoAlgorithmIdSha256: + return EVP_sha256(); + case blink::WebCryptoAlgorithmIdSha384: + return EVP_sha384(); + case blink::WebCryptoAlgorithmIdSha512: + return EVP_sha512(); + default: + return NULL; + } +} + +AlgorithmImplementation* CreatePlatformRsaOaepImplementation() { + // TODO(eroman): + return NULL; +} + +AlgorithmImplementation* CreatePlatformRsaSsaImplementation() { + // TODO(eroman): + return NULL; +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/openssl/util_openssl.h b/content/child/webcrypto/openssl/util_openssl.h new file mode 100644 index 0000000..15d3308 --- /dev/null +++ b/content/child/webcrypto/openssl/util_openssl.h @@ -0,0 +1,26 @@ +// 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. + +#ifndef CONTENT_CHILD_WEBCRYPTO_OPENSSL_UTIL_OPENSSL_H_ +#define CONTENT_CHILD_WEBCRYPTO_OPENSSL_UTIL_OPENSSL_H_ + +#include <openssl/ossl_typ.h> + +#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" + +namespace content { + +namespace webcrypto { + +class CryptoData; + +enum EncryptOrDecrypt { ENCRYPT, DECRYPT }; + +const EVP_MD* GetDigest(blink::WebCryptoAlgorithmId id); + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_OPENSSL_UTIL_OPENSSL_H_ diff --git a/content/child/webcrypto/platform_crypto.h b/content/child/webcrypto/platform_crypto.h index 9e95730..00b9e05 100644 --- a/content/child/webcrypto/platform_crypto.h +++ b/content/child/webcrypto/platform_crypto.h @@ -8,282 +8,32 @@ #include <vector> #include "base/basictypes.h" -#include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" +#include "content/common/content_export.h" #include "third_party/WebKit/public/platform/WebCrypto.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" - -namespace blink { -template <typename T> -class WebVector; -} +// The definitions for these methods lives in either nss/ or openssl/ namespace content { -enum EncryptOrDecrypt { ENCRYPT, DECRYPT }; - namespace webcrypto { -class CryptoData; -class Status; - -// Functions in the webcrypto::platform namespace are intended to be those -// which are OpenSSL/NSS specific. -// -// The general purpose code which applies to both OpenSSL and NSS -// implementations of webcrypto should live in the outter webcrypto namespace, -// and the crypto library specific bits in the "platform" namespace. -// -// ----------------- -// Threading: -// ----------------- -// -// Unless otherwise noted, functions in webcrypto::platform are called -// exclusively from a sequenced worker pool. -// -// This means that operations using a given key cannot occur in -// parallel and it is not necessary to guard against concurrent usage. -// -// The exceptions are: -// -// * Key::ThreadSafeSerializeForClone(), which is called from the -// target Blink thread during structured clone. -// -// * ImportKeyRaw(), ImportKeySpki(), ImportKeyPkcs8(), which can be -// called from the target Blink thread during structured clone -// deserialization, as well as from the webcrypto worker pool. -// -// TODO(eroman): Change it so import happens in worker pool too. -// http://crbug.com/366834 -namespace platform { - -class SymKey; -class PublicKey; -class PrivateKey; - -// Base key class for all platform keys, used to safely cast between types. -class Key : public blink::WebCryptoKeyHandle { - public: - virtual SymKey* AsSymKey() = 0; - virtual PublicKey* AsPublicKey() = 0; - virtual PrivateKey* AsPrivateKey() = 0; - - virtual bool ThreadSafeSerializeForClone( - blink::WebVector<uint8>* key_data) = 0; -}; - -// Do any one-time initialization. Note that this can be called MULTIPLE times -// (once per instantiation of WebCryptoImpl). -void Init(); +class AlgorithmImplementation; -// Preconditions: -// * |key| is a non-null AES-CBC key. -// * |iv| is exactly 16 bytes long -Status EncryptDecryptAesCbc(EncryptOrDecrypt mode, - SymKey* key, - const CryptoData& data, - const CryptoData& iv, - std::vector<uint8>* buffer); +void PlatformInit(); -// Preconditions: -// * |key| is a non-null AES-GCM key. -// * |tag_length_bits| is one of {32, 64, 96, 104, 112, 120, 128} -Status EncryptDecryptAesGcm(EncryptOrDecrypt mode, - SymKey* key, - const CryptoData& data, - const CryptoData& iv, - const CryptoData& additional_data, - unsigned int tag_length_bits, - std::vector<uint8>* buffer); - -// Preconditions: -// * |key| is non-null -// * |hash| is a digest algorithm -// * |label| MAY be empty (e.g. 0 bytes long). -Status EncryptRsaOaep(PublicKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& label, - const CryptoData& data, - std::vector<uint8>* buffer); - -// Preconditions: -// * |key| is non-null -// * |hash| is a digest algorithm -// * |label| MAY be empty (e.g. 0 bytes long). -Status DecryptRsaOaep(PrivateKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& label, - const CryptoData& data, - std::vector<uint8>* buffer); - -// Preconditions: -// * |key| is a non-null HMAC key. -// * |hash| is a digest algorithm. -Status SignHmac(SymKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& data, - std::vector<uint8>* buffer); - -// Preconditions: -// * |algorithm| is a SHA function. -Status DigestSha(blink::WebCryptoAlgorithmId algorithm, - const CryptoData& data, - std::vector<uint8>* buffer); - -// Preconditions: -// * |algorithm| is a SHA function. -scoped_ptr<blink::WebCryptoDigestor> CreateDigestor( +scoped_ptr<blink::WebCryptoDigestor> CreatePlatformDigestor( blink::WebCryptoAlgorithmId algorithm); -// Preconditions: -// * |key| is non-null. -// * |hash| is a digest algorithm. -Status SignRsaSsaPkcs1v1_5(PrivateKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& data, - std::vector<uint8>* buffer); - -// Preconditions: -// * |key| is non-null. -// * |hash| is a digest algorithm. -Status VerifyRsaSsaPkcs1v1_5(PublicKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& signature, - const CryptoData& data, - bool* signature_match); - -// |keylen_bytes| is the desired length of the key in bits. -// -// Preconditions: -// * algorithm.id() is for a symmetric key algorithm. -// * keylen_bytes is non-zero (TODO(eroman): revisit this). -// * For AES algorithms |keylen_bytes| is either 16, 24, or 32 bytes long. -// * usage_mask makes sense for the algorithm. -Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - unsigned keylen_bytes, - blink::WebCryptoKey* key); - -// Preconditions: -// * algorithm.id() is for an RSA algorithm. -// * public_exponent, modulus_length_bits and hash_or_null are the same as what -// is in algorithm. They are split out for convenience. -// * modulus_length_bits is not 0 -// * public_exponent is not empty. -// * {public|private}_key_usage_mask make sense for the algorithm. -Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask public_key_usage_mask, - blink::WebCryptoKeyUsageMask private_key_usage_mask, - unsigned int modulus_length_bits, - unsigned long public_exponent, - blink::WebCryptoKey* public_key, - blink::WebCryptoKey* private_key); - -// Preconditions: -// * |key| is non-null. -// * |algorithm.id()| is for a symmetric key algorithm. -// * For AES algorithms |key_data| is either 16, 24, or 32 bytes long. -// * usage_mask makes sense for the algorithm. -// Note that this may be called from target Blink thread. -Status ImportKeyRaw(const blink::WebCryptoAlgorithm& algorithm, - const CryptoData& key_data, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key); - -// Preconditions: -// * algorithm.id() is for an RSA algorithm. -// * usage_mask makes sense for the algorithm. -Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - const CryptoData& modulus_data, - const CryptoData& exponent_data, - blink::WebCryptoKey* key); - -// Preconditions: -// * algorithm.id() is for an RSA algorithm. -// * modulus, public_exponent, and private_exponent will be non-empty. The -// others will either all be specified (non-empty), or all be unspecified -// (empty). -// * usage_mask makes sense for the algorithm. -Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - const CryptoData& modulus, - const CryptoData& public_exponent, - const CryptoData& private_exponent, - const CryptoData& prime1, - const CryptoData& prime2, - const CryptoData& exponent1, - const CryptoData& exponent2, - const CryptoData& coefficient, - blink::WebCryptoKey* key); - -// Note that this may be called from target Blink thread. -// Preconditions: -// * usage_mask makes sense for the algorithm. -Status ImportKeySpki(const blink::WebCryptoAlgorithm& algorithm, - const CryptoData& key_data, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key); - -// Note that this may be called from target Blink thread. -// Preconditions: -// * usage_mask makes sense for the algorithm. -Status ImportKeyPkcs8(const blink::WebCryptoAlgorithm& algorithm, - const CryptoData& key_data, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key); - -// Preconditions: -// * |key| is non-null. -Status ExportKeyRaw(SymKey* key, std::vector<uint8>* buffer); - -// Preconditions: -// * |key| is non-null. -Status ExportKeySpki(PublicKey* key, std::vector<uint8>* buffer); - -// Preconditions: -// * |key| is non-null. -Status ExportRsaPublicKey(PublicKey* key, - std::vector<uint8>* modulus, - std::vector<uint8>* public_exponent); - -// Preconditions: -// * |key| is non-null. -Status ExportRsaPrivateKey(PrivateKey* key, - std::vector<uint8>* modulus, - std::vector<uint8>* public_exponent, - std::vector<uint8>* private_exponent, - std::vector<uint8>* prime1, - std::vector<uint8>* prime2, - std::vector<uint8>* exponent1, - std::vector<uint8>* exponent2, - std::vector<uint8>* coefficient); - -// Preconditions: -// * |key| is non-null. -Status ExportKeyPkcs8(PrivateKey* key, - const blink::WebCryptoKeyAlgorithm& key_algorithm, - std::vector<uint8>* buffer); - -// Performs AES-KW encryption/decryption on the input |data|. -// Preconditions: -// * |key| is non-null -// * |data| is multiple of 8 bytes. If encrypting it is at least 16 bytes, and -// if decrypting at least 24 bytes. -// * |buffer| is non-null. -Status EncryptDecryptAesKw(EncryptOrDecrypt mode, - SymKey* key, - const CryptoData& data, - std::vector<uint8>* buffer); +AlgorithmImplementation* CreatePlatformShaImplementation(); +AlgorithmImplementation* CreatePlatformAesCbcImplementation(); +AlgorithmImplementation* CreatePlatformAesGcmImplementation(); +AlgorithmImplementation* CreatePlatformAesKwImplementation(); +AlgorithmImplementation* CreatePlatformHmacImplementation(); +AlgorithmImplementation* CreatePlatformRsaOaepImplementation(); +AlgorithmImplementation* CreatePlatformRsaSsaImplementation(); -} // namespace platform +bool PlatformSerializeKeyForClone(const blink::WebCryptoKey& key, + blink::WebVector<uint8>* key_data); } // namespace webcrypto diff --git a/content/child/webcrypto/platform_crypto_nss.cc b/content/child/webcrypto/platform_crypto_nss.cc deleted file mode 100644 index c5c18af..0000000 --- a/content/child/webcrypto/platform_crypto_nss.cc +++ /dev/null @@ -1,1934 +0,0 @@ -// 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/platform_crypto.h" - -#include <cryptohi.h> -#include <pk11pub.h> -#include <secerr.h> -#include <sechash.h> - -#include <vector> - -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "content/child/webcrypto/crypto_data.h" -#include "content/child/webcrypto/status.h" -#include "content/child/webcrypto/webcrypto_util.h" -#include "crypto/nss_util.h" -#include "crypto/scoped_nss_types.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" -#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" - -#if defined(USE_NSS) -#include <dlfcn.h> -#include <secoid.h> -#endif - -// At the time of this writing: -// * Windows and Mac builds ship with their own copy of NSS (3.15+) -// * Linux builds use the system's libnss, which is 3.14 on Debian (but 3.15+ -// on other distros). -// -// Since NSS provides AES-GCM support starting in version 3.15, it may be -// unavailable for Linux Chrome users. -// -// * !defined(CKM_AES_GCM) -// -// This means that at build time, the NSS header pkcs11t.h is older than -// 3.15. However at runtime support may be present. -// -// * !defined(USE_NSS) -// -// This means that Chrome is being built with an embedded copy of NSS, -// which can be assumed to be >= 3.15. On the other hand if USE_NSS is -// defined, it also implies running on Linux. -// -// TODO(eroman): Simplify this once 3.15+ is required by Linux builds. -#if !defined(CKM_AES_GCM) -#define CKM_AES_GCM 0x00001087 - -struct CK_GCM_PARAMS { - CK_BYTE_PTR pIv; - CK_ULONG ulIvLen; - CK_BYTE_PTR pAAD; - CK_ULONG ulAADLen; - CK_ULONG ulTagBits; -}; -#endif // !defined(CKM_AES_GCM) - -namespace { - -// Signature for PK11_Encrypt and PK11_Decrypt. -typedef SECStatus (*PK11_EncryptDecryptFunction)(PK11SymKey*, - CK_MECHANISM_TYPE, - SECItem*, - unsigned char*, - unsigned int*, - unsigned int, - const unsigned char*, - unsigned int); - -// Signature for PK11_PubEncrypt -typedef SECStatus (*PK11_PubEncryptFunction)(SECKEYPublicKey*, - CK_MECHANISM_TYPE, - SECItem*, - unsigned char*, - unsigned int*, - unsigned int, - const unsigned char*, - unsigned int, - void*); - -// Signature for PK11_PrivDecrypt -typedef SECStatus (*PK11_PrivDecryptFunction)(SECKEYPrivateKey*, - CK_MECHANISM_TYPE, - SECItem*, - unsigned char*, - unsigned int*, - unsigned int, - const unsigned char*, - unsigned int); - -// Singleton to abstract away dynamically loading libnss3.so -class NssRuntimeSupport { - public: - bool IsAesGcmSupported() const { - return pk11_encrypt_func_ && pk11_decrypt_func_; - } - - bool IsRsaOaepSupported() const { - return pk11_pub_encrypt_func_ && pk11_priv_decrypt_func_ && - internal_slot_does_oaep_; - } - - // Returns NULL if unsupported. - PK11_EncryptDecryptFunction pk11_encrypt_func() const { - return pk11_encrypt_func_; - } - - // Returns NULL if unsupported. - PK11_EncryptDecryptFunction pk11_decrypt_func() const { - return pk11_decrypt_func_; - } - - // Returns NULL if unsupported. - PK11_PubEncryptFunction pk11_pub_encrypt_func() const { - return pk11_pub_encrypt_func_; - } - - // Returns NULL if unsupported. - PK11_PrivDecryptFunction pk11_priv_decrypt_func() const { - return pk11_priv_decrypt_func_; - } - - private: - friend struct base::DefaultLazyInstanceTraits<NssRuntimeSupport>; - - NssRuntimeSupport() : internal_slot_does_oaep_(false) { -#if !defined(USE_NSS) - // Using a bundled version of NSS that is guaranteed to have this symbol. - pk11_encrypt_func_ = PK11_Encrypt; - pk11_decrypt_func_ = PK11_Decrypt; - pk11_pub_encrypt_func_ = PK11_PubEncrypt; - pk11_priv_decrypt_func_ = PK11_PrivDecrypt; - internal_slot_does_oaep_ = true; -#else - // Using system NSS libraries and PCKS #11 modules, which may not have the - // necessary function (PK11_Encrypt) or mechanism support (CKM_AES_GCM). - - // If PK11_Encrypt() was successfully resolved, then NSS will support - // AES-GCM directly. This was introduced in NSS 3.15. - pk11_encrypt_func_ = reinterpret_cast<PK11_EncryptDecryptFunction>( - dlsym(RTLD_DEFAULT, "PK11_Encrypt")); - pk11_decrypt_func_ = reinterpret_cast<PK11_EncryptDecryptFunction>( - dlsym(RTLD_DEFAULT, "PK11_Decrypt")); - - // Even though NSS's pk11wrap layer may support - // PK11_PubEncrypt/PK11_PubDecrypt (introduced in NSS 3.16.2), it may have - // loaded a softoken that does not include OAEP support. - pk11_pub_encrypt_func_ = reinterpret_cast<PK11_PubEncryptFunction>( - dlsym(RTLD_DEFAULT, "PK11_PubEncrypt")); - pk11_priv_decrypt_func_ = reinterpret_cast<PK11_PrivDecryptFunction>( - dlsym(RTLD_DEFAULT, "PK11_PrivDecrypt")); - if (pk11_priv_decrypt_func_ && pk11_pub_encrypt_func_) { - crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); - internal_slot_does_oaep_ = - !!PK11_DoesMechanism(slot.get(), CKM_RSA_PKCS_OAEP); - } -#endif - } - - PK11_EncryptDecryptFunction pk11_encrypt_func_; - PK11_EncryptDecryptFunction pk11_decrypt_func_; - PK11_PubEncryptFunction pk11_pub_encrypt_func_; - PK11_PrivDecryptFunction pk11_priv_decrypt_func_; - bool internal_slot_does_oaep_; -}; - -base::LazyInstance<NssRuntimeSupport>::Leaky g_nss_runtime_support = - LAZY_INSTANCE_INITIALIZER; - -} // namespace - -namespace content { - -namespace webcrypto { - -namespace platform { - -// Each key maintains a copy of its serialized form -// in either 'raw', 'pkcs8', or 'spki' format. This is to allow -// structured cloning of keys synchronously from the target Blink -// thread without having to lock access to the key. -// -// TODO(eroman): Take advantage of this for implementing exportKey(): no need -// to call into NSS if the serialized form already exists. -// http://crubg.com/366836 -class SymKey : public Key { - public: - static Status Create(crypto::ScopedPK11SymKey key, scoped_ptr<SymKey>* out) { - out->reset(new SymKey(key.Pass())); - return ExportKeyRaw(out->get(), &(*out)->serialized_key_); - } - - PK11SymKey* key() { return key_.get(); } - - virtual SymKey* AsSymKey() OVERRIDE { return this; } - virtual PublicKey* AsPublicKey() OVERRIDE { return NULL; } - virtual PrivateKey* AsPrivateKey() OVERRIDE { return NULL; } - - virtual bool ThreadSafeSerializeForClone( - blink::WebVector<uint8>* key_data) OVERRIDE { - key_data->assign(Uint8VectorStart(serialized_key_), serialized_key_.size()); - return true; - } - - private: - explicit SymKey(crypto::ScopedPK11SymKey key) : key_(key.Pass()) {} - - crypto::ScopedPK11SymKey key_; - std::vector<uint8> serialized_key_; - - DISALLOW_COPY_AND_ASSIGN(SymKey); -}; - -class PublicKey : public Key { - public: - static Status Create(crypto::ScopedSECKEYPublicKey key, - scoped_ptr<PublicKey>* out) { - out->reset(new PublicKey(key.Pass())); - return ExportKeySpki(out->get(), &(*out)->serialized_key_); - } - - SECKEYPublicKey* key() { return key_.get(); } - - virtual SymKey* AsSymKey() OVERRIDE { return NULL; } - virtual PublicKey* AsPublicKey() OVERRIDE { return this; } - virtual PrivateKey* AsPrivateKey() OVERRIDE { return NULL; } - - virtual bool ThreadSafeSerializeForClone( - blink::WebVector<uint8>* key_data) OVERRIDE { - key_data->assign(Uint8VectorStart(serialized_key_), serialized_key_.size()); - return true; - } - - private: - explicit PublicKey(crypto::ScopedSECKEYPublicKey key) : key_(key.Pass()) {} - - crypto::ScopedSECKEYPublicKey key_; - std::vector<uint8> serialized_key_; - - DISALLOW_COPY_AND_ASSIGN(PublicKey); -}; - -class PrivateKey : public Key { - public: - static Status Create(crypto::ScopedSECKEYPrivateKey key, - const blink::WebCryptoKeyAlgorithm& algorithm, - scoped_ptr<PrivateKey>* out) { - out->reset(new PrivateKey(key.Pass())); - return ExportKeyPkcs8(out->get(), algorithm, &(*out)->serialized_key_); - } - - SECKEYPrivateKey* key() { return key_.get(); } - - virtual SymKey* AsSymKey() OVERRIDE { return NULL; } - virtual PublicKey* AsPublicKey() OVERRIDE { return NULL; } - virtual PrivateKey* AsPrivateKey() OVERRIDE { return this; } - - virtual bool ThreadSafeSerializeForClone( - blink::WebVector<uint8>* key_data) OVERRIDE { - key_data->assign(Uint8VectorStart(serialized_key_), serialized_key_.size()); - return true; - } - - private: - explicit PrivateKey(crypto::ScopedSECKEYPrivateKey key) : key_(key.Pass()) {} - - crypto::ScopedSECKEYPrivateKey key_; - std::vector<uint8> serialized_key_; - - DISALLOW_COPY_AND_ASSIGN(PrivateKey); -}; - -namespace { - -Status NssSupportsAesGcm() { - if (g_nss_runtime_support.Get().IsAesGcmSupported()) - return Status::Success(); - return Status::ErrorUnsupported( - "NSS version doesn't support AES-GCM. Try using version 3.15 or later"); -} - -Status NssSupportsRsaOaep() { - if (g_nss_runtime_support.Get().IsRsaOaepSupported()) - return Status::Success(); - return Status::ErrorUnsupported( - "NSS version doesn't support RSA-OAEP. Try using version 3.16.2 or " - "later"); -} - -#if defined(USE_NSS) && !defined(OS_CHROMEOS) -Status ErrorRsaKeyImportNotSupported() { - return Status::ErrorUnsupported( - "NSS version must be at least 3.16.2 for RSA key import. See " - "http://crbug.com/380424"); -} - -Status NssSupportsKeyImport(blink::WebCryptoAlgorithmId algorithm) { - // Prior to NSS 3.16.2 RSA key parameters were not validated. This is - // a security problem for RSA private key import from JWK which uses a - // CKA_ID based on the public modulus to retrieve the private key. - - if (!IsAlgorithmRsa(algorithm)) - return Status::Success(); - - if (!NSS_VersionCheck("3.16.2")) - return ErrorRsaKeyImportNotSupported(); - - // Also ensure that the version of Softoken is 3.16.2 or later. - crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); - CK_SLOT_INFO info = {}; - if (PK11_GetSlotInfo(slot.get(), &info) != SECSuccess) - return ErrorRsaKeyImportNotSupported(); - - // CK_SLOT_INFO.hardwareVersion contains the major.minor - // version info for Softoken in the corresponding .major/.minor - // fields, and .firmwareVersion contains the patch.build - // version info (in the .major/.minor fields) - if ((info.hardwareVersion.major > 3) || - (info.hardwareVersion.major == 3 && - (info.hardwareVersion.minor > 16 || - (info.hardwareVersion.minor == 16 && - info.firmwareVersion.major >= 2)))) { - return Status::Success(); - } - - return ErrorRsaKeyImportNotSupported(); -} -#else -Status NssSupportsKeyImport(blink::WebCryptoAlgorithmId) { - return Status::Success(); -} -#endif - -// Creates a SECItem for the data in |buffer|. This does NOT make a copy, so -// |buffer| should outlive the SECItem. -SECItem MakeSECItemForBuffer(const CryptoData& buffer) { - SECItem item = { - siBuffer, - // NSS requires non-const data even though it is just for input. - const_cast<unsigned char*>(buffer.bytes()), buffer.byte_length()}; - return item; -} - -HASH_HashType WebCryptoAlgorithmToNSSHashType( - blink::WebCryptoAlgorithmId algorithm) { - switch (algorithm) { - case blink::WebCryptoAlgorithmIdSha1: - return HASH_AlgSHA1; - case blink::WebCryptoAlgorithmIdSha256: - return HASH_AlgSHA256; - case blink::WebCryptoAlgorithmIdSha384: - return HASH_AlgSHA384; - case blink::WebCryptoAlgorithmIdSha512: - return HASH_AlgSHA512; - default: - // Not a digest algorithm. - return HASH_AlgNULL; - } -} - -CK_MECHANISM_TYPE WebCryptoHashToHMACMechanism( - const blink::WebCryptoAlgorithm& algorithm) { - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdSha1: - return CKM_SHA_1_HMAC; - case blink::WebCryptoAlgorithmIdSha256: - return CKM_SHA256_HMAC; - case blink::WebCryptoAlgorithmIdSha384: - return CKM_SHA384_HMAC; - case blink::WebCryptoAlgorithmIdSha512: - return CKM_SHA512_HMAC; - default: - // Not a supported algorithm. - return CKM_INVALID_MECHANISM; - } -} - -CK_MECHANISM_TYPE WebCryptoHashToDigestMechanism( - const blink::WebCryptoAlgorithm& algorithm) { - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdSha1: - return CKM_SHA_1; - case blink::WebCryptoAlgorithmIdSha256: - return CKM_SHA256; - case blink::WebCryptoAlgorithmIdSha384: - return CKM_SHA384; - case blink::WebCryptoAlgorithmIdSha512: - return CKM_SHA512; - default: - // Not a supported algorithm. - return CKM_INVALID_MECHANISM; - } -} - -CK_MECHANISM_TYPE WebCryptoHashToMGFMechanism( - const blink::WebCryptoAlgorithm& algorithm) { - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdSha1: - return CKG_MGF1_SHA1; - case blink::WebCryptoAlgorithmIdSha256: - return CKG_MGF1_SHA256; - case blink::WebCryptoAlgorithmIdSha384: - return CKG_MGF1_SHA384; - case blink::WebCryptoAlgorithmIdSha512: - return CKG_MGF1_SHA512; - default: - return CKM_INVALID_MECHANISM; - } -} - -bool InitializeRsaOaepParams(const blink::WebCryptoAlgorithm& hash, - const CryptoData& label, - CK_RSA_PKCS_OAEP_PARAMS* oaep_params) { - oaep_params->source = CKZ_DATA_SPECIFIED; - oaep_params->pSourceData = const_cast<unsigned char*>(label.bytes()); - oaep_params->ulSourceDataLen = label.byte_length(); - oaep_params->mgf = WebCryptoHashToMGFMechanism(hash); - oaep_params->hashAlg = WebCryptoHashToDigestMechanism(hash); - - if (oaep_params->mgf == CKM_INVALID_MECHANISM || - oaep_params->hashAlg == CKM_INVALID_MECHANISM) { - return false; - } - - return true; -} - -Status AesCbcEncryptDecrypt(EncryptOrDecrypt mode, - SymKey* key, - const CryptoData& iv, - const CryptoData& data, - std::vector<uint8>* buffer) { - CK_ATTRIBUTE_TYPE operation = (mode == ENCRYPT) ? CKA_ENCRYPT : CKA_DECRYPT; - - SECItem iv_item = MakeSECItemForBuffer(iv); - - crypto::ScopedSECItem param(PK11_ParamFromIV(CKM_AES_CBC_PAD, &iv_item)); - if (!param) - return Status::OperationError(); - - crypto::ScopedPK11Context context(PK11_CreateContextBySymKey( - CKM_AES_CBC_PAD, operation, key->key(), param.get())); - - if (!context.get()) - return Status::OperationError(); - - // Oddly PK11_CipherOp takes input and output lengths as "int" rather than - // "unsigned int". Do some checks now to avoid integer overflowing. - if (data.byte_length() >= INT_MAX - AES_BLOCK_SIZE) { - // TODO(eroman): Handle this by chunking the input fed into NSS. Right now - // it doesn't make much difference since the one-shot API would end up - // blowing out the memory and crashing anyway. - return Status::ErrorDataTooLarge(); - } - - // PK11_CipherOp does an invalid memory access when given empty decryption - // input, or input which is not a multiple of the block size. See also - // https://bugzilla.mozilla.com/show_bug.cgi?id=921687. - if (operation == CKA_DECRYPT && - (data.byte_length() == 0 || (data.byte_length() % AES_BLOCK_SIZE != 0))) { - return Status::OperationError(); - } - - // TODO(eroman): Refine the output buffer size. It can be computed exactly for - // encryption, and can be smaller for decryption. - unsigned int output_max_len = data.byte_length() + AES_BLOCK_SIZE; - CHECK_GT(output_max_len, data.byte_length()); - - buffer->resize(output_max_len); - - unsigned char* buffer_data = Uint8VectorStart(buffer); - - int output_len; - if (SECSuccess != PK11_CipherOp(context.get(), - buffer_data, - &output_len, - buffer->size(), - data.bytes(), - data.byte_length())) { - return Status::OperationError(); - } - - unsigned int final_output_chunk_len; - if (SECSuccess != PK11_DigestFinal(context.get(), - buffer_data + output_len, - &final_output_chunk_len, - output_max_len - output_len)) { - return Status::OperationError(); - } - - buffer->resize(final_output_chunk_len + output_len); - return Status::Success(); -} - -// Helper to either encrypt or decrypt for AES-GCM. The result of encryption is -// the concatenation of the ciphertext and the authentication tag. Similarly, -// this is the expectation for the input to decryption. -Status AesGcmEncryptDecrypt(EncryptOrDecrypt mode, - SymKey* key, - const CryptoData& data, - const CryptoData& iv, - const CryptoData& additional_data, - unsigned int tag_length_bits, - std::vector<uint8>* buffer) { - Status status = NssSupportsAesGcm(); - if (status.IsError()) - return status; - - unsigned int tag_length_bytes = tag_length_bits / 8; - - CK_GCM_PARAMS gcm_params = {0}; - gcm_params.pIv = const_cast<unsigned char*>(iv.bytes()); - gcm_params.ulIvLen = iv.byte_length(); - - gcm_params.pAAD = const_cast<unsigned char*>(additional_data.bytes()); - gcm_params.ulAADLen = additional_data.byte_length(); - - gcm_params.ulTagBits = tag_length_bits; - - SECItem param; - param.type = siBuffer; - param.data = reinterpret_cast<unsigned char*>(&gcm_params); - param.len = sizeof(gcm_params); - - unsigned int buffer_size = 0; - - // Calculate the output buffer size. - if (mode == ENCRYPT) { - // TODO(eroman): This is ugly, abstract away the safe integer arithmetic. - if (data.byte_length() > (UINT_MAX - tag_length_bytes)) - return Status::ErrorDataTooLarge(); - buffer_size = data.byte_length() + tag_length_bytes; - } else { - // TODO(eroman): In theory the buffer allocated for the plain text should be - // sized as |data.byte_length() - tag_length_bytes|. - // - // However NSS has a bug whereby it will fail if the output buffer size is - // not at least as large as the ciphertext: - // - // https://bugzilla.mozilla.org/show_bug.cgi?id=%20853674 - // - // From the analysis of that bug it looks like it might be safe to pass a - // correctly sized buffer but lie about its size. Since resizing the - // WebCryptoArrayBuffer is expensive that hack may be worth looking into. - buffer_size = data.byte_length(); - } - - buffer->resize(buffer_size); - unsigned char* buffer_data = Uint8VectorStart(buffer); - - PK11_EncryptDecryptFunction func = - (mode == ENCRYPT) ? g_nss_runtime_support.Get().pk11_encrypt_func() - : g_nss_runtime_support.Get().pk11_decrypt_func(); - - unsigned int output_len = 0; - SECStatus result = func(key->key(), - CKM_AES_GCM, - ¶m, - buffer_data, - &output_len, - buffer->size(), - data.bytes(), - data.byte_length()); - - if (result != SECSuccess) - return Status::OperationError(); - - // Unfortunately the buffer needs to be shrunk for decryption (see the NSS bug - // above). - buffer->resize(output_len); - - return Status::Success(); -} - -CK_MECHANISM_TYPE WebCryptoAlgorithmToGenMechanism( - const blink::WebCryptoAlgorithm& algorithm) { - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdAesCbc: - case blink::WebCryptoAlgorithmIdAesGcm: - case blink::WebCryptoAlgorithmIdAesKw: - return CKM_AES_KEY_GEN; - case blink::WebCryptoAlgorithmIdHmac: - return WebCryptoHashToHMACMechanism(algorithm.hmacKeyGenParams()->hash()); - default: - return CKM_INVALID_MECHANISM; - } -} - -bool CreatePublicKeyAlgorithm(const blink::WebCryptoAlgorithm& algorithm, - SECKEYPublicKey* key, - blink::WebCryptoKeyAlgorithm* key_algorithm) { - // TODO(eroman): What about other key types rsaPss, rsaOaep. - if (!key || key->keyType != rsaKey) - return false; - - unsigned int modulus_length_bits = SECKEY_PublicKeyStrength(key) * 8; - CryptoData public_exponent(key->u.rsa.publicExponent.data, - key->u.rsa.publicExponent.len); - - switch (algorithm.paramsType()) { - case blink::WebCryptoAlgorithmParamsTypeRsaHashedImportParams: - case blink::WebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams: - *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed( - algorithm.id(), - modulus_length_bits, - public_exponent.bytes(), - public_exponent.byte_length(), - GetInnerHashAlgorithm(algorithm).id()); - return true; - default: - return false; - } -} - -bool CreatePrivateKeyAlgorithm(const blink::WebCryptoAlgorithm& algorithm, - SECKEYPrivateKey* key, - blink::WebCryptoKeyAlgorithm* key_algorithm) { - crypto::ScopedSECKEYPublicKey public_key(SECKEY_ConvertToPublicKey(key)); - return CreatePublicKeyAlgorithm(algorithm, public_key.get(), key_algorithm); -} - -// The Default IV for AES-KW. See http://www.ietf.org/rfc/rfc3394.txt -// Section 2.2.3.1. -// TODO(padolph): Move to common place to be shared with OpenSSL implementation. -const unsigned char kAesIv[] = {0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6}; - -// Sets NSS CK_MECHANISM_TYPE and CK_FLAGS corresponding to the input Web Crypto -// algorithm ID. -Status WebCryptoAlgorithmToNssMechFlags( - const blink::WebCryptoAlgorithm& algorithm, - CK_MECHANISM_TYPE* mechanism, - CK_FLAGS* flags) { - // Flags are verified at the Blink layer; here the flags are set to all - // possible operations of a key for the input algorithm type. - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdHmac: { - const blink::WebCryptoAlgorithm hash = GetInnerHashAlgorithm(algorithm); - *mechanism = WebCryptoHashToHMACMechanism(hash); - if (*mechanism == CKM_INVALID_MECHANISM) - return Status::ErrorUnsupported(); - *flags = CKF_SIGN | CKF_VERIFY; - return Status::Success(); - } - case blink::WebCryptoAlgorithmIdAesCbc: { - *mechanism = CKM_AES_CBC; - *flags = CKF_ENCRYPT | CKF_DECRYPT; - return Status::Success(); - } - case blink::WebCryptoAlgorithmIdAesKw: { - *mechanism = CKM_NSS_AES_KEY_WRAP; - *flags = CKF_WRAP | CKF_WRAP; - return Status::Success(); - } - case blink::WebCryptoAlgorithmIdAesGcm: { - Status status = NssSupportsAesGcm(); - if (status.IsError()) - return status; - *mechanism = CKM_AES_GCM; - *flags = CKF_ENCRYPT | CKF_DECRYPT; - return Status::Success(); - } - default: - return Status::ErrorUnsupported(); - } -} - -Status DoUnwrapSymKeyAesKw(const CryptoData& wrapped_key_data, - SymKey* wrapping_key, - CK_MECHANISM_TYPE mechanism, - CK_FLAGS flags, - crypto::ScopedPK11SymKey* unwrapped_key) { - DCHECK_GE(wrapped_key_data.byte_length(), 24u); - DCHECK_EQ(wrapped_key_data.byte_length() % 8, 0u); - - SECItem iv_item = MakeSECItemForBuffer(CryptoData(kAesIv, sizeof(kAesIv))); - crypto::ScopedSECItem param_item( - PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP, &iv_item)); - if (!param_item) - return Status::ErrorUnexpected(); - - SECItem cipher_text = MakeSECItemForBuffer(wrapped_key_data); - - // The plaintext length is always 64 bits less than the data size. - const unsigned int plaintext_length = wrapped_key_data.byte_length() - 8; - -#if defined(USE_NSS) - // Part of workaround for - // https://bugzilla.mozilla.org/show_bug.cgi?id=981170. See the explanation - // later in this function. - PORT_SetError(0); -#endif - - crypto::ScopedPK11SymKey new_key( - PK11_UnwrapSymKeyWithFlags(wrapping_key->key(), - CKM_NSS_AES_KEY_WRAP, - param_item.get(), - &cipher_text, - mechanism, - CKA_FLAGS_ONLY, - plaintext_length, - flags)); - - // TODO(padolph): Use NSS PORT_GetError() and friends to report a more - // accurate error, providing if doesn't leak any information to web pages - // about other web crypto users, key details, etc. - if (!new_key) - return Status::OperationError(); - -#if defined(USE_NSS) - // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=981170 - // which was fixed in NSS 3.16.0. - // If unwrap fails, NSS nevertheless returns a valid-looking PK11SymKey, - // with a reasonable length but with key data pointing to uninitialized - // memory. - // To understand this workaround see the fix for 981170: - // https://hg.mozilla.org/projects/nss/rev/753bb69e543c - if (!NSS_VersionCheck("3.16") && PORT_GetError() == SEC_ERROR_BAD_DATA) - return Status::OperationError(); -#endif - - *unwrapped_key = new_key.Pass(); - return Status::Success(); -} - -void CopySECItemToVector(const SECItem& item, std::vector<uint8>* out) { - out->assign(item.data, item.data + item.len); -} - -// From PKCS#1 [http://tools.ietf.org/html/rfc3447]: -// -// RSAPrivateKey ::= SEQUENCE { -// version Version, -// modulus INTEGER, -- n -// publicExponent INTEGER, -- e -// privateExponent INTEGER, -- d -// prime1 INTEGER, -- p -// prime2 INTEGER, -- q -// exponent1 INTEGER, -- d mod (p-1) -// exponent2 INTEGER, -- d mod (q-1) -// coefficient INTEGER, -- (inverse of q) mod p -// otherPrimeInfos OtherPrimeInfos OPTIONAL -// } -// -// Note that otherPrimeInfos is only applicable for version=1. Since NSS -// doesn't use multi-prime can safely use version=0. -struct RSAPrivateKey { - SECItem version; - SECItem modulus; - SECItem public_exponent; - SECItem private_exponent; - SECItem prime1; - SECItem prime2; - SECItem exponent1; - SECItem exponent2; - SECItem coefficient; -}; - -// The system NSS library doesn't have the new PK11_ExportDERPrivateKeyInfo -// function yet (https://bugzilla.mozilla.org/show_bug.cgi?id=519255). So we -// provide a fallback implementation. -#if defined(USE_NSS) -const SEC_ASN1Template RSAPrivateKeyTemplate[] = { - {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(RSAPrivateKey)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, version)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, modulus)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, public_exponent)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, private_exponent)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, prime1)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, prime2)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, exponent1)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, exponent2)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, coefficient)}, - {0}}; -#endif // defined(USE_NSS) - -// On success |value| will be filled with data which must be freed by -// SECITEM_FreeItem(value, PR_FALSE); -bool ReadUint(SECKEYPrivateKey* key, - CK_ATTRIBUTE_TYPE attribute, - SECItem* value) { - SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, attribute, value); - - // PK11_ReadRawAttribute() returns items of type siBuffer. However in order - // for the ASN.1 encoding to be correct, the items must be of type - // siUnsignedInteger. - value->type = siUnsignedInteger; - - return rv == SECSuccess; -} - -// Fills |out| with the RSA private key properties. Returns true on success. -// Regardless of the return value, the caller must invoke FreeRSAPrivateKey() -// to free up any allocated memory. -// -// The passed in RSAPrivateKey must be zero-initialized. -bool InitRSAPrivateKey(SECKEYPrivateKey* key, RSAPrivateKey* out) { - if (key->keyType != rsaKey) - return false; - - // Everything should be zero-ed out. These are just some spot checks. - DCHECK(!out->version.data); - DCHECK(!out->version.len); - DCHECK(!out->modulus.data); - DCHECK(!out->modulus.len); - - // Always use version=0 since not using multi-prime. - if (!SEC_ASN1EncodeInteger(NULL, &out->version, 0)) - return false; - - if (!ReadUint(key, CKA_MODULUS, &out->modulus)) - return false; - if (!ReadUint(key, CKA_PUBLIC_EXPONENT, &out->public_exponent)) - return false; - if (!ReadUint(key, CKA_PRIVATE_EXPONENT, &out->private_exponent)) - return false; - if (!ReadUint(key, CKA_PRIME_1, &out->prime1)) - return false; - if (!ReadUint(key, CKA_PRIME_2, &out->prime2)) - return false; - if (!ReadUint(key, CKA_EXPONENT_1, &out->exponent1)) - return false; - if (!ReadUint(key, CKA_EXPONENT_2, &out->exponent2)) - return false; - if (!ReadUint(key, CKA_COEFFICIENT, &out->coefficient)) - return false; - - return true; -} - -struct FreeRsaPrivateKey { - void operator()(RSAPrivateKey* out) { - SECITEM_FreeItem(&out->version, PR_FALSE); - SECITEM_FreeItem(&out->modulus, PR_FALSE); - SECITEM_FreeItem(&out->public_exponent, PR_FALSE); - SECITEM_FreeItem(&out->private_exponent, PR_FALSE); - SECITEM_FreeItem(&out->prime1, PR_FALSE); - SECITEM_FreeItem(&out->prime2, PR_FALSE); - SECITEM_FreeItem(&out->exponent1, PR_FALSE); - SECITEM_FreeItem(&out->exponent2, PR_FALSE); - SECITEM_FreeItem(&out->coefficient, PR_FALSE); - } -}; - -} // namespace - -class DigestorNSS : public blink::WebCryptoDigestor { - public: - explicit DigestorNSS(blink::WebCryptoAlgorithmId algorithm_id) - : hash_context_(NULL), algorithm_id_(algorithm_id) {} - - virtual ~DigestorNSS() { - if (!hash_context_) - return; - - HASH_Destroy(hash_context_); - hash_context_ = NULL; - } - - virtual bool consume(const unsigned char* data, unsigned int size) { - return ConsumeWithStatus(data, size).IsSuccess(); - } - - Status ConsumeWithStatus(const unsigned char* data, unsigned int size) { - // Initialize everything if the object hasn't been initialized yet. - if (!hash_context_) { - Status error = Init(); - if (!error.IsSuccess()) - return error; - } - - HASH_Update(hash_context_, data, size); - - return Status::Success(); - } - - virtual bool finish(unsigned char*& result_data, - unsigned int& result_data_size) { - Status error = FinishInternal(result_, &result_data_size); - if (!error.IsSuccess()) - return false; - result_data = result_; - return true; - } - - Status FinishWithVectorAndStatus(std::vector<uint8>* result) { - if (!hash_context_) - return Status::ErrorUnexpected(); - - unsigned int result_length = HASH_ResultLenContext(hash_context_); - result->resize(result_length); - unsigned char* digest = Uint8VectorStart(result); - unsigned int digest_size; // ignored - return FinishInternal(digest, &digest_size); - } - - private: - Status Init() { - HASH_HashType hash_type = WebCryptoAlgorithmToNSSHashType(algorithm_id_); - - if (hash_type == HASH_AlgNULL) - return Status::ErrorUnsupported(); - - hash_context_ = HASH_Create(hash_type); - if (!hash_context_) - return Status::OperationError(); - - HASH_Begin(hash_context_); - - return Status::Success(); - } - - Status FinishInternal(unsigned char* result, unsigned int* result_size) { - if (!hash_context_) { - Status error = Init(); - if (!error.IsSuccess()) - return error; - } - - unsigned int hash_result_length = HASH_ResultLenContext(hash_context_); - DCHECK_LE(hash_result_length, static_cast<size_t>(HASH_LENGTH_MAX)); - - HASH_End(hash_context_, result, result_size, hash_result_length); - - if (*result_size != hash_result_length) - return Status::ErrorUnexpected(); - return Status::Success(); - } - - HASHContext* hash_context_; - blink::WebCryptoAlgorithmId algorithm_id_; - unsigned char result_[HASH_LENGTH_MAX]; -}; - -Status ImportKeyRaw(const blink::WebCryptoAlgorithm& algorithm, - const CryptoData& key_data, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key) { - DCHECK(!algorithm.isNull()); - - CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; - CK_FLAGS flags = 0; - Status status = - WebCryptoAlgorithmToNssMechFlags(algorithm, &mechanism, &flags); - if (status.IsError()) - return status; - - SECItem key_item = MakeSECItemForBuffer(key_data); - - crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); - crypto::ScopedPK11SymKey pk11_sym_key( - PK11_ImportSymKeyWithFlags(slot.get(), - mechanism, - PK11_OriginUnwrap, - CKA_FLAGS_ONLY, - &key_item, - flags, - false, - NULL)); - if (!pk11_sym_key.get()) - return Status::OperationError(); - - blink::WebCryptoKeyAlgorithm key_algorithm; - if (!CreateSecretKeyAlgorithm( - algorithm, key_data.byte_length(), &key_algorithm)) - return Status::ErrorUnexpected(); - - scoped_ptr<SymKey> key_handle; - status = SymKey::Create(pk11_sym_key.Pass(), &key_handle); - if (status.IsError()) - return status; - - *key = blink::WebCryptoKey::create(key_handle.release(), - blink::WebCryptoKeyTypeSecret, - extractable, - key_algorithm, - usage_mask); - return Status::Success(); -} - -Status ExportKeyRaw(SymKey* key, std::vector<uint8>* buffer) { - if (PK11_ExtractKeyValue(key->key()) != SECSuccess) - return Status::OperationError(); - - // http://crbug.com/366427: the spec does not define any other failures for - // exporting, so none of the subsequent errors are spec compliant. - const SECItem* key_data = PK11_GetKeyData(key->key()); - if (!key_data) - return Status::OperationError(); - - buffer->assign(key_data->data, key_data->data + key_data->len); - - return Status::Success(); -} - -namespace { - -typedef scoped_ptr<CERTSubjectPublicKeyInfo, - crypto::NSSDestroyer<CERTSubjectPublicKeyInfo, - SECKEY_DestroySubjectPublicKeyInfo> > - ScopedCERTSubjectPublicKeyInfo; - -// Validates an NSS KeyType against a WebCrypto import algorithm. -bool ValidateNssKeyTypeAgainstInputAlgorithm( - KeyType key_type, - const blink::WebCryptoAlgorithm& algorithm) { - switch (key_type) { - case rsaKey: - return IsAlgorithmRsa(algorithm.id()); - case dsaKey: - case ecKey: - case rsaPssKey: - case rsaOaepKey: - // TODO(padolph): Handle other key types. - break; - default: - break; - } - return false; -} - -} // namespace - -Status ImportKeySpki(const blink::WebCryptoAlgorithm& algorithm, - const CryptoData& key_data, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key) { - Status status = NssSupportsKeyImport(algorithm.id()); - if (status.IsError()) - return status; - - DCHECK(key); - - if (!key_data.byte_length()) - return Status::ErrorImportEmptyKeyData(); - DCHECK(key_data.bytes()); - - // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 Subject - // Public Key Info. Decode this to a CERTSubjectPublicKeyInfo. - SECItem spki_item = MakeSECItemForBuffer(key_data); - const ScopedCERTSubjectPublicKeyInfo spki( - SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); - if (!spki) - return Status::DataError(); - - crypto::ScopedSECKEYPublicKey sec_public_key( - SECKEY_ExtractPublicKey(spki.get())); - if (!sec_public_key) - return Status::DataError(); - - const KeyType sec_key_type = SECKEY_GetPublicKeyType(sec_public_key.get()); - if (!ValidateNssKeyTypeAgainstInputAlgorithm(sec_key_type, algorithm)) - return Status::DataError(); - - blink::WebCryptoKeyAlgorithm key_algorithm; - if (!CreatePublicKeyAlgorithm( - algorithm, sec_public_key.get(), &key_algorithm)) - return Status::ErrorUnexpected(); - - scoped_ptr<PublicKey> key_handle; - status = PublicKey::Create(sec_public_key.Pass(), &key_handle); - if (status.IsError()) - return status; - - *key = blink::WebCryptoKey::create(key_handle.release(), - blink::WebCryptoKeyTypePublic, - extractable, - key_algorithm, - usage_mask); - - return Status::Success(); -} - -Status ExportKeySpki(PublicKey* key, std::vector<uint8>* buffer) { - const crypto::ScopedSECItem spki_der( - SECKEY_EncodeDERSubjectPublicKeyInfo(key->key())); - // http://crbug.com/366427: the spec does not define any other failures for - // exporting, so none of the subsequent errors are spec compliant. - if (!spki_der) - return Status::OperationError(); - - DCHECK(spki_der->data); - DCHECK(spki_der->len); - - buffer->assign(spki_der->data, spki_der->data + spki_der->len); - - return Status::Success(); -} - -Status ExportRsaPublicKey(PublicKey* key, - std::vector<uint8>* modulus, - std::vector<uint8>* public_exponent) { - DCHECK(key); - DCHECK(key->key()); - if (key->key()->keyType != rsaKey) - return Status::ErrorUnsupported(); - CopySECItemToVector(key->key()->u.rsa.modulus, modulus); - CopySECItemToVector(key->key()->u.rsa.publicExponent, public_exponent); - if (modulus->empty() || public_exponent->empty()) - return Status::ErrorUnexpected(); - return Status::Success(); -} - -void AssignVectorFromSecItem(const SECItem& item, std::vector<uint8>* output) { - output->assign(item.data, item.data + item.len); -} - -Status ExportRsaPrivateKey(PrivateKey* key, - std::vector<uint8>* modulus, - std::vector<uint8>* public_exponent, - std::vector<uint8>* private_exponent, - std::vector<uint8>* prime1, - std::vector<uint8>* prime2, - std::vector<uint8>* exponent1, - std::vector<uint8>* exponent2, - std::vector<uint8>* coefficient) { - RSAPrivateKey key_props = {}; - scoped_ptr<RSAPrivateKey, FreeRsaPrivateKey> free_private_key(&key_props); - - if (!InitRSAPrivateKey(key->key(), &key_props)) - return Status::OperationError(); - - AssignVectorFromSecItem(key_props.modulus, modulus); - AssignVectorFromSecItem(key_props.public_exponent, public_exponent); - AssignVectorFromSecItem(key_props.private_exponent, private_exponent); - AssignVectorFromSecItem(key_props.prime1, prime1); - AssignVectorFromSecItem(key_props.prime2, prime2); - AssignVectorFromSecItem(key_props.exponent1, exponent1); - AssignVectorFromSecItem(key_props.exponent2, exponent2); - AssignVectorFromSecItem(key_props.coefficient, coefficient); - - return Status::Success(); -} - -Status ExportKeyPkcs8(PrivateKey* key, - const blink::WebCryptoKeyAlgorithm& key_algorithm, - std::vector<uint8>* buffer) { - // TODO(eroman): Support other RSA key types as they are added to Blink. - if (key_algorithm.id() != blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 && - key_algorithm.id() != blink::WebCryptoAlgorithmIdRsaOaep) - return Status::ErrorUnsupported(); - -// TODO(rsleevi): Implement OAEP support according to the spec. - -#if defined(USE_NSS) - // PK11_ExportDERPrivateKeyInfo isn't available. Use our fallback code. - const SECOidTag algorithm = SEC_OID_PKCS1_RSA_ENCRYPTION; - const int kPrivateKeyInfoVersion = 0; - - SECKEYPrivateKeyInfo private_key_info = {}; - RSAPrivateKey rsa_private_key = {}; - scoped_ptr<RSAPrivateKey, FreeRsaPrivateKey> free_private_key( - &rsa_private_key); - - // http://crbug.com/366427: the spec does not define any other failures for - // exporting, so none of the subsequent errors are spec compliant. - if (!InitRSAPrivateKey(key->key(), &rsa_private_key)) - return Status::OperationError(); - - crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); - if (!arena.get()) - return Status::OperationError(); - - if (!SEC_ASN1EncodeItem(arena.get(), - &private_key_info.privateKey, - &rsa_private_key, - RSAPrivateKeyTemplate)) - return Status::OperationError(); - - if (SECSuccess != - SECOID_SetAlgorithmID( - arena.get(), &private_key_info.algorithm, algorithm, NULL)) - return Status::OperationError(); - - if (!SEC_ASN1EncodeInteger( - arena.get(), &private_key_info.version, kPrivateKeyInfoVersion)) - return Status::OperationError(); - - crypto::ScopedSECItem encoded_key( - SEC_ASN1EncodeItem(NULL, - NULL, - &private_key_info, - SEC_ASN1_GET(SECKEY_PrivateKeyInfoTemplate))); -#else // defined(USE_NSS) - crypto::ScopedSECItem encoded_key( - PK11_ExportDERPrivateKeyInfo(key->key(), NULL)); -#endif // defined(USE_NSS) - - if (!encoded_key.get()) - return Status::OperationError(); - - buffer->assign(encoded_key->data, encoded_key->data + encoded_key->len); - return Status::Success(); -} - -Status ImportKeyPkcs8(const blink::WebCryptoAlgorithm& algorithm, - const CryptoData& key_data, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key) { - Status status = NssSupportsKeyImport(algorithm.id()); - if (status.IsError()) - return status; - - DCHECK(key); - - if (!key_data.byte_length()) - return Status::ErrorImportEmptyKeyData(); - DCHECK(key_data.bytes()); - - // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 PKCS#8 - // private key info object. - SECItem pki_der = MakeSECItemForBuffer(key_data); - - SECKEYPrivateKey* seckey_private_key = NULL; - crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); - if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot.get(), - &pki_der, - NULL, // nickname - NULL, // publicValue - false, // isPerm - false, // isPrivate - KU_ALL, // usage - &seckey_private_key, - NULL) != SECSuccess) { - return Status::DataError(); - } - DCHECK(seckey_private_key); - crypto::ScopedSECKEYPrivateKey private_key(seckey_private_key); - - const KeyType sec_key_type = SECKEY_GetPrivateKeyType(private_key.get()); - if (!ValidateNssKeyTypeAgainstInputAlgorithm(sec_key_type, algorithm)) - return Status::DataError(); - - blink::WebCryptoKeyAlgorithm key_algorithm; - if (!CreatePrivateKeyAlgorithm(algorithm, private_key.get(), &key_algorithm)) - return Status::ErrorUnexpected(); - - scoped_ptr<PrivateKey> key_handle; - status = PrivateKey::Create(private_key.Pass(), key_algorithm, &key_handle); - if (status.IsError()) - return status; - - *key = blink::WebCryptoKey::create(key_handle.release(), - blink::WebCryptoKeyTypePrivate, - extractable, - key_algorithm, - usage_mask); - - return Status::Success(); -} - -// ----------------------------------- -// Hmac -// ----------------------------------- - -Status SignHmac(SymKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& data, - std::vector<uint8>* buffer) { - DCHECK_EQ(PK11_GetMechanism(key->key()), WebCryptoHashToHMACMechanism(hash)); - - SECItem param_item = {siBuffer, NULL, 0}; - SECItem data_item = MakeSECItemForBuffer(data); - // First call is to figure out the length. - SECItem signature_item = {siBuffer, NULL, 0}; - - if (PK11_SignWithSymKey(key->key(), - PK11_GetMechanism(key->key()), - ¶m_item, - &signature_item, - &data_item) != SECSuccess) { - return Status::OperationError(); - } - - DCHECK_NE(0u, signature_item.len); - - buffer->resize(signature_item.len); - signature_item.data = Uint8VectorStart(buffer); - - if (PK11_SignWithSymKey(key->key(), - PK11_GetMechanism(key->key()), - ¶m_item, - &signature_item, - &data_item) != SECSuccess) { - return Status::OperationError(); - } - - DCHECK_EQ(buffer->size(), signature_item.len); - return Status::Success(); -} - -// ----------------------------------- -// RsaOaep -// ----------------------------------- - -Status EncryptRsaOaep(PublicKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& label, - const CryptoData& data, - std::vector<uint8>* buffer) { - Status status = NssSupportsRsaOaep(); - if (status.IsError()) - return status; - - CK_RSA_PKCS_OAEP_PARAMS oaep_params = {0}; - if (!InitializeRsaOaepParams(hash, label, &oaep_params)) - return Status::ErrorUnsupported(); - - SECItem param; - param.type = siBuffer; - param.data = reinterpret_cast<unsigned char*>(&oaep_params); - param.len = sizeof(oaep_params); - - buffer->resize(SECKEY_PublicKeyStrength(key->key())); - unsigned char* buffer_data = Uint8VectorStart(buffer); - unsigned int output_len; - if (g_nss_runtime_support.Get().pk11_pub_encrypt_func()(key->key(), - CKM_RSA_PKCS_OAEP, - ¶m, - buffer_data, - &output_len, - buffer->size(), - data.bytes(), - data.byte_length(), - NULL) != SECSuccess) { - return Status::OperationError(); - } - - DCHECK_LE(output_len, buffer->size()); - buffer->resize(output_len); - return Status::Success(); -} - -Status DecryptRsaOaep(PrivateKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& label, - const CryptoData& data, - std::vector<uint8>* buffer) { - Status status = NssSupportsRsaOaep(); - if (status.IsError()) - return status; - - CK_RSA_PKCS_OAEP_PARAMS oaep_params = {0}; - if (!InitializeRsaOaepParams(hash, label, &oaep_params)) - return Status::ErrorUnsupported(); - - SECItem param; - param.type = siBuffer; - param.data = reinterpret_cast<unsigned char*>(&oaep_params); - param.len = sizeof(oaep_params); - - const int modulus_length_bytes = PK11_GetPrivateModulusLen(key->key()); - if (modulus_length_bytes <= 0) - return Status::ErrorUnexpected(); - - buffer->resize(modulus_length_bytes); - - unsigned char* buffer_data = Uint8VectorStart(buffer); - unsigned int output_len; - if (g_nss_runtime_support.Get().pk11_priv_decrypt_func()( - key->key(), - CKM_RSA_PKCS_OAEP, - ¶m, - buffer_data, - &output_len, - buffer->size(), - data.bytes(), - data.byte_length()) != SECSuccess) { - return Status::OperationError(); - } - - DCHECK_LE(output_len, buffer->size()); - buffer->resize(output_len); - return Status::Success(); -} - -// ----------------------------------- -// RsaSsaPkcs1v1_5 -// ----------------------------------- - -Status SignRsaSsaPkcs1v1_5(PrivateKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& data, - std::vector<uint8>* buffer) { - // Pick the NSS signing algorithm by combining RSA-SSA (RSA PKCS1) and the - // inner hash of the input Web Crypto algorithm. - SECOidTag sign_alg_tag; - switch (hash.id()) { - case blink::WebCryptoAlgorithmIdSha1: - sign_alg_tag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; - break; - case blink::WebCryptoAlgorithmIdSha256: - sign_alg_tag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; - break; - case blink::WebCryptoAlgorithmIdSha384: - sign_alg_tag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; - break; - case blink::WebCryptoAlgorithmIdSha512: - sign_alg_tag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; - break; - default: - return Status::ErrorUnsupported(); - } - - crypto::ScopedSECItem signature_item(SECITEM_AllocItem(NULL, NULL, 0)); - if (SEC_SignData(signature_item.get(), - data.bytes(), - data.byte_length(), - key->key(), - sign_alg_tag) != SECSuccess) { - return Status::OperationError(); - } - - buffer->assign(signature_item->data, - signature_item->data + signature_item->len); - return Status::Success(); -} - -Status VerifyRsaSsaPkcs1v1_5(PublicKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& signature, - const CryptoData& data, - bool* signature_match) { - const SECItem signature_item = MakeSECItemForBuffer(signature); - - SECOidTag hash_alg_tag; - switch (hash.id()) { - case blink::WebCryptoAlgorithmIdSha1: - hash_alg_tag = SEC_OID_SHA1; - break; - case blink::WebCryptoAlgorithmIdSha256: - hash_alg_tag = SEC_OID_SHA256; - break; - case blink::WebCryptoAlgorithmIdSha384: - hash_alg_tag = SEC_OID_SHA384; - break; - case blink::WebCryptoAlgorithmIdSha512: - hash_alg_tag = SEC_OID_SHA512; - break; - default: - return Status::ErrorUnsupported(); - } - - *signature_match = - SECSuccess == VFY_VerifyDataDirect(data.bytes(), - data.byte_length(), - key->key(), - &signature_item, - SEC_OID_PKCS1_RSA_ENCRYPTION, - hash_alg_tag, - NULL, - NULL); - return Status::Success(); -} - -Status EncryptDecryptAesCbc(EncryptOrDecrypt mode, - SymKey* key, - const CryptoData& data, - const CryptoData& iv, - std::vector<uint8>* buffer) { - // TODO(eroman): Inline. - return AesCbcEncryptDecrypt(mode, key, iv, data, buffer); -} - -Status EncryptDecryptAesGcm(EncryptOrDecrypt mode, - SymKey* key, - const CryptoData& data, - const CryptoData& iv, - const CryptoData& additional_data, - unsigned int tag_length_bits, - std::vector<uint8>* buffer) { - // TODO(eroman): Inline. - return AesGcmEncryptDecrypt( - mode, key, data, iv, additional_data, tag_length_bits, buffer); -} - -// ----------------------------------- -// Key generation -// ----------------------------------- - -Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask public_key_usage_mask, - blink::WebCryptoKeyUsageMask private_key_usage_mask, - unsigned int modulus_length_bits, - unsigned long public_exponent, - blink::WebCryptoKey* public_key, - blink::WebCryptoKey* private_key) { - if (algorithm.id() == blink::WebCryptoAlgorithmIdRsaOaep) { - Status status = NssSupportsRsaOaep(); - if (status.IsError()) - return status; - } - - crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); - if (!slot) - return Status::OperationError(); - - PK11RSAGenParams rsa_gen_params; - // keySizeInBits is a signed type, don't pass in a negative value. - if (modulus_length_bits > INT_MAX) - return Status::OperationError(); - rsa_gen_params.keySizeInBits = modulus_length_bits; - rsa_gen_params.pe = public_exponent; - - // Flags are verified at the Blink layer; here the flags are set to all - // possible operations for the given key type. - CK_FLAGS operation_flags; - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdRsaOaep: - operation_flags = CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP; - break; - case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: - operation_flags = CKF_SIGN | CKF_VERIFY; - break; - default: - NOTREACHED(); - return Status::ErrorUnexpected(); - } - const CK_FLAGS operation_flags_mask = - CKF_ENCRYPT | CKF_DECRYPT | CKF_SIGN | CKF_VERIFY | CKF_WRAP | CKF_UNWRAP; - - // The private key must be marked as insensitive and extractable, otherwise it - // cannot later be exported in unencrypted form or structured-cloned. - const PK11AttrFlags attribute_flags = - PK11_ATTR_INSENSITIVE | PK11_ATTR_EXTRACTABLE; - - // Note: NSS does not generate an sec_public_key if the call below fails, - // so there is no danger of a leaked sec_public_key. - SECKEYPublicKey* sec_public_key = NULL; - crypto::ScopedSECKEYPrivateKey scoped_sec_private_key( - PK11_GenerateKeyPairWithOpFlags(slot.get(), - CKM_RSA_PKCS_KEY_PAIR_GEN, - &rsa_gen_params, - &sec_public_key, - attribute_flags, - operation_flags, - operation_flags_mask, - NULL)); - if (!scoped_sec_private_key) - return Status::OperationError(); - - blink::WebCryptoKeyAlgorithm key_algorithm; - if (!CreatePublicKeyAlgorithm(algorithm, sec_public_key, &key_algorithm)) - return Status::ErrorUnexpected(); - - scoped_ptr<PublicKey> public_key_handle; - Status status = PublicKey::Create( - crypto::ScopedSECKEYPublicKey(sec_public_key), &public_key_handle); - if (status.IsError()) - return status; - - scoped_ptr<PrivateKey> private_key_handle; - status = PrivateKey::Create( - scoped_sec_private_key.Pass(), key_algorithm, &private_key_handle); - if (status.IsError()) - return status; - - *public_key = blink::WebCryptoKey::create(public_key_handle.release(), - blink::WebCryptoKeyTypePublic, - true, - key_algorithm, - public_key_usage_mask); - *private_key = blink::WebCryptoKey::create(private_key_handle.release(), - blink::WebCryptoKeyTypePrivate, - extractable, - key_algorithm, - private_key_usage_mask); - - return Status::Success(); -} - -void Init() { - crypto::EnsureNSSInit(); -} - -Status DigestSha(blink::WebCryptoAlgorithmId algorithm, - const CryptoData& data, - std::vector<uint8>* buffer) { - DigestorNSS digestor(algorithm); - Status error = digestor.ConsumeWithStatus(data.bytes(), data.byte_length()); - // http://crbug.com/366427: the spec does not define any other failures for - // digest, so none of the subsequent errors are spec compliant. - if (!error.IsSuccess()) - return error; - return digestor.FinishWithVectorAndStatus(buffer); -} - -scoped_ptr<blink::WebCryptoDigestor> CreateDigestor( - blink::WebCryptoAlgorithmId algorithm_id) { - return scoped_ptr<blink::WebCryptoDigestor>(new DigestorNSS(algorithm_id)); -} - -Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - unsigned keylen_bytes, - blink::WebCryptoKey* key) { - CK_MECHANISM_TYPE mech = WebCryptoAlgorithmToGenMechanism(algorithm); - blink::WebCryptoKeyType key_type = blink::WebCryptoKeyTypeSecret; - - if (mech == CKM_INVALID_MECHANISM) - return Status::ErrorUnsupported(); - - crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); - if (!slot) - return Status::OperationError(); - - crypto::ScopedPK11SymKey pk11_key( - PK11_KeyGen(slot.get(), mech, NULL, keylen_bytes, NULL)); - - if (!pk11_key) - return Status::OperationError(); - - blink::WebCryptoKeyAlgorithm key_algorithm; - if (!CreateSecretKeyAlgorithm(algorithm, keylen_bytes, &key_algorithm)) - return Status::ErrorUnexpected(); - - scoped_ptr<SymKey> key_handle; - Status status = SymKey::Create(pk11_key.Pass(), &key_handle); - if (status.IsError()) - return status; - - *key = blink::WebCryptoKey::create( - key_handle.release(), key_type, extractable, key_algorithm, usage_mask); - return Status::Success(); -} - -Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - const CryptoData& modulus_data, - const CryptoData& exponent_data, - blink::WebCryptoKey* key) { - if (!modulus_data.byte_length()) - return Status::ErrorImportRsaEmptyModulus(); - - if (!exponent_data.byte_length()) - return Status::ErrorImportRsaEmptyExponent(); - - DCHECK(modulus_data.bytes()); - DCHECK(exponent_data.bytes()); - - // NSS does not provide a way to create an RSA public key directly from the - // modulus and exponent values, but it can import an DER-encoded ASN.1 blob - // with these values and create the public key from that. The code below - // follows the recommendation described in - // https://developer.mozilla.org/en-US/docs/NSS/NSS_Tech_Notes/nss_tech_note7 - - // Pack the input values into a struct compatible with NSS ASN.1 encoding, and - // set up an ASN.1 encoder template for it. - struct RsaPublicKeyData { - SECItem modulus; - SECItem exponent; - }; - const RsaPublicKeyData pubkey_in = { - {siUnsignedInteger, const_cast<unsigned char*>(modulus_data.bytes()), - modulus_data.byte_length()}, - {siUnsignedInteger, const_cast<unsigned char*>(exponent_data.bytes()), - exponent_data.byte_length()}}; - const SEC_ASN1Template rsa_public_key_template[] = { - {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(RsaPublicKeyData)}, - {SEC_ASN1_INTEGER, offsetof(RsaPublicKeyData, modulus), }, - {SEC_ASN1_INTEGER, offsetof(RsaPublicKeyData, exponent), }, - {0, }}; - - // DER-encode the public key. - crypto::ScopedSECItem pubkey_der( - SEC_ASN1EncodeItem(NULL, NULL, &pubkey_in, rsa_public_key_template)); - if (!pubkey_der) - return Status::OperationError(); - - // Import the DER-encoded public key to create an RSA SECKEYPublicKey. - crypto::ScopedSECKEYPublicKey pubkey( - SECKEY_ImportDERPublicKey(pubkey_der.get(), CKK_RSA)); - if (!pubkey) - return Status::OperationError(); - - blink::WebCryptoKeyAlgorithm key_algorithm; - if (!CreatePublicKeyAlgorithm(algorithm, pubkey.get(), &key_algorithm)) - return Status::ErrorUnexpected(); - - scoped_ptr<PublicKey> key_handle; - Status status = PublicKey::Create(pubkey.Pass(), &key_handle); - if (status.IsError()) - return status; - - *key = blink::WebCryptoKey::create(key_handle.release(), - blink::WebCryptoKeyTypePublic, - extractable, - key_algorithm, - usage_mask); - return Status::Success(); -} - -struct DestroyGenericObject { - void operator()(PK11GenericObject* o) const { - if (o) - PK11_DestroyGenericObject(o); - } -}; - -typedef scoped_ptr<PK11GenericObject, DestroyGenericObject> - ScopedPK11GenericObject; - -// Helper to add an attribute to a template. -void AddAttribute(CK_ATTRIBUTE_TYPE type, - void* value, - unsigned long length, - std::vector<CK_ATTRIBUTE>* templ) { - CK_ATTRIBUTE attribute = {type, value, length}; - templ->push_back(attribute); -} - -// Helper to optionally add an attribute to a template, if the provided data is -// non-empty. -void AddOptionalAttribute(CK_ATTRIBUTE_TYPE type, - const CryptoData& data, - std::vector<CK_ATTRIBUTE>* templ) { - if (!data.byte_length()) - return; - CK_ATTRIBUTE attribute = {type, const_cast<unsigned char*>(data.bytes()), - data.byte_length()}; - templ->push_back(attribute); -} - -Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - const CryptoData& modulus, - const CryptoData& public_exponent, - const CryptoData& private_exponent, - const CryptoData& prime1, - const CryptoData& prime2, - const CryptoData& exponent1, - const CryptoData& exponent2, - const CryptoData& coefficient, - blink::WebCryptoKey* key) { - Status status = NssSupportsKeyImport(algorithm.id()); - if (status.IsError()) - return status; - - CK_OBJECT_CLASS obj_class = CKO_PRIVATE_KEY; - CK_KEY_TYPE key_type = CKK_RSA; - CK_BBOOL ck_false = CK_FALSE; - - std::vector<CK_ATTRIBUTE> key_template; - - AddAttribute(CKA_CLASS, &obj_class, sizeof(obj_class), &key_template); - AddAttribute(CKA_KEY_TYPE, &key_type, sizeof(key_type), &key_template); - AddAttribute(CKA_TOKEN, &ck_false, sizeof(ck_false), &key_template); - AddAttribute(CKA_SENSITIVE, &ck_false, sizeof(ck_false), &key_template); - AddAttribute(CKA_PRIVATE, &ck_false, sizeof(ck_false), &key_template); - - // Required properties. - AddOptionalAttribute(CKA_MODULUS, modulus, &key_template); - AddOptionalAttribute(CKA_PUBLIC_EXPONENT, public_exponent, &key_template); - AddOptionalAttribute(CKA_PRIVATE_EXPONENT, private_exponent, &key_template); - - // Manufacture a CKA_ID so the created key can be retrieved later as a - // SECKEYPrivateKey using FindKeyByKeyID(). Unfortunately there isn't a more - // direct way to do this in NSS. - // - // For consistency with other NSS key creation methods, set the CKA_ID to - // PK11_MakeIDFromPubKey(). There are some problems with - // this approach: - // - // (1) Prior to NSS 3.16.2, there is no parameter validation when creating - // private keys. It is therefore possible to construct a key using the - // known public modulus, and where all the other parameters are bogus. - // FindKeyByKeyID() returns the first key matching the ID. So this would - // effectively allow an attacker to retrieve a private key of their - // choice. - // TODO(eroman): Once NSS rolls and this is fixed, disallow RSA key - // import on older versions of NSS. - // http://crbug.com/378315 - // - // (2) The ID space is shared by different key types. So theoretically - // possible to retrieve a key of the wrong type which has a matching - // CKA_ID. In practice I am told this is not likely except for small key - // sizes, since would require constructing keys with the same public - // data. - // - // (3) FindKeyByKeyID() doesn't necessarily return the object that was just - // created by CreateGenericObject. If the pre-existing key was - // provisioned with flags incompatible with WebCrypto (for instance - // marked sensitive) then this will break things. - SECItem modulus_item = MakeSECItemForBuffer(CryptoData(modulus)); - crypto::ScopedSECItem object_id(PK11_MakeIDFromPubKey(&modulus_item)); - AddOptionalAttribute( - CKA_ID, CryptoData(object_id->data, object_id->len), &key_template); - - // Optional properties (all of these will have been specified or none). - AddOptionalAttribute(CKA_PRIME_1, prime1, &key_template); - AddOptionalAttribute(CKA_PRIME_2, prime2, &key_template); - AddOptionalAttribute(CKA_EXPONENT_1, exponent1, &key_template); - AddOptionalAttribute(CKA_EXPONENT_2, exponent2, &key_template); - AddOptionalAttribute(CKA_COEFFICIENT, coefficient, &key_template); - - crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); - - ScopedPK11GenericObject key_object(PK11_CreateGenericObject( - slot.get(), &key_template[0], key_template.size(), PR_FALSE)); - - if (!key_object) - return Status::OperationError(); - - crypto::ScopedSECKEYPrivateKey private_key_tmp( - PK11_FindKeyByKeyID(slot.get(), object_id.get(), NULL)); - - // PK11_FindKeyByKeyID() may return a handle to an existing key, rather than - // the object created by PK11_CreateGenericObject(). - crypto::ScopedSECKEYPrivateKey private_key( - SECKEY_CopyPrivateKey(private_key_tmp.get())); - - if (!private_key) - return Status::OperationError(); - - blink::WebCryptoKeyAlgorithm key_algorithm; - if (!CreatePrivateKeyAlgorithm(algorithm, private_key.get(), &key_algorithm)) - return Status::ErrorUnexpected(); - - scoped_ptr<PrivateKey> key_handle; - status = PrivateKey::Create(private_key.Pass(), key_algorithm, &key_handle); - if (status.IsError()) - return status; - - *key = blink::WebCryptoKey::create(key_handle.release(), - blink::WebCryptoKeyTypePrivate, - extractable, - key_algorithm, - usage_mask); - return Status::Success(); -} - -Status WrapSymKeyAesKw(PK11SymKey* key, - SymKey* wrapping_key, - std::vector<uint8>* buffer) { - // The data size must be at least 16 bytes and a multiple of 8 bytes. - // RFC 3394 does not specify a maximum allowed data length, but since only - // keys are being wrapped in this application (which are small), a reasonable - // max limit is whatever will fit into an unsigned. For the max size test, - // note that AES Key Wrap always adds 8 bytes to the input data size. - const unsigned int input_length = PK11_GetKeyLength(key); - DCHECK_GE(input_length, 16u); - DCHECK((input_length % 8) == 0); - if (input_length > UINT_MAX - 8) - return Status::ErrorDataTooLarge(); - - SECItem iv_item = MakeSECItemForBuffer(CryptoData(kAesIv, sizeof(kAesIv))); - crypto::ScopedSECItem param_item( - PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP, &iv_item)); - if (!param_item) - return Status::ErrorUnexpected(); - - const unsigned int output_length = input_length + 8; - buffer->resize(output_length); - SECItem wrapped_key_item = MakeSECItemForBuffer(CryptoData(*buffer)); - - if (SECSuccess != PK11_WrapSymKey(CKM_NSS_AES_KEY_WRAP, - param_item.get(), - wrapping_key->key(), - key, - &wrapped_key_item)) { - return Status::OperationError(); - } - if (output_length != wrapped_key_item.len) - return Status::ErrorUnexpected(); - - return Status::Success(); -} - -Status DecryptAesKw(SymKey* wrapping_key, - const CryptoData& data, - std::vector<uint8>* buffer) { - // Due to limitations in the NSS API for the AES-KW algorithm, |data| must be - // temporarily viewed as a symmetric key to be unwrapped (decrypted). - crypto::ScopedPK11SymKey decrypted; - Status status = DoUnwrapSymKeyAesKw( - data, wrapping_key, CKK_GENERIC_SECRET, 0, &decrypted); - if (status.IsError()) - return status; - - // Once the decrypt is complete, extract the resultant raw bytes from NSS and - // return them to the caller. - if (PK11_ExtractKeyValue(decrypted.get()) != SECSuccess) - return Status::OperationError(); - const SECItem* const key_data = PK11_GetKeyData(decrypted.get()); - if (!key_data) - return Status::OperationError(); - buffer->assign(key_data->data, key_data->data + key_data->len); - - return Status::Success(); -} - -Status EncryptAesKw(SymKey* wrapping_key, - const CryptoData& data, - std::vector<uint8>* buffer) { - // Due to limitations in the NSS API for the AES-KW algorithm, |data| must be - // temporarily viewed as a symmetric key to be wrapped (encrypted). - SECItem data_item = MakeSECItemForBuffer(data); - crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); - crypto::ScopedPK11SymKey data_as_sym_key(PK11_ImportSymKey(slot.get(), - CKK_GENERIC_SECRET, - PK11_OriginUnwrap, - CKA_SIGN, - &data_item, - NULL)); - if (!data_as_sym_key) - return Status::OperationError(); - - return WrapSymKeyAesKw(data_as_sym_key.get(), wrapping_key, buffer); -} - -Status EncryptDecryptAesKw(EncryptOrDecrypt mode, - SymKey* wrapping_key, - const CryptoData& data, - std::vector<uint8>* buffer) { - return mode == ENCRYPT ? EncryptAesKw(wrapping_key, data, buffer) - : DecryptAesKw(wrapping_key, data, buffer); -} - -} // namespace platform - -} // namespace webcrypto - -} // namespace content diff --git a/content/child/webcrypto/platform_crypto_openssl.cc b/content/child/webcrypto/platform_crypto_openssl.cc deleted file mode 100644 index 8314053..0000000 --- a/content/child/webcrypto/platform_crypto_openssl.cc +++ /dev/null @@ -1,591 +0,0 @@ -// 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/platform_crypto.h" - -#include <vector> -#include <openssl/aes.h> -#include <openssl/evp.h> -#include <openssl/hmac.h> -#include <openssl/rand.h> -#include <openssl/sha.h> - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "content/child/webcrypto/crypto_data.h" -#include "content/child/webcrypto/status.h" -#include "content/child/webcrypto/webcrypto_util.h" -#include "crypto/openssl_util.h" -#include "crypto/scoped_openssl_types.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" -#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" - -namespace content { - -namespace webcrypto { - -namespace platform { - -class SymKey : public Key { - public: - explicit SymKey(const CryptoData& key_data) - : key_(key_data.bytes(), key_data.bytes() + key_data.byte_length()) {} - - virtual SymKey* AsSymKey() OVERRIDE { return this; } - virtual PublicKey* AsPublicKey() OVERRIDE { return NULL; } - virtual PrivateKey* AsPrivateKey() OVERRIDE { return NULL; } - virtual bool ThreadSafeSerializeForClone( - blink::WebVector<uint8>* key_data) OVERRIDE { - key_data->assign(Uint8VectorStart(key_), key_.size()); - return true; - } - - const std::vector<unsigned char>& key() const { return key_; } - - private: - const std::vector<unsigned char> key_; - - DISALLOW_COPY_AND_ASSIGN(SymKey); -}; - -namespace { - -const EVP_CIPHER* GetAESCipherByKeyLength(unsigned int key_length_bytes) { - // OpenSSL supports AES CBC ciphers for only 2 key lengths: 128, 256 bits - switch (key_length_bytes) { - case 16: - return EVP_aes_128_cbc(); - case 32: - return EVP_aes_256_cbc(); - default: - return NULL; - } -} - -const EVP_MD* GetDigest(blink::WebCryptoAlgorithmId id) { - switch (id) { - case blink::WebCryptoAlgorithmIdSha1: - return EVP_sha1(); - case blink::WebCryptoAlgorithmIdSha256: - return EVP_sha256(); - case blink::WebCryptoAlgorithmIdSha384: - return EVP_sha384(); - case blink::WebCryptoAlgorithmIdSha512: - return EVP_sha512(); - default: - return NULL; - } -} - -// OpenSSL constants for EVP_CipherInit_ex(), do not change -enum CipherOperation { kDoDecrypt = 0, kDoEncrypt = 1 }; - -Status AesCbcEncryptDecrypt(EncryptOrDecrypt mode, - SymKey* key, - const CryptoData& iv, - const CryptoData& data, - std::vector<uint8>* buffer) { - CipherOperation cipher_operation = - (mode == ENCRYPT) ? kDoEncrypt : kDoDecrypt; - - if (data.byte_length() >= INT_MAX - AES_BLOCK_SIZE) { - // TODO(padolph): Handle this by chunking the input fed into OpenSSL. Right - // now it doesn't make much difference since the one-shot API would end up - // blowing out the memory and crashing anyway. - return Status::ErrorDataTooLarge(); - } - - // Note: PKCS padding is enabled by default - crypto::ScopedOpenSSL<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free>::Type context( - EVP_CIPHER_CTX_new()); - - if (!context.get()) - return Status::OperationError(); - - const EVP_CIPHER* const cipher = GetAESCipherByKeyLength(key->key().size()); - DCHECK(cipher); - - if (!EVP_CipherInit_ex(context.get(), - cipher, - NULL, - &key->key()[0], - iv.bytes(), - cipher_operation)) { - return Status::OperationError(); - } - - // According to the openssl docs, the amount of data written may be as large - // as (data_size + cipher_block_size - 1), constrained to a multiple of - // cipher_block_size. - unsigned int output_max_len = data.byte_length() + AES_BLOCK_SIZE - 1; - const unsigned remainder = output_max_len % AES_BLOCK_SIZE; - if (remainder != 0) - output_max_len += AES_BLOCK_SIZE - remainder; - DCHECK_GT(output_max_len, data.byte_length()); - - buffer->resize(output_max_len); - - unsigned char* const buffer_data = Uint8VectorStart(buffer); - - int output_len = 0; - if (!EVP_CipherUpdate(context.get(), - buffer_data, - &output_len, - data.bytes(), - data.byte_length())) - return Status::OperationError(); - int final_output_chunk_len = 0; - if (!EVP_CipherFinal_ex( - context.get(), buffer_data + output_len, &final_output_chunk_len)) { - return Status::OperationError(); - } - - const unsigned int final_output_len = - static_cast<unsigned int>(output_len) + - static_cast<unsigned int>(final_output_chunk_len); - DCHECK_LE(final_output_len, output_max_len); - - buffer->resize(final_output_len); - - return Status::Success(); -} - -} // namespace - -class DigestorOpenSSL : public blink::WebCryptoDigestor { - public: - explicit DigestorOpenSSL(blink::WebCryptoAlgorithmId algorithm_id) - : initialized_(false), - digest_context_(EVP_MD_CTX_create()), - algorithm_id_(algorithm_id) {} - - virtual bool consume(const unsigned char* data, unsigned int size) { - return ConsumeWithStatus(data, size).IsSuccess(); - } - - Status ConsumeWithStatus(const unsigned char* data, unsigned int size) { - crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - Status error = Init(); - if (!error.IsSuccess()) - return error; - - if (!EVP_DigestUpdate(digest_context_.get(), data, size)) - return Status::OperationError(); - - return Status::Success(); - } - - virtual bool finish(unsigned char*& result_data, - unsigned int& result_data_size) { - Status error = FinishInternal(result_, &result_data_size); - if (!error.IsSuccess()) - return false; - result_data = result_; - return true; - } - - Status FinishWithVectorAndStatus(std::vector<uint8>* result) { - const int hash_expected_size = EVP_MD_CTX_size(digest_context_.get()); - result->resize(hash_expected_size); - unsigned char* const hash_buffer = Uint8VectorStart(result); - unsigned int hash_buffer_size; // ignored - return FinishInternal(hash_buffer, &hash_buffer_size); - } - - private: - Status Init() { - if (initialized_) - return Status::Success(); - - const EVP_MD* digest_algorithm = GetDigest(algorithm_id_); - if (!digest_algorithm) - return Status::ErrorUnexpected(); - - if (!digest_context_.get()) - return Status::OperationError(); - - if (!EVP_DigestInit_ex(digest_context_.get(), digest_algorithm, NULL)) - return Status::OperationError(); - - initialized_ = true; - return Status::Success(); - } - - Status FinishInternal(unsigned char* result, unsigned int* result_size) { - crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - Status error = Init(); - if (!error.IsSuccess()) - return error; - - const int hash_expected_size = EVP_MD_CTX_size(digest_context_.get()); - if (hash_expected_size <= 0) - return Status::ErrorUnexpected(); - DCHECK_LE(hash_expected_size, EVP_MAX_MD_SIZE); - - if (!EVP_DigestFinal_ex(digest_context_.get(), result, result_size) || - static_cast<int>(*result_size) != hash_expected_size) - return Status::OperationError(); - - return Status::Success(); - } - - bool initialized_; - crypto::ScopedEVP_MD_CTX digest_context_; - blink::WebCryptoAlgorithmId algorithm_id_; - unsigned char result_[EVP_MAX_MD_SIZE]; -}; - -Status ExportKeyRaw(SymKey* key, std::vector<uint8>* buffer) { - *buffer = key->key(); - return Status::Success(); -} - -void Init() { crypto::EnsureOpenSSLInit(); } - -Status EncryptDecryptAesCbc(EncryptOrDecrypt mode, - SymKey* key, - const CryptoData& data, - const CryptoData& iv, - std::vector<uint8>* buffer) { - // TODO(eroman): inline the function here. - return AesCbcEncryptDecrypt(mode, key, iv, data, buffer); -} - -Status DigestSha(blink::WebCryptoAlgorithmId algorithm, - const CryptoData& data, - std::vector<uint8>* buffer) { - DigestorOpenSSL digestor(algorithm); - Status error = digestor.ConsumeWithStatus(data.bytes(), data.byte_length()); - if (!error.IsSuccess()) - return error; - return digestor.FinishWithVectorAndStatus(buffer); -} - -scoped_ptr<blink::WebCryptoDigestor> CreateDigestor( - blink::WebCryptoAlgorithmId algorithm_id) { - return scoped_ptr<blink::WebCryptoDigestor>( - new DigestorOpenSSL(algorithm_id)); -} - -Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - unsigned keylen_bytes, - blink::WebCryptoKey* key) { - // TODO(eroman): Is this right? - if (keylen_bytes == 0) - return Status::ErrorGenerateKeyLength(); - - crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - - std::vector<unsigned char> random_bytes(keylen_bytes, 0); - if (!(RAND_bytes(&random_bytes[0], keylen_bytes))) - return Status::OperationError(); - - blink::WebCryptoKeyAlgorithm key_algorithm; - if (!CreateSecretKeyAlgorithm(algorithm, keylen_bytes, &key_algorithm)) - return Status::ErrorUnexpected(); - - *key = blink::WebCryptoKey::create(new SymKey(CryptoData(random_bytes)), - blink::WebCryptoKeyTypeSecret, - extractable, - key_algorithm, - usage_mask); - - return Status::Success(); -} - -Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask public_key_usage_mask, - blink::WebCryptoKeyUsageMask private_key_usage_mask, - unsigned int modulus_length_bits, - unsigned long public_exponent, - blink::WebCryptoKey* public_key, - blink::WebCryptoKey* private_key) { - // TODO(padolph): Placeholder for OpenSSL implementation. - // Issue http://crbug.com/267888. - return Status::ErrorUnsupported(); -} - -Status ImportKeyRaw(const blink::WebCryptoAlgorithm& algorithm, - const CryptoData& key_data, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key) { - - blink::WebCryptoKeyAlgorithm key_algorithm; - if (!CreateSecretKeyAlgorithm( - algorithm, key_data.byte_length(), &key_algorithm)) - return Status::ErrorUnexpected(); - - *key = blink::WebCryptoKey::create(new SymKey(key_data), - blink::WebCryptoKeyTypeSecret, - extractable, - key_algorithm, - usage_mask); - - return Status::Success(); -} - -Status SignHmac(SymKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& data, - std::vector<uint8>* buffer) { - crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - - const EVP_MD* digest_algorithm = GetDigest(hash.id()); - if (!digest_algorithm) - return Status::ErrorUnsupported(); - unsigned int hmac_expected_length = EVP_MD_size(digest_algorithm); - - const std::vector<unsigned char>& raw_key = key->key(); - - // OpenSSL wierdness here. - // First, HMAC() needs a void* for the key data, so make one up front as a - // cosmetic to avoid a cast. Second, OpenSSL does not like a NULL key, - // which will result if the raw_key vector is empty; an entirely valid - // case. Handle this specific case by pointing to an empty array. - const unsigned char null_key[] = {}; - const void* const raw_key_voidp = raw_key.size() ? &raw_key[0] : null_key; - - buffer->resize(hmac_expected_length); - crypto::ScopedOpenSSLSafeSizeBuffer<EVP_MAX_MD_SIZE> hmac_result( - Uint8VectorStart(buffer), hmac_expected_length); - - unsigned int hmac_actual_length; - unsigned char* const success = HMAC(digest_algorithm, - raw_key_voidp, - raw_key.size(), - data.bytes(), - data.byte_length(), - hmac_result.safe_buffer(), - &hmac_actual_length); - if (!success || hmac_actual_length != hmac_expected_length) - return Status::OperationError(); - - return Status::Success(); -} - -Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - const CryptoData& modulus_data, - const CryptoData& exponent_data, - blink::WebCryptoKey* key) { - // TODO(padolph): Placeholder for OpenSSL implementation. - // Issue - return Status::ErrorUnsupported(); -} - -Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - const CryptoData& modulus, - const CryptoData& public_exponent, - const CryptoData& private_exponent, - const CryptoData& prime1, - const CryptoData& prime2, - const CryptoData& exponent1, - const CryptoData& exponent2, - const CryptoData& coefficient, - blink::WebCryptoKey* key) { - // TODO(eroman): http://crbug.com/267888 - return Status::ErrorUnsupported(); -} - -const EVP_AEAD* GetAesGcmAlgorithmFromKeySize(unsigned int key_size_bytes) { - switch (key_size_bytes) { - case 16: - return EVP_aead_aes_128_gcm(); - // TODO(eroman): Hook up 256-bit support when it is available. - default: - return NULL; - } -} - -Status EncryptDecryptAesGcm(EncryptOrDecrypt mode, - SymKey* key, - const CryptoData& data, - const CryptoData& iv, - const CryptoData& additional_data, - unsigned int tag_length_bits, - std::vector<uint8>* buffer) { - crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - - DCHECK(tag_length_bits % 8 == 0); - const unsigned int tag_length_bytes = tag_length_bits / 8; - - EVP_AEAD_CTX ctx; - - const EVP_AEAD* const aead_alg = - GetAesGcmAlgorithmFromKeySize(key->key().size()); - if (!aead_alg) - return Status::ErrorUnexpected(); - - if (!EVP_AEAD_CTX_init(&ctx, - aead_alg, - Uint8VectorStart(key->key()), - key->key().size(), - tag_length_bytes, - NULL)) { - return Status::OperationError(); - } - - crypto::ScopedOpenSSL<EVP_AEAD_CTX, EVP_AEAD_CTX_cleanup>::Type ctx_cleanup( - &ctx); - - size_t len; - int ok; - - if (mode == DECRYPT) { - if (data.byte_length() < tag_length_bytes) - return Status::ErrorDataTooSmall(); - - buffer->resize(data.byte_length() - tag_length_bytes); - - ok = EVP_AEAD_CTX_open(&ctx, - Uint8VectorStart(buffer), - &len, - buffer->size(), - iv.bytes(), - iv.byte_length(), - data.bytes(), - data.byte_length(), - additional_data.bytes(), - additional_data.byte_length()); - } else { - // No need to check for unsigned integer overflow here (seal fails if - // the output buffer is too small). - buffer->resize(data.byte_length() + tag_length_bytes); - - ok = EVP_AEAD_CTX_seal(&ctx, - Uint8VectorStart(buffer), - &len, - buffer->size(), - iv.bytes(), - iv.byte_length(), - data.bytes(), - data.byte_length(), - additional_data.bytes(), - additional_data.byte_length()); - } - - if (!ok) - return Status::OperationError(); - buffer->resize(len); - return Status::Success(); -} - -Status EncryptRsaOaep(PublicKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& label, - const CryptoData& data, - std::vector<uint8>* buffer) { - // TODO(eroman): http://crbug.com/267888 - return Status::ErrorUnsupported(); -} - -Status DecryptRsaOaep(PrivateKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& label, - const CryptoData& data, - std::vector<uint8>* buffer) { - // TODO(eroman): http://crbug.com/267888 - return Status::ErrorUnsupported(); -} - -Status SignRsaSsaPkcs1v1_5(PrivateKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& data, - std::vector<uint8>* buffer) { - // TODO(eroman): http://crbug.com/267888 - return Status::ErrorUnsupported(); -} - -// Key is guaranteed to be an RSA SSA key. -Status VerifyRsaSsaPkcs1v1_5(PublicKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& signature, - const CryptoData& data, - bool* signature_match) { - // TODO(eroman): http://crbug.com/267888 - return Status::ErrorUnsupported(); -} - -Status ImportKeySpki(const blink::WebCryptoAlgorithm& algorithm, - const CryptoData& key_data, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key) { - // TODO(eroman): http://crbug.com/267888 - return Status::ErrorUnsupported(); -} - -Status ImportKeyPkcs8(const blink::WebCryptoAlgorithm& algorithm, - const CryptoData& key_data, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key) { - // TODO(eroman): http://crbug.com/267888 - return Status::ErrorUnsupported(); -} - -Status ExportKeySpki(PublicKey* key, std::vector<uint8>* buffer) { - // TODO(eroman): http://crbug.com/267888 - return Status::ErrorUnsupported(); -} - -Status ExportKeyPkcs8(PrivateKey* key, - const blink::WebCryptoKeyAlgorithm& key_algorithm, - std::vector<uint8>* buffer) { - // TODO(eroman): http://crbug.com/267888 - return Status::ErrorUnsupported(); -} - -Status ExportRsaPublicKey(PublicKey* key, - std::vector<uint8>* modulus, - std::vector<uint8>* public_exponent) { - // TODO(eroman): http://crbug.com/267888 - return Status::ErrorUnsupported(); -} - -Status ExportRsaPrivateKey(PrivateKey* key, - std::vector<uint8>* modulus, - std::vector<uint8>* public_exponent, - std::vector<uint8>* private_exponent, - std::vector<uint8>* prime1, - std::vector<uint8>* prime2, - std::vector<uint8>* exponent1, - std::vector<uint8>* exponent2, - std::vector<uint8>* coefficient) { - // TODO(eroman): http://crbug.com/267888 - return Status::ErrorUnsupported(); -} - -Status EncryptDecryptAesKw(EncryptOrDecrypt mode, - SymKey* key, - const CryptoData& data, - std::vector<uint8>* buffer) { - // TODO(eroman): http://crbug.com/267888 - return Status::ErrorUnsupported(); -} - -bool ThreadSafeDeserializeKeyForClone( - const blink::WebCryptoKeyAlgorithm& algorithm, - blink::WebCryptoKeyType type, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - const CryptoData& key_data, - blink::WebCryptoKey* key) { - // TODO(eroman): http://crbug.com/267888 - return false; -} - -} // namespace platform - -} // namespace webcrypto - -} // namespace content diff --git a/content/child/webcrypto/shared_crypto.cc b/content/child/webcrypto/shared_crypto.cc deleted file mode 100644 index 03804ab..0000000 --- a/content/child/webcrypto/shared_crypto.cc +++ /dev/null @@ -1,944 +0,0 @@ -// 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/shared_crypto.h" - -#include "base/logging.h" -#include "content/child/webcrypto/crypto_data.h" -#include "content/child/webcrypto/jwk.h" -#include "content/child/webcrypto/platform_crypto.h" -#include "content/child/webcrypto/status.h" -#include "content/child/webcrypto/webcrypto_util.h" -#include "crypto/secure_util.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" -#include "third_party/WebKit/public/platform/WebCryptoKey.h" -#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" - -namespace content { - -namespace webcrypto { - -// ------------ -// Threading: -// ------------ -// -// All functions in this file are called from the webcrypto worker pool except -// for: -// -// * SerializeKeyForClone() -// * DeserializeKeyForClone() -// * ImportKey() // TODO(eroman): Change this. - -namespace { - -// TODO(eroman): Move this helper to WebCryptoKey. -bool KeyUsageAllows(const blink::WebCryptoKey& key, - const blink::WebCryptoKeyUsage usage) { - return ((key.usages() & usage) != 0); -} - -bool IsValidAesKeyLengthBits(unsigned int length_bits) { - // 192-bit AES is disallowed. - return length_bits == 128 || length_bits == 256; -} - -bool IsValidAesKeyLengthBytes(unsigned int length_bytes) { - // 192-bit AES is disallowed. - return length_bytes == 16 || length_bytes == 32; -} - -const size_t kAesBlockSizeBytes = 16; - -Status EncryptDecryptAesCbc(EncryptOrDecrypt mode, - const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8>* buffer) { - platform::SymKey* sym_key; - Status status = ToPlatformSymKey(key, &sym_key); - if (status.IsError()) - return status; - - const blink::WebCryptoAesCbcParams* params = algorithm.aesCbcParams(); - if (!params) - return Status::ErrorUnexpected(); - - CryptoData iv(params->iv().data(), params->iv().size()); - if (iv.byte_length() != kAesBlockSizeBytes) - return Status::ErrorIncorrectSizeAesCbcIv(); - - return platform::EncryptDecryptAesCbc(mode, sym_key, data, iv, buffer); -} - -Status EncryptDecryptAesGcm(EncryptOrDecrypt mode, - const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8>* buffer) { - platform::SymKey* sym_key; - Status status = ToPlatformSymKey(key, &sym_key); - if (status.IsError()) - return status; - - const blink::WebCryptoAesGcmParams* params = algorithm.aesGcmParams(); - if (!params) - return Status::ErrorUnexpected(); - - unsigned int tag_length_bits = 128; - if (params->hasTagLengthBits()) - tag_length_bits = params->optionalTagLengthBits(); - - if (tag_length_bits != 32 && tag_length_bits != 64 && tag_length_bits != 96 && - tag_length_bits != 104 && tag_length_bits != 112 && - tag_length_bits != 120 && tag_length_bits != 128) - return Status::ErrorInvalidAesGcmTagLength(); - - return platform::EncryptDecryptAesGcm( - mode, - sym_key, - data, - CryptoData(params->iv()), - CryptoData(params->optionalAdditionalData()), - tag_length_bits, - buffer); -} - -Status EncryptRsaOaep(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8>* buffer) { - platform::PublicKey* public_key; - Status status = ToPlatformPublicKey(key, &public_key); - if (status.IsError()) - return status; - - const blink::WebCryptoRsaOaepParams* params = algorithm.rsaOaepParams(); - if (!params) - return Status::ErrorUnexpected(); - - return platform::EncryptRsaOaep(public_key, - key.algorithm().rsaHashedParams()->hash(), - CryptoData(params->optionalLabel()), - data, - buffer); -} - -Status DecryptRsaOaep(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8>* buffer) { - platform::PrivateKey* private_key; - Status status = ToPlatformPrivateKey(key, &private_key); - if (status.IsError()) - return status; - - const blink::WebCryptoRsaOaepParams* params = algorithm.rsaOaepParams(); - if (!params) - return Status::ErrorUnexpected(); - - return platform::DecryptRsaOaep(private_key, - key.algorithm().rsaHashedParams()->hash(), - CryptoData(params->optionalLabel()), - data, - buffer); -} - -Status SignHmac(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8>* buffer) { - platform::SymKey* sym_key; - Status status = ToPlatformSymKey(key, &sym_key); - if (status.IsError()) - return status; - - return platform::SignHmac( - sym_key, key.algorithm().hmacParams()->hash(), data, buffer); -} - -Status VerifyHmac(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& signature, - const CryptoData& data, - bool* signature_match) { - std::vector<uint8> result; - Status status = SignHmac(algorithm, key, data, &result); - if (status.IsError()) - return status; - - // Do not allow verification of truncated MACs. - *signature_match = - result.size() == signature.byte_length() && - crypto::SecureMemEqual( - Uint8VectorStart(result), signature.bytes(), signature.byte_length()); - - return Status::Success(); -} - -Status SignRsaSsaPkcs1v1_5(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8>* buffer) { - platform::PrivateKey* private_key; - Status status = ToPlatformPrivateKey(key, &private_key); - if (status.IsError()) - return status; - - return platform::SignRsaSsaPkcs1v1_5( - private_key, key.algorithm().rsaHashedParams()->hash(), data, buffer); -} - -Status VerifyRsaSsaPkcs1v1_5(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& signature, - const CryptoData& data, - bool* signature_match) { - platform::PublicKey* public_key; - Status status = ToPlatformPublicKey(key, &public_key); - if (status.IsError()) - return status; - - return platform::VerifyRsaSsaPkcs1v1_5( - public_key, - key.algorithm().rsaHashedParams()->hash(), - signature, - data, - signature_match); -} - -// Note that this function may be called from the target Blink thread. -Status ImportKeyRaw(const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key) { - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdAesCtr: - case blink::WebCryptoAlgorithmIdAesCbc: - case blink::WebCryptoAlgorithmIdAesGcm: - case blink::WebCryptoAlgorithmIdAesKw: - if (!IsValidAesKeyLengthBytes(key_data.byte_length())) { - return key_data.byte_length() == 24 - ? Status::ErrorAes192BitUnsupported() - : Status::ErrorImportAesKeyLength(); - } - // Fallthrough intentional! - case blink::WebCryptoAlgorithmIdHmac: - return platform::ImportKeyRaw( - algorithm, key_data, extractable, usage_mask, key); - default: - return Status::ErrorUnsupported(); - } -} - -// Returns the key format to use for structured cloning. -blink::WebCryptoKeyFormat GetCloneFormatForKeyType( - blink::WebCryptoKeyType type) { - switch (type) { - case blink::WebCryptoKeyTypeSecret: - return blink::WebCryptoKeyFormatRaw; - case blink::WebCryptoKeyTypePublic: - return blink::WebCryptoKeyFormatSpki; - case blink::WebCryptoKeyTypePrivate: - return blink::WebCryptoKeyFormatPkcs8; - } - - NOTREACHED(); - return blink::WebCryptoKeyFormatRaw; -} - -// Converts a KeyAlgorithm into an equivalent Algorithm for import. -blink::WebCryptoAlgorithm KeyAlgorithmToImportAlgorithm( - const blink::WebCryptoKeyAlgorithm& algorithm) { - switch (algorithm.paramsType()) { - case blink::WebCryptoKeyAlgorithmParamsTypeAes: - return CreateAlgorithm(algorithm.id()); - case blink::WebCryptoKeyAlgorithmParamsTypeHmac: - return CreateHmacImportAlgorithm(algorithm.hmacParams()->hash().id()); - case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed: - return CreateRsaHashedImportAlgorithm( - algorithm.id(), algorithm.rsaHashedParams()->hash().id()); - case blink::WebCryptoKeyAlgorithmParamsTypeNone: - break; - default: - break; - } - return blink::WebCryptoAlgorithm::createNull(); -} - -// There is some duplicated information in the serialized format used by -// structured clone (since the KeyAlgorithm is serialized separately from the -// key data). Use this extra information to further validate what was -// deserialized from the key data. -// -// A failure here implies either a bug in the code, or that the serialized data -// was corrupted. -bool ValidateDeserializedKey(const blink::WebCryptoKey& key, - const blink::WebCryptoKeyAlgorithm& algorithm, - blink::WebCryptoKeyType type) { - if (algorithm.id() != key.algorithm().id()) - return false; - - if (key.type() != type) - return false; - - switch (algorithm.paramsType()) { - case blink::WebCryptoKeyAlgorithmParamsTypeAes: - if (algorithm.aesParams()->lengthBits() != - key.algorithm().aesParams()->lengthBits()) - return false; - break; - case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed: - if (algorithm.rsaHashedParams()->modulusLengthBits() != - key.algorithm().rsaHashedParams()->modulusLengthBits()) - return false; - if (algorithm.rsaHashedParams()->publicExponent().size() != - key.algorithm().rsaHashedParams()->publicExponent().size()) - return false; - if (memcmp(algorithm.rsaHashedParams()->publicExponent().data(), - key.algorithm().rsaHashedParams()->publicExponent().data(), - key.algorithm().rsaHashedParams()->publicExponent().size()) != - 0) - return false; - break; - case blink::WebCryptoKeyAlgorithmParamsTypeNone: - case blink::WebCryptoKeyAlgorithmParamsTypeHmac: - break; - default: - return false; - } - - return true; -} - -Status EncryptDecryptAesKw(EncryptOrDecrypt mode, - const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8>* buffer) { - platform::SymKey* sym_key; - Status status = ToPlatformSymKey(key, &sym_key); - if (status.IsError()) - return status; - - unsigned int min_length = mode == ENCRYPT ? 16 : 24; - - if (data.byte_length() < min_length) - return Status::ErrorDataTooSmall(); - if (data.byte_length() % 8) - return Status::ErrorInvalidAesKwDataLength(); - - if (status.IsError()) - return status; - return platform::EncryptDecryptAesKw(mode, sym_key, data, buffer); -} - -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(); - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdAesCbc: - return EncryptDecryptAesCbc(DECRYPT, algorithm, key, data, buffer); - case blink::WebCryptoAlgorithmIdAesGcm: - return EncryptDecryptAesGcm(DECRYPT, algorithm, key, data, buffer); - case blink::WebCryptoAlgorithmIdRsaOaep: - return DecryptRsaOaep(algorithm, key, data, buffer); - case blink::WebCryptoAlgorithmIdAesKw: - return EncryptDecryptAesKw(DECRYPT, algorithm, key, data, buffer); - default: - return Status::ErrorUnsupported(); - } -} - -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(); - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdAesCbc: - return EncryptDecryptAesCbc(ENCRYPT, algorithm, key, data, buffer); - case blink::WebCryptoAlgorithmIdAesGcm: - return EncryptDecryptAesGcm(ENCRYPT, algorithm, key, data, buffer); - case blink::WebCryptoAlgorithmIdAesKw: - return EncryptDecryptAesKw(ENCRYPT, algorithm, key, data, buffer); - case blink::WebCryptoAlgorithmIdRsaOaep: - return EncryptRsaOaep(algorithm, key, data, buffer); - default: - return Status::ErrorUnsupported(); - } -} - -Status UnwrapKeyDecryptAndImport( - 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) { - std::vector<uint8> buffer; - Status 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); -} - -Status WrapKeyExportAndEncrypt( - blink::WebCryptoKeyFormat format, - const blink::WebCryptoKey& key_to_wrap, - const blink::WebCryptoKey& wrapping_key, - const blink::WebCryptoAlgorithm& wrapping_algorithm, - std::vector<uint8>* buffer) { - 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); -} - -// Returns the internal block size for SHA-* -unsigned int ShaBlockSizeBytes(blink::WebCryptoAlgorithmId hash_id) { - switch (hash_id) { - case blink::WebCryptoAlgorithmIdSha1: - case blink::WebCryptoAlgorithmIdSha256: - return 64; - case blink::WebCryptoAlgorithmIdSha384: - case blink::WebCryptoAlgorithmIdSha512: - return 128; - default: - NOTREACHED(); - return 0; - } -} - -// Returns the mask of all key usages that are possible for |algorithm| and -// |key_type|. If the combination of |algorithm| and |key_type| doesn't make -// sense, then returns 0 (no usages). -blink::WebCryptoKeyUsageMask GetValidKeyUsagesForKeyType( - blink::WebCryptoAlgorithmId algorithm, - blink::WebCryptoKeyType key_type) { - if (IsAlgorithmAsymmetric(algorithm) == - (key_type == blink::WebCryptoKeyTypeSecret)) - return 0; - - switch (algorithm) { - case blink::WebCryptoAlgorithmIdAesCbc: - case blink::WebCryptoAlgorithmIdAesGcm: - case blink::WebCryptoAlgorithmIdAesCtr: - return blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | - blink::WebCryptoKeyUsageWrapKey | - blink::WebCryptoKeyUsageUnwrapKey; - case blink::WebCryptoAlgorithmIdAesKw: - return blink::WebCryptoKeyUsageWrapKey | - blink::WebCryptoKeyUsageUnwrapKey; - case blink::WebCryptoAlgorithmIdHmac: - return blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; - case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: - switch (key_type) { - case blink::WebCryptoKeyTypePublic: - return blink::WebCryptoKeyUsageVerify; - case blink::WebCryptoKeyTypePrivate: - return blink::WebCryptoKeyUsageSign; - default: - return 0; - } - case blink::WebCryptoAlgorithmIdRsaOaep: - switch (key_type) { - case blink::WebCryptoKeyTypePublic: - return blink::WebCryptoKeyUsageEncrypt | - blink::WebCryptoKeyUsageWrapKey; - case blink::WebCryptoKeyTypePrivate: - return blink::WebCryptoKeyUsageDecrypt | - blink::WebCryptoKeyUsageUnwrapKey; - default: - return 0; - } - default: - return 0; - } -} - -// Returns Status::Success() if |usages| is a valid set of key usages for -// |algorithm| and |key_type|. Otherwise returns an error. -// In the case of JWK format the check is incomplete for asymmetric algorithms. -Status BestEffortCheckKeyUsagesForImport(blink::WebCryptoAlgorithmId algorithm, - blink::WebCryptoKeyFormat format, - blink::WebCryptoKeyUsageMask usages) { - if (!IsAlgorithmAsymmetric(algorithm)) - return CheckKeyUsages(algorithm, blink::WebCryptoKeyTypeSecret, usages); - - // Try to infer the key type given the import format. - switch (format) { - case blink::WebCryptoKeyFormatRaw: - // TODO(eroman): The spec defines Diffie-Hellman raw import for public - // keys, so this will need to be updated in the future when DH is - // implemented. - return Status::ErrorUnexpected(); - case blink::WebCryptoKeyFormatSpki: - return CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePublic, usages); - case blink::WebCryptoKeyFormatPkcs8: - return CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePrivate, usages); - case blink::WebCryptoKeyFormatJwk: - break; - default: - return Status::ErrorUnexpected(); - } - - // If the key type is not known, then the algorithm is asymmetric. Whether the - // key data describes a public or private key isn't known yet. But it must at - // least be ONE of those two. - DCHECK(IsAlgorithmAsymmetric(algorithm)); - - if (CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePublic, usages) - .IsError() && - CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePrivate, usages) - .IsError()) { - return Status::ErrorCreateKeyBadUsages(); - } - - return Status::Success(); -} - -// Returns an error if |combined_usage_mask| is invalid for generating a key -// pair for |algorithm|. Otherwise returns Status::Success(), and fills -// |public_key_usages| with the usages for the public key, and -// |private_key_usages| with those for the private key. -Status CheckKeyUsagesForGenerateKeyPair( - blink::WebCryptoAlgorithmId algorithm, - blink::WebCryptoKeyUsageMask combined_usage_mask, - blink::WebCryptoKeyUsageMask* public_key_usages, - blink::WebCryptoKeyUsageMask* private_key_usages) { - DCHECK(IsAlgorithmAsymmetric(algorithm)); - - blink::WebCryptoKeyUsageMask all_public_key_usages = - GetValidKeyUsagesForKeyType(algorithm, blink::WebCryptoKeyTypePublic); - blink::WebCryptoKeyUsageMask all_private_key_usages = - GetValidKeyUsagesForKeyType(algorithm, blink::WebCryptoKeyTypePrivate); - - if (!ContainsKeyUsages(all_public_key_usages | all_private_key_usages, - combined_usage_mask)) - return Status::ErrorCreateKeyBadUsages(); - - *public_key_usages = combined_usage_mask & all_public_key_usages; - *private_key_usages = combined_usage_mask & all_private_key_usages; - - return Status::Success(); -} - -// Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros, -// to unsigned long. -bool BigIntegerToLong(const uint8* data, - unsigned int data_size, - unsigned long* result) { - // TODO(padolph): Is it correct to say that empty data is an error, or does it - // mean value 0? See https://www.w3.org/Bugs/Public/show_bug.cgi?id=23655 - if (data_size == 0) - return false; - - *result = 0; - for (size_t i = 0; i < data_size; ++i) { - size_t reverse_i = data_size - i - 1; - - if (reverse_i >= sizeof(unsigned long) && data[i]) - return false; // Too large for a long. - - *result |= data[i] << 8 * reverse_i; - } - return true; -} - - -} // namespace - -void Init() { platform::Init(); } - -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) { - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdSha1: - case blink::WebCryptoAlgorithmIdSha256: - case blink::WebCryptoAlgorithmIdSha384: - case blink::WebCryptoAlgorithmIdSha512: - return platform::DigestSha(algorithm.id(), data, buffer); - default: - return Status::ErrorUnsupported(); - } -} - -scoped_ptr<blink::WebCryptoDigestor> CreateDigestor( - blink::WebCryptoAlgorithmId algorithm) { - return platform::CreateDigestor(algorithm); -} - -Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key) { - Status status = - CheckKeyUsages(algorithm.id(), blink::WebCryptoKeyTypeSecret, usage_mask); - if (status.IsError()) - return status; - - unsigned int keylen_bytes = 0; - - // Get the secret key length in bytes from generation parameters. - // This resolves any defaults. - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdAesCbc: - case blink::WebCryptoAlgorithmIdAesGcm: - case blink::WebCryptoAlgorithmIdAesKw: { - if (!IsValidAesKeyLengthBits(algorithm.aesKeyGenParams()->lengthBits())) { - return algorithm.aesKeyGenParams()->lengthBits() == 192 - ? Status::ErrorAes192BitUnsupported() - : Status::ErrorGenerateKeyLength(); - } - keylen_bytes = algorithm.aesKeyGenParams()->lengthBits() / 8; - break; - } - case blink::WebCryptoAlgorithmIdHmac: { - const blink::WebCryptoHmacKeyGenParams* params = - algorithm.hmacKeyGenParams(); - DCHECK(params); - if (params->hasLengthBits()) { - if (params->optionalLengthBits() % 8) - return Status::ErrorGenerateKeyLength(); - keylen_bytes = params->optionalLengthBits() / 8; - } else { - keylen_bytes = ShaBlockSizeBytes(params->hash().id()); - if (keylen_bytes == 0) - return Status::ErrorUnsupported(); - } - break; - } - - default: - return Status::ErrorUnsupported(); - } - - // TODO(eroman): Is this correct? HMAC can import zero-length keys, so should - // probably be able to allowed to generate them too. - if (keylen_bytes == 0) - return Status::ErrorGenerateKeyLength(); - - return platform::GenerateSecretKey( - algorithm, extractable, usage_mask, keylen_bytes, key); -} - -Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask combined_usage_mask, - blink::WebCryptoKey* public_key, - blink::WebCryptoKey* private_key) { - blink::WebCryptoKeyUsageMask public_key_usage_mask = 0; - blink::WebCryptoKeyUsageMask private_key_usage_mask = 0; - - Status status = CheckKeyUsagesForGenerateKeyPair(algorithm.id(), - combined_usage_mask, - &public_key_usage_mask, - &private_key_usage_mask); - if (status.IsError()) - return status; - - // TODO(padolph): Handle other asymmetric algorithm key generation. - switch (algorithm.paramsType()) { - case blink::WebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams: { - const blink::WebCryptoRsaHashedKeyGenParams* params = - algorithm.rsaHashedKeyGenParams(); - - if (!params->modulusLengthBits()) - return Status::ErrorGenerateRsaZeroModulus(); - - unsigned long public_exponent = 0; - if (!BigIntegerToLong(params->publicExponent().data(), - params->publicExponent().size(), - &public_exponent) || - (public_exponent != 3 && public_exponent != 65537)) { - return Status::ErrorGenerateKeyPublicExponent(); - } - - return platform::GenerateRsaKeyPair(algorithm, - extractable, - public_key_usage_mask, - private_key_usage_mask, - params->modulusLengthBits(), - public_exponent, - public_key, - private_key); - } - default: - return Status::ErrorUnsupported(); - } -} - -// 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) { - // This is "best effort" because it is incomplete for JWK (for which the key - // type is not yet known). ImportKeyJwk() does extra checks on key usage once - // the key type has been determined. - Status status = - BestEffortCheckKeyUsagesForImport(algorithm.id(), format, usage_mask); - if (status.IsError()) - return status; - - switch (format) { - case blink::WebCryptoKeyFormatRaw: - return ImportKeyRaw(key_data, algorithm, extractable, usage_mask, key); - case blink::WebCryptoKeyFormatSpki: - return platform::ImportKeySpki( - algorithm, key_data, extractable, usage_mask, key); - case blink::WebCryptoKeyFormatPkcs8: - return platform::ImportKeyPkcs8( - algorithm, key_data, extractable, usage_mask, key); - case blink::WebCryptoKeyFormatJwk: - return ImportKeyJwk(key_data, algorithm, extractable, usage_mask, key); - default: - return Status::ErrorUnsupported(); - } -} - -// TODO(eroman): Move this to anonymous namespace. -Status ExportKeyDontCheckExtractability(blink::WebCryptoKeyFormat format, - const blink::WebCryptoKey& key, - std::vector<uint8>* buffer) { - switch (format) { - case blink::WebCryptoKeyFormatRaw: { - platform::SymKey* sym_key; - Status status = ToPlatformSymKey(key, &sym_key); - if (status.IsError()) - return status; - return platform::ExportKeyRaw(sym_key, buffer); - } - case blink::WebCryptoKeyFormatSpki: { - platform::PublicKey* public_key; - Status status = ToPlatformPublicKey(key, &public_key); - if (status.IsError()) - return status; - return platform::ExportKeySpki(public_key, buffer); - } - case blink::WebCryptoKeyFormatPkcs8: { - platform::PrivateKey* private_key; - Status status = ToPlatformPrivateKey(key, &private_key); - if (status.IsError()) - return status; - return platform::ExportKeyPkcs8(private_key, key.algorithm(), buffer); - } - case blink::WebCryptoKeyFormatJwk: - return ExportKeyJwk(key, buffer); - 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(); - - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdHmac: - return SignHmac(algorithm, key, data, buffer); - case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: - return SignRsaSsaPkcs1v1_5(algorithm, key, data, buffer); - default: - return Status::ErrorUnsupported(); - } -} - -Status VerifySignature(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(); - - 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(); - } - - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdHmac: - return VerifyHmac(algorithm, key, signature, data, signature_match); - case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: - return VerifyRsaSsaPkcs1v1_5( - algorithm, key, signature, data, signature_match); - default: - return Status::ErrorUnsupported(); - } -} - -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(); - if (wrapping_algorithm.id() != wrapping_key.algorithm().id()) - return Status::ErrorUnexpected(); - - return WrapKeyExportAndEncrypt( - format, key_to_wrap, wrapping_key, wrapping_algorithm, 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 key usages don't make sense. This avoids decrypting the - // key only to then have import fail. It is "best effort" because when - // unwrapping JWK for asymmetric algorithms the key type isn't known yet. - Status status = - BestEffortCheckKeyUsagesForImport(algorithm.id(), format, usage_mask); - if (status.IsError()) - return status; - - return UnwrapKeyDecryptAndImport(format, - wrapped_key_data, - wrapping_key, - wrapping_algorithm, - algorithm, - extractable, - usage_mask, - key); -} - -// Note that this function is called from the target Blink thread. -bool SerializeKeyForClone(const blink::WebCryptoKey& key, - blink::WebVector<uint8>* key_data) { - return static_cast<webcrypto::platform::Key*>(key.handle()) - ->ThreadSafeSerializeForClone(key_data); -} - -// Note that this function is called from the target Blink thread. -bool DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, - blink::WebCryptoKeyType type, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - const CryptoData& key_data, - blink::WebCryptoKey* key) { - // TODO(eroman): This should not call into the platform crypto layer. - // Otherwise it runs the risk of stalling while the NSS/OpenSSL global locks - // are held. - // - // An alternate approach is to defer the key import until the key is used. - // However this means that any deserialization errors would have to be - // surfaced as WebCrypto errors, leading to slightly different behaviors. For - // instance you could clone a key which fails to be deserialized. - Status status = ImportKey(GetCloneFormatForKeyType(type), - key_data, - KeyAlgorithmToImportAlgorithm(algorithm), - extractable, - usage_mask, - key); - if (status.IsError()) - return false; - return ValidateDeserializedKey(*key, algorithm, type); -} - -Status ToPlatformSymKey(const blink::WebCryptoKey& key, - platform::SymKey** out) { - *out = static_cast<platform::Key*>(key.handle())->AsSymKey(); - if (!*out) - return Status::ErrorUnexpectedKeyType(); - return Status::Success(); -} - -Status ToPlatformPublicKey(const blink::WebCryptoKey& key, - platform::PublicKey** out) { - *out = static_cast<platform::Key*>(key.handle())->AsPublicKey(); - if (!*out) - return Status::ErrorUnexpectedKeyType(); - return Status::Success(); -} - -Status ToPlatformPrivateKey(const blink::WebCryptoKey& key, - platform::PrivateKey** out) { - *out = static_cast<platform::Key*>(key.handle())->AsPrivateKey(); - if (!*out) - return Status::ErrorUnexpectedKeyType(); - return Status::Success(); -} - -// Returns Status::Success() if |usages| is a valid set of key usages for -// |algorithm| and |key_type|. Otherwise returns an error. -Status CheckKeyUsages(blink::WebCryptoAlgorithmId algorithm, - blink::WebCryptoKeyType key_type, - blink::WebCryptoKeyUsageMask usages) { - if (!ContainsKeyUsages(GetValidKeyUsagesForKeyType(algorithm, key_type), - usages)) - return Status::ErrorCreateKeyBadUsages(); - - return Status::Success(); -} - -} // namespace webcrypto - -} // namespace content diff --git a/content/child/webcrypto/shared_crypto.h b/content/child/webcrypto/shared_crypto.h deleted file mode 100644 index d9e5a95..0000000 --- a/content/child/webcrypto/shared_crypto.h +++ /dev/null @@ -1,187 +0,0 @@ -// 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. - -#ifndef CONTENT_CHILD_WEBCRYPTO_SHARED_CRYPTO_H_ -#define CONTENT_CHILD_WEBCRYPTO_SHARED_CRYPTO_H_ - -#include <vector> - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/memory/scoped_ptr.h" -#include "content/common/content_export.h" -#include "third_party/WebKit/public/platform/WebCrypto.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" - -namespace content { - -namespace webcrypto { - -class CryptoData; -class Status; - -// Do one-time initialization. It is safe to call this multiple times. -// May be called concurrently from multiple threads. -CONTENT_EXPORT void Init(); - -// The functions exported by shared_crypto.h provide a common entry point for -// synchronous crypto operations. -// -// Here is how the layer cake looks. -// -// Blink -// | -// ==============|========================== -// | -// content -// | -// | -// v -// WebCryptoImpl (Implements the blink::WebCrypto interface for -// | asynchronous completions; posts tasks to -// | the webcrypto worker pool to fulfill the request -// using the synchronous methods of shared_crypto.h) -// | -// | [shared_crypto_unittest.cc] -// | / -// | / (The blink::WebCrypto interface is not -// | / testable from the chromium side because -// | / the result object is not mockable. -// | / Tests are done on shared_crypto instead. -// V v -// [shared_crypto.h] (Exposes synchronous functions in the -// | webcrypto:: namespace. This does -// | common validations, infers default -// | parameters, and casts the algorithm -// | parameters to the right types) -// | -// V -// [platform_crypto.h] (Exposes functions in the webcrypto::platform -// | namespace) -// | -// | -// V -// [platform_crypto_{nss|openssl}.cc] (Implements using the platform crypto -// library) -// -// The shared_crypto.h functions are responsible for: -// -// * Validating the key usages -// * Inferring default parameters when not specified -// * Validating key exportability -// * Validating algorithm with key.algorithm -// * Converting the Blink key to a more specific platform::{PublicKey, -// PrivateKey, SymKey} and making sure it was the right type. -// * Validating alogorithm specific parameters (for instance, was the iv for -// AES-CBC 16 bytes). -// * Parse a JWK - -CONTENT_EXPORT Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8>* buffer); - -CONTENT_EXPORT Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8>* buffer); - -CONTENT_EXPORT Status Digest(const blink::WebCryptoAlgorithm& algorithm, - const CryptoData& data, - std::vector<uint8>* buffer); - -CONTENT_EXPORT scoped_ptr<blink::WebCryptoDigestor> CreateDigestor( - blink::WebCryptoAlgorithmId algorithm); - -CONTENT_EXPORT Status - GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key); - -CONTENT_EXPORT Status - GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* public_key, - blink::WebCryptoKey* private_key); - -CONTENT_EXPORT Status ImportKey(blink::WebCryptoKeyFormat format, - const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key); - -CONTENT_EXPORT Status ExportKey(blink::WebCryptoKeyFormat format, - const blink::WebCryptoKey& key, - std::vector<uint8>* buffer); - -CONTENT_EXPORT Status Sign(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8>* buffer); - -CONTENT_EXPORT Status - VerifySignature(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& signature, - const CryptoData& data, - bool* signature_match); - -CONTENT_EXPORT 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); - -CONTENT_EXPORT 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); - -// Called on the target Blink thread. -CONTENT_EXPORT bool SerializeKeyForClone(const blink::WebCryptoKey& key, - blink::WebVector<uint8>* key_data); - -// Called on the target Blink thread. -CONTENT_EXPORT bool DeserializeKeyForClone( - const blink::WebCryptoKeyAlgorithm& algorithm, - blink::WebCryptoKeyType type, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - const CryptoData& key_data, - blink::WebCryptoKey* key); - -namespace platform { -class SymKey; -class PublicKey; -class PrivateKey; -} - -Status ToPlatformSymKey(const blink::WebCryptoKey& key, platform::SymKey** out); - -Status ToPlatformPublicKey(const blink::WebCryptoKey& key, - platform::PublicKey** out); - -Status ToPlatformPrivateKey(const blink::WebCryptoKey& key, - platform::PrivateKey** out); - -// Returns Staus::Success() if |usages| is valid for |key_type| and |algorithm|. -// Otherwise returns a failure -Status CheckKeyUsages(blink::WebCryptoAlgorithmId algorithm, - blink::WebCryptoKeyType key_type, - blink::WebCryptoKeyUsageMask usages); - -} // namespace webcrypto - -} // namespace content - -#endif // CONTENT_CHILD_WEBCRYPTO_SHARED_CRYPTO_H_ diff --git a/content/child/webcrypto/shared_crypto_unittest.cc b/content/child/webcrypto/shared_crypto_unittest.cc index ea2c624..bb58112 100644 --- a/content/child/webcrypto/shared_crypto_unittest.cc +++ b/content/child/webcrypto/shared_crypto_unittest.cc @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/webcrypto/shared_crypto.h" - #include <algorithm> #include <string> #include <vector> @@ -18,6 +16,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" +#include "content/child/webcrypto/algorithm_dispatch.h" #include "content/child/webcrypto/crypto_data.h" #include "content/child/webcrypto/status.h" #include "content/child/webcrypto/webcrypto_util.h" @@ -33,6 +32,7 @@ #include <nss.h> #include <pk11pub.h> +#include "crypto/nss_util.h" #include "crypto/scoped_nss_types.h" #endif @@ -122,6 +122,7 @@ bool SupportsRsaOaep() { #if defined(USE_OPENSSL) return false; #else + crypto::EnsureNSSInit(); // TODO(eroman): Exclude version test for OS_CHROMEOS #if defined(USE_NSS) if (!NSS_VersionCheck("3.16.2")) @@ -135,6 +136,7 @@ bool SupportsRsaOaep() { bool SupportsRsaKeyImport() { // TODO(eroman): Exclude version test for OS_CHROMEOS #if defined(USE_NSS) + crypto::EnsureNSSInit(); if (!NSS_VersionCheck("3.16.2")) { LOG(WARNING) << "RSA key import is not supported by this version of NSS. " "Skipping some tests"; @@ -445,9 +447,8 @@ const char* const kPublicKeyModulusHex = "6B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137"; const char* const kPublicKeyExponentHex = "010001"; +// TODO(eroman): Remove unnecessary test fixture. class SharedCryptoTest : public testing::Test { - protected: - virtual void SetUp() OVERRIDE { Init(); } }; blink::WebCryptoKey ImportSecretKeyFromRaw( @@ -890,41 +891,39 @@ TEST_F(SharedCryptoTest, HMACSampleSets) { bool signature_match = false; EXPECT_EQ(Status::Success(), - VerifySignature(algorithm, - key, - CryptoData(output), - CryptoData(test_message), - &signature_match)); + Verify(algorithm, + key, + CryptoData(output), + CryptoData(test_message), + &signature_match)); EXPECT_TRUE(signature_match); // Ensure truncated signature does not verify by passing one less byte. - EXPECT_EQ( - Status::Success(), - VerifySignature(algorithm, - key, - CryptoData(Uint8VectorStart(output), output.size() - 1), - CryptoData(test_message), - &signature_match)); + EXPECT_EQ(Status::Success(), + Verify(algorithm, + key, + CryptoData(Uint8VectorStart(output), output.size() - 1), + CryptoData(test_message), + &signature_match)); EXPECT_FALSE(signature_match); // Ensure truncated signature does not verify by passing no bytes. EXPECT_EQ(Status::Success(), - VerifySignature(algorithm, - key, - CryptoData(), - CryptoData(test_message), - &signature_match)); + Verify(algorithm, + key, + CryptoData(), + CryptoData(test_message), + &signature_match)); EXPECT_FALSE(signature_match); // Ensure extra long signature does not cause issues and fails. const unsigned char kLongSignature[1024] = {0}; - EXPECT_EQ( - Status::Success(), - VerifySignature(algorithm, - key, - CryptoData(kLongSignature, sizeof(kLongSignature)), - CryptoData(test_message), - &signature_match)); + EXPECT_EQ(Status::Success(), + Verify(algorithm, + key, + CryptoData(kLongSignature, sizeof(kLongSignature)), + CryptoData(test_message), + &signature_match)); EXPECT_FALSE(signature_match); } } @@ -1009,12 +1008,23 @@ TEST_F(SharedCryptoTest, AesCbcFailures) { // Fail exporting the key in SPKI and PKCS#8 formats (not allowed for secret // keys). - EXPECT_EQ(Status::ErrorUnexpectedKeyType(), + EXPECT_EQ(Status::ErrorUnsupportedExportKeyFormat(), ExportKey(blink::WebCryptoKeyFormatSpki, key, &output)); - EXPECT_EQ(Status::ErrorUnexpectedKeyType(), + EXPECT_EQ(Status::ErrorUnsupportedExportKeyFormat(), ExportKey(blink::WebCryptoKeyFormatPkcs8, key, &output)); } +TEST_F(SharedCryptoTest, ImportAesCbcSpkiFailure) { + blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); + ASSERT_EQ(Status::ErrorUnsupportedImportKeyFormat(), + ImportKey(blink::WebCryptoKeyFormatSpki, + CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), + true, + blink::WebCryptoKeyUsageEncrypt, + &key)); +} + TEST_F(SharedCryptoTest, MAYBE(AesCbcSampleSets)) { scoped_ptr<base::ListValue> tests; ASSERT_TRUE(ReadJsonTestFileToList("aes_cbc.json", &tests)); @@ -1367,15 +1377,15 @@ TEST_F(SharedCryptoTest, ImportJwkFailures) { ImportKeyJwk( CryptoData(bad_json_vec), algorithm, false, usage_mask, &key)); - // Fail on JWK alg present but unrecognized. + // Fail on JWK alg present but incorrect (expecting A128CBC). dict.SetString("alg", "A127CBC"); - EXPECT_EQ(Status::ErrorJwkUnrecognizedAlgorithm(), + EXPECT_EQ(Status::ErrorJwkAlgorithmInconsistent(), ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); RestoreJwkOctDictionary(&dict); // Fail on invalid kty. dict.SetString("kty", "foo"); - EXPECT_EQ(Status::ErrorJwkUnrecognizedKty(), + EXPECT_EQ(Status::ErrorJwkUnexpectedKty("oct"), ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); RestoreJwkOctDictionary(&dict); @@ -1734,7 +1744,19 @@ TEST_F(SharedCryptoTest, MAYBE(ImportJwkInputConsistency)) { // Fail: Input algorithm (AES-CBC) is inconsistent with JWK value // (HMAC SHA256). - EXPECT_EQ(Status::ErrorJwkAlgorithmInconsistent(), + dict.Clear(); + dict.SetString("kty", "oct"); + dict.SetString("alg", "HS256"); + dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg"); + EXPECT_EQ( + Status::ErrorJwkAlgorithmInconsistent(), + ImportKeyJwkFromDict(dict, + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), + extractable, + blink::WebCryptoKeyUsageEncrypt, + &key)); + // Fail: Input usage (encrypt) is inconsistent with JWK value (use=sig). + EXPECT_EQ(Status::ErrorJwkUseInconsistent(), ImportKeyJwk(CryptoData(json_vec), CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), extractable, @@ -2037,7 +2059,7 @@ TEST_F(SharedCryptoTest, MAYBE(ImportExportSpki)) { &key)); // Failing case: Import RSA key but provide an inconsistent input algorithm. - EXPECT_EQ(Status::DataError(), + EXPECT_EQ(Status::ErrorUnsupportedImportKeyFormat(), ImportKey(blink::WebCryptoKeyFormatSpki, CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), @@ -2054,7 +2076,7 @@ TEST_F(SharedCryptoTest, MAYBE(ImportExportSpki)) { // Failing case: Try to export a previously imported RSA public key in raw // format (not allowed for a public key). - EXPECT_EQ(Status::ErrorUnexpectedKeyType(), + EXPECT_EQ(Status::ErrorUnsupportedExportKeyFormat(), ExportKey(blink::WebCryptoKeyFormatRaw, key, &output)); // Failing case: Try to export a non-extractable key @@ -2137,7 +2159,7 @@ TEST_F(SharedCryptoTest, MAYBE(ImportExportPkcs8)) { // and usage. Several issues here: // * AES-CBC doesn't support PKCS8 key format // * AES-CBC doesn't support "sign" usage - EXPECT_EQ(Status::ErrorCreateKeyBadUsages(), + EXPECT_EQ(Status::ErrorUnsupportedImportKeyFormat(), ImportKey(blink::WebCryptoKeyFormatPkcs8, CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), @@ -2697,33 +2719,33 @@ TEST_F(SharedCryptoTest, MAYBE(RsaSsaSignVerifyFailures)) { Sign(algorithm, private_key, CryptoData(data), &signature)); // Ensure truncated signature does not verify by passing one less byte. - EXPECT_EQ(Status::Success(), - VerifySignature( - algorithm, - public_key, - CryptoData(Uint8VectorStart(signature), signature.size() - 1), - CryptoData(data), - &signature_match)); + EXPECT_EQ( + Status::Success(), + Verify(algorithm, + public_key, + CryptoData(Uint8VectorStart(signature), signature.size() - 1), + CryptoData(data), + &signature_match)); EXPECT_FALSE(signature_match); // Ensure truncated signature does not verify by passing no bytes. EXPECT_EQ(Status::Success(), - VerifySignature(algorithm, - public_key, - CryptoData(), - CryptoData(data), - &signature_match)); + Verify(algorithm, + public_key, + CryptoData(), + CryptoData(data), + &signature_match)); EXPECT_FALSE(signature_match); // Ensure corrupted signature does not verify. std::vector<uint8> corrupt_sig = signature; corrupt_sig[corrupt_sig.size() / 2] ^= 0x1; EXPECT_EQ(Status::Success(), - VerifySignature(algorithm, - public_key, - CryptoData(corrupt_sig), - CryptoData(data), - &signature_match)); + Verify(algorithm, + public_key, + CryptoData(corrupt_sig), + CryptoData(data), + &signature_match)); EXPECT_FALSE(signature_match); // Ensure signatures that are greater than the modulus size fail. @@ -2731,11 +2753,11 @@ TEST_F(SharedCryptoTest, MAYBE(RsaSsaSignVerifyFailures)) { DCHECK_GT(long_message_size_bytes, kModulusLengthBits / 8); const unsigned char kLongSignature[long_message_size_bytes] = {0}; EXPECT_EQ(Status::Success(), - VerifySignature(algorithm, - public_key, - CryptoData(kLongSignature, sizeof(kLongSignature)), - CryptoData(data), - &signature_match)); + Verify(algorithm, + public_key, + CryptoData(kLongSignature, sizeof(kLongSignature)), + CryptoData(data), + &signature_match)); EXPECT_FALSE(signature_match); // Ensure that signing and verifying with an incompatible algorithm fails. @@ -2744,11 +2766,11 @@ TEST_F(SharedCryptoTest, MAYBE(RsaSsaSignVerifyFailures)) { EXPECT_EQ(Status::ErrorUnexpected(), Sign(algorithm, private_key, CryptoData(data), &signature)); EXPECT_EQ(Status::ErrorUnexpected(), - VerifySignature(algorithm, - public_key, - CryptoData(signature), - CryptoData(data), - &signature_match)); + Verify(algorithm, + public_key, + CryptoData(signature), + CryptoData(data), + &signature_match)); // Some crypto libraries (NSS) can automatically select the RSA SSA inner hash // based solely on the contents of the input signature data. In the Web Crypto @@ -2789,12 +2811,11 @@ TEST_F(SharedCryptoTest, MAYBE(RsaSsaSignVerifyFailures)) { bool is_match; EXPECT_EQ(Status::Success(), - VerifySignature( - CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5), - public_key_256, - CryptoData(signature), - CryptoData(data), - &is_match)); + Verify(CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5), + public_key_256, + CryptoData(signature), + CryptoData(data), + &is_match)); EXPECT_FALSE(is_match); } @@ -2845,11 +2866,11 @@ TEST_F(SharedCryptoTest, MAYBE(RsaSignVerifyKnownAnswer)) { bool is_match = false; ASSERT_EQ(Status::Success(), - VerifySignature(algorithm, - public_key, - CryptoData(test_signature), - CryptoData(test_message), - &is_match)); + Verify(algorithm, + public_key, + CryptoData(test_signature), + CryptoData(test_message), + &is_match)); EXPECT_TRUE(is_match); } } @@ -3083,11 +3104,11 @@ TEST_F(SharedCryptoTest, MAYBE(AesKwRawSymkeyUnwrapSignVerifyHmac)) { bool verify_result; ASSERT_EQ(Status::Success(), - VerifySignature(CreateAlgorithm(blink::WebCryptoAlgorithmIdHmac), - key, - CryptoData(signature), - CryptoData(test_message), - &verify_result)); + Verify(CreateAlgorithm(blink::WebCryptoAlgorithmIdHmac), + key, + CryptoData(signature), + CryptoData(test_message), + &verify_result)); } TEST_F(SharedCryptoTest, MAYBE(AesKwRawSymkeyWrapUnwrapErrors)) { @@ -3430,8 +3451,6 @@ TEST_F(SharedCryptoTest, MAYBE(UnwrapAesCbc192)) { class SharedCryptoRsaOaepTest : public ::testing::Test { public: - SharedCryptoRsaOaepTest() { Init(); } - scoped_ptr<base::DictionaryValue> CreatePublicKeyJwkDict() { scoped_ptr<base::DictionaryValue> jwk(new base::DictionaryValue()); jwk->SetString("kty", "RSA"); @@ -3533,7 +3552,7 @@ TEST_F(SharedCryptoRsaOaepTest, ImportPublicJwkWithMismatchedTypeFails) { jwk->SetString("alg", "RSA-OAEP"); blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::ErrorJwkPropertyMissing("k"), + ASSERT_EQ(Status::ErrorJwkUnexpectedKty("RSA"), ImportKeyJwkFromDict(*jwk.get(), CreateRsaHashedImportAlgorithm( blink::WebCryptoAlgorithmIdRsaOaep, diff --git a/content/child/webcrypto/status.cc b/content/child/webcrypto/status.cc index fc01c2c..9439c93 100644 --- a/content/child/webcrypto/status.cc +++ b/content/child/webcrypto/status.cc @@ -58,11 +58,6 @@ Status Status::ErrorJwkExtInconsistent() { "specified by the Web Crypto call"); } -Status Status::ErrorJwkUnrecognizedAlgorithm() { - return Status(blink::WebCryptoErrorTypeData, - "The JWK \"alg\" property was not recognized"); -} - Status Status::ErrorJwkAlgorithmInconsistent() { return Status(blink::WebCryptoErrorTypeData, "The JWK \"alg\" property was inconsistent with that specified " @@ -99,9 +94,9 @@ Status Status::ErrorJwkUseAndKeyopsInconsistent() { "but are inconsistent with each other."); } -Status Status::ErrorJwkUnrecognizedKty() { +Status Status::ErrorJwkUnexpectedKty(const std::string& expected) { return Status(blink::WebCryptoErrorTypeData, - "The JWK \"kty\" property was unrecognized"); + "The JWK \"kty\" property was not \"" + expected + "\""); } Status Status::ErrorJwkIncorrectKeyLength() { @@ -120,6 +115,16 @@ Status Status::ErrorImportEmptyKeyData() { return Status(blink::WebCryptoErrorTypeData, "No key data was provided"); } +Status Status::ErrorUnsupportedImportKeyFormat() { + return Status(blink::WebCryptoErrorTypeNotSupported, + "Unsupported import key format for algorithm"); +} + +Status Status::ErrorUnsupportedExportKeyFormat() { + return Status(blink::WebCryptoErrorTypeNotSupported, + "Unsupported export key format for algorithm"); +} + Status Status::ErrorImportAesKeyLength() { return Status(blink::WebCryptoErrorTypeData, "AES key data must be 128, 192 or 256 bits"); diff --git a/content/child/webcrypto/status.h b/content/child/webcrypto/status.h index 103c4ed..29bebe5 100644 --- a/content/child/webcrypto/status.h +++ b/content/child/webcrypto/status.h @@ -69,10 +69,6 @@ class CONTENT_EXPORT Status { // incompatible with the value requested by the Web Crypto call. static Status ErrorJwkExtInconsistent(); - // The "alg" parameter could not be converted to an equivalent - // WebCryptoAlgorithm. Either it was malformed or unrecognized. - static Status ErrorJwkUnrecognizedAlgorithm(); - // The "alg" parameter is incompatible with the (optional) Algorithm // specified by the Web Crypto import operation. static Status ErrorJwkAlgorithmInconsistent(); @@ -97,9 +93,9 @@ class CONTENT_EXPORT Status { // are incompatible with each other. static Status ErrorJwkUseAndKeyopsInconsistent(); - // The "kty" parameter was given and was a string, however it was - // unrecognized. - static Status ErrorJwkUnrecognizedKty(); + // The "kty" parameter was given and was a string, however it was not the + // expected value. + static Status ErrorJwkUnexpectedKty(const std::string& expected); // The amount of key data provided was incompatible with the selected // algorithm. For instance if the algorith name was A128CBC then EXACTLY @@ -120,6 +116,14 @@ class CONTENT_EXPORT Status { // key data there. static Status ErrorImportEmptyKeyData(); + // Tried importing a key using an unsupported format for the key type (for + // instance importing an HMAC key using format=spki). + static Status ErrorUnsupportedImportKeyFormat(); + + // Tried exporting a key using an unsupported format for the key type (for + // instance exporting an HMAC key using format=spki). + static Status ErrorUnsupportedExportKeyFormat(); + // The key data buffer provided for importKey() is an incorrect length for // AES. static Status ErrorImportAesKeyLength(); diff --git a/content/child/webcrypto/structured_clone.cc b/content/child/webcrypto/structured_clone.cc new file mode 100644 index 0000000..00f617a --- /dev/null +++ b/content/child/webcrypto/structured_clone.cc @@ -0,0 +1,136 @@ +// 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/structured_clone.h" + +#include "base/logging.h" +#include "content/child/webcrypto/algorithm_dispatch.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 { + +// Returns the key format to use for structured cloning. +blink::WebCryptoKeyFormat GetCloneFormatForKeyType( + blink::WebCryptoKeyType type) { + switch (type) { + case blink::WebCryptoKeyTypeSecret: + return blink::WebCryptoKeyFormatRaw; + case blink::WebCryptoKeyTypePublic: + return blink::WebCryptoKeyFormatSpki; + case blink::WebCryptoKeyTypePrivate: + return blink::WebCryptoKeyFormatPkcs8; + } + + NOTREACHED(); + return blink::WebCryptoKeyFormatRaw; +} + +// Converts a KeyAlgorithm into an equivalent Algorithm for import. +blink::WebCryptoAlgorithm KeyAlgorithmToImportAlgorithm( + const blink::WebCryptoKeyAlgorithm& algorithm) { + switch (algorithm.paramsType()) { + case blink::WebCryptoKeyAlgorithmParamsTypeAes: + return CreateAlgorithm(algorithm.id()); + case blink::WebCryptoKeyAlgorithmParamsTypeHmac: + return CreateHmacImportAlgorithm(algorithm.hmacParams()->hash().id()); + case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed: + return CreateRsaHashedImportAlgorithm( + algorithm.id(), algorithm.rsaHashedParams()->hash().id()); + case blink::WebCryptoKeyAlgorithmParamsTypeNone: + break; + default: + break; + } + return blink::WebCryptoAlgorithm::createNull(); +} + +// There is some duplicated information in the serialized format used by +// structured clone (since the KeyAlgorithm is serialized separately from the +// key data). Use this extra information to further validate what was +// deserialized from the key data. +// +// A failure here implies either a bug in the code, or that the serialized data +// was corrupted. +bool ValidateDeserializedKey(const blink::WebCryptoKey& key, + const blink::WebCryptoKeyAlgorithm& algorithm, + blink::WebCryptoKeyType type) { + if (algorithm.id() != key.algorithm().id()) + return false; + + if (key.type() != type) + return false; + + switch (algorithm.paramsType()) { + case blink::WebCryptoKeyAlgorithmParamsTypeAes: + if (algorithm.aesParams()->lengthBits() != + key.algorithm().aesParams()->lengthBits()) + return false; + break; + case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed: + if (algorithm.rsaHashedParams()->modulusLengthBits() != + key.algorithm().rsaHashedParams()->modulusLengthBits()) + return false; + if (algorithm.rsaHashedParams()->publicExponent().size() != + key.algorithm().rsaHashedParams()->publicExponent().size()) + return false; + if (memcmp(algorithm.rsaHashedParams()->publicExponent().data(), + key.algorithm().rsaHashedParams()->publicExponent().data(), + key.algorithm().rsaHashedParams()->publicExponent().size()) != + 0) + return false; + break; + case blink::WebCryptoKeyAlgorithmParamsTypeNone: + case blink::WebCryptoKeyAlgorithmParamsTypeHmac: + break; + default: + return false; + } + + return true; +} + +} // namespace + +// Note that this function is called from the target Blink thread. +bool SerializeKeyForClone(const blink::WebCryptoKey& key, + blink::WebVector<uint8>* key_data) { + return PlatformSerializeKeyForClone(key, key_data); +} + +// Note that this function is called from the target Blink thread. +bool DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, + blink::WebCryptoKeyType type, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + const CryptoData& key_data, + blink::WebCryptoKey* key) { + // TODO(eroman): This should not call into the platform crypto layer. + // Otherwise it runs the risk of stalling while the NSS/OpenSSL global locks + // are held. + // + // An alternate approach is to defer the key import until the key is used. + // However this means that any deserialization errors would have to be + // surfaced as WebCrypto errors, leading to slightly different behaviors. For + // instance you could clone a key which fails to be deserialized. + Status status = ImportKey(GetCloneFormatForKeyType(type), + key_data, + KeyAlgorithmToImportAlgorithm(algorithm), + extractable, + usage_mask, + key); + if (status.IsError()) + return false; + return ValidateDeserializedKey(*key, algorithm, type); +} + +} // namespace webcrypto + +} // namespace content diff --git a/content/child/webcrypto/structured_clone.h b/content/child/webcrypto/structured_clone.h new file mode 100644 index 0000000..19728f99 --- /dev/null +++ b/content/child/webcrypto/structured_clone.h @@ -0,0 +1,33 @@ +// 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. + +#ifndef CONTENT_CHILD_WEBCRYPTO_STRUCTURED_CLONE_H_ +#define CONTENT_CHILD_WEBCRYPTO_STRUCTURED_CLONE_H_ + +#include "base/basictypes.h" +#include "third_party/WebKit/public/platform/WebCrypto.h" + +namespace content { + +namespace webcrypto { + +class CryptoData; + +// Called on the target Blink thread. +bool SerializeKeyForClone(const blink::WebCryptoKey& key, + blink::WebVector<uint8>* key_data); + +// Called on the target Blink thread. +bool DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, + blink::WebCryptoKeyType type, + bool extractable, + blink::WebCryptoKeyUsageMask usage_mask, + const CryptoData& key_data, + blink::WebCryptoKey* key); + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_STRUCTURED_CLONE_H_ diff --git a/content/child/webcrypto/webcrypto_impl.cc b/content/child/webcrypto/webcrypto_impl.cc index 43b108b..c02263a 100644 --- a/content/child/webcrypto/webcrypto_impl.cc +++ b/content/child/webcrypto/webcrypto_impl.cc @@ -14,9 +14,10 @@ #include "base/thread_task_runner_handle.h" #include "base/threading/sequenced_worker_pool.h" #include "base/threading/worker_pool.h" +#include "content/child/webcrypto/algorithm_dispatch.h" #include "content/child/webcrypto/crypto_data.h" -#include "content/child/webcrypto/shared_crypto.h" #include "content/child/webcrypto/status.h" +#include "content/child/webcrypto/structured_clone.h" #include "content/child/webcrypto/webcrypto_util.h" #include "content/child/worker_thread_task_runner.h" #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" @@ -525,12 +526,11 @@ void DoVerify(scoped_ptr<VerifySignatureState> passed_state) { VerifySignatureState* state = passed_state.get(); if (state->cancelled()) return; - state->status = - webcrypto::VerifySignature(state->algorithm, - state->key, - webcrypto::CryptoData(state->signature), - webcrypto::CryptoData(state->data), - &state->verify_result); + state->status = webcrypto::Verify(state->algorithm, + state->key, + webcrypto::CryptoData(state->signature), + webcrypto::CryptoData(state->data), + &state->verify_result); state->origin_thread->PostTask( FROM_HERE, base::Bind(DoVerifyReply, Passed(&passed_state))); @@ -584,10 +584,6 @@ WebCryptoImpl::WebCryptoImpl() { WebCryptoImpl::~WebCryptoImpl() { } -void WebCryptoImpl::EnsureInit() { - webcrypto::Init(); -} - void WebCryptoImpl::encrypt(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const unsigned char* data, diff --git a/content/child/webcrypto/webcrypto_impl.h b/content/child/webcrypto/webcrypto_impl.h index 485beb9..8d5af3d 100644 --- a/content/child/webcrypto/webcrypto_impl.h +++ b/content/child/webcrypto/webcrypto_impl.h @@ -25,8 +25,6 @@ class WebCryptoImpl : public blink::WebCrypto { virtual ~WebCryptoImpl(); - static void EnsureInit(); - virtual void encrypt(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const unsigned char* data, diff --git a/content/child/webcrypto/webcrypto_util.cc b/content/child/webcrypto/webcrypto_util.cc index 87ac174..7bedc43 100644 --- a/content/child/webcrypto/webcrypto_util.cc +++ b/content/child/webcrypto/webcrypto_util.cc @@ -160,37 +160,17 @@ blink::WebCryptoAlgorithm CreateRsaHashedImportAlgorithm( id, new blink::WebCryptoRsaHashedImportParams(CreateAlgorithm(hash_id))); } -bool CreateSecretKeyAlgorithm(const blink::WebCryptoAlgorithm& algorithm, - unsigned int keylen_bytes, - blink::WebCryptoKeyAlgorithm* key_algorithm) { - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdHmac: { - blink::WebCryptoAlgorithm hash = GetInnerHashAlgorithm(algorithm); - if (hash.isNull()) - return false; - if (keylen_bytes > UINT_MAX / 8) - return false; - *key_algorithm = - blink::WebCryptoKeyAlgorithm::createHmac(hash.id(), keylen_bytes * 8); - return true; - } - case blink::WebCryptoAlgorithmIdAesKw: - case blink::WebCryptoAlgorithmIdAesCbc: - case blink::WebCryptoAlgorithmIdAesCtr: - case blink::WebCryptoAlgorithmIdAesGcm: - *key_algorithm = blink::WebCryptoKeyAlgorithm::createAes( - algorithm.id(), keylen_bytes * 8); - return true; - default: - return false; - } -} - bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a, blink::WebCryptoKeyUsageMask b) { return (a & b) == b; } +// TODO(eroman): Move this helper to WebCryptoKey. +bool KeyUsageAllows(const blink::WebCryptoKey& key, + const blink::WebCryptoKeyUsage usage) { + return ((key.usages() & usage) != 0); +} + bool IsAlgorithmRsa(blink::WebCryptoAlgorithmId alg_id) { return alg_id == blink::WebCryptoAlgorithmIdRsaOaep || alg_id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5; @@ -202,6 +182,84 @@ bool IsAlgorithmAsymmetric(blink::WebCryptoAlgorithmId alg_id) { return IsAlgorithmRsa(alg_id); } +// The WebCrypto spec defines the default value for the tag length, as well as +// the allowed values for tag length. +Status GetAesGcmTagLengthInBits(const blink::WebCryptoAesGcmParams* params, + unsigned int* tag_length_bits) { + *tag_length_bits = 128; + if (params->hasTagLengthBits()) + *tag_length_bits = params->optionalTagLengthBits(); + + if (*tag_length_bits != 32 && *tag_length_bits != 64 && + *tag_length_bits != 96 && *tag_length_bits != 104 && + *tag_length_bits != 112 && *tag_length_bits != 120 && + *tag_length_bits != 128) + return Status::ErrorInvalidAesGcmTagLength(); + + return Status::Success(); +} + +Status GetAesKeyGenLengthInBits(const blink::WebCryptoAesKeyGenParams* params, + unsigned int* keylen_bits) { + *keylen_bits = params->lengthBits(); + + if (*keylen_bits == 128 || *keylen_bits == 256) + return Status::Success(); + + // BoringSSL does not support 192-bit AES. + if (*keylen_bits == 192) + return Status::ErrorAes192BitUnsupported(); + + return Status::ErrorGenerateKeyLength(); +} + +Status GetHmacKeyGenLengthInBits(const blink::WebCryptoHmacKeyGenParams* params, + unsigned int* keylen_bits) { + if (!params->hasLengthBits()) { + switch (params->hash().id()) { + case blink::WebCryptoAlgorithmIdSha1: + case blink::WebCryptoAlgorithmIdSha256: + *keylen_bits = 512; + return Status::Success(); + case blink::WebCryptoAlgorithmIdSha384: + case blink::WebCryptoAlgorithmIdSha512: + *keylen_bits = 1024; + return Status::Success(); + default: + return Status::ErrorUnsupported(); + } + } + + if (params->optionalLengthBits() % 8) + return Status::ErrorGenerateKeyLength(); + + *keylen_bits = params->optionalLengthBits(); + + // TODO(eroman): NSS fails when generating a zero-length secret key. + if (*keylen_bits == 0) + return Status::ErrorGenerateKeyLength(); + + return Status::Success(); +} + +Status VerifyAesKeyLengthForImport(unsigned int keylen_bytes) { + if (keylen_bytes == 16 || keylen_bytes == 32) + return Status::Success(); + + // BoringSSL does not support 192-bit AES. + if (keylen_bytes == 24) + return Status::ErrorAes192BitUnsupported(); + + return Status::ErrorImportAesKeyLength(); +} + +Status CheckKeyCreationUsages(blink::WebCryptoKeyUsageMask all_possible_usages, + blink::WebCryptoKeyUsageMask actual_usages) { + if (!ContainsKeyUsages(all_possible_usages, actual_usages)) + return Status::ErrorCreateKeyBadUsages(); + return Status::Success(); +} + } // namespace webcrypto } // namespace content diff --git a/content/child/webcrypto/webcrypto_util.h b/content/child/webcrypto/webcrypto_util.h index f4a2a6f..b5eb231 100644 --- a/content/child/webcrypto/webcrypto_util.h +++ b/content/child/webcrypto/webcrypto_util.h @@ -67,17 +67,30 @@ CONTENT_EXPORT blink::WebCryptoAlgorithm CreateRsaHashedImportAlgorithm( blink::WebCryptoAlgorithmId id, blink::WebCryptoAlgorithmId hash_id); -bool CreateSecretKeyAlgorithm(const blink::WebCryptoAlgorithm& algorithm, - unsigned int keylen_bytes, - blink::WebCryptoKeyAlgorithm* key_algorithm); - // Returns true if the set bits in b make up a subset of the set bits in a. bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a, blink::WebCryptoKeyUsageMask b); +bool KeyUsageAllows(const blink::WebCryptoKey& key, + const blink::WebCryptoKeyUsage usage); + bool IsAlgorithmRsa(blink::WebCryptoAlgorithmId alg_id); bool IsAlgorithmAsymmetric(blink::WebCryptoAlgorithmId alg_id); +Status GetAesGcmTagLengthInBits(const blink::WebCryptoAesGcmParams* params, + unsigned int* tag_length_bits); + +Status GetAesKeyGenLengthInBits(const blink::WebCryptoAesKeyGenParams* params, + unsigned int* keylen_bits); + +Status GetHmacKeyGenLengthInBits(const blink::WebCryptoHmacKeyGenParams* params, + unsigned int* keylen_bits); + +Status VerifyAesKeyLengthForImport(unsigned int keylen_bytes); + +Status CheckKeyCreationUsages(blink::WebCryptoKeyUsageMask all_possible_usages, + blink::WebCryptoKeyUsageMask actual_usages); + } // namespace webcrypto } // namespace content diff --git a/content/content_child.gypi b/content/content_child.gypi index 902713f..2f3538f 100644 --- a/content/content_child.gypi +++ b/content/content_child.gypi @@ -195,15 +195,21 @@ 'child/web_url_loader_impl.h', 'child/webblobregistry_impl.cc', 'child/webblobregistry_impl.h', + 'child/webcrypto/algorithm_dispatch.cc', + 'child/webcrypto/algorithm_dispatch.h', + 'child/webcrypto/algorithm_implementation.cc', + 'child/webcrypto/algorithm_implementation.h', + 'child/webcrypto/algorithm_registry.cc', + 'child/webcrypto/algorithm_registry.h', 'child/webcrypto/crypto_data.cc', 'child/webcrypto/crypto_data.h', 'child/webcrypto/jwk.cc', 'child/webcrypto/jwk.h', 'child/webcrypto/platform_crypto.h', - 'child/webcrypto/shared_crypto.cc', - 'child/webcrypto/shared_crypto.h', 'child/webcrypto/status.cc', 'child/webcrypto/status.h', + 'child/webcrypto/structured_clone.cc', + 'child/webcrypto/structured_clone.h', 'child/webcrypto/webcrypto_impl.cc', 'child/webcrypto/webcrypto_impl.h', 'child/webcrypto/webcrypto_util.cc', @@ -234,10 +240,38 @@ 'child/worker_thread_task_runner.h', ], 'webcrypto_nss_sources': [ - 'child/webcrypto/platform_crypto_nss.cc', + 'child/webcrypto/nss/aes_cbc_nss.cc', + 'child/webcrypto/nss/aes_gcm_nss.cc', + 'child/webcrypto/nss/aes_key_nss.cc', + 'child/webcrypto/nss/aes_key_nss.h', + 'child/webcrypto/nss/aes_kw_nss.cc', + 'child/webcrypto/nss/hmac_nss.cc', + 'child/webcrypto/nss/key_nss.cc', + 'child/webcrypto/nss/key_nss.h', + 'child/webcrypto/nss/rsa_key_nss.cc', + 'child/webcrypto/nss/rsa_key_nss.h', + 'child/webcrypto/nss/rsa_oaep_nss.cc', + 'child/webcrypto/nss/rsa_ssa_nss.cc', + 'child/webcrypto/nss/sha_nss.cc', + 'child/webcrypto/nss/sym_key_nss.cc', + 'child/webcrypto/nss/sym_key_nss.h', + 'child/webcrypto/nss/util_nss.cc', + 'child/webcrypto/nss/util_nss.h', ], 'webcrypto_openssl_sources': [ - 'child/webcrypto/platform_crypto_openssl.cc', + 'child/webcrypto/openssl/aes_cbc_openssl.cc', + 'child/webcrypto/openssl/aes_gcm_openssl.cc', + 'child/webcrypto/openssl/aes_key_openssl.cc', + 'child/webcrypto/openssl/aes_key_openssl.h', + 'child/webcrypto/openssl/aes_kw_openssl.cc', + 'child/webcrypto/openssl/hmac_openssl.cc', + 'child/webcrypto/openssl/key_openssl.cc', + 'child/webcrypto/openssl/key_openssl.h', + 'child/webcrypto/openssl/sha_openssl.cc', + 'child/webcrypto/openssl/sym_key_openssl.cc', + 'child/webcrypto/openssl/sym_key_openssl.h', + 'child/webcrypto/openssl/util_openssl.cc', + 'child/webcrypto/openssl/util_openssl.h', ], }, 'sources': [ |