break out cipher primitives, add unit tests

git-svn-id: http://encfs.googlecode.com/svn/trunk@94 db9cf616-1c43-0410-9cb8-a902689de0d6
This commit is contained in:
Valient Gough 2013-03-05 06:32:27 +00:00
parent 7799c88df6
commit f00ef5c6d3
29 changed files with 1235 additions and 130 deletions

View File

@ -11,8 +11,28 @@ option (BUILD_SHARED_LIBS "Build dynamic link libraries" OFF)
set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_SOURCE_DIR}/CMakeModules/") "${CMAKE_SOURCE_DIR}/CMakeModules/")
# Tweak compiler flags.
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall") set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall")
include (CheckCXXCompilerFlag)
check_cxx_compiler_flag (-std=c++11 HAVE_C11_FLAG)
check_cxx_compiler_flag (-std=gnu++11 HAVE_GNU11_FLAG)
if (HAVE_GNU11_FLAG)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
elseif (HAVE_C11_FLAG)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif (HAVE_GNU11_FLAG)
# Flume specific flags.
find_package (FUSE REQUIRED)
include_directories (${FUSE_INCLUDE_DIR})
add_definitions (-D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26)
if (APPLE)
add_definitions (-D__FreeBSD__=10)
endif (APPLE)
# Packaging config.
set (CPACK_PACKAGE_NAME "Encfs") set (CPACK_PACKAGE_NAME "Encfs")
set (CPACK_PACKAGE_VERSION_MAJOR ${ENCFS_MAJOR}) set (CPACK_PACKAGE_VERSION_MAJOR ${ENCFS_MAJOR})
set (CPACK_PACKAGE_VERSION_MINOR ${ENCFS_MINOR}) set (CPACK_PACKAGE_VERSION_MINOR ${ENCFS_MINOR})
@ -23,6 +43,7 @@ set (CPACK_SOURCE_IGNORE_FILES
"/build/") "/build/")
include (CPack) include (CPack)
# Check for external files.
include (CheckIncludeFileCXX) include (CheckIncludeFileCXX)
check_include_file_cxx (attr/xattr.h HAVE_ATTR_XATTR_H) check_include_file_cxx (attr/xattr.h HAVE_ATTR_XATTR_H)
check_include_file_cxx (sys/xattr.h HAVE_SYS_XATTR_H) check_include_file_cxx (sys/xattr.h HAVE_SYS_XATTR_H)
@ -45,11 +66,7 @@ CHECK_CXX_SOURCE_COMPILES ("#include <sys/types.h>
#include <sys/xattr.h> #include <sys/xattr.h>
int main() { getxattr(0,0,0,0,0,0); return 1; } " XATTR_ADD_OPT) int main() { getxattr(0,0,0,0,0,0); return 1; } " XATTR_ADD_OPT)
add_definitions (-D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26) # Libraries or programs used for multiple modules.
if (APPLE)
add_definitions (-D__FreeBSD__=10)
endif (APPLE)
find_package (Protobuf REQUIRED) find_package (Protobuf REQUIRED)
include_directories (${PROTOBUF_INCLUDE_DIR}) include_directories (${PROTOBUF_INCLUDE_DIR})
@ -58,9 +75,16 @@ include_directories (${GLOG_INCLUDE_DIRS})
find_program (POD2MAN pod2man) find_program (POD2MAN pod2man)
find_package (GTest)
if (GTEST_FOUND)
enable_testing()
endif (GTEST_FOUND)
# Prefix for encfs module includes.
include_directories (${Encfs_BINARY_DIR}) include_directories (${Encfs_BINARY_DIR})
include_directories (${Encfs_SOURCE_DIR}) include_directories (${Encfs_SOURCE_DIR})
# Subdirectories.
add_subdirectory(base) add_subdirectory(base)
add_subdirectory(cipher) add_subdirectory(cipher)
add_subdirectory(fs) add_subdirectory(fs)
@ -68,3 +92,10 @@ add_subdirectory(encfs)
add_subdirectory(util) add_subdirectory(util)
add_subdirectory(po) add_subdirectory(po)
# Test target.
if (GTEST_FOUND)
add_custom_target (test COMMAND ${CMAKE_TEST_COMMAND} DEPENDS
cipher/cipher-tests fs/fs-tests)
endif (GTEST_FOUND)

View File

@ -17,6 +17,8 @@ add_library (encfs-base
ConfigVar.cpp ConfigVar.cpp
Error.cpp Error.cpp
Interface.cpp Interface.cpp
Range.h
Registry.h
XmlReader.cpp XmlReader.cpp
${PROTO_SRCS} ${PROTO_SRCS}
${PROTO_HDRS} ${PROTO_HDRS}

View File

@ -21,6 +21,8 @@
#ifndef _Range_incl_ #ifndef _Range_incl_
#define _Range_incl_ #define _Range_incl_
#include <ostream>
namespace encfs { namespace encfs {
class Range class Range
@ -42,6 +44,18 @@ public:
int inc() const; int inc() const;
}; };
inline std::ostream & operator << (std::ostream &st, const Range &r) {
bool separator = false;
for (int size = r.min(); size <= r.max(); size += r.inc()) {
if (separator)
st << ", ";
else
separator = true;
st << size;
}
return st;
}
inline Range::Range(int minMax) inline Range::Range(int minMax)
{ {
this->minVal = minMax; this->minVal = minMax;

94
base/Registry.h Normal file
View File

@ -0,0 +1,94 @@
#ifndef REGISTRY_H
#define REGISTRY_H
#include <list>
#include <map>
namespace encfs {
template <typename T>
class Registry
{
public:
typedef T *(*FactoryFn)();
struct Data {
FactoryFn constructor;
typename T::Properties properties;
};
void Register(const char *name, FactoryFn fn,
typename T::Properties properties)
{
Data d;
d.constructor = fn;
d.properties = properties;
data[name] = d;
}
T* Create(const char *name)
{
auto it = data.find(name);
if (it == data.end())
return NULL;
return (*it->second.constructor)();
}
T* CreateForMatch(const std::string &description)
{
for (auto &it : data) {
if (description == it.second.properties.toString())
return (*it.second.constructor)();
}
return NULL;
}
std::list<std::string> GetAll() const {
std::list<std::string> result;
for (auto &it : data) {
result.push_back(it.first);
}
return result;
}
const typename T::Properties *GetProperties(const char *name) const {
auto it = data.find(name);
if (it == data.end())
return NULL;
return &(it->second.properties);
}
private:
std::map<std::string, Data> data;
};
template <typename T, typename BASE>
class Registrar
{
public:
Registrar(const char *name)
{
BASE::GetRegistry().Register(name,
Registrar<T, BASE>::Construct,
T::GetProperties());
}
static BASE *Construct() {
return new T();
}
};
#define DECLARE_REGISTERABLE_TYPE(TYPE) \
static Registry<TYPE>& GetRegistry()
#define DEFINE_REGISTERABLE_TYPE(TYPE) \
Registry<TYPE>& TYPE::GetRegistry() { \
static Registry<TYPE> registry; \
return registry; \
}
#define REGISTER_CLASS(DERIVED, BASE) \
static Registrar<DERIVED, BASE> registrar_##DERIVED(#DERIVED)
} // namespace encfs
#endif // REGISTRY_H

29
cipher/BlockCipher.cpp Normal file
View File

@ -0,0 +1,29 @@
#include "cipher/BlockCipher.h"
// TODO: add ifdef when OpenSSL becomes optional.
#include "cipher/openssl.h"
namespace encfs {
Registry<BlockCipher>& BlockCipher::GetRegistry()
{
static Registry<BlockCipher> registry;
static bool first = true;
if (first)
{
OpenSSL::registerCiphers();
first = false;
}
return registry;
}
BlockCipher::BlockCipher()
{
}
BlockCipher::~BlockCipher()
{
}
} // namespace encfs

28
cipher/BlockCipher.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef BLOCKCIPHER_H
#define BLOCKCIPHER_H
#include "base/Interface.h"
#include "base/Range.h"
#include "base/Registry.h"
#include "base/shared_ptr.h"
#include "base/types.h"
#include "cipher/StreamCipher.h"
namespace encfs {
// BlockCipher is a StreamCipher with a block size.
// Encryption and decryption must be in multiples of the block size.
class BlockCipher : public StreamCipher
{
public:
static Registry<BlockCipher>& GetRegistry();
BlockCipher();
virtual ~BlockCipher();
virtual int blockSize() const =0;
};
} // namespace encfs
#endif // BLOCKCIPHER_H

View File

@ -0,0 +1,95 @@
/*****************************************************************************
* 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 <list>
#include <gtest/gtest.h>
#include "base/shared_ptr.h"
#include "cipher/BlockCipher.h"
#include "cipher/MemoryPool.h"
using namespace encfs;
using std::list;
using std::string;
namespace {
void compare(const byte *a, const byte *b, int size) {
for (int i = 0; i < size; i++) {
bool match = (a[i] == b[i]);
ASSERT_TRUE(match) << "mismatched data at offset " << i
<< " of " << size;
if (!match)
break;
}
}
TEST(BlockEncryptionTest, BlockCipher) {
Registry<BlockCipher> registry = BlockCipher::GetRegistry();
list<string> ciphers = registry.GetAll();
for (const string &name : ciphers) {
const BlockCipher::Properties *properties = registry.GetProperties(name.c_str());
SCOPED_TRACE(testing::Message() << "Cipher " << name);
for (int keySize = properties->keySize.min();
keySize <= properties->keySize.max();
keySize += properties->keySize.inc()) {
SCOPED_TRACE(testing::Message() << "Key size " << keySize);
shared_ptr<BlockCipher> cipher (registry.Create(name.c_str()));
ASSERT_TRUE(cipher->randomKey(keySize / 8));
// Create some data to encrypt.
int blockSize = cipher->blockSize();
MemBlock mb;
mb.allocate(16 * blockSize);
for (int i = 0; i < 16 * blockSize; i++) {
mb.data[i] = i % 256;
}
MemBlock iv;
iv.allocate(blockSize);
for (int i = 0; i < blockSize; i++) {
iv.data[i] = i;
}
// Encrypt.
MemBlock encrypted;
encrypted.allocate(16 * blockSize);
ASSERT_TRUE(cipher->encrypt(iv.data, mb.data,
encrypted.data, 16 * blockSize));
// Decrypt.
MemBlock decrypted;
decrypted.allocate(16 * blockSize);
ASSERT_TRUE(cipher->decrypt(iv.data, encrypted.data,
decrypted.data, 16 * blockSize));
compare(mb.data, decrypted.data, 16 * blockSize);
}
}
}
} // namespace

View File

@ -2,41 +2,42 @@ include_directories (${OPENSSL_INCLUDE_DIR})
link_directories (${Encfs_BINARY_DIR}/base) link_directories (${Encfs_BINARY_DIR}/base)
enable_testing ()
find_package (GTest REQUIRED)
add_library (encfs-cipher add_library (encfs-cipher
readpassphrase.cpp
BlockCipher.cpp BlockCipher.cpp
Cipher.cpp Cipher.cpp
CipherKey.cpp CipherKey.cpp
MAC.cpp
MemoryPool.cpp MemoryPool.cpp
NullCipher.cpp NullCipher.cpp
openssl.cpp openssl.cpp
PBKDF.cpp
readpassphrase.cpp
SSL_Cipher.cpp SSL_Cipher.cpp
StreamCipher.cpp
) )
target_link_libraries (encfs-cipher target_link_libraries (encfs-cipher
${OPENSSL_LIBRARIES} ${OPENSSL_LIBRARIES}
) )
#include_directories (${GTEST_INCLUDE_DIR}) if (GTEST_FOUND)
#add_executable (unittests link_directories (${PROJECT_BINARY_DIR}/base)
#MemBlockFileIO.cpp include_directories (${GTEST_INCLUDE_DIR})
#MemFileIO.cpp
#testing.cpp
#test_IO.cpp
#test_BlockIO.cpp
#)
#target_link_libraries (unittests file (GLOB TEST_FILES "*_test.cpp")
#${GTEST_BOTH_LIBRARIES}
#encfs-fs
#encfs-base
#${GLOG_LIBRARIES}
#)
#add_test (UnitTests unittests) add_executable (cipher-tests
#GTEST_ADD_TESTS (unittests "${UnitTestArgs}" test_IO.cpp test_BlockIO.cpp) testing.cpp
#add_custom_target (test COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS unittests) ${TEST_FILES}
)
target_link_libraries (cipher-tests
${GTEST_BOTH_LIBRARIES}
encfs-cipher
encfs-base
${GLOG_LIBRARIES}
)
add_test (CipherTests cipher-tests)
GTEST_ADD_TESTS (cipher-tests "${CipherTestArgs}" ${TEST_FILES})
endif (GTEST_FOUND)

16
cipher/MAC.cpp Normal file
View File

@ -0,0 +1,16 @@
#include "cipher/MAC.h"
namespace encfs {
DEFINE_REGISTERABLE_TYPE(MessageAuthenticationCode)
MessageAuthenticationCode::MessageAuthenticationCode()
{
}
MessageAuthenticationCode::~MessageAuthenticationCode()
{
}
} // namespace encfs

43
cipher/MAC.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef ENCFS_MAC_H
#define ENCFS_MAC_H
#include <string>
#include "base/Registry.h"
#include "base/types.h"
namespace encfs {
// MessageAuthenticationCode provides keyed MAC algorithms, eg HMAC.
class MessageAuthenticationCode
{
public:
DECLARE_REGISTERABLE_TYPE(MessageAuthenticationCode);
struct Properties {
int blockSize; // Block length of hash function.
std::string hashFunction;
std::string mode;
std::string library;
std::string toString() const {
return hashFunction + "/" + mode;
}
};
MessageAuthenticationCode();
virtual ~MessageAuthenticationCode();
virtual int outputSize() const =0;
virtual bool setKey(const byte *key, int keyLength) =0;
virtual bool randomKey(int keyLength) =0;
virtual void reset() =0;
virtual bool update(const byte *in, int length) =0;
virtual bool write(byte *out) =0;
};
} // namespace encfs
#endif // ENCFS_MAC_H

88
cipher/MAC_test.cpp Normal file
View File

@ -0,0 +1,88 @@
/*****************************************************************************
* 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 <string.h>
#include <gtest/gtest.h>
#include "base/shared_ptr.h"
#include "cipher/MAC.h"
#include "cipher/testing.h"
using namespace encfs;
namespace {
TEST(HMacSha1Test, MessageAuthenticationCode) {
Registry<MessageAuthenticationCode> registry =
MessageAuthenticationCode::GetRegistry();
shared_ptr<MessageAuthenticationCode> hmac(
registry.CreateForMatch( "SHA-1/HMAC" ));
ASSERT_FALSE(!hmac);
// Test cases from rfc2202
// Test case 1
byte key[20];
byte out[20];
for (int i = 0; i < 20; ++i)
key[i] = 0x0b;
hmac->setKey(key, 20);
hmac->reset();
hmac->update((byte *)"Hi There", 8);
hmac->write(out);
ASSERT_EQ("b617318655057264e28bc0b6fb378c8ef146be00", stringToHex(out, 20));
// Test case 2
strcpy((char *)key, "Jefe");
hmac->setKey(key, 4);
hmac->reset();
hmac->update((byte *)"what do ya want for nothing?", 28);
hmac->write(out);
ASSERT_EQ("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79", stringToHex(out, 20));
// Test case 3
for (int i = 0; i < 20; ++i)
key[i] = 0xaa;
hmac->setKey(key, 20);
hmac->reset();
{
byte data[50];
memset(data, 0xdd, 50);
hmac->update(data, 50);
}
hmac->write(out);
ASSERT_EQ("125d7342b9ac11cd91a39af48aa17b4f63f175d3", stringToHex(out, 20));
// Test #7
byte longKey[80];
memset(longKey, 0xaa, 80);
hmac->setKey(longKey, 80);
hmac->reset();
hmac->update((byte *)"Test Using Larger Than Block-Size Key and Larger "
"Than One Block-Size Data", 73);
hmac->write(out);
ASSERT_EQ("e8e99d0f45237d786d6bbaa7965c7808bbff1a91", stringToHex(out, 20));
}
} // namespace

View File

@ -134,7 +134,7 @@ void MemoryPool::destroyAll()
SecureMem::SecureMem(int len) SecureMem::SecureMem(int len)
{ {
rAssert(len > 0); rAssert(len > 0);
data = (char *)OPENSSL_malloc(len); data = (byte *)OPENSSL_malloc(len);
if (data) if (data)
{ {
size = len; size = len;

View File

@ -29,7 +29,8 @@ namespace encfs {
Memory Pool for fixed sized objects. Memory Pool for fixed sized objects.
Usage: Usage:
MemBlock mb( size ); MemBlock mb;
mb.allocate( size );
// do things with storage in mb.data // do things with storage in mb.data
byte *buffer = mb.data; byte *buffer = mb.data;
@ -59,7 +60,7 @@ namespace MemoryPool
struct SecureMem struct SecureMem
{ {
int size; int size;
char *data; byte *data;
SecureMem(int len); SecureMem(int len);
~SecureMem(); ~SecureMem();

16
cipher/PBKDF.cpp Normal file
View File

@ -0,0 +1,16 @@
#include "cipher/PBKDF.h"
namespace encfs {
DEFINE_REGISTERABLE_TYPE(PBKDF)
PBKDF::PBKDF()
{
}
PBKDF::~PBKDF()
{
}
} // namespace encfs

35
cipher/PBKDF.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef ENCFS_PBKDF_H
#define ENCFS_PBKDF_H
#include <string>
#include "base/Registry.h"
#include "base/types.h"
namespace encfs {
// Password Based Key Derivation Function.
class PBKDF
{
public:
DECLARE_REGISTERABLE_TYPE(PBKDF);
struct Properties {
std::string mode;
std::string library;
std::string toString() const { return mode; }
};
PBKDF();
virtual ~PBKDF();
virtual bool makeKey(const char *password, int passwordLength,
const byte *salt, int saltLength,
int numIterations,
byte *outKey, int keyLength) const = 0;
};
} // namespace encfs
#endif // ENCFS_PBKDF_H

78
cipher/PBKDF_test.cpp Normal file
View File

@ -0,0 +1,78 @@
/*****************************************************************************
* 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 <string.h>
#include <gtest/gtest.h>
#include "base/shared_ptr.h"
#include "cipher/PBKDF.h"
#include "cipher/testing.h"
using namespace encfs;
namespace {
TEST(PKCS5_PBKDF2_HMAC_SHA1, PBKDF) {
Registry<PBKDF> registry = PBKDF::GetRegistry();
shared_ptr<PBKDF> impl( registry.CreateForMatch( "PKCS5_PBKDF2_HMAC_SHA1" ));
ASSERT_FALSE(!impl);
// Test cases from rfc6070
// Test case 1
{
byte key[20];
bool ok = impl->makeKey("password", 8,
(byte*)"salt", 4,
1,
key, sizeof(key));
ASSERT_TRUE(ok);
ASSERT_EQ("0c60c80f961f0e71f3a9b524af6012062fe037a6",
stringToHex(key, sizeof(key)));
}
{
byte key[25];
bool ok = impl->makeKey("passwordPASSWORDpassword", 24,
(byte*)"saltSALTsaltSALTsaltSALTsaltSALTsalt", 36,
4096,
key, sizeof(key));
ASSERT_TRUE(ok);
ASSERT_EQ("3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038",
stringToHex(key, sizeof(key)));
}
{
byte key[16];
bool ok = impl->makeKey("pass\0word", 9,
(byte*)"sa\0lt", 5,
4096,
key, sizeof(key));
ASSERT_TRUE(ok);
ASSERT_EQ("56fa6aa75548099dcc37d7f03425e0c3",
stringToHex(key, sizeof(key)));
}
}
} // namespace

View File

@ -28,6 +28,7 @@
#include "cipher/SSL_Cipher.h" #include "cipher/SSL_Cipher.h"
#include "cipher/MemoryPool.h" #include "cipher/MemoryPool.h"
#include "cipher/BlockCipher.h"
#include "base/Error.h" #include "base/Error.h"
#include "base/Mutex.h" #include "base/Mutex.h"
#include "base/Range.h" #include "base/Range.h"
@ -65,13 +66,13 @@ inline int MIN(int a, int b)
DEPRECATED: this is here for backward compatibilty only. Use PBKDF DEPRECATED: this is here for backward compatibilty only. Use PBKDF
*/ */
int BytesToKey( int keyLen, int ivLen, const EVP_MD *md, int BytesToKey(int keyLen, int ivLen, const EVP_MD *md,
const byte *data, int dataLen, const byte *data, int dataLen,
unsigned int rounds, byte *key, byte *iv) unsigned int rounds, byte *key, byte *iv)
{ {
if( data == NULL || dataLen == 0 ) if( data == NULL || dataLen == 0 )
return 0; // OpenSSL returns nkey here, but why? It is a failure.. return 0; // OpenSSL returns nkey here, but why? It is a failure..
byte mdBuf[ EVP_MAX_MD_SIZE ]; byte mdBuf[ EVP_MAX_MD_SIZE ];
unsigned int mds=0; unsigned int mds=0;
int addmd =0; int addmd =0;
@ -110,7 +111,7 @@ int BytesToKey( int keyLen, int ivLen, const EVP_MD *md,
{ {
memcpy( iv, mdBuf+offset, toCopy ); memcpy( iv, mdBuf+offset, toCopy );
iv += toCopy; iv += toCopy;
niv -= toCopy; niv -= toCopy;
offset += toCopy; offset += toCopy;
} }
if((nkey == 0) && (niv == 0)) break; if((nkey == 0) && (niv == 0)) break;
@ -128,9 +129,9 @@ long time_diff(const timeval &end, const timeval &start)
} }
int SSL_Cipher::TimedPBKDF2(const char *pass, int passlen, int SSL_Cipher::TimedPBKDF2(const char *pass, int passlen,
const byte *salt, int saltlen, const byte *salt, int saltlen,
int keylen, byte *out, int keylen, byte *out,
long desiredPDFTime) long desiredPDFTime)
{ {
int iter = 1000; int iter = 1000;
timeval start, end; timeval start, end;
@ -188,8 +189,9 @@ static shared_ptr<Cipher> NewBFCipher( const Interface &iface, int keyLen )
const EVP_CIPHER *blockCipher = EVP_bf_cbc(); const EVP_CIPHER *blockCipher = EVP_bf_cbc();
const EVP_CIPHER *streamCipher = EVP_bf_cfb(); const EVP_CIPHER *streamCipher = EVP_bf_cfb();
return shared_ptr<Cipher>( new SSL_Cipher(iface, BlowfishInterface, return shared_ptr<Cipher>(
blockCipher, streamCipher, keyLen / 8) ); new SSL_Cipher(iface, BlowfishInterface,
blockCipher, streamCipher, keyLen / 8) );
} }
static bool BF_Cipher_registered = Cipher::Register( static bool BF_Cipher_registered = Cipher::Register(
@ -239,8 +241,8 @@ static shared_ptr<Cipher> NewAESCipher( const Interface &iface, int keyLen )
} }
static bool AES_Cipher_registered = Cipher::Register( static bool AES_Cipher_registered = Cipher::Register(
"AES", "16 byte block cipher", "AES", "16 byte block cipher",
AESInterface, AESKeyRange, AESBlockRange, NewAESCipher, true); AESInterface, AESKeyRange, AESBlockRange, NewAESCipher, true);
#endif #endif
#if defined(HAVE_EVP_AES_XTS) #if defined(HAVE_EVP_AES_XTS)
@ -276,35 +278,35 @@ static shared_ptr<Cipher> NewAesXtsCipher( const Interface &iface, int keyLen )
} }
static bool AES_XTS_Cipher_registered = Cipher::Register( static bool AES_XTS_Cipher_registered = Cipher::Register(
"AES_XTS", "Tweakable wide-block cipher", "AES_XTS", "Tweakable wide-block cipher",
AesXtsInterface, AesXtsKeyRange, AesXtsBlockRange, NewAesXtsCipher, false); AesXtsInterface, AesXtsKeyRange, AesXtsBlockRange, NewAesXtsCipher, false);
#endif #endif
class SSLKey : public AbstractCipherKey class SSLKey : public AbstractCipherKey
{ {
public: public:
pthread_mutex_t mutex; pthread_mutex_t mutex;
unsigned int keySize; // in bytes unsigned int keySize; // in bytes
unsigned int ivLength; unsigned int ivLength;
// key data is first _keySize bytes, // key data is first _keySize bytes,
// followed by iv of _ivLength bytes, // followed by iv of _ivLength bytes,
SecureMem buf; SecureMem buf;
EVP_CIPHER_CTX block_enc; EVP_CIPHER_CTX block_enc;
EVP_CIPHER_CTX block_dec; EVP_CIPHER_CTX block_dec;
EVP_CIPHER_CTX stream_enc; EVP_CIPHER_CTX stream_enc;
EVP_CIPHER_CTX stream_dec; EVP_CIPHER_CTX stream_dec;
HMAC_CTX mac_ctx; HMAC_CTX mac_ctx;
SSLKey(int keySize, int ivLength); SSLKey(int keySize, int ivLength);
~SSLKey(); ~SSLKey();
}; };
SSLKey::SSLKey(int keySize_, int ivLength_) SSLKey::SSLKey(int keySize_, int ivLength_)
: buf(keySize_ + ivLength_) : buf(keySize_ + ivLength_)
{ {
rAssert(keySize_ >= 8); rAssert(keySize_ >= 8);
rAssert(ivLength_ >= 8); rAssert(ivLength_ >= 8);
@ -341,7 +343,7 @@ inline byte* IVData( const shared_ptr<SSLKey> &key )
} }
void initKey(const shared_ptr<SSLKey> &key, const EVP_CIPHER *_blockCipher, void initKey(const shared_ptr<SSLKey> &key, const EVP_CIPHER *_blockCipher,
const EVP_CIPHER *_streamCipher, int _keySize) const EVP_CIPHER *_streamCipher, int _keySize)
{ {
Lock lock( key->mutex ); Lock lock( key->mutex );
// initialize the cipher context once so that we don't have to do it for // initialize the cipher context once so that we don't have to do it for
@ -377,10 +379,10 @@ void initKey(const shared_ptr<SSLKey> &key, const EVP_CIPHER *_blockCipher,
SSL_Cipher::SSL_Cipher(const Interface &iface_, SSL_Cipher::SSL_Cipher(const Interface &iface_,
const Interface &realIface_, const Interface &realIface_,
const EVP_CIPHER *blockCipher, const EVP_CIPHER *blockCipher,
const EVP_CIPHER *streamCipher, const EVP_CIPHER *streamCipher,
int keySize_) int keySize_)
{ {
this->iface = iface_; this->iface = iface_;
this->realIface = realIface_; this->realIface = realIface_;
@ -393,8 +395,8 @@ SSL_Cipher::SSL_Cipher(const Interface &iface_,
rAssert(_ivLength <= _keySize); rAssert(_ivLength <= _keySize);
VLOG(1) << "allocated cipher " << iface.name() VLOG(1) << "allocated cipher " << iface.name()
<< ", keySize " << _keySize << ", keySize " << _keySize
<< ", ivlength " << _ivLength; << ", ivlength " << _ivLength;
// EVP_CIPHER_key_length isn't useful for variable-length ciphers like // EVP_CIPHER_key_length isn't useful for variable-length ciphers like
// Blowfish. Version 1 relied upon it incorrectly. // Blowfish. Version 1 relied upon it incorrectly.
@ -402,8 +404,8 @@ SSL_Cipher::SSL_Cipher(const Interface &iface_,
&& iface.major() == 1) && iface.major() == 1)
{ {
LOG(WARNING) << "Running in backward compatibilty mode for 1.0 - \n" LOG(WARNING) << "Running in backward compatibilty mode for 1.0 - \n"
<< "key is really " << EVP_CIPHER_key_length( _blockCipher ) * 8 << "key is really " << EVP_CIPHER_key_length( _blockCipher ) * 8
<< " bits, not " << _keySize * 8; << " bits, not " << _keySize * 8;
} }
} }
@ -422,10 +424,10 @@ Interface SSL_Cipher::interface() const
This algorithm must remain constant for backward compatibility, as this key This algorithm must remain constant for backward compatibility, as this key
is used to encipher/decipher the master key. is used to encipher/decipher the master key.
*/ */
CipherKey SSL_Cipher::newKey(const char *password, int passwdLength, CipherKey SSL_Cipher::newKey(const char *password, int passwdLength,
int &iterationCount, long desiredDuration, int &iterationCount, long desiredDuration,
const byte *salt, int saltLen) const byte *salt, int saltLen)
{ {
shared_ptr<SSLKey> key( new SSLKey( _keySize, _ivLength) ); shared_ptr<SSLKey> key( new SSLKey( _keySize, _ivLength) );
@ -477,7 +479,7 @@ CipherKey SSL_Cipher::newKey(const char *password, int passwdLength)
if(bytes != (int)_keySize) if(bytes != (int)_keySize)
{ {
LOG(WARNING) << "newKey: BytesToKey returned " << bytes LOG(WARNING) << "newKey: BytesToKey returned " << bytes
<< ", expecting " << _keySize << " key bytes"; << ", expecting " << _keySize << " key bytes";
} }
} else } else
{ {
@ -499,7 +501,7 @@ CipherKey SSL_Cipher::newKey(const char *password, int passwdLength)
This algorithm can change at any time without affecting backward This algorithm can change at any time without affecting backward
compatibility. compatibility.
*/ */
CipherKey SSL_Cipher::newRandomKey() CipherKey SSL_Cipher::newRandomKey()
{ {
const int bufLen = MAX_KEYLENGTH; const int bufLen = MAX_KEYLENGTH;
@ -531,11 +533,11 @@ CipherKey SSL_Cipher::newRandomKey()
/* /*
Compute a 64-bit check value for the data using HMAC. Compute a 64-bit check value for the data using HMAC.
*/ */
static uint64_t _checksum_64(SSLKey *key, static uint64_t _checksum_64(SSLKey *key,
const byte *data, const byte *data,
int dataLen, int dataLen,
uint64_t *chainedIV) uint64_t *chainedIV)
{ {
rAssert( dataLen > 0 ); rAssert( dataLen > 0 );
Lock lock( key->mutex ); Lock lock( key->mutex );
@ -576,7 +578,7 @@ static uint64_t _checksum_64(SSLKey *key,
} }
bool SSL_Cipher::randomize( byte *buf, int len, bool SSL_Cipher::randomize( byte *buf, int len,
bool strongRandom ) const bool strongRandom ) const
{ {
// to avoid warnings of uninitialized data from valgrind // to avoid warnings of uninitialized data from valgrind
memset(buf, 0, len); memset(buf, 0, len);
@ -599,7 +601,7 @@ bool SSL_Cipher::randomize( byte *buf, int len,
} }
uint64_t SSL_Cipher::MAC_64( const byte *data, int len, uint64_t SSL_Cipher::MAC_64( const byte *data, int len,
const CipherKey &key, uint64_t *chainedIV ) const const CipherKey &key, uint64_t *chainedIV ) const
{ {
shared_ptr<SSLKey> mk = dynamic_pointer_cast<SSLKey>(key); shared_ptr<SSLKey> mk = dynamic_pointer_cast<SSLKey>(key);
uint64_t tmp = _checksum_64( mk.get(), data, len, chainedIV ); uint64_t tmp = _checksum_64( mk.get(), data, len, chainedIV );
@ -611,7 +613,7 @@ uint64_t SSL_Cipher::MAC_64( const byte *data, int len,
} }
CipherKey SSL_Cipher::readKey(const byte *data, CipherKey SSL_Cipher::readKey(const byte *data,
const CipherKey &masterKey, bool checkKey) const CipherKey &masterKey, bool checkKey)
{ {
shared_ptr<SSLKey> mk = dynamic_pointer_cast<SSLKey>(masterKey); shared_ptr<SSLKey> mk = dynamic_pointer_cast<SSLKey>(masterKey);
rAssert(mk->keySize == _keySize); rAssert(mk->keySize == _keySize);
@ -638,8 +640,8 @@ CipherKey SSL_Cipher::readKey(const byte *data,
if(checksum2 != checksum && checkKey) if(checksum2 != checksum && checkKey)
{ {
VLOG(1) << "checksum mismatch: expected " << checksum VLOG(1) << "checksum mismatch: expected " << checksum
<< ", got " << checksum2 << ", got " << checksum2
<< "on decode of " << _keySize + _ivLength << " bytes"; << "on decode of " << _keySize + _ivLength << " bytes";
OPENSSL_cleanse(tmpBuf, sizeof(tmpBuf)); OPENSSL_cleanse(tmpBuf, sizeof(tmpBuf));
return CipherKey(); return CipherKey();
} }
@ -656,7 +658,7 @@ CipherKey SSL_Cipher::readKey(const byte *data,
} }
void SSL_Cipher::writeKey(const CipherKey &ckey, byte *data, void SSL_Cipher::writeKey(const CipherKey &ckey, byte *data,
const CipherKey &masterKey) const CipherKey &masterKey)
{ {
shared_ptr<SSLKey> key = dynamic_pointer_cast<SSLKey>(ckey); shared_ptr<SSLKey> key = dynamic_pointer_cast<SSLKey>(ckey);
rAssert(key->keySize == _keySize); rAssert(key->keySize == _keySize);
@ -771,7 +773,7 @@ void SSL_Cipher::setIVec_old(byte *ivec,
{ {
unsigned int var1 = 0x060a4011 * seed; unsigned int var1 = 0x060a4011 * seed;
unsigned int var2 = 0x0221040d * (seed ^ 0xD3FEA11C); unsigned int var2 = 0x0221040d * (seed ^ 0xD3FEA11C);
memcpy( ivec, IVData(key), _ivLength ); memcpy( ivec, IVData(key), _ivLength );
ivec[0] ^= (var1 >> 24) & 0xff; ivec[0] ^= (var1 >> 24) & 0xff;
@ -861,7 +863,7 @@ bool SSL_Cipher::streamEncode(byte *buf, int size,
dstLen += tmpLen; dstLen += tmpLen;
LOG_IF(ERROR, dstLen != size) << "encoding " << size LOG_IF(ERROR, dstLen != size) << "encoding " << size
<< " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)"; << " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)";
return true; return true;
} }
@ -897,7 +899,7 @@ bool SSL_Cipher::streamDecode(byte *buf, int size,
dstLen += tmpLen; dstLen += tmpLen;
LOG_IF(ERROR, dstLen != size) << "encoding " << size LOG_IF(ERROR, dstLen != size) << "encoding " << size
<< " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)"; << " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)";
return true; return true;
} }
@ -928,7 +930,7 @@ bool SSL_Cipher::blockEncode(byte *buf, int size,
dstLen += tmpLen; dstLen += tmpLen;
LOG_IF(ERROR, dstLen != size) << "encoding " << size LOG_IF(ERROR, dstLen != size) << "encoding " << size
<< " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)"; << " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)";
return true; return true;
} }
@ -958,7 +960,7 @@ bool SSL_Cipher::blockDecode(byte *buf, int size,
dstLen += tmpLen; dstLen += tmpLen;
LOG_IF(ERROR, dstLen != size) << "decoding " << size LOG_IF(ERROR, dstLen != size) << "decoding " << size
<< " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)"; << " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)";
return true; return true;
} }

20
cipher/StreamCipher.cpp Normal file
View File

@ -0,0 +1,20 @@
#include "cipher/StreamCipher.h"
namespace encfs {
Registry<StreamCipher>& StreamCipher::GetRegistry()
{
static Registry<StreamCipher> registry;
return registry;
}
StreamCipher::StreamCipher()
{
}
StreamCipher::~StreamCipher()
{
}
} // namespace encfs

60
cipher/StreamCipher.h Normal file
View File

@ -0,0 +1,60 @@
/*****************************************************************************
* 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 _STREAMCIPHER_incl_
#define _STREAMCIPHER_incl_
#include "base/Range.h"
#include "base/Registry.h"
#include "base/shared_ptr.h"
#include "base/types.h"
namespace encfs {
class StreamCipher
{
public:
static Registry<StreamCipher>& GetRegistry();
struct Properties {
Range keySize;
std::string cipher;
std::string mode;
std::string library;
};
StreamCipher();
virtual ~StreamCipher();
virtual bool setKey(const byte *key, int keyLength) =0;
virtual bool randomKey(int keyLength) =0;
virtual bool encrypt(const byte *iv, const byte *in,
byte *out, int numBytes) =0;
virtual bool decrypt(const byte *iv, const byte *in,
byte *out, int numBytes) =0;
};
} // namespace encfs
#endif

View File

@ -2,7 +2,7 @@
* Author: Valient Gough <vgough@pobox.com> * Author: Valient Gough <vgough@pobox.com>
* *
***************************************************************************** *****************************************************************************
* Copyright (c) 2007, Valient Gough * Copyright (c) 2007-2013, Valient Gough
* *
* This program is free software: you can redistribute it and/or modify it * 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 * under the terms of the GNU Lesser General Public License as published by the
@ -20,10 +20,16 @@
#include "cipher/openssl.h" #include "cipher/openssl.h"
#include <cstring>
#include <ctime>
#include <pthread.h> #include <pthread.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <glog/logging.h> #include <glog/logging.h>
#include "base/config.h"
#define NO_DES #define NO_DES
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/rand.h> #include <openssl/rand.h>
@ -31,8 +37,345 @@
#include <openssl/engine.h> #include <openssl/engine.h>
#endif #endif
#include <openssl/blowfish.h>
#include <openssl/err.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "base/Error.h"
#include "base/i18n.h"
#include "base/Mutex.h"
#include "base/Range.h"
#include "cipher/BlockCipher.h"
#include "cipher/MAC.h"
#include "cipher/MemoryPool.h"
#include "cipher/PBKDF.h"
#include "cipher/StreamCipher.h"
using namespace std;
namespace encfs { namespace encfs {
const int MAX_KEYLENGTH = 64; // in bytes (256 bit)
const int MAX_IVLENGTH = 16;
const int KEY_CHECKSUM_BYTES = 4;
#ifndef MIN
inline int MIN(int a, int b)
{
return (a < b) ? a : b;
}
#endif
// Base for {Block,Stream}Cipher implementation.
class OpenSSLCipher : public BlockCipher {
public:
OpenSSLCipher() {
}
virtual ~OpenSSLCipher() {
EVP_CIPHER_CTX_cleanup( &enc );
EVP_CIPHER_CTX_cleanup( &dec );
}
bool rekey(const EVP_CIPHER *cipher, const byte *key, int length) {
EVP_CIPHER_CTX_init( &enc );
EVP_EncryptInit_ex( &enc, cipher, NULL, NULL, NULL);
EVP_CIPHER_CTX_set_key_length( &enc, length );
EVP_CIPHER_CTX_set_padding( &enc, 0 );
EVP_EncryptInit_ex( &enc, NULL, NULL, key, NULL);
EVP_CIPHER_CTX_init( &dec );
EVP_DecryptInit_ex( &dec, cipher, NULL, NULL, NULL);
EVP_CIPHER_CTX_set_key_length( &dec, length );
EVP_CIPHER_CTX_set_padding( &dec, 0 );
EVP_DecryptInit_ex( &dec, NULL, NULL, key, NULL);
return true;
}
static bool randomize(byte *out, int len) {
int result = RAND_bytes( out, len );
if(result != 1)
{
char errStr[120]; // specs require string at least 120 bytes long..
unsigned long errVal = 0;
if((errVal = ERR_get_error()) != 0)
LOG(ERROR) << "openssl error: " << ERR_error_string( errVal, errStr );
return false;
}
return true;
}
// Rekey with random key.
bool rekey(const EVP_CIPHER *cipher, int keyLength) {
SecureMem key(keyLength);
if (!randomize(key.data, key.size))
return false;
return rekey(cipher, key.data, key.size);
}
virtual int blockSize() const {
return EVP_CIPHER_CTX_block_size(&enc);
}
virtual bool encrypt(const byte *ivec, const byte *in,
byte *out, int size) {
int dstLen = 0, tmpLen = 0;
EVP_EncryptInit_ex( &enc, NULL, NULL, NULL, ivec);
EVP_EncryptUpdate( &enc, out, &dstLen, in, size);
EVP_EncryptFinal_ex( &enc, out+dstLen, &tmpLen );
dstLen += tmpLen;
if (dstLen != size) {
LOG(ERROR) << "encoding " << size
<< " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)";
return false;
}
return true;
}
virtual bool decrypt(const byte *ivec, const byte *in,
byte *out, int size) {
int dstLen = 0, tmpLen = 0;
EVP_DecryptInit_ex( &dec, NULL, NULL, NULL, ivec);
EVP_DecryptUpdate( &dec, out, &dstLen, in, size );
EVP_DecryptFinal_ex( &dec, out+dstLen, &tmpLen );
dstLen += tmpLen;
if (dstLen != size) {
LOG(ERROR) << "decoding " << size
<< " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)";
return false;
}
return true;
}
private:
EVP_CIPHER_CTX enc;
EVP_CIPHER_CTX dec;
};
#if defined(HAVE_EVP_BF)
static Range BfKeyRange(128,256,32);
class BfCbcBlockCipher : public OpenSSLCipher {
public:
BfCbcBlockCipher() {}
virtual ~BfCbcBlockCipher() {}
virtual bool setKey(const byte *key, int length) {
if (BfKeyRange.allowed(length * 8))
return rekey(EVP_bf_cbc(), key, length);
else
return false;
}
virtual bool randomKey(int length) {
return BfKeyRange.allowed(length * 8) && rekey(EVP_bf_cbc(), length);
}
static Properties GetProperties() {
Properties props;
props.keySize = BfKeyRange;
props.cipher = "Blowfish";
props.mode = "CBC";
props.library = "OpenSSL";
return props;
}
};
REGISTER_CLASS(BfCbcBlockCipher, BlockCipher);
class BfCfbStreamCipher : public OpenSSLCipher {
public:
BfCfbStreamCipher() {}
virtual ~BfCfbStreamCipher() {}
virtual bool setKey(const byte *key, int length) {
return BfKeyRange.allowed(length * 8) && rekey(EVP_bf_cfb(), key, length);
}
virtual bool randomKey(int length) {
return BfKeyRange.allowed(length * 8) && rekey(EVP_bf_cfb(), length);
}
static Properties GetProperties() {
Properties props;
props.keySize = BfKeyRange;
props.cipher = "Blowfish";
props.mode = "CFB";
props.library = "OpenSSL";
return props;
}
};
REGISTER_CLASS(BfCfbStreamCipher, StreamCipher);
#endif
#if defined(HAVE_EVP_AES)
static Range AesKeyRange(128,256,64);
class AesCbcBlockCipher : public OpenSSLCipher {
public:
AesCbcBlockCipher() {}
virtual ~AesCbcBlockCipher() {}
virtual bool setKey(const byte *key, int length) {
const EVP_CIPHER *cipher = getCipher(length);
return (cipher != NULL) && rekey(cipher, key, length);
}
virtual bool randomKey(int length) {
const EVP_CIPHER *cipher = getCipher(length);
return (cipher != NULL) && rekey(cipher, length);
}
static const EVP_CIPHER *getCipher(int keyLength) {
switch(keyLength * 8)
{
case 128: return EVP_aes_128_cbc();
case 192: return EVP_aes_192_cbc();
case 256: return EVP_aes_256_cbc();
default:
return NULL;
}
}
static Properties GetProperties() {
Properties props;
props.keySize = AesKeyRange;
props.cipher = "AES";
props.mode = "CBC";
props.library = "OpenSSL";
return props;
}
};
REGISTER_CLASS(AesCbcBlockCipher, BlockCipher);
#endif
#if defined(HAVE_EVP_AES_XTS)
static Range AesXtsKeyRange(128,256,128);
class AesXtsBlockCipher : public OpenSSLCipher {
public:
AesXtsBlockCipher() {}
virtual ~AesXtsBlockCipher() {}
virtual bool setKey(const byte *key, int length) {
const EVP_CIPHER *cipher = getCipher(length);
return (cipher != NULL) && rekey(cipher, key, length);
}
virtual bool randomKey(int length) {
const EVP_CIPHER *cipher = getCipher(length);
return (cipher != NULL) && rekey(cipher, length);
}
static const EVP_CIPHER *getCipher(int keyLength) {
switch(keyLength * 8)
{
case 128: return EVP_aes_128_xts();
case 256: return EVP_aes_256_xts();
default: return NULL;
}
}
static Properties GetProperties() {
Properties props;
props.keySize = AesXtsKeyRange;
props.cipher = "AES";
props.mode = "XTS";
props.library = "OpenSSL";
return props;
}
};
REGISTER_CLASS(AesXtsBlockCipher, BlockCipher);
#endif
class Sha1HMac : public MessageAuthenticationCode {
public:
Sha1HMac() {}
virtual ~Sha1HMac() {
HMAC_CTX_cleanup(&ctx);
}
virtual int outputSize() const {
return 20; // 160 bit.
}
virtual bool setKey(const byte *key, int keyLength) {
HMAC_CTX_init(&ctx);
HMAC_Init_ex(&ctx, key, keyLength, EVP_sha1(), 0);
return true;
}
virtual bool randomKey(int keyLength) {
SecureMem key(keyLength);
return OpenSSLCipher::randomize(key.data, key.size)
&& setKey(key.data, key.size);
}
virtual void reset() {
HMAC_Init_ex(&ctx, 0, 0, 0, 0);
}
virtual bool update(const byte *in, int length) {
HMAC_Update(&ctx, in, length);
return true;
}
virtual bool write(byte *out) {
unsigned int outSize = 0;
HMAC_Final(&ctx, (unsigned char *)out, &outSize);
CHECK_EQ(outputSize(), outSize) << "Invalid HMAC output size";
return true;
}
static Properties GetProperties() {
Properties props;
props.blockSize = 20;
props.hashFunction = "SHA-1";
props.mode = "HMAC";
props.library = "OpenSSL";
return props;
}
private:
HMAC_CTX ctx;
};
REGISTER_CLASS(Sha1HMac, MessageAuthenticationCode);
class PbkdfPkcs5HmacSha1 : public PBKDF {
public:
PbkdfPkcs5HmacSha1() {}
virtual ~PbkdfPkcs5HmacSha1() {}
virtual bool makeKey(const char *password, int passwordLength,
const byte *salt, int saltLength,
int numIterations,
byte *outKey, int keyLength) const {
return PKCS5_PBKDF2_HMAC_SHA1(
password, passwordLength,
const_cast<byte *>(salt), saltLength,
numIterations, keyLength, outKey) == 1;
}
static Properties GetProperties() {
Properties props;
props.mode = "PKCS5_PBKDF2_HMAC_SHA1";
props.library = "OpenSSL";
return props;
}
};
REGISTER_CLASS(PbkdfPkcs5HmacSha1, PBKDF);
unsigned long pthreads_thread_id() unsigned long pthreads_thread_id()
{ {
return (unsigned long)pthread_self(); return (unsigned long)pthread_self();
@ -73,7 +416,7 @@ void pthreads_locking_cleanup()
} }
} }
void openssl_init(bool threaded) void OpenSSL::init(bool threaded)
{ {
// initialize the SSL library // initialize the SSL library
SSL_load_error_strings(); SSL_load_error_strings();
@ -99,7 +442,7 @@ void openssl_init(bool threaded)
} }
} }
void openssl_shutdown(bool threaded) void OpenSSL::shutdown(bool threaded)
{ {
#ifndef OPENSSL_NO_ENGINE #ifndef OPENSSL_NO_ENGINE
ENGINE_cleanup(); ENGINE_cleanup();
@ -109,4 +452,10 @@ void openssl_shutdown(bool threaded)
pthreads_locking_cleanup(); pthreads_locking_cleanup();
} }
void OpenSSL::registerCiphers()
{
// Nothing required.. Just need to reference this code block to get static
// initializers.
}
} // namespace encfs } // namespace encfs

View File

@ -21,10 +21,17 @@
#ifndef _openssl_incl_ #ifndef _openssl_incl_
#define _openssl_incl_ #define _openssl_incl_
#include "base/Registry.h"
namespace encfs { namespace encfs {
void openssl_init(bool isThreaded); class OpenSSL {
void openssl_shutdown(bool isThreaded); public:
static void init(bool isThreaded);
static void shutdown(bool isThreaded);
static void registerCiphers();
};
} // namespace encfs } // namespace encfs

30
cipher/testing.cpp Normal file
View File

@ -0,0 +1,30 @@
#include <gtest/gtest.h>
#include "cipher/testing.h"
namespace encfs {
std::string stringToHex(const byte *data, int len) {
static const char lookup[] = "0123456789abcdef";
std::string out;
out.reserve(2 * len);
for (int i = 0; i < len; ++i) {
unsigned int c = (unsigned int)data[i] & 0xff;
int first = (unsigned int)c >> 4;
int second = (unsigned int)c & 15;
out.push_back(lookup[first]);
out.push_back(lookup[second]);
}
return out;
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
} // namespace encfs

36
cipher/testing.h Normal file
View File

@ -0,0 +1,36 @@
/*****************************************************************************
* 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 _CIPHER_TESTING_incl_
#define _CIPHER_TESTING_incl_
#include <string>
#include "base/types.h"
namespace encfs {
std::string stringToHex(const byte *data, int len);
} // namespace encfs
#endif

View File

@ -8,10 +8,6 @@ link_directories (${Encfs_BINARY_DIR}/cipher)
include_directories (${Encfs_SOURCE_DIR}/fs) include_directories (${Encfs_SOURCE_DIR}/fs)
link_directories (${Encfs_BINARY_DIR}/fs) link_directories (${Encfs_BINARY_DIR}/fs)
# TODO: move FUSE code into encfs-fs.
find_package (FUSE REQUIRED)
include_directories (${FUSE_INCLUDE_DIR})
include_directories (${CMAKE_BINARY_DIR}/base) include_directories (${CMAKE_BINARY_DIR}/base)
add_executable (encfs add_executable (encfs

View File

@ -578,7 +578,7 @@ int main(int argc, char *argv[])
// encfs_oper.fsetattr_x // encfs_oper.fsetattr_x
#endif #endif
openssl_init( encfsArgs->isThreaded ); OpenSSL::init( encfsArgs->isThreaded );
// context is not a smart pointer because it will live for the life of // context is not a smart pointer because it will live for the life of
// the filesystem. // the filesystem.
@ -671,7 +671,7 @@ int main(int argc, char *argv[])
ctx->setRoot( shared_ptr<DirNode>() ); ctx->setRoot( shared_ptr<DirNode>() );
MemoryPool::destroyAll(); MemoryPool::destroyAll();
openssl_shutdown( encfsArgs->isThreaded ); OpenSSL::shutdown( encfsArgs->isThreaded );
return returnCode; return returnCode;
} }

View File

@ -1,12 +1,6 @@
find_package (FUSE REQUIRED) protobuf_generate_cpp (PROTO_SRCS PROTO_HDRS ${PROJECT_SOURCE_DIR}/protos/fsconfig.proto)
include_directories (${FUSE_INCLUDE_DIR})
protobuf_generate_cpp (PROTO_SRCS PROTO_HDRS ${Encfs_SOURCE_DIR}/protos/fsconfig.proto) include_directories (${PROJECT_BINARY_DIR}/base)
enable_testing ()
find_package (GTest)
include_directories (${Encfs_BINARY_DIR}/base)
add_library (encfs-fs add_library (encfs-fs
encfs.cpp encfs.cpp
Context.cpp Context.cpp
@ -32,11 +26,11 @@ target_link_libraries (encfs-fs
# Unit tests are optional, depends on libgtest (Google's C++ test framework). # Unit tests are optional, depends on libgtest (Google's C++ test framework).
if (GTEST_FOUND) if (GTEST_FOUND)
link_directories (${Encfs_BINARY_DIR}/base) link_directories (${PROJECT_BINARY_DIR}/base)
link_directories (${Encfs_BINARY_DIR}/cipher) link_directories (${PROJECT_BINARY_DIR}/cipher)
include_directories (${GTEST_INCLUDE_DIR}) include_directories (${GTEST_INCLUDE_DIR})
add_executable (unittests add_executable (fs-tests
MemBlockFileIO.cpp MemBlockFileIO.cpp
MemFileIO.cpp MemFileIO.cpp
testing.cpp testing.cpp
@ -44,7 +38,7 @@ if (GTEST_FOUND)
test_BlockIO.cpp test_BlockIO.cpp
) )
target_link_libraries (unittests target_link_libraries (fs-tests
${GTEST_BOTH_LIBRARIES} ${GTEST_BOTH_LIBRARIES}
encfs-fs encfs-fs
encfs-cipher encfs-cipher
@ -52,8 +46,6 @@ if (GTEST_FOUND)
${GLOG_LIBRARIES} ${GLOG_LIBRARIES}
) )
add_test (UnitTests unittests) add_test (FSTests fs-tests)
GTEST_ADD_TESTS (unittests "${UnitTestArgs}" test_IO.cpp test_BlockIO.cpp) GTEST_ADD_TESTS (fs-tests "${FSTestArgs}" test_IO.cpp test_BlockIO.cpp)
add_custom_target (test COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS unittests)
endif (GTEST_FOUND) endif (GTEST_FOUND)

View File

@ -1349,7 +1349,7 @@ std::string readPassword( int FD )
if(rdSize > 0) if(rdSize > 0)
{ {
result.append( buf->data, rdSize ); result.append( (char*)buf->data, rdSize );
} else } else
break; break;
} }
@ -1436,7 +1436,7 @@ SecureMem *passwordFromProgram(const std::string &passProg,
SecureMem *result = new SecureMem(password.length()+1); SecureMem *result = new SecureMem(password.length()+1);
if (result) if (result)
strncpy(result->data, password.c_str(), result->size); strncpy((char *)result->data, password.c_str(), result->size);
password.assign(password.length(), '\0'); password.assign(password.length(), '\0');
return result; return result;
@ -1446,11 +1446,11 @@ SecureMem *passwordFromStdin()
{ {
SecureMem *buf = new SecureMem(MaxPassBuf); SecureMem *buf = new SecureMem(MaxPassBuf);
char *res = fgets( buf->data, buf->size, stdin ); char *res = fgets( (char *)buf->data, buf->size, stdin );
if (res) if (res)
{ {
// Kill the trailing newline. // Kill the trailing newline.
int last = strnlen(buf->data, buf->size); int last = strnlen((char *)buf->data, buf->size);
if (last > 0 && buf->data[last-1] == '\n') if (last > 0 && buf->data[last-1] == '\n')
buf->data[ last-1 ] = '\0'; buf->data[ last-1 ] = '\0';
} }
@ -1464,7 +1464,7 @@ SecureMem *passwordFromPrompt()
// xgroup(common) // xgroup(common)
char *res = readpassphrase( _("EncFS Password: "), char *res = readpassphrase( _("EncFS Password: "),
buf->data, buf->size-1, RPP_ECHO_OFF ); (char *)buf->data, buf->size-1, RPP_ECHO_OFF );
if (!res) if (!res)
{ {
delete buf; delete buf;
@ -1483,12 +1483,13 @@ SecureMem *passwordFromPrompts()
{ {
// xgroup(common) // xgroup(common)
char *res1 = readpassphrase(_("New Encfs Password: "), char *res1 = readpassphrase(_("New Encfs Password: "),
buf->data, buf->size-1, RPP_ECHO_OFF); (char *)buf->data, buf->size-1, RPP_ECHO_OFF);
// xgroup(common) // xgroup(common)
char *res2 = readpassphrase(_("Verify Encfs Password: "), char *res2 = readpassphrase(_("Verify Encfs Password: "),
buf2->data, buf2->size-1, RPP_ECHO_OFF); (char *)buf2->data, buf2->size-1, RPP_ECHO_OFF);
if(res1 && res2 && !strncmp(buf->data, buf2->data, MaxPassBuf)) if(res1 && res2
&& !strncmp((char*)buf->data, (char*)buf2->data, MaxPassBuf))
{ {
break; break;
} else } else
@ -1514,7 +1515,8 @@ CipherKey getUserKey(const EncfsConfig &config, bool useStdin)
if (password) if (password)
{ {
userKey = decryptKey(config, password->data, strlen(password->data)); userKey = decryptKey(config, (char*)password->data,
strlen((char*)password->data));
delete password; delete password;
} }
@ -1529,7 +1531,8 @@ CipherKey getUserKey( const EncfsConfig &config, const std::string &passProg,
if (password) if (password)
{ {
result = decryptKey(config, password->data, strlen(password->data)); result = decryptKey(config, (char*)password->data,
strlen((char*)password->data));
delete password; delete password;
} }
@ -1552,7 +1555,8 @@ CipherKey getNewUserKey(EncfsConfig &config,
if (password) if (password)
{ {
result = makeNewKey(config, password->data, strlen(password->data)); result = makeNewKey(config, (char*)password->data,
strlen((char*)password->data));
delete password; delete password;
} }

View File

@ -8,10 +8,6 @@ link_directories (${Encfs_BINARY_DIR}/cipher)
include_directories (${Encfs_SOURCE_DIR}/fs) include_directories (${Encfs_SOURCE_DIR}/fs)
link_directories (${Encfs_BINARY_DIR}/fs) link_directories (${Encfs_BINARY_DIR}/fs)
# TODO: move FUSE code into encfs-fs.
find_package (FUSE REQUIRED)
include_directories (${FUSE_INCLUDE_DIR})
include_directories (${CMAKE_BINARY_DIR}/base) include_directories (${CMAKE_BINARY_DIR}/base)

View File

@ -24,6 +24,9 @@
#include "base/i18n.h" #include "base/i18n.h"
#include "cipher/Cipher.h" #include "cipher/Cipher.h"
#include "cipher/BlockCipher.h"
#include "cipher/MAC.h"
#include "cipher/StreamCipher.h"
#include "fs/FileUtils.h" #include "fs/FileUtils.h"
#include "fs/Context.h" #include "fs/Context.h"
@ -35,6 +38,7 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <cstdio> #include <cstdio>
#include <list>
#include <getopt.h> #include <getopt.h>
#include <sys/types.h> #include <sys/types.h>
@ -52,6 +56,7 @@ using namespace encfs;
static int showInfo( int argc, char **argv ); static int showInfo( int argc, char **argv );
static int showVersion( int argc, char **argv ); static int showVersion( int argc, char **argv );
static int showCiphers( int argc, char **argv );
static int chpasswd( int argc, char **argv ); static int chpasswd( int argc, char **argv );
static int chpasswdAutomaticly( int argc, char **argv ); static int chpasswdAutomaticly( int argc, char **argv );
static int cmd_ls( int argc, char **argv ); static int cmd_ls( int argc, char **argv );
@ -101,6 +106,9 @@ struct CommandOpts
{"export", 2, 2, cmd_export, "(root dir) path", {"export", 2, 2, cmd_export, "(root dir) path",
// xgroup(usage) // xgroup(usage)
gettext_noop(" -- decrypts a volume and writes results to path")}, gettext_noop(" -- decrypts a volume and writes results to path")},
{"--ciphers", 0, 0, showCiphers, "",
// xgroup(usage)
gettext_noop(" -- show available ciphers")},
{"--version", 0, 0, showVersion, "", {"--version", 0, 0, showVersion, "",
// xgroup(usage) // xgroup(usage)
gettext_noop(" -- print version number and exit")}, gettext_noop(" -- print version number and exit")},
@ -140,7 +148,7 @@ static bool checkDir( string &rootDir )
{ {
if( !isDirectory( rootDir.c_str() )) if( !isDirectory( rootDir.c_str() ))
{ {
cerr << autosprintf(_("directory %s does not exist.\n"), cout << autosprintf(_("directory %s does not exist.\n"),
rootDir.c_str()); rootDir.c_str());
return false; return false;
} }
@ -155,11 +163,45 @@ static int showVersion( int argc, char **argv )
(void)argc; (void)argc;
(void)argv; (void)argv;
// xgroup(usage) // xgroup(usage)
cerr << autosprintf(_("encfsctl version %s"), VERSION) << "\n"; cout << autosprintf(_("encfsctl version %s"), VERSION) << "\n";
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
static int showCiphers( int argc, char **argv )
{
(void)argc;
(void)argv;
list<string> names = BlockCipher::GetRegistry().GetAll();
for (const string& name : names) {
auto props = BlockCipher::GetRegistry().GetProperties(name.c_str());
cout << _("Implementation: ") << name << "\n";
cout << "\t" << _("Block cipher: ") << props->cipher << " / " << props->mode
<< " ( " << autosprintf(_("via %s"), props->library.c_str()) << " )\n";
cout << "\t" << _("Key Sizes: ") << props->keySize << "\n";
}
names = StreamCipher::GetRegistry().GetAll();
for (const string& name : names) {
auto props = StreamCipher::GetRegistry().GetProperties(name.c_str());
cout << _("Implementation: ") << name << "\n";
cout << "\t" << _("Stream cipher: ") << props->cipher << " / " << props->mode
<< " ( " << autosprintf(_("via %s"), props->library.c_str()) << " )\n";
cout << "\t" << _("Key Sizes: ") << props->keySize << "\n";
}
names = MessageAuthenticationCode::GetRegistry().GetAll();
for (const string& name : names) {
auto props = MessageAuthenticationCode::GetRegistry()
.GetProperties(name.c_str());
cout << _("Implementation: ") << name << "\n";
cout << "\t" << _("HMAC: ") << props->hashFunction << " / " << props->mode
<< " ( " << autosprintf(_("via %s"), props->library.c_str()) << " )\n";
cout << "\t" << _("Block size: ") << props->blockSize << "\n";
}
return EXIT_SUCCESS;
}
static int showInfo( int argc, char **argv ) static int showInfo( int argc, char **argv )
{ {
(void)argc; (void)argc;
@ -676,7 +718,7 @@ static int cmd_showcruft( int argc, char **argv )
int filesFound = showcruft( rootInfo, "/" ); int filesFound = showcruft( rootInfo, "/" );
cerr << autosprintf("Found %i invalid file(s).", filesFound) << "\n"; cout << autosprintf("Found %i invalid file(s).", filesFound) << "\n";
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }