mirror of
https://github.com/vgough/encfs.git
synced 2025-01-19 12:28:11 +01:00
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:
parent
88f041a0b2
commit
d1a9ccdd19
@ -27,7 +27,10 @@
|
|||||||
|
|
||||||
namespace encfs {
|
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() << ":"
|
out << iface.name() << "(" << iface.major() << ":" << iface.minor() << ":"
|
||||||
<< iface.age() << ")";
|
<< iface.age() << ")";
|
||||||
return out;
|
return out;
|
||||||
@ -42,7 +45,7 @@ bool implements(const Interface &A, const Interface &B) {
|
|||||||
return (currentDiff >= 0 && currentDiff <= (int)A.age());
|
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;
|
Interface iface;
|
||||||
iface.set_name(name);
|
iface.set_name(name);
|
||||||
iface.set_major(major);
|
iface.set_major(major);
|
||||||
|
@ -32,7 +32,7 @@ namespace encfs {
|
|||||||
// compatibility. Even if A.implements(B) is true, B > A may also be
|
// 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.
|
// true, meaning B is a newer revision of the interface then A.
|
||||||
bool implements(const Interface &a, const Interface &b);
|
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
|
// Read operation
|
||||||
class ConfigVar;
|
class ConfigVar;
|
||||||
|
@ -132,11 +132,9 @@ static const byte Ascii2B64Table[] =
|
|||||||
" 01 23456789:; ";
|
" 01 23456789:; ";
|
||||||
// 0123456789 123456789 123456789 123456789 123456789 123456789 1234
|
// 0123456789 123456789 123456789 123456789 123456789 123456789 1234
|
||||||
// 0 1 2 3 4 5 6
|
// 0 1 2 3 4 5 6
|
||||||
void AsciiToB64(byte *in, int length) { return AsciiToB64(in, in, length); }
|
void AsciiToB64(byte *buf, int length) {
|
||||||
|
|
||||||
void AsciiToB64(byte *out, const byte *in, int length) {
|
|
||||||
while (length--) {
|
while (length--) {
|
||||||
byte ch = *in++;
|
byte ch = *buf;
|
||||||
if (ch >= 'A') {
|
if (ch >= 'A') {
|
||||||
if (ch >= 'a')
|
if (ch >= 'a')
|
||||||
ch += 38 - 'a';
|
ch += 38 - 'a';
|
||||||
@ -145,7 +143,7 @@ void AsciiToB64(byte *out, const byte *in, int length) {
|
|||||||
} else
|
} else
|
||||||
ch = Ascii2B64Table[ch] - '0';
|
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 *buf, int length) {
|
||||||
|
|
||||||
void AsciiToB32(byte *out, const byte *in, int length) {
|
|
||||||
while (length--) {
|
while (length--) {
|
||||||
byte ch = *in++;
|
byte ch = *buf;
|
||||||
int lch = toupper(ch);
|
int lch = toupper(ch);
|
||||||
if (lch >= 'A')
|
if (lch >= 'A')
|
||||||
lch -= 'A';
|
lch -= 'A';
|
||||||
else
|
else
|
||||||
lch += 26 - '2';
|
lch += 26 - '2';
|
||||||
|
|
||||||
*out++ = (byte)lch;
|
*buf++ = (byte)lch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,19 +54,22 @@ void changeBase2Inline(byte *buf, int srcLength, int srcPow2, int dst2Pow,
|
|||||||
bool outputPartialLastByte);
|
bool outputPartialLastByte);
|
||||||
|
|
||||||
// inplace translation from values [0,2^6] => base64 ASCII
|
// 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);
|
void B64ToAscii(byte *buf, int length);
|
||||||
// inplace translation from values [0,2^5] => base32 ASCII
|
// inplace translation from values [0,2^5] => base32 ASCII
|
||||||
void B32ToAscii(byte *buf, int length);
|
void B32ToAscii(byte *buf, int length);
|
||||||
|
|
||||||
// inplace translation from values base64 ASCII => [0,2^6]
|
// 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 *buf, int length);
|
||||||
void AsciiToB64(byte *out, const byte *in, int length);
|
|
||||||
|
|
||||||
// inplace translation from values base32 ASCII => [0,2^5]
|
// inplace translation from values base32 ASCII => [0,2^5]
|
||||||
void AsciiToB32(byte *buf, int length);
|
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).
|
// The output size must be at least B64ToB256Bytes(inputLen).
|
||||||
bool B64StandardDecode(byte *out, const byte *in, int inputLen);
|
bool B64StandardDecode(byte *out, const byte *in, int inputLen);
|
||||||
|
|
||||||
|
@ -26,10 +26,15 @@
|
|||||||
#include "cipher/CipherV1.h"
|
#include "cipher/CipherV1.h"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
|
|
||||||
namespace encfs {
|
namespace encfs {
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
static shared_ptr<NameIO> NewBlockNameIO(const Interface &iface,
|
static shared_ptr<NameIO> NewBlockNameIO(const Interface &iface,
|
||||||
const shared_ptr<CipherV1> &cipher) {
|
const shared_ptr<CipherV1> &cipher) {
|
||||||
return shared_ptr<NameIO>(new BlockNameIO(iface, cipher, false));
|
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.
|
- 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
|
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
|
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).
|
case-insensitive filesystems (eg Mac).
|
||||||
*/
|
*/
|
||||||
Interface BlockNameIO::CurrentInterface(bool caseSensitive) {
|
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)
|
if (caseSensitive)
|
||||||
return makeInterface("nameio/block32", 4, 0, 2);
|
return makeInterface("nameio/block32", 4, 0, 2);
|
||||||
else
|
else
|
||||||
@ -113,16 +118,20 @@ int BlockNameIO::maxDecodedNameLen(int encodedNameLen) const {
|
|||||||
return decLen256 - 2; // 2 checksum bytes removed..
|
return decLen256 - 2; // 2 checksum bytes removed..
|
||||||
}
|
}
|
||||||
|
|
||||||
int BlockNameIO::encodeName(const char *plaintextName, int length, uint64_t *iv,
|
string BlockNameIO::encodeName(const string &plaintextName,
|
||||||
char *encodedName) const {
|
uint64_t *iv) const {
|
||||||
// copy the data into the encoding buffer..
|
int length = plaintextName.length();
|
||||||
memcpy(encodedName + 2, plaintextName, length);
|
|
||||||
|
|
||||||
// Pad encryption buffer to block boundary..
|
// Pad encryption buffer to block boundary..
|
||||||
int padding = _bs - length % _bs;
|
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.
|
// store the IV before it is modified by the MAC call.
|
||||||
uint64_t tmpIV = 0;
|
uint64_t tmpIV = 0;
|
||||||
@ -130,38 +139,29 @@ int BlockNameIO::encodeName(const char *plaintextName, int length, uint64_t *iv,
|
|||||||
|
|
||||||
// include padding in MAC computation
|
// include padding in MAC computation
|
||||||
unsigned int mac = _cipher->reduceMac16(
|
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
|
// add checksum bytes
|
||||||
encodedName[0] = (mac >> 8) & 0xff;
|
tmpBuf[0] = (mac >> 8) & 0xff;
|
||||||
encodedName[1] = (mac) & 0xff;
|
tmpBuf[1] = (mac) & 0xff;
|
||||||
|
|
||||||
_cipher->blockEncode((unsigned char *)encodedName + 2, length + padding,
|
_cipher->blockEncode(tmpBuf.data() + 2, length + padding, tmpIV);
|
||||||
(uint64_t)mac ^ tmpIV);
|
|
||||||
|
|
||||||
// convert to base 64 ascii
|
|
||||||
int encodedStreamLen = length + 2 + padding;
|
|
||||||
int encLen;
|
|
||||||
|
|
||||||
|
// convert to base 32 or 64 ascii
|
||||||
if (_caseSensitive) {
|
if (_caseSensitive) {
|
||||||
encLen = B256ToB32Bytes(encodedStreamLen);
|
changeBase2Inline(tmpBuf.data(), encodedStreamLen, 8, 5, true);
|
||||||
|
B32ToAscii(tmpBuf.data(), encLen);
|
||||||
changeBase2Inline((unsigned char *)encodedName, encodedStreamLen, 8, 5,
|
|
||||||
true);
|
|
||||||
B32ToAscii((unsigned char *)encodedName, encLen);
|
|
||||||
} else {
|
} else {
|
||||||
encLen = B256ToB64Bytes(encodedStreamLen);
|
changeBase2Inline(tmpBuf.data(), encodedStreamLen, 8, 6, true);
|
||||||
|
B64ToAscii(tmpBuf.data(), encLen);
|
||||||
changeBase2Inline((unsigned char *)encodedName, encodedStreamLen, 8, 6,
|
|
||||||
true);
|
|
||||||
B64ToAscii((unsigned char *)encodedName, encLen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return encLen;
|
return string(reinterpret_cast<char*>(tmpBuf.data()), encLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
int BlockNameIO::decodeName(const char *encodedName, int length, uint64_t *iv,
|
string BlockNameIO::decodeName(const string &encodedName, uint64_t *iv) const {
|
||||||
char *plaintextName) const {
|
int length = encodedName.length();
|
||||||
int decLen256 =
|
int decLen256 =
|
||||||
_caseSensitive ? B32ToB256Bytes(length) : B64ToB256Bytes(length);
|
_caseSensitive ? B32ToB256Bytes(length) : B64ToB256Bytes(length);
|
||||||
int decodedStreamLen = decLen256 - 2;
|
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
|
// don't bother trying to decode files which are too small
|
||||||
if (decodedStreamLen < _bs) throw Error("Filename too small to decode");
|
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,
|
// decode into tmpBuf,
|
||||||
if (_caseSensitive) {
|
if (_caseSensitive) {
|
||||||
AsciiToB32((unsigned char *)tmpBuf, (unsigned char *)encodedName, length);
|
AsciiToB32(tmpBuf.data(), length);
|
||||||
changeBase2Inline((unsigned char *)tmpBuf, length, 5, 8, false);
|
changeBase2Inline(tmpBuf.data(), length, 5, 8, false);
|
||||||
} else {
|
} else {
|
||||||
AsciiToB64((unsigned char *)tmpBuf, (unsigned char *)encodedName, length);
|
AsciiToB64(tmpBuf.data(), length);
|
||||||
changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false);
|
changeBase2Inline(tmpBuf.data(), length, 6, 8, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pull out the header information
|
// pull out the header information
|
||||||
unsigned int mac = ((unsigned int)((unsigned char)tmpBuf[0])) << 8 |
|
unsigned int mac = ((unsigned int)tmpBuf[0]) << 8 |
|
||||||
((unsigned int)((unsigned char)tmpBuf[1]));
|
((unsigned int)tmpBuf[1]);
|
||||||
|
|
||||||
uint64_t tmpIV = 0;
|
uint64_t tmpIV = 0;
|
||||||
if (iv && _interface >= 3) tmpIV = *iv;
|
if (iv && _interface >= 3) tmpIV = *iv;
|
||||||
|
tmpIV ^= (uint64_t)mac;
|
||||||
|
|
||||||
_cipher->blockDecode((unsigned char *)tmpBuf + 2, decodedStreamLen,
|
_cipher->blockDecode(&tmpBuf.at(2), decodedStreamLen, tmpIV);
|
||||||
(uint64_t)mac ^ tmpIV);
|
|
||||||
|
|
||||||
// find out true string length
|
// find out true string length
|
||||||
int padding = (unsigned char)tmpBuf[2 + decodedStreamLen - 1];
|
int padding = tmpBuf[2 + decodedStreamLen - 1];
|
||||||
int finalSize = decodedStreamLen - padding;
|
int finalSize = decodedStreamLen - padding;
|
||||||
|
|
||||||
// might happen if there is an error decoding..
|
// 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");
|
throw Error("invalid padding size");
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy out the result..
|
|
||||||
memcpy(plaintextName, tmpBuf + 2, finalSize);
|
|
||||||
plaintextName[finalSize] = '\0';
|
|
||||||
|
|
||||||
// check the mac
|
// check the mac
|
||||||
unsigned int mac2 = _cipher->reduceMac16(
|
unsigned int mac2 = _cipher->reduceMac16(
|
||||||
_cipher->MAC_64((const unsigned char *)tmpBuf + 2, decodedStreamLen, iv));
|
_cipher->MAC_64(&tmpBuf.at(2), decodedStreamLen, iv));
|
||||||
|
|
||||||
BUFFER_RESET(tmpBuf);
|
|
||||||
|
|
||||||
if (mac2 != mac) {
|
if (mac2 != mac) {
|
||||||
LOG(INFO) << "checksum mismatch: expected " << mac << ", got " << mac2
|
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");
|
throw Error("checksum mismatch in filename decode");
|
||||||
}
|
}
|
||||||
|
|
||||||
return finalSize;
|
return string(reinterpret_cast<char*>(&tmpBuf.at(2)), finalSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BlockNameIO::Enabled() { return true; }
|
bool BlockNameIO::Enabled() { return true; }
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "fs/NameIO.h"
|
#include "fs/NameIO.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace encfs {
|
namespace encfs {
|
||||||
|
|
||||||
@ -42,19 +43,19 @@ class BlockNameIO : public NameIO {
|
|||||||
bool caseSensitiveEncoding = false);
|
bool caseSensitiveEncoding = false);
|
||||||
virtual ~BlockNameIO();
|
virtual ~BlockNameIO();
|
||||||
|
|
||||||
virtual Interface interface() const;
|
virtual Interface interface() const override;
|
||||||
|
|
||||||
virtual int maxEncodedNameLen(int plaintextNameLen) const;
|
virtual int maxEncodedNameLen(int plaintextNameLen) const override;
|
||||||
virtual int maxDecodedNameLen(int encodedNameLen) const;
|
virtual int maxDecodedNameLen(int encodedNameLen) const override;
|
||||||
|
|
||||||
// hack to help with static builds
|
// hack to help with static builds
|
||||||
static bool Enabled();
|
static bool Enabled();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual int encodeName(const char *plaintextName, int length, uint64_t *iv,
|
virtual std::string encodeName(const std::string &plaintextName,
|
||||||
char *encodedName) const;
|
uint64_t *iv) const override;
|
||||||
virtual int decodeName(const char *encodedName, int length, uint64_t *iv,
|
virtual std::string decodeName(const std::string &encodedName,
|
||||||
char *plaintextName) const;
|
uint64_t *iv) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _interface;
|
int _interface;
|
||||||
|
@ -41,12 +41,14 @@ if (GTEST_FOUND)
|
|||||||
link_directories (${PROJECT_BINARY_DIR}/cipher)
|
link_directories (${PROJECT_BINARY_DIR}/cipher)
|
||||||
|
|
||||||
include_directories (${GTEST_INCLUDE_DIR})
|
include_directories (${GTEST_INCLUDE_DIR})
|
||||||
|
|
||||||
|
file (GLOB TEST_FILES "*_test.cpp")
|
||||||
|
|
||||||
add_executable (fs-tests
|
add_executable (fs-tests
|
||||||
MemBlockFileIO.cpp
|
MemBlockFileIO.cpp
|
||||||
MemFileIO.cpp
|
MemFileIO.cpp
|
||||||
testing.cpp
|
testing.cpp
|
||||||
test_IO.cpp
|
${TEST_FILES}
|
||||||
test_BlockIO.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries (fs-tests
|
target_link_libraries (fs-tests
|
||||||
@ -58,5 +60,5 @@ if (GTEST_FOUND)
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_test (FSTests fs-tests)
|
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)
|
endif (GTEST_FOUND)
|
||||||
|
@ -288,13 +288,7 @@ string DirNode::plainPath(const char *cipherPath_) {
|
|||||||
if (!strncmp(cipherPath_, rootDir.c_str(), rootDir.length())) {
|
if (!strncmp(cipherPath_, rootDir.c_str(), rootDir.length())) {
|
||||||
return naming->decodePath(cipherPath_ + rootDir.length());
|
return naming->decodePath(cipherPath_ + rootDir.length());
|
||||||
} else {
|
} else {
|
||||||
if (cipherPath_[0] == '+') {
|
return naming->decodePath(cipherPath_);
|
||||||
// decode as fully qualified path
|
|
||||||
return string("/") +
|
|
||||||
naming->decodeName(cipherPath_ + 1, strlen(cipherPath_ + 1));
|
|
||||||
} else {
|
|
||||||
return naming->decodePath(cipherPath_);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Error &err) {
|
catch (Error &err) {
|
||||||
@ -305,13 +299,7 @@ string DirNode::plainPath(const char *cipherPath_) {
|
|||||||
|
|
||||||
string DirNode::relativeCipherPath(const char *plaintextPath) {
|
string DirNode::relativeCipherPath(const char *plaintextPath) {
|
||||||
try {
|
try {
|
||||||
if (plaintextPath[0] == '/') {
|
return naming->encodePath(plaintextPath);
|
||||||
// mark with '+' to indicate special decoding..
|
|
||||||
return string("+") +
|
|
||||||
naming->encodeName(plaintextPath + 1, strlen(plaintextPath + 1));
|
|
||||||
} else {
|
|
||||||
return naming->encodePath(plaintextPath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Error &err) {
|
catch (Error &err) {
|
||||||
LOG(ERROR) << "encode err: " << err.what();
|
LOG(ERROR) << "encode err: " << err.what();
|
||||||
|
139
fs/NameIO.cpp
139
fs/NameIO.cpp
@ -24,27 +24,28 @@
|
|||||||
|
|
||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
|
|
||||||
#include <map>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
// for static build. Need to reference the modules which are registered at
|
// for static build. Need to reference the modules which are registered at
|
||||||
// run-time, to ensure that the linker doesn't optimize them away.
|
// run-time, to ensure that the linker doesn't optimize them away.
|
||||||
#include <iostream>
|
|
||||||
#include "fs/BlockNameIO.h"
|
#include "fs/BlockNameIO.h"
|
||||||
#include "fs/StreamNameIO.h"
|
#include "fs/StreamNameIO.h"
|
||||||
#include "fs/NullNameIO.h"
|
#include "fs/NullNameIO.h"
|
||||||
|
|
||||||
using std::cerr;
|
|
||||||
using std::list;
|
using std::list;
|
||||||
using std::make_pair;
|
using std::make_pair;
|
||||||
using std::multimap;
|
using std::multimap;
|
||||||
using std::string;
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
namespace encfs {
|
namespace encfs {
|
||||||
|
|
||||||
#define REF_MODULE(TYPE) \
|
#define REF_MODULE(TYPE) \
|
||||||
do { \
|
do { \
|
||||||
if (!TYPE::Enabled()) cerr << "referenceModule: should never happen\n"; \
|
CHECK(TYPE::Enabled()) << "referenceModule: should never happen"; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
static void AddSymbolReferences() {
|
static void AddSymbolReferences() {
|
||||||
@ -62,7 +63,7 @@ struct NameIOAlg {
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef multimap<string, NameIOAlg> NameIOMap_t;
|
typedef multimap<string, NameIOAlg> NameIOMap_t;
|
||||||
static NameIOMap_t *gNameIOMap = 0;
|
static NameIOMap_t *gNameIOMap = nullptr;
|
||||||
|
|
||||||
list<NameIO::Algorithm> NameIO::GetAlgorithmList(bool includeHidden) {
|
list<NameIO::Algorithm> NameIO::GetAlgorithmList(bool includeHidden) {
|
||||||
AddSymbolReferences();
|
AddSymbolReferences();
|
||||||
@ -145,136 +146,84 @@ void NameIO::setReverseEncryption(bool enable) { reverseEncryption = enable; }
|
|||||||
|
|
||||||
bool NameIO::getReverseEncryption() const { return reverseEncryption; }
|
bool NameIO::getReverseEncryption() const { return reverseEncryption; }
|
||||||
|
|
||||||
std::string NameIO::recodePath(const char *path,
|
string NameIO::recodePath(const string &path, int (NameIO::*_length)(int) const,
|
||||||
int (NameIO::*_length)(int) const,
|
string (NameIO::*_code)(const string &,
|
||||||
int (NameIO::*_code)(const char *, int,
|
uint64_t *) const,
|
||||||
uint64_t *, char *) const,
|
uint64_t *iv) const {
|
||||||
uint64_t *iv) const {
|
|
||||||
string output;
|
string output;
|
||||||
|
|
||||||
while (*path) {
|
for (auto it = path.begin(); it != path.end();) {
|
||||||
if (*path == '/') {
|
bool isDotFile = (*it == '.');
|
||||||
if (!output.empty()) // don't start the string with '/'
|
auto next = std::find(it, path.end(), '/');
|
||||||
output += '/';
|
int len = std::distance(it, next);
|
||||||
++path;
|
|
||||||
} else {
|
|
||||||
bool isDotFile = (*path == '.');
|
|
||||||
const char *next = strchr(path, '/');
|
|
||||||
int len = next ? next - path : strlen(path);
|
|
||||||
|
|
||||||
// at this point we know that len > 0
|
if (*it == '/') {
|
||||||
if (isDotFile && (path[len - 1] == '.') && (len <= 2)) {
|
// 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 '.'
|
output.append(len, '.'); // append [len] copies of '.'
|
||||||
path += len;
|
} else {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// figure out buffer sizes
|
|
||||||
int approxLen = (this->*_length)(len);
|
int approxLen = (this->*_length)(len);
|
||||||
if (approxLen <= 0) throw Error("Filename too small to decode");
|
if (approxLen <= 0) throw Error("Filename too small to decode");
|
||||||
|
|
||||||
BUFFER_INIT(codeBuf, 32, (unsigned int)approxLen + 1);
|
|
||||||
|
|
||||||
// code the name
|
// code the name
|
||||||
int codedLen = (this->*_code)(path, len, iv, codeBuf);
|
string input(it, it + len);
|
||||||
rAssert(codedLen <= approxLen);
|
string coded = (this->*_code)(input, iv);
|
||||||
rAssert(codeBuf[codedLen] == '\0');
|
|
||||||
path += len;
|
|
||||||
|
|
||||||
// append result to string
|
// append result to string
|
||||||
output += (char *)codeBuf;
|
output.append(coded);
|
||||||
|
|
||||||
BUFFER_RESET(codeBuf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string NameIO::encodePath(const char *plaintextPath) const {
|
string NameIO::encodePath(const string &plaintextPath) const {
|
||||||
uint64_t iv = 0;
|
uint64_t iv = 0;
|
||||||
return encodePath(plaintextPath, &iv);
|
return encodePath(plaintextPath, &iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string NameIO::decodePath(const char *cipherPath) const {
|
string NameIO::decodePath(const string &cipherPath) const {
|
||||||
uint64_t iv = 0;
|
uint64_t iv = 0;
|
||||||
return decodePath(cipherPath, &iv);
|
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 chaining is not enabled, then the iv pointer is not used..
|
||||||
if (!chainedNameIV) iv = 0;
|
if (!chainedNameIV) iv = nullptr;
|
||||||
return recodePath(plaintextPath, &NameIO::maxEncodedNameLen,
|
return recodePath(plaintextPath, &NameIO::maxEncodedNameLen,
|
||||||
&NameIO::encodeName, iv);
|
&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 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,
|
return recodePath(cipherPath, &NameIO::maxDecodedNameLen, &NameIO::decodeName,
|
||||||
iv);
|
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);
|
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);
|
return getReverseEncryption() ? _encodePath(path, iv) : _decodePath(path, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
int NameIO::encodeName(const char *input, int length, char *output) const {
|
string NameIO::encodeName(const string &name) const {
|
||||||
return encodeName(input, length, (uint64_t *)0, output);
|
return getReverseEncryption() ? decodeName(name, nullptr)
|
||||||
|
: encodeName(name, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int NameIO::decodeName(const char *input, int length, char *output) const {
|
string NameIO::decodeName(const string &name) const {
|
||||||
return decodeName(input, length, (uint64_t *)0, output);
|
return getReverseEncryption() ? encodeName(name, nullptr)
|
||||||
}
|
: decodeName(name, nullptr);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace encfs
|
} // namespace encfs
|
||||||
|
63
fs/NameIO.h
63
fs/NameIO.h
@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include "base/Interface.h"
|
#include "base/Interface.h"
|
||||||
#include "base/shared_ptr.h"
|
#include "base/shared_ptr.h"
|
||||||
|
#include "base/types.h"
|
||||||
|
|
||||||
namespace encfs {
|
namespace encfs {
|
||||||
|
|
||||||
@ -67,67 +68,39 @@ class NameIO {
|
|||||||
void setReverseEncryption(bool enable);
|
void setReverseEncryption(bool enable);
|
||||||
bool getReverseEncryption() const;
|
bool getReverseEncryption() const;
|
||||||
|
|
||||||
std::string encodePath(const char *plaintextPath) const;
|
std::string encodePath(const std::string &plaintextPath) const;
|
||||||
std::string decodePath(const char *encodedPath) const;
|
std::string decodePath(const std::string &encodedPath) const;
|
||||||
|
|
||||||
std::string encodePath(const char *plaintextPath, uint64_t *iv) const;
|
std::string encodePath(const std::string &plaintextPath, uint64_t *iv) const;
|
||||||
std::string decodePath(const char *encodedPath, uint64_t *iv) const;
|
std::string decodePath(const std::string &encodedPath, uint64_t *iv) const;
|
||||||
|
|
||||||
virtual int maxEncodedNameLen(int plaintextNameLen) const = 0;
|
virtual int maxEncodedNameLen(int plaintextNameLen) const = 0;
|
||||||
virtual int maxDecodedNameLen(int encodedNameLen) const = 0;
|
virtual int maxDecodedNameLen(int encodedNameLen) const = 0;
|
||||||
|
|
||||||
std::string encodeName(const char *plaintextName, int length) const;
|
std::string encodeName(const std::string &plaintextName) const;
|
||||||
std::string decodeName(const char *encodedName, int length) const;
|
std::string decodeName(const std::string &encodedName) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual int encodeName(const char *plaintextName, int length,
|
// Encode & decode methods implemented by derived classes.
|
||||||
char *encodedName) const;
|
virtual std::string encodeName(const std::string &name,
|
||||||
virtual int decodeName(const char *encodedName, int length,
|
uint64_t *iv) const = 0;
|
||||||
char *plaintextName) const;
|
virtual std::string decodeName(const std::string &name,
|
||||||
|
uint64_t *iv) const = 0;
|
||||||
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;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string recodePath(const char *path, int (NameIO::*codingLen)(int) const,
|
std::string recodePath(const std::string &path,
|
||||||
int (NameIO::*codingFunc)(const char *, int,
|
int (NameIO::*codingLen)(int) const,
|
||||||
uint64_t *, char *) const,
|
std::string (NameIO::*codingFunc)(const std::string &,
|
||||||
|
uint64_t *) const,
|
||||||
uint64_t *iv) const;
|
uint64_t *iv) const;
|
||||||
|
|
||||||
std::string _encodePath(const char *plaintextPath, uint64_t *iv) const;
|
std::string _encodePath(const std::string &plaintextPath, uint64_t *iv) const;
|
||||||
std::string _decodePath(const char *encodedPath, uint64_t *iv) const;
|
std::string _decodePath(const std::string &encodedPath, uint64_t *iv) const;
|
||||||
std::string _encodeName(const char *plaintextName, int length) const;
|
|
||||||
std::string _decodeName(const char *encodedName, int length) const;
|
|
||||||
|
|
||||||
bool chainedNameIV;
|
bool chainedNameIV;
|
||||||
bool reverseEncryption;
|
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
|
} // namespace encfs
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
59
fs/NameIO_test.cpp
Normal file
59
fs/NameIO_test.cpp
Normal 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
|
@ -23,9 +23,12 @@
|
|||||||
#include "fs/NullNameIO.h"
|
#include "fs/NullNameIO.h"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace encfs {
|
namespace encfs {
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
|
||||||
static shared_ptr<NameIO> NewNNIO(const Interface &,
|
static shared_ptr<NameIO> NewNNIO(const Interface &,
|
||||||
const shared_ptr<CipherV1> &) {
|
const shared_ptr<CipherV1> &) {
|
||||||
return shared_ptr<NameIO>(new NullNameIO());
|
return shared_ptr<NameIO>(new NullNameIO());
|
||||||
@ -51,16 +54,12 @@ int NullNameIO::maxDecodedNameLen(int encodedNameLen) const {
|
|||||||
return encodedNameLen;
|
return encodedNameLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
int NullNameIO::encodeName(const char *plaintextName, int length, uint64_t *iv,
|
string NullNameIO::encodeName(const string &plaintextName, uint64_t *iv) const {
|
||||||
char *encodedName) const {
|
return plaintextName;
|
||||||
memcpy(encodedName, plaintextName, length);
|
|
||||||
return length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int NullNameIO::decodeName(const char *encodedName, int length, uint64_t *iv,
|
string NullNameIO::decodeName(const string &encodedName, uint64_t *iv) const {
|
||||||
char *plaintextName) const {
|
return encodedName;
|
||||||
memcpy(plaintextName, encodedName, length);
|
|
||||||
return length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NullNameIO::Enabled() { return true; }
|
bool NullNameIO::Enabled() { return true; }
|
||||||
|
@ -33,19 +33,19 @@ class NullNameIO : public NameIO {
|
|||||||
|
|
||||||
virtual ~NullNameIO();
|
virtual ~NullNameIO();
|
||||||
|
|
||||||
virtual Interface interface() const;
|
virtual Interface interface() const override;
|
||||||
|
|
||||||
virtual int maxEncodedNameLen(int plaintextNameLen) const;
|
virtual int maxEncodedNameLen(int plaintextNameLen) const override;
|
||||||
virtual int maxDecodedNameLen(int encodedNameLen) const;
|
virtual int maxDecodedNameLen(int encodedNameLen) const override;
|
||||||
|
|
||||||
// hack to help with static builds
|
// hack to help with static builds
|
||||||
static bool Enabled();
|
static bool Enabled();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual int encodeName(const char *plaintextName, int length, uint64_t *iv,
|
virtual std::string encodeName(const std::string &plaintextName,
|
||||||
char *encodedName) const;
|
uint64_t *iv) const override;
|
||||||
virtual int decodeName(const char *encodedName, int length, uint64_t *iv,
|
virtual std::string decodeName(const std::string &encodedName,
|
||||||
char *plaintextName) const;
|
uint64_t *iv) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
@ -27,9 +27,14 @@
|
|||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace encfs {
|
namespace encfs {
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
static shared_ptr<NameIO> NewStreamNameIO(const Interface &iface,
|
static shared_ptr<NameIO> NewStreamNameIO(const Interface &iface,
|
||||||
const shared_ptr<CipherV1> &cipher) {
|
const shared_ptr<CipherV1> &cipher) {
|
||||||
return shared_ptr<NameIO>(new StreamNameIO(iface, cipher));
|
return shared_ptr<NameIO>(new StreamNameIO(iface, cipher));
|
||||||
@ -60,10 +65,12 @@ static bool StreamIO_registered = NameIO::Register(
|
|||||||
bits.
|
bits.
|
||||||
|
|
||||||
- Version 2.1 adds support for version 0 for EncFS 0.x compatibility.
|
- Version 2.1 adds support for version 0 for EncFS 0.x compatibility.
|
||||||
|
|
||||||
|
- Version 3.0 drops Encfs 0.x support.
|
||||||
*/
|
*/
|
||||||
Interface StreamNameIO::CurrentInterface() {
|
Interface StreamNameIO::CurrentInterface() {
|
||||||
// implement major version 2, 1, and 0
|
// implements support for version 3, 2, and 1.
|
||||||
return makeInterface("nameio/stream", 2, 1, 2);
|
return makeInterface("nameio/stream", 3, 0, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamNameIO::StreamNameIO(const Interface &iface,
|
StreamNameIO::StreamNameIO(const Interface &iface,
|
||||||
@ -84,92 +91,73 @@ int StreamNameIO::maxDecodedNameLen(int encodedStreamLen) const {
|
|||||||
return decLen256 - 2;
|
return decLen256 - 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
int StreamNameIO::encodeName(const char *plaintextName, int length,
|
string StreamNameIO::encodeName(const string &plaintextName,
|
||||||
uint64_t *iv, char *encodedName) const {
|
uint64_t *iv) const {
|
||||||
uint64_t tmpIV = 0;
|
uint64_t tmpIV = 0;
|
||||||
|
int length = plaintextName.length();
|
||||||
if (iv && _interface >= 2) tmpIV = *iv;
|
if (iv && _interface >= 2) tmpIV = *iv;
|
||||||
|
|
||||||
unsigned int mac = _cipher->reduceMac16(
|
unsigned int mac = _cipher->reduceMac16(_cipher->MAC_64(
|
||||||
_cipher->MAC_64((const byte *)plaintextName, length, iv));
|
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 encodedStreamLen = length + 2;
|
||||||
int encLen64 = B256ToB64Bytes(encodedStreamLen);
|
int encLen64 = B256ToB64Bytes(encodedStreamLen);
|
||||||
|
|
||||||
changeBase2Inline((unsigned char *)encodedName, encodedStreamLen, 8, 6, true);
|
// add on checksum bytes
|
||||||
B64ToAscii((unsigned char *)encodedName, encLen64);
|
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,
|
string StreamNameIO::decodeName(const string &encodedName,
|
||||||
char *plaintextName) const {
|
uint64_t *iv) const {
|
||||||
|
int length = encodedName.length();
|
||||||
rAssert(length > 2);
|
rAssert(length > 2);
|
||||||
int decLen256 = B64ToB256Bytes(length);
|
int decLen256 = B64ToB256Bytes(length);
|
||||||
int decodedStreamLen = decLen256 - 2;
|
int decodedStreamLen = decLen256 - 2;
|
||||||
|
|
||||||
if (decodedStreamLen <= 0) throw Error("Filename too small to decode");
|
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
|
// decode into tmpBuf, because this step produces more data then we can fit
|
||||||
// into the result buffer..
|
// into the result buffer..
|
||||||
AsciiToB64((unsigned char *)tmpBuf, (unsigned char *)encodedName, length);
|
memcpy(tmpBuf.data(), encodedName.data(), length);
|
||||||
changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false);
|
AsciiToB64(tmpBuf.data(), length);
|
||||||
|
changeBase2Inline(tmpBuf.data(), length, 6, 8, false);
|
||||||
|
|
||||||
// pull out the checksum value which is used as an initialization vector
|
// pull out the checksum value which is used as an initialization vector
|
||||||
uint64_t tmpIV = 0;
|
uint64_t tmpIV = 0;
|
||||||
unsigned int mac;
|
unsigned int mac = ((unsigned int)tmpBuf[0]) << 8 | ((unsigned int)tmpBuf[1]);
|
||||||
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]));
|
|
||||||
|
|
||||||
// version 2 adds support for IV chaining..
|
// version 2 adds support for IV chaining..
|
||||||
if (iv && _interface >= 2) tmpIV = *iv;
|
if (iv && _interface >= 2) tmpIV = *iv;
|
||||||
|
|
||||||
memcpy(plaintextName, tmpBuf + 2, decodedStreamLen);
|
tmpIV ^= (uint64_t)mac;
|
||||||
} else {
|
_cipher->streamDecode(&tmpBuf.at(2), decodedStreamLen, tmpIV);
|
||||||
// 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);
|
|
||||||
|
|
||||||
// compute MAC to check with stored value
|
// compute MAC to check with stored value
|
||||||
unsigned int mac2 = _cipher->reduceMac16(
|
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) {
|
if (mac2 != mac) {
|
||||||
VLOG(1) << "checksum mismatch: expected " << mac << ", got " << mac2
|
VLOG(1) << "checksum mismatch: expected " << mac << ", got " << mac2
|
||||||
<< "on decode of " << decodedStreamLen << " bytes";
|
<< "on decode of " << decodedStreamLen << " bytes";
|
||||||
throw Error("checksum mismatch in filename decode");
|
throw Error("checksum mismatch in filename decode");
|
||||||
}
|
}
|
||||||
|
|
||||||
return decodedStreamLen;
|
return string(reinterpret_cast<char*>(&tmpBuf.at(2)), decodedStreamLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StreamNameIO::Enabled() { return true; }
|
bool StreamNameIO::Enabled() { return true; }
|
||||||
|
@ -34,19 +34,19 @@ class StreamNameIO : public NameIO {
|
|||||||
StreamNameIO(const Interface &iface, const shared_ptr<CipherV1> &cipher);
|
StreamNameIO(const Interface &iface, const shared_ptr<CipherV1> &cipher);
|
||||||
virtual ~StreamNameIO();
|
virtual ~StreamNameIO();
|
||||||
|
|
||||||
virtual Interface interface() const;
|
virtual Interface interface() const override;
|
||||||
|
|
||||||
virtual int maxEncodedNameLen(int plaintextNameLen) const;
|
virtual int maxEncodedNameLen(int plaintextNameLen) const override;
|
||||||
virtual int maxDecodedNameLen(int encodedNameLen) const;
|
virtual int maxDecodedNameLen(int encodedNameLen) const override;
|
||||||
|
|
||||||
// hack to help with static builds
|
// hack to help with static builds
|
||||||
static bool Enabled();
|
static bool Enabled();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual int encodeName(const char *plaintextName, int length, uint64_t *iv,
|
virtual std::string encodeName(const std::string &plaintextName,
|
||||||
char *encodedName) const;
|
uint64_t *iv) const override;
|
||||||
virtual int decodeName(const char *encodedName, int length, uint64_t *iv,
|
virtual std::string decodeName(const std::string &encodedName,
|
||||||
char *plaintextName) const;
|
uint64_t *iv) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _interface;
|
int _interface;
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include "cipher/CipherV1.h"
|
#include "cipher/CipherV1.h"
|
||||||
@ -188,6 +189,8 @@ void comparisonTest(FSConfigPtr& cfg, FileIO* a, FileIO* b) {
|
|||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
google::InitGoogleLogging(argv[0]);
|
||||||
|
|
||||||
return RUN_ALL_TESTS();
|
return RUN_ALL_TESTS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user