mirror of
https://github.com/vgough/encfs.git
synced 2025-06-20 03:37:50 +02:00
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:
parent
bcfe2aff09
commit
ab90e2d91f
@ -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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user