File indexing completed on 2024-06-16 03:53:04

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2001-2004 George Staikos <staikos@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kwalletbackend.h"
0009 #include "kwalletbackend_debug.h"
0010 
0011 #include <stdlib.h>
0012 
0013 #include <QSaveFile>
0014 #ifdef HAVE_GPGMEPP
0015 #include <gpgme++/key.h>
0016 #endif
0017 #include <gcrypt.h>
0018 #include <KNotification>
0019 #include <KLocalizedString>
0020 
0021 #include <QDir>
0022 #include <QFile>
0023 #include <QFileInfo>
0024 #include <QSaveFile>
0025 #include <QCryptographicHash>
0026 #include <QRegularExpression>
0027 #include <QStandardPaths>
0028 
0029 #include "blowfish.h"
0030 #include "sha1.h"
0031 #include "cbc.h"
0032 
0033 #include <assert.h>
0034 #include <cerrno>
0035 
0036 // quick fix to get random numbers on win32
0037 #ifdef Q_OS_WIN //krazy:exclude=cpp
0038 #include <windows.h>
0039 #include <wincrypt.h>
0040 #endif
0041 
0042 #define KWALLETSTORAGE_VERSION_MAJOR       0
0043 #define KWALLETSTORAGE_VERSION_MINOR       1
0044 
0045 using namespace KWallet;
0046 
0047 #define KWMAGIC "KWALLET\n\r\0\r\n"
0048 
0049 static const QByteArray walletAllowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789^&'@{}[],$=!-#()%.+_\r\n\t\f\v ";
0050 
0051 /* The encoding works if the name contains at least one unsupported character.
0052  * Names that were allowed prior to the Secret Service API patch remain intact.
0053  */
0054 QString Backend::encodeWalletName(const QString &name) {
0055     /* Use a semicolon as "percent" because it does not conflict with already allowed characters for wallet names
0056      * and is allowed for file names
0057      */
0058     return QString::fromUtf8(name.toUtf8().toPercentEncoding(walletAllowedChars, {}, ';'));
0059 }
0060 
0061 QString Backend::decodeWalletName(const QString &encodedName) {
0062     return QString::fromUtf8(QByteArray::fromPercentEncoding(encodedName.toUtf8(), ';'));
0063 }
0064 
0065 class Backend::BackendPrivate
0066 {
0067 };
0068 
0069 // static void initKWalletDir()
0070 // {
0071 //     KGlobal::dirs()->addResourceType("kwallet", 0, "share/apps/kwallet");
0072 // }
0073 
0074 Backend::Backend(const QString &name, bool isPath)
0075     : d(nullptr),
0076       _name(name),
0077       _cipherType(KWallet::BACKEND_CIPHER_UNKNOWN)
0078 {
0079 //  initKWalletDir();
0080     if (isPath) {
0081         _path = name;
0082     } else {
0083         _path = getSaveLocation() + '/' + encodeWalletName(_name) + ".kwl";
0084     }
0085 
0086     _open = false;
0087 }
0088 
0089 Backend::~Backend()
0090 {
0091     if (_open) {
0092         close();
0093     }
0094     delete d;
0095 }
0096 
0097 QString Backend::getSaveLocation()
0098 {
0099     QString writeLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kwalletd");
0100     QDir writeDir(writeLocation);
0101     if (!writeDir.exists()) {
0102         if (!writeDir.mkpath(writeLocation)) {
0103             qFatal("Cannot create wallet save location!");
0104         }
0105     }
0106 
0107     // qCDebug(KWALLETBACKEND_LOG) << "Using saveLocation " + writeLocation;
0108     return writeLocation;
0109 }
0110 
0111 void Backend::setCipherType(BackendCipherType ct)
0112 {
0113     // changing cipher type on already initialed wallets is not permitted
0114     assert(_cipherType == KWallet::BACKEND_CIPHER_UNKNOWN);
0115     _cipherType = ct;
0116 }
0117 
0118 static int password2PBKDF2_SHA512(const QByteArray &password, QByteArray &hash, const QByteArray &salt)
0119 {
0120     if (!gcry_check_version("1.5.0")) {
0121         qCWarning(KWALLETBACKEND_LOG) << "libcrypt version is too old";
0122         return GPG_ERR_USER_2;
0123     }
0124 
0125     gcry_error_t error;
0126     bool static gcry_secmem_init = false;
0127     if (!gcry_secmem_init) {
0128         error = gcry_control(GCRYCTL_INIT_SECMEM, 32768, 0);
0129         if (error != 0) {
0130             qCWarning(KWALLETBACKEND_LOG) << "Can't get secure memory:" << error;
0131             return error;
0132         }
0133         gcry_secmem_init = true;
0134     }
0135 
0136     gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
0137 
0138     error = gcry_kdf_derive(password.constData(), password.size(),
0139                             GCRY_KDF_PBKDF2, GCRY_MD_SHA512,
0140                             salt.data(), salt.size(),
0141                             PBKDF2_SHA512_ITERATIONS, PBKDF2_SHA512_KEYSIZE, hash.data());
0142 
0143     return error;
0144 }
0145 
0146 // this should be SHA-512 for release probably
0147 static int password2hash(const QByteArray &password, QByteArray &hash)
0148 {
0149     SHA1 sha;
0150     int shasz = sha.size() / 8;
0151 
0152     assert(shasz >= 20);
0153 
0154     QByteArray block1(shasz, 0);
0155 
0156     sha.process(password.data(), qMin(password.size(), 16));
0157 
0158     // To make brute force take longer
0159     for (int i = 0; i < 2000; i++) {
0160         memcpy(block1.data(), sha.hash(), shasz);
0161         sha.reset();
0162         sha.process(block1.data(), shasz);
0163     }
0164 
0165     sha.reset();
0166 
0167     if (password.size() > 16) {
0168         sha.process(password.data() + 16, qMin(password.size() - 16, 16));
0169         QByteArray block2(shasz, 0);
0170         // To make brute force take longer
0171         for (int i = 0; i < 2000; i++) {
0172             memcpy(block2.data(), sha.hash(), shasz);
0173             sha.reset();
0174             sha.process(block2.data(), shasz);
0175         }
0176 
0177         sha.reset();
0178 
0179         if (password.size() > 32) {
0180             sha.process(password.data() + 32, qMin(password.size() - 32, 16));
0181 
0182             QByteArray block3(shasz, 0);
0183             // To make brute force take longer
0184             for (int i = 0; i < 2000; i++) {
0185                 memcpy(block3.data(), sha.hash(), shasz);
0186                 sha.reset();
0187                 sha.process(block3.data(), shasz);
0188             }
0189 
0190             sha.reset();
0191 
0192             if (password.size() > 48) {
0193                 sha.process(password.data() + 48, password.size() - 48);
0194 
0195                 QByteArray block4(shasz, 0);
0196                 // To make brute force take longer
0197                 for (int i = 0; i < 2000; i++) {
0198                     memcpy(block4.data(), sha.hash(), shasz);
0199                     sha.reset();
0200                     sha.process(block4.data(), shasz);
0201                 }
0202 
0203                 sha.reset();
0204                 // split 14/14/14/14
0205                 hash.resize(56);
0206                 memcpy(hash.data(),      block1.data(), 14);
0207                 memcpy(hash.data() + 14, block2.data(), 14);
0208                 memcpy(hash.data() + 28, block3.data(), 14);
0209                 memcpy(hash.data() + 42, block4.data(), 14);
0210                 block4.fill(0);
0211             } else {
0212                 // split 20/20/16
0213                 hash.resize(56);
0214                 memcpy(hash.data(),      block1.data(), 20);
0215                 memcpy(hash.data() + 20, block2.data(), 20);
0216                 memcpy(hash.data() + 40, block3.data(), 16);
0217             }
0218             block3.fill(0);
0219         } else {
0220             // split 20/20
0221             hash.resize(40);
0222             memcpy(hash.data(),      block1.data(), 20);
0223             memcpy(hash.data() + 20, block2.data(), 20);
0224         }
0225         block2.fill(0);
0226     } else {
0227         // entirely block1
0228         hash.resize(20);
0229         memcpy(hash.data(), block1.data(), 20);
0230     }
0231 
0232     block1.fill(0);
0233 
0234     return 0;
0235 }
0236 
0237 int Backend::deref()
0238 {
0239     if (--_ref < 0) {
0240         qCDebug(KWALLETBACKEND_LOG) << "refCount negative!";
0241         _ref = 0;
0242     }
0243     return _ref;
0244 }
0245 
0246 bool Backend::exists(const QString &wallet)
0247 {
0248     QString saveLocation = getSaveLocation();
0249     QString path = saveLocation + '/' + encodeWalletName(wallet) + QLatin1String(".kwl");
0250     // Note: 60 bytes is presently the minimum size of a wallet file.
0251     //       Anything smaller is junk.
0252     return QFile::exists(path) && QFileInfo(path).size() >= 60;
0253 }
0254 
0255 QString Backend::openRCToString(int rc)
0256 {
0257     switch (rc) {
0258     case -255:
0259         return i18n("Already open.");
0260     case -2:
0261         return i18n("Error opening file.");
0262     case -3:
0263         return i18n("Not a wallet file.");
0264     case -4:
0265         return i18n("Unsupported file format revision.");
0266     case -41:
0267         return QStringLiteral("Unknown cipher or hash"); //FIXME: use i18n after string freeze
0268     case -42:
0269         return i18n("Unknown encryption scheme.");
0270     case -43:
0271         return i18n("Corrupt file?");
0272     case -8:
0273         return i18n("Error validating wallet integrity. Possibly corrupted.");
0274     case -5:
0275     case -7:
0276     case -9:
0277         return i18n("Read error - possibly incorrect password.");
0278     case -6:
0279         return i18n("Decryption error.");
0280     default:
0281         return QString();
0282     }
0283 }
0284 
0285 int Backend::open(const QByteArray &password, WId w)
0286 {
0287     if (_open) {
0288         return -255;  // already open
0289     }
0290 
0291     setPassword(password);
0292     return openInternal(w);
0293 }
0294 
0295 #ifdef HAVE_GPGMEPP
0296 int Backend::open(const GpgME::Key &key)
0297 {
0298     if (_open) {
0299         return -255;  // already open
0300     }
0301     _gpgKey = key;
0302     return openInternal();
0303 }
0304 #endif // HAVE_GPGMEPP
0305 
0306 int Backend::openPreHashed(const QByteArray &passwordHash)
0307 {
0308     if (_open) {
0309         return -255;  // already open
0310     }
0311 
0312     // check the password hash for correct size (currently fixed)
0313     if (passwordHash.size() != 20 && passwordHash.size() != 40 &&
0314             passwordHash.size() != 56) {
0315         return -42; // unsupported encryption scheme
0316     }
0317 
0318     _passhash = passwordHash;
0319     _newPassHash = passwordHash;
0320     _useNewHash = true;//Only new hash is supported
0321 
0322     return openInternal();
0323 }
0324 
0325 int Backend::openInternal(WId w)
0326 {
0327     // No wallet existed.  Let's create it.
0328     // Note: 60 bytes is presently the minimum size of a wallet file.
0329     //       Anything smaller is junk and should be deleted.
0330     if (!QFile::exists(_path) || QFileInfo(_path).size() < 60) {
0331         QFile newfile(_path);
0332         if (!newfile.open(QIODevice::ReadWrite)) {
0333             return -2;   // error opening file
0334         }
0335         newfile.close();
0336         _open = true;
0337         if (sync(w) != 0) {
0338             return -2;
0339         }
0340     }
0341 
0342     QFile db(_path);
0343 
0344     if (!db.open(QIODevice::ReadOnly)) {
0345         return -2;         // error opening file
0346     }
0347 
0348     char magicBuf[KWMAGIC_LEN];
0349     db.read(magicBuf, KWMAGIC_LEN);
0350     if (memcmp(magicBuf, KWMAGIC, KWMAGIC_LEN) != 0) {
0351         return -3;         // bad magic
0352     }
0353 
0354     db.read(magicBuf, 4);
0355 
0356     // First byte is major version, second byte is minor version
0357     if (magicBuf[0] != KWALLETSTORAGE_VERSION_MAJOR) {
0358         return -4;         // unknown version
0359     }
0360 
0361     //0 has been the MINOR version until 4.13, from that point we use it to upgrade the hash
0362     if (magicBuf[1] == 1) {
0363         qCDebug(KWALLETBACKEND_LOG) << "Wallet new enough, using new hash";
0364         swapToNewHash();
0365     } else if (magicBuf[1] != 0) {
0366         qCDebug(KWALLETBACKEND_LOG) << "Wallet is old, sad panda :(";
0367         return -4;  // unknown version
0368     }
0369 
0370     BackendPersistHandler *phandler = BackendPersistHandler::getPersistHandler(magicBuf);
0371     if (nullptr == phandler) {
0372         return -41; // unknown cipher or hash
0373     }
0374     int result = phandler->read(this, db, w);
0375     delete phandler;
0376     return result;
0377 }
0378 
0379 void Backend::swapToNewHash()
0380 {
0381     //Runtime error happened and we can't use the new hash
0382     if (!_useNewHash) {
0383         qCDebug(KWALLETBACKEND_LOG) << "Runtime error on the new hash";
0384         return;
0385     }
0386     _passhash.fill(0);//Making sure the old passhash is not around in memory
0387     _passhash = _newPassHash;//Use the new hash, means the wallet is modern enough
0388 }
0389 
0390 QByteArray Backend::createAndSaveSalt(const QString &path) const
0391 {
0392     QFile saltFile(path);
0393     saltFile.remove();
0394 
0395     if (!saltFile.open(QIODevice::WriteOnly)) {
0396         return QByteArray();
0397     }
0398     saltFile.setPermissions(QFile::ReadUser | QFile::WriteUser);
0399 
0400     char *randomData = (char *) gcry_random_bytes(PBKDF2_SHA512_SALTSIZE, GCRY_STRONG_RANDOM);
0401     QByteArray salt(randomData, PBKDF2_SHA512_SALTSIZE);
0402     free(randomData);
0403 
0404     if (saltFile.write(salt) != PBKDF2_SHA512_SALTSIZE) {
0405         return QByteArray();
0406     }
0407 
0408     saltFile.close();
0409 
0410     return salt;
0411 }
0412 
0413 int Backend::sync(WId w)
0414 {
0415     if (!_open) {
0416         return -255;  // not open yet
0417     }
0418 
0419     if (!QFile::exists(_path)) {
0420         return -3; // File does not exist
0421     }
0422 
0423     QSaveFile sf(_path);
0424 
0425     if (!sf.open(QIODevice::WriteOnly | QIODevice::Unbuffered)) {
0426         return -1;      // error opening file
0427     }
0428     sf.setPermissions(QFile::ReadUser | QFile::WriteUser);
0429 
0430     if (sf.write(KWMAGIC, KWMAGIC_LEN) != KWMAGIC_LEN) {
0431         sf.cancelWriting();
0432         return -4; // write error
0433     }
0434 
0435     // Write the version number
0436     QByteArray version(4, 0);
0437     version[0] = KWALLETSTORAGE_VERSION_MAJOR;
0438     if (_useNewHash) {
0439         version[1] = KWALLETSTORAGE_VERSION_MINOR;
0440         //Use the sync to update the hash to PBKDF2_SHA512
0441         swapToNewHash();
0442     } else {
0443         version[1] = 0; //was KWALLETSTORAGE_VERSION_MINOR before the new hash
0444     }
0445 
0446     BackendPersistHandler *phandler = BackendPersistHandler::getPersistHandler(_cipherType);
0447     if (nullptr == phandler) {
0448         return -4; // write error
0449     }
0450     int rc = phandler->write(this, sf, version, w);
0451     if (rc < 0) {
0452         // Oops! wallet file sync filed! Display a notification about that
0453         // TODO: change kwalletd status flags, when status flags will be implemented
0454         KNotification *notification = new KNotification(QStringLiteral("syncFailed"));
0455         notification->setText(i18n("Failed to sync wallet <b>%1</b> to disk. Error codes are:\nRC <b>%2</b>\nSF <b>%3</b>. Please file a BUG report using this information to bugs.kde.org", _name, rc, sf.errorString()));
0456         notification->sendEvent();
0457     }
0458     delete phandler;
0459     return rc;
0460 }
0461 
0462 int Backend::closeInternal(bool save)
0463 {
0464     // save if requested
0465     if (save) {
0466         int rc = sync(0);
0467         if (rc != 0) {
0468             return rc;
0469         }
0470     }
0471 
0472     // do the actual close
0473     for (FolderMap::ConstIterator i = _entries.constBegin(); i != _entries.constEnd(); ++i) {
0474         for (EntryMap::ConstIterator j = i.value().constBegin(); j != i.value().constEnd(); ++j) {
0475             delete j.value();
0476         }
0477     }
0478     _entries.clear();
0479     _open = false;
0480 
0481     return 0;
0482 }
0483 
0484 int Backend::close(bool save)
0485 {
0486     int rc = closeInternal(save);
0487     if (rc)
0488         return rc;
0489 
0490     // empty the password hash
0491     _passhash.fill(0);
0492     _newPassHash.fill(0);
0493 
0494     return 0;
0495 }
0496 
0497 const QString &Backend::walletName() const
0498 {
0499     return _name;
0500 }
0501 
0502 int Backend::renameWallet(const QString &newName, bool isPath)
0503 {
0504     QString newPath;
0505     const auto saveLocation = getSaveLocation();
0506 
0507     if (isPath) {
0508         newPath = newName;
0509     } else {
0510         newPath = saveLocation + QChar::fromLatin1('/') + encodeWalletName(newName) + QStringLiteral(".kwl");
0511     }
0512 
0513     if (newPath == _path) {
0514         return 0;
0515     }
0516 
0517     if (QFile::exists(newPath)) {
0518         return -EEXIST;
0519     }
0520 
0521     int rc = closeInternal(true);
0522     if (rc) {
0523         return rc;
0524     }
0525 
0526     QFile::rename(_path, newPath);
0527     QFile::rename(saveLocation + QChar::fromLatin1('/') + encodeWalletName(_name) + QStringLiteral(".salt"),
0528                   saveLocation + QChar::fromLatin1('/') + encodeWalletName(newName) + QStringLiteral(".salt"));
0529 
0530     _name = newName;
0531     _path = newPath;
0532 
0533     rc = openInternal();
0534     if (rc) {
0535         return rc;
0536     }
0537 
0538     return 0;
0539 }
0540 
0541 bool Backend::isOpen() const
0542 {
0543     return _open;
0544 }
0545 
0546 QStringList Backend::folderList() const
0547 {
0548     return _entries.keys();
0549 }
0550 
0551 QStringList Backend::entryList() const
0552 {
0553     return _entries[_folder].keys();
0554 }
0555 
0556 Entry *Backend::readEntry(const QString &key)
0557 {
0558     Entry *rc = nullptr;
0559 
0560     if (_open && hasEntry(key)) {
0561         rc = _entries[_folder][key];
0562     }
0563 
0564     return rc;
0565 }
0566 
0567 #if KWALLET_BUILD_DEPRECATED_SINCE(5, 72)
0568 QList<Entry *> Backend::readEntryList(const QString &key)
0569 {
0570     QList<Entry *> rc;
0571 
0572     if (!_open) {
0573         return rc;
0574     }
0575 
0576     // HACK: see Wallet::WalletPrivate::forEachItemThatMatches()
0577     const QString pattern = QRegularExpression::wildcardToRegularExpression(key).replace(
0578                                                          QLatin1String("[^/]"), QLatin1String("."));
0579     const QRegularExpression re(pattern);
0580 
0581     const EntryMap &map = _entries[_folder];
0582     for (EntryMap::ConstIterator i = map.begin(); i != map.end(); ++i) {
0583         if (re.match(i.key()).hasMatch()) {
0584             rc.append(i.value());
0585         }
0586     }
0587     return rc;
0588 }
0589 #endif
0590 
0591 QList<Entry *> Backend::entriesList() const
0592 {
0593     if (!_open) {
0594         return QList<Entry *>();
0595     }
0596     const EntryMap &map = _entries[_folder];
0597 
0598     return map.values();
0599 }
0600 
0601 
0602 bool Backend::createFolder(const QString &f)
0603 {
0604     if (_entries.contains(f)) {
0605         return false;
0606     }
0607 
0608     _entries.insert(f, EntryMap());
0609 
0610     QCryptographicHash folderMd5(QCryptographicHash::Md5);
0611     folderMd5.addData(f.toUtf8());
0612     _hashes.insert(MD5Digest(folderMd5.result()), QList<MD5Digest>());
0613 
0614     return true;
0615 }
0616 
0617 int Backend::renameEntry(const QString &oldName, const QString &newName)
0618 {
0619     EntryMap &emap = _entries[_folder];
0620     EntryMap::Iterator oi = emap.find(oldName);
0621     EntryMap::Iterator ni = emap.find(newName);
0622 
0623     if (oi != emap.end() && ni == emap.end()) {
0624         Entry *e = oi.value();
0625         emap.erase(oi);
0626         emap[newName] = e;
0627 
0628         QCryptographicHash folderMd5(QCryptographicHash::Md5);
0629         folderMd5.addData(_folder.toUtf8());
0630 
0631         HashMap::iterator i = _hashes.find(MD5Digest(folderMd5.result()));
0632         if (i != _hashes.end()) {
0633             QCryptographicHash oldMd5(QCryptographicHash::Md5);
0634             QCryptographicHash newMd5(QCryptographicHash::Md5);
0635             oldMd5.addData(oldName.toUtf8());
0636             newMd5.addData(newName.toUtf8());
0637             i.value().removeAll(MD5Digest(oldMd5.result()));
0638             i.value().append(MD5Digest(newMd5.result()));
0639         }
0640         return 0;
0641     }
0642 
0643     return -1;
0644 }
0645 
0646 void Backend::writeEntry(Entry *e)
0647 {
0648     if (!_open) {
0649         return;
0650     }
0651 
0652     if (!hasEntry(e->key())) {
0653         _entries[_folder][e->key()] = new Entry;
0654     }
0655     _entries[_folder][e->key()]->copy(e);
0656 
0657     QCryptographicHash folderMd5(QCryptographicHash::Md5);
0658     folderMd5.addData(_folder.toUtf8());
0659 
0660     HashMap::iterator i = _hashes.find(MD5Digest(folderMd5.result()));
0661     if (i != _hashes.end()) {
0662         QCryptographicHash md5(QCryptographicHash::Md5);
0663         md5.addData(e->key().toUtf8());
0664         i.value().append(MD5Digest(md5.result()));
0665     }
0666 }
0667 
0668 bool Backend::hasEntry(const QString &key) const
0669 {
0670     return _entries.contains(_folder) && _entries[_folder].contains(key);
0671 }
0672 
0673 bool Backend::removeEntry(const QString &key)
0674 {
0675     if (!_open) {
0676         return false;
0677     }
0678 
0679     FolderMap::Iterator fi = _entries.find(_folder);
0680     EntryMap::Iterator ei = fi.value().find(key);
0681 
0682     if (fi != _entries.end() && ei != fi.value().end()) {
0683         delete ei.value();
0684         fi.value().erase(ei);
0685         QCryptographicHash folderMd5(QCryptographicHash::Md5);
0686         folderMd5.addData(_folder.toUtf8());
0687 
0688         HashMap::iterator i = _hashes.find(MD5Digest(folderMd5.result()));
0689         if (i != _hashes.end()) {
0690             QCryptographicHash md5(QCryptographicHash::Md5);
0691             md5.addData(key.toUtf8());
0692             i.value().removeAll(MD5Digest(md5.result()));
0693         }
0694         return true;
0695     }
0696 
0697     return false;
0698 }
0699 
0700 bool Backend::removeFolder(const QString &f)
0701 {
0702     if (!_open) {
0703         return false;
0704     }
0705 
0706     FolderMap::Iterator fi = _entries.find(f);
0707 
0708     if (fi != _entries.end()) {
0709         if (_folder == f) {
0710             _folder.clear();
0711         }
0712 
0713         for (EntryMap::Iterator ei = fi.value().begin(); ei != fi.value().end(); ++ei) {
0714             delete ei.value();
0715         }
0716 
0717         _entries.erase(fi);
0718 
0719         QCryptographicHash folderMd5(QCryptographicHash::Md5);
0720         folderMd5.addData(f.toUtf8());
0721         _hashes.remove(MD5Digest(folderMd5.result()));
0722         return true;
0723     }
0724 
0725     return false;
0726 }
0727 
0728 bool Backend::folderDoesNotExist(const QString &folder) const
0729 {
0730     QCryptographicHash md5(QCryptographicHash::Md5);
0731     md5.addData(folder.toUtf8());
0732     return !_hashes.contains(MD5Digest(md5.result()));
0733 }
0734 
0735 bool Backend::entryDoesNotExist(const QString &folder, const QString &entry) const
0736 {
0737     QCryptographicHash md5(QCryptographicHash::Md5);
0738     md5.addData(folder.toUtf8());
0739     HashMap::const_iterator i = _hashes.find(MD5Digest(md5.result()));
0740     if (i != _hashes.end()) {
0741         md5.reset();
0742         md5.addData(entry.toUtf8());
0743         return !i.value().contains(MD5Digest(md5.result()));
0744     }
0745     return true;
0746 }
0747 
0748 void Backend::setPassword(const QByteArray &password)
0749 {
0750     _passhash.fill(0); // empty just in case
0751     BlowFish _bf;
0752     CipherBlockChain bf(&_bf);
0753     _passhash.resize(bf.keyLen() / 8);
0754     _newPassHash.resize(bf.keyLen() / 8);
0755     _newPassHash.fill(0);
0756 
0757     password2hash(password, _passhash);
0758 
0759     QByteArray salt;
0760     QFile saltFile(getSaveLocation() + '/' + encodeWalletName(_name) + ".salt");
0761     if (!saltFile.exists() || saltFile.size() == 0) {
0762         salt = createAndSaveSalt(saltFile.fileName());
0763     } else {
0764         if (!saltFile.open(QIODevice::ReadOnly)) {
0765             salt = createAndSaveSalt(saltFile.fileName());
0766         } else {
0767             salt = saltFile.readAll();
0768         }
0769     }
0770 
0771     if (!salt.isEmpty() && password2PBKDF2_SHA512(password, _newPassHash,  salt) == 0) {
0772         qCDebug(KWALLETBACKEND_LOG) << "Setting useNewHash to true";
0773         _useNewHash = true;
0774     }
0775 }
0776 
0777 #ifdef HAVE_GPGMEPP
0778 const GpgME::Key &Backend::gpgKey() const
0779 {
0780     return _gpgKey;
0781 }
0782 #endif