From 687b51a7e5237e6f91f33370b3ce3d6e5a0d5226 Mon Sep 17 00:00:00 2001 From: Valient Gough Date: Fri, 30 Dec 2011 00:18:28 +0000 Subject: [PATCH] add base32 encoding support, fixes #103 git-svn-id: http://encfs.googlecode.com/svn/trunk@76 db9cf616-1c43-0410-9cb8-a902689de0d6 --- encfs/BlockNameIO.cpp | 106 ++++++++++++++++++++++++++++++++---------- encfs/BlockNameIO.h | 6 ++- encfs/base64.cpp | 37 +++++++++++++++ encfs/base64.h | 17 +++++++ encfs/test.cpp | 16 +++++++ 5 files changed, 155 insertions(+), 27 deletions(-) diff --git a/encfs/BlockNameIO.cpp b/encfs/BlockNameIO.cpp index 27a210b..c754bab 100644 --- a/encfs/BlockNameIO.cpp +++ b/encfs/BlockNameIO.cpp @@ -2,17 +2,20 @@ * Author: Valient Gough * ***************************************************************************** - * 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 . */ #include "BlockNameIO.h" @@ -41,16 +44,35 @@ static shared_ptr NewBlockNameIO( const Interface &iface, if(cipher) blockSize = cipher->cipherBlockSize(); - return shared_ptr( new BlockNameIO( iface, cipher, key, blockSize)); + return shared_ptr( + new BlockNameIO( iface, cipher, key, blockSize, false)); +} + +static shared_ptr NewBlockNameIO32( const Interface &iface, + const shared_ptr &cipher, const CipherKey &key ) +{ + int blockSize = 8; + if(cipher) + blockSize = cipher->cipherBlockSize(); + + return shared_ptr( + 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, - 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 diff --git a/encfs/BlockNameIO.h b/encfs/BlockNameIO.h index bd9c07f..591d042 100644 --- a/encfs/BlockNameIO.h +++ b/encfs/BlockNameIO.h @@ -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, - 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; CipherKey _key; + bool _caseSensitive; }; diff --git a/encfs/base64.cpp b/encfs/base64.cpp index c0be230..ca2fb43 100644 --- a/encfs/base64.cpp +++ b/encfs/base64.cpp @@ -18,6 +18,8 @@ #include "base64.h" +#include + // 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= 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; + } +} + diff --git a/encfs/base64.h b/encfs/base64.h index 7c7b911..f8b7f89 100644 --- a/encfs/base64.h +++ b/encfs/base64.h @@ -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 diff --git a/encfs/test.cpp b/encfs/test.cpp index ed11b18..95517c4 100644 --- a/encfs/test.cpp +++ b/encfs/test.cpp @@ -295,6 +295,22 @@ bool runTests(const shared_ptr &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) {