diff options
author | Zeno Albisser <zeno.albisser@theqtcompany.com> | 2014-12-05 15:04:29 +0100 |
---|---|---|
committer | Andras Becsi <andras.becsi@theqtcompany.com> | 2014-12-09 10:49:28 +0100 |
commit | af6588f8d723931a298c995fa97259bb7f7deb55 (patch) | |
tree | 060ca707847ba1735f01af2372e0d5e494dc0366 /chromium/content/child/webcrypto | |
parent | 2fff84d821cc7b1c785f6404e0f8091333283e74 (diff) | |
download | qtwebengine-chromium-af6588f8d723931a298c995fa97259bb7f7deb55.tar.gz |
BASELINE: Update chromium to 40.0.2214.28 and ninja to 1.5.3.
Change-Id: I759465284fd64d59ad120219cbe257f7402c4181
Reviewed-by: Andras Becsi <andras.becsi@theqtcompany.com>
Diffstat (limited to 'chromium/content/child/webcrypto')
63 files changed, 7696 insertions, 9215 deletions
diff --git a/chromium/content/child/webcrypto/OWNERS b/chromium/content/child/webcrypto/OWNERS index a1853a1bcb1..6b60858e6e8 100644 --- a/chromium/content/child/webcrypto/OWNERS +++ b/chromium/content/child/webcrypto/OWNERS @@ -1,2 +1,2 @@ -eroman@chromium.org
-rsleevi@chromium.org
+eroman@chromium.org +rsleevi@chromium.org diff --git a/chromium/content/child/webcrypto/algorithm_dispatch.cc b/chromium/content/child/webcrypto/algorithm_dispatch.cc new file mode 100644 index 00000000000..f8d7933c8fc --- /dev/null +++ b/chromium/content/child/webcrypto/algorithm_dispatch.cc @@ -0,0 +1,253 @@ +// 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_t>* 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_t>* 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_t>* 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_t>* 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_t>* 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_t>* buffer) { + const AlgorithmImplementation* impl = NULL; + Status status = GetAlgorithmImplementation(algorithm.id(), &impl); + if (status.IsError()) + return status; + + return impl->Digest(algorithm, data, buffer); +} + +Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + GenerateKeyResult* result) { + const AlgorithmImplementation* impl = NULL; + Status status = GetAlgorithmImplementation(algorithm.id(), &impl); + if (status.IsError()) + return status; + + return impl->GenerateKey(algorithm, extractable, usages, result); +} + +// 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 usages, + blink::WebCryptoKey* key) { + const AlgorithmImplementation* impl = NULL; + Status status = GetAlgorithmImplementation(algorithm.id(), &impl); + if (status.IsError()) + return status; + + status = impl->VerifyKeyUsagesBeforeImportKey(format, usages); + if (status.IsError()) + return status; + + switch (format) { + case blink::WebCryptoKeyFormatRaw: + return impl->ImportKeyRaw(key_data, algorithm, extractable, usages, key); + case blink::WebCryptoKeyFormatSpki: + return impl->ImportKeySpki(key_data, algorithm, extractable, usages, key); + case blink::WebCryptoKeyFormatPkcs8: + return impl->ImportKeyPkcs8( + key_data, algorithm, extractable, usages, key); + case blink::WebCryptoKeyFormatJwk: + return impl->ImportKeyJwk(key_data, algorithm, extractable, usages, key); + default: + return Status::ErrorUnsupported(); + } +} + +Status ExportKey(blink::WebCryptoKeyFormat format, + const blink::WebCryptoKey& key, + std::vector<uint8_t>* 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_t>* 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(); + + 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_t>* buffer) { + if (!KeyUsageAllows(wrapping_key, blink::WebCryptoKeyUsageWrapKey)) + return Status::ErrorUnexpected(); + + std::vector<uint8_t> 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 usages, + 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, usages); + if (status.IsError()) + return status; + + std::vector<uint8_t> 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, usages, key); +} + +scoped_ptr<blink::WebCryptoDigestor> CreateDigestor( + blink::WebCryptoAlgorithmId algorithm) { + PlatformInit(); + return CreatePlatformDigestor(algorithm); +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/algorithm_dispatch.h b/chromium/content/child/webcrypto/algorithm_dispatch.h new file mode 100644 index 00000000000..3eefb46de5d --- /dev/null +++ b/chromium/content/child/webcrypto/algorithm_dispatch.h @@ -0,0 +1,97 @@ +// 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 <stdint.h> +#include <vector> + +#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 GenerateKeyResult; +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_t>* buffer); + +CONTENT_EXPORT Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer); + +CONTENT_EXPORT Status Digest(const blink::WebCryptoAlgorithm& algorithm, + const CryptoData& data, + std::vector<uint8_t>* buffer); + +CONTENT_EXPORT Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + GenerateKeyResult* result); + +CONTENT_EXPORT Status ImportKey(blink::WebCryptoKeyFormat format, + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key); + +CONTENT_EXPORT Status ExportKey(blink::WebCryptoKeyFormat format, + const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer); + +CONTENT_EXPORT Status Sign(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* 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_t>* 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 usages, + 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/chromium/content/child/webcrypto/algorithm_implementation.cc b/chromium/content/child/webcrypto/algorithm_implementation.cc new file mode 100644 index 00000000000..b815a527cf6 --- /dev/null +++ b/chromium/content/child/webcrypto/algorithm_implementation.cc @@ -0,0 +1,131 @@ +// 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_t>* buffer) const { + return Status::ErrorUnsupported(); +} + +Status AlgorithmImplementation::Decrypt( + const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const { + return Status::ErrorUnsupported(); +} + +Status AlgorithmImplementation::Sign(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* 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_t>* buffer) const { + return Status::ErrorUnsupported(); +} + +Status AlgorithmImplementation::GenerateKey( + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + GenerateKeyResult* result) const { + return Status::ErrorUnsupported(); +} + +Status AlgorithmImplementation::VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usages) const { + return Status::ErrorUnsupportedImportKeyFormat(); +} + +Status AlgorithmImplementation::ImportKeyRaw( + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const { + return Status::ErrorUnsupportedImportKeyFormat(); +} + +Status AlgorithmImplementation::ImportKeyPkcs8( + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const { + return Status::ErrorUnsupportedImportKeyFormat(); +} + +Status AlgorithmImplementation::ImportKeySpki( + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const { + return Status::ErrorUnsupportedImportKeyFormat(); +} + +Status AlgorithmImplementation::ImportKeyJwk( + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const { + return Status::ErrorUnsupportedImportKeyFormat(); +} + +Status AlgorithmImplementation::ExportKeyRaw( + const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const { + return Status::ErrorUnsupportedExportKeyFormat(); +} + +Status AlgorithmImplementation::ExportKeyPkcs8( + const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const { + return Status::ErrorUnsupportedExportKeyFormat(); +} + +Status AlgorithmImplementation::ExportKeySpki( + const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const { + return Status::ErrorUnsupportedExportKeyFormat(); +} + +Status AlgorithmImplementation::ExportKeyJwk( + const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const { + return Status::ErrorUnsupportedExportKeyFormat(); +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/algorithm_implementation.h b/chromium/content/child/webcrypto/algorithm_implementation.h new file mode 100644 index 00000000000..e926ad0324d --- /dev/null +++ b/chromium/content/child/webcrypto/algorithm_implementation.h @@ -0,0 +1,157 @@ +// 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_IMPLEMENTATION_H_ +#define CONTENT_CHILD_WEBCRYPTO_ALGORITHM_IMPLEMENTATION_H_ + +#include <stdint.h> +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "third_party/WebKit/public/platform/WebCrypto.h" + +namespace content { + +namespace webcrypto { + +class CryptoData; +class GenerateKeyResult; +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_t>* 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_t>* 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_t>* 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_t>* buffer) const; + + // This method corresponds to Web Crypto's crypto.subtle.generateKey(). + // + // Implementations MUST verify |usages| and return an error if it is not + // appropriate. + virtual Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + GenerateKeyResult* result) 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 usages) 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 usages, + 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 usages, + 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 usages, + 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 usages, + blink::WebCryptoKey* key) const; + + // ----------------------------------------------- + // Key export + // ----------------------------------------------- + + virtual Status ExportKeyRaw(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const; + + virtual Status ExportKeyPkcs8(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const; + + virtual Status ExportKeySpki(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const; + + virtual Status ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const; +}; + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_ALGORITHM_IMPLEMENTATION_H_ diff --git a/chromium/content/child/webcrypto/algorithm_registry.cc b/chromium/content/child/webcrypto/algorithm_registry.cc new file mode 100644 index 00000000000..7f674db0bbe --- /dev/null +++ b/chromium/content/child/webcrypto/algorithm_registry.cc @@ -0,0 +1,90 @@ +// 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 { + +// This class is used as a singleton. All methods must be threadsafe. +class AlgorithmRegistry { + public: + AlgorithmRegistry() + : sha_(CreatePlatformShaImplementation()), + aes_gcm_(CreatePlatformAesGcmImplementation()), + aes_cbc_(CreatePlatformAesCbcImplementation()), + aes_ctr_(CreatePlatformAesCtrImplementation()), + aes_kw_(CreatePlatformAesKwImplementation()), + hmac_(CreatePlatformHmacImplementation()), + rsa_ssa_(CreatePlatformRsaSsaImplementation()), + rsa_oaep_(CreatePlatformRsaOaepImplementation()), + rsa_pss_(CreatePlatformRsaPssImplementation()) { + 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::WebCryptoAlgorithmIdAesCtr: + return aes_ctr_.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(); + case blink::WebCryptoAlgorithmIdRsaPss: + return rsa_pss_.get(); + default: + return NULL; + } + } + + private: + const scoped_ptr<AlgorithmImplementation> sha_; + const scoped_ptr<AlgorithmImplementation> aes_gcm_; + const scoped_ptr<AlgorithmImplementation> aes_cbc_; + const scoped_ptr<AlgorithmImplementation> aes_ctr_; + const scoped_ptr<AlgorithmImplementation> aes_kw_; + const scoped_ptr<AlgorithmImplementation> hmac_; + const scoped_ptr<AlgorithmImplementation> rsa_ssa_; + const scoped_ptr<AlgorithmImplementation> rsa_oaep_; + const scoped_ptr<AlgorithmImplementation> rsa_pss_; +}; + +} // 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/chromium/content/child/webcrypto/algorithm_registry.h b/chromium/content/child/webcrypto/algorithm_registry.h new file mode 100644 index 00000000000..6db5f5f56dd --- /dev/null +++ b/chromium/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/chromium/content/child/webcrypto/generate_key_result.cc b/chromium/content/child/webcrypto/generate_key_result.cc new file mode 100644 index 00000000000..3c61657eb55 --- /dev/null +++ b/chromium/content/child/webcrypto/generate_key_result.cc @@ -0,0 +1,63 @@ +// 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/generate_key_result.h" + +#include "base/logging.h" + +namespace content { + +namespace webcrypto { + +GenerateKeyResult::GenerateKeyResult() : type_(TYPE_NULL) { +} + +GenerateKeyResult::Type GenerateKeyResult::type() const { + return type_; +} + +const blink::WebCryptoKey& GenerateKeyResult::secret_key() const { + DCHECK_EQ(TYPE_SECRET_KEY, type_); + return secret_key_; +} + +const blink::WebCryptoKey& GenerateKeyResult::public_key() const { + DCHECK_EQ(TYPE_PUBLIC_PRIVATE_KEY_PAIR, type_); + return public_key_; +} + +const blink::WebCryptoKey& GenerateKeyResult::private_key() const { + DCHECK_EQ(TYPE_PUBLIC_PRIVATE_KEY_PAIR, type_); + return private_key_; +} + +void GenerateKeyResult::AssignSecretKey(const blink::WebCryptoKey& key) { + type_ = TYPE_SECRET_KEY; + secret_key_ = key; +} + +void GenerateKeyResult::AssignKeyPair(const blink::WebCryptoKey& public_key, + const blink::WebCryptoKey& private_key) { + type_ = TYPE_PUBLIC_PRIVATE_KEY_PAIR; + public_key_ = public_key; + private_key_ = private_key; +} + +void GenerateKeyResult::Complete(blink::WebCryptoResult* out) const { + switch (type_) { + case TYPE_NULL: + NOTREACHED(); + break; + case TYPE_SECRET_KEY: + out->completeWithKey(secret_key()); + break; + case TYPE_PUBLIC_PRIVATE_KEY_PAIR: + out->completeWithKeyPair(public_key(), private_key()); + break; + } +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/generate_key_result.h b/chromium/content/child/webcrypto/generate_key_result.h new file mode 100644 index 00000000000..fcc13f80855 --- /dev/null +++ b/chromium/content/child/webcrypto/generate_key_result.h @@ -0,0 +1,60 @@ +// 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_GENERATE_KEY_RESULT_H_ +#define CONTENT_CHILD_WEBCRYPTO_GENERATE_KEY_RESULT_H_ + +#include "content/common/content_export.h" +#include "third_party/WebKit/public/platform/WebCrypto.h" + +namespace content { + +namespace webcrypto { + +// This is the result object when generating keys. It encapsulates either a +// secret key, or a public/private key pair. +class CONTENT_EXPORT GenerateKeyResult { + public: + enum Type { + // An empty (or "null") result. + TYPE_NULL, + + // The result is a secret key, accessible through secret_key() + TYPE_SECRET_KEY, + + // The result is a public/private key pair, accessible through public_key() + // and private_key() + TYPE_PUBLIC_PRIVATE_KEY_PAIR + }; + + // Initializes a "null" instance. + GenerateKeyResult(); + + Type type() const; + + const blink::WebCryptoKey& secret_key() const; + const blink::WebCryptoKey& public_key() const; + const blink::WebCryptoKey& private_key() const; + + void AssignSecretKey(const blink::WebCryptoKey& key); + void AssignKeyPair(const blink::WebCryptoKey& public_key, + const blink::WebCryptoKey& private_key); + + // Sends the key(s) to the Blink result. Should not be called for "null" + // results. + void Complete(blink::WebCryptoResult* out) const; + + private: + Type type_; + + blink::WebCryptoKey secret_key_; + blink::WebCryptoKey public_key_; + blink::WebCryptoKey private_key_; +}; + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_GENERATE_KEY_RESULT_H_ diff --git a/chromium/content/child/webcrypto/jwk.cc b/chromium/content/child/webcrypto/jwk.cc index a3d65da2051..b0073cc1b8f 100644 --- a/chromium/content/child/webcrypto/jwk.cc +++ b/chromium/content/child/webcrypto/jwk.cc @@ -2,23 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "jwk.h" - -#include <algorithm> -#include <functional> -#include <map> +#include "content/child/webcrypto/jwk.h" +#include "base/base64.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" -#include "base/lazy_instance.h" +#include "base/stl_util.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 @@ -44,7 +42,7 @@ // Web Crypto Key type <-- (deduced) // Web Crypto Key extractable <-- JWK ext + input extractable // Web Crypto Key algorithm <-- JWK alg + input algorithm -// Web Crypto Key keyUsage <-- JWK use, key_ops + input usage_mask +// Web Crypto Key keyUsage <-- JWK use, key_ops + input usages // Web Crypto Key keying material <-- kty-specific parameters // // Values for each JWK entry are case-sensitive and defined in @@ -181,7 +179,7 @@ // +-------+--------------------------------------------------------------+ // // Consistency and conflict resolution -// The 'algorithm', 'extractable', and 'usage_mask' input parameters +// The 'algorithm', 'extractable', and 'usages' input parameters // may be different than the corresponding values inside the JWK. The Web // Crypto spec says that if a JWK value is present but is inconsistent with // the input value, it is an error and the operation must fail. If no @@ -197,10 +195,10 @@ // false but the input parameter is true, it is an inconsistency. If both // are true or both are false, use that value. // -// usage_mask -// The input usage_mask must be a strict subset of the interpreted JWK use -// value, else it is judged inconsistent. In all cases the input usage_mask -// is used as the final usage_mask. +// usages +// The input usages must be a strict subset of the interpreted JWK use +// value, else it is judged inconsistent. In all cases the input usages +// is used as the final usages. // namespace content { @@ -209,22 +207,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,783 +216,494 @@ const blink::WebCryptoKeyUsageMask kJwkEncUsage = const blink::WebCryptoKeyUsageMask kJwkSigUsage = blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; -typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)(); - -class JwkAlgorithmInfo { - 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) {} +// Checks that the "ext" member of the JWK is consistent with +// "expected_extractable". +Status VerifyExt(const JwkReader& jwk, bool expected_extractable) { + // JWK "ext" (optional) --> extractable parameter + bool jwk_ext_value = false; + bool has_jwk_ext; + Status status = jwk.GetOptionalBool("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(); +} - 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); +// Checks that the usages ("use" and "key_ops") of the JWK is consistent with +// "expected_usages". +Status VerifyUsages(const JwkReader& jwk, + blink::WebCryptoKeyUsageMask expected_usages) { + // JWK "key_ops" (optional) --> usages parameter + base::ListValue* jwk_key_ops_value = NULL; + bool has_jwk_key_ops; + Status status = + jwk.GetOptionalList("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 usages must be a subset of jwk_key_ops_mask. + if (!ContainsKeyUsages(jwk_key_ops_mask, expected_usages)) + return Status::ErrorJwkKeyopsInconsistent(); } - bool CreateImportAlgorithm(blink::WebCryptoAlgorithm* algorithm) const { - *algorithm = creation_func_(); - return !algorithm->isNull(); + // JWK "use" (optional) --> usages parameter + std::string jwk_use_value; + bool has_jwk_use; + status = jwk.GetOptionalString("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 usages must be a subset of jwk_use_mask. + if (!ContainsKeyUsages(jwk_use_mask, expected_usages)) + return Status::ErrorJwkUseInconsistent(); } - 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; - } + // 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(); - private: - enum { 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); - } + return Status::Success(); +} - // 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; - } +} // namespace - 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); - } +JwkReader::JwkReader() { +} - JwkAlgorithmInfoMap alg_to_info_; -}; - -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; +JwkReader::~JwkReader() { +} + +Status JwkReader::Init(const CryptoData& bytes, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usages, + const std::string& expected_kty, + const std::string& expected_alg) { + if (!bytes.byte_length()) + return Status::ErrorImportEmptyKeyData(); + + // Parse the incoming JWK JSON. + base::StringPiece json_string(reinterpret_cast<const char*>(bytes.bytes()), + bytes.byte_length()); + + scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); + base::DictionaryValue* dict_value = NULL; + + if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value) + return Status::ErrorJwkNotDictionary(); + + // 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. + std::string kty; + Status status = GetString("kty", &kty); + if (status.IsError()) + return status; + + if (kty != expected_kty) + return Status::ErrorJwkUnexpectedKty(expected_kty); + + status = VerifyExt(*this, expected_extractable); + if (status.IsError()) + return status; + + status = VerifyUsages(*this, expected_usages); + if (status.IsError()) + return status; + + // Verify the algorithm if an expectation was provided. + if (!expected_alg.empty()) { + status = VerifyAlg(expected_alg); + if (status.IsError()) + return status; } + + return Status::Success(); } -// 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. -Status GetJwkString(base::DictionaryValue* dict, - const std::string& path, - std::string* result) { +bool JwkReader::HasMember(const std::string& member_name) const { + return dict_->HasKey(member_name); +} + +Status JwkReader::GetString(const std::string& member_name, + std::string* result) const { base::Value* value = NULL; - if (!dict->Get(path, &value)) - return Status::ErrorJwkPropertyMissing(path); + if (!dict_->Get(member_name, &value)) + return Status::ErrorJwkPropertyMissing(member_name); if (!value->GetAsString(result)) - return Status::ErrorJwkPropertyWrongType(path, "string"); + return Status::ErrorJwkPropertyWrongType(member_name, "string"); return Status::Success(); } -// Extracts the optional string property with key |path| from |dict| and saves -// the result to |*result| if it was found. If the property exists and is not a -// string, returns an error. Otherwise returns success, and sets -// |*property_exists| if it was found. -Status GetOptionalJwkString(base::DictionaryValue* dict, - const std::string& path, - std::string* result, - bool* property_exists) { - *property_exists = false; +Status JwkReader::GetOptionalString(const std::string& member_name, + std::string* result, + bool* member_exists) const { + *member_exists = false; base::Value* value = NULL; - if (!dict->Get(path, &value)) + if (!dict_->Get(member_name, &value)) return Status::Success(); if (!value->GetAsString(result)) - return Status::ErrorJwkPropertyWrongType(path, "string"); + return Status::ErrorJwkPropertyWrongType(member_name, "string"); - *property_exists = true; + *member_exists = true; return Status::Success(); } -// Extracts the optional array property with key |path| from |dict| and saves -// the result to |*result| if it was found. If the property exists and is not an -// array, returns an error. Otherwise returns success, and sets -// |*property_exists| if it was found. Note that |*result| is owned by |dict|. -Status GetOptionalJwkList(base::DictionaryValue* dict, - const std::string& path, - base::ListValue** result, - bool* property_exists) { - *property_exists = false; +Status JwkReader::GetOptionalList(const std::string& member_name, + base::ListValue** result, + bool* member_exists) const { + *member_exists = false; base::Value* value = NULL; - if (!dict->Get(path, &value)) + if (!dict_->Get(member_name, &value)) return Status::Success(); if (!value->GetAsList(result)) - return Status::ErrorJwkPropertyWrongType(path, "list"); + return Status::ErrorJwkPropertyWrongType(member_name, "list"); - *property_exists = true; + *member_exists = true; return Status::Success(); } -// Extracts the required string property with key |path| from |dict| and saves -// the base64url-decoded bytes to |*result|. If the property does not exist or -// is not a string, or could not be base64url-decoded, returns an error. -Status GetJwkBytes(base::DictionaryValue* dict, - const std::string& path, - std::string* result) { +Status JwkReader::GetBytes(const std::string& member_name, + std::string* result) const { std::string base64_string; - Status status = GetJwkString(dict, path, &base64_string); + Status status = GetString(member_name, &base64_string); if (status.IsError()) return status; if (!Base64DecodeUrlSafe(base64_string, result)) - return Status::ErrorJwkBase64Decode(path); + return Status::ErrorJwkBase64Decode(member_name); return Status::Success(); } -// Extracts the optional string property with key |path| from |dict| and saves -// the base64url-decoded bytes to |*result|. If the property exist and is not a -// string, or could not be base64url-decoded, returns an error. In the case -// where the property does not exist, |result| is guaranteed to be empty. -Status GetOptionalJwkBytes(base::DictionaryValue* dict, - const std::string& path, - std::string* result, - bool* property_exists) { - std::string base64_string; - Status status = - GetOptionalJwkString(dict, path, &base64_string, property_exists); +Status JwkReader::GetBigInteger(const std::string& member_name, + std::string* result) const { + Status status = GetBytes(member_name, result); if (status.IsError()) return status; - if (!*property_exists) { - result->clear(); - return Status::Success(); - } + if (result->empty()) + return Status::ErrorJwkEmptyBigInteger(member_name); - if (!Base64DecodeUrlSafe(base64_string, result)) - return Status::ErrorJwkBase64Decode(path); + // The JWA spec says that "The octet sequence MUST utilize the minimum number + // of octets to represent the value." This means there shouldn't be any + // leading zeros. + if (result->size() > 1 && (*result)[0] == 0) + return Status::ErrorJwkBigIntegerHasLeadingZero(member_name); return Status::Success(); } -// Extracts the optional boolean property with key |path| from |dict| and saves -// the result to |*result| if it was found. If the property exists and is not a -// boolean, returns an error. Otherwise returns success, and sets -// |*property_exists| if it was found. -Status GetOptionalJwkBool(base::DictionaryValue* dict, - const std::string& path, - bool* result, - bool* property_exists) { - *property_exists = false; +Status JwkReader::GetOptionalBool(const std::string& member_name, + bool* result, + bool* member_exists) const { + *member_exists = false; base::Value* value = NULL; - if (!dict->Get(path, &value)) + if (!dict_->Get(member_name, &value)) return Status::Success(); if (!value->GetAsBoolean(result)) - return Status::ErrorJwkPropertyWrongType(path, "boolean"); + return Status::ErrorJwkPropertyWrongType(member_name, "boolean"); - *property_exists = true; + *member_exists = true; 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)); +Status JwkReader::GetAlg(std::string* alg, bool* has_alg) const { + return GetOptionalString("alg", alg, has_alg); } -// 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)); -} - -// 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); - if (status.IsError()) - return status; - - // 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); +Status JwkReader::VerifyAlg(const std::string& expected_alg) const { + bool has_jwk_alg; + std::string jwk_alg_value; + Status status = GetAlg(&jwk_alg_value, &has_jwk_alg); if (status.IsError()) return status; - 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 (has_jwk_alg && jwk_alg_value != expected_alg) + return Status::ErrorJwkAlgorithmInconsistent(); 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)); -} - -// Writes a Web Crypto extractable value to a JWK dictionary. -void WriteExt(bool extractable, base::DictionaryValue* jwk_dict) { - jwk_dict->SetBoolean("ext", extractable); -} - -// 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(); +JwkWriter::JwkWriter(const std::string& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + const std::string& kty) { + dict_.SetString("alg", algorithm); + dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usages)); + dict_.SetBoolean("ext", extractable); + dict_.SetString("kty", kty); } -bool IsRsaKey(const blink::WebCryptoKey& key) { - return IsAlgorithmRsa(key.algorithm().id()); +void JwkWriter::SetString(const std::string& member_name, + const std::string& value) { + dict_.SetString(member_name, value); } -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; - - bool is_public_key = !dict->HasKey("d"); +void JwkWriter::SetBytes(const std::string& member_name, + const CryptoData& value) { + dict_.SetString( + member_name, + Base64EncodeUrlSafe(base::StringPiece( + reinterpret_cast<const char*>(value.bytes()), value.byte_length()))); +} - // 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); +void JwkWriter::ToJson(std::vector<uint8_t>* utf8_bytes) const { + std::string json; + base::JSONWriter::Write(&dict_, &json); + utf8_bytes->assign(json.begin(), json.end()); +} +Status ReadSecretKeyNoExpectedAlg(const CryptoData& key_data, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usages, + std::vector<uint8_t>* raw_key_data, + JwkReader* jwk) { + Status status = jwk->Init( + key_data, expected_extractable, expected_usages, "oct", std::string()); 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); - } - - std::string jwk_d_value; - status = GetJwkBytes(dict, "d", &jwk_d_value); + std::string jwk_k_value; + status = jwk->GetBytes("k", &jwk_k_value); if (status.IsError()) return status; + raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end()); - // 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); - if (status.IsError()) - return status; +void WriteSecretKeyJwk(const CryptoData& raw_key_data, + const std::string& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + std::vector<uint8_t>* jwk_key_data) { + JwkWriter writer(algorithm, extractable, usages, "oct"); + writer.SetBytes("k", raw_key_data); + writer.ToJson(jwk_key_data); +} - std::string jwk_q_value; - bool has_q; - status = GetOptionalJwkBytes(dict, "q", &jwk_q_value, &has_q); +Status ReadSecretKeyJwk(const CryptoData& key_data, + const std::string& expected_alg, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usages, + std::vector<uint8_t>* raw_key_data) { + JwkReader jwk; + Status status = ReadSecretKeyNoExpectedAlg( + key_data, expected_extractable, expected_usages, raw_key_data, &jwk); if (status.IsError()) return status; + return jwk.VerifyAlg(expected_alg); +} - std::string jwk_dp_value; - bool has_dp; - status = GetOptionalJwkBytes(dict, "dp", &jwk_dp_value, &has_dp); - if (status.IsError()) - return status; +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(); +} - std::string jwk_dq_value; - bool has_dq; - status = GetOptionalJwkBytes(dict, "dq", &jwk_dq_value, &has_dq); +Status ReadAesSecretKeyJwk(const CryptoData& key_data, + const std::string& algorithm_name_suffix, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usages, + std::vector<uint8_t>* raw_key_data) { + JwkReader jwk; + Status status = ReadSecretKeyNoExpectedAlg( + key_data, expected_extractable, expected_usages, raw_key_data, &jwk); 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 = jwk.GetAlg(&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(); - - 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); + 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 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 usages, + std::vector<uint8_t>* jwk_key_data) { + JwkWriter writer(algorithm, extractable, usages, "RSA"); + writer.SetBytes("n", n); + writer.SetBytes("e", e); + writer.ToJson(jwk_key_data); +} -// 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) { - if (!key_data.byte_length()) - return Status::ErrorImportEmptyKeyData(); - DCHECK(key); +// 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 usages, + std::vector<uint8_t>* jwk_key_data) { + JwkWriter writer(algorithm, extractable, usages, "RSA"); + + writer.SetBytes("n", n); + writer.SetBytes("e", e); + writer.SetBytes("d", d); + // Although these are "optional" in the JWA, WebCrypto spec requires them to + // be emitted. + writer.SetBytes("p", p); + writer.SetBytes("q", q); + writer.SetBytes("dp", dp); + writer.SetBytes("dq", dq); + writer.SetBytes("qi", qi); + writer.ToJson(jwk_key_data); +} - // 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(); +JwkRsaInfo::JwkRsaInfo() : is_private_key(false) { +} - // 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); +JwkRsaInfo::~JwkRsaInfo() { +} + +Status ReadRsaKeyJwk(const CryptoData& key_data, + const std::string& expected_alg, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usages, + JwkRsaInfo* result) { + JwkReader jwk; + Status status = jwk.Init( + key_data, expected_extractable, expected_usages, "RSA", expected_alg); 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(); - } - - // 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 = jwk.GetBigInteger("n", &result->n); + if (status.IsError()) + return status; + status = jwk.GetBigInteger("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 = jwk.HasMember("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 = jwk.GetBigInteger("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 in the JWA + // spec. However they are required by Chromium's WebCrypto implementation. - // 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); + status = jwk.GetBigInteger("p", &result->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); + status = jwk.GetBigInteger("q", &result->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; - - // 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(); - } + status = jwk.GetBigInteger("dp", &result->dp); + if (status.IsError()) + return status; - return ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(jwk_k_value), - algorithm, - extractable, - usage_mask, - key); - } + status = jwk.GetBigInteger("dq", &result->dq); + if (status.IsError()) + return status; - if (jwk_kty_value == "RSA") - return ImportRsaKey(dict_value, algorithm, extractable, usage_mask, key); + status = jwk.GetBigInteger("qi", &result->qi); + if (status.IsError()) + return status; - 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; +// TODO(eroman): This accepts invalid inputs. http://crbug.com/378034 +bool Base64DecodeUrlSafe(const std::string& input, std::string* output) { + std::string base64_encoded_text(input); + std::replace( + base64_encoded_text.begin(), base64_encoded_text.end(), '-', '+'); + std::replace( + base64_encoded_text.begin(), base64_encoded_text.end(), '_', '/'); + base64_encoded_text.append((4 - base64_encoded_text.size() % 4) % 4, '='); + return base::Base64Decode(base64_encoded_text, output); +} - std::string json; - base::JSONWriter::Write(&jwk_dict, &json); - buffer->assign(json.data(), json.data() + json.size()); - return Status::Success(); +std::string Base64EncodeUrlSafe(const base::StringPiece& input) { + std::string output; + base::Base64Encode(input, &output); + std::replace(output.begin(), output.end(), '+', '-'); + std::replace(output.begin(), output.end(), '/', '_'); + output.erase(std::remove(output.begin(), output.end(), '='), output.end()); + return output; +} + +std::string Base64EncodeUrlSafe(const std::vector<uint8_t>& input) { + const base::StringPiece string_piece( + reinterpret_cast<const char*>(vector_as_array(&input)), input.size()); + return Base64EncodeUrlSafe(string_piece); } } // namespace webcrypto diff --git a/chromium/content/child/webcrypto/jwk.h b/chromium/content/child/webcrypto/jwk.h index c9191888256..cb6866df178 100644 --- a/chromium/content/child/webcrypto/jwk.h +++ b/chromium/content/child/webcrypto/jwk.h @@ -5,12 +5,13 @@ #ifndef CONTENT_CHILD_WEBCRYPTO_JWK_H_ #define CONTENT_CHILD_WEBCRYPTO_JWK_H_ +#include <stdint.h> #include <vector> -#include "base/basictypes.h" -#include "third_party/WebKit/public/platform/WebArrayBuffer.h" +#include "base/strings/string_piece.h" +#include "base/values.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 { @@ -19,13 +20,223 @@ 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); +// Helper class for parsing a JWK from JSON. +// +// This primarily exists to ensure strict enforcement of the JWK schema, as the +// type and presence of particular members is security relevant. For example, +// GetString() will ensure a given JSON member is present and is a string type, +// and will fail if these conditions aren't met. +// +// Users of JwkReader must call Init() successfully before any other method can +// be called. +class JwkReader { + public: + JwkReader(); + ~JwkReader(); -Status ExportKeyJwk(const blink::WebCryptoKey& key, std::vector<uint8>* buffer); + // Initializes a JWK reader by parsing the JSON |bytes|. To succeed, the JWK + // must: + // * Have "kty" matching |expected_kty| + // * Have "ext" compatible with |expected_extractable| + // * Have usages ("use", "key_ops") compatible with |expected_usages| + // * Have an "alg" matching |expected_alg| + // + // NOTE: If |expected_alg| is empty, then the test on "alg" is skipped. + Status Init(const CryptoData& bytes, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usages, + const std::string& expected_kty, + const std::string& expected_alg); + + // Returns true if the member |member_name| is present. + bool HasMember(const std::string& member_name) const; + + // Extracts the required string member |member_name| and saves the result to + // |*result|. If the member does not exist or is not a string, returns an + // error. + Status GetString(const std::string& member_name, std::string* result) const; + + // Extracts the optional string member |member_name| and saves the result to + // |*result| if it was found. If the member exists and is not a string, + // returns an error. Otherwise returns success, and sets |*member_exists| if + // it was found. + Status GetOptionalString(const std::string& member_name, + std::string* result, + bool* member_exists) const; + + // Extracts the optional array member |member_name| and saves the result to + // |*result| if it was found. If the member exists and is not an array, + // returns an error. Otherwise returns success, and sets |*member_exists| if + // it was found. + // + // NOTE: |*result| is owned by the JwkReader. + Status GetOptionalList(const std::string& member_name, + base::ListValue** result, + bool* member_exists) const; + + // Extracts the required string member |member_name| and saves the + // base64url-decoded bytes to |*result|. If the member does not exist or is + // not a string, or could not be base64url-decoded, returns an error. + Status GetBytes(const std::string& member_name, std::string* result) const; + + // Extracts the required base64url member, which is interpreted as being a + // big-endian unsigned integer. + // + // Sequences that contain leading zeros will be rejected. + Status GetBigInteger(const std::string& member_name, + std::string* result) const; + + // Extracts the optional boolean member |member_name| and saves the result to + // |*result| if it was found. If the member exists and is not a boolean, + // returns an error. Otherwise returns success, and sets |*member_exists| if + // it was found. + Status GetOptionalBool(const std::string& member_name, + bool* result, + bool* member_exists) const; + + // Gets the optional algorithm ("alg") string. + Status GetAlg(std::string* alg, bool* has_alg) const; + + // Checks if the "alg" member matches |expected_alg|. + Status VerifyAlg(const std::string& expected_alg) const; + + private: + scoped_ptr<base::DictionaryValue> dict_; +}; + +// Helper class for building the JSON for a JWK. +class JwkWriter { + public: + // Initializes a writer, and sets the standard JWK members as indicated. + JwkWriter(const std::string& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + const std::string& kty); + + // Sets a string member |member_name| to |value|. + void SetString(const std::string& member_name, const std::string& value); + + // Sets a bytes member |value| to |value| by base64 url-safe encoding it. + void SetBytes(const std::string& member_name, const CryptoData& value); + + // Flattens the JWK to JSON (UTF-8 encoded if necessary, however in practice + // it will be ASCII). + void ToJson(std::vector<uint8_t>* utf8_bytes) const; + + private: + base::DictionaryValue dict_; +}; + +// 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") +// * usages: The JWK usages (i.e. "key_ops") +void WriteSecretKeyJwk(const CryptoData& raw_key_data, + const std::string& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + std::vector<uint8_t>* jwk_key_data); + +// 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_alg must match the JWK's "alg", if present. +// * expected_extractable must be consistent with the JWK's "ext", if +// present. +// * expected_usages must be a subset of the JWK's "key_ops" if present. +Status ReadSecretKeyJwk(const CryptoData& key_data, + const std::string& expected_alg, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usages, + std::vector<uint8_t>* 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 +// absolute "expected_alg", 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_usages, + std::vector<uint8_t>* 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 usages, + std::vector<uint8_t>* 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 usages, + std::vector<uint8_t>* 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_alg must match the JWK's "alg", if present. +// * expected_extractable must be consistent with the JWK's "ext", if +// present. +// * expected_usages must be a subset of the JWK's "key_ops" if present. +Status ReadRsaKeyJwk(const CryptoData& key_data, + const std::string& expected_alg, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usages, + JwkRsaInfo* result); + +const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash); + +// This function decodes unpadded 'base64url' encoded data, as described in +// RFC4648 (http://www.ietf.org/rfc/rfc4648.txt) Section 5. +CONTENT_EXPORT bool Base64DecodeUrlSafe(const std::string& input, + std::string* output); + +// Returns an unpadded 'base64url' encoding of the input data, the opposite of +// Base64DecodeUrlSafe() above. +CONTENT_EXPORT std::string Base64EncodeUrlSafe(const base::StringPiece& input); +CONTENT_EXPORT std::string Base64EncodeUrlSafe( + const std::vector<uint8_t>& input); } // namespace webcrypto diff --git a/chromium/content/child/webcrypto/nss/aes_cbc_nss.cc b/chromium/content/child/webcrypto/nss/aes_cbc_nss.cc new file mode 100644 index 00000000000..abe371802b2 --- /dev/null +++ b/chromium/content/child/webcrypto/nss/aes_cbc_nss.cc @@ -0,0 +1,128 @@ +// 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 "base/numerics/safe_math.h" +#include "base/stl_util.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_t>* 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. + base::CheckedNumeric<int> output_max_len = data.byte_length(); + output_max_len += AES_BLOCK_SIZE; + if (!output_max_len.IsValid()) { + // 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. + buffer->resize(output_max_len.ValueOrDie()); + + unsigned char* buffer_data = vector_as_array(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).ValueOrDie())) { + 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") {} + + Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + return AesCbcEncryptDecrypt(ENCRYPT, algorithm, key, data, buffer); + } + + Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + return AesCbcEncryptDecrypt(DECRYPT, algorithm, key, data, buffer); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformAesCbcImplementation() { + return new AesCbcImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/nss/aes_gcm_nss.cc b/chromium/content/child/webcrypto/nss/aes_gcm_nss.cc new file mode 100644 index 00000000000..242fdfd48c4 --- /dev/null +++ b/chromium/content/child/webcrypto/nss/aes_gcm_nss.cc @@ -0,0 +1,191 @@ +// 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/numerics/safe_math.h" +#include "base/stl_util.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 "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_t>* 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); + + base::CheckedNumeric<unsigned int> buffer_size(data.byte_length()); + + // Calculate the output buffer size. + if (mode == ENCRYPT) { + buffer_size += tag_length_bytes; + if (!buffer_size.IsValid()) + return Status::ErrorDataTooLarge(); + } + + // 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->resize(buffer_size.ValueOrDie()); + unsigned char* buffer_data = vector_as_array(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") {} + + Status VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usages) const override { + // Prevent importing AES-GCM keys if it is unavailable. + Status status = NssSupportsAesGcm(); + if (status.IsError()) + return status; + return AesAlgorithm::VerifyKeyUsagesBeforeImportKey(format, usages); + } + + Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + GenerateKeyResult* result) const override { + // Prevent generating AES-GCM keys if it is unavailable. + Status status = NssSupportsAesGcm(); + if (status.IsError()) + return status; + + return AesAlgorithm::GenerateKey(algorithm, extractable, usages, result); + } + + Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + return AesGcmEncryptDecrypt(ENCRYPT, algorithm, key, data, buffer); + } + + Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + return AesGcmEncryptDecrypt(DECRYPT, algorithm, key, data, buffer); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformAesGcmImplementation() { + return new AesGcmImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/nss/aes_key_nss.cc b/chromium/content/child/webcrypto/nss/aes_key_nss.cc new file mode 100644 index 00000000000..2bd0c57e396 --- /dev/null +++ b/chromium/content/child/webcrypto/nss/aes_key_nss.cc @@ -0,0 +1,134 @@ +// 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::GenerateKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + GenerateKeyResult* result) const { + Status status = CheckKeyCreationUsages(all_key_usages_, usages); + if (status.IsError()) + return status; + + unsigned int keylen_bits; + status = GetAesKeyGenLengthInBits(algorithm.aesKeyGenParams(), &keylen_bits); + if (status.IsError()) + return status; + + return GenerateSecretKeyNss( + blink::WebCryptoKeyAlgorithm::createAes(algorithm.id(), keylen_bits), + extractable, + usages, + keylen_bits / 8, + CKM_AES_KEY_GEN, + result); +} + +Status AesAlgorithm::VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usages) const { + switch (format) { + case blink::WebCryptoKeyFormatRaw: + case blink::WebCryptoKeyFormatJwk: + return CheckKeyCreationUsages(all_key_usages_, usages); + default: + return Status::ErrorUnsupportedImportKeyFormat(); + } +} +Status AesAlgorithm::ImportKeyRaw(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + 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, + usages, + import_mechanism_, + import_flags_, + key); +} + +Status AesAlgorithm::ImportKeyJwk(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const { + std::vector<uint8_t> raw_data; + Status status = ReadAesSecretKeyJwk( + key_data, jwk_suffix_, extractable, usages, &raw_data); + if (status.IsError()) + return status; + + return ImportKeyRaw( + CryptoData(raw_data), algorithm, extractable, usages, key); +} + +Status AesAlgorithm::ExportKeyRaw(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const { + *buffer = SymKeyNss::Cast(key)->raw_key_data(); + return Status::Success(); +} + +Status AesAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const { + SymKeyNss* sym_key = SymKeyNss::Cast(key); + const std::vector<uint8_t>& 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/chromium/content/child/webcrypto/nss/aes_key_nss.h b/chromium/content/child/webcrypto/nss/aes_key_nss.h new file mode 100644 index 00000000000..5e4cba35e42 --- /dev/null +++ b/chromium/content/child/webcrypto/nss/aes_key_nss.h @@ -0,0 +1,77 @@ +// 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); + + Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + GenerateKeyResult* result) const override; + + Status VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usages) const override; + + Status ImportKeyRaw(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const override; + + Status ImportKeyJwk(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const override; + + Status ExportKeyRaw(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const override; + + Status ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8_t>* 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/chromium/content/child/webcrypto/nss/aes_kw_nss.cc b/chromium/content/child/webcrypto/nss/aes_kw_nss.cc new file mode 100644 index 00000000000..76d3337d51b --- /dev/null +++ b/chromium/content/child/webcrypto/nss/aes_kw_nss.cc @@ -0,0 +1,206 @@ +// 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 "base/numerics/safe_math.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_t>* 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); + + 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(); + + base::CheckedNumeric<unsigned int> output_length = input_length; + output_length += 8; + if (!output_length.IsValid()) + return Status::ErrorDataTooLarge(); + + buffer->resize(output_length.ValueOrDie()); + 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.ValueOrDie() != 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") {} + + Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& wrapping_key, + const CryptoData& data, + std::vector<uint8_t>* 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); + } + + Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& wrapping_key, + const CryptoData& data, + std::vector<uint8_t>* 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/chromium/content/child/webcrypto/nss/hmac_nss.cc b/chromium/content/child/webcrypto/nss/hmac_nss.cc new file mode 100644 index 00000000000..c3424fb94f4 --- /dev/null +++ b/chromium/content/child/webcrypto/nss/hmac_nss.cc @@ -0,0 +1,240 @@ +// 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 "base/numerics/safe_math.h" +#include "base/stl_util.h" +#include "content/child/webcrypto/algorithm_implementation.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() {} + + Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + GenerateKeyResult* result) const override { + Status status = CheckKeyCreationUsages(kAllKeyUsages, usages); + if (status.IsError()) + return status; + + 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 = GetHmacKeyGenLengthInBits(params, &keylen_bits); + if (status.IsError()) + return status; + + return GenerateSecretKeyNss( + blink::WebCryptoKeyAlgorithm::createHmac(hash.id(), keylen_bits), + extractable, + usages, + keylen_bits / 8, + mechanism, + result); + } + + Status VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usages) const override { + switch (format) { + case blink::WebCryptoKeyFormatRaw: + case blink::WebCryptoKeyFormatJwk: + return CheckKeyCreationUsages(kAllKeyUsages, usages); + default: + return Status::ErrorUnsupportedImportKeyFormat(); + } + } + + Status ImportKeyRaw(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + 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(); + + base::CheckedNumeric<unsigned int> keylen_bits(key_data.byte_length()); + keylen_bits *= 8; + + if (!keylen_bits.IsValid()) + return Status::ErrorDataTooLarge(); + + return ImportKeyRawNss(key_data, + blink::WebCryptoKeyAlgorithm::createHmac( + hash.id(), keylen_bits.ValueOrDie()), + extractable, + usages, + mechanism, + CKF_SIGN | CKF_VERIFY, + key); + } + + Status ImportKeyJwk(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const override { + const char* algorithm_name = + GetJwkHmacAlgorithmName(algorithm.hmacImportParams()->hash().id()); + if (!algorithm_name) + return Status::ErrorUnexpected(); + + std::vector<uint8_t> raw_data; + Status status = ReadSecretKeyJwk( + key_data, algorithm_name, extractable, usages, &raw_data); + if (status.IsError()) + return status; + + return ImportKeyRaw( + CryptoData(raw_data), algorithm, extractable, usages, key); + } + + Status ExportKeyRaw(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const override { + *buffer = SymKeyNss::Cast(key)->raw_key_data(); + return Status::Success(); + } + + Status ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const override { + SymKeyNss* sym_key = SymKeyNss::Cast(key); + const std::vector<uint8_t>& 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(); + } + + Status Sign(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* 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 = vector_as_array(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(); + } + + Status Verify(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& signature, + const CryptoData& data, + bool* signature_match) const override { + std::vector<uint8_t> 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(vector_as_array(&result), + signature.bytes(), + signature.byte_length()); + + return Status::Success(); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformHmacImplementation() { + return new HmacImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/nss/key_nss.cc b/chromium/content/child/webcrypto/nss/key_nss.cc new file mode 100644 index 00000000000..9193963fb59 --- /dev/null +++ b/chromium/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_t>* 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/chromium/content/child/webcrypto/nss/key_nss.h b/chromium/content/child/webcrypto/nss/key_nss.h new file mode 100644 index 00000000000..9eaf7c427b0 --- /dev/null +++ b/chromium/content/child/webcrypto/nss/key_nss.h @@ -0,0 +1,109 @@ +// 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 <stdint.h> +#include <vector> + +#include "crypto/scoped_nss_types.h" +#include "third_party/WebKit/public/platform/WebCryptoKey.h" + +namespace content { + +namespace webcrypto { + +class CryptoData; +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); + ~KeyNss() override; + + virtual SymKeyNss* AsSymKey(); + virtual PublicKeyNss* AsPublicKey(); + virtual PrivateKeyNss* AsPrivateKey(); + + const std::vector<uint8_t>& serialized_key_data() const { + return serialized_key_data_; + } + + private: + const std::vector<uint8_t> serialized_key_data_; +}; + +class SymKeyNss : public KeyNss { + public: + ~SymKeyNss() override; + SymKeyNss(crypto::ScopedPK11SymKey key, const CryptoData& raw_key_data); + + static SymKeyNss* Cast(const blink::WebCryptoKey& key); + + PK11SymKey* key() { return key_.get(); } + SymKeyNss* AsSymKey() override; + + const std::vector<uint8_t>& raw_key_data() const { + return serialized_key_data(); + } + + private: + crypto::ScopedPK11SymKey key_; + + DISALLOW_COPY_AND_ASSIGN(SymKeyNss); +}; + +class PublicKeyNss : public KeyNss { + public: + ~PublicKeyNss() override; + PublicKeyNss(crypto::ScopedSECKEYPublicKey key, const CryptoData& spki_data); + + static PublicKeyNss* Cast(const blink::WebCryptoKey& key); + + SECKEYPublicKey* key() { return key_.get(); } + PublicKeyNss* AsPublicKey() override; + + const std::vector<uint8_t>& spki_data() const { + return serialized_key_data(); + } + + private: + crypto::ScopedSECKEYPublicKey key_; + + DISALLOW_COPY_AND_ASSIGN(PublicKeyNss); +}; + +class PrivateKeyNss : public KeyNss { + public: + ~PrivateKeyNss() override; + PrivateKeyNss(crypto::ScopedSECKEYPrivateKey key, + const CryptoData& pkcs8_data); + + static PrivateKeyNss* Cast(const blink::WebCryptoKey& key); + + SECKEYPrivateKey* key() { return key_.get(); } + PrivateKeyNss* AsPrivateKey() override; + + const std::vector<uint8_t>& 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/chromium/content/child/webcrypto/nss/rsa_key_nss.cc b/chromium/content/child/webcrypto/nss/rsa_key_nss.cc new file mode 100644 index 00000000000..f9619a62a16 --- /dev/null +++ b/chromium/content/child/webcrypto/nss/rsa_key_nss.cc @@ -0,0 +1,852 @@ +// 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/generate_key_result.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 { + +#if defined(USE_NSS) && !defined(OS_CHROMEOS) +Status ErrorRsaPrivateKeyImportNotSupported() { + return Status::ErrorUnsupported( + "NSS version must be at least 3.16.2 for RSA private 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 NssSupportsRsaPrivateKeyImport() { + if (!NSS_VersionCheck("3.16.2")) + return ErrorRsaPrivateKeyImportNotSupported(); + + // 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 ErrorRsaPrivateKeyImportNotSupported(); + + // 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 ErrorRsaPrivateKeyImportNotSupported(); +} +#else +Status NssSupportsRsaPrivateKeyImport() { + return Status::Success(); +} +#endif + +bool CreateRsaHashedPublicKeyAlgorithm( + blink::WebCryptoAlgorithmId rsa_algorithm, + blink::WebCryptoAlgorithmId hash_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); + + *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed( + rsa_algorithm, + modulus_length_bits, + public_exponent.bytes(), + public_exponent.byte_length(), + hash_algorithm); + return true; +} + +bool CreateRsaHashedPrivateKeyAlgorithm( + blink::WebCryptoAlgorithmId rsa_algorithm, + blink::WebCryptoAlgorithmId hash_algorithm, + SECKEYPrivateKey* key, + blink::WebCryptoKeyAlgorithm* key_algorithm) { + crypto::ScopedSECKEYPublicKey public_key(SECKEY_ConvertToPublicKey(key)); + if (!public_key) + return false; + return CreateRsaHashedPublicKeyAlgorithm( + rsa_algorithm, hash_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); +} + +void AddAttribute(CK_ATTRIBUTE_TYPE type, + const CryptoData& data, + std::vector<CK_ATTRIBUTE>* templ) { + CK_ATTRIBUTE attribute = {type, const_cast<unsigned char*>(data.bytes()), + data.byte_length()}; + templ->push_back(attribute); +} + +void AddAttribute(CK_ATTRIBUTE_TYPE type, + const std::string& data, + std::vector<CK_ATTRIBUTE>* templ) { + AddAttribute(type, CryptoData(data), templ); +} + +Status ExportKeyPkcs8Nss(SECKEYPrivateKey* key, std::vector<uint8_t>* 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 usages, + const JwkRsaInfo& params, + blink::WebCryptoKey* key) { + Status status = NssSupportsRsaPrivateKeyImport(); + 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 by JWA. + AddAttribute(CKA_MODULUS, params.n, &key_template); + AddAttribute(CKA_PUBLIC_EXPONENT, params.e, &key_template); + AddAttribute(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)); + AddAttribute( + CKA_ID, CryptoData(object_id->data, object_id->len), &key_template); + + // Optional properties by JWA, however guaranteed to be present by Chromium's + // implementation. + AddAttribute(CKA_PRIME_1, params.p, &key_template); + AddAttribute(CKA_PRIME_2, params.q, &key_template); + AddAttribute(CKA_EXPONENT_1, params.dp, &key_template); + AddAttribute(CKA_EXPONENT_2, params.dq, &key_template); + AddAttribute(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 (!CreateRsaHashedPrivateKeyAlgorithm( + algorithm.id(), + algorithm.rsaHashedImportParams()->hash().id(), + private_key.get(), + &key_algorithm)) { + return Status::ErrorUnexpected(); + } + + std::vector<uint8_t> 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, + usages); + return Status::Success(); +} + +Status ExportKeySpkiNss(SECKEYPublicKey* key, std::vector<uint8_t>* 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 usages, + 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 (!CreateRsaHashedPublicKeyAlgorithm( + algorithm.id(), + algorithm.rsaHashedImportParams()->hash().id(), + pubkey.get(), + &key_algorithm)) { + return Status::ErrorUnexpected(); + } + + std::vector<uint8_t> 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, + usages); + return Status::Success(); +} + +} // namespace + +Status RsaHashedAlgorithm::GenerateKey( + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask combined_usages, + GenerateKeyResult* result) const { + Status status = CheckKeyCreationUsages( + all_public_key_usages_ | all_private_key_usages_, combined_usages); + if (status.IsError()) + return status; + + const blink::WebCryptoKeyUsageMask public_usages = + combined_usages & all_public_key_usages_; + const blink::WebCryptoKeyUsageMask private_usages = + combined_usages & all_private_key_usages_; + + unsigned int public_exponent = 0; + unsigned int modulus_length_bits = 0; + status = GetRsaKeyGenParameters(algorithm.rsaHashedKeyGenParams(), + &public_exponent, + &modulus_length_bits); + if (status.IsError()) + return status; + + crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); + if (!slot) + return Status::OperationError(); + + PK11RSAGenParams rsa_gen_params; + rsa_gen_params.keySizeInBits = modulus_length_bits; + 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 (!CreateRsaHashedPublicKeyAlgorithm( + algorithm.id(), + algorithm.rsaHashedKeyGenParams()->hash().id(), + sec_public_key, + &key_algorithm)) { + return Status::ErrorUnexpected(); + } + + std::vector<uint8_t> spki_data; + 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_t> 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))); + + blink::WebCryptoKey public_key = + blink::WebCryptoKey::create(public_key_handle.release(), + blink::WebCryptoKeyTypePublic, + true, + key_algorithm, + public_usages); + + blink::WebCryptoKey private_key = + blink::WebCryptoKey::create(private_key_handle.release(), + blink::WebCryptoKeyTypePrivate, + extractable, + key_algorithm, + private_usages); + + result->AssignKeyPair(public_key, private_key); + return Status::Success(); +} + +Status RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usages) const { + switch (format) { + case blink::WebCryptoKeyFormatSpki: + return CheckKeyCreationUsages(all_public_key_usages_, usages); + case blink::WebCryptoKeyFormatPkcs8: + return CheckKeyCreationUsages(all_private_key_usages_, usages); + case blink::WebCryptoKeyFormatJwk: + // The JWK could represent either a public key or private key. The usages + // must make sense for one of the two. The usages will be checked again by + // ImportKeyJwk() once the key type has been determined. + if (CheckKeyCreationUsages(all_private_key_usages_, usages) + .IsSuccess() || + CheckKeyCreationUsages(all_public_key_usages_, usages) + .IsSuccess()) { + return Status::Success(); + } + return Status::ErrorCreateKeyBadUsages(); + default: + return Status::ErrorUnsupportedImportKeyFormat(); + } +} + +Status RsaHashedAlgorithm::ImportKeyPkcs8( + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const { + Status status = NssSupportsRsaPrivateKeyImport(); + 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.id(), + algorithm.rsaHashedImportParams()->hash().id(), + private_key.get(), + &key_algorithm)) { + return Status::ErrorUnexpected(); + } + + // TODO(eroman): This is probably going to be the same as the input. + std::vector<uint8_t> 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, + usages); + + return Status::Success(); +} + +Status RsaHashedAlgorithm::ImportKeySpki( + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const { + 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.id(), + algorithm.rsaHashedImportParams()->hash().id(), + 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_t> spki_data; + Status 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, + usages); + + return Status::Success(); +} + +Status RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey& key, + std::vector<uint8_t>* 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_t>* 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 usages, + 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, usages, &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_, + usages); + if (status.IsError()) + return Status::ErrorCreateKeyBadUsages(); + + return jwk.is_private_key + ? ImportRsaPrivateKey(algorithm, extractable, usages, jwk, key) + : ImportRsaPublicKey(algorithm, + extractable, + usages, + CryptoData(jwk.n), + CryptoData(jwk.e), + key); +} + +Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8_t>* 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/chromium/content/child/webcrypto/nss/rsa_key_nss.h b/chromium/content/child/webcrypto/nss/rsa_key_nss.h new file mode 100644 index 00000000000..725a25e837b --- /dev/null +++ b/chromium/content/child/webcrypto/nss/rsa_key_nss.h @@ -0,0 +1,92 @@ +// 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; + + Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + GenerateKeyResult* result) const override; + + Status VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usages) const override; + + Status ImportKeyPkcs8(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const override; + + Status ImportKeySpki(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const override; + + Status ExportKeyPkcs8(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const override; + + Status ExportKeySpki(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const override; + + Status ImportKeyJwk(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const override; + + Status ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8_t>* 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/chromium/content/child/webcrypto/nss/rsa_oaep_nss.cc b/chromium/content/child/webcrypto/nss/rsa_oaep_nss.cc new file mode 100644 index 00000000000..f7484f6503a --- /dev/null +++ b/chromium/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 "base/stl_util.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_t>* 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 = vector_as_array(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_t>* 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 = vector_as_array(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) {} + + Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + GenerateKeyResult* result) const override { + Status status = NssSupportsRsaOaep(); + if (status.IsError()) + return status; + return RsaHashedAlgorithm::GenerateKey( + algorithm, extractable, usages, result); + } + + Status VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usages) const override { + Status status = NssSupportsRsaOaep(); + if (status.IsError()) + return status; + return RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey(format, usages); + } + + 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; + } + } + + Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* 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); + } + + Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* 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/chromium/content/child/webcrypto/nss/rsa_ssa_nss.cc b/chromium/content/child/webcrypto/nss/rsa_ssa_nss.cc new file mode 100644 index 00000000000..471aaedb4b3 --- /dev/null +++ b/chromium/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) {} + + 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; + } + } + + Status Sign(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* 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(); + } + + 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/chromium/content/child/webcrypto/nss/sha_nss.cc b/chromium/content/child/webcrypto/nss/sha_nss.cc new file mode 100644 index 00000000000..78a2ea04114 --- /dev/null +++ b/chromium/content/child/webcrypto/nss/sha_nss.cc @@ -0,0 +1,160 @@ +// 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 "base/stl_util.h" +#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) {} + + ~DigestorNSS() override { + if (!hash_context_) + return; + + HASH_Destroy(hash_context_); + hash_context_ = NULL; + } + + bool consume(const unsigned char* data, unsigned int size) override { + 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(); + } + + bool finish(unsigned char*& result_data, + unsigned int& result_data_size) override { + Status error = FinishInternal(result_, &result_data_size); + if (!error.IsSuccess()) + return false; + result_data = result_; + return true; + } + + Status FinishWithVectorAndStatus(std::vector<uint8_t>* result) { + if (!hash_context_) + return Status::ErrorUnexpected(); + + unsigned int result_length = HASH_ResultLenContext(hash_context_); + result->resize(result_length); + unsigned char* digest = vector_as_array(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: + Status Digest(const blink::WebCryptoAlgorithm& algorithm, + const CryptoData& data, + std::vector<uint8_t>* 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/chromium/content/child/webcrypto/nss/sym_key_nss.cc b/chromium/content/child/webcrypto/nss/sym_key_nss.cc new file mode 100644 index 00000000000..5066d5d91d2 --- /dev/null +++ b/chromium/content/child/webcrypto/nss/sym_key_nss.cc @@ -0,0 +1,94 @@ +// 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/generate_key_result.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 usages, + unsigned keylen_bytes, + CK_MECHANISM_TYPE mechanism, + GenerateKeyResult* result) { + 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))); + + result->AssignSecretKey( + blink::WebCryptoKey::create(handle.release(), + blink::WebCryptoKeyTypeSecret, + extractable, + algorithm, + usages)); + + return Status::Success(); +} + +Status ImportKeyRawNss(const CryptoData& key_data, + const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + 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, + usages); + return Status::Success(); +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/nss/sym_key_nss.h b/chromium/content/child/webcrypto/nss/sym_key_nss.h new file mode 100644 index 00000000000..7f1e067ac1d --- /dev/null +++ b/chromium/content/child/webcrypto/nss/sym_key_nss.h @@ -0,0 +1,39 @@ +// 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 GenerateKeyResult; +class Status; + +Status GenerateSecretKeyNss(const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + unsigned keylen_bytes, + CK_MECHANISM_TYPE mechanism, + GenerateKeyResult* result); + +Status ImportKeyRawNss(const CryptoData& key_data, + const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + 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/chromium/content/child/webcrypto/nss/util_nss.cc b/chromium/content/child/webcrypto/nss/util_nss.cc new file mode 100644 index 00000000000..58201cb47dc --- /dev/null +++ b/chromium/content/child/webcrypto/nss/util_nss.cc @@ -0,0 +1,95 @@ +// 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 "content/child/webcrypto/platform_crypto.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(); +} + +AlgorithmImplementation* CreatePlatformAesCtrImplementation() { + // TODO(eroman): http://crbug.com/399084 + return NULL; +} + +AlgorithmImplementation* CreatePlatformRsaPssImplementation() { + // TODO(eroman): http://crbug.com/399090 + return NULL; +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/nss/util_nss.h b/chromium/content/child/webcrypto/nss/util_nss.h new file mode 100644 index 00000000000..0b50178be8a --- /dev/null +++ b/chromium/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/chromium/content/child/webcrypto/openssl/aes_cbc_openssl.cc b/chromium/content/child/webcrypto/openssl/aes_cbc_openssl.cc new file mode 100644 index 00000000000..860acc45d3b --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/aes_cbc_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 <openssl/aes.h> +#include <openssl/evp.h> + +#include "base/logging.h" +#include "base/numerics/safe_math.h" +#include "base/stl_util.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_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; + } +} + +Status AesCbcEncryptDecrypt(EncryptOrDecrypt cipher_operation, + const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) { + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + + const blink::WebCryptoAesCbcParams* params = algorithm.aesCbcParams(); + const std::vector<uint8_t>& raw_key = + SymKeyOpenSsl::Cast(key)->raw_key_data(); + + if (params->iv().size() != 16) + return Status::ErrorIncorrectSizeAesCbcIv(); + + // 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. + base::CheckedNumeric<int> output_max_len = data.byte_length(); + output_max_len += AES_BLOCK_SIZE - 1; + if (!output_max_len.IsValid()) + return Status::ErrorDataTooLarge(); + + const unsigned remainder = output_max_len.ValueOrDie() % AES_BLOCK_SIZE; + if (remainder != 0) + output_max_len += AES_BLOCK_SIZE - remainder; + if (!output_max_len.IsValid()) + 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(); + } + + buffer->resize(output_max_len.ValueOrDie()); + + unsigned char* const buffer_data = vector_as_array(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); + + buffer->resize(final_output_len); + + return Status::Success(); +} + +class AesCbcImplementation : public AesAlgorithm { + public: + AesCbcImplementation() : AesAlgorithm("CBC") {} + + Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + return AesCbcEncryptDecrypt(ENCRYPT, algorithm, key, data, buffer); + } + + Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + return AesCbcEncryptDecrypt(DECRYPT, algorithm, key, data, buffer); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformAesCbcImplementation() { + return new AesCbcImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/openssl/aes_ctr_openssl.cc b/chromium/content/child/webcrypto/openssl/aes_ctr_openssl.cc new file mode 100644 index 00000000000..82408b2559a --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/aes_ctr_openssl.cc @@ -0,0 +1,288 @@ +// 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 "base/macros.h" +#include "base/numerics/safe_math.h" +#include "base/stl_util.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_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_ctr(); + case 32: + return EVP_aes_256_ctr(); + default: + return NULL; + } +} + +// Encrypts/decrypts given a 128-bit counter. +// +// |output| must be a pointer to a buffer which has a length of at least +// |input.byte_length()|. +Status AesCtrEncrypt128BitCounter(const EVP_CIPHER* cipher, + const CryptoData& raw_key, + const CryptoData& input, + const CryptoData& counter, + uint8_t* output) { + DCHECK(cipher); + DCHECK_EQ(16u, counter.byte_length()); + + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + crypto::ScopedOpenSSL<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free>::Type context( + EVP_CIPHER_CTX_new()); + + if (!context.get()) + return Status::OperationError(); + + if (!EVP_CipherInit_ex(context.get(), + cipher, + NULL, + raw_key.bytes(), + counter.bytes(), + ENCRYPT)) { + return Status::OperationError(); + } + + int output_len = 0; + if (!EVP_CipherUpdate(context.get(), + output, + &output_len, + input.bytes(), + input.byte_length())) { + return Status::OperationError(); + } + int final_output_chunk_len = 0; + if (!EVP_CipherFinal_ex( + context.get(), output + output_len, &final_output_chunk_len)) { + return Status::OperationError(); + } + + output_len += final_output_chunk_len; + if (static_cast<unsigned int>(output_len) != input.byte_length()) + return Status::ErrorUnexpected(); + + return Status::Success(); +} + +// Returns ceil(a/b), where a and b are integers. +template <typename T> +T CeilDiv(T a, T b) { + return a == 0 ? 0 : 1 + (a - 1) / b; +} + +// Extracts the counter as a BIGNUM. The counter is the rightmost +// "counter_length_bits" of the block, interpreted as a big-endian number. +crypto::ScopedBIGNUM GetCounter(const CryptoData& counter_block, + unsigned int counter_length_bits) { + unsigned int counter_length_remainder_bits = (counter_length_bits % 8); + + // If the counter is a multiple of 8 bits then can call BN_bin2bn() directly. + if (counter_length_remainder_bits == 0) { + unsigned int byte_length = counter_length_bits / 8; + return crypto::ScopedBIGNUM(BN_bin2bn( + counter_block.bytes() + counter_block.byte_length() - byte_length, + byte_length, + NULL)); + } + + // Otherwise make a copy of the counter and zero out the topmost bits so + // BN_bin2bn() can be called with a byte stream. + unsigned int byte_length = CeilDiv(counter_length_bits, 8u); + std::vector<uint8_t> counter( + counter_block.bytes() + counter_block.byte_length() - byte_length, + counter_block.bytes() + counter_block.byte_length()); + counter[0] &= ~(0xFF << counter_length_remainder_bits); + + return crypto::ScopedBIGNUM( + BN_bin2bn(&counter.front(), counter.size(), NULL)); +} + +// Returns a counter block with the counter bits all set all zero. +std::vector<uint8_t> BlockWithZeroedCounter(const CryptoData& counter_block, + unsigned int counter_length_bits) { + unsigned int counter_length_bytes = counter_length_bits / 8; + unsigned int counter_length_bits_remainder = counter_length_bits % 8; + + std::vector<uint8_t> new_counter_block( + counter_block.bytes(), + counter_block.bytes() + counter_block.byte_length()); + + unsigned int index = new_counter_block.size() - counter_length_bytes; + memset(&new_counter_block.front() + index, 0, counter_length_bytes); + + if (counter_length_bits_remainder) { + new_counter_block[index - 1] &= 0xFF << counter_length_bits_remainder; + } + + return new_counter_block; +} + +// This function does encryption/decryption for AES-CTR (encryption and +// decryption are the same). +// +// BoringSSL's interface for AES-CTR differs from that of WebCrypto. In +// WebCrypto the caller specifies a 16-byte counter block and designates how +// many of the right-most X bits to use as a big-endian counter. Whereas in +// BoringSSL the entire counter block is interpreted as a 128-bit counter. +// +// In AES-CTR, the counter block MUST be unique across all messages that are +// encrypted/decrypted. WebCrypto expects that the counter can start at any +// value, and is therefore permitted to wrap around to zero on overflow. +// +// Some care is taken to fail if the counter wraps back to an earlier value. +// However this protection is only enforced during a *single* call to +// encrypt/decrypt. +Status AesCtrEncryptDecrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) { + const blink::WebCryptoAesCtrParams* params = algorithm.aesCtrParams(); + const std::vector<uint8_t>& raw_key = + SymKeyOpenSsl::Cast(key)->raw_key_data(); + + if (params->counter().size() != 16) + return Status::ErrorIncorrectSizeAesCtrCounter(); + + unsigned int counter_length_bits = params->lengthBits(); + if (counter_length_bits < 1 || counter_length_bits > 128) + return Status::ErrorInvalidAesCtrCounterLength(); + + // The output of AES-CTR is the same size as the input. However BoringSSL + // expects buffer sizes as an "int". + base::CheckedNumeric<int> output_max_len = data.byte_length(); + if (!output_max_len.IsValid()) + return Status::ErrorDataTooLarge(); + + const EVP_CIPHER* const cipher = GetAESCipherByKeyLength(raw_key.size()); + if (!cipher) + return Status::ErrorUnexpected(); + + const CryptoData counter_block(params->counter()); + buffer->resize(output_max_len.ValueOrDie()); + + // The total number of possible counter values is pow(2, counter_length_bits) + crypto::ScopedBIGNUM num_counter_values(BN_new()); + if (!BN_lshift(num_counter_values.get(), BN_value_one(), counter_length_bits)) + return Status::ErrorUnexpected(); + + crypto::ScopedBIGNUM current_counter = + GetCounter(counter_block, counter_length_bits); + + // The number of AES blocks needed for encryption/decryption. The counter is + // incremented this many times. + crypto::ScopedBIGNUM num_output_blocks(BN_new()); + if (!BN_set_word( + num_output_blocks.get(), + CeilDiv(buffer->size(), static_cast<size_t>(AES_BLOCK_SIZE)))) { + return Status::ErrorUnexpected(); + } + + // If the counter is going to be incremented more times than there are counter + // values, fail. (Repeating values of the counter block is bad). + if (BN_cmp(num_output_blocks.get(), num_counter_values.get()) > 0) + return Status::ErrorAesCtrInputTooLongCounterRepeated(); + + // This is the number of blocks that can be successfully encrypted without + // overflowing the counter. Encrypting the subsequent block will need to + // reset the counter to zero. + crypto::ScopedBIGNUM num_blocks_until_reset(BN_new()); + + if (!BN_sub(num_blocks_until_reset.get(), + num_counter_values.get(), + current_counter.get())) { + return Status::ErrorUnexpected(); + } + + // If the counter can be incremented for the entire input without + // wrapping-around, do it as a single call into BoringSSL. + if (BN_cmp(num_blocks_until_reset.get(), num_output_blocks.get()) >= 0) { + return AesCtrEncrypt128BitCounter(cipher, + CryptoData(raw_key), + data, + counter_block, + vector_as_array(buffer)); + } + + // Otherwise the encryption needs to be done in 2 parts. The first part using + // the current counter_block, and the next part resetting the counter portion + // of the block to zero. + + // This is guaranteed to fit in an "unsigned int" because input size in bytes + // fits in an "unsigned int". + BN_ULONG num_blocks_part1 = BN_get_word(num_blocks_until_reset.get()); + BN_ULONG input_size_part1 = num_blocks_part1 * AES_BLOCK_SIZE; + DCHECK_LT(input_size_part1, data.byte_length()); + + // Encrypt the first part (before wrap-around). + Status status = + AesCtrEncrypt128BitCounter(cipher, + CryptoData(raw_key), + CryptoData(data.bytes(), input_size_part1), + counter_block, + vector_as_array(buffer)); + if (status.IsError()) + return status; + + // Encrypt the second part (after wrap-around). + std::vector<uint8_t> counter_block_part2 = + BlockWithZeroedCounter(counter_block, counter_length_bits); + + return AesCtrEncrypt128BitCounter( + cipher, + CryptoData(raw_key), + CryptoData(data.bytes() + input_size_part1, + data.byte_length() - input_size_part1), + CryptoData(counter_block_part2), + vector_as_array(buffer) + input_size_part1); +} + +class AesCtrImplementation : public AesAlgorithm { + public: + AesCtrImplementation() : AesAlgorithm("CTR") {} + + Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + return AesCtrEncryptDecrypt(algorithm, key, data, buffer); + } + + Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + return AesCtrEncryptDecrypt(algorithm, key, data, buffer); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformAesCtrImplementation() { + return new AesCtrImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/openssl/aes_gcm_openssl.cc b/chromium/content/child/webcrypto/openssl/aes_gcm_openssl.cc new file mode 100644 index 00000000000..7c783c9bdcf --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/aes_gcm_openssl.cc @@ -0,0 +1,88 @@ +// 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 "base/stl_util.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(); + case 32: + return EVP_aead_aes_256_gcm(); + default: + return NULL; + } +} + +Status AesGcmEncryptDecrypt(EncryptOrDecrypt mode, + const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) { + const std::vector<uint8_t>& raw_key = + SymKeyOpenSsl::Cast(key)->raw_key_data(); + const blink::WebCryptoAesGcmParams* params = algorithm.aesGcmParams(); + + unsigned int tag_length_bits; + Status status = GetAesGcmTagLengthInBits(params, &tag_length_bits); + if (status.IsError()) + return status; + + return AeadEncryptDecrypt(mode, + raw_key, + data, + tag_length_bits / 8, + CryptoData(params->iv()), + CryptoData(params->optionalAdditionalData()), + GetAesGcmAlgorithmFromKeySize(raw_key.size()), + buffer); +} + +class AesGcmImplementation : public AesAlgorithm { + public: + AesGcmImplementation() : AesAlgorithm("GCM") {} + + Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + return AesGcmEncryptDecrypt(ENCRYPT, algorithm, key, data, buffer); + } + + Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + return AesGcmEncryptDecrypt(DECRYPT, algorithm, key, data, buffer); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformAesGcmImplementation() { + return new AesGcmImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/openssl/aes_key_openssl.cc b/chromium/content/child/webcrypto/openssl/aes_key_openssl.cc new file mode 100644 index 00000000000..14fa24e35c2 --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/aes_key_openssl.cc @@ -0,0 +1,124 @@ +// 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::GenerateKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + GenerateKeyResult* result) const { + Status status = CheckKeyCreationUsages(all_key_usages_, usages); + if (status.IsError()) + return status; + + unsigned int keylen_bits; + status = GetAesKeyGenLengthInBits(algorithm.aesKeyGenParams(), &keylen_bits); + if (status.IsError()) + return status; + + return GenerateSecretKeyOpenSsl( + blink::WebCryptoKeyAlgorithm::createAes(algorithm.id(), keylen_bits), + extractable, + usages, + keylen_bits / 8, + result); +} + +Status AesAlgorithm::VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usages) const { + switch (format) { + case blink::WebCryptoKeyFormatRaw: + case blink::WebCryptoKeyFormatJwk: + return CheckKeyCreationUsages(all_key_usages_, usages); + default: + return Status::ErrorUnsupportedImportKeyFormat(); + } +} + +Status AesAlgorithm::ImportKeyRaw(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + 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, + usages, + key); +} + +Status AesAlgorithm::ImportKeyJwk(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const { + std::vector<uint8_t> raw_data; + Status status = ReadAesSecretKeyJwk( + key_data, jwk_suffix_, extractable, usages, &raw_data); + if (status.IsError()) + return status; + + return ImportKeyRaw( + CryptoData(raw_data), algorithm, extractable, usages, key); +} + +Status AesAlgorithm::ExportKeyRaw(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const { + *buffer = SymKeyOpenSsl::Cast(key)->raw_key_data(); + return Status::Success(); +} + +Status AesAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const { + const std::vector<uint8_t>& 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/chromium/content/child/webcrypto/openssl/aes_key_openssl.h b/chromium/content/child/webcrypto/openssl/aes_key_openssl.h new file mode 100644 index 00000000000..206a1e2bcfc --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/aes_key_openssl.h @@ -0,0 +1,67 @@ +// 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); + + Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + GenerateKeyResult* result) const override; + + Status VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usages) const override; + + Status ImportKeyRaw(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const override; + + Status ImportKeyJwk(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const override; + + Status ExportKeyRaw(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const override; + + Status ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8_t>* 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/chromium/content/child/webcrypto/openssl/aes_kw_openssl.cc b/chromium/content/child/webcrypto/openssl/aes_kw_openssl.cc new file mode 100644 index 00000000000..c612d53dc96 --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/aes_kw_openssl.cc @@ -0,0 +1,94 @@ +// 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 "base/numerics/safe_math.h" +#include "base/stl_util.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 "crypto/openssl_util.h" +#include "crypto/scoped_openssl_types.h" + +namespace content { + +namespace webcrypto { + +namespace { + +const EVP_AEAD* GetAesKwAlgorithmFromKeySize(unsigned int key_size_bytes) { + switch (key_size_bytes) { + case 16: + return EVP_aead_aes_128_key_wrap(); + case 32: + return EVP_aead_aes_256_key_wrap(); + default: + return NULL; + } +} + +Status AesKwEncryptDecrypt(EncryptOrDecrypt mode, + const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) { + // These length checks are done so the returned error matches that of NSS + // implementation. Other than giving a more specific error, these are not + // required. + if ((mode == ENCRYPT && data.byte_length() < 16) || + (mode == DECRYPT && data.byte_length() < 24)) { + return Status::ErrorDataTooSmall(); + } + if (data.byte_length() % 8) + return Status::ErrorInvalidAesKwDataLength(); + + const std::vector<uint8_t>& raw_key = + SymKeyOpenSsl::Cast(key)->raw_key_data(); + + return AeadEncryptDecrypt(mode, + raw_key, + data, + 8, // tag_length_bytes + CryptoData(), // iv + CryptoData(), // additional_data + GetAesKwAlgorithmFromKeySize(raw_key.size()), + buffer); +} + +class AesKwImplementation : public AesAlgorithm { + public: + AesKwImplementation() + : AesAlgorithm( + blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, + "KW") {} + + Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + return AesKwEncryptDecrypt(ENCRYPT, algorithm, key, data, buffer); + } + + Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + return AesKwEncryptDecrypt(DECRYPT, algorithm, key, data, buffer); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformAesKwImplementation() { + return new AesKwImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/openssl/hmac_openssl.cc b/chromium/content/child/webcrypto/openssl/hmac_openssl.cc new file mode 100644 index 00000000000..77b5ccfcf93 --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/hmac_openssl.cc @@ -0,0 +1,216 @@ +// 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 "base/numerics/safe_math.h" +#include "base/stl_util.h" +#include "content/child/webcrypto/algorithm_implementation.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_t>& raw_key, + const blink::WebCryptoAlgorithm& hash, + const CryptoData& data, + std::vector<uint8_t>* 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 a fresh array. + const unsigned char null_key[] = {0}; + 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( + vector_as_array(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() {} + + Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + GenerateKeyResult* result) const override { + Status status = CheckKeyCreationUsages(kAllKeyUsages, usages); + if (status.IsError()) + return status; + + const blink::WebCryptoHmacKeyGenParams* params = + algorithm.hmacKeyGenParams(); + + unsigned int keylen_bits = 0; + status = GetHmacKeyGenLengthInBits(params, &keylen_bits); + if (status.IsError()) + return status; + + return GenerateSecretKeyOpenSsl(blink::WebCryptoKeyAlgorithm::createHmac( + params->hash().id(), keylen_bits), + extractable, + usages, + keylen_bits / 8, + result); + } + + Status VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usages) const override { + switch (format) { + case blink::WebCryptoKeyFormatRaw: + case blink::WebCryptoKeyFormatJwk: + return CheckKeyCreationUsages(kAllKeyUsages, usages); + default: + return Status::ErrorUnsupportedImportKeyFormat(); + } + } + + Status ImportKeyRaw(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const override { + const blink::WebCryptoAlgorithm& hash = + algorithm.hmacImportParams()->hash(); + + base::CheckedNumeric<unsigned int> keylen_bits(key_data.byte_length()); + keylen_bits *= 8; + + if (!keylen_bits.IsValid()) + return Status::ErrorDataTooLarge(); + + return ImportKeyRawOpenSsl(key_data, + blink::WebCryptoKeyAlgorithm::createHmac( + hash.id(), keylen_bits.ValueOrDie()), + extractable, + usages, + key); + } + + Status ImportKeyJwk(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const override { + const char* algorithm_name = + GetJwkHmacAlgorithmName(algorithm.hmacImportParams()->hash().id()); + if (!algorithm_name) + return Status::ErrorUnexpected(); + + std::vector<uint8_t> raw_data; + Status status = ReadSecretKeyJwk( + key_data, algorithm_name, extractable, usages, &raw_data); + if (status.IsError()) + return status; + + return ImportKeyRaw( + CryptoData(raw_data), algorithm, extractable, usages, key); + } + + Status ExportKeyRaw(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const override { + *buffer = SymKeyOpenSsl::Cast(key)->raw_key_data(); + return Status::Success(); + } + + Status ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const override { + SymKeyOpenSsl* sym_key = SymKeyOpenSsl::Cast(key); + const std::vector<uint8_t>& 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(); + } + + Status Sign(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + const blink::WebCryptoAlgorithm& hash = + key.algorithm().hmacParams()->hash(); + + return SignHmac( + SymKeyOpenSsl::Cast(key)->raw_key_data(), hash, data, buffer); + } + + Status Verify(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& signature, + const CryptoData& data, + bool* signature_match) const override { + std::vector<uint8_t> 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(vector_as_array(&result), + signature.bytes(), + signature.byte_length()); + + return Status::Success(); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformHmacImplementation() { + return new HmacImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/openssl/key_openssl.cc b/chromium/content/child/webcrypto/openssl/key_openssl.cc new file mode 100644 index 00000000000..9ea1f20428c --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/key_openssl.cc @@ -0,0 +1,73 @@ +// 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; +} + +AsymKeyOpenSsl* KeyOpenSsl::AsAsymKey() { + 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) { +} + +AsymKeyOpenSsl::~AsymKeyOpenSsl() { +} + +AsymKeyOpenSsl* AsymKeyOpenSsl::Cast(const blink::WebCryptoKey& key) { + return reinterpret_cast<KeyOpenSsl*>(key.handle())->AsAsymKey(); +} + +AsymKeyOpenSsl* AsymKeyOpenSsl::AsAsymKey() { + return this; +} + +AsymKeyOpenSsl::AsymKeyOpenSsl(crypto::ScopedEVP_PKEY key, + const CryptoData& serialized_key_data) + : KeyOpenSsl(serialized_key_data), key_(key.Pass()) { +} + +bool PlatformSerializeKeyForClone(const blink::WebCryptoKey& key, + blink::WebVector<uint8_t>* 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/chromium/content/child/webcrypto/openssl/key_openssl.h b/chromium/content/child/webcrypto/openssl/key_openssl.h new file mode 100644 index 00000000000..48163c18ac0 --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/key_openssl.h @@ -0,0 +1,83 @@ +// 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 <openssl/ossl_typ.h> +#include <stdint.h> +#include <vector> + +#include "base/macros.h" +#include "crypto/scoped_openssl_types.h" +#include "third_party/WebKit/public/platform/WebCryptoKey.h" + +namespace content { + +namespace webcrypto { + +class CryptoData; +class AsymKeyOpenSsl; +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); + ~KeyOpenSsl() override; + + virtual SymKeyOpenSsl* AsSymKey(); + virtual AsymKeyOpenSsl* AsAsymKey(); + + const std::vector<uint8_t>& serialized_key_data() const { + return serialized_key_data_; + } + + private: + const std::vector<uint8_t> serialized_key_data_; +}; + +class SymKeyOpenSsl : public KeyOpenSsl { + public: + ~SymKeyOpenSsl() override; + explicit SymKeyOpenSsl(const CryptoData& raw_key_data); + + static SymKeyOpenSsl* Cast(const blink::WebCryptoKey& key); + + SymKeyOpenSsl* AsSymKey() override; + + const std::vector<uint8_t>& raw_key_data() const { + return serialized_key_data(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(SymKeyOpenSsl); +}; + +class AsymKeyOpenSsl : public KeyOpenSsl { + public: + ~AsymKeyOpenSsl() override; + AsymKeyOpenSsl(crypto::ScopedEVP_PKEY key, + const CryptoData& serialized_key_data); + + static AsymKeyOpenSsl* Cast(const blink::WebCryptoKey& key); + + AsymKeyOpenSsl* AsAsymKey() override; + + EVP_PKEY* key() { return key_.get(); } + + private: + crypto::ScopedEVP_PKEY key_; + + DISALLOW_COPY_AND_ASSIGN(AsymKeyOpenSsl); +}; + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_OPENSSL_KEY_OPENSSL_H_ diff --git a/chromium/content/child/webcrypto/openssl/rsa_key_openssl.cc b/chromium/content/child/webcrypto/openssl/rsa_key_openssl.cc new file mode 100644 index 00000000000..6710c31b0ca --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/rsa_key_openssl.cc @@ -0,0 +1,389 @@ +// 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/rsa_key_openssl.h" + +#include <openssl/evp.h> + +#include "base/logging.h" +#include "base/stl_util.h" +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/generate_key_result.h" +#include "content/child/webcrypto/jwk.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" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace content { + +namespace webcrypto { + +namespace { + +// Creates a blink::WebCryptoAlgorithm having the modulus length and public +// exponent of |key|. +Status CreateRsaHashedKeyAlgorithm( + blink::WebCryptoAlgorithmId rsa_algorithm, + blink::WebCryptoAlgorithmId hash_algorithm, + EVP_PKEY* key, + blink::WebCryptoKeyAlgorithm* key_algorithm) { + DCHECK_EQ(EVP_PKEY_RSA, EVP_PKEY_id(key)); + + crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(key)); + if (!rsa.get()) + return Status::ErrorUnexpected(); + + unsigned int modulus_length_bits = BN_num_bits(rsa.get()->n); + + // Convert the public exponent to big-endian representation. + std::vector<uint8_t> e(BN_num_bytes(rsa.get()->e)); + if (e.size() == 0) + return Status::ErrorUnexpected(); + if (e.size() != BN_bn2bin(rsa.get()->e, &e[0])) + return Status::ErrorUnexpected(); + + *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed( + rsa_algorithm, modulus_length_bits, &e[0], e.size(), hash_algorithm); + + return Status::Success(); +} + +// Creates a WebCryptoKey that wraps |private_key|. +Status CreateWebCryptoRsaPrivateKey( + crypto::ScopedEVP_PKEY private_key, + const blink::WebCryptoAlgorithmId rsa_algorithm_id, + const blink::WebCryptoAlgorithm& hash, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) { + blink::WebCryptoKeyAlgorithm key_algorithm; + Status status = CreateRsaHashedKeyAlgorithm( + rsa_algorithm_id, hash.id(), private_key.get(), &key_algorithm); + if (status.IsError()) + return status; + + return CreateWebCryptoPrivateKey(private_key.Pass(), key_algorithm, + extractable, usages, key); +} + +// Creates a WebCryptoKey that wraps |public_key|. +Status CreateWebCryptoRsaPublicKey( + crypto::ScopedEVP_PKEY public_key, + const blink::WebCryptoAlgorithmId rsa_algorithm_id, + const blink::WebCryptoAlgorithm& hash, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) { + blink::WebCryptoKeyAlgorithm key_algorithm; + Status status = CreateRsaHashedKeyAlgorithm( + rsa_algorithm_id, hash.id(), public_key.get(), &key_algorithm); + if (status.IsError()) + return status; + + return CreateWebCryptoPublicKey(public_key.Pass(), key_algorithm, extractable, + usages, key); +} + +Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + const JwkRsaInfo& params, + blink::WebCryptoKey* key) { + crypto::ScopedRSA rsa(RSA_new()); + + rsa->n = CreateBIGNUM(params.n); + rsa->e = CreateBIGNUM(params.e); + rsa->d = CreateBIGNUM(params.d); + rsa->p = CreateBIGNUM(params.p); + rsa->q = CreateBIGNUM(params.q); + rsa->dmp1 = CreateBIGNUM(params.dp); + rsa->dmq1 = CreateBIGNUM(params.dq); + rsa->iqmp = CreateBIGNUM(params.qi); + + if (!rsa->n || !rsa->e || !rsa->d || !rsa->p || !rsa->q || !rsa->dmp1 || + !rsa->dmq1 || !rsa->iqmp) { + return Status::OperationError(); + } + + // TODO(eroman): This should really be a DataError, however for compatibility + // with NSS it is an OperationError. + if (!RSA_check_key(rsa.get())) + return Status::OperationError(); + + // Create a corresponding EVP_PKEY. + crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new()); + if (!pkey || !EVP_PKEY_set1_RSA(pkey.get(), rsa.get())) + return Status::OperationError(); + + return CreateWebCryptoRsaPrivateKey(pkey.Pass(), algorithm.id(), + algorithm.rsaHashedImportParams()->hash(), + extractable, usages, key); +} + +Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + const CryptoData& n, + const CryptoData& e, + blink::WebCryptoKey* key) { + crypto::ScopedRSA rsa(RSA_new()); + + rsa->n = BN_bin2bn(n.bytes(), n.byte_length(), NULL); + rsa->e = BN_bin2bn(e.bytes(), e.byte_length(), NULL); + + if (!rsa->n || !rsa->e) + return Status::OperationError(); + + // Create a corresponding EVP_PKEY. + crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new()); + if (!pkey || !EVP_PKEY_set1_RSA(pkey.get(), rsa.get())) + return Status::OperationError(); + + return CreateWebCryptoRsaPublicKey(pkey.Pass(), algorithm.id(), + algorithm.rsaHashedImportParams()->hash(), + extractable, usages, key); +} + +} // namespace + +Status RsaHashedAlgorithm::GenerateKey( + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask combined_usages, + GenerateKeyResult* result) const { + Status status = CheckKeyCreationUsages( + all_public_key_usages_ | all_private_key_usages_, combined_usages); + if (status.IsError()) + return status; + + const blink::WebCryptoKeyUsageMask public_usages = + combined_usages & all_public_key_usages_; + const blink::WebCryptoKeyUsageMask private_usages = + combined_usages & all_private_key_usages_; + + const blink::WebCryptoRsaHashedKeyGenParams* params = + algorithm.rsaHashedKeyGenParams(); + + unsigned int public_exponent = 0; + unsigned int modulus_length_bits = 0; + status = + GetRsaKeyGenParameters(params, &public_exponent, &modulus_length_bits); + if (status.IsError()) + return status; + + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + + // Generate an RSA key pair. + crypto::ScopedRSA rsa_private_key(RSA_new()); + crypto::ScopedBIGNUM bn(BN_new()); + if (!rsa_private_key.get() || !bn.get() || + !BN_set_word(bn.get(), public_exponent)) { + return Status::OperationError(); + } + + if (!RSA_generate_key_ex( + rsa_private_key.get(), modulus_length_bits, bn.get(), NULL)) { + return Status::OperationError(); + } + + // Construct an EVP_PKEY for the private key. + crypto::ScopedEVP_PKEY private_pkey(EVP_PKEY_new()); + if (!private_pkey || + !EVP_PKEY_set1_RSA(private_pkey.get(), rsa_private_key.get())) { + return Status::OperationError(); + } + + // Construct an EVP_PKEY for the public key. + crypto::ScopedRSA rsa_public_key(RSAPublicKey_dup(rsa_private_key.get())); + crypto::ScopedEVP_PKEY public_pkey(EVP_PKEY_new()); + if (!public_pkey || + !EVP_PKEY_set1_RSA(public_pkey.get(), rsa_public_key.get())) { + return Status::OperationError(); + } + + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + + // Note that extractable is unconditionally set to true. This is because per + // the WebCrypto spec generated public keys are always public. + status = CreateWebCryptoRsaPublicKey(public_pkey.Pass(), algorithm.id(), + params->hash(), true, public_usages, + &public_key); + if (status.IsError()) + return status; + + status = CreateWebCryptoRsaPrivateKey(private_pkey.Pass(), algorithm.id(), + params->hash(), extractable, + private_usages, &private_key); + if (status.IsError()) + return status; + + result->AssignKeyPair(public_key, private_key); + return Status::Success(); +} + +Status RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usages) const { + switch (format) { + case blink::WebCryptoKeyFormatSpki: + return CheckKeyCreationUsages(all_public_key_usages_, usages); + case blink::WebCryptoKeyFormatPkcs8: + return CheckKeyCreationUsages(all_private_key_usages_, usages); + case blink::WebCryptoKeyFormatJwk: + // The JWK could represent either a public key or private key. The usages + // must make sense for one of the two. The usages will be checked again by + // ImportKeyJwk() once the key type has been determined. + if (CheckKeyCreationUsages(all_private_key_usages_, usages).IsSuccess() || + CheckKeyCreationUsages(all_public_key_usages_, usages).IsSuccess()) { + return Status::Success(); + } + return Status::ErrorCreateKeyBadUsages(); + default: + return Status::ErrorUnsupportedImportKeyFormat(); + } +} + +Status RsaHashedAlgorithm::ImportKeyPkcs8( + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const { + crypto::ScopedEVP_PKEY private_key; + Status status = + ImportUnverifiedPkeyFromPkcs8(key_data, EVP_PKEY_RSA, &private_key); + if (status.IsError()) + return status; + + // Verify the parameters of the key. + crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(private_key.get())); + if (!rsa.get()) + return Status::ErrorUnexpected(); + if (!RSA_check_key(rsa.get())) + return Status::DataError(); + + // TODO(eroman): Validate the algorithm OID against the webcrypto provided + // hash. http://crbug.com/389400 + + return CreateWebCryptoRsaPrivateKey(private_key.Pass(), algorithm.id(), + algorithm.rsaHashedImportParams()->hash(), + extractable, usages, key); +} + +Status RsaHashedAlgorithm::ImportKeySpki( + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const { + crypto::ScopedEVP_PKEY public_key; + Status status = + ImportUnverifiedPkeyFromSpki(key_data, EVP_PKEY_RSA, &public_key); + if (status.IsError()) + return status; + + // TODO(eroman): Validate the algorithm OID against the webcrypto provided + // hash. http://crbug.com/389400 + + return CreateWebCryptoRsaPublicKey(public_key.Pass(), algorithm.id(), + algorithm.rsaHashedImportParams()->hash(), + extractable, usages, key); +} + +Status RsaHashedAlgorithm::ImportKeyJwk( + const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const { + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + + 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, usages, &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_, + usages); + if (status.IsError()) + return status; + + return jwk.is_private_key + ? ImportRsaPrivateKey(algorithm, extractable, usages, jwk, key) + : ImportRsaPublicKey(algorithm, extractable, usages, + CryptoData(jwk.n), CryptoData(jwk.e), key); +} + +Status RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const { + if (key.type() != blink::WebCryptoKeyTypePrivate) + return Status::ErrorUnexpectedKeyType(); + *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data(); + return Status::Success(); +} + +Status RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const { + if (key.type() != blink::WebCryptoKeyTypePublic) + return Status::ErrorUnexpectedKeyType(); + *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data(); + return Status::Success(); +} + +Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const { + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + + EVP_PKEY* pkey = AsymKeyOpenSsl::Cast(key)->key(); + crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(pkey)); + if (!rsa.get()) + return Status::ErrorUnexpected(); + + const char* jwk_algorithm = + GetJwkAlgorithm(key.algorithm().rsaHashedParams()->hash().id()); + if (!jwk_algorithm) + return Status::ErrorUnexpected(); + + switch (key.type()) { + case blink::WebCryptoKeyTypePublic: + WriteRsaPublicKeyJwk(CryptoData(BIGNUMToVector(rsa->n)), + CryptoData(BIGNUMToVector(rsa->e)), jwk_algorithm, + key.extractable(), key.usages(), buffer); + return Status::Success(); + case blink::WebCryptoKeyTypePrivate: + WriteRsaPrivateKeyJwk(CryptoData(BIGNUMToVector(rsa->n)), + CryptoData(BIGNUMToVector(rsa->e)), + CryptoData(BIGNUMToVector(rsa->d)), + CryptoData(BIGNUMToVector(rsa->p)), + CryptoData(BIGNUMToVector(rsa->q)), + CryptoData(BIGNUMToVector(rsa->dmp1)), + CryptoData(BIGNUMToVector(rsa->dmq1)), + CryptoData(BIGNUMToVector(rsa->iqmp)), + jwk_algorithm, key.extractable(), key.usages(), + buffer); + return Status::Success(); + + default: + return Status::ErrorUnexpected(); + } +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/openssl/rsa_key_openssl.h b/chromium/content/child/webcrypto/openssl/rsa_key_openssl.h new file mode 100644 index 00000000000..d7935052dc7 --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/rsa_key_openssl.h @@ -0,0 +1,83 @@ +// 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_RSA_KEY_OPENSSL_H_ +#define CONTENT_CHILD_WEBCRYPTO_OPENSSL_RSA_KEY_OPENSSL_H_ + +#include "content/child/webcrypto/algorithm_implementation.h" + +namespace content { + +namespace webcrypto { + +// 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: + // |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(blink::WebCryptoKeyUsageMask all_public_key_usages, + blink::WebCryptoKeyUsageMask all_private_key_usages) + : 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; + + Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + GenerateKeyResult* result) const override; + + Status VerifyKeyUsagesBeforeImportKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask usages) const override; + + Status ImportKeyPkcs8(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const override; + + Status ImportKeySpki(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const override; + + Status ImportKeyJwk(const CryptoData& key_data, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) const override; + + Status ExportKeyPkcs8(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const override; + + Status ExportKeySpki(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const override; + + Status ExportKeyJwk(const blink::WebCryptoKey& key, + std::vector<uint8_t>* buffer) const override; + + private: + blink::WebCryptoKeyUsageMask all_public_key_usages_; + blink::WebCryptoKeyUsageMask all_private_key_usages_; +}; + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_OPENSSL_RSA_KEY_OPENSSL_H_ diff --git a/chromium/content/child/webcrypto/openssl/rsa_oaep_openssl.cc b/chromium/content/child/webcrypto/openssl/rsa_oaep_openssl.cc new file mode 100644 index 00000000000..b1e40174fa5 --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/rsa_oaep_openssl.cc @@ -0,0 +1,153 @@ +// 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/evp.h> + +#include "base/stl_util.h" +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/openssl/key_openssl.h" +#include "content/child/webcrypto/openssl/rsa_key_openssl.h" +#include "content/child/webcrypto/openssl/util_openssl.h" +#include "content/child/webcrypto/status.h" +#include "crypto/openssl_util.h" +#include "crypto/scoped_openssl_types.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace content { + +namespace webcrypto { + +namespace { + +typedef int (*InitFunc)(EVP_PKEY_CTX* ctx); +typedef int (*EncryptDecryptFunc)(EVP_PKEY_CTX* ctx, + unsigned char* out, + size_t* outlen, + const unsigned char* in, + size_t inlen); + +// Helper for doing either RSA-OAEP encryption or decryption. +// +// To encrypt call with: +// init_func=EVP_PKEY_encrypt_init, encrypt_decrypt_func=EVP_PKEY_encrypt +// +// To decrypt call with: +// init_func=EVP_PKEY_decrypt_init, encrypt_decrypt_func=EVP_PKEY_decrypt +Status CommonEncryptDecrypt(InitFunc init_func, + EncryptDecryptFunc encrypt_decrypt_func, + const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) { + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + + EVP_PKEY* pkey = AsymKeyOpenSsl::Cast(key)->key(); + const EVP_MD* digest = + GetDigest(key.algorithm().rsaHashedParams()->hash().id()); + if (!digest) + return Status::ErrorUnsupported(); + + crypto::ScopedEVP_PKEY_CTX ctx(EVP_PKEY_CTX_new(pkey, NULL)); + + if (!init_func(ctx.get()) || + 1 != EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) || + 1 != EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), digest) || + 1 != EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), digest)) { + return Status::OperationError(); + } + + const blink::WebVector<uint8_t>& label = + algorithm.rsaOaepParams()->optionalLabel(); + + if (label.size()) { + // Make a copy of the label, since the ctx takes ownership of it when + // calling set0_rsa_oaep_label(). + crypto::ScopedOpenSSLBytes label_copy; + label_copy.reset(static_cast<uint8_t*>(OPENSSL_malloc(label.size()))); + memcpy(label_copy.get(), label.data(), label.size()); + + if (1 != EVP_PKEY_CTX_set0_rsa_oaep_label( + ctx.get(), label_copy.release(), label.size())) { + return Status::OperationError(); + } + } + + // Determine the maximum length of the output. + size_t outlen = 0; + if (!encrypt_decrypt_func( + ctx.get(), NULL, &outlen, data.bytes(), data.byte_length())) { + return Status::OperationError(); + } + buffer->resize(outlen); + + // Do the actual encryption/decryption. + if (!encrypt_decrypt_func(ctx.get(), + vector_as_array(buffer), + &outlen, + data.bytes(), + data.byte_length())) { + return Status::OperationError(); + } + buffer->resize(outlen); + + return Status::Success(); +} + +class RsaOaepImplementation : public RsaHashedAlgorithm { + public: + RsaOaepImplementation() + : RsaHashedAlgorithm( + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageWrapKey, + blink::WebCryptoKeyUsageDecrypt | + blink::WebCryptoKeyUsageUnwrapKey) {} + + 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; + } + } + + Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + if (key.type() != blink::WebCryptoKeyTypePublic) + return Status::ErrorUnexpectedKeyType(); + + return CommonEncryptDecrypt( + EVP_PKEY_encrypt_init, EVP_PKEY_encrypt, algorithm, key, data, buffer); + } + + Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + if (key.type() != blink::WebCryptoKeyTypePrivate) + return Status::ErrorUnexpectedKeyType(); + + return CommonEncryptDecrypt( + EVP_PKEY_decrypt_init, EVP_PKEY_decrypt, algorithm, key, data, buffer); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformRsaOaepImplementation() { + return new RsaOaepImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/openssl/rsa_pss_openssl.cc b/chromium/content/child/webcrypto/openssl/rsa_pss_openssl.cc new file mode 100644 index 00000000000..a6bf4d55a69 --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/rsa_pss_openssl.cc @@ -0,0 +1,67 @@ +// 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/rsa_key_openssl.h" +#include "content/child/webcrypto/openssl/rsa_sign_openssl.h" +#include "content/child/webcrypto/status.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" + +namespace content { + +namespace webcrypto { + +namespace { + +class RsaPssImplementation : public RsaHashedAlgorithm { + public: + RsaPssImplementation() + : RsaHashedAlgorithm(blink::WebCryptoKeyUsageVerify, + blink::WebCryptoKeyUsageSign) {} + + const char* GetJwkAlgorithm( + const blink::WebCryptoAlgorithmId hash) const override { + switch (hash) { + case blink::WebCryptoAlgorithmIdSha1: + return "PS1"; + case blink::WebCryptoAlgorithmIdSha256: + return "PS256"; + case blink::WebCryptoAlgorithmIdSha384: + return "PS384"; + case blink::WebCryptoAlgorithmIdSha512: + return "PS512"; + default: + return NULL; + } + } + + Status Sign(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + return RsaSign( + key, algorithm.rsaPssParams()->saltLengthBytes(), data, buffer); + } + + Status Verify(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& signature, + const CryptoData& data, + bool* signature_match) const override { + return RsaVerify(key, + algorithm.rsaPssParams()->saltLengthBytes(), + signature, + data, + signature_match); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformRsaPssImplementation() { + return new RsaPssImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/openssl/rsa_sign_openssl.cc b/chromium/content/child/webcrypto/openssl/rsa_sign_openssl.cc new file mode 100644 index 00000000000..1410aabb25b --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/rsa_sign_openssl.cc @@ -0,0 +1,155 @@ +// 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/numerics/safe_math.h" +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/openssl/key_openssl.h" +#include "content/child/webcrypto/openssl/rsa_key_openssl.h" +#include "content/child/webcrypto/openssl/rsa_sign_openssl.h" +#include "content/child/webcrypto/openssl/util_openssl.h" +#include "content/child/webcrypto/status.h" +#include "crypto/openssl_util.h" +#include "crypto/scoped_openssl_types.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace content { + +namespace webcrypto { + +namespace { + +// Extracts the OpenSSL key and digest from a WebCrypto key. The returned +// pointers will remain valid as long as |key| is alive. +Status GetPKeyAndDigest(const blink::WebCryptoKey& key, + EVP_PKEY** pkey, + const EVP_MD** digest) { + *pkey = AsymKeyOpenSsl::Cast(key)->key(); + + *digest = GetDigest(key.algorithm().rsaHashedParams()->hash().id()); + if (!*digest) + return Status::ErrorUnsupported(); + + return Status::Success(); +} + +// Sets the PSS parameters on |pctx| if the key is for RSA-PSS. +// +// Otherwise returns Success without doing anything. +Status ApplyRsaPssOptions(const blink::WebCryptoKey& key, + const EVP_MD* const mgf_digest, + unsigned int salt_length_bytes, + EVP_PKEY_CTX* pctx) { + // Only apply RSA-PSS options if the key is for RSA-PSS. + if (key.algorithm().id() != blink::WebCryptoAlgorithmIdRsaPss) { + DCHECK_EQ(0u, salt_length_bytes); + DCHECK_EQ(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, key.algorithm().id()); + return Status::Success(); + } + + // BoringSSL takes a signed int for the salt length, and interprets + // negative values in a special manner. Make sure not to silently underflow. + base::CheckedNumeric<int> salt_length_bytes_int(salt_length_bytes); + if (!salt_length_bytes_int.IsValid()) { + // TODO(eroman): Give a better error message. + return Status::OperationError(); + } + + if (1 != EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) || + 1 != EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf_digest) || + 1 != EVP_PKEY_CTX_set_rsa_pss_saltlen( + pctx, salt_length_bytes_int.ValueOrDie())) { + return Status::OperationError(); + } + + return Status::Success(); +} + +} // namespace + +Status RsaSign(const blink::WebCryptoKey& key, + unsigned int pss_salt_length_bytes, + const CryptoData& data, + std::vector<uint8_t>* buffer) { + if (key.type() != blink::WebCryptoKeyTypePrivate) + return Status::ErrorUnexpectedKeyType(); + + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create()); + EVP_PKEY_CTX* pctx = NULL; // Owned by |ctx|. + + EVP_PKEY* private_key = NULL; + const EVP_MD* digest = NULL; + Status status = GetPKeyAndDigest(key, &private_key, &digest); + if (status.IsError()) + return status; + + // NOTE: A call to EVP_DigestSignFinal() with a NULL second parameter + // returns a maximum allocation size, while the call without a NULL returns + // the real one, which may be smaller. + size_t sig_len = 0; + if (!ctx.get() || + !EVP_DigestSignInit(ctx.get(), &pctx, digest, NULL, private_key)) { + return Status::OperationError(); + } + + // Set PSS-specific options (if applicable). + status = ApplyRsaPssOptions(key, digest, pss_salt_length_bytes, pctx); + if (status.IsError()) + return status; + + if (!EVP_DigestSignUpdate(ctx.get(), data.bytes(), data.byte_length()) || + !EVP_DigestSignFinal(ctx.get(), NULL, &sig_len)) { + return Status::OperationError(); + } + + buffer->resize(sig_len); + if (!EVP_DigestSignFinal(ctx.get(), &buffer->front(), &sig_len)) + return Status::OperationError(); + + buffer->resize(sig_len); + return Status::Success(); +} + +Status RsaVerify(const blink::WebCryptoKey& key, + unsigned int pss_salt_length_bytes, + const CryptoData& signature, + const CryptoData& data, + bool* signature_match) { + if (key.type() != blink::WebCryptoKeyTypePublic) + return Status::ErrorUnexpectedKeyType(); + + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create()); + EVP_PKEY_CTX* pctx = NULL; // Owned by |ctx|. + + EVP_PKEY* public_key = NULL; + const EVP_MD* digest = NULL; + Status status = GetPKeyAndDigest(key, &public_key, &digest); + if (status.IsError()) + return status; + + if (!EVP_DigestVerifyInit(ctx.get(), &pctx, digest, NULL, public_key)) + return Status::OperationError(); + + // Set PSS-specific options (if applicable). + status = ApplyRsaPssOptions(key, digest, pss_salt_length_bytes, pctx); + if (status.IsError()) + return status; + + if (!EVP_DigestVerifyUpdate(ctx.get(), data.bytes(), data.byte_length())) + return Status::OperationError(); + + // Note that the return value can be: + // 1 --> Success + // 0 --> Verification failed + // <0 --> Operation error + int rv = EVP_DigestVerifyFinal( + ctx.get(), signature.bytes(), signature.byte_length()); + *signature_match = rv == 1; + return rv >= 0 ? Status::Success() : Status::OperationError(); +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/openssl/rsa_sign_openssl.h b/chromium/content/child/webcrypto/openssl/rsa_sign_openssl.h new file mode 100644 index 00000000000..c750ee2e57c --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/rsa_sign_openssl.h @@ -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. + +#ifndef CONTENT_CHILD_WEBCRYPTO_OPENSSL_RSA_SIGN_OPENSSL_H_ +#define CONTENT_CHILD_WEBCRYPTO_OPENSSL_RSA_SIGN_OPENSSL_H_ + +#include <stdint.h> + +#include <vector> + +namespace blink { +class WebCryptoKey; +} + +namespace content { + +namespace webcrypto { + +class CryptoData; +class Status; + +// Helper functions for doing RSA-SSA signing and verification +// (both PKCS1-v1_5 and PSS flavor). +// +// The salt length parameter is only relevant when the key is for RSA-PSS. In +// other cases it should be set to zero. + +Status RsaSign(const blink::WebCryptoKey& key, + unsigned int pss_salt_length_bytes, + const CryptoData& data, + std::vector<uint8_t>* buffer); + +Status RsaVerify(const blink::WebCryptoKey& key, + unsigned int pss_salt_length_bytes, + const CryptoData& signature, + const CryptoData& data, + bool* signature_match); + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_OPENSSL_RSA_SIGN_OPENSSL_H_ diff --git a/chromium/content/child/webcrypto/openssl/rsa_ssa_openssl.cc b/chromium/content/child/webcrypto/openssl/rsa_ssa_openssl.cc new file mode 100644 index 00000000000..1d3b834fc79 --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/rsa_ssa_openssl.cc @@ -0,0 +1,61 @@ +// 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/rsa_key_openssl.h" +#include "content/child/webcrypto/openssl/rsa_sign_openssl.h" +#include "content/child/webcrypto/status.h" + +namespace content { + +namespace webcrypto { + +namespace { + +class RsaSsaImplementation : public RsaHashedAlgorithm { + public: + RsaSsaImplementation() + : RsaHashedAlgorithm(blink::WebCryptoKeyUsageVerify, + blink::WebCryptoKeyUsageSign) {} + + 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; + } + } + + Status Sign(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& data, + std::vector<uint8_t>* buffer) const override { + return RsaSign(key, 0, data, buffer); + } + + Status Verify(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const CryptoData& signature, + const CryptoData& data, + bool* signature_match) const override { + return RsaVerify(key, 0, signature, data, signature_match); + } +}; + +} // namespace + +AlgorithmImplementation* CreatePlatformRsaSsaImplementation() { + return new RsaSsaImplementation; +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/openssl/sha_openssl.cc b/chromium/content/child/webcrypto/openssl/sha_openssl.cc new file mode 100644 index 00000000000..e7ee049fefd --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/sha_openssl.cc @@ -0,0 +1,140 @@ +// 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 "base/stl_util.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) {} + + bool consume(const unsigned char* data, unsigned int size) override { + 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(); + } + + bool finish(unsigned char*& result_data, + unsigned int& result_data_size) override { + Status error = FinishInternal(result_, &result_data_size); + if (!error.IsSuccess()) + return false; + result_data = result_; + return true; + } + + Status FinishWithVectorAndStatus(std::vector<uint8_t>* result) { + const int hash_expected_size = EVP_MD_CTX_size(digest_context_.get()); + result->resize(hash_expected_size); + unsigned char* const hash_buffer = vector_as_array(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: + Status Digest(const blink::WebCryptoAlgorithm& algorithm, + const CryptoData& data, + std::vector<uint8_t>* 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/chromium/content/child/webcrypto/openssl/sym_key_openssl.cc b/chromium/content/child/webcrypto/openssl/sym_key_openssl.cc new file mode 100644 index 00000000000..39238335a26 --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/sym_key_openssl.cc @@ -0,0 +1,60 @@ +// 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/generate_key_result.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 usages, + unsigned keylen_bytes, + GenerateKeyResult* result) { + 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(); + } + + result->AssignSecretKey( + blink::WebCryptoKey::create(new SymKeyOpenSsl(CryptoData(random_bytes)), + blink::WebCryptoKeyTypeSecret, + extractable, + algorithm, + usages)); + + return Status::Success(); +} + +Status ImportKeyRawOpenSsl(const CryptoData& key_data, + const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) { + *key = blink::WebCryptoKey::create(new SymKeyOpenSsl(key_data), + blink::WebCryptoKeyTypeSecret, + extractable, + algorithm, + usages); + return Status::Success(); +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/openssl/sym_key_openssl.h b/chromium/content/child/webcrypto/openssl/sym_key_openssl.h new file mode 100644 index 00000000000..bb5def4704f --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/sym_key_openssl.h @@ -0,0 +1,34 @@ +// 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 GenerateKeyResult; +class Status; + +Status GenerateSecretKeyOpenSsl(const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + unsigned keylen_bytes, + GenerateKeyResult* result); + +Status ImportKeyRawOpenSsl(const CryptoData& key_data, + const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key); + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_OPENSSL_SYM_KEY_OPENSSL_H_ diff --git a/chromium/content/child/webcrypto/openssl/util_openssl.cc b/chromium/content/child/webcrypto/openssl/util_openssl.cc new file mode 100644 index 00000000000..4abf04278bf --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/util_openssl.cc @@ -0,0 +1,249 @@ +// 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 <openssl/pkcs12.h> + +#include "base/stl_util.h" +#include "content/child/webcrypto/crypto_data.h" +#include "content/child/webcrypto/openssl/key_openssl.h" +#include "content/child/webcrypto/platform_crypto.h" +#include "content/child/webcrypto/status.h" +#include "crypto/openssl_util.h" + +namespace content { + +namespace webcrypto { + +namespace { + +// Exports an EVP_PKEY public key to the SPKI format. +Status ExportPKeySpki(EVP_PKEY* key, std::vector<uint8_t>* buffer) { + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + crypto::ScopedBIO bio(BIO_new(BIO_s_mem())); + + // TODO(eroman): Use the OID specified by webcrypto spec. + // http://crbug.com/373545 + if (!i2d_PUBKEY_bio(bio.get(), key)) + return Status::ErrorUnexpected(); + + char* data = NULL; + long len = BIO_get_mem_data(bio.get(), &data); + if (!data || len < 0) + return Status::ErrorUnexpected(); + + buffer->assign(data, data + len); + return Status::Success(); +} + +// Exports an EVP_PKEY private key to the PKCS8 format. +Status ExportPKeyPkcs8(EVP_PKEY* key, std::vector<uint8_t>* buffer) { + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + crypto::ScopedBIO bio(BIO_new(BIO_s_mem())); + + // TODO(eroman): Use the OID specified by webcrypto spec. + // http://crbug.com/373545 + if (!i2d_PKCS8PrivateKeyInfo_bio(bio.get(), key)) + return Status::ErrorUnexpected(); + + char* data = NULL; + long len = BIO_get_mem_data(bio.get(), &data); + if (!data || len < 0) + return Status::ErrorUnexpected(); + + buffer->assign(data, data + len); + return Status::Success(); +} + +} // namespace + +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; + } +} + +Status AeadEncryptDecrypt(EncryptOrDecrypt mode, + const std::vector<uint8_t>& raw_key, + const CryptoData& data, + unsigned int tag_length_bytes, + const CryptoData& iv, + const CryptoData& additional_data, + const EVP_AEAD* aead_alg, + std::vector<uint8_t>* buffer) { + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + EVP_AEAD_CTX ctx; + + if (!aead_alg) + return Status::ErrorUnexpected(); + + if (!EVP_AEAD_CTX_init(&ctx, + aead_alg, + vector_as_array(&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, + vector_as_array(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() + EVP_AEAD_max_overhead(aead_alg)); + + ok = EVP_AEAD_CTX_seal(&ctx, + vector_as_array(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 CreateWebCryptoPublicKey(crypto::ScopedEVP_PKEY public_key, + const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) { + // Serialize the key at creation time so that if structured cloning is + // requested it can be done synchronously from the Blink thread. + std::vector<uint8_t> spki_data; + Status status = ExportPKeySpki(public_key.get(), &spki_data); + if (status.IsError()) + return status; + + *key = blink::WebCryptoKey::create( + new AsymKeyOpenSsl(public_key.Pass(), CryptoData(spki_data)), + blink::WebCryptoKeyTypePublic, extractable, algorithm, usages); + return Status::Success(); +} + +Status CreateWebCryptoPrivateKey(crypto::ScopedEVP_PKEY private_key, + const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) { + // Serialize the key at creation time so that if structured cloning is + // requested it can be done synchronously from the Blink thread. + std::vector<uint8_t> pkcs8_data; + Status status = ExportPKeyPkcs8(private_key.get(), &pkcs8_data); + if (status.IsError()) + return status; + + *key = blink::WebCryptoKey::create( + new AsymKeyOpenSsl(private_key.Pass(), CryptoData(pkcs8_data)), + blink::WebCryptoKeyTypePrivate, extractable, algorithm, usages); + return Status::Success(); +} + +Status ImportUnverifiedPkeyFromSpki(const CryptoData& key_data, + int expected_pkey_id, + crypto::ScopedEVP_PKEY* pkey) { + if (!key_data.byte_length()) + return Status::ErrorImportEmptyKeyData(); + + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + + crypto::ScopedBIO bio(BIO_new_mem_buf(const_cast<uint8_t*>(key_data.bytes()), + key_data.byte_length())); + if (!bio.get()) + return Status::ErrorUnexpected(); + + pkey->reset(d2i_PUBKEY_bio(bio.get(), NULL)); + if (!pkey->get()) + return Status::DataError(); + + if (EVP_PKEY_id(pkey->get()) != expected_pkey_id) + return Status::DataError(); // Data did not define expected key type. + + return Status::Success(); +} + +Status ImportUnverifiedPkeyFromPkcs8(const CryptoData& key_data, + int expected_pkey_id, + crypto::ScopedEVP_PKEY* pkey) { + if (!key_data.byte_length()) + return Status::ErrorImportEmptyKeyData(); + + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + + crypto::ScopedBIO bio(BIO_new_mem_buf(const_cast<uint8_t*>(key_data.bytes()), + key_data.byte_length())); + if (!bio.get()) + return Status::ErrorUnexpected(); + + crypto::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>::Type + p8inf(d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), NULL)); + if (!p8inf.get()) + return Status::DataError(); + + pkey->reset(EVP_PKCS82PKEY(p8inf.get())); + if (!pkey->get()) + return Status::DataError(); + + if (EVP_PKEY_id(pkey->get()) != expected_pkey_id) + return Status::DataError(); // Data did not define expected key type. + + return Status::Success(); +} + +BIGNUM* CreateBIGNUM(const std::string& n) { + return BN_bin2bn(reinterpret_cast<const uint8_t*>(n.data()), n.size(), NULL); +} + +std::vector<uint8_t> BIGNUMToVector(const BIGNUM* n) { + std::vector<uint8_t> v(BN_num_bytes(n)); + BN_bn2bin(n, vector_as_array(&v)); + return v; +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/openssl/util_openssl.h b/chromium/content/child/webcrypto/openssl/util_openssl.h new file mode 100644 index 00000000000..5031cf27e9b --- /dev/null +++ b/chromium/content/child/webcrypto/openssl/util_openssl.h @@ -0,0 +1,87 @@ +// 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 <string> +#include <vector> + +#include <openssl/ossl_typ.h> + +#include "crypto/scoped_openssl_types.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" +#include "third_party/WebKit/public/platform/WebCryptoKey.h" + +namespace content { + +namespace webcrypto { + +class CryptoData; +class Status; + +// The values of these constants correspond with the "enc" parameter of +// EVP_CipherInit_ex(), do not change. +enum EncryptOrDecrypt { DECRYPT=0, ENCRYPT=1 }; + +const EVP_MD* GetDigest(blink::WebCryptoAlgorithmId id); + +// Does either encryption or decryption for an AEAD algorithm. +// * |mode| controls whether encryption or decryption is done +// * |aead_alg| the algorithm (for instance AES-GCM) +// * |buffer| where the ciphertext or plaintext is written to. +Status AeadEncryptDecrypt(EncryptOrDecrypt mode, + const std::vector<uint8_t>& raw_key, + const CryptoData& data, + unsigned int tag_length_bytes, + const CryptoData& iv, + const CryptoData& additional_data, + const EVP_AEAD* aead_alg, + std::vector<uint8_t>* buffer); + +// Creates a WebCrypto public key given an EVP_PKEY. This step includes +// exporting the key to SPKI format, for use by serialization later. +Status CreateWebCryptoPublicKey( + crypto::ScopedEVP_PKEY public_key, + const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key); + +// Creates a WebCrypto private key given an EVP_PKEY. This step includes +// exporting the key to PKCS8 format, for use by serialization later. +Status CreateWebCryptoPrivateKey( + crypto::ScopedEVP_PKEY private_key, + const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key); + +// Imports SPKI bytes to an EVP_PKEY for a public key. The resulting asymmetric +// key may be invalid, and should be verified using something like +// RSA_check_key(). The only validation performed by this function is to ensure +// the key type matched |expected_pkey_id|. +Status ImportUnverifiedPkeyFromSpki(const CryptoData& key_data, + int expected_pkey_id, + crypto::ScopedEVP_PKEY* pkey); + +// Imports PKCS8 bytes to an EVP_PKEY for a private key. The resulting +// asymmetric key may be invalid, and should be verified using something like +// RSA_check_key(). The only validation performed by this function is to ensure +// the key type matched |expected_pkey_id|. +Status ImportUnverifiedPkeyFromPkcs8(const CryptoData& key_data, + int expected_pkey_id, + crypto::ScopedEVP_PKEY* pkey); + +// Allocates a new BIGNUM given a std::string big-endian representation. +BIGNUM* CreateBIGNUM(const std::string& n); + +// Converts a BIGNUM to a big endian byte array. +std::vector<uint8_t> BIGNUMToVector(const BIGNUM* n); + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_OPENSSL_UTIL_OPENSSL_H_ diff --git a/chromium/content/child/webcrypto/platform_crypto.h b/chromium/content/child/webcrypto/platform_crypto.h index 9e957301c01..2977b9c97f5 100644 --- a/chromium/content/child/webcrypto/platform_crypto.h +++ b/chromium/content/child/webcrypto/platform_crypto.h @@ -5,285 +5,36 @@ #ifndef CONTENT_CHILD_WEBCRYPTO_PLATFORM_CRYPTO_H_ #define CONTENT_CHILD_WEBCRYPTO_PLATFORM_CRYPTO_H_ +#include <stdint.h> #include <vector> -#include "base/basictypes.h" -#include "base/compiler_specific.h" #include "base/memory/scoped_ptr.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); - -} // namespace platform +AlgorithmImplementation* CreatePlatformShaImplementation(); +AlgorithmImplementation* CreatePlatformAesCbcImplementation(); +AlgorithmImplementation* CreatePlatformAesCtrImplementation(); +AlgorithmImplementation* CreatePlatformAesGcmImplementation(); +AlgorithmImplementation* CreatePlatformAesKwImplementation(); +AlgorithmImplementation* CreatePlatformHmacImplementation(); +AlgorithmImplementation* CreatePlatformRsaOaepImplementation(); +AlgorithmImplementation* CreatePlatformRsaSsaImplementation(); +AlgorithmImplementation* CreatePlatformRsaPssImplementation(); + +bool PlatformSerializeKeyForClone(const blink::WebCryptoKey& key, + blink::WebVector<uint8_t>* key_data); } // namespace webcrypto diff --git a/chromium/content/child/webcrypto/platform_crypto_nss.cc b/chromium/content/child/webcrypto/platform_crypto_nss.cc deleted file mode 100644 index c5c18afc3d4..00000000000 --- a/chromium/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/chromium/content/child/webcrypto/platform_crypto_openssl.cc b/chromium/content/child/webcrypto/platform_crypto_openssl.cc deleted file mode 100644 index 1235e51f2cc..00000000000 --- a/chromium/content/child/webcrypto/platform_crypto_openssl.cc +++ /dev/null @@ -1,520 +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 "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 3 key lengths: 128, 192, 256 bits - switch (key_length_bytes) { - case 16: - return EVP_aes_128_cbc(); - case 24: - return EVP_aes_192_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> 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(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(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::ScopedOpenSSL<EVP_MD_CTX, EVP_MD_CTX_destroy> 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(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) { - 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); - - crypto::OpenSSLErrStackTracer(FROM_HERE); - - 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(); -} - -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): http://crbug.com/267888 - return Status::ErrorUnsupported(); -} - -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/chromium/content/child/webcrypto/shared_crypto.cc b/chromium/content/child/webcrypto/shared_crypto.cc deleted file mode 100644 index 65559b04c82..00000000000 --- a/chromium/content/child/webcrypto/shared_crypto.cc +++ /dev/null @@ -1,955 +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. - blink::WebCryptoKeyType key_type; - bool key_type_known = false; - - 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: - key_type = blink::WebCryptoKeyTypePublic; - key_type_known = true; - break; - case blink::WebCryptoKeyFormatPkcs8: - key_type = blink::WebCryptoKeyTypePrivate; - key_type_known = true; - break; - case blink::WebCryptoKeyFormatJwk: - key_type_known = false; - break; - default: - return Status::ErrorUnexpected(); - } - - if (key_type_known) - return CheckKeyUsages(algorithm, key_type, usages); - - // 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/chromium/content/child/webcrypto/shared_crypto.h b/chromium/content/child/webcrypto/shared_crypto.h deleted file mode 100644 index d9e5a959db7..00000000000 --- a/chromium/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/chromium/content/child/webcrypto/shared_crypto_unittest.cc b/chromium/content/child/webcrypto/shared_crypto_unittest.cc deleted file mode 100644 index f4afaf70f61..00000000000 --- a/chromium/content/child/webcrypto/shared_crypto_unittest.cc +++ /dev/null @@ -1,4399 +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 <algorithm> -#include <string> -#include <vector> - -#include "base/basictypes.h" -#include "base/file_util.h" -#include "base/json/json_reader.h" -#include "base/json/json_writer.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/path_service.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "content/child/webcrypto/crypto_data.h" -#include "content/child/webcrypto/status.h" -#include "content/child/webcrypto/webcrypto_util.h" -#include "content/public/common/content_paths.h" -#include "testing/gtest/include/gtest/gtest.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" -#include "third_party/re2/re2/re2.h" - -#if !defined(USE_OPENSSL) -#include <nss.h> -#include <pk11pub.h> - -#include "crypto/scoped_nss_types.h" -#endif - -// The OpenSSL implementation of WebCrypto is less complete, so don't run all of -// the tests: http://crbug.com/267888 -#if defined(USE_OPENSSL) -#define MAYBE(test_name) DISABLED_##test_name -#else -#define MAYBE(test_name) test_name -#endif - -#define EXPECT_BYTES_EQ(expected, actual) \ - EXPECT_EQ(CryptoData(expected), CryptoData(actual)) - -#define EXPECT_BYTES_EQ_HEX(expected_hex, actual_bytes) \ - EXPECT_BYTES_EQ(HexStringToBytes(expected_hex), actual_bytes) - -namespace content { - -namespace webcrypto { - -// These functions are used by GTEST to support EXPECT_EQ() for -// webcrypto::Status and webcrypto::CryptoData - -void PrintTo(const Status& status, ::std::ostream* os) { - if (status.IsSuccess()) - *os << "Success"; - else - *os << "Error type: " << status.error_type() - << " Error details: " << status.error_details(); -} - -bool operator==(const content::webcrypto::Status& a, - const content::webcrypto::Status& b) { - if (a.IsSuccess() != b.IsSuccess()) - return false; - if (a.IsSuccess()) - return true; - return a.error_type() == b.error_type() && - a.error_details() == b.error_details(); -} - -bool operator!=(const content::webcrypto::Status& a, - const content::webcrypto::Status& b) { - return !(a == b); -} - -void PrintTo(const CryptoData& data, ::std::ostream* os) { - *os << "[" << base::HexEncode(data.bytes(), data.byte_length()) << "]"; -} - -bool operator==(const content::webcrypto::CryptoData& a, - const content::webcrypto::CryptoData& b) { - return a.byte_length() == b.byte_length() && - memcmp(a.bytes(), b.bytes(), a.byte_length()) == 0; -} - -bool operator!=(const content::webcrypto::CryptoData& a, - const content::webcrypto::CryptoData& b) { - return !(a == b); -} - -namespace { - -// ----------------------------------------------------------------------------- - -// TODO(eroman): For Linux builds using system NSS, AES-GCM support is a -// runtime dependency. Test it by trying to import a key. -// TODO(padolph): Consider caching the result of the import key test. -bool SupportsAesGcm() { - std::vector<uint8> key_raw(16, 0); - - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - Status status = ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(key_raw), - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm), - true, - blink::WebCryptoKeyUsageEncrypt, - &key); - - if (status.IsError()) - EXPECT_EQ(blink::WebCryptoErrorTypeNotSupported, status.error_type()); - return status.IsSuccess(); -} - -bool SupportsRsaOaep() { -#if defined(USE_OPENSSL) - return false; -#else - // TODO(eroman): Exclude version test for OS_CHROMEOS -#if defined(USE_NSS) - if (!NSS_VersionCheck("3.16.2")) - return false; -#endif - crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); - return !!PK11_DoesMechanism(slot.get(), CKM_RSA_PKCS_OAEP); -#endif -} - -bool SupportsRsaKeyImport() { -// TODO(eroman): Exclude version test for OS_CHROMEOS -#if defined(USE_NSS) - if (!NSS_VersionCheck("3.16.2")) { - LOG(WARNING) << "RSA key import is not supported by this version of NSS. " - "Skipping some tests"; - return false; - } -#endif - return true; -} - -blink::WebCryptoAlgorithm CreateRsaHashedKeyGenAlgorithm( - blink::WebCryptoAlgorithmId algorithm_id, - const blink::WebCryptoAlgorithmId hash_id, - unsigned int modulus_length, - const std::vector<uint8>& public_exponent) { - DCHECK(algorithm_id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 || - algorithm_id == blink::WebCryptoAlgorithmIdRsaOaep); - DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); - return blink::WebCryptoAlgorithm::adoptParamsAndCreate( - algorithm_id, - new blink::WebCryptoRsaHashedKeyGenParams( - CreateAlgorithm(hash_id), - modulus_length, - webcrypto::Uint8VectorStart(public_exponent), - public_exponent.size())); -} - -// Creates an RSA-OAEP algorithm -blink::WebCryptoAlgorithm CreateRsaOaepAlgorithm( - const std::vector<uint8>& label) { - return blink::WebCryptoAlgorithm::adoptParamsAndCreate( - blink::WebCryptoAlgorithmIdRsaOaep, - new blink::WebCryptoRsaOaepParams( - !label.empty(), Uint8VectorStart(label), label.size())); -} - -// Creates an AES-CBC algorithm. -blink::WebCryptoAlgorithm CreateAesCbcAlgorithm(const std::vector<uint8>& iv) { - return blink::WebCryptoAlgorithm::adoptParamsAndCreate( - blink::WebCryptoAlgorithmIdAesCbc, - new blink::WebCryptoAesCbcParams(Uint8VectorStart(iv), iv.size())); -} - -// Creates an AES-GCM algorithm. -blink::WebCryptoAlgorithm CreateAesGcmAlgorithm( - const std::vector<uint8>& iv, - const std::vector<uint8>& additional_data, - unsigned int tag_length_bits) { - EXPECT_TRUE(SupportsAesGcm()); - return blink::WebCryptoAlgorithm::adoptParamsAndCreate( - blink::WebCryptoAlgorithmIdAesGcm, - new blink::WebCryptoAesGcmParams(Uint8VectorStart(iv), - iv.size(), - true, - Uint8VectorStart(additional_data), - additional_data.size(), - true, - tag_length_bits)); -} - -// Creates an HMAC algorithm whose parameters struct is compatible with key -// generation. It is an error to call this with a hash_id that is not a SHA*. -// The key_length_bits parameter is optional, with zero meaning unspecified. -blink::WebCryptoAlgorithm CreateHmacKeyGenAlgorithm( - blink::WebCryptoAlgorithmId hash_id, - unsigned int key_length_bits) { - DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); - // key_length_bytes == 0 means unspecified - return blink::WebCryptoAlgorithm::adoptParamsAndCreate( - blink::WebCryptoAlgorithmIdHmac, - new blink::WebCryptoHmacKeyGenParams( - CreateAlgorithm(hash_id), (key_length_bits != 0), key_length_bits)); -} - -// Returns a slightly modified version of the input vector. -// -// - For non-empty inputs a single bit is inverted. -// - For empty inputs, a byte is added. -std::vector<uint8> Corrupted(const std::vector<uint8>& input) { - std::vector<uint8> corrupted_data(input); - if (corrupted_data.empty()) - corrupted_data.push_back(0); - corrupted_data[corrupted_data.size() / 2] ^= 0x01; - return corrupted_data; -} - -std::vector<uint8> HexStringToBytes(const std::string& hex) { - std::vector<uint8> bytes; - base::HexStringToBytes(hex, &bytes); - return bytes; -} - -std::vector<uint8> MakeJsonVector(const std::string& json_string) { - return std::vector<uint8>(json_string.begin(), json_string.end()); -} - -std::vector<uint8> MakeJsonVector(const base::DictionaryValue& dict) { - std::string json; - base::JSONWriter::Write(&dict, &json); - return MakeJsonVector(json); -} - -// ---------------------------------------------------------------- -// Helpers for working with JSON data files for test expectations. -// ---------------------------------------------------------------- - -// Reads a file in "src/content/test/data/webcrypto" to a base::Value. -// The file must be JSON, however it can also include C++ style comments. -::testing::AssertionResult ReadJsonTestFile(const char* test_file_name, - scoped_ptr<base::Value>* value) { - base::FilePath test_data_dir; - if (!PathService::Get(DIR_TEST_DATA, &test_data_dir)) - return ::testing::AssertionFailure() << "Couldn't retrieve test dir"; - - base::FilePath file_path = - test_data_dir.AppendASCII("webcrypto").AppendASCII(test_file_name); - - std::string file_contents; - if (!base::ReadFileToString(file_path, &file_contents)) { - return ::testing::AssertionFailure() - << "Couldn't read test file: " << file_path.value(); - } - - // Strip C++ style comments out of the "json" file, otherwise it cannot be - // parsed. - re2::RE2::GlobalReplace(&file_contents, re2::RE2("\\s*//.*"), ""); - - // Parse the JSON to a dictionary. - value->reset(base::JSONReader::Read(file_contents)); - if (!value->get()) { - return ::testing::AssertionFailure() - << "Couldn't parse test file JSON: " << file_path.value(); - } - - return ::testing::AssertionSuccess(); -} - -// Same as ReadJsonTestFile(), but return the value as a List. -::testing::AssertionResult ReadJsonTestFileToList( - const char* test_file_name, - scoped_ptr<base::ListValue>* list) { - // Read the JSON. - scoped_ptr<base::Value> json; - ::testing::AssertionResult result = ReadJsonTestFile(test_file_name, &json); - if (!result) - return result; - - // Cast to an ListValue. - base::ListValue* list_value = NULL; - if (!json->GetAsList(&list_value) || !list_value) - return ::testing::AssertionFailure() << "The JSON was not a list"; - - list->reset(list_value); - ignore_result(json.release()); - - return ::testing::AssertionSuccess(); -} - -// Read a string property from the dictionary with path |property_name| -// (which can include periods for nested dictionaries). Interprets the -// string as a hex encoded string and converts it to a bytes list. -// -// Returns empty vector on failure. -std::vector<uint8> GetBytesFromHexString(base::DictionaryValue* dict, - const char* property_name) { - std::string hex_string; - if (!dict->GetString(property_name, &hex_string)) { - EXPECT_TRUE(false) << "Couldn't get string property: " << property_name; - return std::vector<uint8>(); - } - - return HexStringToBytes(hex_string); -} - -// Reads a string property with path "property_name" and converts it to a -// WebCryptoAlgorith. Returns null algorithm on failure. -blink::WebCryptoAlgorithm GetDigestAlgorithm(base::DictionaryValue* dict, - const char* property_name) { - std::string algorithm_name; - if (!dict->GetString(property_name, &algorithm_name)) { - EXPECT_TRUE(false) << "Couldn't get string property: " << property_name; - return blink::WebCryptoAlgorithm::createNull(); - } - - struct { - const char* name; - blink::WebCryptoAlgorithmId id; - } kDigestNameToId[] = { - {"sha-1", blink::WebCryptoAlgorithmIdSha1}, - {"sha-256", blink::WebCryptoAlgorithmIdSha256}, - {"sha-384", blink::WebCryptoAlgorithmIdSha384}, - {"sha-512", blink::WebCryptoAlgorithmIdSha512}, - }; - - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDigestNameToId); ++i) { - if (kDigestNameToId[i].name == algorithm_name) - return CreateAlgorithm(kDigestNameToId[i].id); - } - - return blink::WebCryptoAlgorithm::createNull(); -} - -// Helper for ImportJwkFailures and ImportJwkOctFailures. Restores the JWK JSON -// dictionary to a good state -void RestoreJwkOctDictionary(base::DictionaryValue* dict) { - dict->Clear(); - dict->SetString("kty", "oct"); - dict->SetString("alg", "A128CBC"); - dict->SetString("use", "enc"); - dict->SetBoolean("ext", false); - dict->SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); -} - -// Helper for ImportJwkRsaFailures. Restores the JWK JSON -// dictionary to a good state -void RestoreJwkRsaDictionary(base::DictionaryValue* dict) { - dict->Clear(); - dict->SetString("kty", "RSA"); - dict->SetString("alg", "RS256"); - dict->SetString("use", "sig"); - dict->SetBoolean("ext", false); - dict->SetString( - "n", - "qLOyhK-OtQs4cDSoYPFGxJGfMYdjzWxVmMiuSBGh4KvEx-CwgtaTpef87Wdc9GaFEncsDLxk" - "p0LGxjD1M8jMcvYq6DPEC_JYQumEu3i9v5fAEH1VvbZi9cTg-rmEXLUUjvc5LdOq_5OuHmtm" - "e7PUJHYW1PW6ENTP0ibeiNOfFvs"); - dict->SetString("e", "AQAB"); -} - -// Returns true if any of the vectors in the input list have identical content. -// Dumb O(n^2) implementation but should be fast enough for the input sizes that -// are used. -bool CopiesExist(const std::vector<std::vector<uint8> >& bufs) { - for (size_t i = 0; i < bufs.size(); ++i) { - for (size_t j = i + 1; j < bufs.size(); ++j) { - if (CryptoData(bufs[i]) == CryptoData(bufs[j])) - return true; - } - } - return false; -} - -blink::WebCryptoAlgorithm CreateAesKeyGenAlgorithm( - blink::WebCryptoAlgorithmId aes_alg_id, - unsigned short length) { - return blink::WebCryptoAlgorithm::adoptParamsAndCreate( - aes_alg_id, new blink::WebCryptoAesKeyGenParams(length)); -} - -blink::WebCryptoAlgorithm CreateAesCbcKeyGenAlgorithm( - unsigned short key_length_bits) { - return CreateAesKeyGenAlgorithm(blink::WebCryptoAlgorithmIdAesCbc, - key_length_bits); -} - -blink::WebCryptoAlgorithm CreateAesGcmKeyGenAlgorithm( - unsigned short key_length_bits) { - EXPECT_TRUE(SupportsAesGcm()); - return CreateAesKeyGenAlgorithm(blink::WebCryptoAlgorithmIdAesGcm, - key_length_bits); -} - -blink::WebCryptoAlgorithm CreateAesKwKeyGenAlgorithm( - unsigned short key_length_bits) { - return CreateAesKeyGenAlgorithm(blink::WebCryptoAlgorithmIdAesKw, - key_length_bits); -} - -// The following key pair is comprised of the SPKI (public key) and PKCS#8 -// (private key) representations of the key pair provided in Example 1 of the -// NIST test vectors at -// ftp://ftp.rsa.com/pub/rsalabs/tmp/pkcs1v15sign-vectors.txt -const unsigned int kModulusLengthBits = 1024; -const char* const kPublicKeySpkiDerHex = - "30819f300d06092a864886f70d010101050003818d0030818902818100a5" - "6e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad9" - "91d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfc" - "e0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e" - "6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cf" - "fb2249bd9a21370203010001"; -const char* const kPrivateKeyPkcs8DerHex = - "30820275020100300d06092a864886f70d01010105000482025f3082025b" - "02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52" - "a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab" - "7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921c" - "b23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef" - "22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d" - "4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c" - "568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee" - "896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31" - "b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b3" - "25024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e8629" - "6b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b" - "3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde12" - "3fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc72" - "3e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca" - "5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8d" - "d3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa71" - "2049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd" - "48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027" - "156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319" - "584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24" - "a79f4d"; -// The modulus and exponent (in hex) of kPublicKeySpkiDerHex -const char* const kPublicKeyModulusHex = - "A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056" - "FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B" - "8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E138" - "6B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137"; -const char* const kPublicKeyExponentHex = "010001"; - -class SharedCryptoTest : public testing::Test { - protected: - virtual void SetUp() OVERRIDE { Init(); } -}; - -blink::WebCryptoKey ImportSecretKeyFromRaw( - const std::vector<uint8>& key_raw, - const blink::WebCryptoAlgorithm& algorithm, - blink::WebCryptoKeyUsageMask usage) { - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - bool extractable = true; - EXPECT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(key_raw), - algorithm, - extractable, - usage, - &key)); - - EXPECT_FALSE(key.isNull()); - EXPECT_TRUE(key.handle()); - EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); - EXPECT_EQ(algorithm.id(), key.algorithm().id()); - EXPECT_EQ(extractable, key.extractable()); - EXPECT_EQ(usage, key.usages()); - return key; -} - -void ImportRsaKeyPair(const std::vector<uint8>& spki_der, - const std::vector<uint8>& pkcs8_der, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask public_key_usage_mask, - blink::WebCryptoKeyUsageMask private_key_usage_mask, - blink::WebCryptoKey* public_key, - blink::WebCryptoKey* private_key) { - ASSERT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatSpki, - CryptoData(spki_der), - algorithm, - true, - public_key_usage_mask, - public_key)); - EXPECT_FALSE(public_key->isNull()); - EXPECT_TRUE(public_key->handle()); - EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key->type()); - EXPECT_EQ(algorithm.id(), public_key->algorithm().id()); - EXPECT_TRUE(public_key->extractable()); - EXPECT_EQ(public_key_usage_mask, public_key->usages()); - - ASSERT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatPkcs8, - CryptoData(pkcs8_der), - algorithm, - extractable, - private_key_usage_mask, - private_key)); - EXPECT_FALSE(private_key->isNull()); - EXPECT_TRUE(private_key->handle()); - EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key->type()); - EXPECT_EQ(algorithm.id(), private_key->algorithm().id()); - EXPECT_EQ(extractable, private_key->extractable()); - EXPECT_EQ(private_key_usage_mask, private_key->usages()); -} - -Status AesGcmEncrypt(const blink::WebCryptoKey& key, - const std::vector<uint8>& iv, - const std::vector<uint8>& additional_data, - unsigned int tag_length_bits, - const std::vector<uint8>& plain_text, - std::vector<uint8>* cipher_text, - std::vector<uint8>* authentication_tag) { - EXPECT_TRUE(SupportsAesGcm()); - blink::WebCryptoAlgorithm algorithm = - CreateAesGcmAlgorithm(iv, additional_data, tag_length_bits); - - std::vector<uint8> output; - Status status = Encrypt(algorithm, key, CryptoData(plain_text), &output); - if (status.IsError()) - return status; - - if ((tag_length_bits % 8) != 0) { - EXPECT_TRUE(false) << "Encrypt should have failed."; - return Status::OperationError(); - } - - size_t tag_length_bytes = tag_length_bits / 8; - - if (tag_length_bytes > output.size()) { - EXPECT_TRUE(false) << "tag length is larger than output"; - return Status::OperationError(); - } - - // The encryption result is cipher text with authentication tag appended. - cipher_text->assign(output.begin(), - output.begin() + (output.size() - tag_length_bytes)); - authentication_tag->assign(output.begin() + cipher_text->size(), - output.end()); - - return Status::Success(); -} - -Status AesGcmDecrypt(const blink::WebCryptoKey& key, - const std::vector<uint8>& iv, - const std::vector<uint8>& additional_data, - unsigned int tag_length_bits, - const std::vector<uint8>& cipher_text, - const std::vector<uint8>& authentication_tag, - std::vector<uint8>* plain_text) { - EXPECT_TRUE(SupportsAesGcm()); - blink::WebCryptoAlgorithm algorithm = - CreateAesGcmAlgorithm(iv, additional_data, tag_length_bits); - - // Join cipher text and authentication tag. - std::vector<uint8> cipher_text_with_tag; - cipher_text_with_tag.reserve(cipher_text.size() + authentication_tag.size()); - cipher_text_with_tag.insert( - cipher_text_with_tag.end(), cipher_text.begin(), cipher_text.end()); - cipher_text_with_tag.insert(cipher_text_with_tag.end(), - authentication_tag.begin(), - authentication_tag.end()); - - return Decrypt(algorithm, key, CryptoData(cipher_text_with_tag), plain_text); -} - -Status ImportKeyJwk(const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key) { - return ImportKey(blink::WebCryptoKeyFormatJwk, - key_data, - algorithm, - extractable, - usage_mask, - key); -} - -Status ImportKeyJwkFromDict(const base::DictionaryValue& dict, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, - blink::WebCryptoKey* key) { - return ImportKeyJwk(CryptoData(MakeJsonVector(dict)), - algorithm, - extractable, - usage_mask, - key); -} - -// Parses a vector of JSON into a dictionary. -scoped_ptr<base::DictionaryValue> GetJwkDictionary( - const std::vector<uint8>& json) { - base::StringPiece json_string( - reinterpret_cast<const char*>(Uint8VectorStart(json)), json.size()); - base::Value* value = base::JSONReader::Read(json_string); - EXPECT_TRUE(value); - base::DictionaryValue* dict_value = NULL; - value->GetAsDictionary(&dict_value); - return scoped_ptr<base::DictionaryValue>(dict_value); -} - -// Verifies the input dictionary contains the expected values. Exact matches are -// required on the fields examined. -::testing::AssertionResult VerifyJwk( - const scoped_ptr<base::DictionaryValue>& dict, - const std::string& kty_expected, - const std::string& alg_expected, - blink::WebCryptoKeyUsageMask use_mask_expected) { - // ---- kty - std::string value_string; - if (!dict->GetString("kty", &value_string)) - return ::testing::AssertionFailure() << "Missing 'kty'"; - if (value_string != kty_expected) - return ::testing::AssertionFailure() << "Expected 'kty' to be " - << kty_expected << "but found " - << value_string; - - // ---- alg - if (!dict->GetString("alg", &value_string)) - return ::testing::AssertionFailure() << "Missing 'alg'"; - if (value_string != alg_expected) - return ::testing::AssertionFailure() << "Expected 'alg' to be " - << alg_expected << " but found " - << value_string; - - // ---- ext - // always expect ext == true in this case - bool ext_value; - if (!dict->GetBoolean("ext", &ext_value)) - return ::testing::AssertionFailure() << "Missing 'ext'"; - if (!ext_value) - return ::testing::AssertionFailure() - << "Expected 'ext' to be true but found false"; - - // ---- key_ops - base::ListValue* key_ops; - if (!dict->GetList("key_ops", &key_ops)) - return ::testing::AssertionFailure() << "Missing 'key_ops'"; - blink::WebCryptoKeyUsageMask key_ops_mask = 0; - Status status = GetWebCryptoUsagesFromJwkKeyOps(key_ops, &key_ops_mask); - if (status.IsError()) - return ::testing::AssertionFailure() << "Failure extracting 'key_ops'"; - if (key_ops_mask != use_mask_expected) - return ::testing::AssertionFailure() - << "Expected 'key_ops' mask to be " << use_mask_expected - << " but found " << key_ops_mask << " (" << value_string << ")"; - - return ::testing::AssertionSuccess(); -} - -// Verifies that the JSON in the input vector contains the provided -// expected values. Exact matches are required on the fields examined. -::testing::AssertionResult VerifySecretJwk( - const std::vector<uint8>& json, - const std::string& alg_expected, - const std::string& k_expected_hex, - blink::WebCryptoKeyUsageMask use_mask_expected) { - scoped_ptr<base::DictionaryValue> dict = GetJwkDictionary(json); - if (!dict.get() || dict->empty()) - return ::testing::AssertionFailure() << "JSON parsing failed"; - - // ---- k - std::string value_string; - if (!dict->GetString("k", &value_string)) - return ::testing::AssertionFailure() << "Missing 'k'"; - std::string k_value; - if (!webcrypto::Base64DecodeUrlSafe(value_string, &k_value)) - return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(k) failed"; - if (!LowerCaseEqualsASCII(base::HexEncode(k_value.data(), k_value.size()), - k_expected_hex.c_str())) { - return ::testing::AssertionFailure() << "Expected 'k' to be " - << k_expected_hex - << " but found something different"; - } - - return VerifyJwk(dict, "oct", alg_expected, use_mask_expected); -} - -// Verifies that the JSON in the input vector contains the provided -// expected values. Exact matches are required on the fields examined. -::testing::AssertionResult VerifyPublicJwk( - const std::vector<uint8>& json, - const std::string& alg_expected, - const std::string& n_expected_hex, - const std::string& e_expected_hex, - blink::WebCryptoKeyUsageMask use_mask_expected) { - scoped_ptr<base::DictionaryValue> dict = GetJwkDictionary(json); - if (!dict.get() || dict->empty()) - return ::testing::AssertionFailure() << "JSON parsing failed"; - - // ---- n - std::string value_string; - if (!dict->GetString("n", &value_string)) - return ::testing::AssertionFailure() << "Missing 'n'"; - std::string n_value; - if (!webcrypto::Base64DecodeUrlSafe(value_string, &n_value)) - return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(n) failed"; - if (base::HexEncode(n_value.data(), n_value.size()) != n_expected_hex) { - return ::testing::AssertionFailure() << "'n' does not match the expected " - "value"; - } - // TODO(padolph): LowerCaseEqualsASCII() does not work for above! - - // ---- e - if (!dict->GetString("e", &value_string)) - return ::testing::AssertionFailure() << "Missing 'e'"; - std::string e_value; - if (!webcrypto::Base64DecodeUrlSafe(value_string, &e_value)) - return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(e) failed"; - if (!LowerCaseEqualsASCII(base::HexEncode(e_value.data(), e_value.size()), - e_expected_hex.c_str())) { - return ::testing::AssertionFailure() << "Expected 'e' to be " - << e_expected_hex - << " but found something different"; - } - - return VerifyJwk(dict, "RSA", alg_expected, use_mask_expected); -} - -} // namespace - -TEST_F(SharedCryptoTest, CheckAesGcm) { - if (!SupportsAesGcm()) { - LOG(WARNING) << "AES GCM not supported on this platform, so some tests " - "will be skipped. Consider upgrading local NSS libraries"; - return; - } -} - -// Tests several Status objects against their expected hard coded values, as -// well as ensuring that comparison of Status objects works. -// Comparison should take into account both the error details, as well as the -// error type. -TEST_F(SharedCryptoTest, Status) { - // Even though the error message is the same, these should not be considered - // the same by the tests because the error type is different. - EXPECT_NE(Status::DataError(), Status::OperationError()); - EXPECT_NE(Status::Success(), Status::OperationError()); - - EXPECT_EQ(Status::Success(), Status::Success()); - EXPECT_EQ(Status::ErrorJwkPropertyWrongType("kty", "string"), - Status::ErrorJwkPropertyWrongType("kty", "string")); - - Status status = Status::Success(); - - EXPECT_FALSE(status.IsError()); - EXPECT_EQ("", status.error_details()); - - status = Status::OperationError(); - EXPECT_TRUE(status.IsError()); - EXPECT_EQ("", status.error_details()); - EXPECT_EQ(blink::WebCryptoErrorTypeOperation, status.error_type()); - - status = Status::DataError(); - EXPECT_TRUE(status.IsError()); - EXPECT_EQ("", status.error_details()); - EXPECT_EQ(blink::WebCryptoErrorTypeData, status.error_type()); - - status = Status::ErrorUnsupported(); - EXPECT_TRUE(status.IsError()); - EXPECT_EQ("The requested operation is unsupported", status.error_details()); - EXPECT_EQ(blink::WebCryptoErrorTypeNotSupported, status.error_type()); - - status = Status::ErrorJwkPropertyMissing("kty"); - EXPECT_TRUE(status.IsError()); - EXPECT_EQ("The required JWK property \"kty\" was missing", - status.error_details()); - EXPECT_EQ(blink::WebCryptoErrorTypeData, status.error_type()); - - status = Status::ErrorJwkPropertyWrongType("kty", "string"); - EXPECT_TRUE(status.IsError()); - EXPECT_EQ("The JWK property \"kty\" must be a string", - status.error_details()); - EXPECT_EQ(blink::WebCryptoErrorTypeData, status.error_type()); - - status = Status::ErrorJwkBase64Decode("n"); - EXPECT_TRUE(status.IsError()); - EXPECT_EQ("The JWK property \"n\" could not be base64 decoded", - status.error_details()); - EXPECT_EQ(blink::WebCryptoErrorTypeData, status.error_type()); -} - -TEST_F(SharedCryptoTest, DigestSampleSets) { - scoped_ptr<base::ListValue> tests; - ASSERT_TRUE(ReadJsonTestFileToList("digest.json", &tests)); - - for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { - SCOPED_TRACE(test_index); - base::DictionaryValue* test; - ASSERT_TRUE(tests->GetDictionary(test_index, &test)); - - blink::WebCryptoAlgorithm test_algorithm = - GetDigestAlgorithm(test, "algorithm"); - std::vector<uint8> test_input = GetBytesFromHexString(test, "input"); - std::vector<uint8> test_output = GetBytesFromHexString(test, "output"); - - std::vector<uint8> output; - ASSERT_EQ(Status::Success(), - Digest(test_algorithm, CryptoData(test_input), &output)); - EXPECT_BYTES_EQ(test_output, output); - } -} - -TEST_F(SharedCryptoTest, DigestSampleSetsInChunks) { - scoped_ptr<base::ListValue> tests; - ASSERT_TRUE(ReadJsonTestFileToList("digest.json", &tests)); - - for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { - SCOPED_TRACE(test_index); - base::DictionaryValue* test; - ASSERT_TRUE(tests->GetDictionary(test_index, &test)); - - blink::WebCryptoAlgorithm test_algorithm = - GetDigestAlgorithm(test, "algorithm"); - std::vector<uint8> test_input = GetBytesFromHexString(test, "input"); - std::vector<uint8> test_output = GetBytesFromHexString(test, "output"); - - // Test the chunk version of the digest functions. Test with 129 byte chunks - // because the SHA-512 chunk size is 128 bytes. - unsigned char* output; - unsigned int output_length; - static const size_t kChunkSizeBytes = 129; - size_t length = test_input.size(); - scoped_ptr<blink::WebCryptoDigestor> digestor( - CreateDigestor(test_algorithm.id())); - std::vector<uint8>::iterator begin = test_input.begin(); - size_t chunk_index = 0; - while (begin != test_input.end()) { - size_t chunk_length = std::min(kChunkSizeBytes, length - chunk_index); - std::vector<uint8> chunk(begin, begin + chunk_length); - ASSERT_TRUE(chunk.size() > 0); - EXPECT_TRUE(digestor->consume(&chunk.front(), chunk.size())); - chunk_index = chunk_index + chunk_length; - begin = begin + chunk_length; - } - EXPECT_TRUE(digestor->finish(output, output_length)); - EXPECT_BYTES_EQ(test_output, CryptoData(output, output_length)); - } -} - -TEST_F(SharedCryptoTest, HMACSampleSets) { - scoped_ptr<base::ListValue> tests; - ASSERT_TRUE(ReadJsonTestFileToList("hmac.json", &tests)); - // TODO(padolph): Missing known answer tests for HMAC SHA384, and SHA512. - for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { - SCOPED_TRACE(test_index); - base::DictionaryValue* test; - ASSERT_TRUE(tests->GetDictionary(test_index, &test)); - - blink::WebCryptoAlgorithm test_hash = GetDigestAlgorithm(test, "hash"); - const std::vector<uint8> test_key = GetBytesFromHexString(test, "key"); - const std::vector<uint8> test_message = - GetBytesFromHexString(test, "message"); - const std::vector<uint8> test_mac = GetBytesFromHexString(test, "mac"); - - blink::WebCryptoAlgorithm algorithm = - CreateAlgorithm(blink::WebCryptoAlgorithmIdHmac); - - blink::WebCryptoAlgorithm import_algorithm = - CreateHmacImportAlgorithm(test_hash.id()); - - blink::WebCryptoKey key = ImportSecretKeyFromRaw( - test_key, - import_algorithm, - blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify); - - EXPECT_EQ(test_hash.id(), key.algorithm().hmacParams()->hash().id()); - EXPECT_EQ(test_key.size() * 8, key.algorithm().hmacParams()->lengthBits()); - - // Verify exported raw key is identical to the imported data - std::vector<uint8> raw_key; - EXPECT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); - EXPECT_BYTES_EQ(test_key, raw_key); - - std::vector<uint8> output; - - ASSERT_EQ(Status::Success(), - Sign(algorithm, key, CryptoData(test_message), &output)); - - EXPECT_BYTES_EQ(test_mac, output); - - bool signature_match = false; - EXPECT_EQ(Status::Success(), - VerifySignature(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_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)); - 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_FALSE(signature_match); - } -} - -TEST_F(SharedCryptoTest, AesCbcFailures) { - const std::string key_hex = "2b7e151628aed2a6abf7158809cf4f3c"; - blink::WebCryptoKey key = ImportSecretKeyFromRaw( - HexStringToBytes(key_hex), - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), - blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt); - - // Verify exported raw key is identical to the imported data - std::vector<uint8> raw_key; - EXPECT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); - EXPECT_BYTES_EQ_HEX(key_hex, raw_key); - - std::vector<uint8> output; - - // Use an invalid |iv| (fewer than 16 bytes) - { - std::vector<uint8> input(32); - std::vector<uint8> iv; - EXPECT_EQ(Status::ErrorIncorrectSizeAesCbcIv(), - Encrypt(webcrypto::CreateAesCbcAlgorithm(iv), - key, - CryptoData(input), - &output)); - EXPECT_EQ(Status::ErrorIncorrectSizeAesCbcIv(), - Decrypt(webcrypto::CreateAesCbcAlgorithm(iv), - key, - CryptoData(input), - &output)); - } - - // Use an invalid |iv| (more than 16 bytes) - { - std::vector<uint8> input(32); - std::vector<uint8> iv(17); - EXPECT_EQ(Status::ErrorIncorrectSizeAesCbcIv(), - Encrypt(webcrypto::CreateAesCbcAlgorithm(iv), - key, - CryptoData(input), - &output)); - EXPECT_EQ(Status::ErrorIncorrectSizeAesCbcIv(), - Decrypt(webcrypto::CreateAesCbcAlgorithm(iv), - key, - CryptoData(input), - &output)); - } - - // Give an input that is too large (would cause integer overflow when - // narrowing to an int). - { - std::vector<uint8> iv(16); - - // Pretend the input is large. Don't pass data pointer as NULL in case that - // is special cased; the implementation shouldn't actually dereference the - // data. - CryptoData input(&iv[0], INT_MAX - 3); - - EXPECT_EQ(Status::ErrorDataTooLarge(), - Encrypt(CreateAesCbcAlgorithm(iv), key, input, &output)); - EXPECT_EQ(Status::ErrorDataTooLarge(), - Decrypt(CreateAesCbcAlgorithm(iv), key, input, &output)); - } - - // Fail importing the key (too few bytes specified) - { - std::vector<uint8> key_raw(1); - std::vector<uint8> iv(16); - - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - EXPECT_EQ(Status::ErrorImportAesKeyLength(), - ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(key_raw), - CreateAesCbcAlgorithm(iv), - true, - blink::WebCryptoKeyUsageEncrypt, - &key)); - } - - // Fail exporting the key in SPKI and PKCS#8 formats (not allowed for secret - // keys). - EXPECT_EQ(Status::ErrorUnexpectedKeyType(), - ExportKey(blink::WebCryptoKeyFormatSpki, key, &output)); - EXPECT_EQ(Status::ErrorUnexpectedKeyType(), - ExportKey(blink::WebCryptoKeyFormatPkcs8, key, &output)); -} - -TEST_F(SharedCryptoTest, MAYBE(AesCbcSampleSets)) { - scoped_ptr<base::ListValue> tests; - ASSERT_TRUE(ReadJsonTestFileToList("aes_cbc.json", &tests)); - - for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { - SCOPED_TRACE(test_index); - base::DictionaryValue* test; - ASSERT_TRUE(tests->GetDictionary(test_index, &test)); - - std::vector<uint8> test_key = GetBytesFromHexString(test, "key"); - std::vector<uint8> test_iv = GetBytesFromHexString(test, "iv"); - std::vector<uint8> test_plain_text = - GetBytesFromHexString(test, "plain_text"); - std::vector<uint8> test_cipher_text = - GetBytesFromHexString(test, "cipher_text"); - - blink::WebCryptoKey key = ImportSecretKeyFromRaw( - test_key, - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), - blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt); - - EXPECT_EQ(test_key.size() * 8, key.algorithm().aesParams()->lengthBits()); - - // Verify exported raw key is identical to the imported data - std::vector<uint8> raw_key; - EXPECT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); - EXPECT_BYTES_EQ(test_key, raw_key); - - std::vector<uint8> output; - - // Test encryption. - EXPECT_EQ(Status::Success(), - Encrypt(webcrypto::CreateAesCbcAlgorithm(test_iv), - key, - CryptoData(test_plain_text), - &output)); - EXPECT_BYTES_EQ(test_cipher_text, output); - - // Test decryption. - EXPECT_EQ(Status::Success(), - Decrypt(webcrypto::CreateAesCbcAlgorithm(test_iv), - key, - CryptoData(test_cipher_text), - &output)); - EXPECT_BYTES_EQ(test_plain_text, output); - - const unsigned int kAesCbcBlockSize = 16; - - // Decrypt with a padding error by stripping the last block. This also ends - // up testing decryption over empty cipher text. - if (test_cipher_text.size() >= kAesCbcBlockSize) { - EXPECT_EQ(Status::OperationError(), - Decrypt(CreateAesCbcAlgorithm(test_iv), - key, - CryptoData(&test_cipher_text[0], - test_cipher_text.size() - kAesCbcBlockSize), - &output)); - } - - // Decrypt cipher text which is not a multiple of block size by stripping - // a few bytes off the cipher text. - if (test_cipher_text.size() > 3) { - EXPECT_EQ( - Status::OperationError(), - Decrypt(CreateAesCbcAlgorithm(test_iv), - key, - CryptoData(&test_cipher_text[0], test_cipher_text.size() - 3), - &output)); - } - } -} - -TEST_F(SharedCryptoTest, MAYBE(GenerateKeyAes)) { - // Check key generation for each of AES-CBC, AES-GCM, and AES-KW, and for each - // allowed key length. - std::vector<blink::WebCryptoAlgorithm> algorithm; - const unsigned short kKeyLength[] = {128, 256}; - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kKeyLength); ++i) { - algorithm.push_back(CreateAesCbcKeyGenAlgorithm(kKeyLength[i])); - algorithm.push_back(CreateAesKwKeyGenAlgorithm(kKeyLength[i])); - if (SupportsAesGcm()) - algorithm.push_back(CreateAesGcmKeyGenAlgorithm(kKeyLength[i])); - } - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - std::vector<std::vector<uint8> > keys; - std::vector<uint8> key_bytes; - for (size_t i = 0; i < algorithm.size(); ++i) { - SCOPED_TRACE(i); - // Generate a small sample of keys. - keys.clear(); - for (int j = 0; j < 16; ++j) { - ASSERT_EQ(Status::Success(), - GenerateSecretKey(algorithm[i], true, 0, &key)); - EXPECT_TRUE(key.handle()); - EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatRaw, key, &key_bytes)); - EXPECT_EQ(key_bytes.size() * 8, - key.algorithm().aesParams()->lengthBits()); - keys.push_back(key_bytes); - } - // Ensure all entries in the key sample set are unique. This is a simplistic - // estimate of whether the generated keys appear random. - EXPECT_FALSE(CopiesExist(keys)); - } -} - -TEST_F(SharedCryptoTest, MAYBE(GenerateKeyAesBadLength)) { - const unsigned short kKeyLen[] = {0, 127, 257}; - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kKeyLen); ++i) { - SCOPED_TRACE(i); - EXPECT_EQ(Status::ErrorGenerateKeyLength(), - GenerateSecretKey( - CreateAesCbcKeyGenAlgorithm(kKeyLen[i]), true, 0, &key)); - EXPECT_EQ(Status::ErrorGenerateKeyLength(), - GenerateSecretKey( - CreateAesKwKeyGenAlgorithm(kKeyLen[i]), true, 0, &key)); - if (SupportsAesGcm()) { - EXPECT_EQ(Status::ErrorGenerateKeyLength(), - GenerateSecretKey( - CreateAesGcmKeyGenAlgorithm(kKeyLen[i]), true, 0, &key)); - } - } -} - -TEST_F(SharedCryptoTest, MAYBE(GenerateKeyHmac)) { - // Generate a small sample of HMAC keys. - std::vector<std::vector<uint8> > keys; - for (int i = 0; i < 16; ++i) { - std::vector<uint8> key_bytes; - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - blink::WebCryptoAlgorithm algorithm = - CreateHmacKeyGenAlgorithm(blink::WebCryptoAlgorithmIdSha1, 512); - ASSERT_EQ(Status::Success(), GenerateSecretKey(algorithm, true, 0, &key)); - EXPECT_FALSE(key.isNull()); - EXPECT_TRUE(key.handle()); - EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, - key.algorithm().hmacParams()->hash().id()); - EXPECT_EQ(512u, key.algorithm().hmacParams()->lengthBits()); - - std::vector<uint8> raw_key; - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); - EXPECT_EQ(64U, raw_key.size()); - keys.push_back(raw_key); - } - // Ensure all entries in the key sample set are unique. This is a simplistic - // estimate of whether the generated keys appear random. - EXPECT_FALSE(CopiesExist(keys)); -} - -// If the key length is not provided, then the block size is used. -TEST_F(SharedCryptoTest, MAYBE(GenerateKeyHmacNoLength)) { - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - blink::WebCryptoAlgorithm algorithm = - CreateHmacKeyGenAlgorithm(blink::WebCryptoAlgorithmIdSha1, 0); - ASSERT_EQ(Status::Success(), GenerateSecretKey(algorithm, true, 0, &key)); - EXPECT_TRUE(key.handle()); - EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, - key.algorithm().hmacParams()->hash().id()); - EXPECT_EQ(512u, key.algorithm().hmacParams()->lengthBits()); - std::vector<uint8> raw_key; - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); - EXPECT_EQ(64U, raw_key.size()); - - // The block size for HMAC SHA-512 is larger. - algorithm = CreateHmacKeyGenAlgorithm(blink::WebCryptoAlgorithmIdSha512, 0); - ASSERT_EQ(Status::Success(), GenerateSecretKey(algorithm, true, 0, &key)); - EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdSha512, - key.algorithm().hmacParams()->hash().id()); - EXPECT_EQ(1024u, key.algorithm().hmacParams()->lengthBits()); - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); - EXPECT_EQ(128U, raw_key.size()); -} - -TEST_F(SharedCryptoTest, ImportJwkKeyUsage) { - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - base::DictionaryValue dict; - dict.SetString("kty", "oct"); - dict.SetBoolean("ext", false); - dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); - const blink::WebCryptoAlgorithm aes_cbc_algorithm = - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); - const blink::WebCryptoAlgorithm hmac_algorithm = - webcrypto::CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256); - const blink::WebCryptoAlgorithm aes_kw_algorithm = - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); - - // Test null usage. - base::ListValue* key_ops = new base::ListValue; - // Note: the following call makes dict assume ownership of key_ops. - dict.Set("key_ops", key_ops); - EXPECT_EQ(Status::Success(), - ImportKeyJwkFromDict(dict, aes_cbc_algorithm, false, 0, &key)); - EXPECT_EQ(0, key.usages()); - - // Test each key_ops value translates to the correct Web Crypto value. - struct TestCase { - const char* jwk_key_op; - const char* jwk_alg; - const blink::WebCryptoAlgorithm algorithm; - const blink::WebCryptoKeyUsage usage; - }; - // TODO(padolph): Add 'deriveBits' key_ops value once it is supported. - const TestCase test_case[] = { - {"encrypt", "A128CBC", aes_cbc_algorithm, - blink::WebCryptoKeyUsageEncrypt}, - {"decrypt", "A128CBC", aes_cbc_algorithm, - blink::WebCryptoKeyUsageDecrypt}, - {"sign", "HS256", hmac_algorithm, blink::WebCryptoKeyUsageSign}, - {"verify", "HS256", hmac_algorithm, blink::WebCryptoKeyUsageVerify}, - {"wrapKey", "A128KW", aes_kw_algorithm, blink::WebCryptoKeyUsageWrapKey}, - {"unwrapKey", "A128KW", aes_kw_algorithm, - blink::WebCryptoKeyUsageUnwrapKey}}; - for (size_t test_index = 0; test_index < ARRAYSIZE_UNSAFE(test_case); - ++test_index) { - SCOPED_TRACE(test_index); - dict.SetString("alg", test_case[test_index].jwk_alg); - key_ops->Clear(); - key_ops->AppendString(test_case[test_index].jwk_key_op); - EXPECT_EQ(Status::Success(), - ImportKeyJwkFromDict(dict, - test_case[test_index].algorithm, - false, - test_case[test_index].usage, - &key)); - EXPECT_EQ(test_case[test_index].usage, key.usages()); - } - - // Test discrete multiple usages. - dict.SetString("alg", "A128CBC"); - key_ops->Clear(); - key_ops->AppendString("encrypt"); - key_ops->AppendString("decrypt"); - EXPECT_EQ(Status::Success(), - ImportKeyJwkFromDict(dict, - aes_cbc_algorithm, - false, - blink::WebCryptoKeyUsageDecrypt | - blink::WebCryptoKeyUsageEncrypt, - &key)); - EXPECT_EQ(blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt, - key.usages()); - - // Test constrained key usage (input usage is a subset of JWK usage). - key_ops->Clear(); - key_ops->AppendString("encrypt"); - key_ops->AppendString("decrypt"); - EXPECT_EQ(Status::Success(), - ImportKeyJwkFromDict(dict, - aes_cbc_algorithm, - false, - blink::WebCryptoKeyUsageDecrypt, - &key)); - EXPECT_EQ(blink::WebCryptoKeyUsageDecrypt, key.usages()); - - // Test failure if input usage is NOT a strict subset of the JWK usage. - key_ops->Clear(); - key_ops->AppendString("encrypt"); - EXPECT_EQ(Status::ErrorJwkKeyopsInconsistent(), - ImportKeyJwkFromDict(dict, - aes_cbc_algorithm, - false, - blink::WebCryptoKeyUsageEncrypt | - blink::WebCryptoKeyUsageDecrypt, - &key)); - - // Test 'use' inconsistent with 'key_ops'. - dict.SetString("alg", "HS256"); - dict.SetString("use", "sig"); - key_ops->AppendString("sign"); - key_ops->AppendString("verify"); - key_ops->AppendString("encrypt"); - EXPECT_EQ(Status::ErrorJwkUseAndKeyopsInconsistent(), - ImportKeyJwkFromDict( - dict, - hmac_algorithm, - false, - blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, - &key)); - - // Test JWK composite 'sig' use - dict.Remove("key_ops", NULL); - dict.SetString("use", "sig"); - EXPECT_EQ(Status::Success(), - ImportKeyJwkFromDict( - dict, - hmac_algorithm, - false, - blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, - &key)); - EXPECT_EQ(blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, - key.usages()); - - // Test JWK composite use 'enc' usage - dict.SetString("alg", "A128CBC"); - dict.SetString("use", "enc"); - EXPECT_EQ(Status::Success(), - ImportKeyJwkFromDict(dict, - aes_cbc_algorithm, - false, - blink::WebCryptoKeyUsageDecrypt | - blink::WebCryptoKeyUsageEncrypt | - blink::WebCryptoKeyUsageWrapKey | - blink::WebCryptoKeyUsageUnwrapKey, - &key)); - EXPECT_EQ(blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt | - blink::WebCryptoKeyUsageWrapKey | - blink::WebCryptoKeyUsageUnwrapKey, - key.usages()); -} - -TEST_F(SharedCryptoTest, ImportJwkFailures) { - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - blink::WebCryptoAlgorithm algorithm = - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); - blink::WebCryptoKeyUsageMask usage_mask = blink::WebCryptoKeyUsageEncrypt; - - // Baseline pass: each test below breaks a single item, so we start with a - // passing case to make sure each failure is caused by the isolated break. - // Each breaking subtest below resets the dictionary to this passing case when - // complete. - base::DictionaryValue dict; - RestoreJwkOctDictionary(&dict); - EXPECT_EQ(Status::Success(), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - - // Fail on empty JSON. - EXPECT_EQ( - Status::ErrorImportEmptyKeyData(), - ImportKeyJwk( - CryptoData(MakeJsonVector("")), algorithm, false, usage_mask, &key)); - - // Fail on invalid JSON. - const std::vector<uint8> bad_json_vec = MakeJsonVector( - "{" - "\"kty\" : \"oct\"," - "\"alg\" : \"HS256\"," - "\"use\" : "); - EXPECT_EQ(Status::ErrorJwkNotDictionary(), - ImportKeyJwk( - CryptoData(bad_json_vec), algorithm, false, usage_mask, &key)); - - // Fail on JWK alg present but unrecognized. - dict.SetString("alg", "A127CBC"); - EXPECT_EQ(Status::ErrorJwkUnrecognizedAlgorithm(), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkOctDictionary(&dict); - - // Fail on invalid kty. - dict.SetString("kty", "foo"); - EXPECT_EQ(Status::ErrorJwkUnrecognizedKty(), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkOctDictionary(&dict); - - // Fail on missing kty. - dict.Remove("kty", NULL); - EXPECT_EQ(Status::ErrorJwkPropertyMissing("kty"), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkOctDictionary(&dict); - - // Fail on kty wrong type. - dict.SetDouble("kty", 0.1); - EXPECT_EQ(Status::ErrorJwkPropertyWrongType("kty", "string"), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkOctDictionary(&dict); - - // Fail on invalid use. - dict.SetString("use", "foo"); - EXPECT_EQ(Status::ErrorJwkUnrecognizedUse(), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkOctDictionary(&dict); - - // Fail on invalid use (wrong type). - dict.SetBoolean("use", true); - EXPECT_EQ(Status::ErrorJwkPropertyWrongType("use", "string"), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkOctDictionary(&dict); - - // Fail on invalid extractable (wrong type). - dict.SetInteger("ext", 0); - EXPECT_EQ(Status::ErrorJwkPropertyWrongType("ext", "boolean"), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkOctDictionary(&dict); - - // Fail on invalid key_ops (wrong type). - dict.SetBoolean("key_ops", true); - EXPECT_EQ(Status::ErrorJwkPropertyWrongType("key_ops", "list"), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkOctDictionary(&dict); - - // Fail on inconsistent key_ops - asking for "encrypt" however JWK contains - // only "foo". - base::ListValue* key_ops = new base::ListValue; - // Note: the following call makes dict assume ownership of key_ops. - dict.Set("key_ops", key_ops); - key_ops->AppendString("foo"); - EXPECT_EQ(Status::ErrorJwkKeyopsInconsistent(), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkOctDictionary(&dict); -} - -// Import a JWK with unrecognized values for "key_ops". -TEST_F(SharedCryptoTest, ImportJwkUnrecognizedKeyOps) { - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - blink::WebCryptoAlgorithm algorithm = - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); - blink::WebCryptoKeyUsageMask usage_mask = blink::WebCryptoKeyUsageEncrypt; - - base::DictionaryValue dict; - RestoreJwkOctDictionary(&dict); - - base::ListValue* key_ops = new base::ListValue; - dict.Set("key_ops", key_ops); - key_ops->AppendString("foo"); - key_ops->AppendString("bar"); - key_ops->AppendString("baz"); - key_ops->AppendString("encrypt"); - EXPECT_EQ(Status::Success(), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); -} - -// Import a JWK with a value in key_ops array that is not a string. -TEST_F(SharedCryptoTest, ImportJwkNonStringKeyOp) { - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - blink::WebCryptoAlgorithm algorithm = - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); - blink::WebCryptoKeyUsageMask usage_mask = blink::WebCryptoKeyUsageEncrypt; - - base::DictionaryValue dict; - RestoreJwkOctDictionary(&dict); - - base::ListValue* key_ops = new base::ListValue; - dict.Set("key_ops", key_ops); - key_ops->AppendString("encrypt"); - key_ops->AppendInteger(3); - EXPECT_EQ(Status::ErrorJwkPropertyWrongType("key_ops[1]", "string"), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); -} - -TEST_F(SharedCryptoTest, ImportJwkOctFailures) { - base::DictionaryValue dict; - RestoreJwkOctDictionary(&dict); - blink::WebCryptoAlgorithm algorithm = - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); - blink::WebCryptoKeyUsageMask usage_mask = blink::WebCryptoKeyUsageEncrypt; - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - - // Baseline pass. - EXPECT_EQ(Status::Success(), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - EXPECT_EQ(algorithm.id(), key.algorithm().id()); - EXPECT_FALSE(key.extractable()); - EXPECT_EQ(blink::WebCryptoKeyUsageEncrypt, key.usages()); - EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); - - // The following are specific failure cases for when kty = "oct". - - // Fail on missing k. - dict.Remove("k", NULL); - EXPECT_EQ(Status::ErrorJwkPropertyMissing("k"), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkOctDictionary(&dict); - - // Fail on bad b64 encoding for k. - dict.SetString("k", "Qk3f0DsytU8lfza2au #$% Htaw2xpop9GYyTuH0p5GghxTI="); - EXPECT_EQ(Status::ErrorJwkBase64Decode("k"), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkOctDictionary(&dict); - - // Fail on empty k. - dict.SetString("k", ""); - EXPECT_EQ(Status::ErrorJwkIncorrectKeyLength(), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkOctDictionary(&dict); - - // Fail on k actual length (120 bits) inconsistent with the embedded JWK alg - // value (128) for an AES key. - dict.SetString("k", "AVj42h0Y5aqGtE3yluKL"); - EXPECT_EQ(Status::ErrorJwkIncorrectKeyLength(), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkOctDictionary(&dict); - - // Fail on k actual length (192 bits) inconsistent with the embedded JWK alg - // value (128) for an AES key. - dict.SetString("k", "dGhpcyAgaXMgIDI0ICBieXRlcyBsb25n"); - EXPECT_EQ(Status::ErrorJwkIncorrectKeyLength(), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkOctDictionary(&dict); -} - -TEST_F(SharedCryptoTest, MAYBE(ImportExportJwkRsaPublicKey)) { - if (!SupportsRsaKeyImport()) - return; - - const bool supports_rsa_oaep = SupportsRsaOaep(); - if (!supports_rsa_oaep) { - LOG(WARNING) << "RSA-OAEP not supported on this platform. Skipping some" - << "tests."; - } - - struct TestCase { - const blink::WebCryptoAlgorithm algorithm; - const blink::WebCryptoKeyUsageMask usage; - const char* const jwk_alg; - }; - const TestCase kTests[] = { - // RSASSA-PKCS1-v1_5 SHA-1 - {CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha1), - blink::WebCryptoKeyUsageVerify, "RS1"}, - // RSASSA-PKCS1-v1_5 SHA-256 - {CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256), - blink::WebCryptoKeyUsageVerify, "RS256"}, - // RSASSA-PKCS1-v1_5 SHA-384 - {CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha384), - blink::WebCryptoKeyUsageVerify, "RS384"}, - // RSASSA-PKCS1-v1_5 SHA-512 - {CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha512), - blink::WebCryptoKeyUsageVerify, "RS512"}, - // RSA-OAEP with SHA-1 and MGF-1 / SHA-1 - {CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep, - blink::WebCryptoAlgorithmIdSha1), - blink::WebCryptoKeyUsageEncrypt, "RSA-OAEP"}, - // RSA-OAEP with SHA-256 and MGF-1 / SHA-256 - {CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep, - blink::WebCryptoAlgorithmIdSha256), - blink::WebCryptoKeyUsageEncrypt, "RSA-OAEP-256"}, - // RSA-OAEP with SHA-384 and MGF-1 / SHA-384 - {CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep, - blink::WebCryptoAlgorithmIdSha384), - blink::WebCryptoKeyUsageEncrypt, "RSA-OAEP-384"}, - // RSA-OAEP with SHA-512 and MGF-1 / SHA-512 - {CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep, - blink::WebCryptoAlgorithmIdSha512), - blink::WebCryptoKeyUsageEncrypt, "RSA-OAEP-512"}}; - - for (size_t test_index = 0; test_index < ARRAYSIZE_UNSAFE(kTests); - ++test_index) { - SCOPED_TRACE(test_index); - const TestCase& test = kTests[test_index]; - if (!supports_rsa_oaep && - test.algorithm.id() == blink::WebCryptoAlgorithmIdRsaOaep) { - continue; - } - - // Import the spki to create a public key - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatSpki, - CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), - test.algorithm, - true, - test.usage, - &public_key)); - - // Export the public key as JWK and verify its contents - std::vector<uint8> jwk; - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &jwk)); - EXPECT_TRUE(VerifyPublicJwk(jwk, - test.jwk_alg, - kPublicKeyModulusHex, - kPublicKeyExponentHex, - test.usage)); - - // Import the JWK back in to create a new key - blink::WebCryptoKey public_key2 = blink::WebCryptoKey::createNull(); - ASSERT_EQ( - Status::Success(), - ImportKeyJwk( - CryptoData(jwk), test.algorithm, true, test.usage, &public_key2)); - ASSERT_TRUE(public_key2.handle()); - EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key2.type()); - EXPECT_TRUE(public_key2.extractable()); - EXPECT_EQ(test.algorithm.id(), public_key2.algorithm().id()); - - // Only perform SPKI consistency test for RSA-SSA as its - // export format is the same as kPublicKeySpkiDerHex - if (test.algorithm.id() == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5) { - // Export the new key as spki and compare to the original. - std::vector<uint8> spki; - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatSpki, public_key2, &spki)); - EXPECT_BYTES_EQ_HEX(kPublicKeySpkiDerHex, CryptoData(spki)); - } - } -} - -TEST_F(SharedCryptoTest, MAYBE(ImportJwkRsaFailures)) { - base::DictionaryValue dict; - RestoreJwkRsaDictionary(&dict); - blink::WebCryptoAlgorithm algorithm = - CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256); - blink::WebCryptoKeyUsageMask usage_mask = blink::WebCryptoKeyUsageVerify; - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - - // An RSA public key JWK _must_ have an "n" (modulus) and an "e" (exponent) - // entry, 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. - - // Baseline pass. - EXPECT_EQ(Status::Success(), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - EXPECT_EQ(algorithm.id(), key.algorithm().id()); - EXPECT_FALSE(key.extractable()); - EXPECT_EQ(blink::WebCryptoKeyUsageVerify, key.usages()); - EXPECT_EQ(blink::WebCryptoKeyTypePublic, key.type()); - - // The following are specific failure cases for when kty = "RSA". - - // Fail if either "n" or "e" is not present or malformed. - const std::string kKtyParmName[] = {"n", "e"}; - for (size_t idx = 0; idx < ARRAYSIZE_UNSAFE(kKtyParmName); ++idx) { - // Fail on missing parameter. - dict.Remove(kKtyParmName[idx], NULL); - EXPECT_NE(Status::Success(), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkRsaDictionary(&dict); - - // Fail on bad b64 parameter encoding. - dict.SetString(kKtyParmName[idx], "Qk3f0DsytU8lfza2au #$% Htaw2xpop9yTuH0"); - EXPECT_NE(Status::Success(), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkRsaDictionary(&dict); - - // Fail on empty parameter. - dict.SetString(kKtyParmName[idx], ""); - EXPECT_NE(Status::Success(), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - RestoreJwkRsaDictionary(&dict); - } -} - -TEST_F(SharedCryptoTest, MAYBE(ImportJwkInputConsistency)) { - // The Web Crypto spec says that if a JWK value is present, but is - // inconsistent with the input value, the operation must fail. - - // Consistency rules when JWK value is not present: Inputs should be used. - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - bool extractable = false; - blink::WebCryptoAlgorithm algorithm = - CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256); - blink::WebCryptoKeyUsageMask usage_mask = blink::WebCryptoKeyUsageVerify; - base::DictionaryValue dict; - dict.SetString("kty", "oct"); - dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg"); - std::vector<uint8> json_vec = MakeJsonVector(dict); - EXPECT_EQ( - Status::Success(), - ImportKeyJwk( - CryptoData(json_vec), algorithm, extractable, usage_mask, &key)); - EXPECT_TRUE(key.handle()); - EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); - EXPECT_EQ(extractable, key.extractable()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, - key.algorithm().hmacParams()->hash().id()); - EXPECT_EQ(320u, key.algorithm().hmacParams()->lengthBits()); - EXPECT_EQ(blink::WebCryptoKeyUsageVerify, key.usages()); - key = blink::WebCryptoKey::createNull(); - - // Consistency rules when JWK value exists: Fail if inconsistency is found. - - // Pass: All input values are consistent with the JWK values. - dict.Clear(); - dict.SetString("kty", "oct"); - dict.SetString("alg", "HS256"); - dict.SetString("use", "sig"); - dict.SetBoolean("ext", false); - dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg"); - json_vec = MakeJsonVector(dict); - EXPECT_EQ( - Status::Success(), - ImportKeyJwk( - CryptoData(json_vec), algorithm, extractable, usage_mask, &key)); - - // Extractable cases: - // 1. input=T, JWK=F ==> fail (inconsistent) - // 4. input=F, JWK=F ==> pass, result extractable is F - // 2. input=T, JWK=T ==> pass, result extractable is T - // 3. input=F, JWK=T ==> pass, result extractable is F - EXPECT_EQ( - Status::ErrorJwkExtInconsistent(), - ImportKeyJwk(CryptoData(json_vec), algorithm, true, usage_mask, &key)); - EXPECT_EQ( - Status::Success(), - ImportKeyJwk(CryptoData(json_vec), algorithm, false, usage_mask, &key)); - EXPECT_FALSE(key.extractable()); - dict.SetBoolean("ext", true); - EXPECT_EQ(Status::Success(), - ImportKeyJwkFromDict(dict, algorithm, true, usage_mask, &key)); - EXPECT_TRUE(key.extractable()); - EXPECT_EQ(Status::Success(), - ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); - EXPECT_FALSE(key.extractable()); - dict.SetBoolean("ext", true); // restore previous value - - // Fail: Input algorithm (AES-CBC) is inconsistent with JWK value - // (HMAC SHA256). - EXPECT_EQ(Status::ErrorJwkAlgorithmInconsistent(), - ImportKeyJwk(CryptoData(json_vec), - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), - extractable, - blink::WebCryptoKeyUsageEncrypt, - &key)); - - // Fail: Input algorithm (HMAC SHA1) is inconsistent with JWK value - // (HMAC SHA256). - EXPECT_EQ( - Status::ErrorJwkAlgorithmInconsistent(), - ImportKeyJwk(CryptoData(json_vec), - CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1), - extractable, - usage_mask, - &key)); - - // Pass: JWK alg missing but input algorithm specified: use input value - dict.Remove("alg", NULL); - EXPECT_EQ(Status::Success(), - ImportKeyJwkFromDict( - dict, - CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256), - extractable, - usage_mask, - &key)); - EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, algorithm.id()); - dict.SetString("alg", "HS256"); - - // Fail: Input usage_mask (encrypt) is not a subset of the JWK value - // (sign|verify). Moreover "encrypt" is not a valid usage for HMAC. - EXPECT_EQ(Status::ErrorCreateKeyBadUsages(), - ImportKeyJwk(CryptoData(json_vec), - algorithm, - extractable, - blink::WebCryptoKeyUsageEncrypt, - &key)); - - // Fail: Input usage_mask (encrypt|sign|verify) is not a subset of the JWK - // value (sign|verify). Moreover "encrypt" is not a valid usage for HMAC. - usage_mask = blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageSign | - blink::WebCryptoKeyUsageVerify; - EXPECT_EQ( - Status::ErrorCreateKeyBadUsages(), - ImportKeyJwk( - CryptoData(json_vec), algorithm, extractable, usage_mask, &key)); - - // TODO(padolph): kty vs alg consistency tests: Depending on the kty value, - // only certain alg values are permitted. For example, when kty = "RSA" alg - // must be of the RSA family, or when kty = "oct" alg must be symmetric - // algorithm. - - // TODO(padolph): key_ops consistency tests -} - -TEST_F(SharedCryptoTest, MAYBE(ImportJwkHappy)) { - // This test verifies the happy path of JWK import, including the application - // of the imported key material. - - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - bool extractable = false; - blink::WebCryptoAlgorithm algorithm = - CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256); - blink::WebCryptoKeyUsageMask usage_mask = blink::WebCryptoKeyUsageSign; - - // Import a symmetric key JWK and HMAC-SHA256 sign() - // Uses the first SHA256 test vector from the HMAC sample set above. - - base::DictionaryValue dict; - dict.SetString("kty", "oct"); - dict.SetString("alg", "HS256"); - dict.SetString("use", "sig"); - dict.SetBoolean("ext", false); - dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg"); - - ASSERT_EQ( - Status::Success(), - ImportKeyJwkFromDict(dict, algorithm, extractable, usage_mask, &key)); - - EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, - key.algorithm().hmacParams()->hash().id()); - - const std::vector<uint8> message_raw = HexStringToBytes( - "b1689c2591eaf3c9e66070f8a77954ffb81749f1b00346f9dfe0b2ee905dcc288baf4a" - "92de3f4001dd9f44c468c3d07d6c6ee82faceafc97c2fc0fc0601719d2dcd0aa2aec92" - "d1b0ae933c65eb06a03c9c935c2bad0459810241347ab87e9f11adb30415424c6c7f5f" - "22a003b8ab8de54f6ded0e3ab9245fa79568451dfa258e"); - - std::vector<uint8> output; - - ASSERT_EQ(Status::Success(), - Sign(CreateAlgorithm(blink::WebCryptoAlgorithmIdHmac), - key, - CryptoData(message_raw), - &output)); - - const std::string mac_raw = - "769f00d3e6a6cc1fb426a14a4f76c6462e6149726e0dee0ec0cf97a16605ac8b"; - - EXPECT_BYTES_EQ_HEX(mac_raw, output); - - // TODO(padolph): Import an RSA public key JWK and use it -} - -TEST_F(SharedCryptoTest, MAYBE(ImportExportJwkSymmetricKey)) { - // Raw keys are generated by openssl: - // % openssl rand -hex <key length bytes> - const char* const key_hex_128 = "3f1e7cd4f6f8543f6b1e16002e688623"; - const char* const key_hex_256 = - "bd08286b81a74783fd1ccf46b7e05af84ee25ae021210074159e0c4d9d907692"; - const char* const key_hex_384 = - "a22c5441c8b185602283d64c7221de1d0951e706bfc09539435ec0e0ed614e1d406623f2" - "b31d31819fec30993380dd82"; - const char* const key_hex_512 = - "5834f639000d4cf82de124fbfd26fb88d463e99f839a76ba41ac88967c80a3f61e1239a4" - "52e573dba0750e988152988576efd75b8d0229b7aca2ada2afd392ee"; - const blink::WebCryptoAlgorithm aes_cbc_alg = - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); - const blink::WebCryptoAlgorithm aes_gcm_alg = - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm); - const blink::WebCryptoAlgorithm aes_kw_alg = - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); - const blink::WebCryptoAlgorithm hmac_sha_1_alg = - webcrypto::CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1); - const blink::WebCryptoAlgorithm hmac_sha_256_alg = - webcrypto::CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256); - const blink::WebCryptoAlgorithm hmac_sha_384_alg = - webcrypto::CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha384); - const blink::WebCryptoAlgorithm hmac_sha_512_alg = - webcrypto::CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha512); - - struct TestCase { - const char* const key_hex; - const blink::WebCryptoAlgorithm algorithm; - const blink::WebCryptoKeyUsageMask usage; - const char* const jwk_alg; - }; - - // TODO(padolph): Test AES-CTR JWK export, once AES-CTR import works. - const TestCase kTests[] = { - // AES-CBC 128 - {key_hex_128, aes_cbc_alg, - blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, - "A128CBC"}, - // AES-CBC 256 - {key_hex_256, aes_cbc_alg, blink::WebCryptoKeyUsageDecrypt, "A256CBC"}, - // AES-GCM 128 - {key_hex_128, aes_gcm_alg, - blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, - "A128GCM"}, - // AES-GCM 256 - {key_hex_256, aes_gcm_alg, blink::WebCryptoKeyUsageDecrypt, "A256GCM"}, - // AES-KW 128 - {key_hex_128, aes_kw_alg, - blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, - "A128KW"}, - // AES-KW 256 - {key_hex_256, aes_kw_alg, - blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, - "A256KW"}, - // HMAC SHA-1 - {key_hex_256, hmac_sha_1_alg, - blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, "HS1"}, - // HMAC SHA-384 - {key_hex_384, hmac_sha_384_alg, blink::WebCryptoKeyUsageSign, "HS384"}, - // HMAC SHA-512 - {key_hex_512, hmac_sha_512_alg, blink::WebCryptoKeyUsageVerify, "HS512"}, - // Large usage value - {key_hex_256, aes_cbc_alg, - blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | - blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, - "A256CBC"}, - // Zero usage value - {key_hex_512, hmac_sha_512_alg, 0, "HS512"}, - }; - - // Round-trip import/export each key. - - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - std::vector<uint8> json; - for (size_t test_index = 0; test_index < ARRAYSIZE_UNSAFE(kTests); - ++test_index) { - SCOPED_TRACE(test_index); - const TestCase& test = kTests[test_index]; - - // Skip AES-GCM tests where not supported. - if (test.algorithm.id() == blink::WebCryptoAlgorithmIdAesGcm && - !SupportsAesGcm()) { - continue; - } - - // Import a raw key. - key = ImportSecretKeyFromRaw( - HexStringToBytes(test.key_hex), test.algorithm, test.usage); - - // Export the key in JWK format and validate. - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatJwk, key, &json)); - EXPECT_TRUE(VerifySecretJwk(json, test.jwk_alg, test.key_hex, test.usage)); - - // Import the JWK-formatted key. - ASSERT_EQ( - Status::Success(), - ImportKeyJwk(CryptoData(json), test.algorithm, true, test.usage, &key)); - EXPECT_TRUE(key.handle()); - EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); - EXPECT_EQ(test.algorithm.id(), key.algorithm().id()); - EXPECT_EQ(true, key.extractable()); - EXPECT_EQ(test.usage, key.usages()); - - // Export the key in raw format and compare to the original. - std::vector<uint8> key_raw_out; - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatRaw, key, &key_raw_out)); - EXPECT_BYTES_EQ_HEX(test.key_hex, key_raw_out); - } -} - -TEST_F(SharedCryptoTest, MAYBE(ExportJwkEmptySymmetricKey)) { - const blink::WebCryptoAlgorithm import_algorithm = - webcrypto::CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1); - - blink::WebCryptoKeyUsageMask usages = blink::WebCryptoKeyUsageSign; - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - - // Import a zero-byte HMAC key. - const char key_data_hex[] = ""; - key = ImportSecretKeyFromRaw( - HexStringToBytes(key_data_hex), import_algorithm, usages); - EXPECT_EQ(0u, key.algorithm().hmacParams()->lengthBits()); - - // Export the key in JWK format and validate. - std::vector<uint8> json; - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatJwk, key, &json)); - EXPECT_TRUE(VerifySecretJwk(json, "HS1", key_data_hex, usages)); - - // Now try re-importing the JWK key. - key = blink::WebCryptoKey::createNull(); - EXPECT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatJwk, - CryptoData(json), - import_algorithm, - true, - usages, - &key)); - - EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); - EXPECT_EQ(0u, key.algorithm().hmacParams()->lengthBits()); - - std::vector<uint8> exported_key_data; - EXPECT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatRaw, key, &exported_key_data)); - - EXPECT_EQ(0u, exported_key_data.size()); -} - -TEST_F(SharedCryptoTest, MAYBE(ImportExportSpki)) { - if (!SupportsRsaKeyImport()) - return; - - // Passing case: Import a valid RSA key in SPKI format. - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatSpki, - CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256), - true, - blink::WebCryptoKeyUsageVerify, - &key)); - EXPECT_TRUE(key.handle()); - EXPECT_EQ(blink::WebCryptoKeyTypePublic, key.type()); - EXPECT_TRUE(key.extractable()); - EXPECT_EQ(blink::WebCryptoKeyUsageVerify, key.usages()); - EXPECT_EQ(kModulusLengthBits, - key.algorithm().rsaHashedParams()->modulusLengthBits()); - EXPECT_BYTES_EQ_HEX( - "010001", - CryptoData(key.algorithm().rsaHashedParams()->publicExponent())); - - // Failing case: Empty SPKI data - EXPECT_EQ( - Status::ErrorImportEmptyKeyData(), - ImportKey(blink::WebCryptoKeyFormatSpki, - CryptoData(std::vector<uint8>()), - CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5), - true, - blink::WebCryptoKeyUsageVerify, - &key)); - - // Failing case: Bad DER encoding. - EXPECT_EQ( - Status::DataError(), - ImportKey(blink::WebCryptoKeyFormatSpki, - CryptoData(HexStringToBytes("618333c4cb")), - CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5), - true, - blink::WebCryptoKeyUsageVerify, - &key)); - - // Failing case: Import RSA key but provide an inconsistent input algorithm. - EXPECT_EQ(Status::DataError(), - ImportKey(blink::WebCryptoKeyFormatSpki, - CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), - true, - blink::WebCryptoKeyUsageEncrypt, - &key)); - - // Passing case: Export a previously imported RSA public key in SPKI format - // and compare to original data. - std::vector<uint8> output; - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatSpki, key, &output)); - EXPECT_BYTES_EQ_HEX(kPublicKeySpkiDerHex, output); - - // Failing case: Try to export a previously imported RSA public key in raw - // format (not allowed for a public key). - EXPECT_EQ(Status::ErrorUnexpectedKeyType(), - ExportKey(blink::WebCryptoKeyFormatRaw, key, &output)); - - // Failing case: Try to export a non-extractable key - ASSERT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatSpki, - CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256), - false, - blink::WebCryptoKeyUsageVerify, - &key)); - EXPECT_TRUE(key.handle()); - EXPECT_FALSE(key.extractable()); - EXPECT_EQ(Status::ErrorKeyNotExtractable(), - ExportKey(blink::WebCryptoKeyFormatSpki, key, &output)); - - // TODO(eroman): Failing test: Import a SPKI with an unrecognized hash OID - // TODO(eroman): Failing test: Import a SPKI with invalid algorithm params - // TODO(eroman): Failing test: Import a SPKI with inconsistent parameters - // (e.g. SHA-1 in OID, SHA-256 in params) - // TODO(eroman): Failing test: Import a SPKI for RSA-SSA, but with params - // as OAEP/PSS -} - -TEST_F(SharedCryptoTest, MAYBE(ImportExportPkcs8)) { - if (!SupportsRsaKeyImport()) - return; - - // Passing case: Import a valid RSA key in PKCS#8 format. - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatPkcs8, - CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha1), - true, - blink::WebCryptoKeyUsageSign, - &key)); - EXPECT_TRUE(key.handle()); - EXPECT_EQ(blink::WebCryptoKeyTypePrivate, key.type()); - EXPECT_TRUE(key.extractable()); - EXPECT_EQ(blink::WebCryptoKeyUsageSign, key.usages()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, - key.algorithm().rsaHashedParams()->hash().id()); - EXPECT_EQ(kModulusLengthBits, - key.algorithm().rsaHashedParams()->modulusLengthBits()); - EXPECT_BYTES_EQ_HEX( - "010001", - CryptoData(key.algorithm().rsaHashedParams()->publicExponent())); - - std::vector<uint8> exported_key; - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatPkcs8, key, &exported_key)); - EXPECT_BYTES_EQ_HEX(kPrivateKeyPkcs8DerHex, exported_key); - - // Failing case: Empty PKCS#8 data - EXPECT_EQ(Status::ErrorImportEmptyKeyData(), - ImportKey(blink::WebCryptoKeyFormatPkcs8, - CryptoData(std::vector<uint8>()), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha1), - true, - blink::WebCryptoKeyUsageSign, - &key)); - - // Failing case: Bad DER encoding. - EXPECT_EQ( - Status::DataError(), - ImportKey(blink::WebCryptoKeyFormatPkcs8, - CryptoData(HexStringToBytes("618333c4cb")), - CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5), - true, - blink::WebCryptoKeyUsageSign, - &key)); - - // Failing case: Import RSA key but provide an inconsistent input algorithm - // and usage. Several issues here: - // * AES-CBC doesn't support PKCS8 key format - // * AES-CBC doesn't support "sign" usage - EXPECT_EQ(Status::ErrorCreateKeyBadUsages(), - ImportKey(blink::WebCryptoKeyFormatPkcs8, - CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), - true, - blink::WebCryptoKeyUsageSign, - &key)); -} - -// Tests JWK import and export by doing a roundtrip key conversion and ensuring -// it was lossless: -// -// PKCS8 --> JWK --> PKCS8 -TEST_F(SharedCryptoTest, MAYBE(ImportRsaPrivateKeyJwkToPkcs8RoundTrip)) { - if (!SupportsRsaKeyImport()) - return; - - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatPkcs8, - CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha1), - true, - blink::WebCryptoKeyUsageSign, - &key)); - - std::vector<uint8> exported_key_jwk; - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatJwk, key, &exported_key_jwk)); - - // All of the optional parameters (p, q, dp, dq, qi) should be present in the - // output. - const char* expected_jwk = - "{\"alg\":\"RS1\",\"d\":\"M6UEKpCyfU9UUcqbu9C0R3GhAa-IQ0Cu-YhfKku-" - "kuiUpySsPFaMj5eFOtB8AmbIxqPKCSnx6PESMYhEKfxNmuVf7olqEM5wfD7X5zTkRyejlXRQ" - "GlMmgxCcKrrKuig8MbS9L1PD7jfjUs7jT55QO9gMBiKtecbc7og1R8ajsyU\",\"dp\":" - "\"KPoTk4ZVvh-" - "KFZy6ylpy6hkMMAieGc0nSlVvNsT24Z9VSzTAd3kEJ7vdjdPt4kSDKPOF2Bsw6OQ7L_-" - "gJ4YZeQ\",\"dq\":\"Gos485j6cSBJiY1_t57gp3ZoeRKZzfoJ78DlB6yyHtdDAe9b_Ui-" - "RV6utuFnglWCdYCo5OjhQVHRUQqCo_LnKQ\",\"e\":\"AQAB\",\"ext\":true,\"key_" - "ops\":[\"sign\"],\"kty\":\"RSA\",\"n\":" - "\"pW5KDnAQF1iaUYfcfqhB0Vby7A42rVKkTf6x5h962ZHYxRBW_-2xYrTA8oOhKoijlN_" - "1JqtykcuzB86r_OCx39XNlQgJbVsri2311nHvY3fAkhyyPCcKcOJZjm_4nRnxBazC0_" - "DLNfKSgOE4a29kxO8i4eHyDQzoz_siSb2aITc\",\"p\":\"5-" - "iUJyCod1Fyc6NWBT6iobwMlKpy1VxuhilrLfyWeUjApyy8zKfqyzVwbgmh31WhU1vZs8w0Fg" - "s7bc0-2o5kQw\",\"q\":\"tp3KHPfU1-yB51uQ_MqHSrzeEj_" - "ScAGAqpBHm25I3o1n7ST58Z2FuidYdPVCzSDccj5pYzZKH5QlRSsmmmeZ_Q\",\"qi\":" - "\"JxVqukEm0kqB86Uoy_sn9WiG-" - "ECp9uhuF6RLlP6TGVhLjiL93h5aLjvYqluo2FhBlOshkKz4MrhH8To9JKefTQ\"}"; - - ASSERT_EQ(CryptoData(std::string(expected_jwk)), - CryptoData(exported_key_jwk)); - - ASSERT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatJwk, - CryptoData(exported_key_jwk), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha1), - true, - blink::WebCryptoKeyUsageSign, - &key)); - - std::vector<uint8> exported_key_pkcs8; - ASSERT_EQ( - Status::Success(), - ExportKey(blink::WebCryptoKeyFormatPkcs8, key, &exported_key_pkcs8)); - - ASSERT_EQ(CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), - CryptoData(exported_key_pkcs8)); -} - -// Tests importing multiple RSA private keys from JWK, and then exporting to -// PKCS8. -// -// This is a regression test for http://crbug.com/378315, for which importing -// a sequence of keys from JWK could yield the wrong key. The first key would -// be imported correctly, however every key after that would actually import -// the first key. -TEST_F(SharedCryptoTest, MAYBE(ImportMultipleRSAPrivateKeysJwk)) { - if (!SupportsRsaKeyImport()) - return; - - scoped_ptr<base::ListValue> key_list; - ASSERT_TRUE(ReadJsonTestFileToList("rsa_private_keys.json", &key_list)); - - // For this test to be meaningful the keys MUST be kept alive before importing - // new keys. - std::vector<blink::WebCryptoKey> live_keys; - - for (size_t key_index = 0; key_index < key_list->GetSize(); ++key_index) { - SCOPED_TRACE(key_index); - - base::DictionaryValue* key_values; - ASSERT_TRUE(key_list->GetDictionary(key_index, &key_values)); - - // Get the JWK representation of the key. - base::DictionaryValue* key_jwk; - ASSERT_TRUE(key_values->GetDictionary("jwk", &key_jwk)); - - // Get the PKCS8 representation of the key. - std::string pkcs8_hex_string; - ASSERT_TRUE(key_values->GetString("pkcs8", &pkcs8_hex_string)); - std::vector<uint8> pkcs8_bytes = HexStringToBytes(pkcs8_hex_string); - - // Get the modulus length for the key. - int modulus_length_bits = 0; - ASSERT_TRUE(key_values->GetInteger("modulusLength", &modulus_length_bits)); - - blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); - - // Import the key from JWK. - ASSERT_EQ( - Status::Success(), - ImportKeyJwkFromDict(*key_jwk, - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256), - true, - blink::WebCryptoKeyUsageSign, - &private_key)); - - live_keys.push_back(private_key); - - EXPECT_EQ( - modulus_length_bits, - static_cast<int>( - private_key.algorithm().rsaHashedParams()->modulusLengthBits())); - - // Export to PKCS8 and verify that it matches expectation. - std::vector<uint8> exported_key_pkcs8; - ASSERT_EQ( - Status::Success(), - ExportKey( - blink::WebCryptoKeyFormatPkcs8, private_key, &exported_key_pkcs8)); - - EXPECT_BYTES_EQ(pkcs8_bytes, exported_key_pkcs8); - } -} - -// Import an RSA private key using JWK. Next import a JWK containing the same -// modulus, but mismatched parameters for the rest. It should NOT be possible -// that the second import retrieves the first key. See http://crbug.com/378315 -// for how that could happen. -TEST_F(SharedCryptoTest, MAYBE(ImportJwkExistingModulusAndInvalid)) { -#if defined(USE_NSS) - if (!NSS_VersionCheck("3.16.2")) { - LOG(WARNING) << "Skipping test because lacks NSS support"; - return; - } -#endif - - scoped_ptr<base::ListValue> key_list; - ASSERT_TRUE(ReadJsonTestFileToList("rsa_private_keys.json", &key_list)); - - // Import a 1024-bit private key. - base::DictionaryValue* key1_props; - ASSERT_TRUE(key_list->GetDictionary(1, &key1_props)); - base::DictionaryValue* key1_jwk; - ASSERT_TRUE(key1_props->GetDictionary("jwk", &key1_jwk)); - - blink::WebCryptoKey key1 = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - ImportKeyJwkFromDict(*key1_jwk, - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256), - true, - blink::WebCryptoKeyUsageSign, - &key1)); - - ASSERT_EQ(1024u, key1.algorithm().rsaHashedParams()->modulusLengthBits()); - - // Construct a JWK using the modulus of key1, but all the other fields from - // another key (also a 1024-bit private key). - base::DictionaryValue* key2_props; - ASSERT_TRUE(key_list->GetDictionary(5, &key2_props)); - base::DictionaryValue* key2_jwk; - ASSERT_TRUE(key2_props->GetDictionary("jwk", &key2_jwk)); - std::string modulus; - key1_jwk->GetString("n", &modulus); - key2_jwk->SetString("n", modulus); - - // This should fail, as the n,e,d parameters are not consistent. It MUST NOT - // somehow return the key created earlier. - blink::WebCryptoKey key2 = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::OperationError(), - ImportKeyJwkFromDict(*key2_jwk, - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256), - true, - blink::WebCryptoKeyUsageSign, - &key2)); -} - -// Import a JWK RSA private key with some optional parameters missing (q, dp, -// dq, qi). -// -// The only optional parameter included is "p". -// -// This fails because JWA says that producers must include either ALL optional -// parameters or NONE. -TEST_F(SharedCryptoTest, MAYBE(ImportRsaPrivateKeyJwkMissingOptionalParams)) { - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - - base::DictionaryValue dict; - dict.SetString("kty", "RSA"); - dict.SetString("alg", "RS1"); - - dict.SetString( - "n", - "pW5KDnAQF1iaUYfcfqhB0Vby7A42rVKkTf6x5h962ZHYxRBW_-2xYrTA8oOhKoijlN_" - "1JqtykcuzB86r_OCx39XNlQgJbVsri2311nHvY3fAkhyyPCcKcOJZjm_4nRnxBazC0_" - "DLNfKSgOE4a29kxO8i4eHyDQzoz_siSb2aITc"); - dict.SetString("e", "AQAB"); - dict.SetString( - "d", - "M6UEKpCyfU9UUcqbu9C0R3GhAa-IQ0Cu-YhfKku-" - "kuiUpySsPFaMj5eFOtB8AmbIxqPKCSnx6PESMYhEKfxNmuVf7olqEM5wfD7X5zTkRyejlXRQ" - "GlMmgxCcKrrKuig8MbS9L1PD7jfjUs7jT55QO9gMBiKtecbc7og1R8ajsyU"); - - dict.SetString("p", - "5-" - "iUJyCod1Fyc6NWBT6iobwMlKpy1VxuhilrLfyWeUjApyy8zKfqyzVwbgmh31W" - "hU1vZs8w0Fgs7bc0-2o5kQw"); - - ASSERT_EQ(Status::ErrorJwkIncompleteOptionalRsaPrivateKey(), - ImportKeyJwkFromDict(dict, - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha1), - true, - blink::WebCryptoKeyUsageSign, - &key)); -} - -// Import a JWK RSA private key, without any of the optional parameters. -// -// This is expected to work, however based on the current NSS implementation it -// does not. -// -// TODO(eroman): http://crbug/com/374927 -TEST_F(SharedCryptoTest, MAYBE(ImportRsaPrivateKeyJwkIncorrectOptionalEmpty)) { - if (!SupportsRsaKeyImport()) - return; - - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - - base::DictionaryValue dict; - dict.SetString("kty", "RSA"); - dict.SetString("alg", "RS1"); - - dict.SetString( - "n", - "pW5KDnAQF1iaUYfcfqhB0Vby7A42rVKkTf6x5h962ZHYxRBW_-2xYrTA8oOhKoijlN_" - "1JqtykcuzB86r_OCx39XNlQgJbVsri2311nHvY3fAkhyyPCcKcOJZjm_4nRnxBazC0_" - "DLNfKSgOE4a29kxO8i4eHyDQzoz_siSb2aITc"); - dict.SetString("e", "AQAB"); - dict.SetString( - "d", - "M6UEKpCyfU9UUcqbu9C0R3GhAa-IQ0Cu-YhfKku-" - "kuiUpySsPFaMj5eFOtB8AmbIxqPKCSnx6PESMYhEKfxNmuVf7olqEM5wfD7X5zTkRyejlXRQ" - "GlMmgxCcKrrKuig8MbS9L1PD7jfjUs7jT55QO9gMBiKtecbc7og1R8ajsyU"); - - // TODO(eroman): This should pass, see: http://crbug/com/374927 - // - // Technically it is OK to fail since JWA says that consumer are not required - // to support lack of the optional parameters. - ASSERT_EQ(Status::OperationError(), - ImportKeyJwkFromDict(dict, - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha1), - true, - blink::WebCryptoKeyUsageSign, - &key)); - -} - -TEST_F(SharedCryptoTest, MAYBE(GenerateKeyPairRsa)) { - // Note: using unrealistic short key lengths here to avoid bogging down tests. - - // Successful WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 key generation (sha256) - const unsigned int modulus_length = 256; - const std::vector<uint8> public_exponent = HexStringToBytes("010001"); - blink::WebCryptoAlgorithm algorithm = - CreateRsaHashedKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256, - modulus_length, - public_exponent); - bool extractable = true; - const blink::WebCryptoKeyUsageMask usage_mask = 0; - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); - - EXPECT_EQ(Status::Success(), - GenerateKeyPair( - algorithm, extractable, usage_mask, &public_key, &private_key)); - EXPECT_FALSE(public_key.isNull()); - EXPECT_FALSE(private_key.isNull()); - EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key.type()); - EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key.type()); - EXPECT_EQ(modulus_length, - public_key.algorithm().rsaHashedParams()->modulusLengthBits()); - EXPECT_EQ(modulus_length, - private_key.algorithm().rsaHashedParams()->modulusLengthBits()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, - public_key.algorithm().rsaHashedParams()->hash().id()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, - private_key.algorithm().rsaHashedParams()->hash().id()); - EXPECT_TRUE(public_key.extractable()); - EXPECT_EQ(extractable, private_key.extractable()); - EXPECT_EQ(usage_mask, public_key.usages()); - EXPECT_EQ(usage_mask, private_key.usages()); - - // Try exporting the generated key pair, and then re-importing to verify that - // the exported data was valid. - std::vector<uint8> public_key_spki; - EXPECT_EQ( - Status::Success(), - ExportKey(blink::WebCryptoKeyFormatSpki, public_key, &public_key_spki)); - - if (SupportsRsaKeyImport()) { - public_key = blink::WebCryptoKey::createNull(); - EXPECT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatSpki, - CryptoData(public_key_spki), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256), - true, - usage_mask, - &public_key)); - EXPECT_EQ(modulus_length, - public_key.algorithm().rsaHashedParams()->modulusLengthBits()); - - std::vector<uint8> private_key_pkcs8; - EXPECT_EQ( - Status::Success(), - ExportKey( - blink::WebCryptoKeyFormatPkcs8, private_key, &private_key_pkcs8)); - private_key = blink::WebCryptoKey::createNull(); - EXPECT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatPkcs8, - CryptoData(private_key_pkcs8), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256), - true, - usage_mask, - &private_key)); - EXPECT_EQ(modulus_length, - private_key.algorithm().rsaHashedParams()->modulusLengthBits()); - } - - // Fail with bad modulus. - algorithm = - CreateRsaHashedKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256, - 0, - public_exponent); - EXPECT_EQ(Status::ErrorGenerateRsaZeroModulus(), - GenerateKeyPair( - algorithm, extractable, usage_mask, &public_key, &private_key)); - - // Fail with bad exponent: larger than unsigned long. - unsigned int exponent_length = sizeof(unsigned long) + 1; // NOLINT - const std::vector<uint8> long_exponent(exponent_length, 0x01); - algorithm = - CreateRsaHashedKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256, - modulus_length, - long_exponent); - EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), - GenerateKeyPair( - algorithm, extractable, usage_mask, &public_key, &private_key)); - - // Fail with bad exponent: empty. - const std::vector<uint8> empty_exponent; - algorithm = - CreateRsaHashedKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256, - modulus_length, - empty_exponent); - EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), - GenerateKeyPair( - algorithm, extractable, usage_mask, &public_key, &private_key)); - - // Fail with bad exponent: all zeros. - std::vector<uint8> exponent_with_leading_zeros(15, 0x00); - algorithm = - CreateRsaHashedKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256, - modulus_length, - exponent_with_leading_zeros); - EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), - GenerateKeyPair( - algorithm, extractable, usage_mask, &public_key, &private_key)); - - // Key generation success using exponent with leading zeros. - exponent_with_leading_zeros.insert(exponent_with_leading_zeros.end(), - public_exponent.begin(), - public_exponent.end()); - algorithm = - CreateRsaHashedKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256, - modulus_length, - exponent_with_leading_zeros); - EXPECT_EQ(Status::Success(), - GenerateKeyPair( - algorithm, extractable, usage_mask, &public_key, &private_key)); - EXPECT_FALSE(public_key.isNull()); - EXPECT_FALSE(private_key.isNull()); - EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key.type()); - EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key.type()); - EXPECT_TRUE(public_key.extractable()); - EXPECT_EQ(extractable, private_key.extractable()); - EXPECT_EQ(usage_mask, public_key.usages()); - EXPECT_EQ(usage_mask, private_key.usages()); - - // Successful WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 key generation (sha1) - algorithm = - CreateRsaHashedKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha1, - modulus_length, - public_exponent); - EXPECT_EQ( - Status::Success(), - GenerateKeyPair(algorithm, false, usage_mask, &public_key, &private_key)); - EXPECT_FALSE(public_key.isNull()); - EXPECT_FALSE(private_key.isNull()); - EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key.type()); - EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key.type()); - EXPECT_EQ(modulus_length, - public_key.algorithm().rsaHashedParams()->modulusLengthBits()); - EXPECT_EQ(modulus_length, - private_key.algorithm().rsaHashedParams()->modulusLengthBits()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, - public_key.algorithm().rsaHashedParams()->hash().id()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, - private_key.algorithm().rsaHashedParams()->hash().id()); - // Even though "extractable" was set to false, the public key remains - // extractable. - EXPECT_TRUE(public_key.extractable()); - EXPECT_FALSE(private_key.extractable()); - EXPECT_EQ(usage_mask, public_key.usages()); - EXPECT_EQ(usage_mask, private_key.usages()); - - // Exporting a private key as SPKI format doesn't make sense. However this - // will first fail because the key is not extractable. - std::vector<uint8> output; - EXPECT_EQ(Status::ErrorKeyNotExtractable(), - ExportKey(blink::WebCryptoKeyFormatSpki, private_key, &output)); - - // Re-generate an extractable private_key and try to export it as SPKI format. - // This should fail since spki is for public keys. - EXPECT_EQ( - Status::Success(), - GenerateKeyPair(algorithm, true, usage_mask, &public_key, &private_key)); - EXPECT_EQ(Status::ErrorUnexpectedKeyType(), - ExportKey(blink::WebCryptoKeyFormatSpki, private_key, &output)); -} - -TEST_F(SharedCryptoTest, MAYBE(GenerateKeyPairRsaBadModulusLength)) { - const unsigned int kBadModulus[] = { - 0, - 255, // Not a multiple of 8. - 1023, // Not a multiple of 8. - 0xFFFFFFFF, // Cannot fit in a signed int. - 16384 + 8, // 16384 is the maxmimum length that NSS succeeds for. - }; - - const std::vector<uint8> public_exponent = HexStringToBytes("010001"); - - for (size_t i = 0; i < arraysize(kBadModulus); ++i) { - const unsigned int modulus_length = kBadModulus[i]; - blink::WebCryptoAlgorithm algorithm = CreateRsaHashedKeyGenAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256, - modulus_length, - public_exponent); - bool extractable = true; - const blink::WebCryptoKeyUsageMask usage_mask = 0; - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); - - EXPECT_FALSE( - GenerateKeyPair( - algorithm, extractable, usage_mask, &public_key, &private_key) - .IsSuccess()); - } -} - -// Try generating RSA key pairs using unsupported public exponents. Only -// exponents of 3 and 65537 are supported. While both OpenSSL and NSS can -// support other values, OpenSSL hangs when given invalid exponents, so use a -// whitelist to validate the parameters. -TEST_F(SharedCryptoTest, MAYBE(GenerateKeyPairRsaBadExponent)) { - const unsigned int modulus_length = 1024; - - const char* const kPublicExponents[] = { - "11", // 17 - This is a valid public exponent, but currently disallowed. - "00", - "01", - "02", - "010000", // 65536 - }; - - for (size_t i = 0; i < arraysize(kPublicExponents); ++i) { - SCOPED_TRACE(i); - blink::WebCryptoAlgorithm algorithm = CreateRsaHashedKeyGenAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256, - modulus_length, - HexStringToBytes(kPublicExponents[i])); - - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); - - EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), - GenerateKeyPair( - algorithm, true, 0, &public_key, &private_key)); - } -} - -TEST_F(SharedCryptoTest, MAYBE(RsaSsaSignVerifyFailures)) { - if (!SupportsRsaKeyImport()) - return; - - // Import a key pair. - blink::WebCryptoAlgorithm import_algorithm = - CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha1); - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); - ASSERT_NO_FATAL_FAILURE( - ImportRsaKeyPair(HexStringToBytes(kPublicKeySpkiDerHex), - HexStringToBytes(kPrivateKeyPkcs8DerHex), - import_algorithm, - false, - blink::WebCryptoKeyUsageVerify, - blink::WebCryptoKeyUsageSign, - &public_key, - &private_key)); - - blink::WebCryptoAlgorithm algorithm = - CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5); - - std::vector<uint8> signature; - bool signature_match; - - // Compute a signature. - const std::vector<uint8> data = HexStringToBytes("010203040506070809"); - ASSERT_EQ(Status::Success(), - 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_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)); - 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)); - EXPECT_FALSE(signature_match); - - // Ensure signatures that are greater than the modulus size fail. - const unsigned int long_message_size_bytes = 1024; - 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)); - EXPECT_FALSE(signature_match); - - // Ensure that signing and verifying with an incompatible algorithm fails. - algorithm = CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep); - - 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)); - - // 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 - // implementation, the inner hash should be specified uniquely by the key - // algorithm parameter. To validate this behavior, call Verify with a computed - // signature that used one hash type (SHA-1), but pass in a key with a - // different inner hash type (SHA-256). If the hash type is determined by the - // signature itself (undesired), the verify will pass, while if the hash type - // is specified by the key algorithm (desired), the verify will fail. - - // Compute a signature using SHA-1 as the inner hash. - EXPECT_EQ(Status::Success(), - Sign(CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5), - private_key, - CryptoData(data), - &signature)); - - blink::WebCryptoKey public_key_256 = blink::WebCryptoKey::createNull(); - EXPECT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatSpki, - CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256), - true, - blink::WebCryptoKeyUsageVerify, - &public_key_256)); - - // Now verify using an algorithm whose inner hash is SHA-256, not SHA-1. The - // signature should not verify. - // NOTE: public_key was produced by generateKey, and so its associated - // algorithm has WebCryptoRsaKeyGenParams and not WebCryptoRsaSsaParams. Thus - // it has no inner hash to conflict with the input algorithm. - EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, - private_key.algorithm().rsaHashedParams()->hash().id()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, - public_key_256.algorithm().rsaHashedParams()->hash().id()); - - bool is_match; - EXPECT_EQ(Status::Success(), - VerifySignature( - CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5), - public_key_256, - CryptoData(signature), - CryptoData(data), - &is_match)); - EXPECT_FALSE(is_match); -} - -TEST_F(SharedCryptoTest, MAYBE(RsaSignVerifyKnownAnswer)) { - if (!SupportsRsaKeyImport()) - return; - - scoped_ptr<base::ListValue> tests; - ASSERT_TRUE(ReadJsonTestFileToList("pkcs1v15_sign.json", &tests)); - - // Import the key pair. - blink::WebCryptoAlgorithm import_algorithm = - CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha1); - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); - ASSERT_NO_FATAL_FAILURE( - ImportRsaKeyPair(HexStringToBytes(kPublicKeySpkiDerHex), - HexStringToBytes(kPrivateKeyPkcs8DerHex), - import_algorithm, - false, - blink::WebCryptoKeyUsageVerify, - blink::WebCryptoKeyUsageSign, - &public_key, - &private_key)); - - blink::WebCryptoAlgorithm algorithm = - CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5); - - // Validate the signatures are computed and verified as expected. - std::vector<uint8> signature; - for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { - SCOPED_TRACE(test_index); - - base::DictionaryValue* test; - ASSERT_TRUE(tests->GetDictionary(test_index, &test)); - - std::vector<uint8> test_message = - GetBytesFromHexString(test, "message_hex"); - std::vector<uint8> test_signature = - GetBytesFromHexString(test, "signature_hex"); - - signature.clear(); - ASSERT_EQ( - Status::Success(), - Sign(algorithm, private_key, CryptoData(test_message), &signature)); - EXPECT_BYTES_EQ(test_signature, signature); - - bool is_match = false; - ASSERT_EQ(Status::Success(), - VerifySignature(algorithm, - public_key, - CryptoData(test_signature), - CryptoData(test_message), - &is_match)); - EXPECT_TRUE(is_match); - } -} - -TEST_F(SharedCryptoTest, MAYBE(AesKwKeyImport)) { - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - blink::WebCryptoAlgorithm algorithm = - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); - - // Import a 128-bit Key Encryption Key (KEK) - std::string key_raw_hex_in = "025a8cf3f08b4f6c5f33bbc76a471939"; - ASSERT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(HexStringToBytes(key_raw_hex_in)), - algorithm, - true, - blink::WebCryptoKeyUsageWrapKey, - &key)); - std::vector<uint8> key_raw_out; - EXPECT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatRaw, key, &key_raw_out)); - EXPECT_BYTES_EQ_HEX(key_raw_hex_in, key_raw_out); - - // Import a 192-bit KEK - key_raw_hex_in = "c0192c6466b2370decbb62b2cfef4384544ffeb4d2fbc103"; - ASSERT_EQ(Status::ErrorAes192BitUnsupported(), - ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(HexStringToBytes(key_raw_hex_in)), - algorithm, - true, - blink::WebCryptoKeyUsageWrapKey, - &key)); - - // Import a 256-bit Key Encryption Key (KEK) - key_raw_hex_in = - "e11fe66380d90fa9ebefb74e0478e78f95664d0c67ca20ce4a0b5842863ac46f"; - ASSERT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(HexStringToBytes(key_raw_hex_in)), - algorithm, - true, - blink::WebCryptoKeyUsageWrapKey, - &key)); - EXPECT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatRaw, key, &key_raw_out)); - EXPECT_BYTES_EQ_HEX(key_raw_hex_in, key_raw_out); - - // Fail import of 0 length key - EXPECT_EQ(Status::ErrorImportAesKeyLength(), - ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(HexStringToBytes("")), - algorithm, - true, - blink::WebCryptoKeyUsageWrapKey, - &key)); - - // Fail import of 124-bit KEK - key_raw_hex_in = "3e4566a2bdaa10cb68134fa66c15ddb"; - EXPECT_EQ(Status::ErrorImportAesKeyLength(), - ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(HexStringToBytes(key_raw_hex_in)), - algorithm, - true, - blink::WebCryptoKeyUsageWrapKey, - &key)); - - // Fail import of 200-bit KEK - key_raw_hex_in = "0a1d88608a5ad9fec64f1ada269ebab4baa2feeb8d95638c0e"; - EXPECT_EQ(Status::ErrorImportAesKeyLength(), - ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(HexStringToBytes(key_raw_hex_in)), - algorithm, - true, - blink::WebCryptoKeyUsageWrapKey, - &key)); - - // Fail import of 260-bit KEK - key_raw_hex_in = - "72d4e475ff34215416c9ad9c8281247a4d730c5f275ac23f376e73e3bce8d7d5a"; - EXPECT_EQ(Status::ErrorImportAesKeyLength(), - ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(HexStringToBytes(key_raw_hex_in)), - algorithm, - true, - blink::WebCryptoKeyUsageWrapKey, - &key)); -} - -TEST_F(SharedCryptoTest, MAYBE(UnwrapFailures)) { - // This test exercises the code path common to all unwrap operations. - scoped_ptr<base::ListValue> tests; - ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests)); - base::DictionaryValue* test; - ASSERT_TRUE(tests->GetDictionary(0, &test)); - const std::vector<uint8> test_kek = GetBytesFromHexString(test, "kek"); - const std::vector<uint8> test_ciphertext = - GetBytesFromHexString(test, "ciphertext"); - - blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); - - // Using a wrapping algorithm that does not match the wrapping key algorithm - // should fail. - blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( - test_kek, - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw), - blink::WebCryptoKeyUsageUnwrapKey); - EXPECT_EQ( - Status::ErrorUnexpected(), - UnwrapKey(blink::WebCryptoKeyFormatRaw, - CryptoData(test_ciphertext), - wrapping_key, - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), - true, - blink::WebCryptoKeyUsageEncrypt, - &unwrapped_key)); -} - -TEST_F(SharedCryptoTest, MAYBE(AesKwRawSymkeyWrapUnwrapKnownAnswer)) { - scoped_ptr<base::ListValue> tests; - ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests)); - - for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { - SCOPED_TRACE(test_index); - base::DictionaryValue* test; - ASSERT_TRUE(tests->GetDictionary(test_index, &test)); - const std::vector<uint8> test_kek = GetBytesFromHexString(test, "kek"); - const std::vector<uint8> test_key = GetBytesFromHexString(test, "key"); - const std::vector<uint8> test_ciphertext = - GetBytesFromHexString(test, "ciphertext"); - const blink::WebCryptoAlgorithm wrapping_algorithm = - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); - - // Import the wrapping key. - blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( - test_kek, - wrapping_algorithm, - blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey); - - // Import the key to be wrapped. - blink::WebCryptoKey key = ImportSecretKeyFromRaw( - test_key, - CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1), - blink::WebCryptoKeyUsageSign); - - // Wrap the key and verify the ciphertext result against the known answer. - std::vector<uint8> wrapped_key; - ASSERT_EQ(Status::Success(), - WrapKey(blink::WebCryptoKeyFormatRaw, - key, - wrapping_key, - wrapping_algorithm, - &wrapped_key)); - EXPECT_BYTES_EQ(test_ciphertext, wrapped_key); - - // Unwrap the known ciphertext to get a new test_key. - blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ( - Status::Success(), - UnwrapKey(blink::WebCryptoKeyFormatRaw, - CryptoData(test_ciphertext), - wrapping_key, - wrapping_algorithm, - CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1), - true, - blink::WebCryptoKeyUsageSign, - &unwrapped_key)); - EXPECT_FALSE(key.isNull()); - EXPECT_TRUE(key.handle()); - EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); - EXPECT_EQ(true, key.extractable()); - EXPECT_EQ(blink::WebCryptoKeyUsageSign, key.usages()); - - // Export the new key and compare its raw bytes with the original known key. - std::vector<uint8> raw_key; - EXPECT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatRaw, unwrapped_key, &raw_key)); - EXPECT_BYTES_EQ(test_key, raw_key); - } -} - -// Unwrap a HMAC key using AES-KW, and then try doing a sign/verify with the -// unwrapped key -TEST_F(SharedCryptoTest, MAYBE(AesKwRawSymkeyUnwrapSignVerifyHmac)) { - scoped_ptr<base::ListValue> tests; - ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests)); - - base::DictionaryValue* test; - ASSERT_TRUE(tests->GetDictionary(0, &test)); - const std::vector<uint8> test_kek = GetBytesFromHexString(test, "kek"); - const std::vector<uint8> test_ciphertext = - GetBytesFromHexString(test, "ciphertext"); - const blink::WebCryptoAlgorithm wrapping_algorithm = - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); - - // Import the wrapping key. - blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( - test_kek, wrapping_algorithm, blink::WebCryptoKeyUsageUnwrapKey); - - // Unwrap the known ciphertext. - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - ASSERT_EQ( - Status::Success(), - UnwrapKey(blink::WebCryptoKeyFormatRaw, - CryptoData(test_ciphertext), - wrapping_key, - wrapping_algorithm, - CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1), - false, - blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, - &key)); - - EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); - EXPECT_FALSE(key.extractable()); - EXPECT_EQ(blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, - key.usages()); - - // Sign an empty message and ensure it is verified. - std::vector<uint8> test_message; - std::vector<uint8> signature; - - ASSERT_EQ(Status::Success(), - Sign(CreateAlgorithm(blink::WebCryptoAlgorithmIdHmac), - key, - CryptoData(test_message), - &signature)); - - EXPECT_GT(signature.size(), 0u); - - bool verify_result; - ASSERT_EQ(Status::Success(), - VerifySignature(CreateAlgorithm(blink::WebCryptoAlgorithmIdHmac), - key, - CryptoData(signature), - CryptoData(test_message), - &verify_result)); -} - -TEST_F(SharedCryptoTest, MAYBE(AesKwRawSymkeyWrapUnwrapErrors)) { - scoped_ptr<base::ListValue> tests; - ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests)); - base::DictionaryValue* test; - // Use 256 bits of data with a 256-bit KEK - ASSERT_TRUE(tests->GetDictionary(3, &test)); - const std::vector<uint8> test_kek = GetBytesFromHexString(test, "kek"); - const std::vector<uint8> test_key = GetBytesFromHexString(test, "key"); - const std::vector<uint8> test_ciphertext = - GetBytesFromHexString(test, "ciphertext"); - const blink::WebCryptoAlgorithm wrapping_algorithm = - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); - const blink::WebCryptoAlgorithm key_algorithm = - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); - // Import the wrapping key. - blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( - test_kek, - wrapping_algorithm, - blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey); - // Import the key to be wrapped. - blink::WebCryptoKey key = ImportSecretKeyFromRaw( - test_key, - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), - blink::WebCryptoKeyUsageEncrypt); - - // Unwrap with wrapped data too small must fail. - const std::vector<uint8> small_data(test_ciphertext.begin(), - test_ciphertext.begin() + 23); - blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); - EXPECT_EQ(Status::ErrorDataTooSmall(), - UnwrapKey(blink::WebCryptoKeyFormatRaw, - CryptoData(small_data), - wrapping_key, - wrapping_algorithm, - key_algorithm, - true, - blink::WebCryptoKeyUsageEncrypt, - &unwrapped_key)); - - // Unwrap with wrapped data size not a multiple of 8 bytes must fail. - const std::vector<uint8> unaligned_data(test_ciphertext.begin(), - test_ciphertext.end() - 2); - EXPECT_EQ(Status::ErrorInvalidAesKwDataLength(), - UnwrapKey(blink::WebCryptoKeyFormatRaw, - CryptoData(unaligned_data), - wrapping_key, - wrapping_algorithm, - key_algorithm, - true, - blink::WebCryptoKeyUsageEncrypt, - &unwrapped_key)); -} - -TEST_F(SharedCryptoTest, MAYBE(AesKwRawSymkeyUnwrapCorruptData)) { - scoped_ptr<base::ListValue> tests; - ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests)); - base::DictionaryValue* test; - // Use 256 bits of data with a 256-bit KEK - ASSERT_TRUE(tests->GetDictionary(3, &test)); - const std::vector<uint8> test_kek = GetBytesFromHexString(test, "kek"); - const std::vector<uint8> test_key = GetBytesFromHexString(test, "key"); - const std::vector<uint8> test_ciphertext = - GetBytesFromHexString(test, "ciphertext"); - const blink::WebCryptoAlgorithm wrapping_algorithm = - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); - - // Import the wrapping key. - blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( - test_kek, - wrapping_algorithm, - blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey); - - // Unwrap of a corrupted version of the known ciphertext should fail, due to - // AES-KW's built-in integrity check. - blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); - EXPECT_EQ( - Status::OperationError(), - UnwrapKey(blink::WebCryptoKeyFormatRaw, - CryptoData(Corrupted(test_ciphertext)), - wrapping_key, - wrapping_algorithm, - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), - true, - blink::WebCryptoKeyUsageEncrypt, - &unwrapped_key)); -} - -TEST_F(SharedCryptoTest, MAYBE(AesKwJwkSymkeyUnwrapKnownData)) { - // The following data lists a known HMAC SHA-256 key, then a JWK - // representation of this key which was encrypted ("wrapped") using AES-KW and - // the following wrapping key. - // For reference, the intermediate clear JWK is - // {"alg":"HS256","ext":true,"k":<b64urlKey>,"key_ops":["verify"],"kty":"oct"} - // (Not shown is space padding to ensure the cleartext meets the size - // requirements of the AES-KW algorithm.) - const std::vector<uint8> key_data = HexStringToBytes( - "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"); - const std::vector<uint8> wrapped_key_data = HexStringToBytes( - "14E6380B35FDC5B72E1994764B6CB7BFDD64E7832894356AAEE6C3768FC3D0F115E6B0" - "6729756225F999AA99FDF81FD6A359F1576D3D23DE6CB69C3937054EB497AC1E8C38D5" - "5E01B9783A20C8D930020932CF25926103002213D0FC37279888154FEBCEDF31832158" - "97938C5CFE5B10B4254D0C399F39D0"); - const std::vector<uint8> wrapping_key_data = - HexStringToBytes("000102030405060708090A0B0C0D0E0F"); - const blink::WebCryptoAlgorithm wrapping_algorithm = - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); - - // Import the wrapping key. - blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( - wrapping_key_data, wrapping_algorithm, blink::WebCryptoKeyUsageUnwrapKey); - - // Unwrap the known wrapped key data to produce a new key - blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ( - Status::Success(), - UnwrapKey(blink::WebCryptoKeyFormatJwk, - CryptoData(wrapped_key_data), - wrapping_key, - wrapping_algorithm, - CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256), - true, - blink::WebCryptoKeyUsageVerify, - &unwrapped_key)); - - // Validate the new key's attributes. - EXPECT_FALSE(unwrapped_key.isNull()); - EXPECT_TRUE(unwrapped_key.handle()); - EXPECT_EQ(blink::WebCryptoKeyTypeSecret, unwrapped_key.type()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, unwrapped_key.algorithm().id()); - EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, - unwrapped_key.algorithm().hmacParams()->hash().id()); - EXPECT_EQ(256u, unwrapped_key.algorithm().hmacParams()->lengthBits()); - EXPECT_EQ(true, unwrapped_key.extractable()); - EXPECT_EQ(blink::WebCryptoKeyUsageVerify, unwrapped_key.usages()); - - // Export the new key's raw data and compare to the known original. - std::vector<uint8> raw_key; - EXPECT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatRaw, unwrapped_key, &raw_key)); - EXPECT_BYTES_EQ(key_data, raw_key); -} - -// TODO(eroman): -// * Test decryption when the tag length exceeds input size -// * Test decryption with empty input -// * Test decryption with tag length of 0. -TEST_F(SharedCryptoTest, MAYBE(AesGcmSampleSets)) { - // Some Linux test runners may not have a new enough version of NSS. - if (!SupportsAesGcm()) { - LOG(WARNING) << "AES GCM not supported, skipping tests"; - return; - } - - scoped_ptr<base::ListValue> tests; - ASSERT_TRUE(ReadJsonTestFileToList("aes_gcm.json", &tests)); - - // Note that WebCrypto appends the authentication tag to the ciphertext. - for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { - SCOPED_TRACE(test_index); - base::DictionaryValue* test; - ASSERT_TRUE(tests->GetDictionary(test_index, &test)); - - const std::vector<uint8> test_key = GetBytesFromHexString(test, "key"); - const std::vector<uint8> test_iv = GetBytesFromHexString(test, "iv"); - const std::vector<uint8> test_additional_data = - GetBytesFromHexString(test, "additional_data"); - const std::vector<uint8> test_plain_text = - GetBytesFromHexString(test, "plain_text"); - const std::vector<uint8> test_authentication_tag = - GetBytesFromHexString(test, "authentication_tag"); - const unsigned int test_tag_size_bits = test_authentication_tag.size() * 8; - const std::vector<uint8> test_cipher_text = - GetBytesFromHexString(test, "cipher_text"); - - blink::WebCryptoKey key = ImportSecretKeyFromRaw( - test_key, - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm), - blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt); - - // Verify exported raw key is identical to the imported data - std::vector<uint8> raw_key; - EXPECT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); - - EXPECT_BYTES_EQ(test_key, raw_key); - - // Test encryption. - std::vector<uint8> cipher_text; - std::vector<uint8> authentication_tag; - EXPECT_EQ(Status::Success(), - AesGcmEncrypt(key, - test_iv, - test_additional_data, - test_tag_size_bits, - test_plain_text, - &cipher_text, - &authentication_tag)); - - EXPECT_BYTES_EQ(test_cipher_text, cipher_text); - EXPECT_BYTES_EQ(test_authentication_tag, authentication_tag); - - // Test decryption. - std::vector<uint8> plain_text; - EXPECT_EQ(Status::Success(), - AesGcmDecrypt(key, - test_iv, - test_additional_data, - test_tag_size_bits, - test_cipher_text, - test_authentication_tag, - &plain_text)); - EXPECT_BYTES_EQ(test_plain_text, plain_text); - - // Decryption should fail if any of the inputs are tampered with. - EXPECT_EQ(Status::OperationError(), - AesGcmDecrypt(key, - Corrupted(test_iv), - test_additional_data, - test_tag_size_bits, - test_cipher_text, - test_authentication_tag, - &plain_text)); - EXPECT_EQ(Status::OperationError(), - AesGcmDecrypt(key, - test_iv, - Corrupted(test_additional_data), - test_tag_size_bits, - test_cipher_text, - test_authentication_tag, - &plain_text)); - EXPECT_EQ(Status::OperationError(), - AesGcmDecrypt(key, - test_iv, - test_additional_data, - test_tag_size_bits, - Corrupted(test_cipher_text), - test_authentication_tag, - &plain_text)); - EXPECT_EQ(Status::OperationError(), - AesGcmDecrypt(key, - test_iv, - test_additional_data, - test_tag_size_bits, - test_cipher_text, - Corrupted(test_authentication_tag), - &plain_text)); - - // Try different incorrect tag lengths - uint8 kAlternateTagLengths[] = {0, 8, 96, 120, 128, 160, 255}; - for (size_t tag_i = 0; tag_i < arraysize(kAlternateTagLengths); ++tag_i) { - unsigned int wrong_tag_size_bits = kAlternateTagLengths[tag_i]; - if (test_tag_size_bits == wrong_tag_size_bits) - continue; - EXPECT_NE(Status::Success(), - AesGcmDecrypt(key, - test_iv, - test_additional_data, - wrong_tag_size_bits, - test_cipher_text, - test_authentication_tag, - &plain_text)); - } - } -} - -// AES 192-bit is not allowed: http://crbug.com/381829 -TEST_F(SharedCryptoTest, MAYBE(ImportAesCbc192Raw)) { - std::vector<uint8> key_raw(24, 0); - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - Status status = ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(key_raw), - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), - true, - blink::WebCryptoKeyUsageEncrypt, - &key); - ASSERT_EQ(Status::ErrorAes192BitUnsupported(), status); -} - -// AES 192-bit is not allowed: http://crbug.com/381829 -TEST_F(SharedCryptoTest, MAYBE(ImportAesCbc192Jwk)) { - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - - base::DictionaryValue dict; - dict.SetString("kty", "oct"); - dict.SetString("alg", "A192CBC"); - dict.SetString("k", "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh"); - - EXPECT_EQ( - Status::ErrorAes192BitUnsupported(), - ImportKeyJwkFromDict(dict, - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), - false, - blink::WebCryptoKeyUsageEncrypt, - &key)); -} - -// AES 192-bit is not allowed: http://crbug.com/381829 -TEST_F(SharedCryptoTest, MAYBE(GenerateAesCbc192)) { - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - Status status = GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(192), - true, - blink::WebCryptoKeyUsageEncrypt, - &key); - ASSERT_EQ(Status::ErrorAes192BitUnsupported(), status); -} - -// AES 192-bit is not allowed: http://crbug.com/381829 -TEST_F(SharedCryptoTest, MAYBE(UnwrapAesCbc192)) { - std::vector<uint8> wrapping_key_data(16, 0); - std::vector<uint8> wrapped_key = HexStringToBytes( - "1A07ACAB6C906E50883173C29441DB1DE91D34F45C435B5F99C822867FB3956F"); - - blink::WebCryptoKey wrapping_key = - ImportSecretKeyFromRaw(wrapping_key_data, - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw), - blink::WebCryptoKeyUsageUnwrapKey); - - blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::ErrorAes192BitUnsupported(), - UnwrapKey(blink::WebCryptoKeyFormatRaw, - CryptoData(wrapped_key), - wrapping_key, - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw), - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), - true, - blink::WebCryptoKeyUsageEncrypt, - &unwrapped_key)); -} - -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"); - jwk->SetString("n", - Base64EncodeUrlSafe(HexStringToBytes(kPublicKeyModulusHex))); - jwk->SetString( - "e", Base64EncodeUrlSafe(HexStringToBytes(kPublicKeyExponentHex))); - return jwk.Pass(); - } -}; - -// Import a PKCS#8 private key that uses RSAPrivateKey with the -// id-rsaEncryption OID. -TEST_F(SharedCryptoRsaOaepTest, ImportPkcs8WithRsaEncryption) { - if (!SupportsRsaOaep()) { - LOG(WARNING) << "RSA-OAEP support not present; skipping."; - return; - } - - blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatPkcs8, - CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaOaep, - blink::WebCryptoAlgorithmIdSha1), - true, - blink::WebCryptoKeyUsageDecrypt, - &private_key)); -} - -TEST_F(SharedCryptoRsaOaepTest, ImportPublicJwkWithNoAlg) { - if (!SupportsRsaOaep()) { - LOG(WARNING) << "RSA-OAEP support not present; skipping."; - return; - } - - scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); - - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - ImportKeyJwkFromDict(*jwk.get(), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaOaep, - blink::WebCryptoAlgorithmIdSha1), - true, - blink::WebCryptoKeyUsageEncrypt, - &public_key)); -} - -TEST_F(SharedCryptoRsaOaepTest, ImportPublicJwkWithMatchingAlg) { - if (!SupportsRsaOaep()) { - LOG(WARNING) << "RSA-OAEP support not present; skipping."; - return; - } - - scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); - jwk->SetString("alg", "RSA-OAEP"); - - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - ImportKeyJwkFromDict(*jwk.get(), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaOaep, - blink::WebCryptoAlgorithmIdSha1), - true, - blink::WebCryptoKeyUsageEncrypt, - &public_key)); -} - -TEST_F(SharedCryptoRsaOaepTest, ImportPublicJwkWithMismatchedAlgFails) { - if (!SupportsRsaOaep()) { - LOG(WARNING) << "RSA-OAEP support not present; skipping."; - return; - } - - scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); - jwk->SetString("alg", "RSA-OAEP-512"); - - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::ErrorJwkAlgorithmInconsistent(), - ImportKeyJwkFromDict(*jwk.get(), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaOaep, - blink::WebCryptoAlgorithmIdSha1), - true, - blink::WebCryptoKeyUsageEncrypt, - &public_key)); -} - -TEST_F(SharedCryptoRsaOaepTest, ImportPublicJwkWithMismatchedTypeFails) { - if (!SupportsRsaOaep()) { - LOG(WARNING) << "RSA-OAEP support not present; skipping."; - return; - } - - scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); - jwk->SetString("kty", "oct"); - jwk->SetString("alg", "RSA-OAEP"); - - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::ErrorJwkPropertyMissing("k"), - ImportKeyJwkFromDict(*jwk.get(), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaOaep, - blink::WebCryptoAlgorithmIdSha1), - true, - blink::WebCryptoKeyUsageEncrypt, - &public_key)); -} - -TEST_F(SharedCryptoRsaOaepTest, ExportPublicJwk) { - if (!SupportsRsaOaep()) { - LOG(WARNING) << "RSA-OAEP support not present; skipping."; - return; - } - - struct TestData { - blink::WebCryptoAlgorithmId hash_alg; - const char* expected_jwk_alg; - } kTestData[] = {{blink::WebCryptoAlgorithmIdSha1, "RSA-OAEP"}, - {blink::WebCryptoAlgorithmIdSha256, "RSA-OAEP-256"}, - {blink::WebCryptoAlgorithmIdSha384, "RSA-OAEP-384"}, - {blink::WebCryptoAlgorithmIdSha512, "RSA-OAEP-512"}}; - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestData); ++i) { - const TestData& test_data = kTestData[i]; - SCOPED_TRACE(test_data.expected_jwk_alg); - - scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); - jwk->SetString("alg", test_data.expected_jwk_alg); - - // Import the key in a known-good format - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - ImportKeyJwkFromDict( - *jwk.get(), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaOaep, test_data.hash_alg), - true, - blink::WebCryptoKeyUsageEncrypt, - &public_key)); - - // Now export the key as JWK and verify its contents - std::vector<uint8> jwk_data; - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &jwk_data)); - EXPECT_TRUE(VerifyPublicJwk(jwk_data, - test_data.expected_jwk_alg, - kPublicKeyModulusHex, - kPublicKeyExponentHex, - blink::WebCryptoKeyUsageEncrypt)); - } -} - -TEST_F(SharedCryptoRsaOaepTest, EncryptDecryptKnownAnswerTest) { - if (!SupportsRsaOaep()) { - LOG(WARNING) << "RSA-OAEP support not present; skipping."; - return; - } - - scoped_ptr<base::ListValue> tests; - ASSERT_TRUE(ReadJsonTestFileToList("rsa_oaep.json", &tests)); - - for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { - SCOPED_TRACE(test_index); - - base::DictionaryValue* test = NULL; - ASSERT_TRUE(tests->GetDictionary(test_index, &test)); - - blink::WebCryptoAlgorithm digest_algorithm = - GetDigestAlgorithm(test, "hash"); - ASSERT_FALSE(digest_algorithm.isNull()); - std::vector<uint8> public_key_der = - GetBytesFromHexString(test, "public_key"); - std::vector<uint8> private_key_der = - GetBytesFromHexString(test, "private_key"); - std::vector<uint8> ciphertext = GetBytesFromHexString(test, "ciphertext"); - std::vector<uint8> plaintext = GetBytesFromHexString(test, "plaintext"); - std::vector<uint8> label = GetBytesFromHexString(test, "label"); - - blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaOaep, digest_algorithm.id()); - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); - - ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair(public_key_der, - private_key_der, - import_algorithm, - false, - blink::WebCryptoKeyUsageEncrypt, - blink::WebCryptoKeyUsageDecrypt, - &public_key, - &private_key)); - - blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); - std::vector<uint8> decrypted_data; - ASSERT_EQ(Status::Success(), - Decrypt(op_algorithm, - private_key, - CryptoData(ciphertext), - &decrypted_data)); - EXPECT_BYTES_EQ(plaintext, decrypted_data); - std::vector<uint8> encrypted_data; - ASSERT_EQ( - Status::Success(), - Encrypt( - op_algorithm, public_key, CryptoData(plaintext), &encrypted_data)); - std::vector<uint8> redecrypted_data; - ASSERT_EQ(Status::Success(), - Decrypt(op_algorithm, - private_key, - CryptoData(encrypted_data), - &redecrypted_data)); - EXPECT_BYTES_EQ(plaintext, redecrypted_data); - } -} - -TEST_F(SharedCryptoRsaOaepTest, EncryptWithLargeMessageFails) { - if (!SupportsRsaOaep()) { - LOG(WARNING) << "RSA-OAEP support not present; skipping."; - return; - } - - const blink::WebCryptoAlgorithmId kHash = blink::WebCryptoAlgorithmIdSha1; - const size_t kHashSize = 20; - - scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); - - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - ImportKeyJwkFromDict(*jwk.get(), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaOaep, kHash), - true, - blink::WebCryptoKeyUsageEncrypt, - &public_key)); - - // The maximum size of an encrypted message is: - // modulus length - // - 1 (leading octet) - // - hash size (maskedSeed) - // - hash size (lHash portion of maskedDB) - // - 1 (at least one octet for the padding string) - size_t kMaxMessageSize = (kModulusLengthBits / 8) - 2 - (2 * kHashSize); - - // The label has no influence on the maximum message size. For simplicity, - // use the empty string. - std::vector<uint8> label; - blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); - - // Test that a message just before the boundary succeeds. - std::string large_message; - large_message.resize(kMaxMessageSize - 1, 'A'); - - std::vector<uint8> ciphertext; - ASSERT_EQ( - Status::Success(), - Encrypt( - op_algorithm, public_key, CryptoData(large_message), &ciphertext)); - - // Test that a message at the boundary succeeds. - large_message.resize(kMaxMessageSize, 'A'); - ciphertext.clear(); - - ASSERT_EQ( - Status::Success(), - Encrypt( - op_algorithm, public_key, CryptoData(large_message), &ciphertext)); - - // Test that a message greater than the largest size fails. - large_message.resize(kMaxMessageSize + 1, 'A'); - ciphertext.clear(); - - ASSERT_EQ( - Status::OperationError(), - Encrypt( - op_algorithm, public_key, CryptoData(large_message), &ciphertext)); -} - -// Ensures that if the selected hash algorithm for the RSA-OAEP message is too -// large, then it is rejected, independent of the actual message to be -// encrypted. -// For example, a 1024-bit RSA key is too small to accomodate a message that -// uses OAEP with SHA-512, since it requires 1040 bits to encode -// (2 * hash size + 2 padding bytes). -TEST_F(SharedCryptoRsaOaepTest, EncryptWithLargeDigestFails) { - if (!SupportsRsaOaep()) { - LOG(WARNING) << "RSA-OAEP support not present; skipping."; - return; - } - - const blink::WebCryptoAlgorithmId kHash = blink::WebCryptoAlgorithmIdSha512; - - scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); - - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - ImportKeyJwkFromDict(*jwk.get(), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaOaep, kHash), - true, - blink::WebCryptoKeyUsageEncrypt, - &public_key)); - - // The label has no influence on the maximum message size. For simplicity, - // use the empty string. - std::vector<uint8> label; - blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); - - std::string small_message("A"); - std::vector<uint8> ciphertext; - // This is an operation error, as the internal consistency checking of the - // algorithm parameters is up to the implementation. - ASSERT_EQ( - Status::OperationError(), - Encrypt( - op_algorithm, public_key, CryptoData(small_message), &ciphertext)); -} - -TEST_F(SharedCryptoRsaOaepTest, DecryptWithLargeMessageFails) { - if (!SupportsRsaOaep()) { - LOG(WARNING) << "RSA-OAEP support not present; skipping."; - return; - } - - blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatPkcs8, - CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), - CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaOaep, - blink::WebCryptoAlgorithmIdSha1), - true, - blink::WebCryptoKeyUsageDecrypt, - &private_key)); - - // The label has no influence on the maximum message size. For simplicity, - // use the empty string. - std::vector<uint8> label; - blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); - - std::string large_dummy_message(kModulusLengthBits / 8, 'A'); - std::vector<uint8> plaintext; - - ASSERT_EQ(Status::OperationError(), - Decrypt(op_algorithm, - private_key, - CryptoData(large_dummy_message), - &plaintext)); -} - -TEST_F(SharedCryptoRsaOaepTest, WrapUnwrapRawKey) { - if (!SupportsRsaOaep()) { - LOG(WARNING) << "RSA-OAEP support not present; skipping."; - return; - } - - blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaOaep, blink::WebCryptoAlgorithmIdSha1); - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); - - ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( - HexStringToBytes(kPublicKeySpkiDerHex), - HexStringToBytes(kPrivateKeyPkcs8DerHex), - import_algorithm, - false, - blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageWrapKey, - blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageUnwrapKey, - &public_key, - &private_key)); - - std::vector<uint8> label; - blink::WebCryptoAlgorithm wrapping_algorithm = CreateRsaOaepAlgorithm(label); - - const std::string key_hex = "000102030405060708090A0B0C0D0E0F"; - const blink::WebCryptoAlgorithm key_algorithm = - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); - - blink::WebCryptoKey key = - ImportSecretKeyFromRaw(HexStringToBytes(key_hex), - key_algorithm, - blink::WebCryptoKeyUsageEncrypt); - ASSERT_FALSE(key.isNull()); - - std::vector<uint8> wrapped_key; - ASSERT_EQ(Status::Success(), - WrapKey(blink::WebCryptoKeyFormatRaw, - key, - public_key, - wrapping_algorithm, - &wrapped_key)); - - // Verify that |wrapped_key| can be decrypted and yields the key data. - // Because |private_key| supports both decrypt and unwrap, this is valid. - std::vector<uint8> decrypted_key; - ASSERT_EQ(Status::Success(), - Decrypt(wrapping_algorithm, - private_key, - CryptoData(wrapped_key), - &decrypted_key)); - EXPECT_BYTES_EQ_HEX(key_hex, decrypted_key); - - // Now attempt to unwrap the key, which should also decrypt the data. - blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - UnwrapKey(blink::WebCryptoKeyFormatRaw, - CryptoData(wrapped_key), - private_key, - wrapping_algorithm, - key_algorithm, - true, - blink::WebCryptoKeyUsageEncrypt, - &unwrapped_key)); - ASSERT_FALSE(unwrapped_key.isNull()); - - std::vector<uint8> raw_key; - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatRaw, unwrapped_key, &raw_key)); - EXPECT_BYTES_EQ_HEX(key_hex, raw_key); -} - -TEST_F(SharedCryptoRsaOaepTest, WrapUnwrapJwkSymKey) { - if (!SupportsRsaOaep()) { - LOG(WARNING) << "RSA-OAEP support not present; skipping."; - return; - } - - // The public and private portions of a 2048-bit RSA key with the - // id-rsaEncryption OID - const char kPublicKey2048SpkiDerHex[] = - "30820122300d06092a864886f70d01010105000382010f003082010a0282010100c5d8ce" - "137a38168c8ab70229cfa5accc640567159750a312ce2e7d54b6e2fdd59b300c6a6c9764" - "f8de6f00519cdb90111453d273a967462786480621f9e7cee5b73d63358448e7183a3a68" - "e991186359f26aa88fbca5f53e673e502e4c5a2ba5068aeba60c9d0c44d872458d1b1e2f" - "7f339f986076d516e93dc750f0b7680b6f5f02bc0d5590495be04c4ae59d34ba17bc5d08" - "a93c75cfda2828f4a55b153af912038438276cb4a14f8116ca94db0ea9893652d02fc606" - "36f19975e3d79a4d8ea8bfed6f8e0a24b63d243b08ea70a086ad56dd6341d733711c89ca" - "749d4a80b3e6ecd2f8e53731eadeac2ea77788ee55d7b4b47c0f2523fbd61b557c16615d" - "5d0203010001"; - const char kPrivateKey2048Pkcs8DerHex[] = - "308204bd020100300d06092a864886f70d0101010500048204a7308204a3020100028201" - "0100c5d8ce137a38168c8ab70229cfa5accc640567159750a312ce2e7d54b6e2fdd59b30" - "0c6a6c9764f8de6f00519cdb90111453d273a967462786480621f9e7cee5b73d63358448" - "e7183a3a68e991186359f26aa88fbca5f53e673e502e4c5a2ba5068aeba60c9d0c44d872" - "458d1b1e2f7f339f986076d516e93dc750f0b7680b6f5f02bc0d5590495be04c4ae59d34" - "ba17bc5d08a93c75cfda2828f4a55b153af912038438276cb4a14f8116ca94db0ea98936" - "52d02fc60636f19975e3d79a4d8ea8bfed6f8e0a24b63d243b08ea70a086ad56dd6341d7" - "33711c89ca749d4a80b3e6ecd2f8e53731eadeac2ea77788ee55d7b4b47c0f2523fbd61b" - "557c16615d5d02030100010282010074b70feb41a0b0fcbc207670400556c9450042ede3" - "d4383fb1ce8f3558a6d4641d26dd4c333fa4db842d2b9cf9d2354d3e16ad027a9f682d8c" - "f4145a1ad97b9edcd8a41c402bd9d8db10f62f43df854cdccbbb2100834f083f53ed6d42" - "b1b729a59072b004a4e945fc027db15e9c121d1251464d320d4774d5732df6b3dbf751f4" - "9b19c9db201e19989c883bbaad5333db47f64f6f7a95b8d4936b10d945aa3f794cfaab62" - "e7d47686129358914f3b8085f03698a650ab5b8c7e45813f2b0515ec05b6e5195b6a7c2a" - "0d36969745f431ded4fd059f6aa361a4649541016d356297362b778e90f077d48815b339" - "ec6f43aba345df93e67fcb6c2cb5b4544e9be902818100e9c90abe5f9f32468c5b6d630c" - "54a4d7d75e29a72cf792f21e242aac78fd7995c42dfd4ae871d2619ff7096cb05baa78e3" - "23ecab338401a8059adf7a0d8be3b21edc9a9c82c5605634a2ec81ec053271721351868a" - "4c2e50c689d7cef94e31ff23658af5843366e2b289c5bf81d72756a7b93487dd8770d69c" - "1f4e089d6d89f302818100d8a58a727c4e209132afd9933b98c89aca862a01cc0be74133" - "bee517909e5c379e526895ac4af11780c1fe91194c777c9670b6423f0f5a32fd7691a622" - "113eef4bed2ef863363a335fd55b0e75088c582437237d7f3ed3f0a643950237bc6e6277" - "ccd0d0a1b4170aa1047aa7ffa7c8c54be10e8c7327ae2e0885663963817f6f02818100e5" - "aed9ba4d71b7502e6748a1ce247ecb7bd10c352d6d9256031cdf3c11a65e44b0b7ca2945" - "134671195af84c6b3bb3d10ebf65ae916f38bd5dbc59a0ad1c69b8beaf57cb3a8335f19b" - "c7117b576987b48331cd9fd3d1a293436b7bb5e1a35c6560de4b5688ea834367cb0997eb" - "b578f59ed4cb724c47dba94d3b484c1876dcd70281807f15bc7d2406007cac2b138a96af" - "2d1e00276b84da593132c253fcb73212732dfd25824c2a615bc3d9b7f2c8d2fa542d3562" - "b0c7738e61eeff580a6056239fb367ea9e5efe73d4f846033602e90c36a78db6fa8ea792" - "0769675ec58e237bd994d189c8045a96f5dd3a4f12547257ce224e3c9af830a4da3c0eab" - "9227a0035ae9028180067caea877e0b23090fc689322b71fbcce63d6596e66ab5fcdbaa0" - "0d49e93aba8effb4518c2da637f209028401a68f344865b4956b032c69acde51d29177ca" - "3db99fdbf5e74848ed4fa7bdfc2ebb60e2aaa5354770a763e1399ab7a2099762d525fea0" - "37f3e1972c45a477e66db95c9609bb27f862700ef93379930786cf751b"; - blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaOaep, blink::WebCryptoAlgorithmIdSha1); - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); - - ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( - HexStringToBytes(kPublicKey2048SpkiDerHex), - HexStringToBytes(kPrivateKey2048Pkcs8DerHex), - import_algorithm, - false, - blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageWrapKey, - blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageUnwrapKey, - &public_key, - &private_key)); - - std::vector<uint8> label; - blink::WebCryptoAlgorithm wrapping_algorithm = CreateRsaOaepAlgorithm(label); - - const std::string key_hex = "000102030405060708090a0b0c0d0e0f"; - const blink::WebCryptoAlgorithm key_algorithm = - webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); - - blink::WebCryptoKey key = - ImportSecretKeyFromRaw(HexStringToBytes(key_hex), - key_algorithm, - blink::WebCryptoKeyUsageEncrypt); - ASSERT_FALSE(key.isNull()); - - std::vector<uint8> wrapped_key; - ASSERT_EQ(Status::Success(), - WrapKey(blink::WebCryptoKeyFormatJwk, - key, - public_key, - wrapping_algorithm, - &wrapped_key)); - - // Verify that |wrapped_key| can be decrypted and yields a valid JWK object. - // Because |private_key| supports both decrypt and unwrap, this is valid. - std::vector<uint8> decrypted_jwk; - ASSERT_EQ(Status::Success(), - Decrypt(wrapping_algorithm, - private_key, - CryptoData(wrapped_key), - &decrypted_jwk)); - EXPECT_TRUE(VerifySecretJwk( - decrypted_jwk, "A128CBC", key_hex, blink::WebCryptoKeyUsageEncrypt)); - - // Now attempt to unwrap the key, which should also decrypt the data. - blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - UnwrapKey(blink::WebCryptoKeyFormatJwk, - CryptoData(wrapped_key), - private_key, - wrapping_algorithm, - key_algorithm, - true, - blink::WebCryptoKeyUsageEncrypt, - &unwrapped_key)); - ASSERT_FALSE(unwrapped_key.isNull()); - - std::vector<uint8> raw_key; - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatRaw, unwrapped_key, &raw_key)); - EXPECT_BYTES_EQ_HEX(key_hex, raw_key); -} - -// Try importing an RSA-SSA public key with unsupported key usages using SPKI -// format. RSA-SSA public keys only support the 'verify' usage. -TEST_F(SharedCryptoTest, MAYBE(ImportRsaSsaPublicKeyBadUsage_SPKI)) { - const blink::WebCryptoAlgorithm algorithm = - CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256); - - blink::WebCryptoKeyUsageMask bad_usages[] = { - blink::WebCryptoKeyUsageSign, - blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, - blink::WebCryptoKeyUsageEncrypt, - blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, - }; - - for (size_t i = 0; i < arraysize(bad_usages); ++i) { - SCOPED_TRACE(i); - - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), - ImportKey(blink::WebCryptoKeyFormatSpki, - CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), - algorithm, - false, - bad_usages[i], - &public_key)); - } -} - -// Try importing an RSA-SSA public key with unsupported key usages using JWK -// format. RSA-SSA public keys only support the 'verify' usage. -TEST_F(SharedCryptoTest, MAYBE(ImportRsaSsaPublicKeyBadUsage_JWK)) { - const blink::WebCryptoAlgorithm algorithm = - CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256); - - blink::WebCryptoKeyUsageMask bad_usages[] = { - blink::WebCryptoKeyUsageSign, - blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, - blink::WebCryptoKeyUsageEncrypt, - blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, - }; - - base::DictionaryValue dict; - RestoreJwkRsaDictionary(&dict); - dict.Remove("use", NULL); - dict.SetString("alg", "RS256"); - - for (size_t i = 0; i < arraysize(bad_usages); ++i) { - SCOPED_TRACE(i); - - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), - ImportKeyJwkFromDict( - dict, algorithm, false, bad_usages[i], &public_key)); - } -} - -// Try importing an AES-CBC key with unsupported key usages using raw -// format. AES-CBC keys support the following usages: -// 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey' -TEST_F(SharedCryptoTest, MAYBE(ImportAesCbcKeyBadUsage_Raw)) { - const blink::WebCryptoAlgorithm algorithm = - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); - - blink::WebCryptoKeyUsageMask bad_usages[] = { - blink::WebCryptoKeyUsageSign, - blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageDecrypt, - blink::WebCryptoKeyUsageDeriveBits, - blink::WebCryptoKeyUsageUnwrapKey | blink::WebCryptoKeyUsageVerify, - }; - - std::vector<uint8> key_bytes(16); - - for (size_t i = 0; i < arraysize(bad_usages); ++i) { - SCOPED_TRACE(i); - - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), - ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(key_bytes), - algorithm, - true, - bad_usages[i], - &key)); - } -} - -// Try importing an AES-KW key with unsupported key usages using raw -// format. AES-KW keys support the following usages: -// 'wrapKey', 'unwrapKey' -TEST_F(SharedCryptoTest, MAYBE(ImportAesKwKeyBadUsage_Raw)) { - const blink::WebCryptoAlgorithm algorithm = - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); - - blink::WebCryptoKeyUsageMask bad_usages[] = { - blink::WebCryptoKeyUsageEncrypt, - blink::WebCryptoKeyUsageDecrypt, - blink::WebCryptoKeyUsageSign, - blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageUnwrapKey, - blink::WebCryptoKeyUsageDeriveBits, - blink::WebCryptoKeyUsageUnwrapKey | blink::WebCryptoKeyUsageVerify, - }; - - std::vector<uint8> key_bytes(16); - - for (size_t i = 0; i < arraysize(bad_usages); ++i) { - SCOPED_TRACE(i); - - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), - ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(key_bytes), - algorithm, - true, - bad_usages[i], - &key)); - } -} - -// Try unwrapping an HMAC key with unsupported usages using JWK format and -// AES-KW. HMAC keys support the following usages: -// 'sign', 'verify' -TEST_F(SharedCryptoTest, MAYBE(UnwrapHmacKeyBadUsage_JWK)) { - const blink::WebCryptoAlgorithm unwrap_algorithm = - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); - - blink::WebCryptoKeyUsageMask bad_usages[] = { - blink::WebCryptoKeyUsageEncrypt, - blink::WebCryptoKeyUsageDecrypt, - blink::WebCryptoKeyUsageWrapKey, - blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageWrapKey, - blink::WebCryptoKeyUsageVerify | blink::WebCryptoKeyUsageDeriveKey, - }; - - // Import the wrapping key. - blink::WebCryptoKey wrapping_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(std::vector<uint8>(16)), - unwrap_algorithm, - true, - blink::WebCryptoKeyUsageUnwrapKey, - &wrapping_key)); - - // The JWK plain text is: - // { "kty": "oct","alg": "HS256","k": "GADWrMRHwQfoNaXU5fZvTg=="} - const char* kWrappedJwk = - "0AA245F17064FFB2A7A094436A39BEBFC962C627303D1327EA750CE9F917688C2782A943" - "7AE7586547AC490E8AE7D5B02D63868D5C3BB57D36C4C8C5BF3962ACEC6F42E767E5706" - "4"; - - for (size_t i = 0; i < arraysize(bad_usages); ++i) { - SCOPED_TRACE(i); - - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - - ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), - UnwrapKey(blink::WebCryptoKeyFormatJwk, - CryptoData(HexStringToBytes(kWrappedJwk)), - wrapping_key, - unwrap_algorithm, - webcrypto::CreateHmacImportAlgorithm( - blink::WebCryptoAlgorithmIdSha256), - true, - bad_usages[i], - &key)); - } -} - -// Try unwrapping an RSA-SSA public key with unsupported usages using JWK format -// and AES-KW. RSA-SSA public keys support the following usages: -// 'verify' -TEST_F(SharedCryptoTest, MAYBE(UnwrapRsaSsaPublicKeyBadUsage_JWK)) { - const blink::WebCryptoAlgorithm unwrap_algorithm = - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); - - blink::WebCryptoKeyUsageMask bad_usages[] = { - blink::WebCryptoKeyUsageEncrypt, - blink::WebCryptoKeyUsageSign, - blink::WebCryptoKeyUsageDecrypt, - blink::WebCryptoKeyUsageWrapKey, - blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageWrapKey, - }; - - // Import the wrapping key. - blink::WebCryptoKey wrapping_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - ImportKey(blink::WebCryptoKeyFormatRaw, - CryptoData(std::vector<uint8>(16)), - unwrap_algorithm, - true, - blink::WebCryptoKeyUsageUnwrapKey, - &wrapping_key)); - - // The JWK plaintext is: - // { "kty": "RSA","alg": "RS256","n": "...","e": "AQAB"} - - const char* kWrappedJwk = - "CE8DAEF99E977EE58958B8C4494755C846E883B2ECA575C5366622839AF71AB30875F152" - "E8E33E15A7817A3A2874EB53EFE05C774D98BC936BA9BA29BEB8BB3F3C3CE2323CB3359D" - "E3F426605CF95CCF0E01E870ABD7E35F62E030B5FB6E520A5885514D1D850FB64B57806D" - "1ADA57C6E27DF345D8292D80F6B074F1BE51C4CF3D76ECC8886218551308681B44FAC60B" - "8CF6EA439BC63239103D0AE81ADB96F908680586C6169284E32EB7DD09D31103EBDAC0C2" - "40C72DCF0AEA454113CC47457B13305B25507CBEAB9BDC8D8E0F867F9167F9DCEF0D9F9B" - "30F2EE83CEDFD51136852C8A5939B768"; - - for (size_t i = 0; i < arraysize(bad_usages); ++i) { - SCOPED_TRACE(i); - - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - - ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), - UnwrapKey(blink::WebCryptoKeyFormatJwk, - CryptoData(HexStringToBytes(kWrappedJwk)), - wrapping_key, - unwrap_algorithm, - webcrypto::CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256), - true, - bad_usages[i], - &key)); - } -} - -// Generate an AES-CBC key with invalid usages. AES-CBC supports: -// 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey' -TEST_F(SharedCryptoTest, MAYBE(GenerateAesKeyBadUsages)) { - blink::WebCryptoKeyUsageMask bad_usages[] = { - blink::WebCryptoKeyUsageSign, blink::WebCryptoKeyUsageVerify, - blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageVerify, - }; - - for (size_t i = 0; i < arraysize(bad_usages); ++i) { - SCOPED_TRACE(i); - - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - - ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), - GenerateSecretKey( - CreateAesCbcKeyGenAlgorithm(128), true, bad_usages[i], &key)); - } -} - -// Generate an RSA-SSA key pair with invalid usages. RSA-SSA supports: -// 'sign', 'verify' -TEST_F(SharedCryptoTest, MAYBE(GenerateRsaSsaBadUsages)) { - blink::WebCryptoKeyUsageMask bad_usages[] = { - blink::WebCryptoKeyUsageDecrypt, - blink::WebCryptoKeyUsageVerify | blink::WebCryptoKeyUsageDecrypt, - blink::WebCryptoKeyUsageWrapKey, - }; - - const unsigned int modulus_length = 256; - const std::vector<uint8> public_exponent = HexStringToBytes("010001"); - - for (size_t i = 0; i < arraysize(bad_usages); ++i) { - SCOPED_TRACE(i); - - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); - - ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), - GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256, - modulus_length, - public_exponent), - true, - bad_usages[i], - &public_key, - &private_key)); - } -} - -// Generate an RSA-SSA key pair. The public and private keys should select the -// key usages which are applicable, and not have the exact same usages as was -// specified to GenerateKey -TEST_F(SharedCryptoTest, MAYBE(GenerateRsaSsaKeyPairIntersectUsages)) { - const unsigned int modulus_length = 256; - const std::vector<uint8> public_exponent = HexStringToBytes("010001"); - - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); - - ASSERT_EQ(Status::Success(), - GenerateKeyPair( - CreateRsaHashedKeyGenAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256, - modulus_length, - public_exponent), - true, - blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, - &public_key, - &private_key)); - - EXPECT_EQ(blink::WebCryptoKeyUsageVerify, public_key.usages()); - EXPECT_EQ(blink::WebCryptoKeyUsageSign, private_key.usages()); - - // Try again but this time without the Verify usages. - ASSERT_EQ(Status::Success(), - GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256, - modulus_length, - public_exponent), - true, - blink::WebCryptoKeyUsageSign, - &public_key, - &private_key)); - - EXPECT_EQ(0, public_key.usages()); - EXPECT_EQ(blink::WebCryptoKeyUsageSign, private_key.usages()); -} - -// Generate an AES-CBC key and an RSA key pair. Use the AES-CBC key to wrap the -// key pair (using SPKI format for public key, PKCS8 format for private key). -// Then unwrap the wrapped key pair and verify that the key data is the same. -TEST_F(SharedCryptoTest, MAYBE(WrapUnwrapRoundtripSpkiPkcs8UsingAesCbc)) { - if (!SupportsRsaKeyImport()) - return; - - // Generate the wrapping key. - blink::WebCryptoKey wrapping_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(128), - true, - blink::WebCryptoKeyUsageWrapKey | - blink::WebCryptoKeyUsageUnwrapKey, - &wrapping_key)); - - // Generate an RSA key pair to be wrapped. - const unsigned int modulus_length = 256; - const std::vector<uint8> public_exponent = HexStringToBytes("010001"); - - blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); - blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); - ASSERT_EQ(Status::Success(), - GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( - blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256, - modulus_length, - public_exponent), - true, - 0, - &public_key, - &private_key)); - - // Export key pair as SPKI + PKCS8 - std::vector<uint8> public_key_spki; - ASSERT_EQ( - Status::Success(), - ExportKey(blink::WebCryptoKeyFormatSpki, public_key, &public_key_spki)); - - std::vector<uint8> private_key_pkcs8; - ASSERT_EQ( - Status::Success(), - ExportKey( - blink::WebCryptoKeyFormatPkcs8, private_key, &private_key_pkcs8)); - - // Wrap the key pair. - blink::WebCryptoAlgorithm wrap_algorithm = - CreateAesCbcAlgorithm(std::vector<uint8>(16, 0)); - - std::vector<uint8> wrapped_public_key; - ASSERT_EQ(Status::Success(), - WrapKey(blink::WebCryptoKeyFormatSpki, - public_key, - wrapping_key, - wrap_algorithm, - &wrapped_public_key)); - - std::vector<uint8> wrapped_private_key; - ASSERT_EQ(Status::Success(), - WrapKey(blink::WebCryptoKeyFormatPkcs8, - private_key, - wrapping_key, - wrap_algorithm, - &wrapped_private_key)); - - // Unwrap the key pair. - blink::WebCryptoAlgorithm rsa_import_algorithm = - CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, - blink::WebCryptoAlgorithmIdSha256); - - blink::WebCryptoKey unwrapped_public_key = blink::WebCryptoKey::createNull(); - - ASSERT_EQ(Status::Success(), - UnwrapKey(blink::WebCryptoKeyFormatSpki, - CryptoData(wrapped_public_key), - wrapping_key, - wrap_algorithm, - rsa_import_algorithm, - true, - 0, - &unwrapped_public_key)); - - blink::WebCryptoKey unwrapped_private_key = blink::WebCryptoKey::createNull(); - - ASSERT_EQ(Status::Success(), - UnwrapKey(blink::WebCryptoKeyFormatPkcs8, - CryptoData(wrapped_private_key), - wrapping_key, - wrap_algorithm, - rsa_import_algorithm, - true, - 0, - &unwrapped_private_key)); - - // Export unwrapped key pair as SPKI + PKCS8 - std::vector<uint8> unwrapped_public_key_spki; - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatSpki, - unwrapped_public_key, - &unwrapped_public_key_spki)); - - std::vector<uint8> unwrapped_private_key_pkcs8; - ASSERT_EQ(Status::Success(), - ExportKey(blink::WebCryptoKeyFormatPkcs8, - unwrapped_private_key, - &unwrapped_private_key_pkcs8)); - - EXPECT_EQ(public_key_spki, unwrapped_public_key_spki); - EXPECT_EQ(private_key_pkcs8, unwrapped_private_key_pkcs8); - - EXPECT_NE(public_key_spki, wrapped_public_key); - EXPECT_NE(private_key_pkcs8, wrapped_private_key); -} - -} // namespace webcrypto - -} // namespace content diff --git a/chromium/content/child/webcrypto/status.cc b/chromium/content/child/webcrypto/status.cc index fc01c2c290a..a7afdb3c063 100644 --- a/chromium/content/child/webcrypto/status.cc +++ b/chromium/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() { @@ -110,16 +105,37 @@ Status Status::ErrorJwkIncorrectKeyLength() { "of key data for the given algorithm."); } -Status Status::ErrorJwkIncompleteOptionalRsaPrivateKey() { +Status Status::ErrorJwkEmptyBigInteger(const std::string& property) { + return Status(blink::WebCryptoErrorTypeData, + "The JWK \"" + property + "\" property was empty."); +} + +Status Status::ErrorJwkBigIntegerHasLeadingZero(const std::string& property) { + return Status( + blink::WebCryptoErrorTypeData, + "The JWK \"" + property + "\" property contained a leading zero."); +} + +Status Status::ErrorJwkDuplicateKeyOps() { return Status(blink::WebCryptoErrorTypeData, - "The optional JWK properties p, q, dp, dq, qi must either all " - "be provided, or none provided"); + "The \"key_ops\" property of the JWK dictionary contains " + "duplicate usages."); } 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"); @@ -140,6 +156,21 @@ Status Status::ErrorIncorrectSizeAesCbcIv() { "The \"iv\" has an unexpected length -- must be 16 bytes"); } +Status Status::ErrorIncorrectSizeAesCtrCounter() { + return Status(blink::WebCryptoErrorTypeData, + "The \"counter\" has an unexpected length -- must be 16 bytes"); +} + +Status Status::ErrorInvalidAesCtrCounterLength() { + return Status(blink::WebCryptoErrorTypeData, + "The \"length\" property must be >= 1 and <= 128"); +} + +Status Status::ErrorAesCtrInputTooLongCounterRepeated() { + return Status(blink::WebCryptoErrorTypeData, + "The input is too large for the counter length."); +} + Status Status::ErrorDataTooLarge() { return Status(blink::WebCryptoErrorTypeData, "The provided data is too large"); @@ -185,9 +216,10 @@ Status Status::ErrorImportRsaEmptyModulus() { return Status(blink::WebCryptoErrorTypeData, "The modulus is empty"); } -Status Status::ErrorGenerateRsaZeroModulus() { - return Status(blink::WebCryptoErrorTypeData, - "The modulus bit length cannot be zero"); +Status Status::ErrorGenerateRsaUnsupportedModulus() { + return Status(blink::WebCryptoErrorTypeNotSupported, + "The modulus length must be a multiple of 8 bits and >= 256 " + "and <= 16384"); } Status Status::ErrorImportRsaEmptyExponent() { @@ -207,7 +239,7 @@ Status Status::ErrorGenerateKeyLength() { } Status Status::ErrorCreateKeyBadUsages() { - return Status(blink::WebCryptoErrorTypeData, + return Status(blink::WebCryptoErrorTypeSyntax, "Cannot create a key using the specified key usages."); } diff --git a/chromium/content/child/webcrypto/status.h b/chromium/content/child/webcrypto/status.h index 103c4edf6b5..4c1aae6e185 100644 --- a/chromium/content/child/webcrypto/status.h +++ b/chromium/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 @@ -107,9 +103,16 @@ class CONTENT_EXPORT Status { // given that is an error. static Status ErrorJwkIncorrectKeyLength(); - // The JWK was for an RSA private key but only partially provided the optional - // parameters (p, q, dq, dq, qi). - static Status ErrorJwkIncompleteOptionalRsaPrivateKey(); + // The JWK property |property| is supposed to represent a big-endian unsigned + // integer, however was the empty string. + static Status ErrorJwkEmptyBigInteger(const std::string& property); + + // The big-endian unsigned integer |property| contained leading zeros. This + // violates the JWA requirement that such octet strings be minimal. + static Status ErrorJwkBigIntegerHasLeadingZero(const std::string& property); + + // The key_ops lists a usage more than once. + static Status ErrorJwkDuplicateKeyOps(); // ------------------------------------ // Other errors @@ -120,6 +123,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(); @@ -136,6 +147,18 @@ class CONTENT_EXPORT Status { // bytes. static Status ErrorIncorrectSizeAesCbcIv(); + // When doing AES-CTR encryption/decryption, the "counter" parameter was not + // 16 bytes. + static Status ErrorIncorrectSizeAesCtrCounter(); + + // When doing AES-CTR encryption/decryption, the "length" parameter for the + // counter was out of range. + static Status ErrorInvalidAesCtrCounterLength(); + + // The input to encrypt/decrypt was too large. Based on the counter size, it + // would cause the counter to wraparound and repeat earlier values. + static Status ErrorAesCtrInputTooLongCounterRepeated(); + // The data provided to an encrypt/decrypt/sign/verify operation was too // large. This can either represent an internal limitation (for instance // representing buffer lengths as uints). @@ -172,8 +195,8 @@ class CONTENT_EXPORT Status { // The modulus bytes were empty when importing an RSA public key. static Status ErrorImportRsaEmptyModulus(); - // The the modulus length was zero bits when generating an RSA public key. - static Status ErrorGenerateRsaZeroModulus(); + // The modulus length was unsupported when generating an RSA key pair. + static Status ErrorGenerateRsaUnsupportedModulus(); // The exponent bytes were empty when importing an RSA public key. static Status ErrorImportRsaEmptyExponent(); diff --git a/chromium/content/child/webcrypto/structured_clone.cc b/chromium/content/child/webcrypto/structured_clone.cc new file mode 100644 index 00000000000..68d449a065e --- /dev/null +++ b/chromium/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_t>* 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 usages, + 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, + usages, + key); + if (status.IsError()) + return false; + return ValidateDeserializedKey(*key, algorithm, type); +} + +} // namespace webcrypto + +} // namespace content diff --git a/chromium/content/child/webcrypto/structured_clone.h b/chromium/content/child/webcrypto/structured_clone.h new file mode 100644 index 00000000000..3e218c85a80 --- /dev/null +++ b/chromium/content/child/webcrypto/structured_clone.h @@ -0,0 +1,34 @@ +// 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 <stdint.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_t>* key_data); + +// Called on the target Blink thread. +bool DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, + blink::WebCryptoKeyType type, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + const CryptoData& key_data, + blink::WebCryptoKey* key); + +} // namespace webcrypto + +} // namespace content + +#endif // CONTENT_CHILD_WEBCRYPTO_STRUCTURED_CLONE_H_ diff --git a/chromium/content/child/webcrypto/webcrypto_impl.cc b/chromium/content/child/webcrypto/webcrypto_impl.cc index 43b108bb0c6..2099a901073 100644 --- a/chromium/content/child/webcrypto/webcrypto_impl.cc +++ b/chromium/content/child/webcrypto/webcrypto_impl.cc @@ -10,13 +10,16 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/single_thread_task_runner.h" +#include "base/stl_util.h" #include "base/task_runner.h" #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/generate_key_result.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" @@ -108,7 +111,7 @@ void CompleteWithError(const Status& status, blink::WebCryptoResult* result) { } void CompleteWithBufferOrError(const Status& status, - const std::vector<uint8>& buffer, + const std::vector<uint8_t>& buffer, blink::WebCryptoResult* result) { if (status.IsError()) { CompleteWithError(status, result); @@ -118,8 +121,7 @@ void CompleteWithBufferOrError(const Status& status, // theoretically this could overflow. CompleteWithError(Status::ErrorUnexpected(), result); } else { - result->completeWithBuffer(webcrypto::Uint8VectorStart(buffer), - buffer.size()); + result->completeWithBuffer(vector_as_array(&buffer), buffer.size()); } } } @@ -198,9 +200,9 @@ struct EncryptState : public BaseState { const blink::WebCryptoAlgorithm algorithm; const blink::WebCryptoKey key; - const std::vector<uint8> data; + const std::vector<uint8_t> data; - std::vector<uint8> buffer; + std::vector<uint8_t> buffer; }; typedef EncryptState DecryptState; @@ -209,25 +211,18 @@ typedef EncryptState DigestState; struct GenerateKeyState : public BaseState { GenerateKeyState(const blink::WebCryptoAlgorithm& algorithm, bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKeyUsageMask usages, const blink::WebCryptoResult& result) : BaseState(result), algorithm(algorithm), extractable(extractable), - usage_mask(usage_mask), - public_key(blink::WebCryptoKey::createNull()), - private_key(blink::WebCryptoKey::createNull()), - is_asymmetric(false) {} + usages(usages) {} const blink::WebCryptoAlgorithm algorithm; const bool extractable; - const blink::WebCryptoKeyUsageMask usage_mask; + const blink::WebCryptoKeyUsageMask usages; - // If |is_asymmetric| is false, then |public_key| is understood to mean the - // symmetric key, and |private_key| is unused. - blink::WebCryptoKey public_key; - blink::WebCryptoKey private_key; - bool is_asymmetric; + webcrypto::GenerateKeyResult generate_key_result; }; struct ImportKeyState : public BaseState { @@ -236,21 +231,20 @@ struct ImportKeyState : public BaseState { unsigned int key_data_size, const blink::WebCryptoAlgorithm& algorithm, bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKeyUsageMask usages, const blink::WebCryptoResult& result) : BaseState(result), format(format), key_data(key_data, key_data + key_data_size), algorithm(algorithm), extractable(extractable), - usage_mask(usage_mask), - key(blink::WebCryptoKey::createNull()) {} + usages(usages) {} const blink::WebCryptoKeyFormat format; - const std::vector<uint8> key_data; + const std::vector<uint8_t> key_data; const blink::WebCryptoAlgorithm algorithm; const bool extractable; - const blink::WebCryptoKeyUsageMask usage_mask; + const blink::WebCryptoKeyUsageMask usages; blink::WebCryptoKey key; }; @@ -264,7 +258,7 @@ struct ExportKeyState : public BaseState { const blink::WebCryptoKeyFormat format; const blink::WebCryptoKey key; - std::vector<uint8> buffer; + std::vector<uint8_t> buffer; }; typedef EncryptState SignState; @@ -286,8 +280,8 @@ struct VerifySignatureState : public BaseState { const blink::WebCryptoAlgorithm algorithm; const blink::WebCryptoKey key; - const std::vector<uint8> signature; - const std::vector<uint8> data; + const std::vector<uint8_t> signature; + const std::vector<uint8_t> data; bool verify_result; }; @@ -309,7 +303,7 @@ struct WrapKeyState : public BaseState { const blink::WebCryptoKey wrapping_key; const blink::WebCryptoAlgorithm wrap_algorithm; - std::vector<uint8> buffer; + std::vector<uint8_t> buffer; }; struct UnwrapKeyState : public BaseState { @@ -329,11 +323,10 @@ struct UnwrapKeyState : public BaseState { unwrap_algorithm(unwrap_algorithm), unwrapped_key_algorithm(unwrapped_key_algorithm), extractable(extractable), - usages(usages), - unwrapped_key(blink::WebCryptoKey::createNull()) {} + usages(usages) {} const blink::WebCryptoKeyFormat format; - const std::vector<uint8> wrapped_key; + const std::vector<uint8_t> wrapped_key; const blink::WebCryptoKey wrapping_key; const blink::WebCryptoAlgorithm unwrap_algorithm; const blink::WebCryptoAlgorithm unwrapped_key_algorithm; @@ -400,10 +393,7 @@ void DoGenerateKeyReply(scoped_ptr<GenerateKeyState> state) { if (state->status.IsError()) { CompleteWithError(state->status, &state->result); } else { - if (state->is_asymmetric) - state->result.completeWithKeyPair(state->public_key, state->private_key); - else - state->result.completeWithKey(state->public_key); + state->generate_key_result.Complete(&state->result); } } @@ -411,37 +401,10 @@ void DoGenerateKey(scoped_ptr<GenerateKeyState> passed_state) { GenerateKeyState* state = passed_state.get(); if (state->cancelled()) return; - state->is_asymmetric = - webcrypto::IsAlgorithmAsymmetric(state->algorithm.id()); - if (state->is_asymmetric) { - state->status = webcrypto::GenerateKeyPair(state->algorithm, - state->extractable, - state->usage_mask, - &state->public_key, - &state->private_key); - - if (state->status.IsSuccess()) { - DCHECK(state->public_key.handle()); - DCHECK(state->private_key.handle()); - DCHECK_EQ(state->algorithm.id(), state->public_key.algorithm().id()); - DCHECK_EQ(state->algorithm.id(), state->private_key.algorithm().id()); - DCHECK_EQ(true, state->public_key.extractable()); - DCHECK_EQ(state->extractable, state->private_key.extractable()); - } - } else { - blink::WebCryptoKey* key = &state->public_key; - - state->status = webcrypto::GenerateSecretKey( - state->algorithm, state->extractable, state->usage_mask, key); - - if (state->status.IsSuccess()) { - DCHECK(key->handle()); - DCHECK_EQ(state->algorithm.id(), key->algorithm().id()); - DCHECK_EQ(state->extractable, key->extractable()); - DCHECK_EQ(state->usage_mask, key->usages()); - } - } - + state->status = webcrypto::GenerateKey(state->algorithm, + state->extractable, + state->usages, + &state->generate_key_result); state->origin_thread->PostTask( FROM_HERE, base::Bind(DoGenerateKeyReply, Passed(&passed_state))); } @@ -458,7 +421,7 @@ void DoImportKey(scoped_ptr<ImportKeyState> passed_state) { webcrypto::CryptoData(state->key_data), state->algorithm, state->extractable, - state->usage_mask, + state->usages, &state->key); if (state->status.IsSuccess()) { DCHECK(state->key.handle()); @@ -480,8 +443,7 @@ void DoExportKeyReply(scoped_ptr<ExportKeyState> state) { CompleteWithError(state->status, &state->result); } else { state->result.completeWithJson( - reinterpret_cast<const char*>( - webcrypto::Uint8VectorStart(&state->buffer)), + reinterpret_cast<const char*>(vector_as_array(&state->buffer)), state->buffer.size()); } } @@ -525,12 +487,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 +545,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, @@ -634,12 +591,12 @@ void WebCryptoImpl::digest(const blink::WebCryptoAlgorithm& algorithm, void WebCryptoImpl::generateKey(const blink::WebCryptoAlgorithm& algorithm, bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKeyUsageMask usages, blink::WebCryptoResult result) { DCHECK(!algorithm.isNull()); scoped_ptr<GenerateKeyState> state( - new GenerateKeyState(algorithm, extractable, usage_mask, result)); + new GenerateKeyState(algorithm, extractable, usages, result)); if (!CryptoThreadPool::PostTask(FROM_HERE, base::Bind(DoGenerateKey, Passed(&state)))) { CompleteWithThreadPoolError(&result); @@ -651,15 +608,10 @@ void WebCryptoImpl::importKey(blink::WebCryptoKeyFormat format, unsigned int key_data_size, const blink::WebCryptoAlgorithm& algorithm, bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKeyUsageMask usages, blink::WebCryptoResult result) { - scoped_ptr<ImportKeyState> state(new ImportKeyState(format, - key_data, - key_data_size, - algorithm, - extractable, - usage_mask, - result)); + scoped_ptr<ImportKeyState> state(new ImportKeyState( + format, key_data, key_data_size, algorithm, extractable, usages, result)); if (!CryptoThreadPool::PostTask(FROM_HERE, base::Bind(DoImportKey, Passed(&state)))) { CompleteWithThreadPoolError(&result); diff --git a/chromium/content/child/webcrypto/webcrypto_impl.h b/chromium/content/child/webcrypto/webcrypto_impl.h index 485beb9698d..9263f6eefa1 100644 --- a/chromium/content/child/webcrypto/webcrypto_impl.h +++ b/chromium/content/child/webcrypto/webcrypto_impl.h @@ -23,9 +23,10 @@ class WebCryptoImpl : public blink::WebCrypto { public: WebCryptoImpl(); - virtual ~WebCryptoImpl(); + // TODO(eroman): Once Blink and Chromium repositories are merged, use + // "override" in place of virtual. - static void EnsureInit(); + virtual ~WebCryptoImpl(); virtual void encrypt(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, @@ -43,14 +44,14 @@ class WebCryptoImpl : public blink::WebCrypto { blink::WebCryptoResult result); virtual void generateKey(const blink::WebCryptoAlgorithm& algorithm, bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKeyUsageMask usages, blink::WebCryptoResult result); virtual void importKey(blink::WebCryptoKeyFormat format, const unsigned char* key_data, unsigned int key_data_size, const blink::WebCryptoAlgorithm& algorithm, bool extractable, - blink::WebCryptoKeyUsageMask usage_mask, + blink::WebCryptoKeyUsageMask usages, blink::WebCryptoResult result); virtual void exportKey(blink::WebCryptoKeyFormat format, const blink::WebCryptoKey& key, diff --git a/chromium/content/child/webcrypto/webcrypto_util.cc b/chromium/content/child/webcrypto/webcrypto_util.cc index 87ac17465e8..410acdda738 100644 --- a/chromium/content/child/webcrypto/webcrypto_util.cc +++ b/chromium/content/child/webcrypto/webcrypto_util.cc @@ -4,7 +4,8 @@ #include "content/child/webcrypto/webcrypto_util.h" -#include "base/base64.h" +#include <set> + #include "base/logging.h" #include "base/strings/stringprintf.h" #include "content/child/webcrypto/status.h" @@ -16,48 +17,30 @@ namespace content { namespace webcrypto { -const uint8* Uint8VectorStart(const std::vector<uint8>& data) { - if (data.empty()) - return NULL; - return &data[0]; -} +namespace { -uint8* Uint8VectorStart(std::vector<uint8>* data) { - if (data->empty()) - return NULL; - return &(*data)[0]; -} +// Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros, +// to unsigned int. +bool BigIntegerToUint(const uint8_t* data, + unsigned int data_size, + unsigned int* result) { + if (data_size == 0) + return false; -// This function decodes unpadded 'base64url' encoded data, as described in -// RFC4648 (http://www.ietf.org/rfc/rfc4648.txt) Section 5. To do this, first -// change the incoming data to 'base64' encoding by applying the appropriate -// transformation including adding padding if required, and then call a base64 -// decoder. -bool Base64DecodeUrlSafe(const std::string& input, std::string* output) { - std::string base64EncodedText(input); - std::replace(base64EncodedText.begin(), base64EncodedText.end(), '-', '+'); - std::replace(base64EncodedText.begin(), base64EncodedText.end(), '_', '/'); - base64EncodedText.append((4 - base64EncodedText.size() % 4) % 4, '='); - return base::Base64Decode(base64EncodedText, output); -} + *result = 0; + for (size_t i = 0; i < data_size; ++i) { + size_t reverse_i = data_size - i - 1; -// Returns an unpadded 'base64url' encoding of the input data, using the -// inverse of the process above. -std::string Base64EncodeUrlSafe(const base::StringPiece& input) { - std::string output; - base::Base64Encode(input, &output); - std::replace(output.begin(), output.end(), '+', '-'); - std::replace(output.begin(), output.end(), '/', '_'); - output.erase(std::remove(output.begin(), output.end(), '='), output.end()); - return output; -} + if (reverse_i >= sizeof(*result) && data[i]) + return false; // Too large for a uint. -std::string Base64EncodeUrlSafe(const std::vector<uint8>& input) { - const base::StringPiece string_piece( - reinterpret_cast<const char*>(Uint8VectorStart(input)), input.size()); - return Base64EncodeUrlSafe(string_piece); + *result |= data[i] << 8 * reverse_i; + } + return true; } +} // namespace + struct JwkToWebCryptoUsage { const char* const jwk_key_op; const blink::WebCryptoKeyUsage webcrypto_usage; @@ -80,12 +63,11 @@ const JwkToWebCryptoUsage kJwkWebCryptoUsageMap[] = { {"wrapKey", blink::WebCryptoKeyUsageWrapKey}, {"unwrapKey", blink::WebCryptoKeyUsageUnwrapKey}}; -// Modifies the input usage_mask by according to the key_op value. bool JwkKeyOpToWebCryptoUsage(const std::string& key_op, - blink::WebCryptoKeyUsageMask* usage_mask) { + blink::WebCryptoKeyUsage* usage) { for (size_t i = 0; i < arraysize(kJwkWebCryptoUsageMap); ++i) { if (kJwkWebCryptoUsageMap[i].jwk_key_op == key_op) { - *usage_mask |= kJwkWebCryptoUsageMap[i].webcrypto_usage; + *usage = kJwkWebCryptoUsageMap[i].webcrypto_usage; return true; } } @@ -93,18 +75,32 @@ bool JwkKeyOpToWebCryptoUsage(const std::string& key_op, } // Composes a Web Crypto usage mask from an array of JWK key_ops values. -Status GetWebCryptoUsagesFromJwkKeyOps( - const base::ListValue* jwk_key_ops_value, - blink::WebCryptoKeyUsageMask* usage_mask) { - *usage_mask = 0; - for (size_t i = 0; i < jwk_key_ops_value->GetSize(); ++i) { +Status GetWebCryptoUsagesFromJwkKeyOps(const base::ListValue* key_ops, + blink::WebCryptoKeyUsageMask* usages) { + // This set keeps track of all unrecognized key_ops values. + std::set<std::string> unrecognized_usages; + + *usages = 0; + for (size_t i = 0; i < key_ops->GetSize(); ++i) { std::string key_op; - if (!jwk_key_ops_value->GetString(i, &key_op)) { + if (!key_ops->GetString(i, &key_op)) { return Status::ErrorJwkPropertyWrongType( base::StringPrintf("key_ops[%d]", static_cast<int>(i)), "string"); } - // Unrecognized key_ops are silently skipped. - ignore_result(JwkKeyOpToWebCryptoUsage(key_op, usage_mask)); + + blink::WebCryptoKeyUsage usage; + if (JwkKeyOpToWebCryptoUsage(key_op, &usage)) { + // Ensure there are no duplicate usages. + if (*usages & usage) + return Status::ErrorJwkDuplicateKeyOps(); + *usages |= usage; + } + + // Reaching here means the usage was unrecognized. Such usages are skipped + // over, however they are kept track of in a set to ensure there were no + // duplicates. + if (!unrecognized_usages.insert(key_op).second) + return Status::ErrorJwkDuplicateKeyOps(); } return Status::Success(); } @@ -112,32 +108,15 @@ Status GetWebCryptoUsagesFromJwkKeyOps( // Composes a JWK key_ops List from a Web Crypto usage mask. // Note: Caller must assume ownership of returned instance. base::ListValue* CreateJwkKeyOpsFromWebCryptoUsages( - blink::WebCryptoKeyUsageMask usage_mask) { + blink::WebCryptoKeyUsageMask usages) { base::ListValue* jwk_key_ops = new base::ListValue(); for (size_t i = 0; i < arraysize(kJwkWebCryptoUsageMap); ++i) { - if (usage_mask & kJwkWebCryptoUsageMap[i].webcrypto_usage) + if (usages & kJwkWebCryptoUsageMap[i].webcrypto_usage) jwk_key_ops->AppendString(kJwkWebCryptoUsageMap[i].jwk_key_op); } return jwk_key_ops; } -blink::WebCryptoAlgorithm GetInnerHashAlgorithm( - const blink::WebCryptoAlgorithm& algorithm) { - DCHECK(!algorithm.isNull()); - switch (algorithm.paramsType()) { - case blink::WebCryptoAlgorithmParamsTypeHmacImportParams: - return algorithm.hmacImportParams()->hash(); - case blink::WebCryptoAlgorithmParamsTypeHmacKeyGenParams: - return algorithm.hmacKeyGenParams()->hash(); - case blink::WebCryptoAlgorithmParamsTypeRsaHashedImportParams: - return algorithm.rsaHashedImportParams()->hash(); - case blink::WebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams: - return algorithm.rsaHashedKeyGenParams()->hash(); - default: - return blink::WebCryptoAlgorithm::createNull(); - } -} - blink::WebCryptoAlgorithm CreateAlgorithm(blink::WebCryptoAlgorithmId id) { return blink::WebCryptoAlgorithm::adoptParamsAndCreate(id, NULL); } @@ -154,52 +133,125 @@ blink::WebCryptoAlgorithm CreateRsaHashedImportAlgorithm( blink::WebCryptoAlgorithmId id, blink::WebCryptoAlgorithmId hash_id) { DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); - DCHECK(id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 || - id == blink::WebCryptoAlgorithmIdRsaOaep); return blink::WebCryptoAlgorithm::adoptParamsAndCreate( 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; +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); +} + +// 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(); } - 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; } + + 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(); } -bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a, - blink::WebCryptoKeyUsageMask b) { - return (a & b) == b; +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(); } -bool IsAlgorithmRsa(blink::WebCryptoAlgorithmId alg_id) { - return alg_id == blink::WebCryptoAlgorithmIdRsaOaep || - alg_id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5; +Status CheckKeyCreationUsages(blink::WebCryptoKeyUsageMask all_possible_usages, + blink::WebCryptoKeyUsageMask actual_usages) { + if (!ContainsKeyUsages(all_possible_usages, actual_usages)) + return Status::ErrorCreateKeyBadUsages(); + return Status::Success(); } -bool IsAlgorithmAsymmetric(blink::WebCryptoAlgorithmId alg_id) { - // TODO(padolph): include all other asymmetric algorithms once they are - // defined, e.g. EC and DH. - return IsAlgorithmRsa(alg_id); +Status GetRsaKeyGenParameters( + const blink::WebCryptoRsaHashedKeyGenParams* params, + unsigned int* public_exponent, + unsigned int* modulus_length_bits) { + *modulus_length_bits = params->modulusLengthBits(); + + // Limit key sizes to those supported by NSS: + // * Multiple of 8 bits + // * 256 bits to 16K bits + if (*modulus_length_bits < 256 || *modulus_length_bits > 16384 || + (*modulus_length_bits % 8) != 0) { + return Status::ErrorGenerateRsaUnsupportedModulus(); + } + + if (!BigIntegerToUint(params->publicExponent().data(), + params->publicExponent().size(), + public_exponent)) { + return Status::ErrorGenerateKeyPublicExponent(); + } + + // OpenSSL hangs when given bad public exponents, whereas NSS simply fails. To + // avoid feeding OpenSSL data that will hang use a whitelist. + if (*public_exponent != 3 && *public_exponent != 65537) + return Status::ErrorGenerateKeyPublicExponent(); + + return Status::Success(); } } // namespace webcrypto diff --git a/chromium/content/child/webcrypto/webcrypto_util.h b/chromium/content/child/webcrypto/webcrypto_util.h index f4a2a6f6528..26b4675f059 100644 --- a/chromium/content/child/webcrypto/webcrypto_util.h +++ b/chromium/content/child/webcrypto/webcrypto_util.h @@ -5,10 +5,9 @@ #ifndef CONTENT_CHILD_WEBCRYPTO_WEBCRYPTO_UTIL_H_ #define CONTENT_CHILD_WEBCRYPTO_WEBCRYPTO_UTIL_H_ +#include <stdint.h> #include <string> -#include <vector> -#include "base/basictypes.h" -#include "base/strings/string_piece.h" + #include "base/values.h" #include "content/common/content_export.h" #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" @@ -20,36 +19,14 @@ namespace webcrypto { class Status; -// Returns a pointer to the start of |data|, or NULL if it is empty. This is a -// convenience function for getting the pointer, and should not be used beyond -// the expected lifetime of |data|. -CONTENT_EXPORT const uint8* Uint8VectorStart(const std::vector<uint8>& data); -CONTENT_EXPORT uint8* Uint8VectorStart(std::vector<uint8>* data); - -// This function decodes unpadded 'base64url' encoded data, as described in -// RFC4648 (http://www.ietf.org/rfc/rfc4648.txt) Section 5. -// In Web Crypto, this type of encoding is only used inside JWK. -CONTENT_EXPORT bool Base64DecodeUrlSafe(const std::string& input, - std::string* output); - -// Returns an unpadded 'base64url' encoding of the input data, the opposite of -// Base64DecodeUrlSafe() above. -CONTENT_EXPORT std::string Base64EncodeUrlSafe(const base::StringPiece& input); -CONTENT_EXPORT std::string Base64EncodeUrlSafe(const std::vector<uint8>& input); - -// Composes a Web Crypto usage mask from an array of JWK key_ops values. -CONTENT_EXPORT Status GetWebCryptoUsagesFromJwkKeyOps( - const base::ListValue* jwk_key_ops_value, - blink::WebCryptoKeyUsageMask* jwk_key_ops_mask); +// Converts a JWK "key_ops" array to the corresponding WebCrypto usages. +CONTENT_EXPORT Status + GetWebCryptoUsagesFromJwkKeyOps(const base::ListValue* key_ops, + blink::WebCryptoKeyUsageMask* usages); // Composes a JWK key_ops array from a Web Crypto usage mask. base::ListValue* CreateJwkKeyOpsFromWebCryptoUsages( - blink::WebCryptoKeyUsageMask usage_mask); - -// Returns the "hash" param for an algorithm if it exists, otherwise returns -// a null algorithm. -blink::WebCryptoAlgorithm GetInnerHashAlgorithm( - const blink::WebCryptoAlgorithm& algorithm); + blink::WebCryptoKeyUsageMask usages); // Creates a WebCryptoAlgorithm without any parameters. CONTENT_EXPORT blink::WebCryptoAlgorithm CreateAlgorithm( @@ -67,16 +44,37 @@ 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 IsAlgorithmRsa(blink::WebCryptoAlgorithmId alg_id); -bool IsAlgorithmAsymmetric(blink::WebCryptoAlgorithmId alg_id); +bool KeyUsageAllows(const blink::WebCryptoKey& key, + const blink::WebCryptoKeyUsage usage); + +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); + +// Extracts the public exponent and modulus length from the Blink parameters. +// On success it is guaranteed that: +// * public_exponent is either 3 or 65537 +// * modulus_length_bits is a multiple of 8 +// * modulus_length is >= 256 +// * modulus_length is <= 16K +Status GetRsaKeyGenParameters( + const blink::WebCryptoRsaHashedKeyGenParams* params, + unsigned int* public_exponent, + unsigned int* modulus_length_bits); } // namespace webcrypto |