summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorwtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-09 18:40:50 +0000
committerwtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-09 18:40:50 +0000
commit692033a35dd662cd04b965cfa31581176ebdcfe6 (patch)
tree6224a1b37f7c1a0f6296490ba5e805e815b51247 /base
parent256865a6ab0cf3affd171b2eb275bfa7bb8c964d (diff)
downloadchromium_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.h14
-rw-r--r--base/crypto/encryptor_mac.cc1
-rw-r--r--base/crypto/encryptor_nss.cc1
-rw-r--r--base/crypto/encryptor_unittest.cc91
-rw-r--r--base/crypto/encryptor_win.cc89
-rw-r--r--base/crypto/rsa_private_key.h11
-rw-r--r--base/crypto/rsa_private_key_win.cc21
-rw-r--r--base/crypto/scoped_capi_types.h124
-rw-r--r--base/crypto/signature_creator.h9
-rw-r--r--base/crypto/signature_creator_win.cc11
-rw-r--r--base/crypto/signature_verifier.h13
-rw-r--r--base/crypto/signature_verifier_win.cc26
-rw-r--r--base/crypto/symmetric_key.h30
-rw-r--r--base/crypto/symmetric_key_mac.cc10
-rw-r--r--base/crypto/symmetric_key_nss.cc2
-rw-r--r--base/crypto/symmetric_key_unittest.cc10
-rw-r--r--base/crypto/symmetric_key_win.cc495
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),
+ &param_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), &param_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