KWallet

backendpersisthandler.cpp
1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 2013 Valentin Rusu <kde@rusu.info>
4
5 SPDX-License-Identifier: LGPL-2.0-only
6*/
7
8#include "backendpersisthandler.h"
9#include "kwalletbackend_debug.h"
10
11#include <KLocalizedString>
12#include <KMessageBox>
13#include <QCryptographicHash>
14#include <QFile>
15#include <QIODevice>
16#include <QSaveFile>
17#include <assert.h>
18#ifdef HAVE_GPGMEPP
19#include <gpgme++/context.h>
20#include <gpgme++/data.h>
21#include <gpgme++/decryptionresult.h>
22#include <gpgme++/encryptionresult.h>
23#include <gpgme++/key.h>
24#include <gpgme++/keylistresult.h>
25#endif
26#include "blowfish.h"
27#include "cbc.h"
28#include "kwalletbackend.h"
29#include "sha1.h"
30
31#ifdef Q_OS_WIN
32#include <windows.h> // Must be included before wincrypt.h
33
34#include <wincrypt.h>
35#endif
36
37#define KWALLET_CIPHER_BLOWFISH_ECB 0 // this was the old KWALLET_CIPHER_BLOWFISH_CBC
38#define KWALLET_CIPHER_3DES_CBC 1 // unsupported
39#define KWALLET_CIPHER_GPG 2
40#define KWALLET_CIPHER_BLOWFISH_CBC 3
41
42#define KWALLET_HASH_SHA1 0
43#define KWALLET_HASH_MD5 1 // unsupported
44#define KWALLET_HASH_PBKDF2_SHA512 2 // used when using kwallet with pam or since 4.13 version
45
46namespace KWallet
47{
48typedef char Digest[16];
49
50static int getRandomBlock(QByteArray &randBlock)
51{
52#ifdef Q_OS_WIN // krazy:exclude=cpp
53
54 // Use windows crypto API to get randomness on win32
55 // HACK: this should be done using qca
56 HCRYPTPROV hProv;
57
58 if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
59 return -1; // couldn't get random data
60 }
61
62 if (!CryptGenRandom(hProv, static_cast<DWORD>(randBlock.size()), (BYTE *)randBlock.data())) {
63 return -3; // read error
64 }
65
66 // release the crypto context
67 CryptReleaseContext(hProv, 0);
68
69 return randBlock.size();
70
71#else
72
73 // First try /dev/urandom
74 if (QFile::exists(QStringLiteral("/dev/urandom"))) {
75 QFile devrand(QStringLiteral("/dev/urandom"));
76 if (devrand.open(QIODevice::ReadOnly)) {
77 int rc = devrand.read(randBlock.data(), randBlock.size());
78
79 if (rc != randBlock.size()) {
80 return -3; // not enough data read
81 }
82
83 return 0;
84 }
85 }
86
87 // If that failed, try /dev/random
88 // FIXME: open in noblocking mode!
89 if (QFile::exists(QStringLiteral("/dev/random"))) {
90 QFile devrand(QStringLiteral("/dev/random"));
91 if (devrand.open(QIODevice::ReadOnly)) {
92 int rc = 0;
93 int cnt = 0;
94
95 do {
96 int rc2 = devrand.read(randBlock.data() + rc, randBlock.size());
97
98 if (rc2 < 0) {
99 return -3; // read error
100 }
101
102 rc += rc2;
103 cnt++;
104 if (cnt > randBlock.size()) {
105 return -4; // reading forever?!
106 }
107 } while (rc < randBlock.size());
108
109 return 0;
110 }
111 }
112
113 // EGD method
114 QString randFilename = QString::fromLocal8Bit(qgetenv("RANDFILE"));
115 if (!randFilename.isEmpty()) {
116 if (QFile::exists(randFilename)) {
117 QFile devrand(randFilename);
118 if (devrand.open(QIODevice::ReadOnly)) {
119 int rc = devrand.read(randBlock.data(), randBlock.size());
120 if (rc != randBlock.size()) {
121 return -3; // not enough data read
122 }
123 return 0;
124 }
125 }
126 }
127
128 // Couldn't get any random data!!
129 return -1;
130
131#endif
132}
133
134BackendPersistHandler *BackendPersistHandler::getPersistHandler(BackendCipherType cipherType)
135{
136 switch (cipherType) {
137 case BACKEND_CIPHER_BLOWFISH:
138 return new BlowfishPersistHandler;
139#ifdef HAVE_GPGMEPP
140 case BACKEND_CIPHER_GPG:
141 return new GpgPersistHandler;
142#endif // HAVE_GPGMEPP
143 default:
144 Q_ASSERT(0);
145 return nullptr;
146 }
147}
148
149BackendPersistHandler *BackendPersistHandler::getPersistHandler(char magicBuf[12])
150{
151 if ((magicBuf[2] == KWALLET_CIPHER_BLOWFISH_ECB || magicBuf[2] == KWALLET_CIPHER_BLOWFISH_CBC)
152 && (magicBuf[3] == KWALLET_HASH_SHA1 || magicBuf[3] == KWALLET_HASH_PBKDF2_SHA512)) {
153 bool useECBforReading = magicBuf[2] == KWALLET_CIPHER_BLOWFISH_ECB;
154 if (useECBforReading) {
155 qCDebug(KWALLETBACKEND_LOG) << "this wallet uses ECB encryption. It'll be converted to CBC on next save.";
156 }
157 return new BlowfishPersistHandler(useECBforReading);
158 }
159#ifdef HAVE_GPGMEPP
160 if (magicBuf[2] == KWALLET_CIPHER_GPG && magicBuf[3] == 0) {
161 return new GpgPersistHandler;
162 }
163#endif // HAVE_GPGMEPP
164 return nullptr; // unknown cipher or hash
165}
166
167int BlowfishPersistHandler::write(Backend *wb, QSaveFile &sf, QByteArray &version, WId)
168{
169 assert(wb->_cipherType == BACKEND_CIPHER_BLOWFISH);
170
171 if (_useECBforReading) {
172 qCDebug(KWALLETBACKEND_LOG) << "This wallet used ECB and is now saved using CBC";
173 _useECBforReading = false;
174 }
175
176 version[2] = KWALLET_CIPHER_BLOWFISH_CBC;
177 if (!wb->_useNewHash) {
178 version[3] = KWALLET_HASH_SHA1;
179 } else {
180 version[3] = KWALLET_HASH_PBKDF2_SHA512; // Since 4.13 we always use PBKDF2_SHA512
181 }
182
183 if (sf.write(version) != 4) {
184 sf.cancelWriting();
185 return -4; // write error
186 }
187
188 // Holds the hashes we write out
189 QByteArray hashes;
190 QDataStream hashStream(&hashes, QIODevice::WriteOnly);
192 hashStream << static_cast<quint32>(wb->_entries.count());
193
194 // Holds decrypted data prior to encryption
195 QByteArray decrypted;
196
197 // FIXME: we should estimate the amount of data we will write in each
198 // buffer and resize them approximately in order to avoid extra
199 // resizes.
200
201 // populate decrypted
202 QDataStream dStream(&decrypted, QIODevice::WriteOnly);
203 for (Backend::FolderMap::ConstIterator i = wb->_entries.constBegin(); i != wb->_entries.constEnd(); ++i) {
204 dStream << i.key();
205 dStream << static_cast<quint32>(i.value().count());
206
207 md5.reset();
208 md5.addData(i.key().toUtf8());
209 hashStream.writeRawData(md5.result().constData(), 16);
210 hashStream << static_cast<quint32>(i.value().count());
211
212 for (Backend::EntryMap::ConstIterator j = i.value().constBegin(); j != i.value().constEnd(); ++j) {
213 dStream << j.key();
214 dStream << static_cast<qint32>(j.value()->type());
215 dStream << j.value()->value();
216
217 md5.reset();
218 md5.addData(j.key().toUtf8());
219 hashStream.writeRawData(md5.result().constData(), 16);
220 }
221 }
222
223 if (sf.write(hashes) != hashes.size()) {
224 sf.cancelWriting();
225 return -4; // write error
226 }
227
228 // calculate the hash of the file
229 SHA1 sha;
230 BlowFish _bf;
231 CipherBlockChain bf(&_bf);
232
233 sha.process(decrypted.data(), decrypted.size());
234
235 // prepend and append the random data
236 QByteArray wholeFile;
237 long blksz = bf.blockSize();
238 long newsize = decrypted.size() + blksz + // encrypted block
239 4 + // file size
240 20; // size of the SHA hash
241
242 int delta = (blksz - (newsize % blksz));
243 newsize += delta;
244 wholeFile.resize(newsize);
245
246 QByteArray randBlock;
247 randBlock.resize(blksz + delta);
248 if (getRandomBlock(randBlock) < 0) {
249 sha.reset();
250 decrypted.fill(0);
251 sf.cancelWriting();
252 return -3; // Fatal error: can't get random
253 }
254
255 for (int i = 0; i < blksz; i++) {
256 wholeFile[i] = randBlock[i];
257 }
258
259 for (int i = 0; i < 4; i++) {
260 wholeFile[(int)(i + blksz)] = (decrypted.size() >> 8 * (3 - i)) & 0xff;
261 }
262
263 for (int i = 0; i < decrypted.size(); i++) {
264 wholeFile[(int)(i + blksz + 4)] = decrypted[i];
265 }
266
267 for (int i = 0; i < delta; i++) {
268 wholeFile[(int)(i + blksz + 4 + decrypted.size())] = randBlock[(int)(i + blksz)];
269 }
270
271 const char *hash = (const char *)sha.hash();
272 for (int i = 0; i < 20; i++) {
273 wholeFile[(int)(newsize - 20 + i)] = hash[i];
274 }
275
276 sha.reset();
277 decrypted.fill(0);
278
279 // encrypt the data
280 if (!bf.setKey(wb->_passhash.data(), wb->_passhash.size() * 8)) {
281 wholeFile.fill(0);
282 sf.cancelWriting();
283 return -2; // encrypt error
284 }
285
286 int rc = bf.encrypt(wholeFile.data(), wholeFile.size());
287 if (rc < 0) {
288 wholeFile.fill(0);
289 sf.cancelWriting();
290 return -2; // encrypt error
291 }
292
293 // write the file
294 auto written = sf.write(wholeFile);
295 if (written != wholeFile.size()) {
296 wholeFile.fill(0);
297 sf.cancelWriting();
298 return -4; // write error
299 }
300 if (!sf.commit()) {
301 qCDebug(KWALLETBACKEND_LOG) << "WARNING: wallet sync to disk failed! QSaveFile status was " << sf.errorString();
302 wholeFile.fill(0);
303 return -4; // write error
304 }
305
306 wholeFile.fill(0);
307
308 return 0;
309}
310
311int BlowfishPersistHandler::read(Backend *wb, QFile &db, WId)
312{
313 wb->_cipherType = BACKEND_CIPHER_BLOWFISH;
314 wb->_hashes.clear();
315 // Read in the hashes
316 QDataStream hds(&db);
317 quint32 n;
318 hds >> n;
319 if (n > 0xffff) { // sanity check
320 return -43;
321 }
322
323 for (size_t i = 0; i < n; ++i) {
324 Digest d;
325 Digest d2; // judgment day
326 MD5Digest ba;
328 quint32 fsz;
329 if (hds.atEnd()) {
330 return -43;
331 }
332 hds.readRawData(d, 16);
333 hds >> fsz;
334 ba = MD5Digest(reinterpret_cast<char *>(d));
335 it = wb->_hashes.insert(ba, QList<MD5Digest>());
336 for (size_t j = 0; j < fsz; ++j) {
337 hds.readRawData(d2, 16);
338 ba = MD5Digest(d2);
339 (*it).append(ba);
340 }
341 }
342
343 // Read in the rest of the file.
344 QByteArray encrypted = db.readAll();
345 assert(encrypted.size() < db.size());
346
347 BlowFish _bf;
348 CipherBlockChain bf(&_bf, _useECBforReading);
349 int blksz = bf.blockSize();
350 if ((encrypted.size() % blksz) != 0) {
351 return -5; // invalid file structure
352 }
353
354 bf.setKey((void *)wb->_passhash.data(), wb->_passhash.size() * 8);
355
356 if (!encrypted.data()) {
357 wb->_passhash.fill(0);
358 encrypted.fill(0);
359 return -7; // file structure error
360 }
361
362 int rc = bf.decrypt(encrypted.data(), encrypted.size());
363 if (rc < 0) {
364 wb->_passhash.fill(0);
365 encrypted.fill(0);
366 return -6; // decrypt error
367 }
368
369 const char *t = encrypted.data();
370
371 // strip the leading data
372 t += blksz; // one block of random data
373
374 // strip the file size off
375 long fsize = 0;
376
377 fsize |= (long(*t) << 24) & 0xff000000;
378 t++;
379 fsize |= (long(*t) << 16) & 0x00ff0000;
380 t++;
381 fsize |= (long(*t) << 8) & 0x0000ff00;
382 t++;
383 fsize |= long(*t) & 0x000000ff;
384 t++;
385
386 if (fsize < 0 || fsize > long(encrypted.size()) - blksz - 4) {
387 qCDebug(KWALLETBACKEND_LOG) << "fsize: " << fsize << " encrypted.size(): " << encrypted.size() << " blksz: " << blksz;
388 encrypted.fill(0);
389 return -9; // file structure error.
390 }
391
392 // compute the hash ourself
393 SHA1 sha;
394 sha.process(t, fsize);
395 const char *testhash = (const char *)sha.hash();
396
397 // compare hashes
398 int sz = encrypted.size();
399 for (int i = 0; i < 20; i++) {
400 if (testhash[i] != encrypted[sz - 20 + i]) {
401 encrypted.fill(0);
402 sha.reset();
403 return -8; // hash error.
404 }
405 }
406
407 sha.reset();
408
409 // chop off the leading blksz+4 bytes
410 QByteArray tmpenc(encrypted.data() + blksz + 4, fsize);
411 encrypted = tmpenc;
412 tmpenc.fill(0);
413
414 // Load the data structures up
415 QDataStream eStream(encrypted);
416
417 while (!eStream.atEnd()) {
418 QString folder;
419 quint32 n;
420
421 eStream >> folder;
422 eStream >> n;
423
424 // Force initialisation
425 wb->_entries[folder].clear();
426
427 for (size_t i = 0; i < n; ++i) {
428 QString key;
429 KWallet::Wallet::EntryType et = KWallet::Wallet::Unknown;
430 Entry *e = new Entry;
431 eStream >> key;
432 qint32 x = 0; // necessary to read properly
433 eStream >> x;
434 et = static_cast<KWallet::Wallet::EntryType>(x);
435
436 switch (et) {
437 case KWallet::Wallet::Password:
438 case KWallet::Wallet::Stream:
439 case KWallet::Wallet::Map:
440 break;
441 default: // Unknown entry
442 delete e;
443 continue;
444 }
445
446 QByteArray a;
447 eStream >> a;
448 e->setValue(a);
449 e->setType(et);
450 e->setKey(key);
451 wb->_entries[folder][key] = e;
452 }
453 }
454
455 wb->_open = true;
456 encrypted.fill(0);
457 return 0;
458}
459
460#ifdef HAVE_GPGMEPP
461GpgME::Error initGpgME()
462{
463 GpgME::Error err;
464 static bool alreadyInitialized = false;
465 if (!alreadyInitialized) {
466 GpgME::initializeLibrary();
467 err = GpgME::checkEngine(GpgME::OpenPGP);
468 if (err) {
469 qCDebug(KWALLETBACKEND_LOG) << "OpenPGP not supported!";
470 }
471 alreadyInitialized = true;
472 }
473 return err;
474}
475
476int GpgPersistHandler::write(Backend *wb, QSaveFile &sf, QByteArray &version, WId w)
477{
478 version[2] = KWALLET_CIPHER_GPG;
479 version[3] = 0;
480 if (sf.write(version) != 4) {
481 sf.cancelWriting();
482 return -4; // write error
483 }
484
485 GpgME::Error err = initGpgME();
486 if (err) {
487 qCDebug(KWALLETBACKEND_LOG) << "initGpgME returned " << err.code();
489 i18n("<qt>Error when attempting to initialize OpenPGP while attempting to save the wallet <b>%1</b>. Error code is <b>%2</b>. "
490 "Please fix your system configuration, then try again.</qt>",
491 wb->_name.toHtmlEscaped(),
492 err.code()));
493 sf.cancelWriting();
494 return -5;
495 }
496
497 std::shared_ptr<GpgME::Context> ctx(GpgME::Context::createForProtocol(GpgME::OpenPGP));
498 if (!ctx) {
499 qCDebug(KWALLETBACKEND_LOG) << "Cannot setup OpenPGP context!";
501 i18n("<qt>Error when attempting to initialize OpenPGP while attempting to save the wallet <b>%1</b>. Please fix your system "
502 "configuration, then try again.</qt>"),
503 wb->_name.toHtmlEscaped());
504 return -6;
505 }
506
507 assert(wb->_cipherType == BACKEND_CIPHER_GPG);
508
509 QByteArray hashes;
510 QDataStream hashStream(&hashes, QIODevice::WriteOnly);
512 hashStream << static_cast<quint32>(wb->_entries.count());
513
514 QByteArray values;
515 QDataStream valueStream(&values, QIODevice::WriteOnly);
516 Backend::FolderMap::ConstIterator i = wb->_entries.constBegin();
517 Backend::FolderMap::ConstIterator ie = wb->_entries.constEnd();
518 for (; i != ie; ++i) {
519 valueStream << i.key();
520 valueStream << static_cast<quint32>(i.value().count());
521
522 md5.reset();
523 md5.addData(i.key().toUtf8());
524 hashStream.writeRawData(md5.result().constData(), 16);
525 hashStream << static_cast<quint32>(i.value().count());
526
527 Backend::EntryMap::ConstIterator j = i.value().constBegin();
528 Backend::EntryMap::ConstIterator je = i.value().constEnd();
529 for (; j != je; ++j) {
530 valueStream << j.key();
531 valueStream << static_cast<qint32>(j.value()->type());
532 valueStream << j.value()->value();
533
534 md5.reset();
535 md5.addData(j.key().toUtf8());
536 hashStream.writeRawData(md5.result().constData(), 16);
537 }
538 }
539
540 QByteArray dataBuffer;
541 QDataStream dataStream(&dataBuffer, QIODevice::WriteOnly);
542 QString keyID(wb->_gpgKey.keyID());
543 dataStream << keyID;
544 dataStream << hashes;
545 dataStream << values;
546
547 GpgME::Data decryptedData(dataBuffer.data(), size_t(dataBuffer.size()), false);
548 GpgME::Data encryptedData;
549 std::vector<GpgME::Key> keys;
550 keys.push_back(wb->_gpgKey);
551 const GpgME::EncryptionResult res = ctx->encrypt(keys, decryptedData, encryptedData, GpgME::Context::None);
552 if (res.error()) {
553 const int gpgerr = res.error().code();
555 i18n("<qt>Encryption error while attempting to save the wallet <b>%1</b>. Error code is <b>%2 (%3)</b>. Please fix your system "
556 "configuration, then try again. This error may occur if you are not using a full trust GPG key. Please ensure you have the "
557 "secret key for the key you are using.</qt>",
558 wb->_name.toHtmlEscaped(),
559 gpgerr,
560 res.error().asString()));
561 qCDebug(KWALLETBACKEND_LOG) << "GpgME encryption error: " << gpgerr;
562 sf.cancelWriting();
563 return -7;
564 }
565
566 char buffer[4096];
567 ssize_t bytes = 0;
568 encryptedData.seek(0, SEEK_SET);
569 while ((bytes = encryptedData.read(buffer, sizeof(buffer) / sizeof(buffer[0]))) > 0) {
570 if (sf.write(buffer, bytes) != bytes) {
572 i18n("<qt>File handling error while attempting to save the wallet <b>%1</b>. Error was <b>%2</b>. Please fix your system "
573 "configuration, then try again.</qt>",
574 wb->_name.toHtmlEscaped(),
575 sf.errorString()));
576 sf.cancelWriting();
577 return -4; // write error
578 }
579 }
580
581 if (!sf.commit()) {
582 qCDebug(KWALLETBACKEND_LOG) << "WARNING: wallet sync to disk failed! QSaveFile status was " << sf.errorString();
583 return -4; // write error
584 }
585
586 return 0;
587}
588
589int GpgPersistHandler::read(Backend *wb, QFile &sf, WId w)
590{
591 GpgME::Error err = initGpgME();
592 if (err) {
594 i18n("<qt>Error when attempting to initialize OpenPGP while attempting to open the wallet <b>%1</b>. Error code is <b>%2</b>. "
595 "Please fix your system configuration, then try again.</qt>",
596 wb->_name.toHtmlEscaped(),
597 err.code()));
598 return -1;
599 }
600
601 wb->_cipherType = BACKEND_CIPHER_GPG;
602 wb->_hashes.clear();
603
604 // the remainder of the file is GPG encrypted. Let's decrypt it
605 GpgME::Data encryptedData;
606 char buffer[4096];
607 ssize_t bytes = 0;
608 while ((bytes = sf.read(buffer, sizeof(buffer) / sizeof(buffer[0])))) {
609 encryptedData.write(buffer, bytes);
610 }
611
612retry_label:
613 std::shared_ptr<GpgME::Context> ctx(GpgME::Context::createForProtocol(GpgME::OpenPGP));
614 if (nullptr == ctx) {
616 i18n("<qt>Error when attempting to initialize OpenPGP while attempting to open the wallet <b>%1</b>. Please fix your system "
617 "configuration, then try again.</qt>",
618 wb->_name.toHtmlEscaped()));
619 qCDebug(KWALLETBACKEND_LOG) << "Cannot setup OpenPGP context!";
620 return -1;
621 }
622
623 GpgME::Data decryptedData;
624 encryptedData.seek(0, SEEK_SET);
625 GpgME::DecryptionResult res = ctx->decrypt(encryptedData, decryptedData);
626 if (res.error()) {
627 qCDebug(KWALLETBACKEND_LOG) << "Error decrypting message: " << res.error().asString() << ", code " << res.error().code() << ", source "
628 << res.error().source();
629 KGuiItem btnRetry(i18n("Retry"));
630 // FIXME the logic here should be a little more elaborate; a dialog box should be used with "retry", "cancel", but also "troubleshoot" with options to
631 // show card status and to kill scdaemon
632 int userChoice =
634 i18n("<qt>Error when attempting to decrypt the wallet <b>%1</b> using GPG. If you're using a SmartCard, "
635 "please ensure it's inserted then try again.<br><br>GPG error was <b>%2</b></qt>",
636 wb->_name.toHtmlEscaped(),
637 res.error().asString()),
638 i18n("kwalletd GPG backend"),
639 btnRetry,
641 if (userChoice == KMessageBox::PrimaryAction) {
642 decryptedData.seek(0, SEEK_SET);
643 goto retry_label;
644 }
645 return -1;
646 }
647
648 decryptedData.seek(0, SEEK_SET);
649 QByteArray dataBuffer;
650 while ((bytes = decryptedData.read(buffer, sizeof(buffer) / sizeof(buffer[0])))) {
651 dataBuffer.append(buffer, bytes);
652 }
653
654 // load the wallet from the decrypted data
655 QDataStream dataStream(dataBuffer);
656 QString keyID;
657 QByteArray hashes;
658 QByteArray values;
659 dataStream >> keyID;
660 dataStream >> hashes;
661 dataStream >> values;
662
663 // locate the GPG key having the ID found inside the file. This will be needed later, when writing changes to disk.
664 QDataStream fileStream(&sf);
665 fileStream.setDevice(nullptr);
666 qCDebug(KWALLETBACKEND_LOG) << "This wallet was encrypted using GPG key with ID " << keyID;
667
668 ctx->setKeyListMode(GpgME::KeyListMode::Local);
669 err = ctx->startKeyListing();
670 while (!err) {
671 GpgME::Key k = ctx->nextKey(err);
672 if (err) {
673 break;
674 }
675 if (keyID == k.keyID()) {
676 qCDebug(KWALLETBACKEND_LOG) << "The key was found.";
677 wb->_gpgKey = k;
678 break;
679 }
680 }
681 ctx->endKeyListing();
682 if (wb->_gpgKey.isNull()) {
684 i18n("<qt>Error when attempting to open the wallet <b>%1</b>. The wallet was encrypted using the GPG Key ID <b>%2</b> but this "
685 "key was not found on your system.</qt>",
686 wb->_name.toHtmlEscaped(),
687 keyID));
688 return -1;
689 }
690
691 QDataStream hashStream(hashes);
692 QDataStream valueStream(values);
693
694 quint32 hashCount;
695 hashStream >> hashCount;
696 if (hashCount > 0xFFFF) {
697 return -43;
698 }
699
700 quint32 folderCount = hashCount;
701 while (hashCount--) {
702 Digest d;
703 hashStream.readRawData(d, 16);
704
705 quint32 folderSize;
706 hashStream >> folderSize;
707
708 MD5Digest ba = MD5Digest(reinterpret_cast<char *>(d));
709 QMap<MD5Digest, QList<MD5Digest>>::iterator it = wb->_hashes.insert(ba, QList<MD5Digest>());
710 while (folderSize--) {
711 Digest d2;
712 hashStream.readRawData(d2, 16);
713 ba = MD5Digest(d2);
714 (*it).append(ba);
715 }
716 }
717
718 while (folderCount--) {
719 QString folder;
720 valueStream >> folder;
721
722 quint32 entryCount;
723 valueStream >> entryCount;
724
725 wb->_entries[folder].clear();
726
727 while (entryCount--) {
728 KWallet::Wallet::EntryType et = KWallet::Wallet::Unknown;
729 Entry *e = new Entry;
730
731 QString key;
732 valueStream >> key;
733
734 qint32 x = 0; // necessary to read properly
735 valueStream >> x;
736 et = static_cast<KWallet::Wallet::EntryType>(x);
737
738 switch (et) {
739 case KWallet::Wallet::Password:
740 case KWallet::Wallet::Stream:
741 case KWallet::Wallet::Map:
742 break;
743 default: // Unknown entry
744 delete e;
745 continue;
746 }
747
748 QByteArray a;
749 valueStream >> a;
750 e->setValue(a);
751 e->setType(et);
752 e->setKey(key);
753 wb->_entries[folder][key] = e;
754 }
755 }
756
757 wb->_open = true;
758
759 return 0;
760}
761#endif // HAVE_GPGMEPP
762
763} // namespace
QString i18n(const char *text, const TYPE &arg...)
KDB_EXPORT KDbVersionInfo version()
ButtonCode warningTwoActionsWId(WId parent_id, const QString &text, const QString &title, const KGuiItem &primaryAction, const KGuiItem &secondaryAction, const QString &dontAskAgainName=QString(), Options options=Options(Notify|Dangerous))
void errorWId(WId parent_id, const QString &text, const QString &title=QString(), Options options=Notify)
KGuiItem cancel()
QByteArray & append(QByteArrayView data)
char * data()
QByteArray & fill(char ch, qsizetype size)
void resize(qsizetype newSize, char c)
qsizetype size() const const
bool exists() const const
virtual qint64 size() const const override
QString errorString() const const
QByteArray read(qint64 maxSize)
QByteArray readAll()
qint64 write(const QByteArray &data)
iterator insert(const Key &key, const T &value)
void cancelWriting()
bool commit()
void clear()
QString fromLocal8Bit(QByteArrayView str)
bool isEmpty() const const
QString toHtmlEscaped() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:16:05 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.