diff --git a/CMakeLists.txt b/CMakeLists.txt index e4764f7..2a3ed14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,9 @@ include_directories (${PROTOBUF_INCLUDE_DIR}) find_package (GLog REQUIRED) include_directories (${GLOG_INCLUDE_DIRS}) +find_package (Threads) + +set (CMAKE_THREAD_PREFER_PTHREAD) find_program (POD2MAN pod2man) find_package (GTest) diff --git a/base/Mutex.h b/base/Mutex.h index 4675af6..7e8df4e 100644 --- a/base/Mutex.h +++ b/base/Mutex.h @@ -21,15 +21,38 @@ #ifndef _Mutex_incl_ #define _Mutex_incl_ +#include "base/config.h" + +#ifdef CMAKE_USE_PTHREADS_INIT #include +#else +#warning No thread support. +#endif namespace encfs { +class Mutex +{ + public: +#ifdef CMAKE_USE_PTHREADS_INIT + pthread_mutex_t _mutex; + Mutex() { + pthread_mutex_init( &_mutex, 0 ); + } + ~Mutex() { + pthread_mutex_destroy( &_mutex ); + } +#endif + + void lock(); + void unlock(); +}; + class Lock { public: - Lock( pthread_mutex_t &mutex ); + explicit Lock( Mutex &mutex ); ~Lock(); // leave the lock as it is. When the Lock wrapper is destroyed, it @@ -40,24 +63,39 @@ private: Lock(const Lock &src); // not allowed Lock &operator = (const Lock &src); // not allowed - pthread_mutex_t *_mutex; + Mutex *_mutex; }; -inline Lock::Lock( pthread_mutex_t &mutex ) +inline void Mutex::lock() +{ +#ifdef CMAKE_USE_PTHREADS_INIT + pthread_mutex_lock( &_mutex ); +#endif +} + +inline void Mutex::unlock() +{ +#ifdef CMAKE_USE_PTHREADS_INIT + pthread_mutex_unlock( &_mutex ); +#endif +} + +inline Lock::Lock( Mutex &mutex ) : _mutex( &mutex ) { - pthread_mutex_lock( _mutex ); + if (_mutex) + _mutex->lock(); } inline Lock::~Lock( ) { if(_mutex) - pthread_mutex_unlock( _mutex ); + _mutex->unlock(); } inline void Lock::leave() { - _mutex = 0; + _mutex = NULL; } } // namespace encfs diff --git a/base/Registry.h b/base/Registry.h index 49c66c7..e9182d1 100644 --- a/base/Registry.h +++ b/base/Registry.h @@ -3,6 +3,7 @@ #include #include +#include namespace encfs { @@ -36,7 +37,8 @@ public: T* CreateForMatch(const std::string &description) { for (auto &it : data) { - if (description == it.second.properties.toString()) + auto name = it.second.properties.toString(); + if (!name.compare(0, description.size(), description)) return (*it.second.constructor)(); } return NULL; @@ -56,6 +58,17 @@ public: return NULL; return &(it->second.properties); } + + const typename T::Properties *GetPropertiesForMatch( + const std::string &description) const { + for (auto &it : data) { + auto name = it.second.properties.toString(); + if (!name.compare(0, description.size(), description)) + return &(it.second.properties); + } + return NULL; + } + private: std::map data; @@ -87,7 +100,7 @@ public: } #define REGISTER_CLASS(DERIVED, BASE) \ - static Registrar registrar_##DERIVED(#DERIVED) + static Registrar registrar_ ## DERIVED ## _ ## BASE (#DERIVED) } // namespace encfs diff --git a/base/config.h.cmake b/base/config.h.cmake index b9834f2..748bd61 100644 --- a/base/config.h.cmake +++ b/base/config.h.cmake @@ -1,3 +1,5 @@ +#define VERSION "@ENCFS_VERSION@" + #cmakedefine HAVE_ATTR_XATTR_H #cmakedefine HAVE_SYS_XATTR_H #cmakedefine XATTR_ADD_OPT @@ -15,5 +17,6 @@ #cmakedefine HAVE_VALGRIND_VALGRIND_H #cmakedefine HAVE_VALGRIND_MEMCHECK_H -#define VERSION "@ENCFS_VERSION@" +/* TODO: add other thread library support. */ +#cmakedefine CMAKE_USE_PTHREADS_INIT diff --git a/cipher/BlockCipher.cpp b/cipher/BlockCipher.cpp index 3bc9d15..75305c6 100644 --- a/cipher/BlockCipher.cpp +++ b/cipher/BlockCipher.cpp @@ -2,6 +2,7 @@ // TODO: add ifdef when OpenSSL becomes optional. #include "cipher/openssl.h" +#include "cipher/NullCiphers.h" namespace encfs { @@ -12,6 +13,7 @@ Registry& BlockCipher::GetRegistry() if (first) { OpenSSL::registerCiphers(); + NullCiphers::registerCiphers(); first = false; } return registry; diff --git a/cipher/BlockCipher.h b/cipher/BlockCipher.h index 7f853dc..f920a9c 100644 --- a/cipher/BlockCipher.h +++ b/cipher/BlockCipher.h @@ -10,16 +10,21 @@ namespace encfs { +static const char NAME_AES_CBC[] = "AES/CBC"; +static const char NAME_BLOWFISH_CBC[] = "Blowfish/CBC"; + // 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(); + DECLARE_REGISTERABLE_TYPE(BlockCipher); BlockCipher(); virtual ~BlockCipher(); + // Not valid until a key has been set, as they key size may determine the + // block size. virtual int blockSize() const =0; }; diff --git a/cipher/BlockCipher_test.cpp b/cipher/BlockCipher_test.cpp index 724d8f0..8cf89f9 100644 --- a/cipher/BlockCipher_test.cpp +++ b/cipher/BlockCipher_test.cpp @@ -26,6 +26,8 @@ #include "base/shared_ptr.h" #include "cipher/BlockCipher.h" #include "cipher/MemoryPool.h" +#include "cipher/PBKDF.h" +#include "cipher/testing.h" using namespace encfs; using std::list; @@ -34,62 +36,112 @@ 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; - } + 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(RequiredBlockCiphers, BlockCipher) { + auto aes_cbc = BlockCipher::GetRegistry().CreateForMatch(NAME_AES_CBC); + ASSERT_TRUE(aes_cbc != NULL); + + auto bf_cbc = BlockCipher::GetRegistry().CreateForMatch(NAME_BLOWFISH_CBC); + ASSERT_TRUE(bf_cbc != NULL); +} + +TEST(RequiredStreamCiphers, StreamCipher) { + auto aes_cfb = StreamCipher::GetRegistry().CreateForMatch(NAME_AES_CFB); + ASSERT_TRUE(aes_cfb != NULL); + + auto bf_cfb = StreamCipher::GetRegistry().CreateForMatch(NAME_BLOWFISH_CFB); + ASSERT_TRUE(bf_cfb != NULL); +} + +TEST(BlowfishTestVector, BlockCihper) { + auto cbc = BlockCipher::GetRegistry().CreateForMatch(NAME_BLOWFISH_CBC); + auto cfb = StreamCipher::GetRegistry().CreateForMatch(NAME_BLOWFISH_CFB); + + CipherKey key(16); + setDataFromHex(key.data(), key.size(), "0123456789abcdeff0e1d2c3b4a59687"); + cbc->setKey(key); + cfb->setKey(key); + + byte iv[8]; + setDataFromHex(iv, 8, "fedcba9876543210"); + + byte data[32]; + setDataFromHex(data, 32, + "37363534333231204e6f77206973207468652074696d6520666f722000000000"); + + byte cipherData[32]; + cbc->encrypt(iv, data, cipherData, 32); + + ASSERT_EQ("6b77b4d63006dee605b156e27403979358deb9e7154616d959f1652bd5ff92cc", + stringToHex(cipherData, 32)); + + cfb->encrypt(iv, data, cipherData, 29); + ASSERT_EQ("e73214a2822139caf26ecf6d2eb9e76e3da3de04d1517200519d57a6c3", + stringToHex(cipherData, 29)); } 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); + Registry registry = BlockCipher::GetRegistry(); - for (int keySize = properties->keySize.min(); - keySize <= properties->keySize.max(); - keySize += properties->keySize.inc()) { - SCOPED_TRACE(testing::Message() << "Key size " << keySize); + shared_ptr pbkdf( + PBKDF::GetRegistry().CreateForMatch(NAME_PKCS5_PBKDF2_HMAC_SHA1)); - shared_ptr cipher (registry.Create(name.c_str())); + list ciphers = registry.GetAll(); + for (const string &name : ciphers) { + const BlockCipher::Properties *properties = + registry.GetProperties(name.c_str()); + SCOPED_TRACE(testing::Message() << "Cipher " << name); - ASSERT_TRUE(cipher->randomKey(keySize / 8)); + for (int keySize = properties->keySize.min(); + keySize <= properties->keySize.max(); + keySize += properties->keySize.inc()) { + SCOPED_TRACE(testing::Message() << "Key size " << keySize); - // Create some data to encrypt. - int blockSize = cipher->blockSize(); - MemBlock mb; - mb.allocate(16 * blockSize); + shared_ptr cipher (registry.Create(name.c_str())); - for (int i = 0; i < 16 * blockSize; i++) { - mb.data[i] = i % 256; - } + CipherKey key = pbkdf->randomKey(keySize / 8); + ASSERT_TRUE(key.valid()); + cipher->setKey(key); - MemBlock iv; - iv.allocate(blockSize); - for (int i = 0; i < blockSize; i++) { - iv.data[i] = i; - } + // Create some data to encrypt. + int blockSize = cipher->blockSize(); + MemBlock mb; + mb.allocate(16 * blockSize); - // Encrypt. - MemBlock encrypted; - encrypted.allocate(16 * blockSize); + for (int i = 0; i < 16 * blockSize; i++) { + mb.data[i] = i % 256; + } - ASSERT_TRUE(cipher->encrypt(iv.data, mb.data, - encrypted.data, 16 * blockSize)); + MemBlock iv; + iv.allocate(blockSize); + for (int i = 0; i < blockSize; i++) { + iv.data[i] = i; + } - // Decrypt. - MemBlock decrypted; - decrypted.allocate(16 * blockSize); - ASSERT_TRUE(cipher->decrypt(iv.data, encrypted.data, - decrypted.data, 16 * blockSize)); + // Encrypt. + MemBlock encrypted; + encrypted.allocate(16 * blockSize); - compare(mb.data, decrypted.data, 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 f586f46..f00a2c4 100644 --- a/cipher/CMakeLists.txt +++ b/cipher/CMakeLists.txt @@ -4,15 +4,14 @@ link_directories (${Encfs_BINARY_DIR}/base) add_library (encfs-cipher BlockCipher.cpp - Cipher.cpp CipherKey.cpp + CipherV1.cpp MAC.cpp MemoryPool.cpp - NullCipher.cpp + NullCiphers.cpp openssl.cpp PBKDF.cpp readpassphrase.cpp - SSL_Cipher.cpp StreamCipher.cpp ) diff --git a/cipher/Cipher.cpp b/cipher/Cipher.cpp deleted file mode 100644 index b84684e..0000000 --- a/cipher/Cipher.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2002-2004, 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 "base/config.h" -#include "cipher/Cipher.h" - -#include -#include -#include -#include - -#include "base/Interface.h" -#include "base/Range.h" -#include "base/base64.h" - -// for static build. Need to reference the modules which are registered at -// run-time, to ensure that the linker doesn't optimize them away. -#include "cipher/NullCipher.h" -#include "cipher/SSL_Cipher.h" - -using namespace std; - -namespace encfs { - -#define REF_MODULE(TYPE) \ - if( !TYPE::Enabled() ) \ - cerr << "referenceModule: should never happen\n"; - -static -void AddSymbolReferences() -{ - REF_MODULE(SSL_Cipher) - REF_MODULE(NullCipher) -} - - -struct CipherAlg -{ - bool hidden; - Cipher::CipherConstructor constructor; - string description; - Interface iface; - Range keyLength; - Range blockSize; - bool hasStreamMode; -}; - -typedef multimap< string, CipherAlg> CipherMap_t; -static CipherMap_t *gCipherMap = NULL; - -std::list -Cipher::GetAlgorithmList( bool includeHidden ) -{ - AddSymbolReferences(); - - list result; - - if(!gCipherMap) - return result; - - CipherMap_t::const_iterator it; - CipherMap_t::const_iterator mapEnd = gCipherMap->end(); - for(it = gCipherMap->begin(); it != mapEnd; ++it) - { - if(includeHidden || !it->second.hidden) - { - CipherAlgorithm tmp; - tmp.name = it->first; - tmp.description = it->second.description; - tmp.iface = it->second.iface; - tmp.keyLength = it->second.keyLength; - tmp.blockSize = it->second.blockSize; - tmp.hasStreamMode = it->second.hasStreamMode; - - result.push_back( tmp ); - } - } - - return result; -} - -bool Cipher::Register(const char *name, const char *description, - const Interface &iface, CipherConstructor fn, - bool hasStreamMode, bool hidden) -{ - Range keyLength(-1,-1,1); - Range blockSize(-1,-1,1); - return Cipher::Register( name, description, iface, - keyLength, blockSize, fn, hasStreamMode, hidden ); -} - -bool Cipher::Register(const char *name, const char *description, - const Interface &iface, const Range &keyLength, - const Range &blockSize, - CipherConstructor fn, - bool hasStreamMode, - bool hidden) -{ - if(!gCipherMap) - gCipherMap = new CipherMap_t; - - CipherAlg ca; - ca.hidden = hidden; - ca.constructor = fn; - ca.description = description; - ca.iface = iface; - ca.keyLength = keyLength; - ca.blockSize = blockSize; - ca.hasStreamMode = hasStreamMode; - - gCipherMap->insert( make_pair(string(name), ca) ); - return true; -} - -shared_ptr Cipher::New(const string &name, int keyLen) -{ - shared_ptr result; - - if(gCipherMap) - { - CipherMap_t::const_iterator it = gCipherMap->find( name ); - if(it != gCipherMap->end()) - { - CipherConstructor fn = it->second.constructor; - // use current interface.. - result = (*fn)( it->second.iface, keyLen ); - } - } - - return result; -} - -shared_ptr Cipher::New( const Interface &iface, int keyLen ) -{ - shared_ptr result; - if(gCipherMap) - { - CipherMap_t::const_iterator it; - CipherMap_t::const_iterator mapEnd = gCipherMap->end(); - - for(it = gCipherMap->begin(); it != mapEnd; ++it) - { - // TODO: we should look for the newest implementation.. - if( implements(it->second.iface, iface) ) - { - CipherConstructor fn = it->second.constructor; - // pass in requested interface.. - result = (*fn)( iface, keyLen ); - - // if we're not going to compare the options, then just stop - // now.. - break; - } - } - } - - return result; -} - -Cipher::Cipher() -{ -} - -Cipher::~Cipher() -{ -} - -unsigned int Cipher::MAC_32( const byte *src, int len, - const CipherKey &key, uint64_t *chainedIV ) const -{ - uint64_t mac64 = MAC_64( src, len, key, chainedIV ); - - unsigned int mac32 = ((mac64 >> 32) & 0xffffffff) ^ (mac64 & 0xffffffff); - - return mac32; -} - -unsigned int Cipher::MAC_16( const byte *src, int len, - const CipherKey &key, uint64_t *chainedIV ) const -{ - uint64_t mac64 = MAC_64( src, len, key, chainedIV ); - - unsigned int mac32 = ((mac64 >> 32) & 0xffffffff) ^ (mac64 & 0xffffffff); - unsigned int mac16 = ((mac32 >> 16) & 0xffff) ^ (mac32 & 0xffff); - - return mac16; -} - -string Cipher::encodeAsString(const CipherKey &key, - const CipherKey &encodingKey ) -{ - int encodedKeySize = this->encodedKeySize(); - byte *keyBuf = new byte[ encodedKeySize ]; - - this->writeKey( key, keyBuf, encodingKey ); - - int b64Len = B256ToB64Bytes( encodedKeySize ); - byte *b64Key = new byte[ b64Len + 1 ]; - - changeBase2( keyBuf, encodedKeySize, 8, b64Key, - b64Len, 6 ); - B64ToAscii( b64Key, b64Len ); - b64Key[ b64Len - 1 ] = '\0'; - - return string( (const char *)b64Key ); -} - -bool Cipher::hasStreamMode() const -{ - return true; -} - -} // namespace encfs diff --git a/cipher/Cipher.h b/cipher/Cipher.h deleted file mode 100644 index 229dffb..0000000 --- a/cipher/Cipher.h +++ /dev/null @@ -1,165 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2002-2003, 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_incl_ -#define _Cipher_incl_ - -#include "cipher/CipherKey.h" -#include "base/Interface.h" -#include "base/Range.h" -#include "base/types.h" - -#include -#include -#include - -namespace encfs { - -/* - Mostly pure virtual interface defining operations on a cipher. - - Cipher's should register themselves so they can be instanciated via - Cipher::New(). -*/ -class Cipher -{ -public: - // if no key length was indicated when cipher was registered, then keyLen - // <= 0 will be used. - typedef shared_ptr (*CipherConstructor)( - const Interface &iface, int keyLenBits ); - - struct CipherAlgorithm - { - std::string name; - std::string description; - Interface iface; - Range keyLength; - Range blockSize; - bool hasStreamMode; - }; - - - typedef std::list AlgorithmList; - static AlgorithmList GetAlgorithmList( bool includeHidden = false ); - - - static shared_ptr New( const Interface &iface, - int keyLen = -1); - static shared_ptr New( const std::string &cipherName, - int keyLen = -1 ); - - - static bool Register(const char *cipherName, - const char *description, - const Interface &iface, - CipherConstructor constructor, - bool hasStreamMode, - bool hidden = false); - - static bool Register(const char *cipherName, - const char *description, - const Interface &iface, - const Range &keyLength, const Range &blockSize, - CipherConstructor constructor, - bool hasStreamMode, - bool hidden = false); - - Cipher(); - virtual ~Cipher(); - - virtual Interface interface() const =0; - - // create a new key based on a password - // if iterationCount == 0, then iteration count will be determined - // by newKey function and filled in. - // If iterationCount == 0, then desiredFunctionDuration is how many - // milliseconds the password derivation function should take to run. - virtual CipherKey newKey(const char *password, int passwdLength, - int &iterationCount, long desiredFunctionDuration, - const byte *salt, int saltLen) =0; - - // deprecated - for backward compatibility - virtual CipherKey newKey(const char *password, int passwdLength ) =0; - - // create a new random key - virtual CipherKey newRandomKey() =0; - - // data must be len encodedKeySize() - virtual CipherKey readKey(const byte *data, - const CipherKey &encodingKey, - bool checkKey = true) =0; - - virtual void writeKey(const CipherKey &key, byte *data, - const CipherKey &encodingKey) =0; - - virtual std::string encodeAsString(const CipherKey &key, - const CipherKey &encodingKey ); - - // for testing purposes - virtual bool compareKey( const CipherKey &A, const CipherKey &B ) const =0; - - // meta-data about the cypher - virtual int keySize() const=0; - virtual int encodedKeySize() const=0; // size - virtual int cipherBlockSize() const=0; // size of a cipher block - - virtual bool hasStreamMode() const; - - // fill the supplied buffer with random data - // The data may be pseudo random and might not be suitable for key - // generation. For generating keys, uses newRandomKey() instead. - // Returns true on success, false on failure. - virtual bool randomize( byte *buf, int len, - bool strongRandom ) const =0; - - // 64 bit MAC of the data with the given key - virtual uint64_t MAC_64( const byte *src, int len, - const CipherKey &key, uint64_t *chainedIV = 0 ) const =0; - - // based on reductions of MAC_64 - unsigned int MAC_32( const byte *src, int len, - const CipherKey &key, uint64_t *chainedIV = 0 ) const; - unsigned int MAC_16( const byte *src, int len, - const CipherKey &key, uint64_t *chainedIV = 0 ) const; - - // functional interfaces - /* - Stream encoding of data in-place. The stream data can be any length. - */ - virtual bool streamEncode( byte *data, int len, - uint64_t iv64, const CipherKey &key) const=0; - virtual bool streamDecode( byte *data, int len, - uint64_t iv64, const CipherKey &key) const=0; - - /* - Block encoding of data in-place. The data size should be a multiple of - the cipher block size. - */ - virtual bool blockEncode(byte *buf, int size, - uint64_t iv64, const CipherKey &key) const=0; - virtual bool blockDecode(byte *buf, int size, - uint64_t iv64, const CipherKey &key) const=0; -}; - -} // namespace encfs - -#endif - diff --git a/cipher/CipherKey.cpp b/cipher/CipherKey.cpp index 6512466..aebf4d7 100644 --- a/cipher/CipherKey.cpp +++ b/cipher/CipherKey.cpp @@ -1,18 +1,19 @@ + /***************************************************************************** * Author: Valient Gough * ***************************************************************************** - * Copyright (c) 2007, 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 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. + * 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 . @@ -20,14 +21,72 @@ #include "cipher/CipherKey.h" +#include "base/shared_ptr.h" +#include "base/types.h" +#include "cipher/MemoryPool.h" + namespace encfs { -AbstractCipherKey::AbstractCipherKey() +CipherKey::CipherKey() + : _valid(false) { } -AbstractCipherKey::~AbstractCipherKey() +CipherKey::CipherKey(int length) + : _valid(true) { + if (length > 0) + _mem.reset(new SecureMem(length)); +} + +CipherKey::CipherKey(const byte *data, int length) + : _valid(true) +{ + _mem.reset(new SecureMem(length)); + memcpy(_mem->data, data, length); +} + +CipherKey::CipherKey(const CipherKey& src) + : _valid(src._valid), + _mem(src._mem) +{ +} + +CipherKey::~CipherKey() +{ +} + +void CipherKey::operator = (const CipherKey& src) +{ + _mem = src._mem; + _valid = src._valid; +} + +byte *CipherKey::data() const +{ + return !_mem ? NULL : _mem->data; +} + +int CipherKey::size() const +{ + return !_mem ? 0 : _mem->size; +} + +void CipherKey::reset() +{ + _mem.reset(); + _valid = false; +} + +bool CipherKey::valid() const +{ + return _valid; +} + +bool operator == (const CipherKey &a, const CipherKey &b) { + if (a.size() != b.size()) + return false; + return memcmp(a.data(), b.data(), a.size()) == 0; } } // namespace encfs diff --git a/cipher/CipherKey.h b/cipher/CipherKey.h index 62edaf0..4cd6e27 100644 --- a/cipher/CipherKey.h +++ b/cipher/CipherKey.h @@ -21,18 +21,40 @@ #ifndef _CipherKey_incl_ #define _CipherKey_incl_ +#include + #include "base/shared_ptr.h" +#include "base/types.h" namespace encfs { -class AbstractCipherKey +class SecureMem; + +class CipherKey { -public: - AbstractCipherKey(); - virtual ~AbstractCipherKey(); + public: + CipherKey(); // Creates a key for which valid() returns false. + explicit CipherKey(int length); + CipherKey(const byte *data, int length); + CipherKey(const CipherKey& src); + ~CipherKey(); + + void operator = (const CipherKey& src); + + byte *data() const; + int size() const; + + // Clear memory associated with key. + void reset(); + + bool valid() const; + + private: + bool _valid; + shared_ptr _mem; }; -typedef shared_ptr CipherKey; +bool operator == (const CipherKey &a, const CipherKey &b); } // namespace encfs diff --git a/cipher/CipherV1.cpp b/cipher/CipherV1.cpp new file mode 100644 index 0000000..fec3206 --- /dev/null +++ b/cipher/CipherV1.cpp @@ -0,0 +1,668 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, 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 "cipher/CipherV1.h" + +#include +#include + +#include +#include + +#include + +#include "base/base64.h" +#include "base/Error.h" +#include "base/i18n.h" +#include "base/Mutex.h" +#include "base/Range.h" +#include "cipher/MemoryPool.h" +#include "cipher/MAC.h" +#include "cipher/BlockCipher.h" +#include "cipher/PBKDF.h" +#include "cipher/StreamCipher.h" + +using namespace std; + +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 + +/* + DEPRECATED: this is here for backward compatibilty only. Use PBKDF + + This duplicated some code in OpenSSL, correcting an issue with key lengths + produced for Blowfish. +*/ +bool BytesToKey(const byte *data, int dataLen, + unsigned int rounds, CipherKey *key) +{ + Registry registry = MAC::GetRegistry(); + shared_ptr sha1(registry.CreateForMatch("SHA-1")); + if (!sha1) + return false; + + if( data == NULL || dataLen == 0 ) + return false; // OpenSSL returns nkey here, but why? It is a failure.. + + SecureMem mdBuf( sha1->outputSize() ); + int addmd = 0; + int remaining = key->size(); + + for(;;) + { + sha1->reset(); + if( addmd++ ) + sha1->update(mdBuf.data, mdBuf.size); + sha1->update(data, dataLen); + sha1->write(mdBuf.data); + + for(unsigned int i=1; i < rounds; ++i) + { + sha1->reset(); + sha1->update(mdBuf.data, mdBuf.size); + sha1->write(mdBuf.data); + } + + int offset = 0; + int toCopy = MIN( remaining, (int)mdBuf.size - offset ); + if( toCopy ) + { + memcpy( key->data(), mdBuf.data+offset, toCopy ); + key += toCopy; + remaining -= toCopy; + offset += toCopy; + } + if(remaining == 0) break; + } + + return true; +} + +long time_diff(const timeval &end, const timeval &start) +{ + return (end.tv_sec - start.tv_sec) * 1000 * 1000 + + (end.tv_usec - start.tv_usec); +} + +int CipherV1::TimedPBKDF2(const char *pass, int passlen, + const byte *salt, int saltlen, + CipherKey *key, long desiredPDFTime) +{ + Registry registry = PBKDF::GetRegistry(); + shared_ptr impl(registry.CreateForMatch(NAME_PKCS5_PBKDF2_HMAC_SHA1)); + if (!impl) + return -1; + + int iter = 1000; + timeval start, end; + + for(;;) + { + gettimeofday( &start, 0 ); + if (!impl->makeKey(pass, passlen, salt, saltlen, iter, key)) + return -1; + + gettimeofday( &end, 0 ); + + long delta = time_diff(end, start); + if(delta < desiredPDFTime / 8) + { + iter *= 4; + } else if(delta < (5 * desiredPDFTime / 6)) + { + // estimate number of iterations to get close to desired time + iter = (int)((double)iter * (double)desiredPDFTime + / (double)delta); + } else + return iter; + } +} + + +// - Version 1:0 used EVP_BytesToKey, which didn't do the right thing for +// Blowfish key lengths > 128 bit. +// - Version 2:0 uses BytesToKey. +// We support both 2:0 and 1:0, hence current:revision:age = 2:0:1 +// - Version 2:1 adds support for Message Digest function interface +// - Version 2:2 adds PBKDF2 for password derivation +// - Version 3:0 adds a new IV mechanism +// - Version 3:1 drops support for verison 1:0 blowfish keys, in order to avoid +// having to duplicate the behavior of old EVP_BytesToKey implementations. +static Interface BlowfishInterface = makeInterface( "ssl/blowfish", 3, 1, 1 ); +static Interface AESInterface = makeInterface( "ssl/aes", 3, 1, 2 ); + +static Interface NullCipherInterface = makeInterface( "nullCipher", 1, 0, 0); + +static Range BFKeyRange(128,256,32); +static int BFDefaultKeyLen = 160; + +static Range AESKeyRange(128,256,64); +static int AESDefaultKeyLen = 192; + +list CipherV1::GetAlgorithmList() +{ + list result; + Registry blockCipherRegistry = BlockCipher::GetRegistry(); + + if (blockCipherRegistry.GetPropertiesForMatch(NAME_AES_CBC) != NULL) { + CipherV1::CipherAlgorithm alg; + alg.name = "AES"; + alg.description = "16 byte block cipher"; + alg.iface = AESInterface; + alg.keyLength = AESKeyRange; + alg.blockSize = Range(64, 4096, 16); + result.push_back(alg); + } + + if (blockCipherRegistry.GetPropertiesForMatch(NAME_BLOWFISH_CBC) != NULL) { + CipherV1::CipherAlgorithm alg; + alg.name = "Blowfish"; + alg.description = "8 byte block cipher"; + alg.iface = BlowfishInterface; + alg.keyLength = BFKeyRange; + alg.blockSize = Range(64, 4096, 8); + result.push_back(alg); + } + + CipherV1::CipherAlgorithm alg; + alg.name = "Null"; + alg.description = "Pass-through cipher, for testing only!"; + alg.iface = NullCipherInterface; + alg.keyLength = Range(0); + alg.blockSize = Range(64, 4096, 8); + result.push_back(alg); + + return result; +} + +shared_ptr CipherV1::New(const std::string& name, int keyLen) { + for (auto &it : GetAlgorithmList()) { + if (it.name == name) + return New(it.iface, keyLen); + } + + return shared_ptr(); +} + +shared_ptr CipherV1::New(const Interface &iface, int keyLen) { + return shared_ptr(new CipherV1(iface, iface, keyLen)); +} + +CipherV1::CipherV1(const Interface &iface, const Interface &realIface, + int keyLength) +{ + this->iface = iface; + this->realIface = realIface; + + Registry blockCipherRegistry = BlockCipher::GetRegistry(); + Registry streamCipherRegistry = StreamCipher::GetRegistry(); + + int defaultKeyLength; + Range keyRange; + + if (implements(AESInterface, iface)) { + keyRange = AESKeyRange; + defaultKeyLength = AESDefaultKeyLen; + _blockCipher.reset( blockCipherRegistry.CreateForMatch(NAME_AES_CBC) ); + _streamCipher.reset( streamCipherRegistry.CreateForMatch(NAME_AES_CFB) ); + } else if (implements(BlowfishInterface, iface)) { + keyRange = BFKeyRange; + defaultKeyLength = BFDefaultKeyLen; + _blockCipher.reset( blockCipherRegistry.CreateForMatch(NAME_BLOWFISH_CBC) ); + _streamCipher.reset( streamCipherRegistry.CreateForMatch + (NAME_BLOWFISH_CFB) ); + } else if (implements(NullCipherInterface, iface)) { + keyRange = Range(0); + defaultKeyLength = 0; + _blockCipher.reset( blockCipherRegistry.CreateForMatch("NullCipher") ); + _streamCipher.reset( streamCipherRegistry.CreateForMatch("NullCipher") ); + } else { + throw Error("Unsupported cipher"); + } + + if (!_blockCipher || !_streamCipher) { + throw Error("Requested cipher not available"); + } + + if (keyLength <= 0) + _keySize = defaultKeyLength / 8; + else + _keySize = keyRange.closest(keyLength) / 8; + + _pbkdf.reset(PBKDF::GetRegistry().CreateForMatch( + NAME_PKCS5_PBKDF2_HMAC_SHA1)); + if (!_pbkdf) { + throw Error("PBKDF not available"); + } + + // Initialize the cipher with a temporary key in order to determine the block + // size. + CipherKey tmpKey = _pbkdf->randomKey(_keySize); + _blockCipher->setKey(tmpKey); + _ivLength = _blockCipher->blockSize(); + _iv.reset(new SecureMem(_ivLength)); + _keySet = false; + + Lock l(_hmacMutex); + _hmac.reset(MAC::GetRegistry().CreateForMatch(NAME_SHA1_HMAC)); + if (!_hmac) + throw Error("SHA1_HMAC not available"); +} + +CipherV1::~CipherV1() +{ +} + +Interface CipherV1::interface() const +{ + return realIface; +} + +/* + Create a key from the password. + Use SHA to distribute entropy from the password into the key. + + This algorithm must remain constant for backward compatibility, as this key + is used to encipher/decipher the master key. + */ +CipherKey CipherV1::newKey(const char *password, int passwdLength, + int *iterationCount, long desiredDuration, + const byte *salt, int saltLen) +{ + CipherKey key(_keySize + _ivLength); + + if(*iterationCount == 0) + { + // timed run, fills in iteration count + int res = TimedPBKDF2(password, passwdLength, + salt, saltLen, &key, + 1000 * desiredDuration); + if(res <= 0) + { + LOG(ERROR) << "openssl error, PBKDF2 failed"; + return CipherKey(); + } else + *iterationCount = res; + } else + { + // known iteration length + if (!_pbkdf->makeKey(password, passwdLength, + salt, saltLen, *iterationCount, &key)) + { + LOG(ERROR) << "openssl error, PBKDF2 failed"; + return CipherKey(); + } + } + + return key; +} + +// Deprecated - for use only with filesystems which used a fixed-round PBKDF. +// Such configurations are replaced with a new PBKDF2 implementation when the +// password is changed or configuration is rewritten. +CipherKey CipherV1::newKey(const char *password, int passwdLength) +{ + CipherKey key(_keySize + _ivLength); + + bool ok = BytesToKey((byte *)password, passwdLength, 16, &key); + LOG_IF(ERROR, !ok) << "newKey: BytesToKey failed"; + if (!ok) + throw Error("BytesToKey failed"); + + return key; +} + +CipherKey CipherV1::newRandomKey() +{ + return _pbkdf->randomKey(_keySize + _ivLength); +} + +bool CipherV1::pseudoRandomize( byte *buf, int len ) +{ + return _pbkdf->pseudoRandom(buf, len); +} + +bool CipherV1::setKey(const CipherKey &keyIv) { + Lock l(_hmacMutex); + + // Key is actually key plus iv, so extract the different parts. + CipherKey key(_keySize); + memcpy(key.data(), keyIv.data(), _keySize); + memcpy(_iv->data, keyIv.data() + _keySize, _ivLength); + + if (_blockCipher->setKey(key) + && _streamCipher->setKey(key) + && _hmac->setKey(key)) { + _keySet = true; + return true; + } + + return false; +} + +uint64_t CipherV1::MAC_64(const byte *data, int len, + uint64_t *chainedIV ) const +{ + rAssert( len > 0 ); + rAssert( _keySet ); + + byte md[_hmac->outputSize()]; + + Lock l(_hmacMutex); + _hmac->reset(); + + _hmac->update(data, len); + if(chainedIV) + { + // toss in the chained IV as well + uint64_t tmp = *chainedIV; + byte h[8]; + for(unsigned int i=0; i<8; ++i) + { + h[i] = tmp & 0xff; + tmp >>= 8; + } + + _hmac->update(h, 8); + } + + bool ok = _hmac->write(md); + rAssert(ok); + + // chop this down to a 64bit value.. + byte h[8] = {0,0,0,0,0,0,0,0}; + // TODO: outputSize - 1? + for(unsigned int i=0; i<_hmac->outputSize(); ++i) + h[i%8] ^= (byte)(md[i]); + + uint64_t value = (uint64_t)h[0]; + for(int i=1; i<8; ++i) + value = (value << 8) | (uint64_t)h[i]; + + // TODO: should not be here. + if(chainedIV) + *chainedIV = value; + + return value; +} + +unsigned int CipherV1::reduceMac32(uint64_t mac64) +{ + return ((mac64 >> 32) & 0xffffffff) ^ (mac64 & 0xffffffff); +} + +unsigned int CipherV1::reduceMac16(uint64_t mac64) +{ + unsigned int mac32 = reduceMac32(mac64); + return ((mac32 >> 16) & 0xffff) ^ (mac32 & 0xffff); +} + +CipherKey CipherV1::readKey(const byte *data, bool checkKey) +{ + rAssert( _keySet ); + CipherKey key(_keySize + _ivLength); + + // First N bytes are checksum bytes. + unsigned int checksum = 0; + for(int i=0; i KEY_CHECKSUM_BYTES); + + SecureMem tmpBuf(ckey.size()); + memcpy(tmpBuf.data, ckey.data(), tmpBuf.size); + + unsigned int checksum = reduceMac32( + MAC_64( tmpBuf.data, tmpBuf.size, NULL )); + streamEncode(tmpBuf.data, tmpBuf.size, checksum); + + memcpy( out+KEY_CHECKSUM_BYTES, tmpBuf.data, tmpBuf.size ); + + // first N bytes contain HMAC derived checksum.. + for(int i=1; i<=KEY_CHECKSUM_BYTES; ++i) + { + out[KEY_CHECKSUM_BYTES-i] = checksum & 0xff; + checksum >>= 8; + } +} + +std::string CipherV1::encodeAsString(const CipherKey &key) +{ + rAssert( _keySet ); + int encodedSize = encodedKeySize(); + vector buf(encodedSize); + writeKey(key, buf.data()); + + int b64Len = B256ToB64Bytes( encodedSize ); + byte *b64Key = new byte[b64Len + 1]; + + changeBase2( buf.data(), encodedSize, 8, b64Key, b64Len, 6); + B64ToAscii( b64Key, b64Len ); + b64Key[ b64Len - 1 ] = '\0'; + + return string( (const char *)b64Key ); +} + +int CipherV1::encodedKeySize() const +{ + return _keySize + _ivLength + KEY_CHECKSUM_BYTES; +} + +int CipherV1::keySize() const +{ + return _keySize; +} + +int CipherV1::cipherBlockSize() const +{ + rAssert( _keySet ); + return _blockCipher->blockSize(); +} + +// Deprecated: For backward compatibility only. +// A watermark attack was discovered against this IV construction. If an +// attacker could get a victim to store a carefully crafted file, they could +// later determine if the victim had the file in encrypted storage (without +// decrypting the file). +static void setIVec_old(byte *ivec, int ivLen, unsigned int seed) +{ + unsigned int var1 = 0x060a4011 * seed; + unsigned int var2 = 0x0221040d * (seed ^ 0xD3FEA11C); + + ivec[0] ^= (var1 >> 24) & 0xff; + ivec[1] ^= (var2 >> 16) & 0xff; + ivec[2] ^= (var1 >> 8 ) & 0xff; + ivec[3] ^= (var2 ) & 0xff; + ivec[4] ^= (var2 >> 24) & 0xff; + ivec[5] ^= (var1 >> 16) & 0xff; + ivec[6] ^= (var2 >> 8 ) & 0xff; + ivec[7] ^= (var1 ) & 0xff; + + if(ivLen > 8) + { + ivec[8+0] ^= (var1 ) & 0xff; + ivec[8+1] ^= (var2 >> 8 ) & 0xff; + ivec[8+2] ^= (var1 >> 16) & 0xff; + ivec[8+3] ^= (var2 >> 24) & 0xff; + ivec[8+4] ^= (var1 >> 24) & 0xff; + ivec[8+5] ^= (var2 >> 16) & 0xff; + ivec[8+6] ^= (var1 >> 8 ) & 0xff; + ivec[8+7] ^= (var2 ) & 0xff; + } +} + +void CipherV1::setIVec(byte *ivec, uint64_t seed) const +{ + rAssert( _keySet ); + memcpy( ivec, _iv->data, _ivLength ); + if (iface.major() < 3) + { + // Backward compatible mode. + setIVec_old(ivec, _ivLength, seed); + return; + } + + vector md(_hmac->outputSize()); + for(int i=0; i<8; ++i) + { + md[i] = (byte)(seed & 0xff); + seed >>= 8; + } + + // combine ivec and seed with HMAC + Lock l(_hmacMutex); + _hmac->reset(); + _hmac->update(ivec, _ivLength); + _hmac->update(md.data(), 8); + _hmac->write(md.data()); + + memcpy(ivec, md.data(), _ivLength); +} + +static void flipBytes(byte *buf, int size) +{ + byte revBuf[64]; + + int bytesLeft = size; + while(bytesLeft) + { + int toFlip = MIN( (int)sizeof(revBuf), bytesLeft ); + + for(int i=0; i 0 ); + + vector ivec(_ivLength); + shuffleBytes( buf, size ); + + setIVec( ivec.data(), iv64 ); + if (!_streamCipher->encrypt(ivec.data(), buf, buf, size)) + return false; + + flipBytes( buf, size ); + shuffleBytes( buf, size ); + + setIVec( ivec.data(), iv64 + 1 ); + if (!_streamCipher->encrypt(ivec.data(), buf, buf, size)) + return false; + + return true; +} + +bool CipherV1::streamDecode(byte *buf, int size, uint64_t iv64) const +{ + rAssert( _keySet ); + rAssert( size > 0 ); + + vector ivec(_ivLength); + setIVec( ivec.data(), iv64 + 1 ); + if (!_streamCipher->decrypt(ivec.data(), buf, buf, size)) + return false; + + unshuffleBytes( buf, size ); + flipBytes( buf, size ); + + setIVec( ivec.data(), iv64 ); + if (!_streamCipher->decrypt(ivec.data(), buf, buf, size)) + return false; + + unshuffleBytes( buf, size ); + + return true; +} + + +bool CipherV1::blockEncode(byte *buf, int size, uint64_t iv64) const +{ + rAssert( _keySet ); + rAssert( size > 0 ); + + vector ivec(_ivLength); + setIVec( ivec.data(), iv64 ); + return _blockCipher->encrypt(ivec.data(), buf, buf, size); +} + +bool CipherV1::blockDecode(byte *buf, int size, uint64_t iv64) const +{ + rAssert( _keySet ); + rAssert( size > 0 ); + + vector ivec(_ivLength); + setIVec( ivec.data(), iv64 ); + return _blockCipher->decrypt(ivec.data(), buf, buf, size); +} + +} // namespace encfs diff --git a/cipher/SSL_Cipher.h b/cipher/CipherV1.h similarity index 57% rename from cipher/SSL_Cipher.h rename to cipher/CipherV1.h index 1dcd1d6..376cfc1 100644 --- a/cipher/SSL_Cipher.h +++ b/cipher/CipherV1.h @@ -2,7 +2,7 @@ * Author: Valient Gough * ***************************************************************************** - * Copyright (c) 2004, Valient Gough + * Copyright (c) 2004-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 @@ -18,22 +18,24 @@ * along with this program. If not, see . */ -#ifndef _SSL_Cipher_incl_ -#define _SSL_Cipher_incl_ +#ifndef _CipherV1_incl_ +#define _CipherV1_incl_ -#include "cipher/Cipher.h" #include "base/Interface.h" +#include "base/Mutex.h" +#include "base/shared_ptr.h" -#ifndef EVP_CIPHER -struct evp_cipher_st; -typedef struct evp_cipher_st EVP_CIPHER; -#endif +#include "cipher/BlockCipher.h" +#include "cipher/StreamCipher.h" +#include "cipher/MAC.h" +#include "cipher/PBKDF.h" namespace encfs { -class SSLKey; + +class SecureMem; /* - Implements Cipher interface for OpenSSL's ciphers. + Implements Encfs V1.x ciphers support. Design: Variable algorithm, key size, and block size. @@ -70,91 +72,107 @@ class SSLKey; initial value vector to randomize the output. But it makes the code simpler to reuse the encryption algorithm as is. */ -class SSL_Cipher : public Cipher +class CipherV1 { Interface iface; Interface realIface; - const EVP_CIPHER *_blockCipher; - const EVP_CIPHER *_streamCipher; + + shared_ptr _blockCipher; + shared_ptr _streamCipher; + shared_ptr _pbkdf; + + // HMac is stateful, so access is controlled via mutex. + mutable Mutex _hmacMutex; + mutable shared_ptr _hmac; + unsigned int _keySize; // in bytes unsigned int _ivLength; + shared_ptr _iv; + bool _keySet; + public: - SSL_Cipher(const Interface &iface, const Interface &realIface, - const EVP_CIPHER *blockCipher, const EVP_CIPHER *streamCipher, - int keyLength); - virtual ~SSL_Cipher(); - // returns the real interface, not the one we're emulating (if any).. - virtual Interface interface() const; + struct CipherAlgorithm + { + std::string name; + std::string description; + Interface iface; + Range keyLength; + Range blockSize; + }; - // create a new key based on a password - virtual CipherKey newKey(const char *password, int passwdLength, - int &iterationCount, long desiredDuration, - const byte *salt, int saltLen); - // deprecated - for backward compatibility - virtual CipherKey newKey(const char *password, int passwdLength); - // create a new random key - virtual CipherKey newRandomKey(); - - // data must be len keySize() - virtual CipherKey readKey(const byte *data, - const CipherKey &encodingKey, - bool checkKey); - virtual void writeKey(const CipherKey &key, byte *data, - const CipherKey &encodingKey); - virtual bool compareKey( const CipherKey &A, - const CipherKey &B ) const; - - // meta-data about the cypher - virtual int keySize() const; - virtual int encodedKeySize() const; - virtual int cipherBlockSize() const; - - virtual bool hasStreamMode() const; - - virtual bool randomize( byte *buf, int len, - bool strongRandom ) const; - - virtual uint64_t MAC_64( const byte *src, int len, - const CipherKey &key, uint64_t *augment ) const; - - // functional interfaces - /* - Stream encoding in-place. - */ - virtual bool streamEncode(byte *in, int len, - uint64_t iv64, const CipherKey &key) const; - virtual bool streamDecode(byte *in, int len, - uint64_t iv64, const CipherKey &key) const; - - /* - Block encoding is done in-place. Partial blocks are supported, but - blocks are always expected to begin on a block boundary. See - blockSize(). - */ - virtual bool blockEncode(byte *buf, int size, - uint64_t iv64, const CipherKey &key) const; - virtual bool blockDecode(byte *buf, int size, - uint64_t iv64, const CipherKey &key) const; - - // hack to help with static builds - static bool Enabled(); + // Returns a list of supported algorithms. + static std::list GetAlgorithmList(); + static shared_ptr New(const std::string &name, int keyLen = -1); + static shared_ptr New(const Interface &alg, int keyLen = -1); // Password-based key derivation function which determines the // number of iterations based on a desired execution time (in microseconds). // Returns the number of iterations applied. static int TimedPBKDF2(const char *pass, int passLen, const byte *salt, int saltLen, - int keyLen, byte *out, - long desiredPDFTimeMicroseconds); - private: - void setIVec( byte *ivec, uint64_t seed, - const shared_ptr &key ) const; + CipherKey *out, long desiredPDFTimeMicroseconds); + CipherV1(const Interface &iface, const Interface &realIface, int keyLength); + ~CipherV1(); + + // returns the real interface, not the one we're emulating (if any).. + Interface interface() const; + + // create a new key based on a password + CipherKey newKey(const char *password, int passwdLength, + int *iterationCount, long desiredDuration, + const byte *salt, int saltLen); // deprecated - for backward compatibility - void setIVec_old( byte *ivec, unsigned int seed, - const shared_ptr &key ) const; + CipherKey newKey(const char *password, int passwdLength); + // create a new random key + CipherKey newRandomKey(); + + // Read and decrypt a key. + // data must be len keySize() + CipherKey readKey(const byte *data, bool checkKey); + + // Encrypt and write the given key. + void writeKey(const CipherKey &key, byte *data); + + // Encrypt and store a key as a string. + std::string encodeAsString(const CipherKey &key); + + + // meta-data about the cypher + int keySize() const; + int encodedKeySize() const; + int cipherBlockSize() const; + + bool pseudoRandomize(byte *buf, int len); + + // Sets the key used for encoding / decoding, and MAC operations. + bool setKey(const CipherKey &key); + + uint64_t MAC_64(const byte *src, int len, + uint64_t *augment = NULL) const; + + static unsigned int reduceMac32(uint64_t mac64); + static unsigned int reduceMac16(uint64_t mac64); + + // functional interfaces + /* + Stream encoding in-place. + */ + bool streamEncode(byte *data, int len, uint64_t iv64) const; + bool streamDecode(byte *data, int len, uint64_t iv64) const; + + /* + Block encoding is done in-place. Partial blocks are supported, but + blocks are always expected to begin on a block boundary. See + blockSize(). + */ + bool blockEncode(byte *buf, int size, uint64_t iv64) const; + bool blockDecode(byte *buf, int size, uint64_t iv64) const; + + private: + void setIVec(byte *out, uint64_t seed) const; }; } // namespace encfs diff --git a/cipher/MAC.cpp b/cipher/MAC.cpp index 64e08e1..8534d77 100644 --- a/cipher/MAC.cpp +++ b/cipher/MAC.cpp @@ -2,13 +2,13 @@ namespace encfs { -DEFINE_REGISTERABLE_TYPE(MessageAuthenticationCode) +DEFINE_REGISTERABLE_TYPE(MAC) -MessageAuthenticationCode::MessageAuthenticationCode() +MAC::MAC() { } -MessageAuthenticationCode::~MessageAuthenticationCode() +MAC::~MAC() { } diff --git a/cipher/MAC.h b/cipher/MAC.h index d7849e2..436e6d6 100644 --- a/cipher/MAC.h +++ b/cipher/MAC.h @@ -5,14 +5,17 @@ #include "base/Registry.h" #include "base/types.h" +#include "cipher/CipherKey.h" namespace encfs { -// MessageAuthenticationCode provides keyed MAC algorithms, eg HMAC. -class MessageAuthenticationCode +static const char NAME_SHA1_HMAC[] = "SHA-1/HMAC"; + +// MAC provides keyed MessageAuthenticationCode algorithms, eg HMAC. +class MAC { public: - DECLARE_REGISTERABLE_TYPE(MessageAuthenticationCode); + DECLARE_REGISTERABLE_TYPE(MAC); struct Properties { int blockSize; // Block length of hash function. @@ -25,13 +28,12 @@ class MessageAuthenticationCode } }; - MessageAuthenticationCode(); - virtual ~MessageAuthenticationCode(); + MAC(); + virtual ~MAC(); virtual int outputSize() const =0; - virtual bool setKey(const byte *key, int keyLength) =0; - virtual bool randomKey(int keyLength) =0; + virtual bool setKey(const CipherKey &key) =0; virtual void reset() =0; virtual bool update(const byte *in, int length) =0; diff --git a/cipher/MAC_test.cpp b/cipher/MAC_test.cpp index c93bd8a..de8115c 100644 --- a/cipher/MAC_test.cpp +++ b/cipher/MAC_test.cpp @@ -32,37 +32,36 @@ using namespace encfs; namespace { -TEST(HMacSha1Test, MessageAuthenticationCode) { - Registry registry = - MessageAuthenticationCode::GetRegistry(); - shared_ptr hmac( - registry.CreateForMatch( "SHA-1/HMAC" )); +TEST(HMacSha1Test, MAC) { + Registry registry = MAC::GetRegistry(); + shared_ptr hmac( registry.CreateForMatch( NAME_SHA1_HMAC )); ASSERT_FALSE(!hmac); // Test cases from rfc2202 // Test case 1 - byte key[20]; + CipherKey key(20); byte out[20]; for (int i = 0; i < 20; ++i) - key[i] = 0x0b; - hmac->setKey(key, 20); + key.data()[i] = 0x0b; + hmac->setKey(key); 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); + key = CipherKey((const byte *)"Jefe", 4); + hmac->setKey(key); hmac->reset(); hmac->update((byte *)"what do ya want for nothing?", 28); hmac->write(out); ASSERT_EQ("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79", stringToHex(out, 20)); // Test case 3 + key = CipherKey(20); for (int i = 0; i < 20; ++i) - key[i] = 0xaa; - hmac->setKey(key, 20); + key.data()[i] = 0xaa; + hmac->setKey(key); hmac->reset(); { byte data[50]; @@ -73,9 +72,9 @@ TEST(HMacSha1Test, MessageAuthenticationCode) { ASSERT_EQ("125d7342b9ac11cd91a39af48aa17b4f63f175d3", stringToHex(out, 20)); // Test #7 - byte longKey[80]; - memset(longKey, 0xaa, 80); - hmac->setKey(longKey, 80); + key = CipherKey(80); + memset(key.data(), 0xaa, 80); + hmac->setKey(key); hmac->reset(); hmac->update((byte *)"Test Using Larger Than Block-Size Key and Larger " "Than One Block-Size Data", 73); diff --git a/cipher/MemoryPool.h b/cipher/MemoryPool.h index b6f1c54..26070a4 100644 --- a/cipher/MemoryPool.h +++ b/cipher/MemoryPool.h @@ -57,12 +57,13 @@ namespace MemoryPool void destroyAll(); } -struct SecureMem +class SecureMem { + public: int size; byte *data; - SecureMem(int len); + explicit SecureMem(int len); ~SecureMem(); }; diff --git a/cipher/NullCipher.cpp b/cipher/NullCipher.cpp deleted file mode 100644 index 7af4a10..0000000 --- a/cipher/NullCipher.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2004, 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 "cipher/NullCipher.h" - -#include "base/Range.h" -#include "base/Interface.h" -#include "base/shared_ptr.h" - -#include - -using namespace std; - -namespace encfs { - -static Interface NullInterface = makeInterface( "nullCipher", 1, 0, 0 ); -static Range NullKeyRange(0); -static Range NullBlockRange(1,4096,1); - -static shared_ptr NewNullCipher(const Interface &iface, int keyLen) -{ - (void)keyLen; - return shared_ptr( new NullCipher( iface ) ); -} - -const bool HiddenCipher = true; - -static bool NullCipher_registered = Cipher::Register("Null", - "Non encrypting cipher. For testing only!", - NullInterface, NullKeyRange, NullBlockRange, NewNullCipher, - HiddenCipher); - -class NullKey : public AbstractCipherKey -{ -public: - NullKey() {} - virtual ~NullKey() {} -}; - -class NullDestructor -{ -public: - NullDestructor() {} - NullDestructor(const NullDestructor &) {} - ~NullDestructor() {} - - NullDestructor &operator = (const NullDestructor &){ return *this; } - void operator ()(NullKey *&) {} -}; - -shared_ptr gNullKey( new NullKey(), NullDestructor() ); - -NullCipher::NullCipher(const Interface &iface_) -{ - this->iface = iface_; -} - -NullCipher::~NullCipher() -{ -} - -Interface NullCipher::interface() const -{ - return iface; -} - -CipherKey NullCipher::newKey(const char *, int, - int &, long, const byte *, int ) -{ - return gNullKey; -} - -CipherKey NullCipher::newKey(const char *, int) -{ - return gNullKey; -} - -CipherKey NullCipher::newRandomKey() -{ - return gNullKey; -} - -bool NullCipher::randomize( byte *buf, int len, bool ) const -{ - memset( buf, 0, len ); - return true; -} - -uint64_t NullCipher::MAC_64(const byte *, int , - const CipherKey &, uint64_t *) const -{ - return 0; -} - -CipherKey NullCipher::readKey( const byte *, - const CipherKey &, bool) -{ - return gNullKey; -} - -void NullCipher::writeKey(const CipherKey &, byte *, - const CipherKey &) -{ -} - -bool NullCipher::compareKey(const CipherKey &A_, - const CipherKey &B_) const -{ - shared_ptr A = dynamic_pointer_cast(A_); - shared_ptr B = dynamic_pointer_cast(B_); - return A.get() == B.get(); -} - -int NullCipher::encodedKeySize() const -{ - return 0; -} - -int NullCipher::keySize() const -{ - return 0; -} - -int NullCipher::cipherBlockSize() const -{ - return 1; -} - -bool NullCipher::streamEncode( byte *src, int len, - uint64_t iv64, const CipherKey &key) const -{ - (void)src; - (void)len; - (void)iv64; - (void)key; - return true; -} - -bool NullCipher::streamDecode( byte *src, int len, - uint64_t iv64, const CipherKey &key) const -{ - (void)src; - (void)len; - (void)iv64; - (void)key; - return true; -} - -bool NullCipher::blockEncode( byte *, int , uint64_t, - const CipherKey & ) const -{ - return true; -} - -bool NullCipher::blockDecode( byte *, int, uint64_t, - const CipherKey & ) const -{ - return true; -} - -bool NullCipher::Enabled() -{ - return true; -} - -} // namespace encfs diff --git a/cipher/NullCipher.h b/cipher/NullCipher.h deleted file mode 100644 index e3b815f..0000000 --- a/cipher/NullCipher.h +++ /dev/null @@ -1,89 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2004, 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 _NullCipher_incl_ -#define _NullCipher_incl_ - -#include "cipher/Cipher.h" -#include "base/Interface.h" - -namespace encfs { - -/* - Implements Cipher interface for a pass-through mode. May be useful for - testing, but that's it. -*/ -class NullCipher : public Cipher -{ - Interface iface; - -public: - NullCipher(const Interface &iface); - virtual ~NullCipher(); - - virtual Interface interface() const; - - // create a new key based on a password - virtual CipherKey newKey(const char *password, int passwdLength, - int &iterationCount, long desiredDuration, - const byte *salt, int saltLen); - virtual CipherKey newKey(const char *password, int passwdLength); - // create a new random key - virtual CipherKey newRandomKey(); - - // data must be len keySize() - virtual CipherKey readKey(const byte *data, - const CipherKey &encodingKey, - bool checkKey); - virtual void writeKey(const CipherKey &key, byte *data, - const CipherKey &encodingKey); - virtual bool compareKey( const CipherKey &A, - const CipherKey &B ) const; - - // meta-data about the cypher - virtual int keySize() const; - virtual int encodedKeySize() const; - virtual int cipherBlockSize() const; - - virtual bool randomize( byte *buf, int len, - bool strongRandom ) const; - - virtual uint64_t MAC_64(const byte *data, int len, - const CipherKey &key, uint64_t *chainedIV) const; - - // functional interfaces - virtual bool streamEncode(byte *in, int len, - uint64_t iv64, const CipherKey &key) const; - virtual bool streamDecode(byte *in, int len, - uint64_t iv64, const CipherKey &key) const; - - virtual bool blockEncode(byte *buf, int size, - uint64_t iv64, const CipherKey &key) const; - virtual bool blockDecode(byte *buf, int size, - uint64_t iv64, const CipherKey &key) const; - - // hack to help with static builds - static bool Enabled(); -}; - -} // namespace encfs - -#endif - diff --git a/cipher/NullCiphers.cpp b/cipher/NullCiphers.cpp new file mode 100644 index 0000000..b09ed4c --- /dev/null +++ b/cipher/NullCiphers.cpp @@ -0,0 +1,73 @@ + +/***************************************************************************** + * 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 "cipher/NullCiphers.h" + +#include "cipher/BlockCipher.h" +#include "cipher/StreamCipher.h" + +namespace encfs { + +class NullCipher : public BlockCipher { + public: + virtual ~NullCipher() {} + + virtual int blockSize() const { + return 8; + } + + virtual bool setKey(const CipherKey &key) { + return true; + } + + virtual bool encrypt(const byte *iv, const byte *in, + byte *out, int numBytes) { + if (in != out) + memcpy(out, in, numBytes); + return true; + } + + virtual bool decrypt(const byte *iv, const byte *in, + byte *out, int numBytes) { + if (in != out) + memcpy(out, in, numBytes); + return true; + } + + static Properties GetProperties() { + Properties props; + props.keySize = Range(0); + props.cipher = "NullCipher"; + props.mode = "ECB"; + props.library = "internal"; + return props; + } +}; + +REGISTER_CLASS(NullCipher, BlockCipher); +REGISTER_CLASS(NullCipher, StreamCipher); + +void NullCiphers::registerCiphers() { + // Nothing required. +} + +} // namespace encfs + diff --git a/cipher/NullCiphers.h b/cipher/NullCiphers.h new file mode 100644 index 0000000..b810ac9 --- /dev/null +++ b/cipher/NullCiphers.h @@ -0,0 +1,37 @@ + +/***************************************************************************** + * 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 _NULLCIPHERS_incl_ +#define _NULLCIPHERS_incl_ + +#include "base/Registry.h" + +namespace encfs { + +class NullCiphers { + public: + static void registerCiphers(); +}; + +} // namespace encfs + +#endif + diff --git a/cipher/PBKDF.h b/cipher/PBKDF.h index 228d9b1..0f1435d 100644 --- a/cipher/PBKDF.h +++ b/cipher/PBKDF.h @@ -5,9 +5,13 @@ #include "base/Registry.h" #include "base/types.h" +#include "cipher/CipherKey.h" namespace encfs { +// Well-known algorithms. +static const char NAME_PKCS5_PBKDF2_HMAC_SHA1[] = "PKCS5_PBKDF2_HMAC_SHA1"; + // Password Based Key Derivation Function. class PBKDF { @@ -26,8 +30,14 @@ class PBKDF virtual bool makeKey(const char *password, int passwordLength, const byte *salt, int saltLength, - int numIterations, - byte *outKey, int keyLength) const = 0; + int numIterations, CipherKey *outKey) = 0; + + // Create a new key with strong randomization. + virtual CipherKey randomKey(int length) =0; + + // Randomize the output. Pseudo randomization is allowed, so this may not be + // used for keys or other critical values. + virtual bool pseudoRandom(byte *out, int byteLen) =0; }; } // namespace encfs diff --git a/cipher/PBKDF_test.cpp b/cipher/PBKDF_test.cpp index 0faeb23..350de9f 100644 --- a/cipher/PBKDF_test.cpp +++ b/cipher/PBKDF_test.cpp @@ -34,42 +34,37 @@ namespace { TEST(PKCS5_PBKDF2_HMAC_SHA1, PBKDF) { Registry registry = PBKDF::GetRegistry(); - shared_ptr impl( registry.CreateForMatch( "PKCS5_PBKDF2_HMAC_SHA1" )); + shared_ptr impl( registry.CreateForMatch(NAME_PKCS5_PBKDF2_HMAC_SHA1)); ASSERT_FALSE(!impl); // Test cases from rfc6070 // Test case 1 { - byte key[20]; + CipherKey key(20); bool ok = impl->makeKey("password", 8, (byte*)"salt", 4, - 1, - key, sizeof(key)); + 1, &key); ASSERT_TRUE(ok); - ASSERT_EQ("0c60c80f961f0e71f3a9b524af6012062fe037a6", - stringToHex(key, sizeof(key))); + ASSERT_EQ("0c60c80f961f0e71f3a9b524af6012062fe037a6", stringToHex(key)); } { - byte key[25]; + CipherKey key(25); bool ok = impl->makeKey("passwordPASSWORDpassword", 24, (byte*)"saltSALTsaltSALTsaltSALTsaltSALTsalt", 36, - 4096, - key, sizeof(key)); + 4096, &key); ASSERT_TRUE(ok); ASSERT_EQ("3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038", - stringToHex(key, sizeof(key))); + stringToHex(key)); } { - byte key[16]; + CipherKey key(16); bool ok = impl->makeKey("pass\0word", 9, (byte*)"sa\0lt", 5, - 4096, - key, sizeof(key)); + 4096, &key); ASSERT_TRUE(ok); - ASSERT_EQ("56fa6aa75548099dcc37d7f03425e0c3", - stringToHex(key, sizeof(key))); + ASSERT_EQ("56fa6aa75548099dcc37d7f03425e0c3", stringToHex(key)); } } diff --git a/cipher/SSL_Cipher.cpp b/cipher/SSL_Cipher.cpp deleted file mode 100644 index b434ee6..0000000 --- a/cipher/SSL_Cipher.cpp +++ /dev/null @@ -1,978 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2004, 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 "base/config.h" - -#include -#include -#include -#include -#include - -#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" - -#include -#include - -#include -#include - -#include - -#include "base/i18n.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 - -/* - This produces the same result as OpenSSL's EVP_BytesToKey. The difference - is that here we can explicitly specify the key size, instead of relying on - the state of EVP_CIPHER struct. EVP_BytesToKey will only produce 128 bit - keys for the EVP Blowfish interface, which is not what we want. - - DEPRECATED: this is here for backward compatibilty only. Use PBKDF -*/ -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; - int nkey = key ? keyLen : 0; - int niv = iv ? ivLen : 0; - - EVP_MD_CTX cx; - EVP_MD_CTX_init( &cx ); - - for(;;) - { - EVP_DigestInit_ex( &cx, md, NULL ); - if( addmd++ ) - EVP_DigestUpdate( &cx, mdBuf, mds ); - EVP_DigestUpdate( &cx, data, dataLen ); - EVP_DigestFinal_ex( &cx, mdBuf, &mds ); - - for(unsigned int i=1; i < rounds; ++i) - { - EVP_DigestInit_ex( &cx, md, NULL ); - EVP_DigestUpdate( &cx, mdBuf, mds ); - EVP_DigestFinal_ex( &cx, mdBuf, &mds ); - } - - int offset = 0; - int toCopy = MIN( nkey, (int)mds - offset ); - if( toCopy ) - { - memcpy( key, mdBuf+offset, toCopy ); - key += toCopy; - nkey -= toCopy; - offset += toCopy; - } - toCopy = MIN( niv, (int)mds - offset ); - if( toCopy ) - { - memcpy( iv, mdBuf+offset, toCopy ); - iv += toCopy; - niv -= toCopy; - offset += toCopy; - } - if((nkey == 0) && (niv == 0)) break; - } - EVP_MD_CTX_cleanup( &cx ); - OPENSSL_cleanse( mdBuf, sizeof(mdBuf) ); - - return keyLen; -} - -long time_diff(const timeval &end, const timeval &start) -{ - return (end.tv_sec - start.tv_sec) * 1000 * 1000 + - (end.tv_usec - start.tv_usec); -} - -int SSL_Cipher::TimedPBKDF2(const char *pass, int passlen, - const byte *salt, int saltlen, - int keylen, byte *out, - long desiredPDFTime) -{ - int iter = 1000; - timeval start, end; - - for(;;) - { - gettimeofday( &start, 0 ); - int res = PKCS5_PBKDF2_HMAC_SHA1( - pass, passlen, const_cast(salt), saltlen, - iter, keylen, out); - if(res != 1) - return -1; - - gettimeofday( &end, 0 ); - - long delta = time_diff(end, start); - if(delta < desiredPDFTime / 8) - { - iter *= 4; - } else if(delta < (5 * desiredPDFTime / 6)) - { - // estimate number of iterations to get close to desired time - iter = (int)((double)iter * (double)desiredPDFTime - / (double)delta); - } else - return iter; - } -} - - -// - Version 1:0 used EVP_BytesToKey, which didn't do the right thing for -// Blowfish key lengths > 128 bit. -// - Version 2:0 uses BytesToKey. -// We support both 2:0 and 1:0, hence current:revision:age = 2:0:1 -// - Version 2:1 adds support for Message Digest function interface -// - Version 2:2 adds PBKDF2 for password derivation -// - Version 3:0 adds a new IV mechanism -// - Version 3:1 adds ssl/aes_xts -static Interface BlowfishInterface = makeInterface( "ssl/blowfish", 3, 0, 2 ); -static Interface AESInterface = makeInterface( "ssl/aes", 3, 0, 2 ); -static Interface AesXtsInterface = makeInterface( "ssl/aes_xts", 3, 1, 2 ); - -#if defined(HAVE_EVP_BF) - -static Range BFKeyRange(128,256,32); -static Range BFBlockRange(64,4096,8); - -static shared_ptr NewBFCipher( const Interface &iface, int keyLen ) -{ - if( keyLen <= 0 ) - keyLen = 160; - - keyLen = BFKeyRange.closest( 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) ); -} - -static bool BF_Cipher_registered = Cipher::Register( - "Blowfish", - // xgroup(setup) - gettext_noop("8 byte block cipher"), - BlowfishInterface, BFKeyRange, BFBlockRange, NewBFCipher, true); -#endif - - -#if defined(HAVE_EVP_AES) - -static Range AESKeyRange(128,256,64); -static Range AESBlockRange(64,4096,16); - -static shared_ptr NewAESCipher( const Interface &iface, int keyLen ) -{ - if( keyLen <= 0 ) - keyLen = 192; - - keyLen = AESKeyRange.closest( keyLen ); - - const EVP_CIPHER *blockCipher = 0; - const EVP_CIPHER *streamCipher = 0; - - switch(keyLen) - { - case 128: - blockCipher = EVP_aes_128_cbc(); - streamCipher = EVP_aes_128_cfb(); - break; - - case 192: - blockCipher = EVP_aes_192_cbc(); - streamCipher = EVP_aes_192_cfb(); - break; - - case 256: - default: - blockCipher = EVP_aes_256_cbc(); - streamCipher = EVP_aes_256_cfb(); - break; - } - - return shared_ptr( new SSL_Cipher(iface, AESInterface, - blockCipher, streamCipher, keyLen / 8) ); -} - -static bool AES_Cipher_registered = Cipher::Register( - "AES", "16 byte block cipher", - AESInterface, AESKeyRange, AESBlockRange, NewAESCipher, true); -#endif - -#if defined(HAVE_EVP_AES_XTS) - -static Range AesXtsKeyRange(128,256,128); -static Range AesXtsBlockRange(1024,8192,256); - -static shared_ptr NewAesXtsCipher( const Interface &iface, int keyLen ) -{ - if( keyLen <= 0 ) - keyLen = 256; - - keyLen = AesXtsKeyRange.closest( keyLen ); - - const EVP_CIPHER *blockCipher = 0; - - switch(keyLen) - { - case 128: - blockCipher = EVP_aes_128_xts(); - break; - - case 256: - default: - blockCipher = EVP_aes_256_xts(); - break; - } - - // XTS uses 2 keys, so the key size is doubled here. - // Eg XTS-AES-256 uses two 256 bit keys. - return shared_ptr( new SSL_Cipher(iface, AesXtsInterface, - blockCipher, NULL, 2 * keyLen / 8) ); -} - -static bool AES_XTS_Cipher_registered = Cipher::Register( - "AES_XTS", "Tweakable wide-block cipher", - AesXtsInterface, AesXtsKeyRange, AesXtsBlockRange, NewAesXtsCipher, false); -#endif - -class SSLKey : public AbstractCipherKey -{ -public: - pthread_mutex_t mutex; - - unsigned int keySize; // in bytes - unsigned int ivLength; - - // 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; - - HMAC_CTX mac_ctx; - - SSLKey(int keySize, int ivLength); - ~SSLKey(); -}; - -SSLKey::SSLKey(int keySize_, int ivLength_) -: buf(keySize_ + ivLength_) -{ - rAssert(keySize_ >= 8); - rAssert(ivLength_ >= 8); - - this->keySize = keySize_; - this->ivLength = ivLength_; - pthread_mutex_init( &mutex, 0 ); -} - -SSLKey::~SSLKey() -{ - keySize = 0; - ivLength = 0; - - EVP_CIPHER_CTX_cleanup( &block_enc ); - EVP_CIPHER_CTX_cleanup( &block_dec ); - - EVP_CIPHER_CTX_cleanup( &stream_enc ); - EVP_CIPHER_CTX_cleanup( &stream_dec ); - - HMAC_CTX_cleanup( &mac_ctx ); - - pthread_mutex_destroy( &mutex ); -} - -inline byte* KeyData( const shared_ptr &key ) -{ - return (byte *)key->buf.data; -} - -inline byte* IVData( const shared_ptr &key ) -{ - return (byte *)key->buf.data + key->keySize; -} - -void initKey(const shared_ptr &key, const EVP_CIPHER *_blockCipher, - 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 - // every block.. - EVP_CIPHER_CTX_init( &key->block_enc ); - EVP_CIPHER_CTX_init( &key->block_dec ); - EVP_EncryptInit_ex( &key->block_enc, _blockCipher, NULL, NULL, NULL); - EVP_DecryptInit_ex( &key->block_dec, _blockCipher, NULL, NULL, NULL); - EVP_CIPHER_CTX_set_key_length( &key->block_enc, _keySize ); - EVP_CIPHER_CTX_set_key_length( &key->block_dec, _keySize ); - EVP_CIPHER_CTX_set_padding( &key->block_enc, 0 ); - EVP_CIPHER_CTX_set_padding( &key->block_dec, 0 ); - EVP_EncryptInit_ex( &key->block_enc, NULL, NULL, KeyData(key), NULL); - EVP_DecryptInit_ex( &key->block_dec, NULL, NULL, KeyData(key), NULL); - - EVP_CIPHER_CTX_init( &key->stream_enc ); - EVP_CIPHER_CTX_init( &key->stream_dec ); - if (_streamCipher != NULL) - { - EVP_EncryptInit_ex( &key->stream_enc, _streamCipher, NULL, NULL, NULL); - EVP_DecryptInit_ex( &key->stream_dec, _streamCipher, NULL, NULL, NULL); - EVP_CIPHER_CTX_set_key_length( &key->stream_enc, _keySize ); - EVP_CIPHER_CTX_set_key_length( &key->stream_dec, _keySize ); - EVP_CIPHER_CTX_set_padding( &key->stream_enc, 0 ); - EVP_CIPHER_CTX_set_padding( &key->stream_dec, 0 ); - EVP_EncryptInit_ex( &key->stream_enc, NULL, NULL, KeyData(key), NULL); - EVP_DecryptInit_ex( &key->stream_dec, NULL, NULL, KeyData(key), NULL); - } - - HMAC_CTX_init( &key->mac_ctx ); - HMAC_Init_ex( &key->mac_ctx, KeyData(key), _keySize, EVP_sha1(), 0 ); -} - - -SSL_Cipher::SSL_Cipher(const Interface &iface_, - const Interface &realIface_, - const EVP_CIPHER *blockCipher, - const EVP_CIPHER *streamCipher, - int keySize_) -{ - this->iface = iface_; - this->realIface = realIface_; - this->_blockCipher = blockCipher; - this->_streamCipher = streamCipher; - this->_keySize = keySize_; - this->_ivLength = EVP_CIPHER_iv_length( _blockCipher ); - - rAssert(_ivLength == 8 || _ivLength == 16); - rAssert(_ivLength <= _keySize); - - VLOG(1) << "allocated cipher " << iface.name() - << ", keySize " << _keySize - << ", ivlength " << _ivLength; - - // EVP_CIPHER_key_length isn't useful for variable-length ciphers like - // Blowfish. Version 1 relied upon it incorrectly. - if( (EVP_CIPHER_key_length( _blockCipher ) != (int )_keySize) - && 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; - } -} - -SSL_Cipher::~SSL_Cipher() -{ -} - -Interface SSL_Cipher::interface() const -{ - return realIface; -} - -/* - Create a key from the password. - Use SHA to distribute entropy from the password into the key. - - 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) -{ - shared_ptr key( new SSLKey( _keySize, _ivLength) ); - - if(iterationCount == 0) - { - // timed run, fills in iteration count - int res = TimedPBKDF2(password, passwdLength, - salt, saltLen, - _keySize+_ivLength, KeyData(key), - 1000 * desiredDuration); - if(res <= 0) - { - LOG(ERROR) << "openssl error, PBKDF2 failed"; - return CipherKey(); - } else - iterationCount = res; - } else - { - // known iteration length - if(PKCS5_PBKDF2_HMAC_SHA1( - password, passwdLength, - const_cast(salt), saltLen, - iterationCount, _keySize + _ivLength, KeyData(key)) != 1) - { - LOG(ERROR) << "openssl error, PBKDF2 failed"; - return CipherKey(); - } - } - - initKey( key, _blockCipher, _streamCipher, _keySize ); - - return key; -} - -CipherKey SSL_Cipher::newKey(const char *password, int passwdLength) -{ - shared_ptr key( new SSLKey( _keySize, _ivLength) ); - - int bytes = 0; - if( iface.major() > 1 ) - { - // now we use BytesToKey, which can deal with Blowfish keys larger then - // 128 bits. - bytes = BytesToKey( _keySize, _ivLength, EVP_sha1(), - (byte *)password, passwdLength, 16, - KeyData(key), IVData(key) ); - - // the reason for moving from EVP_BytesToKey to BytesToKey function.. - if(bytes != (int)_keySize) - { - LOG(WARNING) << "newKey: BytesToKey returned " << bytes - << ", expecting " << _keySize << " key bytes"; - } - } else - { - // for backward compatibility with filesystems created with 1:0 - bytes = EVP_BytesToKey( _blockCipher, EVP_sha1(), NULL, - (byte *)password, passwdLength, 16, - KeyData(key), IVData(key) ); - } - - initKey( key, _blockCipher, _streamCipher, _keySize ); - - return key; -} - -/* - Create a random key. - We use the OpenSSL library to generate random bytes, then take the hash of - those bytes to use as the key. - - This algorithm can change at any time without affecting backward - compatibility. - */ -CipherKey SSL_Cipher::newRandomKey() -{ - const int bufLen = MAX_KEYLENGTH; - byte tmpBuf[ bufLen ]; - int saltLen = 20; - byte saltBuf[ saltLen ]; - - if(!randomize(tmpBuf, bufLen, true) || - !randomize(saltBuf, saltLen, true)) - return CipherKey(); - - shared_ptr key( new SSLKey( _keySize, _ivLength) ); - - // doesn't need to be versioned, because a random key is a random key.. - // Doesn't need to be reproducable.. - if(PKCS5_PBKDF2_HMAC_SHA1((char*)tmpBuf, bufLen, saltBuf, saltLen, - 1000, _keySize + _ivLength, KeyData(key)) != 1) - { - LOG(ERROR) << "openssl error, PBKDF2 failed"; - return CipherKey(); - } - - OPENSSL_cleanse(tmpBuf, bufLen); - - initKey( key, _blockCipher, _streamCipher, _keySize ); - - return key; -} - -/* - 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) -{ - rAssert( dataLen > 0 ); - Lock lock( key->mutex ); - - byte md[EVP_MAX_MD_SIZE]; - unsigned int mdLen = EVP_MAX_MD_SIZE; - - HMAC_Init_ex( &key->mac_ctx, 0, 0, 0, 0 ); - HMAC_Update( &key->mac_ctx, data, dataLen ); - if(chainedIV) - { - // toss in the chained IV as well - uint64_t tmp = *chainedIV; - byte h[8]; - for(unsigned int i=0; i<8; ++i) - { - h[i] = tmp & 0xff; - tmp >>= 8; - } - - HMAC_Update( &key->mac_ctx, h, 8 ); - } - - HMAC_Final( &key->mac_ctx, md, &mdLen ); - - rAssert(mdLen >= 8); - - // chop this down to a 64bit value.. - byte h[8] = {0,0,0,0,0,0,0,0}; - for(unsigned int i=0; i<(mdLen-1); ++i) - h[i%8] ^= (byte)(md[i]); - - uint64_t value = (uint64_t)h[0]; - for(int i=1; i<8; ++i) - value = (value << 8) | (uint64_t)h[i]; - - return value; -} - -bool SSL_Cipher::randomize( byte *buf, int len, - bool strongRandom ) const -{ - // to avoid warnings of uninitialized data from valgrind - memset(buf, 0, len); - int result; - if(strongRandom) - result = RAND_bytes( buf, len ); - else - result = RAND_pseudo_bytes( buf, 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; - } else - return true; -} - -uint64_t SSL_Cipher::MAC_64( const byte *data, int len, - const CipherKey &key, uint64_t *chainedIV ) const -{ - shared_ptr mk = dynamic_pointer_cast(key); - uint64_t tmp = _checksum_64( mk.get(), data, len, chainedIV ); - - if(chainedIV) - *chainedIV = tmp; - - return tmp; -} - -CipherKey SSL_Cipher::readKey(const byte *data, - const CipherKey &masterKey, bool checkKey) -{ - shared_ptr mk = dynamic_pointer_cast(masterKey); - rAssert(mk->keySize == _keySize); - - byte tmpBuf[ 2 * MAX_KEYLENGTH ]; - - // First N bytes are checksum bytes. - unsigned int checksum = 0; - for(int i=0; i key( new SSLKey( _keySize, _ivLength) ); - - rAssert(_keySize + _ivLength == (unsigned int)key->buf.size ); - memcpy( key->buf.data, tmpBuf, key->buf.size ); - OPENSSL_cleanse(tmpBuf, sizeof(tmpBuf)); - - initKey( key, _blockCipher, _streamCipher, _keySize ); - - return key; -} - -void SSL_Cipher::writeKey(const CipherKey &ckey, byte *data, - const CipherKey &masterKey) -{ - shared_ptr key = dynamic_pointer_cast(ckey); - rAssert(key->keySize == _keySize); - rAssert(key->ivLength == _ivLength); - - shared_ptr mk = dynamic_pointer_cast(masterKey); - rAssert(mk->keySize == _keySize); - rAssert(mk->ivLength == _ivLength); - - byte tmpBuf[ 2 * MAX_KEYLENGTH ]; - - unsigned int bufLen = key->buf.size; - rAssert(_keySize + _ivLength == bufLen ); - memcpy( tmpBuf, key->buf.data, bufLen ); - - unsigned int checksum = MAC_32( tmpBuf, bufLen, masterKey ); - - if (_streamCipher != NULL) - streamEncode(tmpBuf, bufLen, checksum, masterKey); - else - { - bufLen = 2 * _keySize; - blockEncode(tmpBuf, bufLen, checksum, masterKey); - } - - memcpy( data+KEY_CHECKSUM_BYTES, tmpBuf, bufLen ); - - // first N bytes contain HMAC derived checksum.. - for(int i=1; i<=KEY_CHECKSUM_BYTES; ++i) - { - data[KEY_CHECKSUM_BYTES-i] = checksum & 0xff; - checksum >>= 8; - } - - OPENSSL_cleanse(tmpBuf, sizeof(tmpBuf)); -} - -bool SSL_Cipher::compareKey( const CipherKey &A, const CipherKey &B) const -{ - shared_ptr key1 = dynamic_pointer_cast(A); - shared_ptr key2 = dynamic_pointer_cast(B); - - rAssert(key1->buf.size == key2->buf.size); - - if(memcmp(key1->buf.data, key2->buf.data, key1->buf.size) != 0) - return false; - else - return true; -} - -int SSL_Cipher::encodedKeySize() const -{ - if (_streamCipher != NULL) - return _keySize + _ivLength + KEY_CHECKSUM_BYTES; - else - return 2 * _keySize + KEY_CHECKSUM_BYTES; -} - -int SSL_Cipher::keySize() const -{ - return _keySize; -} - -int SSL_Cipher::cipherBlockSize() const -{ - int size = EVP_CIPHER_block_size( _blockCipher ); - // OpenSSL (1.0.1-4ubuntu5.5) reports a block size of 1 for aes_xts. - // If this happens, use a single key width (ie 32 bytes for aes-xts-256). - if (size == 1) - size = _keySize / 2; - return size; -} - -void SSL_Cipher::setIVec(byte *ivec, uint64_t seed, - const shared_ptr &key) const -{ - if (iface.major() >= 3) - { - memcpy( ivec, IVData(key), _ivLength ); - - byte md[EVP_MAX_MD_SIZE]; - unsigned int mdLen = EVP_MAX_MD_SIZE; - - for(int i=0; i<8; ++i) - { - md[i] = (byte)(seed & 0xff); - seed >>= 8; - } - - // combine ivec and seed with HMAC - HMAC_Init_ex( &key->mac_ctx, 0, 0, 0, 0 ); - HMAC_Update( &key->mac_ctx, ivec, _ivLength ); - HMAC_Update( &key->mac_ctx, md, 8 ); - HMAC_Final( &key->mac_ctx, md, &mdLen ); - rAssert(mdLen >= _ivLength); - - memcpy( ivec, md, _ivLength ); - } else - { - setIVec_old(ivec, seed, key); - } -} - -// Deprecated: For backward compatibility only. -// A watermark attack was discovered against this IV setup. If an attacker -// could get a victim to store a carefully crafted file, they could later -// determine if the victim had the file in encrypted storage (without decrypting -// the file). -void SSL_Cipher::setIVec_old(byte *ivec, - unsigned int seed, - const shared_ptr &key) const -{ - unsigned int var1 = 0x060a4011 * seed; - unsigned int var2 = 0x0221040d * (seed ^ 0xD3FEA11C); - - memcpy( ivec, IVData(key), _ivLength ); - - ivec[0] ^= (var1 >> 24) & 0xff; - ivec[1] ^= (var2 >> 16) & 0xff; - ivec[2] ^= (var1 >> 8 ) & 0xff; - ivec[3] ^= (var2 ) & 0xff; - ivec[4] ^= (var2 >> 24) & 0xff; - ivec[5] ^= (var1 >> 16) & 0xff; - ivec[6] ^= (var2 >> 8 ) & 0xff; - ivec[7] ^= (var1 ) & 0xff; - - if(_ivLength > 8) - { - ivec[8+0] ^= (var1 ) & 0xff; - ivec[8+1] ^= (var2 >> 8 ) & 0xff; - ivec[8+2] ^= (var1 >> 16) & 0xff; - ivec[8+3] ^= (var2 >> 24) & 0xff; - ivec[8+4] ^= (var1 >> 24) & 0xff; - ivec[8+5] ^= (var2 >> 16) & 0xff; - ivec[8+6] ^= (var1 >> 8 ) & 0xff; - ivec[8+7] ^= (var2 ) & 0xff; - } -} - -static void flipBytes(byte *buf, int size) -{ - byte revBuf[64]; - - int bytesLeft = size; - while(bytesLeft) - { - int toFlip = MIN( (int)sizeof(revBuf), bytesLeft ); - - for(int i=0; i 0 ); - shared_ptr key = dynamic_pointer_cast(ckey); - rAssert(key->keySize == _keySize); - rAssert(key->ivLength == _ivLength); - rAssert( key->stream_enc.key_len > 0 ); - - Lock lock( key->mutex ); - - byte ivec[ MAX_IVLENGTH ]; - int dstLen=0, tmpLen=0; - - shuffleBytes( buf, size ); - - setIVec( ivec, iv64, key ); - EVP_EncryptInit_ex( &key->stream_enc, NULL, NULL, NULL, ivec); - EVP_EncryptUpdate( &key->stream_enc, buf, &dstLen, buf, size ); - EVP_EncryptFinal_ex( &key->stream_enc, buf+dstLen, &tmpLen ); - - flipBytes( buf, size ); - shuffleBytes( buf, size ); - - setIVec( ivec, iv64 + 1, key ); - EVP_EncryptInit_ex( &key->stream_enc, NULL, NULL, NULL, ivec); - EVP_EncryptUpdate( &key->stream_enc, buf, &dstLen, buf, size ); - EVP_EncryptFinal_ex( &key->stream_enc, buf+dstLen, &tmpLen ); - - dstLen += tmpLen; - LOG_IF(ERROR, dstLen != size) << "encoding " << size - << " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)"; - - return true; -} - -bool SSL_Cipher::streamDecode(byte *buf, int size, - uint64_t iv64, const CipherKey &ckey) const -{ - rAssert( size > 0 ); - shared_ptr key = dynamic_pointer_cast(ckey); - rAssert(key->keySize == _keySize); - rAssert(key->ivLength == _ivLength); - rAssert( key->stream_dec.key_len > 0 ); - - Lock lock( key->mutex ); - - byte ivec[ MAX_IVLENGTH ]; - int dstLen=0, tmpLen=0; - - setIVec( ivec, iv64 + 1, key ); - EVP_DecryptInit_ex( &key->stream_dec, NULL, NULL, NULL, ivec); - EVP_DecryptUpdate( &key->stream_dec, buf, &dstLen, buf, size ); - EVP_DecryptFinal_ex( &key->stream_dec, buf+dstLen, &tmpLen ); - - unshuffleBytes( buf, size ); - flipBytes( buf, size ); - - setIVec( ivec, iv64, key ); - EVP_DecryptInit_ex( &key->stream_dec, NULL, NULL, NULL, ivec); - EVP_DecryptUpdate( &key->stream_dec, buf, &dstLen, buf, size ); - EVP_DecryptFinal_ex( &key->stream_dec, buf+dstLen, &tmpLen ); - - unshuffleBytes( buf, size ); - - dstLen += tmpLen; - LOG_IF(ERROR, dstLen != size) << "encoding " << size - << " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)"; - - return true; -} - - -bool SSL_Cipher::blockEncode(byte *buf, int size, - uint64_t iv64, const CipherKey &ckey ) const -{ - rAssert( size > 0 ); - shared_ptr key = dynamic_pointer_cast(ckey); - rAssert(key->keySize == _keySize); - rAssert(key->ivLength == _ivLength); - - // data must be integer number of blocks - const int blockMod = size % EVP_CIPHER_CTX_block_size( &key->block_enc ); - rAssert(blockMod == 0); - - Lock lock( key->mutex ); - - byte ivec[ MAX_IVLENGTH ]; - - int dstLen = 0, tmpLen = 0; - setIVec( ivec, iv64, key ); - - EVP_EncryptInit_ex( &key->block_enc, NULL, NULL, NULL, ivec); - EVP_EncryptUpdate( &key->block_enc, buf, &dstLen, buf, size ); - EVP_EncryptFinal_ex( &key->block_enc, buf+dstLen, &tmpLen ); - dstLen += tmpLen; - - LOG_IF(ERROR, dstLen != size) << "encoding " << size - << " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)"; - - return true; -} - -bool SSL_Cipher::blockDecode(byte *buf, int size, - uint64_t iv64, const CipherKey &ckey ) const -{ - rAssert( size > 0 ); - shared_ptr key = dynamic_pointer_cast(ckey); - rAssert(key->keySize == _keySize); - rAssert(key->ivLength == _ivLength); - - // data must be integer number of blocks - const int blockMod = size % EVP_CIPHER_CTX_block_size( &key->block_dec ); - rAssert(blockMod == 0); - - Lock lock( key->mutex ); - - byte ivec[ MAX_IVLENGTH ]; - - int dstLen = 0, tmpLen = 0; - setIVec( ivec, iv64, key ); - - EVP_DecryptInit_ex( &key->block_dec, NULL, NULL, NULL, ivec); - EVP_DecryptUpdate( &key->block_dec, buf, &dstLen, buf, size ); - EVP_DecryptFinal_ex( &key->block_dec, buf+dstLen, &tmpLen ); - dstLen += tmpLen; - - LOG_IF(ERROR, dstLen != size) << "decoding " << size - << " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)"; - - return true; -} - -bool SSL_Cipher::Enabled() -{ - return true; -} - -bool SSL_Cipher::hasStreamMode() const -{ - return false; -} - -} // namespace encfs diff --git a/cipher/StreamCipher.cpp b/cipher/StreamCipher.cpp index c12e31a..ef92dba 100644 --- a/cipher/StreamCipher.cpp +++ b/cipher/StreamCipher.cpp @@ -2,11 +2,7 @@ namespace encfs { -Registry& StreamCipher::GetRegistry() -{ - static Registry registry; - return registry; -} +DEFINE_REGISTERABLE_TYPE(StreamCipher); StreamCipher::StreamCipher() { diff --git a/cipher/StreamCipher.h b/cipher/StreamCipher.h index 6efb6e0..3a54e1e 100644 --- a/cipher/StreamCipher.h +++ b/cipher/StreamCipher.h @@ -26,26 +26,32 @@ #include "base/Registry.h" #include "base/shared_ptr.h" #include "base/types.h" +#include "cipher/CipherKey.h" namespace encfs { +static const char NAME_AES_CFB[] = "AES/CFB"; +static const char NAME_BLOWFISH_CFB[] = "Blowfish/CFB"; + class StreamCipher { public: - static Registry& GetRegistry(); + DECLARE_REGISTERABLE_TYPE(StreamCipher); struct Properties { Range keySize; std::string cipher; std::string mode; std::string library; + std::string toString() const { + return cipher + "/" + mode; + } }; StreamCipher(); virtual ~StreamCipher(); - virtual bool setKey(const byte *key, int keyLength) =0; - virtual bool randomKey(int keyLength) =0; + virtual bool setKey(const CipherKey& key) =0; virtual bool encrypt(const byte *iv, const byte *in, byte *out, int numBytes) =0; diff --git a/cipher/openssl.cpp b/cipher/openssl.cpp index 9ec2982..ebf5f63 100644 --- a/cipher/openssl.cpp +++ b/cipher/openssl.cpp @@ -74,6 +74,8 @@ inline int MIN(int a, int b) class OpenSSLCipher : public BlockCipher { public: OpenSSLCipher() { + EVP_CIPHER_CTX_init( &enc ); + EVP_CIPHER_CTX_init( &dec ); } virtual ~OpenSSLCipher() { @@ -81,23 +83,36 @@ class OpenSSLCipher : public BlockCipher { EVP_CIPHER_CTX_cleanup( &dec ); } - bool rekey(const EVP_CIPHER *cipher, const byte *key, int length) { - EVP_CIPHER_CTX_init( &enc ); + bool rekey(const EVP_CIPHER *cipher, const CipherKey &key) { + VLOG(1) << "setting key length " << key.size(); EVP_EncryptInit_ex( &enc, cipher, NULL, NULL, NULL); - EVP_CIPHER_CTX_set_key_length( &enc, length ); + EVP_CIPHER_CTX_set_key_length( &enc, key.size() ); EVP_CIPHER_CTX_set_padding( &enc, 0 ); - EVP_EncryptInit_ex( &enc, NULL, NULL, key, NULL); + EVP_EncryptInit_ex( &enc, NULL, NULL, key.data(), 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_key_length( &dec, key.size() ); EVP_CIPHER_CTX_set_padding( &dec, 0 ); - EVP_DecryptInit_ex( &dec, NULL, NULL, key, NULL); + EVP_DecryptInit_ex( &dec, NULL, NULL, key.data(), NULL); return true; } - static bool randomize(byte *out, int len) { - int result = RAND_bytes( out, len ); + static bool randomize(CipherKey *key) { + int result = RAND_bytes( key->data(), key->size() ); + 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; + } + + static bool pseudoRandomize(byte *out, int length) { + int result = RAND_pseudo_bytes( out, length ); if(result != 1) { char errStr[120]; // specs require string at least 120 bytes long.. @@ -112,12 +127,12 @@ class OpenSSLCipher : public BlockCipher { // Rekey with random key. bool rekey(const EVP_CIPHER *cipher, int keyLength) { - SecureMem key(keyLength); + CipherKey key(keyLength); - if (!randomize(key.data, key.size)) + if (!randomize(&key)) return false; - return rekey(cipher, key.data, key.size); + return rekey(cipher, key); } virtual int blockSize() const { @@ -171,17 +186,13 @@ class BfCbcBlockCipher : public OpenSSLCipher { BfCbcBlockCipher() {} virtual ~BfCbcBlockCipher() {} - virtual bool setKey(const byte *key, int length) { - if (BfKeyRange.allowed(length * 8)) - return rekey(EVP_bf_cbc(), key, length); + virtual bool setKey(const CipherKey &key) { + if (BfKeyRange.allowed(key.size() * 8)) + return rekey(EVP_bf_cbc(), key); 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; @@ -198,12 +209,8 @@ class BfCfbStreamCipher : public OpenSSLCipher { 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); + virtual bool setKey(const CipherKey &key) { + return BfKeyRange.allowed(key.size() * 8) && rekey(EVP_bf_cfb(), key); } static Properties GetProperties() { @@ -226,14 +233,9 @@ class AesCbcBlockCipher : public OpenSSLCipher { 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); + virtual bool setKey(const CipherKey& key) { + const EVP_CIPHER *cipher = getCipher(key.size()); + return (cipher != NULL) && rekey(cipher, key); } static const EVP_CIPHER *getCipher(int keyLength) { @@ -243,6 +245,7 @@ class AesCbcBlockCipher : public OpenSSLCipher { case 192: return EVP_aes_192_cbc(); case 256: return EVP_aes_256_cbc(); default: + LOG(INFO) << "Unsupported key length: " << keyLength; return NULL; } } @@ -257,6 +260,39 @@ class AesCbcBlockCipher : public OpenSSLCipher { } }; REGISTER_CLASS(AesCbcBlockCipher, BlockCipher); + +class AesCfbStreamCipher : public OpenSSLCipher { + public: + AesCfbStreamCipher() {} + virtual ~AesCfbStreamCipher() {} + + virtual bool setKey(const CipherKey& key) { + const EVP_CIPHER *cipher = getCipher(key.size()); + return (cipher != NULL) && rekey(cipher, key); + } + + static const EVP_CIPHER *getCipher(int keyLength) { + switch(keyLength * 8) + { + case 128: return EVP_aes_128_cfb(); + case 192: return EVP_aes_192_cfb(); + case 256: return EVP_aes_256_cfb(); + default: + LOG(INFO) << "Unsupported key length: " << keyLength; + return NULL; + } + } + + static Properties GetProperties() { + Properties props; + props.keySize = AesKeyRange; + props.cipher = "AES"; + props.mode = "CFB"; + props.library = "OpenSSL"; + return props; + } +}; +REGISTER_CLASS(AesCfbStreamCipher, StreamCipher); #endif #if defined(HAVE_EVP_AES_XTS) @@ -266,14 +302,9 @@ class AesXtsBlockCipher : public OpenSSLCipher { 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); + virtual bool setKey(const CipherKey &key) { + const EVP_CIPHER *cipher = getCipher(key.size()); + return (cipher != NULL) && rekey(cipher, key); } static const EVP_CIPHER *getCipher(int keyLength) { @@ -297,9 +328,11 @@ class AesXtsBlockCipher : public OpenSSLCipher { REGISTER_CLASS(AesXtsBlockCipher, BlockCipher); #endif -class Sha1HMac : public MessageAuthenticationCode { +class Sha1HMac : public MAC { public: - Sha1HMac() {} + Sha1HMac() { + HMAC_CTX_init(&ctx); + } virtual ~Sha1HMac() { HMAC_CTX_cleanup(&ctx); } @@ -308,19 +341,11 @@ class Sha1HMac : public MessageAuthenticationCode { 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); + virtual bool setKey(const CipherKey &key) { + HMAC_Init_ex(&ctx, key.data(), key.size(), 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); } @@ -331,6 +356,11 @@ class Sha1HMac : public MessageAuthenticationCode { } virtual bool write(byte *out) { +#ifdef HAVE_VALGRIND_MEMCHECK_H + if (VALGRIND_CHECK_MEM_IS_ADDRESSABLE(out, 20) != 0) { + return false; + } +#endif unsigned int outSize = 0; HMAC_Final(&ctx, (unsigned char *)out, &outSize); CHECK_EQ(outputSize(), outSize) << "Invalid HMAC output size"; @@ -348,7 +378,7 @@ class Sha1HMac : public MessageAuthenticationCode { private: HMAC_CTX ctx; }; -REGISTER_CLASS(Sha1HMac, MessageAuthenticationCode); +REGISTER_CLASS(Sha1HMac, MAC); class PbkdfPkcs5HmacSha1 : public PBKDF { @@ -359,16 +389,27 @@ class PbkdfPkcs5HmacSha1 : public PBKDF { virtual bool makeKey(const char *password, int passwordLength, const byte *salt, int saltLength, int numIterations, - byte *outKey, int keyLength) const { + CipherKey *outKey) { return PKCS5_PBKDF2_HMAC_SHA1( password, passwordLength, const_cast(salt), saltLength, - numIterations, keyLength, outKey) == 1; + numIterations, outKey->size(), outKey->data()) == 1; + } + + virtual CipherKey randomKey(int length) { + CipherKey key(length); + if (!OpenSSLCipher::randomize(&key)) + key.reset(); + return key; + } + + virtual bool pseudoRandom(byte *out, int length) { + return OpenSSLCipher::pseudoRandomize(out, length); } static Properties GetProperties() { Properties props; - props.mode = "PKCS5_PBKDF2_HMAC_SHA1"; + props.mode = NAME_PKCS5_PBKDF2_HMAC_SHA1; props.library = "OpenSSL"; return props; } diff --git a/cipher/testing.cpp b/cipher/testing.cpp index 0af7ce1..547b694 100644 --- a/cipher/testing.cpp +++ b/cipher/testing.cpp @@ -5,9 +5,9 @@ namespace encfs { -std::string stringToHex(const byte *data, int len) { - static const char lookup[] = "0123456789abcdef"; +static const char hexLut[] = "0123456789abcdef"; +std::string stringToHex(const byte *data, int len) { std::string out; out.reserve(2 * len); for (int i = 0; i < len; ++i) { @@ -15,12 +15,39 @@ std::string stringToHex(const byte *data, int len) { int first = (unsigned int)c >> 4; int second = (unsigned int)c & 15; - out.push_back(lookup[first]); - out.push_back(lookup[second]); + out.push_back(hexLut[first]); + out.push_back(hexLut[second]); } return out; } +void setDataFromHex(byte *out, int len, const char *hex) { + bool odd = false; + unsigned int last = 0; + while (len > 0 && *hex != '\0') { + byte nibble = *hex++; + if (nibble >= '0' && nibble <= '9') + nibble -= '0'; + else if (nibble >= 'A' && nibble <= 'F') + nibble -= 'A' - 10; + else if (nibble >= 'a' && nibble <= 'f') + nibble -= 'a' - 10; + else + nibble = 0; + + last |= (unsigned int)nibble; + if (odd) { + *out++ = (byte)last; + --len; + last = 0; + odd = false; + } else { + last <<= 4; + odd = true; + } + } +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/cipher/testing.h b/cipher/testing.h index a2aa9ad..484e36b 100644 --- a/cipher/testing.h +++ b/cipher/testing.h @@ -30,6 +30,13 @@ namespace encfs { std::string stringToHex(const byte *data, int len); +template +std::string stringToHex(const T& value) { + return stringToHex(value.data(), value.size()); +} + +void setDataFromHex(byte *out, int size, const char *hex); + } // namespace encfs #endif diff --git a/encfs/main.cpp b/encfs/main.cpp index 64ce2d2..f1cfbc4 100644 --- a/encfs/main.cpp +++ b/encfs/main.cpp @@ -475,14 +475,16 @@ void encfs_destroy( void *_ctx ) { ctx->running = false; +#ifdef CMAKE_USE_PTHREADS_INIT // wake up the thread if it is waiting.. VLOG(1) << "waking up monitoring thread"; - pthread_mutex_lock( &ctx->wakeupMutex ); + ctx->wakeupMutex.lock(); pthread_cond_signal( &ctx->wakeupCond ); - pthread_mutex_unlock( &ctx->wakeupMutex ); + ctx->wakeupMutex.unlock(); VLOG(1) << "joining with idle monitoring thread"; pthread_join( ctx->monitorThread , 0 ); VLOG(1) << "join done"; +#endif } } @@ -695,7 +697,7 @@ void * idleMonitor(void *_arg) const int timeoutCycles = 60 * arg->idleTimeout / ActivityCheckInterval; int idleCycles = 0; - pthread_mutex_lock( &ctx->wakeupMutex ); + ctx->wakeupMutex.lock(); while(ctx->running) { @@ -711,8 +713,10 @@ void * idleMonitor(void *_arg) int openCount = ctx->openFileCount(); if( openCount == 0 && unmountFS( ctx ) ) { +#ifdef CMAKE_USE_PTHREADS_INIT // wait for main thread to wake us up - pthread_cond_wait( &ctx->wakeupCond, &ctx->wakeupMutex ); + pthread_cond_wait( &ctx->wakeupCond, &ctx->wakeupMutex._mutex ); +#endif break; } @@ -727,11 +731,13 @@ void * idleMonitor(void *_arg) struct timespec wakeupTime; wakeupTime.tv_sec = currentTime.tv_sec + ActivityCheckInterval; wakeupTime.tv_nsec = currentTime.tv_usec * 1000; +#ifdef CMAKE_USE_PTHREADS_INIT pthread_cond_timedwait( &ctx->wakeupCond, - &ctx->wakeupMutex, &wakeupTime ); + &ctx->wakeupMutex._mutex, &wakeupTime ); +#endif } - pthread_mutex_unlock( &ctx->wakeupMutex ); + ctx->wakeupMutex.unlock(); VLOG(1) << "Idle monitoring thread exiting"; diff --git a/fs/BlockNameIO.cpp b/fs/BlockNameIO.cpp index 3b78f54..293e76f 100644 --- a/fs/BlockNameIO.cpp +++ b/fs/BlockNameIO.cpp @@ -23,7 +23,7 @@ #include "base/base64.h" #include "base/Error.h" #include "base/i18n.h" -#include "cipher/Cipher.h" +#include "cipher/CipherV1.h" #include #include @@ -31,17 +31,17 @@ namespace encfs { static shared_ptr NewBlockNameIO( const Interface &iface, - const shared_ptr &cipher, const CipherKey &key ) + const shared_ptr &cipher ) { return shared_ptr( - new BlockNameIO( iface, cipher, key, false)); + new BlockNameIO( iface, cipher, false)); } static shared_ptr NewBlockNameIO32( const Interface &iface, - const shared_ptr &cipher, const CipherKey &key ) + const shared_ptr &cipher ) { return shared_ptr( - new BlockNameIO( iface, cipher, key, true)); + new BlockNameIO( iface, cipher, true)); } static bool BlockIO_registered = NameIO::Register("Block", @@ -85,12 +85,11 @@ Interface BlockNameIO::CurrentInterface(bool caseSensitive) } BlockNameIO::BlockNameIO( const Interface &iface, - const shared_ptr &cipher, - const CipherKey &key, bool caseSensitiveEncoding ) + const shared_ptr &cipher, + bool caseSensitiveEncoding ) : _interface( iface.major() ) , _bs( cipher->cipherBlockSize() ) , _cipher( cipher ) - , _key( key ) , _caseSensitive( caseSensitiveEncoding ) { rAssert( _bs < 128 ); @@ -144,15 +143,16 @@ int BlockNameIO::encodeName( const char *plaintextName, int length, tmpIV = *iv; // include padding in MAC computation - unsigned int mac = _cipher->MAC_16( (unsigned char *)encodedName+2, - length+padding, _key, iv ); + unsigned int mac = _cipher->reduceMac16( + _cipher->MAC_64( (unsigned char *)encodedName+2, + length+padding, iv )); // add checksum bytes encodedName[0] = (mac >> 8) & 0xff; encodedName[1] = (mac ) & 0xff; _cipher->blockEncode( (unsigned char *)encodedName+2, length+padding, - (uint64_t)mac ^ tmpIV, _key); + (uint64_t)mac ^ tmpIV ); // convert to base 64 ascii int encodedStreamLen = length + 2 + padding; @@ -211,7 +211,7 @@ int BlockNameIO::decodeName( const char *encodedName, int length, tmpIV = *iv; _cipher->blockDecode( (unsigned char *)tmpBuf+2, decodedStreamLen, - (uint64_t)mac ^ tmpIV, _key); + (uint64_t)mac ^ tmpIV ); // find out true string length int padding = (unsigned char)tmpBuf[2+decodedStreamLen-1]; @@ -230,8 +230,9 @@ int BlockNameIO::decodeName( const char *encodedName, int length, plaintextName[finalSize] = '\0'; // check the mac - unsigned int mac2 = _cipher->MAC_16((const unsigned char *)tmpBuf+2, - decodedStreamLen, _key, iv); + unsigned int mac2 = _cipher->reduceMac16( + _cipher->MAC_64((const unsigned char *)tmpBuf+2, + decodedStreamLen, iv)); BUFFER_RESET( tmpBuf ); diff --git a/fs/BlockNameIO.h b/fs/BlockNameIO.h index cfce608..bd19791 100644 --- a/fs/BlockNameIO.h +++ b/fs/BlockNameIO.h @@ -21,14 +21,13 @@ #ifndef _BlockNameIO_incl_ #define _BlockNameIO_incl_ -#include "cipher/CipherKey.h" #include "fs/NameIO.h" #include namespace encfs { -class Cipher; +class CipherV1; /* Implement NameIO interface for filename encoding. Uses cipher in block @@ -37,34 +36,32 @@ class Cipher; */ class BlockNameIO : public NameIO { -public: - static Interface CurrentInterface(bool caseSensitive = false); + public: + static Interface CurrentInterface(bool caseSensitive = false); - BlockNameIO( const Interface &iface, - const shared_ptr &cipher, - const CipherKey &key, - bool caseSensitiveEncoding = false ); - virtual ~BlockNameIO(); + BlockNameIO(const Interface &iface, + const shared_ptr &cipher, + bool caseSensitiveEncoding = false ); + virtual ~BlockNameIO(); - virtual Interface interface() const; + virtual Interface interface() const; - virtual int maxEncodedNameLen( int plaintextNameLen ) const; - virtual int maxDecodedNameLen( int encodedNameLen ) const; + virtual int maxEncodedNameLen( int plaintextNameLen ) const; + virtual int maxDecodedNameLen( int encodedNameLen ) const; - // hack to help with static builds - static bool Enabled(); -protected: - virtual int encodeName( const char *plaintextName, int length, - uint64_t *iv, char *encodedName ) const; - virtual int decodeName( const char *encodedName, int length, - uint64_t *iv, char *plaintextName ) const; + // hack to help with static builds + static bool Enabled(); + protected: + virtual int encodeName(const char *plaintextName, int length, + uint64_t *iv, char *encodedName ) const; + virtual int decodeName(const char *encodedName, int length, + uint64_t *iv, char *plaintextName ) const; -private: - int _interface; - int _bs; - shared_ptr _cipher; - CipherKey _key; - bool _caseSensitive; + private: + int _interface; + int _bs; + shared_ptr _cipher; + bool _caseSensitive; }; } // namespace encfs diff --git a/fs/CipherFileIO.cpp b/fs/CipherFileIO.cpp index e2cde83..762a090 100644 --- a/fs/CipherFileIO.cpp +++ b/fs/CipherFileIO.cpp @@ -21,7 +21,7 @@ #include "fs/CipherFileIO.h" #include "base/Error.h" -#include "cipher/Cipher.h" +#include "cipher/CipherV1.h" #include "cipher/MemoryPool.h" #include "fs/fsconfig.pb.h" @@ -33,9 +33,6 @@ namespace encfs { /* - Version 3:0 adds support for block-only encryption by adding space for - a full block to the file header. - Version 2:0 adds support for a per-file initialization vector with a fixed 8 byte header. The headers are enabled globally within a filesystem at the filesystem configuration level. @@ -48,7 +45,6 @@ CipherFileIO::CipherFileIO( const shared_ptr &_base, : BlockFileIO( cfg->config->block_size(), cfg ) , base( _base ) , headerLen( 0 ) - , blockOnlyMode( cfg->config->block_mode_only() ) , perFileIV( cfg->config->unique_iv() ) , externalIV( 0 ) , fileIV( 0 ) @@ -56,18 +52,9 @@ CipherFileIO::CipherFileIO( const shared_ptr &_base, { fsConfig = cfg; cipher = cfg->cipher; - key = cfg->key; - if ( blockOnlyMode ) - { - headerLen += blockSize(); - if ( perFileIV ) - headerLen += cipher->cipherBlockSize(); - } else - { - if ( perFileIV ) - headerLen += sizeof(uint64_t); // 64bit IV per file - } + if ( perFileIV ) + headerLen += sizeof(uint64_t); // 64bit IV per file int blockBoundary = fsConfig->config->block_size() % fsConfig->cipher->cipherBlockSize(); @@ -200,19 +187,13 @@ void CipherFileIO::initHeader( ) IORequest req; req.offset = 0; - if (blockOnlyMode) - req.offset += blockSize(); - req.data = mb.data; - req.dataLen = blockOnlyMode ? cbs : sizeof(uint64_t); + req.dataLen = sizeof(uint64_t); base->read( req ); if (perFileIV) { - if (blockOnlyMode) - cipher->blockDecode( mb.data, cbs, externalIV, key ); - else - cipher->streamDecode( mb.data, sizeof(uint64_t), externalIV, key ); + cipher->streamDecode( mb.data, sizeof(uint64_t), externalIV ); fileIV = 0; for(unsigned int i=0; irandomize( mb.data, 8, false )) + if(!cipher->pseudoRandomize( mb.data, 8 )) throw Error("Unable to generate a random file IV"); fileIV = 0; @@ -237,20 +218,14 @@ void CipherFileIO::initHeader( ) << "Unexpected result: randomize returned 8 null bytes!"; } while(fileIV == 0); // don't accept 0 as an option.. - if (blockOnlyMode) - cipher->blockEncode( mb.data, cbs, externalIV, key ); - else - cipher->streamEncode( mb.data, sizeof(uint64_t), externalIV, key ); + cipher->streamEncode( mb.data, sizeof(uint64_t), externalIV ); if( base->isWritable() ) { IORequest req; req.offset = 0; - if (blockOnlyMode) - req.offset += blockSize(); - req.data = mb.data; - req.dataLen = blockOnlyMode ? cbs : sizeof(uint64_t); + req.dataLen = sizeof(uint64_t); base->write( req ); } else @@ -281,8 +256,7 @@ bool CipherFileIO::writeHeader( ) if (perFileIV) { - int cbs = cipher->cipherBlockSize(); - unsigned char *buf = mb.data + (blockOnlyMode ? blockSize() : 0); + unsigned char *buf = mb.data; for(int i=sizeof(buf)-1; i>=0; --i) { @@ -290,10 +264,7 @@ bool CipherFileIO::writeHeader( ) fileIV >>= 8; } - if (blockOnlyMode) - cipher->blockEncode( buf, cbs, externalIV, key ); - else - cipher->streamEncode( buf, sizeof(uint64_t), externalIV, key); + cipher->streamEncode( buf, sizeof(uint64_t), externalIV ); } IORequest req; @@ -322,25 +293,6 @@ ssize_t CipherFileIO::readOneBlock( const IORequest &req ) const tmpReq.offset += headerLen; int maxReadSize = req.dataLen; - if (blockOnlyMode) - { - off_t size = getSize(); - if (req.offset + req.dataLen > size) - { - // Last block written as full block at front of the file header. - mb.allocate(bs); - - tmpReq.offset = 0; - tmpReq.dataLen = bs; - tmpReq.data = mb.data; - - // TODO: what is the expected behavior if req.offset >= size? - maxReadSize = size - req.offset; - if (maxReadSize <= 0) - return 0; - } - } - readSize = base->read( tmpReq ); bool ok; @@ -349,7 +301,7 @@ ssize_t CipherFileIO::readOneBlock( const IORequest &req ) const if(headerLen != 0 && fileIV == 0) const_cast(this)->initHeader(); - if(blockOnlyMode || readSize == bs) + if(readSize == bs) { ok = blockRead( tmpReq.data, bs, blockNum ^ fileIV); } else @@ -378,7 +330,6 @@ ssize_t CipherFileIO::readOneBlock( const IORequest &req ) const bool CipherFileIO::writeOneBlock( const IORequest &req ) { int bs = blockSize(); - int cbs = cipher->cipherBlockSize(); off_t blockNum = req.offset / bs; if(headerLen != 0 && fileIV == 0) @@ -390,13 +341,6 @@ bool CipherFileIO::writeOneBlock( const IORequest &req ) if (req.dataLen == bs) { ok = blockWrite( req.data, bs, blockNum ^ fileIV ); - } else if (blockOnlyMode) - { - mb.allocate(bs); - cipher->randomize(mb.data + bs - cbs, cbs, false); - memcpy(mb.data, req.data, req.dataLen); - - ok = blockWrite( mb.data, bs, blockNum ^ fileIV ); } else { ok = streamWrite( req.data, (int)req.dataLen, @@ -437,18 +381,18 @@ bool CipherFileIO::blockWrite( unsigned char *buf, int size, uint64_t _iv64 ) const { if (!fsConfig->reverseEncryption) - return cipher->blockEncode( buf, size, _iv64, key ); + return cipher->blockEncode( buf, size, _iv64 ); else - return cipher->blockDecode( buf, size, _iv64, key ); + return cipher->blockDecode( buf, size, _iv64 ); } bool CipherFileIO::streamWrite( unsigned char *buf, int size, uint64_t _iv64 ) const { if (!fsConfig->reverseEncryption) - return cipher->streamEncode( buf, size, _iv64, key ); + return cipher->streamEncode( buf, size, _iv64 ); else - return cipher->streamDecode( buf, size, _iv64, key ); + return cipher->streamDecode( buf, size, _iv64 ); } @@ -456,26 +400,26 @@ bool CipherFileIO::blockRead( unsigned char *buf, int size, uint64_t _iv64 ) const { if (fsConfig->reverseEncryption) - return cipher->blockEncode( buf, size, _iv64, key ); + return cipher->blockEncode( buf, size, _iv64 ); else if(_allowHoles) { // special case - leave all 0's alone for(int i=0; iblockDecode( buf, size, _iv64, key ); + return cipher->blockDecode( buf, size, _iv64 ); return true; } else - return cipher->blockDecode( buf, size, _iv64, key ); + return cipher->blockDecode( buf, size, _iv64 ); } bool CipherFileIO::streamRead( unsigned char *buf, int size, uint64_t _iv64 ) const { if (fsConfig->reverseEncryption) - return cipher->streamEncode( buf, size, _iv64, key ); + return cipher->streamEncode( buf, size, _iv64 ); else - return cipher->streamDecode( buf, size, _iv64, key ); + return cipher->streamDecode( buf, size, _iv64 ); } int CipherFileIO::truncate( off_t size ) diff --git a/fs/CipherFileIO.h b/fs/CipherFileIO.h index 5ebb4e6..2193b39 100644 --- a/fs/CipherFileIO.h +++ b/fs/CipherFileIO.h @@ -29,7 +29,7 @@ namespace encfs { -class Cipher; +class CipherV1; /* Implement the FileIO interface encrypting data in blocks. @@ -93,8 +93,7 @@ private: uint64_t fileIV; int lastFlags; - shared_ptr cipher; - CipherKey key; + shared_ptr cipher; }; } // namespace encfs diff --git a/fs/Context.cpp b/fs/Context.cpp index af7fc9c..a4900a0 100644 --- a/fs/Context.cpp +++ b/fs/Context.cpp @@ -18,10 +18,10 @@ * along with this program. If not, see . */ -#include "base/Mutex.h" +#include "fs/Context.h" + #include "base/Error.h" #include "fs/FileNode.h" -#include "fs/Context.h" #include "fs/FileUtils.h" #include "fs/DirNode.h" @@ -29,18 +29,18 @@ namespace encfs { EncFS_Context::EncFS_Context() { +#ifdef CMAKE_USE_PTHREADS_INIT pthread_cond_init( &wakeupCond, 0 ); - pthread_mutex_init( &wakeupMutex, 0 ); - pthread_mutex_init( &contextMutex, 0 ); +#endif usageCount = 0; } EncFS_Context::~EncFS_Context() { - pthread_mutex_destroy( &contextMutex ); - pthread_mutex_destroy( &wakeupMutex ); +#ifdef CMAKE_USE_PTHREADS_INIT pthread_cond_destroy( &wakeupCond ); +#endif // release all entries from map openFiles.clear(); diff --git a/fs/Context.h b/fs/Context.h index a27da53..61888e3 100644 --- a/fs/Context.h +++ b/fs/Context.h @@ -21,8 +21,10 @@ #ifndef _Context_incl_ #define _Context_incl_ +#include "base/config.h" #include "base/shared_ptr.h" -#include "fs/encfs.h" +#include "base/Mutex.h" + #include #ifdef HAVE_TR1_UNORDERED_MAP @@ -42,64 +44,70 @@ class DirNode; class EncFS_Context { -public: - EncFS_Context(); - ~EncFS_Context(); + public: + EncFS_Context(); + ~EncFS_Context(); - shared_ptr getNode(void *ptr); - shared_ptr lookupNode(const char *path); + shared_ptr getNode(void *ptr); + shared_ptr lookupNode(const char *path); - int getAndResetUsageCounter(); - int openFileCount() const; + int getAndResetUsageCounter(); + int openFileCount() const; - void *putNode(const char *path, const shared_ptr &node); + void *putNode(const char *path, const shared_ptr &node); - void eraseNode(const char *path, void *placeholder); + void eraseNode(const char *path, void *placeholder); - void renameNode(const char *oldName, const char *newName); + void renameNode(const char *oldName, const char *newName); - void setRoot(const shared_ptr &root); - shared_ptr getRoot(int *err); - bool isMounted(); + void setRoot(const shared_ptr &root); + shared_ptr getRoot(int *err); + bool isMounted(); - shared_ptr args; - shared_ptr opts; - bool publicFilesystem; + shared_ptr args; + shared_ptr opts; + bool publicFilesystem; - // root path to cipher dir - std::string rootCipherDir; + // root path to cipher dir + std::string rootCipherDir; - // for idle monitor - bool running; - pthread_t monitorThread; - pthread_cond_t wakeupCond; - pthread_mutex_t wakeupMutex; + // for idle monitor + bool running; -private: - /* This placeholder is what is referenced in FUSE context (passed to - * callbacks). - * - * A FileNode may be opened many times, but only one FileNode instance per - * file is kept. Rather then doing reference counting in FileNode, we - * store a unique Placeholder for each open() until the corresponding - * release() is called. shared_ptr then does our reference counting for - * us. - */ - struct Placeholder - { - shared_ptr node; +#ifdef CMAKE_USE_PTHREADS_INIT + pthread_t monitorThread; + pthread_cond_t wakeupCond; + Mutex wakeupMutex; +#endif - Placeholder( const shared_ptr &ptr ) : node(ptr) {} - }; + private: + /* This placeholder is what is referenced in FUSE context (passed to + * callbacks). + * + * A FileNode may be opened many times, but only one FileNode instance per + * file is kept. Rather then doing reference counting in FileNode, we + * store a unique Placeholder for each open() until the corresponding + * release() is called. shared_ptr then does our reference counting for + * us. + */ + struct Placeholder + { + shared_ptr node; - // set of open files, indexed by path - typedef unordered_map > FileMap; - mutable pthread_mutex_t contextMutex; + Placeholder( const shared_ptr &ptr ) : node(ptr) {} + }; - FileMap openFiles; + // set of open files, indexed by path + typedef unordered_map > FileMap; - int usageCount; - shared_ptr root; +#ifdef CMAKE_USE_PTHREADS_INIT + mutable Mutex contextMutex; +#endif + + FileMap openFiles; + + int usageCount; + shared_ptr root; }; int remountFS( EncFS_Context *ctx ); diff --git a/fs/DirNode.cpp b/fs/DirNode.cpp index 13aa807..54ab0cd 100644 --- a/fs/DirNode.cpp +++ b/fs/DirNode.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #ifdef linux #include @@ -33,7 +32,6 @@ #include -#include "cipher/Cipher.h" #include "base/Error.h" #include "base/Mutex.h" #include "fs/Context.h" @@ -309,8 +307,6 @@ DirNode::DirNode(EncFS_Context *_ctx, const string &sourceDir, const FSConfigPtr &_config) { - pthread_mutex_init( &mutex, 0 ); - Lock _lock( mutex ); ctx = _ctx; diff --git a/fs/DirNode.h b/fs/DirNode.h index 8bee674..7360536 100644 --- a/fs/DirNode.h +++ b/fs/DirNode.h @@ -30,6 +30,7 @@ #include #include +#include "base/Mutex.h" #include "base/shared_ptr.h" #include "cipher/CipherKey.h" #include "fs/FileNode.h" @@ -161,7 +162,7 @@ private: shared_ptr findOrCreate( const char *plainName); - pthread_mutex_t mutex; + Mutex mutex; EncFS_Context *ctx; diff --git a/fs/FSConfig.h b/fs/FSConfig.h index a351870..296841a 100644 --- a/fs/FSConfig.h +++ b/fs/FSConfig.h @@ -43,7 +43,7 @@ enum ConfigType }; struct EncFS_Opts; -class Cipher; +class CipherV1; class NameIO; CipherKey getUserKey(const EncfsConfig &config, bool useStdin); @@ -54,8 +54,8 @@ CipherKey getUserKey(const EncfsConfig &config, CipherKey getNewUserKey(EncfsConfig &config, bool useStdin, const std::string &program, const std::string &rootDir); -shared_ptr getCipher(const EncfsConfig &cfg); -shared_ptr getCipher(const Interface &iface, int keySize); +shared_ptr getCipher(const EncfsConfig &cfg); +shared_ptr getCipher(const Interface &iface, int keySize); // helpers for serializing to/from a stream std::ostream &operator << (std::ostream &os, const EncfsConfig &cfg); @@ -67,7 +67,7 @@ struct FSConfig shared_ptr config; shared_ptr opts; - shared_ptr cipher; + shared_ptr cipher; CipherKey key; shared_ptr nameCoding; diff --git a/fs/FileNode.cpp b/fs/FileNode.cpp index ca38c61..978d7c4 100644 --- a/fs/FileNode.cpp +++ b/fs/FileNode.cpp @@ -36,7 +36,6 @@ #include "base/config.h" #include "base/Error.h" #include "base/Mutex.h" -#include "cipher/Cipher.h" #include "cipher/MemoryPool.h" #include "fs/CipherFileIO.h" @@ -66,8 +65,6 @@ namespace encfs { FileNode::FileNode(DirNode *parent_, const FSConfigPtr &cfg, const char *plaintextName_, const char *cipherName_) { - pthread_mutex_init( &mutex, 0 ); - Lock _lock( mutex ); this->_pname = plaintextName_; @@ -87,13 +84,10 @@ FileNode::FileNode(DirNode *parent_, const FSConfigPtr &cfg, FileNode::~FileNode() { // FileNode mutex should be locked before the destructor is called - //pthread_mutex_lock( &mutex ); _pname.assign( _pname.length(), '\0' ); _cname.assign( _cname.length(), '\0' ); io.reset(); - - pthread_mutex_destroy( &mutex ); } const char *FileNode::cipherName() const diff --git a/fs/FileNode.h b/fs/FileNode.h index 9bae285..a4e6633 100644 --- a/fs/FileNode.h +++ b/fs/FileNode.h @@ -21,6 +21,7 @@ #ifndef _FileNode_incl_ #define _FileNode_incl_ +#include "base/Mutex.h" #include "cipher/CipherKey.h" #include "fs/encfs.h" #include "fs/FileUtils.h" @@ -82,7 +83,7 @@ private: // easier to avoid any race conditions with operations such as // truncate() which may result in multiple calls down to the FileIO // level. - mutable pthread_mutex_t mutex; + mutable Mutex mutex; FSConfigPtr fsConfig; diff --git a/fs/FileUtils.cpp b/fs/FileUtils.cpp index d47b8c0..938da03 100644 --- a/fs/FileUtils.cpp +++ b/fs/FileUtils.cpp @@ -34,7 +34,7 @@ #include "base/i18n.h" #include "base/XmlReader.h" -#include "cipher/Cipher.h" +#include "cipher/CipherV1.h" #include "cipher/MemoryPool.h" #include "cipher/readpassphrase.h" @@ -528,60 +528,57 @@ bool readProtoConfig( const char *fileName, EncfsConfig &config, } static -Cipher::CipherAlgorithm findCipherAlgorithm(const char *name, +CipherV1::CipherAlgorithm findCipherAlgorithm(const char *name, int keySize ) { - Cipher::AlgorithmList algorithms = Cipher::GetAlgorithmList(); - Cipher::AlgorithmList::const_iterator it; - for(it = algorithms.begin(); it != algorithms.end(); ++it) + for (auto &it : CipherV1::GetAlgorithmList()) { - if( !strcmp( name, it->name.c_str() ) - && it->keyLength.allowed( keySize )) + if( !strcmp( name, it.name.c_str() ) + && it.keyLength.allowed( keySize )) { - return *it; + return it; } } - Cipher::CipherAlgorithm result; + CipherV1::CipherAlgorithm result; return result; } static -Cipher::CipherAlgorithm selectCipherAlgorithm() +CipherV1::CipherAlgorithm selectCipherAlgorithm() { for(;;) { // figure out what cipher they want to use.. // xgroup(setup) cout << _("The following cipher algorithms are available:") << "\n"; - Cipher::AlgorithmList algorithms = Cipher::GetAlgorithmList(); - Cipher::AlgorithmList::const_iterator it; - int optNum = 1; - for(it = algorithms.begin(); it != algorithms.end(); ++it, ++optNum) + int optNum = 0; + auto algorithms = CipherV1::GetAlgorithmList(); + for (auto &it : algorithms) { - cout << optNum << ". " << it->name - << " : " << gettext(it->description.c_str()) << "\n"; - if(it->keyLength.min() == it->keyLength.max()) + cout << ++optNum << ". " << it.name + << " : " << gettext(it.description.c_str()) << "\n"; + if(it.keyLength.min() == it.keyLength.max()) { // shown after algorithm name and description. // xgroup(setup) cout << autosprintf(_(" -- key length %i bits") - , it->keyLength.min()) << "\n"; + , it.keyLength.min()) << "\n"; } else { cout << autosprintf( // shown after algorithm name and description. // xgroup(setup) _(" -- Supports key lengths of %i to %i bits"), - it->keyLength.min(), it->keyLength.max()) << "\n"; + it.keyLength.min(), it.keyLength.max()) << "\n"; } - if(it->blockSize.min() == it->blockSize.max()) + if(it.blockSize.min() == it.blockSize.max()) { cout << autosprintf( // shown after algorithm name and description. // xgroup(setup) - _(" -- block size %i bytes"), it->blockSize.min()) + _(" -- block size %i bytes"), it.blockSize.min()) << "\n"; } else { @@ -589,7 +586,7 @@ Cipher::CipherAlgorithm selectCipherAlgorithm() // shown after algorithm name and description. // xgroup(setup) _(" -- Supports block sizes of %i to %i bytes"), - it->blockSize.min(), it->blockSize.max()) << "\n"; + it.blockSize.min(), it.blockSize.max()) << "\n"; } } @@ -606,11 +603,15 @@ Cipher::CipherAlgorithm selectCipherAlgorithm() continue; } - it = algorithms.begin(); - while(--cipherNum) // numbering starts at 1 - ++it; - - Cipher::CipherAlgorithm alg = *it; + CipherV1::CipherAlgorithm alg; + for (auto &it : algorithms) + { + if (!--cipherNum) + { + alg = it; + break; + } + } // xgroup(setup) cout << autosprintf(_("Selected algorithm \"%s\""), alg.name.c_str()) @@ -621,7 +622,7 @@ Cipher::CipherAlgorithm selectCipherAlgorithm() } static -Interface selectNameCoding(const Cipher::CipherAlgorithm &alg) +Interface selectNameCoding(const CipherV1::CipherAlgorithm &alg) { for(;;) { @@ -635,9 +636,6 @@ Interface selectNameCoding(const Cipher::CipherAlgorithm &alg) map algMap; for(it = algorithms.begin(); it != algorithms.end(); ++it) { - if (it->needsStreamMode && !alg.hasStreamMode) - continue; - cout << optNum << ". " << it->name << " : " << gettext(it->description.c_str()) << "\n"; algMap[optNum++] = it; @@ -667,7 +665,7 @@ Interface selectNameCoding(const Cipher::CipherAlgorithm &alg) } static -int selectKeySize( const Cipher::CipherAlgorithm &alg ) +int selectKeySize( const CipherV1::CipherAlgorithm &alg ) { if(alg.keyLength.min() == alg.keyLength.max()) { @@ -724,7 +722,7 @@ int selectKeySize( const Cipher::CipherAlgorithm &alg ) } static -int selectBlockSize( const Cipher::CipherAlgorithm &alg ) +int selectBlockSize( const CipherV1::CipherAlgorithm &alg ) { if(alg.blockSize.min() == alg.blockSize.max()) { @@ -918,7 +916,7 @@ RootPtr createConfig( EncFS_Context *ctx, int keySize = 0; int blockSize = 0; - Cipher::CipherAlgorithm alg; + CipherV1::CipherAlgorithm alg; Interface nameIOIface; int blockMACBytes = 0; int blockMACRandBytes = 0; @@ -954,7 +952,7 @@ RootPtr createConfig( EncFS_Context *ctx, // Enable filename initialization vector chaning keySize = 256; blockSize = DefaultBlockSize; - alg = findCipherAlgorithm("AES_XTS", keySize); + alg = findCipherAlgorithm("AES", keySize); nameIOIface = BlockNameIO::CurrentInterface(); blockMACBytes = 8; blockMACRandBytes = 0; // using uniqueIV, so this isn't necessary @@ -1028,7 +1026,7 @@ RootPtr createConfig( EncFS_Context *ctx, } } - shared_ptr cipher = Cipher::New( alg.name, keySize ); + shared_ptr cipher = CipherV1::New( alg.iface, keySize ); if(!cipher) { LOG(ERROR) << "Unable to instanciate cipher " << alg.name @@ -1043,10 +1041,6 @@ RootPtr createConfig( EncFS_Context *ctx, EncfsConfig config; config.mutable_cipher()->MergeFrom( cipher->interface() ); - // TODO: allow user config - if (!cipher->hasStreamMode()) - config.set_block_mode_only(true); - config.set_block_size( blockSize ); config.mutable_naming()->MergeFrom( nameIOIface ); config.set_creator( "EncFS " VERSION ); @@ -1106,25 +1100,26 @@ RootPtr createConfig( EncFS_Context *ctx, } userKey = getNewUserKey( config, useStdin, passwordProgram, rootDir ); - cipher->writeKey( volumeKey, encodedKey, userKey ); + cipher->setKey( userKey ); + cipher->writeKey( volumeKey, encodedKey ); userKey.reset(); key->set_ciphertext(encodedKey, encodedKeySize); delete[] encodedKey; - if(!volumeKey) + if(!volumeKey.valid()) { LOG(ERROR) << "Failure generating new volume key! " << "Please report this error."; return rootInfo; } + cipher->setKey( volumeKey ); if(!saveConfig( rootDir, config )) return rootInfo; // fill in config struct - shared_ptr nameCoder = NameIO::New( config.naming(), - cipher, volumeKey ); + shared_ptr nameCoder = NameIO::New( config.naming(), cipher ); if(!nameCoder) { LOG(WARNING) << "Name coding interface not supported"; @@ -1157,7 +1152,7 @@ RootPtr createConfig( EncFS_Context *ctx, void showFSInfo( const EncfsConfig &config ) { - shared_ptr cipher = Cipher::New( config.cipher(), -1 ); + shared_ptr cipher = CipherV1::New( config.cipher(), -1 ); { cout << autosprintf( // xgroup(diag) @@ -1192,8 +1187,7 @@ void showFSInfo( const EncfsConfig &config ) } else { // check if we support the filename encoding interface.. - shared_ptr nameCoder = NameIO::New( config.naming(), - cipher, CipherKey() ); + shared_ptr nameCoder = NameIO::New( config.naming(), cipher ); if(!nameCoder) { // xgroup(diag) @@ -1276,34 +1270,35 @@ void showFSInfo( const EncfsConfig &config ) cout << "\n"; } -shared_ptr getCipher(const EncfsConfig &config) +shared_ptr getCipher(const EncfsConfig &config) { return getCipher(config.cipher(), 8 * config.key().size()); } -shared_ptr getCipher(const Interface &iface, int keySize) +shared_ptr getCipher(const Interface &iface, int keySize) { - return Cipher::New( iface, keySize ); + return CipherV1::New( iface, keySize ); } CipherKey makeNewKey(EncfsConfig &config, const char *password, int passwdLen) { CipherKey userKey; - shared_ptr cipher = getCipher(config); + shared_ptr cipher = getCipher(config); + + EncryptedKey *key = config.mutable_key(); unsigned char salt[20]; - if(!cipher->randomize( salt, sizeof(salt), true)) + if(!cipher->pseudoRandomize( salt, sizeof(salt))) { cout << _("Error creating salt\n"); return userKey; } - EncryptedKey *key = config.mutable_key(); key->set_salt(salt, sizeof(salt)); int iterations = key->kdf_iterations(); - userKey = cipher->newKey( password, passwdLen, - iterations, key->kdf_duration(), - salt, sizeof(salt)); + userKey = cipher->newKey(password, passwdLen, + &iterations, key->kdf_duration(), + salt, sizeof(salt)); key->set_kdf_iterations(iterations); return userKey; @@ -1313,14 +1308,15 @@ CipherKey decryptKey(const EncfsConfig &config, const char *password, int passwd { const EncryptedKey &key = config.key(); CipherKey userKey; - shared_ptr cipher = getCipher(config.cipher(), 8 * key.size()); + shared_ptr cipher = getCipher(config.cipher(), 8 * key.size()); if(!key.salt().empty()) { int iterations = key.kdf_iterations(); - userKey = cipher->newKey( password, passwdLen, - iterations, key.kdf_duration(), - (const unsigned char *)key.salt().data(), key.salt().size()); + userKey = cipher->newKey(password, passwdLen, + &iterations, key.kdf_duration(), + (const byte *)key.salt().data(), + key.salt().size()); if (iterations != key.kdf_iterations()) { @@ -1582,7 +1578,7 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr &opts ) } // first, instanciate the cipher. - shared_ptr cipher = getCipher(config); + shared_ptr cipher = getCipher(config); if(!cipher) { Interface iface = config.cipher(); @@ -1605,24 +1601,27 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr &opts ) } else userKey = getUserKey( config, opts->passwordProgram, opts->rootDir ); - if(!userKey) + if(!userKey.valid()) return rootInfo; + cipher->setKey(userKey); + VLOG(1) << "cipher encoded key size = " << cipher->encodedKeySize(); // decode volume key.. CipherKey volumeKey = cipher->readKey( - (const unsigned char *)config.key().ciphertext().data(), userKey, opts->checkKey); + (const unsigned char *)config.key().ciphertext().data(), opts->checkKey); userKey.reset(); - if(!volumeKey) + if(!volumeKey.valid()) { // xgroup(diag) cout << _("Error decoding volume key, password incorrect\n"); return rootInfo; } - shared_ptr nameCoder = NameIO::New( config.naming(), - cipher, volumeKey ); + cipher->setKey(volumeKey); + + shared_ptr nameCoder = NameIO::New( config.naming(), cipher ); if(!nameCoder) { Interface iface = config.naming(); diff --git a/fs/FileUtils.h b/fs/FileUtils.h index bbcdfa4..4008c40 100644 --- a/fs/FileUtils.h +++ b/fs/FileUtils.h @@ -44,12 +44,12 @@ std::string parentDirectory( const std::string &path ); bool userAllowMkdir(const char *dirPath, mode_t mode ); bool userAllowMkdir(int promptno, const char *dirPath, mode_t mode ); -class Cipher; +class CipherV1; class DirNode; struct EncFS_Root { - shared_ptr cipher; + shared_ptr cipher; CipherKey volumeKey; shared_ptr root; diff --git a/fs/MACFileIO.cpp b/fs/MACFileIO.cpp index 95c4aa7..cef8e6d 100644 --- a/fs/MACFileIO.cpp +++ b/fs/MACFileIO.cpp @@ -61,7 +61,6 @@ MACFileIO::MACFileIO( const shared_ptr &_base, : BlockFileIO( dataBlockSize( cfg ), cfg ) , base( _base ) , cipher( cfg->cipher ) - , key( cfg->key ) , macBytes( cfg->config->block_mac_bytes() ) , randBytes( cfg->config->block_mac_rand_bytes() ) , warnOnly( cfg->opts->forceDecode ) @@ -202,7 +201,7 @@ ssize_t MACFileIO::readOneBlock( const IORequest &req ) const // At this point the data has been decoded. So, compute the MAC of // the block and check against the checksum stored in the header.. uint64_t mac = cipher->MAC_64( tmp.data + macBytes, - readSize - macBytes, key ); + readSize - macBytes ); for(int i=0; i>= 8) { @@ -255,7 +254,7 @@ bool MACFileIO::writeOneBlock( const IORequest &req ) memcpy( newReq.data + headerSize, req.data, req.dataLen ); if(randBytes > 0) { - if(!cipher->randomize( newReq.data+macBytes, randBytes, false )) + if(!cipher->pseudoRandomize( newReq.data+macBytes, randBytes)) return false; } @@ -263,7 +262,7 @@ bool MACFileIO::writeOneBlock( const IORequest &req ) { // compute the mac (which includes the random data) and fill it in uint64_t mac = cipher->MAC_64( newReq.data+macBytes, - req.dataLen + randBytes, key ); + req.dataLen + randBytes ); for(int i=0; i base; - shared_ptr cipher; - CipherKey key; + shared_ptr cipher; int macBytes; int randBytes; bool warnOnly; diff --git a/fs/NameIO.cpp b/fs/NameIO.cpp index 0ec1b2f..a7494aa 100644 --- a/fs/NameIO.cpp +++ b/fs/NameIO.cpp @@ -112,8 +112,8 @@ bool NameIO::Register( const char *name, const char *description, return true; } -shared_ptr NameIO::New( const string &name, - const shared_ptr &cipher, const CipherKey &key) +shared_ptr NameIO::New(const string &name, + const shared_ptr &cipher) { shared_ptr result; if(gNameIOMap) @@ -122,14 +122,14 @@ shared_ptr NameIO::New( const string &name, if(it != gNameIOMap->end()) { Constructor fn = it->second.constructor; - result = (*fn)( it->second.iface, cipher, key ); + result = (*fn)( it->second.iface, cipher ); } } return result; } -shared_ptr NameIO::New( const Interface &iface, - const shared_ptr &cipher, const CipherKey &key ) +shared_ptr NameIO::New(const Interface &iface, + const shared_ptr &cipher) { shared_ptr result; if(gNameIOMap) @@ -141,7 +141,7 @@ shared_ptr NameIO::New( const Interface &iface, if( implements(it->second.iface, iface )) { Constructor fn = it->second.constructor; - result = (*fn)( iface, cipher, key ); + result = (*fn)( iface, cipher ); break; } } diff --git a/fs/NameIO.h b/fs/NameIO.h index d5344dd..548df9e 100644 --- a/fs/NameIO.h +++ b/fs/NameIO.h @@ -27,117 +27,115 @@ #include #include "base/Interface.h" -#include "cipher/CipherKey.h" +#include "base/shared_ptr.h" namespace encfs { -class Cipher; +class CipherV1; class NameIO { -public: - typedef shared_ptr (*Constructor)( const Interface &iface, - const shared_ptr &cipher, const CipherKey &key); + public: + typedef shared_ptr (*Constructor)(const Interface &iface, + const shared_ptr &cipher); - struct Algorithm - { - std::string name; - std::string description; - Interface iface; - bool needsStreamMode; - }; + struct Algorithm + { + std::string name; + std::string description; + Interface iface; + bool needsStreamMode; + }; - typedef std::list AlgorithmList; - static AlgorithmList GetAlgorithmList( bool includeHidden = false ); + typedef std::list AlgorithmList; + static AlgorithmList GetAlgorithmList( bool includeHidden = false ); - static shared_ptr New( const Interface &iface, - const shared_ptr &cipher, const CipherKey &key); - static shared_ptr New( const std::string &name, - const shared_ptr &cipher, const CipherKey &key); + static shared_ptr New(const Interface &iface, + const shared_ptr &cipher); + static shared_ptr New(const std::string &name, + const shared_ptr &cipher); - static bool Register( const char *name, const char *description, - const Interface &iface, Constructor constructor, - bool needsStreamMode, - bool hidden = false); + static bool Register( const char *name, const char *description, + const Interface &iface, Constructor constructor, + bool needsStreamMode, + bool hidden = false); - NameIO(); - virtual ~NameIO(); + NameIO(); + virtual ~NameIO(); - virtual Interface interface() const =0; + virtual Interface interface() const =0; - void setChainedNameIV( bool enable ); - bool getChainedNameIV() const; - void setReverseEncryption( bool enable ); - bool getReverseEncryption() const; + void setChainedNameIV( bool enable ); + bool getChainedNameIV() const; + void setReverseEncryption( bool enable ); + bool getReverseEncryption() const; - std::string encodePath( const char *plaintextPath ) const; - std::string decodePath( const char *encodedPath ) const; + std::string encodePath( const char *plaintextPath ) const; + std::string decodePath( const char *encodedPath ) const; - std::string encodePath( const char *plaintextPath, uint64_t *iv ) const; - std::string decodePath( const char *encodedPath, uint64_t *iv ) const; + std::string encodePath( const char *plaintextPath, uint64_t *iv ) const; + std::string decodePath( const char *encodedPath, uint64_t *iv ) const; - virtual int maxEncodedNameLen( int plaintextNameLen ) const =0; - virtual int maxDecodedNameLen( int encodedNameLen ) const =0; + virtual int maxEncodedNameLen( int plaintextNameLen ) const =0; + virtual int maxDecodedNameLen( int encodedNameLen ) const =0; - std::string encodeName( const char *plaintextName, int length ) const; - std::string decodeName( const char *encodedName, int length ) const; + std::string encodeName( const char *plaintextName, int length ) const; + std::string decodeName( const char *encodedName, int length ) const; -protected: - virtual int encodeName( const char *plaintextName, int length, - char *encodedName ) const; - virtual int decodeName( const char *encodedName, int length, - char *plaintextName ) const; + protected: + virtual int encodeName( const char *plaintextName, int length, + char *encodedName ) const; + virtual int decodeName( const char *encodedName, int length, + char *plaintextName ) const; - virtual int encodeName( const char *plaintextName, int length, - uint64_t *iv, char *encodedName ) const =0; - virtual int decodeName( const char *encodedName, int length, - uint64_t *iv, char *plaintextName ) const =0; + virtual int encodeName( const char *plaintextName, int length, + uint64_t *iv, char *encodedName ) const =0; + virtual int decodeName( const char *encodedName, int length, + uint64_t *iv, char *plaintextName ) const =0; -private: + private: - std::string recodePath( const char *path, - int (NameIO::*codingLen)(int) const, - int (NameIO::*codingFunc)(const char *, int, - uint64_t *, char *) const, - uint64_t *iv ) const; + std::string recodePath( const char *path, + int (NameIO::*codingLen)(int) const, + int (NameIO::*codingFunc)(const char *, int, + uint64_t *, char *) const, + uint64_t *iv ) const; - std::string _encodePath( const char *plaintextPath, uint64_t *iv ) const; - std::string _decodePath( const char *encodedPath, uint64_t *iv ) const; - std::string _encodeName( const char *plaintextName, int length ) const; - std::string _decodeName( const char *encodedName, int length ) const; + std::string _encodePath( const char *plaintextPath, uint64_t *iv ) const; + std::string _decodePath( const char *encodedPath, uint64_t *iv ) const; + std::string _encodeName( const char *plaintextName, int length ) const; + std::string _decodeName( const char *encodedName, int length ) const; - bool chainedNameIV; - bool reverseEncryption; + bool chainedNameIV; + bool reverseEncryption; }; /* - Helper macros for creating temporary buffers with an optimization that - below a given size (OptimizedSize) is allocated on the stack, and when a - larger size is requested it is allocated on the heap. + Helper macros for creating temporary buffers with an optimization that + below a given size (OptimizedSize) is allocated on the stack, and when a + larger size is requested it is allocated on the heap. - BUFFER_RESET should be called for the same name as BUFFER_INIT -*/ + BUFFER_RESET should be called for the same name as BUFFER_INIT + */ #define BUFFER_INIT( Name, OptimizedSize, Size ) \ -char Name ## _Raw [ OptimizedSize ]; \ -char *Name = Name ## _Raw; \ -if( sizeof(Name ## _Raw) < Size ) \ -{ \ - Name = new char[ Size ];\ -} \ -memset( Name, 0, Size ) + char Name ## _Raw [ OptimizedSize ]; \ + char *Name = Name ## _Raw; \ + if( sizeof(Name ## _Raw) < Size ) { \ + Name = new char[ Size ];\ + } \ + memset( Name, 0, Size ) #define BUFFER_RESET( Name ) \ -do { \ - if( Name != Name ## _Raw ) \ - { \ - delete[] Name; \ - Name = Name ## _Raw; \ - } \ -} while(0) + do { \ + if( Name != Name ## _Raw ) { \ + delete[] Name; \ + Name = Name ## _Raw; \ + } \ + } while(0) } // namespace encfs diff --git a/fs/NullNameIO.cpp b/fs/NullNameIO.cpp index deeb2a6..0b78e64 100644 --- a/fs/NullNameIO.cpp +++ b/fs/NullNameIO.cpp @@ -19,7 +19,7 @@ */ #include "base/base64.h" -#include "cipher/Cipher.h" +#include "cipher/CipherV1.h" #include "fs/NullNameIO.h" #include @@ -27,7 +27,7 @@ namespace encfs { static shared_ptr NewNNIO( const Interface &, - const shared_ptr &, const CipherKey & ) + const shared_ptr & ) { return shared_ptr( new NullNameIO() ); } diff --git a/fs/StreamNameIO.cpp b/fs/StreamNameIO.cpp index df9b18a..a7a703d 100644 --- a/fs/StreamNameIO.cpp +++ b/fs/StreamNameIO.cpp @@ -21,7 +21,7 @@ #include "base/base64.h" #include "base/Error.h" #include "base/i18n.h" -#include "cipher/Cipher.h" +#include "cipher/CipherV1.h" #include "fs/StreamNameIO.h" #include @@ -33,9 +33,9 @@ using namespace std; namespace encfs { static shared_ptr NewStreamNameIO( const Interface &iface, - const shared_ptr &cipher, const CipherKey &key) + const shared_ptr &cipher ) { - return shared_ptr( new StreamNameIO( iface, cipher, key ) ); + return shared_ptr( new StreamNameIO( iface, cipher ) ); } static bool StreamIO_registered = NameIO::Register("Stream", @@ -72,11 +72,9 @@ Interface StreamNameIO::CurrentInterface() } StreamNameIO::StreamNameIO( const Interface &iface, - const shared_ptr &cipher, - const CipherKey &key ) + const shared_ptr &cipher ) : _interface( iface.major() ) , _cipher( cipher ) - , _key( key ) { } @@ -109,8 +107,8 @@ int StreamNameIO::encodeName( const char *plaintextName, int length, if( iv && _interface >= 2 ) tmpIV = *iv; - unsigned int mac = _cipher->MAC_16( (const unsigned char *)plaintextName, - length, _key, iv ); + unsigned int mac = _cipher->reduceMac16( + _cipher->MAC_64((const byte *)plaintextName, length, iv )); // add on checksum bytes unsigned char *encodeBegin; @@ -130,7 +128,7 @@ int StreamNameIO::encodeName( const char *plaintextName, int length, // stream encode the plaintext bytes memcpy( encodeBegin, plaintextName, length ); - _cipher->streamEncode( encodeBegin, length, (uint64_t)mac ^ tmpIV, _key); + _cipher->streamEncode( encodeBegin, length, (uint64_t)mac ^ tmpIV); // convert the entire thing to base 64 ascii.. int encodedStreamLen = length + 2; @@ -184,11 +182,11 @@ int StreamNameIO::decodeName( const char *encodedName, int length, } _cipher->streamDecode( (unsigned char *)plaintextName, decodedStreamLen, - (uint64_t)mac ^ tmpIV, _key); + (uint64_t)mac ^ tmpIV ); // compute MAC to check with stored value - unsigned int mac2 = _cipher->MAC_16((const unsigned char *)plaintextName, - decodedStreamLen, _key, iv); + unsigned int mac2 = _cipher->reduceMac16( + _cipher->MAC_64((const byte *)plaintextName, decodedStreamLen, iv)); BUFFER_RESET( tmpBuf ); if(mac2 != mac) diff --git a/fs/StreamNameIO.h b/fs/StreamNameIO.h index 5238cda..60b03de 100644 --- a/fs/StreamNameIO.h +++ b/fs/StreamNameIO.h @@ -21,12 +21,11 @@ #ifndef _StreamNameIO_incl_ #define _StreamNameIO_incl_ -#include "cipher/CipherKey.h" #include "fs/NameIO.h" namespace encfs { -class Cipher; +class CipherV1; class StreamNameIO : public NameIO { @@ -34,8 +33,7 @@ public: static Interface CurrentInterface(); StreamNameIO( const Interface &iface, - const shared_ptr &cipher, - const CipherKey &key ); + const shared_ptr &cipher); virtual ~StreamNameIO(); virtual Interface interface() const; @@ -52,8 +50,7 @@ protected: uint64_t *iv, char *plaintextName ) const; private: int _interface; - shared_ptr _cipher; - CipherKey _key; + shared_ptr _cipher; }; } // namespace encfs diff --git a/fs/test_BlockIO.cpp b/fs/test_BlockIO.cpp index b5f529c..6b43be7 100644 --- a/fs/test_BlockIO.cpp +++ b/fs/test_BlockIO.cpp @@ -39,7 +39,7 @@ TEST(BlockFileIOTest, BasicIO) { MemFileIO base(1024); ASSERT_EQ(1024, base.getSize()); - FSConfigPtr cfg = makeConfig( Cipher::New("Null"), 512); + FSConfigPtr cfg = makeConfig( CipherV1::New("Null"), 512); MemBlockFileIO block(512, cfg); block.truncate(1024); ASSERT_EQ(1024, block.getSize()); diff --git a/fs/test_IO.cpp b/fs/test_IO.cpp index 5efe7cf..2e82cb9 100644 --- a/fs/test_IO.cpp +++ b/fs/test_IO.cpp @@ -24,7 +24,6 @@ #include #include "fs/testing.h" -#include "cipher/Cipher.h" #include "cipher/MemoryPool.h" #include "fs/CipherFileIO.h" diff --git a/fs/testing.cpp b/fs/testing.cpp index c3288cf..329e08b 100644 --- a/fs/testing.cpp +++ b/fs/testing.cpp @@ -27,7 +27,7 @@ #include -#include "cipher/Cipher.h" +#include "cipher/CipherV1.h" #include "cipher/MemoryPool.h" #include "fs/FSConfig.h" @@ -40,10 +40,11 @@ using namespace std; namespace encfs { -FSConfigPtr makeConfig(const shared_ptr& cipher, int blockSize) { +FSConfigPtr makeConfig(const shared_ptr& cipher, int blockSize) { FSConfigPtr cfg = FSConfigPtr(new FSConfig); cfg->cipher = cipher; cfg->key = cipher->newRandomKey(); + cfg->cipher->setKey(cfg->key); cfg->config.reset(new EncfsConfig); cfg->config->set_block_size(blockSize); cfg->opts.reset(new EncFS_Opts); @@ -53,7 +54,7 @@ FSConfigPtr makeConfig(const shared_ptr& cipher, int blockSize) { void runWithCipher(const string& cipherName, int blockSize, void (*func)(FSConfigPtr& config)) { - shared_ptr cipher = Cipher::New(cipherName); + shared_ptr cipher = CipherV1::New(cipherName); ASSERT_TRUE(cipher.get() != NULL); FSConfigPtr cfg = makeConfig(cipher, blockSize); @@ -61,14 +62,14 @@ void runWithCipher(const string& cipherName, int blockSize, } void runWithAllCiphers(void (*func)(FSConfigPtr& config)) { - list algorithms = Cipher::GetAlgorithmList(); - list::const_iterator it; + list algorithms = CipherV1::GetAlgorithmList(); + list::const_iterator it; for (it = algorithms.begin(); it != algorithms.end(); ++it) { int blockSize = it->blockSize.closest(512); int keyLength = it->keyLength.closest(128); SCOPED_TRACE(testing::Message() << "Testng with cipher " << it->name << ", blocksize " << blockSize << ", keyLength " << keyLength); - shared_ptr cipher = Cipher::New(it->name, keyLength); + shared_ptr cipher = CipherV1::New(it->iface, keyLength); ASSERT_TRUE(cipher.get() != NULL); FSConfigPtr cfg = makeConfig(cipher, blockSize); @@ -95,7 +96,7 @@ void writeRandom(FSConfigPtr& cfg, FileIO* a, FileIO* b, int offset, int len) { a->truncate(offset + len); unsigned char *buf = new unsigned char[len]; - ASSERT_TRUE(cfg->cipher->randomize(buf, len, false)); + ASSERT_TRUE(cfg->cipher->pseudoRandomize(buf, len)); IORequest req; req.data = new unsigned char[len]; diff --git a/fs/testing.h b/fs/testing.h index ab291cf..04ec7c3 100644 --- a/fs/testing.h +++ b/fs/testing.h @@ -3,7 +3,7 @@ #include -#include "cipher/Cipher.h" +#include "cipher/CipherV1.h" #include "fs/FileUtils.h" #include "fs/FSConfig.h" @@ -11,7 +11,7 @@ namespace encfs { class FileIO; -FSConfigPtr makeConfig(const shared_ptr& cipher, int blockSize); +FSConfigPtr makeConfig(const shared_ptr& cipher, int blockSize); void runWithCipher(const std::string& cipherName, int blockSize, void (*func)(FSConfigPtr& config)); diff --git a/protos/fsconfig.proto b/protos/fsconfig.proto index 5d34f94..88df3e1 100644 --- a/protos/fsconfig.proto +++ b/protos/fsconfig.proto @@ -12,9 +12,6 @@ message EncfsConfig optional int32 revision = 2 [default=0]; required Interface cipher = 3; - // added for FileIO/Cipher 3.0 (encfs 1.8) - // Use only block encryption, no stream encryption. - optional bool block_mode_only = 31; required EncryptedKey key = 4; optional Interface naming = 5; diff --git a/util/encfsctl.cpp b/util/encfsctl.cpp index 6b46b1b..2caa72a 100644 --- a/util/encfsctl.cpp +++ b/util/encfsctl.cpp @@ -23,7 +23,7 @@ #include "base/Error.h" #include "base/i18n.h" -#include "cipher/Cipher.h" +#include "cipher/CipherV1.h" #include "cipher/BlockCipher.h" #include "cipher/MAC.h" #include "cipher/StreamCipher.h" @@ -172,33 +172,49 @@ static int showCiphers( int argc, char **argv ) { (void)argc; (void)argv; - list names = BlockCipher::GetRegistry().GetAll(); - for (const string& name : names) { + + cout << _("Block modes:\n"); + for (const string& name : BlockCipher::GetRegistry().GetAll()) { 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" << _("Provider: ") << props->library << "\n"; + cout << "\t" << _("Block cipher: ") << props->cipher << " / " + << props->mode << "\n"; cout << "\t" << _("Key Sizes: ") << props->keySize << "\n"; } - - names = StreamCipher::GetRegistry().GetAll(); - for (const string& name : names) { + cout << "\n"; + + cout << _("Stream modes:\n"); + for (const string& name : StreamCipher::GetRegistry().GetAll()) { 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" << _("Provider: ") << props->library << "\n"; + cout << "\t" << _("Stream cipher: ") << props->cipher << " / " + << props->mode << "\n"; cout << "\t" << _("Key Sizes: ") << props->keySize << "\n"; } + cout << "\n"; - names = MessageAuthenticationCode::GetRegistry().GetAll(); - for (const string& name : names) { - auto props = MessageAuthenticationCode::GetRegistry() - .GetProperties(name.c_str()); + cout << _("MAC modes:\n"); + for (const string& name : MAC::GetRegistry().GetAll()) { + auto props = MAC::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" << _("Provider: ") << props->library << "\n"; + cout << "\t" << _("Hash mode: ") << props->hashFunction << " / " + << props->mode << "\n"; cout << "\t" << _("Block size: ") << props->blockSize << "\n"; } + cout << "\n"; + + cout << _("PBKDF modes:\n"); + for (const string& name : PBKDF::GetRegistry().GetAll()) { + auto props = PBKDF::GetRegistry().GetProperties(name.c_str()); + cout << _("Implementation: ") << name << "\n"; + cout << "\t" << _("Provider: ") << props->library << "\n"; + cout << "\t" << _("Mode: ") << props->mode << "\n"; + } + cout << "\n"; + return EXIT_SUCCESS; } @@ -335,8 +351,7 @@ static int cmd_showKey( int argc, char **argv ) else { // encode with itself - string b64Key = rootInfo->cipher->encodeAsString( - rootInfo->volumeKey, rootInfo->volumeKey ); + string b64Key = rootInfo->cipher->encodeAsString( rootInfo->volumeKey ); cout << b64Key << "\n"; @@ -739,34 +754,37 @@ static int do_chpasswd( bool useStdin, bool annotate, int argc, char **argv ) return EXIT_FAILURE; } + // ask for existing password + cout << _("Enter current Encfs password\n"); + if (annotate) + cerr << "$PROMPT$ passwd" << endl; + CipherKey userKey = getUserKey( config, useStdin ); + if(!userKey.valid()) + return EXIT_FAILURE; + // instanciate proper cipher - shared_ptr cipher = getCipher(config); + shared_ptr cipher = getCipher(config); if(!cipher) { cout << autosprintf(_("Unable to find specified cipher \"%s\"\n"), config.cipher().name().c_str()); return EXIT_FAILURE; } - - // ask for existing password - cout << _("Enter current Encfs password\n"); - if (annotate) - cerr << "$PROMPT$ passwd" << endl; - CipherKey userKey = getUserKey( config, useStdin ); - if(!userKey) - return EXIT_FAILURE; + cipher->setKey(userKey); // decode volume key using user key -- at this point we detect an incorrect // password if the key checksum does not match (causing readKey to fail). CipherKey volumeKey = cipher->readKey( - (const unsigned char *)config.key().ciphertext().data(), userKey ); + (const unsigned char *)config.key().ciphertext().data(), true ); - if(!volumeKey) + if(!volumeKey.valid()) { cout << _("Invalid password\n"); return EXIT_FAILURE; } + cipher->setKey(volumeKey); + // Now, get New user key.. userKey.reset(); cout << _("Enter new Encfs password\n"); @@ -782,14 +800,16 @@ static int do_chpasswd( bool useStdin, bool annotate, int argc, char **argv ) // re-encode the volume key using the new user key and write it out.. int result = EXIT_FAILURE; - if(userKey) + if(userKey.valid()) { int encodedKeySize = cipher->encodedKeySize(); unsigned char *keyBuf = new unsigned char[ encodedKeySize ]; // encode volume key with new user key - cipher->writeKey( volumeKey, keyBuf, userKey ); + cipher->setKey(userKey); + cipher->writeKey( volumeKey, keyBuf ); userKey.reset(); + cipher->setKey(volumeKey); EncryptedKey *key = config.mutable_key(); key->set_ciphertext( keyBuf, encodedKeySize );