encapsulate old cipher support layer

git-svn-id: http://encfs.googlecode.com/svn/trunk@95 db9cf616-1c43-0410-9cb8-a902689de0d6
This commit is contained in:
Valient Gough 2013-03-05 06:36:32 +00:00
parent f00ef5c6d3
commit 7e95ff90c8
57 changed files with 1705 additions and 2320 deletions

View File

@ -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)

View File

@ -21,15 +21,38 @@
#ifndef _Mutex_incl_
#define _Mutex_incl_
#include "base/config.h"
#ifdef CMAKE_USE_PTHREADS_INIT
#include <pthread.h>
#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

View File

@ -3,6 +3,7 @@
#include <list>
#include <map>
#include <string>
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<std::string, Data> data;
@ -87,7 +100,7 @@ public:
}
#define REGISTER_CLASS(DERIVED, BASE) \
static Registrar<DERIVED, BASE> registrar_##DERIVED(#DERIVED)
static Registrar<DERIVED, BASE> registrar_ ## DERIVED ## _ ## BASE (#DERIVED)
} // namespace encfs

View File

@ -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

View File

@ -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>& BlockCipher::GetRegistry()
if (first)
{
OpenSSL::registerCiphers();
NullCiphers::registerCiphers();
first = false;
}
return registry;

View File

@ -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<BlockCipher>& 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;
};

View File

@ -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<BlockCipher> registry = BlockCipher::GetRegistry();
list<string> ciphers = registry.GetAll();
for (const string &name : ciphers) {
const BlockCipher::Properties *properties = registry.GetProperties(name.c_str());
SCOPED_TRACE(testing::Message() << "Cipher " << name);
Registry<BlockCipher> 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(
PBKDF::GetRegistry().CreateForMatch(NAME_PKCS5_PBKDF2_HMAC_SHA1));
shared_ptr<BlockCipher> cipher (registry.Create(name.c_str()));
list<string> ciphers = registry.GetAll();
for (const string &name : ciphers) {
const BlockCipher::Properties *properties =
registry.GetProperties(name.c_str());
SCOPED_TRACE(testing::Message() << "Cipher " << name);
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<BlockCipher> 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

View File

@ -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
)

View File

@ -1,230 +0,0 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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 <http://www.gnu.org/licenses/>.
*/
#include "base/config.h"
#include "cipher/Cipher.h"
#include <map>
#include <list>
#include <string>
#include <iostream>
#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::CipherAlgorithm>
Cipher::GetAlgorithmList( bool includeHidden )
{
AddSymbolReferences();
list<CipherAlgorithm> 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> Cipher::New(const string &name, int keyLen)
{
shared_ptr<Cipher> 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> Cipher::New( const Interface &iface, int keyLen )
{
shared_ptr<Cipher> 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

View File

@ -1,165 +0,0 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _Cipher_incl_
#define _Cipher_incl_
#include "cipher/CipherKey.h"
#include "base/Interface.h"
#include "base/Range.h"
#include "base/types.h"
#include <string>
#include <list>
#include <inttypes.h>
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<Cipher> (*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<CipherAlgorithm> AlgorithmList;
static AlgorithmList GetAlgorithmList( bool includeHidden = false );
static shared_ptr<Cipher> New( const Interface &iface,
int keyLen = -1);
static shared_ptr<Cipher> 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

View File

@ -1,18 +1,19 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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 <http://www.gnu.org/licenses/>.
@ -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

View File

@ -21,18 +21,40 @@
#ifndef _CipherKey_incl_
#define _CipherKey_incl_
#include <cstring>
#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<SecureMem> _mem;
};
typedef shared_ptr<AbstractCipherKey> CipherKey;
bool operator == (const CipherKey &a, const CipherKey &b);
} // namespace encfs

668
cipher/CipherV1.cpp Normal file
View File

@ -0,0 +1,668 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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 <http://www.gnu.org/licenses/>.
*/
#include "cipher/CipherV1.h"
#include <cstring>
#include <ctime>
#include <sys/mman.h>
#include <sys/time.h>
#include <glog/logging.h>
#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<MAC> registry = MAC::GetRegistry();
shared_ptr<MAC> 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<PBKDF> registry = PBKDF::GetRegistry();
shared_ptr<PBKDF> 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::CipherAlgorithm> CipherV1::GetAlgorithmList()
{
list<CipherV1::CipherAlgorithm> result;
Registry<BlockCipher> 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> CipherV1::New(const std::string& name, int keyLen) {
for (auto &it : GetAlgorithmList()) {
if (it.name == name)
return New(it.iface, keyLen);
}
return shared_ptr<CipherV1>();
}
shared_ptr<CipherV1> CipherV1::New(const Interface &iface, int keyLen) {
return shared_ptr<CipherV1>(new CipherV1(iface, iface, keyLen));
}
CipherV1::CipherV1(const Interface &iface, const Interface &realIface,
int keyLength)
{
this->iface = iface;
this->realIface = realIface;
Registry<BlockCipher> blockCipherRegistry = BlockCipher::GetRegistry();
Registry<StreamCipher> 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; ++i)
checksum = (checksum << 8) | (unsigned int)data[i];
memcpy( key.data(), data+KEY_CHECKSUM_BYTES, key.size() );
streamDecode(key.data(), key.size(), checksum);
// check for success
unsigned int checksum2 = reduceMac32(
MAC_64( key.data(), key.size(), NULL ));
if(checksum2 != checksum && checkKey)
{
VLOG(1) << "checksum mismatch: expected " << checksum
<< ", got " << checksum2
<< "on decode of " << _keySize + _ivLength << " bytes";
return CipherKey();
}
return key;
}
void CipherV1::writeKey(const CipherKey &ckey, byte *out)
{
rAssert( _keySet );
rAssert(ckey.size() > KEY_CHECKSUM_BYTES);
SecureMem tmpBuf(ckey.size());
memcpy(tmpBuf.data, ckey.data(), tmpBuf.size);
unsigned int checksum = reduceMac32(
MAC_64( tmpBuf.data, tmpBuf.size, NULL ));
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<byte> 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<byte> 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<toFlip; ++i)
revBuf[i] = buf[toFlip - (i+1)];
memcpy( buf, revBuf, toFlip );
bytesLeft -= toFlip;
buf += toFlip;
}
memset(revBuf, 0, sizeof(revBuf));
}
static void shuffleBytes(byte *buf, int size)
{
for(int i=0; i<size-1; ++i)
buf[i+1] ^= buf[i];
}
static void unshuffleBytes(byte *buf, int size)
{
for(int i=size-1; i; --i)
buf[i] ^= buf[i-1];
}
/* Partial blocks are encoded with a stream cipher. We make multiple passes on
the data to ensure that the ends of the data depend on each other.
*/
bool CipherV1::streamEncode(byte *buf, int size, uint64_t iv64) const
{
rAssert( _keySet );
rAssert( size > 0 );
vector<byte> 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<byte> 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<byte> 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<byte> ivec(_ivLength);
setIVec( ivec.data(), iv64 );
return _blockCipher->decrypt(ivec.data(), buf, buf, size);
}
} // namespace encfs

View File

@ -2,7 +2,7 @@
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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 <http://www.gnu.org/licenses/>.
*/
#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> _blockCipher;
shared_ptr<StreamCipher> _streamCipher;
shared_ptr<PBKDF> _pbkdf;
// HMac is stateful, so access is controlled via mutex.
mutable Mutex _hmacMutex;
mutable shared_ptr<MAC> _hmac;
unsigned int _keySize; // in bytes
unsigned int _ivLength;
shared_ptr<SecureMem> _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<CipherAlgorithm> GetAlgorithmList();
static shared_ptr<CipherV1> New(const std::string &name, int keyLen = -1);
static shared_ptr<CipherV1> 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<SSLKey> &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<SSLKey> &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

View File

@ -2,13 +2,13 @@
namespace encfs {
DEFINE_REGISTERABLE_TYPE(MessageAuthenticationCode)
DEFINE_REGISTERABLE_TYPE(MAC)
MessageAuthenticationCode::MessageAuthenticationCode()
MAC::MAC()
{
}
MessageAuthenticationCode::~MessageAuthenticationCode()
MAC::~MAC()
{
}

View File

@ -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;

View File

@ -32,37 +32,36 @@ using namespace encfs;
namespace {
TEST(HMacSha1Test, MessageAuthenticationCode) {
Registry<MessageAuthenticationCode> registry =
MessageAuthenticationCode::GetRegistry();
shared_ptr<MessageAuthenticationCode> hmac(
registry.CreateForMatch( "SHA-1/HMAC" ));
TEST(HMacSha1Test, MAC) {
Registry<MAC> registry = MAC::GetRegistry();
shared_ptr<MAC> 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);

View File

@ -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();
};

View File

@ -1,183 +0,0 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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 <http://www.gnu.org/licenses/>.
*/
#include "cipher/NullCipher.h"
#include "base/Range.h"
#include "base/Interface.h"
#include "base/shared_ptr.h"
#include <cstring>
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<Cipher> NewNullCipher(const Interface &iface, int keyLen)
{
(void)keyLen;
return shared_ptr<Cipher>( 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<AbstractCipherKey> 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<NullKey> A = dynamic_pointer_cast<NullKey>(A_);
shared_ptr<NullKey> B = dynamic_pointer_cast<NullKey>(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

View File

@ -1,89 +0,0 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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 <http://www.gnu.org/licenses/>.
*/
#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

73
cipher/NullCiphers.cpp Normal file
View File

@ -0,0 +1,73 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2013 Valient Gough
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cipher/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

37
cipher/NullCiphers.h Normal file
View File

@ -0,0 +1,37 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2013 Valient Gough
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _NULLCIPHERS_incl_
#define _NULLCIPHERS_incl_
#include "base/Registry.h"
namespace encfs {
class NullCiphers {
public:
static void registerCiphers();
};
} // namespace encfs
#endif

View File

@ -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

View File

@ -34,42 +34,37 @@ namespace {
TEST(PKCS5_PBKDF2_HMAC_SHA1, PBKDF) {
Registry<PBKDF> registry = PBKDF::GetRegistry();
shared_ptr<PBKDF> impl( registry.CreateForMatch( "PKCS5_PBKDF2_HMAC_SHA1" ));
shared_ptr<PBKDF> 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));
}
}

View File

@ -1,978 +0,0 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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 <http://www.gnu.org/licenses/>.
*/
#include "base/config.h"
#include <openssl/blowfish.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/hmac.h>
#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 <cstring>
#include <ctime>
#include <sys/mman.h>
#include <sys/time.h>
#include <glog/logging.h>
#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<byte*>(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<Cipher> 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<Cipher>(
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<Cipher> 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<Cipher>( 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<Cipher> 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<Cipher>( 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<SSLKey> &key )
{
return (byte *)key->buf.data;
}
inline byte* IVData( const shared_ptr<SSLKey> &key )
{
return (byte *)key->buf.data + key->keySize;
}
void initKey(const shared_ptr<SSLKey> &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<SSLKey> 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<byte*>(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<SSLKey> 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<SSLKey> 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<SSLKey> mk = dynamic_pointer_cast<SSLKey>(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<SSLKey> mk = dynamic_pointer_cast<SSLKey>(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_CHECKSUM_BYTES; ++i)
checksum = (checksum << 8) | (unsigned int)data[i];
if (_streamCipher != NULL)
{
memcpy( tmpBuf, data+KEY_CHECKSUM_BYTES, _keySize + _ivLength );
streamDecode(tmpBuf, _keySize + _ivLength, checksum, masterKey);
} else
{
memcpy( tmpBuf, data+KEY_CHECKSUM_BYTES, 2 * _keySize );
blockDecode(tmpBuf, 2 * _keySize, checksum, masterKey);
}
// check for success
unsigned int checksum2 = MAC_32( tmpBuf, _keySize + _ivLength, masterKey );
if(checksum2 != checksum && checkKey)
{
VLOG(1) << "checksum mismatch: expected " << checksum
<< ", got " << checksum2
<< "on decode of " << _keySize + _ivLength << " bytes";
OPENSSL_cleanse(tmpBuf, sizeof(tmpBuf));
return CipherKey();
}
shared_ptr<SSLKey> 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<SSLKey> key = dynamic_pointer_cast<SSLKey>(ckey);
rAssert(key->keySize == _keySize);
rAssert(key->ivLength == _ivLength);
shared_ptr<SSLKey> mk = dynamic_pointer_cast<SSLKey>(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<SSLKey> key1 = dynamic_pointer_cast<SSLKey>(A);
shared_ptr<SSLKey> key2 = dynamic_pointer_cast<SSLKey>(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<SSLKey> &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<SSLKey> &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<toFlip; ++i)
revBuf[i] = buf[toFlip - (i+1)];
memcpy( buf, revBuf, toFlip );
bytesLeft -= toFlip;
buf += toFlip;
}
memset(revBuf, 0, sizeof(revBuf));
}
static void shuffleBytes(byte *buf, int size)
{
for(int i=0; i<size-1; ++i)
buf[i+1] ^= buf[i];
}
static void unshuffleBytes(byte *buf, int size)
{
for(int i=size-1; i; --i)
buf[i] ^= buf[i-1];
}
/* Partial blocks are encoded with a stream cipher. We make multiple passes on
the data to ensure that the ends of the data depend on each other.
*/
bool SSL_Cipher::streamEncode(byte *buf, int size,
uint64_t iv64, const CipherKey &ckey) const
{
rAssert( size > 0 );
shared_ptr<SSLKey> key = dynamic_pointer_cast<SSLKey>(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<SSLKey> key = dynamic_pointer_cast<SSLKey>(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<SSLKey> key = dynamic_pointer_cast<SSLKey>(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<SSLKey> key = dynamic_pointer_cast<SSLKey>(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

View File

@ -2,11 +2,7 @@
namespace encfs {
Registry<StreamCipher>& StreamCipher::GetRegistry()
{
static Registry<StreamCipher> registry;
return registry;
}
DEFINE_REGISTERABLE_TYPE(StreamCipher);
StreamCipher::StreamCipher()
{

View File

@ -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<StreamCipher>& 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;

View File

@ -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<byte *>(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;
}

View File

@ -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();

View File

@ -30,6 +30,13 @@ namespace encfs {
std::string stringToHex(const byte *data, int len);
template <typename T>
std::string stringToHex(const T& value) {
return stringToHex(value.data(), value.size());
}
void setDataFromHex(byte *out, int size, const char *hex);
} // namespace encfs
#endif

View File

@ -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";

View File

@ -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 <cstring>
#include <glog/logging.h>
@ -31,17 +31,17 @@
namespace encfs {
static shared_ptr<NameIO> NewBlockNameIO( const Interface &iface,
const shared_ptr<Cipher> &cipher, const CipherKey &key )
const shared_ptr<CipherV1> &cipher )
{
return shared_ptr<NameIO>(
new BlockNameIO( iface, cipher, key, false));
new BlockNameIO( iface, cipher, false));
}
static shared_ptr<NameIO> NewBlockNameIO32( const Interface &iface,
const shared_ptr<Cipher> &cipher, const CipherKey &key )
const shared_ptr<CipherV1> &cipher )
{
return shared_ptr<NameIO>(
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> &cipher,
const CipherKey &key, bool caseSensitiveEncoding )
const shared_ptr<CipherV1> &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 );

View File

@ -21,14 +21,13 @@
#ifndef _BlockNameIO_incl_
#define _BlockNameIO_incl_
#include "cipher/CipherKey.h"
#include "fs/NameIO.h"
#include <memory>
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> &cipher,
const CipherKey &key,
bool caseSensitiveEncoding = false );
virtual ~BlockNameIO();
BlockNameIO(const Interface &iface,
const shared_ptr<CipherV1> &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> _cipher;
CipherKey _key;
bool _caseSensitive;
private:
int _interface;
int _bs;
shared_ptr<CipherV1> _cipher;
bool _caseSensitive;
};
} // namespace encfs

View File

@ -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<FileIO> &_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<FileIO> &_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; i<sizeof(uint64_t); ++i)
@ -226,7 +207,7 @@ void CipherFileIO::initHeader( )
do
{
if(!cipher->randomize( 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<CipherFileIO*>(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; i<size; ++i)
if(buf[i] != 0)
return cipher->blockDecode( 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 )

View File

@ -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> cipher;
CipherKey key;
shared_ptr<CipherV1> cipher;
};
} // namespace encfs

View File

@ -18,10 +18,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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();

View File

@ -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 <set>
#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<FileNode> getNode(void *ptr);
shared_ptr<FileNode> lookupNode(const char *path);
shared_ptr<FileNode> getNode(void *ptr);
shared_ptr<FileNode> lookupNode(const char *path);
int getAndResetUsageCounter();
int openFileCount() const;
int getAndResetUsageCounter();
int openFileCount() const;
void *putNode(const char *path, const shared_ptr<FileNode> &node);
void *putNode(const char *path, const shared_ptr<FileNode> &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<DirNode> &root);
shared_ptr<DirNode> getRoot(int *err);
bool isMounted();
void setRoot(const shared_ptr<DirNode> &root);
shared_ptr<DirNode> getRoot(int *err);
bool isMounted();
shared_ptr<EncFS_Args> args;
shared_ptr<EncFS_Opts> opts;
bool publicFilesystem;
shared_ptr<EncFS_Args> args;
shared_ptr<EncFS_Opts> 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<FileNode> node;
#ifdef CMAKE_USE_PTHREADS_INIT
pthread_t monitorThread;
pthread_cond_t wakeupCond;
Mutex wakeupMutex;
#endif
Placeholder( const shared_ptr<FileNode> &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<FileNode> node;
// set of open files, indexed by path
typedef unordered_map<std::string, std::set<Placeholder*> > FileMap;
mutable pthread_mutex_t contextMutex;
Placeholder( const shared_ptr<FileNode> &ptr ) : node(ptr) {}
};
FileMap openFiles;
// set of open files, indexed by path
typedef unordered_map<std::string, std::set<Placeholder*> > FileMap;
int usageCount;
shared_ptr<DirNode> root;
#ifdef CMAKE_USE_PTHREADS_INIT
mutable Mutex contextMutex;
#endif
FileMap openFiles;
int usageCount;
shared_ptr<DirNode> root;
};
int remountFS( EncFS_Context *ctx );

View File

@ -25,7 +25,6 @@
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
#ifdef linux
#include <sys/fsuid.h>
@ -33,7 +32,6 @@
#include <cstring>
#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;

View File

@ -30,6 +30,7 @@
#include <vector>
#include <string>
#include "base/Mutex.h"
#include "base/shared_ptr.h"
#include "cipher/CipherKey.h"
#include "fs/FileNode.h"
@ -161,7 +162,7 @@ private:
shared_ptr<FileNode> findOrCreate( const char *plainName);
pthread_mutex_t mutex;
Mutex mutex;
EncFS_Context *ctx;

View File

@ -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<Cipher> getCipher(const EncfsConfig &cfg);
shared_ptr<Cipher> getCipher(const Interface &iface, int keySize);
shared_ptr<CipherV1> getCipher(const EncfsConfig &cfg);
shared_ptr<CipherV1> 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<EncfsConfig> config;
shared_ptr<EncFS_Opts> opts;
shared_ptr<Cipher> cipher;
shared_ptr<CipherV1> cipher;
CipherKey key;
shared_ptr<NameIO> nameCoding;

View File

@ -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

View File

@ -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;

View File

@ -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<int, NameIO::AlgorithmList::const_iterator> 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 = Cipher::New( alg.name, keySize );
shared_ptr<CipherV1> 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<NameIO> nameCoder = NameIO::New( config.naming(),
cipher, volumeKey );
shared_ptr<NameIO> 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 = Cipher::New( config.cipher(), -1 );
shared_ptr<CipherV1> 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<NameIO> nameCoder = NameIO::New( config.naming(),
cipher, CipherKey() );
shared_ptr<NameIO> nameCoder = NameIO::New( config.naming(), cipher );
if(!nameCoder)
{
// xgroup(diag)
@ -1276,34 +1270,35 @@ void showFSInfo( const EncfsConfig &config )
cout << "\n";
}
shared_ptr<Cipher> getCipher(const EncfsConfig &config)
shared_ptr<CipherV1> getCipher(const EncfsConfig &config)
{
return getCipher(config.cipher(), 8 * config.key().size());
}
shared_ptr<Cipher> getCipher(const Interface &iface, int keySize)
shared_ptr<CipherV1> 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> cipher = getCipher(config);
shared_ptr<CipherV1> 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> cipher = getCipher(config.cipher(), 8 * key.size());
shared_ptr<CipherV1> 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<EncFS_Opts> &opts )
}
// first, instanciate the cipher.
shared_ptr<Cipher> cipher = getCipher(config);
shared_ptr<CipherV1> cipher = getCipher(config);
if(!cipher)
{
Interface iface = config.cipher();
@ -1605,24 +1601,27 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &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<NameIO> nameCoder = NameIO::New( config.naming(),
cipher, volumeKey );
cipher->setKey(volumeKey);
shared_ptr<NameIO> nameCoder = NameIO::New( config.naming(), cipher );
if(!nameCoder)
{
Interface iface = config.naming();

View File

@ -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> cipher;
shared_ptr<CipherV1> cipher;
CipherKey volumeKey;
shared_ptr<DirNode> root;

View File

@ -61,7 +61,6 @@ MACFileIO::MACFileIO( const shared_ptr<FileIO> &_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<macBytes; ++i, mac >>= 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<macBytes; ++i)
{

View File

@ -21,7 +21,7 @@
#ifndef _MACFileIO_incl_
#define _MACFileIO_incl_
#include "cipher/Cipher.h"
#include "cipher/CipherV1.h"
#include "fs/BlockFileIO.h"
namespace encfs {
@ -58,8 +58,7 @@ private:
virtual bool writeOneBlock( const IORequest &req );
shared_ptr<FileIO> base;
shared_ptr<Cipher> cipher;
CipherKey key;
shared_ptr<CipherV1> cipher;
int macBytes;
int randBytes;
bool warnOnly;

View File

@ -112,8 +112,8 @@ bool NameIO::Register( const char *name, const char *description,
return true;
}
shared_ptr<NameIO> NameIO::New( const string &name,
const shared_ptr<Cipher> &cipher, const CipherKey &key)
shared_ptr<NameIO> NameIO::New(const string &name,
const shared_ptr<CipherV1> &cipher)
{
shared_ptr<NameIO> result;
if(gNameIOMap)
@ -122,14 +122,14 @@ shared_ptr<NameIO> 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> NameIO::New( const Interface &iface,
const shared_ptr<Cipher> &cipher, const CipherKey &key )
shared_ptr<NameIO> NameIO::New(const Interface &iface,
const shared_ptr<CipherV1> &cipher)
{
shared_ptr<NameIO> result;
if(gNameIOMap)
@ -141,7 +141,7 @@ shared_ptr<NameIO> 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;
}
}

View File

@ -27,117 +27,115 @@
#include <inttypes.h>
#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<NameIO> (*Constructor)( const Interface &iface,
const shared_ptr<Cipher> &cipher, const CipherKey &key);
public:
typedef shared_ptr<NameIO> (*Constructor)(const Interface &iface,
const shared_ptr<CipherV1> &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<Algorithm> AlgorithmList;
static AlgorithmList GetAlgorithmList( bool includeHidden = false );
typedef std::list<Algorithm> AlgorithmList;
static AlgorithmList GetAlgorithmList( bool includeHidden = false );
static shared_ptr<NameIO> New( const Interface &iface,
const shared_ptr<Cipher> &cipher, const CipherKey &key);
static shared_ptr<NameIO> New( const std::string &name,
const shared_ptr<Cipher> &cipher, const CipherKey &key);
static shared_ptr<NameIO> New(const Interface &iface,
const shared_ptr<CipherV1> &cipher);
static shared_ptr<NameIO> New(const std::string &name,
const shared_ptr<CipherV1> &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

View File

@ -19,7 +19,7 @@
*/
#include "base/base64.h"
#include "cipher/Cipher.h"
#include "cipher/CipherV1.h"
#include "fs/NullNameIO.h"
#include <cstring>
@ -27,7 +27,7 @@
namespace encfs {
static shared_ptr<NameIO> NewNNIO( const Interface &,
const shared_ptr<Cipher> &, const CipherKey & )
const shared_ptr<CipherV1> & )
{
return shared_ptr<NameIO>( new NullNameIO() );
}

View File

@ -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 <glog/logging.h>
@ -33,9 +33,9 @@ using namespace std;
namespace encfs {
static shared_ptr<NameIO> NewStreamNameIO( const Interface &iface,
const shared_ptr<Cipher> &cipher, const CipherKey &key)
const shared_ptr<CipherV1> &cipher )
{
return shared_ptr<NameIO>( new StreamNameIO( iface, cipher, key ) );
return shared_ptr<NameIO>( 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> &cipher,
const CipherKey &key )
const shared_ptr<CipherV1> &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)

View File

@ -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> &cipher,
const CipherKey &key );
const shared_ptr<CipherV1> &cipher);
virtual ~StreamNameIO();
virtual Interface interface() const;
@ -52,8 +50,7 @@ protected:
uint64_t *iv, char *plaintextName ) const;
private:
int _interface;
shared_ptr<Cipher> _cipher;
CipherKey _key;
shared_ptr<CipherV1> _cipher;
};
} // namespace encfs

View File

@ -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());

View File

@ -24,7 +24,6 @@
#include <gtest/gtest.h>
#include "fs/testing.h"
#include "cipher/Cipher.h"
#include "cipher/MemoryPool.h"
#include "fs/CipherFileIO.h"

View File

@ -27,7 +27,7 @@
#include <gtest/gtest.h>
#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>& cipher, int blockSize) {
FSConfigPtr makeConfig(const shared_ptr<CipherV1>& 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>& cipher, int blockSize) {
void runWithCipher(const string& cipherName, int blockSize,
void (*func)(FSConfigPtr& config)) {
shared_ptr<Cipher> cipher = Cipher::New(cipherName);
shared_ptr<CipherV1> 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<Cipher::CipherAlgorithm> algorithms = Cipher::GetAlgorithmList();
list<Cipher::CipherAlgorithm>::const_iterator it;
list<CipherV1::CipherAlgorithm> algorithms = CipherV1::GetAlgorithmList();
list<CipherV1::CipherAlgorithm>::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 = Cipher::New(it->name, keyLength);
shared_ptr<CipherV1> 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];

View File

@ -3,7 +3,7 @@
#include <string>
#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>& cipher, int blockSize);
FSConfigPtr makeConfig(const shared_ptr<CipherV1>& cipher, int blockSize);
void runWithCipher(const std::string& cipherName, int blockSize,
void (*func)(FSConfigPtr& config));

View File

@ -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;

View File

@ -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<string> 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> cipher = getCipher(config);
shared_ptr<CipherV1> 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 );