diff --git a/encfs/Cipher_test.cpp b/encfs/Cipher_test.cpp new file mode 100644 index 0000000..00d45b0 --- /dev/null +++ b/encfs/Cipher_test.cpp @@ -0,0 +1,195 @@ +#include "gtest/gtest.h" + +#include "BlockNameIO.h" +#include "Cipher.h" +#include "CipherKey.h" +#include "DirNode.h" +#include "FSConfig.h" +#include "FileUtils.h" +#include "StreamNameIO.h" + +using namespace encfs; +using namespace testing; +using std::string; + +const int FSBlockSize = 256; +const char TEST_ROOTDIR[] = "/foo"; + +static void testNameCoding(DirNode &dirNode) { + // encrypt a name + const char *name[] = { + "1234567", "12345678", "123456789", + "123456789ABCDEF", "123456789ABCDEF0", "123456789ABCDEF01", + "test-name", "test-name2", "test", + "../test", "/foo/bar/blah", "test-name.21", + "test-name.22", "test-name.o", "1.test", + "2.test", "a/b/c/d", "a/c/d/e", + "b/c/d/e", "b/a/c/d", NULL}; + + const char **orig = name; + while (*orig) { + string encName = dirNode.relativeCipherPath(*orig); + + // decrypt name + string decName = dirNode.plainPath(encName.c_str()); + + ASSERT_EQ(decName, *orig); + orig++; + } +} + +class CipherTest : public TestWithParam { + protected: + virtual void SetUp() { + Cipher::CipherAlgorithm alg = GetParam(); + cipher = Cipher::New(alg.name, alg.keyLength.closest(256)); + } + virtual void TearDown() {} + std::shared_ptr cipher; +}; + +TEST_P(CipherTest, SaveRestoreKey) { + auto key = cipher->newRandomKey(); + + auto encodingKey = cipher->newRandomKey(); + int encodedKeySize = cipher->encodedKeySize(); + unsigned char keyBuf[encodedKeySize]; + + cipher->writeKey(key, keyBuf, encodingKey); + auto restored = cipher->readKey(keyBuf, encodingKey); + EXPECT_TRUE(restored); + EXPECT_TRUE(cipher->compareKey(key, restored)); +} + +TEST_P(CipherTest, NameStreamEncoding) { + auto key = cipher->newRandomKey(); + + FSConfigPtr fsCfg = FSConfigPtr(new FSConfig); + fsCfg->cipher = cipher; + fsCfg->key = key; + fsCfg->config.reset(new EncFSConfig); + fsCfg->config->blockSize = FSBlockSize; + fsCfg->opts.reset(new EncFS_Opts); + fsCfg->opts->idleTracking = false; + fsCfg->config->uniqueIV = false; + + fsCfg->nameCoding.reset( + new StreamNameIO(StreamNameIO::CurrentInterface(), cipher, key)); + + { + fsCfg->nameCoding->setChainedNameIV(true); + DirNode dirNode(NULL, TEST_ROOTDIR, fsCfg); + testNameCoding(dirNode); + } + { + fsCfg->nameCoding->setChainedNameIV(false); + DirNode dirNode(NULL, TEST_ROOTDIR, fsCfg); + testNameCoding(dirNode); + } +} + +TEST_P(CipherTest, NameBlockEncoding) { + auto key = cipher->newRandomKey(); + + FSConfigPtr fsCfg = FSConfigPtr(new FSConfig); + fsCfg->cipher = cipher; + fsCfg->key = key; + fsCfg->config.reset(new EncFSConfig); + fsCfg->config->blockSize = FSBlockSize; + fsCfg->opts.reset(new EncFS_Opts); + fsCfg->opts->idleTracking = false; + fsCfg->config->uniqueIV = false; + fsCfg->nameCoding.reset(new BlockNameIO( + BlockNameIO::CurrentInterface(), cipher, key, cipher->cipherBlockSize())); + + { + fsCfg->nameCoding->setChainedNameIV(true); + DirNode dirNode(NULL, TEST_ROOTDIR, fsCfg); + testNameCoding(dirNode); + } + { + fsCfg->nameCoding->setChainedNameIV(false); + DirNode dirNode(NULL, TEST_ROOTDIR, fsCfg); + testNameCoding(dirNode); + } +} + +TEST_P(CipherTest, NameBlockBase32Encoding) { + auto key = cipher->newRandomKey(); + + FSConfigPtr fsCfg = FSConfigPtr(new FSConfig); + fsCfg->cipher = cipher; + fsCfg->key = key; + fsCfg->config.reset(new EncFSConfig); + fsCfg->config->blockSize = FSBlockSize; + fsCfg->opts.reset(new EncFS_Opts); + fsCfg->opts->idleTracking = false; + fsCfg->config->uniqueIV = false; + fsCfg->nameCoding.reset(new BlockNameIO(BlockNameIO::CurrentInterface(), + cipher, key, + cipher->cipherBlockSize(), true)); + + { + fsCfg->nameCoding->setChainedNameIV(true); + DirNode dirNode(NULL, TEST_ROOTDIR, fsCfg); + testNameCoding(dirNode); + } + + { + fsCfg->nameCoding->setChainedNameIV(false); + DirNode dirNode(NULL, TEST_ROOTDIR, fsCfg); + testNameCoding(dirNode); + } +} + +TEST_P(CipherTest, ConfigLoadStore) { + auto key = cipher->newRandomKey(); + CipherKey encodingKey = cipher->newRandomKey(); + int encodedKeySize = cipher->encodedKeySize(); + unsigned char keyBuf[encodedKeySize]; + + cipher->writeKey(key, keyBuf, encodingKey); + + // store in config struct.. + EncFSConfig cfg; + cfg.cipherIface = cipher->interface(); + cfg.keySize = 8 * cipher->keySize(); + cfg.blockSize = FSBlockSize; + cfg.assignKeyData(keyBuf, encodedKeySize); + + // save config + // Creation of a temporary file should be more platform independent. On + // c++17 we could use std::filesystem. + std::string name = "/tmp/encfstestXXXXXX"; + int tmpFd = mkstemp(&name[0]); + EXPECT_GE(tmpFd, 0); + // mkstemp opens the temporary file, but we only need its name -> close it + EXPECT_EQ(close(tmpFd), 0); + { + auto ok = writeV6Config(name.c_str(), &cfg); + EXPECT_TRUE(ok); + } + + // read back in and check everything.. + EncFSConfig cfg2; + { + auto ok = readV6Config(name.c_str(), &cfg2, nullptr); + EXPECT_TRUE(ok); + } + // delete the temporary file where we stored the config + EXPECT_EQ(unlink(name.c_str()), 0); + + // check.. + EXPECT_TRUE(cfg.cipherIface.implements(cfg2.cipherIface)); + EXPECT_EQ(cfg.keySize, cfg2.keySize); + EXPECT_EQ(cfg.blockSize, cfg2.blockSize); + + // try decoding key.. + + CipherKey key2 = cipher->readKey(cfg2.getKeyData(), encodingKey); + EXPECT_TRUE(key2); + EXPECT_TRUE(cipher->compareKey(key, key2)); +} + +INSTANTIATE_TEST_CASE_P(CipherKey, CipherTest, + ValuesIn(Cipher::GetAlgorithmList()));