mirror of
https://github.com/vgough/encfs.git
synced 2024-11-21 23:43:26 +01:00
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:
parent
f00ef5c6d3
commit
7e95ff90c8
@ -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)
|
||||
|
50
base/Mutex.h
50
base/Mutex.h
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
@ -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
|
165
cipher/Cipher.h
165
cipher/Cipher.h
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
668
cipher/CipherV1.cpp
Normal 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
|
@ -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
|
@ -2,13 +2,13 @@
|
||||
|
||||
namespace encfs {
|
||||
|
||||
DEFINE_REGISTERABLE_TYPE(MessageAuthenticationCode)
|
||||
DEFINE_REGISTERABLE_TYPE(MAC)
|
||||
|
||||
MessageAuthenticationCode::MessageAuthenticationCode()
|
||||
MAC::MAC()
|
||||
{
|
||||
}
|
||||
|
||||
MessageAuthenticationCode::~MessageAuthenticationCode()
|
||||
MAC::~MAC()
|
||||
{
|
||||
}
|
||||
|
||||
|
16
cipher/MAC.h
16
cipher/MAC.h
@ -5,14 +5,17 @@
|
||||
|
||||
#include "base/Registry.h"
|
||||
#include "base/types.h"
|
||||
#include "cipher/CipherKey.h"
|
||||
|
||||
namespace encfs {
|
||||
|
||||
// MessageAuthenticationCode provides keyed MAC algorithms, eg HMAC.
|
||||
class MessageAuthenticationCode
|
||||
static const char NAME_SHA1_HMAC[] = "SHA-1/HMAC";
|
||||
|
||||
// MAC provides keyed MessageAuthenticationCode algorithms, eg HMAC.
|
||||
class MAC
|
||||
{
|
||||
public:
|
||||
DECLARE_REGISTERABLE_TYPE(MessageAuthenticationCode);
|
||||
DECLARE_REGISTERABLE_TYPE(MAC);
|
||||
|
||||
struct Properties {
|
||||
int blockSize; // Block length of hash function.
|
||||
@ -25,13 +28,12 @@ class MessageAuthenticationCode
|
||||
}
|
||||
};
|
||||
|
||||
MessageAuthenticationCode();
|
||||
virtual ~MessageAuthenticationCode();
|
||||
MAC();
|
||||
virtual ~MAC();
|
||||
|
||||
virtual int outputSize() const =0;
|
||||
|
||||
virtual bool setKey(const byte *key, int keyLength) =0;
|
||||
virtual bool randomKey(int keyLength) =0;
|
||||
virtual bool setKey(const CipherKey &key) =0;
|
||||
|
||||
virtual void reset() =0;
|
||||
virtual bool update(const byte *in, int length) =0;
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
@ -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
|
@ -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
73
cipher/NullCiphers.cpp
Normal 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
37
cipher/NullCiphers.h
Normal 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
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
@ -2,11 +2,7 @@
|
||||
|
||||
namespace encfs {
|
||||
|
||||
Registry<StreamCipher>& StreamCipher::GetRegistry()
|
||||
{
|
||||
static Registry<StreamCipher> registry;
|
||||
return registry;
|
||||
}
|
||||
DEFINE_REGISTERABLE_TYPE(StreamCipher);
|
||||
|
||||
StreamCipher::StreamCipher()
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 )
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
98
fs/Context.h
98
fs/Context.h
@ -21,8 +21,10 @@
|
||||
#ifndef _Context_incl_
|
||||
#define _Context_incl_
|
||||
|
||||
#include "base/config.h"
|
||||
#include "base/shared_ptr.h"
|
||||
#include "fs/encfs.h"
|
||||
#include "base/Mutex.h"
|
||||
|
||||
#include <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 );
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
131
fs/FileUtils.cpp
131
fs/FileUtils.cpp
@ -34,7 +34,7 @@
|
||||
#include "base/i18n.h"
|
||||
#include "base/XmlReader.h"
|
||||
|
||||
#include "cipher/Cipher.h"
|
||||
#include "cipher/CipherV1.h"
|
||||
#include "cipher/MemoryPool.h"
|
||||
#include "cipher/readpassphrase.h"
|
||||
|
||||
@ -528,60 +528,57 @@ bool readProtoConfig( const char *fileName, EncfsConfig &config,
|
||||
}
|
||||
|
||||
static
|
||||
Cipher::CipherAlgorithm findCipherAlgorithm(const char *name,
|
||||
CipherV1::CipherAlgorithm findCipherAlgorithm(const char *name,
|
||||
int keySize )
|
||||
{
|
||||
Cipher::AlgorithmList algorithms = Cipher::GetAlgorithmList();
|
||||
Cipher::AlgorithmList::const_iterator it;
|
||||
for(it = algorithms.begin(); it != algorithms.end(); ++it)
|
||||
for (auto &it : CipherV1::GetAlgorithmList())
|
||||
{
|
||||
if( !strcmp( name, it->name.c_str() )
|
||||
&& it->keyLength.allowed( keySize ))
|
||||
if( !strcmp( name, it.name.c_str() )
|
||||
&& it.keyLength.allowed( keySize ))
|
||||
{
|
||||
return *it;
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
||||
Cipher::CipherAlgorithm result;
|
||||
CipherV1::CipherAlgorithm result;
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
Cipher::CipherAlgorithm selectCipherAlgorithm()
|
||||
CipherV1::CipherAlgorithm selectCipherAlgorithm()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
// figure out what cipher they want to use..
|
||||
// xgroup(setup)
|
||||
cout << _("The following cipher algorithms are available:") << "\n";
|
||||
Cipher::AlgorithmList algorithms = Cipher::GetAlgorithmList();
|
||||
Cipher::AlgorithmList::const_iterator it;
|
||||
int optNum = 1;
|
||||
for(it = algorithms.begin(); it != algorithms.end(); ++it, ++optNum)
|
||||
int optNum = 0;
|
||||
auto algorithms = CipherV1::GetAlgorithmList();
|
||||
for (auto &it : algorithms)
|
||||
{
|
||||
cout << optNum << ". " << it->name
|
||||
<< " : " << gettext(it->description.c_str()) << "\n";
|
||||
if(it->keyLength.min() == it->keyLength.max())
|
||||
cout << ++optNum << ". " << it.name
|
||||
<< " : " << gettext(it.description.c_str()) << "\n";
|
||||
if(it.keyLength.min() == it.keyLength.max())
|
||||
{
|
||||
// shown after algorithm name and description.
|
||||
// xgroup(setup)
|
||||
cout << autosprintf(_(" -- key length %i bits")
|
||||
, it->keyLength.min()) << "\n";
|
||||
, it.keyLength.min()) << "\n";
|
||||
} else
|
||||
{
|
||||
cout << autosprintf(
|
||||
// shown after algorithm name and description.
|
||||
// xgroup(setup)
|
||||
_(" -- Supports key lengths of %i to %i bits"),
|
||||
it->keyLength.min(), it->keyLength.max()) << "\n";
|
||||
it.keyLength.min(), it.keyLength.max()) << "\n";
|
||||
}
|
||||
|
||||
if(it->blockSize.min() == it->blockSize.max())
|
||||
if(it.blockSize.min() == it.blockSize.max())
|
||||
{
|
||||
cout << autosprintf(
|
||||
// shown after algorithm name and description.
|
||||
// xgroup(setup)
|
||||
_(" -- block size %i bytes"), it->blockSize.min())
|
||||
_(" -- block size %i bytes"), it.blockSize.min())
|
||||
<< "\n";
|
||||
} else
|
||||
{
|
||||
@ -589,7 +586,7 @@ Cipher::CipherAlgorithm selectCipherAlgorithm()
|
||||
// shown after algorithm name and description.
|
||||
// xgroup(setup)
|
||||
_(" -- Supports block sizes of %i to %i bytes"),
|
||||
it->blockSize.min(), it->blockSize.max()) << "\n";
|
||||
it.blockSize.min(), it.blockSize.max()) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@ -606,11 +603,15 @@ Cipher::CipherAlgorithm selectCipherAlgorithm()
|
||||
continue;
|
||||
}
|
||||
|
||||
it = algorithms.begin();
|
||||
while(--cipherNum) // numbering starts at 1
|
||||
++it;
|
||||
|
||||
Cipher::CipherAlgorithm alg = *it;
|
||||
CipherV1::CipherAlgorithm alg;
|
||||
for (auto &it : algorithms)
|
||||
{
|
||||
if (!--cipherNum)
|
||||
{
|
||||
alg = it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// xgroup(setup)
|
||||
cout << autosprintf(_("Selected algorithm \"%s\""), alg.name.c_str())
|
||||
@ -621,7 +622,7 @@ Cipher::CipherAlgorithm selectCipherAlgorithm()
|
||||
}
|
||||
|
||||
static
|
||||
Interface selectNameCoding(const Cipher::CipherAlgorithm &alg)
|
||||
Interface selectNameCoding(const CipherV1::CipherAlgorithm &alg)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
@ -635,9 +636,6 @@ Interface selectNameCoding(const Cipher::CipherAlgorithm &alg)
|
||||
map<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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
152
fs/NameIO.h
152
fs/NameIO.h
@ -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
|
||||
|
||||
|
@ -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() );
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "fs/testing.h"
|
||||
|
||||
#include "cipher/Cipher.h"
|
||||
#include "cipher/MemoryPool.h"
|
||||
|
||||
#include "fs/CipherFileIO.h"
|
||||
|
@ -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];
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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 );
|
||||
|
Loading…
Reference in New Issue
Block a user