bool runTests(const std::shared_ptr<Cipher> &cipher, bool verbose) { // create a random key if (verbose) { cerr << "Generating new key, output will be different on each run\n\n"; } CipherKey key = cipher->newRandomKey(); if (verbose) cerr << "Testing key save / restore :"; { CipherKey encodingKey = cipher->newRandomKey(); int encodedKeySize = cipher->encodedKeySize(); unsigned char keyBuf[encodedKeySize]; cipher->writeKey(key, keyBuf, encodingKey); CipherKey key2 = cipher->readKey(keyBuf, encodingKey); if (!key2) { if (verbose) cerr << " FAILED (decode error)\n"; return false; } if (cipher->compareKey(key, key2)) { if (verbose) cerr << " OK\n"; } else { if (verbose) cerr << " FAILED\n"; return false; } } if (verbose) cerr << "Testing Config interface load / store :"; { 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. string name = "/tmp/encfstestXXXXXX"; int tmpFd = mkstemp(&name[0]); rAssert(-1 != tmpFd); // mkstemp opens the temporary file, but we only need its name -> close it rAssert(0 == close(tmpFd)); { auto ok = writeV6Config(name.c_str(), &cfg); rAssert(ok == true); } // read back in and check everything.. EncFSConfig cfg2; { auto ok = readV6Config(name.c_str(), &cfg2, nullptr); rAssert(ok == true); } // delete the temporary file where we stored the config rAssert(0 == unlink(name.c_str())); // check.. rAssert(cfg.cipherIface.implements(cfg2.cipherIface)); rAssert(cfg.keySize == cfg2.keySize); rAssert(cfg.blockSize == cfg2.blockSize); // try decoding key.. CipherKey key2 = cipher->readKey(cfg2.getKeyData(), encodingKey); if (!key2) { if (verbose) cerr << " FAILED (decode error)\n"; return false; } if (cipher->compareKey(key, key2)) { if (verbose) cerr << " OK\n"; } else { if (verbose) cerr << " FAILED\n"; return false; } } FSConfigPtr fsCfg = FSConfigPtr(new FSConfig); fsCfg->cipher = cipher; fsCfg->key = key; fsCfg->config.reset(new EncFSConfig); fsCfg->config->blockSize = FSBlockSize; if (verbose) cerr << "Testing name encode/decode (stream coding w/ IV chaining)\n"; { 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); if (!testNameCoding(dirNode, verbose)) return false; } if (verbose) cerr << "Testing name encode/decode (block coding w/ IV chaining)\n"; { 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); if (!testNameCoding(dirNode, verbose)) return false; } if (verbose) cerr << "Testing name encode/decode (block coding w/ IV chaining, base32)\n"; { fsCfg->opts->idleTracking = false; fsCfg->config->uniqueIV = false; fsCfg->nameCoding.reset(new BlockNameIO(BlockNameIO::CurrentInterface(), cipher, key, cipher->cipherBlockSize(), true)); fsCfg->nameCoding->setChainedNameIV(true); DirNode dirNode(NULL, TEST_ROOTDIR, fsCfg); if (!testNameCoding(dirNode, verbose)) return false; } if (!verbose) { { // test stream mode, this time without IV chaining fsCfg->nameCoding = std::shared_ptr<NameIO>( new StreamNameIO(StreamNameIO::CurrentInterface(), cipher, key)); fsCfg->nameCoding->setChainedNameIV(false); DirNode dirNode(NULL, TEST_ROOTDIR, fsCfg); if (!testNameCoding(dirNode, verbose)) return false; } { // test block mode, this time without IV chaining fsCfg->nameCoding = std::shared_ptr<NameIO>( new BlockNameIO(BlockNameIO::CurrentInterface(), cipher, key, cipher->cipherBlockSize())); fsCfg->nameCoding->setChainedNameIV(false); DirNode dirNode(NULL, TEST_ROOTDIR, fsCfg); if (!testNameCoding(dirNode, verbose)) return false; } } if (verbose) cerr << "Testing block encode/decode on full block - "; { int numErrors = checkErrorPropogation(cipher, FSBlockSize, -1, key); if (numErrors) { if (verbose) cerr << " FAILED!\n"; return false; } else { if (verbose) cerr << " OK\n"; } } if (verbose) cerr << "Testing block encode/decode on partial block - "; { int numErrors = checkErrorPropogation(cipher, FSBlockSize - 1, -1, key); if (numErrors) { if (verbose) cerr << " FAILED!\n"; return false; } else { if (verbose) cerr << " OK\n"; } } if (verbose) cerr << "Checking error propogation in partial block:\n"; { int minChanges = FSBlockSize - 1; int maxChanges = 0; int minAt = 0; int maxAt = 0; for (int i = 0; i < FSBlockSize - 1; ++i) { int numErrors = checkErrorPropogation(cipher, FSBlockSize - 1, i, key); if (numErrors < minChanges) { minChanges = numErrors; minAt = i; } if (numErrors > maxChanges) { maxChanges = numErrors; maxAt = i; } } if (verbose) { cerr << "modification of 1 byte affected between " << minChanges << " and " << maxChanges << " decoded bytes\n"; cerr << "minimum change at byte " << minAt << " and maximum at byte " << maxAt << "\n"; } } if (verbose) cerr << "Checking error propogation on full block:\n"; { int minChanges = FSBlockSize; int maxChanges = 0; int minAt = 0; int maxAt = 0; for (int i = 0; i < FSBlockSize; ++i) { int numErrors = checkErrorPropogation(cipher, FSBlockSize, i, key); if (numErrors < minChanges) { minChanges = numErrors; minAt = i; } if (numErrors > maxChanges) { maxChanges = numErrors; maxAt = i; } } if (verbose) { cerr << "modification of 1 byte affected between " << minChanges << " and " << maxChanges << " decoded bytes\n"; cerr << "minimum change at byte " << minAt << " and maximum at byte " << maxAt << "\n"; } } return true; }
bool runTests(const shared_ptr<Cipher> &cipher, bool verbose) { // create a random key if(verbose) cerr << "Generating new key, output will be different on each run\n\n"; CipherKey key = cipher->newRandomKey(); if(verbose) cerr << "Testing key save / restore :"; { CipherKey encodingKey = cipher->newRandomKey(); int encodedKeySize = cipher->encodedKeySize(); unsigned char *keyBuf = new unsigned char [ encodedKeySize ]; cipher->writeKey( key, keyBuf, encodingKey ); CipherKey key2 = cipher->readKey( keyBuf, encodingKey ); if(!key2) { if(verbose) cerr << " FAILED (decode error)\n"; return false; } if(cipher->compareKey( key, key2 )) { if(verbose) cerr << " OK\n"; } else { if(verbose) cerr << " FAILED\n"; return false; } } if(verbose) cerr << "Testing Config interface load / store :"; { CipherKey encodingKey = cipher->newRandomKey(); int encodedKeySize = cipher->encodedKeySize(); unsigned char *keyBuf = new unsigned char [ 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 string data; { ostringstream st; st << cfg; data = st.str(); } // read back in and check everything.. EncFSConfig cfg2; { istringstream st(data); st >> cfg2; } // check.. rAssert( cfg.cipherIface.implements(cfg2.cipherIface) ); rAssert( cfg.keySize == cfg2.keySize ); rAssert( cfg.blockSize == cfg2.blockSize ); // try decoding key.. CipherKey key2 = cipher->readKey( cfg2.getKeyData(), encodingKey ); if(!key2) { if(verbose) cerr << " FAILED (decode error)\n"; return false; } if(cipher->compareKey( key, key2 )) { if(verbose) cerr << " OK\n"; } else { if(verbose) cerr << " FAILED\n"; return false; } } FSConfigPtr fsCfg = FSConfigPtr(new FSConfig); fsCfg->cipher = cipher; fsCfg->key = key; fsCfg->config.reset(new EncFSConfig); fsCfg->config->blockSize = FSBlockSize; if(verbose) cerr << "Testing name encode/decode (stream coding w/ IV chaining)\n"; { 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 ); if(!testNameCoding( dirNode, verbose )) return false; } if(verbose) cerr << "Testing name encode/decode (block coding w/ IV chaining)\n"; { 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 ); if(!testNameCoding( dirNode, verbose )) return false; } if(verbose) cerr << "Testing name encode/decode (block coding w/ IV chaining, base32)\n"; { fsCfg->opts->idleTracking = false; fsCfg->config->uniqueIV = false; fsCfg->nameCoding.reset( new BlockNameIO( BlockNameIO::CurrentInterface(), cipher, key, cipher->cipherBlockSize(), true ) ); fsCfg->nameCoding->setChainedNameIV( true ); DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); if(!testNameCoding( dirNode, verbose )) return false; } if(!verbose) { { // test stream mode, this time without IV chaining fsCfg->nameCoding = shared_ptr<NameIO>( new StreamNameIO( StreamNameIO::CurrentInterface(), cipher, key ) ); fsCfg->nameCoding->setChainedNameIV( false ); DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); if(!testNameCoding( dirNode, verbose )) return false; } { // test block mode, this time without IV chaining fsCfg->nameCoding = shared_ptr<NameIO>( new BlockNameIO( BlockNameIO::CurrentInterface(), cipher, key, cipher->cipherBlockSize() ) ); fsCfg->nameCoding->setChainedNameIV( false ); DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); if(!testNameCoding( dirNode, verbose )) return false; } } if(verbose) cerr << "Testing block encode/decode on full block - "; { int numErrors = checkErrorPropogation( cipher, FSBlockSize, -1, key ); if(numErrors) { if(verbose) cerr << " FAILED!\n"; return false; } else { if(verbose) cerr << " OK\n"; } } if(verbose) cerr << "Testing block encode/decode on partial block - "; { int numErrors = checkErrorPropogation( cipher, FSBlockSize-1, -1, key ); if(numErrors) { if(verbose) cerr << " FAILED!\n"; return false; } else { if(verbose) cerr << " OK\n"; } } if(verbose) cerr << "Checking error propogation in partial block:\n"; { int minChanges = FSBlockSize-1; int maxChanges = 0; int minAt = 0; int maxAt = 0; for(int i=0; i<FSBlockSize-1; ++i) { int numErrors = checkErrorPropogation( cipher, FSBlockSize-1, i, key ); if(numErrors < minChanges) { minChanges = numErrors; minAt = i; } if(numErrors > maxChanges) { maxChanges = numErrors; maxAt = i; } } if(verbose) { cerr << "modification of 1 byte affected between " << minChanges << " and " << maxChanges << " decoded bytes\n"; cerr << "minimum change at byte " << minAt << " and maximum at byte " << maxAt << "\n"; } } if(verbose) cerr << "Checking error propogation on full block:\n"; { int minChanges = FSBlockSize; int maxChanges = 0; int minAt = 0; int maxAt = 0; for(int i=0; i<FSBlockSize; ++i) { int numErrors = checkErrorPropogation( cipher, FSBlockSize, i, key ); if(numErrors < minChanges) { minChanges = numErrors; minAt = i; } if(numErrors > maxChanges) { maxChanges = numErrors; maxAt = i; } } if(verbose) { cerr << "modification of 1 byte affected between " << minChanges << " and " << maxChanges << " decoded bytes\n"; cerr << "minimum change at byte " << minAt << " and maximum at byte " << maxAt << "\n"; } } return true; }
static int do_chpasswd(bool useStdin, bool annotate, bool checkOnly, int argc, char **argv) { (void)argc; string rootDir = argv[1]; if (!checkDir(rootDir)) return EXIT_FAILURE; EncFSConfig *config = new EncFSConfig; ConfigType cfgType = readConfig(rootDir, config); if (cfgType == Config_None) { cout << _("Unable to load or parse config file\n"); return EXIT_FAILURE; } // instanciate proper cipher std::shared_ptr<Cipher> cipher = Cipher::New(config->cipherIface, config->keySize); if (!cipher) { cout << autosprintf(_("Unable to find specified cipher \"%s\"\n"), config->cipherIface.name().c_str()); return EXIT_FAILURE; } // ask for existing password cout << _("Enter current Encfs password\n"); if (annotate) cerr << "$PROMPT$ passwd" << endl; CipherKey userKey = config->getUserKey(useStdin); if (!userKey) return EXIT_FAILURE; // decode volume key using user key -- at this point we detect an incorrect // password if the key checksum does not match (causing readKey to fail). CipherKey volumeKey = cipher->readKey(config->getKeyData(), userKey); if (!volumeKey) { cout << _("Invalid password\n"); return EXIT_FAILURE; } if (checkOnly) { cout << _("Password is correct\n"); return EXIT_SUCCESS; } // Now, get New user key.. userKey.reset(); cout << _("Enter new Encfs password\n"); // reinitialize salt and iteration count config->kdfIterations = 0; // generate new if (useStdin) { if (annotate) cerr << "$PROMPT$ new_passwd" << endl; userKey = config->getUserKey(true); } else userKey = config->getNewUserKey(); // re-encode the volume key using the new user key and write it out.. int result = EXIT_FAILURE; if (userKey) { int encodedKeySize = cipher->encodedKeySize(); unsigned char *keyBuf = new unsigned char[encodedKeySize]; // encode volume key with new user key cipher->writeKey(volumeKey, keyBuf, userKey); userKey.reset(); config->assignKeyData(keyBuf, encodedKeySize); delete[] keyBuf; if (saveConfig(cfgType, rootDir, config)) { // password modified -- changes volume key of filesystem.. cout << _("Volume Key successfully updated.\n"); result = EXIT_SUCCESS; } else { cout << _("Error saving modified config file.\n"); } } else { cout << _("Error creating key\n"); } volumeKey.reset(); return result; }