rework NameIO interface to use string instead of C-strings, avoid leaking memory if error occurs

git-svn-id: http://encfs.googlecode.com/svn/trunk@123 db9cf616-1c43-0410-9cb8-a902689de0d6
This commit is contained in:
Valient Gough 2013-10-21 05:38:27 +00:00
parent 88f041a0b2
commit d1a9ccdd19
18 changed files with 265 additions and 306 deletions

View File

@ -27,7 +27,10 @@
namespace encfs {
std::ostream &operator<<(std::ostream &out, const Interface &iface) {
using std::string;
using std::ostream;
ostream &operator<<(ostream &out, const Interface &iface) {
out << iface.name() << "(" << iface.major() << ":" << iface.minor() << ":"
<< iface.age() << ")";
return out;
@ -42,7 +45,7 @@ bool implements(const Interface &A, const Interface &B) {
return (currentDiff >= 0 && currentDiff <= (int)A.age());
}
Interface makeInterface(const char *name, int major, int minor, int age) {
Interface makeInterface(const string &name, int major, int minor, int age) {
Interface iface;
iface.set_name(name);
iface.set_major(major);

View File

@ -32,7 +32,7 @@ namespace encfs {
// compatibility. Even if A.implements(B) is true, B > A may also be
// true, meaning B is a newer revision of the interface then A.
bool implements(const Interface &a, const Interface &b);
Interface makeInterface(const char *name, int major, int minor, int age);
Interface makeInterface(const std::string &name, int major, int minor, int age);
// Read operation
class ConfigVar;

View File

@ -132,11 +132,9 @@ static const byte Ascii2B64Table[] =
" 01 23456789:; ";
// 0123456789 123456789 123456789 123456789 123456789 123456789 1234
// 0 1 2 3 4 5 6
void AsciiToB64(byte *in, int length) { return AsciiToB64(in, in, length); }
void AsciiToB64(byte *out, const byte *in, int length) {
void AsciiToB64(byte *buf, int length) {
while (length--) {
byte ch = *in++;
byte ch = *buf;
if (ch >= 'A') {
if (ch >= 'a')
ch += 38 - 'a';
@ -145,7 +143,7 @@ void AsciiToB64(byte *out, const byte *in, int length) {
} else
ch = Ascii2B64Table[ch] - '0';
*out++ = ch;
*buf++ = ch;
}
}
@ -161,18 +159,16 @@ void B32ToAscii(byte *buf, int len) {
}
}
void AsciiToB32(byte *in, int length) { return AsciiToB32(in, in, length); }
void AsciiToB32(byte *out, const byte *in, int length) {
void AsciiToB32(byte *buf, int length) {
while (length--) {
byte ch = *in++;
byte ch = *buf;
int lch = toupper(ch);
if (lch >= 'A')
lch -= 'A';
else
lch += 26 - '2';
*out++ = (byte)lch;
*buf++ = (byte)lch;
}
}

View File

@ -54,19 +54,22 @@ void changeBase2Inline(byte *buf, int srcLength, int srcPow2, int dst2Pow,
bool outputPartialLastByte);
// inplace translation from values [0,2^6] => base64 ASCII
// This is a nonstandard B64 encoding, which uses ',' and '-' as
// non-alphanumeric chars.
void B64ToAscii(byte *buf, int length);
// inplace translation from values [0,2^5] => base32 ASCII
void B32ToAscii(byte *buf, int length);
// inplace translation from values base64 ASCII => [0,2^6]
// This is a nonstandard B64 encoding, which uses ',' and '-' as
// non-alphanumeric chars.
void AsciiToB64(byte *buf, int length);
void AsciiToB64(byte *out, const byte *in, int length);
// inplace translation from values base32 ASCII => [0,2^5]
void AsciiToB32(byte *buf, int length);
void AsciiToB32(byte *out, const byte *in, int length);
// Decode B64 standard into the output array.
// Decode standard B64 into the output array.
// Used only to decode legacy Boost XML serialized config format.
// The output size must be at least B64ToB256Bytes(inputLen).
bool B64StandardDecode(byte *out, const byte *in, int inputLen);

View File

@ -26,10 +26,15 @@
#include "cipher/CipherV1.h"
#include <cstring>
#include <vector>
#include <glog/logging.h>
namespace encfs {
using std::string;
using std::vector;
static shared_ptr<NameIO> NewBlockNameIO(const Interface &iface,
const shared_ptr<CipherV1> &cipher) {
return shared_ptr<NameIO>(new BlockNameIO(iface, cipher, false));
@ -67,13 +72,13 @@ static bool BlockIO32_registered = NameIO::Register(
- 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
changes that to a 1 in 2^64 chance..
reduces to a 1 in 2^64 chance..
- Version 4.0 adds support for base32, creating names more suitable for
- Version 4.0 adds support for base32, creating names better suited to
case-insensitive filesystems (eg Mac).
*/
Interface BlockNameIO::CurrentInterface(bool caseSensitive) {
// implement major version 4 plus support for two prior versions
// implement major version 4 plus support for prior versions
if (caseSensitive)
return makeInterface("nameio/block32", 4, 0, 2);
else
@ -113,16 +118,20 @@ int BlockNameIO::maxDecodedNameLen(int encodedNameLen) const {
return decLen256 - 2; // 2 checksum bytes removed..
}
int BlockNameIO::encodeName(const char *plaintextName, int length, uint64_t *iv,
char *encodedName) const {
// copy the data into the encoding buffer..
memcpy(encodedName + 2, plaintextName, length);
string BlockNameIO::encodeName(const string &plaintextName,
uint64_t *iv) const {
int length = plaintextName.length();
// Pad encryption buffer to block boundary..
int padding = _bs - length % _bs;
if (padding == 0) padding = _bs; // padding a full extra block!
int encodedStreamLen = length + 2 + padding;
int encLen = _caseSensitive ? B256ToB32Bytes(encodedStreamLen)
: B256ToB64Bytes(encodedStreamLen);
memset(encodedName + length + 2, (unsigned char)padding, padding);
vector<byte> tmpBuf(encLen);
// copy the data into the encoding buffer..
memcpy(tmpBuf.data() + 2, plaintextName.data(), length);
memset(tmpBuf.data() + length + 2, (unsigned char)padding, padding);
// store the IV before it is modified by the MAC call.
uint64_t tmpIV = 0;
@ -130,38 +139,29 @@ int BlockNameIO::encodeName(const char *plaintextName, int length, uint64_t *iv,
// include padding in MAC computation
unsigned int mac = _cipher->reduceMac16(
_cipher->MAC_64((unsigned char *)encodedName + 2, length + padding, iv));
_cipher->MAC_64(tmpBuf.data() + 2, length + padding, iv));
tmpIV ^= (uint64_t)mac;
// add checksum bytes
encodedName[0] = (mac >> 8) & 0xff;
encodedName[1] = (mac) & 0xff;
tmpBuf[0] = (mac >> 8) & 0xff;
tmpBuf[1] = (mac) & 0xff;
_cipher->blockEncode((unsigned char *)encodedName + 2, length + padding,
(uint64_t)mac ^ tmpIV);
// convert to base 64 ascii
int encodedStreamLen = length + 2 + padding;
int encLen;
_cipher->blockEncode(tmpBuf.data() + 2, length + padding, tmpIV);
// convert to base 32 or 64 ascii
if (_caseSensitive) {
encLen = B256ToB32Bytes(encodedStreamLen);
changeBase2Inline((unsigned char *)encodedName, encodedStreamLen, 8, 5,
true);
B32ToAscii((unsigned char *)encodedName, encLen);
changeBase2Inline(tmpBuf.data(), encodedStreamLen, 8, 5, true);
B32ToAscii(tmpBuf.data(), encLen);
} else {
encLen = B256ToB64Bytes(encodedStreamLen);
changeBase2Inline((unsigned char *)encodedName, encodedStreamLen, 8, 6,
true);
B64ToAscii((unsigned char *)encodedName, encLen);
changeBase2Inline(tmpBuf.data(), encodedStreamLen, 8, 6, true);
B64ToAscii(tmpBuf.data(), encLen);
}
return encLen;
return string(reinterpret_cast<char*>(tmpBuf.data()), encLen);
}
int BlockNameIO::decodeName(const char *encodedName, int length, uint64_t *iv,
char *plaintextName) const {
string BlockNameIO::decodeName(const string &encodedName, uint64_t *iv) const {
int length = encodedName.length();
int decLen256 =
_caseSensitive ? B32ToB256Bytes(length) : B64ToB256Bytes(length);
int decodedStreamLen = decLen256 - 2;
@ -169,29 +169,30 @@ int BlockNameIO::decodeName(const char *encodedName, int length, uint64_t *iv,
// don't bother trying to decode files which are too small
if (decodedStreamLen < _bs) throw Error("Filename too small to decode");
BUFFER_INIT(tmpBuf, 32, (unsigned int)length);
vector<byte> tmpBuf(length, 0);
memcpy(tmpBuf.data(), encodedName.data(), length);
// decode into tmpBuf,
if (_caseSensitive) {
AsciiToB32((unsigned char *)tmpBuf, (unsigned char *)encodedName, length);
changeBase2Inline((unsigned char *)tmpBuf, length, 5, 8, false);
AsciiToB32(tmpBuf.data(), length);
changeBase2Inline(tmpBuf.data(), length, 5, 8, false);
} else {
AsciiToB64((unsigned char *)tmpBuf, (unsigned char *)encodedName, length);
changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false);
AsciiToB64(tmpBuf.data(), length);
changeBase2Inline(tmpBuf.data(), length, 6, 8, false);
}
// pull out the header information
unsigned int mac = ((unsigned int)((unsigned char)tmpBuf[0])) << 8 |
((unsigned int)((unsigned char)tmpBuf[1]));
unsigned int mac = ((unsigned int)tmpBuf[0]) << 8 |
((unsigned int)tmpBuf[1]);
uint64_t tmpIV = 0;
if (iv && _interface >= 3) tmpIV = *iv;
tmpIV ^= (uint64_t)mac;
_cipher->blockDecode((unsigned char *)tmpBuf + 2, decodedStreamLen,
(uint64_t)mac ^ tmpIV);
_cipher->blockDecode(&tmpBuf.at(2), decodedStreamLen, tmpIV);
// find out true string length
int padding = (unsigned char)tmpBuf[2 + decodedStreamLen - 1];
int padding = tmpBuf[2 + decodedStreamLen - 1];
int finalSize = decodedStreamLen - padding;
// might happen if there is an error decoding..
@ -201,15 +202,9 @@ int BlockNameIO::decodeName(const char *encodedName, int length, uint64_t *iv,
throw Error("invalid padding size");
}
// copy out the result..
memcpy(plaintextName, tmpBuf + 2, finalSize);
plaintextName[finalSize] = '\0';
// check the mac
unsigned int mac2 = _cipher->reduceMac16(
_cipher->MAC_64((const unsigned char *)tmpBuf + 2, decodedStreamLen, iv));
BUFFER_RESET(tmpBuf);
_cipher->MAC_64(&tmpBuf.at(2), decodedStreamLen, iv));
if (mac2 != mac) {
LOG(INFO) << "checksum mismatch: expected " << mac << ", got " << mac2
@ -217,7 +212,7 @@ int BlockNameIO::decodeName(const char *encodedName, int length, uint64_t *iv,
throw Error("checksum mismatch in filename decode");
}
return finalSize;
return string(reinterpret_cast<char*>(&tmpBuf.at(2)), finalSize);
}
bool BlockNameIO::Enabled() { return true; }

View File

@ -24,6 +24,7 @@
#include "fs/NameIO.h"
#include <memory>
#include <vector>
namespace encfs {
@ -42,19 +43,19 @@ class BlockNameIO : public NameIO {
bool caseSensitiveEncoding = false);
virtual ~BlockNameIO();
virtual Interface interface() const;
virtual Interface interface() const override;
virtual int maxEncodedNameLen(int plaintextNameLen) const;
virtual int maxDecodedNameLen(int encodedNameLen) const;
virtual int maxEncodedNameLen(int plaintextNameLen) const override;
virtual int maxDecodedNameLen(int encodedNameLen) const override;
// hack to help with static builds
static bool Enabled();
protected:
virtual int encodeName(const char *plaintextName, int length, uint64_t *iv,
char *encodedName) const;
virtual int decodeName(const char *encodedName, int length, uint64_t *iv,
char *plaintextName) const;
virtual std::string encodeName(const std::string &plaintextName,
uint64_t *iv) const override;
virtual std::string decodeName(const std::string &encodedName,
uint64_t *iv) const override;
private:
int _interface;

View File

@ -41,12 +41,14 @@ if (GTEST_FOUND)
link_directories (${PROJECT_BINARY_DIR}/cipher)
include_directories (${GTEST_INCLUDE_DIR})
file (GLOB TEST_FILES "*_test.cpp")
add_executable (fs-tests
MemBlockFileIO.cpp
MemFileIO.cpp
testing.cpp
test_IO.cpp
test_BlockIO.cpp
${TEST_FILES}
)
target_link_libraries (fs-tests
@ -58,5 +60,5 @@ if (GTEST_FOUND)
)
add_test (FSTests fs-tests)
GTEST_ADD_TESTS (fs-tests "${FSTestArgs}" test_IO.cpp test_BlockIO.cpp)
GTEST_ADD_TESTS (fs-tests "${FSTestArgs}" ${TEST_FILES})
endif (GTEST_FOUND)

View File

@ -288,13 +288,7 @@ string DirNode::plainPath(const char *cipherPath_) {
if (!strncmp(cipherPath_, rootDir.c_str(), rootDir.length())) {
return naming->decodePath(cipherPath_ + rootDir.length());
} else {
if (cipherPath_[0] == '+') {
// decode as fully qualified path
return string("/") +
naming->decodeName(cipherPath_ + 1, strlen(cipherPath_ + 1));
} else {
return naming->decodePath(cipherPath_);
}
return naming->decodePath(cipherPath_);
}
}
catch (Error &err) {
@ -305,13 +299,7 @@ string DirNode::plainPath(const char *cipherPath_) {
string DirNode::relativeCipherPath(const char *plaintextPath) {
try {
if (plaintextPath[0] == '/') {
// mark with '+' to indicate special decoding..
return string("+") +
naming->encodeName(plaintextPath + 1, strlen(plaintextPath + 1));
} else {
return naming->encodePath(plaintextPath);
}
return naming->encodePath(plaintextPath);
}
catch (Error &err) {
LOG(ERROR) << "encode err: " << err.what();

View File

@ -24,27 +24,28 @@
#include <glog/logging.h>
#include <map>
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
// for static build. Need to reference the modules which are registered at
// run-time, to ensure that the linker doesn't optimize them away.
#include <iostream>
#include "fs/BlockNameIO.h"
#include "fs/StreamNameIO.h"
#include "fs/NullNameIO.h"
using std::cerr;
using std::list;
using std::make_pair;
using std::multimap;
using std::string;
using std::vector;
namespace encfs {
#define REF_MODULE(TYPE) \
do { \
if (!TYPE::Enabled()) cerr << "referenceModule: should never happen\n"; \
#define REF_MODULE(TYPE) \
do { \
CHECK(TYPE::Enabled()) << "referenceModule: should never happen"; \
} while (0)
static void AddSymbolReferences() {
@ -62,7 +63,7 @@ struct NameIOAlg {
};
typedef multimap<string, NameIOAlg> NameIOMap_t;
static NameIOMap_t *gNameIOMap = 0;
static NameIOMap_t *gNameIOMap = nullptr;
list<NameIO::Algorithm> NameIO::GetAlgorithmList(bool includeHidden) {
AddSymbolReferences();
@ -145,136 +146,84 @@ void NameIO::setReverseEncryption(bool enable) { reverseEncryption = enable; }
bool NameIO::getReverseEncryption() const { return reverseEncryption; }
std::string NameIO::recodePath(const char *path,
int (NameIO::*_length)(int) const,
int (NameIO::*_code)(const char *, int,
uint64_t *, char *) const,
uint64_t *iv) const {
string NameIO::recodePath(const string &path, int (NameIO::*_length)(int) const,
string (NameIO::*_code)(const string &,
uint64_t *) const,
uint64_t *iv) const {
string output;
while (*path) {
if (*path == '/') {
if (!output.empty()) // don't start the string with '/'
output += '/';
++path;
} else {
bool isDotFile = (*path == '.');
const char *next = strchr(path, '/');
int len = next ? next - path : strlen(path);
for (auto it = path.begin(); it != path.end();) {
bool isDotFile = (*it == '.');
auto next = std::find(it, path.end(), '/');
int len = std::distance(it, next);
// at this point we know that len > 0
if (isDotFile && (path[len - 1] == '.') && (len <= 2)) {
if (*it == '/') {
// don't start the string with '/'
output += output.empty() ? '+' : '/';
len = 1;
} else if (*it == '+' && output.empty()) {
output += '/';
len = 1;
} else if (isDotFile && (len <= 2) && (it[len - 1] == '.')) {
output.append(len, '.'); // append [len] copies of '.'
path += len;
continue;
}
// figure out buffer sizes
} else {
int approxLen = (this->*_length)(len);
if (approxLen <= 0) throw Error("Filename too small to decode");
BUFFER_INIT(codeBuf, 32, (unsigned int)approxLen + 1);
// code the name
int codedLen = (this->*_code)(path, len, iv, codeBuf);
rAssert(codedLen <= approxLen);
rAssert(codeBuf[codedLen] == '\0');
path += len;
string input(it, it + len);
string coded = (this->*_code)(input, iv);
// append result to string
output += (char *)codeBuf;
BUFFER_RESET(codeBuf);
output.append(coded);
}
it += len;
}
return output;
}
std::string NameIO::encodePath(const char *plaintextPath) const {
string NameIO::encodePath(const string &plaintextPath) const {
uint64_t iv = 0;
return encodePath(plaintextPath, &iv);
}
std::string NameIO::decodePath(const char *cipherPath) const {
string NameIO::decodePath(const string &cipherPath) const {
uint64_t iv = 0;
return decodePath(cipherPath, &iv);
}
std::string NameIO::_encodePath(const char *plaintextPath, uint64_t *iv) const {
string NameIO::_encodePath(const string &plaintextPath, uint64_t *iv) const {
// if chaining is not enabled, then the iv pointer is not used..
if (!chainedNameIV) iv = 0;
if (!chainedNameIV) iv = nullptr;
return recodePath(plaintextPath, &NameIO::maxEncodedNameLen,
&NameIO::encodeName, iv);
}
std::string NameIO::_decodePath(const char *cipherPath, uint64_t *iv) const {
string NameIO::_decodePath(const string &cipherPath, uint64_t *iv) const {
// if chaining is not enabled, then the iv pointer is not used..
if (!chainedNameIV) iv = 0;
if (!chainedNameIV) iv = nullptr;
return recodePath(cipherPath, &NameIO::maxDecodedNameLen, &NameIO::decodeName,
iv);
}
std::string NameIO::encodePath(const char *path, uint64_t *iv) const {
string NameIO::encodePath(const string &path, uint64_t *iv) const {
return getReverseEncryption() ? _decodePath(path, iv) : _encodePath(path, iv);
}
std::string NameIO::decodePath(const char *path, uint64_t *iv) const {
string NameIO::decodePath(const string &path, uint64_t *iv) const {
return getReverseEncryption() ? _encodePath(path, iv) : _decodePath(path, iv);
}
int NameIO::encodeName(const char *input, int length, char *output) const {
return encodeName(input, length, (uint64_t *)0, output);
string NameIO::encodeName(const string &name) const {
return getReverseEncryption() ? decodeName(name, nullptr)
: encodeName(name, nullptr);
}
int NameIO::decodeName(const char *input, int length, char *output) const {
return decodeName(input, length, (uint64_t *)0, output);
}
std::string NameIO::_encodeName(const char *plaintextName, int length) const {
int approxLen = maxEncodedNameLen(length);
BUFFER_INIT(codeBuf, 32, (unsigned int)approxLen + 1);
// code the name
int codedLen = encodeName(plaintextName, length, 0, codeBuf);
rAssert(codedLen <= approxLen);
rAssert(codeBuf[codedLen] == '\0');
// append result to string
std::string result = (char *)codeBuf;
BUFFER_RESET(codeBuf);
return result;
}
std::string NameIO::_decodeName(const char *encodedName, int length) const {
int approxLen = maxDecodedNameLen(length);
BUFFER_INIT(codeBuf, 32, (unsigned int)approxLen + 1);
// code the name
int codedLen = decodeName(encodedName, length, 0, codeBuf);
rAssert(codedLen <= approxLen);
rAssert(codeBuf[codedLen] == '\0');
// append result to string
std::string result = (char *)codeBuf;
BUFFER_RESET(codeBuf);
return result;
}
std::string NameIO::encodeName(const char *path, int length) const {
return getReverseEncryption() ? _decodeName(path, length)
: _encodeName(path, length);
}
std::string NameIO::decodeName(const char *path, int length) const {
return getReverseEncryption() ? _encodeName(path, length)
: _decodeName(path, length);
string NameIO::decodeName(const string &name) const {
return getReverseEncryption() ? encodeName(name, nullptr)
: decodeName(name, nullptr);
}
} // namespace encfs

View File

@ -28,6 +28,7 @@
#include "base/Interface.h"
#include "base/shared_ptr.h"
#include "base/types.h"
namespace encfs {
@ -67,67 +68,39 @@ class NameIO {
void setReverseEncryption(bool enable);
bool getReverseEncryption() const;
std::string encodePath(const char *plaintextPath) const;
std::string decodePath(const char *encodedPath) const;
std::string encodePath(const std::string &plaintextPath) const;
std::string decodePath(const std::string &encodedPath) const;
std::string encodePath(const char *plaintextPath, uint64_t *iv) const;
std::string decodePath(const char *encodedPath, uint64_t *iv) const;
std::string encodePath(const std::string &plaintextPath, uint64_t *iv) const;
std::string decodePath(const std::string &encodedPath, uint64_t *iv) const;
virtual int maxEncodedNameLen(int plaintextNameLen) const = 0;
virtual int maxDecodedNameLen(int encodedNameLen) const = 0;
std::string encodeName(const char *plaintextName, int length) const;
std::string decodeName(const char *encodedName, int length) const;
std::string encodeName(const std::string &plaintextName) const;
std::string decodeName(const std::string &encodedName) const;
protected:
virtual int encodeName(const char *plaintextName, int length,
char *encodedName) const;
virtual int decodeName(const char *encodedName, int length,
char *plaintextName) const;
virtual int encodeName(const char *plaintextName, int length, uint64_t *iv,
char *encodedName) const = 0;
virtual int decodeName(const char *encodedName, int length, uint64_t *iv,
char *plaintextName) const = 0;
// Encode & decode methods implemented by derived classes.
virtual std::string encodeName(const std::string &name,
uint64_t *iv) const = 0;
virtual std::string decodeName(const std::string &name,
uint64_t *iv) const = 0;
private:
std::string recodePath(const char *path, int (NameIO::*codingLen)(int) const,
int (NameIO::*codingFunc)(const char *, int,
uint64_t *, char *) const,
std::string recodePath(const std::string &path,
int (NameIO::*codingLen)(int) const,
std::string (NameIO::*codingFunc)(const std::string &,
uint64_t *) const,
uint64_t *iv) const;
std::string _encodePath(const char *plaintextPath, uint64_t *iv) const;
std::string _decodePath(const char *encodedPath, uint64_t *iv) const;
std::string _encodeName(const char *plaintextName, int length) const;
std::string _decodeName(const char *encodedName, int length) const;
std::string _encodePath(const std::string &plaintextPath, uint64_t *iv) const;
std::string _decodePath(const std::string &encodedPath, uint64_t *iv) const;
bool chainedNameIV;
bool reverseEncryption;
};
/*
Helper macros for creating temporary buffers with an optimization that
below a given size (OptimizedSize) is allocated on the stack, and when a
larger size is requested it is allocated on the heap.
BUFFER_RESET should be called for the same name as BUFFER_INIT
*/
#define BUFFER_INIT(Name, OptimizedSize, Size) \
char Name##_Raw[OptimizedSize]; \
char *Name = Name##_Raw; \
if (sizeof(Name##_Raw) < Size) { \
Name = new char[Size]; \
} \
memset(Name, 0, Size)
#define BUFFER_RESET(Name) \
do { \
if (Name != Name##_Raw) { \
delete[] Name; \
Name = Name##_Raw; \
} \
} while (0)
} // namespace encfs
#endif

59
fs/NameIO_test.cpp Normal file
View File

@ -0,0 +1,59 @@
#include <gtest/gtest.h>
#include <string>
#include "cipher/CipherV1.h"
#include "fs/BlockNameIO.h"
#include "fs/NameIO.h"
#include "fs/NullNameIO.h"
#include "fs/StreamNameIO.h"
#include "fs/testing.h"
namespace {
using namespace encfs;
using std::string;
string TEST_PATHS[] = {"a/b/c/d/e", //
"/a/b", //
"/a", //
"/", //
"../../foo/bar", //
"./.encfs", //
"."};
TEST(NameIOTest, NameIO) {
NameIO::AlgorithmList algorithms = NameIO::GetAlgorithmList(true);
// Test all NameIO algorithms.
for (auto algorithm : algorithms) {
shared_ptr<CipherV1> cipher = CipherV1::New("AES", 256);
CipherKey key = cipher->newRandomKey();
cipher->setKey(key);
// Test all supported versions.
for (unsigned int version = algorithm.iface.major() - algorithm.iface.age();
version <= algorithm.iface.major(); ++version) {
Interface iface = makeInterface(algorithm.iface.name(), version, 0, 0);
SCOPED_TRACE(testing::Message() << "Testing " << iface.DebugString());
auto io = NameIO::New(iface, cipher);
// Check round-trip of test paths.
for (string path : TEST_PATHS) {
string encoded = io->encodePath(path);
string decoded = io->decodePath(encoded);
ASSERT_EQ(path, decoded);
}
// Try encoding names of various lengths.
for (int len = 1; len < 40; ++len) {
string name(len, 'A');
string encoded = io->encodeName(name);
string decoded = io->decodeName(encoded);
ASSERT_EQ(name, decoded);
}
}
}
}
} // namespace

View File

@ -23,9 +23,12 @@
#include "fs/NullNameIO.h"
#include <cstring>
#include <string>
namespace encfs {
using std::string;
static shared_ptr<NameIO> NewNNIO(const Interface &,
const shared_ptr<CipherV1> &) {
return shared_ptr<NameIO>(new NullNameIO());
@ -51,16 +54,12 @@ int NullNameIO::maxDecodedNameLen(int encodedNameLen) const {
return encodedNameLen;
}
int NullNameIO::encodeName(const char *plaintextName, int length, uint64_t *iv,
char *encodedName) const {
memcpy(encodedName, plaintextName, length);
return length;
string NullNameIO::encodeName(const string &plaintextName, uint64_t *iv) const {
return plaintextName;
}
int NullNameIO::decodeName(const char *encodedName, int length, uint64_t *iv,
char *plaintextName) const {
memcpy(plaintextName, encodedName, length);
return length;
string NullNameIO::decodeName(const string &encodedName, uint64_t *iv) const {
return encodedName;
}
bool NullNameIO::Enabled() { return true; }

View File

@ -33,19 +33,19 @@ class NullNameIO : public NameIO {
virtual ~NullNameIO();
virtual Interface interface() const;
virtual Interface interface() const override;
virtual int maxEncodedNameLen(int plaintextNameLen) const;
virtual int maxDecodedNameLen(int encodedNameLen) const;
virtual int maxEncodedNameLen(int plaintextNameLen) const override;
virtual int maxDecodedNameLen(int encodedNameLen) const override;
// hack to help with static builds
static bool Enabled();
protected:
virtual int encodeName(const char *plaintextName, int length, uint64_t *iv,
char *encodedName) const;
virtual int decodeName(const char *encodedName, int length, uint64_t *iv,
char *plaintextName) const;
virtual std::string encodeName(const std::string &plaintextName,
uint64_t *iv) const override;
virtual std::string decodeName(const std::string &encodedName,
uint64_t *iv) const override;
private:
};

View File

@ -27,9 +27,14 @@
#include <glog/logging.h>
#include <cstring>
#include <string>
#include <vector>
namespace encfs {
using std::string;
using std::vector;
static shared_ptr<NameIO> NewStreamNameIO(const Interface &iface,
const shared_ptr<CipherV1> &cipher) {
return shared_ptr<NameIO>(new StreamNameIO(iface, cipher));
@ -60,10 +65,12 @@ static bool StreamIO_registered = NameIO::Register(
bits.
- Version 2.1 adds support for version 0 for EncFS 0.x compatibility.
- Version 3.0 drops Encfs 0.x support.
*/
Interface StreamNameIO::CurrentInterface() {
// implement major version 2, 1, and 0
return makeInterface("nameio/stream", 2, 1, 2);
// implements support for version 3, 2, and 1.
return makeInterface("nameio/stream", 3, 0, 2);
}
StreamNameIO::StreamNameIO(const Interface &iface,
@ -84,92 +91,73 @@ int StreamNameIO::maxDecodedNameLen(int encodedStreamLen) const {
return decLen256 - 2;
}
int StreamNameIO::encodeName(const char *plaintextName, int length,
uint64_t *iv, char *encodedName) const {
string StreamNameIO::encodeName(const string &plaintextName,
uint64_t *iv) const {
uint64_t tmpIV = 0;
int length = plaintextName.length();
if (iv && _interface >= 2) tmpIV = *iv;
unsigned int mac = _cipher->reduceMac16(
_cipher->MAC_64((const byte *)plaintextName, length, iv));
unsigned int mac = _cipher->reduceMac16(_cipher->MAC_64(
reinterpret_cast<const byte *>(plaintextName.data()), length, iv));
tmpIV ^= (uint64_t)mac;
// add on checksum bytes
unsigned char *encodeBegin;
if (_interface >= 1) {
// current versions store the checksum at the beginning
encodedName[0] = (mac >> 8) & 0xff;
encodedName[1] = (mac) & 0xff;
encodeBegin = (unsigned char *)encodedName + 2;
} else {
// encfs 0.x stored checksums at the end.
encodedName[length] = (mac >> 8) & 0xff;
encodedName[length + 1] = (mac) & 0xff;
encodeBegin = (unsigned char *)encodedName;
}
// stream encode the plaintext bytes
memcpy(encodeBegin, plaintextName, length);
_cipher->streamEncode(encodeBegin, length, (uint64_t)mac ^ tmpIV);
// convert the entire thing to base 64 ascii..
int encodedStreamLen = length + 2;
int encLen64 = B256ToB64Bytes(encodedStreamLen);
changeBase2Inline((unsigned char *)encodedName, encodedStreamLen, 8, 6, true);
B64ToAscii((unsigned char *)encodedName, encLen64);
// add on checksum bytes
vector<byte> encoded(encLen64);
encoded[0] = static_cast<byte>((mac >> 8) & 0xff);
encoded[1] = static_cast<byte>((mac) & 0xff);
return encLen64;
// stream encode the plaintext bytes
memcpy(&encoded[2], plaintextName.data(), length);
_cipher->streamEncode(&encoded[2], length, tmpIV);
// convert the entire thing to base 64 ascii..
changeBase2Inline(encoded.data(), encodedStreamLen, 8, 6, true);
B64ToAscii(encoded.data(), encLen64);
return string(encoded.begin(), encoded.end());
}
int StreamNameIO::decodeName(const char *encodedName, int length, uint64_t *iv,
char *plaintextName) const {
string StreamNameIO::decodeName(const string &encodedName,
uint64_t *iv) const {
int length = encodedName.length();
rAssert(length > 2);
int decLen256 = B64ToB256Bytes(length);
int decodedStreamLen = decLen256 - 2;
if (decodedStreamLen <= 0) throw Error("Filename too small to decode");
BUFFER_INIT(tmpBuf, 32, (unsigned int)length);
vector<byte> tmpBuf(length, 0);
// decode into tmpBuf, because this step produces more data then we can fit
// into the result buffer..
AsciiToB64((unsigned char *)tmpBuf, (unsigned char *)encodedName, length);
changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false);
memcpy(tmpBuf.data(), encodedName.data(), length);
AsciiToB64(tmpBuf.data(), length);
changeBase2Inline(tmpBuf.data(), length, 6, 8, false);
// pull out the checksum value which is used as an initialization vector
uint64_t tmpIV = 0;
unsigned int mac;
if (_interface >= 1) {
// current versions store the checksum at the beginning
mac = ((unsigned int)((unsigned char)tmpBuf[0])) << 8 |
((unsigned int)((unsigned char)tmpBuf[1]));
unsigned int mac = ((unsigned int)tmpBuf[0]) << 8 | ((unsigned int)tmpBuf[1]);
// version 2 adds support for IV chaining..
if (iv && _interface >= 2) tmpIV = *iv;
// version 2 adds support for IV chaining..
if (iv && _interface >= 2) tmpIV = *iv;
memcpy(plaintextName, tmpBuf + 2, decodedStreamLen);
} else {
// encfs 0.x stored checksums at the end.
mac = ((unsigned int)((unsigned char)tmpBuf[decodedStreamLen])) << 8 |
((unsigned int)((unsigned char)tmpBuf[decodedStreamLen + 1]));
memcpy(plaintextName, tmpBuf, decodedStreamLen);
}
_cipher->streamDecode((unsigned char *)plaintextName, decodedStreamLen,
(uint64_t)mac ^ tmpIV);
tmpIV ^= (uint64_t)mac;
_cipher->streamDecode(&tmpBuf.at(2), decodedStreamLen, tmpIV);
// compute MAC to check with stored value
unsigned int mac2 = _cipher->reduceMac16(
_cipher->MAC_64((const byte *)plaintextName, decodedStreamLen, iv));
_cipher->MAC_64(&tmpBuf.at(2), decodedStreamLen, iv));
BUFFER_RESET(tmpBuf);
if (mac2 != mac) {
VLOG(1) << "checksum mismatch: expected " << mac << ", got " << mac2
<< "on decode of " << decodedStreamLen << " bytes";
throw Error("checksum mismatch in filename decode");
}
return decodedStreamLen;
return string(reinterpret_cast<char*>(&tmpBuf.at(2)), decodedStreamLen);
}
bool StreamNameIO::Enabled() { return true; }

View File

@ -34,19 +34,19 @@ class StreamNameIO : public NameIO {
StreamNameIO(const Interface &iface, const shared_ptr<CipherV1> &cipher);
virtual ~StreamNameIO();
virtual Interface interface() const;
virtual Interface interface() const override;
virtual int maxEncodedNameLen(int plaintextNameLen) const;
virtual int maxDecodedNameLen(int encodedNameLen) const;
virtual int maxEncodedNameLen(int plaintextNameLen) const override;
virtual int maxDecodedNameLen(int encodedNameLen) const override;
// hack to help with static builds
static bool Enabled();
protected:
virtual int encodeName(const char *plaintextName, int length, uint64_t *iv,
char *encodedName) const;
virtual int decodeName(const char *encodedName, int length, uint64_t *iv,
char *plaintextName) const;
virtual std::string encodeName(const std::string &plaintextName,
uint64_t *iv) const override;
virtual std::string decodeName(const std::string &encodedName,
uint64_t *iv) const override;
private:
int _interface;

View File

@ -24,6 +24,7 @@
#include <list>
#include <string>
#include <glog/logging.h>
#include <gtest/gtest.h>
#include "cipher/CipherV1.h"
@ -188,6 +189,8 @@ void comparisonTest(FSConfigPtr& cfg, FileIO* a, FileIO* b) {
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
google::InitGoogleLogging(argv[0]);
return RUN_ALL_TESTS();
}