summaryrefslogtreecommitdiff
path: root/chromium/content/child/webcrypto
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@theqtcompany.com>2014-12-05 15:04:29 +0100
committerAndras Becsi <andras.becsi@theqtcompany.com>2014-12-09 10:49:28 +0100
commitaf6588f8d723931a298c995fa97259bb7f7deb55 (patch)
tree060ca707847ba1735f01af2372e0d5e494dc0366 /chromium/content/child/webcrypto
parent2fff84d821cc7b1c785f6404e0f8091333283e74 (diff)
downloadqtwebengine-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')
-rw-r--r--chromium/content/child/webcrypto/OWNERS4
-rw-r--r--chromium/content/child/webcrypto/algorithm_dispatch.cc253
-rw-r--r--chromium/content/child/webcrypto/algorithm_dispatch.h97
-rw-r--r--chromium/content/child/webcrypto/algorithm_implementation.cc131
-rw-r--r--chromium/content/child/webcrypto/algorithm_implementation.h157
-rw-r--r--chromium/content/child/webcrypto/algorithm_registry.cc90
-rw-r--r--chromium/content/child/webcrypto/algorithm_registry.h32
-rw-r--r--chromium/content/child/webcrypto/generate_key_result.cc63
-rw-r--r--chromium/content/child/webcrypto/generate_key_result.h60
-rw-r--r--chromium/content/child/webcrypto/jwk.cc1071
-rw-r--r--chromium/content/child/webcrypto/jwk.h229
-rw-r--r--chromium/content/child/webcrypto/nss/aes_cbc_nss.cc128
-rw-r--r--chromium/content/child/webcrypto/nss/aes_gcm_nss.cc191
-rw-r--r--chromium/content/child/webcrypto/nss/aes_key_nss.cc134
-rw-r--r--chromium/content/child/webcrypto/nss/aes_key_nss.h77
-rw-r--r--chromium/content/child/webcrypto/nss/aes_kw_nss.cc206
-rw-r--r--chromium/content/child/webcrypto/nss/hmac_nss.cc240
-rw-r--r--chromium/content/child/webcrypto/nss/key_nss.cc96
-rw-r--r--chromium/content/child/webcrypto/nss/key_nss.h109
-rw-r--r--chromium/content/child/webcrypto/nss/rsa_key_nss.cc852
-rw-r--r--chromium/content/child/webcrypto/nss/rsa_key_nss.h92
-rw-r--r--chromium/content/child/webcrypto/nss/rsa_oaep_nss.cc246
-rw-r--r--chromium/content/child/webcrypto/nss/rsa_ssa_nss.cc144
-rw-r--r--chromium/content/child/webcrypto/nss/sha_nss.cc160
-rw-r--r--chromium/content/child/webcrypto/nss/sym_key_nss.cc94
-rw-r--r--chromium/content/child/webcrypto/nss/sym_key_nss.h39
-rw-r--r--chromium/content/child/webcrypto/nss/util_nss.cc95
-rw-r--r--chromium/content/child/webcrypto/nss/util_nss.h113
-rw-r--r--chromium/content/child/webcrypto/openssl/aes_cbc_openssl.cc139
-rw-r--r--chromium/content/child/webcrypto/openssl/aes_ctr_openssl.cc288
-rw-r--r--chromium/content/child/webcrypto/openssl/aes_gcm_openssl.cc88
-rw-r--r--chromium/content/child/webcrypto/openssl/aes_key_openssl.cc124
-rw-r--r--chromium/content/child/webcrypto/openssl/aes_key_openssl.h67
-rw-r--r--chromium/content/child/webcrypto/openssl/aes_kw_openssl.cc94
-rw-r--r--chromium/content/child/webcrypto/openssl/hmac_openssl.cc216
-rw-r--r--chromium/content/child/webcrypto/openssl/key_openssl.cc73
-rw-r--r--chromium/content/child/webcrypto/openssl/key_openssl.h83
-rw-r--r--chromium/content/child/webcrypto/openssl/rsa_key_openssl.cc389
-rw-r--r--chromium/content/child/webcrypto/openssl/rsa_key_openssl.h83
-rw-r--r--chromium/content/child/webcrypto/openssl/rsa_oaep_openssl.cc153
-rw-r--r--chromium/content/child/webcrypto/openssl/rsa_pss_openssl.cc67
-rw-r--r--chromium/content/child/webcrypto/openssl/rsa_sign_openssl.cc155
-rw-r--r--chromium/content/child/webcrypto/openssl/rsa_sign_openssl.h44
-rw-r--r--chromium/content/child/webcrypto/openssl/rsa_ssa_openssl.cc61
-rw-r--r--chromium/content/child/webcrypto/openssl/sha_openssl.cc140
-rw-r--r--chromium/content/child/webcrypto/openssl/sym_key_openssl.cc60
-rw-r--r--chromium/content/child/webcrypto/openssl/sym_key_openssl.h34
-rw-r--r--chromium/content/child/webcrypto/openssl/util_openssl.cc249
-rw-r--r--chromium/content/child/webcrypto/openssl/util_openssl.h87
-rw-r--r--chromium/content/child/webcrypto/platform_crypto.h283
-rw-r--r--chromium/content/child/webcrypto/platform_crypto_nss.cc1934
-rw-r--r--chromium/content/child/webcrypto/platform_crypto_openssl.cc520
-rw-r--r--chromium/content/child/webcrypto/shared_crypto.cc955
-rw-r--r--chromium/content/child/webcrypto/shared_crypto.h187
-rw-r--r--chromium/content/child/webcrypto/shared_crypto_unittest.cc4399
-rw-r--r--chromium/content/child/webcrypto/status.cc60
-rw-r--r--chromium/content/child/webcrypto/status.h47
-rw-r--r--chromium/content/child/webcrypto/structured_clone.cc136
-rw-r--r--chromium/content/child/webcrypto/structured_clone.h34
-rw-r--r--chromium/content/child/webcrypto/webcrypto_impl.cc126
-rw-r--r--chromium/content/child/webcrypto/webcrypto_impl.h9
-rw-r--r--chromium/content/child/webcrypto/webcrypto_util.cc254
-rw-r--r--chromium/content/child/webcrypto/webcrypto_util.h70
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,
+ &param,
+ 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, &param_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, &param_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,
+ &param,
+ 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,
+ &param,
+ 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,
- &param,
- 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()),
- &param_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()),
- &param_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,
- &param,
- 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,
- &param,
- 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