summaryrefslogtreecommitdiffstats
path: root/base/crypto
diff options
context:
space:
mode:
authorjoth@chromium.org <joth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-12 16:29:06 +0000
committerjoth@chromium.org <joth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-12 16:29:06 +0000
commit2500710027b1034c281b4ef99e0907d96759996b (patch)
tree16d3ff653634ab32dd3cb1f25d447e1e7318d3c6 /base/crypto
parent43a4020cd2aa067b10c55731978d9ac127a4cd50 (diff)
downloadchromium_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.h7
-rw-r--r--base/crypto/encryptor_nss.cc3
-rw-r--r--base/crypto/encryptor_openssl.cc103
-rw-r--r--base/crypto/encryptor_unittest.cc122
-rw-r--r--base/crypto/encryptor_win.cc2
-rw-r--r--base/crypto/symmetric_key.h4
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;