diff options
author | joth@chromium.org <joth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-12 16:29:06 +0000 |
---|---|---|
committer | joth@chromium.org <joth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-12 16:29:06 +0000 |
commit | 2500710027b1034c281b4ef99e0907d96759996b (patch) | |
tree | 16d3ff653634ab32dd3cb1f25d447e1e7318d3c6 /base/crypto | |
parent | 43a4020cd2aa067b10c55731978d9ac127a4cd50 (diff) | |
download | chromium_src-2500710027b1034c281b4ef99e0907d96759996b.zip chromium_src-2500710027b1034c281b4ef99e0907d96759996b.tar.gz chromium_src-2500710027b1034c281b4ef99e0907d96759996b.tar.bz2 |
Implements encryptor_openssl.cc
Depends on pending CL http://codereview.chromium.org/4691003/
BUG=None
TEST=base_unittests Encryptor*
Review URL: http://codereview.chromium.org/4777001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@65952 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/crypto')
-rw-r--r-- | base/crypto/encryptor.h | 7 | ||||
-rw-r--r-- | base/crypto/encryptor_nss.cc | 3 | ||||
-rw-r--r-- | base/crypto/encryptor_openssl.cc | 103 | ||||
-rw-r--r-- | base/crypto/encryptor_unittest.cc | 122 | ||||
-rw-r--r-- | base/crypto/encryptor_win.cc | 2 | ||||
-rw-r--r-- | base/crypto/symmetric_key.h | 4 |
6 files changed, 230 insertions, 11 deletions
diff --git a/base/crypto/encryptor.h b/base/crypto/encryptor.h index f1d6f28..7718240 100644 --- a/base/crypto/encryptor.h +++ b/base/crypto/encryptor.h @@ -44,7 +44,12 @@ class Encryptor { SymmetricKey* key_; Mode mode_; -#if defined(USE_NSS) +#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) diff --git a/base/crypto/encryptor_nss.cc b/base/crypto/encryptor_nss.cc index ef77d9d..737e8f2 100644 --- a/base/crypto/encryptor_nss.cc +++ b/base/crypto/encryptor_nss.cc @@ -48,9 +48,6 @@ bool Encryptor::Init(SymmetricKey* key, Mode mode, const std::string& iv) { } bool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) { - if (plaintext.size() == 0) - return false; - ScopedPK11Context context(PK11_CreateContextBySymKey(CKM_AES_CBC_PAD, CKA_ENCRYPT, key_->key(), diff --git a/base/crypto/encryptor_openssl.cc b/base/crypto/encryptor_openssl.cc index 71a84be..6b08bd6 100644 --- a/base/crypto/encryptor_openssl.cc +++ b/base/crypto/encryptor_openssl.cc @@ -4,10 +4,46 @@ #include "base/crypto/encryptor.h" +#include <openssl/aes.h> +#include <openssl/evp.h> + +#include "base/crypto/symmetric_key.h" #include "base/logging.h" +#include "base/openssl_util.h" +#include "base/string_util.h" namespace base { +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(); + } + EVP_CIPHER_CTX* get() { return &ctx_; } + + private: + EVP_CIPHER_CTX ctx_; +}; + +} // namespace + Encryptor::Encryptor() { } @@ -15,18 +51,73 @@ Encryptor::~Encryptor() { } bool Encryptor::Init(SymmetricKey* key, Mode mode, const std::string& iv) { - NOTIMPLEMENTED(); - return false; + DCHECK(key); + DCHECK_EQ(CBC, mode); + + 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) { - NOTIMPLEMENTED(); - return false; + return Crypt(true, plaintext, ciphertext); } bool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) { - NOTIMPLEMENTED(); - return false; + return Crypt(false, ciphertext, plaintext); +} + +bool Encryptor::Crypt(bool do_encrypt, + const std::string& input, + std::string* output) { + // 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<int>(iv_.length())); + DCHECK_EQ(EVP_CIPHER_key_length(cipher), static_cast<int>(key.length())); + + ScopedCipherCTX ctx; + if (!EVP_CipherInit_ex(ctx.get(), cipher, NULL, + reinterpret_cast<const uint8*>(key.data()), + reinterpret_cast<const uint8*>(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<uint8*>(WriteInto(&result, + output_size + 1)); + int out_len; + if (!EVP_CipherUpdate(ctx.get(), out_ptr, &out_len, + reinterpret_cast<const uint8*>(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<int>(output_size)); + result.resize(out_len); + + output->swap(result); + return true; } } // namespace base diff --git a/base/crypto/encryptor_unittest.cc b/base/crypto/encryptor_unittest.cc index bc66e55..e8d055b 100644 --- a/base/crypto/encryptor_unittest.cc +++ b/base/crypto/encryptor_unittest.cc @@ -8,6 +8,7 @@ #include "base/crypto/symmetric_key.h" #include "base/scoped_ptr.h" +#include "base/string_number_conversions.h" #include "testing/gtest/include/gtest/gtest.h" TEST(EncryptorTest, EncryptDecrypt) { @@ -108,3 +109,124 @@ TEST(EncryptorTest, EncryptAES256CBC) { 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<base::SymmetricKey> sym_key(base::SymmetricKey::Import( + base::SymmetricKey::AES, key)); + ASSERT_TRUE(NULL != sym_key.get()); + + base::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(), base::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<base::SymmetricKey> sym_key(base::SymmetricKey::Import( + base::SymmetricKey::AES, key)); + ASSERT_TRUE(NULL != sym_key.get()); + + base::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(), base::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<base::SymmetricKey> sym_key(base::SymmetricKey::Import( + base::SymmetricKey::AES, key)); + ASSERT_TRUE(NULL != sym_key.get()); + + base::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(), base::Encryptor::CBC, iv)); +} +#endif // unsupported platforms. + +TEST(EncryptorTest, UnsupportedIV) { + std::string key = "128=SixteenBytes"; + std::string iv = "OnlyForteen :("; + scoped_ptr<base::SymmetricKey> sym_key(base::SymmetricKey::Import( + base::SymmetricKey::AES, key)); + ASSERT_TRUE(NULL != sym_key.get()); + + base::Encryptor encryptor; + EXPECT_FALSE(encryptor.Init(sym_key.get(), base::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<base::SymmetricKey> sym_key(base::SymmetricKey::Import( + base::SymmetricKey::AES, key)); + ASSERT_TRUE(NULL != sym_key.get()); + + base::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(), base::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<base::SymmetricKey> sym_key(base::SymmetricKey::Import( + base::SymmetricKey::AES, key)); + ASSERT_TRUE(NULL != sym_key.get()); + + base::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(), base::Encryptor::CBC, iv)); + + std::string decrypted; + EXPECT_FALSE(encryptor.Decrypt("", &decrypted)); + EXPECT_EQ("", decrypted); +} diff --git a/base/crypto/encryptor_win.cc b/base/crypto/encryptor_win.cc index fe1f5a8..4a137b3 100644 --- a/base/crypto/encryptor_win.cc +++ b/base/crypto/encryptor_win.cc @@ -93,6 +93,8 @@ bool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) { bool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) { DWORD data_len = ciphertext.size(); + if (data_len == 0) + return false; std::vector<BYTE> tmp(data_len); memcpy(&tmp[0], ciphertext.data(), data_len); diff --git a/base/crypto/symmetric_key.h b/base/crypto/symmetric_key.h index 9729166..1038728 100644 --- a/base/crypto/symmetric_key.h +++ b/base/crypto/symmetric_key.h @@ -51,7 +51,9 @@ class SymmetricKey { // SymmetricKey. static SymmetricKey* Import(Algorithm algorithm, const std::string& raw_key); -#if defined(USE_NSS) +#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; |