add zero-block pass-through option, enabling allow-holes code

git-svn-id: http://encfs.googlecode.com/svn/trunk@14 db9cf616-1c43-0410-9cb8-a902689de0d6
This commit is contained in:
Valient Gough 2008-04-11 08:37:19 +00:00
parent 9ce4a03887
commit 30ed7062d3
11 changed files with 184 additions and 111 deletions

View File

@ -189,7 +189,7 @@ bool BlockFileIO::write( const IORequest &req )
if(lastBlockSize == 0) if(lastBlockSize == 0)
--lastNonEmptyBlock; --lastNonEmptyBlock;
if( (req.offset > fileSize) && !_allowHoles ) if( req.offset > fileSize )
{ {
// extend file first to fill hole with 0's.. // extend file first to fill hole with 0's..
const bool forceWrite = false; const bool forceWrite = false;
@ -340,15 +340,18 @@ void BlockFileIO::padFile( off_t oldSize, off_t newSize, bool forceWrite )
++oldLastBlock; ++oldLastBlock;
} }
// 2 // 2, pad zero blocks unless holes are allowed
for(; oldLastBlock != newLastBlock; ++oldLastBlock) if(!_allowHoles)
{ {
rDebug("padding block %" PRIi64, oldLastBlock); for(; oldLastBlock != newLastBlock; ++oldLastBlock)
req.offset = oldLastBlock * _blockSize; {
req.dataLen = _blockSize; rDebug("padding block %" PRIi64, oldLastBlock);
memset( mb.data, 0, req.dataLen ); req.offset = oldLastBlock * _blockSize;
cacheWriteOneBlock( req ); req.dataLen = _blockSize;
} memset( mb.data, 0, req.dataLen );
cacheWriteOneBlock( req );
}
}
// 3. only necessary if write is forced and block is non 0 length // 3. only necessary if write is forced and block is non 0 length
if(forceWrite && newBlockSize) if(forceWrite && newBlockSize)

View File

@ -40,12 +40,12 @@ public:
virtual int blockSize() const; virtual int blockSize() const;
protected:
// default is false, but setting this to true will allow holes to be stored // default is false, but setting this to true will allow holes to be stored
// in the file. Only works if supported by the underlying FileIO // in the file. Only works if supported by the underlying FileIO
// implementation.. // implementation..
void allowHoles( bool allow ); virtual void allowHoles( bool allow );
protected:
int truncate( off_t size, FileIO *base ); int truncate( off_t size, FileIO *base );
void padFile( off_t oldSize, off_t newSize, bool forceWrite ); void padFile( off_t oldSize, off_t newSize, bool forceWrite );

View File

@ -372,7 +372,18 @@ bool CipherFileIO::blockRead( unsigned char *buf, int size,
if (reverseEncryption) if (reverseEncryption)
return cipher->blockEncode( buf, size, _iv64, key ); return cipher->blockEncode( buf, size, _iv64, key );
else else
return cipher->blockDecode( buf, size, _iv64, key ); {
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 true;
} else
return cipher->blockDecode( buf, size, _iv64, key );
}
} }
bool CipherFileIO::streamRead( unsigned char *buf, int size, bool CipherFileIO::streamRead( unsigned char *buf, int size,

View File

@ -726,7 +726,8 @@ shared_ptr<FileNode> DirNode::findOrCreate( const char *plainName)
config->uniqueIV, config->uniqueIV,
config->externalIVChaining, config->externalIVChaining,
config->forceDecode, config->forceDecode,
config->reverseEncryption) ); config->reverseEncryption,
config->allowHoles) );
if(config->externalIVChaining) if(config->externalIVChaining)
node->setName(0, 0, iv); node->setName(0, 0, iv);

View File

@ -101,6 +101,7 @@ public:
bool externalIVChaining; bool externalIVChaining;
bool forceDecode; // force decoding, even if errors are detected bool forceDecode; // force decoding, even if errors are detected
bool reverseEncryption; bool reverseEncryption;
bool allowHoles; // allow holes in files
Config() Config()
: fsSubVersion(0) : fsSubVersion(0)
, blockSize(1) , blockSize(1)
@ -111,6 +112,7 @@ public:
, externalIVChaining( false ) , externalIVChaining( false )
, forceDecode( false ) , forceDecode( false )
, reverseEncryption ( false ) , reverseEncryption ( false )
, allowHoles( false )
{ } { }
}; };

View File

@ -48,6 +48,7 @@
using namespace std; using namespace std;
using namespace rel; using namespace rel;
using namespace rlog; using namespace rlog;
using boost::dynamic_pointer_cast;
/* /*
TODO: locking at the FileNode level is inefficient, since this precludes TODO: locking at the FileNode level is inefficient, since this precludes
@ -65,7 +66,8 @@ FileNode::FileNode(DirNode *parent_,
const char *plaintextName_, const char *cipherName_, const char *plaintextName_, const char *cipherName_,
const shared_ptr<Cipher> &dataCipher, const CipherKey &key, const shared_ptr<Cipher> &dataCipher, const CipherKey &key,
int blockSize, int blockMACBytes, int blockMACRandBytes, bool uniqueIV, int blockSize, int blockMACBytes, int blockMACRandBytes, bool uniqueIV,
bool externalIVChaining_, bool forceDecode, bool reverseEncryption_ ) bool externalIVChaining_, bool forceDecode, bool reverseEncryption_,
bool allowHoles )
{ {
pthread_mutex_init( &mutex, 0 ); pthread_mutex_init( &mutex, 0 );
@ -88,6 +90,9 @@ FileNode::FileNode(DirNode *parent_,
io = shared_ptr<FileIO>(new MACFileIO(io, dataCipher, key, io = shared_ptr<FileIO>(new MACFileIO(io, dataCipher, key,
blockSize,blockMACBytes,blockMACRandBytes,forceDecode)); blockSize,blockMACBytes,blockMACRandBytes,forceDecode));
} }
if(allowHoles)
dynamic_pointer_cast<BlockFileIO>(io)->allowHoles( allowHoles );
} }
FileNode::~FileNode() FileNode::~FileNode()

View File

@ -43,7 +43,8 @@ public:
bool uniqueIV, // enable per-file initialization vectors bool uniqueIV, // enable per-file initialization vectors
bool externalIVChaining, bool externalIVChaining,
bool forceDecode, // decode, even if decoding errors are detected bool forceDecode, // decode, even if decoding errors are detected
bool reverseEncryption ); bool reverseEncryption,
bool allowHoles );
~FileNode(); ~FileNode();
const char *plaintextName() const; const char *plaintextName() const;

View File

@ -271,7 +271,9 @@ bool readV5Config( const char *configFile, EncFSConfig *config,
config->blockMACBytes = cfgRdr["blockMACBytes"].readInt(0); config->blockMACBytes = cfgRdr["blockMACBytes"].readInt(0);
config->blockMACRandBytes = config->blockMACRandBytes =
cfgRdr["blockMACRandBytes"].readInt(0); cfgRdr["blockMACRandBytes"].readInt(0);
config->allowHoles = cfgRdr["allowHoles"].readBool( false );
ok = true; ok = true;
} catch( rlog::Error &err) } catch( rlog::Error &err)
{ {
@ -411,6 +413,7 @@ bool writeV5Config( const char *configFile, EncFSConfig *config )
cfg["uniqueIV"] << config->uniqueIV; cfg["uniqueIV"] << config->uniqueIV;
cfg["chainedIV"] << config->chainedNameIV; cfg["chainedIV"] << config->chainedNameIV;
cfg["externalIV"] << config->externalIVChaining; cfg["externalIV"] << config->externalIVChaining;
cfg["allowHoles"] << config->allowHoles;
return cfg.save( configFile ); return cfg.save( configFile );
} }
@ -679,23 +682,35 @@ int selectBlockSize( const Cipher::CipherAlgorithm &alg )
return blockSize; return blockSize;
} }
static static
void selectBlockMAC(int *macBytes, int *macRandBytes) bool boolDefaultNo(const char *prompt)
{ {
// xgroup(setup) cout << prompt << "\n";
cout << _("Enable block authentication code headers\n" cout << _("The default here is No.\n"
"on every block in a file? This adds about 12 bytes per block\n" "Any response that does not begin with 'y' will mean No: ");
"to the storage requirements for a file, and significantly affects\n"
"performance but it also means [almost] any modifications or errors\n"
"within a block will be caught and will cause a read error.\n"
"The default here is No. \n"
"Any response that does not begin with 'y' will mean No: ");
char answer[10]; char answer[10];
fgets( answer, sizeof(answer), stdin ); fgets( answer, sizeof(answer), stdin );
cout << "\n"; cout << "\n";
if(tolower(answer[0]) == 'y') if(tolower(answer[0]) == 'n')
return false;
else
return true;
}
static
void selectBlockMAC(int *macBytes, int *macRandBytes)
{
// xgroup(setup)
bool addMAC = boolDefaultNo(
_("Enable block authentication code headers\n"
"on every block in a file? This adds about 12 bytes per block\n"
"to the storage requirements for a file, and significantly affects\n"
"performance but it also means [almost] any modifications or errors\n"
"within a block will be caught and will cause a read error."));
if(addMAC)
{ {
*macBytes = 8; *macBytes = 8;
@ -707,7 +722,8 @@ void selectBlockMAC(int *macBytes, int *macRandBytes)
"vectors, which does not come with as great of performance\n" "vectors, which does not come with as great of performance\n"
"penalty. \n" "penalty. \n"
"Select a number of bytes, from 0 (no random bytes) to 8: "); "Select a number of bytes, from 0 (no random bytes) to 8: ");
char answer[10];
int randSize = 0; int randSize = 0;
fgets( answer, sizeof(answer), stdin ); fgets( answer, sizeof(answer), stdin );
cout << "\n"; cout << "\n";
@ -726,59 +742,12 @@ void selectBlockMAC(int *macBytes, int *macRandBytes)
} }
} }
static static
bool selectUniqueIV() bool boolDefaultYes(const char *prompt)
{ {
// xgroup(setup) cout << prompt << "\n";
cout << _("Enable per-file initialization vectors?\n" cout << _("The default here is Yes.\n"
"This adds about 8 bytes per file to the storage requirements.\n" "Any response that does not begin with 'n' will mean Yes: ");
"It should not affect performance except possibly with applications\n"
"which rely on block-aligned file io for performance.\n"
"The default here is Yes. \n"
"Any response that does not begin with 'n' will mean Yes: ");
char answer[10];
fgets( answer, sizeof(answer), stdin );
cout << "\n";
if(tolower(answer[0]) == 'n')
return false;
else
return true;
}
static
bool selectChainedIV()
{
// xgroup(setup)
cout << _("Enable filename initialization vector chaining?\n"
"This makes filename encoding dependent on the complete path, \n"
"rather then encoding each path element individually. \n"
"This is normally desireable, therefor the default is Yes. \n"
"Any response that does not begin with 'n' will mean Yes: ");
char answer[10];
fgets( answer, sizeof(answer), stdin );
cout << "\n";
if(tolower(answer[0]) == 'n')
return false;
else
return true;
}
static
bool selectExternalChainedIV()
{
// xgroup(setup)
cout << _("Enable filename to IV header chaining?\n"
"This makes file data encoding dependent on the complete file path.\n"
"If a file is renamed, it will not decode sucessfully unless it\n"
"was renamed by encfs with the proper key.\n"
"If this option is enabled, then hard links will not be supported\n"
"in the filesystem.\n"
"The default is No. \n"
"Any response that does not begin with 'y' will mean No: ");
char answer[10]; char answer[10];
fgets( answer, sizeof(answer), stdin ); fgets( answer, sizeof(answer), stdin );
@ -790,6 +759,49 @@ bool selectExternalChainedIV()
return false; return false;
} }
static
bool selectUniqueIV()
{
// xgroup(setup)
return boolDefaultYes(
_("Enable per-file initialization vectors?\n"
"This adds about 8 bytes per file to the storage requirements.\n"
"It should not affect performance except possibly with applications\n"
"which rely on block-aligned file io for performance."));
}
static
bool selectChainedIV()
{
// xgroup(setup)
return boolDefaultYes(
_("Enable filename initialization vector chaining?\n"
"This makes filename encoding dependent on the complete path, \n"
"rather then encoding each path element individually."));
}
static
bool selectExternalChainedIV()
{
// xgroup(setup)
return boolDefaultNo(
_("Enable filename to IV header chaining?\n"
"This makes file data encoding dependent on the complete file path.\n"
"If a file is renamed, it will not decode sucessfully unless it\n"
"was renamed by encfs with the proper key.\n"
"If this option is enabled, then hard links will not be supported\n"
"in the filesystem."));
}
static
bool selectZeroBlockPassThrough()
{
// xgroup(setup)
return boolDefaultNo(
_("Enable file-hole pass-through?\n"
"This avoids writing encrypted blocks when file holes are created."));
}
RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir, RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
bool enableIdleTracking, bool forceDecode, bool enableIdleTracking, bool forceDecode,
const std::string &passwordProgram, const std::string &passwordProgram,
@ -822,6 +834,7 @@ RootPtr createV5Config( 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;
if (reverseEncryption) if (reverseEncryption)
{ {
@ -861,13 +874,11 @@ RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
{ {
// xgroup(setup) // xgroup(setup)
cout << _("Standard configuration selected.") << "\n"; cout << _("Standard configuration selected.") << "\n";
// look for blowfish with 160 bit key.. // AES w/ 192 bit key, block name encoding, per-file initialization
// block name encoding is now standard as well.. // vectors are all standard.
// per-file initialization vectors are now standard, as they shouldn't keySize = 192;
// have any significant performance penalty.
keySize = 160;
blockSize = DefaultBlockSize; blockSize = DefaultBlockSize;
alg = findCipherAlgorithm("Blowfish", keySize); alg = findCipherAlgorithm("AES", keySize);
blockMACBytes = 0; blockMACBytes = 0;
externalIV = false; externalIV = false;
nameIOIface = BlockNameIO::CurrentInterface(); nameIOIface = BlockNameIO::CurrentInterface();
@ -917,10 +928,11 @@ RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
// xgroup(setup) // xgroup(setup)
cout << _("External chained IV disabled, as both 'IV chaining'\n" cout << _("External chained IV disabled, as both 'IV chaining'\n"
"and 'unique IV' features are required for this option.") "and 'unique IV' features are required for this option.")
<< endl; << "\n";
externalIV = false; externalIV = false;
} }
selectBlockMAC(&blockMACBytes, &blockMACRandBytes); selectBlockMAC(&blockMACBytes, &blockMACRandBytes);
allowHoles = selectZeroBlockPassThrough();
} }
} }
@ -949,6 +961,7 @@ RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
config.uniqueIV = uniqueIV; config.uniqueIV = uniqueIV;
config.chainedNameIV = chainedIV; config.chainedNameIV = chainedIV;
config.externalIVChaining = externalIV; config.externalIVChaining = externalIV;
config.allowHoles = allowHoles;
cout << "\n"; cout << "\n";
// xgroup(setup) // xgroup(setup)
@ -1035,6 +1048,7 @@ RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
dirNodeConfig->externalIVChaining = config.externalIVChaining; dirNodeConfig->externalIVChaining = config.externalIVChaining;
dirNodeConfig->forceDecode = forceDecode; dirNodeConfig->forceDecode = forceDecode;
dirNodeConfig->reverseEncryption = reverseEncryption; dirNodeConfig->reverseEncryption = reverseEncryption;
dirNodeConfig->allowHoles = config.allowHoles;
rootInfo = RootPtr( new EncFS_Root ); rootInfo = RootPtr( new EncFS_Root );
rootInfo->cipher = cipher; rootInfo->cipher = cipher;
@ -1145,6 +1159,11 @@ void showFSInfo( const EncFSConfig &config )
// xgroup(diag) // xgroup(diag)
cout << _("File data IV is chained to filename IV.\n"); cout << _("File data IV is chained to filename IV.\n");
} }
if(config.allowHoles)
{
// xgroup(diag)
cout << _("File holes passed through to ciphertext.\n");
}
cout << "\n"; cout << "\n";
} }

View File

@ -58,6 +58,7 @@ struct EncFSConfig
bool externalIVChaining; // IV seeding by filename IV chaining bool externalIVChaining; // IV seeding by filename IV chaining
bool chainedNameIV; // filename IV chaining bool chainedNameIV; // filename IV chaining
bool allowHoles; // allow holes in files (implicit zero blocks)
}; };
enum ConfigType enum ConfigType

View File

@ -30,6 +30,8 @@
using namespace rlog; using namespace rlog;
using namespace rel; using namespace rel;
using namespace std; using namespace std;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
static RLogChannel *Info = DEF_CHANNEL("info/MACFileIO", Log_Info); static RLogChannel *Info = DEF_CHANNEL("info/MACFileIO", Log_Info);
// //
@ -157,6 +159,14 @@ off_t MACFileIO::getSize() const
return size; return size;
} }
void MACFileIO::allowHoles( bool allow )
{
BlockFileIO::allowHoles( allow );
shared_ptr<BlockFileIO> bf = dynamic_pointer_cast<BlockFileIO>( base );
if(bf)
bf->allowHoles( allow );
}
ssize_t MACFileIO::readOneBlock( const IORequest &req ) const ssize_t MACFileIO::readOneBlock( const IORequest &req ) const
{ {
int headerSize = macBytes + randBytes; int headerSize = macBytes + randBytes;
@ -173,31 +183,49 @@ ssize_t MACFileIO::readOneBlock( const IORequest &req ) const
// get the data from the base FileIO layer // get the data from the base FileIO layer
ssize_t readSize = base->read( tmp ); ssize_t readSize = base->read( tmp );
// don't store zeros if configured for zero-block pass-through
bool skipBlock;
if( _allowHoles )
{
skipBlock = true;
for(int i=0; i<readSize; ++i)
if(tmp.data[i] != 0)
{
skipBlock = false;
break;
}
} else
skipBlock = false;
if(readSize > headerSize) if(readSize > headerSize)
{ {
// At this point the data has been decoded. So, compute the MAC of the if(!skipBlock)
// block and check against the checksum stored in the header.. {
uint64_t mac = cipher->MAC_64( tmp.data + macBytes, // At this point the data has been decoded. So, compute the MAC of
readSize - macBytes, key ); // the block and check against the checksum stored in the header..
uint64_t mac = cipher->MAC_64( tmp.data + macBytes,
readSize - macBytes, key );
for(int i=0; i<macBytes; ++i, mac >>= 8) for(int i=0; i<macBytes; ++i, mac >>= 8)
{ {
int test = mac & 0xff; int test = mac & 0xff;
int stored = tmp.data[i]; int stored = tmp.data[i];
if(test != stored) if(test != stored)
{ {
// uh oh.. // uh oh..
long blockNum = req.offset / bs; long blockNum = req.offset / bs;
rWarning(_("MAC comparison failure in block %li"), rWarning(_("MAC comparison failure in block %li"),
blockNum); blockNum);
if( !warnOnly ) if( !warnOnly )
{ {
MemoryPool::release( mb ); MemoryPool::release( mb );
throw ERROR(_("MAC comparison failure, refusing to read")); throw ERROR(
} _("MAC comparison failure, refusing to read"));
break; }
} break;
} }
}
}
// now copy the data to the output buffer // now copy the data to the output buffer
readSize -= headerSize; readSize -= headerSize;

View File

@ -53,6 +53,8 @@ public:
virtual bool isWritable() const; virtual bool isWritable() const;
virtual void allowHoles( bool allow );
private: private:
virtual ssize_t readOneBlock( const IORequest &req ) const; virtual ssize_t readOneBlock( const IORequest &req ) const;
virtual bool writeOneBlock( const IORequest &req ); virtual bool writeOneBlock( const IORequest &req );