mirror of
https://github.com/vgough/encfs.git
synced 2024-11-21 23:43:26 +01:00
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:
parent
275d4be255
commit
687b51a7e5
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user