add base32 encoding support, fixes #103

git-svn-id: http://encfs.googlecode.com/svn/trunk@76 db9cf616-1c43-0410-9cb8-a902689de0d6
This commit is contained in:
Valient Gough 2011-12-30 00:18:28 +00:00
parent 275d4be255
commit 687b51a7e5
5 changed files with 155 additions and 27 deletions

View File

@ -2,17 +2,20 @@
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, Valient Gough
* Copyright (c) 2004-2011, Valient Gough
*
* This library is free software; you can distribute it and/or modify it under
* the terms of the GNU General Public License (GPL), as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more
* details.
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "BlockNameIO.h"
@ -41,16 +44,35 @@ static shared_ptr<NameIO> NewBlockNameIO( const Interface &iface,
if(cipher)
blockSize = cipher->cipherBlockSize();
return shared_ptr<NameIO>( new BlockNameIO( iface, cipher, key, blockSize));
return shared_ptr<NameIO>(
new BlockNameIO( iface, cipher, key, blockSize, false));
}
static shared_ptr<NameIO> NewBlockNameIO32( const Interface &iface,
const shared_ptr<Cipher> &cipher, const CipherKey &key )
{
int blockSize = 8;
if(cipher)
blockSize = cipher->cipherBlockSize();
return shared_ptr<NameIO>(
new BlockNameIO( iface, cipher, key, blockSize, true));
}
static bool BlockIO_registered = NameIO::Register("Block",
// description of block name encoding algorithm..
// xgroup(setup)
gettext_noop("Block encoding, hides file name size somewhat"),
BlockNameIO::CurrentInterface(),
BlockNameIO::CurrentInterface(false),
NewBlockNameIO);
static bool BlockIO32_registered = NameIO::Register("Block32",
// description of block name encoding algorithm..
// xgroup(setup)
gettext_noop("Block encoding with base32 output for case-sensitive systems"),
BlockNameIO::CurrentInterface(true),
NewBlockNameIO32);
/*
- Version 1.0 computed MAC over the filename, but not the padding bytes.
This version was from pre-release 1.1, never publically released, so no
@ -64,20 +86,28 @@ static bool BlockIO_registered = NameIO::Register("Block",
Prior versions used only the output from the MAC_16 call, giving a 1 in
2^16 chance of the same name being produced. Using the full 64 bit IV
changes that to a 1 in 2^64 chance..
- Version 4.0 adds support for base32, creating names more suitable for
case-insensitive filesystems (eg Mac).
*/
Interface BlockNameIO::CurrentInterface()
Interface BlockNameIO::CurrentInterface(bool caseSensitive)
{
// implement major version 3 and 2
return Interface("nameio/block", 3, 0, 1);
// implement major version 4 plus support for two prior versions
if (caseSensitive)
return Interface("nameio/block32", 4, 0, 2);
else
return Interface("nameio/block", 4, 0, 2);
}
BlockNameIO::BlockNameIO( const rel::Interface &iface,
const shared_ptr<Cipher> &cipher,
const CipherKey &key, int blockSize )
const CipherKey &key, int blockSize,
bool caseSensitiveEncoding )
: _interface( iface.current() )
, _bs( blockSize )
, _cipher( cipher )
, _key( key )
, _caseSensitive( caseSensitiveEncoding )
{
// just to be safe..
rAssert( blockSize < 128 );
@ -89,7 +119,7 @@ BlockNameIO::~BlockNameIO()
Interface BlockNameIO::interface() const
{
return CurrentInterface();
return CurrentInterface(_caseSensitive);
}
int BlockNameIO::maxEncodedNameLen( int plaintextNameLen ) const
@ -98,12 +128,17 @@ int BlockNameIO::maxEncodedNameLen( int plaintextNameLen ) const
// the size of too much space rather then too little.
int numBlocks = ( plaintextNameLen + _bs ) / _bs;
int encodedNameLen = numBlocks * _bs + 2; // 2 checksum bytes
return B256ToB64Bytes( encodedNameLen );
if (_caseSensitive)
return B256ToB32Bytes( encodedNameLen );
else
return B256ToB64Bytes( encodedNameLen );
}
int BlockNameIO::maxDecodedNameLen( int encodedNameLen ) const
{
int decLen256 = B64ToB256Bytes( encodedNameLen );
int decLen256 = _caseSensitive ?
B32ToB256Bytes( encodedNameLen ) :
B64ToB256Bytes( encodedNameLen );
return decLen256 - 2; // 2 checksum bytes removed..
}
@ -138,19 +173,33 @@ int BlockNameIO::encodeName( const char *plaintextName, int length,
// convert to base 64 ascii
int encodedStreamLen = length + 2 + padding;
int encLen64 = B256ToB64Bytes( encodedStreamLen );
int encLen;
if (_caseSensitive)
{
encLen = B256ToB32Bytes( encodedStreamLen );
changeBase2Inline( (unsigned char *)encodedName, encodedStreamLen,
8, 6, true );
B64ToAscii( (unsigned char *)encodedName, encLen64 );
changeBase2Inline( (unsigned char *)encodedName, encodedStreamLen,
8, 5, true );
B32ToAscii( (unsigned char *)encodedName, encLen );
} else
{
encLen = B256ToB64Bytes( encodedStreamLen );
return encLen64;
changeBase2Inline( (unsigned char *)encodedName, encodedStreamLen,
8, 6, true );
B64ToAscii( (unsigned char *)encodedName, encLen );
}
return encLen;
}
int BlockNameIO::decodeName( const char *encodedName, int length,
uint64_t *iv, char *plaintextName ) const
{
int decLen256 = B64ToB256Bytes( length );
int decLen256 = _caseSensitive ?
B32ToB256Bytes( length ) :
B64ToB256Bytes( length );
int decodedStreamLen = decLen256 - 2;
// don't bother trying to decode files which are too small
@ -160,8 +209,15 @@ int BlockNameIO::decodeName( const char *encodedName, int length,
BUFFER_INIT( tmpBuf, 32, (unsigned int)length );
// decode into tmpBuf,
AsciiToB64((unsigned char *)tmpBuf, (unsigned char *)encodedName, length);
changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false);
if (_caseSensitive)
{
AsciiToB32((unsigned char *)tmpBuf, (unsigned char *)encodedName, length);
changeBase2Inline((unsigned char *)tmpBuf, length, 5, 8, false);
} else
{
AsciiToB64((unsigned char *)tmpBuf, (unsigned char *)encodedName, length);
changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false);
}
// pull out the header information
unsigned int mac = ((unsigned int)((unsigned char)tmpBuf[0])) << 8

View File

@ -33,11 +33,12 @@ class Cipher;
class BlockNameIO : public NameIO
{
public:
static rel::Interface CurrentInterface();
static rel::Interface CurrentInterface(bool caseSensitive = false);
BlockNameIO( const rel::Interface &iface,
const boost::shared_ptr<Cipher> &cipher,
const CipherKey &key, int blockSize );
const CipherKey &key, int blockSize,
bool caseSensitiveEncoding = false );
virtual ~BlockNameIO();
virtual rel::Interface interface() const;
@ -58,6 +59,7 @@ private:
int _bs;
boost::shared_ptr<Cipher> _cipher;
CipherKey _key;
bool _caseSensitive;
};

View File

@ -18,6 +18,8 @@
#include "base64.h"
#include <ctype.h>
// change between two powers of two, stored as the low bits of the bytes in the
// arrays.
// It is the caller's responsibility to make sure the output array is large
@ -169,3 +171,38 @@ void AsciiToB64(unsigned char *out, const unsigned char *in, int length)
}
}
void B32ToAscii(unsigned char *buf, int len)
{
for(int offset=0; offset<len; ++offset)
{
int ch = buf[offset];
if (ch >= 0 && ch < 26)
ch += 'A';
else
ch += '2' - 26;
buf[offset] = ch;
}
}
void AsciiToB32(unsigned char *in, int length)
{
return AsciiToB32(in, in, length);
}
void AsciiToB32(unsigned char *out, const unsigned char *in, int length)
{
while(length--)
{
unsigned char ch = *in++;
int lch = toupper(ch);
if (lch >= 'A')
lch -= 'A';
else
lch += 26 - '2';
*out++ = (unsigned char)lch;
}
}

View File

@ -25,11 +25,21 @@ inline int B64ToB256Bytes( int numB64Bytes )
return (numB64Bytes * 6) / 8; // round down
}
inline int B32ToB256Bytes( int numB32Bytes )
{
return (numB32Bytes * 5) / 8; // round down
}
inline int B256ToB64Bytes( int numB256Bytes )
{
return (numB256Bytes * 8 + 5) / 6; // round up
}
inline int B256ToB32Bytes( int numB256Bytes )
{
return (numB256Bytes * 8 + 4) / 5; // round up
}
/*
convert data between different bases - each being a power of 2.
@ -47,9 +57,16 @@ void changeBase2Inline(unsigned char *buf, int srcLength,
// inplace translation from values [0,2^6] => base64 ASCII
void B64ToAscii(unsigned char *buf, int length);
// inplace translation from values [0,2^5] => base32 ASCII
void B32ToAscii(unsigned char *buf, int length);
// inplace translation from values base64 ASCII => [0,2^6]
void AsciiToB64(unsigned char *buf, int length);
void AsciiToB64(unsigned char *out, const unsigned char *in, int length);
// inplace translation from values base32 ASCII => [0,2^5]
void AsciiToB32(unsigned char *buf, int length);
void AsciiToB32(unsigned char *out, const unsigned char *in, int length);
#endif

View File

@ -295,6 +295,22 @@ bool runTests(const shared_ptr<Cipher> &cipher, bool verbose)
if(!testNameCoding( dirNode, verbose ))
return false;
}
if(verbose)
cerr << "Testing name encode/decode (block coding w/ IV chaining, base32)\n";
{
fsCfg->opts->idleTracking = false;
fsCfg->config->uniqueIV = false;
fsCfg->nameCoding.reset( new BlockNameIO(
BlockNameIO::CurrentInterface(), cipher, key,
cipher->cipherBlockSize(), true ) );
fsCfg->nameCoding->setChainedNameIV( true );
DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg );
if(!testNameCoding( dirNode, verbose ))
return false;
}
if(!verbose)
{