add CommonCrypto support, other misc fixes

git-svn-id: http://encfs.googlecode.com/svn/trunk@97 db9cf616-1c43-0410-9cb8-a902689de0d6
This commit is contained in:
Valient Gough 2013-03-05 06:39:51 +00:00
parent 5a126ea797
commit 63c2d1c539
42 changed files with 917 additions and 292 deletions

View File

@ -3,11 +3,17 @@ project(Encfs)
set (ENCFS_MAJOR 2) set (ENCFS_MAJOR 2)
set (ENCFS_MINOR 0) set (ENCFS_MINOR 0)
set (ENCFS_PATCH 0) set (ENCFS_VERSION "${ENCFS_MAJOR}.${ENCFS_MINOR}")
set (ENCFS_VERSION "${ENCFS_MAJOR}.${ENCFS_MINOR}.${ENCFS_PATCH}")
option (BUILD_SHARED_LIBS "Build dynamic link libraries" OFF) option (BUILD_SHARED_LIBS "Build dynamic link libraries" OFF)
option (WITH_OPENSSL "WithOpenSSL" ON)
option (WITH_COMMON_CRYPTO "WithCommonCrypto" OFF)
if (WITH_COMMON_CRYPTO)
set (WITH_OPENSSL OFF)
endif (WITH_COMMON_CRYPTO)
set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_SOURCE_DIR}/CMakeModules/") "${CMAKE_SOURCE_DIR}/CMakeModules/")
@ -36,7 +42,6 @@ endif (APPLE)
set (CPACK_PACKAGE_NAME "Encfs") set (CPACK_PACKAGE_NAME "Encfs")
set (CPACK_PACKAGE_VERSION_MAJOR ${ENCFS_MAJOR}) set (CPACK_PACKAGE_VERSION_MAJOR ${ENCFS_MAJOR})
set (CPACK_PACKAGE_VERSION_MINOR ${ENCFS_MINOR}) set (CPACK_PACKAGE_VERSION_MINOR ${ENCFS_MINOR})
set (CPACK_PACKAGE_VERSION_PATCH ${ENCFS_PATCH})
set (CPACK_SOURCE_GENERATOR TGZ) set (CPACK_SOURCE_GENERATOR TGZ)
set (CPACK_SOURCE_IGNORE_FILES set (CPACK_SOURCE_IGNORE_FILES
"/_darcs/" "/_darcs/"
@ -56,9 +61,15 @@ check_include_file_cxx (tr1/tuple HAVE_TR1_TUPLE)
check_include_file_cxx (valgrind/valgrind.h HAVE_VALGRIND_VALGRIND_H) check_include_file_cxx (valgrind/valgrind.h HAVE_VALGRIND_VALGRIND_H)
check_include_file_cxx (valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H) check_include_file_cxx (valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
# TODO: move this to cipher directory. if (WITH_COMMON_CRYPTO)
find_package (OpenSSL REQUIRED) check_include_file_cxx (Security/SecRandom.h HAVE_SEC_RANDOM_H)
include (OpenSSLTests) endif (WITH_COMMON_CRYPTO)
if (WITH_OPENSSL)
# TODO: move this to cipher directory.
find_package (OpenSSL REQUIRED)
include (OpenSSLTests)
endif (WITH_OPENSSL)
# Check if xattr functions take extra argument. # Check if xattr functions take extra argument.
include (CheckCXXSourceCompiles) include (CheckCXXSourceCompiles)

View File

@ -30,7 +30,9 @@
#include "base/types.h" #include "base/types.h"
using namespace std; using std::make_pair;
using std::map;
using std::string;
namespace encfs { namespace encfs {

View File

@ -38,8 +38,7 @@
#include <glog/logging.h> #include <glog/logging.h>
#include "base/base64.h" #include "base/base64.h"
#include "base/Interface.h" #include "base/Interface.h"
#include "base/shared_ptr.h"
using namespace std;
namespace encfs { namespace encfs {
@ -116,16 +115,9 @@ bool XmlValue::readB64(const char *path, byte *data, int length) const
std::string s = value->text(); std::string s = value->text();
s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end()); s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end());
s.erase(s.find_last_not_of("=")+1);
BIO *b64 = BIO_new(BIO_f_base64()); int decodedSize = B64ToB256Bytes(s.size());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
BIO *bmem = BIO_new_mem_buf((void *)s.c_str(), s.size());
bmem = BIO_push(b64, bmem);
int decodedSize = BIO_read(bmem, data, length);
BIO_free_all(b64);
if (decodedSize != length) if (decodedSize != length)
{ {
LOG(ERROR) << "decoding bytes len " << s.size() LOG(ERROR) << "decoding bytes len " << s.size()
@ -133,6 +125,8 @@ bool XmlValue::readB64(const char *path, byte *data, int length) const
<< ", got " << decodedSize; << ", got " << decodedSize;
return false; return false;
} }
changeBase2((byte *)s.data(), s.size(), 6, data, length, 8);
B64ToAsciiStandard(data, length);
return true; return true;
} }

View File

@ -148,6 +148,25 @@ void B64ToAscii(byte *in, int length)
} }
} }
void B64ToAsciiStandard(byte *in, int length)
{
static const char LookupTable[] = "+/0123456789";
for(int offset=0; offset<length; ++offset)
{
int ch = in[offset];
if(ch > 11)
{
if(ch > 37)
ch += 'a' - 38;
else
ch += 'A' - 12;
} else
ch = LookupTable[ ch ];
in[offset] = ch;
}
}
static const byte Ascii2B64Table[] = static const byte Ascii2B64Table[] =
" 01 23456789:; "; " 01 23456789:; ";
// 0123456789 123456789 123456789 123456789 123456789 123456789 1234 // 0123456789 123456789 123456789 123456789 123456789 123456789 1234

View File

@ -62,6 +62,8 @@ void changeBase2Inline(byte *buf, int srcLength,
// inplace translation from values [0,2^6] => base64 ASCII // inplace translation from values [0,2^6] => base64 ASCII
void B64ToAscii(byte *buf, int length); void B64ToAscii(byte *buf, int length);
// Like B64ToAscii, but uses standard characters "+" and "/" in encoding.
void B64ToAsciiStandard(byte *buf, int length);
// inplace translation from values [0,2^5] => base32 ASCII // inplace translation from values [0,2^5] => base32 ASCII
void B32ToAscii(byte *buf, int length); void B32ToAscii(byte *buf, int length);

View File

@ -10,15 +10,19 @@
#cmakedefine HAVE_TR1_UNORDERED_SET #cmakedefine HAVE_TR1_UNORDERED_SET
#cmakedefine HAVE_TR1_TUPLE #cmakedefine HAVE_TR1_TUPLE
#cmakedefine HAVE_VALGRIND_VALGRIND_H
#cmakedefine HAVE_VALGRIND_MEMCHECK_H
#cmakedefine WITH_OPENSSL
#cmakedefine WITH_COMMON_CRYPTO
#cmakedefine HAVE_SEC_RANDOM_H
#cmakedefine HAVE_EVP_BF #cmakedefine HAVE_EVP_BF
#cmakedefine HAVE_EVP_AES #cmakedefine HAVE_EVP_AES
#cmakedefine HAVE_EVP_AES_XTS #cmakedefine HAVE_EVP_AES_XTS
#cmakedefine HAVE_LCHMOD #cmakedefine HAVE_LCHMOD
#cmakedefine HAVE_VALGRIND_VALGRIND_H
#cmakedefine HAVE_VALGRIND_MEMCHECK_H
/* TODO: add other thread library support. */ /* TODO: add other thread library support. */
#cmakedefine CMAKE_USE_PTHREADS_INIT #cmakedefine CMAKE_USE_PTHREADS_INIT

View File

@ -1,7 +1,14 @@
#include "cipher/BlockCipher.h" #include "cipher/BlockCipher.h"
// TODO: add ifdef when OpenSSL becomes optional. #include "base/config.h"
#ifdef WITH_OPENSSL
#include "cipher/openssl.h" #include "cipher/openssl.h"
#endif
#ifdef WITH_COMMON_CRYPTO
#include "cipher/CommonCrypto.h"
#endif
#include "cipher/NullCiphers.h" #include "cipher/NullCiphers.h"
namespace encfs { namespace encfs {
@ -12,7 +19,12 @@ Registry<BlockCipher>& BlockCipher::GetRegistry()
static bool first = true; static bool first = true;
if (first) if (first)
{ {
#ifdef WITH_OPENSSL
OpenSSL::registerCiphers(); OpenSSL::registerCiphers();
#endif
#ifdef WITH_COMMON_CRYPTO
CommonCrypto::registerCiphers();
#endif
NullCiphers::registerCiphers(); NullCiphers::registerCiphers();
first = false; first = false;
} }

View File

@ -23,12 +23,17 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "base/config.h"
#include "base/shared_ptr.h" #include "base/shared_ptr.h"
#include "cipher/BlockCipher.h" #include "cipher/BlockCipher.h"
#include "cipher/MemoryPool.h" #include "cipher/MemoryPool.h"
#include "cipher/PBKDF.h" #include "cipher/PBKDF.h"
#include "cipher/testing.h" #include "cipher/testing.h"
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
using namespace encfs; using namespace encfs;
using std::list; using std::list;
using std::string; using std::string;
@ -36,6 +41,10 @@ using std::string;
namespace { namespace {
void compare(const byte *a, const byte *b, int size) { 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));
ASSERT_EQ(0, VALGRIND_CHECK_MEM_IS_DEFINED(b, size));
#endif
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
bool match = (a[i] == b[i]); bool match = (a[i] == b[i]);
ASSERT_TRUE(match) << "mismatched data at offset " << i ASSERT_TRUE(match) << "mismatched data at offset " << i
@ -61,31 +70,83 @@ TEST(RequiredStreamCiphers, StreamCipher) {
ASSERT_TRUE(bf_cfb != NULL); ASSERT_TRUE(bf_cfb != NULL);
} }
TEST(BlowfishTestVector, BlockCihper) { template <typename T>
auto cbc = BlockCipher::GetRegistry().CreateForMatch(NAME_BLOWFISH_CBC); void checkTestVector(const char *cipherName,
auto cfb = StreamCipher::GetRegistry().CreateForMatch(NAME_BLOWFISH_CFB); const char *hexKey,
const char *hexIv,
const char *hexPlaintext,
const char *hexCipher) {
SCOPED_TRACE(testing::Message() << "Testing cipher: " << cipherName
<< ", key = " << hexKey << ", plaintext = " << hexPlaintext);
CipherKey key(16); auto cipher = T::GetRegistry().CreateForMatch(cipherName);
setDataFromHex(key.data(), key.size(), "0123456789abcdeff0e1d2c3b4a59687"); ASSERT_TRUE(cipher != NULL);
cbc->setKey(key);
cfb->setKey(key);
byte iv[8]; CipherKey key(strlen(hexKey)/2);
setDataFromHex(iv, 8, "fedcba9876543210"); setDataFromHex(key.data(), key.size(), hexKey);
ASSERT_TRUE(cipher->setKey(key));
byte data[32]; byte iv[strlen(hexIv)/2];
setDataFromHex(data, 32, setDataFromHex(iv, sizeof(iv), hexIv);
"37363534333231204e6f77206973207468652074696d6520666f722000000000");
byte cipherData[32]; byte plaintext[strlen(hexPlaintext)/2];
cbc->encrypt(iv, data, cipherData, 32); setDataFromHex(plaintext, sizeof(plaintext), hexPlaintext);
ASSERT_EQ("6b77b4d63006dee605b156e27403979358deb9e7154616d959f1652bd5ff92cc", byte ciphertext[sizeof(plaintext)];
stringToHex(cipherData, 32)); ASSERT_TRUE(cipher->encrypt(iv, plaintext, ciphertext, sizeof(ciphertext)));
cfb->encrypt(iv, data, cipherData, 29); ASSERT_EQ(hexCipher, stringToHex(ciphertext, sizeof(ciphertext)));
ASSERT_EQ("e73214a2822139caf26ecf6d2eb9e76e3da3de04d1517200519d57a6c3",
stringToHex(cipherData, 29)); 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) {
// BF128 CBC
checkTestVector<BlockCipher>(NAME_BLOWFISH_CBC,
"0123456789abcdeff0e1d2c3b4a59687",
"fedcba9876543210",
"37363534333231204e6f77206973207468652074696d6520666f722000000000",
"6b77b4d63006dee605b156e27403979358deb9e7154616d959f1652bd5ff92cc");
// BF128 CFB
checkTestVector<StreamCipher>(NAME_BLOWFISH_CFB,
"0123456789abcdeff0e1d2c3b4a59687",
"fedcba9876543210",
"37363534333231204e6f77206973207468652074696d6520666f722000",
"e73214a2822139caf26ecf6d2eb9e76e3da3de04d1517200519d57a6c3");
// AES128 CBC
checkTestVector<BlockCipher>(NAME_AES_CBC,
"2b7e151628aed2a6abf7158809cf4f3c",
"000102030405060708090a0b0c0d0e0f",
"6bc1bee22e409f96e93d7e117393172a",
"7649abac8119b246cee98e9b12e9197d");
// AES128 CFB
checkTestVector<StreamCipher>(NAME_AES_CFB,
"2b7e151628aed2a6abf7158809cf4f3c",
"000102030405060708090a0b0c0d0e0f",
"6bc1bee22e409f96e93d7e117393172a",
"3b3fd92eb72dad20333449f8e83cfb4a");
// AES256 CBC
checkTestVector<BlockCipher>(NAME_AES_CBC,
"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
"000102030405060708090a0b0c0d0e0f",
"6bc1bee22e409f96e93d7e117393172a",
"f58c4c04d6e5f1ba779eabfb5f7bfbd6");
// AES256 CFB
checkTestVector<StreamCipher>(NAME_AES_CFB,
"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
"000102030405060708090a0b0c0d0e0f",
"6bc1bee22e409f96e93d7e117393172a",
"dc7e84bfda79164b7ecd8486985d3860");
} }
TEST(BlockEncryptionTest, BlockCipher) { TEST(BlockEncryptionTest, BlockCipher) {
@ -109,10 +170,12 @@ TEST(BlockEncryptionTest, BlockCipher) {
CipherKey key = pbkdf->randomKey(keySize / 8); CipherKey key = pbkdf->randomKey(keySize / 8);
ASSERT_TRUE(key.valid()); ASSERT_TRUE(key.valid());
cipher->setKey(key); ASSERT_TRUE(cipher->setKey(key));
// Create some data to encrypt. // Create some data to encrypt.
int blockSize = cipher->blockSize(); int blockSize = cipher->blockSize();
SCOPED_TRACE(testing::Message() << "blockSize " << blockSize);
MemBlock mb; MemBlock mb;
mb.allocate(16 * blockSize); mb.allocate(16 * blockSize);

View File

@ -1,7 +1,16 @@
include_directories (${OPENSSL_INCLUDE_DIR})
link_directories (${Encfs_BINARY_DIR}/base) link_directories (${Encfs_BINARY_DIR}/base)
if (WITH_COMMON_CRYPTO)
find_library (SECURITY_FRAMEWORK Security)
mark_as_advanced (SECURITY_FRAMEWORK)
set (EXTRA_LIBS ${SECURITY_FRAMEWORK})
set (EXTRA_SOURCE CommonCrypto.cpp)
elseif (WITH_OPENSSL)
include_directories (${OPENSSL_INCLUDE_DIR})
set (EXTRA_LIBS ${OPENSSL_LIBRARIES})
set (EXTRA_SOURCE openssl.cpp)
endif (WITH_COMMON_CRYPTO)
add_library (encfs-cipher add_library (encfs-cipher
BlockCipher.cpp BlockCipher.cpp
CipherKey.cpp CipherKey.cpp
@ -9,14 +18,14 @@ add_library (encfs-cipher
MAC.cpp MAC.cpp
MemoryPool.cpp MemoryPool.cpp
NullCiphers.cpp NullCiphers.cpp
openssl.cpp
PBKDF.cpp PBKDF.cpp
readpassphrase.cpp readpassphrase.cpp
StreamCipher.cpp StreamCipher.cpp
${EXTRA_SOURCE}
) )
target_link_libraries (encfs-cipher target_link_libraries (encfs-cipher
${OPENSSL_LIBRARIES} ${EXTRA_LIBS}
) )
if (GTEST_FOUND) if (GTEST_FOUND)

45
cipher/CipherKey_test.cpp Normal file
View File

@ -0,0 +1,45 @@
#include <list>
#include <gtest/gtest.h>
#include "base/config.h"
#include "base/shared_ptr.h"
#include "cipher/CipherV1.h"
#include "cipher/MemoryPool.h"
#include "cipher/testing.h"
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
using namespace encfs;
using std::list;
using std::string;
namespace {
TEST(CipherKey, ReadWrite) {
for (auto alg : CipherV1::GetAlgorithmList()) {
auto cipher = CipherV1::New(alg.iface);
CipherKey masterKey = cipher->newRandomKey();
CipherKey volumeKey = cipher->newRandomKey();
int encodedSize = cipher->encodedKeySize();
unsigned char *keyBuf = new unsigned char[encodedSize];
cipher->setKey(masterKey);
cipher->writeKey(volumeKey, keyBuf);
CipherKey readKey = cipher->readKey(keyBuf, true);
ASSERT_TRUE(readKey.valid());
ASSERT_TRUE(readKey == volumeKey);
ASSERT_FALSE(readKey == masterKey);
delete[] keyBuf;
}
}
} // namespace

View File

@ -19,6 +19,7 @@
*/ */
#include "cipher/CipherV1.h" #include "cipher/CipherV1.h"
#include "base/config.h"
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
@ -28,18 +29,29 @@
#include <glog/logging.h> #include <glog/logging.h>
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
#include "base/base64.h" #include "base/base64.h"
#include "base/Error.h" #include "base/Error.h"
#include "base/i18n.h" #include "base/i18n.h"
#include "base/Mutex.h" #include "base/Mutex.h"
#include "base/Range.h" #include "base/Range.h"
#include "cipher/MemoryPool.h" #include "cipher/MemoryPool.h"
#include "cipher/MAC.h" #include "cipher/MAC.h"
#include "cipher/BlockCipher.h" #include "cipher/BlockCipher.h"
#include "cipher/PBKDF.h" #include "cipher/PBKDF.h"
#include "cipher/StreamCipher.h" #include "cipher/StreamCipher.h"
using namespace std; #ifdef WITH_OPENSSL
#include "cipher/openssl.h"
#endif
using std::list;
using std::string;
using std::vector;
namespace encfs { namespace encfs {
@ -54,6 +66,18 @@ inline int MIN(int a, int b)
} }
#endif #endif
void CipherV1::init(bool threaded) {
#ifdef WITH_OPENSSL
OpenSSL::init(threaded);
#endif
}
void CipherV1::shutdown(bool threaded) {
#ifdef WITH_OPENSSL
OpenSSL::shutdown(threaded);
#endif
}
/* /*
DEPRECATED: this is here for backward compatibilty only. Use PBKDF DEPRECATED: this is here for backward compatibilty only. Use PBKDF
@ -77,7 +101,7 @@ bool BytesToKey(const byte *data, int dataLen,
for(;;) for(;;)
{ {
sha1->reset(); sha1->init();
if( addmd++ ) if( addmd++ )
sha1->update(mdBuf.data, mdBuf.size); sha1->update(mdBuf.data, mdBuf.size);
sha1->update(data, dataLen); sha1->update(data, dataLen);
@ -85,7 +109,7 @@ bool BytesToKey(const byte *data, int dataLen,
for(unsigned int i=1; i < rounds; ++i) for(unsigned int i=1; i < rounds; ++i)
{ {
sha1->reset(); sha1->init();
sha1->update(mdBuf.data, mdBuf.size); sha1->update(mdBuf.data, mdBuf.size);
sha1->write(mdBuf.data); sha1->write(mdBuf.data);
} }
@ -115,6 +139,10 @@ int CipherV1::TimedPBKDF2(const char *pass, int passlen,
const byte *salt, int saltlen, const byte *salt, int saltlen,
CipherKey *key, long desiredPDFTime) CipherKey *key, long desiredPDFTime)
{ {
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_CHECK_MEM_IS_DEFINED(pass, passlen);
VALGRIND_CHECK_MEM_IS_DEFINED(salt, saltlen);
#endif
Registry<PBKDF> registry = PBKDF::GetRegistry(); Registry<PBKDF> registry = PBKDF::GetRegistry();
shared_ptr<PBKDF> impl(registry.CreateForMatch(NAME_PKCS5_PBKDF2_HMAC_SHA1)); shared_ptr<PBKDF> impl(registry.CreateForMatch(NAME_PKCS5_PBKDF2_HMAC_SHA1));
if (!impl) if (!impl)
@ -212,10 +240,17 @@ shared_ptr<CipherV1> CipherV1::New(const std::string& name, int keyLen) {
} }
shared_ptr<CipherV1> CipherV1::New(const Interface &iface, int keyLen) { shared_ptr<CipherV1> CipherV1::New(const Interface &iface, int keyLen) {
return shared_ptr<CipherV1>(new CipherV1(iface, iface, keyLen)); shared_ptr<CipherV1> result(new CipherV1());
if (!result->initCiphers(iface, iface, keyLen))
result.reset();
return result;
} }
CipherV1::CipherV1(const Interface &iface, const Interface &realIface, CipherV1::CipherV1()
{
}
bool CipherV1::initCiphers(const Interface &iface, const Interface &realIface,
int keyLength) int keyLength)
{ {
this->iface = iface; this->iface = iface;
@ -243,12 +278,11 @@ CipherV1::CipherV1(const Interface &iface, const Interface &realIface,
defaultKeyLength = 0; defaultKeyLength = 0;
_blockCipher.reset( blockCipherRegistry.CreateForMatch("NullCipher") ); _blockCipher.reset( blockCipherRegistry.CreateForMatch("NullCipher") );
_streamCipher.reset( streamCipherRegistry.CreateForMatch("NullCipher") ); _streamCipher.reset( streamCipherRegistry.CreateForMatch("NullCipher") );
} else {
throw Error("Unsupported cipher");
} }
if (!_blockCipher || !_streamCipher) { if (!_blockCipher || !_streamCipher) {
throw Error("Requested cipher not available"); LOG(INFO) << "Unsupported cipher " << iface.name();
return false;
} }
if (keyLength <= 0) if (keyLength <= 0)
@ -259,7 +293,8 @@ CipherV1::CipherV1(const Interface &iface, const Interface &realIface,
_pbkdf.reset(PBKDF::GetRegistry().CreateForMatch( _pbkdf.reset(PBKDF::GetRegistry().CreateForMatch(
NAME_PKCS5_PBKDF2_HMAC_SHA1)); NAME_PKCS5_PBKDF2_HMAC_SHA1));
if (!_pbkdf) { if (!_pbkdf) {
throw Error("PBKDF not available"); LOG(ERROR) << "PBKDF missing";
return false;
} }
// Initialize the cipher with a temporary key in order to determine the block // Initialize the cipher with a temporary key in order to determine the block
@ -272,8 +307,12 @@ CipherV1::CipherV1(const Interface &iface, const Interface &realIface,
Lock l(_hmacMutex); Lock l(_hmacMutex);
_hmac.reset(MAC::GetRegistry().CreateForMatch(NAME_SHA1_HMAC)); _hmac.reset(MAC::GetRegistry().CreateForMatch(NAME_SHA1_HMAC));
if (!_hmac) if (!_hmac) {
throw Error("SHA1_HMAC not available"); LOG(ERROR) << "SHA1_HMAC not available";
return false;
}
return true;
} }
CipherV1::~CipherV1() CipherV1::~CipherV1()
@ -296,6 +335,10 @@ CipherKey CipherV1::newKey(const char *password, int passwdLength,
int *iterationCount, long desiredDuration, int *iterationCount, long desiredDuration,
const byte *salt, int saltLen) const byte *salt, int saltLen)
{ {
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_CHECK_MEM_IS_DEFINED(password, passwdLength);
VALGRIND_CHECK_MEM_IS_DEFINED(salt, saltLen);
#endif
CipherKey key(_keySize + _ivLength); CipherKey key(_keySize + _ivLength);
if(*iterationCount == 0) if(*iterationCount == 0)
@ -329,6 +372,9 @@ CipherKey CipherV1::newKey(const char *password, int passwdLength,
// password is changed or configuration is rewritten. // password is changed or configuration is rewritten.
CipherKey CipherV1::newKey(const char *password, int passwdLength) CipherKey CipherV1::newKey(const char *password, int passwdLength)
{ {
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_CHECK_MEM_IS_DEFINED(password, passwdLength);
#endif
CipherKey key(_keySize + _ivLength); CipherKey key(_keySize + _ivLength);
bool ok = BytesToKey((byte *)password, passwdLength, 16, &key); bool ok = BytesToKey((byte *)password, passwdLength, 16, &key);
@ -376,8 +422,8 @@ uint64_t CipherV1::MAC_64(const byte *data, int len,
byte md[_hmac->outputSize()]; byte md[_hmac->outputSize()];
Lock l(_hmacMutex); Lock l(_hmacMutex);
_hmac->reset();
_hmac->init();
_hmac->update(data, len); _hmac->update(data, len);
if(chainedIV) if(chainedIV)
{ {
@ -398,8 +444,11 @@ uint64_t CipherV1::MAC_64(const byte *data, int len,
// chop this down to a 64bit value.. // chop this down to a 64bit value..
byte h[8] = {0,0,0,0,0,0,0,0}; byte h[8] = {0,0,0,0,0,0,0,0};
// TODO: outputSize - 1?
for(unsigned int i=0; i<_hmac->outputSize(); ++i) // XXX: the last byte off the hmac isn't used. This minor inconsistency
// must be maintained in order to maintain backward compatiblity with earlier
// releases.
for(int i=0; i<_hmac->outputSize()-1; ++i)
h[i%8] ^= (byte)(md[i]); h[i%8] ^= (byte)(md[i]);
uint64_t value = (uint64_t)h[0]; uint64_t value = (uint64_t)h[0];
@ -435,14 +484,27 @@ CipherKey CipherV1::readKey(const byte *data, bool checkKey)
checksum = (checksum << 8) | (unsigned int)data[i]; checksum = (checksum << 8) | (unsigned int)data[i];
memcpy( key.data(), data+KEY_CHECKSUM_BYTES, key.size() ); memcpy( key.data(), data+KEY_CHECKSUM_BYTES, key.size() );
streamDecode(key.data(), key.size(), checksum); if (!streamDecode(key.data(), key.size(), checksum)) {
LOG(ERROR) << "stream decode failure";
return CipherKey();
}
// check for success // check for success
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_CHECK_MEM_IS_DEFINED(key.data(), key.size());
#endif
unsigned int checksum2 = reduceMac32( unsigned int checksum2 = reduceMac32(
MAC_64( key.data(), key.size(), NULL )); MAC_64( key.data(), key.size(), NULL ));
if(checksum2 != checksum && checkKey)
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_CHECK_VALUE_IS_DEFINED(checksum2);
VALGRIND_CHECK_VALUE_IS_DEFINED(checksum);
#endif
if(checkKey && (checksum2 != checksum))
{ {
VLOG(1) << "checksum mismatch: expected " << checksum LOG(INFO) << "checksum mismatch: expected " << checksum
<< ", got " << checksum2 << ", got " << checksum2
<< "on decode of " << _keySize + _ivLength << " bytes"; << "on decode of " << _keySize + _ivLength << " bytes";
return CipherKey(); return CipherKey();
@ -454,23 +516,22 @@ CipherKey CipherV1::readKey(const byte *data, bool checkKey)
void CipherV1::writeKey(const CipherKey &ckey, byte *out) void CipherV1::writeKey(const CipherKey &ckey, byte *out)
{ {
rAssert( _keySet ); rAssert( _keySet );
rAssert(ckey.size() > KEY_CHECKSUM_BYTES);
SecureMem tmpBuf(ckey.size()); SecureMem tmpBuf(ckey.size());
memcpy(tmpBuf.data, ckey.data(), tmpBuf.size); memcpy(tmpBuf.data, ckey.data(), tmpBuf.size);
unsigned int checksum = reduceMac32( unsigned int checksum = reduceMac32(
MAC_64( tmpBuf.data, tmpBuf.size, NULL )); MAC_64(tmpBuf.data, tmpBuf.size, NULL));
streamEncode(tmpBuf.data, tmpBuf.size, checksum); streamEncode(tmpBuf.data, tmpBuf.size, checksum);
memcpy( out+KEY_CHECKSUM_BYTES, tmpBuf.data, tmpBuf.size );
// first N bytes contain HMAC derived checksum.. // first N bytes contain HMAC derived checksum..
for(int i=1; i<=KEY_CHECKSUM_BYTES; ++i) for(int i=1; i<=KEY_CHECKSUM_BYTES; ++i)
{ {
out[KEY_CHECKSUM_BYTES-i] = checksum & 0xff; out[KEY_CHECKSUM_BYTES-i] = checksum & 0xff;
checksum >>= 8; checksum >>= 8;
} }
memcpy( out+KEY_CHECKSUM_BYTES, tmpBuf.data, tmpBuf.size );
} }
std::string CipherV1::encodeAsString(const CipherKey &key) std::string CipherV1::encodeAsString(const CipherKey &key)
@ -558,7 +619,7 @@ void CipherV1::setIVec(byte *ivec, uint64_t seed) const
// combine ivec and seed with HMAC // combine ivec and seed with HMAC
Lock l(_hmacMutex); Lock l(_hmacMutex);
_hmac->reset(); _hmac->init();
_hmac->update(ivec, _ivLength); _hmac->update(ivec, _ivLength);
_hmac->update(md.data(), 8); _hmac->update(md.data(), 8);
_hmac->write(md.data()); _hmac->write(md.data());

View File

@ -102,6 +102,9 @@ class CipherV1
Range blockSize; Range blockSize;
}; };
static void init(bool threaded);
static void shutdown(bool threaded);
// Returns a list of supported algorithms. // Returns a list of supported algorithms.
static std::list<CipherAlgorithm> GetAlgorithmList(); static std::list<CipherAlgorithm> GetAlgorithmList();
static shared_ptr<CipherV1> New(const std::string &name, int keyLen = -1); static shared_ptr<CipherV1> New(const std::string &name, int keyLen = -1);
@ -114,9 +117,12 @@ class CipherV1
const byte *salt, int saltLen, const byte *salt, int saltLen,
CipherKey *out, long desiredPDFTimeMicroseconds); CipherKey *out, long desiredPDFTimeMicroseconds);
CipherV1(const Interface &iface, const Interface &realIface, int keyLength); CipherV1();
~CipherV1(); ~CipherV1();
bool initCiphers(const Interface &iface,
const Interface &realIface, int keyLength);
// returns the real interface, not the one we're emulating (if any).. // returns the real interface, not the one we're emulating (if any)..
Interface interface() const; Interface interface() const;

263
cipher/CommonCrypto.cpp Normal file
View File

@ -0,0 +1,263 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2013 Valient Gough
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cipher/CommonCrypto.h"
#include <glog/logging.h>
#include <CommonCrypto/CommonCryptor.h>
#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonHMAC.h>
#include <CommonCrypto/CommonKeyDerivation.h>
#include "base/config.h"
#include "cipher/BlockCipher.h"
#include "cipher/MAC.h"
#include "cipher/PBKDF.h"
#ifdef HAVE_SEC_RANDOM_H
#include <Security/SecRandom.h>
#endif
namespace encfs {
namespace commoncrypto {
class PbkdfPkcs5HmacSha1CC : public PBKDF {
public:
PbkdfPkcs5HmacSha1CC() {}
virtual ~PbkdfPkcs5HmacSha1CC() {}
virtual bool makeKey(const char *password, int passwordLength,
const byte *salt, int saltLength,
int numIterations,
CipherKey *outKey) {
int ret = CCKeyDerivationPBKDF(kCCPBKDF2, password, passwordLength,
salt, saltLength, kCCPRFHmacAlgSHA1,
numIterations,
outKey->data(), outKey->size());
if (ret != 0) {
PLOG(ERROR) << "CCKeyDerivationPBKDF failed";
return false;
}
return true;
}
virtual CipherKey randomKey(int length) {
CipherKey key(length);
if (length == 0) return key;
#ifdef HAVE_SEC_RANDOM_H
if (SecRandomCopyBytes(kSecRandomDefault, key.size(), key.data()) < 0) {
PLOG(ERROR) << "random key generation failure for length " << length;
key.reset();
}
#else
#error No random number generator provided.
#endif
return key;
}
virtual bool pseudoRandom(byte *out, int length) {
if (length == 0) return true;
#ifdef HAVE_SEC_RANDOM_H
if (SecRandomCopyBytes(kSecRandomDefault, length, out) < 0) {
PLOG(ERROR) << "random key generation failure for length " << length;
return false;
}
#else
#error No random number generator provided.
#endif
return true;
}
static Properties GetProperties() {
Properties props;
props.mode = NAME_PKCS5_PBKDF2_HMAC_SHA1;
props.library = "CommonCrypto";
return props;
}
};
REGISTER_CLASS(PbkdfPkcs5HmacSha1CC, PBKDF);
class CCCipher : public BlockCipher {
CipherKey key;
CCAlgorithm algorithm;
CCMode mode;
public:
CCCipher() { }
virtual ~CCCipher() { }
bool rekey(const CipherKey &key, CCAlgorithm algorithm, CCMode mode) {
this->key = key;
this->algorithm = algorithm;
this->mode = mode;
return true;
}
virtual bool encrypt(const byte *iv, const byte *in, byte *out, int size) {
CCCryptorRef cryptor;
CCCryptorCreateWithMode(kCCEncrypt, mode, algorithm, 0,
iv, key.data(), key.size(),
NULL, 0, 0, 0, &cryptor);
size_t updateLength = 0;
CCCryptorUpdate(cryptor, in, size, out, size, &updateLength);
CCCryptorRelease(cryptor);
return true;
}
virtual bool decrypt(const byte *iv, const byte *in, byte *out, int size) {
CCCryptorRef cryptor;
CCCryptorCreateWithMode(kCCDecrypt, mode, algorithm, 0,
iv, key.data(), key.size(),
NULL, 0, 0, 0, &cryptor);
size_t updateLength = 0;
CCCryptorUpdate(cryptor, in, size, out, size, &updateLength);
CCCryptorRelease(cryptor);
return true;
}
};
class BfCbc : public CCCipher {
public:
BfCbc() {}
virtual ~BfCbc() {}
virtual bool setKey(const CipherKey &key) {
return CCCipher::rekey(key, kCCAlgorithmBlowfish, kCCModeCBC);
}
virtual int blockSize() const {
return kCCBlockSizeBlowfish;
}
static Properties GetProperties() {
return Properties(Range(128,256,32), "Blowfish", "CBC", "CommonCrypto");
}
};
REGISTER_CLASS(BfCbc, BlockCipher);
class AesCbc : public CCCipher {
public:
AesCbc() {}
virtual ~AesCbc() {}
virtual bool setKey(const CipherKey &key) {
return CCCipher::rekey(key, kCCAlgorithmAES128, kCCModeCBC);
}
virtual int blockSize() const {
return kCCBlockSizeAES128;
}
static Properties GetProperties() {
return Properties(Range(128,256,64), "AES", "CBC", "CommonCrypto");
}
};
REGISTER_CLASS(AesCbc, BlockCipher);
class BfCfb : public CCCipher {
public:
BfCfb() {}
virtual ~BfCfb() {}
virtual bool setKey(const CipherKey &key) {
return CCCipher::rekey(key, kCCAlgorithmBlowfish, kCCModeCFB);
}
virtual int blockSize() const { return 1; }
static Properties GetProperties() {
return Properties(Range(128,256,32), "Blowfish", "CFB", "CommonCrypto");
}
};
REGISTER_CLASS(BfCfb, StreamCipher);
class AesCfb : public CCCipher {
public:
AesCfb() {}
virtual ~AesCfb() {}
virtual bool setKey(const CipherKey &key) {
return CCCipher::rekey(key, kCCAlgorithmAES128, kCCModeCFB);
}
virtual int blockSize() const { return 1; }
static Properties GetProperties() {
return Properties(Range(128,256,64), "AES", "CFB", "CommonCrypto");
}
};
REGISTER_CLASS(AesCfb, StreamCipher);
class Sha1HMac : public MAC {
public:
Sha1HMac() {}
virtual ~Sha1HMac() {}
virtual int outputSize() const {
return CC_SHA1_DIGEST_LENGTH;
}
virtual bool setKey(const CipherKey &key) {
this->key = key;
return true;
}
virtual void init() {
if (key.size() > 0) {
CCHmacInit(&ctx, kCCHmacAlgSHA1, key.data(), key.size());
} else {
// CommonCrypto will segfault later on if a null key is passed, even if
// key length is 0.
CCHmacInit(&ctx, kCCHmacAlgSHA1, &ctx, 0);
}
}
virtual bool update (const byte *in, int length) {
CCHmacUpdate(&ctx, in, length);
return true;
}
virtual bool write(byte *out) {
CCHmacFinal(&ctx, out);
return true;
}
static Properties GetProperties() {
Properties props;
props.blockSize = CC_SHA1_DIGEST_LENGTH;
props.hashFunction = "SHA-1";
props.mode = "HMAC";
props.library = "CommonCrypto";
return props;
}
private:
CipherKey key;
CCHmacContext ctx;
};
REGISTER_CLASS(Sha1HMac, MAC);
} // namespace commoncrypto
void CommonCrypto::registerCiphers() {
}
} // namespace encfs

35
cipher/CommonCrypto.h Normal file
View File

@ -0,0 +1,35 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2013 Valient Gough
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _COMMONCRYPTO_incl_
#define _COMMONCRYPTO_incl_
namespace encfs {
struct CommonCrypto {
static void registerCiphers();
};
} // namespace encfs
#endif

View File

@ -35,7 +35,8 @@ class MAC
virtual bool setKey(const CipherKey &key) =0; virtual bool setKey(const CipherKey &key) =0;
virtual void reset() =0; // Init must be called before any calls to update.
virtual void init() =0;
virtual bool update(const byte *in, int length) =0; virtual bool update(const byte *in, int length) =0;
virtual bool write(byte *out) =0; virtual bool write(byte *out) =0;
}; };

View File

@ -44,7 +44,7 @@ TEST(HMacSha1Test, MAC) {
for (int i = 0; i < 20; ++i) for (int i = 0; i < 20; ++i)
key.data()[i] = 0x0b; key.data()[i] = 0x0b;
hmac->setKey(key); hmac->setKey(key);
hmac->reset(); hmac->init();
hmac->update((byte *)"Hi There", 8); hmac->update((byte *)"Hi There", 8);
hmac->write(out); hmac->write(out);
ASSERT_EQ("b617318655057264e28bc0b6fb378c8ef146be00", stringToHex(out, 20)); ASSERT_EQ("b617318655057264e28bc0b6fb378c8ef146be00", stringToHex(out, 20));
@ -52,7 +52,7 @@ TEST(HMacSha1Test, MAC) {
// Test case 2 // Test case 2
key = CipherKey((const byte *)"Jefe", 4); key = CipherKey((const byte *)"Jefe", 4);
hmac->setKey(key); hmac->setKey(key);
hmac->reset(); hmac->init();
hmac->update((byte *)"what do ya want for nothing?", 28); hmac->update((byte *)"what do ya want for nothing?", 28);
hmac->write(out); hmac->write(out);
ASSERT_EQ("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79", stringToHex(out, 20)); ASSERT_EQ("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79", stringToHex(out, 20));
@ -62,7 +62,7 @@ TEST(HMacSha1Test, MAC) {
for (int i = 0; i < 20; ++i) for (int i = 0; i < 20; ++i)
key.data()[i] = 0xaa; key.data()[i] = 0xaa;
hmac->setKey(key); hmac->setKey(key);
hmac->reset(); hmac->init();
{ {
byte data[50]; byte data[50];
memset(data, 0xdd, 50); memset(data, 0xdd, 50);
@ -75,7 +75,7 @@ TEST(HMacSha1Test, MAC) {
key = CipherKey(80); key = CipherKey(80);
memset(key.data(), 0xaa, 80); memset(key.data(), 0xaa, 80);
hmac->setKey(key); hmac->setKey(key);
hmac->reset(); hmac->init();
hmac->update((byte *)"Test Using Larger Than Block-Size Key and Larger " hmac->update((byte *)"Test Using Larger Than Block-Size Key and Larger "
"Than One Block-Size Data", 73); "Than One Block-Size Data", 73);
hmac->write(out); hmac->write(out);

View File

@ -42,105 +42,69 @@
#include <map> #include <map>
#include <list> #include <list>
using namespace std; #ifdef WITH_OPENSSL
# include <openssl/crypto.h> # include <openssl/crypto.h>
# include <openssl/buffer.h> # include <openssl/buffer.h>
#endif
namespace encfs { namespace encfs {
static BUF_MEM *allocBlock( int size ) #ifdef WITH_OPENSSL
static byte *allocBlock( int size )
{ {
BUF_MEM *block = BUF_MEM_new( ); byte *block = (byte *)OPENSSL_malloc(size);
BUF_MEM_grow( block, size );
VALGRIND_MAKE_MEM_NOACCESS( block->data, block->max );
return block; return block;
} }
static void freeBlock( BUF_MEM *block ) static void freeBlock( byte *block, int size )
{ {
VALGRIND_MAKE_MEM_UNDEFINED( block->data, block->max ); OPENSSL_cleanse(block, size);
BUF_MEM_free( block ); OPENSSL_free(block);
}
#elif defined(WITH_COMMON_CRYPTO)
static byte *allocBlock(int size) {
byte *block = new byte[size];
return block;
} }
static pthread_mutex_t gMPoolMutex = PTHREAD_MUTEX_INITIALIZER; unsigned char cleanse_ctr = 0;
static void freeBlock(byte *data, int len) {
typedef std::map<int, std::list<BUF_MEM* > > FreeBlockMap; byte *p = data;
static FreeBlockMap gFreeBlocks; size_t loop = len, ctr = cleanse_ctr;
while(loop--)
{
*(p++) = (unsigned char)ctr;
ctr += (17 + ((size_t)p & 0xF));
}
// Try to ensure the compiler doesn't optimize away the loop.
p=(byte *)memchr(data, (unsigned char)ctr, len);
if(p)
ctr += (63 + (size_t)p);
cleanse_ctr = (unsigned char)ctr;
delete[] data;
}
#endif
void MemBlock::allocate(int size) void MemBlock::allocate(int size)
{ {
rAssert(size > 0); rAssert(size > 0);
pthread_mutex_lock( &gMPoolMutex ); this->data = allocBlock(size);
this->size = size;
list<BUF_MEM*> &freeList = gFreeBlocks[size];
BUF_MEM *mem;
if (!freeList.empty())
{
mem = freeList.front();
freeList.pop_front();
pthread_mutex_unlock( &gMPoolMutex );
} else
{
pthread_mutex_unlock( &gMPoolMutex );
mem = allocBlock( size );
}
internalData = mem;
data = reinterpret_cast<byte *>(mem->data);
VALGRIND_MAKE_MEM_UNDEFINED( data, size );
} }
MemBlock::~MemBlock() MemBlock::~MemBlock()
{ {
BUF_MEM *block = (BUF_MEM*)internalData; freeBlock(data, size);
data = NULL;
internalData = NULL;
if (block)
{
// wipe the buffer..
VALGRIND_MAKE_MEM_UNDEFINED( block->data, block->max );
memset( block->data , 0, block->max);
VALGRIND_MAKE_MEM_NOACCESS( block->data, block->max );
pthread_mutex_lock( &gMPoolMutex );
gFreeBlocks[ block->max ].push_front(block);
pthread_mutex_unlock( &gMPoolMutex );
}
}
void MemoryPool::destroyAll()
{
pthread_mutex_lock( &gMPoolMutex );
for (FreeBlockMap::const_iterator it = gFreeBlocks.begin();
it != gFreeBlocks.end(); it++)
{
for (list<BUF_MEM*>::const_iterator bIt = it->second.begin();
bIt != it->second.end(); bIt++)
{
freeBlock( *bIt );
}
}
gFreeBlocks.clear();
pthread_mutex_unlock( &gMPoolMutex );
} }
SecureMem::SecureMem(int len) SecureMem::SecureMem(int len)
{ {
rAssert(len > 0); rAssert(len > 0);
data = (byte *)OPENSSL_malloc(len); data = allocBlock(len);
if (data) if (data)
{ {
size = len; size = len;
mlock(data, size); mlock(data, size);
memset(data, '\0', size);
VALGRIND_MAKE_MEM_UNDEFINED( data, size );
} else } else
{ {
size = 0; size = 0;
@ -151,17 +115,18 @@ SecureMem::~SecureMem()
{ {
if (size) if (size)
{ {
memset(data, '\0', size); freeBlock(data, size);
OPENSSL_cleanse(data, size);
munlock(data, size); munlock(data, size);
OPENSSL_free(data);
VALGRIND_MAKE_MEM_NOACCESS( data, size );
data = NULL; data = NULL;
size = 0; size = 0;
} }
} }
bool operator == (const SecureMem &a, const SecureMem &b) {
return (a.size == b.size) &&
(memcmp(a.data, b.data, a.size) == 0);
}
} // namespace encfs } // namespace encfs

View File

@ -39,7 +39,7 @@ namespace encfs {
struct MemBlock struct MemBlock
{ {
byte *data; byte *data;
void *internalData; int size;
MemBlock(); MemBlock();
~MemBlock(); ~MemBlock();
@ -48,15 +48,10 @@ struct MemBlock
}; };
inline MemBlock::MemBlock() inline MemBlock::MemBlock()
: data(0), internalData(0) : data(0), size(0)
{ {
} }
namespace MemoryPool
{
void destroyAll();
}
class SecureMem class SecureMem
{ {
public: public:
@ -67,6 +62,8 @@ class SecureMem
~SecureMem(); ~SecureMem();
}; };
bool operator == (const SecureMem &a, const SecureMem &b);
} // namespace encfs } // namespace encfs
#endif #endif

View File

@ -46,6 +46,13 @@ class StreamCipher
std::string toString() const { std::string toString() const {
return cipher + "/" + mode; return cipher + "/" + mode;
} }
Properties() {}
Properties(Range keys, const char *cipher_, const char *mode_,
const char *library_)
: keySize(keys),
cipher(cipher_),
mode(mode_),
library(library_) { }
}; };
StreamCipher(); StreamCipher();
@ -53,9 +60,9 @@ class StreamCipher
virtual bool setKey(const CipherKey& key) =0; virtual bool setKey(const CipherKey& key) =0;
virtual bool encrypt(const byte *iv, const byte *in, virtual bool encrypt(const byte *ivec, const byte *in,
byte *out, int numBytes) =0; byte *out, int numBytes) =0;
virtual bool decrypt(const byte *iv, const byte *in, virtual bool decrypt(const byte *ivec, const byte *in,
byte *out, int numBytes) =0; byte *out, int numBytes) =0;
}; };

View File

@ -19,6 +19,7 @@
*/ */
#include "cipher/openssl.h" #include "cipher/openssl.h"
#include "base/config.h"
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
@ -28,6 +29,10 @@
#include <glog/logging.h> #include <glog/logging.h>
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
#include "base/config.h" #include "base/config.h"
#define NO_DES #define NO_DES
@ -54,8 +59,6 @@
#include "cipher/PBKDF.h" #include "cipher/PBKDF.h"
#include "cipher/StreamCipher.h" #include "cipher/StreamCipher.h"
using namespace std;
namespace encfs { namespace encfs {
const int MAX_KEYLENGTH = 64; // in bytes (256 bit) const int MAX_KEYLENGTH = 64; // in bytes (256 bit)
@ -108,10 +111,18 @@ class OpenSSLCipher : public BlockCipher {
return false; return false;
} }
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(key->data(), key->size());
#endif
return true; return true;
} }
static bool pseudoRandomize(byte *out, int length) { static bool pseudoRandomize(byte *out, int length) {
#ifdef HAVE_VALGRIND_MEMCHECK_H
if (VALGRIND_CHECK_MEM_IS_ADDRESSABLE(out, length) != 0) {
return false;
}
#endif
int result = RAND_pseudo_bytes( out, length ); int result = RAND_pseudo_bytes( out, length );
if(result != 1) if(result != 1)
{ {
@ -136,12 +147,27 @@ class OpenSSLCipher : public BlockCipher {
} }
virtual int blockSize() const { virtual int blockSize() const {
return EVP_CIPHER_CTX_block_size(&enc); int len = EVP_CIPHER_CTX_block_size(&enc);
// Some versions of OpenSSL report 1 for block size os AES_XTS..
if (len == 1)
len = EVP_CIPHER_CTX_key_length(&enc);
return len;
} }
virtual bool encrypt(const byte *ivec, const byte *in, virtual bool encrypt(const byte *ivec, const byte *in,
byte *out, int size) { byte *out, int size) {
int dstLen = 0, tmpLen = 0; int dstLen = 0, tmpLen = 0;
#ifdef HAVE_VALGRIND_MEMCHECK_H
int ivLen = EVP_CIPHER_CTX_iv_length(&enc);
if (VALGRIND_CHECK_MEM_IS_DEFINED(ivec, ivLen) != 0) {
LOG(ERROR) << "expected iv of length " << ivLen;
return false;
}
if (VALGRIND_CHECK_MEM_IS_ADDRESSABLE(out, size) != 0) {
LOG(ERROR) << "expected output length " << size;
return false;
}
#endif
EVP_EncryptInit_ex( &enc, NULL, NULL, NULL, ivec); EVP_EncryptInit_ex( &enc, NULL, NULL, NULL, ivec);
EVP_EncryptUpdate( &enc, out, &dstLen, in, size); EVP_EncryptUpdate( &enc, out, &dstLen, in, size);
EVP_EncryptFinal_ex( &enc, out+dstLen, &tmpLen ); EVP_EncryptFinal_ex( &enc, out+dstLen, &tmpLen );
@ -159,6 +185,17 @@ class OpenSSLCipher : public BlockCipher {
virtual bool decrypt(const byte *ivec, const byte *in, virtual bool decrypt(const byte *ivec, const byte *in,
byte *out, int size) { byte *out, int size) {
int dstLen = 0, tmpLen = 0; int dstLen = 0, tmpLen = 0;
#ifdef HAVE_VALGRIND_MEMCHECK_H
int ivLen = EVP_CIPHER_CTX_iv_length(&enc);
if (VALGRIND_CHECK_MEM_IS_DEFINED(ivec, ivLen) != 0) {
LOG(ERROR) << "expected iv of length " << ivLen;
return false;
}
if (VALGRIND_CHECK_MEM_IS_ADDRESSABLE(out, size) != 0) {
LOG(ERROR) << "expected output length " << size;
return false;
}
#endif
EVP_DecryptInit_ex( &dec, NULL, NULL, NULL, ivec); EVP_DecryptInit_ex( &dec, NULL, NULL, NULL, ivec);
EVP_DecryptUpdate( &dec, out, &dstLen, in, size ); EVP_DecryptUpdate( &dec, out, &dstLen, in, size );
EVP_DecryptFinal_ex( &dec, out+dstLen, &tmpLen ); EVP_DecryptFinal_ex( &dec, out+dstLen, &tmpLen );
@ -346,7 +383,7 @@ class Sha1HMac : public MAC {
return true; return true;
} }
virtual void reset() { virtual void init() {
HMAC_Init_ex(&ctx, 0, 0, 0, 0); HMAC_Init_ex(&ctx, 0, 0, 0, 0);
} }
@ -364,6 +401,9 @@ class Sha1HMac : public MAC {
unsigned int outSize = 0; unsigned int outSize = 0;
HMAC_Final(&ctx, (unsigned char *)out, &outSize); HMAC_Final(&ctx, (unsigned char *)out, &outSize);
CHECK_EQ(outputSize(), outSize) << "Invalid HMAC output size"; CHECK_EQ(outputSize(), outSize) << "Invalid HMAC output size";
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(out, outSize);
#endif
return true; return true;
} }

View File

@ -1,8 +1,14 @@
#include <glog/logging.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "base/config.h"
#include "cipher/testing.h" #include "cipher/testing.h"
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
namespace encfs { namespace encfs {
static const char hexLut[] = "0123456789abcdef"; static const char hexLut[] = "0123456789abcdef";
@ -22,6 +28,9 @@ std::string stringToHex(const byte *data, int len) {
} }
void setDataFromHex(byte *out, int len, const char *hex) { void setDataFromHex(byte *out, int len, const char *hex) {
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_CHECK_MEM_IS_ADDRESSABLE(out, len);
#endif
bool odd = false; bool odd = false;
unsigned int last = 0; unsigned int last = 0;
while (len > 0 && *hex != '\0') { while (len > 0 && *hex != '\0') {
@ -49,6 +58,7 @@ void setDataFromHex(byte *out, int len, const char *hex) {
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
google::InitGoogleLogging(argv[0]);
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
} }

View File

@ -40,8 +40,7 @@
#include "base/Interface.h" #include "base/Interface.h"
#include "base/i18n.h" #include "base/i18n.h"
#include "cipher/MemoryPool.h" #include "cipher/CipherV1.h"
#include "cipher/openssl.h"
#include "fs/FileUtils.h" #include "fs/FileUtils.h"
#include "fs/DirNode.h" #include "fs/DirNode.h"
@ -61,9 +60,12 @@ inline static int MAX(int a, int b)
} }
#endif #endif
using namespace std;
using namespace gnu;
using namespace encfs; using namespace encfs;
using gnu::autosprintf;
using std::cerr;
using std::endl;
using std::string;
using std::ostringstream;
namespace encfs { namespace encfs {
@ -580,7 +582,7 @@ int main(int argc, char *argv[])
// encfs_oper.fsetattr_x // encfs_oper.fsetattr_x
#endif #endif
OpenSSL::init( encfsArgs->isThreaded ); CipherV1::init( encfsArgs->isThreaded );
// context is not a smart pointer because it will live for the life of // context is not a smart pointer because it will live for the life of
// the filesystem. // the filesystem.
@ -672,8 +674,7 @@ int main(int argc, char *argv[])
rootInfo.reset(); rootInfo.reset();
ctx->setRoot( shared_ptr<DirNode>() ); ctx->setRoot( shared_ptr<DirNode>() );
MemoryPool::destroyAll(); CipherV1::shutdown( encfsArgs->isThreaded );
OpenSSL::shutdown( encfsArgs->isThreaded );
return returnCode; return returnCode;
} }

View File

@ -24,6 +24,16 @@ target_link_libraries (encfs-fs
${PROTOBUF_LIBRARY} ${PROTOBUF_LIBRARY}
) )
add_executable (checkops
checkops.cpp
)
target_link_libraries (checkops
encfs-fs
encfs-cipher
encfs-base
${GLOG_LIBRARIES}
)
# Unit tests are optional, depends on libgtest (Google's C++ test framework). # Unit tests are optional, depends on libgtest (Google's C++ test framework).
if (GTEST_FOUND) if (GTEST_FOUND)
link_directories (${PROJECT_BINARY_DIR}/base) link_directories (${PROJECT_BINARY_DIR}/base)

View File

@ -289,7 +289,6 @@ ssize_t CipherFileIO::readOneBlock( const IORequest &req ) const
IORequest tmpReq = req; IORequest tmpReq = req;
MemBlock mb; MemBlock mb;
if (headerLen != 0)
tmpReq.offset += headerLen; tmpReq.offset += headerLen;
int maxReadSize = req.dataLen; int maxReadSize = req.dataLen;

View File

@ -84,8 +84,6 @@ private:
// if haveHeader is true, then we have a transparent file header which // if haveHeader is true, then we have a transparent file header which
int headerLen; int headerLen;
// Use block only encryption, no stream encryption.
bool blockOnlyMode;
bool perFileIV; bool perFileIV;
bool externalIVChaining; bool externalIVChaining;

View File

@ -26,6 +26,7 @@
#include "base/Mutex.h" #include "base/Mutex.h"
#include <set> #include <set>
#include <string>
#ifdef HAVE_TR1_UNORDERED_MAP #ifdef HAVE_TR1_UNORDERED_MAP
#include <tr1/unordered_map> #include <tr1/unordered_map>

View File

@ -44,7 +44,8 @@
#include <iostream> #include <iostream>
using namespace std; using std::list;
using std::string;
namespace encfs { namespace encfs {

View File

@ -75,6 +75,11 @@ struct FSConfig
bool reverseEncryption; // reverse encryption operation bool reverseEncryption; // reverse encryption operation
bool idleTracking; // turn on idle monitoring of filesystem bool idleTracking; // turn on idle monitoring of filesystem
FSConfig()
: forceDecode(false),
reverseEncryption(false),
idleTracking(false) { }
}; };
typedef shared_ptr<FSConfig> FSConfigPtr; typedef shared_ptr<FSConfig> FSConfigPtr;

View File

@ -49,7 +49,7 @@
#include <glog/logging.h> #include <glog/logging.h>
using namespace std; using std::string;
namespace encfs { namespace encfs {

View File

@ -66,8 +66,12 @@
#include <google/protobuf/text_format.h> #include <google/protobuf/text_format.h>
#include <google/protobuf/io/zero_copy_stream_impl.h> #include <google/protobuf/io/zero_copy_stream_impl.h>
using namespace std; using gnu::autosprintf;
using namespace gnu; using std::cout;
using std::cerr;
using std::endl;
using std::map;
using std::string;
namespace encfs { namespace encfs {
@ -1186,6 +1190,11 @@ void showFSInfo( const EncfsConfig &config )
cout << "\n"; cout << "\n";
} else } else
{ {
// Set a null key - the cipher won't work, but it will at least know the
// key size and blocksize.
CipherKey tmpKey(config.key().size());
cipher->setKey(tmpKey);
// check if we support the filename encoding interface.. // check if we support the filename encoding interface..
shared_ptr<NameIO> nameCoder = NameIO::New( config.naming(), cipher ); shared_ptr<NameIO> nameCoder = NameIO::New( config.naming(), cipher );
if(!nameCoder) if(!nameCoder)

View File

@ -30,8 +30,6 @@
#include <cstring> #include <cstring>
using namespace std;
namespace encfs { namespace encfs {
// //

View File

@ -34,7 +34,11 @@
#include "fs/StreamNameIO.h" #include "fs/StreamNameIO.h"
#include "fs/NullNameIO.h" #include "fs/NullNameIO.h"
using namespace std; using std::cerr;
using std::list;
using std::make_pair;
using std::multimap;
using std::string;
namespace encfs { namespace encfs {

View File

@ -35,8 +35,6 @@
#include <cerrno> #include <cerrno>
using namespace std;
namespace encfs { namespace encfs {
static Interface RawFileIO_iface = makeInterface("FileIO/Raw", 1, 0, 0); static Interface RawFileIO_iface = makeInterface("FileIO/Raw", 1, 0, 0);

View File

@ -28,8 +28,6 @@
#include <cstring> #include <cstring>
using namespace std;
namespace encfs { namespace encfs {
static shared_ptr<NameIO> NewStreamNameIO( const Interface &iface, static shared_ptr<NameIO> NewStreamNameIO( const Interface &iface,

View File

@ -26,7 +26,7 @@
#include "base/config.h" #include "base/config.h"
#include "base/Interface.h" #include "base/Interface.h"
#include "base/Error.h" #include "base/Error.h"
#include "cipher/Cipher.h" #include "cipher/CipherV1.h"
#include "cipher/MemoryPool.h" #include "cipher/MemoryPool.h"
#include "fs/DirNode.h" #include "fs/DirNode.h"
#include "fs/FileUtils.h" #include "fs/FileUtils.h"
@ -46,7 +46,7 @@
#include <google/protobuf/text_format.h> #include <google/protobuf/text_format.h>
#if HAVE_TR1_UNORDERED_SET #ifdef HAVE_TR1_UNORDERED_SET
#include <tr1/unordered_set> #include <tr1/unordered_set>
using std::tr1::unordered_set; using std::tr1::unordered_set;
#else #else
@ -54,15 +54,17 @@ using std::tr1::unordered_set;
using std::unordered_set; using std::unordered_set;
#endif #endif
using namespace std; using std::cerr;
using std::string;
using namespace encfs;
namespace encfs { namespace encfs {
const int FSBlockSize = 256; const int FSBlockSize = 256;
static static
int checkErrorPropogation( const shared_ptr<Cipher> &cipher, int checkErrorPropogation( const shared_ptr<CipherV1> &cipher,
int size, int byteToChange, const CipherKey &key ) int size, int byteToChange )
{ {
MemBlock orig; MemBlock orig;
orig.allocate(size); orig.allocate(size);
@ -77,9 +79,9 @@ int checkErrorPropogation( const shared_ptr<Cipher> &cipher,
} }
if(size != FSBlockSize) if(size != FSBlockSize)
cipher->streamEncode( data.data, size, 0, key ); cipher->streamEncode( data.data, size, 0 );
else else
cipher->blockEncode( data.data, size, 0, key ); cipher->blockEncode( data.data, size, 0 );
// intoduce an error in the encoded data, so we can check error propogation // intoduce an error in the encoded data, so we can check error propogation
if(byteToChange >= 0 && byteToChange < size) if(byteToChange >= 0 && byteToChange < size)
@ -92,9 +94,9 @@ int checkErrorPropogation( const shared_ptr<Cipher> &cipher,
} }
if(size != FSBlockSize) if(size != FSBlockSize)
cipher->streamDecode( data.data, size, 0, key ); cipher->streamDecode( data.data, size, 0 );
else else
cipher->blockDecode( data.data, size, 0, key ); cipher->blockDecode( data.data, size, 0 );
int numByteErrors = 0; int numByteErrors = 0;
for(int i=0; i<size; ++i) for(int i=0; i<size; ++i)
@ -191,7 +193,7 @@ bool testNameCoding( DirNode &dirNode, bool verbose,
return true; return true;
} }
bool runTests(const shared_ptr<Cipher> &cipher, bool verbose) bool runTests(const shared_ptr<CipherV1> &cipher, bool verbose)
{ {
// create a random key // create a random key
if(verbose) if(verbose)
@ -205,16 +207,17 @@ bool runTests(const shared_ptr<Cipher> &cipher, bool verbose)
int encodedKeySize = cipher->encodedKeySize(); int encodedKeySize = cipher->encodedKeySize();
unsigned char *keyBuf = new unsigned char [ encodedKeySize ]; unsigned char *keyBuf = new unsigned char [ encodedKeySize ];
cipher->writeKey( key, keyBuf, encodingKey ); cipher->setKey(encodingKey);
CipherKey key2 = cipher->readKey( keyBuf, encodingKey ); cipher->writeKey( key, keyBuf );
if(!key2) CipherKey key2 = cipher->readKey( keyBuf, true );
if(!key2.valid())
{ {
if(verbose) if(verbose)
cerr << " FAILED (decode error)\n"; cerr << " FAILED (decode error)\n";
return false; return false;
} }
if(cipher->compareKey( key, key2 )) if(key == key2)
{ {
if(verbose) if(verbose)
cerr << " OK\n"; cerr << " OK\n";
@ -233,7 +236,8 @@ bool runTests(const shared_ptr<Cipher> &cipher, bool verbose)
int encodedKeySize = cipher->encodedKeySize(); int encodedKeySize = cipher->encodedKeySize();
unsigned char *keyBuf = new unsigned char [ encodedKeySize ]; unsigned char *keyBuf = new unsigned char [ encodedKeySize ];
cipher->writeKey( key, keyBuf, encodingKey ); cipher->setKey(encodingKey);
cipher->writeKey( key, keyBuf );
// store in config struct.. // store in config struct..
EncfsConfig cfg; EncfsConfig cfg;
@ -257,16 +261,15 @@ bool runTests(const shared_ptr<Cipher> &cipher, bool verbose)
rAssert( cfg.block_size() == cfg2.block_size() ); rAssert( cfg.block_size() == cfg2.block_size() );
// try decoding key.. // try decoding key..
CipherKey key2 = cipher->readKey( (unsigned char *)cfg2.key().ciphertext().data(), true );
CipherKey key2 = cipher->readKey( (unsigned char *)cfg2.key().ciphertext().data(), encodingKey ); if(!key2.valid())
if(!key2)
{ {
if(verbose) if(verbose)
cerr << " FAILED (decode error)\n"; cerr << " FAILED (decode error)\n";
return false; return false;
} }
if(cipher->compareKey( key, key2 )) if(key == key2)
{ {
if(verbose) if(verbose)
cerr << " OK\n"; cerr << " OK\n";
@ -285,15 +288,15 @@ bool runTests(const shared_ptr<Cipher> &cipher, bool verbose)
fsCfg->config->set_block_size(FSBlockSize); fsCfg->config->set_block_size(FSBlockSize);
fsCfg->opts.reset(new EncFS_Opts); fsCfg->opts.reset(new EncFS_Opts);
cipher->setKey(key);
if(verbose) if(verbose)
cerr << "Testing name encode/decode (stream coding w/ IV chaining)\n"; cerr << "Testing name encode/decode (stream coding w/ IV chaining)\n";
if (cipher->hasStreamMode())
{ {
fsCfg->opts->idleTracking = false; fsCfg->opts->idleTracking = false;
fsCfg->config->set_unique_iv(false); fsCfg->config->set_unique_iv(false);
fsCfg->nameCoding.reset( new StreamNameIO( fsCfg->nameCoding.reset( new StreamNameIO(
StreamNameIO::CurrentInterface(), cipher, key ) ); StreamNameIO::CurrentInterface(), cipher) );
fsCfg->nameCoding->setChainedNameIV( true ); fsCfg->nameCoding->setChainedNameIV( true );
DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg );
@ -308,7 +311,7 @@ bool runTests(const shared_ptr<Cipher> &cipher, bool verbose)
fsCfg->opts->idleTracking = false; fsCfg->opts->idleTracking = false;
fsCfg->config->set_unique_iv(false); fsCfg->config->set_unique_iv(false);
fsCfg->nameCoding.reset( new BlockNameIO( fsCfg->nameCoding.reset( new BlockNameIO(
BlockNameIO::CurrentInterface(), cipher, key ) ); BlockNameIO::CurrentInterface(), cipher) );
fsCfg->nameCoding->setChainedNameIV( true ); fsCfg->nameCoding->setChainedNameIV( true );
DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg );
@ -323,7 +326,7 @@ bool runTests(const shared_ptr<Cipher> &cipher, bool verbose)
fsCfg->opts->idleTracking = false; fsCfg->opts->idleTracking = false;
fsCfg->config->set_unique_iv(false); fsCfg->config->set_unique_iv(false);
fsCfg->nameCoding.reset( new BlockNameIO( fsCfg->nameCoding.reset( new BlockNameIO(
BlockNameIO::CurrentInterface(), cipher, key, true ) ); BlockNameIO::CurrentInterface(), cipher) );
fsCfg->nameCoding->setChainedNameIV( true ); fsCfg->nameCoding->setChainedNameIV( true );
DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg );
@ -334,12 +337,11 @@ bool runTests(const shared_ptr<Cipher> &cipher, bool verbose)
if(!verbose) if(!verbose)
{ {
if (cipher->hasStreamMode())
{ {
// test stream mode, this time without IV chaining // test stream mode, this time without IV chaining
fsCfg->nameCoding = fsCfg->nameCoding =
shared_ptr<NameIO>( new StreamNameIO( shared_ptr<NameIO>( new StreamNameIO(
StreamNameIO::CurrentInterface(), cipher, key ) ); StreamNameIO::CurrentInterface(), cipher) );
fsCfg->nameCoding->setChainedNameIV( false ); fsCfg->nameCoding->setChainedNameIV( false );
DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg );
@ -351,7 +353,7 @@ bool runTests(const shared_ptr<Cipher> &cipher, bool verbose)
{ {
// test block mode, this time without IV chaining // test block mode, this time without IV chaining
fsCfg->nameCoding = shared_ptr<NameIO>( new BlockNameIO( fsCfg->nameCoding = shared_ptr<NameIO>( new BlockNameIO(
BlockNameIO::CurrentInterface(), cipher, key ) ); BlockNameIO::CurrentInterface(), cipher) );
fsCfg->nameCoding->setChainedNameIV( false ); fsCfg->nameCoding->setChainedNameIV( false );
DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg );
@ -365,7 +367,7 @@ bool runTests(const shared_ptr<Cipher> &cipher, bool verbose)
cerr << "Testing block encode/decode on full block - "; cerr << "Testing block encode/decode on full block - ";
{ {
int numErrors = checkErrorPropogation( cipher, int numErrors = checkErrorPropogation( cipher,
FSBlockSize, -1, key ); FSBlockSize, -1 );
if(numErrors) if(numErrors)
{ {
if(verbose) if(verbose)
@ -379,10 +381,9 @@ bool runTests(const shared_ptr<Cipher> &cipher, bool verbose)
} }
if(verbose) if(verbose)
cerr << "Testing block encode/decode on partial block - "; cerr << "Testing block encode/decode on partial block - ";
if (cipher->hasStreamMode())
{ {
int numErrors = checkErrorPropogation( cipher, int numErrors = checkErrorPropogation( cipher,
FSBlockSize-1, -1, key ); FSBlockSize-1, -1 );
if(numErrors) if(numErrors)
{ {
if(verbose) if(verbose)
@ -397,7 +398,6 @@ bool runTests(const shared_ptr<Cipher> &cipher, bool verbose)
if(verbose) if(verbose)
cerr << "Checking error propogation in partial block:\n"; cerr << "Checking error propogation in partial block:\n";
if (cipher->hasStreamMode())
{ {
int minChanges = FSBlockSize-1; int minChanges = FSBlockSize-1;
int maxChanges = 0; int maxChanges = 0;
@ -406,7 +406,7 @@ bool runTests(const shared_ptr<Cipher> &cipher, bool verbose)
for(int i=0; i<FSBlockSize-1; ++i) for(int i=0; i<FSBlockSize-1; ++i)
{ {
int numErrors = checkErrorPropogation( cipher, int numErrors = checkErrorPropogation( cipher,
FSBlockSize-1, i, key ); FSBlockSize-1, i );
if(numErrors < minChanges) if(numErrors < minChanges)
{ {
@ -438,7 +438,7 @@ bool runTests(const shared_ptr<Cipher> &cipher, bool verbose)
for(int i=0; i<FSBlockSize; ++i) for(int i=0; i<FSBlockSize; ++i)
{ {
int numErrors = checkErrorPropogation( cipher, int numErrors = checkErrorPropogation( cipher,
FSBlockSize, i, key ); FSBlockSize, i );
if(numErrors < minChanges) if(numErrors < minChanges)
{ {
@ -464,6 +464,7 @@ bool runTests(const shared_ptr<Cipher> &cipher, bool verbose)
return true; return true;
} }
} // namespace encfs
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
@ -488,9 +489,9 @@ int main(int argc, char *argv[])
srand( time(0) ); srand( time(0) );
// get a list of the available algorithms // get a list of the available algorithms
std::list<Cipher::CipherAlgorithm> algorithms = std::list<CipherV1::CipherAlgorithm> algorithms =
Cipher::GetAlgorithmList(); CipherV1::GetAlgorithmList();
std::list<Cipher::CipherAlgorithm>::const_iterator it; std::list<CipherV1::CipherAlgorithm>::const_iterator it;
cerr << "Supported Crypto interfaces:\n"; cerr << "Supported Crypto interfaces:\n";
for(it = algorithms.begin(); it != algorithms.end(); ++it) for(it = algorithms.begin(); it != algorithms.end(); ++it)
{ {
@ -515,7 +516,7 @@ int main(int argc, char *argv[])
cerr << it->name << ", key length " << keySize cerr << it->name << ", key length " << keySize
<< ", block size " << blockSize << ": "; << ", block size " << blockSize << ": ";
shared_ptr<Cipher> cipher = Cipher::New( it->name, keySize ); shared_ptr<CipherV1> cipher = CipherV1::New( it->name, keySize );
if(!cipher) if(!cipher)
{ {
cerr << "FAILED TO CREATE\n"; cerr << "FAILED TO CREATE\n";
@ -536,7 +537,7 @@ int main(int argc, char *argv[])
} }
// run one test with verbose output too.. // run one test with verbose output too..
shared_ptr<Cipher> cipher = Cipher::New("AES", 192); shared_ptr<CipherV1> cipher = CipherV1::New("AES", 192);
if(!cipher) if(!cipher)
{ {
cerr << "\nNo AES cipher found, skipping verbose test.\n"; cerr << "\nNo AES cipher found, skipping verbose test.\n";
@ -549,10 +550,6 @@ int main(int argc, char *argv[])
runTests( cipher, true ); runTests( cipher, true );
} }
MemoryPool::destroyAll();
return 0; return 0;
} }
} // namespace encfs

View File

@ -43,11 +43,14 @@
#ifdef HAVE_TR1_TUPLE #ifdef HAVE_TR1_TUPLE
#include <tr1/tuple> #include <tr1/tuple>
using namespace std; using std::tr1::get;
using namespace std::tr1; using std::tr1::make_tuple;
using std::tr1::tuple;
#else #else
#include <tuple> #include <tuple>
using namespace std; using std::get;
using std::make_tuple;
using std::tuple;
#endif #endif
#include "base/config.h" #include "base/config.h"
@ -61,6 +64,10 @@ using namespace std;
#include <glog/logging.h> #include <glog/logging.h>
using std::map;
using std::string;
using std::vector;
namespace encfs { namespace encfs {
#ifndef MIN #ifndef MIN

View File

@ -76,6 +76,38 @@ TEST(IOTest, MacIO) {
runWithAllCiphers(testMacIO); runWithAllCiphers(testMacIO);
} }
void testBasicCipherIO(FSConfigPtr& cfg) {
shared_ptr<MemFileIO> base(new MemFileIO(0));
shared_ptr<CipherFileIO> test(new CipherFileIO(base, cfg));
byte buf[1024];
cfg->cipher->pseudoRandomize(buf, sizeof(buf));
IORequest req;
req.data = new byte[sizeof(buf)];
req.offset = 0;
req.dataLen = sizeof(buf);
memcpy(req.data, buf, sizeof(buf));
ASSERT_TRUE(test->write(req));
memset(req.data, 0, sizeof(buf));
ASSERT_EQ(req.dataLen, test->read(req));
for (unsigned int i = 0; i < sizeof(buf); ++i) {
bool match = (buf[i] == req.data[i]);
ASSERT_TRUE(match) << "mismatched data at offset " << i;
if (!match)
break;
}
delete[] req.data;
}
TEST(IOTest, BasicCipherFileIO) {
runWithAllCiphers(testBasicCipherIO);
}
void testCipherIO(FSConfigPtr& cfg) { void testCipherIO(FSConfigPtr& cfg) {
shared_ptr<MemFileIO> base(new MemFileIO(0)); shared_ptr<MemFileIO> base(new MemFileIO(0));
shared_ptr<CipherFileIO> test(new CipherFileIO(base, cfg)); shared_ptr<CipherFileIO> test(new CipherFileIO(base, cfg));

View File

@ -36,7 +36,8 @@
#include "fs/MACFileIO.h" #include "fs/MACFileIO.h"
#include "fs/MemFileIO.h" #include "fs/MemFileIO.h"
using namespace std; using std::list;
using std::string;
namespace encfs { namespace encfs {
@ -92,8 +93,13 @@ void truncate(FileIO* a, FileIO* b, int len) {
void writeRandom(FSConfigPtr& cfg, FileIO* a, FileIO* b, int offset, int len) { void writeRandom(FSConfigPtr& cfg, FileIO* a, FileIO* b, int offset, int len) {
SCOPED_TRACE(testing::Message() << "Write random " << offset << ", " << len); SCOPED_TRACE(testing::Message() << "Write random " << offset << ", " << len);
if (a->getSize() < offset + len) if (a->getSize() < offset + len) {
a->truncate(offset + len); a->truncate(offset + len);
}
if (b->getSize() < offset + len) {
b->truncate(offset + len);
}
unsigned char *buf = new unsigned char[len]; unsigned char *buf = new unsigned char[len];
ASSERT_TRUE(cfg->cipher->pseudoRandomize(buf, len)); ASSERT_TRUE(cfg->cipher->pseudoRandomize(buf, len));
@ -106,13 +112,27 @@ void writeRandom(FSConfigPtr& cfg, FileIO* a, FileIO* b, int offset, int len) {
req.offset = offset; req.offset = offset;
ASSERT_TRUE(a->write(req)); ASSERT_TRUE(a->write(req));
// Check that write succeeded.
req.offset = offset;
req.dataLen = len;
ASSERT_EQ(len, a->read(req));
ASSERT_TRUE(memcmp(req.data, buf, len) == 0);
memcpy(req.data, buf, len); memcpy(req.data, buf, len);
req.offset = offset; req.offset = offset;
req.dataLen = len;
ASSERT_TRUE(b->write(req)); ASSERT_TRUE(b->write(req));
// Check that write succeeded.
req.offset = offset;
req.dataLen = len;
ASSERT_EQ(len, b->read(req));
ASSERT_TRUE(memcmp(req.data, buf, len) == 0);
compare(a, b, offset, len); compare(a, b, offset, len);
delete[] buf; delete[] buf;
delete[] req.data;
} }
void compare(FileIO* a, FileIO* b, int offset, int len) { void compare(FileIO* a, FileIO* b, int offset, int len) {
@ -137,7 +157,8 @@ void compare(FileIO* a, FileIO* b, int offset, int len) {
ASSERT_EQ(size1, size2); ASSERT_EQ(size1, size2);
for(int i = 0; i < len; i++) { for(int i = 0; i < len; i++) {
bool match = (buf1[i] == buf2[i]); bool match = (buf1[i] == buf2[i]);
ASSERT_TRUE(match) << "mismatched data at offset " << i << " of " << len; ASSERT_TRUE(match) << "mismatched data at offset " << i << " of " << len
<< ", got " << int(buf1[i]) << " and " << int(buf2[i]);
if(!match) { if(!match) {
break; break;
} }
@ -148,15 +169,13 @@ void compare(FileIO* a, FileIO* b, int offset, int len) {
} }
void comparisonTest(FSConfigPtr& cfg, FileIO* a, FileIO* b) { void comparisonTest(FSConfigPtr& cfg, FileIO* a, FileIO* b) {
const int size = 18*1024; const int size = 2*1024;
writeRandom(cfg, a, b, 0, size); writeRandom(cfg, a, b, 0, size);
if (testing::Test::HasFatalFailure()) return; if (testing::Test::HasFatalFailure()) return;
compare(a, b, 0, size);
if (testing::Test::HasFatalFailure()) return;
for (int i = 0; i < 10000; i++) { for (int i = 0; i < 10000; i++) {
SCOPED_TRACE(testing::Message() << "Test Loop " << i); SCOPED_TRACE(testing::Message() << "Test Loop " << i);
int len = 128 + random() % 2048; int len = 128 + random() % 512;
int offset = (len == a->getSize()) ? 0 int offset = (len == a->getSize()) ? 0
: random() % (a->getSize() - len); : random() % (a->getSize() - len);
writeRandom(cfg, a, b, offset, len); writeRandom(cfg, a, b, offset, len);

View File

@ -4,7 +4,7 @@ file (GLOB CXX_FILES RELATIVE ${PROJECT_SOURCE_DIR} "*/*.cpp")
set (GettextTranslate_GMO_BINARY true) set (GettextTranslate_GMO_BINARY true)
include (GetTextTranslate) include (GettextTranslate)

View File

@ -18,7 +18,6 @@ target_link_libraries (encfsctl
encfs-cipher encfs-cipher
encfs-base encfs-base
${GLOG_LIBRARIES} ${GLOG_LIBRARIES}
${FUSE_LIBRARIES}
) )
if (POD2MAN) if (POD2MAN)

View File

@ -50,9 +50,14 @@
#include <openssl/ssl.h> #include <openssl/ssl.h>
#endif #endif
using namespace std;
using namespace gnu;
using namespace encfs; using namespace encfs;
using gnu::autosprintf;
using std::cerr;
using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;
static int showInfo( int argc, char **argv ); static int showInfo( int argc, char **argv );
static int showVersion( int argc, char **argv ); static int showVersion( int argc, char **argv );

View File

@ -29,8 +29,6 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
using namespace std;
void genKey( const shared_ptr<Cipher> &cipher ) void genKey( const shared_ptr<Cipher> &cipher )
{ {
CipherKey key = cipher->newRandomKey(); CipherKey key = cipher->newRandomKey();