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_MINOR 0)
set (ENCFS_PATCH 0)
set (ENCFS_VERSION "${ENCFS_MAJOR}.${ENCFS_MINOR}.${ENCFS_PATCH}")
set (ENCFS_VERSION "${ENCFS_MAJOR}.${ENCFS_MINOR}")
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}
"${CMAKE_SOURCE_DIR}/CMakeModules/")
@ -36,7 +42,6 @@ endif (APPLE)
set (CPACK_PACKAGE_NAME "Encfs")
set (CPACK_PACKAGE_VERSION_MAJOR ${ENCFS_MAJOR})
set (CPACK_PACKAGE_VERSION_MINOR ${ENCFS_MINOR})
set (CPACK_PACKAGE_VERSION_PATCH ${ENCFS_PATCH})
set (CPACK_SOURCE_GENERATOR TGZ)
set (CPACK_SOURCE_IGNORE_FILES
"/_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/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
# TODO: move this to cipher directory.
find_package (OpenSSL REQUIRED)
include (OpenSSLTests)
if (WITH_COMMON_CRYPTO)
check_include_file_cxx (Security/SecRandom.h HAVE_SEC_RANDOM_H)
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.
include (CheckCXXSourceCompiles)

View File

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

View File

@ -38,8 +38,7 @@
#include <glog/logging.h>
#include "base/base64.h"
#include "base/Interface.h"
using namespace std;
#include "base/shared_ptr.h"
namespace encfs {
@ -116,16 +115,9 @@ bool XmlValue::readB64(const char *path, byte *data, int length) const
std::string s = value->text();
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());
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);
int decodedSize = B64ToB256Bytes(s.size());
if (decodedSize != length)
{
LOG(ERROR) << "decoding bytes len " << s.size()
@ -133,6 +125,8 @@ bool XmlValue::readB64(const char *path, byte *data, int length) const
<< ", got " << decodedSize;
return false;
}
changeBase2((byte *)s.data(), s.size(), 6, data, length, 8);
B64ToAsciiStandard(data, length);
return true;
}

View File

@ -132,20 +132,39 @@ void changeBase2Inline(byte *src, int srcLen,
static const char B642AsciiTable[] = ",-0123456789";
void B64ToAscii(byte *in, int length)
{
for(int offset=0; offset<length; ++offset)
for(int offset=0; offset<length; ++offset)
{
int ch = in[offset];
if(ch > 11)
{
int ch = in[offset];
if(ch > 11)
{
if(ch > 37)
ch += 'a' - 38;
else
ch += 'A' - 12;
} else
ch = B642AsciiTable[ ch ];
in[offset] = ch;
}
if(ch > 37)
ch += 'a' - 38;
else
ch += 'A' - 12;
} else
ch = B642AsciiTable[ ch ];
in[offset] = ch;
}
}
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[] =
@ -159,55 +178,55 @@ void AsciiToB64(byte *in, int length)
void AsciiToB64(byte *out, const byte *in, int length)
{
while(length--)
while(length--)
{
byte ch = *in++;
if(ch >= 'A')
{
byte ch = *in++;
if(ch >= 'A')
{
if(ch >= 'a')
ch += 38 - 'a';
else
ch += 12 - 'A';
} else
ch = Ascii2B64Table[ ch ] - '0';
if(ch >= 'a')
ch += 38 - 'a';
else
ch += 12 - 'A';
} else
ch = Ascii2B64Table[ ch ] - '0';
*out++ = ch;
}
*out++ = ch;
}
}
void B32ToAscii(byte *buf, int len)
{
for(int offset=0; offset<len; ++offset)
{
int ch = buf[offset];
if (ch >= 0 && ch < 26)
ch += 'A';
else
ch += '2' - 26;
buf[offset] = ch;
}
for(int offset=0; offset<len; ++offset)
{
int ch = buf[offset];
if (ch >= 0 && ch < 26)
ch += 'A';
else
ch += '2' - 26;
buf[offset] = ch;
}
}
void AsciiToB32(byte *in, int length)
{
return AsciiToB32(in, in, length);
return AsciiToB32(in, in, length);
}
void AsciiToB32(byte *out, const byte *in, int length)
{
while(length--)
{
byte ch = *in++;
int lch = toupper(ch);
if (lch >= 'A')
lch -= 'A';
else
lch += 26 - '2';
while(length--)
{
byte ch = *in++;
int lch = toupper(ch);
if (lch >= 'A')
lch -= 'A';
else
lch += 26 - '2';
*out++ = (byte)lch;
}
*out++ = (byte)lch;
}
}
} // namespace encfs

View File

@ -62,6 +62,8 @@ void changeBase2Inline(byte *buf, int srcLength,
// inplace translation from values [0,2^6] => base64 ASCII
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
void B32ToAscii(byte *buf, int length);

View File

@ -10,15 +10,19 @@
#cmakedefine HAVE_TR1_UNORDERED_SET
#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_AES
#cmakedefine HAVE_EVP_AES_XTS
#cmakedefine HAVE_LCHMOD
#cmakedefine HAVE_VALGRIND_VALGRIND_H
#cmakedefine HAVE_VALGRIND_MEMCHECK_H
/* TODO: add other thread library support. */
#cmakedefine CMAKE_USE_PTHREADS_INIT

View File

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

View File

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

View File

@ -1,7 +1,16 @@
include_directories (${OPENSSL_INCLUDE_DIR})
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
BlockCipher.cpp
CipherKey.cpp
@ -9,14 +18,14 @@ add_library (encfs-cipher
MAC.cpp
MemoryPool.cpp
NullCiphers.cpp
openssl.cpp
PBKDF.cpp
readpassphrase.cpp
StreamCipher.cpp
${EXTRA_SOURCE}
)
target_link_libraries (encfs-cipher
${OPENSSL_LIBRARIES}
${EXTRA_LIBS}
)
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 "base/config.h"
#include <cstring>
#include <ctime>
@ -28,18 +29,29 @@
#include <glog/logging.h>
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
#include "base/base64.h"
#include "base/Error.h"
#include "base/i18n.h"
#include "base/Mutex.h"
#include "base/Range.h"
#include "cipher/MemoryPool.h"
#include "cipher/MAC.h"
#include "cipher/BlockCipher.h"
#include "cipher/PBKDF.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 {
@ -54,6 +66,18 @@ inline int MIN(int a, int b)
}
#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
@ -77,7 +101,7 @@ bool BytesToKey(const byte *data, int dataLen,
for(;;)
{
sha1->reset();
sha1->init();
if( addmd++ )
sha1->update(mdBuf.data, mdBuf.size);
sha1->update(data, dataLen);
@ -85,7 +109,7 @@ bool BytesToKey(const byte *data, int dataLen,
for(unsigned int i=1; i < rounds; ++i)
{
sha1->reset();
sha1->init();
sha1->update(mdBuf.data, mdBuf.size);
sha1->write(mdBuf.data);
}
@ -115,6 +139,10 @@ int CipherV1::TimedPBKDF2(const char *pass, int passlen,
const byte *salt, int saltlen,
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();
shared_ptr<PBKDF> impl(registry.CreateForMatch(NAME_PKCS5_PBKDF2_HMAC_SHA1));
if (!impl)
@ -212,11 +240,18 @@ shared_ptr<CipherV1> CipherV1::New(const std::string& name, 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,
int keyLength)
CipherV1::CipherV1()
{
}
bool CipherV1::initCiphers(const Interface &iface, const Interface &realIface,
int keyLength)
{
this->iface = iface;
this->realIface = realIface;
@ -243,12 +278,11 @@ CipherV1::CipherV1(const Interface &iface, const Interface &realIface,
defaultKeyLength = 0;
_blockCipher.reset( blockCipherRegistry.CreateForMatch("NullCipher") );
_streamCipher.reset( streamCipherRegistry.CreateForMatch("NullCipher") );
} else {
throw Error("Unsupported cipher");
}
if (!_blockCipher || !_streamCipher) {
throw Error("Requested cipher not available");
LOG(INFO) << "Unsupported cipher " << iface.name();
return false;
}
if (keyLength <= 0)
@ -259,7 +293,8 @@ CipherV1::CipherV1(const Interface &iface, const Interface &realIface,
_pbkdf.reset(PBKDF::GetRegistry().CreateForMatch(
NAME_PKCS5_PBKDF2_HMAC_SHA1));
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
@ -272,8 +307,12 @@ CipherV1::CipherV1(const Interface &iface, const Interface &realIface,
Lock l(_hmacMutex);
_hmac.reset(MAC::GetRegistry().CreateForMatch(NAME_SHA1_HMAC));
if (!_hmac)
throw Error("SHA1_HMAC not available");
if (!_hmac) {
LOG(ERROR) << "SHA1_HMAC not available";
return false;
}
return true;
}
CipherV1::~CipherV1()
@ -296,6 +335,10 @@ CipherKey CipherV1::newKey(const char *password, int passwdLength,
int *iterationCount, long desiredDuration,
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);
if(*iterationCount == 0)
@ -329,6 +372,9 @@ CipherKey CipherV1::newKey(const char *password, int passwdLength,
// password is changed or configuration is rewritten.
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);
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()];
Lock l(_hmacMutex);
_hmac->reset();
_hmac->init();
_hmac->update(data, len);
if(chainedIV)
{
@ -398,8 +444,11 @@ uint64_t CipherV1::MAC_64(const byte *data, int len,
// chop this down to a 64bit value..
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]);
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];
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
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_CHECK_MEM_IS_DEFINED(key.data(), key.size());
#endif
unsigned int checksum2 = reduceMac32(
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
<< "on decode of " << _keySize + _ivLength << " bytes";
return CipherKey();
@ -454,23 +516,22 @@ CipherKey CipherV1::readKey(const byte *data, bool checkKey)
void CipherV1::writeKey(const CipherKey &ckey, byte *out)
{
rAssert( _keySet );
rAssert(ckey.size() > KEY_CHECKSUM_BYTES);
SecureMem tmpBuf(ckey.size());
memcpy(tmpBuf.data, ckey.data(), tmpBuf.size);
unsigned int checksum = reduceMac32(
MAC_64( tmpBuf.data, tmpBuf.size, NULL ));
MAC_64(tmpBuf.data, tmpBuf.size, NULL));
streamEncode(tmpBuf.data, tmpBuf.size, checksum);
memcpy( out+KEY_CHECKSUM_BYTES, tmpBuf.data, tmpBuf.size );
// first N bytes contain HMAC derived checksum..
for(int i=1; i<=KEY_CHECKSUM_BYTES; ++i)
{
out[KEY_CHECKSUM_BYTES-i] = checksum & 0xff;
checksum >>= 8;
}
memcpy( out+KEY_CHECKSUM_BYTES, tmpBuf.data, tmpBuf.size );
}
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
Lock l(_hmacMutex);
_hmac->reset();
_hmac->init();
_hmac->update(ivec, _ivLength);
_hmac->update(md.data(), 8);
_hmac->write(md.data());

View File

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

View File

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

View File

@ -42,105 +42,69 @@
#include <map>
#include <list>
using namespace std;
#ifdef WITH_OPENSSL
# include <openssl/crypto.h>
# include <openssl/buffer.h>
#endif
namespace encfs {
static BUF_MEM *allocBlock( int size )
#ifdef WITH_OPENSSL
static byte *allocBlock( int size )
{
BUF_MEM *block = BUF_MEM_new( );
BUF_MEM_grow( block, size );
VALGRIND_MAKE_MEM_NOACCESS( block->data, block->max );
return block;
byte *block = (byte *)OPENSSL_malloc(size);
return block;
}
static void freeBlock( BUF_MEM *block )
static void freeBlock( byte *block, int size )
{
VALGRIND_MAKE_MEM_UNDEFINED( block->data, block->max );
BUF_MEM_free( block );
OPENSSL_cleanse(block, size);
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;
typedef std::map<int, std::list<BUF_MEM* > > FreeBlockMap;
static FreeBlockMap gFreeBlocks;
unsigned char cleanse_ctr = 0;
static void freeBlock(byte *data, int len) {
byte *p = data;
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)
{
rAssert(size > 0);
pthread_mutex_lock( &gMPoolMutex );
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 );
rAssert(size > 0);
this->data = allocBlock(size);
this->size = size;
}
MemBlock::~MemBlock()
{
BUF_MEM *block = (BUF_MEM*)internalData;
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 );
freeBlock(data, size);
}
SecureMem::SecureMem(int len)
{
rAssert(len > 0);
data = (byte *)OPENSSL_malloc(len);
data = allocBlock(len);
if (data)
{
size = len;
mlock(data, size);
memset(data, '\0', size);
VALGRIND_MAKE_MEM_UNDEFINED( data, size );
} else
{
size = 0;
@ -151,17 +115,18 @@ SecureMem::~SecureMem()
{
if (size)
{
memset(data, '\0', size);
OPENSSL_cleanse(data, size);
freeBlock(data, size);
munlock(data, size);
OPENSSL_free(data);
VALGRIND_MAKE_MEM_NOACCESS( data, size );
data = NULL;
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

View File

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

View File

@ -46,6 +46,13 @@ class StreamCipher
std::string toString() const {
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();
@ -53,9 +60,9 @@ class StreamCipher
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;
virtual bool decrypt(const byte *iv, const byte *in,
virtual bool decrypt(const byte *ivec, const byte *in,
byte *out, int numBytes) =0;
};

View File

@ -19,6 +19,7 @@
*/
#include "cipher/openssl.h"
#include "base/config.h"
#include <cstring>
#include <ctime>
@ -28,6 +29,10 @@
#include <glog/logging.h>
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
#include "base/config.h"
#define NO_DES
@ -54,8 +59,6 @@
#include "cipher/PBKDF.h"
#include "cipher/StreamCipher.h"
using namespace std;
namespace encfs {
const int MAX_KEYLENGTH = 64; // in bytes (256 bit)
@ -108,10 +111,18 @@ class OpenSSLCipher : public BlockCipher {
return false;
}
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(key->data(), key->size());
#endif
return true;
}
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 );
if(result != 1)
{
@ -136,12 +147,27 @@ class OpenSSLCipher : public BlockCipher {
}
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,
byte *out, int size) {
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_EncryptUpdate( &enc, out, &dstLen, in, size);
EVP_EncryptFinal_ex( &enc, out+dstLen, &tmpLen );
@ -159,6 +185,17 @@ class OpenSSLCipher : public BlockCipher {
virtual bool decrypt(const byte *ivec, const byte *in,
byte *out, int size) {
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_DecryptUpdate( &dec, out, &dstLen, in, size );
EVP_DecryptFinal_ex( &dec, out+dstLen, &tmpLen );
@ -346,7 +383,7 @@ class Sha1HMac : public MAC {
return true;
}
virtual void reset() {
virtual void init() {
HMAC_Init_ex(&ctx, 0, 0, 0, 0);
}
@ -364,6 +401,9 @@ class Sha1HMac : public MAC {
unsigned int outSize = 0;
HMAC_Final(&ctx, (unsigned char *)out, &outSize);
CHECK_EQ(outputSize(), outSize) << "Invalid HMAC output size";
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(out, outSize);
#endif
return true;
}

View File

@ -1,8 +1,14 @@
#include <glog/logging.h>
#include <gtest/gtest.h>
#include "base/config.h"
#include "cipher/testing.h"
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
namespace encfs {
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) {
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_CHECK_MEM_IS_ADDRESSABLE(out, len);
#endif
bool odd = false;
unsigned int last = 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) {
google::InitGoogleLogging(argv[0]);
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

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

View File

@ -24,6 +24,16 @@ target_link_libraries (encfs-fs
${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).
if (GTEST_FOUND)
link_directories (${PROJECT_BINARY_DIR}/base)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -66,8 +66,12 @@
#include <google/protobuf/text_format.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
using namespace std;
using namespace gnu;
using gnu::autosprintf;
using std::cout;
using std::cerr;
using std::endl;
using std::map;
using std::string;
namespace encfs {
@ -1186,6 +1190,11 @@ void showFSInfo( const EncfsConfig &config )
cout << "\n";
} 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..
shared_ptr<NameIO> nameCoder = NameIO::New( config.naming(), cipher );
if(!nameCoder)

View File

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

View File

@ -34,7 +34,11 @@
#include "fs/StreamNameIO.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 {

View File

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

View File

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

View File

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

View File

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

View File

@ -76,6 +76,38 @@ TEST(IOTest, MacIO) {
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) {
shared_ptr<MemFileIO> base(new MemFileIO(0));
shared_ptr<CipherFileIO> test(new CipherFileIO(base, cfg));

View File

@ -36,7 +36,8 @@
#include "fs/MACFileIO.h"
#include "fs/MemFileIO.h"
using namespace std;
using std::list;
using std::string;
namespace encfs {
@ -92,27 +93,46 @@ void truncate(FileIO* a, FileIO* b, int len) {
void writeRandom(FSConfigPtr& cfg, FileIO* a, FileIO* b, int offset, int len) {
SCOPED_TRACE(testing::Message() << "Write random " << offset << ", " << len);
if (a->getSize() < offset + len)
if (a->getSize() < offset + len) {
a->truncate(offset + len);
}
if (b->getSize() < offset + len) {
b->truncate(offset + len);
}
unsigned char *buf = new unsigned char[len];
ASSERT_TRUE(cfg->cipher->pseudoRandomize(buf, len));
IORequest req;
req.data = new unsigned char[len];
req.dataLen = len;
memcpy(req.data, buf, len);
req.offset = offset;
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);
req.offset = offset;
req.dataLen = len;
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);
delete[] buf;
delete[] req.data;
}
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);
for(int i = 0; i < len; 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) {
break;
}
@ -148,15 +169,13 @@ void compare(FileIO* a, FileIO* b, int offset, int len) {
}
void comparisonTest(FSConfigPtr& cfg, FileIO* a, FileIO* b) {
const int size = 18*1024;
const int size = 2*1024;
writeRandom(cfg, a, b, 0, size);
if (testing::Test::HasFatalFailure()) return;
compare(a, b, 0, size);
if (testing::Test::HasFatalFailure()) return;
for (int i = 0; i < 10000; i++) {
SCOPED_TRACE(testing::Message() << "Test Loop " << i);
int len = 128 + random() % 2048;
int len = 128 + random() % 512;
int offset = (len == a->getSize()) ? 0
: random() % (a->getSize() - 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)
include (GetTextTranslate)
include (GettextTranslate)

View File

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

View File

@ -50,9 +50,14 @@
#include <openssl/ssl.h>
#endif
using namespace std;
using namespace gnu;
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 showVersion( int argc, char **argv );

View File

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