From 4b559b4ddffc0b7f688019bcb80658f05e063af7 Mon Sep 17 00:00:00 2001 From: "rvargas@google.com" Date: Thu, 14 Apr 2011 17:37:14 +0000 Subject: Move crypto files out of base, to a top level directory. src/crypto is now an independent project that contains our cryptographic primitives (except md5 and sha1). This removes the base dependency from nss, openssl and sqlite. BUG=76996 TEST=none Review URL: http://codereview.chromium.org/6805019 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@81611 0039d316-1c4b-4281-b951-d872f2087c98 --- crypto/OWNERS | 5 + crypto/capi_util.cc | 49 + crypto/capi_util.h | 32 + crypto/crypto.gyp | 223 ++++ crypto/crypto_module_blocking_password_delegate.h | 34 + crypto/cssm_init.cc | 204 +++ crypto/cssm_init.h | 60 + crypto/encryptor.h | 69 + crypto/encryptor_mac.cc | 76 ++ crypto/encryptor_nss.cc | 125 ++ crypto/encryptor_openssl.cc | 127 ++ crypto/encryptor_unittest.cc | 233 ++++ crypto/encryptor_win.cc | 115 ++ crypto/hmac.h | 60 + crypto/hmac_mac.cc | 73 ++ crypto/hmac_nss.cc | 117 ++ crypto/hmac_openssl.cc | 57 + crypto/hmac_unittest.cc | 236 ++++ crypto/hmac_win.cc | 197 +++ crypto/mac_security_services_lock.cc | 42 + crypto/mac_security_services_lock.h | 25 + crypto/nss_util.cc | 706 +++++++++++ crypto/nss_util.h | 138 ++ crypto/nss_util_internal.h | 30 + crypto/openssl_util.cc | 113 ++ crypto/openssl_util.h | 113 ++ crypto/rsa_private_key.cc | 390 ++++++ crypto/rsa_private_key.h | 273 ++++ crypto/rsa_private_key_mac.cc | 196 +++ crypto/rsa_private_key_nss.cc | 248 ++++ crypto/rsa_private_key_nss_unittest.cc | 64 + crypto/rsa_private_key_openssl.cc | 135 ++ crypto/rsa_private_key_unittest.cc | 386 ++++++ crypto/rsa_private_key_win.cc | 229 ++++ crypto/run_all_unittests.cc | 17 + crypto/scoped_capi_types.h | 125 ++ crypto/scoped_nss_types.h | 52 + crypto/secure_hash.h | 36 + crypto/secure_hash_default.cc | 49 + crypto/secure_hash_openssl.cc | 53 + crypto/secure_hash_unittest.cc | 34 + crypto/sha2.cc | 25 + crypto/sha2.h | 33 + crypto/sha2_unittest.cc | 100 ++ crypto/signature_creator.h | 69 + crypto/signature_creator_mac.cc | 74 ++ crypto/signature_creator_nss.cc | 76 ++ crypto/signature_creator_openssl.cc | 54 + crypto/signature_creator_unittest.cc | 53 + crypto/signature_creator_win.cc | 60 + crypto/signature_verifier.h | 108 ++ crypto/signature_verifier_mac.cc | 105 ++ crypto/signature_verifier_nss.cc | 113 ++ crypto/signature_verifier_openssl.cc | 94 ++ crypto/signature_verifier_unittest.cc | 268 ++++ crypto/signature_verifier_win.cc | 134 ++ crypto/symmetric_key.h | 104 ++ crypto/symmetric_key_mac.cc | 155 +++ crypto/symmetric_key_nss.cc | 127 ++ crypto/symmetric_key_openssl.cc | 76 ++ crypto/symmetric_key_unittest.cc | 225 ++++ crypto/symmetric_key_win.cc | 536 ++++++++ crypto/third_party/nss/LICENSE | 35 + crypto/third_party/nss/README.chromium | 13 + crypto/third_party/nss/blapi.h | 101 ++ crypto/third_party/nss/blapit.h | 91 ++ crypto/third_party/nss/sha256.h | 51 + crypto/third_party/nss/sha512.cc | 1391 +++++++++++++++++++++ 68 files changed, 9817 insertions(+) create mode 100644 crypto/OWNERS create mode 100644 crypto/capi_util.cc create mode 100644 crypto/capi_util.h create mode 100644 crypto/crypto.gyp create mode 100644 crypto/crypto_module_blocking_password_delegate.h create mode 100644 crypto/cssm_init.cc create mode 100644 crypto/cssm_init.h create mode 100644 crypto/encryptor.h create mode 100644 crypto/encryptor_mac.cc create mode 100644 crypto/encryptor_nss.cc create mode 100644 crypto/encryptor_openssl.cc create mode 100644 crypto/encryptor_unittest.cc create mode 100644 crypto/encryptor_win.cc create mode 100644 crypto/hmac.h create mode 100644 crypto/hmac_mac.cc create mode 100644 crypto/hmac_nss.cc create mode 100644 crypto/hmac_openssl.cc create mode 100644 crypto/hmac_unittest.cc create mode 100644 crypto/hmac_win.cc create mode 100644 crypto/mac_security_services_lock.cc create mode 100644 crypto/mac_security_services_lock.h create mode 100644 crypto/nss_util.cc create mode 100644 crypto/nss_util.h create mode 100644 crypto/nss_util_internal.h create mode 100644 crypto/openssl_util.cc create mode 100644 crypto/openssl_util.h create mode 100644 crypto/rsa_private_key.cc create mode 100644 crypto/rsa_private_key.h create mode 100644 crypto/rsa_private_key_mac.cc create mode 100644 crypto/rsa_private_key_nss.cc create mode 100644 crypto/rsa_private_key_nss_unittest.cc create mode 100644 crypto/rsa_private_key_openssl.cc create mode 100644 crypto/rsa_private_key_unittest.cc create mode 100644 crypto/rsa_private_key_win.cc create mode 100644 crypto/run_all_unittests.cc create mode 100644 crypto/scoped_capi_types.h create mode 100644 crypto/scoped_nss_types.h create mode 100644 crypto/secure_hash.h create mode 100644 crypto/secure_hash_default.cc create mode 100644 crypto/secure_hash_openssl.cc create mode 100644 crypto/secure_hash_unittest.cc create mode 100644 crypto/sha2.cc create mode 100644 crypto/sha2.h create mode 100644 crypto/sha2_unittest.cc create mode 100644 crypto/signature_creator.h create mode 100644 crypto/signature_creator_mac.cc create mode 100644 crypto/signature_creator_nss.cc create mode 100644 crypto/signature_creator_openssl.cc create mode 100644 crypto/signature_creator_unittest.cc create mode 100644 crypto/signature_creator_win.cc create mode 100644 crypto/signature_verifier.h create mode 100644 crypto/signature_verifier_mac.cc create mode 100644 crypto/signature_verifier_nss.cc create mode 100644 crypto/signature_verifier_openssl.cc create mode 100644 crypto/signature_verifier_unittest.cc create mode 100644 crypto/signature_verifier_win.cc create mode 100644 crypto/symmetric_key.h create mode 100644 crypto/symmetric_key_mac.cc create mode 100644 crypto/symmetric_key_nss.cc create mode 100644 crypto/symmetric_key_openssl.cc create mode 100644 crypto/symmetric_key_unittest.cc create mode 100644 crypto/symmetric_key_win.cc create mode 100644 crypto/third_party/nss/LICENSE create mode 100644 crypto/third_party/nss/README.chromium create mode 100644 crypto/third_party/nss/blapi.h create mode 100644 crypto/third_party/nss/blapit.h create mode 100644 crypto/third_party/nss/sha256.h create mode 100644 crypto/third_party/nss/sha512.cc (limited to 'crypto') diff --git a/crypto/OWNERS b/crypto/OWNERS new file mode 100644 index 0000000..d93b7ea --- /dev/null +++ b/crypto/OWNERS @@ -0,0 +1,5 @@ +set noparent +agl@chromium.org +rsleevi@chromium.org +rvargas@chromium.org +wtc@chromium.org diff --git a/crypto/capi_util.cc b/crypto/capi_util.cc new file mode 100644 index 0000000..7593f39 --- /dev/null +++ b/crypto/capi_util.cc @@ -0,0 +1,49 @@ +// Copyright (c) 2011 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 "crypto/capi_util.h" + +#include "base/basictypes.h" +#include "base/memory/singleton.h" +#include "base/synchronization/lock.h" + +namespace { + +class CAPIUtilSingleton { + public: + static CAPIUtilSingleton* GetInstance() { + return Singleton::get(); + } + + // Returns a lock to guard calls to CryptAcquireContext with + // CRYPT_DELETEKEYSET or CRYPT_NEWKEYSET. + base::Lock& acquire_context_lock() { + return acquire_context_lock_; + } + + private: + friend class Singleton; + friend struct DefaultSingletonTraits; + + CAPIUtilSingleton() {} + + base::Lock acquire_context_lock_; + + DISALLOW_COPY_AND_ASSIGN(CAPIUtilSingleton); +}; + +} // namespace + +namespace crypto { + +BOOL CryptAcquireContextLocked(HCRYPTPROV* prov, + LPCWSTR container, + LPCWSTR provider, + DWORD prov_type, + DWORD flags) { + base::AutoLock lock(CAPIUtilSingleton::GetInstance()->acquire_context_lock()); + return CryptAcquireContext(prov, container, provider, prov_type, flags); +} + +} // namespace crypto diff --git a/crypto/capi_util.h b/crypto/capi_util.h new file mode 100644 index 0000000..faaf012 --- /dev/null +++ b/crypto/capi_util.h @@ -0,0 +1,32 @@ +// Copyright (c) 2011 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 CRYPTO_CAPI_UTIL_H_ +#define CRYPTO_CAPI_UTIL_H_ +#pragma once + +#include +#include + +namespace crypto { + +// CryptAcquireContext when passed CRYPT_NEWKEYSET or CRYPT_DELETEKEYSET in +// flags is not thread-safe. For such calls, we create a global lock to +// synchronize it. +// +// From "Threading Issues with Cryptographic Service Providers", +// : +// +// "The CryptAcquireContext function is generally thread safe unless +// CRYPT_NEWKEYSET or CRYPT_DELETEKEYSET is specified in the dwFlags +// parameter." +BOOL CryptAcquireContextLocked(HCRYPTPROV* prov, + LPCWSTR container, + LPCWSTR provider, + DWORD prov_type, + DWORD flags); + +} // namespace crypto + +#endif // CRYPTO_CAPI_UTIL_H_ diff --git a/crypto/crypto.gyp b/crypto/crypto.gyp new file mode 100644 index 0000000..9c861cf --- /dev/null +++ b/crypto/crypto.gyp @@ -0,0 +1,223 @@ +# Copyright (c) 2011 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. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'targets': [ + { + 'target_name': 'crypto', + 'type': '<(library)', + 'dependencies': [ + '../base/base.gyp:base', + ], + 'msvs_disabled_warnings': [ + 4018, + ], + 'conditions': [ + [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', { + 'conditions': [ + [ 'chromeos==1', { + 'sources/': [ ['include', '_chromeos\\.cc$'] ] + }, + ], + [ 'use_openssl==1', { + 'dependencies': [ + '../third_party/openssl/openssl.gyp:openssl', + ], + }, { # use_openssl==0 + 'dependencies': [ + '../build/linux/system.gyp:nss', + ], + 'export_dependent_settings': [ + '../build/linux/system.gyp:nss', + ], + } + ], + ], + }, { # OS != "linux" and OS != "freebsd" and OS != "openbsd" and OS != "solaris" + 'sources/': [ + ['exclude', '_nss\.cc$'], + ], + }], + [ 'OS == "freebsd" or OS == "openbsd"', { + 'link_settings': { + 'libraries': [ + '-L/usr/local/lib -lexecinfo', + ], + }, + }, + ], + [ 'OS == "mac"', { + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/Security.framework', + ], + }, + }, { # OS != "mac" + 'sources!': [ + 'cssm_init.cc', + 'cssm_init.h', + 'mac_security_services_lock.cc', + 'mac_security_services_lock.h', + ], + }], + [ 'OS == "mac" or OS == "win"', { + 'dependencies': [ + '../third_party/nss/nss.gyp:nss', + ], + },], + [ 'OS != "win"', { + 'sources!': [ + 'capi_util.h', + 'capi_util.cc', + ], + },], + [ 'use_openssl==1', { + # TODO(joth): Use a glob to match exclude patterns once the + # OpenSSL file set is complete. + 'sources!': [ + 'encryptor_nss.cc', + 'hmac_nss.cc', + 'nss_util.cc', + 'nss_util.h', + 'rsa_private_key_nss.cc', + 'secure_hash_default.cc', + 'signature_creator_nss.cc', + 'signature_verifier_nss.cc', + 'symmetric_key_nss.cc', + 'third_party/nss/blapi.h', + 'third_party/nss/blapit.h', + 'third_party/nss/sha256.h', + 'third_party/nss/sha512.cc', + ], + }, { + 'sources!': [ + 'encryptor_openssl.cc', + 'hmac_openssl.cc', + 'openssl_util.cc', + 'openssl_util.h', + 'rsa_private_key_openssl.cc', + 'secure_hash_openssl.cc', + 'signature_creator_openssl.cc', + 'signature_verifier_openssl.cc', + 'symmetric_key_openssl.cc', + ], + },], + ], + 'sources': [ + 'capi_util.cc', + 'capi_util.h', + 'crypto_module_blocking_password_delegate.h', + 'cssm_init.cc', + 'cssm_init.h', + 'encryptor.h', + 'encryptor_mac.cc', + 'encryptor_nss.cc', + 'encryptor_openssl.cc', + 'encryptor_win.cc', + 'hmac.h', + 'hmac_mac.cc', + 'hmac_nss.cc', + 'hmac_openssl.cc', + 'hmac_win.cc', + 'mac_security_services_lock.cc', + 'mac_security_services_lock.h', + 'openssl_util.cc', + 'openssl_util.h', + 'nss_util.cc', + 'nss_util.h', + 'nss_util_internal.h', + 'rsa_private_key.h', + 'rsa_private_key.cc', + 'rsa_private_key_mac.cc', + 'rsa_private_key_nss.cc', + 'rsa_private_key_openssl.cc', + 'rsa_private_key_win.cc', + 'scoped_capi_types.h', + 'scoped_nss_types.h', + 'secure_hash.h', + 'secure_hash_default.cc', + 'secure_hash_openssl.cc', + 'sha2.cc', + 'sha2.h', + 'signature_creator.h', + 'signature_creator_mac.cc', + 'signature_creator_nss.cc', + 'signature_creator_openssl.cc', + 'signature_creator_win.cc', + 'signature_verifier.h', + 'signature_verifier_mac.cc', + 'signature_verifier_nss.cc', + 'signature_verifier_openssl.cc', + 'signature_verifier_win.cc', + 'symmetric_key.h', + 'symmetric_key_mac.cc', + 'symmetric_key_nss.cc', + 'symmetric_key_openssl.cc', + 'symmetric_key_win.cc', + 'third_party/nss/blapi.h', + 'third_party/nss/blapit.h', + 'third_party/nss/sha256.h', + 'third_party/nss/sha512.cc', + ], + }, + { + 'target_name': 'crypto_unittests', + 'type': 'executable', + 'sources': [ + # Infrastructure files. + 'run_all_unittests.cc', + + # Tests. + 'encryptor_unittest.cc', + 'hmac_unittest.cc', + 'rsa_private_key_unittest.cc', + 'rsa_private_key_nss_unittest.cc', + 'secure_hash_unittest.cc', + 'sha2_unittest.cc', + 'signature_creator_unittest.cc', + 'signature_verifier_unittest.cc', + 'symmetric_key_unittest.cc', + ], + 'dependencies': [ + 'crypto', + '../base/base.gyp:base', + '../base/base.gyp:test_support_base', + '../testing/gmock.gyp:gmock', + '../testing/gtest.gyp:gtest', + ], + 'conditions': [ + [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', { + 'conditions': [ + [ 'linux_use_tcmalloc==1', { + 'dependencies': [ + '../base/allocator/allocator.gyp:allocator', + ], + }, + ], + ], + 'dependencies': [ + '../build/linux/system.gyp:nss', + ], + }, { # OS != "linux" and OS != "freebsd" and OS != "openbsd" and OS != "solaris" + 'sources!': [ + 'rsa_private_key_nss_unittest.cc', + ] + }], + [ 'OS == "mac" or OS == "win"', { + 'dependencies': [ + '../third_party/nss/nss.gyp:nss', + ], + }], + [ 'use_openssl==1', { + 'sources!': [ + 'rsa_private_key_nss_unittest.cc', + ], + }], + ], + }, + ], +} diff --git a/crypto/crypto_module_blocking_password_delegate.h b/crypto/crypto_module_blocking_password_delegate.h new file mode 100644 index 0000000..847f484 --- /dev/null +++ b/crypto/crypto_module_blocking_password_delegate.h @@ -0,0 +1,34 @@ +// Copyright (c) 2011 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 CRYPTO_CRYPTO_MODULE_BLOCKING_PASSWORD_DELEGATE_H_ +#define CRYPTO_CRYPTO_MODULE_BLOCKING_PASSWORD_DELEGATE_H_ +#pragma once + +#include + +namespace crypto { + +// PK11_SetPasswordFunc is a global setting. An implementation of +// CryptoModuleBlockingPasswordDelegate should be passed as the user data +// argument (|wincx|) to relevant NSS functions, which the global password +// handler will call to do the actual work. +class CryptoModuleBlockingPasswordDelegate { + public: + virtual ~CryptoModuleBlockingPasswordDelegate() {} + + // Requests a password to unlock |slot_name|. The interface is + // synchronous because NSS cannot issue an asynchronous + // request. |retry| is true if this is a request for the retry + // and we previously returned the wrong password. + // The implementation should set |*cancelled| to true if the user cancelled + // instead of entering a password, otherwise it should return the password the + // user entered. + virtual std::string RequestPassword(const std::string& slot_name, bool retry, + bool* cancelled) = 0; +}; + +} // namespace crypto + +#endif // CRYPTO_CRYPTO_MODULE_BLOCKING_PASSWORD_DELEGATE_H_ diff --git a/crypto/cssm_init.cc b/crypto/cssm_init.cc new file mode 100644 index 0000000..5a5e3cc --- /dev/null +++ b/crypto/cssm_init.cc @@ -0,0 +1,204 @@ +// Copyright (c) 2011 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 "crypto/cssm_init.h" + +#include + +#include "base/logging.h" +#include "base/mac/scoped_cftyperef.h" +#include "base/memory/singleton.h" +#include "base/sys_string_conversions.h" + +// When writing crypto code for Mac OS X, you may find the following +// documentation useful: +// - Common Security: CDSA and CSSM, Version 2 (with corrigenda) +// http://www.opengroup.org/security/cdsa.htm +// - Apple Cryptographic Service Provider Functional Specification +// - CryptoSample: http://developer.apple.com/SampleCode/CryptoSample/ + +namespace { + +void* CSSMMalloc(CSSM_SIZE size, void* alloc_ref) { + return malloc(size); +} + +void CSSMFree(void* mem_ptr, void* alloc_ref) { + free(mem_ptr); +} + +void* CSSMRealloc(void* ptr, CSSM_SIZE size, void* alloc_ref) { + return realloc(ptr, size); +} + +void* CSSMCalloc(uint32 num, CSSM_SIZE size, void* alloc_ref) { + return calloc(num, size); +} + +class CSSMInitSingleton { + public: + static CSSMInitSingleton* GetInstance() { + return Singleton >::get(); + } + + CSSM_CSP_HANDLE csp_handle() const { return csp_handle_; } + CSSM_CL_HANDLE cl_handle() const { return cl_handle_; } + CSSM_TP_HANDLE tp_handle() const { return tp_handle_; } + + private: + CSSMInitSingleton() + : inited_(false), csp_loaded_(false), cl_loaded_(false), + tp_loaded_(false), csp_handle_(NULL), cl_handle_(NULL), + tp_handle_(NULL) { + static CSSM_VERSION version = {2, 0}; + // TODO(wtc): what should our caller GUID be? + static const CSSM_GUID test_guid = { + 0xFADE, 0, 0, { 1, 2, 3, 4, 5, 6, 7, 0 } + }; + CSSM_RETURN crtn; + CSSM_PVC_MODE pvc_policy = CSSM_PVC_NONE; + crtn = CSSM_Init(&version, CSSM_PRIVILEGE_SCOPE_NONE, &test_guid, + CSSM_KEY_HIERARCHY_NONE, &pvc_policy, NULL); + if (crtn) { + NOTREACHED(); + return; + } + inited_ = true; + + crtn = CSSM_ModuleLoad(&gGuidAppleCSP, CSSM_KEY_HIERARCHY_NONE, NULL, NULL); + if (crtn) { + NOTREACHED(); + return; + } + csp_loaded_ = true; + crtn = CSSM_ModuleLoad( + &gGuidAppleX509CL, CSSM_KEY_HIERARCHY_NONE, NULL, NULL); + if (crtn) { + NOTREACHED(); + return; + } + cl_loaded_ = true; + crtn = CSSM_ModuleLoad( + &gGuidAppleX509TP, CSSM_KEY_HIERARCHY_NONE, NULL, NULL); + if (crtn) { + NOTREACHED(); + return; + } + tp_loaded_ = true; + + const CSSM_API_MEMORY_FUNCS cssmMemoryFunctions = { + CSSMMalloc, + CSSMFree, + CSSMRealloc, + CSSMCalloc, + NULL + }; + + crtn = CSSM_ModuleAttach(&gGuidAppleCSP, &version, &cssmMemoryFunctions, 0, + CSSM_SERVICE_CSP, 0, CSSM_KEY_HIERARCHY_NONE, + NULL, 0, NULL, &csp_handle_); + DCHECK(crtn == CSSM_OK); + crtn = CSSM_ModuleAttach(&gGuidAppleX509CL, &version, &cssmMemoryFunctions, + 0, CSSM_SERVICE_CL, 0, CSSM_KEY_HIERARCHY_NONE, + NULL, 0, NULL, &cl_handle_); + DCHECK(crtn == CSSM_OK); + crtn = CSSM_ModuleAttach(&gGuidAppleX509TP, &version, &cssmMemoryFunctions, + 0, CSSM_SERVICE_TP, 0, CSSM_KEY_HIERARCHY_NONE, + NULL, 0, NULL, &tp_handle_); + DCHECK(crtn == CSSM_OK); + } + + ~CSSMInitSingleton() { + CSSM_RETURN crtn; + if (csp_handle_) { + CSSM_RETURN crtn = CSSM_ModuleDetach(csp_handle_); + DCHECK(crtn == CSSM_OK); + } + if (cl_handle_) { + CSSM_RETURN crtn = CSSM_ModuleDetach(cl_handle_); + DCHECK(crtn == CSSM_OK); + } + if (tp_handle_) { + CSSM_RETURN crtn = CSSM_ModuleDetach(tp_handle_); + DCHECK(crtn == CSSM_OK); + } + if (csp_loaded_) { + crtn = CSSM_ModuleUnload(&gGuidAppleCSP, NULL, NULL); + DCHECK(crtn == CSSM_OK); + } + if (cl_loaded_) { + crtn = CSSM_ModuleUnload(&gGuidAppleX509CL, NULL, NULL); + DCHECK(crtn == CSSM_OK); + } + if (tp_loaded_) { + crtn = CSSM_ModuleUnload(&gGuidAppleX509TP, NULL, NULL); + DCHECK(crtn == CSSM_OK); + } + if (inited_) { + crtn = CSSM_Terminate(); + DCHECK(crtn == CSSM_OK); + } + } + + bool inited_; // True if CSSM_Init has been called successfully. + bool csp_loaded_; // True if gGuidAppleCSP has been loaded + bool cl_loaded_; // True if gGuidAppleX509CL has been loaded. + bool tp_loaded_; // True if gGuidAppleX509TP has been loaded. + CSSM_CSP_HANDLE csp_handle_; + CSSM_CL_HANDLE cl_handle_; + CSSM_TP_HANDLE tp_handle_; + + friend struct DefaultSingletonTraits; +}; + +} // namespace + +namespace crypto { + +void EnsureCSSMInit() { + CSSMInitSingleton::GetInstance(); +} + +CSSM_CSP_HANDLE GetSharedCSPHandle() { + return CSSMInitSingleton::GetInstance()->csp_handle(); +} + +CSSM_CL_HANDLE GetSharedCLHandle() { + return CSSMInitSingleton::GetInstance()->cl_handle(); +} + +CSSM_TP_HANDLE GetSharedTPHandle() { + return CSSMInitSingleton::GetInstance()->tp_handle(); +} + +void* CSSMMalloc(CSSM_SIZE size) { + return ::CSSMMalloc(size, NULL); +} + +void CSSMFree(void* ptr) { + ::CSSMFree(ptr, NULL); +} + +void LogCSSMError(const char* fn_name, CSSM_RETURN err) { + if (!err) + return; + base::mac::ScopedCFTypeRef cfstr( + SecCopyErrorMessageString(err, NULL)); + LOG(ERROR) << fn_name << " returned " << err + << " (" << base::SysCFStringRefToUTF8(cfstr) << ")"; +} + +ScopedCSSMData::ScopedCSSMData() { + memset(&data_, 0, sizeof(data_)); +} + +ScopedCSSMData::~ScopedCSSMData() { + if (data_.Data) { + CSSMFree(data_.Data); + data_.Data = NULL; + } +} + +} // namespace crypto diff --git a/crypto/cssm_init.h b/crypto/cssm_init.h new file mode 100644 index 0000000..9093b0f --- /dev/null +++ b/crypto/cssm_init.h @@ -0,0 +1,60 @@ +// Copyright (c) 2011 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 CRYPTO_CSSM_INIT_H_ +#define CRYPTO_CSSM_INIT_H_ +#pragma once + +#include + +#include "base/basictypes.h" + +namespace crypto { + +// Initialize CSSM if it isn't already initialized. This must be called before +// any other CSSM functions. This function is thread-safe, and CSSM will only +// ever be initialized once. CSSM will be properly shut down on program exit. +void EnsureCSSMInit(); + +// Returns the shared CSP handle used by CSSM functions. +CSSM_CSP_HANDLE GetSharedCSPHandle(); + +// Returns the shared CL handle used by CSSM functions. +CSSM_CL_HANDLE GetSharedCLHandle(); + +// Returns the shared TP handle used by CSSM functions. +CSSM_TP_HANDLE GetSharedTPHandle(); + +// Set of pointers to memory function wrappers that are required for CSSM +extern const CSSM_API_MEMORY_FUNCS kCssmMemoryFunctions; + +// Utility function to log an error message including the error name. +void LogCSSMError(const char *function_name, CSSM_RETURN err); + +// Utility functions to allocate and release CSSM memory. +void* CSSMMalloc(CSSM_SIZE size); +void CSSMFree(void* ptr); + +// Wrapper class for CSSM_DATA type. This should only be used when using the +// CL/TP/CSP handles from above, since that's the only time we're guaranteed (or +// supposed to be guaranteed) that our memory management functions will be used. +// Apple's Sec* APIs manage their own memory so it shouldn't be used for those. +// The constructor initializes data_ to zero and the destructor releases the +// data properly. +class ScopedCSSMData { + public: + ScopedCSSMData(); + ~ScopedCSSMData(); + operator CSSM_DATA*() { return &data_; } + CSSM_DATA* operator ->() { return &data_; } + + private: + CSSM_DATA data_; + + DISALLOW_COPY_AND_ASSIGN(ScopedCSSMData); +}; + +} // namespace crypto + +#endif // CRYPTO_CSSM_INIT_H_ diff --git a/crypto/encryptor.h b/crypto/encryptor.h new file mode 100644 index 0000000..d8250f6 --- /dev/null +++ b/crypto/encryptor.h @@ -0,0 +1,69 @@ +// Copyright (c) 2011 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 CRYPTO_ENCRYPTOR_H_ +#define CRYPTO_ENCRYPTOR_H_ +#pragma once + +#include + +#include "build/build_config.h" + +#if defined(USE_NSS) +#include "crypto/scoped_nss_types.h" +#elif defined(OS_WIN) +#include "crypto/scoped_capi_types.h" +#endif + +namespace crypto { + +class SymmetricKey; + +class Encryptor { + public: + enum Mode { + CBC + }; + Encryptor(); + virtual ~Encryptor(); + + // Initializes the encryptor using |key| and |iv|. Returns false if either the + // key or the initialization vector cannot be used. + bool Init(SymmetricKey* key, Mode mode, const std::string& iv); + + // Encrypts |plaintext| into |ciphertext|. + bool Encrypt(const std::string& plaintext, std::string* ciphertext); + + // Decrypts |ciphertext| into |plaintext|. + bool Decrypt(const std::string& ciphertext, std::string* plaintext); + + // TODO(albertb): Support streaming encryption. + + private: + SymmetricKey* key_; + Mode mode_; + +#if defined(USE_OPENSSL) + bool Crypt(bool encrypt, // Pass true to encrypt, false to decrypt. + const std::string& input, + std::string* output); + std::string iv_; +#elif defined(USE_NSS) + ScopedPK11Slot slot_; + ScopedSECItem param_; +#elif defined(OS_MACOSX) + bool Crypt(int /*CCOperation*/ op, + const std::string& input, + std::string* output); + + std::string iv_; +#elif defined(OS_WIN) + ScopedHCRYPTKEY capi_key_; + DWORD block_size_; +#endif +}; + +} // namespace crypto + +#endif // CRYPTO_ENCRYPTOR_H_ diff --git a/crypto/encryptor_mac.cc b/crypto/encryptor_mac.cc new file mode 100644 index 0000000..ff6e019 --- /dev/null +++ b/crypto/encryptor_mac.cc @@ -0,0 +1,76 @@ +// Copyright (c) 2011 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 "crypto/encryptor.h" + +#include + +#include "base/logging.h" +#include "base/string_util.h" +#include "crypto/symmetric_key.h" + +namespace crypto { + +Encryptor::Encryptor() + : key_(NULL), + mode_(CBC) { +} + +Encryptor::~Encryptor() { +} + +bool Encryptor::Init(SymmetricKey* key, Mode mode, const std::string& iv) { + DCHECK(key); + DCHECK_EQ(CBC, mode) << "Unsupported mode of operation"; + CSSM_DATA raw_key = key->cssm_data(); + if (raw_key.Length != kCCKeySizeAES128 && + raw_key.Length != kCCKeySizeAES192 && + raw_key.Length != kCCKeySizeAES256) + return false; + if (iv.size() != kCCBlockSizeAES128) + return false; + + key_ = key; + mode_ = mode; + iv_ = iv; + return true; +} + +bool Encryptor::Crypt(int /*CCOperation*/ op, + const std::string& input, + std::string* output) { + DCHECK(key_); + CSSM_DATA raw_key = key_->cssm_data(); + // CommonCryptor.h: "A general rule for the size of the output buffer which + // must be provided by the caller is that for block ciphers, the output + // length is never larger than the input length plus the block size." + + size_t output_size = input.size() + iv_.size(); + CCCryptorStatus err = CCCrypt(op, + kCCAlgorithmAES128, + kCCOptionPKCS7Padding, + raw_key.Data, raw_key.Length, + iv_.data(), + input.data(), input.size(), + WriteInto(output, output_size+1), + output_size, + &output_size); + if (err) { + output->resize(0); + LOG(ERROR) << "CCCrypt returned " << err; + return false; + } + output->resize(output_size); + return true; +} + +bool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) { + return Crypt(kCCEncrypt, plaintext, ciphertext); +} + +bool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) { + return Crypt(kCCDecrypt, ciphertext, plaintext); +} + +} // namespace crypto diff --git a/crypto/encryptor_nss.cc b/crypto/encryptor_nss.cc new file mode 100644 index 0000000..aaa6626 --- /dev/null +++ b/crypto/encryptor_nss.cc @@ -0,0 +1,125 @@ +// Copyright (c) 2011 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 "crypto/encryptor.h" + +#include +#include + +#include "base/logging.h" +#include "crypto/nss_util.h" +#include "crypto/symmetric_key.h" + +namespace crypto { + +Encryptor::Encryptor() + : key_(NULL), + mode_(CBC) { + EnsureNSSInit(); +} + +Encryptor::~Encryptor() { +} + +bool Encryptor::Init(SymmetricKey* key, Mode mode, const std::string& iv) { + DCHECK(key); + DCHECK_EQ(CBC, mode); + + key_ = key; + mode_ = mode; + + if (iv.size() != AES_BLOCK_SIZE) + return false; + + slot_.reset(PK11_GetBestSlot(CKM_AES_CBC_PAD, NULL)); + if (!slot_.get()) + return false; + + SECItem iv_item; + iv_item.type = siBuffer; + iv_item.data = reinterpret_cast( + const_cast(iv.data())); + iv_item.len = iv.size(); + + param_.reset(PK11_ParamFromIV(CKM_AES_CBC_PAD, &iv_item)); + if (!param_.get()) + return false; + + return true; +} + +bool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) { + ScopedPK11Context context(PK11_CreateContextBySymKey(CKM_AES_CBC_PAD, + CKA_ENCRYPT, + key_->key(), + param_.get())); + if (!context.get()) + return false; + + size_t ciphertext_len = plaintext.size() + AES_BLOCK_SIZE; + std::vector buffer(ciphertext_len); + + int op_len; + SECStatus rv = PK11_CipherOp(context.get(), + &buffer[0], + &op_len, + ciphertext_len, + reinterpret_cast( + const_cast(plaintext.data())), + plaintext.size()); + if (SECSuccess != rv) + return false; + + unsigned int digest_len; + rv = PK11_DigestFinal(context.get(), + &buffer[op_len], + &digest_len, + ciphertext_len - op_len); + if (SECSuccess != rv) + return false; + + ciphertext->assign(reinterpret_cast(&buffer[0]), + op_len + digest_len); + return true; +} + +bool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) { + if (ciphertext.empty()) + return false; + + ScopedPK11Context context(PK11_CreateContextBySymKey(CKM_AES_CBC_PAD, + CKA_DECRYPT, + key_->key(), + param_.get())); + if (!context.get()) + return false; + + size_t plaintext_len = ciphertext.size(); + std::vector buffer(plaintext_len); + + int op_len; + SECStatus rv = PK11_CipherOp(context.get(), + &buffer[0], + &op_len, + plaintext_len, + reinterpret_cast( + const_cast(ciphertext.data())), + ciphertext.size()); + if (SECSuccess != rv) + return false; + + unsigned int digest_len; + rv = PK11_DigestFinal(context.get(), + &buffer[op_len], + &digest_len, + plaintext_len - op_len); + if (SECSuccess != rv) + return false; + + plaintext->assign(reinterpret_cast(&buffer[0]), + op_len + digest_len); + return true; +} + +} // namespace crypto diff --git a/crypto/encryptor_openssl.cc b/crypto/encryptor_openssl.cc new file mode 100644 index 0000000..7b1e13f --- /dev/null +++ b/crypto/encryptor_openssl.cc @@ -0,0 +1,127 @@ +// Copyright (c) 2011 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 "crypto/encryptor.h" + +#include +#include + +#include "base/logging.h" +#include "base/string_util.h" +#include "crypto/openssl_util.h" +#include "crypto/symmetric_key.h" + +namespace crypto { + +namespace { + +const EVP_CIPHER* GetCipherForKey(SymmetricKey* key) { + switch (key->key().length()) { + case 16: return EVP_aes_128_cbc(); + case 24: return EVP_aes_192_cbc(); + case 32: return EVP_aes_256_cbc(); + default: return NULL; + } +} + +// On destruction this class will cleanup the ctx, and also clear the OpenSSL +// ERR stack as a convenience. +class ScopedCipherCTX { + public: + explicit ScopedCipherCTX() { + EVP_CIPHER_CTX_init(&ctx_); + } + ~ScopedCipherCTX() { + EVP_CIPHER_CTX_cleanup(&ctx_); + ClearOpenSSLERRStack(FROM_HERE); + } + EVP_CIPHER_CTX* get() { return &ctx_; } + + private: + EVP_CIPHER_CTX ctx_; +}; + +} // namespace + +Encryptor::Encryptor() + : key_(NULL), + mode_(CBC) { +} + +Encryptor::~Encryptor() { +} + +bool Encryptor::Init(SymmetricKey* key, Mode mode, const std::string& iv) { + DCHECK(key); + DCHECK_EQ(CBC, mode); + + EnsureOpenSSLInit(); + if (iv.size() != AES_BLOCK_SIZE) + return false; + + if (GetCipherForKey(key) == NULL) + return false; + + key_ = key; + mode_ = mode; + iv_ = iv; + return true; +} + +bool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) { + return Crypt(true, plaintext, ciphertext); +} + +bool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) { + return Crypt(false, ciphertext, plaintext); +} + +bool Encryptor::Crypt(bool do_encrypt, + const std::string& input, + std::string* output) { + DCHECK(key_); // Must call Init() before En/De-crypt. + // Work on the result in a local variable, and then only transfer it to + // |output| on success to ensure no partial data is returned. + std::string result; + output->swap(result); + + const EVP_CIPHER* cipher = GetCipherForKey(key_); + DCHECK(cipher); // Already handled in Init(); + + const std::string& key = key_->key(); + DCHECK_EQ(EVP_CIPHER_iv_length(cipher), static_cast(iv_.length())); + DCHECK_EQ(EVP_CIPHER_key_length(cipher), static_cast(key.length())); + + ScopedCipherCTX ctx; + if (!EVP_CipherInit_ex(ctx.get(), cipher, NULL, + reinterpret_cast(key.data()), + reinterpret_cast(iv_.data()), + do_encrypt)) + return false; + + // When encrypting, add another block size of space to allow for any padding. + const size_t output_size = input.size() + (do_encrypt ? iv_.size() : 0); + uint8* out_ptr = reinterpret_cast(WriteInto(&result, + output_size + 1)); + int out_len; + if (!EVP_CipherUpdate(ctx.get(), out_ptr, &out_len, + reinterpret_cast(input.data()), + input.length())) + return false; + + // Write out the final block plus padding (if any) to the end of the data + // just written. + int tail_len; + if (!EVP_CipherFinal_ex(ctx.get(), out_ptr + out_len, &tail_len)) + return false; + + out_len += tail_len; + DCHECK_LE(out_len, static_cast(output_size)); + result.resize(out_len); + + output->swap(result); + return true; +} + +} // namespace crypto diff --git a/crypto/encryptor_unittest.cc b/crypto/encryptor_unittest.cc new file mode 100644 index 0000000..b916854 --- /dev/null +++ b/crypto/encryptor_unittest.cc @@ -0,0 +1,233 @@ +// Copyright (c) 2011 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 "crypto/encryptor.h" + +#include + +#include "base/memory/scoped_ptr.h" +#include "base/string_number_conversions.h" +#include "crypto/symmetric_key.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(EncryptorTest, EncryptDecrypt) { + scoped_ptr key( + crypto::SymmetricKey::DeriveKeyFromPassword( + crypto::SymmetricKey::AES, "password", "saltiest", 1000, 256)); + EXPECT_TRUE(NULL != key.get()); + + crypto::Encryptor encryptor; + // The IV must be exactly as long as the cipher block size. + std::string iv("the iv: 16 bytes"); + EXPECT_EQ(16U, iv.size()); + EXPECT_TRUE(encryptor.Init(key.get(), crypto::Encryptor::CBC, iv)); + + std::string plaintext("this is the plaintext"); + std::string ciphertext; + EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext)); + + EXPECT_LT(0U, ciphertext.size()); + + std::string decypted; + EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decypted)); + + EXPECT_EQ(plaintext, decypted); +} + +// TODO(wtc): add more known-answer tests. Test vectors are available from +// http://www.ietf.org/rfc/rfc3602 +// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf +// http://gladman.plushost.co.uk/oldsite/AES/index.php +// http://csrc.nist.gov/groups/STM/cavp/documents/aes/KAT_AES.zip + +// NIST SP 800-38A test vector F.2.5 CBC-AES256.Encrypt. +TEST(EncryptorTest, EncryptAES256CBC) { + // From NIST SP 800-38a test cast F.2.5 CBC-AES256.Encrypt. + static const unsigned char raw_key[] = { + 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, + 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, + 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, + 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 + }; + static const unsigned char raw_iv[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + static const unsigned char raw_plaintext[] = { + // Block #1 + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + // Block #2 + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + // Block #3 + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + // Block #4 + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, + }; + static const unsigned char raw_ciphertext[] = { + // Block #1 + 0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, + 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6, + // Block #2 + 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, + 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d, + // Block #3 + 0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, + 0xa5, 0x30, 0xe2, 0x63, 0x04, 0x23, 0x14, 0x61, + // Block #4 + 0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, + 0xda, 0x6c, 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b, + // PKCS #5 padding, encrypted. + 0x3f, 0x46, 0x17, 0x96, 0xd6, 0xb0, 0xd6, 0xb2, + 0xe0, 0xc2, 0xa7, 0x2b, 0x4d, 0x80, 0xe6, 0x44 + }; + + std::string key(reinterpret_cast(raw_key), sizeof(raw_key)); + scoped_ptr sym_key(crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, key)); + ASSERT_TRUE(NULL != sym_key.get()); + + crypto::Encryptor encryptor; + // The IV must be exactly as long a the cipher block size. + std::string iv(reinterpret_cast(raw_iv), sizeof(raw_iv)); + EXPECT_EQ(16U, iv.size()); + EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv)); + + std::string plaintext(reinterpret_cast(raw_plaintext), + sizeof(raw_plaintext)); + std::string ciphertext; + EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext)); + + EXPECT_EQ(sizeof(raw_ciphertext), ciphertext.size()); + EXPECT_EQ(0, memcmp(ciphertext.data(), raw_ciphertext, ciphertext.size())); + + std::string decypted; + EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decypted)); + + EXPECT_EQ(plaintext, decypted); +} + +// Expected output derived from the NSS implementation. +TEST(EncryptorTest, EncryptAES128CBCRegression) { + std::string key = "128=SixteenBytes"; + std::string iv = "Sweet Sixteen IV"; + std::string plaintext = "Plain text with a g-clef U+1D11E \360\235\204\236"; + std::string expected_ciphertext_hex = + "D4A67A0BA33C30F207344D81D1E944BBE65587C3D7D9939A" + "C070C62B9C15A3EA312EA4AD1BC7929F4D3C16B03AD5ADA8"; + + scoped_ptr sym_key(crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, key)); + ASSERT_TRUE(NULL != sym_key.get()); + + crypto::Encryptor encryptor; + // The IV must be exactly as long a the cipher block size. + EXPECT_EQ(16U, iv.size()); + EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv)); + + std::string ciphertext; + EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext)); + EXPECT_EQ(expected_ciphertext_hex, base::HexEncode(ciphertext.data(), + ciphertext.size())); + + std::string decypted; + EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decypted)); + EXPECT_EQ(plaintext, decypted); +} + +// Expected output derived from the NSS implementation. +TEST(EncryptorTest, EncryptAES192CBCRegression) { + std::string key = "192bitsIsTwentyFourByte!"; + std::string iv = "Sweet Sixteen IV"; + std::string plaintext = "Small text"; + std::string expected_ciphertext_hex = "78DE5D7C2714FC5C61346C5416F6C89A"; + + scoped_ptr sym_key(crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, key)); + ASSERT_TRUE(NULL != sym_key.get()); + + crypto::Encryptor encryptor; + // The IV must be exactly as long a the cipher block size. + EXPECT_EQ(16U, iv.size()); + EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv)); + + std::string ciphertext; + EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext)); + EXPECT_EQ(expected_ciphertext_hex, base::HexEncode(ciphertext.data(), + ciphertext.size())); + + std::string decypted; + EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decypted)); + EXPECT_EQ(plaintext, decypted); +} + +// Not all platforms allow import/generation of symmetric keys with an +// unsupported size. +#if !defined(OS_WIN) && !defined(USE_NSS) +TEST(EncryptorTest, UnsupportedKeySize) { + std::string key = "7 = bad"; + std::string iv = "Sweet Sixteen IV"; + scoped_ptr sym_key(crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, key)); + ASSERT_TRUE(NULL != sym_key.get()); + + crypto::Encryptor encryptor; + // The IV must be exactly as long a the cipher block size. + EXPECT_EQ(16U, iv.size()); + EXPECT_FALSE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv)); +} +#endif // unsupported platforms. + +TEST(EncryptorTest, UnsupportedIV) { + std::string key = "128=SixteenBytes"; + std::string iv = "OnlyForteen :("; + scoped_ptr sym_key(crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, key)); + ASSERT_TRUE(NULL != sym_key.get()); + + crypto::Encryptor encryptor; + EXPECT_FALSE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv)); +} + +TEST(EncryptorTest, EmptyEncrypt) { + std::string key = "128=SixteenBytes"; + std::string iv = "Sweet Sixteen IV"; + std::string plaintext; + std::string expected_ciphertext_hex = "8518B8878D34E7185E300D0FCC426396"; + + scoped_ptr sym_key(crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, key)); + ASSERT_TRUE(NULL != sym_key.get()); + + crypto::Encryptor encryptor; + // The IV must be exactly as long a the cipher block size. + EXPECT_EQ(16U, iv.size()); + EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv)); + + std::string ciphertext; + EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext)); + EXPECT_EQ(expected_ciphertext_hex, base::HexEncode(ciphertext.data(), + ciphertext.size())); +} + +TEST(EncryptorTest, EmptyDecrypt) { + std::string key = "128=SixteenBytes"; + std::string iv = "Sweet Sixteen IV"; + + scoped_ptr sym_key(crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, key)); + ASSERT_TRUE(NULL != sym_key.get()); + + crypto::Encryptor encryptor; + // The IV must be exactly as long a the cipher block size. + EXPECT_EQ(16U, iv.size()); + EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv)); + + std::string decrypted; + EXPECT_FALSE(encryptor.Decrypt("", &decrypted)); + EXPECT_EQ("", decrypted); +} diff --git a/crypto/encryptor_win.cc b/crypto/encryptor_win.cc new file mode 100644 index 0000000..8bbd6b8 --- /dev/null +++ b/crypto/encryptor_win.cc @@ -0,0 +1,115 @@ +// Copyright (c) 2011 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 "crypto/encryptor.h" + +#include + +#include "crypto/symmetric_key.h" + +namespace crypto { + +namespace { + +// On success, returns the block size (in bytes) for the algorithm that |key| +// is for. On failure, returns 0. +DWORD GetCipherBlockSize(HCRYPTKEY key) { + DWORD block_size_in_bits = 0; + DWORD param_size = sizeof(block_size_in_bits); + BOOL ok = CryptGetKeyParam(key, KP_BLOCKLEN, + reinterpret_cast(&block_size_in_bits), + ¶m_size, 0); + if (!ok) + return 0; + + return block_size_in_bits / 8; +} + +} // namespace + +Encryptor::Encryptor() + : key_(NULL), + mode_(CBC), + block_size_(0) { +} + +Encryptor::~Encryptor() { +} + +bool Encryptor::Init(SymmetricKey* key, Mode mode, const std::string& iv) { + DCHECK(key); + DCHECK_EQ(CBC, mode) << "Unsupported mode of operation"; + + // In CryptoAPI, the IV, padding mode, and feedback register (for a chaining + // mode) are properties of a key, so we have to create a copy of the key for + // the Encryptor. See the Remarks section of the CryptEncrypt MSDN page. + BOOL ok = CryptDuplicateKey(key->key(), NULL, 0, capi_key_.receive()); + if (!ok) + return false; + + // CRYPT_MODE_CBC is the default for Microsoft Base Cryptographic Provider, + // but we set it anyway to be safe. + DWORD cipher_mode = CRYPT_MODE_CBC; + ok = CryptSetKeyParam(capi_key_.get(), KP_MODE, + reinterpret_cast(&cipher_mode), 0); + if (!ok) + return false; + + block_size_ = GetCipherBlockSize(capi_key_.get()); + if (block_size_ == 0) + return false; + + if (iv.size() != block_size_) + return false; + + ok = CryptSetKeyParam(capi_key_.get(), KP_IV, + reinterpret_cast(iv.data()), 0); + if (!ok) + return false; + + DWORD padding_method = PKCS5_PADDING; + ok = CryptSetKeyParam(capi_key_.get(), KP_PADDING, + reinterpret_cast(&padding_method), 0); + if (!ok) + return false; + + return true; +} + +bool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) { + DWORD data_len = plaintext.size(); + DWORD total_len = data_len + block_size_; + + // CryptoAPI encrypts/decrypts in place. + std::vector tmp(total_len); + memcpy(&tmp[0], plaintext.data(), data_len); + + BOOL ok = CryptEncrypt(capi_key_.get(), NULL, TRUE, 0, &tmp[0], + &data_len, total_len); + if (!ok) + return false; + + ciphertext->assign(reinterpret_cast(&tmp[0]), data_len); + return true; +} + +bool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) { + DWORD data_len = ciphertext.size(); + if (data_len == 0) + return false; + + std::vector tmp(data_len); + memcpy(&tmp[0], ciphertext.data(), data_len); + + BOOL ok = CryptDecrypt(capi_key_.get(), NULL, TRUE, 0, &tmp[0], &data_len); + if (!ok) + return false; + + DCHECK_GT(tmp.size(), data_len); + + plaintext->assign(reinterpret_cast(&tmp[0]), data_len); + return true; +} + +} // namespace crypto diff --git a/crypto/hmac.h b/crypto/hmac.h new file mode 100644 index 0000000..816bf60 --- /dev/null +++ b/crypto/hmac.h @@ -0,0 +1,60 @@ +// Copyright (c) 2011 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. + +// Utility class for calculating the HMAC for a given message. We currently +// only support SHA1 for the hash algorithm, but this can be extended easily. + +#ifndef CRYPTO_HMAC_H_ +#define CRYPTO_HMAC_H_ +#pragma once + +#include + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" + +namespace crypto { + +// Simplify the interface and reduce includes by abstracting out the internals. +struct HMACPlatformData; + +class HMAC { + public: + // The set of supported hash functions. Extend as required. + enum HashAlgorithm { + SHA1, + SHA256, + }; + + explicit HMAC(HashAlgorithm hash_alg); + ~HMAC(); + + // Initializes this instance using |key| of the length |key_length|. Call Init + // only once. It returns false on the second or later calls. + bool Init(const unsigned char* key, int key_length); + + // Initializes this instance using |key|. Call Init only once. It returns + // false on the second or later calls. + bool Init(const std::string& key) { + return Init(reinterpret_cast(key.data()), + static_cast(key.size())); + } + + // Calculates the HMAC for the message in |data| using the algorithm supplied + // to the constructor and the key supplied to the Init method. The HMAC is + // returned in |digest|, which has |digest_length| bytes of storage available. + bool Sign(const std::string& data, unsigned char* digest, int digest_length); + + // TODO(albertb): Add a Verify method. + + private: + HashAlgorithm hash_alg_; + scoped_ptr plat_; + + DISALLOW_COPY_AND_ASSIGN(HMAC); +}; + +} // namespace crypto + +#endif // CRYPTO_HMAC_H_ diff --git a/crypto/hmac_mac.cc b/crypto/hmac_mac.cc new file mode 100644 index 0000000..d7cec61 --- /dev/null +++ b/crypto/hmac_mac.cc @@ -0,0 +1,73 @@ +// Copyright (c) 2011 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 "crypto/hmac.h" + +#include + +#include "base/logging.h" + +namespace crypto { + +struct HMACPlatformData { + std::string key_; +}; + +HMAC::HMAC(HashAlgorithm hash_alg) + : hash_alg_(hash_alg), plat_(new HMACPlatformData()) { + // Only SHA-1 and SHA-256 hash algorithms are supported now. + DCHECK(hash_alg_ == SHA1 || hash_alg_ == SHA256); +} + +bool HMAC::Init(const unsigned char *key, int key_length) { + if (!plat_->key_.empty()) { + // Init must not be called more than once on the same HMAC object. + NOTREACHED(); + return false; + } + + plat_->key_.assign(reinterpret_cast(key), key_length); + + return true; +} + +HMAC::~HMAC() { + // Zero out key copy. + plat_->key_.assign(plat_->key_.length(), std::string::value_type()); + plat_->key_.clear(); + plat_->key_.reserve(0); +} + +bool HMAC::Sign(const std::string& data, + unsigned char* digest, + int digest_length) { + CCHmacAlgorithm algorithm; + int algorithm_digest_length; + switch (hash_alg_) { + case SHA1: + algorithm = kCCHmacAlgSHA1; + algorithm_digest_length = CC_SHA1_DIGEST_LENGTH; + break; + case SHA256: + algorithm = kCCHmacAlgSHA256; + algorithm_digest_length = CC_SHA256_DIGEST_LENGTH; + break; + default: + NOTREACHED(); + return false; + } + + if (digest_length < algorithm_digest_length) { + NOTREACHED(); + return false; + } + + CCHmac(algorithm, + plat_->key_.data(), plat_->key_.length(), data.data(), data.length(), + digest); + + return true; +} + +} // namespace crypto diff --git a/crypto/hmac_nss.cc b/crypto/hmac_nss.cc new file mode 100644 index 0000000..957f9db --- /dev/null +++ b/crypto/hmac_nss.cc @@ -0,0 +1,117 @@ +// Copyright (c) 2011 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 "crypto/hmac.h" + +#include +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "crypto/nss_util.h" +#include "crypto/scoped_nss_types.h" + +namespace crypto { + +struct HMACPlatformData { + CK_MECHANISM_TYPE mechanism_; + ScopedPK11Slot slot_; + ScopedPK11SymKey sym_key_; +}; + +HMAC::HMAC(HashAlgorithm hash_alg) + : hash_alg_(hash_alg), plat_(new HMACPlatformData()) { + // Only SHA-1 and SHA-256 hash algorithms are supported. + switch (hash_alg_) { + case SHA1: + plat_->mechanism_ = CKM_SHA_1_HMAC; + break; + case SHA256: + plat_->mechanism_ = CKM_SHA256_HMAC; + break; + default: + NOTREACHED() << "Unsupported hash algorithm"; + break; + } +} + +HMAC::~HMAC() { +} + +bool HMAC::Init(const unsigned char *key, int key_length) { + EnsureNSSInit(); + + if (plat_->slot_.get()) { + // Init must not be called more than twice on the same HMAC object. + NOTREACHED(); + return false; + } + + plat_->slot_.reset(PK11_GetBestSlot(plat_->mechanism_, NULL)); + if (!plat_->slot_.get()) { + NOTREACHED(); + return false; + } + + SECItem key_item; + key_item.type = siBuffer; + key_item.data = const_cast(key); // NSS API isn't const. + key_item.len = key_length; + + plat_->sym_key_.reset(PK11_ImportSymKey(plat_->slot_.get(), + plat_->mechanism_, + PK11_OriginUnwrap, + CKA_SIGN, + &key_item, + NULL)); + if (!plat_->sym_key_.get()) { + NOTREACHED(); + return false; + } + + return true; +} + +bool HMAC::Sign(const std::string& data, + unsigned char* digest, + int digest_length) { + if (!plat_->sym_key_.get()) { + // Init has not been called before Sign. + NOTREACHED(); + return false; + } + + SECItem param = { siBuffer, NULL, 0 }; + ScopedPK11Context context(PK11_CreateContextBySymKey(plat_->mechanism_, + CKA_SIGN, + plat_->sym_key_.get(), + ¶m)); + if (!context.get()) { + NOTREACHED(); + return false; + } + + if (PK11_DigestBegin(context.get()) != SECSuccess) { + NOTREACHED(); + return false; + } + + if (PK11_DigestOp(context.get(), + reinterpret_cast(data.data()), + data.length()) != SECSuccess) { + NOTREACHED(); + return false; + } + + unsigned int len = 0; + if (PK11_DigestFinal(context.get(), + digest, &len, digest_length) != SECSuccess) { + NOTREACHED(); + return false; + } + + return true; +} + +} // namespace crypto diff --git a/crypto/hmac_openssl.cc b/crypto/hmac_openssl.cc new file mode 100644 index 0000000..6fbc437 --- /dev/null +++ b/crypto/hmac_openssl.cc @@ -0,0 +1,57 @@ +// Copyright (c) 2011 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 "crypto/hmac.h" + +#include + +#include +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util-inl.h" +#include "crypto/openssl_util.h" + +namespace crypto { + +struct HMACPlatformData { + std::vector key; +}; + +HMAC::HMAC(HashAlgorithm hash_alg) + : hash_alg_(hash_alg), plat_(new HMACPlatformData()) { + // Only SHA-1 and SHA-256 hash algorithms are supported now. + DCHECK(hash_alg_ == SHA1 || hash_alg_ == SHA256); +} + +bool HMAC::Init(const unsigned char* key, int key_length) { + // Init must not be called more than once on the same HMAC object. + DCHECK(plat_->key.empty()); + + plat_->key.assign(key, key + key_length); + return true; +} + +HMAC::~HMAC() { + // Zero out key copy. + plat_->key.assign(plat_->key.size(), 0); + STLClearObject(&plat_->key); +} + +bool HMAC::Sign(const std::string& data, + unsigned char* digest, + int digest_length) { + DCHECK_GE(digest_length, 0); + DCHECK(!plat_->key.empty()); // Init must be called before Sign. + + ScopedOpenSSLSafeSizeBuffer result(digest, digest_length); + return ::HMAC(hash_alg_ == SHA1 ? EVP_sha1() : EVP_sha256(), + &plat_->key[0], plat_->key.size(), + reinterpret_cast(data.data()), + data.size(), + result.safe_buffer(), NULL); +} + +} // namespace crypto diff --git a/crypto/hmac_unittest.cc b/crypto/hmac_unittest.cc new file mode 100644 index 0000000..c537c36 --- /dev/null +++ b/crypto/hmac_unittest.cc @@ -0,0 +1,236 @@ +// Copyright (c) 2011 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 + +#include "crypto/hmac.h" +#include "testing/gtest/include/gtest/gtest.h" + +static const int kSHA1DigestSize = 20; +static const int kSHA256DigestSize = 32; + +TEST(HMACTest, HmacSafeBrowsingResponseTest) { + const int kKeySize = 16; + + // Client key. + const unsigned char kClientKey[kKeySize] = + { 0xbf, 0xf6, 0x83, 0x4b, 0x3e, 0xa3, 0x23, 0xdd, + 0x96, 0x78, 0x70, 0x8e, 0xa1, 0x9d, 0x3b, 0x40 }; + + // Expected HMAC result using kMessage and kClientKey. + const unsigned char kReceivedHmac[kSHA1DigestSize] = + { 0xb9, 0x3c, 0xd6, 0xf0, 0x49, 0x47, 0xe2, 0x52, + 0x59, 0x7a, 0xbd, 0x1f, 0x2b, 0x4c, 0x83, 0xad, + 0x86, 0xd2, 0x48, 0x85 }; + + const char kMessage[] = +"n:1896\ni:goog-malware-shavar\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shav" +"ar_s_445-450\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_439-444\nu:s" +".ytimg.com/safebrowsing/rd/goog-malware-shavar_s_437\nu:s.ytimg.com/safebrowsi" +"ng/rd/goog-malware-shavar_s_436\nu:s.ytimg.com/safebrowsing/rd/goog-malware-sh" +"avar_s_433-435\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_431\nu:s.y" +"timg.com/safebrowsing/rd/goog-malware-shavar_s_430\nu:s.ytimg.com/safebrowsing" +"/rd/goog-malware-shavar_s_429\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shav" +"ar_s_428\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_426\nu:s.ytimg.c" +"om/safebrowsing/rd/goog-malware-shavar_s_424\nu:s.ytimg.com/safebrowsing/rd/go" +"og-malware-shavar_s_423\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_4" +"22\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_420\nu:s.ytimg.com/saf" +"ebrowsing/rd/goog-malware-shavar_s_419\nu:s.ytimg.com/safebrowsing/rd/goog-mal" +"ware-shavar_s_414\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_409-411" +"\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_405\nu:s.ytimg.com/safeb" +"rowsing/rd/goog-malware-shavar_s_404\nu:s.ytimg.com/safebrowsing/rd/goog-malwa" +"re-shavar_s_402\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_401\nu:s." +"ytimg.com/safebrowsing/rd/goog-malware-shavar_a_973-978\nu:s.ytimg.com/safebro" +"wsing/rd/goog-malware-shavar_a_937-972\nu:s.ytimg.com/safebrowsing/rd/goog-mal" +"ware-shavar_a_931-936\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_a_925" +"-930\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_a_919-924\ni:goog-phis" +"h-shavar\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2633\nu:s.ytimg.co" +"m/safebrowsing/rd/goog-phish-shavar_a_2632\nu:s.ytimg.com/safebrowsing/rd/goog" +"-phish-shavar_a_2629-2631\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2" +"626-2628\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2625\n"; + + std::string message_data(kMessage); + + crypto::HMAC hmac(crypto::HMAC::SHA1); + ASSERT_TRUE(hmac.Init(kClientKey, kKeySize)); + unsigned char calculated_hmac[kSHA1DigestSize]; + + EXPECT_TRUE(hmac.Sign(message_data, calculated_hmac, kSHA1DigestSize)); + EXPECT_EQ(0, memcmp(kReceivedHmac, calculated_hmac, kSHA1DigestSize)); +} + +// Test cases from RFC 2202 section 3 +TEST(HMACTest, RFC2202TestCases) { + const struct { + const char *key; + const int key_len; + const char *data; + const int data_len; + const char *digest; + } cases[] = { + { "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B" + "\x0B\x0B\x0B\x0B", 20, + "Hi There", 8, + "\xB6\x17\x31\x86\x55\x05\x72\x64\xE2\x8B\xC0\xB6\xFB\x37\x8C\x8E" + "\xF1\x46\xBE\x00" }, + { "Jefe", 4, + "what do ya want for nothing?", 28, + "\xEF\xFC\xDF\x6A\xE5\xEB\x2F\xA2\xD2\x74\x16\xD5\xF1\x84\xDF\x9C" + "\x25\x9A\x7C\x79" }, + { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA", 20, + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD", 50, + "\x12\x5D\x73\x42\xB9\xAC\x11\xCD\x91\xA3\x9A\xF4\x8A\xA1\x7B\x4F" + "\x63\xF1\x75\xD3" }, + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19", 25, + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD", 50, + "\x4C\x90\x07\xF4\x02\x62\x50\xC6\xBC\x84\x14\xF9\xBF\x50\xC8\x6C" + "\x2D\x72\x35\xDA" }, + { "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C" + "\x0C\x0C\x0C\x0C", 20, + "Test With Truncation", 20, + "\x4C\x1A\x03\x42\x4B\x55\xE0\x7F\xE7\xF2\x7B\xE1\xD5\x8B\xB9\x32" + "\x4A\x9A\x5A\x04" }, + { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", + 80, + "Test Using Larger Than Block-Size Key - Hash Key First", 54, + "\xAA\x4A\xE5\xE1\x52\x72\xD0\x0E\x95\x70\x56\x37\xCE\x8A\x3B\x55" + "\xED\x40\x21\x12" }, + { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", + 80, + "Test Using Larger Than Block-Size Key and Larger " + "Than One Block-Size Data", 73, + "\xE8\xE9\x9D\x0F\x45\x23\x7D\x78\x6D\x6B\xBA\xA7\x96\x5C\x78\x08" + "\xBB\xFF\x1A\x91" } + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + crypto::HMAC hmac(crypto::HMAC::SHA1); + ASSERT_TRUE(hmac.Init(reinterpret_cast(cases[i].key), + cases[i].key_len)); + std::string data_string(cases[i].data, cases[i].data_len); + unsigned char digest[kSHA1DigestSize]; + EXPECT_TRUE(hmac.Sign(data_string, digest, kSHA1DigestSize)); + EXPECT_EQ(0, memcmp(cases[i].digest, digest, kSHA1DigestSize)); + } +} + +// TODO(wtc): add other test vectors from RFC 4231. +TEST(HMACTest, RFC4231TestCase6) { + unsigned char key[131]; + for (size_t i = 0; i < sizeof(key); ++i) + key[i] = 0xaa; + + std::string data = "Test Using Larger Than Block-Size Key - Hash Key First"; + ASSERT_EQ(54U, data.size()); + + static unsigned char kKnownHMACSHA256[] = { + 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, + 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f, + 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, + 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54 + }; + + crypto::HMAC hmac(crypto::HMAC::SHA256); + ASSERT_TRUE(hmac.Init(key, sizeof(key))); + unsigned char calculated_hmac[kSHA256DigestSize]; + + EXPECT_TRUE(hmac.Sign(data, calculated_hmac, kSHA256DigestSize)); + EXPECT_EQ(0, memcmp(kKnownHMACSHA256, calculated_hmac, kSHA256DigestSize)); +} + +// Based on NSS's FIPS HMAC power-up self-test. +TEST(HMACTest, NSSFIPSPowerUpSelfTest) { + static const char kKnownMessage[] = + "The test message for the MD2, MD5, and SHA-1 hashing algorithms."; + + static const unsigned char kKnownSecretKey[] = { + 0x46, 0x69, 0x72, 0x65, 0x66, 0x6f, 0x78, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x54, 0x68, 0x75, 0x6e, + 0x64, 0x65, 0x72, 0x42, 0x69, 0x72, 0x64, 0x20, + 0x61, 0x72, 0x65, 0x20, 0x61, 0x77, 0x65, 0x73, + 0x6f, 0x6d, 0x65, 0x21, 0x00 + }; + + static const size_t kKnownSecretKeySize = sizeof(kKnownSecretKey); + + // HMAC-SHA-1 known answer (20 bytes). + static const unsigned char kKnownHMACSHA1[] = { + 0xd5, 0x85, 0xf6, 0x5b, 0x39, 0xfa, 0xb9, 0x05, + 0x3b, 0x57, 0x1d, 0x61, 0xe7, 0xb8, 0x84, 0x1e, + 0x5d, 0x0e, 0x1e, 0x11 + }; + + // HMAC-SHA-256 known answer (32 bytes). + static const unsigned char kKnownHMACSHA256[] = { + 0x05, 0x75, 0x9a, 0x9e, 0x70, 0x5e, 0xe7, 0x44, + 0xe2, 0x46, 0x4b, 0x92, 0x22, 0x14, 0x22, 0xe0, + 0x1b, 0x92, 0x8a, 0x0c, 0xfe, 0xf5, 0x49, 0xe9, + 0xa7, 0x1b, 0x56, 0x7d, 0x1d, 0x29, 0x40, 0x48 + }; + + std::string message_data(kKnownMessage); + + crypto::HMAC hmac(crypto::HMAC::SHA1); + ASSERT_TRUE(hmac.Init(kKnownSecretKey, kKnownSecretKeySize)); + unsigned char calculated_hmac[kSHA1DigestSize]; + + EXPECT_TRUE(hmac.Sign(message_data, calculated_hmac, kSHA1DigestSize)); + EXPECT_EQ(0, memcmp(kKnownHMACSHA1, calculated_hmac, kSHA1DigestSize)); + + crypto::HMAC hmac2(crypto::HMAC::SHA256); + ASSERT_TRUE(hmac2.Init(kKnownSecretKey, kKnownSecretKeySize)); + unsigned char calculated_hmac2[kSHA256DigestSize]; + + EXPECT_TRUE(hmac2.Sign(message_data, calculated_hmac2, kSHA256DigestSize)); + EXPECT_EQ(0, memcmp(kKnownHMACSHA256, calculated_hmac2, kSHA256DigestSize)); +} + +TEST(HMACTest, HMACObjectReuse) { + const char *key = + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"; + const int key_len = 80; + + const struct { + const char *data; + const int data_len; + const char *digest; + } cases[] = { + { "Test Using Larger Than Block-Size Key - Hash Key First", 54, + "\xAA\x4A\xE5\xE1\x52\x72\xD0\x0E\x95\x70\x56\x37\xCE\x8A\x3B\x55" + "\xED\x40\x21\x12" }, + { "Test Using Larger Than Block-Size Key and Larger " + "Than One Block-Size Data", 73, + "\xE8\xE9\x9D\x0F\x45\x23\x7D\x78\x6D\x6B\xBA\xA7\x96\x5C\x78\x08" + "\xBB\xFF\x1A\x91" } + }; + + crypto::HMAC hmac(crypto::HMAC::SHA1); + ASSERT_TRUE(hmac.Init(reinterpret_cast(key), key_len)); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + std::string data_string(cases[i].data, cases[i].data_len); + unsigned char digest[kSHA1DigestSize]; + EXPECT_TRUE(hmac.Sign(data_string, digest, kSHA1DigestSize)); + EXPECT_EQ(0, memcmp(cases[i].digest, digest, kSHA1DigestSize)); + } +} diff --git a/crypto/hmac_win.cc b/crypto/hmac_win.cc new file mode 100644 index 0000000..e5511e0 --- /dev/null +++ b/crypto/hmac_win.cc @@ -0,0 +1,197 @@ +// Copyright (c) 2011 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 "crypto/hmac.h" + +#include +#include + +#include +#include + +#include "base/logging.h" +#include "crypto/scoped_capi_types.h" +#include "crypto/third_party/nss/blapi.h" +#include "crypto/third_party/nss/sha256.h" + +namespace crypto { + +namespace { + +// Implementation of HMAC-SHA-256: +// +// SHA-256 is supported in Windows XP SP3 or later. We still need to support +// Windows XP SP2, so unfortunately we have to implement HMAC-SHA-256 here. + +enum { + SHA256_BLOCK_SIZE = 64 // Block size (in bytes) of the input to SHA-256. +}; + +// See FIPS 198: The Keyed-Hash Message Authentication Code (HMAC). +void ComputeHMACSHA256(const unsigned char* key, size_t key_len, + const unsigned char* text, size_t text_len, + unsigned char* output, size_t output_len) { + SHA256Context ctx; + + // Pre-process the key, if necessary. + unsigned char key0[SHA256_BLOCK_SIZE]; + if (key_len > SHA256_BLOCK_SIZE) { + SHA256_Begin(&ctx); + SHA256_Update(&ctx, key, key_len); + SHA256_End(&ctx, key0, NULL, SHA256_LENGTH); + memset(key0 + SHA256_LENGTH, 0, SHA256_BLOCK_SIZE - SHA256_LENGTH); + } else { + memcpy(key0, key, key_len); + memset(key0 + key_len, 0, SHA256_BLOCK_SIZE - key_len); + } + + unsigned char padded_key[SHA256_BLOCK_SIZE]; + unsigned char inner_hash[SHA256_LENGTH]; + + // XOR key0 with ipad. + for (int i = 0; i < SHA256_BLOCK_SIZE; ++i) + padded_key[i] = key0[i] ^ 0x36; + + // Compute the inner hash. + SHA256_Begin(&ctx); + SHA256_Update(&ctx, padded_key, SHA256_BLOCK_SIZE); + SHA256_Update(&ctx, text, text_len); + SHA256_End(&ctx, inner_hash, NULL, SHA256_LENGTH); + + // XOR key0 with opad. + for (int i = 0; i < SHA256_BLOCK_SIZE; ++i) + padded_key[i] = key0[i] ^ 0x5c; + + // Compute the outer hash. + SHA256_Begin(&ctx); + SHA256_Update(&ctx, padded_key, SHA256_BLOCK_SIZE); + SHA256_Update(&ctx, inner_hash, SHA256_LENGTH); + SHA256_End(&ctx, output, NULL, output_len); +} + +} // namespace + +struct HMACPlatformData { + ~HMACPlatformData() { + if (!raw_key_.empty()) { + SecureZeroMemory(&raw_key_[0], raw_key_.size()); + } + + // Destroy the key before releasing the provider. + key_.reset(); + } + + ScopedHCRYPTPROV provider_; + ScopedHCRYPTKEY key_; + + // For HMAC-SHA-256 only. + std::vector raw_key_; +}; + +HMAC::HMAC(HashAlgorithm hash_alg) + : hash_alg_(hash_alg), plat_(new HMACPlatformData()) { + // Only SHA-1 and SHA-256 hash algorithms are supported now. + DCHECK(hash_alg_ == SHA1 || hash_alg_ == SHA256); +} + +bool HMAC::Init(const unsigned char* key, int key_length) { + if (plat_->provider_ || plat_->key_ || !plat_->raw_key_.empty()) { + // Init must not be called more than once on the same HMAC object. + NOTREACHED(); + return false; + } + + if (hash_alg_ == SHA256) { + if (key_length < SHA256_LENGTH / 2) + return false; // Key is too short. + plat_->raw_key_.assign(key, key + key_length); + return true; + } + + if (!CryptAcquireContext(plat_->provider_.receive(), NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + NOTREACHED(); + return false; + } + + // This code doesn't work on Win2k because PLAINTEXTKEYBLOB and + // CRYPT_IPSEC_HMAC_KEY are not supported on Windows 2000. PLAINTEXTKEYBLOB + // allows the import of an unencrypted key. For Win2k support, a cubmbersome + // exponent-of-one key procedure must be used: + // http://support.microsoft.com/kb/228786/en-us + // CRYPT_IPSEC_HMAC_KEY allows keys longer than 16 bytes. + + struct KeyBlob { + BLOBHEADER header; + DWORD key_size; + BYTE key_data[1]; + }; + size_t key_blob_size = std::max(offsetof(KeyBlob, key_data) + key_length, + sizeof(KeyBlob)); + std::vector key_blob_storage = std::vector(key_blob_size); + KeyBlob* key_blob = reinterpret_cast(&key_blob_storage[0]); + key_blob->header.bType = PLAINTEXTKEYBLOB; + key_blob->header.bVersion = CUR_BLOB_VERSION; + key_blob->header.reserved = 0; + key_blob->header.aiKeyAlg = CALG_RC2; + key_blob->key_size = key_length; + memcpy(key_blob->key_data, key, key_length); + + if (!CryptImportKey(plat_->provider_, &key_blob_storage[0], + key_blob_storage.size(), 0, CRYPT_IPSEC_HMAC_KEY, + plat_->key_.receive())) { + NOTREACHED(); + return false; + } + + // Destroy the copy of the key. + SecureZeroMemory(key_blob->key_data, key_length); + + return true; +} + +HMAC::~HMAC() { +} + +bool HMAC::Sign(const std::string& data, + unsigned char* digest, + int digest_length) { + if (hash_alg_ == SHA256) { + if (plat_->raw_key_.empty()) + return false; + ComputeHMACSHA256(&plat_->raw_key_[0], plat_->raw_key_.size(), + reinterpret_cast(data.data()), + data.size(), digest, digest_length); + return true; + } + + if (!plat_->provider_ || !plat_->key_) + return false; + + if (hash_alg_ != SHA1) { + NOTREACHED(); + return false; + } + + ScopedHCRYPTHASH hash; + if (!CryptCreateHash(plat_->provider_, CALG_HMAC, plat_->key_, 0, + hash.receive())) + return false; + + HMAC_INFO hmac_info; + memset(&hmac_info, 0, sizeof(hmac_info)); + hmac_info.HashAlgid = CALG_SHA1; + if (!CryptSetHashParam(hash, HP_HMAC_INFO, + reinterpret_cast(&hmac_info), 0)) + return false; + + if (!CryptHashData(hash, reinterpret_cast(data.data()), + static_cast(data.size()), 0)) + return false; + + DWORD sha1_size = digest_length; + return !!CryptGetHashParam(hash, HP_HASHVAL, digest, &sha1_size, 0); +} + +} // namespace crypto diff --git a/crypto/mac_security_services_lock.cc b/crypto/mac_security_services_lock.cc new file mode 100644 index 0000000..c0b8712 --- /dev/null +++ b/crypto/mac_security_services_lock.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2011 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 "crypto/mac_security_services_lock.h" + +#include "base/memory/singleton.h" +#include "base/synchronization/lock.h" + +namespace { + +// This singleton pertains to Apple's wrappers over their own CSSM handles, +// as opposed to our own CSSM_CSP_HANDLE in cssm_init.cc. +class SecurityServicesSingleton { + public: + static SecurityServicesSingleton* GetInstance() { + return Singleton >::get(); + } + + base::Lock& lock() { return lock_; } + + private: + friend struct DefaultSingletonTraits; + + SecurityServicesSingleton() {} + ~SecurityServicesSingleton() {} + + base::Lock lock_; + + DISALLOW_COPY_AND_ASSIGN(SecurityServicesSingleton); +}; + +} // namespace + +namespace crypto { + +base::Lock& GetMacSecurityServicesLock() { + return SecurityServicesSingleton::GetInstance()->lock(); +} + +} // namespace crypto diff --git a/crypto/mac_security_services_lock.h b/crypto/mac_security_services_lock.h new file mode 100644 index 0000000..85db73e --- /dev/null +++ b/crypto/mac_security_services_lock.h @@ -0,0 +1,25 @@ +// Copyright (c) 2011 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 CRYPTO_MAC_SECURITY_SERVICES_LOCK_H_ +#define CRYPTO_MAC_SECURITY_SERVICES_LOCK_H_ +#pragma once + +namespace base { +class Lock; +} + + +namespace crypto { + +// The Mac OS X certificate and key management wrappers over CSSM are not +// thread-safe. In particular, code that accesses the CSSM database is +// problematic. +// +// http://developer.apple.com/mac/library/documentation/Security/Reference/certifkeytrustservices/Reference/reference.html +base::Lock& GetMacSecurityServicesLock(); + +} // namespace crypto + +#endif // CRYPTO_MAC_SECURITY_SERVICES_LOCK_H_ diff --git a/crypto/nss_util.cc b/crypto/nss_util.cc new file mode 100644 index 0000000..976a5e3 --- /dev/null +++ b/crypto/nss_util.cc @@ -0,0 +1,706 @@ +// Copyright (c) 2011 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 "crypto/nss_util.h" +#include "crypto/nss_util_internal.h" + +#include +#include +#include +#include +#include +#include +#include + +#if defined(OS_LINUX) +#include +#include +#endif + +#include + +#include "base/environment.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/native_library.h" +#include "base/stringprintf.h" +#include "base/threading/thread_restrictions.h" +#include "crypto/scoped_nss_types.h" + +// USE_NSS means we use NSS for everything crypto-related. If USE_NSS is not +// defined, such as on Mac and Windows, we use NSS for SSL only -- we don't +// use NSS for crypto or certificate verification, and we don't use the NSS +// certificate and key databases. +#if defined(USE_NSS) +#include "base/synchronization/lock.h" +#include "crypto/crypto_module_blocking_password_delegate.h" +#endif // defined(USE_NSS) + +namespace crypto { + +namespace { + +#if defined(OS_CHROMEOS) +const char kNSSDatabaseName[] = "Real NSS database"; + +// Constants for loading opencryptoki. +const char kOpencryptokiModuleName[] = "opencryptoki"; +const char kOpencryptokiPath[] = "/usr/lib/opencryptoki/libopencryptoki.so"; + +// TODO(gspencer): Get these values from cryptohomed's dbus API when +// we ask if it has initialized the TPM yet. These should not be +// hard-coded here. +const char kTPMTokenName[] = "Initialized by CrOS"; +const char kTPMUserPIN[] = "111111"; +const char kTPMSecurityOfficerPIN[] = "000000"; + +// Fake certificate authority database used for testing. +static const FilePath::CharType kReadOnlyCertDB[] = + FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb"); +#endif // defined(OS_CHROMEOS) + +std::string GetNSSErrorMessage() { + std::string result; + if (PR_GetErrorTextLength()) { + scoped_array error_text(new char[PR_GetErrorTextLength() + 1]); + PRInt32 copied = PR_GetErrorText(error_text.get()); + result = std::string(error_text.get(), copied); + } else { + result = StringPrintf("NSS error code: %d", PR_GetError()); + } + return result; +} + +#if defined(USE_NSS) +FilePath GetDefaultConfigDirectory() { + FilePath dir = file_util::GetHomeDir(); + if (dir.empty()) { + LOG(ERROR) << "Failed to get home directory."; + return dir; + } + dir = dir.AppendASCII(".pki").AppendASCII("nssdb"); + if (!file_util::CreateDirectory(dir)) { + LOG(ERROR) << "Failed to create ~/.pki/nssdb directory."; + dir.clear(); + } + return dir; +} + +// On non-chromeos platforms, return the default config directory. +// On chromeos, return a read-only directory with fake root CA certs for testing +// (which will not exist on non-testing images). These root CA certs are used +// by the local Google Accounts server mock we use when testing our login code. +// If this directory is not present, NSS_Init() will fail. It is up to the +// caller to failover to NSS_NoDB_Init() at that point. +FilePath GetInitialConfigDirectory() { +#if defined(OS_CHROMEOS) + return FilePath(kReadOnlyCertDB); +#else + return GetDefaultConfigDirectory(); +#endif // defined(OS_CHROMEOS) +} + +// This callback for NSS forwards all requests to a caller-specified +// CryptoModuleBlockingPasswordDelegate object. +char* PKCS11PasswordFunc(PK11SlotInfo* slot, PRBool retry, void* arg) { +#if defined(OS_CHROMEOS) + // If we get asked for a password for the TPM, then return the + // static password we use. + if (PK11_GetTokenName(slot) == crypto::GetTPMTokenName()) + return PORT_Strdup(GetTPMUserPIN().c_str()); +#endif + crypto::CryptoModuleBlockingPasswordDelegate* delegate = + reinterpret_cast(arg); + if (delegate) { + bool cancelled = false; + std::string password = delegate->RequestPassword(PK11_GetTokenName(slot), + retry != PR_FALSE, + &cancelled); + if (cancelled) + return NULL; + char* result = PORT_Strdup(password.c_str()); + password.replace(0, password.size(), password.size(), 0); + return result; + } + DLOG(ERROR) << "PK11 password requested with NULL arg"; + return NULL; +} + +// NSS creates a local cache of the sqlite database if it detects that the +// filesystem the database is on is much slower than the local disk. The +// detection doesn't work with the latest versions of sqlite, such as 3.6.22 +// (NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=578561). So we set +// the NSS environment variable NSS_SDB_USE_CACHE to "yes" to override NSS's +// detection when database_dir is on NFS. See http://crbug.com/48585. +// +// TODO(wtc): port this function to other USE_NSS platforms. It is defined +// only for OS_LINUX simply because the statfs structure is OS-specific. +// +// Because this function sets an environment variable it must be run before we +// go multi-threaded. +void UseLocalCacheOfNSSDatabaseIfNFS(const FilePath& database_dir) { +#if defined(OS_LINUX) + struct statfs buf; + if (statfs(database_dir.value().c_str(), &buf) == 0) { + if (buf.f_type == NFS_SUPER_MAGIC) { + scoped_ptr env(base::Environment::Create()); + const char* use_cache_env_var = "NSS_SDB_USE_CACHE"; + if (!env->HasVar(use_cache_env_var)) + env->SetVar(use_cache_env_var, "yes"); + } + } +#endif // defined(OS_LINUX) +} + +// A helper class that acquires the SECMOD list read lock while the +// AutoSECMODListReadLock is in scope. +class AutoSECMODListReadLock { + public: + AutoSECMODListReadLock() + : lock_(SECMOD_GetDefaultModuleListLock()) { + SECMOD_GetReadLock(lock_); + } + + ~AutoSECMODListReadLock() { + SECMOD_ReleaseReadLock(lock_); + } + + private: + SECMODListLock* lock_; + DISALLOW_COPY_AND_ASSIGN(AutoSECMODListReadLock); +}; + +PK11SlotInfo* FindSlotWithTokenName(const std::string& token_name) { + AutoSECMODListReadLock auto_lock; + SECMODModuleList* head = SECMOD_GetDefaultModuleList(); + for (SECMODModuleList* item = head; item != NULL; item = item->next) { + int slot_count = item->module->loaded ? item->module->slotCount : 0; + for (int i = 0; i < slot_count; i++) { + PK11SlotInfo* slot = item->module->slots[i]; + if (PK11_GetTokenName(slot) == token_name) + return PK11_ReferenceSlot(slot); + } + } + return NULL; +} + +#endif // defined(USE_NSS) + +// A singleton to initialize/deinitialize NSPR. +// Separate from the NSS singleton because we initialize NSPR on the UI thread. +// Now that we're leaking the singleton, we could merge back with the NSS +// singleton. +class NSPRInitSingleton { + private: + friend struct base::DefaultLazyInstanceTraits; + + NSPRInitSingleton() { + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + } + + // NOTE(willchan): We don't actually execute this code since we leak NSS to + // prevent non-joinable threads from using NSS after it's already been shut + // down. + ~NSPRInitSingleton() { + PL_ArenaFinish(); + PRStatus prstatus = PR_Cleanup(); + if (prstatus != PR_SUCCESS) { + LOG(ERROR) << "PR_Cleanup failed; was NSPR initialized on wrong thread?"; + } + } +}; + +base::LazyInstance > + g_nspr_singleton(base::LINKER_INITIALIZED); + +class NSSInitSingleton { + public: +#if defined(OS_CHROMEOS) + void OpenPersistentNSSDB() { + if (!chromeos_user_logged_in_) { + // GetDefaultConfigDirectory causes us to do blocking IO on UI thread. + // Temporarily allow it until we fix http://crbug.com/70119 + base::ThreadRestrictions::ScopedAllowIO allow_io; + chromeos_user_logged_in_ = true; + + // This creates another DB slot in NSS that is read/write, unlike + // the fake root CA cert DB and the "default" crypto key + // provider, which are still read-only (because we initialized + // NSS before we had a cryptohome mounted). + software_slot_ = OpenUserDB(GetDefaultConfigDirectory(), + kNSSDatabaseName); + } + } + + bool EnableTPMForNSS() { + if (!opencryptoki_module_) { + // This loads the opencryptoki module so we can talk to the + // hardware TPM. + opencryptoki_module_ = LoadModule( + kOpencryptokiModuleName, + kOpencryptokiPath, + // trustOrder=100 -- means it'll select this as the most + // trusted slot for the mechanisms it provides. + // slotParams=... -- selects RSA as the only mechanism, and only + // asks for the password when necessary (instead of every + // time, or after a timeout). + "trustOrder=100 slotParams=(1={slotFlags=[RSA] askpw=only})"); + if (opencryptoki_module_) { + // We shouldn't need to initialize the TPM PIN here because + // it'll be taken care of by cryptohomed, but we have to make + // sure that it is initialized. + + // TODO(gspencer): replace this with a dbus call that will + // check to see that cryptohomed has initialized the PINs, and + // will fetch the token name and PINs for accessing the TPM. + EnsureTPMInit(); + + // If this is set, then we'll use the TPM by default. + tpm_slot_ = GetTPMSlot(); + return true; + } + } + return false; + } + + std::string GetTPMTokenName() { + // TODO(gspencer): This should come from the dbus interchange with + // cryptohomed instead of being hard-coded. + return std::string(kTPMTokenName); + } + + std::string GetTPMUserPIN() { + // TODO(gspencer): This should come from the dbus interchange with + // cryptohomed instead of being hard-coded. + return std::string(kTPMUserPIN); + } + + PK11SlotInfo* GetTPMSlot() { + return FindSlotWithTokenName(GetTPMTokenName()); + } +#endif // defined(OS_CHROMEOS) + + + bool OpenTestNSSDB(const FilePath& path, const char* description) { + test_slot_ = OpenUserDB(path, description); + return !!test_slot_; + } + + void CloseTestNSSDB() { + if (test_slot_) { + SECStatus status = SECMOD_CloseUserDB(test_slot_); + if (status != SECSuccess) + LOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError(); + PK11_FreeSlot(test_slot_); + test_slot_ = NULL; + } + } + + PK11SlotInfo* GetPublicNSSKeySlot() { + if (test_slot_) + return PK11_ReferenceSlot(test_slot_); + if (software_slot_) + return PK11_ReferenceSlot(software_slot_); + return PK11_GetInternalKeySlot(); + } + + PK11SlotInfo* GetPrivateNSSKeySlot() { + if (test_slot_) + return PK11_ReferenceSlot(test_slot_); + // If the TPM slot has been opened, then return that one. + if (tpm_slot_) + return PK11_ReferenceSlot(tpm_slot_); + // If it hasn't, then return the software slot. + if (software_slot_) + return PK11_ReferenceSlot(software_slot_); + return PK11_GetInternalKeySlot(); + } + +#if defined(USE_NSS) + base::Lock* write_lock() { + return &write_lock_; + } +#endif // defined(USE_NSS) + + // This method is used to force NSS to be initialized without a DB. + // Call this method before NSSInitSingleton() is constructed. + static void ForceNoDBInit() { + force_nodb_init_ = true; + } + + private: + friend struct base::DefaultLazyInstanceTraits; + + NSSInitSingleton() + : opencryptoki_module_(NULL), + software_slot_(NULL), + test_slot_(NULL), + tpm_slot_(NULL), + root_(NULL), + chromeos_user_logged_in_(false) { + EnsureNSPRInit(); + + // We *must* have NSS >= 3.12.3. See bug 26448. + COMPILE_ASSERT( + (NSS_VMAJOR == 3 && NSS_VMINOR == 12 && NSS_VPATCH >= 3) || + (NSS_VMAJOR == 3 && NSS_VMINOR > 12) || + (NSS_VMAJOR > 3), + nss_version_check_failed); + // Also check the run-time NSS version. + // NSS_VersionCheck is a >= check, not strict equality. + if (!NSS_VersionCheck("3.12.3")) { + // It turns out many people have misconfigured NSS setups, where + // their run-time NSPR doesn't match the one their NSS was compiled + // against. So rather than aborting, complain loudly. + LOG(ERROR) << "NSS_VersionCheck(\"3.12.3\") failed. " + "We depend on NSS >= 3.12.3, and this error is not fatal " + "only because many people have busted NSS setups (for " + "example, using the wrong version of NSPR). " + "Please upgrade to the latest NSS and NSPR, and if you " + "still get this error, contact your distribution " + "maintainer."; + } + + SECStatus status = SECFailure; + bool nodb_init = force_nodb_init_; + +#if !defined(USE_NSS) + // Use the system certificate store, so initialize NSS without database. + nodb_init = true; +#endif + + if (nodb_init) { + status = NSS_NoDB_Init(NULL); + if (status != SECSuccess) { + LOG(ERROR) << "Error initializing NSS without a persistent " + "database: " << GetNSSErrorMessage(); + } + } else { +#if defined(USE_NSS) + FilePath database_dir = GetInitialConfigDirectory(); + if (!database_dir.empty()) { + // This duplicates the work which should have been done in + // EarlySetupForNSSInit. However, this function is idempotent so + // there's no harm done. + UseLocalCacheOfNSSDatabaseIfNFS(database_dir); + + // Initialize with a persistent database (likely, ~/.pki/nssdb). + // Use "sql:" which can be shared by multiple processes safely. + std::string nss_config_dir = + StringPrintf("sql:%s", database_dir.value().c_str()); +#if defined(OS_CHROMEOS) + status = NSS_Init(nss_config_dir.c_str()); +#else + status = NSS_InitReadWrite(nss_config_dir.c_str()); +#endif + if (status != SECSuccess) { + LOG(ERROR) << "Error initializing NSS with a persistent " + "database (" << nss_config_dir + << "): " << GetNSSErrorMessage(); + } + } + if (status != SECSuccess) { + VLOG(1) << "Initializing NSS without a persistent database."; + status = NSS_NoDB_Init(NULL); + if (status != SECSuccess) { + LOG(ERROR) << "Error initializing NSS without a persistent " + "database: " << GetNSSErrorMessage(); + return; + } + } + + PK11_SetPasswordFunc(PKCS11PasswordFunc); + + // If we haven't initialized the password for the NSS databases, + // initialize an empty-string password so that we don't need to + // log in. + PK11SlotInfo* slot = PK11_GetInternalKeySlot(); + if (slot) { + // PK11_InitPin may write to the keyDB, but no other thread can use NSS + // yet, so we don't need to lock. + if (PK11_NeedUserInit(slot)) + PK11_InitPin(slot, NULL, NULL); + PK11_FreeSlot(slot); + } + + root_ = InitDefaultRootCerts(); +#endif // defined(USE_NSS) + } + } + + // NOTE(willchan): We don't actually execute this code since we leak NSS to + // prevent non-joinable threads from using NSS after it's already been shut + // down. + ~NSSInitSingleton() { + if (tpm_slot_) { + PK11_FreeSlot(tpm_slot_); + tpm_slot_ = NULL; + } + if (software_slot_) { + SECMOD_CloseUserDB(software_slot_); + PK11_FreeSlot(software_slot_); + software_slot_ = NULL; + } + CloseTestNSSDB(); + if (root_) { + SECMOD_UnloadUserModule(root_); + SECMOD_DestroyModule(root_); + root_ = NULL; + } + if (opencryptoki_module_) { + SECMOD_UnloadUserModule(opencryptoki_module_); + SECMOD_DestroyModule(opencryptoki_module_); + opencryptoki_module_ = NULL; + } + + SECStatus status = NSS_Shutdown(); + if (status != SECSuccess) { + // We VLOG(1) because this failure is relatively harmless (leaking, but + // we're shutting down anyway). + VLOG(1) << "NSS_Shutdown failed; see http://crbug.com/4609"; + } + } + +#if defined(USE_NSS) + // Load nss's built-in root certs. + SECMODModule* InitDefaultRootCerts() { + SECMODModule* root = LoadModule("Root Certs", "libnssckbi.so", NULL); + if (root) + return root; + + // Aw, snap. Can't find/load root cert shared library. + // This will make it hard to talk to anybody via https. + NOTREACHED(); + return NULL; + } + + // Load the given module for this NSS session. + SECMODModule* LoadModule(const char* name, + const char* library_path, + const char* params) { + std::string modparams = StringPrintf( + "name=\"%s\" library=\"%s\" %s", + name, library_path, params ? params : ""); + + // Shouldn't need to const_cast here, but SECMOD doesn't properly + // declare input string arguments as const. Bug + // https://bugzilla.mozilla.org/show_bug.cgi?id=642546 was filed + // on NSS codebase to address this. + SECMODModule* module = SECMOD_LoadUserModule( + const_cast(modparams.c_str()), NULL, PR_FALSE); + if (!module) { + LOG(ERROR) << "Error loading " << name << " module into NSS: " + << GetNSSErrorMessage(); + return NULL; + } + return module; + } +#endif + +#if defined(OS_CHROMEOS) + void EnsureTPMInit() { + crypto::ScopedPK11Slot tpm_slot(GetTPMSlot()); + if (tpm_slot.get()) { + // TODO(gspencer): Remove this in favor of the dbus API for + // cryptohomed when that is available. + if (PK11_NeedUserInit(tpm_slot.get())) { + PK11_InitPin(tpm_slot.get(), + kTPMSecurityOfficerPIN, + kTPMUserPIN); + } + } + } +#endif + + static PK11SlotInfo* OpenUserDB(const FilePath& path, + const char* description) { + const std::string modspec = + StringPrintf("configDir='sql:%s' tokenDescription='%s'", + path.value().c_str(), description); + PK11SlotInfo* db_slot = SECMOD_OpenUserDB(modspec.c_str()); + if (db_slot) { + if (PK11_NeedUserInit(db_slot)) + PK11_InitPin(db_slot, NULL, NULL); + } + else { + LOG(ERROR) << "Error opening persistent database (" << modspec + << "): " << GetNSSErrorMessage(); + } + return db_slot; + } + + // If this is set to true NSS is forced to be initialized without a DB. + static bool force_nodb_init_; + + SECMODModule* opencryptoki_module_; + PK11SlotInfo* software_slot_; + PK11SlotInfo* test_slot_; + PK11SlotInfo* tpm_slot_; + SECMODModule* root_; + bool chromeos_user_logged_in_; +#if defined(USE_NSS) + // TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011 + // is fixed, we will no longer need the lock. + base::Lock write_lock_; +#endif // defined(USE_NSS) +}; + +// static +bool NSSInitSingleton::force_nodb_init_ = false; + +base::LazyInstance > + g_nss_singleton(base::LINKER_INITIALIZED); + +} // namespace + +#if defined(USE_NSS) +void EarlySetupForNSSInit() { + FilePath database_dir = GetInitialConfigDirectory(); + if (!database_dir.empty()) + UseLocalCacheOfNSSDatabaseIfNFS(database_dir); +} +#endif + +void EnsureNSPRInit() { + g_nspr_singleton.Get(); +} + +void EnsureNSSInit() { + // Initializing SSL causes us to do blocking IO. + // Temporarily allow it until we fix + // http://code.google.com/p/chromium/issues/detail?id=59847 + base::ThreadRestrictions::ScopedAllowIO allow_io; + g_nss_singleton.Get(); +} + +void ForceNSSNoDBInit() { + NSSInitSingleton::ForceNoDBInit(); +} + +void DisableNSSForkCheck() { + scoped_ptr env(base::Environment::Create()); + env->SetVar("NSS_STRICT_NOFORK", "DISABLED"); +} + +void LoadNSSLibraries() { + // Some NSS libraries are linked dynamically so load them here. +#if defined(USE_NSS) + // Try to search for multiple directories to load the libraries. + std::vector paths; + + // Use relative path to Search PATH for the library files. + paths.push_back(FilePath()); + + // For Debian derivaties NSS libraries are located here. + paths.push_back(FilePath("/usr/lib/nss")); + + // A list of library files to load. + std::vector libs; + libs.push_back("libsoftokn3.so"); + libs.push_back("libfreebl3.so"); + + // For each combination of library file and path, check for existence and + // then load. + size_t loaded = 0; + for (size_t i = 0; i < libs.size(); ++i) { + for (size_t j = 0; j < paths.size(); ++j) { + FilePath path = paths[j].Append(libs[i]); + base::NativeLibrary lib = base::LoadNativeLibrary(path); + if (lib) { + ++loaded; + break; + } + } + } + + if (loaded == libs.size()) { + VLOG(3) << "NSS libraries loaded."; + } else { + LOG(WARNING) << "Failed to load NSS libraries."; + } +#endif +} + +bool CheckNSSVersion(const char* version) { + return !!NSS_VersionCheck(version); +} + +#if defined(USE_NSS) +bool OpenTestNSSDB(const FilePath& path, const char* description) { + return g_nss_singleton.Get().OpenTestNSSDB(path, description); +} + +void CloseTestNSSDB() { + g_nss_singleton.Get().CloseTestNSSDB(); +} + +base::Lock* GetNSSWriteLock() { + return g_nss_singleton.Get().write_lock(); +} + +AutoNSSWriteLock::AutoNSSWriteLock() : lock_(GetNSSWriteLock()) { + // May be NULL if the lock is not needed in our version of NSS. + if (lock_) + lock_->Acquire(); +} + +AutoNSSWriteLock::~AutoNSSWriteLock() { + if (lock_) { + lock_->AssertAcquired(); + lock_->Release(); + } +} +#endif // defined(USE_NSS) + +#if defined(OS_CHROMEOS) +void OpenPersistentNSSDB() { + g_nss_singleton.Get().OpenPersistentNSSDB(); +} + +bool EnableTPMForNSS() { + return g_nss_singleton.Get().EnableTPMForNSS(); +} + +std::string GetTPMTokenName() { + return g_nss_singleton.Get().GetTPMTokenName(); +} + +std::string GetTPMUserPIN() { + return g_nss_singleton.Get().GetTPMUserPIN(); +} +#endif // defined(OS_CHROMEOS) + +// TODO(port): Implement this more simply. We can convert by subtracting an +// offset (the difference between NSPR's and base::Time's epochs). +base::Time PRTimeToBaseTime(PRTime prtime) { + PRExplodedTime prxtime; + PR_ExplodeTime(prtime, PR_GMTParameters, &prxtime); + + base::Time::Exploded exploded; + exploded.year = prxtime.tm_year; + exploded.month = prxtime.tm_month + 1; + exploded.day_of_week = prxtime.tm_wday; + exploded.day_of_month = prxtime.tm_mday; + exploded.hour = prxtime.tm_hour; + exploded.minute = prxtime.tm_min; + exploded.second = prxtime.tm_sec; + exploded.millisecond = prxtime.tm_usec / 1000; + + return base::Time::FromUTCExploded(exploded); +} + +PK11SlotInfo* GetPublicNSSKeySlot() { + return g_nss_singleton.Get().GetPublicNSSKeySlot(); +} + +PK11SlotInfo* GetPrivateNSSKeySlot() { + return g_nss_singleton.Get().GetPrivateNSSKeySlot(); +} + +} // namespace crypto diff --git a/crypto/nss_util.h b/crypto/nss_util.h new file mode 100644 index 0000000..6cb03a8 --- /dev/null +++ b/crypto/nss_util.h @@ -0,0 +1,138 @@ +// Copyright (c) 2011 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 CRYPTO_NSS_UTIL_H_ +#define CRYPTO_NSS_UTIL_H_ +#pragma once + +#include +#include "base/basictypes.h" + +#if defined(USE_NSS) +class FilePath; +#endif // defined(USE_NSS) + +namespace base { +class Lock; +class Time; +} // namespace base + +// This file specifically doesn't depend on any NSS or NSPR headers because it +// is included by various (non-crypto) parts of chrome to call the +// initialization functions. +namespace crypto { + +#if defined(USE_NSS) +// EarlySetupForNSSInit performs lightweight setup which must occur before the +// process goes multithreaded. This does not initialise NSS. For test, see +// EnsureNSSInit. +void EarlySetupForNSSInit(); +#endif + +// Initialize NRPR if it isn't already initialized. This function is +// thread-safe, and NSPR will only ever be initialized once. +void EnsureNSPRInit(); + +// Initialize NSS if it isn't already initialized. This must be called before +// any other NSS functions. This function is thread-safe, and NSS will only +// ever be initialized once. +void EnsureNSSInit(); + +// Call this before calling EnsureNSSInit() will force NSS to initialize +// without a persistent DB. This is used for the special case where access of +// persistent DB is prohibited. +// +// TODO(hclam): Isolate loading default root certs. +// +// NSS will be initialized without loading any user security modules, including +// the built-in root certificates module. User security modules need to be +// loaded manually after NSS initialization. +// +// If EnsureNSSInit() is called before then this function has no effect. +// +// Calling this method only has effect on Linux. +// +// WARNING: Use this with caution. +void ForceNSSNoDBInit(); + +// This methods is used to disable checks in NSS when used in a forked process. +// NSS checks whether it is running a forked process to avoid problems when +// using user security modules in a forked process. However if we are sure +// there are no modules loaded before the process is forked then there is no +// harm disabling the check. +// +// This method must be called before EnsureNSSInit() to take effect. +// +// WARNING: Use this with caution. +void DisableNSSForkCheck(); + +// Load NSS library files. This function has no effect on Mac and Windows. +// This loads the necessary NSS library files so that NSS can be initialized +// after loading additional library files is disallowed, for example when the +// sandbox is active. +// +// Note that this does not load libnssckbi.so which contains the root +// certificates. +void LoadNSSLibraries(); + +// Check if the current NSS version is greater than or equals to |version|. +// A sample version string is "3.12.3". +bool CheckNSSVersion(const char* version); + +#if defined(OS_CHROMEOS) +// Open the r/w nssdb that's stored inside the user's encrypted home +// directory. This is the default slot returned by +// GetPublicNSSKeySlot(). +void OpenPersistentNSSDB(); + +// Load the opencryptoki library into NSS so that we can access the +// TPM through NSS. Once this is called, GetPrivateNSSKeySlot() will +// return the TPM slot if one was found. Returns false if it was +// unable to load opencryptoki or open the TPM slot. +bool EnableTPMForNSS(); + +// Get name for the built-in TPM token on ChromeOS. +std::string GetTPMTokenName(); + +// Get the user PIN for the built-in TPM token on ChromeOS. +std::string GetTPMUserPIN(); +#endif + +// Convert a NSS PRTime value into a base::Time object. +// We use a int64 instead of PRTime here to avoid depending on NSPR headers. +base::Time PRTimeToBaseTime(int64 prtime); + +#if defined(USE_NSS) +// Exposed for unittests only. |path| should be an existing directory under +// which the DB files will be placed. |description| is a user-visible name for +// the DB, as a utf8 string, which will be truncated at 32 bytes. +bool OpenTestNSSDB(const FilePath& path, const char* description); +void CloseTestNSSDB(); + +// NSS has a bug which can cause a deadlock or stall in some cases when writing +// to the certDB and keyDB. It also has a bug which causes concurrent key pair +// generations to scribble over each other. To work around this, we synchronize +// writes to the NSS databases with a global lock. The lock is hidden beneath a +// function for easy disabling when the bug is fixed. Callers should allow for +// it to return NULL in the future. +// +// See https://bugzilla.mozilla.org/show_bug.cgi?id=564011 +base::Lock* GetNSSWriteLock(); + +// A helper class that acquires the NSS write Lock while the AutoNSSWriteLock +// is in scope. +class AutoNSSWriteLock { + public: + AutoNSSWriteLock(); + ~AutoNSSWriteLock(); + private: + base::Lock *lock_; + DISALLOW_COPY_AND_ASSIGN(AutoNSSWriteLock); +}; + +#endif // defined(USE_NSS) + +} // namespace crypto + +#endif // CRYPTO_NSS_UTIL_H_ diff --git a/crypto/nss_util_internal.h b/crypto/nss_util_internal.h new file mode 100644 index 0000000..ea40fdb --- /dev/null +++ b/crypto/nss_util_internal.h @@ -0,0 +1,30 @@ +// Copyright (c) 2011 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 CRYPTO_NSS_UTIL_INTERNAL_H_ +#define CRYPTO_NSS_UTIL_INTERNAL_H_ +#pragma once + +#include + +// These functions return a type defined in an NSS header, and so cannot be +// declared in nss_util.h. Hence, they are declared here. + +namespace crypto { + +// Returns a reference to the default NSS key slot for storing +// public-key data only (e.g. server certs). Caller must release +// returned reference with PK11_FreeSlot. +PK11SlotInfo* GetPublicNSSKeySlot(); + +// Returns a reference to the default slot for storing private-key and +// mixed private-key/public-key data. Returns a hardware (TPM) NSS +// key slot if on ChromeOS and EnableTPMForNSS() has been called +// successfully. Caller must release returned reference with +// PK11_FreeSlot. +PK11SlotInfo* GetPrivateNSSKeySlot(); + +} // namespace crypto + +#endif // CRYPTO_NSS_UTIL_INTERNAL_H_ diff --git a/crypto/openssl_util.cc b/crypto/openssl_util.cc new file mode 100644 index 0000000..feb47ca --- /dev/null +++ b/crypto/openssl_util.cc @@ -0,0 +1,113 @@ +// Copyright (c) 2011 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 "crypto/openssl_util.h" + +#include +#include + +#include "base/logging.h" +#include "base/memory/scoped_vector.h" +#include "base/memory/singleton.h" +#include "base/string_piece.h" +#include "base/synchronization/lock.h" + +namespace crypto { + +namespace { + +unsigned long CurrentThreadId() { + return static_cast(base::PlatformThread::CurrentId()); +} + +// Singleton for initializing and cleaning up the OpenSSL library. +class OpenSSLInitSingleton { + public: + static OpenSSLInitSingleton* GetInstance() { + // We allow the SSL environment to leak for multiple reasons: + // - it is used from a non-joinable worker thread that is not stopped on + // shutdown, hence may still be using OpenSSL library after the AtExit + // runner has completed. + // - There are other OpenSSL related singletons (e.g. the client socket + // context) who's cleanup depends on the global environment here, but + // we can't control the order the AtExit handlers will run in so + // allowing the global environment to leak at least ensures it is + // available for those other singletons to reliably cleanup. + return base::Singleton >::get(); + } + private: + friend struct DefaultSingletonTraits; + OpenSSLInitSingleton() { + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); + int num_locks = CRYPTO_num_locks(); + locks_.reserve(num_locks); + for (int i = 0; i < num_locks; ++i) + locks_.push_back(new base::Lock()); + CRYPTO_set_locking_callback(LockingCallback); + CRYPTO_set_id_callback(CurrentThreadId); + } + + ~OpenSSLInitSingleton() { + CRYPTO_set_locking_callback(NULL); + EVP_cleanup(); + ERR_free_strings(); + } + + static void LockingCallback(int mode, int n, const char* file, int line) { + OpenSSLInitSingleton::GetInstance()->OnLockingCallback(mode, n, file, line); + } + + void OnLockingCallback(int mode, int n, const char* file, int line) { + CHECK_LT(static_cast(n), locks_.size()); + if (mode & CRYPTO_LOCK) + locks_[n]->Acquire(); + else + locks_[n]->Release(); + } + + // These locks are used and managed by OpenSSL via LockingCallback(). + ScopedVector locks_; + + DISALLOW_COPY_AND_ASSIGN(OpenSSLInitSingleton); +}; + +// Callback routine for OpenSSL to print error messages. |str| is a +// NULL-terminated string of length |len| containing diagnostic information +// such as the library, function and reason for the error, the file and line +// where the error originated, plus potentially any context-specific +// information about the error. |context| contains a pointer to user-supplied +// data, which is currently unused. +// If this callback returns a value <= 0, OpenSSL will stop processing the +// error queue and return, otherwise it will continue calling this function +// until all errors have been removed from the queue. +int OpenSSLErrorCallback(const char* str, size_t len, void* context) { + DVLOG(1) << "\t" << StringPiece(str, len); + return 1; +} + +} // namespace + +void EnsureOpenSSLInit() { + (void)OpenSSLInitSingleton::GetInstance(); +} + +void ClearOpenSSLERRStack(const tracked_objects::Location& location) { + if (logging::DEBUG_MODE && VLOG_IS_ON(1)) { + int error_num = ERR_peek_error(); + if (error_num == 0) + return; + + std::string message; + location.Write(true, true, &message); + DVLOG(1) << "OpenSSL ERR_get_error stack from " << message; + ERR_print_errors_cb(&OpenSSLErrorCallback, NULL); + } else { + ERR_clear_error(); + } +} + +} // namespace crypto diff --git a/crypto/openssl_util.h b/crypto/openssl_util.h new file mode 100644 index 0000000..d1f3d1f --- /dev/null +++ b/crypto/openssl_util.h @@ -0,0 +1,113 @@ +// Copyright (c) 2011 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 CRYPTO_OPENSSL_UTIL_H_ +#define CRYPTO_OPENSSL_UTIL_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/tracked.h" + +namespace crypto { + +// A helper class that takes care of destroying OpenSSL objects when it goes out +// of scope. +template +class ScopedOpenSSL { + public: + ScopedOpenSSL() : ptr_(NULL) { } + explicit ScopedOpenSSL(T* ptr) : ptr_(ptr) { } + ~ScopedOpenSSL() { + reset(NULL); + } + + T* get() const { return ptr_; } + void reset(T* ptr) { + if (ptr != ptr_) { + if (ptr_) (*destructor)(ptr_); + ptr_ = ptr; + } + } + + private: + T* ptr_; + + DISALLOW_COPY_AND_ASSIGN(ScopedOpenSSL); +}; + +// Provides a buffer of at least MIN_SIZE bytes, for use when calling OpenSSL's +// SHA256, HMAC, etc functions, adapting the buffer sizing rules to meet those +// of the our base wrapper APIs. +// This allows the library to write directly to the caller's buffer if it is of +// sufficient size, but if not it will write to temporary |min_sized_buffer_| +// of required size and then its content is automatically copied out on +// destruction, with truncation as appropriate. +template +class ScopedOpenSSLSafeSizeBuffer { + public: + ScopedOpenSSLSafeSizeBuffer(unsigned char* output, size_t output_len) + : output_(output), + output_len_(output_len) { + } + + ~ScopedOpenSSLSafeSizeBuffer() { + if (output_len_ < MIN_SIZE) { + // Copy the temporary buffer out, truncating as needed. + memcpy(output_, min_sized_buffer_, output_len_); + } + // else... any writing already happened directly into |output_|. + } + + unsigned char* safe_buffer() { + return output_len_ < MIN_SIZE ? min_sized_buffer_ : output_; + } + + private: + // Pointer to the caller's data area and it's associated size, where data + // written via safe_buffer() will [eventually] end up. + unsigned char* output_; + size_t output_len_; + + // Temporary buffer writen into in the case where the caller's + // buffer is not of sufficient size. + unsigned char min_sized_buffer_[MIN_SIZE]; + + DISALLOW_COPY_AND_ASSIGN(ScopedOpenSSLSafeSizeBuffer); +}; + +// Initialize OpenSSL if it isn't already initialized. This must be called +// before any other OpenSSL functions. +// This function is thread-safe, and OpenSSL will only ever be initialized once. +// OpenSSL will be properly shut down on program exit. +void EnsureOpenSSLInit(); + +// Drains the OpenSSL ERR_get_error stack. On a debug build the error codes +// are send to VLOG(1), on a release build they are disregarded. In most +// cases you should pass FROM_HERE as the |location|. +void ClearOpenSSLERRStack(const tracked_objects::Location& location); + +// Place an instance of this class on the call stack to automatically clear +// the OpenSSL error stack on function exit. +class OpenSSLErrStackTracer { + public: + // Pass FROM_HERE as |location|, to help track the source of OpenSSL error + // messages. Note any diagnostic emitted will be tagged with the location of + // the constructor call as it's not possible to trace a destructor's callsite. + explicit OpenSSLErrStackTracer(const tracked_objects::Location& location) + : location_(location) { + EnsureOpenSSLInit(); + } + ~OpenSSLErrStackTracer() { + ClearOpenSSLERRStack(location_); + } + + private: + const tracked_objects::Location location_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(OpenSSLErrStackTracer); +}; + +} // namespace crypto + +#endif // CRYPTO_OPENSSL_UTIL_H_ diff --git a/crypto/rsa_private_key.cc b/crypto/rsa_private_key.cc new file mode 100644 index 0000000..8290d16 --- /dev/null +++ b/crypto/rsa_private_key.cc @@ -0,0 +1,390 @@ +// Copyright (c) 2011 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 "crypto/rsa_private_key.h" + +#include +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/string_util.h" + +// This file manually encodes and decodes RSA private keys using PrivateKeyInfo +// from PKCS #8 and RSAPrivateKey from PKCS #1. These structures are: +// +// PrivateKeyInfo ::= SEQUENCE { +// version Version, +// privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, +// privateKey PrivateKey, +// attributes [0] IMPLICIT Attributes OPTIONAL +// } +// +// RSAPrivateKey ::= SEQUENCE { +// version Version, +// modulus INTEGER, +// publicExponent INTEGER, +// privateExponent INTEGER, +// prime1 INTEGER, +// prime2 INTEGER, +// exponent1 INTEGER, +// exponent2 INTEGER, +// coefficient INTEGER +// } + +namespace { +// Helper for error handling during key import. +#define READ_ASSERT(truth) \ + if (!(truth)) { \ + NOTREACHED(); \ + return false; \ + } +} // namespace + +namespace crypto { + +const uint8 PrivateKeyInfoCodec::kRsaAlgorithmIdentifier[] = { + 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, + 0x05, 0x00 +}; + +PrivateKeyInfoCodec::PrivateKeyInfoCodec(bool big_endian) + : big_endian_(big_endian) {} + +PrivateKeyInfoCodec::~PrivateKeyInfoCodec() {} + +bool PrivateKeyInfoCodec::Export(std::vector* output) { + std::list content; + + // Version (always zero) + uint8 version = 0; + + PrependInteger(coefficient_, &content); + PrependInteger(exponent2_, &content); + PrependInteger(exponent1_, &content); + PrependInteger(prime2_, &content); + PrependInteger(prime1_, &content); + PrependInteger(private_exponent_, &content); + PrependInteger(public_exponent_, &content); + PrependInteger(modulus_, &content); + PrependInteger(&version, 1, &content); + PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content); + PrependTypeHeaderAndLength(kOctetStringTag, content.size(), &content); + + // RSA algorithm OID + for (size_t i = sizeof(kRsaAlgorithmIdentifier); i > 0; --i) + content.push_front(kRsaAlgorithmIdentifier[i - 1]); + + PrependInteger(&version, 1, &content); + PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content); + + // Copy everying into the output. + output->reserve(content.size()); + for (std::list::iterator i = content.begin(); i != content.end(); ++i) + output->push_back(*i); + + return true; +} + +bool PrivateKeyInfoCodec::ExportPublicKeyInfo(std::vector* output) { + // Create a sequence with the modulus (n) and public exponent (e). + std::vector bit_string; + if (!ExportPublicKey(&bit_string)) + return false; + + // Add the sequence as the contents of a bit string. + std::list content; + PrependBitString(&bit_string[0], static_cast(bit_string.size()), + &content); + + // Add the RSA algorithm OID. + for (size_t i = sizeof(kRsaAlgorithmIdentifier); i > 0; --i) + content.push_front(kRsaAlgorithmIdentifier[i - 1]); + + // Finally, wrap everything in a sequence. + PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content); + + // Copy everything into the output. + output->reserve(content.size()); + for (std::list::iterator i = content.begin(); i != content.end(); ++i) + output->push_back(*i); + + return true; +} + +bool PrivateKeyInfoCodec::ExportPublicKey(std::vector* output) { + // Create a sequence with the modulus (n) and public exponent (e). + std::list content; + PrependInteger(&public_exponent_[0], + static_cast(public_exponent_.size()), + &content); + PrependInteger(&modulus_[0], static_cast(modulus_.size()), &content); + PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content); + + // Copy everything into the output. + output->reserve(content.size()); + for (std::list::iterator i = content.begin(); i != content.end(); ++i) + output->push_back(*i); + + return true; +} + +bool PrivateKeyInfoCodec::Import(const std::vector& input) { + if (input.empty()) { + return false; + } + + // Parse the private key info up to the public key values, ignoring + // the subsequent private key values. + uint8* src = const_cast(&input.front()); + uint8* end = src + input.size(); + if (!ReadSequence(&src, end) || + !ReadVersion(&src, end) || + !ReadAlgorithmIdentifier(&src, end) || + !ReadTypeHeaderAndLength(&src, end, kOctetStringTag, NULL) || + !ReadSequence(&src, end) || + !ReadVersion(&src, end) || + !ReadInteger(&src, end, &modulus_)) + return false; + + int mod_size = modulus_.size(); + READ_ASSERT(mod_size % 2 == 0); + int primes_size = mod_size / 2; + + if (!ReadIntegerWithExpectedSize(&src, end, 4, &public_exponent_) || + !ReadIntegerWithExpectedSize(&src, end, mod_size, &private_exponent_) || + !ReadIntegerWithExpectedSize(&src, end, primes_size, &prime1_) || + !ReadIntegerWithExpectedSize(&src, end, primes_size, &prime2_) || + !ReadIntegerWithExpectedSize(&src, end, primes_size, &exponent1_) || + !ReadIntegerWithExpectedSize(&src, end, primes_size, &exponent2_) || + !ReadIntegerWithExpectedSize(&src, end, primes_size, &coefficient_)) + return false; + + READ_ASSERT(src == end); + + + return true; +} + +void PrivateKeyInfoCodec::PrependInteger(const std::vector& in, + std::list* out) { + uint8* ptr = const_cast(&in.front()); + PrependIntegerImpl(ptr, in.size(), out, big_endian_); +} + +// Helper to prepend an ASN.1 integer. +void PrivateKeyInfoCodec::PrependInteger(uint8* val, + int num_bytes, + std::list* data) { + PrependIntegerImpl(val, num_bytes, data, big_endian_); +} + +void PrivateKeyInfoCodec::PrependIntegerImpl(uint8* val, + int num_bytes, + std::list* data, + bool big_endian) { + // Reverse input if little-endian. + std::vector tmp; + if (!big_endian) { + tmp.assign(val, val + num_bytes); + reverse(tmp.begin(), tmp.end()); + val = &tmp.front(); + } + + // ASN.1 integers are unpadded byte arrays, so skip any null padding bytes + // from the most-significant end of the integer. + int start = 0; + while (start < (num_bytes - 1) && val[start] == 0x00) { + start++; + num_bytes--; + } + PrependBytes(val, start, num_bytes, data); + + // ASN.1 integers are signed. To encode a positive integer whose sign bit + // (the most significant bit) would otherwise be set and make the number + // negative, ASN.1 requires a leading null byte to force the integer to be + // positive. + uint8 front = data->front(); + if ((front & 0x80) != 0) { + data->push_front(0x00); + num_bytes++; + } + + PrependTypeHeaderAndLength(kIntegerTag, num_bytes, data); +} + +bool PrivateKeyInfoCodec::ReadInteger(uint8** pos, + uint8* end, + std::vector* out) { + return ReadIntegerImpl(pos, end, out, big_endian_); +} + +bool PrivateKeyInfoCodec::ReadIntegerWithExpectedSize(uint8** pos, + uint8* end, + size_t expected_size, + std::vector* out) { + std::vector temp; + if (!ReadIntegerImpl(pos, end, &temp, true)) // Big-Endian + return false; + + int pad = expected_size - temp.size(); + int index = 0; + if (out->size() == expected_size + 1) { + READ_ASSERT(out->front() == 0x00); + pad++; + index++; + } else { + READ_ASSERT(out->size() <= expected_size); + } + + while (pad) { + out->push_back(0x00); + pad--; + } + out->insert(out->end(), temp.begin(), temp.end()); + + // Reverse output if little-endian. + if (!big_endian_) + reverse(out->begin(), out->end()); + return true; +} + +bool PrivateKeyInfoCodec::ReadIntegerImpl(uint8** pos, + uint8* end, + std::vector* out, + bool big_endian) { + uint32 length = 0; + if (!ReadTypeHeaderAndLength(pos, end, kIntegerTag, &length) || !length) + return false; + + // The first byte can be zero to force positiveness. We can ignore this. + if (**pos == 0x00) { + ++(*pos); + --length; + } + + if (length) + out->insert(out->end(), *pos, (*pos) + length); + + (*pos) += length; + + // Reverse output if little-endian. + if (!big_endian) + reverse(out->begin(), out->end()); + return true; +} + +void PrivateKeyInfoCodec::PrependBytes(uint8* val, + int start, + int num_bytes, + std::list* data) { + while (num_bytes > 0) { + --num_bytes; + data->push_front(val[start + num_bytes]); + } +} + +void PrivateKeyInfoCodec::PrependLength(size_t size, std::list* data) { + // The high bit is used to indicate whether additional octets are needed to + // represent the length. + if (size < 0x80) { + data->push_front(static_cast(size)); + } else { + uint8 num_bytes = 0; + while (size > 0) { + data->push_front(static_cast(size & 0xFF)); + size >>= 8; + num_bytes++; + } + CHECK_LE(num_bytes, 4); + data->push_front(0x80 | num_bytes); + } +} + +void PrivateKeyInfoCodec::PrependTypeHeaderAndLength(uint8 type, + uint32 length, + std::list* output) { + PrependLength(length, output); + output->push_front(type); +} + +void PrivateKeyInfoCodec::PrependBitString(uint8* val, + int num_bytes, + std::list* output) { + // Start with the data. + PrependBytes(val, 0, num_bytes, output); + // Zero unused bits. + output->push_front(0); + // Add the length. + PrependLength(num_bytes + 1, output); + // Finally, add the bit string tag. + output->push_front((uint8) kBitStringTag); +} + +bool PrivateKeyInfoCodec::ReadLength(uint8** pos, uint8* end, uint32* result) { + READ_ASSERT(*pos < end); + int length = 0; + + // If the MSB is not set, the length is just the byte itself. + if (!(**pos & 0x80)) { + length = **pos; + (*pos)++; + } else { + // Otherwise, the lower 7 indicate the length of the length. + int length_of_length = **pos & 0x7F; + READ_ASSERT(length_of_length <= 4); + (*pos)++; + READ_ASSERT(*pos + length_of_length < end); + + length = 0; + for (int i = 0; i < length_of_length; ++i) { + length <<= 8; + length |= **pos; + (*pos)++; + } + } + + READ_ASSERT(*pos + length <= end); + if (result) *result = length; + return true; +} + +bool PrivateKeyInfoCodec::ReadTypeHeaderAndLength(uint8** pos, + uint8* end, + uint8 expected_tag, + uint32* length) { + READ_ASSERT(*pos < end); + READ_ASSERT(**pos == expected_tag); + (*pos)++; + + return ReadLength(pos, end, length); +} + +bool PrivateKeyInfoCodec::ReadSequence(uint8** pos, uint8* end) { + return ReadTypeHeaderAndLength(pos, end, kSequenceTag, NULL); +} + +bool PrivateKeyInfoCodec::ReadAlgorithmIdentifier(uint8** pos, uint8* end) { + READ_ASSERT(*pos + sizeof(kRsaAlgorithmIdentifier) < end); + READ_ASSERT(memcmp(*pos, kRsaAlgorithmIdentifier, + sizeof(kRsaAlgorithmIdentifier)) == 0); + (*pos) += sizeof(kRsaAlgorithmIdentifier); + return true; +} + +bool PrivateKeyInfoCodec::ReadVersion(uint8** pos, uint8* end) { + uint32 length = 0; + if (!ReadTypeHeaderAndLength(pos, end, kIntegerTag, &length)) + return false; + + // The version should be zero. + for (uint32 i = 0; i < length; ++i) { + READ_ASSERT(**pos == 0x00); + (*pos)++; + } + + return true; +} + +} // namespace crypto diff --git a/crypto/rsa_private_key.h b/crypto/rsa_private_key.h new file mode 100644 index 0000000..080db46 --- /dev/null +++ b/crypto/rsa_private_key.h @@ -0,0 +1,273 @@ +// Copyright (c) 2011 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 CRYPTO_RSA_PRIVATE_KEY_H_ +#define CRYPTO_RSA_PRIVATE_KEY_H_ +#pragma once + +#include "build/build_config.h" + +#if defined(USE_OPENSSL) +// Forward declaration for openssl/*.h +typedef struct evp_pkey_st EVP_PKEY; +#elif defined(USE_NSS) +// Forward declaration. +struct SECKEYPrivateKeyStr; +struct SECKEYPublicKeyStr; +#elif defined(OS_MACOSX) +#include +#endif + +#include +#include + +#include "base/basictypes.h" + +#if defined(OS_WIN) +#include "crypto/scoped_capi_types.h" +#endif +#if defined(USE_NSS) +#include "base/gtest_prod_util.h" +#endif + +namespace crypto { + +// Used internally by RSAPrivateKey for serializing and deserializing +// PKCS #8 PrivateKeyInfo and PublicKeyInfo. +class PrivateKeyInfoCodec { + public: + + // ASN.1 encoding of the AlgorithmIdentifier from PKCS #8. + static const uint8 kRsaAlgorithmIdentifier[]; + + // ASN.1 tags for some types we use. + static const uint8 kBitStringTag = 0x03; + static const uint8 kIntegerTag = 0x02; + static const uint8 kNullTag = 0x05; + static const uint8 kOctetStringTag = 0x04; + static const uint8 kSequenceTag = 0x30; + + // |big_endian| here specifies the byte-significance of the integer components + // that will be parsed & serialized (modulus(), etc...) during Import(), + // Export() and ExportPublicKeyInfo() -- not the ASN.1 DER encoding of the + // PrivateKeyInfo/PublicKeyInfo (which is always big-endian). + explicit PrivateKeyInfoCodec(bool big_endian); + + ~PrivateKeyInfoCodec(); + + // Exports the contents of the integer components to the ASN.1 DER encoding + // of the PrivateKeyInfo structure to |output|. + bool Export(std::vector* output); + + // Exports the contents of the integer components to the ASN.1 DER encoding + // of the PublicKeyInfo structure to |output|. + bool ExportPublicKeyInfo(std::vector* output); + + // Exports the contents of the integer components to the ASN.1 DER encoding + // of the RSAPublicKey structure to |output|. + bool ExportPublicKey(std::vector* output); + + // Parses the ASN.1 DER encoding of the PrivateKeyInfo structure in |input| + // and populates the integer components with |big_endian_| byte-significance. + // IMPORTANT NOTE: This is currently *not* security-approved for importing + // keys from unstrusted sources. + bool Import(const std::vector& input); + + // Accessors to the contents of the integer components of the PrivateKeyInfo + // structure. + std::vector* modulus() { return &modulus_; }; + std::vector* public_exponent() { return &public_exponent_; }; + std::vector* private_exponent() { return &private_exponent_; }; + std::vector* prime1() { return &prime1_; }; + std::vector* prime2() { return &prime2_; }; + std::vector* exponent1() { return &exponent1_; }; + std::vector* exponent2() { return &exponent2_; }; + std::vector* coefficient() { return &coefficient_; }; + + private: + // Utility wrappers for PrependIntegerImpl that use the class's |big_endian_| + // value. + void PrependInteger(const std::vector& in, std::list* out); + void PrependInteger(uint8* val, int num_bytes, std::list* data); + + // Prepends the integer stored in |val| - |val + num_bytes| with |big_endian| + // byte-significance into |data| as an ASN.1 integer. + void PrependIntegerImpl(uint8* val, + int num_bytes, + std::list* data, + bool big_endian); + + // Utility wrappers for ReadIntegerImpl that use the class's |big_endian_| + // value. + bool ReadInteger(uint8** pos, uint8* end, std::vector* out); + bool ReadIntegerWithExpectedSize(uint8** pos, + uint8* end, + size_t expected_size, + std::vector* out); + + // Reads an ASN.1 integer from |pos|, and stores the result into |out| with + // |big_endian| byte-significance. + bool ReadIntegerImpl(uint8** pos, + uint8* end, + std::vector* out, + bool big_endian); + + // Prepends the integer stored in |val|, starting a index |start|, for + // |num_bytes| bytes onto |data|. + void PrependBytes(uint8* val, + int start, + int num_bytes, + std::list* data); + + // Helper to prepend an ASN.1 length field. + void PrependLength(size_t size, std::list* data); + + // Helper to prepend an ASN.1 type header. + void PrependTypeHeaderAndLength(uint8 type, + uint32 length, + std::list* output); + + // Helper to prepend an ASN.1 bit string + void PrependBitString(uint8* val, int num_bytes, std::list* output); + + // Read an ASN.1 length field. This also checks that the length does not + // extend beyond |end|. + bool ReadLength(uint8** pos, uint8* end, uint32* result); + + // Read an ASN.1 type header and its length. + bool ReadTypeHeaderAndLength(uint8** pos, + uint8* end, + uint8 expected_tag, + uint32* length); + + // Read an ASN.1 sequence declaration. This consumes the type header and + // length field, but not the contents of the sequence. + bool ReadSequence(uint8** pos, uint8* end); + + // Read the RSA AlgorithmIdentifier. + bool ReadAlgorithmIdentifier(uint8** pos, uint8* end); + + // Read one of the two version fields in PrivateKeyInfo. + bool ReadVersion(uint8** pos, uint8* end); + + // The byte-significance of the stored components (modulus, etc..). + bool big_endian_; + + // Component integers of the PrivateKeyInfo + std::vector modulus_; + std::vector public_exponent_; + std::vector private_exponent_; + std::vector prime1_; + std::vector prime2_; + std::vector exponent1_; + std::vector exponent2_; + std::vector coefficient_; + + DISALLOW_COPY_AND_ASSIGN(PrivateKeyInfoCodec); +}; + +// Encapsulates an RSA private key. Can be used to generate new keys, export +// keys to other formats, or to extract a public key. +// TODO(hclam): This class should be ref-counted so it can be reused easily. +class RSAPrivateKey { + public: + ~RSAPrivateKey(); + + // Create a new random instance. Can return NULL if initialization fails. + static RSAPrivateKey* Create(uint16 num_bits); + + // Create a new random instance. Can return NULL if initialization fails. + // The created key is permanent and is not exportable in plaintext form. + // + // NOTE: Currently only available if USE_NSS is defined. + static RSAPrivateKey* CreateSensitive(uint16 num_bits); + + // Create a new instance by importing an existing private key. The format is + // an ASN.1-encoded PrivateKeyInfo block from PKCS #8. This can return NULL if + // initialization fails. + static RSAPrivateKey* CreateFromPrivateKeyInfo( + const std::vector& input); + + // Create a new instance by importing an existing private key. The format is + // an ASN.1-encoded PrivateKeyInfo block from PKCS #8. This can return NULL if + // initialization fails. + // The created key is permanent and is not exportable in plaintext form. + // + // NOTE: Currently only available if USE_NSS is defined. + static RSAPrivateKey* CreateSensitiveFromPrivateKeyInfo( + const std::vector& input); + + // Import an existing public key, and then search for the private + // half in the key database. The format of the public key blob is is + // an X509 SubjectPublicKeyInfo block. This can return NULL if + // initialization fails or the private key cannot be found. The + // caller takes ownership of the returned object, but nothing new is + // created in the key database. + // + // NOTE: Currently only available if USE_NSS is defined. + static RSAPrivateKey* FindFromPublicKeyInfo( + const std::vector& input); + +#if defined(USE_OPENSSL) + EVP_PKEY* key() { return key_; } +#elif defined(USE_NSS) + SECKEYPrivateKeyStr* key() { return key_; } + SECKEYPublicKeyStr* public_key() { return public_key_; } +#elif defined(OS_WIN) + HCRYPTPROV provider() { return provider_; } + HCRYPTKEY key() { return key_; } +#elif defined(OS_MACOSX) + CSSM_KEY_PTR key() { return &key_; } + CSSM_KEY_PTR public_key() { return &public_key_; } +#endif + + // Exports the private key to a PKCS #1 PrivateKey block. + bool ExportPrivateKey(std::vector* output); + + // Exports the public key to an X509 SubjectPublicKeyInfo block. + bool ExportPublicKey(std::vector* output); + + private: +#if defined(USE_NSS) + FRIEND_TEST_ALL_PREFIXES(RSAPrivateKeyNSSTest, FindFromPublicKey); + FRIEND_TEST_ALL_PREFIXES(RSAPrivateKeyNSSTest, FailedFindFromPublicKey); +#endif + + // Constructor is private. Use one of the Create*() or Find*() + // methods above instead. + RSAPrivateKey(); + + // Shared helper for Create() and CreateSensitive(). + // TODO(cmasone): consider replacing |permanent| and |sensitive| with a + // flags arg created by ORing together some enumerated values. + static RSAPrivateKey* CreateWithParams(uint16 num_bits, + bool permanent, + bool sensitive); + + // Shared helper for CreateFromPrivateKeyInfo() and + // CreateSensitiveFromPrivateKeyInfo(). + static RSAPrivateKey* CreateFromPrivateKeyInfoWithParams( + const std::vector& input, bool permanent, bool sensitive); + +#if defined(USE_OPENSSL) + EVP_PKEY* key_; +#elif defined(USE_NSS) + SECKEYPrivateKeyStr* key_; + SECKEYPublicKeyStr* public_key_; +#elif defined(OS_WIN) + bool InitProvider(); + + ScopedHCRYPTPROV provider_; + ScopedHCRYPTKEY key_; +#elif defined(OS_MACOSX) + CSSM_KEY key_; + CSSM_KEY public_key_; +#endif + + DISALLOW_COPY_AND_ASSIGN(RSAPrivateKey); +}; + +} // namespace crypto + +#endif // CRYPTO_RSA_PRIVATE_KEY_H_ diff --git a/crypto/rsa_private_key_mac.cc b/crypto/rsa_private_key_mac.cc new file mode 100644 index 0000000..85dadfa --- /dev/null +++ b/crypto/rsa_private_key_mac.cc @@ -0,0 +1,196 @@ +// Copyright (c) 2011 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 "crypto/rsa_private_key.h" + +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "crypto/cssm_init.h" + +namespace crypto { + +// static +RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) { + scoped_ptr result(new RSAPrivateKey); + + CSSM_CC_HANDLE cc_handle; + CSSM_RETURN crtn; + crtn = CSSM_CSP_CreateKeyGenContext(GetSharedCSPHandle(), CSSM_ALGID_RSA, + num_bits, NULL, NULL, NULL, NULL, NULL, + &cc_handle); + if (crtn) { + NOTREACHED() << "CSSM_CSP_CreateKeyGenContext failed: " << crtn; + return NULL; + } + + CSSM_DATA label = { 9, + const_cast(reinterpret_cast("temp_key")) }; + crtn = CSSM_GenerateKeyPair(cc_handle, + CSSM_KEYUSE_VERIFY, + CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, &label, + result->public_key(), CSSM_KEYUSE_SIGN, + CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, &label, NULL, + result->key()); + CSSM_DeleteContext(cc_handle); + if (crtn) { + NOTREACHED() << "CSSM_CSP_CreateKeyGenContext failed: " << crtn; + return NULL; + } + + return result.release(); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateSensitive(uint16 num_bits) { + NOTIMPLEMENTED(); + return NULL; +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo( + const std::vector& input) { + if (input.empty()) + return NULL; + + scoped_ptr result(new RSAPrivateKey); + + CSSM_KEY key; + memset(&key, 0, sizeof(key)); + key.KeyData.Data = const_cast(&input.front()); + key.KeyData.Length = input.size(); + key.KeyHeader.Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS8; + key.KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION; + key.KeyHeader.BlobType = CSSM_KEYBLOB_RAW; + key.KeyHeader.AlgorithmId = CSSM_ALGID_RSA; + key.KeyHeader.KeyClass = CSSM_KEYCLASS_PRIVATE_KEY; + key.KeyHeader.KeyAttr = CSSM_KEYATTR_EXTRACTABLE; + key.KeyHeader.KeyUsage = CSSM_KEYUSE_ANY; + + CSSM_KEY_SIZE key_size; + CSSM_RETURN crtn; + crtn = CSSM_QueryKeySizeInBits(GetSharedCSPHandle(), NULL, &key, &key_size); + if (crtn) { + NOTREACHED() << "CSSM_QueryKeySizeInBits failed: " << crtn; + return NULL; + } + key.KeyHeader.LogicalKeySizeInBits = key_size.LogicalKeySizeInBits; + + // Perform a NULL unwrap operation on the key so that result's key_ + // instance variable points to a key that can be released via CSSM_FreeKey(). + CSSM_ACCESS_CREDENTIALS creds; + memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); + CSSM_CC_HANDLE cc_handle; + crtn = CSSM_CSP_CreateSymmetricContext(GetSharedCSPHandle(), CSSM_ALGID_NONE, + CSSM_ALGMODE_NONE, &creds, NULL, NULL, CSSM_PADDING_NONE, 0, &cc_handle); + if (crtn) { + NOTREACHED() << "CSSM_CSP_CreateSymmetricContext failed: " << crtn; + return NULL; + } + CSSM_DATA label_data, desc_data = { 0, NULL }; + label_data.Data = + const_cast(reinterpret_cast("unwrapped")); + label_data.Length = 9; + crtn = CSSM_UnwrapKey(cc_handle, NULL, &key, CSSM_KEYUSE_ANY, + CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, &label_data, + NULL, result->key(), &desc_data); + if (crtn) { + NOTREACHED() << "CSSM_UnwrapKey failed: " << crtn; + return NULL; + } + + // Extract a public key from the private key. + // Apple doesn't accept CSSM_KEYBLOB_RAW_FORMAT_X509 as a valid key + // format when attempting to generate certs, so use PKCS1 instead. + PrivateKeyInfoCodec codec(true); + std::vector private_key_data; + private_key_data.assign(key.KeyData.Data, + key.KeyData.Data + key.KeyData.Length); + if (!codec.Import(private_key_data)) { + return NULL; + } + std::vector public_key_data; + if (!codec.ExportPublicKey(&public_key_data)) { + return NULL; + } + + CSSM_KEY* public_key = result->public_key(); + size_t size = public_key_data.size(); + public_key->KeyData.Data = reinterpret_cast(CSSMMalloc(size)); + if (!public_key->KeyData.Data) { + NOTREACHED() << "CSSMMalloc failed"; + return NULL; + } + memcpy(public_key->KeyData.Data, &public_key_data.front(), size); + public_key->KeyData.Length = size; + public_key->KeyHeader.Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1; + public_key->KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION; + public_key->KeyHeader.BlobType = CSSM_KEYBLOB_RAW; + public_key->KeyHeader.AlgorithmId = CSSM_ALGID_RSA; + public_key->KeyHeader.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY; + public_key->KeyHeader.KeyAttr = CSSM_KEYATTR_EXTRACTABLE; + public_key->KeyHeader.KeyUsage = CSSM_KEYUSE_ANY; + + crtn = CSSM_QueryKeySizeInBits(GetSharedCSPHandle(), NULL, public_key, + &key_size); + if (crtn) { + DLOG(ERROR) << "CSSM_QueryKeySizeInBits failed " << crtn; + return NULL; + } + public_key->KeyHeader.LogicalKeySizeInBits = key_size.LogicalKeySizeInBits; + + return result.release(); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateSensitiveFromPrivateKeyInfo( + const std::vector& input) { + NOTIMPLEMENTED(); + return NULL; +} + +// static +RSAPrivateKey* RSAPrivateKey::FindFromPublicKeyInfo( + const std::vector& input) { + NOTIMPLEMENTED(); + return NULL; +} + +RSAPrivateKey::RSAPrivateKey() { + memset(&key_, 0, sizeof(key_)); + memset(&public_key_, 0, sizeof(public_key_)); + + EnsureCSSMInit(); +} + +RSAPrivateKey::~RSAPrivateKey() { + if (key_.KeyData.Data) { + CSSM_FreeKey(GetSharedCSPHandle(), NULL, &key_, CSSM_FALSE); + } + if (public_key_.KeyData.Data) { + CSSM_FreeKey(GetSharedCSPHandle(), NULL, &public_key_, CSSM_FALSE); + } +} + +bool RSAPrivateKey::ExportPrivateKey(std::vector* output) { + if (!key_.KeyData.Data || !key_.KeyData.Length) { + return false; + } + output->clear(); + output->insert(output->end(), key_.KeyData.Data, + key_.KeyData.Data + key_.KeyData.Length); + return true; +} + +bool RSAPrivateKey::ExportPublicKey(std::vector* output) { + PrivateKeyInfoCodec private_key_info(true); + std::vector private_key_data; + private_key_data.assign(key_.KeyData.Data, + key_.KeyData.Data + key_.KeyData.Length); + return (private_key_info.Import(private_key_data) && + private_key_info.ExportPublicKeyInfo(output)); +} + +} // namespace crypto diff --git a/crypto/rsa_private_key_nss.cc b/crypto/rsa_private_key_nss.cc new file mode 100644 index 0000000..f5d4222 --- /dev/null +++ b/crypto/rsa_private_key_nss.cc @@ -0,0 +1,248 @@ +// Copyright (c) 2011 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 "crypto/rsa_private_key.h" + +#include +#include +#include + +#include + +#include "base/debug/leak_annotations.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/string_util.h" +#include "crypto/nss_util.h" +#include "crypto/nss_util_internal.h" + +// TODO(rafaelw): Consider refactoring common functions and definitions from +// rsa_private_key_win.cc or using NSS's ASN.1 encoder. +namespace { + +static bool ReadAttribute(SECKEYPrivateKey* key, + CK_ATTRIBUTE_TYPE type, + std::vector* output) { + SECItem item; + SECStatus rv; + rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, type, &item); + if (rv != SECSuccess) { + NOTREACHED(); + return false; + } + + output->assign(item.data, item.data + item.len); + SECITEM_FreeItem(&item, PR_FALSE); + return true; +} + +} // namespace + +namespace crypto { + +RSAPrivateKey::~RSAPrivateKey() { + if (key_) + SECKEY_DestroyPrivateKey(key_); + if (public_key_) + SECKEY_DestroyPublicKey(public_key_); +} + +// static +RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) { + return CreateWithParams(num_bits, + PR_FALSE /* not permanent */, + PR_FALSE /* not sensitive */); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateSensitive(uint16 num_bits) { + return CreateWithParams(num_bits, + PR_TRUE /* permanent */, + PR_TRUE /* sensitive */); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo( + const std::vector& input) { + return CreateFromPrivateKeyInfoWithParams(input, + PR_FALSE /* not permanent */, + PR_FALSE /* not sensitive */); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateSensitiveFromPrivateKeyInfo( + const std::vector& input) { + return CreateFromPrivateKeyInfoWithParams(input, + PR_TRUE /* permanent */, + PR_TRUE /* seneitive */); +} + +// static +RSAPrivateKey* RSAPrivateKey::FindFromPublicKeyInfo( + const std::vector& input) { + EnsureNSSInit(); + + scoped_ptr result(new RSAPrivateKey); + + // First, decode and save the public key. + SECItem key_der; + key_der.type = siBuffer; + key_der.data = const_cast(&input[0]); + key_der.len = input.size(); + + CERTSubjectPublicKeyInfo *spki = + SECKEY_DecodeDERSubjectPublicKeyInfo(&key_der); + if (!spki) { + NOTREACHED(); + return NULL; + } + + result->public_key_ = SECKEY_ExtractPublicKey(spki); + SECKEY_DestroySubjectPublicKeyInfo(spki); + if (!result->public_key_) { + NOTREACHED(); + return NULL; + } + + // Now, look for the associated private key in the user's NSS DB. If it's + // not there, consider that an error. + PK11SlotInfo *slot = GetPrivateNSSKeySlot(); + if (!slot) { + NOTREACHED(); + return NULL; + } + + // Make sure the key is an RSA key. If not, that's an error + if (result->public_key_->keyType != rsaKey) { + PK11_FreeSlot(slot); + NOTREACHED(); + return NULL; + } + + SECItem *ck_id = PK11_MakeIDFromPubKey(&(result->public_key_->u.rsa.modulus)); + if (!ck_id) { + PK11_FreeSlot(slot); + NOTREACHED(); + return NULL; + } + + // Finally...Look for the key! + result->key_ = PK11_FindKeyByKeyID(slot, ck_id, NULL); + + // Cleanup... + PK11_FreeSlot(slot); + SECITEM_FreeItem(ck_id, PR_TRUE); + + // If we didn't find it, that's ok. + if (!result->key_) + return NULL; + + return result.release(); +} + + +bool RSAPrivateKey::ExportPrivateKey(std::vector* output) { + PrivateKeyInfoCodec private_key_info(true); + + // Manually read the component attributes of the private key and build up + // the PrivateKeyInfo. + if (!ReadAttribute(key_, CKA_MODULUS, private_key_info.modulus()) || + !ReadAttribute(key_, CKA_PUBLIC_EXPONENT, + private_key_info.public_exponent()) || + !ReadAttribute(key_, CKA_PRIVATE_EXPONENT, + private_key_info.private_exponent()) || + !ReadAttribute(key_, CKA_PRIME_1, private_key_info.prime1()) || + !ReadAttribute(key_, CKA_PRIME_2, private_key_info.prime2()) || + !ReadAttribute(key_, CKA_EXPONENT_1, private_key_info.exponent1()) || + !ReadAttribute(key_, CKA_EXPONENT_2, private_key_info.exponent2()) || + !ReadAttribute(key_, CKA_COEFFICIENT, private_key_info.coefficient())) { + NOTREACHED(); + return false; + } + + return private_key_info.Export(output); +} + +bool RSAPrivateKey::ExportPublicKey(std::vector* output) { + SECItem* der_pubkey = SECKEY_EncodeDERSubjectPublicKeyInfo(public_key_); + if (!der_pubkey) { + NOTREACHED(); + return false; + } + + for (size_t i = 0; i < der_pubkey->len; ++i) + output->push_back(der_pubkey->data[i]); + + SECITEM_FreeItem(der_pubkey, PR_TRUE); + return true; +} + +RSAPrivateKey::RSAPrivateKey() : key_(NULL), public_key_(NULL) { + EnsureNSSInit(); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateWithParams(uint16 num_bits, + bool permanent, + bool sensitive) { + EnsureNSSInit(); + + scoped_ptr result(new RSAPrivateKey); + + PK11SlotInfo *slot = GetPrivateNSSKeySlot(); + if (!slot) + return NULL; + + PK11RSAGenParams param; + param.keySizeInBits = num_bits; + param.pe = 65537L; + result->key_ = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, ¶m, + &result->public_key_, permanent, sensitive, NULL); + PK11_FreeSlot(slot); + if (!result->key_) + return NULL; + + return result.release(); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfoWithParams( + const std::vector& input, bool permanent, bool sensitive) { + // This method currently leaks some memory. + // See http://crbug.com/34742. + ANNOTATE_SCOPED_MEMORY_LEAK; + EnsureNSSInit(); + + scoped_ptr result(new RSAPrivateKey); + + PK11SlotInfo *slot = GetPrivateNSSKeySlot(); + if (!slot) + return NULL; + + SECItem der_private_key_info; + der_private_key_info.data = const_cast(&input.front()); + der_private_key_info.len = input.size(); + // Allow the private key to be used for key unwrapping, data decryption, + // and signature generation. + const unsigned int key_usage = KU_KEY_ENCIPHERMENT | KU_DATA_ENCIPHERMENT | + KU_DIGITAL_SIGNATURE; + SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( + slot, &der_private_key_info, NULL, NULL, permanent, sensitive, + key_usage, &result->key_, NULL); + PK11_FreeSlot(slot); + if (rv != SECSuccess) { + NOTREACHED(); + return NULL; + } + + result->public_key_ = SECKEY_ConvertToPublicKey(result->key_); + if (!result->public_key_) { + NOTREACHED(); + return NULL; + } + + return result.release(); +} + +} // namespace crypto diff --git a/crypto/rsa_private_key_nss_unittest.cc b/crypto/rsa_private_key_nss_unittest.cc new file mode 100644 index 0000000..d3370fa --- /dev/null +++ b/crypto/rsa_private_key_nss_unittest.cc @@ -0,0 +1,64 @@ +// Copyright (c) 2011 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 "crypto/rsa_private_key.h" + +#include +#include + +#include "base/memory/scoped_ptr.h" +#include "base/nss_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace crypto { + +class RSAPrivateKeyNSSTest : public testing::Test { + public: + RSAPrivateKeyNSSTest() {} + virtual ~RSAPrivateKeyNSSTest() {} + + virtual void SetUp() { +#if defined(OS_CHROMEOS) + OpenPersistentNSSDB(); +#endif + } + + private: + DISALLOW_COPY_AND_ASSIGN(RSAPrivateKeyNSSTest); +}; + +TEST_F(RSAPrivateKeyNSSTest, FindFromPublicKey) { + // Create a keypair, which will put the keys in the user's NSSDB. + scoped_ptr key_pair(RSAPrivateKey::Create(256)); + + std::vector public_key; + ASSERT_TRUE(key_pair->ExportPublicKey(&public_key)); + + scoped_ptr key_pair_2( + crypto::RSAPrivateKey::FindFromPublicKeyInfo(public_key)); + + EXPECT_EQ(key_pair->key_->pkcs11ID, key_pair_2->key_->pkcs11ID); +} + +TEST_F(RSAPrivateKeyNSSTest, FailedFindFromPublicKey) { + // Create a keypair, which will put the keys in the user's NSSDB. + scoped_ptr key_pair(RSAPrivateKey::Create(256)); + + std::vector public_key; + ASSERT_TRUE(key_pair->ExportPublicKey(&public_key)); + + // Remove the keys from the DB, and make sure we can't find them again. + if (key_pair->key_) { + PK11_DestroyTokenObject(key_pair->key_->pkcs11Slot, + key_pair->key_->pkcs11ID); + } + if (key_pair->public_key_) { + PK11_DestroyTokenObject(key_pair->public_key_->pkcs11Slot, + key_pair->public_key_->pkcs11ID); + } + + EXPECT_EQ(NULL, crypto::RSAPrivateKey::FindFromPublicKeyInfo(public_key)); +} + +} // namespace crypto diff --git a/crypto/rsa_private_key_openssl.cc b/crypto/rsa_private_key_openssl.cc new file mode 100644 index 0000000..f39b718 --- /dev/null +++ b/crypto/rsa_private_key_openssl.cc @@ -0,0 +1,135 @@ +// Copyright (c) 2011 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 "crypto/rsa_private_key.h" + +#include +#include +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util-inl.h" +#include "crypto/openssl_util.h" + +namespace crypto { + +namespace { + +// Function pointer definition, for injecting the required key export function +// into ExportKey, below. The supplied function should export EVP_PKEY into +// the supplied BIO, returning 1 on success or 0 on failure. +typedef int (ExportFunction)(BIO*, EVP_PKEY*); + +// Helper to export |key| into |output| via the specified ExportFunction. +bool ExportKey(EVP_PKEY* key, + ExportFunction export_fn, + std::vector* output) { + if (!key) + return false; + + OpenSSLErrStackTracer err_tracer(FROM_HERE); + ScopedOpenSSL bio(BIO_new(BIO_s_mem())); + + int res = export_fn(bio.get(), key); + if (!res) + return false; + + char* data = NULL; + long len = BIO_get_mem_data(bio.get(), &data); + if (!data || len < 0) + return false; + + STLAssignToVector(output, reinterpret_cast(data), len); + return true; +} + +} // namespace + +// static +RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + + ScopedOpenSSL rsa_key(RSA_new()); + ScopedOpenSSL bn(BN_new()); + if (!rsa_key.get() || !bn.get() || !BN_set_word(bn.get(), 65537L)) + return NULL; + + if (!RSA_generate_key_ex(rsa_key.get(), num_bits, bn.get(), NULL)) + return NULL; + + scoped_ptr result(new RSAPrivateKey); + result->key_ = EVP_PKEY_new(); + if (!result->key_ || !EVP_PKEY_set1_RSA(result->key_, rsa_key.get())) + return NULL; + + return result.release(); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateSensitive(uint16 num_bits) { + NOTIMPLEMENTED(); + return NULL; +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo( + const std::vector& input) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + + // BIO_new_mem_buf is not const aware, but it does not modify the buffer. + char* data = reinterpret_cast(const_cast( + vector_as_array(&input))); + ScopedOpenSSL bio(BIO_new_mem_buf(data, input.size())); + if (!bio.get()) + return NULL; + + // Importing is a little more involved than exporting, as we must first + // PKCS#8 decode the input, and then import the EVP_PKEY from Private Key + // Info structure returned. + ScopedOpenSSL p8inf( + d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), NULL)); + if (!p8inf.get()) + return NULL; + + scoped_ptr result(new RSAPrivateKey); + result->key_ = EVP_PKCS82PKEY(p8inf.get()); + if (!result->key_) + return NULL; + + return result.release(); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateSensitiveFromPrivateKeyInfo( + const std::vector& input) { + NOTIMPLEMENTED(); + return NULL; +} + +// static +RSAPrivateKey* RSAPrivateKey::FindFromPublicKeyInfo( + const std::vector& input) { + NOTIMPLEMENTED(); + return NULL; +} + +RSAPrivateKey::RSAPrivateKey() + : key_(NULL) { +} + +RSAPrivateKey::~RSAPrivateKey() { + if (key_) + EVP_PKEY_free(key_); +} + +bool RSAPrivateKey::ExportPrivateKey(std::vector* output) { + return ExportKey(key_, i2d_PKCS8PrivateKeyInfo_bio, output); +} + +bool RSAPrivateKey::ExportPublicKey(std::vector* output) { + return ExportKey(key_, i2d_PUBKEY_bio, output); +} + +} // namespace crypto diff --git a/crypto/rsa_private_key_unittest.cc b/crypto/rsa_private_key_unittest.cc new file mode 100644 index 0000000..1fbc03e --- /dev/null +++ b/crypto/rsa_private_key_unittest.cc @@ -0,0 +1,386 @@ +// Copyright (c) 2011 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 "crypto/rsa_private_key.h" + +#include "base/memory/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +// Generate random private keys with two different sizes. Reimport, then +// export them again. We should get back the same exact bytes. +TEST(RSAPrivateKeyUnitTest, InitRandomTest) { + scoped_ptr keypair1( + crypto::RSAPrivateKey::Create(1024)); + scoped_ptr keypair2( + crypto::RSAPrivateKey::Create(2048)); + ASSERT_TRUE(keypair1.get()); + ASSERT_TRUE(keypair2.get()); + + std::vector privkey1; + std::vector privkey2; + std::vector pubkey1; + std::vector pubkey2; + + ASSERT_TRUE(keypair1->ExportPrivateKey(&privkey1)); + ASSERT_TRUE(keypair2->ExportPrivateKey(&privkey2)); + ASSERT_TRUE(keypair1->ExportPublicKey(&pubkey1)); + ASSERT_TRUE(keypair2->ExportPublicKey(&pubkey2)); + + scoped_ptr keypair3( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(privkey1)); + scoped_ptr keypair4( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(privkey2)); + ASSERT_TRUE(keypair3.get()); + ASSERT_TRUE(keypair4.get()); + + std::vector privkey3; + std::vector privkey4; + ASSERT_TRUE(keypair3->ExportPrivateKey(&privkey3)); + ASSERT_TRUE(keypair4->ExportPrivateKey(&privkey4)); + + ASSERT_EQ(privkey1.size(), privkey3.size()); + ASSERT_EQ(privkey2.size(), privkey4.size()); + ASSERT_TRUE(0 == memcmp(&privkey1.front(), &privkey3.front(), + privkey1.size())); + ASSERT_TRUE(0 == memcmp(&privkey2.front(), &privkey4.front(), + privkey2.size())); +} + + +// Verify that generated public keys look good. This test data was generated +// with the openssl command line tool. +TEST(RSAPrivateKeyUnitTest, PublicKeyTest) { + const uint8 private_key_info[] = { + 0x30, 0x82, 0x02, 0x78, 0x02, 0x01, 0x00, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x02, 0x62, 0x30, 0x82, 0x02, 0x5e, 0x02, 0x01, + 0x00, 0x02, 0x81, 0x81, 0x00, 0xb8, 0x7f, 0x2b, + 0x20, 0xdc, 0x7c, 0x9b, 0x0c, 0xdc, 0x51, 0x61, + 0x99, 0x0d, 0x36, 0x0f, 0xd4, 0x66, 0x88, 0x08, + 0x55, 0x84, 0xd5, 0x3a, 0xbf, 0x2b, 0xa4, 0x64, + 0x85, 0x7b, 0x0c, 0x04, 0x13, 0x3f, 0x8d, 0xf4, + 0xbc, 0x38, 0x0d, 0x49, 0xfe, 0x6b, 0xc4, 0x5a, + 0xb0, 0x40, 0x53, 0x3a, 0xd7, 0x66, 0x09, 0x0f, + 0x9e, 0x36, 0x74, 0x30, 0xda, 0x8a, 0x31, 0x4f, + 0x1f, 0x14, 0x50, 0xd7, 0xc7, 0x20, 0x94, 0x17, + 0xde, 0x4e, 0xb9, 0x57, 0x5e, 0x7e, 0x0a, 0xe5, + 0xb2, 0x65, 0x7a, 0x89, 0x4e, 0xb6, 0x47, 0xff, + 0x1c, 0xbd, 0xb7, 0x38, 0x13, 0xaf, 0x47, 0x85, + 0x84, 0x32, 0x33, 0xf3, 0x17, 0x49, 0xbf, 0xe9, + 0x96, 0xd0, 0xd6, 0x14, 0x6f, 0x13, 0x8d, 0xc5, + 0xfc, 0x2c, 0x72, 0xba, 0xac, 0xea, 0x7e, 0x18, + 0x53, 0x56, 0xa6, 0x83, 0xa2, 0xce, 0x93, 0x93, + 0xe7, 0x1f, 0x0f, 0xe6, 0x0f, 0x02, 0x03, 0x01, + 0x00, 0x01, 0x02, 0x81, 0x80, 0x03, 0x61, 0x89, + 0x37, 0xcb, 0xf2, 0x98, 0xa0, 0xce, 0xb4, 0xcb, + 0x16, 0x13, 0xf0, 0xe6, 0xaf, 0x5c, 0xc5, 0xa7, + 0x69, 0x71, 0xca, 0xba, 0x8d, 0xe0, 0x4d, 0xdd, + 0xed, 0xb8, 0x48, 0x8b, 0x16, 0x93, 0x36, 0x95, + 0xc2, 0x91, 0x40, 0x65, 0x17, 0xbd, 0x7f, 0xd6, + 0xad, 0x9e, 0x30, 0x28, 0x46, 0xe4, 0x3e, 0xcc, + 0x43, 0x78, 0xf9, 0xfe, 0x1f, 0x33, 0x23, 0x1e, + 0x31, 0x12, 0x9d, 0x3c, 0xa7, 0x08, 0x82, 0x7b, + 0x7d, 0x25, 0x4e, 0x5e, 0x19, 0xa8, 0x9b, 0xed, + 0x86, 0xb2, 0xcb, 0x3c, 0xfe, 0x4e, 0xa1, 0xfa, + 0x62, 0x87, 0x3a, 0x17, 0xf7, 0x60, 0xec, 0x38, + 0x29, 0xe8, 0x4f, 0x34, 0x9f, 0x76, 0x9d, 0xee, + 0xa3, 0xf6, 0x85, 0x6b, 0x84, 0x43, 0xc9, 0x1e, + 0x01, 0xff, 0xfd, 0xd0, 0x29, 0x4c, 0xfa, 0x8e, + 0x57, 0x0c, 0xc0, 0x71, 0xa5, 0xbb, 0x88, 0x46, + 0x29, 0x5c, 0xc0, 0x4f, 0x01, 0x02, 0x41, 0x00, + 0xf5, 0x83, 0xa4, 0x64, 0x4a, 0xf2, 0xdd, 0x8c, + 0x2c, 0xed, 0xa8, 0xd5, 0x60, 0x5a, 0xe4, 0xc7, + 0xcc, 0x61, 0xcd, 0x38, 0x42, 0x20, 0xd3, 0x82, + 0x18, 0xf2, 0x35, 0x00, 0x72, 0x2d, 0xf7, 0x89, + 0x80, 0x67, 0xb5, 0x93, 0x05, 0x5f, 0xdd, 0x42, + 0xba, 0x16, 0x1a, 0xea, 0x15, 0xc6, 0xf0, 0xb8, + 0x8c, 0xbc, 0xbf, 0x54, 0x9e, 0xf1, 0xc1, 0xb2, + 0xb3, 0x8b, 0xb6, 0x26, 0x02, 0x30, 0xc4, 0x81, + 0x02, 0x41, 0x00, 0xc0, 0x60, 0x62, 0x80, 0xe1, + 0x22, 0x78, 0xf6, 0x9d, 0x83, 0x18, 0xeb, 0x72, + 0x45, 0xd7, 0xc8, 0x01, 0x7f, 0xa9, 0xca, 0x8f, + 0x7d, 0xd6, 0xb8, 0x31, 0x2b, 0x84, 0x7f, 0x62, + 0xd9, 0xa9, 0x22, 0x17, 0x7d, 0x06, 0x35, 0x6c, + 0xf3, 0xc1, 0x94, 0x17, 0x85, 0x5a, 0xaf, 0x9c, + 0x5c, 0x09, 0x3c, 0xcf, 0x2f, 0x44, 0x9d, 0xb6, + 0x52, 0x68, 0x5f, 0xf9, 0x59, 0xc8, 0x84, 0x2b, + 0x39, 0x22, 0x8f, 0x02, 0x41, 0x00, 0xb2, 0x04, + 0xe2, 0x0e, 0x56, 0xca, 0x03, 0x1a, 0xc0, 0xf9, + 0x12, 0x92, 0xa5, 0x6b, 0x42, 0xb8, 0x1c, 0xda, + 0x4d, 0x93, 0x9d, 0x5f, 0x6f, 0xfd, 0xc5, 0x58, + 0xda, 0x55, 0x98, 0x74, 0xfc, 0x28, 0x17, 0x93, + 0x1b, 0x75, 0x9f, 0x50, 0x03, 0x7f, 0x7e, 0xae, + 0xc8, 0x95, 0x33, 0x75, 0x2c, 0xd6, 0xa4, 0x35, + 0xb8, 0x06, 0x03, 0xba, 0x08, 0x59, 0x2b, 0x17, + 0x02, 0xdc, 0x4c, 0x7a, 0x50, 0x01, 0x02, 0x41, + 0x00, 0x9d, 0xdb, 0x39, 0x59, 0x09, 0xe4, 0x30, + 0xa0, 0x24, 0xf5, 0xdb, 0x2f, 0xf0, 0x2f, 0xf1, + 0x75, 0x74, 0x0d, 0x5e, 0xb5, 0x11, 0x73, 0xb0, + 0x0a, 0xaa, 0x86, 0x4c, 0x0d, 0xff, 0x7e, 0x1d, + 0xb4, 0x14, 0xd4, 0x09, 0x91, 0x33, 0x5a, 0xfd, + 0xa0, 0x58, 0x80, 0x9b, 0xbe, 0x78, 0x2e, 0x69, + 0x82, 0x15, 0x7c, 0x72, 0xf0, 0x7b, 0x18, 0x39, + 0xff, 0x6e, 0xeb, 0xc6, 0x86, 0xf5, 0xb4, 0xc7, + 0x6f, 0x02, 0x41, 0x00, 0x8d, 0x1a, 0x37, 0x0f, + 0x76, 0xc4, 0x82, 0xfa, 0x5c, 0xc3, 0x79, 0x35, + 0x3e, 0x70, 0x8a, 0xbf, 0x27, 0x49, 0xb0, 0x99, + 0x63, 0xcb, 0x77, 0x5f, 0xa8, 0x82, 0x65, 0xf6, + 0x03, 0x52, 0x51, 0xf1, 0xae, 0x2e, 0x05, 0xb3, + 0xc6, 0xa4, 0x92, 0xd1, 0xce, 0x6c, 0x72, 0xfb, + 0x21, 0xb3, 0x02, 0x87, 0xe4, 0xfd, 0x61, 0xca, + 0x00, 0x42, 0x19, 0xf0, 0xda, 0x5a, 0x53, 0xe3, + 0xb1, 0xc5, 0x15, 0xf3 + }; + + const uint8 expected_public_key_info[] = { + 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, + 0x89, 0x02, 0x81, 0x81, 0x00, 0xb8, 0x7f, 0x2b, + 0x20, 0xdc, 0x7c, 0x9b, 0x0c, 0xdc, 0x51, 0x61, + 0x99, 0x0d, 0x36, 0x0f, 0xd4, 0x66, 0x88, 0x08, + 0x55, 0x84, 0xd5, 0x3a, 0xbf, 0x2b, 0xa4, 0x64, + 0x85, 0x7b, 0x0c, 0x04, 0x13, 0x3f, 0x8d, 0xf4, + 0xbc, 0x38, 0x0d, 0x49, 0xfe, 0x6b, 0xc4, 0x5a, + 0xb0, 0x40, 0x53, 0x3a, 0xd7, 0x66, 0x09, 0x0f, + 0x9e, 0x36, 0x74, 0x30, 0xda, 0x8a, 0x31, 0x4f, + 0x1f, 0x14, 0x50, 0xd7, 0xc7, 0x20, 0x94, 0x17, + 0xde, 0x4e, 0xb9, 0x57, 0x5e, 0x7e, 0x0a, 0xe5, + 0xb2, 0x65, 0x7a, 0x89, 0x4e, 0xb6, 0x47, 0xff, + 0x1c, 0xbd, 0xb7, 0x38, 0x13, 0xaf, 0x47, 0x85, + 0x84, 0x32, 0x33, 0xf3, 0x17, 0x49, 0xbf, 0xe9, + 0x96, 0xd0, 0xd6, 0x14, 0x6f, 0x13, 0x8d, 0xc5, + 0xfc, 0x2c, 0x72, 0xba, 0xac, 0xea, 0x7e, 0x18, + 0x53, 0x56, 0xa6, 0x83, 0xa2, 0xce, 0x93, 0x93, + 0xe7, 0x1f, 0x0f, 0xe6, 0x0f, 0x02, 0x03, 0x01, + 0x00, 0x01 + }; + + std::vector input; + input.resize(sizeof(private_key_info)); + memcpy(&input.front(), private_key_info, sizeof(private_key_info)); + + scoped_ptr key( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(input)); + ASSERT_TRUE(key.get()); + + std::vector output; + ASSERT_TRUE(key->ExportPublicKey(&output)); + + ASSERT_TRUE( + memcmp(expected_public_key_info, &output.front(), output.size()) == 0); +} + +// These two test keys each contain an integer that has 0x00 for its most +// significant byte. When encoded as ASN.1, this byte is dropped and there are +// two interesting sub-cases. When the sign bit of the integer is set, an extra +// null byte is added back to force the encoded value to be positive. When the +// sign bit is not set, the encoded integer is just left shorter than usual. +// See also: http://code.google.com/p/chromium/issues/detail?id=14877. +// +// Before we were handling this correctly, we would see one of two failures: +// * RSAPrivateKey::CreateFromPrivateKeyInfo would return null because the +// underlying windows API failed to import the key. +// * The import would succeed, but incorrectly interpret the data. On export, +// the key would contain different values. +// +// This test case verifies these two failures modes don't occur. +TEST(RSAPrivateKeyUnitTest, ShortIntegers) { + const uint8 short_integer_with_high_bit[] = { + 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x00, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x02, 0x61, 0x30, 0x82, 0x02, 0x5d, 0x02, 0x01, + 0x00, 0x02, 0x81, 0x81, 0x00, 0x92, 0x59, 0x32, + 0x7d, 0x8e, 0xaf, 0x2e, 0xd5, 0xb2, 0x5c, 0x67, + 0xc8, 0x7d, 0x48, 0xb7, 0x84, 0x12, 0xd0, 0x76, + 0xda, 0xe1, 0xa3, 0x1e, 0x40, 0x01, 0x14, 0x5c, + 0xef, 0x26, 0x6e, 0x28, 0xa2, 0xf7, 0xa5, 0xb4, + 0x02, 0x37, 0xd0, 0x53, 0x10, 0xcb, 0x7c, 0x6a, + 0xf4, 0x53, 0x9f, 0xb8, 0xe0, 0x83, 0x93, 0xd1, + 0x19, 0xd8, 0x28, 0xd1, 0xd1, 0xd8, 0x87, 0x8f, + 0x92, 0xfd, 0x73, 0xc0, 0x4d, 0x3e, 0x07, 0x22, + 0x1f, 0xc1, 0x20, 0xb0, 0x70, 0xb2, 0x3b, 0xea, + 0xb1, 0xe5, 0x0a, 0xfd, 0x56, 0x49, 0x5e, 0x39, + 0x90, 0x91, 0xce, 0x04, 0x83, 0x29, 0xaa, 0xfd, + 0x12, 0xa4, 0x42, 0x26, 0x6c, 0x6e, 0x79, 0x70, + 0x77, 0x03, 0xb2, 0x07, 0x01, 0x3d, 0x85, 0x81, + 0x95, 0x9e, 0xda, 0x5a, 0xa3, 0xf4, 0x2d, 0x38, + 0x04, 0x58, 0xf5, 0x6b, 0xc9, 0xf1, 0xb5, 0x65, + 0xfe, 0x66, 0x0d, 0xa2, 0xd5, 0x02, 0x03, 0x01, + 0x00, 0x01, 0x02, 0x81, 0x80, 0x5e, 0x01, 0x5f, + 0xb6, 0x59, 0x1d, 0xdc, 0x36, 0xb6, 0x60, 0x36, + 0xe6, 0x08, 0xdb, 0xd9, 0xcd, 0xc3, 0x8c, 0x16, + 0x9c, 0x98, 0x8d, 0x7f, 0xd3, 0xdb, 0x1d, 0xaa, + 0x68, 0x8f, 0xc5, 0xf8, 0xe2, 0x5d, 0xb3, 0x19, + 0xc2, 0xc6, 0xf9, 0x51, 0x32, 0x1b, 0x93, 0x6a, + 0xdc, 0x50, 0x8e, 0xeb, 0x61, 0x84, 0x03, 0x42, + 0x30, 0x98, 0xb1, 0xf7, 0xbd, 0x14, 0x9a, 0x57, + 0x36, 0x33, 0x09, 0xd4, 0x3e, 0x90, 0xda, 0xef, + 0x09, 0x6e, 0xef, 0x49, 0xb6, 0x60, 0x68, 0x5e, + 0x54, 0x17, 0x25, 0x5b, 0x37, 0xe3, 0x35, 0x63, + 0x5b, 0x60, 0x3c, 0xbd, 0x50, 0xdf, 0x46, 0x43, + 0x08, 0xa4, 0x71, 0x21, 0xf1, 0x30, 0x71, 0xdc, + 0xda, 0xd7, 0x6f, 0xd2, 0x18, 0xbd, 0x39, 0xf1, + 0xe1, 0xbe, 0xa8, 0x8d, 0x62, 0xdf, 0xa2, 0x3e, + 0xb6, 0x15, 0x26, 0xb6, 0x57, 0xbd, 0x63, 0xdb, + 0xc1, 0x91, 0xec, 0xb8, 0x01, 0x02, 0x41, 0x00, + 0xc6, 0x1a, 0x06, 0x48, 0xf2, 0x12, 0x1c, 0x9f, + 0x74, 0x20, 0x5c, 0x85, 0xa2, 0xda, 0xe5, 0x62, + 0x96, 0x8d, 0x22, 0x7b, 0x78, 0x73, 0xea, 0xbb, + 0x9f, 0x59, 0x42, 0x13, 0x15, 0xc8, 0x11, 0x50, + 0x6c, 0x55, 0xf6, 0xdf, 0x8b, 0xfe, 0xc7, 0xdd, + 0xa8, 0xca, 0x54, 0x41, 0xe8, 0xce, 0xbe, 0x7d, + 0xbd, 0xe2, 0x13, 0x4b, 0x5b, 0x61, 0xeb, 0x69, + 0x6c, 0xb1, 0x9b, 0x28, 0x68, 0x5b, 0xd6, 0x01, + 0x02, 0x41, 0x00, 0xbd, 0x1e, 0xfe, 0x51, 0x99, + 0xb6, 0xe3, 0x84, 0xfe, 0xf1, 0x9e, 0xfd, 0x9c, + 0xe7, 0x86, 0x43, 0x68, 0x7f, 0x2f, 0x6a, 0x2a, + 0x4c, 0xae, 0xa6, 0x41, 0x1c, 0xf0, 0x10, 0x37, + 0x54, 0x23, 0xba, 0x05, 0x0d, 0x18, 0x27, 0x8d, + 0xb8, 0xe4, 0x8f, 0xf2, 0x25, 0x73, 0x8a, 0xd7, + 0x05, 0x98, 0x6b, 0x3d, 0x55, 0xb7, 0x6f, 0x7c, + 0xec, 0x77, 0x61, 0x54, 0x7b, 0xb6, 0x6b, 0x31, + 0xec, 0x94, 0xd5, 0x02, 0x41, 0x00, 0x90, 0xa2, + 0xa5, 0x9e, 0x12, 0xa7, 0x68, 0xa0, 0x7e, 0xdf, + 0xb5, 0xcd, 0x98, 0x26, 0xab, 0xbd, 0xbc, 0x5f, + 0xd5, 0x22, 0x42, 0xc2, 0x97, 0x4a, 0x5f, 0x40, + 0x82, 0xfe, 0x7e, 0x33, 0xb1, 0x78, 0x7f, 0x70, + 0x90, 0x2b, 0x8d, 0x01, 0xfb, 0x18, 0xfa, 0x48, + 0xa7, 0x15, 0xec, 0x0d, 0x2e, 0x85, 0x8d, 0xe2, + 0x86, 0xe5, 0xc9, 0x15, 0x88, 0x14, 0x53, 0xd8, + 0xa4, 0x88, 0xef, 0x10, 0xc6, 0x01, 0x02, 0x41, + 0x00, 0xba, 0xe4, 0xaf, 0x14, 0xfa, 0xdf, 0xf6, + 0xd5, 0xce, 0x8f, 0xfe, 0xbb, 0xc8, 0x5c, 0x30, + 0x9d, 0xda, 0xdd, 0x9d, 0x80, 0xc0, 0x0e, 0x89, + 0xa5, 0xb8, 0xc1, 0x1d, 0x28, 0x19, 0x55, 0x67, + 0xfd, 0x03, 0xd2, 0xdd, 0xe4, 0xf0, 0xb4, 0x20, + 0x03, 0x74, 0x9b, 0xb8, 0x24, 0x23, 0xbb, 0xde, + 0xd5, 0x53, 0x86, 0xaa, 0xc1, 0x5d, 0x65, 0xdd, + 0xcf, 0xec, 0x8a, 0x59, 0x4a, 0x73, 0xca, 0xc5, + 0x85, 0x02, 0x40, 0x00, 0xc4, 0x5e, 0x8d, 0xa4, + 0xea, 0xbb, 0x6a, 0x9b, 0xe6, 0x3a, 0x4d, 0xc1, + 0xdb, 0xe5, 0x52, 0x38, 0xf9, 0x59, 0x91, 0x2d, + 0x90, 0x82, 0xe3, 0x31, 0x1b, 0x48, 0xb7, 0x42, + 0xfa, 0x1d, 0x83, 0xd5, 0x3d, 0x02, 0xc2, 0x12, + 0x71, 0x10, 0x3a, 0xbd, 0x92, 0x8f, 0x9b, 0xa2, + 0x6b, 0x2d, 0x21, 0xa4, 0x65, 0xe9, 0xfa, 0x8c, + 0x30, 0x2a, 0x89, 0xce, 0xd0, 0xa7, 0x67, 0xd8, + 0x45, 0x84, 0xb0 + }; + + const uint8 short_integer_without_high_bit[] = { + 0x30, 0x82, 0x02, 0x76, 0x02, 0x01, 0x00, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x02, 0x60, 0x30, 0x82, 0x02, 0x5c, 0x02, 0x01, + 0x00, 0x02, 0x81, 0x81, 0x00, 0xc3, 0x9e, 0x8d, + 0xc4, 0x6d, 0x38, 0xe8, 0x0e, 0x9f, 0x84, 0x03, + 0x40, 0x8e, 0x81, 0x2e, 0x56, 0x67, 0x78, 0x11, + 0x85, 0x27, 0x81, 0x52, 0xf2, 0x1b, 0x3e, 0x5b, + 0xf8, 0xab, 0xfc, 0xaf, 0xca, 0x5c, 0x26, 0xd5, + 0xfa, 0xd4, 0x55, 0x50, 0x38, 0xb9, 0x9d, 0x89, + 0x92, 0x7e, 0x34, 0xcf, 0x37, 0x82, 0x48, 0x2d, + 0xaa, 0xc4, 0x6a, 0x0e, 0x93, 0xea, 0xad, 0x8a, + 0x33, 0xf0, 0x42, 0x23, 0xe0, 0x4c, 0x98, 0xbf, + 0x01, 0x00, 0x1b, 0xfe, 0x06, 0x15, 0xc6, 0xe3, + 0x80, 0x79, 0x6d, 0xfe, 0x48, 0xcd, 0x40, 0xbb, + 0xf9, 0x58, 0xe6, 0xbf, 0xd5, 0x4c, 0x29, 0x48, + 0x53, 0x78, 0x06, 0x03, 0x0d, 0x59, 0xf5, 0x20, + 0xe0, 0xe6, 0x8c, 0xb2, 0xf5, 0xd8, 0x61, 0x52, + 0x7e, 0x40, 0x83, 0xd7, 0x69, 0xae, 0xd7, 0x75, + 0x02, 0x2d, 0x49, 0xd5, 0x15, 0x5b, 0xf1, 0xd9, + 0x4d, 0x60, 0x7d, 0x62, 0xa5, 0x02, 0x03, 0x01, + 0x00, 0x01, 0x02, 0x7f, 0x6d, 0x45, 0x23, 0xeb, + 0x95, 0x17, 0x34, 0x88, 0xf6, 0x91, 0xc7, 0x3f, + 0x48, 0x5a, 0xe0, 0x87, 0x63, 0x44, 0xae, 0x84, + 0xb2, 0x8c, 0x8a, 0xc8, 0xb2, 0x6f, 0x22, 0xf0, + 0xc5, 0x21, 0x61, 0x10, 0xa8, 0x69, 0x09, 0x1e, + 0x13, 0x7d, 0x94, 0x52, 0x1b, 0x5c, 0xe4, 0x7b, + 0xf0, 0x03, 0x8f, 0xbc, 0x72, 0x09, 0xdf, 0x78, + 0x84, 0x3e, 0xb9, 0xe5, 0xe6, 0x31, 0x0a, 0x01, + 0xf9, 0x32, 0xf8, 0xd6, 0x57, 0xa3, 0x87, 0xe6, + 0xf5, 0x98, 0xbc, 0x8e, 0x41, 0xb9, 0x50, 0x17, + 0x7b, 0xd3, 0x97, 0x5a, 0x44, 0x3a, 0xee, 0xff, + 0x6b, 0xb3, 0x3a, 0x52, 0xe7, 0xa4, 0x96, 0x9a, + 0xf6, 0x83, 0xc8, 0x97, 0x1c, 0x63, 0xa1, 0xd6, + 0xb3, 0xa8, 0xb2, 0xc7, 0x73, 0x25, 0x0f, 0x58, + 0x36, 0xb9, 0x7a, 0x47, 0xa7, 0x4d, 0x30, 0xfe, + 0x4d, 0x74, 0x56, 0xe8, 0xfb, 0xd6, 0x50, 0xe5, + 0xe0, 0x28, 0x15, 0x02, 0x41, 0x00, 0xeb, 0x15, + 0x62, 0xb6, 0x37, 0x41, 0x7c, 0xc5, 0x00, 0x22, + 0x2c, 0x5a, 0x5e, 0xe4, 0xb2, 0x11, 0x87, 0x89, + 0xad, 0xf4, 0x57, 0x68, 0x90, 0xb7, 0x9f, 0xe2, + 0x79, 0x20, 0x6b, 0x98, 0x00, 0x0d, 0x3a, 0x3b, + 0xc1, 0xcd, 0x36, 0xf9, 0x27, 0xda, 0x40, 0x36, + 0x1d, 0xb8, 0x5c, 0x96, 0xeb, 0x04, 0x08, 0xe1, + 0x3f, 0xfa, 0x94, 0x8b, 0x0f, 0xa0, 0xff, 0xc1, + 0x51, 0xea, 0x90, 0xad, 0x15, 0xc7, 0x02, 0x41, + 0x00, 0xd5, 0x06, 0x45, 0xd7, 0x55, 0x63, 0x1a, + 0xf0, 0x89, 0x81, 0xae, 0x87, 0x23, 0xa2, 0x39, + 0xfe, 0x3d, 0x82, 0xc7, 0xcb, 0x15, 0xb9, 0xe3, + 0xe2, 0x5b, 0xc6, 0xd2, 0x55, 0xdd, 0xab, 0x55, + 0x29, 0x7c, 0xda, 0x0e, 0x1c, 0x09, 0xfc, 0x73, + 0x0d, 0x01, 0xed, 0x6d, 0x2f, 0x05, 0xd0, 0xd5, + 0x1d, 0xce, 0x18, 0x7f, 0xb0, 0xc8, 0x47, 0x77, + 0xd2, 0xa9, 0x9e, 0xfc, 0x39, 0x4b, 0x3d, 0x94, + 0x33, 0x02, 0x41, 0x00, 0x8f, 0x94, 0x09, 0x2d, + 0x17, 0x44, 0x75, 0x0a, 0xf1, 0x10, 0xee, 0x1b, + 0xe7, 0xd7, 0x2f, 0xf6, 0xca, 0xdc, 0x49, 0x15, + 0x72, 0x09, 0x58, 0x51, 0xfe, 0x61, 0xd8, 0xee, + 0xf7, 0x27, 0xe7, 0xe8, 0x2c, 0x47, 0xf1, 0x0f, + 0x00, 0x63, 0x5e, 0x76, 0xcb, 0x3f, 0x02, 0x19, + 0xe6, 0xda, 0xfa, 0x01, 0x05, 0xd7, 0x65, 0x37, + 0x0b, 0x60, 0x7f, 0x94, 0x2a, 0x80, 0x8d, 0x22, + 0x81, 0x68, 0x65, 0x63, 0x02, 0x41, 0x00, 0xc2, + 0xd4, 0x18, 0xde, 0x47, 0x9e, 0xfb, 0x8d, 0x91, + 0x05, 0xc5, 0x3c, 0x9d, 0xcf, 0x8a, 0x60, 0xc7, + 0x9b, 0x2b, 0xe5, 0xc6, 0xba, 0x1b, 0xfc, 0xf3, + 0xd9, 0x54, 0x97, 0xe9, 0xc4, 0x00, 0x80, 0x90, + 0x4a, 0xd2, 0x6a, 0xbc, 0x8b, 0x62, 0x22, 0x3c, + 0x68, 0x0c, 0xda, 0xdb, 0xe3, 0xd2, 0x76, 0x8e, + 0xff, 0x03, 0x12, 0x09, 0x2a, 0xac, 0x21, 0x44, + 0xb7, 0x3e, 0x91, 0x9c, 0x09, 0xf6, 0xd7, 0x02, + 0x41, 0x00, 0xc0, 0xa1, 0xbb, 0x70, 0xdc, 0xf8, + 0xeb, 0x17, 0x61, 0xd4, 0x8c, 0x7c, 0x3b, 0x82, + 0x91, 0x58, 0xff, 0xf9, 0x19, 0xac, 0x3a, 0x73, + 0xa7, 0x20, 0xe5, 0x22, 0x02, 0xc4, 0xf6, 0xb9, + 0xb9, 0x43, 0x53, 0x35, 0x88, 0xe1, 0x05, 0xb6, + 0x43, 0x9b, 0x39, 0xc8, 0x04, 0x4d, 0x2b, 0x01, + 0xf7, 0xe6, 0x1b, 0x8d, 0x7e, 0x89, 0xe3, 0x43, + 0xd4, 0xf3, 0xab, 0x28, 0xd4, 0x5a, 0x1f, 0x20, + 0xea, 0xbe + }; + + std::vector input1; + std::vector input2; + + input1.resize(sizeof(short_integer_with_high_bit)); + input2.resize(sizeof(short_integer_without_high_bit)); + + memcpy(&input1.front(), short_integer_with_high_bit, + sizeof(short_integer_with_high_bit)); + memcpy(&input2.front(), short_integer_without_high_bit, + sizeof(short_integer_without_high_bit)); + + scoped_ptr keypair1( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(input1)); + scoped_ptr keypair2( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(input2)); + ASSERT_TRUE(keypair1.get()); + ASSERT_TRUE(keypair2.get()); + + std::vector output1; + std::vector output2; + ASSERT_TRUE(keypair1->ExportPrivateKey(&output1)); + ASSERT_TRUE(keypair2->ExportPrivateKey(&output2)); + + ASSERT_EQ(input1.size(), output1.size()); + ASSERT_EQ(input2.size(), output2.size()); + ASSERT_TRUE(0 == memcmp(&output1.front(), &input1.front(), + input1.size())); + ASSERT_TRUE(0 == memcmp(&output2.front(), &input2.front(), + input2.size())); +} diff --git a/crypto/rsa_private_key_win.cc b/crypto/rsa_private_key_win.cc new file mode 100644 index 0000000..293e4d66 --- /dev/null +++ b/crypto/rsa_private_key_win.cc @@ -0,0 +1,229 @@ +// Copyright (c) 2011 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 "crypto/rsa_private_key.h" + +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/string_util.h" + +namespace { + // Helper for error handling during key import. +#define READ_ASSERT(truth) \ + if (!(truth)) { \ + NOTREACHED(); \ + return false; \ + } +} // namespace + +namespace crypto { + +// static +RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) { + scoped_ptr result(new RSAPrivateKey); + if (!result->InitProvider()) + return NULL; + + DWORD flags = CRYPT_EXPORTABLE; + + // The size is encoded as the upper 16 bits of the flags. :: sigh ::. + flags |= (num_bits << 16); + if (!CryptGenKey(result->provider_, CALG_RSA_SIGN, flags, + result->key_.receive())) + return NULL; + + return result.release(); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateSensitive(uint16 num_bits) { + NOTIMPLEMENTED(); + return NULL; +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo( + const std::vector& input) { + scoped_ptr result(new RSAPrivateKey); + if (!result->InitProvider()) + return NULL; + + PrivateKeyInfoCodec pki(false); // Little-Endian + pki.Import(input); + + int blob_size = sizeof(PUBLICKEYSTRUC) + + sizeof(RSAPUBKEY) + + pki.modulus()->size() + + pki.prime1()->size() + + pki.prime2()->size() + + pki.exponent1()->size() + + pki.exponent2()->size() + + pki.coefficient()->size() + + pki.private_exponent()->size(); + scoped_array blob(new BYTE[blob_size]); + + uint8* dest = blob.get(); + PUBLICKEYSTRUC* public_key_struc = reinterpret_cast(dest); + public_key_struc->bType = PRIVATEKEYBLOB; + public_key_struc->bVersion = 0x02; + public_key_struc->reserved = 0; + public_key_struc->aiKeyAlg = CALG_RSA_SIGN; + dest += sizeof(PUBLICKEYSTRUC); + + RSAPUBKEY* rsa_pub_key = reinterpret_cast(dest); + rsa_pub_key->magic = 0x32415352; + rsa_pub_key->bitlen = pki.modulus()->size() * 8; + int public_exponent_int = 0; + for (size_t i = pki.public_exponent()->size(); i > 0; --i) { + public_exponent_int <<= 8; + public_exponent_int |= (*pki.public_exponent())[i - 1]; + } + rsa_pub_key->pubexp = public_exponent_int; + dest += sizeof(RSAPUBKEY); + + memcpy(dest, &pki.modulus()->front(), pki.modulus()->size()); + dest += pki.modulus()->size(); + memcpy(dest, &pki.prime1()->front(), pki.prime1()->size()); + dest += pki.prime1()->size(); + memcpy(dest, &pki.prime2()->front(), pki.prime2()->size()); + dest += pki.prime2()->size(); + memcpy(dest, &pki.exponent1()->front(), pki.exponent1()->size()); + dest += pki.exponent1()->size(); + memcpy(dest, &pki.exponent2()->front(), pki.exponent2()->size()); + dest += pki.exponent2()->size(); + memcpy(dest, &pki.coefficient()->front(), pki.coefficient()->size()); + dest += pki.coefficient()->size(); + memcpy(dest, &pki.private_exponent()->front(), + pki.private_exponent()->size()); + dest += pki.private_exponent()->size(); + + READ_ASSERT(dest == blob.get() + blob_size); + if (!CryptImportKey(result->provider_, + reinterpret_cast(public_key_struc), blob_size, 0, + CRYPT_EXPORTABLE, result->key_.receive())) + return NULL; + + return result.release(); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateSensitiveFromPrivateKeyInfo( + const std::vector& input) { + NOTIMPLEMENTED(); + return NULL; +} + +// static +RSAPrivateKey* RSAPrivateKey::FindFromPublicKeyInfo( + const std::vector& input) { + NOTIMPLEMENTED(); + return NULL; +} + +RSAPrivateKey::RSAPrivateKey() : provider_(NULL), key_(NULL) {} + +RSAPrivateKey::~RSAPrivateKey() {} + +bool RSAPrivateKey::InitProvider() { + return FALSE != CryptAcquireContext(provider_.receive(), NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); +} + +bool RSAPrivateKey::ExportPrivateKey(std::vector* output) { + // Export the key + DWORD blob_length = 0; + if (!CryptExportKey(key_, 0, PRIVATEKEYBLOB, 0, NULL, &blob_length)) { + NOTREACHED(); + return false; + } + + scoped_array blob(new uint8[blob_length]); + if (!CryptExportKey(key_, 0, PRIVATEKEYBLOB, 0, blob.get(), &blob_length)) { + NOTREACHED(); + return false; + } + + uint8* pos = blob.get(); + PUBLICKEYSTRUC *publickey_struct = reinterpret_cast(pos); + pos += sizeof(PUBLICKEYSTRUC); + + RSAPUBKEY *rsa_pub_key = reinterpret_cast(pos); + pos += sizeof(RSAPUBKEY); + + int mod_size = rsa_pub_key->bitlen / 8; + int primes_size = rsa_pub_key->bitlen / 16; + + PrivateKeyInfoCodec pki(false); // Little-Endian + + pki.modulus()->assign(pos, pos + mod_size); + pos += mod_size; + + pki.prime1()->assign(pos, pos + primes_size); + pos += primes_size; + pki.prime2()->assign(pos, pos + primes_size); + pos += primes_size; + + pki.exponent1()->assign(pos, pos + primes_size); + pos += primes_size; + pki.exponent2()->assign(pos, pos + primes_size); + pos += primes_size; + + pki.coefficient()->assign(pos, pos + primes_size); + pos += primes_size; + + pki.private_exponent()->assign(pos, pos + mod_size); + pos += mod_size; + + pki.public_exponent()->assign(reinterpret_cast(&rsa_pub_key->pubexp), + reinterpret_cast(&rsa_pub_key->pubexp) + 4); + + CHECK_EQ(pos - blob_length, reinterpret_cast(publickey_struct)); + + return pki.Export(output); +} + +bool RSAPrivateKey::ExportPublicKey(std::vector* output) { + DWORD key_info_len; + if (!CryptExportPublicKeyInfo( + provider_, AT_SIGNATURE, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + NULL, &key_info_len)) { + NOTREACHED(); + return false; + } + + scoped_array key_info(new uint8[key_info_len]); + if (!CryptExportPublicKeyInfo( + provider_, AT_SIGNATURE, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + reinterpret_cast(key_info.get()), &key_info_len)) { + NOTREACHED(); + return false; + } + + DWORD encoded_length; + if (!CryptEncodeObject( + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, + reinterpret_cast(key_info.get()), NULL, + &encoded_length)) { + NOTREACHED(); + return false; + } + + scoped_array encoded(new BYTE[encoded_length]); + if (!CryptEncodeObject( + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, + reinterpret_cast(key_info.get()), encoded.get(), + &encoded_length)) { + NOTREACHED(); + return false; + } + + for (size_t i = 0; i < encoded_length; ++i) + output->push_back(encoded[i]); + + return true; +} + +} // namespace crypto diff --git a/crypto/run_all_unittests.cc b/crypto/run_all_unittests.cc new file mode 100644 index 0000000..6f37e66 --- /dev/null +++ b/crypto/run_all_unittests.cc @@ -0,0 +1,17 @@ +// Copyright (c) 2011 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/test/test_suite.h" +#include "crypto/nss_util.h" + +int main(int argc, char** argv) { +#if defined(USE_NSS) + // This is most likely not needed, but it basically replaces a similar call + // that was performed on test_support_base. + TODO(rvargas) Bug 79359: remove this. + crypto::EnsureNSSInit(); +#endif // defined(USE_NSS) + + return base::TestSuite(argc, argv).Run(); +} diff --git a/crypto/scoped_capi_types.h b/crypto/scoped_capi_types.h new file mode 100644 index 0000000..f565121 --- /dev/null +++ b/crypto/scoped_capi_types.h @@ -0,0 +1,125 @@ +// Copyright (c) 2011 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 CRYPTO_SCOPED_CAPI_TYPES_H_ +#define CRYPTO_SCOPED_CAPI_TYPES_H_ +#pragma once + +#include +#include + +#include + +#include "base/logging.h" + +namespace crypto { + +// Simple destructor for the Free family of CryptoAPI functions, such as +// CryptDestroyHash, which take only a single argument to release. +template +struct CAPIDestroyer { + void operator()(CAPIHandle handle) const { + if (handle) { + BOOL ok = Destroyer(handle); + DCHECK(ok); + } + } +}; + +// Destructor for the Close/Release family of CryptoAPI functions, which take +// a second DWORD parameter indicating flags to use when closing or releasing. +// This includes functions like CertCloseStore or CryptReleaseContext. +template +struct CAPIDestroyerWithFlags { + void operator()(CAPIHandle handle) const { + if (handle) { + BOOL ok = Destroyer(handle, flags); + DCHECK(ok); + } + } +}; + +// scoped_ptr-like class for the CryptoAPI cryptography and certificate +// handles. Because these handles are defined as integer types, and not +// pointers, the existing scoped classes, such as scoped_ptr_malloc, are +// insufficient. The semantics are the same as scoped_ptr. +template +class ScopedCAPIHandle { + public: + explicit ScopedCAPIHandle(CAPIHandle handle = NULL) : handle_(handle) {} + + ~ScopedCAPIHandle() { + free_(handle_); + } + + void reset(CAPIHandle handle = NULL) { + if (handle_ != handle) { + free_(handle_); + handle_ = handle; + } + } + + operator CAPIHandle() const { return handle_; } + CAPIHandle get() const { return handle_; } + + CAPIHandle* receive() { + CHECK(handle_ == NULL); + return &handle_; + } + + bool operator==(CAPIHandle handle) const { + return handle_ == handle; + } + + bool operator!=(CAPIHandle handle) const { + return handle_ != handle; + } + + void swap(ScopedCAPIHandle& b) { + CAPIHandle tmp = b.handle_; + b.handle_ = handle_; + handle_ = tmp; + } + + CAPIHandle release() { + CAPIHandle tmp = handle_; + handle_ = NULL; + return tmp; + } + + private: + CAPIHandle handle_; + static const FreeProc free_; + + DISALLOW_COPY_AND_ASSIGN(ScopedCAPIHandle); +}; + +template +const FP ScopedCAPIHandle::free_ = FP(); + +template inline +bool operator==(CH h, const ScopedCAPIHandle& b) { + return h == b.get(); +} + +template inline +bool operator!=(CH h, const ScopedCAPIHandle& b) { + return h != b.get(); +} + +typedef ScopedCAPIHandle< + HCRYPTPROV, + CAPIDestroyerWithFlags > ScopedHCRYPTPROV; + +typedef ScopedCAPIHandle< + HCRYPTKEY, CAPIDestroyer > ScopedHCRYPTKEY; + +typedef ScopedCAPIHandle< + HCRYPTHASH, CAPIDestroyer > ScopedHCRYPTHASH; + +} // namespace crypto + +#endif // CRYPTO_SCOPED_CAPI_TYPES_H_ diff --git a/crypto/scoped_nss_types.h b/crypto/scoped_nss_types.h new file mode 100644 index 0000000..3e84cb0 --- /dev/null +++ b/crypto/scoped_nss_types.h @@ -0,0 +1,52 @@ +// Copyright (c) 2011 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 CRYPTO_SCOPED_NSS_TYPES_H_ +#define CRYPTO_SCOPED_NSS_TYPES_H_ +#pragma once + +#include +#include + +#include "base/memory/scoped_ptr.h" + +namespace crypto { + +template +struct NSSDestroyer { + void operator()(Type* ptr) const { + if (ptr) + Destroyer(ptr); + } +}; + +template +struct NSSDestroyer1 { + void operator()(Type* ptr) const { + if (ptr) + Destroyer(ptr, freeit); + } +}; + +// Define some convenient scopers around NSS pointers. +typedef scoped_ptr_malloc< + PK11Context, NSSDestroyer1 > ScopedPK11Context; +typedef scoped_ptr_malloc< + PK11SlotInfo, NSSDestroyer > ScopedPK11Slot; +typedef scoped_ptr_malloc< + PK11SymKey, NSSDestroyer > ScopedPK11SymKey; +typedef scoped_ptr_malloc< + SECAlgorithmID, NSSDestroyer1 > ScopedSECAlgorithmID; +typedef scoped_ptr_malloc< + SECItem, NSSDestroyer1 > ScopedSECItem; + +} // namespace crypto + +#endif // CRYPTO_SCOPED_NSS_TYPES_H_ diff --git a/crypto/secure_hash.h b/crypto/secure_hash.h new file mode 100644 index 0000000..35a2f8b --- /dev/null +++ b/crypto/secure_hash.h @@ -0,0 +1,36 @@ +// Copyright (c) 2011 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 CRYPTO_SECURE_HASH_H_ +#define CRYPTO_SECURE_HASH_H_ +#pragma once + +#include "base/basictypes.h" + +namespace crypto { + +// A wrapper to calculate secure hashes incrementally, allowing to +// be used when the full input is not known in advance. +class SecureHash { + public: + enum Algorithm { + SHA256, + }; + virtual ~SecureHash() {} + + static SecureHash* Create(Algorithm type); + + virtual void Update(const void* input, size_t len) = 0; + virtual void Finish(void* output, size_t len) = 0; + + protected: + SecureHash() {} + + private: + DISALLOW_COPY_AND_ASSIGN(SecureHash); +}; + +} // namespace crypto + +#endif // CRYPTO_SECURE_HASH_H_ diff --git a/crypto/secure_hash_default.cc b/crypto/secure_hash_default.cc new file mode 100644 index 0000000..b24221b --- /dev/null +++ b/crypto/secure_hash_default.cc @@ -0,0 +1,49 @@ +// Copyright (c) 2011 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 "crypto/secure_hash.h" + +#include "base/logging.h" +#include "crypto/third_party/nss/blapi.h" +#include "crypto/third_party/nss/sha256.h" + +namespace crypto { + +namespace { + +class SecureHashSHA256NSS : public SecureHash { + public: + SecureHashSHA256NSS() { + SHA256_Begin(&ctx_); + } + + virtual ~SecureHashSHA256NSS() { + } + + virtual void Update(const void* input, size_t len) { + SHA256_Update(&ctx_, static_cast(input), len); + } + + virtual void Finish(void* output, size_t len) { + SHA256_End(&ctx_, static_cast(output), NULL, + static_cast(len)); + } + + private: + SHA256Context ctx_; +}; + +} // namespace + +SecureHash* SecureHash::Create(Algorithm algorithm) { + switch (algorithm) { + case SHA256: + return new SecureHashSHA256NSS(); + default: + NOTIMPLEMENTED(); + return NULL; + } +} + +} // namespace crypto diff --git a/crypto/secure_hash_openssl.cc b/crypto/secure_hash_openssl.cc new file mode 100644 index 0000000..8e2f128 --- /dev/null +++ b/crypto/secure_hash_openssl.cc @@ -0,0 +1,53 @@ +// Copyright (c) 2011 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 "crypto/secure_hash.h" + +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "crypto/openssl_util.h" + +namespace crypto { + +namespace { + +class SecureHashSHA256OpenSSL : public SecureHash { + public: + SecureHashSHA256OpenSSL() { + SHA256_Init(&ctx_); + } + + virtual ~SecureHashSHA256OpenSSL() { + OPENSSL_cleanse(&ctx_, sizeof(ctx_)); + } + + virtual void Update(const void* input, size_t len) { + SHA256_Update(&ctx_, static_cast(input), len); + } + + virtual void Finish(void* output, size_t len) { + ScopedOpenSSLSafeSizeBuffer result( + static_cast(output), len); + SHA256_Final(result.safe_buffer(), &ctx_); + } + + private: + SHA256_CTX ctx_; +}; + +} // namespace + +SecureHash* SecureHash::Create(Algorithm algorithm) { + switch (algorithm) { + case SHA256: + return new SecureHashSHA256OpenSSL(); + default: + NOTIMPLEMENTED(); + return NULL; + } +} + +} // namespace crypto diff --git a/crypto/secure_hash_unittest.cc b/crypto/secure_hash_unittest.cc new file mode 100644 index 0000000..49b9da5 --- /dev/null +++ b/crypto/secure_hash_unittest.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2011 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 "crypto/secure_hash.h" + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "crypto/sha2.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(SecureHashTest, TestUpdate) { + // Example B.3 from FIPS 180-2: long message. + std::string input3(500000, 'a'); // 'a' repeated half a million times + int expected3[] = { 0xcd, 0xc7, 0x6e, 0x5c, + 0x99, 0x14, 0xfb, 0x92, + 0x81, 0xa1, 0xc7, 0xe2, + 0x84, 0xd7, 0x3e, 0x67, + 0xf1, 0x80, 0x9a, 0x48, + 0xa4, 0x97, 0x20, 0x0e, + 0x04, 0x6d, 0x39, 0xcc, + 0xc7, 0x11, 0x2c, 0xd0 }; + + uint8 output3[crypto::SHA256_LENGTH]; + + scoped_ptr ctx(crypto::SecureHash::Create( + crypto::SecureHash::SHA256)); + ctx->Update(input3.data(), input3.size()); + ctx->Update(input3.data(), input3.size()); + + ctx->Finish(output3, sizeof(output3)); + for (size_t i = 0; i < crypto::SHA256_LENGTH; i++) + EXPECT_EQ(expected3[i], static_cast(output3[i])); +} diff --git a/crypto/sha2.cc b/crypto/sha2.cc new file mode 100644 index 0000000..7c9b9d2 --- /dev/null +++ b/crypto/sha2.cc @@ -0,0 +1,25 @@ +// Copyright (c) 2011 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 "crypto/sha2.h" + +#include "base/scoped_ptr.h" +#include "base/stl_util-inl.h" +#include "crypto/secure_hash.h" + +namespace crypto { + +void SHA256HashString(const std::string& str, void* output, size_t len) { + scoped_ptr ctx(SecureHash::Create(SecureHash::SHA256)); + ctx->Update(str.data(), str.length()); + ctx->Finish(output, len); +} + +std::string SHA256HashString(const std::string& str) { + std::string output(SHA256_LENGTH, 0); + SHA256HashString(str, string_as_array(&output), output.size()); + return output; +} + +} // namespace crypto diff --git a/crypto/sha2.h b/crypto/sha2.h new file mode 100644 index 0000000..349a606 --- /dev/null +++ b/crypto/sha2.h @@ -0,0 +1,33 @@ +// Copyright (c) 2011 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 CRYPTO_SHA2_H_ +#define CRYPTO_SHA2_H_ +#pragma once + +#include + +namespace crypto { + +// These functions perform SHA-256 operations. +// +// Functions for SHA-384 and SHA-512 can be added when the need arises. + +enum { + SHA256_LENGTH = 32 // length in bytes of a SHA-256 hash +}; + +// Computes the SHA-256 hash of the input string 'str' and stores the first +// 'len' bytes of the hash in the output buffer 'output'. If 'len' > 32, +// only 32 bytes (the full hash) are stored in the 'output' buffer. +void SHA256HashString(const std::string& str, + void* output, size_t len); + +// Convenience version of the above that returns the result in a 32-byte +// string. +std::string SHA256HashString(const std::string& str); + +} // namespace crypto + +#endif // CRYPTO_SHA2_H_ diff --git a/crypto/sha2_unittest.cc b/crypto/sha2_unittest.cc new file mode 100644 index 0000000..8a28a5b --- /dev/null +++ b/crypto/sha2_unittest.cc @@ -0,0 +1,100 @@ +// Copyright (c) 2011 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 "crypto/sha2.h" + +#include "base/basictypes.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(Sha256Test, Test1) { + // Example B.1 from FIPS 180-2: one-block message. + std::string input1 = "abc"; + int expected1[] = { 0xba, 0x78, 0x16, 0xbf, + 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, + 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, + 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, + 0xf2, 0x00, 0x15, 0xad }; + + uint8 output1[crypto::SHA256_LENGTH]; + crypto::SHA256HashString(input1, output1, sizeof(output1)); + for (size_t i = 0; i < crypto::SHA256_LENGTH; i++) + EXPECT_EQ(expected1[i], static_cast(output1[i])); + + uint8 output_truncated1[4]; // 4 bytes == 32 bits + crypto::SHA256HashString(input1, + output_truncated1, sizeof(output_truncated1)); + for (size_t i = 0; i < sizeof(output_truncated1); i++) + EXPECT_EQ(expected1[i], static_cast(output_truncated1[i])); +} + +TEST(Sha256Test, Test1_String) { + // Same as the above, but using the wrapper that returns a std::string. + // Example B.1 from FIPS 180-2: one-block message. + std::string input1 = "abc"; + int expected1[] = { 0xba, 0x78, 0x16, 0xbf, + 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, + 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, + 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, + 0xf2, 0x00, 0x15, 0xad }; + + std::string output1 = crypto::SHA256HashString(input1); + ASSERT_EQ(crypto::SHA256_LENGTH, output1.size()); + for (size_t i = 0; i < crypto::SHA256_LENGTH; i++) + EXPECT_EQ(expected1[i], static_cast(output1[i])); +} + +TEST(Sha256Test, Test2) { + // Example B.2 from FIPS 180-2: multi-block message. + std::string input2 = + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + int expected2[] = { 0x24, 0x8d, 0x6a, 0x61, + 0xd2, 0x06, 0x38, 0xb8, + 0xe5, 0xc0, 0x26, 0x93, + 0x0c, 0x3e, 0x60, 0x39, + 0xa3, 0x3c, 0xe4, 0x59, + 0x64, 0xff, 0x21, 0x67, + 0xf6, 0xec, 0xed, 0xd4, + 0x19, 0xdb, 0x06, 0xc1 }; + + uint8 output2[crypto::SHA256_LENGTH]; + crypto::SHA256HashString(input2, output2, sizeof(output2)); + for (size_t i = 0; i < crypto::SHA256_LENGTH; i++) + EXPECT_EQ(expected2[i], static_cast(output2[i])); + + uint8 output_truncated2[6]; + crypto::SHA256HashString(input2, + output_truncated2, sizeof(output_truncated2)); + for (size_t i = 0; i < sizeof(output_truncated2); i++) + EXPECT_EQ(expected2[i], static_cast(output_truncated2[i])); +} + +TEST(Sha256Test, Test3) { + // Example B.3 from FIPS 180-2: long message. + std::string input3(1000000, 'a'); // 'a' repeated a million times + int expected3[] = { 0xcd, 0xc7, 0x6e, 0x5c, + 0x99, 0x14, 0xfb, 0x92, + 0x81, 0xa1, 0xc7, 0xe2, + 0x84, 0xd7, 0x3e, 0x67, + 0xf1, 0x80, 0x9a, 0x48, + 0xa4, 0x97, 0x20, 0x0e, + 0x04, 0x6d, 0x39, 0xcc, + 0xc7, 0x11, 0x2c, 0xd0 }; + + uint8 output3[crypto::SHA256_LENGTH]; + crypto::SHA256HashString(input3, output3, sizeof(output3)); + for (size_t i = 0; i < crypto::SHA256_LENGTH; i++) + EXPECT_EQ(expected3[i], static_cast(output3[i])); + + uint8 output_truncated3[12]; + crypto::SHA256HashString(input3, + output_truncated3, sizeof(output_truncated3)); + for (size_t i = 0; i < sizeof(output_truncated3); i++) + EXPECT_EQ(expected3[i], static_cast(output_truncated3[i])); +} diff --git a/crypto/signature_creator.h b/crypto/signature_creator.h new file mode 100644 index 0000000..07be4b9 --- /dev/null +++ b/crypto/signature_creator.h @@ -0,0 +1,69 @@ +// Copyright (c) 2011 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 CRYPTO_SIGNATURE_CREATOR_H_ +#define CRYPTO_SIGNATURE_CREATOR_H_ +#pragma once + +#include "build/build_config.h" + +#if defined(USE_OPENSSL) +// Forward declaration for openssl/*.h +typedef struct env_md_ctx_st EVP_MD_CTX; +#elif defined(USE_NSS) +// Forward declaration. +struct SGNContextStr; +#elif defined(OS_MACOSX) +#include +#endif + +#include + +#include "base/basictypes.h" +#include "crypto/rsa_private_key.h" + +#if defined(OS_WIN) +#include "crypto/scoped_capi_types.h" +#endif + +namespace crypto { + +// Signs data using a bare private key (as opposed to a full certificate). +// Currently can only sign data using SHA-1 with RSA encryption. +class SignatureCreator { + public: + ~SignatureCreator(); + + // Create an instance. The caller must ensure that the provided PrivateKey + // instance outlives the created SignatureCreator. + static SignatureCreator* Create(RSAPrivateKey* key); + + // Update the signature with more data. + bool Update(const uint8* data_part, int data_part_len); + + // Finalize the signature. + bool Final(std::vector* signature); + + private: + // Private constructor. Use the Create() method instead. + SignatureCreator(); + + RSAPrivateKey* key_; + +#if defined(USE_OPENSSL) + EVP_MD_CTX* sign_context_; +#elif defined(USE_NSS) + SGNContextStr* sign_context_; +#elif defined(OS_MACOSX) + CSSM_CC_HANDLE sig_handle_; +#elif defined(OS_WIN) + ScopedHCRYPTHASH hash_object_; +#endif + + DISALLOW_COPY_AND_ASSIGN(SignatureCreator); +}; + +} // namespace crypto + +#endif // CRYPTO_SIGNATURE_CREATOR_H_ diff --git a/crypto/signature_creator_mac.cc b/crypto/signature_creator_mac.cc new file mode 100644 index 0000000..fa0bded --- /dev/null +++ b/crypto/signature_creator_mac.cc @@ -0,0 +1,74 @@ +// Copyright (c) 2011 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 "crypto/signature_creator.h" + +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "crypto/cssm_init.h" + +namespace crypto { + +// static +SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key) { + scoped_ptr result(new SignatureCreator); + result->key_ = key; + + CSSM_RETURN crtn; + crtn = CSSM_CSP_CreateSignatureContext(GetSharedCSPHandle(), + CSSM_ALGID_SHA1WithRSA, + NULL, + key->key(), + &result->sig_handle_); + if (crtn) { + NOTREACHED(); + return NULL; + } + + crtn = CSSM_SignDataInit(result->sig_handle_); + if (crtn) { + NOTREACHED(); + return NULL; + } + + return result.release(); +} + +SignatureCreator::SignatureCreator() : sig_handle_(0) { + EnsureCSSMInit(); +} + +SignatureCreator::~SignatureCreator() { + CSSM_RETURN crtn; + if (sig_handle_) { + crtn = CSSM_DeleteContext(sig_handle_); + DCHECK(crtn == CSSM_OK); + } +} + +bool SignatureCreator::Update(const uint8* data_part, int data_part_len) { + CSSM_DATA data; + data.Data = const_cast(data_part); + data.Length = data_part_len; + CSSM_RETURN crtn = CSSM_SignDataUpdate(sig_handle_, &data, 1); + DCHECK(crtn == CSSM_OK); + return true; +} + +bool SignatureCreator::Final(std::vector* signature) { + ScopedCSSMData sig; + CSSM_RETURN crtn = CSSM_SignDataFinal(sig_handle_, sig); + + if (crtn) { + NOTREACHED(); + return false; + } + + signature->assign(sig->Data, sig->Data + sig->Length); + return true; +} + +} // namespace crypto diff --git a/crypto/signature_creator_nss.cc b/crypto/signature_creator_nss.cc new file mode 100644 index 0000000..2614944 --- /dev/null +++ b/crypto/signature_creator_nss.cc @@ -0,0 +1,76 @@ +// Copyright (c) 2011 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 "crypto/signature_creator.h" + +#include +#include +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "crypto/nss_util.h" + +namespace crypto { + +SignatureCreator::~SignatureCreator() { + if (sign_context_) { + SGN_DestroyContext(sign_context_, PR_TRUE); + sign_context_ = NULL; + } +} + +// static +SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key) { + scoped_ptr result(new SignatureCreator); + result->key_ = key; + + result->sign_context_ = SGN_NewContext(SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, + key->key()); + if (!result->sign_context_) { + NOTREACHED(); + return NULL; + } + + SECStatus rv = SGN_Begin(result->sign_context_); + if (rv != SECSuccess) { + NOTREACHED(); + return NULL; + } + + return result.release(); +} + +bool SignatureCreator::Update(const uint8* data_part, int data_part_len) { + // TODO(wtc): Remove this const_cast when we require NSS 3.12.5. + // See NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=518255 + SECStatus rv = SGN_Update(sign_context_, + const_cast(data_part), + data_part_len); + if (rv != SECSuccess) { + NOTREACHED(); + return false; + } + + return true; +} + +bool SignatureCreator::Final(std::vector* signature) { + SECItem signature_item; + SECStatus rv = SGN_End(sign_context_, &signature_item); + if (rv != SECSuccess) { + NOTREACHED(); + return false; + } + signature->assign(signature_item.data, + signature_item.data + signature_item.len); + SECITEM_FreeItem(&signature_item, PR_FALSE); + return true; +} + +SignatureCreator::SignatureCreator() : sign_context_(NULL) { + EnsureNSSInit(); +} + +} // namespace crypto diff --git a/crypto/signature_creator_openssl.cc b/crypto/signature_creator_openssl.cc new file mode 100644 index 0000000..e6aa422 --- /dev/null +++ b/crypto/signature_creator_openssl.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2011 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 "crypto/signature_creator.h" + +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util-inl.h" +#include "crypto/openssl_util.h" + +namespace crypto { + +// static +SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + scoped_ptr result(new SignatureCreator); + result->key_ = key; + if (!EVP_SignInit_ex(result->sign_context_, EVP_sha1(), NULL)) + return NULL; + return result.release(); +} + +SignatureCreator::SignatureCreator() + : sign_context_(EVP_MD_CTX_create()) { +} + +SignatureCreator::~SignatureCreator() { + EVP_MD_CTX_destroy(sign_context_); +} + +bool SignatureCreator::Update(const uint8* data_part, int data_part_len) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + return EVP_SignUpdate(sign_context_, data_part, data_part_len) == 1; +} + +bool SignatureCreator::Final(std::vector* signature) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + EVP_PKEY* key = key_->key(); + signature->resize(EVP_PKEY_size(key)); + + unsigned int len = 0; + int rv = EVP_SignFinal(sign_context_, vector_as_array(signature), &len, key); + if (!rv) { + signature->clear(); + return false; + } + signature->resize(len); + return true; +} + +} // namespace crypto diff --git a/crypto/signature_creator_unittest.cc b/crypto/signature_creator_unittest.cc new file mode 100644 index 0000000..623e9ed --- /dev/null +++ b/crypto/signature_creator_unittest.cc @@ -0,0 +1,53 @@ +// Copyright (c) 2011 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 + +#include "base/memory/scoped_ptr.h" +#include "crypto/signature_creator.h" +#include "crypto/signature_verifier.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(SignatureCreatorTest, BasicTest) { + // Do a verify round trip. + scoped_ptr key_original( + crypto::RSAPrivateKey::Create(1024)); + ASSERT_TRUE(key_original.get()); + + std::vector key_info; + key_original->ExportPrivateKey(&key_info); + scoped_ptr key( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_info)); + ASSERT_TRUE(key.get()); + + scoped_ptr signer( + crypto::SignatureCreator::Create(key.get())); + ASSERT_TRUE(signer.get()); + + std::string data("Hello, World!"); + ASSERT_TRUE(signer->Update(reinterpret_cast(data.c_str()), + data.size())); + + std::vector signature; + ASSERT_TRUE(signer->Final(&signature)); + + std::vector public_key_info; + ASSERT_TRUE(key_original->ExportPublicKey(&public_key_info)); + + // This is the algorithm ID for SHA-1 with RSA encryption. + // TODO(aa): Factor this out into some shared location. + const uint8 kSHA1WithRSAAlgorithmID[] = { + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00 + }; + crypto::SignatureVerifier verifier; + ASSERT_TRUE(verifier.VerifyInit( + kSHA1WithRSAAlgorithmID, sizeof(kSHA1WithRSAAlgorithmID), + &signature.front(), signature.size(), + &public_key_info.front(), public_key_info.size())); + + verifier.VerifyUpdate(reinterpret_cast(data.c_str()), + data.size()); + ASSERT_TRUE(verifier.VerifyFinal()); +} diff --git a/crypto/signature_creator_win.cc b/crypto/signature_creator_win.cc new file mode 100644 index 0000000..244b06a --- /dev/null +++ b/crypto/signature_creator_win.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2011 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 "crypto/signature_creator.h" + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" + +namespace crypto { + +// static +SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key) { + scoped_ptr result(new SignatureCreator); + result->key_ = key; + + if (!CryptCreateHash(key->provider(), CALG_SHA1, 0, 0, + result->hash_object_.receive())) { + NOTREACHED(); + return NULL; + } + + return result.release(); +} + +SignatureCreator::SignatureCreator() : hash_object_(0) {} + +SignatureCreator::~SignatureCreator() {} + +bool SignatureCreator::Update(const uint8* data_part, int data_part_len) { + if (!CryptHashData(hash_object_, data_part, data_part_len, 0)) { + NOTREACHED(); + return false; + } + + return true; +} + +bool SignatureCreator::Final(std::vector* signature) { + DWORD signature_length = 0; + if (!CryptSignHash(hash_object_, AT_SIGNATURE, NULL, 0, NULL, + &signature_length)) { + return false; + } + + std::vector temp; + temp.resize(signature_length); + if (!CryptSignHash(hash_object_, AT_SIGNATURE, NULL, 0, &temp.front(), + &signature_length)) { + return false; + } + + temp.resize(signature_length); + for (size_t i = temp.size(); i > 0; --i) + signature->push_back(temp[i - 1]); + + return true; +} + +} // namespace crypto diff --git a/crypto/signature_verifier.h b/crypto/signature_verifier.h new file mode 100644 index 0000000..fb6202c --- /dev/null +++ b/crypto/signature_verifier.h @@ -0,0 +1,108 @@ +// Copyright (c) 2011 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 CRYPTO_SIGNATURE_VERIFIER_H_ +#define CRYPTO_SIGNATURE_VERIFIER_H_ +#pragma once + +#include "build/build_config.h" + +#if defined(USE_NSS) +#include +#elif defined(OS_MACOSX) +#include +#endif + +#include + +#include "base/basictypes.h" + +#if defined(OS_WIN) +#include "crypto/scoped_capi_types.h" +#endif + +namespace crypto { + +// The SignatureVerifier class verifies a signature using a bare public key +// (as opposed to a certificate). +class SignatureVerifier { + public: + SignatureVerifier(); + ~SignatureVerifier(); + + // Streaming interface: + + // Initiates a signature verification operation. This should be followed + // by one or more VerifyUpdate calls and a VerifyFinal call. + // + // The signature algorithm is specified as a DER encoded ASN.1 + // AlgorithmIdentifier structure: + // AlgorithmIdentifier ::= SEQUENCE { + // algorithm OBJECT IDENTIFIER, + // parameters ANY DEFINED BY algorithm OPTIONAL } + // + // The signature is encoded according to the signature algorithm, but it + // must not be further encoded in an ASN.1 BIT STRING. + // Note: An RSA signatures is actually a big integer. It must be in the + // big-endian byte order. + // + // The public key is specified as a DER encoded ASN.1 SubjectPublicKeyInfo + // structure, which contains not only the public key but also its type + // (algorithm): + // SubjectPublicKeyInfo ::= SEQUENCE { + // algorithm AlgorithmIdentifier, + // subjectPublicKey BIT STRING } + bool VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len); + + // Feeds a piece of the data to the signature verifier. + void VerifyUpdate(const uint8* data_part, int data_part_len); + + // Concludes a signature verification operation. Returns true if the + // signature is valid. Returns false if the signature is invalid or an + // error occurred. + bool VerifyFinal(); + + // Note: we can provide a one-shot interface if there is interest: + // bool Verify(const uint8* data, + // int data_len, + // const uint8* signature_algorithm, + // int signature_algorithm_len, + // const uint8* signature, + // int signature_len, + // const uint8* public_key_info, + // int public_key_info_len); + + private: + void Reset(); + + std::vector signature_; + +#if defined(USE_OPENSSL) + struct VerifyContext; + VerifyContext* verify_context_; +#elif defined(USE_NSS) + VFYContext* vfy_context_; +#elif defined(OS_MACOSX) + std::vector public_key_info_; + + CSSM_CC_HANDLE sig_handle_; + + CSSM_KEY public_key_; +#elif defined(OS_WIN) + ScopedHCRYPTPROV provider_; + + ScopedHCRYPTHASH hash_object_; + + ScopedHCRYPTKEY public_key_; +#endif +}; + +} // namespace crypto + +#endif // CRYPTO_SIGNATURE_VERIFIER_H_ diff --git a/crypto/signature_verifier_mac.cc b/crypto/signature_verifier_mac.cc new file mode 100644 index 0000000..33cdfcf --- /dev/null +++ b/crypto/signature_verifier_mac.cc @@ -0,0 +1,105 @@ +// Copyright (c) 2011 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 "crypto/signature_verifier.h" + +#include + +#include "base/logging.h" +#include "crypto/cssm_init.h" + +namespace crypto { + +SignatureVerifier::SignatureVerifier() : sig_handle_(0) { + EnsureCSSMInit(); +} + +SignatureVerifier::~SignatureVerifier() { + Reset(); +} + +bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len) { + signature_.assign(signature, signature + signature_len); + public_key_info_.assign(public_key_info, + public_key_info + public_key_info_len); + + CSSM_ALGORITHMS key_alg = CSSM_ALGID_RSA; // TODO(wtc): hardcoded. + + memset(&public_key_, 0, sizeof(public_key_)); + public_key_.KeyData.Data = const_cast(&public_key_info_[0]); + public_key_.KeyData.Length = public_key_info_.size(); + public_key_.KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION; + public_key_.KeyHeader.BlobType = CSSM_KEYBLOB_RAW; + public_key_.KeyHeader.Format = CSSM_KEYBLOB_RAW_FORMAT_X509; + public_key_.KeyHeader.AlgorithmId = key_alg; + public_key_.KeyHeader.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY; + public_key_.KeyHeader.KeyAttr = CSSM_KEYATTR_EXTRACTABLE; + public_key_.KeyHeader.KeyUsage = CSSM_KEYUSE_VERIFY; + CSSM_KEY_SIZE key_size; + CSSM_RETURN crtn; + crtn = CSSM_QueryKeySizeInBits(GetSharedCSPHandle(), NULL, + &public_key_, &key_size); + if (crtn) { + NOTREACHED() << "CSSM_QueryKeySizeInBits failed: " << crtn; + return false; + } + public_key_.KeyHeader.LogicalKeySizeInBits = key_size.LogicalKeySizeInBits; + + // TODO(wtc): decode signature_algorithm... + CSSM_ALGORITHMS sig_alg = CSSM_ALGID_SHA1WithRSA; + + crtn = CSSM_CSP_CreateSignatureContext(GetSharedCSPHandle(), sig_alg, NULL, + &public_key_, &sig_handle_); + if (crtn) { + NOTREACHED(); + return false; + } + crtn = CSSM_VerifyDataInit(sig_handle_); + if (crtn) { + NOTREACHED(); + return false; + } + return true; +} + +void SignatureVerifier::VerifyUpdate(const uint8* data_part, + int data_part_len) { + CSSM_DATA data; + data.Data = const_cast(data_part); + data.Length = data_part_len; + CSSM_RETURN crtn = CSSM_VerifyDataUpdate(sig_handle_, &data, 1); + DCHECK(crtn == CSSM_OK); +} + +bool SignatureVerifier::VerifyFinal() { + CSSM_DATA sig; + sig.Data = const_cast(&signature_[0]); + sig.Length = signature_.size(); + CSSM_RETURN crtn = CSSM_VerifyDataFinal(sig_handle_, &sig); + Reset(); + + // crtn is CSSMERR_CSP_VERIFY_FAILED if signature verification fails. + return (crtn == CSSM_OK); +} + +void SignatureVerifier::Reset() { + CSSM_RETURN crtn; + if (sig_handle_) { + crtn = CSSM_DeleteContext(sig_handle_); + DCHECK(crtn == CSSM_OK); + sig_handle_ = 0; + } + signature_.clear(); + + // Can't call CSSM_FreeKey on public_key_ because we constructed + // public_key_ manually. +} + +} // namespace crypto + diff --git a/crypto/signature_verifier_nss.cc b/crypto/signature_verifier_nss.cc new file mode 100644 index 0000000..cf82785 --- /dev/null +++ b/crypto/signature_verifier_nss.cc @@ -0,0 +1,113 @@ +// Copyright (c) 2011 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 "crypto/signature_verifier.h" + +#include +#include +#include + +#include "base/logging.h" +#include "crypto/nss_util.h" + +namespace crypto { + +SignatureVerifier::SignatureVerifier() : vfy_context_(NULL) { + EnsureNSSInit(); +} + +SignatureVerifier::~SignatureVerifier() { + Reset(); +} + +bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len) { + signature_.assign(signature, signature + signature_len); + + CERTSubjectPublicKeyInfo* spki = NULL; + SECItem spki_der; + spki_der.type = siBuffer; + spki_der.data = const_cast(public_key_info); + spki_der.len = public_key_info_len; + spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_der); + if (!spki) + return false; + SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki); + SECKEY_DestroySubjectPublicKeyInfo(spki); // Done with spki. + if (!public_key) + return false; + + PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + SECKEY_DestroyPublicKey(public_key); + return false; + } + + SECItem sig_alg_der; + sig_alg_der.type = siBuffer; + sig_alg_der.data = const_cast(signature_algorithm); + sig_alg_der.len = signature_algorithm_len; + SECAlgorithmID sig_alg_id; + SECStatus rv; + rv = SEC_QuickDERDecodeItem(arena, &sig_alg_id, SECOID_AlgorithmIDTemplate, + &sig_alg_der); + if (rv != SECSuccess) { + SECKEY_DestroyPublicKey(public_key); + PORT_FreeArena(arena, PR_TRUE); + return false; + } + + SECItem sig; + sig.type = siBuffer; + sig.data = const_cast(signature); + sig.len = signature_len; + SECOidTag hash_alg_tag; + vfy_context_ = VFY_CreateContextWithAlgorithmID(public_key, &sig, + &sig_alg_id, &hash_alg_tag, + NULL); + SECKEY_DestroyPublicKey(public_key); // Done with public_key. + PORT_FreeArena(arena, PR_TRUE); // Done with sig_alg_id. + if (!vfy_context_) { + // A corrupted RSA signature could be detected without the data, so + // VFY_CreateContextWithAlgorithmID may fail with SEC_ERROR_BAD_SIGNATURE + // (-8182). + return false; + } + + rv = VFY_Begin(vfy_context_); + if (rv != SECSuccess) { + NOTREACHED(); + return false; + } + return true; +} + +void SignatureVerifier::VerifyUpdate(const uint8* data_part, + int data_part_len) { + SECStatus rv = VFY_Update(vfy_context_, data_part, data_part_len); + DCHECK(rv == SECSuccess); +} + +bool SignatureVerifier::VerifyFinal() { + SECStatus rv = VFY_End(vfy_context_); + Reset(); + + // If signature verification fails, the error code is + // SEC_ERROR_BAD_SIGNATURE (-8182). + return (rv == SECSuccess); +} + +void SignatureVerifier::Reset() { + if (vfy_context_) { + VFY_DestroyContext(vfy_context_, PR_TRUE); + vfy_context_ = NULL; + } + signature_.clear(); +} + +} // namespace crypto diff --git a/crypto/signature_verifier_openssl.cc b/crypto/signature_verifier_openssl.cc new file mode 100644 index 0000000..2a58155 --- /dev/null +++ b/crypto/signature_verifier_openssl.cc @@ -0,0 +1,94 @@ +// Copyright (c) 2011 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 "crypto/signature_verifier.h" + +#include +#include + +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util-inl.h" +#include "crypto/openssl_util.h" + +namespace crypto { + +struct SignatureVerifier::VerifyContext { + ScopedOpenSSL public_key; + ScopedOpenSSL ctx; +}; + +SignatureVerifier::SignatureVerifier() + : verify_context_(NULL) { +} + +SignatureVerifier::~SignatureVerifier() { + Reset(); +} + +bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len) { + DCHECK(!verify_context_); + verify_context_ = new VerifyContext; + OpenSSLErrStackTracer err_tracer(FROM_HERE); + + ScopedOpenSSL algorithm( + d2i_X509_ALGOR(NULL, &signature_algorithm, signature_algorithm_len)); + if (!algorithm.get()) + return false; + + const EVP_MD* digest = EVP_get_digestbyobj(algorithm.get()->algorithm); + DCHECK(digest); + + signature_.assign(signature, signature + signature_len); + + // BIO_new_mem_buf is not const aware, but it does not modify the buffer. + char* data = reinterpret_cast(const_cast(public_key_info)); + ScopedOpenSSL bio(BIO_new_mem_buf(data, + public_key_info_len)); + if (!bio.get()) + return false; + + verify_context_->public_key.reset(d2i_PUBKEY_bio(bio.get(), NULL)); + if (!verify_context_->public_key.get()) + return false; + + verify_context_->ctx.reset(EVP_MD_CTX_create()); + int rv = EVP_VerifyInit_ex(verify_context_->ctx.get(), digest, NULL); + return rv == 1; +} + +void SignatureVerifier::VerifyUpdate(const uint8* data_part, + int data_part_len) { + DCHECK(verify_context_); + OpenSSLErrStackTracer err_tracer(FROM_HERE); + int rv = EVP_VerifyUpdate(verify_context_->ctx.get(), + data_part, data_part_len); + DCHECK_EQ(rv, 1); +} + +bool SignatureVerifier::VerifyFinal() { + DCHECK(verify_context_); + OpenSSLErrStackTracer err_tracer(FROM_HERE); + int rv = EVP_VerifyFinal(verify_context_->ctx.get(), + vector_as_array(&signature_), signature_.size(), + verify_context_->public_key.get()); + DCHECK_GE(rv, 0); + Reset(); + return rv == 1; +} + +void SignatureVerifier::Reset() { + delete verify_context_; + verify_context_ = NULL; + signature_.clear(); +} + +} // namespace crypto diff --git a/crypto/signature_verifier_unittest.cc b/crypto/signature_verifier_unittest.cc new file mode 100644 index 0000000..0294379 --- /dev/null +++ b/crypto/signature_verifier_unittest.cc @@ -0,0 +1,268 @@ +// Copyright (c) 2011 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 "crypto/signature_verifier.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(SignatureVerifierTest, BasicTest) { + // The input data in this test comes from real certificates. + // + // tbs_certificate ("to-be-signed certificate", the part of a certificate + // that is signed), signature_algorithm, and algorithm come from the + // certificate of bugs.webkit.org. + // + // public_key_info comes from the certificate of the issuer, Go Daddy Secure + // Certification Authority. + // + // The bytes in the array initializers are formatted to expose the DER + // encoding of the ASN.1 structures. + + // The data that is signed is the following ASN.1 structure: + // TBSCertificate ::= SEQUENCE { + // ... -- omitted, not important + // } + const uint8 tbs_certificate[1017] = { + 0x30, 0x82, 0x03, 0xf5, // a SEQUENCE of length 1013 (0x3f5) + 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x43, 0xdd, 0x63, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, + 0x00, 0x30, 0x81, 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, + 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, + 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, + 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, + 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x6f, 0x72, 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x27, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, + 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x04, 0x05, 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32, + 0x38, 0x37, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x38, 0x30, 0x33, 0x31, 0x38, + 0x32, 0x33, 0x33, 0x35, 0x31, 0x39, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, + 0x33, 0x31, 0x38, 0x32, 0x33, 0x33, 0x35, 0x31, 0x39, 0x5a, 0x30, 0x79, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, + 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x12, + 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x43, 0x75, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x6e, 0x6f, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x0c, 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x46, 0x6f, 0x72, + 0x67, 0x65, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x0c, 0x2a, 0x2e, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2e, 0x6f, 0x72, + 0x67, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, + 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xa7, 0x62, 0x79, 0x41, 0xda, 0x28, + 0xf2, 0xc0, 0x4f, 0xe0, 0x25, 0xaa, 0xa1, 0x2e, 0x3b, 0x30, 0x94, 0xb5, + 0xc9, 0x26, 0x3a, 0x1b, 0xe2, 0xd0, 0xcc, 0xa2, 0x95, 0xe2, 0x91, 0xc0, + 0xf0, 0x40, 0x9e, 0x27, 0x6e, 0xbd, 0x6e, 0xde, 0x7c, 0xb6, 0x30, 0x5c, + 0xb8, 0x9b, 0x01, 0x2f, 0x92, 0x04, 0xa1, 0xef, 0x4a, 0xb1, 0x6c, 0xb1, + 0x7e, 0x8e, 0xcd, 0xa6, 0xf4, 0x40, 0x73, 0x1f, 0x2c, 0x96, 0xad, 0xff, + 0x2a, 0x6d, 0x0e, 0xba, 0x52, 0x84, 0x83, 0xb0, 0x39, 0xee, 0xc9, 0x39, + 0xdc, 0x1e, 0x34, 0xd0, 0xd8, 0x5d, 0x7a, 0x09, 0xac, 0xa9, 0xee, 0xca, + 0x65, 0xf6, 0x85, 0x3a, 0x6b, 0xee, 0xe4, 0x5c, 0x5e, 0xf8, 0xda, 0xd1, + 0xce, 0x88, 0x47, 0xcd, 0x06, 0x21, 0xe0, 0xb9, 0x4b, 0xe4, 0x07, 0xcb, + 0x57, 0xdc, 0xca, 0x99, 0x54, 0xf7, 0x0e, 0xd5, 0x17, 0x95, 0x05, 0x2e, + 0xe9, 0xb1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xce, 0x30, + 0x82, 0x01, 0xca, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, + 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, + 0x02, 0x05, 0xa0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, + 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x57, + 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x50, 0x30, 0x4e, 0x30, 0x4c, 0xa0, + 0x4a, 0xa0, 0x48, 0x86, 0x46, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, + 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, + 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x65, 0x78, 0x74, 0x65, 0x6e, + 0x64, 0x65, 0x64, 0x69, 0x73, 0x73, 0x75, 0x69, 0x6e, 0x67, 0x33, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x52, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4b, + 0x30, 0x49, 0x30, 0x47, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, + 0x6d, 0x01, 0x07, 0x17, 0x02, 0x30, 0x38, 0x30, 0x36, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2a, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x6f, 0x72, 0x79, 0x30, 0x7f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x73, 0x30, 0x71, 0x30, 0x23, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64, + 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4a, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x72, 0x74, + 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x48, + 0xdf, 0x60, 0x32, 0xcc, 0x89, 0x01, 0xb6, 0xdc, 0x2f, 0xe3, 0x73, 0xb5, + 0x9c, 0x16, 0x58, 0x32, 0x68, 0xa9, 0xc3, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xfd, 0xac, 0x61, 0x32, + 0x93, 0x6c, 0x45, 0xd6, 0xe2, 0xee, 0x85, 0x5f, 0x9a, 0xba, 0xe7, 0x76, + 0x99, 0x68, 0xcc, 0xe7, 0x30, 0x23, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, + 0x1c, 0x30, 0x1a, 0x82, 0x0c, 0x2a, 0x2e, 0x77, 0x65, 0x62, 0x6b, 0x69, + 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x82, 0x0a, 0x77, 0x65, 0x62, 0x6b, 0x69, + 0x74, 0x2e, 0x6f, 0x72, 0x67 + }; + + // The signature algorithm is specified as the following ASN.1 structure: + // AlgorithmIdentifier ::= SEQUENCE { + // algorithm OBJECT IDENTIFIER, + // parameters ANY DEFINED BY algorithm OPTIONAL } + // + const uint8 signature_algorithm[15] = { + 0x30, 0x0d, // a SEQUENCE of length 13 (0xd) + 0x06, 0x09, // an OBJECT IDENTIFIER of length 9 + // 1.2.840.113549.1.1.5 - sha1WithRSAEncryption + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + 0x05, 0x00, // a NULL of length 0 + }; + + // RSA signature, a big integer in the big-endian byte order. + const uint8 signature[256] = { + 0x1e, 0x6a, 0xe7, 0xe0, 0x4f, 0xe7, 0x4d, 0xd0, 0x69, 0x7c, 0xf8, 0x8f, + 0x99, 0xb4, 0x18, 0x95, 0x36, 0x24, 0x0f, 0x0e, 0xa3, 0xea, 0x34, 0x37, + 0xf4, 0x7d, 0xd5, 0x92, 0x35, 0x53, 0x72, 0x76, 0x3f, 0x69, 0xf0, 0x82, + 0x56, 0xe3, 0x94, 0x7a, 0x1d, 0x1a, 0x81, 0xaf, 0x9f, 0xc7, 0x43, 0x01, + 0x64, 0xd3, 0x7c, 0x0d, 0xc8, 0x11, 0x4e, 0x4a, 0xe6, 0x1a, 0xc3, 0x01, + 0x74, 0xe8, 0x35, 0x87, 0x5c, 0x61, 0xaa, 0x8a, 0x46, 0x06, 0xbe, 0x98, + 0x95, 0x24, 0x9e, 0x01, 0xe3, 0xe6, 0xa0, 0x98, 0xee, 0x36, 0x44, 0x56, + 0x8d, 0x23, 0x9c, 0x65, 0xea, 0x55, 0x6a, 0xdf, 0x66, 0xee, 0x45, 0xe8, + 0xa0, 0xe9, 0x7d, 0x9a, 0xba, 0x94, 0xc5, 0xc8, 0xc4, 0x4b, 0x98, 0xff, + 0x9a, 0x01, 0x31, 0x6d, 0xf9, 0x2b, 0x58, 0xe7, 0xe7, 0x2a, 0xc5, 0x4d, + 0xbb, 0xbb, 0xcd, 0x0d, 0x70, 0xe1, 0xad, 0x03, 0xf5, 0xfe, 0xf4, 0x84, + 0x71, 0x08, 0xd2, 0xbc, 0x04, 0x7b, 0x26, 0x1c, 0xa8, 0x0f, 0x9c, 0xd8, + 0x12, 0x6a, 0x6f, 0x2b, 0x67, 0xa1, 0x03, 0x80, 0x9a, 0x11, 0x0b, 0xe9, + 0xe0, 0xb5, 0xb3, 0xb8, 0x19, 0x4e, 0x0c, 0xa4, 0xd9, 0x2b, 0x3b, 0xc2, + 0xca, 0x20, 0xd3, 0x0c, 0xa4, 0xff, 0x93, 0x13, 0x1f, 0xfc, 0xba, 0x94, + 0x93, 0x8c, 0x64, 0x15, 0x2e, 0x28, 0xa9, 0x55, 0x8c, 0x2c, 0x48, 0xd3, + 0xd3, 0xc1, 0x50, 0x69, 0x19, 0xe8, 0x34, 0xd3, 0xf1, 0x04, 0x9f, 0x0a, + 0x7a, 0x21, 0x87, 0xbf, 0xb9, 0x59, 0x37, 0x2e, 0xf4, 0x71, 0xa5, 0x3e, + 0xbe, 0xcd, 0x70, 0x83, 0x18, 0xf8, 0x8a, 0x72, 0x85, 0x45, 0x1f, 0x08, + 0x01, 0x6f, 0x37, 0xf5, 0x2b, 0x7b, 0xea, 0xb9, 0x8b, 0xa3, 0xcc, 0xfd, + 0x35, 0x52, 0xdd, 0x66, 0xde, 0x4f, 0x30, 0xc5, 0x73, 0x81, 0xb6, 0xe8, + 0x3c, 0xd8, 0x48, 0x8a + }; + + // The public key is specified as the following ASN.1 structure: + // SubjectPublicKeyInfo ::= SEQUENCE { + // algorithm AlgorithmIdentifier, + // subjectPublicKey BIT STRING } + const uint8 public_key_info[294] = { + 0x30, 0x82, 0x01, 0x22, // a SEQUENCE of length 290 (0x122) + // algorithm + 0x30, 0x0d, // a SEQUENCE of length 13 + 0x06, 0x09, // an OBJECT IDENTIFIER of length 9 + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, // a NULL of length 0 + // subjectPublicKey + 0x03, 0x82, 0x01, 0x0f, // a BIT STRING of length 271 (0x10f) + 0x00, // number of unused bits + 0x30, 0x82, 0x01, 0x0a, // a SEQUENCE of length 266 (0x10a) + // modulus + 0x02, 0x82, 0x01, 0x01, // an INTEGER of length 257 (0x101) + 0x00, 0xc4, 0x2d, 0xd5, 0x15, 0x8c, 0x9c, 0x26, 0x4c, 0xec, + 0x32, 0x35, 0xeb, 0x5f, 0xb8, 0x59, 0x01, 0x5a, 0xa6, 0x61, + 0x81, 0x59, 0x3b, 0x70, 0x63, 0xab, 0xe3, 0xdc, 0x3d, 0xc7, + 0x2a, 0xb8, 0xc9, 0x33, 0xd3, 0x79, 0xe4, 0x3a, 0xed, 0x3c, + 0x30, 0x23, 0x84, 0x8e, 0xb3, 0x30, 0x14, 0xb6, 0xb2, 0x87, + 0xc3, 0x3d, 0x95, 0x54, 0x04, 0x9e, 0xdf, 0x99, 0xdd, 0x0b, + 0x25, 0x1e, 0x21, 0xde, 0x65, 0x29, 0x7e, 0x35, 0xa8, 0xa9, + 0x54, 0xeb, 0xf6, 0xf7, 0x32, 0x39, 0xd4, 0x26, 0x55, 0x95, + 0xad, 0xef, 0xfb, 0xfe, 0x58, 0x86, 0xd7, 0x9e, 0xf4, 0x00, + 0x8d, 0x8c, 0x2a, 0x0c, 0xbd, 0x42, 0x04, 0xce, 0xa7, 0x3f, + 0x04, 0xf6, 0xee, 0x80, 0xf2, 0xaa, 0xef, 0x52, 0xa1, 0x69, + 0x66, 0xda, 0xbe, 0x1a, 0xad, 0x5d, 0xda, 0x2c, 0x66, 0xea, + 0x1a, 0x6b, 0xbb, 0xe5, 0x1a, 0x51, 0x4a, 0x00, 0x2f, 0x48, + 0xc7, 0x98, 0x75, 0xd8, 0xb9, 0x29, 0xc8, 0xee, 0xf8, 0x66, + 0x6d, 0x0a, 0x9c, 0xb3, 0xf3, 0xfc, 0x78, 0x7c, 0xa2, 0xf8, + 0xa3, 0xf2, 0xb5, 0xc3, 0xf3, 0xb9, 0x7a, 0x91, 0xc1, 0xa7, + 0xe6, 0x25, 0x2e, 0x9c, 0xa8, 0xed, 0x12, 0x65, 0x6e, 0x6a, + 0xf6, 0x12, 0x44, 0x53, 0x70, 0x30, 0x95, 0xc3, 0x9c, 0x2b, + 0x58, 0x2b, 0x3d, 0x08, 0x74, 0x4a, 0xf2, 0xbe, 0x51, 0xb0, + 0xbf, 0x87, 0xd0, 0x4c, 0x27, 0x58, 0x6b, 0xb5, 0x35, 0xc5, + 0x9d, 0xaf, 0x17, 0x31, 0xf8, 0x0b, 0x8f, 0xee, 0xad, 0x81, + 0x36, 0x05, 0x89, 0x08, 0x98, 0xcf, 0x3a, 0xaf, 0x25, 0x87, + 0xc0, 0x49, 0xea, 0xa7, 0xfd, 0x67, 0xf7, 0x45, 0x8e, 0x97, + 0xcc, 0x14, 0x39, 0xe2, 0x36, 0x85, 0xb5, 0x7e, 0x1a, 0x37, + 0xfd, 0x16, 0xf6, 0x71, 0x11, 0x9a, 0x74, 0x30, 0x16, 0xfe, + 0x13, 0x94, 0xa3, 0x3f, 0x84, 0x0d, 0x4f, + // public exponent + 0x02, 0x03, // an INTEGER of length 3 + 0x01, 0x00, 0x01 + }; + + // We use the signature verifier to perform four signature verification + // tests. + crypto::SignatureVerifier verifier; + bool ok; + + // Test 1: feed all of the data to the verifier at once (a single + // VerifyUpdate call). + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + signature, sizeof(signature), + public_key_info, sizeof(public_key_info)); + EXPECT_TRUE(ok); + verifier.VerifyUpdate(tbs_certificate, sizeof(tbs_certificate)); + ok = verifier.VerifyFinal(); + EXPECT_TRUE(ok); + + // Test 2: feed the data to the verifier in three parts (three VerifyUpdate + // calls). + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + signature, sizeof(signature), + public_key_info, sizeof(public_key_info)); + EXPECT_TRUE(ok); + verifier.VerifyUpdate(tbs_certificate, 256); + verifier.VerifyUpdate(tbs_certificate + 256, 256); + verifier.VerifyUpdate(tbs_certificate + 512, sizeof(tbs_certificate) - 512); + ok = verifier.VerifyFinal(); + EXPECT_TRUE(ok); + + // Test 3: verify the signature with incorrect data. + uint8 bad_tbs_certificate[sizeof(tbs_certificate)]; + memcpy(bad_tbs_certificate, tbs_certificate, sizeof(tbs_certificate)); + bad_tbs_certificate[10] += 1; // Corrupt one byte of the data. + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + signature, sizeof(signature), + public_key_info, sizeof(public_key_info)); + EXPECT_TRUE(ok); + verifier.VerifyUpdate(bad_tbs_certificate, sizeof(bad_tbs_certificate)); + ok = verifier.VerifyFinal(); + + // Purify disables digital signature verification, causing the Windows + // CryptoAPI function CryptVerifySignature to always succeed. So we can't + // check the signature verification results of the negative tests when + // running inside Purify. See http://crbug.com/10031. +#ifndef PURIFY + EXPECT_FALSE(ok); +#endif + + // Test 4: verify a bad signature. + uint8 bad_signature[sizeof(signature)]; + memcpy(bad_signature, signature, sizeof(signature)); + bad_signature[10] += 1; // Corrupt one byte of the signature. + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + bad_signature, sizeof(bad_signature), + public_key_info, sizeof(public_key_info)); + + // A crypto library (e.g., NSS) may detect that the signature is corrupted + // and cause VerifyInit to return false, so it is fine for 'ok' to be false. + if (ok) { + verifier.VerifyUpdate(tbs_certificate, sizeof(tbs_certificate)); + ok = verifier.VerifyFinal(); +#ifndef PURIFY + EXPECT_FALSE(ok); +#endif + } +} diff --git a/crypto/signature_verifier_win.cc b/crypto/signature_verifier_win.cc new file mode 100644 index 0000000..8bf094f --- /dev/null +++ b/crypto/signature_verifier_win.cc @@ -0,0 +1,134 @@ +// Copyright (c) 2011 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 "crypto/signature_verifier.h" + +#include "base/logging.h" + +#pragma comment(lib, "crypt32.lib") + +namespace { + +// Wrappers of malloc and free for CRYPT_DECODE_PARA, which requires the +// WINAPI calling convention. +void* WINAPI MyCryptAlloc(size_t size) { + return malloc(size); +} + +void WINAPI MyCryptFree(void* p) { + free(p); +} + +} // namespace + +namespace crypto { + +SignatureVerifier::SignatureVerifier() : hash_object_(0), public_key_(0) { + if (!CryptAcquireContext(provider_.receive(), NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + provider_.reset(); +} + +SignatureVerifier::~SignatureVerifier() { +} + +bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len) { + signature_.reserve(signature_len); + // CryptoAPI uses big integers in the little-endian byte order, so we need + // to first swap the order of signature bytes. + for (int i = signature_len - 1; i >= 0; --i) + signature_.push_back(signature[i]); + + CRYPT_DECODE_PARA decode_para; + decode_para.cbSize = sizeof(decode_para); + decode_para.pfnAlloc = MyCryptAlloc; + decode_para.pfnFree = MyCryptFree; + CERT_PUBLIC_KEY_INFO* cert_public_key_info = NULL; + DWORD struct_len = 0; + BOOL ok; + ok = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + X509_PUBLIC_KEY_INFO, + public_key_info, + public_key_info_len, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, + &decode_para, + &cert_public_key_info, + &struct_len); + if (!ok) + return false; + + ok = CryptImportPublicKeyInfo(provider_, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + cert_public_key_info, public_key_.receive()); + free(cert_public_key_info); + if (!ok) + return false; + + CRYPT_ALGORITHM_IDENTIFIER* signature_algorithm_id; + struct_len = 0; + ok = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + X509_ALGORITHM_IDENTIFIER, + signature_algorithm, + signature_algorithm_len, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, + &decode_para, + &signature_algorithm_id, + &struct_len); + DCHECK(ok || GetLastError() == ERROR_FILE_NOT_FOUND); + ALG_ID hash_alg_id; + if (ok) { + hash_alg_id = CALG_MD4; // Initialize to a weak hash algorithm that we + // don't support. + if (!strcmp(signature_algorithm_id->pszObjId, szOID_RSA_SHA1RSA)) + hash_alg_id = CALG_SHA1; + else if (!strcmp(signature_algorithm_id->pszObjId, szOID_RSA_MD5RSA)) + hash_alg_id = CALG_MD5; + free(signature_algorithm_id); + DCHECK(hash_alg_id != CALG_MD4); + if (hash_alg_id == CALG_MD4) + return false; // Unsupported hash algorithm. + } else if (GetLastError() == ERROR_FILE_NOT_FOUND) { + // TODO(wtc): X509_ALGORITHM_IDENTIFIER isn't supported on XP SP2. We + // may be able to encapsulate signature_algorithm in a dummy SignedContent + // and decode it with X509_CERT into a CERT_SIGNED_CONTENT_INFO. For now, + // just hardcode the hash algorithm to be SHA-1. + hash_alg_id = CALG_SHA1; + } else { + return false; + } + + ok = CryptCreateHash(provider_, hash_alg_id, 0, 0, hash_object_.receive()); + if (!ok) + return false; + return true; +} + +void SignatureVerifier::VerifyUpdate(const uint8* data_part, + int data_part_len) { + BOOL ok = CryptHashData(hash_object_, data_part, data_part_len, 0); + DCHECK(ok) << "CryptHashData failed: " << GetLastError(); +} + +bool SignatureVerifier::VerifyFinal() { + BOOL ok = CryptVerifySignature(hash_object_, &signature_[0], + signature_.size(), public_key_, NULL, 0); + Reset(); + if (!ok) + return false; + return true; +} + +void SignatureVerifier::Reset() { + hash_object_.reset(); + public_key_.reset(); + signature_.clear(); +} + +} // namespace crypto + diff --git a/crypto/symmetric_key.h b/crypto/symmetric_key.h new file mode 100644 index 0000000..c5860b5 --- /dev/null +++ b/crypto/symmetric_key.h @@ -0,0 +1,104 @@ +// Copyright (c) 2011 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 CRYPTO_SYMMETRIC_KEY_H_ +#define CRYPTO_SYMMETRIC_KEY_H_ +#pragma once + +#include + +#include "base/basictypes.h" + +#if defined(USE_NSS) +#include "crypto/scoped_nss_types.h" +#elif defined(OS_MACOSX) +#include +#elif defined(OS_WIN) +#include "crypto/scoped_capi_types.h" +#endif + +namespace crypto { + +// Wraps a platform-specific symmetric key and allows it to be held in a +// scoped_ptr. +class SymmetricKey { + public: + // Defines the algorithm that a key will be used with. See also + // classs Encrptor. + enum Algorithm { + AES, + HMAC_SHA1, + }; + + virtual ~SymmetricKey(); + + // Generates a random key suitable to be used with |algorithm| and of + // |key_size_in_bits| bits. + // The caller is responsible for deleting the returned SymmetricKey. + static SymmetricKey* GenerateRandomKey(Algorithm algorithm, + size_t key_size_in_bits); + + // Derives a key from the supplied password and salt using PBKDF2, suitable + // for use with specified |algorithm|. Note |algorithm| is not the algorithm + // used to derive the key from the password. The caller is responsible for + // deleting the returned SymmetricKey. + static SymmetricKey* DeriveKeyFromPassword(Algorithm algorithm, + const std::string& password, + const std::string& salt, + size_t iterations, + size_t key_size_in_bits); + + // Imports an array of key bytes in |raw_key|. This key may have been + // generated by GenerateRandomKey or DeriveKeyFromPassword and exported with + // GetRawKey, or via another compatible method. The key must be of suitable + // size for use with |algorithm|. The caller owns the returned SymmetricKey. + static SymmetricKey* Import(Algorithm algorithm, const std::string& raw_key); + +#if defined(USE_OPENSSL) + const std::string& key() { return key_; } +#elif defined(USE_NSS) + PK11SymKey* key() const { return key_.get(); } +#elif defined(OS_MACOSX) + CSSM_DATA cssm_data() const; +#elif defined(OS_WIN) + HCRYPTKEY key() const { return key_.get(); } +#endif + + // Extracts the raw key from the platform specific data. + // Warning: |raw_key| holds the raw key as bytes and thus must be handled + // carefully. + bool GetRawKey(std::string* raw_key); + + private: +#if defined(USE_OPENSSL) + SymmetricKey() {} + std::string key_; +#elif defined(USE_NSS) + explicit SymmetricKey(PK11SymKey* key); + ScopedPK11SymKey key_; +#elif defined(OS_MACOSX) + SymmetricKey(const void* key_data, size_t key_size_in_bits); + std::string key_; +#elif defined(OS_WIN) + SymmetricKey(HCRYPTPROV provider, HCRYPTKEY key, + const void* key_data, size_t key_size_in_bytes); + + ScopedHCRYPTPROV provider_; + ScopedHCRYPTKEY key_; + + // Contains the raw key, if it is known during initialization and when it + // is likely that the associated |provider_| will be unable to export the + // |key_|. This is the case of HMAC keys when the key size exceeds 16 bytes + // when using the default RSA provider. + // TODO(rsleevi): See if KP_EFFECTIVE_KEYLEN is the reason why CryptExportKey + // fails with NTE_BAD_KEY/NTE_BAD_LEN + std::string raw_key_; +#endif + + DISALLOW_COPY_AND_ASSIGN(SymmetricKey); +}; + +} // namespace crypto + +#endif // CRYPTO_SYMMETRIC_KEY_H_ diff --git a/crypto/symmetric_key_mac.cc b/crypto/symmetric_key_mac.cc new file mode 100644 index 0000000..47193a08 --- /dev/null +++ b/crypto/symmetric_key_mac.cc @@ -0,0 +1,155 @@ +// Copyright (c) 2011 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 "crypto/symmetric_key.h" + +#include +#include +#include + +#include "base/logging.h" +#include "crypto/cssm_init.h" + +namespace { + +CSSM_KEY_TYPE CheckKeyParams(crypto::SymmetricKey::Algorithm algorithm, + size_t key_size_in_bits) { + if (algorithm == crypto::SymmetricKey::AES) { + CHECK(key_size_in_bits == 128 || + key_size_in_bits == 192 || + key_size_in_bits == 256) + << "Invalid key size " << key_size_in_bits << " bits"; + return CSSM_ALGID_AES; + } else { + // FIPS 198 Section 3 requires a HMAC-SHA-1 derived keys to be at least + // (HMAC-SHA-1 output size / 2) to be compliant. Since the ouput size of + // HMAC-SHA-1 is 160 bits, we require at least 80 bits here. + CHECK(algorithm == crypto::SymmetricKey::HMAC_SHA1); + CHECK(key_size_in_bits >= 80 && (key_size_in_bits % 8) == 0) + << "Invalid key size " << key_size_in_bits << " bits"; + return CSSM_ALGID_SHA1HMAC_LEGACY; + } +} + +void* CreateRandomBytes(size_t size) { + CSSM_RETURN err; + CSSM_CC_HANDLE ctx; + err = CSSM_CSP_CreateRandomGenContext(crypto::GetSharedCSPHandle(), + CSSM_ALGID_APPLE_YARROW, + NULL, + size, &ctx); + if (err) { + crypto::LogCSSMError("CSSM_CSP_CreateRandomGenContext", err); + return NULL; + } + CSSM_DATA random_data = {}; + err = CSSM_GenerateRandom(ctx, &random_data); + if (err) { + crypto::LogCSSMError("CSSM_GenerateRandom", err); + random_data.Data = NULL; + } + CSSM_DeleteContext(ctx); + return random_data.Data; // Caller responsible for freeing this +} + +inline CSSM_DATA StringToData(const std::string& str) { + CSSM_DATA data = { + str.size(), + reinterpret_cast(const_cast(str.data())) + }; + return data; +} + +} // namespace + +namespace crypto { + +SymmetricKey::~SymmetricKey() {} + +// static +SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm, + size_t key_size_in_bits) { + CheckKeyParams(algorithm, key_size_in_bits); + void* random_bytes = CreateRandomBytes((key_size_in_bits + 7) / 8); + if (!random_bytes) + return NULL; + SymmetricKey *key = new SymmetricKey(random_bytes, key_size_in_bits); + free(random_bytes); + return key; +} + +// static +SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm, + const std::string& password, + const std::string& salt, + size_t iterations, + size_t key_size_in_bits) { + // Derived (haha) from cdsaDeriveKey() in Apple's CryptoSample. + CSSM_KEY_TYPE key_type = CheckKeyParams(algorithm, key_size_in_bits); + SymmetricKey* derived_key = NULL; + CSSM_KEY cssm_key = {}; + + CSSM_CC_HANDLE ctx = 0; + CSSM_ACCESS_CREDENTIALS credentials = {}; + CSSM_RETURN err; + CSSM_DATA salt_data = StringToData(salt); + err = CSSM_CSP_CreateDeriveKeyContext(GetSharedCSPHandle(), + CSSM_ALGID_PKCS5_PBKDF2, + key_type, key_size_in_bits, + &credentials, + NULL, + iterations, + &salt_data, + NULL, + &ctx); + if (err) { + LogCSSMError("CSSM_CSP_CreateDeriveKeyContext", err); + return NULL; + } + + CSSM_PKCS5_PBKDF2_PARAMS params = {}; + params.Passphrase = StringToData(password); + params.PseudoRandomFunction = CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1; + CSSM_DATA param_data = {sizeof(params), reinterpret_cast(¶ms)}; + err = CSSM_DeriveKey(ctx, + ¶m_data, + CSSM_KEYUSE_ANY, + CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, + NULL, + NULL, + &cssm_key); + if (err) { + LogCSSMError("CSSM_DeriveKey", err); + goto exit; + } + + DCHECK_EQ(cssm_key.KeyData.Length, key_size_in_bits / 8); + derived_key = new SymmetricKey(cssm_key.KeyData.Data, key_size_in_bits); + + exit: + CSSM_DeleteContext(ctx); + CSSM_FreeKey(GetSharedCSPHandle(), &credentials, &cssm_key, false); + return derived_key; +} + +// static +SymmetricKey* SymmetricKey::Import(Algorithm algorithm, + const std::string& raw_key) { + return new SymmetricKey(raw_key.data(), raw_key.size() * 8); +} + +SymmetricKey::SymmetricKey(const void *key_data, size_t key_size_in_bits) + : key_(reinterpret_cast(key_data), + key_size_in_bits / 8) {} + +bool SymmetricKey::GetRawKey(std::string* raw_key) { + *raw_key = key_; + return true; +} + +CSSM_DATA SymmetricKey::cssm_data() const { + return StringToData(key_); +} + +} // namespace crypto diff --git a/crypto/symmetric_key_nss.cc b/crypto/symmetric_key_nss.cc new file mode 100644 index 0000000..9690265 --- /dev/null +++ b/crypto/symmetric_key_nss.cc @@ -0,0 +1,127 @@ +// Copyright (c) 2011 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 "crypto/symmetric_key.h" + +#include +#include + +#include "base/logging.h" +#include "crypto/nss_util.h" + +namespace crypto { + +SymmetricKey::~SymmetricKey() {} + +// static +SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm, + size_t key_size_in_bits) { + DCHECK_EQ(AES, algorithm); + + EnsureNSSInit(); + if (key_size_in_bits == 0) + return NULL; + + ScopedPK11Slot slot(PK11_GetBestSlot(CKM_AES_KEY_GEN, NULL)); + if (!slot.get()) + return NULL; + + PK11SymKey* sym_key = PK11_KeyGen(slot.get(), CKM_AES_KEY_GEN, NULL, + key_size_in_bits / 8, NULL); + if (!sym_key) + return NULL; + + return new SymmetricKey(sym_key); +} + +// static +SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm, + const std::string& password, + const std::string& salt, + size_t iterations, + size_t key_size_in_bits) { + EnsureNSSInit(); + if (salt.empty() || iterations == 0 || key_size_in_bits == 0) + return NULL; + + SECItem password_item; + password_item.type = siBuffer; + password_item.data = reinterpret_cast( + const_cast(password.data())); + password_item.len = password.size(); + + SECItem salt_item; + salt_item.type = siBuffer; + salt_item.data = reinterpret_cast( + const_cast(salt.data())); + salt_item.len = salt.size(); + + SECOidTag cipher_algorithm = + algorithm == AES ? SEC_OID_AES_256_CBC : SEC_OID_HMAC_SHA1; + ScopedSECAlgorithmID alg_id(PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, + cipher_algorithm, + SEC_OID_HMAC_SHA1, + key_size_in_bits / 8, + iterations, + &salt_item)); + if (!alg_id.get()) + return NULL; + + ScopedPK11Slot slot(PK11_GetBestSlot(SEC_OID_PKCS5_PBKDF2, NULL)); + if (!slot.get()) + return NULL; + + PK11SymKey* sym_key = PK11_PBEKeyGen(slot.get(), alg_id.get(), &password_item, + PR_FALSE, NULL); + if (!sym_key) + return NULL; + + return new SymmetricKey(sym_key); +} + +// static +SymmetricKey* SymmetricKey::Import(Algorithm algorithm, + const std::string& raw_key) { + CK_MECHANISM_TYPE cipher = + algorithm == AES ? CKM_AES_CBC : CKM_SHA_1_HMAC; + + SECItem key_item; + key_item.type = siBuffer; + key_item.data = reinterpret_cast( + const_cast(raw_key.data())); + key_item.len = raw_key.size(); + + ScopedPK11Slot slot(PK11_GetBestSlot(cipher, NULL)); + if (!slot.get()) + return NULL; + + // The exact value of the |origin| argument doesn't matter to NSS as long as + // it's not PK11_OriginFortezzaHack, so we pass PK11_OriginUnwrap as a + // placeholder. + PK11SymKey* sym_key = PK11_ImportSymKey(slot.get(), cipher, PK11_OriginUnwrap, + CKA_ENCRYPT, &key_item, NULL); + if (!sym_key) + return NULL; + + return new SymmetricKey(sym_key); +} + +bool SymmetricKey::GetRawKey(std::string* raw_key) { + SECStatus rv = PK11_ExtractKeyValue(key_.get()); + if (SECSuccess != rv) + return false; + + SECItem* key_item = PK11_GetKeyData(key_.get()); + if (!key_item) + return false; + + raw_key->assign(reinterpret_cast(key_item->data), key_item->len); + return true; +} + +SymmetricKey::SymmetricKey(PK11SymKey* key) : key_(key) { + DCHECK(key); +} + +} // namespace crypto diff --git a/crypto/symmetric_key_openssl.cc b/crypto/symmetric_key_openssl.cc new file mode 100644 index 0000000..1d1ad23 --- /dev/null +++ b/crypto/symmetric_key_openssl.cc @@ -0,0 +1,76 @@ +// Copyright (c) 2011 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 "crypto/symmetric_key.h" + +#include +#include + +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/string_util.h" +#include "crypto/openssl_util.h" + +namespace crypto { + +SymmetricKey::~SymmetricKey() { + std::fill(key_.begin(), key_.end(), '\0'); // Zero out the confidential key. +} + +// static +SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm, + size_t key_size_in_bits) { + DCHECK_EQ(AES, algorithm); + int key_size_in_bytes = key_size_in_bits / 8; + DCHECK_EQ(static_cast(key_size_in_bits), key_size_in_bytes * 8); + + if (key_size_in_bits == 0) + return NULL; + + OpenSSLErrStackTracer err_tracer(FROM_HERE); + scoped_ptr key(new SymmetricKey); + uint8* key_data = + reinterpret_cast(WriteInto(&key->key_, key_size_in_bytes + 1)); + + int rv = RAND_bytes(key_data, key_size_in_bytes); + return rv == 1 ? key.release() : NULL; +} + +// static +SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm, + const std::string& password, + const std::string& salt, + size_t iterations, + size_t key_size_in_bits) { + DCHECK(algorithm == AES || algorithm == HMAC_SHA1); + int key_size_in_bytes = key_size_in_bits / 8; + DCHECK_EQ(static_cast(key_size_in_bits), key_size_in_bytes * 8); + + OpenSSLErrStackTracer err_tracer(FROM_HERE); + scoped_ptr key(new SymmetricKey); + uint8* key_data = + reinterpret_cast(WriteInto(&key->key_, key_size_in_bytes + 1)); + int rv = PKCS5_PBKDF2_HMAC_SHA1(password.data(), password.length(), + reinterpret_cast(salt.data()), + salt.length(), iterations, + key_size_in_bytes, key_data); + return rv == 1 ? key.release() : NULL; +} + +// static +SymmetricKey* SymmetricKey::Import(Algorithm algorithm, + const std::string& raw_key) { + scoped_ptr key(new SymmetricKey); + key->key_ = raw_key; + return key.release(); +} + +bool SymmetricKey::GetRawKey(std::string* raw_key) { + *raw_key = key_; + return true; +} + +} // namespace crypto diff --git a/crypto/symmetric_key_unittest.cc b/crypto/symmetric_key_unittest.cc new file mode 100644 index 0000000..a07194e --- /dev/null +++ b/crypto/symmetric_key_unittest.cc @@ -0,0 +1,225 @@ +// Copyright (c) 2011 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 "crypto/symmetric_key.h" + +#include + +#include "base/memory/scoped_ptr.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(SymmetricKeyTest, GenerateRandomKey) { + scoped_ptr key( + crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES, 256)); + ASSERT_TRUE(NULL != key.get()); + std::string raw_key; + EXPECT_TRUE(key->GetRawKey(&raw_key)); + EXPECT_EQ(32U, raw_key.size()); + + // Do it again and check that the keys are different. + // (Note: this has a one-in-10^77 chance of failure!) + scoped_ptr key2( + crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES, 256)); + ASSERT_TRUE(NULL != key2.get()); + std::string raw_key2; + EXPECT_TRUE(key2->GetRawKey(&raw_key2)); + EXPECT_EQ(32U, raw_key2.size()); + EXPECT_NE(raw_key, raw_key2); +} + +TEST(SymmetricKeyTest, ImportGeneratedKey) { + scoped_ptr key1( + crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES, 256)); + ASSERT_TRUE(NULL != key1.get()); + std::string raw_key1; + EXPECT_TRUE(key1->GetRawKey(&raw_key1)); + + scoped_ptr key2( + crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key1)); + ASSERT_TRUE(NULL != key2.get()); + + std::string raw_key2; + EXPECT_TRUE(key2->GetRawKey(&raw_key2)); + + EXPECT_EQ(raw_key1, raw_key2); +} + +TEST(SymmetricKeyTest, ImportDerivedKey) { + scoped_ptr key1( + crypto::SymmetricKey::DeriveKeyFromPassword( + crypto::SymmetricKey::HMAC_SHA1, "password", "somesalt", 1024, 160)); + ASSERT_TRUE(NULL != key1.get()); + std::string raw_key1; + EXPECT_TRUE(key1->GetRawKey(&raw_key1)); + + scoped_ptr key2( + crypto::SymmetricKey::Import(crypto::SymmetricKey::HMAC_SHA1, raw_key1)); + ASSERT_TRUE(NULL != key2.get()); + + std::string raw_key2; + EXPECT_TRUE(key2->GetRawKey(&raw_key2)); + + EXPECT_EQ(raw_key1, raw_key2); +} + +struct PBKDF2TestVector { + crypto::SymmetricKey::Algorithm algorithm; + const char* password; + const char* salt; + unsigned int rounds; + unsigned int key_size_in_bits; + const char* expected; // ASCII encoded hex bytes +}; + +class SymmetricKeyDeriveKeyFromPasswordTest + : public testing::TestWithParam { +}; + +TEST_P(SymmetricKeyDeriveKeyFromPasswordTest, DeriveKeyFromPassword) { + PBKDF2TestVector test_data(GetParam()); +#if defined(OS_MACOSX) + // The OS X crypto libraries have minimum salt and iteration requirements + // so some of the tests below will cause them to barf. Skip these. + if (strlen(test_data.salt) < 8 || test_data.rounds < 1000) { + VLOG(1) << "Skipped test vector for " << test_data.expected; + return; + } +#endif // OS_MACOSX + + scoped_ptr key( + crypto::SymmetricKey::DeriveKeyFromPassword( + test_data.algorithm, + test_data.password, test_data.salt, + test_data.rounds, test_data.key_size_in_bits)); + ASSERT_TRUE(NULL != key.get()); + + std::string raw_key; + key->GetRawKey(&raw_key); + EXPECT_EQ(test_data.key_size_in_bits / 8, raw_key.size()); + EXPECT_EQ(test_data.expected, + StringToLowerASCII(base::HexEncode(raw_key.data(), + raw_key.size()))); +} + +static const PBKDF2TestVector kTestVectors[] = { + // These tests come from + // http://www.ietf.org/id/draft-josefsson-pbkdf2-test-vectors-00.txt + { + crypto::SymmetricKey::HMAC_SHA1, + "password", + "salt", + 1, + 160, + "0c60c80f961f0e71f3a9b524af6012062fe037a6", + }, + { + crypto::SymmetricKey::HMAC_SHA1, + "password", + "salt", + 2, + 160, + "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957", + }, + { + crypto::SymmetricKey::HMAC_SHA1, + "password", + "salt", + 4096, + 160, + "4b007901b765489abead49d926f721d065a429c1", + }, + // This test takes over 30s to run on the trybots. +#if 0 + { + crypto::SymmetricKey::HMAC_SHA1, + "password", + "salt", + 16777216, + 160, + "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984", + }, +#endif + + // These tests come from RFC 3962, via BSD source code at + // http://www.openbsd.org/cgi-bin/cvsweb/src/sbin/bioctl/pbkdf2.c?rev=HEAD&content-type=text/plain + { + crypto::SymmetricKey::HMAC_SHA1, + "password", + "ATHENA.MIT.EDUraeburn", + 1, + 160, + "cdedb5281bb2f801565a1122b25635150ad1f7a0", + }, + { + crypto::SymmetricKey::HMAC_SHA1, + "password", + "ATHENA.MIT.EDUraeburn", + 2, + 160, + "01dbee7f4a9e243e988b62c73cda935da05378b9", + }, + { + crypto::SymmetricKey::HMAC_SHA1, + "password", + "ATHENA.MIT.EDUraeburn", + 1200, + 160, + "5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddb", + }, + { + crypto::SymmetricKey::HMAC_SHA1, + "password", + "\0224VxxV4\022", /* 0x1234567878563412 */ + 5, + 160, + "d1daa78615f287e6a1c8b120d7062a493f98d203", + }, + { + crypto::SymmetricKey::HMAC_SHA1, + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "pass phrase equals block size", + 1200, + 160, + "139c30c0966bc32ba55fdbf212530ac9c5ec59f1", + }, + { + crypto::SymmetricKey::HMAC_SHA1, + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "pass phrase exceeds block size", + 1200, + 160, + "9ccad6d468770cd51b10e6a68721be611a8b4d28", + }, + { + crypto::SymmetricKey::HMAC_SHA1, + "\360\235\204\236", /* g-clef (0xf09d849e) */ + "EXAMPLE.COMpianist", + 50, + 160, + "6b9cf26d45455a43a5b8bb276a403b39e7fe37a0", + }, + + // Regression tests for AES keys, derived from the Linux NSS implementation. + { + crypto::SymmetricKey::AES, + "A test password", + "saltsalt", + 1, + 256, + "44899a7777f0e6e8b752f875f02044b8ac593de146de896f2e8a816e315a36de", + }, + { + crypto::SymmetricKey::AES, + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "pass phrase exceeds block size", + 20, + 256, + "e0739745dc28b8721ba402e05214d2ac1eab54cf72bee1fba388297a09eb493c", + }, +}; + +INSTANTIATE_TEST_CASE_P(, SymmetricKeyDeriveKeyFromPasswordTest, + testing::ValuesIn(kTestVectors)); diff --git a/crypto/symmetric_key_win.cc b/crypto/symmetric_key_win.cc new file mode 100644 index 0000000..d2034e0 --- /dev/null +++ b/crypto/symmetric_key_win.cc @@ -0,0 +1,536 @@ +// Copyright (c) 2011 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 "crypto/symmetric_key.h" + +#include // For htonl. + +#include + +// TODO(wtc): replace scoped_array by std::vector. +#include "base/memory/scoped_ptr.h" + +namespace crypto { + +namespace { + +// The following is a non-public Microsoft header documented in MSDN under +// CryptImportKey / CryptExportKey. Following the header is the byte array of +// the actual plaintext key. +struct PlaintextBlobHeader { + BLOBHEADER hdr; + DWORD cbKeySize; +}; + +// CryptoAPI makes use of three distinct ALG_IDs for AES, rather than just +// CALG_AES (which exists, but depending on the functions you are calling, may +// result in function failure, whereas the subtype would succeed). +ALG_ID GetAESAlgIDForKeySize(size_t key_size_in_bits) { + // Only AES-128/-192/-256 is supported in CryptoAPI. + switch (key_size_in_bits) { + case 128: + return CALG_AES_128; + case 192: + return CALG_AES_192; + case 256: + return CALG_AES_256; + default: + NOTREACHED(); + return 0; + } +}; + +// Imports a raw/plaintext key of |key_size| stored in |*key_data| into a new +// key created for the specified |provider|. |alg| contains the algorithm of +// the key being imported. +// If |key_data| is intended to be used as an HMAC key, then |alg| should be +// CALG_HMAC. +// If successful, returns true and stores the imported key in |*key|. +// TODO(wtc): use this function in hmac_win.cc. +bool ImportRawKey(HCRYPTPROV provider, + ALG_ID alg, + const void* key_data, DWORD key_size, + ScopedHCRYPTKEY* key) { + DCHECK_GT(key_size, 0); + + DWORD actual_size = sizeof(PlaintextBlobHeader) + key_size; + std::vector tmp_data(actual_size); + BYTE* actual_key = &tmp_data[0]; + memcpy(actual_key + sizeof(PlaintextBlobHeader), key_data, key_size); + PlaintextBlobHeader* key_header = + reinterpret_cast(actual_key); + memset(key_header, 0, sizeof(PlaintextBlobHeader)); + + key_header->hdr.bType = PLAINTEXTKEYBLOB; + key_header->hdr.bVersion = CUR_BLOB_VERSION; + key_header->hdr.aiKeyAlg = alg; + + key_header->cbKeySize = key_size; + + HCRYPTKEY unsafe_key = NULL; + DWORD flags = CRYPT_EXPORTABLE; + if (alg == CALG_HMAC) { + // Though it may appear odd that IPSEC and RC2 are being used, this is + // done in accordance with Microsoft's FIPS 140-2 Security Policy for the + // RSA Enhanced Provider, as the approved means of using arbitrary HMAC + // key material. + key_header->hdr.aiKeyAlg = CALG_RC2; + flags |= CRYPT_IPSEC_HMAC_KEY; + } + + BOOL ok = + CryptImportKey(provider, actual_key, actual_size, 0, flags, &unsafe_key); + + // Clean up the temporary copy of key, regardless of whether it was imported + // sucessfully or not. + SecureZeroMemory(actual_key, actual_size); + + if (!ok) + return false; + + key->reset(unsafe_key); + return true; +} + +// Attempts to generate a random AES key of |key_size_in_bits|. Returns true +// if generation is successful, storing the generated key in |*key| and the +// key provider (CSP) in |*provider|. +bool GenerateAESKey(size_t key_size_in_bits, + ScopedHCRYPTPROV* provider, + ScopedHCRYPTKEY* key) { + DCHECK(provider); + DCHECK(key); + + ALG_ID alg = GetAESAlgIDForKeySize(key_size_in_bits); + if (alg == 0) + return false; + + ScopedHCRYPTPROV safe_provider; + // Note: The only time NULL is safe to be passed as pszContainer is when + // dwFlags contains CRYPT_VERIFYCONTEXT, as all keys generated and/or used + // will be treated as ephemeral keys and not persisted. + BOOL ok = CryptAcquireContext(safe_provider.receive(), NULL, NULL, + PROV_RSA_AES, CRYPT_VERIFYCONTEXT); + if (!ok) + return false; + + ScopedHCRYPTKEY safe_key; + // In the FIPS 140-2 Security Policy for CAPI on XP/Vista+, Microsoft notes + // that CryptGenKey makes use of the same functionality exposed via + // CryptGenRandom. The reason this is being used, as opposed to + // CryptGenRandom and CryptImportKey is for compliance with the security + // policy + ok = CryptGenKey(safe_provider.get(), alg, CRYPT_EXPORTABLE, + safe_key.receive()); + if (!ok) + return false; + + key->swap(safe_key); + provider->swap(safe_provider); + + return true; +} + +// Returns true if the HMAC key size meets the requirement of FIPS 198 +// Section 3. |alg| is the hash function used in the HMAC. +bool CheckHMACKeySize(size_t key_size_in_bits, ALG_ID alg) { + DWORD hash_size = 0; + switch (alg) { + case CALG_SHA1: + hash_size = 20; + break; + case CALG_SHA_256: + hash_size = 32; + break; + case CALG_SHA_384: + hash_size = 48; + break; + case CALG_SHA_512: + hash_size = 64; + break; + } + if (hash_size == 0) + return false; + + // An HMAC key must be >= L/2, where L is the output size of the hash + // function being used. + return (key_size_in_bits >= (hash_size / 2 * 8) && + (key_size_in_bits % 8) == 0); +} + +// Attempts to generate a random, |key_size_in_bits|-long HMAC key, for use +// with the hash function |alg|. +// |key_size_in_bits| must be >= 1/2 the hash size of |alg| for security. +// Returns true if generation is successful, storing the generated key in +// |*key| and the key provider (CSP) in |*provider|. +bool GenerateHMACKey(size_t key_size_in_bits, + ALG_ID alg, + ScopedHCRYPTPROV* provider, + ScopedHCRYPTKEY* key, + scoped_array* raw_key) { + DCHECK(provider); + DCHECK(key); + DCHECK(raw_key); + + if (!CheckHMACKeySize(key_size_in_bits, alg)) + return false; + + ScopedHCRYPTPROV safe_provider; + // See comment in GenerateAESKey as to why NULL is acceptable for the + // container name. + BOOL ok = CryptAcquireContext(safe_provider.receive(), NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); + if (!ok) + return false; + + DWORD key_size_in_bytes = key_size_in_bits / 8; + scoped_array random(new BYTE[key_size_in_bytes]); + ok = CryptGenRandom(safe_provider, key_size_in_bytes, random.get()); + if (!ok) + return false; + + ScopedHCRYPTKEY safe_key; + bool rv = ImportRawKey(safe_provider, CALG_HMAC, random.get(), + key_size_in_bytes, &safe_key); + if (rv) { + key->swap(safe_key); + provider->swap(safe_provider); + raw_key->swap(random); + } + + SecureZeroMemory(random.get(), key_size_in_bytes); + return rv; +} + +// Attempts to create an HMAC hash instance using the specified |provider| +// and |key|. The inner hash function will be |hash_alg|. If successful, +// returns true and stores the hash in |*hash|. +// TODO(wtc): use this function in hmac_win.cc. +bool CreateHMACHash(HCRYPTPROV provider, + HCRYPTKEY key, + ALG_ID hash_alg, + ScopedHCRYPTHASH* hash) { + ScopedHCRYPTHASH safe_hash; + BOOL ok = CryptCreateHash(provider, CALG_HMAC, key, 0, safe_hash.receive()); + if (!ok) + return false; + + HMAC_INFO hmac_info; + memset(&hmac_info, 0, sizeof(hmac_info)); + hmac_info.HashAlgid = hash_alg; + + ok = CryptSetHashParam(safe_hash, HP_HMAC_INFO, + reinterpret_cast(&hmac_info), 0); + if (!ok) + return false; + + hash->swap(safe_hash); + return true; +} + +// Computes a block of the derived key using the PBKDF2 function F for the +// specified |block_index| using the PRF |hash|, writing the output to +// |output_buf|. +// |output_buf| must have enough space to accomodate the output of the PRF +// specified by |hash|. +// Returns true if the block was successfully computed. +bool ComputePBKDF2Block(HCRYPTHASH hash, + DWORD hash_size, + const std::string& salt, + size_t iterations, + uint32 block_index, + BYTE* output_buf) { + // From RFC 2898: + // 3. The function F is defined as the exclusive-or sum of the first + // c iterates of the underlying pseudorandom function PRF applied to the + // password P and the concatenation of the salt S and the block index i: + // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c + // where + // U_1 = PRF(P, S || INT (i)) + // U_2 = PRF(P, U_1) + // ... + // U_c = PRF(P, U_{c-1}) + ScopedHCRYPTHASH safe_hash; + BOOL ok = CryptDuplicateHash(hash, NULL, 0, safe_hash.receive()); + if (!ok) + return false; + + // Iteration U_1: Compute PRF for S. + ok = CryptHashData(safe_hash, reinterpret_cast(salt.data()), + salt.size(), 0); + if (!ok) + return false; + + // Iteration U_1: and append (big-endian) INT (i). + uint32 big_endian_block_index = htonl(block_index); + ok = CryptHashData(safe_hash, + reinterpret_cast(&big_endian_block_index), + sizeof(big_endian_block_index), 0); + + std::vector hash_value(hash_size); + + DWORD size = hash_size; + ok = CryptGetHashParam(safe_hash, HP_HASHVAL, &hash_value[0], &size, 0); + if (!ok || size != hash_size) + return false; + + memcpy(output_buf, &hash_value[0], hash_size); + + // Iteration 2 - c: Compute U_{iteration} by applying the PRF to + // U_{iteration - 1}, then xor the resultant hash with |output|, which + // contains U_1 ^ U_2 ^ ... ^ U_{iteration - 1}. + for (size_t iteration = 2; iteration <= iterations; ++iteration) { + safe_hash.reset(); + ok = CryptDuplicateHash(hash, NULL, 0, safe_hash.receive()); + if (!ok) + return false; + + ok = CryptHashData(safe_hash, &hash_value[0], hash_size, 0); + if (!ok) + return false; + + size = hash_size; + ok = CryptGetHashParam(safe_hash, HP_HASHVAL, &hash_value[0], &size, 0); + if (!ok || size != hash_size) + return false; + + for (int i = 0; i < hash_size; ++i) + output_buf[i] ^= hash_value[i]; + } + + return true; +} + +} // namespace + +SymmetricKey::~SymmetricKey() { + // TODO(wtc): create a "secure" string type that zeroes itself in the + // destructor. + if (!raw_key_.empty()) + SecureZeroMemory(const_cast(raw_key_.data()), raw_key_.size()); +} + +// static +SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm, + size_t key_size_in_bits) { + DCHECK_GE(key_size_in_bits, 8); + + ScopedHCRYPTPROV provider; + ScopedHCRYPTKEY key; + + bool ok = false; + scoped_array raw_key; + + switch (algorithm) { + case AES: + ok = GenerateAESKey(key_size_in_bits, &provider, &key); + break; + case HMAC_SHA1: + ok = GenerateHMACKey(key_size_in_bits, CALG_SHA1, &provider, + &key, &raw_key); + break; + } + + if (!ok) { + NOTREACHED(); + return NULL; + } + + size_t key_size_in_bytes = key_size_in_bits / 8; + if (raw_key == NULL) + key_size_in_bytes = 0; + + SymmetricKey* result = new SymmetricKey(provider.release(), + key.release(), + raw_key.get(), + key_size_in_bytes); + if (raw_key != NULL) + SecureZeroMemory(raw_key.get(), key_size_in_bytes); + + return result; +} + +// static +SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm, + const std::string& password, + const std::string& salt, + size_t iterations, + size_t key_size_in_bits) { + // CryptoAPI lacks routines to perform PBKDF2 derivation as specified + // in RFC 2898, so it must be manually implemented. Only HMAC-SHA1 is + // supported as the PRF. + + // While not used until the end, sanity-check the input before proceeding + // with the expensive computation. + DWORD provider_type = 0; + ALG_ID alg = 0; + switch (algorithm) { + case AES: + provider_type = PROV_RSA_AES; + alg = GetAESAlgIDForKeySize(key_size_in_bits); + break; + case HMAC_SHA1: + provider_type = PROV_RSA_FULL; + alg = CALG_HMAC; + break; + default: + NOTREACHED(); + break; + } + if (provider_type == 0 || alg == 0) + return NULL; + + ScopedHCRYPTPROV provider; + BOOL ok = CryptAcquireContext(provider.receive(), NULL, NULL, provider_type, + CRYPT_VERIFYCONTEXT); + if (!ok) + return NULL; + + // Convert the user password into a key suitable to be fed into the PRF + // function. + ScopedHCRYPTKEY password_as_key; + BYTE* password_as_bytes = + const_cast(reinterpret_cast(password.data())); + if (!ImportRawKey(provider, CALG_HMAC, password_as_bytes, + password.size(), &password_as_key)) + return NULL; + + // Configure the PRF function. Only HMAC variants are supported, with the + // only hash function supported being SHA1. + // TODO(rsleevi): Support SHA-256 on XP SP3+. + ScopedHCRYPTHASH prf; + if (!CreateHMACHash(provider, password_as_key, CALG_SHA1, &prf)) + return NULL; + + DWORD hLen = 0; + DWORD param_size = sizeof(hLen); + ok = CryptGetHashParam(prf, HP_HASHSIZE, + reinterpret_cast(&hLen), ¶m_size, 0); + if (!ok || hLen == 0) + return NULL; + + // 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and stop. + size_t dkLen = key_size_in_bits / 8; + DCHECK_GT(dkLen, 0); + + if ((dkLen / hLen) > 0xFFFFFFFF) { + DLOG(ERROR) << "Derived key too long."; + return NULL; + } + + // 2. Let l be the number of hLen-octet blocks in the derived key, + // rounding up, and let r be the number of octets in the last + // block: + size_t L = (dkLen + hLen - 1) / hLen; + DCHECK_GT(L, 0); + + size_t total_generated_size = L * hLen; + std::vector generated_key(total_generated_size); + BYTE* block_offset = &generated_key[0]; + + // 3. For each block of the derived key apply the function F defined below + // to the password P, the salt S, the iteration count c, and the block + // index to compute the block: + // T_1 = F (P, S, c, 1) + // T_2 = F (P, S, c, 2) + // ... + // T_l = F (P, S, c, l) + // + // 4. Concatenate the blocks and extract the first dkLen octets to produce + // a derived key DK: + // DK = T_1 || T_2 || ... || T_l<0..r-1> + for (uint32 block_index = 1; block_index <= L; ++block_index) { + if (!ComputePBKDF2Block(prf, hLen, salt, iterations, block_index, + block_offset)) + return NULL; + block_offset += hLen; + } + + // Convert the derived key bytes into a key handle for the desired algorithm. + ScopedHCRYPTKEY key; + if (!ImportRawKey(provider, alg, &generated_key[0], dkLen, &key)) + return NULL; + + SymmetricKey* result = new SymmetricKey(provider.release(), key.release(), + &generated_key[0], dkLen); + + SecureZeroMemory(&generated_key[0], total_generated_size); + + return result; +} + +// static +SymmetricKey* SymmetricKey::Import(Algorithm algorithm, + const std::string& raw_key) { + DWORD provider_type = 0; + ALG_ID alg = 0; + switch (algorithm) { + case AES: + provider_type = PROV_RSA_AES; + alg = GetAESAlgIDForKeySize(raw_key.size() * 8); + break; + case HMAC_SHA1: + provider_type = PROV_RSA_FULL; + alg = CALG_HMAC; + break; + default: + NOTREACHED(); + break; + } + if (provider_type == 0 || alg == 0) + return NULL; + + ScopedHCRYPTPROV provider; + BOOL ok = CryptAcquireContext(provider.receive(), NULL, NULL, provider_type, + CRYPT_VERIFYCONTEXT); + if (!ok) + return NULL; + + ScopedHCRYPTKEY key; + if (!ImportRawKey(provider, alg, raw_key.data(), raw_key.size(), &key)) + return NULL; + + return new SymmetricKey(provider.release(), key.release(), + raw_key.data(), raw_key.size()); +} + +bool SymmetricKey::GetRawKey(std::string* raw_key) { + // Short circuit for when the key was supplied to the constructor. + if (!raw_key_.empty()) { + *raw_key = raw_key_; + return true; + } + + DWORD size = 0; + BOOL ok = CryptExportKey(key_, 0, PLAINTEXTKEYBLOB, 0, NULL, &size); + if (!ok) + return false; + + std::vector result(size); + + ok = CryptExportKey(key_, 0, PLAINTEXTKEYBLOB, 0, &result[0], &size); + if (!ok) + return false; + + PlaintextBlobHeader* header = + reinterpret_cast(&result[0]); + raw_key->assign(reinterpret_cast(&result[sizeof(*header)]), + header->cbKeySize); + + SecureZeroMemory(&result[0], size); + + return true; +} + +SymmetricKey::SymmetricKey(HCRYPTPROV provider, + HCRYPTKEY key, + const void* key_data, size_t key_size_in_bytes) + : provider_(provider), key_(key) { + if (key_data) { + raw_key_.assign(reinterpret_cast(key_data), + key_size_in_bytes); + } +} + +} // namespace crypto diff --git a/crypto/third_party/nss/LICENSE b/crypto/third_party/nss/LICENSE new file mode 100644 index 0000000..0367164 --- /dev/null +++ b/crypto/third_party/nss/LICENSE @@ -0,0 +1,35 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ diff --git a/crypto/third_party/nss/README.chromium b/crypto/third_party/nss/README.chromium new file mode 100644 index 0000000..9b466c8 --- /dev/null +++ b/crypto/third_party/nss/README.chromium @@ -0,0 +1,13 @@ +Name: Network Security Services (NSS) +URL: http://www.mozilla.org/projects/security/pki/nss/ + +We extracted the SHA-256 source files, eliminated unneeded dependencies, +deleted or commented out unused code, and tweaked them for Chrome's source +tree. sha512.c is renamed sha512.cc so that it can include Chrome's C++ +header "base/basictypes.h". We define NOUNROLL256 to reduce the object code +size. + +In blapi.h and sha512.cc, replaced uint32 by unsigned int so that they can +be compiled with -DNO_NSPR_10_SUPPORT. NO_NSPR_10_SUPPORT turns off the +definition of the NSPR 1.0 types int8 - int64 and uint8 - uint64 to avoid +conflict with the same-named types defined in "base/basictypes.h". diff --git a/crypto/third_party/nss/blapi.h b/crypto/third_party/nss/blapi.h new file mode 100644 index 0000000..3b0c60a --- /dev/null +++ b/crypto/third_party/nss/blapi.h @@ -0,0 +1,101 @@ +/* + * crypto.h - public data structures and prototypes for the crypto library + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dr Vipul Gupta , Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* $Id: blapi.h,v 1.27 2007/11/09 18:49:32 wtc%google.com Exp $ */ + +#ifndef _BLAPI_H_ +#define _BLAPI_H_ + +#include "crypto/third_party/nss/blapit.h" + +/******************************************/ + +extern SHA256Context *SHA256_NewContext(void); +extern void SHA256_DestroyContext(SHA256Context *cx, PRBool freeit); +extern void SHA256_Begin(SHA256Context *cx); +extern void SHA256_Update(SHA256Context *cx, const unsigned char *input, + unsigned int inputLen); +extern void SHA256_End(SHA256Context *cx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen); +extern SECStatus SHA256_HashBuf(unsigned char *dest, const unsigned char *src, + unsigned int src_length); +extern SECStatus SHA256_Hash(unsigned char *dest, const char *src); +extern void SHA256_TraceState(SHA256Context *cx); +extern unsigned int SHA256_FlattenSize(SHA256Context *cx); +extern SECStatus SHA256_Flatten(SHA256Context *cx,unsigned char *space); +extern SHA256Context * SHA256_Resurrect(unsigned char *space, void *arg); +extern void SHA256_Clone(SHA256Context *dest, SHA256Context *src); + +/******************************************/ + +extern SHA512Context *SHA512_NewContext(void); +extern void SHA512_DestroyContext(SHA512Context *cx, PRBool freeit); +extern void SHA512_Begin(SHA512Context *cx); +extern void SHA512_Update(SHA512Context *cx, const unsigned char *input, + unsigned int inputLen); +extern void SHA512_End(SHA512Context *cx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen); +extern SECStatus SHA512_HashBuf(unsigned char *dest, const unsigned char *src, + unsigned int src_length); +extern SECStatus SHA512_Hash(unsigned char *dest, const char *src); +extern void SHA512_TraceState(SHA512Context *cx); +extern unsigned int SHA512_FlattenSize(SHA512Context *cx); +extern SECStatus SHA512_Flatten(SHA512Context *cx,unsigned char *space); +extern SHA512Context * SHA512_Resurrect(unsigned char *space, void *arg); +extern void SHA512_Clone(SHA512Context *dest, SHA512Context *src); + +/******************************************/ + +extern SHA384Context *SHA384_NewContext(void); +extern void SHA384_DestroyContext(SHA384Context *cx, PRBool freeit); +extern void SHA384_Begin(SHA384Context *cx); +extern void SHA384_Update(SHA384Context *cx, const unsigned char *input, + unsigned int inputLen); +extern void SHA384_End(SHA384Context *cx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen); +extern SECStatus SHA384_HashBuf(unsigned char *dest, const unsigned char *src, + unsigned int src_length); +extern SECStatus SHA384_Hash(unsigned char *dest, const char *src); +extern void SHA384_TraceState(SHA384Context *cx); +extern unsigned int SHA384_FlattenSize(SHA384Context *cx); +extern SECStatus SHA384_Flatten(SHA384Context *cx,unsigned char *space); +extern SHA384Context * SHA384_Resurrect(unsigned char *space, void *arg); +extern void SHA384_Clone(SHA384Context *dest, SHA384Context *src); + +#endif /* _BLAPI_H_ */ diff --git a/crypto/third_party/nss/blapit.h b/crypto/third_party/nss/blapit.h new file mode 100644 index 0000000..e16a084 --- /dev/null +++ b/crypto/third_party/nss/blapit.h @@ -0,0 +1,91 @@ +/* + * blapit.h - public data structures for the crypto library + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dr Vipul Gupta and + * Douglas Stebila , Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* $Id: blapit.h,v 1.20 2007/02/28 19:47:37 rrelyea%redhat.com Exp $ */ + +#ifndef _BLAPIT_H_ +#define _BLAPIT_H_ + +#include "base/third_party/nspr/prtypes.h" + +/* +** A status code. Status's are used by procedures that return status +** values. Again the motivation is so that a compiler can generate +** warnings when return values are wrong. Correct testing of status codes: +** +** SECStatus rv; +** rv = some_function (some_argument); +** if (rv != SECSuccess) +** do_an_error_thing(); +** +*/ +typedef enum _SECStatus { + SECWouldBlock = -2, + SECFailure = -1, + SECSuccess = 0 +} SECStatus; + +#define SHA256_LENGTH 32 /* bytes */ +#define SHA384_LENGTH 48 /* bytes */ +#define SHA512_LENGTH 64 /* bytes */ +#define HASH_LENGTH_MAX SHA512_LENGTH + +/* + * Input block size for each hash algorithm. + */ + +#define SHA256_BLOCK_LENGTH 64 /* bytes */ +#define SHA384_BLOCK_LENGTH 128 /* bytes */ +#define SHA512_BLOCK_LENGTH 128 /* bytes */ +#define HASH_BLOCK_LENGTH_MAX SHA512_BLOCK_LENGTH + +/*************************************************************************** +** Opaque objects +*/ + +struct SHA256ContextStr ; +struct SHA512ContextStr ; + +typedef struct SHA256ContextStr SHA256Context; +typedef struct SHA512ContextStr SHA512Context; +/* SHA384Context is really a SHA512ContextStr. This is not a mistake. */ +typedef struct SHA512ContextStr SHA384Context; + +#endif /* _BLAPIT_H_ */ diff --git a/crypto/third_party/nss/sha256.h b/crypto/third_party/nss/sha256.h new file mode 100644 index 0000000..e641b49 --- /dev/null +++ b/crypto/third_party/nss/sha256.h @@ -0,0 +1,51 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _SHA_256_H_ +#define _SHA_256_H_ + +#include "base/third_party/nspr/prtypes.h" + +struct SHA256ContextStr { + union { + PRUint32 w[64]; /* message schedule, input buffer, plus 48 words */ + PRUint8 b[256]; + } u; + PRUint32 h[8]; /* 8 state variables */ + PRUint32 sizeHi,sizeLo; /* 64-bit count of hashed bytes. */ +}; + +#endif /* _SHA_256_H_ */ diff --git a/crypto/third_party/nss/sha512.cc b/crypto/third_party/nss/sha512.cc new file mode 100644 index 0000000..6c04674 --- /dev/null +++ b/crypto/third_party/nss/sha512.cc @@ -0,0 +1,1391 @@ +/* + * sha512.c - implementation of SHA256, SHA384 and SHA512 + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* $Id: sha512.c,v 1.9 2006/10/13 16:54:04 wtchang%redhat.com Exp $ */ + +// Prevent manual unrolling in the sha256 code, which reduces the binary code +// size from ~10k to ~1k. The performance should be reasonable for our use. +#define NOUNROLL256 1 + +#include "base/third_party/nspr/prtypes.h" /* for PRUintXX */ +#if defined(_X86_) || defined(SHA_NO_LONG_LONG) +#define NOUNROLL512 1 +#undef HAVE_LONG_LONG +#endif +#include "crypto/third_party/nss/blapi.h" +#include "crypto/third_party/nss/sha256.h" /* for struct SHA256ContextStr */ + +#include +#include +#define PORT_New(type) static_cast(malloc(sizeof(type))) +#define PORT_ZFree(ptr, len) do { memset(ptr, 0, len); free(ptr); } while (0) +#define PORT_Strlen(s) static_cast(strlen(s)) +#define PORT_Memcpy memcpy + +/* ============= Common constants and defines ======================= */ + +#define W ctx->u.w +#define B ctx->u.b +#define H ctx->h + +#define SHR(x,n) (x >> n) +#define SHL(x,n) (x << n) +#define Ch(x,y,z) ((x & y) ^ (~x & z)) +#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z)) + +/* Padding used with all flavors of SHA */ +static const PRUint8 pad[240] = { +0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + /* compiler will fill the rest in with zeros */ +}; + +/* ============= SHA256 implemenmtation ================================== */ + +/* SHA-256 constants, K256. */ +static const PRUint32 K256[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +/* SHA-256 initial hash values */ +static const PRUint32 H256[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 +}; + +#if defined(_MSC_VER) && defined(_X86_) +#ifndef FORCEINLINE +#if (_MSC_VER >= 1200) +#define FORCEINLINE __forceinline +#else +#define FORCEINLINE __inline +#endif +#endif +#define FASTCALL __fastcall + +static FORCEINLINE PRUint32 FASTCALL +swap4b(PRUint32 dwd) +{ + __asm { + mov eax,dwd + bswap eax + } +} + +#define SHA_HTONL(x) swap4b(x) +#define BYTESWAP4(x) x = SHA_HTONL(x) + +#elif defined(LINUX) && defined(_X86_) +#undef __OPTIMIZE__ +#define __OPTIMIZE__ 1 +#undef __pentium__ +#define __pentium__ 1 +#include +#define SHA_HTONL(x) bswap_32(x) +#define BYTESWAP4(x) x = SHA_HTONL(x) + +#else /* neither windows nor Linux PC */ +#define SWAP4MASK 0x00FF00FF +#define SHA_HTONL(x) (t1 = (x), t1 = (t1 << 16) | (t1 >> 16), \ + ((t1 & SWAP4MASK) << 8) | ((t1 >> 8) & SWAP4MASK)) +#define BYTESWAP4(x) x = SHA_HTONL(x) +#endif + +#if defined(_MSC_VER) && defined(_X86_) +#pragma intrinsic (_lrotr, _lrotl) +#define ROTR32(x,n) _lrotr(x,n) +#define ROTL32(x,n) _lrotl(x,n) +#else +#define ROTR32(x,n) ((x >> n) | (x << ((8 * sizeof x) - n))) +#define ROTL32(x,n) ((x << n) | (x >> ((8 * sizeof x) - n))) +#endif + +/* Capitol Sigma and lower case sigma functions */ +#define S0(x) (ROTR32(x, 2) ^ ROTR32(x,13) ^ ROTR32(x,22)) +#define S1(x) (ROTR32(x, 6) ^ ROTR32(x,11) ^ ROTR32(x,25)) +#define s0(x) (t1 = x, ROTR32(t1, 7) ^ ROTR32(t1,18) ^ SHR(t1, 3)) +#define s1(x) (t2 = x, ROTR32(t2,17) ^ ROTR32(t2,19) ^ SHR(t2,10)) + +SHA256Context * +SHA256_NewContext(void) +{ + SHA256Context *ctx = PORT_New(SHA256Context); + return ctx; +} + +void +SHA256_DestroyContext(SHA256Context *ctx, PRBool freeit) +{ + if (freeit) { + PORT_ZFree(ctx, sizeof *ctx); + } +} + +void +SHA256_Begin(SHA256Context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + memcpy(H, H256, sizeof H256); +} + +static void +SHA256_Compress(SHA256Context *ctx) +{ + { + register PRUint32 t1, t2; + +#if defined(IS_LITTLE_ENDIAN) + BYTESWAP4(W[0]); + BYTESWAP4(W[1]); + BYTESWAP4(W[2]); + BYTESWAP4(W[3]); + BYTESWAP4(W[4]); + BYTESWAP4(W[5]); + BYTESWAP4(W[6]); + BYTESWAP4(W[7]); + BYTESWAP4(W[8]); + BYTESWAP4(W[9]); + BYTESWAP4(W[10]); + BYTESWAP4(W[11]); + BYTESWAP4(W[12]); + BYTESWAP4(W[13]); + BYTESWAP4(W[14]); + BYTESWAP4(W[15]); +#endif + +#define INITW(t) W[t] = (s1(W[t-2]) + W[t-7] + s0(W[t-15]) + W[t-16]) + + /* prepare the "message schedule" */ +#ifdef NOUNROLL256 + { + int t; + for (t = 16; t < 64; ++t) { + INITW(t); + } + } +#else + INITW(16); + INITW(17); + INITW(18); + INITW(19); + + INITW(20); + INITW(21); + INITW(22); + INITW(23); + INITW(24); + INITW(25); + INITW(26); + INITW(27); + INITW(28); + INITW(29); + + INITW(30); + INITW(31); + INITW(32); + INITW(33); + INITW(34); + INITW(35); + INITW(36); + INITW(37); + INITW(38); + INITW(39); + + INITW(40); + INITW(41); + INITW(42); + INITW(43); + INITW(44); + INITW(45); + INITW(46); + INITW(47); + INITW(48); + INITW(49); + + INITW(50); + INITW(51); + INITW(52); + INITW(53); + INITW(54); + INITW(55); + INITW(56); + INITW(57); + INITW(58); + INITW(59); + + INITW(60); + INITW(61); + INITW(62); + INITW(63); + +#endif +#undef INITW + } + { + PRUint32 a, b, c, d, e, f, g, h; + + a = H[0]; + b = H[1]; + c = H[2]; + d = H[3]; + e = H[4]; + f = H[5]; + g = H[6]; + h = H[7]; + +#define ROUND(n,a,b,c,d,e,f,g,h) \ + h += S1(e) + Ch(e,f,g) + K256[n] + W[n]; \ + d += h; \ + h += S0(a) + Maj(a,b,c); + +#ifdef NOUNROLL256 + { + int t; + for (t = 0; t < 64; t+= 8) { + ROUND(t+0,a,b,c,d,e,f,g,h) + ROUND(t+1,h,a,b,c,d,e,f,g) + ROUND(t+2,g,h,a,b,c,d,e,f) + ROUND(t+3,f,g,h,a,b,c,d,e) + ROUND(t+4,e,f,g,h,a,b,c,d) + ROUND(t+5,d,e,f,g,h,a,b,c) + ROUND(t+6,c,d,e,f,g,h,a,b) + ROUND(t+7,b,c,d,e,f,g,h,a) + } + } +#else + ROUND( 0,a,b,c,d,e,f,g,h) + ROUND( 1,h,a,b,c,d,e,f,g) + ROUND( 2,g,h,a,b,c,d,e,f) + ROUND( 3,f,g,h,a,b,c,d,e) + ROUND( 4,e,f,g,h,a,b,c,d) + ROUND( 5,d,e,f,g,h,a,b,c) + ROUND( 6,c,d,e,f,g,h,a,b) + ROUND( 7,b,c,d,e,f,g,h,a) + + ROUND( 8,a,b,c,d,e,f,g,h) + ROUND( 9,h,a,b,c,d,e,f,g) + ROUND(10,g,h,a,b,c,d,e,f) + ROUND(11,f,g,h,a,b,c,d,e) + ROUND(12,e,f,g,h,a,b,c,d) + ROUND(13,d,e,f,g,h,a,b,c) + ROUND(14,c,d,e,f,g,h,a,b) + ROUND(15,b,c,d,e,f,g,h,a) + + ROUND(16,a,b,c,d,e,f,g,h) + ROUND(17,h,a,b,c,d,e,f,g) + ROUND(18,g,h,a,b,c,d,e,f) + ROUND(19,f,g,h,a,b,c,d,e) + ROUND(20,e,f,g,h,a,b,c,d) + ROUND(21,d,e,f,g,h,a,b,c) + ROUND(22,c,d,e,f,g,h,a,b) + ROUND(23,b,c,d,e,f,g,h,a) + + ROUND(24,a,b,c,d,e,f,g,h) + ROUND(25,h,a,b,c,d,e,f,g) + ROUND(26,g,h,a,b,c,d,e,f) + ROUND(27,f,g,h,a,b,c,d,e) + ROUND(28,e,f,g,h,a,b,c,d) + ROUND(29,d,e,f,g,h,a,b,c) + ROUND(30,c,d,e,f,g,h,a,b) + ROUND(31,b,c,d,e,f,g,h,a) + + ROUND(32,a,b,c,d,e,f,g,h) + ROUND(33,h,a,b,c,d,e,f,g) + ROUND(34,g,h,a,b,c,d,e,f) + ROUND(35,f,g,h,a,b,c,d,e) + ROUND(36,e,f,g,h,a,b,c,d) + ROUND(37,d,e,f,g,h,a,b,c) + ROUND(38,c,d,e,f,g,h,a,b) + ROUND(39,b,c,d,e,f,g,h,a) + + ROUND(40,a,b,c,d,e,f,g,h) + ROUND(41,h,a,b,c,d,e,f,g) + ROUND(42,g,h,a,b,c,d,e,f) + ROUND(43,f,g,h,a,b,c,d,e) + ROUND(44,e,f,g,h,a,b,c,d) + ROUND(45,d,e,f,g,h,a,b,c) + ROUND(46,c,d,e,f,g,h,a,b) + ROUND(47,b,c,d,e,f,g,h,a) + + ROUND(48,a,b,c,d,e,f,g,h) + ROUND(49,h,a,b,c,d,e,f,g) + ROUND(50,g,h,a,b,c,d,e,f) + ROUND(51,f,g,h,a,b,c,d,e) + ROUND(52,e,f,g,h,a,b,c,d) + ROUND(53,d,e,f,g,h,a,b,c) + ROUND(54,c,d,e,f,g,h,a,b) + ROUND(55,b,c,d,e,f,g,h,a) + + ROUND(56,a,b,c,d,e,f,g,h) + ROUND(57,h,a,b,c,d,e,f,g) + ROUND(58,g,h,a,b,c,d,e,f) + ROUND(59,f,g,h,a,b,c,d,e) + ROUND(60,e,f,g,h,a,b,c,d) + ROUND(61,d,e,f,g,h,a,b,c) + ROUND(62,c,d,e,f,g,h,a,b) + ROUND(63,b,c,d,e,f,g,h,a) +#endif + + H[0] += a; + H[1] += b; + H[2] += c; + H[3] += d; + H[4] += e; + H[5] += f; + H[6] += g; + H[7] += h; + } +#undef ROUND +} + +#undef s0 +#undef s1 +#undef S0 +#undef S1 + +void +SHA256_Update(SHA256Context *ctx, const unsigned char *input, + unsigned int inputLen) +{ + unsigned int inBuf = ctx->sizeLo & 0x3f; + if (!inputLen) + return; + + /* Add inputLen into the count of bytes processed, before processing */ + if ((ctx->sizeLo += inputLen) < inputLen) + ctx->sizeHi++; + + /* if data already in buffer, attemp to fill rest of buffer */ + if (inBuf) { + unsigned int todo = SHA256_BLOCK_LENGTH - inBuf; + if (inputLen < todo) + todo = inputLen; + memcpy(B + inBuf, input, todo); + input += todo; + inputLen -= todo; + if (inBuf + todo == SHA256_BLOCK_LENGTH) + SHA256_Compress(ctx); + } + + /* if enough data to fill one or more whole buffers, process them. */ + while (inputLen >= SHA256_BLOCK_LENGTH) { + memcpy(B, input, SHA256_BLOCK_LENGTH); + input += SHA256_BLOCK_LENGTH; + inputLen -= SHA256_BLOCK_LENGTH; + SHA256_Compress(ctx); + } + /* if data left over, fill it into buffer */ + if (inputLen) + memcpy(B, input, inputLen); +} + +void +SHA256_End(SHA256Context *ctx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen) +{ + unsigned int inBuf = ctx->sizeLo & 0x3f; + unsigned int padLen = (inBuf < 56) ? (56 - inBuf) : (56 + 64 - inBuf); + PRUint32 hi, lo; +#ifdef SWAP4MASK + PRUint32 t1; +#endif + + hi = (ctx->sizeHi << 3) | (ctx->sizeLo >> 29); + lo = (ctx->sizeLo << 3); + + SHA256_Update(ctx, pad, padLen); + +#if defined(IS_LITTLE_ENDIAN) + W[14] = SHA_HTONL(hi); + W[15] = SHA_HTONL(lo); +#else + W[14] = hi; + W[15] = lo; +#endif + SHA256_Compress(ctx); + + /* now output the answer */ +#if defined(IS_LITTLE_ENDIAN) + BYTESWAP4(H[0]); + BYTESWAP4(H[1]); + BYTESWAP4(H[2]); + BYTESWAP4(H[3]); + BYTESWAP4(H[4]); + BYTESWAP4(H[5]); + BYTESWAP4(H[6]); + BYTESWAP4(H[7]); +#endif + padLen = PR_MIN(SHA256_LENGTH, maxDigestLen); + memcpy(digest, H, padLen); + if (digestLen) + *digestLen = padLen; +} + +/* Comment out unused code, mostly the SHA384 and SHA512 implementations. */ +#if 0 +SECStatus +SHA256_HashBuf(unsigned char *dest, const unsigned char *src, + unsigned int src_length) +{ + SHA256Context ctx; + unsigned int outLen; + + SHA256_Begin(&ctx); + SHA256_Update(&ctx, src, src_length); + SHA256_End(&ctx, dest, &outLen, SHA256_LENGTH); + + return SECSuccess; +} + + +SECStatus +SHA256_Hash(unsigned char *dest, const char *src) +{ + return SHA256_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src)); +} + + +void SHA256_TraceState(SHA256Context *ctx) { } + +unsigned int +SHA256_FlattenSize(SHA256Context *ctx) +{ + return sizeof *ctx; +} + +SECStatus +SHA256_Flatten(SHA256Context *ctx,unsigned char *space) +{ + PORT_Memcpy(space, ctx, sizeof *ctx); + return SECSuccess; +} + +SHA256Context * +SHA256_Resurrect(unsigned char *space, void *arg) +{ + SHA256Context *ctx = SHA256_NewContext(); + if (ctx) + PORT_Memcpy(ctx, space, sizeof *ctx); + return ctx; +} + +void SHA256_Clone(SHA256Context *dest, SHA256Context *src) +{ + memcpy(dest, src, sizeof *dest); +} + + +/* ======= SHA512 and SHA384 common constants and defines ================= */ + +/* common #defines for SHA512 and SHA384 */ +#if defined(HAVE_LONG_LONG) +#define ROTR64(x,n) ((x >> n) | (x << (64 - n))) +#define ROTL64(x,n) ((x << n) | (x >> (64 - n))) + +#define S0(x) (ROTR64(x,28) ^ ROTR64(x,34) ^ ROTR64(x,39)) +#define S1(x) (ROTR64(x,14) ^ ROTR64(x,18) ^ ROTR64(x,41)) +#define s0(x) (t1 = x, ROTR64(t1, 1) ^ ROTR64(t1, 8) ^ SHR(t1,7)) +#define s1(x) (t2 = x, ROTR64(t2,19) ^ ROTR64(t2,61) ^ SHR(t2,6)) + +#if PR_BYTES_PER_LONG == 8 +#define ULLC(hi,lo) 0x ## hi ## lo ## UL +#elif defined(_MSC_VER) +#define ULLC(hi,lo) 0x ## hi ## lo ## ui64 +#else +#define ULLC(hi,lo) 0x ## hi ## lo ## ULL +#endif + +#define SHA_MASK16 ULLC(0000FFFF,0000FFFF) +#define SHA_MASK8 ULLC(00FF00FF,00FF00FF) +#define SHA_HTONLL(x) (t1 = x, \ + t1 = ((t1 & SHA_MASK8 ) << 8) | ((t1 >> 8) & SHA_MASK8 ), \ + t1 = ((t1 & SHA_MASK16) << 16) | ((t1 >> 16) & SHA_MASK16), \ + (t1 >> 32) | (t1 << 32)) +#define BYTESWAP8(x) x = SHA_HTONLL(x) + +#else /* no long long */ + +#if defined(IS_LITTLE_ENDIAN) +#define ULLC(hi,lo) { 0x ## lo ## U, 0x ## hi ## U } +#else +#define ULLC(hi,lo) { 0x ## hi ## U, 0x ## lo ## U } +#endif + +#define SHA_HTONLL(x) ( BYTESWAP4(x.lo), BYTESWAP4(x.hi), \ + x.hi ^= x.lo ^= x.hi ^= x.lo, x) +#define BYTESWAP8(x) do { PRUint32 tmp; BYTESWAP4(x.lo); BYTESWAP4(x.hi); \ + tmp = x.lo; x.lo = x.hi; x.hi = tmp; } while (0) +#endif + +/* SHA-384 and SHA-512 constants, K512. */ +static const PRUint64 K512[80] = { +#if PR_BYTES_PER_LONG == 8 + 0x428a2f98d728ae22UL , 0x7137449123ef65cdUL , + 0xb5c0fbcfec4d3b2fUL , 0xe9b5dba58189dbbcUL , + 0x3956c25bf348b538UL , 0x59f111f1b605d019UL , + 0x923f82a4af194f9bUL , 0xab1c5ed5da6d8118UL , + 0xd807aa98a3030242UL , 0x12835b0145706fbeUL , + 0x243185be4ee4b28cUL , 0x550c7dc3d5ffb4e2UL , + 0x72be5d74f27b896fUL , 0x80deb1fe3b1696b1UL , + 0x9bdc06a725c71235UL , 0xc19bf174cf692694UL , + 0xe49b69c19ef14ad2UL , 0xefbe4786384f25e3UL , + 0x0fc19dc68b8cd5b5UL , 0x240ca1cc77ac9c65UL , + 0x2de92c6f592b0275UL , 0x4a7484aa6ea6e483UL , + 0x5cb0a9dcbd41fbd4UL , 0x76f988da831153b5UL , + 0x983e5152ee66dfabUL , 0xa831c66d2db43210UL , + 0xb00327c898fb213fUL , 0xbf597fc7beef0ee4UL , + 0xc6e00bf33da88fc2UL , 0xd5a79147930aa725UL , + 0x06ca6351e003826fUL , 0x142929670a0e6e70UL , + 0x27b70a8546d22ffcUL , 0x2e1b21385c26c926UL , + 0x4d2c6dfc5ac42aedUL , 0x53380d139d95b3dfUL , + 0x650a73548baf63deUL , 0x766a0abb3c77b2a8UL , + 0x81c2c92e47edaee6UL , 0x92722c851482353bUL , + 0xa2bfe8a14cf10364UL , 0xa81a664bbc423001UL , + 0xc24b8b70d0f89791UL , 0xc76c51a30654be30UL , + 0xd192e819d6ef5218UL , 0xd69906245565a910UL , + 0xf40e35855771202aUL , 0x106aa07032bbd1b8UL , + 0x19a4c116b8d2d0c8UL , 0x1e376c085141ab53UL , + 0x2748774cdf8eeb99UL , 0x34b0bcb5e19b48a8UL , + 0x391c0cb3c5c95a63UL , 0x4ed8aa4ae3418acbUL , + 0x5b9cca4f7763e373UL , 0x682e6ff3d6b2b8a3UL , + 0x748f82ee5defb2fcUL , 0x78a5636f43172f60UL , + 0x84c87814a1f0ab72UL , 0x8cc702081a6439ecUL , + 0x90befffa23631e28UL , 0xa4506cebde82bde9UL , + 0xbef9a3f7b2c67915UL , 0xc67178f2e372532bUL , + 0xca273eceea26619cUL , 0xd186b8c721c0c207UL , + 0xeada7dd6cde0eb1eUL , 0xf57d4f7fee6ed178UL , + 0x06f067aa72176fbaUL , 0x0a637dc5a2c898a6UL , + 0x113f9804bef90daeUL , 0x1b710b35131c471bUL , + 0x28db77f523047d84UL , 0x32caab7b40c72493UL , + 0x3c9ebe0a15c9bebcUL , 0x431d67c49c100d4cUL , + 0x4cc5d4becb3e42b6UL , 0x597f299cfc657e2aUL , + 0x5fcb6fab3ad6faecUL , 0x6c44198c4a475817UL +#else + ULLC(428a2f98,d728ae22), ULLC(71374491,23ef65cd), + ULLC(b5c0fbcf,ec4d3b2f), ULLC(e9b5dba5,8189dbbc), + ULLC(3956c25b,f348b538), ULLC(59f111f1,b605d019), + ULLC(923f82a4,af194f9b), ULLC(ab1c5ed5,da6d8118), + ULLC(d807aa98,a3030242), ULLC(12835b01,45706fbe), + ULLC(243185be,4ee4b28c), ULLC(550c7dc3,d5ffb4e2), + ULLC(72be5d74,f27b896f), ULLC(80deb1fe,3b1696b1), + ULLC(9bdc06a7,25c71235), ULLC(c19bf174,cf692694), + ULLC(e49b69c1,9ef14ad2), ULLC(efbe4786,384f25e3), + ULLC(0fc19dc6,8b8cd5b5), ULLC(240ca1cc,77ac9c65), + ULLC(2de92c6f,592b0275), ULLC(4a7484aa,6ea6e483), + ULLC(5cb0a9dc,bd41fbd4), ULLC(76f988da,831153b5), + ULLC(983e5152,ee66dfab), ULLC(a831c66d,2db43210), + ULLC(b00327c8,98fb213f), ULLC(bf597fc7,beef0ee4), + ULLC(c6e00bf3,3da88fc2), ULLC(d5a79147,930aa725), + ULLC(06ca6351,e003826f), ULLC(14292967,0a0e6e70), + ULLC(27b70a85,46d22ffc), ULLC(2e1b2138,5c26c926), + ULLC(4d2c6dfc,5ac42aed), ULLC(53380d13,9d95b3df), + ULLC(650a7354,8baf63de), ULLC(766a0abb,3c77b2a8), + ULLC(81c2c92e,47edaee6), ULLC(92722c85,1482353b), + ULLC(a2bfe8a1,4cf10364), ULLC(a81a664b,bc423001), + ULLC(c24b8b70,d0f89791), ULLC(c76c51a3,0654be30), + ULLC(d192e819,d6ef5218), ULLC(d6990624,5565a910), + ULLC(f40e3585,5771202a), ULLC(106aa070,32bbd1b8), + ULLC(19a4c116,b8d2d0c8), ULLC(1e376c08,5141ab53), + ULLC(2748774c,df8eeb99), ULLC(34b0bcb5,e19b48a8), + ULLC(391c0cb3,c5c95a63), ULLC(4ed8aa4a,e3418acb), + ULLC(5b9cca4f,7763e373), ULLC(682e6ff3,d6b2b8a3), + ULLC(748f82ee,5defb2fc), ULLC(78a5636f,43172f60), + ULLC(84c87814,a1f0ab72), ULLC(8cc70208,1a6439ec), + ULLC(90befffa,23631e28), ULLC(a4506ceb,de82bde9), + ULLC(bef9a3f7,b2c67915), ULLC(c67178f2,e372532b), + ULLC(ca273ece,ea26619c), ULLC(d186b8c7,21c0c207), + ULLC(eada7dd6,cde0eb1e), ULLC(f57d4f7f,ee6ed178), + ULLC(06f067aa,72176fba), ULLC(0a637dc5,a2c898a6), + ULLC(113f9804,bef90dae), ULLC(1b710b35,131c471b), + ULLC(28db77f5,23047d84), ULLC(32caab7b,40c72493), + ULLC(3c9ebe0a,15c9bebc), ULLC(431d67c4,9c100d4c), + ULLC(4cc5d4be,cb3e42b6), ULLC(597f299c,fc657e2a), + ULLC(5fcb6fab,3ad6faec), ULLC(6c44198c,4a475817) +#endif +}; + +struct SHA512ContextStr { + union { + PRUint64 w[80]; /* message schedule, input buffer, plus 64 words */ + PRUint32 l[160]; + PRUint8 b[640]; + } u; + PRUint64 h[8]; /* 8 state variables */ + PRUint64 sizeLo; /* 64-bit count of hashed bytes. */ +}; + +/* =========== SHA512 implementation ===================================== */ + +/* SHA-512 initial hash values */ +static const PRUint64 H512[8] = { +#if PR_BYTES_PER_LONG == 8 + 0x6a09e667f3bcc908UL , 0xbb67ae8584caa73bUL , + 0x3c6ef372fe94f82bUL , 0xa54ff53a5f1d36f1UL , + 0x510e527fade682d1UL , 0x9b05688c2b3e6c1fUL , + 0x1f83d9abfb41bd6bUL , 0x5be0cd19137e2179UL +#else + ULLC(6a09e667,f3bcc908), ULLC(bb67ae85,84caa73b), + ULLC(3c6ef372,fe94f82b), ULLC(a54ff53a,5f1d36f1), + ULLC(510e527f,ade682d1), ULLC(9b05688c,2b3e6c1f), + ULLC(1f83d9ab,fb41bd6b), ULLC(5be0cd19,137e2179) +#endif +}; + + +SHA512Context * +SHA512_NewContext(void) +{ + SHA512Context *ctx = PORT_New(SHA512Context); + return ctx; +} + +void +SHA512_DestroyContext(SHA512Context *ctx, PRBool freeit) +{ + if (freeit) { + PORT_ZFree(ctx, sizeof *ctx); + } +} + +void +SHA512_Begin(SHA512Context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + memcpy(H, H512, sizeof H512); +} + +#if defined(SHA512_TRACE) +#if defined(HAVE_LONG_LONG) +#define DUMP(n,a,d,e,h) printf(" t = %2d, %s = %016lx, %s = %016lx\n", \ + n, #e, d, #a, h); +#else +#define DUMP(n,a,d,e,h) printf(" t = %2d, %s = %08x%08x, %s = %08x%08x\n", \ + n, #e, d.hi, d.lo, #a, h.hi, h.lo); +#endif +#else +#define DUMP(n,a,d,e,h) +#endif + +#if defined(HAVE_LONG_LONG) + +#define ADDTO(x,y) y += x + +#define INITW(t) W[t] = (s1(W[t-2]) + W[t-7] + s0(W[t-15]) + W[t-16]) + +#define ROUND(n,a,b,c,d,e,f,g,h) \ + h += S1(e) + Ch(e,f,g) + K512[n] + W[n]; \ + d += h; \ + h += S0(a) + Maj(a,b,c); \ + DUMP(n,a,d,e,h) + +#else /* use only 32-bit variables, and don't unroll loops */ + +#undef NOUNROLL512 +#define NOUNROLL512 1 + +#define ADDTO(x,y) y.lo += x.lo; y.hi += x.hi + (x.lo > y.lo) + +#define ROTR64a(x,n,lo,hi) (x.lo >> n | x.hi << (32-n)) +#define ROTR64A(x,n,lo,hi) (x.lo << (64-n) | x.hi >> (n-32)) +#define SHR64a(x,n,lo,hi) (x.lo >> n | x.hi << (32-n)) + +/* Capitol Sigma and lower case sigma functions */ +#define s0lo(x) (ROTR64a(x,1,lo,hi) ^ ROTR64a(x,8,lo,hi) ^ SHR64a(x,7,lo,hi)) +#define s0hi(x) (ROTR64a(x,1,hi,lo) ^ ROTR64a(x,8,hi,lo) ^ (x.hi >> 7)) + +#define s1lo(x) (ROTR64a(x,19,lo,hi) ^ ROTR64A(x,61,lo,hi) ^ SHR64a(x,6,lo,hi)) +#define s1hi(x) (ROTR64a(x,19,hi,lo) ^ ROTR64A(x,61,hi,lo) ^ (x.hi >> 6)) + +#define S0lo(x)(ROTR64a(x,28,lo,hi) ^ ROTR64A(x,34,lo,hi) ^ ROTR64A(x,39,lo,hi)) +#define S0hi(x)(ROTR64a(x,28,hi,lo) ^ ROTR64A(x,34,hi,lo) ^ ROTR64A(x,39,hi,lo)) + +#define S1lo(x)(ROTR64a(x,14,lo,hi) ^ ROTR64a(x,18,lo,hi) ^ ROTR64A(x,41,lo,hi)) +#define S1hi(x)(ROTR64a(x,14,hi,lo) ^ ROTR64a(x,18,hi,lo) ^ ROTR64A(x,41,hi,lo)) + +/* 32-bit versions of Ch and Maj */ +#define Chxx(x,y,z,lo) ((x.lo & y.lo) ^ (~x.lo & z.lo)) +#define Majx(x,y,z,lo) ((x.lo & y.lo) ^ (x.lo & z.lo) ^ (y.lo & z.lo)) + +#define INITW(t) \ + do { \ + PRUint32 lo, tm; \ + PRUint32 cy = 0; \ + lo = s1lo(W[t-2]); \ + lo += (tm = W[t-7].lo); if (lo < tm) cy++; \ + lo += (tm = s0lo(W[t-15])); if (lo < tm) cy++; \ + lo += (tm = W[t-16].lo); if (lo < tm) cy++; \ + W[t].lo = lo; \ + W[t].hi = cy + s1hi(W[t-2]) + W[t-7].hi + s0hi(W[t-15]) + W[t-16].hi; \ + } while (0) + +#define ROUND(n,a,b,c,d,e,f,g,h) \ + { \ + PRUint32 lo, tm, cy; \ + lo = S1lo(e); \ + lo += (tm = Chxx(e,f,g,lo)); cy = (lo < tm); \ + lo += (tm = K512[n].lo); if (lo < tm) cy++; \ + lo += (tm = W[n].lo); if (lo < tm) cy++; \ + h.lo += lo; if (h.lo < lo) cy++; \ + h.hi += cy + S1hi(e) + Chxx(e,f,g,hi) + K512[n].hi + W[n].hi; \ + d.lo += h.lo; \ + d.hi += h.hi + (d.lo < h.lo); \ + lo = S0lo(a); \ + lo += (tm = Majx(a,b,c,lo)); cy = (lo < tm); \ + h.lo += lo; if (h.lo < lo) cy++; \ + h.hi += cy + S0hi(a) + Majx(a,b,c,hi); \ + DUMP(n,a,d,e,h) \ + } +#endif + +static void +SHA512_Compress(SHA512Context *ctx) +{ +#if defined(IS_LITTLE_ENDIAN) + { +#if defined(HAVE_LONG_LONG) + PRUint64 t1; +#else + PRUint32 t1; +#endif + BYTESWAP8(W[0]); + BYTESWAP8(W[1]); + BYTESWAP8(W[2]); + BYTESWAP8(W[3]); + BYTESWAP8(W[4]); + BYTESWAP8(W[5]); + BYTESWAP8(W[6]); + BYTESWAP8(W[7]); + BYTESWAP8(W[8]); + BYTESWAP8(W[9]); + BYTESWAP8(W[10]); + BYTESWAP8(W[11]); + BYTESWAP8(W[12]); + BYTESWAP8(W[13]); + BYTESWAP8(W[14]); + BYTESWAP8(W[15]); + } +#endif + + { + PRUint64 t1, t2; +#ifdef NOUNROLL512 + { + /* prepare the "message schedule" */ + int t; + for (t = 16; t < 80; ++t) { + INITW(t); + } + } +#else + INITW(16); + INITW(17); + INITW(18); + INITW(19); + + INITW(20); + INITW(21); + INITW(22); + INITW(23); + INITW(24); + INITW(25); + INITW(26); + INITW(27); + INITW(28); + INITW(29); + + INITW(30); + INITW(31); + INITW(32); + INITW(33); + INITW(34); + INITW(35); + INITW(36); + INITW(37); + INITW(38); + INITW(39); + + INITW(40); + INITW(41); + INITW(42); + INITW(43); + INITW(44); + INITW(45); + INITW(46); + INITW(47); + INITW(48); + INITW(49); + + INITW(50); + INITW(51); + INITW(52); + INITW(53); + INITW(54); + INITW(55); + INITW(56); + INITW(57); + INITW(58); + INITW(59); + + INITW(60); + INITW(61); + INITW(62); + INITW(63); + INITW(64); + INITW(65); + INITW(66); + INITW(67); + INITW(68); + INITW(69); + + INITW(70); + INITW(71); + INITW(72); + INITW(73); + INITW(74); + INITW(75); + INITW(76); + INITW(77); + INITW(78); + INITW(79); +#endif + } +#ifdef SHA512_TRACE + { + int i; + for (i = 0; i < 80; ++i) { +#ifdef HAVE_LONG_LONG + printf("W[%2d] = %016lx\n", i, W[i]); +#else + printf("W[%2d] = %08x%08x\n", i, W[i].hi, W[i].lo); +#endif + } + } +#endif + { + PRUint64 a, b, c, d, e, f, g, h; + + a = H[0]; + b = H[1]; + c = H[2]; + d = H[3]; + e = H[4]; + f = H[5]; + g = H[6]; + h = H[7]; + +#ifdef NOUNROLL512 + { + int t; + for (t = 0; t < 80; t+= 8) { + ROUND(t+0,a,b,c,d,e,f,g,h) + ROUND(t+1,h,a,b,c,d,e,f,g) + ROUND(t+2,g,h,a,b,c,d,e,f) + ROUND(t+3,f,g,h,a,b,c,d,e) + ROUND(t+4,e,f,g,h,a,b,c,d) + ROUND(t+5,d,e,f,g,h,a,b,c) + ROUND(t+6,c,d,e,f,g,h,a,b) + ROUND(t+7,b,c,d,e,f,g,h,a) + } + } +#else + ROUND( 0,a,b,c,d,e,f,g,h) + ROUND( 1,h,a,b,c,d,e,f,g) + ROUND( 2,g,h,a,b,c,d,e,f) + ROUND( 3,f,g,h,a,b,c,d,e) + ROUND( 4,e,f,g,h,a,b,c,d) + ROUND( 5,d,e,f,g,h,a,b,c) + ROUND( 6,c,d,e,f,g,h,a,b) + ROUND( 7,b,c,d,e,f,g,h,a) + + ROUND( 8,a,b,c,d,e,f,g,h) + ROUND( 9,h,a,b,c,d,e,f,g) + ROUND(10,g,h,a,b,c,d,e,f) + ROUND(11,f,g,h,a,b,c,d,e) + ROUND(12,e,f,g,h,a,b,c,d) + ROUND(13,d,e,f,g,h,a,b,c) + ROUND(14,c,d,e,f,g,h,a,b) + ROUND(15,b,c,d,e,f,g,h,a) + + ROUND(16,a,b,c,d,e,f,g,h) + ROUND(17,h,a,b,c,d,e,f,g) + ROUND(18,g,h,a,b,c,d,e,f) + ROUND(19,f,g,h,a,b,c,d,e) + ROUND(20,e,f,g,h,a,b,c,d) + ROUND(21,d,e,f,g,h,a,b,c) + ROUND(22,c,d,e,f,g,h,a,b) + ROUND(23,b,c,d,e,f,g,h,a) + + ROUND(24,a,b,c,d,e,f,g,h) + ROUND(25,h,a,b,c,d,e,f,g) + ROUND(26,g,h,a,b,c,d,e,f) + ROUND(27,f,g,h,a,b,c,d,e) + ROUND(28,e,f,g,h,a,b,c,d) + ROUND(29,d,e,f,g,h,a,b,c) + ROUND(30,c,d,e,f,g,h,a,b) + ROUND(31,b,c,d,e,f,g,h,a) + + ROUND(32,a,b,c,d,e,f,g,h) + ROUND(33,h,a,b,c,d,e,f,g) + ROUND(34,g,h,a,b,c,d,e,f) + ROUND(35,f,g,h,a,b,c,d,e) + ROUND(36,e,f,g,h,a,b,c,d) + ROUND(37,d,e,f,g,h,a,b,c) + ROUND(38,c,d,e,f,g,h,a,b) + ROUND(39,b,c,d,e,f,g,h,a) + + ROUND(40,a,b,c,d,e,f,g,h) + ROUND(41,h,a,b,c,d,e,f,g) + ROUND(42,g,h,a,b,c,d,e,f) + ROUND(43,f,g,h,a,b,c,d,e) + ROUND(44,e,f,g,h,a,b,c,d) + ROUND(45,d,e,f,g,h,a,b,c) + ROUND(46,c,d,e,f,g,h,a,b) + ROUND(47,b,c,d,e,f,g,h,a) + + ROUND(48,a,b,c,d,e,f,g,h) + ROUND(49,h,a,b,c,d,e,f,g) + ROUND(50,g,h,a,b,c,d,e,f) + ROUND(51,f,g,h,a,b,c,d,e) + ROUND(52,e,f,g,h,a,b,c,d) + ROUND(53,d,e,f,g,h,a,b,c) + ROUND(54,c,d,e,f,g,h,a,b) + ROUND(55,b,c,d,e,f,g,h,a) + + ROUND(56,a,b,c,d,e,f,g,h) + ROUND(57,h,a,b,c,d,e,f,g) + ROUND(58,g,h,a,b,c,d,e,f) + ROUND(59,f,g,h,a,b,c,d,e) + ROUND(60,e,f,g,h,a,b,c,d) + ROUND(61,d,e,f,g,h,a,b,c) + ROUND(62,c,d,e,f,g,h,a,b) + ROUND(63,b,c,d,e,f,g,h,a) + + ROUND(64,a,b,c,d,e,f,g,h) + ROUND(65,h,a,b,c,d,e,f,g) + ROUND(66,g,h,a,b,c,d,e,f) + ROUND(67,f,g,h,a,b,c,d,e) + ROUND(68,e,f,g,h,a,b,c,d) + ROUND(69,d,e,f,g,h,a,b,c) + ROUND(70,c,d,e,f,g,h,a,b) + ROUND(71,b,c,d,e,f,g,h,a) + + ROUND(72,a,b,c,d,e,f,g,h) + ROUND(73,h,a,b,c,d,e,f,g) + ROUND(74,g,h,a,b,c,d,e,f) + ROUND(75,f,g,h,a,b,c,d,e) + ROUND(76,e,f,g,h,a,b,c,d) + ROUND(77,d,e,f,g,h,a,b,c) + ROUND(78,c,d,e,f,g,h,a,b) + ROUND(79,b,c,d,e,f,g,h,a) +#endif + + ADDTO(a,H[0]); + ADDTO(b,H[1]); + ADDTO(c,H[2]); + ADDTO(d,H[3]); + ADDTO(e,H[4]); + ADDTO(f,H[5]); + ADDTO(g,H[6]); + ADDTO(h,H[7]); + } +} + +void +SHA512_Update(SHA512Context *ctx, const unsigned char *input, + unsigned int inputLen) +{ + unsigned int inBuf; + if (!inputLen) + return; + +#if defined(HAVE_LONG_LONG) + inBuf = (unsigned int)ctx->sizeLo & 0x7f; + /* Add inputLen into the count of bytes processed, before processing */ + ctx->sizeLo += inputLen; +#else + inBuf = (unsigned int)ctx->sizeLo.lo & 0x7f; + ctx->sizeLo.lo += inputLen; + if (ctx->sizeLo.lo < inputLen) ctx->sizeLo.hi++; +#endif + + /* if data already in buffer, attemp to fill rest of buffer */ + if (inBuf) { + unsigned int todo = SHA512_BLOCK_LENGTH - inBuf; + if (inputLen < todo) + todo = inputLen; + memcpy(B + inBuf, input, todo); + input += todo; + inputLen -= todo; + if (inBuf + todo == SHA512_BLOCK_LENGTH) + SHA512_Compress(ctx); + } + + /* if enough data to fill one or more whole buffers, process them. */ + while (inputLen >= SHA512_BLOCK_LENGTH) { + memcpy(B, input, SHA512_BLOCK_LENGTH); + input += SHA512_BLOCK_LENGTH; + inputLen -= SHA512_BLOCK_LENGTH; + SHA512_Compress(ctx); + } + /* if data left over, fill it into buffer */ + if (inputLen) + memcpy(B, input, inputLen); +} + +void +SHA512_End(SHA512Context *ctx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen) +{ +#if defined(HAVE_LONG_LONG) + unsigned int inBuf = (unsigned int)ctx->sizeLo & 0x7f; + unsigned int padLen = (inBuf < 112) ? (112 - inBuf) : (112 + 128 - inBuf); + PRUint64 lo, t1; + lo = (ctx->sizeLo << 3); +#else + unsigned int inBuf = (unsigned int)ctx->sizeLo.lo & 0x7f; + unsigned int padLen = (inBuf < 112) ? (112 - inBuf) : (112 + 128 - inBuf); + PRUint64 lo = ctx->sizeLo; + PRUint32 t1; + lo.lo <<= 3; +#endif + + SHA512_Update(ctx, pad, padLen); + +#if defined(HAVE_LONG_LONG) + W[14] = 0; +#else + W[14].lo = 0; + W[14].hi = 0; +#endif + + W[15] = lo; +#if defined(IS_LITTLE_ENDIAN) + BYTESWAP8(W[15]); +#endif + SHA512_Compress(ctx); + + /* now output the answer */ +#if defined(IS_LITTLE_ENDIAN) + BYTESWAP8(H[0]); + BYTESWAP8(H[1]); + BYTESWAP8(H[2]); + BYTESWAP8(H[3]); + BYTESWAP8(H[4]); + BYTESWAP8(H[5]); + BYTESWAP8(H[6]); + BYTESWAP8(H[7]); +#endif + padLen = PR_MIN(SHA512_LENGTH, maxDigestLen); + memcpy(digest, H, padLen); + if (digestLen) + *digestLen = padLen; +} + +SECStatus +SHA512_HashBuf(unsigned char *dest, const unsigned char *src, + unsigned int src_length) +{ + SHA512Context ctx; + unsigned int outLen; + + SHA512_Begin(&ctx); + SHA512_Update(&ctx, src, src_length); + SHA512_End(&ctx, dest, &outLen, SHA512_LENGTH); + + return SECSuccess; +} + + +SECStatus +SHA512_Hash(unsigned char *dest, const char *src) +{ + return SHA512_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src)); +} + + +void SHA512_TraceState(SHA512Context *ctx) { } + +unsigned int +SHA512_FlattenSize(SHA512Context *ctx) +{ + return sizeof *ctx; +} + +SECStatus +SHA512_Flatten(SHA512Context *ctx,unsigned char *space) +{ + PORT_Memcpy(space, ctx, sizeof *ctx); + return SECSuccess; +} + +SHA512Context * +SHA512_Resurrect(unsigned char *space, void *arg) +{ + SHA512Context *ctx = SHA512_NewContext(); + if (ctx) + PORT_Memcpy(ctx, space, sizeof *ctx); + return ctx; +} + +void SHA512_Clone(SHA512Context *dest, SHA512Context *src) +{ + memcpy(dest, src, sizeof *dest); +} + +/* ======================================================================= */ +/* SHA384 uses a SHA512Context as the real context. +** The only differences between SHA384 an SHA512 are: +** a) the intialization values for the context, and +** b) the number of bytes of data produced as output. +*/ + +/* SHA-384 initial hash values */ +static const PRUint64 H384[8] = { +#if PR_BYTES_PER_LONG == 8 + 0xcbbb9d5dc1059ed8UL , 0x629a292a367cd507UL , + 0x9159015a3070dd17UL , 0x152fecd8f70e5939UL , + 0x67332667ffc00b31UL , 0x8eb44a8768581511UL , + 0xdb0c2e0d64f98fa7UL , 0x47b5481dbefa4fa4UL +#else + ULLC(cbbb9d5d,c1059ed8), ULLC(629a292a,367cd507), + ULLC(9159015a,3070dd17), ULLC(152fecd8,f70e5939), + ULLC(67332667,ffc00b31), ULLC(8eb44a87,68581511), + ULLC(db0c2e0d,64f98fa7), ULLC(47b5481d,befa4fa4) +#endif +}; + +SHA384Context * +SHA384_NewContext(void) +{ + return SHA512_NewContext(); +} + +void +SHA384_DestroyContext(SHA384Context *ctx, PRBool freeit) +{ + SHA512_DestroyContext(ctx, freeit); +} + +void +SHA384_Begin(SHA384Context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + memcpy(H, H384, sizeof H384); +} + +void +SHA384_Update(SHA384Context *ctx, const unsigned char *input, + unsigned int inputLen) +{ + SHA512_Update(ctx, input, inputLen); +} + +void +SHA384_End(SHA384Context *ctx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen) +{ +#define SHA_MIN(a,b) (a < b ? a : b) + unsigned int maxLen = SHA_MIN(maxDigestLen, SHA384_LENGTH); + SHA512_End(ctx, digest, digestLen, maxLen); +} + +SECStatus +SHA384_HashBuf(unsigned char *dest, const unsigned char *src, + unsigned int src_length) +{ + SHA512Context ctx; + unsigned int outLen; + + SHA384_Begin(&ctx); + SHA512_Update(&ctx, src, src_length); + SHA512_End(&ctx, dest, &outLen, SHA384_LENGTH); + + return SECSuccess; +} + +SECStatus +SHA384_Hash(unsigned char *dest, const char *src) +{ + return SHA384_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src)); +} + +void SHA384_TraceState(SHA384Context *ctx) { } + +unsigned int +SHA384_FlattenSize(SHA384Context *ctx) +{ + return sizeof(SHA384Context); +} + +SECStatus +SHA384_Flatten(SHA384Context *ctx,unsigned char *space) +{ + return SHA512_Flatten(ctx, space); +} + +SHA384Context * +SHA384_Resurrect(unsigned char *space, void *arg) +{ + return SHA512_Resurrect(space, arg); +} + +void SHA384_Clone(SHA384Context *dest, SHA384Context *src) +{ + memcpy(dest, src, sizeof *dest); +} +#endif /* Comment out unused code. */ + +/* ======================================================================= */ +#ifdef SELFTEST +#include + +static const char abc[] = { "abc" }; +static const char abcdbc[] = { + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" +}; +static const char abcdef[] = { + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" +}; + +void +dumpHash32(const unsigned char *buf, unsigned int bufLen) +{ + unsigned int i; + for (i = 0; i < bufLen; i += 4) { + printf(" %02x%02x%02x%02x", buf[i], buf[i+1], buf[i+2], buf[i+3]); + } + printf("\n"); +} + +void test256(void) +{ + unsigned char outBuf[SHA256_LENGTH]; + + printf("SHA256, input = %s\n", abc); + SHA256_Hash(outBuf, abc); + dumpHash32(outBuf, sizeof outBuf); + + printf("SHA256, input = %s\n", abcdbc); + SHA256_Hash(outBuf, abcdbc); + dumpHash32(outBuf, sizeof outBuf); +} + +void +dumpHash64(const unsigned char *buf, unsigned int bufLen) +{ + unsigned int i; + for (i = 0; i < bufLen; i += 8) { + if (i % 32 == 0) + printf("\n"); + printf(" %02x%02x%02x%02x%02x%02x%02x%02x", + buf[i ], buf[i+1], buf[i+2], buf[i+3], + buf[i+4], buf[i+5], buf[i+6], buf[i+7]); + } + printf("\n"); +} + +void test512(void) +{ + unsigned char outBuf[SHA512_LENGTH]; + + printf("SHA512, input = %s\n", abc); + SHA512_Hash(outBuf, abc); + dumpHash64(outBuf, sizeof outBuf); + + printf("SHA512, input = %s\n", abcdef); + SHA512_Hash(outBuf, abcdef); + dumpHash64(outBuf, sizeof outBuf); +} + +void time512(void) +{ + unsigned char outBuf[SHA512_LENGTH]; + + SHA512_Hash(outBuf, abc); + SHA512_Hash(outBuf, abcdef); +} + +void test384(void) +{ + unsigned char outBuf[SHA384_LENGTH]; + + printf("SHA384, input = %s\n", abc); + SHA384_Hash(outBuf, abc); + dumpHash64(outBuf, sizeof outBuf); + + printf("SHA384, input = %s\n", abcdef); + SHA384_Hash(outBuf, abcdef); + dumpHash64(outBuf, sizeof outBuf); +} + +int main (int argc, char *argv[], char *envp[]) +{ + int i = 1; + if (argc > 1) { + i = atoi(argv[1]); + } + if (i < 2) { + test256(); + test512(); + test384(); + } else { + while (i-- > 0) { + time512(); + } + printf("done\n"); + } + return 0; +} + +#endif -- cgit v1.1