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