Automatic upgrade to PBKDF2 when changing password of a V6 filesystem.

Increase desired KDF duration to 3 seconds in paranoia mode.
Bump to version 1.5.


git-svn-id: http://encfs.googlecode.com/svn/trunk@37 db9cf616-1c43-0410-9cb8-a902689de0d6
This commit is contained in:
Valient Gough 2008-08-17 07:26:52 +00:00
parent bcfe2aff09
commit ab90e2d91f
10 changed files with 208 additions and 139 deletions

View File

@ -1,7 +1,7 @@
dnl Process this file with autoconf to produce a configure script. dnl Process this file with autoconf to produce a configure script.
AC_INIT(encfs/encfs.h) dnl a source file from your sub dir AC_INIT(encfs/encfs.h) dnl a source file from your sub dir
AM_INIT_AUTOMAKE(encfs, 1.4.3) dnl searches for some needed programs AM_INIT_AUTOMAKE(encfs, 1.5) dnl searches for some needed programs
dnl without this order in this file, automake will be confused! dnl without this order in this file, automake will be confused!
dnl dnl

View File

@ -86,8 +86,13 @@ public:
// create a new key based on a password // create a new key based on a password
// if iterationCount == 0, then iteration count will be determined // if iterationCount == 0, then iteration count will be determined
// by newKey function and filled in. // 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, virtual CipherKey newKey(const char *password, int passwdLength,
int &iterationCount, const unsigned char *salt, int saltLen) =0; int &iterationCount, long desiredFunctionDuration,
const unsigned char *salt, int saltLen) =0;
// deprecated - for backward compatibility
virtual CipherKey newKey(const char *password, int passwdLength ) =0;
// create a new random key // create a new random key
virtual CipherKey newRandomKey() =0; virtual CipherKey newRandomKey() =0;

View File

@ -75,6 +75,9 @@ static const int DefaultBlockSize = 1024;
// use the extpass option, as extpass can return arbitrary length binary data. // use the extpass option, as extpass can return arbitrary length binary data.
static const int MaxPassBuf = 512; static const int MaxPassBuf = 512;
static const int NormalKDFDuration = 500; // 1/2 a second
static const int ParanoiaKDFDuration = 3000; // 3 seconds
// environment variable names for values encfs stores in the environment when // environment variable names for values encfs stores in the environment when
// calling an external password program. // calling an external password program.
static const char ENCFS_ENV_ROOTDIR[] = "encfs_root"; static const char ENCFS_ENV_ROOTDIR[] = "encfs_root";
@ -173,6 +176,7 @@ namespace boost
ar << make_nvp("saltData", ar << make_nvp("saltData",
serial::make_binary_object(cfg.saltData, cfg.saltSize)); serial::make_binary_object(cfg.saltData, cfg.saltSize));
ar << make_nvp("kdfIterations", cfg.kdfIterations); ar << make_nvp("kdfIterations", cfg.kdfIterations);
ar << make_nvp("desiredKDFDuration", cfg.desiredKDFDuration);
} }
template<class Archive> template<class Archive>
@ -200,11 +204,13 @@ namespace boost
ar >> make_nvp("saltData", ar >> make_nvp("saltData",
serial::make_binary_object(cfg.saltData, cfg.saltSize)); serial::make_binary_object(cfg.saltData, cfg.saltSize));
ar >> make_nvp("kdfIterations", cfg.kdfIterations); ar >> make_nvp("kdfIterations", cfg.kdfIterations);
ar >> make_nvp("desiredKDFDuration", cfg.desiredKDFDuration);
} else } else
{ {
cfg.saltSize = 0; cfg.saltSize = 0;
cfg.saltData = NULL; cfg.saltData = NULL;
cfg.kdfIterations = 0; cfg.kdfIterations = 16;
cfg.desiredKDFDuration = NormalKDFDuration;
} }
} }
@ -318,7 +324,10 @@ ConfigType readConfig_load( ConfigInfo *nm, const char *path,
try try
{ {
if( (*nm->loadFunc)( path, config, nm )) if( (*nm->loadFunc)( path, config, nm ))
{
config->cfgType = nm->type;
return nm->type; return nm->type;
}
} catch( rlog::Error & err ) } catch( rlog::Error & err )
{ {
err.log( _RLWarningChannel ); err.log( _RLWarningChannel );
@ -329,6 +338,7 @@ ConfigType readConfig_load( ConfigInfo *nm, const char *path,
} else } else
{ {
// No load function - must be an unsupported type.. // No load function - must be an unsupported type..
config->cfgType = nm->type;
return nm->type; return nm->type;
} }
} }
@ -901,7 +911,7 @@ static
bool selectZeroBlockPassThrough() bool selectZeroBlockPassThrough()
{ {
// xgroup(setup) // xgroup(setup)
return boolDefaultNo( return boolDefaultYes(
_("Enable file-hole pass-through?\n" _("Enable file-hole pass-through?\n"
"This avoids writing encrypted blocks when file holes are created.")); "This avoids writing encrypted blocks when file holes are created."));
} }
@ -938,7 +948,8 @@ RootPtr createV6Config( EncFS_Context *ctx, const std::string &rootDir,
bool uniqueIV = false; bool uniqueIV = false;
bool chainedIV = false; bool chainedIV = false;
bool externalIV = false; bool externalIV = false;
bool allowHoles = false; bool allowHoles = true;
long desiredKDFDuration = NormalKDFDuration;
if (reverseEncryption) if (reverseEncryption)
{ {
@ -973,6 +984,7 @@ RootPtr createV6Config( EncFS_Context *ctx, const std::string &rootDir,
uniqueIV = true; uniqueIV = true;
chainedIV = true; chainedIV = true;
externalIV = true; externalIV = true;
desiredKDFDuration = ParanoiaKDFDuration;
} else } else
if(answer[0] != 'x') if(answer[0] != 'x')
{ {
@ -1054,6 +1066,7 @@ RootPtr createV6Config( EncFS_Context *ctx, const std::string &rootDir,
EncFSConfig config; EncFSConfig config;
config.cfgType = Config_V6;
config.cipherIface = cipher->interface(); config.cipherIface = cipher->interface();
config.keySize = keySize; config.keySize = keySize;
config.blockSize = blockSize; config.blockSize = blockSize;
@ -1067,16 +1080,10 @@ RootPtr createV6Config( EncFS_Context *ctx, const std::string &rootDir,
config.externalIVChaining = externalIV; config.externalIVChaining = externalIV;
config.allowHoles = allowHoles; config.allowHoles = allowHoles;
config.saltSize = 20; config.saltSize = 0;
config.saltData = new unsigned char[config.saltSize]; config.saltData = NULL;
config.kdfIterations = 0; // filled in by keying function config.kdfIterations = 0; // filled in by keying function
config.desiredKDFDuration = desiredKDFDuration;
if(!cipher->randomize(config.saltData, config.saltSize, true))
{
cout << _("Unable to generate random data for key derivation\n");
return rootInfo;
}
cout << "\n"; cout << "\n";
// xgroup(setup) // xgroup(setup)
@ -1114,17 +1121,11 @@ RootPtr createV6Config( EncFS_Context *ctx, const std::string &rootDir,
CipherKey userKey; CipherKey userKey;
rDebug( "useStdin: %i", useStdin ); rDebug( "useStdin: %i", useStdin );
if(useStdin) if(useStdin)
userKey = getUserKey( cipher, useStdin, userKey = config.getUserKey( useStdin );
config.saltData, config.saltSize, else if(!passwordProgram.empty())
config.kdfIterations); userKey = config.getUserKey( passwordProgram, rootDir );
else if(passwordProgram.empty())
userKey = getNewUserKey( cipher,
config.saltData, config.saltSize,
config.kdfIterations );
else else
userKey = getUserKey( passwordProgram, cipher, rootDir, userKey = config.getNewUserKey();
config.saltData, config.saltSize,
config.kdfIterations );
cipher->writeKey( volumeKey, encodedKey, userKey ); cipher->writeKey( volumeKey, encodedKey, userKey );
userKey.reset(); userKey.reset();
@ -1232,7 +1233,7 @@ void showFSInfo( const EncFSConfig &config )
} }
{ {
cout << autosprintf(_("Key Size: %i bits"), config.keySize); cout << autosprintf(_("Key Size: %i bits"), config.keySize);
cipher = Cipher::New( config.cipherIface, config.keySize ); cipher = config.getCipher();
if(!cipher) if(!cipher)
{ {
// xgroup(diag) // xgroup(diag)
@ -1240,6 +1241,13 @@ void showFSInfo( const EncFSConfig &config )
} else } else
cout << "\n"; cout << "\n";
} }
if(config.kdfIterations > 0 && config.saltSize > 0)
{
cout << autosprintf(_("Using PBKDF2, with %i iterations"),
config.kdfIterations) << "\n";
cout << autosprintf(_("Salt Size: %i bits"), 8*config.saltSize)
<< "\n";
}
if(config.blockMACBytes) if(config.blockMACBytes)
{ {
if(config.subVersion < 20040813) if(config.subVersion < 20040813)
@ -1287,9 +1295,48 @@ void showFSInfo( const EncFSConfig &config )
} }
cout << "\n"; cout << "\n";
} }
CipherKey getUserKey( const shared_ptr<Cipher> &cipher, bool useStdin, shared_ptr<Cipher> EncFSConfig::getCipher()
const unsigned char *salt, int saltSize, int &iterations) {
return Cipher::New( cipherIface, keySize );
}
CipherKey EncFSConfig::makeKey(const char *password, int passwdLen)
{
CipherKey userKey;
shared_ptr<Cipher> cipher = getCipher();
// if no salt is set and we're creating a new password for a new
// FS type, then initialize salt..
if(saltSize == 0 && kdfIterations == 0 && cfgType >= Config_V6)
{
// upgrade to using salt
saltSize = 20;
saltData = new unsigned char[saltSize];
}
if(saltSize > 0)
{
// if iterations isn't known, then we're creating a new key, so
// randomize the salt..
if(kdfIterations == 0 && !cipher->randomize( saltData, saltSize, true))
{
cout << _("Error creating salt\n");
return userKey;
}
userKey = cipher->newKey( password, passwdLen,
kdfIterations, desiredKDFDuration,
saltData, saltSize);
} else
{
userKey = cipher->newKey( password, passwdLen );
}
return userKey;
}
CipherKey EncFSConfig::getUserKey(bool useStdin)
{ {
char passBuf[MaxPassBuf]; char passBuf[MaxPassBuf];
char *res; char *res;
@ -1311,8 +1358,7 @@ CipherKey getUserKey( const shared_ptr<Cipher> &cipher, bool useStdin,
if(!res) if(!res)
cerr << _("Zero length password not allowed\n"); cerr << _("Zero length password not allowed\n");
else else
userKey = cipher->newKey( passBuf, strlen(passBuf), userKey = makeKey(passBuf, strlen(passBuf));
iterations, salt, saltSize);
memset( passBuf, 0, sizeof(passBuf) ); memset( passBuf, 0, sizeof(passBuf) );
@ -1345,9 +1391,8 @@ std::string readPassword( int FD )
return result; return result;
} }
CipherKey getUserKey( const std::string &passProg, CipherKey EncFSConfig::getUserKey( const std::string &passProg,
const shared_ptr<Cipher> &cipher, const std::string &rootDir, const std::string &rootDir )
const unsigned char *salt, int saltSize, int &iterations)
{ {
// have a child process run the command and get the result back to us. // have a child process run the command and get the result back to us.
int fds[2], pid; int fds[2], pid;
@ -1418,8 +1463,7 @@ CipherKey getUserKey( const std::string &passProg,
waitpid(pid, NULL, 0); waitpid(pid, NULL, 0);
// convert to key.. // convert to key..
result = cipher->newKey( password.c_str(), password.length(), result = makeKey(password.c_str(), password.length());
iterations, salt, saltSize );
// clear buffer.. // clear buffer..
password.assign( password.length(), '\0' ); password.assign( password.length(), '\0' );
@ -1427,8 +1471,7 @@ CipherKey getUserKey( const std::string &passProg,
return result; return result;
} }
CipherKey getNewUserKey( const shared_ptr<Cipher> &cipher, CipherKey EncFSConfig::getNewUserKey()
const unsigned char *salt, int saltSize, int &iterations )
{ {
CipherKey userKey; CipherKey userKey;
char passBuf[MaxPassBuf]; char passBuf[MaxPassBuf];
@ -1444,9 +1487,9 @@ CipherKey getNewUserKey( const shared_ptr<Cipher> &cipher,
sizeof(passBuf2)-1, RPP_ECHO_OFF); sizeof(passBuf2)-1, RPP_ECHO_OFF);
if(res1 && res2 && !strcmp(passBuf, passBuf2)) if(res1 && res2 && !strcmp(passBuf, passBuf2))
userKey = cipher->newKey( passBuf, strlen(passBuf), {
iterations, salt, saltSize ); userKey = makeKey(passBuf, strlen(passBuf));
else } else
{ {
// xgroup(common) -- probably not common, but group with the others // xgroup(common) -- probably not common, but group with the others
cerr << _("Passwords did not match, please try again\n"); cerr << _("Passwords did not match, please try again\n");
@ -1478,8 +1521,7 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts )
} }
// first, instanciate the cipher. // first, instanciate the cipher.
shared_ptr<Cipher> cipher = shared_ptr<Cipher> cipher = config.getCipher();
Cipher::New( config.cipherIface, config.keySize );
if(!cipher) if(!cipher)
{ {
rError(_("Unable to find cipher %s, version %i:%i:%i"), rError(_("Unable to find cipher %s, version %i:%i:%i"),
@ -1494,14 +1536,13 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts )
// get user key // get user key
CipherKey userKey; CipherKey userKey;
rDebug( "useStdin: %i", opts->useStdin );
if(opts->passwordProgram.empty()) if(opts->passwordProgram.empty())
userKey = getUserKey( cipher, opts->useStdin, {
config.saltData, config.saltSize, config.kdfIterations); rDebug( "useStdin: %i", opts->useStdin );
else userKey = config.getUserKey( opts->useStdin );
userKey = getUserKey( opts->passwordProgram, } else
cipher, opts->rootDir, userKey = config.getUserKey( opts->passwordProgram, opts->rootDir );
config.saltData, config.saltSize, config.kdfIterations);
if(!userKey) if(!userKey)
return rootInfo; return rootInfo;

View File

@ -38,8 +38,20 @@ std::string parentDirectory( const std::string &path );
// do it and return true. // do it and return true.
bool userAllowMkdir( const char *dirPath, mode_t mode ); bool userAllowMkdir( const char *dirPath, mode_t mode );
enum ConfigType
{
Config_None = 0,
Config_Prehistoric,
Config_V3,
Config_V4,
Config_V5,
Config_V6
};
struct EncFSConfig struct EncFSConfig
{ {
ConfigType cfgType;
std::string creator; std::string creator;
int subVersion; int subVersion;
@ -54,6 +66,7 @@ struct EncFSConfig
int saltSize; // in bytes int saltSize; // in bytes
unsigned char *saltData; unsigned char *saltData;
int kdfIterations; int kdfIterations;
long desiredKDFDuration;
int blockMACBytes; // MAC headers on blocks.. int blockMACBytes; // MAC headers on blocks..
int blockMACRandBytes; // number of random bytes in the block header int blockMACRandBytes; // number of random bytes in the block header
@ -66,6 +79,7 @@ struct EncFSConfig
EncFSConfig() EncFSConfig()
{ {
cfgType = Config_None;
subVersion = 0; subVersion = 0;
blockMACBytes = 0; blockMACBytes = 0;
blockMACRandBytes = 0; blockMACRandBytes = 0;
@ -77,6 +91,7 @@ struct EncFSConfig
saltSize = 0; saltSize = 0;
saltData = NULL; saltData = NULL;
kdfIterations = 0; kdfIterations = 0;
desiredKDFDuration = 500;
} }
~EncFSConfig() ~EncFSConfig()
@ -84,16 +99,15 @@ struct EncFSConfig
if(saltData != NULL) if(saltData != NULL)
delete[] saltData; delete[] saltData;
} }
};
enum ConfigType CipherKey getUserKey(bool useStdin);
{ CipherKey getUserKey(const std::string &passwordProgram,
Config_None = 0, const std::string &rootDir);
Config_Prehistoric, CipherKey getNewUserKey();
Config_V3,
Config_V4, shared_ptr<Cipher> getCipher();
Config_V5, private:
Config_V6 CipherKey makeKey(const char *password, int passwdLen);
}; };
class Cipher; class Cipher;
@ -178,14 +192,4 @@ bool readV6Config( const char *configFile, EncFSConfig *config,
bool writeV6Config( const char *configFile, EncFSConfig *config); bool writeV6Config( const char *configFile, EncFSConfig *config);
CipherKey getUserKey(const boost::shared_ptr<Cipher> &cipher, bool useStdin,
const unsigned char *salt, int saltLen, int &iterations);
CipherKey getUserKey(const std::string &passwordProgram,
const boost::shared_ptr<Cipher> &cipher,
const std::string &rootDir,
const unsigned char *salt, int saltLen, int &iterations);
CipherKey getNewUserKey(const boost::shared_ptr<Cipher> &cipher,
const unsigned char *salt, int saltLen, int &iterations);
#endif #endif

View File

@ -84,7 +84,12 @@ Interface NullCipher::interface() const
} }
CipherKey NullCipher::newKey(const char *, int, CipherKey NullCipher::newKey(const char *, int,
int &, const unsigned char *, int ) int &, long, const unsigned char *, int )
{
return gNullKey;
}
CipherKey NullCipher::newKey(const char *, int)
{ {
return gNullKey; return gNullKey;
} }

View File

@ -38,7 +38,9 @@ public:
// create a new key based on a password // create a new key based on a password
virtual CipherKey newKey(const char *password, int passwdLength, virtual CipherKey newKey(const char *password, int passwdLength,
int &iterationCount, const unsigned char *salt, int saltLen); int &iterationCount, long desiredDuration,
const unsigned char *salt, int saltLen);
virtual CipherKey newKey(const char *password, int passwdLength);
// create a new random key // create a new random key
virtual CipherKey newRandomKey(); virtual CipherKey newRandomKey();

View File

@ -53,9 +53,6 @@ const int MAX_KEYLENGTH = 32; // in bytes (256 bit)
const int MAX_IVLENGTH = 16; const int MAX_IVLENGTH = 16;
const int KEY_CHECKSUM_BYTES = 4; const int KEY_CHECKSUM_BYTES = 4;
// how long we'd like the PDF function to take, in micro seconds
const long DesiredPDFTime = 500 * 1000; // 1/2 second
#ifndef MIN #ifndef MIN
inline int MIN(int a, int b) inline int MIN(int a, int b)
{ {
@ -139,7 +136,7 @@ long time_diff(const timeval &end, const timeval &start)
int TimedPBKDF2(const char *pass, int passlen, int TimedPBKDF2(const char *pass, int passlen,
const unsigned char *salt, int saltlen, const unsigned char *salt, int saltlen,
int keylen, unsigned char *out, int keylen, unsigned char *out,
int &numIterations) long desiredPDFTime)
{ {
int iter = 1000; int iter = 1000;
timeval start, end; timeval start, end;
@ -150,25 +147,21 @@ int TimedPBKDF2(const char *pass, int passlen,
int res = PKCS5_PBKDF2_HMAC_SHA1(pass, passlen, salt, saltlen, int res = PKCS5_PBKDF2_HMAC_SHA1(pass, passlen, salt, saltlen,
iter, keylen, out); iter, keylen, out);
if(res != 1) if(res != 1)
return res; return -1;
gettimeofday( &end, 0 ); gettimeofday( &end, 0 );
long delta = time_diff(end, start); long delta = time_diff(end, start);
if(delta < DesiredPDFTime / 8) if(delta < desiredPDFTime / 8)
{ {
iter *= 4; iter *= 4;
} else if(delta < (5 * DesiredPDFTime / 6)) } else if(delta < (5 * desiredPDFTime / 6))
{ {
// estimate number of iterations to get close to desired time // estimate number of iterations to get close to desired time
iter = (int)((double)iter * (double)DesiredPDFTime iter = (int)((double)iter * (double)desiredPDFTime
/ (double)delta); / (double)delta);
} else } else
{ return iter;
// done..
numIterations = iter;
return 1;
}
} }
} }
@ -178,9 +171,9 @@ int TimedPBKDF2(const char *pass, int passlen,
// - Version 2:0 uses BytesToKey. // - Version 2:0 uses BytesToKey.
// We support both 2:0 and 1:0, hence current:revision:age = 2:0:1 // 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:1 adds support for Message Digest function interface
// - Version 3:0 uses PBKDF2 for password derivation // - Version 2:2 adds PBKDF2 for password derivation
static Interface BlowfishInterface( "ssl/blowfish", 3, 0, 2 ); static Interface BlowfishInterface( "ssl/blowfish", 2, 2, 1 );
static Interface AESInterface( "ssl/aes", 3, 0, 2 ); static Interface AESInterface( "ssl/aes", 2, 2, 1 );
#if defined(HAVE_EVP_BF) #if defined(HAVE_EVP_BF)
@ -402,7 +395,8 @@ Interface SSL_Cipher::interface() const
is used to encipher/decipher the master key. is used to encipher/decipher the master key.
*/ */
CipherKey SSL_Cipher::newKey(const char *password, int passwdLength, CipherKey SSL_Cipher::newKey(const char *password, int passwdLength,
int &iterationCount, const unsigned char *salt, int saltLen) int &iterationCount, long desiredDuration,
const unsigned char *salt, int saltLen)
{ {
const EVP_MD *md = EVP_sha1(); const EVP_MD *md = EVP_sha1();
if(!md) if(!md)
@ -414,35 +408,49 @@ CipherKey SSL_Cipher::newKey(const char *password, int passwdLength,
shared_ptr<SSLKey> key( new SSLKey( _keySize, _ivLength) ); shared_ptr<SSLKey> key( new SSLKey( _keySize, _ivLength) );
int bytes = 0; int bytes = 0;
if( iface.current() > 2 ) if(iterationCount == 0)
{ {
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)
{ {
// timed run, fills in iteration count rWarning("openssl error, PBKDF2 failed");
if(TimedPBKDF2(password, passwdLength, return CipherKey();
salt, saltLen,
_keySize+_ivLength, KeyData(key),
iterationCount) != 1)
{
rWarning("openssl error, PBKDF2 failed");
return CipherKey();
} else if(iterationCount == 0)
{
rWarning("TimedPBKDF2 failed to fill in iteration count");
return CipherKey();
}
} else } else
iterationCount = res;
} else
{
// known iteration length
if(PKCS5_PBKDF2_HMAC_SHA1(password, passwdLength, salt, saltLen,
iterationCount, _keySize + _ivLength,
KeyData(key)) != 1)
{ {
// known iteration length rWarning("openssl error, PBKDF2 failed");
if(PKCS5_PBKDF2_HMAC_SHA1(password, passwdLength, salt, saltLen, return CipherKey();
iterationCount, _keySize + _ivLength,
KeyData(key)) != 1)
{
rWarning("openssl error, PBKDF2 failed");
return CipherKey();
}
} }
} else if( iface.current() > 1 ) }
initKey( key, _blockCipher, _streamCipher, _keySize );
return key;
}
CipherKey SSL_Cipher::newKey(const char *password, int passwdLength)
{
const EVP_MD *md = EVP_sha1();
if(!md)
{
rError("Unknown digest SHA1");
return CipherKey();
}
shared_ptr<SSLKey> key( new SSLKey( _keySize, _ivLength) );
int bytes = 0;
if( iface.current() > 1 )
{ {
// now we use BytesToKey, which can deal with Blowfish keys larger then // now we use BytesToKey, which can deal with Blowfish keys larger then
// 128 bits. // 128 bits.

View File

@ -88,7 +88,10 @@ public:
// create a new key based on a password // create a new key based on a password
virtual CipherKey newKey(const char *password, int passwdLength, virtual CipherKey newKey(const char *password, int passwdLength,
int &iterationCount, const unsigned char *salt, int saltLen); int &iterationCount, long desiredDuration,
const unsigned char *salt, int saltLen);
// deprecated - for backward compatibility
virtual CipherKey newKey(const char *password, int passwdLength);
// create a new random key // create a new random key
virtual CipherKey newRandomKey(); virtual CipherKey newRandomKey();

View File

@ -295,16 +295,18 @@ A choice is provided for two pre-configured settings ('standard' and
'paranoia'), along with an expert configuration mode. 'paranoia'), along with an expert configuration mode.
I<Standard> mode uses the following settings: I<Standard> mode uses the following settings:
Cipher: Blowfish Cipher: AES
Key Size: 160 bits Key Size: 192 bits
Filesystem Block Size: 512 bytes PBKDF2 with 1/2 second runtime, 160 bit salt
Filesystem Block Size: 1024 bytes
Filename Encoding: Block encoding with IV chaining Filename Encoding: Block encoding with IV chaining
Unique initialization vector file headers Unique initialization vector file headers
I<Paranoia> mode uses the following settings: I<Paranoia> mode uses the following settings:
Cipher: AES Cipher: AES
Key Size: 256 bits Key Size: 256 bits
Filesystem Block Size: 512 bytes PBKDF2 with 3 second runtime, 160 bit salt
Filesystem Block Size: 1024 bytes
Filename Encoding: Block encoding with IV chaining Filename Encoding: Block encoding with IV chaining
Unique initialization vector file headers Unique initialization vector file headers
Message Authentication Code block headers Message Authentication Code block headers
@ -314,6 +316,23 @@ In the expert / manual configuration mode, each of the above options is
configurable. Here is a list of current options with some notes about what configurable. Here is a list of current options with some notes about what
they mean: they mean:
=head1 Key Derivation Function
As of version 1.5, B<EncFS> now uses PBKDF2 as the default key derivation
function. The number of iterations in the keying function is selected based on
wall clock time to generate the key. In standard mode, a target time of 0.5
seconds is used, and in paranoia mode a target of 3.0 seconds is used.
On a 1.6Ghz AMD 64 system, it rougly 64k iterations of the key derivation
function can be handled in half a second. The exact number of iterations to
use is stored in the configuration file, as it is needed to remount the
filesystem.
If an B<EncFS> filesystem configuration from 1.4.x is modified with version 1.5
(such as when using encfsctl to change the password), then the new PBKDF2
function will be used and the filesystem will no longer be readable by older
versions.
=over 4 =over 4
=item I<Cipher> =item I<Cipher>
@ -382,7 +401,7 @@ vector. So "a/foo" and "b/foo" will have completely different encoded names
for "foo". This features has almost no performance impact (for most for "foo". This features has almost no performance impact (for most
operations), and so is the default in all modes. operations), and so is the default in all modes.
B<Note:> One significant exception is directory renames. Since the B<Note:> One significant performance exception is directory renames. Since the
initialization vector for filename encoding depends on the directory path, any initialization vector for filename encoding depends on the directory path, any
rename requires re-encoding every filename in the tree of the directory being rename requires re-encoding every filename in the tree of the directory being
changed. If there are thousands of files, then EncFS will have to do thousands changed. If there are thousands of files, then EncFS will have to do thousands

View File

@ -669,9 +669,7 @@ static int do_chpasswd( bool useStdin, int argc, char **argv )
// ask for existing password // ask for existing password
cout << _("Enter current Encfs password\n"); cout << _("Enter current Encfs password\n");
CipherKey userKey = getUserKey( cipher, useStdin, CipherKey userKey = config.getUserKey( useStdin );
config.saltData, config.saltSize,
config.kdfIterations );
if(!userKey) if(!userKey)
return EXIT_FAILURE; return EXIT_FAILURE;
@ -694,27 +692,11 @@ static int do_chpasswd( bool useStdin, int argc, char **argv )
cout << _("Enter new Encfs password\n"); cout << _("Enter new Encfs password\n");
// reinitialize salt and iteration count // reinitialize salt and iteration count
config.kdfIterations = 0; // generate new config.kdfIterations = 0; // generate new
if(config.saltSize != 20)
{
if(config.saltData != NULL)
delete[] config.saltData;
config.saltSize = 20;
config.saltData = new unsigned char[config.saltSize];
}
if(!cipher->randomize(config.saltData, config.saltSize, 20))
{
cout << _("Error creating salt\n");
return EXIT_FAILURE;
}
if( useStdin ) if( useStdin )
userKey = getUserKey( cipher, true, userKey = config.getUserKey( true );
config.saltData, config.saltSize,
config.kdfIterations );
else else
userKey = getNewUserKey( cipher, userKey = config.getNewUserKey();
config.saltData, config.saltSize,
config.kdfIterations );
// re-encode the volume key using the new user key and write it out.. // re-encode the volume key using the new user key and write it out..
int result = EXIT_FAILURE; int result = EXIT_FAILURE;