mirror of
https://github.com/vgough/encfs.git
synced 2024-11-21 23:43:26 +01:00
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:
parent
5a126ea797
commit
63c2d1c539
@ -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)
|
||||
|
@ -30,7 +30,9 @@
|
||||
|
||||
#include "base/types.h"
|
||||
|
||||
using namespace std;
|
||||
using std::make_pair;
|
||||
using std::map;
|
||||
using std::string;
|
||||
|
||||
namespace encfs {
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
111
base/base64.cpp
111
base/base64.cpp
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
45
cipher/CipherKey_test.cpp
Normal 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
|
@ -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());
|
||||
|
@ -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
263
cipher/CommonCrypto.cpp
Normal 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
35
cipher/CommonCrypto.h
Normal 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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 );
|
||||
|
@ -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;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "base/Mutex.h"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#ifdef HAVE_TR1_UNORDERED_MAP
|
||||
#include <tr1/unordered_map>
|
||||
|
@ -44,7 +44,8 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using std::list;
|
||||
using std::string;
|
||||
|
||||
namespace encfs {
|
||||
|
||||
|
@ -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;
|
||||
|
@ -49,7 +49,7 @@
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
using namespace std;
|
||||
using std::string;
|
||||
|
||||
namespace encfs {
|
||||
|
||||
|
@ -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)
|
||||
|
@ -30,8 +30,6 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace encfs {
|
||||
|
||||
//
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -35,8 +35,6 @@
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace encfs {
|
||||
|
||||
static Interface RawFileIO_iface = makeInterface("FileIO/Raw", 1, 0, 0);
|
||||
|
@ -28,8 +28,6 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace encfs {
|
||||
|
||||
static shared_ptr<NameIO> NewStreamNameIO( const Interface &iface,
|
||||
|
@ -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
|
||||
|
13
fs/encfs.cpp
13
fs/encfs.cpp
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -4,7 +4,7 @@ file (GLOB CXX_FILES RELATIVE ${PROJECT_SOURCE_DIR} "*/*.cpp")
|
||||
|
||||
set (GettextTranslate_GMO_BINARY true)
|
||||
|
||||
include (GetTextTranslate)
|
||||
include (GettextTranslate)
|
||||
|
||||
|
||||
|
||||
|
@ -18,7 +18,6 @@ target_link_libraries (encfsctl
|
||||
encfs-cipher
|
||||
encfs-base
|
||||
${GLOG_LIBRARIES}
|
||||
${FUSE_LIBRARIES}
|
||||
)
|
||||
|
||||
if (POD2MAN)
|
||||
|
@ -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 );
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user