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