use PBKDF2 for new keys with salt and variable iteration count

git-svn-id: http://encfs.googlecode.com/svn/trunk@35 db9cf616-1c43-0410-9cb8-a902689de0d6
This commit is contained in:
Valient Gough 2008-08-16 16:39:31 +00:00
parent ebd17ec8da
commit 921a361e1b
11 changed files with 319 additions and 75 deletions

View File

@ -1,13 +1,65 @@
Sat Aug 16 2008 Valient Gough <vgough@pobox.com>
* use PBKDF2 for new keys with salt and variable iteration count.
When creating a new key, adjusts iteration count to take
approximatly 1/2 a second of CPU time to test key.
Tue Aug 5 2008 Valient Gough <vgough@pobox.com>
* bump version to 1.4.3
Fri Aug 1 2008 Valient Gough <vgough@pobox.com>
* fix xattr support for Mac
Tue Jul 1 2008 Valient Gough <vgough@pobox.com>
* add patch info to Changelog
* separate RenameOp definition from implementation to avoid gcc 4.3
errors
Sat Jun 28 2008 Valient Gough <vgough@pobox.com>
* remove logs in Context, which displayed plaintext names
Mon Jun 2 2008 Valient Gough <vgough@pobox.com>
* fix defaultYes/defaultNo functions
Tue Jul 1 2008 Valient Gough <vgough@pobox.com>
* patch to fix compile errors in w/ gcc 4.3 from Anthony Shipman.
2008-05-22 gettextize <bug-gnu-gettext@gnu.org>
Thu May 22 2008 Valient Gough <vgough@pobox.com>
* use autoreconf in reconfig.sh
* update autoconf and gettext tools
* remove AM_MKINSTALLDIRS from configure.ac
* m4/gettext.m4: Upgrade to gettext-0.17.
* m4/iconv.m4: Upgrade to gettext-0.17.
* m4/lib-link.m4: Upgrade to gettext-0.17.
* m4/po.m4: Upgrade to gettext-0.17.
* configure.ac (AM_GNU_GETTEXT_VERSION): Bump to 0.17.
Sun May 18 2008 Valient Gough <vgough@pobox.com>
* add makeKey program and showKey option to encfsctl
* replace C header includes with C++ versions
Sat May 17 2008 Valient Gough <vgough@pobox.com>
* fix EVP initialization
Thu May 15 2008 Valient Gough <vgough@pobox.com>
* include cstring in several files, patch by A.Klitzing
* improve return code check on RAND_bytes call
Wed May 14 2008 Valient Gough <vgough@pobox.com>
* include binary_object header in FuseUtils
Sat May 10 2008 Valient Gough <vgough@pobox.com>
* explicit namespace for make_binary_object calls
Wed May 7 2008 Valient Gough <vgough@pobox.com>
* add string.h to ConfigVar
Sun May 4 2008 Valient Gough <vgough@pobox.com>
* change boost requirement to 1.34+, to eliminate fs::native usage
requirement
* ensure boost::filesystem::path is created with native option"
Sat Apr 19 2008 Valient Gough <vgough@pobox.com>
* add direct-load method so that encfsctl cat can work with direct
cipher paths
Tue Apr 15 2008 Valient Gough <vgough@pobox.com>
* add boost filesystem lib check
Sun Apr 13 2008 Valient Gough <vgough@pobox.com>
* fix bug in export - wasn't able to export symlinks.

View File

@ -84,7 +84,10 @@ public:
virtual rel::Interface interface() const =0;
// create a new key based on a password
virtual CipherKey newKey(const char *password, int passwdLength) =0;
// if iterationCount == 0, then iteration count will be determined
// by newKey function and filled in.
virtual CipherKey newKey(const char *password, int passwdLength,
int &iterationCount, const unsigned char *salt, int saltLen) =0;
// create a new random key
virtual CipherKey newRandomKey() =0;
@ -109,7 +112,9 @@ public:
// 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.
virtual void randomize( unsigned char *buf, int len ) const =0;
// Returns true on success, false on failure.
virtual bool randomize( unsigned char *buf, int len,
bool strongRandom ) const =0;
// 64 bit MAC of the data with the given key
virtual uint64_t MAC_64( const unsigned char *src, int len,

View File

@ -21,6 +21,7 @@
#include "MemoryPool.h"
#include <rlog/rlog.h>
#include <rlog/Error.h>
#include <fcntl.h>
#include <cerrno>
@ -206,7 +207,8 @@ void CipherFileIO::initHeader( )
unsigned char buf[8] = {0};
do
{
cipher->randomize( buf, 8 );
if(!cipher->randomize( buf, 8, false ))
throw ERROR("Unable to generate a random file IV");
fileIV = 0;
for(int i=0; i<8; ++i)

View File

@ -87,7 +87,9 @@ static const char ENCFS_ENV_STDERR[] = "encfs_stderr";
static int V5SubVersion = 20040813; // fix MACFileIO block size issues
static int V5SubVersionDefault = 0;
const int V6SubVersion = 20080813; // switch to v6/XML, add allowHoles option
// 20080813 was really made on 20080413 -- typo on date..
//const int V6SubVersion = 20080813; // switch to v6/XML, add allowHoles option
const int V6SubVersion = 20080816; // add salt and iteration count
struct ConfigInfo
{
@ -118,6 +120,35 @@ namespace boost
{
namespace serialization
{
template<class Archive>
void encodeBinary(Archive &ar,
const char *id, const std::string &in)
{
int encodedSize = in.length();
std::string name = std::string("encoded") + id + "Size";
ar << make_nvp(name.c_str(), encodedSize);
char data[encodedSize];
memcpy(data, in.data(), encodedSize);
name = std::string("encoded") + id + "Data";
ar << make_nvp(name.c_str(),
serial::make_binary_object(data, encodedSize));
}
template<class Archive>
void decodeBinary(Archive &ar, const char *id, std::string &out)
{
int encodedSize;
std::string name = std::string("encoded") + id + "Size";
ar >> make_nvp(name.c_str(), encodedSize);
char data[encodedSize];
name = std::string("encoded") + id + "Data";
ar >> make_nvp(name.c_str(),
serial::make_binary_object(data, encodedSize));
out.assign( (char*)data, encodedSize );
}
template<class Archive>
void save(Archive &ar, const EncFSConfig &cfg,
unsigned int version)
@ -135,18 +166,18 @@ namespace boost
ar << make_nvp("blockMACRandBytes", cfg.blockMACRandBytes);
ar << make_nvp("allowHoles", cfg.allowHoles);
int keyLen = cfg.keyData.length();
ar << make_nvp("encodedKeySize", keyLen);
char key[keyLen];
memcpy(key, cfg.keyData.data(), keyLen);
ar << make_nvp("encodedKeyData",
serial::make_binary_object(key, keyLen));
encodeBinary(ar, "Key", cfg.keyData);
// version 20080816
ar << make_nvp("saltSize", cfg.saltSize);
ar << make_nvp("saltData",
serial::make_binary_object(cfg.saltData, cfg.saltSize));
ar << make_nvp("kdfIterations", cfg.kdfIterations);
}
template<class Archive>
void load(Archive &ar, EncFSConfig &cfg, unsigned int version)
{
(void)version;
cfg.subVersion = version;
ar >> make_nvp("creator", cfg.creator);
ar >> make_nvp("cipherAlg", cfg.cipherIface);
@ -160,12 +191,21 @@ namespace boost
ar >> make_nvp("blockMACRandBytes", cfg.blockMACRandBytes);
ar >> make_nvp("allowHoles", cfg.allowHoles);
int encodedKeySize;
ar >> make_nvp("encodedKeySize", encodedKeySize);
char key[encodedKeySize];
ar >> make_nvp("encodedKeyData",
serial::make_binary_object(key, encodedKeySize));
cfg.keyData.assign( (char*)key, encodedKeySize );
decodeBinary(ar, "Key", cfg.keyData);
if(version >= 20080816)
{
ar >> make_nvp("saltSize", cfg.saltSize);
cfg.saltData = new unsigned char[cfg.saltSize];
ar >> make_nvp("saltData",
serial::make_binary_object(cfg.saltData, cfg.saltSize));
ar >> make_nvp("kdfIterations", cfg.kdfIterations);
} else
{
cfg.saltSize = 0;
cfg.saltData = NULL;
cfg.kdfIterations = 0;
}
}
template<class Archive>
@ -1027,6 +1067,17 @@ RootPtr createV6Config( EncFS_Context *ctx, const std::string &rootDir,
config.externalIVChaining = externalIV;
config.allowHoles = allowHoles;
config.saltSize = 20;
config.saltData = new unsigned char[config.saltSize];
config.kdfIterations = 0; // filled in by keying function
if(!cipher->randomize(config.saltData, config.saltSize, true))
{
cout << _("Unable to generate random data for key derivation\n");
return rootInfo;
}
cout << "\n";
// xgroup(setup)
cout << _("Configuration finished. The filesystem to be created has\n"
@ -1063,11 +1114,17 @@ RootPtr createV6Config( EncFS_Context *ctx, const std::string &rootDir,
CipherKey userKey;
rDebug( "useStdin: %i", useStdin );
if(useStdin)
userKey = getUserKey( cipher, useStdin );
userKey = getUserKey( cipher, useStdin,
config.saltData, config.saltSize,
config.kdfIterations);
else if(passwordProgram.empty())
userKey = getNewUserKey( cipher );
userKey = getNewUserKey( cipher,
config.saltData, config.saltSize,
config.kdfIterations );
else
userKey = getUserKey( passwordProgram, cipher, rootDir );
userKey = getUserKey( passwordProgram, cipher, rootDir,
config.saltData, config.saltSize,
config.kdfIterations );
cipher->writeKey( volumeKey, encodedKey, userKey );
userKey.reset();
@ -1231,7 +1288,8 @@ void showFSInfo( const EncFSConfig &config )
cout << "\n";
}
CipherKey getUserKey( const shared_ptr<Cipher> &cipher, bool useStdin )
CipherKey getUserKey( const shared_ptr<Cipher> &cipher, bool useStdin,
const unsigned char *salt, int saltSize, int &iterations)
{
char passBuf[MaxPassBuf];
char *res;
@ -1253,7 +1311,8 @@ CipherKey getUserKey( const shared_ptr<Cipher> &cipher, bool useStdin )
if(!res)
cerr << _("Zero length password not allowed\n");
else
userKey = cipher->newKey( passBuf, strlen(passBuf) );
userKey = cipher->newKey( passBuf, strlen(passBuf),
iterations, salt, saltSize);
memset( passBuf, 0, sizeof(passBuf) );
@ -1287,7 +1346,8 @@ std::string readPassword( int FD )
}
CipherKey getUserKey( const std::string &passProg,
const shared_ptr<Cipher> &cipher, const std::string &rootDir )
const shared_ptr<Cipher> &cipher, 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.
int fds[2], pid;
@ -1358,7 +1418,8 @@ CipherKey getUserKey( const std::string &passProg,
waitpid(pid, NULL, 0);
// convert to key..
result = cipher->newKey( password.c_str(), password.length() );
result = cipher->newKey( password.c_str(), password.length(),
iterations, salt, saltSize );
// clear buffer..
password.assign( password.length(), '\0' );
@ -1366,7 +1427,8 @@ CipherKey getUserKey( const std::string &passProg,
return result;
}
CipherKey getNewUserKey( const shared_ptr<Cipher> &cipher )
CipherKey getNewUserKey( const shared_ptr<Cipher> &cipher,
const unsigned char *salt, int saltSize, int &iterations )
{
CipherKey userKey;
char passBuf[MaxPassBuf];
@ -1382,7 +1444,8 @@ CipherKey getNewUserKey( const shared_ptr<Cipher> &cipher )
sizeof(passBuf2)-1, RPP_ECHO_OFF);
if(res1 && res2 && !strcmp(passBuf, passBuf2))
userKey = cipher->newKey( passBuf, strlen(passBuf) );
userKey = cipher->newKey( passBuf, strlen(passBuf),
iterations, salt, saltSize );
else
{
// xgroup(common) -- probably not common, but group with the others
@ -1433,10 +1496,12 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts )
CipherKey userKey;
rDebug( "useStdin: %i", opts->useStdin );
if(opts->passwordProgram.empty())
userKey = getUserKey( cipher, opts->useStdin );
userKey = getUserKey( cipher, opts->useStdin,
config.saltData, config.saltSize, config.kdfIterations);
else
userKey = getUserKey( opts->passwordProgram,
cipher, opts->rootDir );
cipher, opts->rootDir,
config.saltData, config.saltSize, config.kdfIterations);
if(!userKey)
return rootInfo;

View File

@ -51,6 +51,10 @@ struct EncFSConfig
int blockSize; // reported in bytes
std::string keyData;
int saltSize; // in bytes
unsigned char *saltData;
int kdfIterations;
int blockMACBytes; // MAC headers on blocks..
int blockMACRandBytes; // number of random bytes in the block header
@ -69,6 +73,16 @@ struct EncFSConfig
externalIVChaining = false;
chainedNameIV = false;
allowHoles = false;
saltSize = 0;
saltData = NULL;
kdfIterations = 0;
}
~EncFSConfig()
{
if(saltData != NULL)
delete[] saltData;
}
};
@ -165,10 +179,13 @@ bool writeV6Config( const char *configFile, EncFSConfig *config);
CipherKey getUserKey(const boost::shared_ptr<Cipher> &cipher, bool useStdin);
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 );
CipherKey getNewUserKey(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

View File

@ -259,7 +259,10 @@ bool MACFileIO::writeOneBlock( const IORequest &req )
memset( newReq.data, 0, headerSize );
memcpy( newReq.data + headerSize, req.data, req.dataLen );
if(randBytes)
cipher->randomize( newReq.data+macBytes, randBytes );
{
if(!cipher->randomize( newReq.data+macBytes, randBytes, false ))
return false;
}
// compute the mac (which includes the random data) and fill it in
uint64_t mac = cipher->MAC_64( newReq.data+macBytes,

View File

@ -83,7 +83,8 @@ Interface NullCipher::interface() const
return iface;
}
CipherKey NullCipher::newKey(const char *, int )
CipherKey NullCipher::newKey(const char *, int,
int &, const unsigned char *, int )
{
return gNullKey;
}
@ -93,9 +94,10 @@ CipherKey NullCipher::newRandomKey()
return gNullKey;
}
void NullCipher::randomize( unsigned char *buf, int len ) const
bool NullCipher::randomize( unsigned char *buf, int len, bool ) const
{
memset( buf, 0, len );
return true;
}
uint64_t NullCipher::MAC_64(const unsigned char *, int ,

View File

@ -37,7 +37,8 @@ public:
virtual rel::Interface interface() const;
// 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);
// create a new random key
virtual CipherKey newRandomKey();
@ -55,7 +56,8 @@ public:
virtual int encodedKeySize() const;
virtual int cipherBlockSize() const;
virtual void randomize( unsigned char *buf, int len ) const;
virtual bool randomize( unsigned char *buf, int len,
bool strongRandom ) const;
virtual uint64_t MAC_64(const unsigned char *data, int len,
const CipherKey &key, uint64_t *chainedIV) const;

View File

@ -32,8 +32,10 @@
#include "Mutex.h"
#include <cstring>
#include <ctime>
#include <sys/mman.h>
#include <sys/time.h>
#include <rlog/rlog.h>
#include <rlog/Error.h>
@ -51,6 +53,9 @@ const int MAX_KEYLENGTH = 32; // in bytes (256 bit)
const int MAX_IVLENGTH = 16;
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
inline int MIN(int a, int b)
{
@ -125,13 +130,57 @@ int BytesToKey( int keyLen, int ivLen, const EVP_MD *md,
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 TimedPBKDF2(const char *pass, int passlen,
const unsigned char *salt, int saltlen,
int keylen, unsigned char *out,
int &numIterations)
{
int iter = 1000;
timeval start, end;
for(;;)
{
gettimeofday( &start, 0 );
int res = PKCS5_PBKDF2_HMAC_SHA1(pass, passlen, salt, saltlen,
iter, keylen, out);
if(res != 1)
return res;
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
{
// done..
numIterations = iter;
return 1;
}
}
}
// - 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
static Interface BlowfishInterface( "ssl/blowfish", 2, 1, 1 );
static Interface AESInterface( "ssl/aes", 2, 1, 1 );
// - Version 3:0 uses PBKDF2 for password derivation
static Interface BlowfishInterface( "ssl/blowfish", 3, 0, 2 );
static Interface AESInterface( "ssl/aes", 3, 0, 2 );
#if defined(HAVE_EVP_BF)
@ -210,8 +259,8 @@ public:
unsigned int keySize; // in bytes
unsigned int ivLength;
// key data is first _keySize bytes, followed by _ivLength length bytes for
// iv
// key data is first _keySize bytes,
// followed by iv of _ivLength bytes,
unsigned char *buffer;
EVP_CIPHER_CTX block_enc;
@ -232,6 +281,7 @@ SSLKey::SSLKey(int keySize_, int ivLength_)
pthread_mutex_init( &mutex, 0 );
buffer = (unsigned char *)OPENSSL_malloc( keySize + ivLength );
memset( buffer, 0, keySize + ivLength );
// most likely fails unless we're running as root, or a user-page-lock
// kernel patch is applied..
mlock( buffer, keySize + ivLength );
@ -351,7 +401,8 @@ Interface SSL_Cipher::interface() const
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)
CipherKey SSL_Cipher::newKey(const char *password, int passwdLength,
int &iterationCount, const unsigned char *salt, int saltLen)
{
const EVP_MD *md = EVP_sha1();
if(!md)
@ -363,7 +414,35 @@ CipherKey SSL_Cipher::newKey(const char *password, int passwdLength)
shared_ptr<SSLKey> key( new SSLKey( _keySize, _ivLength) );
int bytes = 0;
if( iface.current() > 1 )
if( iface.current() > 2 )
{
if(iterationCount == 0)
{
// timed run, fills in iteration count
if(TimedPBKDF2(password, passwdLength,
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
{
// known iteration length
if(PKCS5_PBKDF2_HMAC_SHA1(password, passwdLength, salt, saltLen,
iterationCount, _keySize + _ivLength,
KeyData(key)) != 1)
{
rWarning("openssl error, PBKDF2 failed");
return CipherKey();
}
}
} else if( iface.current() > 1 )
{
// now we use BytesToKey, which can deal with Blowfish keys larger then
// 128 bits.
@ -402,32 +481,25 @@ CipherKey SSL_Cipher::newRandomKey()
{
const int bufLen = MAX_KEYLENGTH;
unsigned char tmpBuf[ bufLen ];
// to avoid warnings of uninitialized data from valgrind
memset(tmpBuf, 0, sizeof(tmpBuf));
if(RAND_bytes( tmpBuf, bufLen ) != 1)
{
char errStr[120]; // specs require string at least 120 bytes long..
unsigned long errVal = 0;
if((errVal = ERR_get_error()) != 0)
{
rWarning("openssl error: %s", ERR_error_string( errVal, errStr ));
int saltLen = 20;
unsigned char 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..
int bytes = BytesToKey( _keySize, _ivLength, EVP_sha1(), tmpBuf,
bufLen, 16, KeyData(key), IVData(key) );
if(bytes != (int)_keySize)
if(PKCS5_PBKDF2_HMAC_SHA1((char*)tmpBuf, bufLen, saltBuf, saltLen,
1000, _keySize + _ivLength, KeyData(key)) != 1)
{
rWarning("newKey: BytesToKey returned %i, expecting %i key bytes",
bytes, _keySize);
rWarning("openssl error, PBKDF2 failed");
return CipherKey();
}
memset( tmpBuf, 0, bufLen );
OPENSSL_cleanse(tmpBuf, bufLen);
initKey( key, _blockCipher, _streamCipher, _keySize );
@ -478,11 +550,27 @@ static uint64_t _checksum_64( SSLKey *key,
return value;
}
void SSL_Cipher::randomize( unsigned char *buf, int len ) const
bool SSL_Cipher::randomize( unsigned char *buf, int len,
bool strongRandom ) const
{
// to avoid warnings of uninitialized data from valgrind
memset(buf, 0, len);
int result = RAND_pseudo_bytes( buf, len );
rAssert( result >= 0 );
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)
rWarning("openssl error: %s", ERR_error_string( errVal, errStr ));
return false;
} else
return true;
}
uint64_t SSL_Cipher::MAC_64( const unsigned char *data, int len,

View File

@ -87,7 +87,8 @@ public:
virtual rel::Interface interface() const;
// 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);
// create a new random key
virtual CipherKey newRandomKey();
@ -105,7 +106,8 @@ public:
virtual int encodedKeySize() const;
virtual int cipherBlockSize() const;
virtual void randomize( unsigned char *buf, int len ) const;
virtual bool randomize( unsigned char *buf, int len,
bool strongRandom ) const;
virtual uint64_t MAC_64( const unsigned char *src, int len,
const CipherKey &key, uint64_t *augment ) const;

View File

@ -669,7 +669,9 @@ static int do_chpasswd( bool useStdin, int argc, char **argv )
// ask for existing password
cout << _("Enter current Encfs password\n");
CipherKey userKey = getUserKey( cipher, useStdin );
CipherKey userKey = getUserKey( cipher, useStdin,
config.saltData, config.saltSize,
config.kdfIterations );
if(!userKey)
return EXIT_FAILURE;
@ -691,9 +693,13 @@ static int do_chpasswd( bool useStdin, int argc, char **argv )
userKey.reset();
cout << _("Enter new Encfs password\n");
if( useStdin )
userKey = getUserKey( cipher, true );
userKey = getUserKey( cipher, true,
config.saltData, config.saltSize,
config.kdfIterations );
else
userKey = getNewUserKey( cipher );
userKey = getNewUserKey( cipher,
config.saltData, config.saltSize,
config.kdfIterations );
// re-encode the volume key using the new user key and write it out..
int result = EXIT_FAILURE;