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