diff --git a/CMakeLists.txt b/CMakeLists.txt index 660d9cd..e4764f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,8 +11,28 @@ option (BUILD_SHARED_LIBS "Build dynamic link libraries" OFF) set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMakeModules/") +# Tweak compiler flags. 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_VERSION_MAJOR ${ENCFS_MAJOR}) set (CPACK_PACKAGE_VERSION_MINOR ${ENCFS_MINOR}) @@ -23,6 +43,7 @@ set (CPACK_SOURCE_IGNORE_FILES "/build/") include (CPack) +# Check for external files. include (CheckIncludeFileCXX) check_include_file_cxx (attr/xattr.h HAVE_ATTR_XATTR_H) check_include_file_cxx (sys/xattr.h HAVE_SYS_XATTR_H) @@ -45,11 +66,7 @@ CHECK_CXX_SOURCE_COMPILES ("#include #include 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) -if (APPLE) - add_definitions (-D__FreeBSD__=10) -endif (APPLE) - +# Libraries or programs used for multiple modules. find_package (Protobuf REQUIRED) include_directories (${PROTOBUF_INCLUDE_DIR}) @@ -58,9 +75,16 @@ include_directories (${GLOG_INCLUDE_DIRS}) 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_SOURCE_DIR}) +# Subdirectories. add_subdirectory(base) add_subdirectory(cipher) add_subdirectory(fs) @@ -68,3 +92,10 @@ add_subdirectory(encfs) add_subdirectory(util) 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) + + diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt index f27f773..9287894 100644 --- a/base/CMakeLists.txt +++ b/base/CMakeLists.txt @@ -17,6 +17,8 @@ add_library (encfs-base ConfigVar.cpp Error.cpp Interface.cpp + Range.h + Registry.h XmlReader.cpp ${PROTO_SRCS} ${PROTO_HDRS} diff --git a/base/Range.h b/base/Range.h index f5f6a81..1639d6b 100644 --- a/base/Range.h +++ b/base/Range.h @@ -21,6 +21,8 @@ #ifndef _Range_incl_ #define _Range_incl_ +#include + namespace encfs { class Range @@ -42,6 +44,18 @@ public: 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) { this->minVal = minMax; diff --git a/base/Registry.h b/base/Registry.h new file mode 100644 index 0000000..49c66c7 --- /dev/null +++ b/base/Registry.h @@ -0,0 +1,94 @@ +#ifndef REGISTRY_H +#define REGISTRY_H + +#include +#include + +namespace encfs { + +template +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 GetAll() const { + std::list 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 data; +}; + +template +class Registrar +{ +public: + Registrar(const char *name) + { + BASE::GetRegistry().Register(name, + Registrar::Construct, + T::GetProperties()); + } + + static BASE *Construct() { + return new T(); + } +}; + +#define DECLARE_REGISTERABLE_TYPE(TYPE) \ + static Registry& GetRegistry() + +#define DEFINE_REGISTERABLE_TYPE(TYPE) \ + Registry& TYPE::GetRegistry() { \ + static Registry registry; \ + return registry; \ + } + +#define REGISTER_CLASS(DERIVED, BASE) \ + static Registrar registrar_##DERIVED(#DERIVED) + +} // namespace encfs + +#endif // REGISTRY_H diff --git a/cipher/BlockCipher.cpp b/cipher/BlockCipher.cpp new file mode 100644 index 0000000..3bc9d15 --- /dev/null +++ b/cipher/BlockCipher.cpp @@ -0,0 +1,29 @@ +#include "cipher/BlockCipher.h" + +// TODO: add ifdef when OpenSSL becomes optional. +#include "cipher/openssl.h" + +namespace encfs { + +Registry& BlockCipher::GetRegistry() +{ + static Registry registry; + static bool first = true; + if (first) + { + OpenSSL::registerCiphers(); + first = false; + } + return registry; +} + +BlockCipher::BlockCipher() +{ +} + +BlockCipher::~BlockCipher() +{ +} + +} // namespace encfs + diff --git a/cipher/BlockCipher.h b/cipher/BlockCipher.h new file mode 100644 index 0000000..7f853dc --- /dev/null +++ b/cipher/BlockCipher.h @@ -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& GetRegistry(); + + BlockCipher(); + virtual ~BlockCipher(); + + virtual int blockSize() const =0; +}; + +} // namespace encfs + +#endif // BLOCKCIPHER_H diff --git a/cipher/BlockCipher_test.cpp b/cipher/BlockCipher_test.cpp new file mode 100644 index 0000000..724d8f0 --- /dev/null +++ b/cipher/BlockCipher_test.cpp @@ -0,0 +1,95 @@ + +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * 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 . + */ + +#include + +#include + +#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 registry = BlockCipher::GetRegistry(); + list 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 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 diff --git a/cipher/CMakeLists.txt b/cipher/CMakeLists.txt index 158b28e..f586f46 100644 --- a/cipher/CMakeLists.txt +++ b/cipher/CMakeLists.txt @@ -2,41 +2,42 @@ include_directories (${OPENSSL_INCLUDE_DIR}) link_directories (${Encfs_BINARY_DIR}/base) -enable_testing () -find_package (GTest REQUIRED) - add_library (encfs-cipher - readpassphrase.cpp BlockCipher.cpp Cipher.cpp CipherKey.cpp + MAC.cpp MemoryPool.cpp NullCipher.cpp openssl.cpp + PBKDF.cpp + readpassphrase.cpp SSL_Cipher.cpp + StreamCipher.cpp ) target_link_libraries (encfs-cipher ${OPENSSL_LIBRARIES} ) -#include_directories (${GTEST_INCLUDE_DIR}) -#add_executable (unittests -#MemBlockFileIO.cpp -#MemFileIO.cpp -#testing.cpp -#test_IO.cpp -#test_BlockIO.cpp -#) +if (GTEST_FOUND) + link_directories (${PROJECT_BINARY_DIR}/base) + include_directories (${GTEST_INCLUDE_DIR}) -#target_link_libraries (unittests -#${GTEST_BOTH_LIBRARIES} -#encfs-fs -#encfs-base -#${GLOG_LIBRARIES} -#) + file (GLOB TEST_FILES "*_test.cpp") -#add_test (UnitTests unittests) -#GTEST_ADD_TESTS (unittests "${UnitTestArgs}" test_IO.cpp test_BlockIO.cpp) -#add_custom_target (test COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS unittests) + add_executable (cipher-tests + testing.cpp + ${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) diff --git a/cipher/MAC.cpp b/cipher/MAC.cpp new file mode 100644 index 0000000..64e08e1 --- /dev/null +++ b/cipher/MAC.cpp @@ -0,0 +1,16 @@ +#include "cipher/MAC.h" + +namespace encfs { + +DEFINE_REGISTERABLE_TYPE(MessageAuthenticationCode) + +MessageAuthenticationCode::MessageAuthenticationCode() +{ +} + +MessageAuthenticationCode::~MessageAuthenticationCode() +{ +} + +} // namespace encfs + diff --git a/cipher/MAC.h b/cipher/MAC.h new file mode 100644 index 0000000..d7849e2 --- /dev/null +++ b/cipher/MAC.h @@ -0,0 +1,43 @@ +#ifndef ENCFS_MAC_H +#define ENCFS_MAC_H + +#include + +#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 diff --git a/cipher/MAC_test.cpp b/cipher/MAC_test.cpp new file mode 100644 index 0000000..c93bd8a --- /dev/null +++ b/cipher/MAC_test.cpp @@ -0,0 +1,88 @@ + +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * 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 . + */ + + +#include + +#include + +#include "base/shared_ptr.h" +#include "cipher/MAC.h" +#include "cipher/testing.h" + +using namespace encfs; + +namespace { + +TEST(HMacSha1Test, MessageAuthenticationCode) { + Registry registry = + MessageAuthenticationCode::GetRegistry(); + shared_ptr 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 + diff --git a/cipher/MemoryPool.cpp b/cipher/MemoryPool.cpp index 5d7793f..5c079f3 100644 --- a/cipher/MemoryPool.cpp +++ b/cipher/MemoryPool.cpp @@ -134,7 +134,7 @@ void MemoryPool::destroyAll() SecureMem::SecureMem(int len) { rAssert(len > 0); - data = (char *)OPENSSL_malloc(len); + data = (byte *)OPENSSL_malloc(len); if (data) { size = len; diff --git a/cipher/MemoryPool.h b/cipher/MemoryPool.h index 1fe3b72..b6f1c54 100644 --- a/cipher/MemoryPool.h +++ b/cipher/MemoryPool.h @@ -29,7 +29,8 @@ namespace encfs { Memory Pool for fixed sized objects. Usage: - MemBlock mb( size ); + MemBlock mb; + mb.allocate( size ); // do things with storage in mb.data byte *buffer = mb.data; @@ -59,7 +60,7 @@ namespace MemoryPool struct SecureMem { int size; - char *data; + byte *data; SecureMem(int len); ~SecureMem(); diff --git a/cipher/PBKDF.cpp b/cipher/PBKDF.cpp new file mode 100644 index 0000000..5267090 --- /dev/null +++ b/cipher/PBKDF.cpp @@ -0,0 +1,16 @@ +#include "cipher/PBKDF.h" + +namespace encfs { + +DEFINE_REGISTERABLE_TYPE(PBKDF) + +PBKDF::PBKDF() +{ +} + +PBKDF::~PBKDF() +{ +} + +} // namespace encfs + diff --git a/cipher/PBKDF.h b/cipher/PBKDF.h new file mode 100644 index 0000000..228d9b1 --- /dev/null +++ b/cipher/PBKDF.h @@ -0,0 +1,35 @@ +#ifndef ENCFS_PBKDF_H +#define ENCFS_PBKDF_H + +#include + +#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 diff --git a/cipher/PBKDF_test.cpp b/cipher/PBKDF_test.cpp new file mode 100644 index 0000000..0faeb23 --- /dev/null +++ b/cipher/PBKDF_test.cpp @@ -0,0 +1,78 @@ + +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * 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 . + */ + + +#include + +#include + +#include "base/shared_ptr.h" +#include "cipher/PBKDF.h" +#include "cipher/testing.h" + +using namespace encfs; + +namespace { + +TEST(PKCS5_PBKDF2_HMAC_SHA1, PBKDF) { + Registry registry = PBKDF::GetRegistry(); + shared_ptr 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 + diff --git a/cipher/SSL_Cipher.cpp b/cipher/SSL_Cipher.cpp index d8614ce..b434ee6 100644 --- a/cipher/SSL_Cipher.cpp +++ b/cipher/SSL_Cipher.cpp @@ -28,6 +28,7 @@ #include "cipher/SSL_Cipher.h" #include "cipher/MemoryPool.h" +#include "cipher/BlockCipher.h" #include "base/Error.h" #include "base/Mutex.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 */ -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, unsigned int rounds, byte *key, byte *iv) { if( data == NULL || dataLen == 0 ) return 0; // OpenSSL returns nkey here, but why? It is a failure.. - + byte mdBuf[ EVP_MAX_MD_SIZE ]; unsigned int mds=0; int addmd =0; @@ -110,7 +111,7 @@ int BytesToKey( int keyLen, int ivLen, const EVP_MD *md, { memcpy( iv, mdBuf+offset, toCopy ); iv += toCopy; - niv -= toCopy; + niv -= toCopy; offset += toCopy; } 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, - const byte *salt, int saltlen, - int keylen, byte *out, - long desiredPDFTime) + const byte *salt, int saltlen, + int keylen, byte *out, + long desiredPDFTime) { int iter = 1000; timeval start, end; @@ -188,8 +189,9 @@ static shared_ptr NewBFCipher( const Interface &iface, int keyLen ) const EVP_CIPHER *blockCipher = EVP_bf_cbc(); const EVP_CIPHER *streamCipher = EVP_bf_cfb(); - return shared_ptr( new SSL_Cipher(iface, BlowfishInterface, - blockCipher, streamCipher, keyLen / 8) ); + return shared_ptr( + new SSL_Cipher(iface, BlowfishInterface, + blockCipher, streamCipher, keyLen / 8) ); } static bool BF_Cipher_registered = Cipher::Register( @@ -239,8 +241,8 @@ static shared_ptr NewAESCipher( const Interface &iface, int keyLen ) } static bool AES_Cipher_registered = Cipher::Register( - "AES", "16 byte block cipher", - AESInterface, AESKeyRange, AESBlockRange, NewAESCipher, true); + "AES", "16 byte block cipher", + AESInterface, AESKeyRange, AESBlockRange, NewAESCipher, true); #endif #if defined(HAVE_EVP_AES_XTS) @@ -276,35 +278,35 @@ static shared_ptr NewAesXtsCipher( const Interface &iface, int keyLen ) } static bool AES_XTS_Cipher_registered = Cipher::Register( - "AES_XTS", "Tweakable wide-block cipher", - AesXtsInterface, AesXtsKeyRange, AesXtsBlockRange, NewAesXtsCipher, false); + "AES_XTS", "Tweakable wide-block cipher", + AesXtsInterface, AesXtsKeyRange, AesXtsBlockRange, NewAesXtsCipher, false); #endif class SSLKey : public AbstractCipherKey { - public: - pthread_mutex_t mutex; +public: + pthread_mutex_t mutex; - unsigned int keySize; // in bytes - unsigned int ivLength; + unsigned int keySize; // in bytes + unsigned int ivLength; - // key data is first _keySize bytes, - // followed by iv of _ivLength bytes, - SecureMem buf; + // key data is first _keySize bytes, + // followed by iv of _ivLength bytes, + SecureMem buf; - EVP_CIPHER_CTX block_enc; - EVP_CIPHER_CTX block_dec; - EVP_CIPHER_CTX stream_enc; - EVP_CIPHER_CTX stream_dec; + EVP_CIPHER_CTX block_enc; + EVP_CIPHER_CTX block_dec; + EVP_CIPHER_CTX stream_enc; + EVP_CIPHER_CTX stream_dec; - HMAC_CTX mac_ctx; + HMAC_CTX mac_ctx; - SSLKey(int keySize, int ivLength); - ~SSLKey(); + SSLKey(int keySize, int ivLength); + ~SSLKey(); }; SSLKey::SSLKey(int keySize_, int ivLength_) - : buf(keySize_ + ivLength_) +: buf(keySize_ + ivLength_) { rAssert(keySize_ >= 8); rAssert(ivLength_ >= 8); @@ -341,7 +343,7 @@ inline byte* IVData( const shared_ptr &key ) } void initKey(const shared_ptr &key, const EVP_CIPHER *_blockCipher, - const EVP_CIPHER *_streamCipher, int _keySize) + const EVP_CIPHER *_streamCipher, int _keySize) { Lock lock( key->mutex ); // initialize the cipher context once so that we don't have to do it for @@ -377,10 +379,10 @@ void initKey(const shared_ptr &key, const EVP_CIPHER *_blockCipher, SSL_Cipher::SSL_Cipher(const Interface &iface_, - const Interface &realIface_, - const EVP_CIPHER *blockCipher, - const EVP_CIPHER *streamCipher, - int keySize_) + const Interface &realIface_, + const EVP_CIPHER *blockCipher, + const EVP_CIPHER *streamCipher, + int keySize_) { this->iface = iface_; this->realIface = realIface_; @@ -393,8 +395,8 @@ SSL_Cipher::SSL_Cipher(const Interface &iface_, rAssert(_ivLength <= _keySize); VLOG(1) << "allocated cipher " << iface.name() - << ", keySize " << _keySize - << ", ivlength " << _ivLength; + << ", keySize " << _keySize + << ", ivlength " << _ivLength; // EVP_CIPHER_key_length isn't useful for variable-length ciphers like // Blowfish. Version 1 relied upon it incorrectly. @@ -402,8 +404,8 @@ SSL_Cipher::SSL_Cipher(const Interface &iface_, && iface.major() == 1) { LOG(WARNING) << "Running in backward compatibilty mode for 1.0 - \n" - << "key is really " << EVP_CIPHER_key_length( _blockCipher ) * 8 - << " bits, not " << _keySize * 8; + << "key is really " << EVP_CIPHER_key_length( _blockCipher ) * 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 is used to encipher/decipher the master key. -*/ + */ CipherKey SSL_Cipher::newKey(const char *password, int passwdLength, - int &iterationCount, long desiredDuration, - const byte *salt, int saltLen) + int &iterationCount, long desiredDuration, + const byte *salt, int saltLen) { shared_ptr key( new SSLKey( _keySize, _ivLength) ); @@ -477,7 +479,7 @@ CipherKey SSL_Cipher::newKey(const char *password, int passwdLength) if(bytes != (int)_keySize) { LOG(WARNING) << "newKey: BytesToKey returned " << bytes - << ", expecting " << _keySize << " key bytes"; + << ", expecting " << _keySize << " key bytes"; } } else { @@ -499,7 +501,7 @@ CipherKey SSL_Cipher::newKey(const char *password, int passwdLength) This algorithm can change at any time without affecting backward compatibility. -*/ + */ CipherKey SSL_Cipher::newRandomKey() { const int bufLen = MAX_KEYLENGTH; @@ -531,11 +533,11 @@ CipherKey SSL_Cipher::newRandomKey() /* Compute a 64-bit check value for the data using HMAC. -*/ + */ static uint64_t _checksum_64(SSLKey *key, - const byte *data, - int dataLen, - uint64_t *chainedIV) + const byte *data, + int dataLen, + uint64_t *chainedIV) { rAssert( dataLen > 0 ); Lock lock( key->mutex ); @@ -576,7 +578,7 @@ static uint64_t _checksum_64(SSLKey *key, } bool SSL_Cipher::randomize( byte *buf, int len, - bool strongRandom ) const + bool strongRandom ) const { // to avoid warnings of uninitialized data from valgrind 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, - const CipherKey &key, uint64_t *chainedIV ) const + const CipherKey &key, uint64_t *chainedIV ) const { shared_ptr mk = dynamic_pointer_cast(key); 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, - const CipherKey &masterKey, bool checkKey) + const CipherKey &masterKey, bool checkKey) { shared_ptr mk = dynamic_pointer_cast(masterKey); rAssert(mk->keySize == _keySize); @@ -638,8 +640,8 @@ CipherKey SSL_Cipher::readKey(const byte *data, if(checksum2 != checksum && checkKey) { VLOG(1) << "checksum mismatch: expected " << checksum - << ", got " << checksum2 - << "on decode of " << _keySize + _ivLength << " bytes"; + << ", got " << checksum2 + << "on decode of " << _keySize + _ivLength << " bytes"; OPENSSL_cleanse(tmpBuf, sizeof(tmpBuf)); return CipherKey(); } @@ -656,7 +658,7 @@ CipherKey SSL_Cipher::readKey(const byte *data, } void SSL_Cipher::writeKey(const CipherKey &ckey, byte *data, - const CipherKey &masterKey) + const CipherKey &masterKey) { shared_ptr key = dynamic_pointer_cast(ckey); rAssert(key->keySize == _keySize); @@ -771,7 +773,7 @@ void SSL_Cipher::setIVec_old(byte *ivec, { unsigned int var1 = 0x060a4011 * seed; unsigned int var2 = 0x0221040d * (seed ^ 0xD3FEA11C); - + memcpy( ivec, IVData(key), _ivLength ); ivec[0] ^= (var1 >> 24) & 0xff; @@ -861,7 +863,7 @@ bool SSL_Cipher::streamEncode(byte *buf, int size, dstLen += tmpLen; 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; } @@ -897,7 +899,7 @@ bool SSL_Cipher::streamDecode(byte *buf, int size, dstLen += tmpLen; 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; } @@ -928,7 +930,7 @@ bool SSL_Cipher::blockEncode(byte *buf, int size, dstLen += tmpLen; 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; } @@ -958,7 +960,7 @@ bool SSL_Cipher::blockDecode(byte *buf, int size, dstLen += tmpLen; 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; } diff --git a/cipher/StreamCipher.cpp b/cipher/StreamCipher.cpp new file mode 100644 index 0000000..c12e31a --- /dev/null +++ b/cipher/StreamCipher.cpp @@ -0,0 +1,20 @@ +#include "cipher/StreamCipher.h" + +namespace encfs { + +Registry& StreamCipher::GetRegistry() +{ + static Registry registry; + return registry; +} + +StreamCipher::StreamCipher() +{ +} + +StreamCipher::~StreamCipher() +{ +} + +} // namespace encfs + diff --git a/cipher/StreamCipher.h b/cipher/StreamCipher.h new file mode 100644 index 0000000..6efb6e0 --- /dev/null +++ b/cipher/StreamCipher.h @@ -0,0 +1,60 @@ + +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * 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 . + */ + +#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& 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 + diff --git a/cipher/openssl.cpp b/cipher/openssl.cpp index f38e4b0..9ec2982 100644 --- a/cipher/openssl.cpp +++ b/cipher/openssl.cpp @@ -2,7 +2,7 @@ * Author: Valient Gough * ***************************************************************************** - * Copyright (c) 2007, Valient Gough + * Copyright (c) 2007-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 @@ -20,10 +20,16 @@ #include "cipher/openssl.h" +#include +#include #include +#include +#include #include +#include "base/config.h" + #define NO_DES #include #include @@ -31,8 +37,345 @@ #include #endif +#include +#include +#include +#include +#include + +#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 { +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(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() { 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 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 ENGINE_cleanup(); @@ -109,4 +452,10 @@ void openssl_shutdown(bool threaded) pthreads_locking_cleanup(); } +void OpenSSL::registerCiphers() +{ + // Nothing required.. Just need to reference this code block to get static + // initializers. +} + } // namespace encfs diff --git a/cipher/openssl.h b/cipher/openssl.h index c021c48..b38506e 100644 --- a/cipher/openssl.h +++ b/cipher/openssl.h @@ -21,10 +21,17 @@ #ifndef _openssl_incl_ #define _openssl_incl_ +#include "base/Registry.h" + namespace encfs { -void openssl_init(bool isThreaded); -void openssl_shutdown(bool isThreaded); +class OpenSSL { + public: + static void init(bool isThreaded); + static void shutdown(bool isThreaded); + + static void registerCiphers(); +}; } // namespace encfs diff --git a/cipher/testing.cpp b/cipher/testing.cpp new file mode 100644 index 0000000..0af7ce1 --- /dev/null +++ b/cipher/testing.cpp @@ -0,0 +1,30 @@ + +#include + +#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 + diff --git a/cipher/testing.h b/cipher/testing.h new file mode 100644 index 0000000..a2aa9ad --- /dev/null +++ b/cipher/testing.h @@ -0,0 +1,36 @@ + +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * 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 . + */ + +#ifndef _CIPHER_TESTING_incl_ +#define _CIPHER_TESTING_incl_ + +#include + +#include "base/types.h" + +namespace encfs { + +std::string stringToHex(const byte *data, int len); + +} // namespace encfs + +#endif + diff --git a/encfs/CMakeLists.txt b/encfs/CMakeLists.txt index ef21be2..458e52a 100644 --- a/encfs/CMakeLists.txt +++ b/encfs/CMakeLists.txt @@ -8,10 +8,6 @@ link_directories (${Encfs_BINARY_DIR}/cipher) include_directories (${Encfs_SOURCE_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) add_executable (encfs diff --git a/encfs/main.cpp b/encfs/main.cpp index f331baa..64ce2d2 100644 --- a/encfs/main.cpp +++ b/encfs/main.cpp @@ -578,7 +578,7 @@ int main(int argc, char *argv[]) // encfs_oper.fsetattr_x #endif - openssl_init( encfsArgs->isThreaded ); + OpenSSL::init( encfsArgs->isThreaded ); // context is not a smart pointer because it will live for the life of // the filesystem. @@ -671,7 +671,7 @@ int main(int argc, char *argv[]) ctx->setRoot( shared_ptr() ); MemoryPool::destroyAll(); - openssl_shutdown( encfsArgs->isThreaded ); + OpenSSL::shutdown( encfsArgs->isThreaded ); return returnCode; } diff --git a/fs/CMakeLists.txt b/fs/CMakeLists.txt index 2b64e50..ae6b86c 100644 --- a/fs/CMakeLists.txt +++ b/fs/CMakeLists.txt @@ -1,12 +1,6 @@ -find_package (FUSE REQUIRED) -include_directories (${FUSE_INCLUDE_DIR}) +protobuf_generate_cpp (PROTO_SRCS PROTO_HDRS ${PROJECT_SOURCE_DIR}/protos/fsconfig.proto) -protobuf_generate_cpp (PROTO_SRCS PROTO_HDRS ${Encfs_SOURCE_DIR}/protos/fsconfig.proto) - -enable_testing () -find_package (GTest) - -include_directories (${Encfs_BINARY_DIR}/base) +include_directories (${PROJECT_BINARY_DIR}/base) add_library (encfs-fs encfs.cpp Context.cpp @@ -32,11 +26,11 @@ target_link_libraries (encfs-fs # Unit tests are optional, depends on libgtest (Google's C++ test framework). if (GTEST_FOUND) - link_directories (${Encfs_BINARY_DIR}/base) - link_directories (${Encfs_BINARY_DIR}/cipher) + link_directories (${PROJECT_BINARY_DIR}/base) + link_directories (${PROJECT_BINARY_DIR}/cipher) include_directories (${GTEST_INCLUDE_DIR}) - add_executable (unittests + add_executable (fs-tests MemBlockFileIO.cpp MemFileIO.cpp testing.cpp @@ -44,7 +38,7 @@ if (GTEST_FOUND) test_BlockIO.cpp ) - target_link_libraries (unittests + target_link_libraries (fs-tests ${GTEST_BOTH_LIBRARIES} encfs-fs encfs-cipher @@ -52,8 +46,6 @@ if (GTEST_FOUND) ${GLOG_LIBRARIES} ) - add_test (UnitTests unittests) - GTEST_ADD_TESTS (unittests "${UnitTestArgs}" test_IO.cpp test_BlockIO.cpp) - add_custom_target (test COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS unittests) - + add_test (FSTests fs-tests) + GTEST_ADD_TESTS (fs-tests "${FSTestArgs}" test_IO.cpp test_BlockIO.cpp) endif (GTEST_FOUND) diff --git a/fs/FileUtils.cpp b/fs/FileUtils.cpp index b8a5a34..d47b8c0 100644 --- a/fs/FileUtils.cpp +++ b/fs/FileUtils.cpp @@ -1349,7 +1349,7 @@ std::string readPassword( int FD ) if(rdSize > 0) { - result.append( buf->data, rdSize ); + result.append( (char*)buf->data, rdSize ); } else break; } @@ -1436,7 +1436,7 @@ SecureMem *passwordFromProgram(const std::string &passProg, SecureMem *result = new SecureMem(password.length()+1); if (result) - strncpy(result->data, password.c_str(), result->size); + strncpy((char *)result->data, password.c_str(), result->size); password.assign(password.length(), '\0'); return result; @@ -1446,11 +1446,11 @@ SecureMem *passwordFromStdin() { SecureMem *buf = new SecureMem(MaxPassBuf); - char *res = fgets( buf->data, buf->size, stdin ); + char *res = fgets( (char *)buf->data, buf->size, stdin ); if (res) { // 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') buf->data[ last-1 ] = '\0'; } @@ -1464,7 +1464,7 @@ SecureMem *passwordFromPrompt() // xgroup(common) char *res = readpassphrase( _("EncFS Password: "), - buf->data, buf->size-1, RPP_ECHO_OFF ); + (char *)buf->data, buf->size-1, RPP_ECHO_OFF ); if (!res) { delete buf; @@ -1483,12 +1483,13 @@ SecureMem *passwordFromPrompts() { // xgroup(common) 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) 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; } else @@ -1514,7 +1515,8 @@ CipherKey getUserKey(const EncfsConfig &config, bool useStdin) if (password) { - userKey = decryptKey(config, password->data, strlen(password->data)); + userKey = decryptKey(config, (char*)password->data, + strlen((char*)password->data)); delete password; } @@ -1529,7 +1531,8 @@ CipherKey getUserKey( const EncfsConfig &config, const std::string &passProg, if (password) { - result = decryptKey(config, password->data, strlen(password->data)); + result = decryptKey(config, (char*)password->data, + strlen((char*)password->data)); delete password; } @@ -1552,7 +1555,8 @@ CipherKey getNewUserKey(EncfsConfig &config, if (password) { - result = makeNewKey(config, password->data, strlen(password->data)); + result = makeNewKey(config, (char*)password->data, + strlen((char*)password->data)); delete password; } diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index e362709..6bdfde1 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -8,10 +8,6 @@ link_directories (${Encfs_BINARY_DIR}/cipher) include_directories (${Encfs_SOURCE_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) diff --git a/util/encfsctl.cpp b/util/encfsctl.cpp index 1357fc8..6b46b1b 100644 --- a/util/encfsctl.cpp +++ b/util/encfsctl.cpp @@ -24,6 +24,9 @@ #include "base/i18n.h" #include "cipher/Cipher.h" +#include "cipher/BlockCipher.h" +#include "cipher/MAC.h" +#include "cipher/StreamCipher.h" #include "fs/FileUtils.h" #include "fs/Context.h" @@ -35,6 +38,7 @@ #include #include #include +#include #include #include @@ -52,6 +56,7 @@ using namespace encfs; static int showInfo( 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 chpasswdAutomaticly( 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", // xgroup(usage) 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, "", // xgroup(usage) gettext_noop(" -- print version number and exit")}, @@ -140,7 +148,7 @@ static bool checkDir( string &rootDir ) { if( !isDirectory( rootDir.c_str() )) { - cerr << autosprintf(_("directory %s does not exist.\n"), + cout << autosprintf(_("directory %s does not exist.\n"), rootDir.c_str()); return false; } @@ -155,11 +163,45 @@ static int showVersion( int argc, char **argv ) (void)argc; (void)argv; // xgroup(usage) - cerr << autosprintf(_("encfsctl version %s"), VERSION) << "\n"; + cout << autosprintf(_("encfsctl version %s"), VERSION) << "\n"; return EXIT_SUCCESS; } +static int showCiphers( int argc, char **argv ) +{ + (void)argc; + (void)argv; + list 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 ) { (void)argc; @@ -676,7 +718,7 @@ static int cmd_showcruft( int argc, char **argv ) 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; }