CipherKey decryptKey(const EncfsConfig &config, const char *password, int passwdLen) { const EncryptedKey &key = config.key(); CipherKey userKey; shared_ptr<CipherV1> cipher = getCipher(config.cipher(), 8 * key.size()); if(!key.salt().empty()) { int iterations = key.kdf_iterations(); userKey = cipher->newKey(password, passwdLen, &iterations, key.kdf_duration(), (const byte *)key.salt().data(), key.salt().size()); if (iterations != key.kdf_iterations()) { LOG(ERROR) << "Error in KDF, iteration mismatch"; return userKey; } } else { // old KDF, no salt.. userKey = cipher->newKey( password, passwdLen ); } return userKey; }
RootPtr initFS( EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts ) { RootPtr rootInfo; EncfsConfig config; if(readConfig( opts->rootDir, config ) != Config_None) { if(opts->reverseEncryption) { if (config.block_mac_bytes() != 0 || config.block_mac_rand_bytes() != 0 || config.unique_iv() || config.external_iv() || config.chained_iv() ) { cout << _("The configuration loaded is not compatible with --reverse\n"); return rootInfo; } } // first, instanciate the cipher. shared_ptr<CipherV1> cipher = getCipher(config); if(!cipher) { Interface iface = config.cipher(); LOG(ERROR) << "Unable to find cipher " << iface.name() << ", version " << iface.major() << ":" << iface.minor() << ":" << iface.age(); // xgroup(diag) cout << _("The requested cipher interface is not available\n"); return rootInfo; } // get user key CipherKey userKey; if(opts->passwordProgram.empty()) { if (opts->annotate) cerr << "$PROMPT$ passwd" << endl; userKey = getUserKey( config, opts->useStdin ); } else userKey = getUserKey( config, opts->passwordProgram, opts->rootDir ); if(!userKey.valid()) return rootInfo; cipher->setKey(userKey); VLOG(1) << "cipher encoded key size = " << cipher->encodedKeySize(); // decode volume key.. CipherKey volumeKey = cipher->readKey( (const unsigned char *)config.key().ciphertext().data(), opts->checkKey); userKey.reset(); if(!volumeKey.valid()) { // xgroup(diag) cout << _("Error decoding volume key, password incorrect\n"); return rootInfo; } cipher->setKey(volumeKey); shared_ptr<NameIO> nameCoder = NameIO::New( config.naming(), cipher ); if(!nameCoder) { Interface iface = config.naming(); LOG(ERROR) << "Unable to find nameio interface " << iface.name() << ", version " << iface.major() << ":" << iface.minor() << ":" << iface.age(); // xgroup(diag) cout << _("The requested filename coding interface is " "not available\n"); return rootInfo; } nameCoder->setChainedNameIV( config.chained_iv() ); nameCoder->setReverseEncryption( opts->reverseEncryption ); FSConfigPtr fsConfig( new FSConfig ); fsConfig->cipher = cipher; fsConfig->key = volumeKey; fsConfig->nameCoding = nameCoder; fsConfig->config = shared_ptr<EncfsConfig>(new EncfsConfig(config)); fsConfig->forceDecode = opts->forceDecode; fsConfig->reverseEncryption = opts->reverseEncryption; fsConfig->opts = opts; rootInfo = RootPtr( new EncFS_Root ); rootInfo->cipher = cipher; rootInfo->volumeKey = volumeKey; rootInfo->root = shared_ptr<DirNode>( new DirNode( ctx, opts->rootDir, fsConfig )); } else { if(opts->createIfNotFound) { // creating a new encrypted filesystem rootInfo = createConfig( ctx, opts ); } } return rootInfo; }
shared_ptr<CipherV1> getCipher(const EncfsConfig &config) { return getCipher(config.cipher(), 8 * config.key().size()); }
void showFSInfo( const EncfsConfig &config ) { shared_ptr<CipherV1> cipher = CipherV1::New( config.cipher(), config.key().size() ); { cout << autosprintf( // xgroup(diag) _("Filesystem cipher: \"%s\", version %i:%i:%i"), config.cipher().name().c_str(), config.cipher().major(), config.cipher().minor(), config.cipher().age()); // check if we support this interface.. if(!cipher) cout << _(" (NOT supported)\n"); else { // if we're using a newer interface, show the version number if( config.cipher() != cipher->interface() ) { Interface iface = cipher->interface(); // xgroup(diag) cout << autosprintf(_(" (using %i:%i:%i)\n"), iface.major(), iface.minor(), iface.age()); } else cout << "\n"; } } // xgroup(diag) cout << autosprintf(_("Filename encoding: \"%s\", version %i:%i:%i"), config.naming().name().c_str(), config.naming().major(), config.naming().minor(), config.naming().age()); if (!cipher) { cout << "\n"; } else { // check if we support the filename encoding interface.. shared_ptr<NameIO> nameCoder = NameIO::New( config.naming(), cipher ); if(!nameCoder) { // xgroup(diag) cout << _(" (NOT supported)\n"); } else { // if we're using a newer interface, show the version number if( config.naming() != nameCoder->interface() ) { Interface iface = nameCoder->interface(); cout << autosprintf(_(" (using %i:%i:%i)\n"), iface.major(), iface.minor(), iface.age()); } else cout << "\n"; } } const EncryptedKey &key = config.key(); { cout << autosprintf(_("Key Size: %i bits"), 8 * key.size()); cipher = getCipher(config); if(!cipher) { // xgroup(diag) cout << _(" (NOT supported)\n"); } else cout << "\n"; } if(key.kdf_iterations() > 0 && key.salt().size() > 0) { cout << autosprintf(_("Using PBKDF2, with %i iterations"), key.kdf_iterations()) << "\n"; cout << autosprintf(_("Salt Size: %i bits"), 8*(int)key.salt().size()) << "\n"; } if(config.block_mac_bytes() || config.block_mac_rand_bytes()) { if(config.revision() < V5Latest) { cout << autosprintf( // xgroup(diag) _("Block Size: %i bytes + %i byte MAC header"), config.block_size(), config.block_mac_bytes() + config.block_mac_rand_bytes()) << endl; } else { // new version stores the header as part of that block size.. cout << autosprintf( // xgroup(diag) _("Block Size: %i bytes, including %i byte MAC header"), config.block_size(), config.block_mac_bytes() + config.block_mac_rand_bytes()) << endl; } } else { // xgroup(diag) cout << autosprintf(_("Block Size: %i bytes"), config.block_size()); cout << "\n"; } if(config.unique_iv()) { // xgroup(diag) cout << _("Each file contains 8 byte header with unique IV data.\n"); } if(config.chained_iv()) { // xgroup(diag) cout << _("Filenames encoded using IV chaining mode.\n"); } if(config.external_iv()) { // xgroup(diag) cout << _("File data IV is chained to filename IV.\n"); } if(config.allow_holes()) { // xgroup(diag) cout << _("File holes passed through to ciphertext.\n"); } cout << "\n"; }
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.mutable_cipher()->MergeFrom(cipher->interface()); EncryptedKey *encryptedKey = cfg.mutable_key(); encryptedKey->set_size(8 * cipher->keySize()); encryptedKey->set_ciphertext( keyBuf, encodedKeySize ); cfg.set_block_size(FSBlockSize); // save config string data; google::protobuf::TextFormat::PrintToString(cfg, &data); // read back in and check everything.. EncfsConfig cfg2; google::protobuf::TextFormat::ParseFromString(data, &cfg2); // check.. rAssert( implements(cfg.cipher(),cfg2.cipher()) ); rAssert( cfg.key().size() == cfg2.key().size() ); rAssert( cfg.block_size() == cfg2.block_size() ); // try decoding key.. CipherKey key2 = cipher->readKey( (unsigned char *)cfg2.key().ciphertext().data(), 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->set_block_size(FSBlockSize); fsCfg->opts.reset(new EncFS_Opts); if(verbose) cerr << "Testing name encode/decode (stream coding w/ IV chaining)\n"; if (cipher->hasStreamMode()) { fsCfg->opts->idleTracking = false; fsCfg->config->set_unique_iv(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->set_unique_iv(false); fsCfg->nameCoding.reset( new BlockNameIO( BlockNameIO::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, base32)\n"; { fsCfg->opts->idleTracking = false; fsCfg->config->set_unique_iv(false); fsCfg->nameCoding.reset( new BlockNameIO( BlockNameIO::CurrentInterface(), cipher, key, true ) ); fsCfg->nameCoding->setChainedNameIV( true ); DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); if(!testNameCoding( dirNode, verbose )) return false; } if(!verbose) { if (cipher->hasStreamMode()) { // 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 ) ); 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 - "; if (cipher->hasStreamMode()) { 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"; if (cipher->hasStreamMode()) { 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, int argc, char **argv ) { (void)argc; string rootDir = argv[1]; if( !checkDir( rootDir )) return EXIT_FAILURE; EncfsConfig config; ConfigType cfgType = readConfig( rootDir, config ); if(cfgType == Config_None) { cout << _("Unable to load or parse config file\n"); return EXIT_FAILURE; } // instanciate proper cipher shared_ptr<Cipher> cipher = getCipher(config); if(!cipher) { cout << autosprintf(_("Unable to find specified cipher \"%s\"\n"), config.cipher().name().c_str()); return EXIT_FAILURE; } // ask for existing password cout << _("Enter current Encfs password\n"); if (annotate) cerr << "$PROMPT$ passwd" << endl; CipherKey userKey = getUserKey( config, 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( (const unsigned char *)config.key().ciphertext().data(), userKey ); if(!volumeKey) { cout << _("Invalid password\n"); return EXIT_FAILURE; } // Now, get New user key.. userKey.reset(); cout << _("Enter new Encfs password\n"); // create new key if( useStdin ) { if (annotate) cerr << "$PROMPT$ new_passwd" << endl; } userKey = getNewUserKey( config, useStdin, string(), string() ); // 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(); EncryptedKey *key = config.mutable_key(); key->set_ciphertext( keyBuf, encodedKeySize ); delete[] keyBuf; if(saveConfig( 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; }