diff options
author | wtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-09 18:40:50 +0000 |
---|---|---|
committer | wtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-09 18:40:50 +0000 |
commit | 692033a35dd662cd04b965cfa31581176ebdcfe6 (patch) | |
tree | 6224a1b37f7c1a0f6296490ba5e805e815b51247 /base | |
parent | 256865a6ab0cf3affd171b2eb275bfa7bb8c964d (diff) | |
download | chromium_src-692033a35dd662cd04b965cfa31581176ebdcfe6.zip chromium_src-692033a35dd662cd04b965cfa31581176ebdcfe6.tar.gz chromium_src-692033a35dd662cd04b965cfa31581176ebdcfe6.tar.bz2 |
Implement PBKDF2-based key derivation, random key generation,
and AES-CBC encryption/decryption using CryptoAPI.
Contributed by Ryan Sleevi <ryan.sleevi@gmail.com>.
Original review URL: http://codereview.chromium.org/1558018
R=wtc,albertb
BUG=none
TEST=SymmetricKeyTest.* and EncryptorTest.*
Review URL: http://codereview.chromium.org/1528021
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@44106 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/crypto/encryptor.h | 14 | ||||
-rw-r--r-- | base/crypto/encryptor_mac.cc | 1 | ||||
-rw-r--r-- | base/crypto/encryptor_nss.cc | 1 | ||||
-rw-r--r-- | base/crypto/encryptor_unittest.cc | 91 | ||||
-rw-r--r-- | base/crypto/encryptor_win.cc | 89 | ||||
-rw-r--r-- | base/crypto/rsa_private_key.h | 11 | ||||
-rw-r--r-- | base/crypto/rsa_private_key_win.cc | 21 | ||||
-rw-r--r-- | base/crypto/scoped_capi_types.h | 124 | ||||
-rw-r--r-- | base/crypto/signature_creator.h | 9 | ||||
-rw-r--r-- | base/crypto/signature_creator_win.cc | 11 | ||||
-rw-r--r-- | base/crypto/signature_verifier.h | 13 | ||||
-rw-r--r-- | base/crypto/signature_verifier_win.cc | 26 | ||||
-rw-r--r-- | base/crypto/symmetric_key.h | 30 | ||||
-rw-r--r-- | base/crypto/symmetric_key_mac.cc | 10 | ||||
-rw-r--r-- | base/crypto/symmetric_key_nss.cc | 2 | ||||
-rw-r--r-- | base/crypto/symmetric_key_unittest.cc | 10 | ||||
-rw-r--r-- | base/crypto/symmetric_key_win.cc | 495 |
17 files changed, 866 insertions, 92 deletions
diff --git a/base/crypto/encryptor.h b/base/crypto/encryptor.h index 96a6d6a..5267549 100644 --- a/base/crypto/encryptor.h +++ b/base/crypto/encryptor.h @@ -7,11 +7,18 @@ #include <string> -#include "base/crypto/symmetric_key.h" -#include "base/scoped_ptr.h" +#include "build/build_config.h" + +#if defined(USE_NSS) +#include "base/crypto/scoped_nss_types.h" +#elif defined(OS_WIN) +#include "base/crypto/scoped_capi_types.h" +#endif namespace base { +class SymmetricKey; + class Encryptor { public: enum Mode { @@ -45,6 +52,9 @@ class Encryptor { std::string* output); std::string iv_; +#elif defined(OS_WIN) + ScopedHCRYPTKEY capi_key_; + DWORD block_size_; #endif }; diff --git a/base/crypto/encryptor_mac.cc b/base/crypto/encryptor_mac.cc index e892c12..2baa499 100644 --- a/base/crypto/encryptor_mac.cc +++ b/base/crypto/encryptor_mac.cc @@ -6,6 +6,7 @@ #include <CommonCrypto/CommonCryptor.h> +#include "base/crypto/symmetric_key.h" #include "base/logging.h" #include "base/string_util.h" diff --git a/base/crypto/encryptor_nss.cc b/base/crypto/encryptor_nss.cc index eac6779..ef77d9d 100644 --- a/base/crypto/encryptor_nss.cc +++ b/base/crypto/encryptor_nss.cc @@ -7,6 +7,7 @@ #include <cryptohi.h> #include <vector> +#include "base/crypto/symmetric_key.h" #include "base/logging.h" #include "base/nss_util.h" diff --git a/base/crypto/encryptor_unittest.cc b/base/crypto/encryptor_unittest.cc index b75c360..a4f4afd 100644 --- a/base/crypto/encryptor_unittest.cc +++ b/base/crypto/encryptor_unittest.cc @@ -8,23 +8,17 @@ #include "base/crypto/symmetric_key.h" #include "base/scoped_ptr.h" -#include "base/string_util.h" #include "testing/gtest/include/gtest/gtest.h" -#if defined(USE_NSS) || defined(OS_MACOSX) -#define MAYBE(name) name -#else -#define MAYBE(name) DISABLED_ ## name -#endif - -TEST(EncryptorTest, MAYBE(EncryptDecrypt)) { +TEST(EncryptorTest, EncryptDecrypt) { scoped_ptr<base::SymmetricKey> key(base::SymmetricKey::DeriveKeyFromPassword( base::SymmetricKey::AES, "password", "saltiest", 1000, 256)); EXPECT_TRUE(NULL != key.get()); base::Encryptor encryptor; - // The IV must be exactly as long a the cipher block size. + // 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(), base::Encryptor::CBC, iv)); std::string plaintext("this is the plaintext"); @@ -38,3 +32,82 @@ TEST(EncryptorTest, MAYBE(EncryptDecrypt)) { 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 + +// TODO(wtc): implement base::SymmetricKey::Import and enable this test for +// other platforms. +#if defined(OS_WIN) +// 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 + }; + + scoped_ptr<base::SymmetricKey> key(base::SymmetricKey::Import( + base::SymmetricKey::AES, raw_key, sizeof(raw_key))); + EXPECT_TRUE(NULL != key.get()); + + base::Encryptor encryptor; + // The IV must be exactly as long a the cipher block size. + std::string iv(reinterpret_cast<const char*>(raw_iv), sizeof(raw_iv)); + EXPECT_EQ(16U, iv.size()); + EXPECT_TRUE(encryptor.Init(key.get(), base::Encryptor::CBC, iv)); + + std::string plaintext(reinterpret_cast<const char*>(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); +} +#endif // OS_WIN diff --git a/base/crypto/encryptor_win.cc b/base/crypto/encryptor_win.cc index 6411408..fe1f5a8 100644 --- a/base/crypto/encryptor_win.cc +++ b/base/crypto/encryptor_win.cc @@ -4,9 +4,29 @@ #include "base/crypto/encryptor.h" +#include <vector> + +#include "base/crypto/symmetric_key.h" + namespace base { -// TODO(albertb): Implement on Windows. +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<BYTE*>(&block_size_in_bits), + ¶m_size, 0); + if (!ok) + return 0; + + return block_size_in_bits / 8; +} + +} // namespace Encryptor::Encryptor() { } @@ -15,15 +35,76 @@ Encryptor::~Encryptor() { } bool Encryptor::Init(SymmetricKey* key, Mode mode, const std::string& iv) { - return false; + 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<BYTE*>(&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<const BYTE*>(iv.data()), 0); + if (!ok) + return false; + + DWORD padding_method = PKCS5_PADDING; + ok = CryptSetKeyParam(capi_key_.get(), KP_PADDING, + reinterpret_cast<BYTE*>(&padding_method), 0); + if (!ok) + return false; + + return true; } bool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) { - return false; + DWORD data_len = plaintext.size(); + DWORD total_len = data_len + block_size_; + + // CryptoAPI encrypts/decrypts in place. + std::vector<BYTE> 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<char*>(&tmp[0]), data_len); + return true; } bool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) { - return false; + DWORD data_len = ciphertext.size(); + + std::vector<BYTE> 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<char*>(&tmp[0]), data_len); + return true; } } // namespace base diff --git a/base/crypto/rsa_private_key.h b/base/crypto/rsa_private_key.h index ae410e0..9c8bfd4 100644 --- a/base/crypto/rsa_private_key.h +++ b/base/crypto/rsa_private_key.h @@ -13,9 +13,6 @@ struct SECKEYPrivateKeyStr; struct SECKEYPublicKeyStr; #elif defined(OS_MACOSX) #include <Security/cssm.h> -#elif defined(OS_WIN) -#include <windows.h> -#include <wincrypt.h> #endif #include <list> @@ -23,6 +20,10 @@ struct SECKEYPublicKeyStr; #include "base/basictypes.h" +#if defined(OS_WIN) +#include "base/crypto/scoped_capi_types.h" +#endif + namespace base { // Used internally by RSAPrivateKey for serializing and deserializing @@ -194,8 +195,8 @@ private: #elif defined(OS_WIN) bool InitProvider(); - HCRYPTPROV provider_; - HCRYPTKEY key_; + ScopedHCRYPTPROV provider_; + ScopedHCRYPTKEY key_; #elif defined(OS_MACOSX) CSSM_KEY key_; #endif diff --git a/base/crypto/rsa_private_key_win.cc b/base/crypto/rsa_private_key_win.cc index 870d650..40aa80a 100644 --- a/base/crypto/rsa_private_key_win.cc +++ b/base/crypto/rsa_private_key_win.cc @@ -32,7 +32,8 @@ RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) { // 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_)) + if (!CryptGenKey(result->provider_, CALG_RSA_SIGN, flags, + result->key_.receive())) return NULL; return result.release(); @@ -95,8 +96,8 @@ RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo( READ_ASSERT(dest == blob.get() + blob_size); if (!CryptImportKey( - result->provider_, reinterpret_cast<uint8*>(public_key_struc), blob_size, - NULL, CRYPT_EXPORTABLE, &result->key_)) { + result->provider_, reinterpret_cast<uint8*>(public_key_struc), + blob_size, NULL, CRYPT_EXPORTABLE, result->key_.receive())) { return NULL; } @@ -105,20 +106,10 @@ RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo( RSAPrivateKey::RSAPrivateKey() : provider_(NULL), key_(NULL) {} -RSAPrivateKey::~RSAPrivateKey() { - if (key_) { - if (!CryptDestroyKey(key_)) - NOTREACHED(); - } - - if (provider_) { - if (!CryptReleaseContext(provider_, 0)) - NOTREACHED(); - } -} +RSAPrivateKey::~RSAPrivateKey() {} bool RSAPrivateKey::InitProvider() { - return FALSE != CryptAcquireContext(&provider_, NULL, NULL, + return FALSE != CryptAcquireContext(provider_.receive(), NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); } diff --git a/base/crypto/scoped_capi_types.h b/base/crypto/scoped_capi_types.h new file mode 100644 index 0000000..73fe51b --- /dev/null +++ b/base/crypto/scoped_capi_types.h @@ -0,0 +1,124 @@ +// Copyright (c) 2010 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 BASE_CRYPTO_SCOPED_CAPI_TYPES_H_ +#define BASE_CRYPTO_SCOPED_CAPI_TYPES_H_ + +#include <windows.h> +#include <wincrypt.h> + +#include <algorithm> + +#include "base/logging.h" + +namespace base { + +// Simple destructor for the Free family of CryptoAPI functions, such as +// CryptDestroyHash, which take only a single argument to release. +template <typename CAPIHandle, BOOL (WINAPI *Destroyer)(CAPIHandle)> +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 <typename CAPIHandle, BOOL (WINAPI *Destroyer)(CAPIHandle, DWORD), + DWORD flags> +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 CAPIHandle, typename FreeProc> +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_EQ(NULL, handle_); + 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<class CH, typename FP> +const FP ScopedCAPIHandle<CH, FP>::free_ = FP(); + +template<class CH, typename FP> inline +bool operator==(CH h, const ScopedCAPIHandle<CH, FP>& b) { + return h == b.get(); +} + +template<class CH, typename FP> inline +bool operator!=(CH h, const ScopedCAPIHandle<CH, FP>& b) { + return h != b.get(); +} + +typedef ScopedCAPIHandle< + HCRYPTPROV, + CAPIDestroyerWithFlags<HCRYPTPROV, + CryptReleaseContext, 0> > ScopedHCRYPTPROV; + +typedef ScopedCAPIHandle< + HCRYPTKEY, CAPIDestroyer<HCRYPTKEY, CryptDestroyKey> > ScopedHCRYPTKEY; + +typedef ScopedCAPIHandle< + HCRYPTHASH, CAPIDestroyer<HCRYPTHASH, CryptDestroyHash> > ScopedHCRYPTHASH; + +} // namespace base + +#endif // BASE_CRYPTO_SCOPED_CAPI_TYPES_H_ diff --git a/base/crypto/signature_creator.h b/base/crypto/signature_creator.h index 9f5a909..0e11ec3 100644 --- a/base/crypto/signature_creator.h +++ b/base/crypto/signature_creator.h @@ -12,9 +12,6 @@ struct SGNContextStr; #elif defined(OS_MACOSX) #include <Security/cssm.h> -#elif defined(OS_WIN) -#include <windows.h> -#include <wincrypt.h> #endif #include <vector> @@ -22,6 +19,10 @@ struct SGNContextStr; #include "base/basictypes.h" #include "base/crypto/rsa_private_key.h" +#if defined(OS_WIN) +#include "base/crypto/scoped_capi_types.h" +#endif + namespace base { // Signs data using a bare private key (as opposed to a full certificate). @@ -51,7 +52,7 @@ class SignatureCreator { #elif defined(OS_MACOSX) CSSM_CC_HANDLE sig_handle_; #elif defined(OS_WIN) - HCRYPTHASH hash_object_; + ScopedHCRYPTHASH hash_object_; #endif DISALLOW_COPY_AND_ASSIGN(SignatureCreator); diff --git a/base/crypto/signature_creator_win.cc b/base/crypto/signature_creator_win.cc index 4ac303d..45924f0 100644 --- a/base/crypto/signature_creator_win.cc +++ b/base/crypto/signature_creator_win.cc @@ -15,7 +15,7 @@ SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key) { result->key_ = key; if (!CryptCreateHash(key->provider(), CALG_SHA1, 0, 0, - &result->hash_object_)) { + result->hash_object_.receive())) { NOTREACHED(); return NULL; } @@ -25,14 +25,7 @@ SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key) { SignatureCreator::SignatureCreator() : hash_object_(0) {} -SignatureCreator::~SignatureCreator() { - if (hash_object_) { - if (!CryptDestroyHash(hash_object_)) - NOTREACHED(); - - 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)) { diff --git a/base/crypto/signature_verifier.h b/base/crypto/signature_verifier.h index bfe6c332..1ef08cf 100644 --- a/base/crypto/signature_verifier.h +++ b/base/crypto/signature_verifier.h @@ -11,15 +11,16 @@ #include <cryptoht.h> #elif defined(OS_MACOSX) #include <Security/cssm.h> -#elif defined(OS_WIN) -#include <windows.h> -#include <wincrypt.h> #endif #include <vector> #include "base/basictypes.h" +#if defined(OS_WIN) +#include "base/crypto/scoped_capi_types.h" +#endif + namespace base { // The SignatureVerifier class verifies a signature using a bare public key @@ -90,11 +91,11 @@ class SignatureVerifier { CSSM_KEY public_key_; #elif defined(OS_WIN) - HCRYPTPROV provider_; + ScopedHCRYPTPROV provider_; - HCRYPTHASH hash_object_; + ScopedHCRYPTHASH hash_object_; - HCRYPTKEY public_key_; + ScopedHCRYPTKEY public_key_; #endif }; diff --git a/base/crypto/signature_verifier_win.cc b/base/crypto/signature_verifier_win.cc index be1ecec..c040d05 100644 --- a/base/crypto/signature_verifier_win.cc +++ b/base/crypto/signature_verifier_win.cc @@ -25,17 +25,12 @@ void WINAPI MyCryptFree(void* p) { namespace base { SignatureVerifier::SignatureVerifier() : hash_object_(0), public_key_(0) { - if (!CryptAcquireContext(&provider_, NULL, NULL, + if (!CryptAcquireContext(provider_.receive(), NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) - provider_ = 0; + provider_.reset(); } SignatureVerifier::~SignatureVerifier() { - Reset(); - if (provider_) { - BOOL ok = CryptReleaseContext(provider_, 0); - DCHECK(ok); - } } bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, @@ -70,7 +65,7 @@ bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, ok = CryptImportPublicKeyInfo(provider_, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - cert_public_key_info, &public_key_); + cert_public_key_info, public_key_.receive()); free(cert_public_key_info); if (!ok) return false; @@ -108,7 +103,7 @@ bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, return false; } - ok = CryptCreateHash(provider_, hash_alg_id, 0, 0, &hash_object_); + ok = CryptCreateHash(provider_, hash_alg_id, 0, 0, hash_object_.receive()); if (!ok) return false; return true; @@ -130,17 +125,8 @@ bool SignatureVerifier::VerifyFinal() { } void SignatureVerifier::Reset() { - BOOL ok; - if (hash_object_) { - ok = CryptDestroyHash(hash_object_); - DCHECK(ok); - hash_object_ = 0; - } - if (public_key_) { - ok = CryptDestroyKey(public_key_); - DCHECK(ok); - public_key_ = 0; - } + hash_object_.reset(); + public_key_.reset(); signature_.clear(); } diff --git a/base/crypto/symmetric_key.h b/base/crypto/symmetric_key.h index f528213..48b3708 100644 --- a/base/crypto/symmetric_key.h +++ b/base/crypto/symmetric_key.h @@ -13,6 +13,8 @@ #include "base/crypto/scoped_nss_types.h" #elif defined(OS_MACOSX) #include <Security/cssmtype.h> +#elif defined(OS_WIN) +#include "base/crypto/scoped_capi_types.h" #endif namespace base { @@ -26,7 +28,7 @@ class SymmetricKey { HMAC_SHA1, }; - virtual ~SymmetricKey() {} + virtual ~SymmetricKey(); // Generates a random key suitable to be used with |cipher| and of // |key_size_in_bits| bits. @@ -42,10 +44,20 @@ class SymmetricKey { size_t iterations, size_t key_size_in_bits); + // TODO(wtc): port this method to Mac and NSS. +#if defined(OS_WIN) + // Imports a raw key. This method is only used by unit tests. + static SymmetricKey* Import(Algorithm algorithm, + const void* key_data, + size_t key_size_in_bytes); +#endif + #if 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. This should only be @@ -59,6 +71,20 @@ class SymmetricKey { #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); @@ -66,4 +92,4 @@ class SymmetricKey { } // namespace base -#endif // BASE_CRYPTO_SYMMETRIC_KEY_H_ +#endif // BASE_CRYPTO_SYMMETRIC_KEY_H_ diff --git a/base/crypto/symmetric_key_mac.cc b/base/crypto/symmetric_key_mac.cc index 89277c6..504f204 100644 --- a/base/crypto/symmetric_key_mac.cc +++ b/base/crypto/symmetric_key_mac.cc @@ -29,7 +29,7 @@ CSSM_KEY_TYPE CheckKeyParams(base::SymmetricKey::Algorithm algorithm, return CSSM_ALGID_SHA1HMAC_LEGACY; } } - + void* CreateRandomBytes(size_t size) { CSSM_RETURN err; CSSM_CC_HANDLE ctx; @@ -49,8 +49,8 @@ void* CreateRandomBytes(size_t size) { } 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(), @@ -63,6 +63,8 @@ inline CSSM_DATA StringToData(const std::string& str) { namespace base { +SymmetricKey::~SymmetricKey() {} + // static SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm, size_t key_size_in_bits) { @@ -123,7 +125,7 @@ SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm, DCHECK_EQ(cssm_key.KeyData.Length, key_size_in_bits / 8); derived_key = new SymmetricKey(cssm_key.KeyData.Data, key_size_in_bits); -exit: + exit: CSSM_DeleteContext(ctx); CSSM_FreeKey(GetSharedCSPHandle(), &credentials, &cssm_key, false); return derived_key; diff --git a/base/crypto/symmetric_key_nss.cc b/base/crypto/symmetric_key_nss.cc index 5af7cde..ed4804f 100644 --- a/base/crypto/symmetric_key_nss.cc +++ b/base/crypto/symmetric_key_nss.cc @@ -12,6 +12,8 @@ namespace base { +SymmetricKey::~SymmetricKey() {} + // static SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm, size_t key_size_in_bits) { diff --git a/base/crypto/symmetric_key_unittest.cc b/base/crypto/symmetric_key_unittest.cc index 486052d..9be846c 100644 --- a/base/crypto/symmetric_key_unittest.cc +++ b/base/crypto/symmetric_key_unittest.cc @@ -10,13 +10,7 @@ #include "base/string_util.h" #include "testing/gtest/include/gtest/gtest.h" -#if defined(USE_NSS) || defined(OS_MACOSX) -#define MAYBE(name) name -#else -#define MAYBE(name) DISABLED_ ## name -#endif - -TEST(SymmetricKeyTest, MAYBE(GenerateRandomKey)) { +TEST(SymmetricKeyTest, GenerateRandomKey) { scoped_ptr<base::SymmetricKey> key( base::SymmetricKey::GenerateRandomKey(base::SymmetricKey::AES, 256)); EXPECT_TRUE(NULL != key.get()); @@ -165,7 +159,7 @@ static const PBKDF2TestVector test_vectors[] = { } }; -TEST(SymmetricKeyTest, MAYBE(DeriveKeyFromPassword)) { +TEST(SymmetricKeyTest, DeriveKeyFromPassword) { for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(test_vectors); ++i) { SCOPED_TRACE(StringPrintf("Test[%u]", i)); #if defined(OS_MACOSX) diff --git a/base/crypto/symmetric_key_win.cc b/base/crypto/symmetric_key_win.cc index c9be914..eb772d5 100644 --- a/base/crypto/symmetric_key_win.cc +++ b/base/crypto/symmetric_key_win.cc @@ -4,14 +4,342 @@ #include "base/crypto/symmetric_key.h" +#include <winsock2.h> // For htonl. + +#include <vector> + +// TODO(wtc): replace scoped_array by std::vector. +#include "base/scoped_ptr.h" + namespace base { -// TODO(albertb): Implement on Windows. +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<BYTE> tmp_data(actual_size); + BYTE* actual_key = &tmp_data[0]; + memcpy(actual_key + sizeof(PlaintextBlobHeader), key_data, key_size); + PlaintextBlobHeader* key_header = + reinterpret_cast<PlaintextBlobHeader*>(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, NULL, + 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<BYTE>* 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<BYTE> 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<const BYTE*>(&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. <snip> 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<const BYTE*>(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<BYTE*>(&big_endian_block_index), + sizeof(big_endian_block_index), 0); + + std::vector<BYTE> 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<char *>(raw_key_.data()), raw_key_.size()); +} // static SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm, size_t key_size_in_bits) { - return NULL; + DCHECK_GE(key_size_in_bits, 8); + + ScopedHCRYPTPROV provider; + ScopedHCRYPTKEY key; + + bool ok = false; + scoped_array<BYTE> 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 @@ -20,11 +348,170 @@ SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm, const std::string& salt, size_t iterations, size_t key_size_in_bits) { - return NULL; + // 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<BYTE*>(reinterpret_cast<const BYTE*>(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<BYTE*>(&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<BYTE> 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) + // <snip> + // 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 void* key_data, + size_t key_size_in_bytes) { + // TODO(wtc): support HMAC. + DCHECK_EQ(algorithm, AES); + + ScopedHCRYPTPROV provider; + BOOL ok = CryptAcquireContext(provider.receive(), NULL, NULL, + PROV_RSA_AES, CRYPT_VERIFYCONTEXT); + if (!ok) + return NULL; + + ALG_ID alg = GetAESAlgIDForKeySize(key_size_in_bytes * 8); + if (alg == 0) + return NULL; + + ScopedHCRYPTKEY key; + if (!ImportRawKey(provider, alg, key_data, key_size_in_bytes, &key)) + return NULL; + + return new SymmetricKey(provider.release(), key.release(), + key_data, key_size_in_bytes); } bool SymmetricKey::GetRawKey(std::string* raw_key) { - return false; + // 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_, NULL, PLAINTEXTKEYBLOB, 0, NULL, &size); + if (!ok) + return false; + + std::vector<BYTE> result(size); + + ok = CryptExportKey(key_, NULL, PLAINTEXTKEYBLOB, 0, &result[0], &size); + if (!ok) + return false; + + PlaintextBlobHeader* header = + reinterpret_cast<PlaintextBlobHeader*>(&result[0]); + raw_key->assign(reinterpret_cast<char*>(&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<const char*>(key_data), + key_size_in_bytes); + } } } // namespace base |