2013-01-29 04:07:54 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
* Author: Valient Gough <vgough@pobox.com>
|
|
|
|
*
|
|
|
|
*****************************************************************************
|
|
|
|
* Copyright (c) 2004-2011, Valient Gough
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU Lesser General Public License as published by the
|
|
|
|
* Free Software Foundation, either version 3 of the License, or (at your
|
2013-10-20 00:35:26 +02:00
|
|
|
* option) any later version.
|
2013-01-29 04:07:54 +01:00
|
|
|
*
|
|
|
|
* 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 GNU Lesser General Public License
|
|
|
|
* for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "fs/BlockNameIO.h"
|
|
|
|
|
|
|
|
#include "base/base64.h"
|
|
|
|
#include "base/Error.h"
|
|
|
|
#include "base/i18n.h"
|
2013-03-05 07:36:32 +01:00
|
|
|
#include "cipher/CipherV1.h"
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
#include <cstring>
|
2013-10-21 07:38:27 +02:00
|
|
|
#include <vector>
|
|
|
|
|
2013-01-29 04:07:54 +01:00
|
|
|
#include <glog/logging.h>
|
|
|
|
|
2013-03-05 07:29:58 +01:00
|
|
|
namespace encfs {
|
|
|
|
|
2013-10-21 07:38:27 +02:00
|
|
|
using std::string;
|
|
|
|
using std::vector;
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static shared_ptr<NameIO> NewBlockNameIO(const Interface &iface,
|
|
|
|
const shared_ptr<CipherV1> &cipher) {
|
|
|
|
return shared_ptr<NameIO>(new BlockNameIO(iface, cipher, false));
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static shared_ptr<NameIO> NewBlockNameIO32(const Interface &iface,
|
|
|
|
const shared_ptr<CipherV1> &cipher) {
|
|
|
|
return shared_ptr<NameIO>(new BlockNameIO(iface, cipher, true));
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static bool BlockIO_registered = NameIO::Register(
|
|
|
|
"Block",
|
2013-01-29 04:07:54 +01:00
|
|
|
// description of block name encoding algorithm..
|
|
|
|
// xgroup(setup)
|
|
|
|
gettext_noop("Block encoding, hides file name size somewhat"),
|
2013-10-20 00:35:26 +02:00
|
|
|
BlockNameIO::CurrentInterface(false), NewBlockNameIO, false);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static bool BlockIO32_registered = NameIO::Register(
|
|
|
|
"Block32",
|
2013-01-29 04:07:54 +01:00
|
|
|
// description of block name encoding algorithm..
|
|
|
|
// xgroup(setup)
|
2013-10-20 00:35:26 +02:00
|
|
|
gettext_noop(
|
|
|
|
"Block encoding with base32 output for case-sensitive systems"),
|
|
|
|
BlockNameIO::CurrentInterface(true), NewBlockNameIO32, false);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
- 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
|
|
|
|
backward compatibility necessary.
|
|
|
|
|
|
|
|
- Version 2.0 includes padding bytes in MAC computation. This way the MAC
|
|
|
|
computation uses the same number of bytes regardless of the number of
|
|
|
|
padding bytes.
|
|
|
|
|
|
|
|
- Version 3.0 uses full 64 bit initialization vector during IV chaining.
|
|
|
|
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
|
2013-10-21 07:38:27 +02:00
|
|
|
reduces to a 1 in 2^64 chance..
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-21 07:38:27 +02:00
|
|
|
- Version 4.0 adds support for base32, creating names better suited to
|
2013-01-29 04:07:54 +01:00
|
|
|
case-insensitive filesystems (eg Mac).
|
|
|
|
*/
|
2013-10-20 00:35:26 +02:00
|
|
|
Interface BlockNameIO::CurrentInterface(bool caseSensitive) {
|
2013-10-21 07:38:27 +02:00
|
|
|
// implement major version 4 plus support for prior versions
|
2013-01-29 04:07:54 +01:00
|
|
|
if (caseSensitive)
|
|
|
|
return makeInterface("nameio/block32", 4, 0, 2);
|
|
|
|
else
|
|
|
|
return makeInterface("nameio/block", 4, 0, 2);
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
BlockNameIO::BlockNameIO(const Interface &iface,
|
|
|
|
const shared_ptr<CipherV1> &cipher,
|
|
|
|
bool caseSensitiveEncoding)
|
|
|
|
: _interface(iface.major()),
|
|
|
|
_bs(cipher->cipherBlockSize()),
|
|
|
|
_cipher(cipher),
|
|
|
|
_caseSensitive(caseSensitiveEncoding) {
|
|
|
|
rAssert(_bs < 128);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
BlockNameIO::~BlockNameIO() {}
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
Interface BlockNameIO::interface() const {
|
2013-01-29 04:07:54 +01:00
|
|
|
return CurrentInterface(_caseSensitive);
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int BlockNameIO::maxEncodedNameLen(int plaintextNameLen) const {
|
2013-01-29 04:07:54 +01:00
|
|
|
// number of blocks, rounded up.. Only an estimate at this point, err on
|
|
|
|
// the size of too much space rather then too little.
|
2013-10-20 00:35:26 +02:00
|
|
|
int numBlocks = (plaintextNameLen + _bs) / _bs;
|
|
|
|
int encodedNameLen = numBlocks * _bs + 2; // 2 checksum bytes
|
2013-01-29 04:07:54 +01:00
|
|
|
if (_caseSensitive)
|
2013-10-20 00:35:26 +02:00
|
|
|
return B256ToB32Bytes(encodedNameLen);
|
2013-01-29 04:07:54 +01:00
|
|
|
else
|
2013-10-20 00:35:26 +02:00
|
|
|
return B256ToB64Bytes(encodedNameLen);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int BlockNameIO::maxDecodedNameLen(int encodedNameLen) const {
|
|
|
|
int decLen256 = _caseSensitive ? B32ToB256Bytes(encodedNameLen)
|
|
|
|
: B64ToB256Bytes(encodedNameLen);
|
|
|
|
return decLen256 - 2; // 2 checksum bytes removed..
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-21 07:38:27 +02:00
|
|
|
string BlockNameIO::encodeName(const string &plaintextName,
|
|
|
|
uint64_t *iv) const {
|
|
|
|
int length = plaintextName.length();
|
2013-01-29 04:07:54 +01:00
|
|
|
// Pad encryption buffer to block boundary..
|
|
|
|
int padding = _bs - length % _bs;
|
2013-10-21 07:38:27 +02:00
|
|
|
int encodedStreamLen = length + 2 + padding;
|
|
|
|
int encLen = _caseSensitive ? B256ToB32Bytes(encodedStreamLen)
|
|
|
|
: B256ToB64Bytes(encodedStreamLen);
|
|
|
|
|
|
|
|
vector<byte> tmpBuf(encLen);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-21 07:38:27 +02:00
|
|
|
// copy the data into the encoding buffer..
|
|
|
|
memcpy(tmpBuf.data() + 2, plaintextName.data(), length);
|
|
|
|
memset(tmpBuf.data() + length + 2, (unsigned char)padding, padding);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
// store the IV before it is modified by the MAC call.
|
|
|
|
uint64_t tmpIV = 0;
|
2013-10-20 00:35:26 +02:00
|
|
|
if (iv && _interface >= 3) tmpIV = *iv;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
// include padding in MAC computation
|
2013-03-05 07:36:32 +01:00
|
|
|
unsigned int mac = _cipher->reduceMac16(
|
2013-10-21 07:38:27 +02:00
|
|
|
_cipher->MAC_64(tmpBuf.data() + 2, length + padding, iv));
|
|
|
|
tmpIV ^= (uint64_t)mac;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
// add checksum bytes
|
2013-10-21 07:38:27 +02:00
|
|
|
tmpBuf[0] = (mac >> 8) & 0xff;
|
|
|
|
tmpBuf[1] = (mac) & 0xff;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-21 07:38:27 +02:00
|
|
|
_cipher->blockEncode(tmpBuf.data() + 2, length + padding, tmpIV);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-21 07:38:27 +02:00
|
|
|
// convert to base 32 or 64 ascii
|
2013-10-20 00:35:26 +02:00
|
|
|
if (_caseSensitive) {
|
2013-10-21 07:38:27 +02:00
|
|
|
changeBase2Inline(tmpBuf.data(), encodedStreamLen, 8, 5, true);
|
|
|
|
B32ToAscii(tmpBuf.data(), encLen);
|
2013-10-20 00:35:26 +02:00
|
|
|
} else {
|
2013-10-21 07:38:27 +02:00
|
|
|
changeBase2Inline(tmpBuf.data(), encodedStreamLen, 8, 6, true);
|
|
|
|
B64ToAscii(tmpBuf.data(), encLen);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-21 07:38:27 +02:00
|
|
|
return string(reinterpret_cast<char*>(tmpBuf.data()), encLen);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-21 07:38:27 +02:00
|
|
|
string BlockNameIO::decodeName(const string &encodedName, uint64_t *iv) const {
|
|
|
|
int length = encodedName.length();
|
2013-10-20 00:35:26 +02:00
|
|
|
int decLen256 =
|
|
|
|
_caseSensitive ? B32ToB256Bytes(length) : B64ToB256Bytes(length);
|
2013-01-29 04:07:54 +01:00
|
|
|
int decodedStreamLen = decLen256 - 2;
|
|
|
|
|
|
|
|
// don't bother trying to decode files which are too small
|
2013-10-20 00:35:26 +02:00
|
|
|
if (decodedStreamLen < _bs) throw Error("Filename too small to decode");
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-21 07:38:27 +02:00
|
|
|
vector<byte> tmpBuf(length, 0);
|
|
|
|
memcpy(tmpBuf.data(), encodedName.data(), length);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
// decode into tmpBuf,
|
2013-10-20 00:35:26 +02:00
|
|
|
if (_caseSensitive) {
|
2013-10-21 07:38:27 +02:00
|
|
|
AsciiToB32(tmpBuf.data(), length);
|
|
|
|
changeBase2Inline(tmpBuf.data(), length, 5, 8, false);
|
2013-10-20 00:35:26 +02:00
|
|
|
} else {
|
2013-10-21 07:38:27 +02:00
|
|
|
AsciiToB64(tmpBuf.data(), length);
|
|
|
|
changeBase2Inline(tmpBuf.data(), length, 6, 8, false);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// pull out the header information
|
2013-10-21 07:38:27 +02:00
|
|
|
unsigned int mac = ((unsigned int)tmpBuf[0]) << 8 |
|
|
|
|
((unsigned int)tmpBuf[1]);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
uint64_t tmpIV = 0;
|
2013-10-20 00:35:26 +02:00
|
|
|
if (iv && _interface >= 3) tmpIV = *iv;
|
2013-10-21 07:38:27 +02:00
|
|
|
tmpIV ^= (uint64_t)mac;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-21 07:38:27 +02:00
|
|
|
_cipher->blockDecode(&tmpBuf.at(2), decodedStreamLen, tmpIV);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
// find out true string length
|
2013-10-21 07:38:27 +02:00
|
|
|
int padding = tmpBuf[2 + decodedStreamLen - 1];
|
2013-01-29 04:07:54 +01:00
|
|
|
int finalSize = decodedStreamLen - padding;
|
|
|
|
|
|
|
|
// might happen if there is an error decoding..
|
2013-10-20 00:35:26 +02:00
|
|
|
if (padding > _bs || finalSize < 0) {
|
|
|
|
VLOG(1) << "padding, _bx, finalSize = " << padding << ", " << _bs << ", "
|
|
|
|
<< finalSize;
|
|
|
|
throw Error("invalid padding size");
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// check the mac
|
2013-03-05 07:36:32 +01:00
|
|
|
unsigned int mac2 = _cipher->reduceMac16(
|
2013-10-21 07:38:27 +02:00
|
|
|
_cipher->MAC_64(&tmpBuf.at(2), decodedStreamLen, iv));
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (mac2 != mac) {
|
|
|
|
LOG(INFO) << "checksum mismatch: expected " << mac << ", got " << mac2
|
|
|
|
<< " on decode of " << finalSize << " bytes";
|
|
|
|
throw Error("checksum mismatch in filename decode");
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-21 07:38:27 +02:00
|
|
|
return string(reinterpret_cast<char*>(&tmpBuf.at(2)), finalSize);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
bool BlockNameIO::Enabled() { return true; }
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-03-05 07:29:58 +01:00
|
|
|
} // namespace encfs
|