Implement more Botan support.

git-svn-id: http://encfs.googlecode.com/svn/trunk@99 db9cf616-1c43-0410-9cb8-a902689de0d6
This commit is contained in:
Valient Gough 2013-06-17 03:11:46 +00:00
parent 95750d4539
commit bd182db260
9 changed files with 268 additions and 35 deletions

View File

@ -91,6 +91,7 @@ find_program (POD2MAN pod2man)
find_package (GTest)
if (GTEST_FOUND)
include_directories(${GTEST_INCLUDE_DIR})
enable_testing()
endif (GTEST_FOUND)

View File

@ -31,10 +31,6 @@
#include <tinyxml.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <glog/logging.h>
#include "base/base64.h"
#include "base/Interface.h"

View File

@ -26,6 +26,7 @@
#include "base/config.h"
#include "base/shared_ptr.h"
#include "cipher/BlockCipher.h"
#include "cipher/CipherV1.h"
#include "cipher/MemoryPool.h"
#include "cipher/PBKDF.h"
#include "cipher/testing.h"
@ -40,6 +41,13 @@ using std::string;
namespace {
class BlockCipherTest : public testing::Test {
public:
virtual void SetUp() {
CipherV1::init(false);
}
};
void compare(const byte *a, const byte *b, int size) {
#ifdef HAVE_VALGRIND_MEMCHECK_H
ASSERT_EQ(0, VALGRIND_CHECK_MEM_IS_DEFINED(a, size));
@ -54,7 +62,7 @@ void compare(const byte *a, const byte *b, int size) {
}
}
TEST(RequiredBlockCiphers, BlockCipher) {
TEST_F(BlockCipherTest, RequiredBlockCiphers) {
auto aes_cbc = BlockCipher::GetRegistry().CreateForMatch(NAME_AES_CBC);
ASSERT_TRUE(aes_cbc != NULL);
@ -62,7 +70,7 @@ TEST(RequiredBlockCiphers, BlockCipher) {
ASSERT_TRUE(bf_cbc != NULL);
}
TEST(RequiredStreamCiphers, StreamCipher) {
TEST_F(BlockCipherTest, RequiredStreamCiphers) {
auto aes_cfb = StreamCipher::GetRegistry().CreateForMatch(NAME_AES_CFB);
ASSERT_TRUE(aes_cfb != NULL);
@ -84,6 +92,7 @@ void checkTestVector(const char *cipherName,
CipherKey key(strlen(hexKey)/2);
setDataFromHex(key.data(), key.size(), hexKey);
ASSERT_TRUE(cipher->setKey(key));
byte iv[strlen(hexIv)/2];
@ -93,19 +102,23 @@ void checkTestVector(const char *cipherName,
setDataFromHex(plaintext, sizeof(plaintext), hexPlaintext);
byte ciphertext[sizeof(plaintext)];
ASSERT_TRUE(cipher->encrypt(iv, plaintext, ciphertext, sizeof(ciphertext)));
ASSERT_EQ(hexCipher, stringToHex(ciphertext, sizeof(ciphertext)));
// Run test in a loop with the same cipher, since that's how we use it later.
for (int i = 0; i < 2; ++i) {
ASSERT_TRUE(cipher->encrypt(iv, plaintext, ciphertext, sizeof(ciphertext)));
byte decypered[sizeof(plaintext)];
ASSERT_TRUE(cipher->decrypt(iv, ciphertext, decypered, sizeof(ciphertext)));
ASSERT_EQ(hexCipher, stringToHex(ciphertext, sizeof(ciphertext)));
for (unsigned int i = 0; i < sizeof(plaintext); ++i) {
ASSERT_EQ(plaintext[i], decypered[i]);
byte decypered[sizeof(plaintext)];
ASSERT_TRUE(cipher->decrypt(iv, ciphertext, decypered, sizeof(ciphertext)));
for (unsigned int i = 0; i < sizeof(plaintext); ++i) {
ASSERT_EQ(plaintext[i], decypered[i]);
}
}
}
TEST(TestVectors, BlockCipher) {
TEST_F(BlockCipherTest, TestVectors) {
// BF128 CBC
checkTestVector<BlockCipher>(NAME_BLOWFISH_CBC,
"0123456789abcdeff0e1d2c3b4a59687",
@ -149,7 +162,7 @@ TEST(TestVectors, BlockCipher) {
"dc7e84bfda79164b7ecd8486985d3860");
}
TEST(BlockEncryptionTest, BlockCipher) {
TEST_F(BlockCipherTest, BlockEncryptionTest) {
Registry<BlockCipher> registry = BlockCipher::GetRegistry();
shared_ptr<PBKDF> pbkdf(

View File

@ -15,6 +15,7 @@ elseif (WITH_BOTAN)
find_package (Botan REQUIRED)
set (EXTRA_LIBS ${BOTAN_LIBRARIES})
set (EXTRA_SOURCE botan.cpp)
include_directories (${BOTAN_INCLUDE_DIR})
endif (WITH_COMMON_CRYPTO)
add_library (encfs-cipher

View File

@ -19,9 +19,17 @@ using std::string;
namespace {
TEST(CipherKey, ReadWrite) {
class CipherKeyTest : public testing::Test {
protected:
virtual void SetUp() {
CipherV1::init(false);
}
};
TEST_F(CipherKeyTest, ReadWrite) {
for (auto alg : CipherV1::GetAlgorithmList()) {
auto cipher = CipherV1::New(alg.iface);
ASSERT_FALSE(!cipher);
CipherKey masterKey = cipher->newRandomKey();
CipherKey volumeKey = cipher->newRandomKey();

View File

@ -297,6 +297,8 @@ bool CipherV1::initCiphers(const Interface &iface, const Interface &realIface,
else
_keySize = keyRange.closest(keyLength) / 8;
LOG_IF(ERROR, _keySize == 0) << "invalid key size";
_pbkdf.reset(PBKDF::GetRegistry().CreateForMatch(
NAME_PBKDF2_HMAC_SHA1));
if (!_pbkdf) {
@ -405,6 +407,9 @@ bool CipherV1::pseudoRandomize( byte *buf, int len )
bool CipherV1::setKey(const CipherKey &keyIv) {
Lock l(_hmacMutex);
LOG_IF(ERROR, _keySize != keyIv.size()) << "Mismatched key size: passed "
<< keyIv.size() << ", expecting " << _keySize;
// Key is actually key plus iv, so extract the different parts.
CipherKey key(_keySize);
memcpy(key.data(), keyIv.data(), _keySize);

View File

@ -49,6 +49,7 @@
#ifdef WITH_BOTAN
# include <botan/botan.h>
# include <botan/version.h>
#endif
namespace encfs {
@ -104,13 +105,33 @@ MemBlock::~MemBlock()
delete[] data;
}
SecureMem::SecureMem(int len)
#ifdef WITH_BOTAN
: data_(len)
#endif
SecureMem::SecureMem(int len)
: data_(new Botan::SecureVector<unsigned char>(len))
{
rAssert(len > 0);
}
SecureMem::~SecureMem()
{
# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,0)
data_->destroy();
# endif
delete data_;
}
byte* SecureMem::data() const {
return const_cast<byte*>(data_->begin());
}
int SecureMem::size() const {
return data_->size();
}
#else
SecureMem::SecureMem(int len)
{
rAssert(len > 0);
#ifndef WITH_BOTAN
data_ = allocBlock(len);
if (data_)
{
@ -120,14 +141,10 @@ SecureMem::SecureMem(int len)
{
size_ = 0;
}
#endif
}
SecureMem::~SecureMem()
{
#ifdef WITH_BOTAN
data_.destroy();
#else
if (size_)
{
cleanBlock(data_, size_);
@ -137,8 +154,8 @@ SecureMem::~SecureMem()
data_ = NULL;
size_ = 0;
}
#endif
}
#endif
bool operator == (const SecureMem &a, const SecureMem &b) {
return (a.size() == b.size()) &&

View File

@ -25,7 +25,9 @@
#include "base/types.h"
#ifdef WITH_BOTAN
#include <botan/secmem.h>
namespace Botan {
template <typename T> class SecureVector;
}
#endif
namespace encfs {
@ -69,21 +71,14 @@ class SecureMem
private:
#ifdef WITH_BOTAN
Botan::SecureVector<Botan::byte> data_;
Botan::SecureVector<unsigned char> *data_;
#else
byte *data_;
int size_;
#endif
};
#ifdef WITH_BOTAN
inline byte* SecureMem::data() const {
return const_cast<byte*>(data_.begin());
}
inline int SecureMem::size() const {
return data_.size();
}
#else
#ifndef WITH_BOTAN
inline byte* SecureMem::data() const {
return data_;
}

View File

@ -21,6 +21,7 @@
#include "cipher/botan.h"
#include "base/config.h"
#include "base/shared_ptr.h"
#include <glog/logging.h>
#include <botan/botan.h>
@ -35,9 +36,14 @@
#include "cipher/PBKDF.h"
#include "cipher/StreamCipher.h"
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
#include <string>
using namespace Botan;
using std::string;
namespace encfs {
namespace botan {
@ -115,6 +121,197 @@ class PbkdfPkcs5HmacSha256 : public PbkdfPkcs5Hmac {
REGISTER_CLASS(PbkdfPkcs5HmacSha256, PBKDF);
class BotanBlockCipher : public BlockCipher {
Keyed_Filter *encryption; // Not owned.
Keyed_Filter *decryption; // Not owned.
shared_ptr<Pipe> encryptor;
shared_ptr<Pipe> decryptor;
public:
BotanBlockCipher() {}
virtual ~BotanBlockCipher() {}
bool rekey(const CipherKey& key, const string& cipherMode) {
SymmetricKey bkey(key.data(), key.size());
OctetString iv;
encryption = Botan::get_cipher(cipherMode, bkey, iv, Botan::ENCRYPTION);
decryption = Botan::get_cipher(cipherMode, bkey, iv, Botan::DECRYPTION);
if (encryption == nullptr || decryption == nullptr) {
return false;
}
encryptor.reset(new Pipe(encryption));
decryptor.reset(new Pipe(decryption));
return true;
}
virtual bool encrypt(const byte* iv, const byte* in, byte* out, int size) {
#ifdef HAVE_VALGRIND_MEMCHECK_H
if (VALGRIND_CHECK_MEM_IS_ADDRESSABLE(in, size) != 0 ||
VALGRIND_CHECK_MEM_IS_ADDRESSABLE(out, size) != 0 ||
VALGRIND_CHECK_MEM_IS_ADDRESSABLE(iv, blockSize())) {
return false;
}
#endif
encryption->set_iv(OctetString(iv, blockSize()));
encryptor->process_msg(in, size);
auto written = encryptor->read(out, size, Pipe::LAST_MESSAGE);
LOG_IF(ERROR, (int)written != size) << "expected output size " << size
<< ", got " << written;
LOG_IF(ERROR, encryptor->remaining() > 0) << "unread bytes in pipe: "
<< encryptor->remaining();
return true;
}
virtual bool decrypt(const byte* iv, const byte* in, byte* out, int size) {
#ifdef HAVE_VALGRIND_MEMCHECK_H
if (VALGRIND_CHECK_MEM_IS_ADDRESSABLE(in, size) != 0 ||
VALGRIND_CHECK_MEM_IS_ADDRESSABLE(out, size) != 0 ||
VALGRIND_CHECK_MEM_IS_ADDRESSABLE(iv, blockSize())) {
return false;
}
#endif
decryption->set_iv(OctetString(iv, blockSize()));
decryptor->process_msg(in, size);
auto written = decryptor->read(out, size, Pipe::LAST_MESSAGE);
LOG_IF(ERROR, (int)written != size) << "expected output size " << size
<< ", got " << written;
LOG_IF(ERROR, encryptor->remaining() > 0) << "unread bytes in pipe: "
<< encryptor->remaining();
return true;
}
};
class BotanAesCbc : public BotanBlockCipher {
public:
BotanAesCbc() {}
virtual ~BotanAesCbc() {}
virtual bool setKey(const CipherKey& key) {
std::ostringstream ss;
ss << "AES-" << (key.size() * 8) << "/CBC/NoPadding";
return rekey(key, ss.str());
}
virtual int blockSize() const {
return 128 >> 3;
}
static Properties GetProperties() {
return Properties(Range(128,256,64), "AES", "CBC", "Botan");
}
};
REGISTER_CLASS(BotanAesCbc, BlockCipher);
class BotanAesCfb : public BotanBlockCipher {
public:
BotanAesCfb() {}
virtual ~BotanAesCfb() {}
virtual bool setKey(const CipherKey& key) {
std::ostringstream ss;
ss << "AES-" << (key.size() * 8) << "/CFB";
return rekey(key, ss.str());
}
virtual int blockSize() const {
return 128 >> 3;
}
static Properties GetProperties() {
return Properties(Range(128,256,64), "AES", "CFB", "Botan");
}
};
REGISTER_CLASS(BotanAesCfb, StreamCipher);
class BotanBlowfishCbc : public BotanBlockCipher {
public:
BotanBlowfishCbc() {}
virtual ~BotanBlowfishCbc() {}
virtual bool setKey(const CipherKey& key) {
std::ostringstream ss;
ss << "Blowfish" << "/CBC/NoPadding";
return rekey(key, ss.str());
}
virtual int blockSize() const {
return 64 >> 3;
}
static Properties GetProperties() {
return Properties(Range(128,256,32), "Blowfish", "CBC", "Botan");
}
};
REGISTER_CLASS(BotanBlowfishCbc, BlockCipher);
class BotanBlowfishCfb : public BotanBlockCipher {
public:
BotanBlowfishCfb() {}
virtual ~BotanBlowfishCfb() {}
virtual bool setKey(const CipherKey& key) {
std::ostringstream ss;
ss << "Blowfish" << "/CFB";
return rekey(key, ss.str());
}
virtual int blockSize() const {
return 64 >> 3;
}
static Properties GetProperties() {
return Properties(Range(128,256,32), "Blowfish", "CFB", "Botan");
}
};
REGISTER_CLASS(BotanBlowfishCfb, StreamCipher);
class Sha1HMac : public MAC {
MessageAuthenticationCode *mac;
public:
Sha1HMac() : mac(Botan::get_mac("HMAC(SHA-1)")) {}
virtual ~Sha1HMac() {
delete mac;
}
virtual int outputSize() const {
return mac->output_length();
}
virtual bool setKey(const CipherKey &key) {
SymmetricKey bkey(key.data(), key.size());
mac->set_key(bkey);
return true;
}
virtual void init() {
}
virtual bool update(const byte *in, int length) {
mac->update(in, length);
return true;
}
virtual bool write(byte *out) {
#ifdef HAVE_VALGRIND_MEMCHECK_H
if (VALGRIND_CHECK_MEM_IS_ADDRESSABLE(out, outputSize()) != 0) {
return false;
}
#endif
mac->final(out);
return true;
}
static Properties GetProperties() {
Properties props;
props.blockSize = 160 >> 3;
props.hashFunction = "SHA-1";
props.mode = "HMAC";
props.library = "Botan";
return props;
}
};
REGISTER_CLASS(Sha1HMac, MAC);
} // namespace botan