18
#include "KeePass1Reader.h"
24
#include "core/Endian.h"
25
#include "core/Group.h"
26
#include "core/Metadata.h"
27
#include "core/Tools.h"
28
#include "crypto/CryptoHash.h"
29
#include "format/KeePass1.h"
30
#include "keys/FileKey.h"
31
#include "streams/SymmetricCipherStream.h"
33
class KeePass1Key : public CompositeKey
36
QByteArray rawKey() const override;
38
void setPassword(const QByteArray& password);
39
void setKeyfileData(const QByteArray& keyfileData);
42
QByteArray m_password;
43
QByteArray m_keyfileData;
46
KeePass1Reader::KeePass1Reader()
47
: m_tmpParent(nullptr)
49
, m_encryptionFlags(0)
50
, m_transformRounds(0)
55
QSharedPointer<Database>
56
KeePass1Reader::readDatabase(QIODevice* device, const QString& password, QIODevice* keyfileDevice)
61
QByteArray keyfileData;
62
auto newFileKey = QSharedPointer<FileKey>::create();
65
keyfileData = readKeyfile(keyfileDevice);
67
if (keyfileData.isEmpty()) {
68
raiseError(tr("Unable to read keyfile.").append("\n").append(keyfileDevice->errorString()));
71
if (!keyfileDevice->seek(0)) {
72
raiseError(tr("Unable to read keyfile.").append("\n").append(keyfileDevice->errorString()));
76
if (!newFileKey->load(keyfileDevice)) {
77
raiseError(tr("Unable to read keyfile.").append("\n").append(keyfileDevice->errorString()));
82
auto db = QSharedPointer<Database>::create();
83
QScopedPointer<Group> tmpParent(new Group());
85
m_tmpParent = tmpParent.data();
90
auto signature1 = Endian::readSizedInt<quint32>(m_device, KeePass1::BYTEORDER, &ok);
91
if (!ok || signature1 != KeePass1::SIGNATURE_1) {
92
raiseError(tr("Not a KeePass database."));
96
auto signature2 = Endian::readSizedInt<quint32>(m_device, KeePass1::BYTEORDER, &ok);
97
if (!ok || signature2 != KeePass1::SIGNATURE_2) {
98
raiseError(tr("Not a KeePass database."));
102
m_encryptionFlags = Endian::readSizedInt<quint32>(m_device, KeePass1::BYTEORDER, &ok);
103
if (!ok || !(m_encryptionFlags & KeePass1::Rijndael || m_encryptionFlags & KeePass1::Twofish)) {
104
raiseError(tr("Unsupported encryption algorithm."));
108
auto version = Endian::readSizedInt<quint32>(m_device, KeePass1::BYTEORDER, &ok);
110
|| (version & KeePass1::FILE_VERSION_CRITICAL_MASK)
111
!= (KeePass1::FILE_VERSION & KeePass1::FILE_VERSION_CRITICAL_MASK)) {
112
raiseError(tr("Unsupported KeePass database version."));
116
m_masterSeed = m_device->read(16);
117
if (m_masterSeed.size() != 16) {
118
raiseError("Unable to read master seed");
122
m_encryptionIV = m_device->read(16);
123
if (m_encryptionIV.size() != 16) {
124
raiseError(tr("Unable to read encryption IV", "IV = Initialization Vector for symmetric cipher"));
128
auto numGroups = Endian::readSizedInt<quint32>(m_device, KeePass1::BYTEORDER, &ok);
130
raiseError(tr("Invalid number of groups"));
134
auto numEntries = Endian::readSizedInt<quint32>(m_device, KeePass1::BYTEORDER, &ok);
136
raiseError(tr("Invalid number of entries"));
140
m_contentHashHeader = m_device->read(32);
141
if (m_contentHashHeader.size() != 32) {
142
raiseError(tr("Invalid content hash size"));
146
m_transformSeed = m_device->read(32);
147
if (m_transformSeed.size() != 32) {
148
raiseError(tr("Invalid transform seed size"));
152
m_transformRounds = Endian::readSizedInt<quint32>(m_device, KeePass1::BYTEORDER, &ok);
154
raiseError(tr("Invalid number of transform rounds"));
157
auto kdf = QSharedPointer<AesKdf>::create(true);
158
kdf->setRounds(m_transformRounds);
159
kdf->setSeed(m_transformSeed);
162
qint64 contentPos = m_device->pos();
164
QScopedPointer<SymmetricCipherStream> cipherStream(testKeys(password, keyfileData, contentPos));
170
QList<Group*> groups;
171
for (quint32 i = 0; i < numGroups; i++) {
172
Group* group = readGroup(cipherStream.data());
176
groups.append(group);
179
QList<Entry*> entries;
180
for (quint32 i = 0; i < numEntries; i++) {
181
Entry* entry = readEntry(cipherStream.data());
185
entries.append(entry);
188
if (!constructGroupTree(groups)) {
189
raiseError(tr("Unable to construct group tree"));
193
for (Entry* entry : asConst(entries)) {
194
if (isMetaStream(entry)) {
195
parseMetaStream(entry);
199
quint32 groupId = m_entryGroupIds.value(entry);
200
if (!m_groupIds.contains(groupId)) {
201
qWarning("Orphaned entry found, assigning to root group.");
202
entry->setGroup(m_db->rootGroup());
204
entry->setGroup(m_groupIds.value(groupId));
206
entry->setUuid(QUuid::createUuid());
210
db->rootGroup()->setName(tr("Root"));
212
const QList<Group*> children = db->rootGroup()->children();
213
for (Group* group : children) {
214
if (group->name() == "Backup") {
215
group->setSearchingEnabled(Group::Disable);
216
group->setAutoTypeEnabled(Group::Disable);
220
Q_ASSERT(m_tmpParent->children().isEmpty());
221
Q_ASSERT(m_tmpParent->entries().isEmpty());
223
for (Group* group : asConst(groups)) {
224
group->setUpdateTimeinfo(true);
227
const QList<Entry*> dbEntries = m_db->rootGroup()->entriesRecursive();
228
for (Entry* entry : dbEntries) {
229
entry->setUpdateTimeinfo(true);
232
auto key = QSharedPointer<CompositeKey>::create();
233
if (!password.isEmpty()) {
234
key->addKey(QSharedPointer<PasswordKey>::create(password));
237
key->addKey(newFileKey);
240
if (!db->setKey(key)) {
241
raiseError(tr("Unable to calculate database key"));
248
QSharedPointer<Database>
249
KeePass1Reader::readDatabase(QIODevice* device, const QString& password, const QString& keyfileName)
251
QScopedPointer<QFile> keyFile;
252
if (!keyfileName.isEmpty()) {
253
keyFile.reset(new QFile(keyfileName));
254
if (!keyFile->open(QFile::ReadOnly)) {
255
raiseError(keyFile->errorString());
260
return QSharedPointer<Database>(readDatabase(device, password, keyFile.data()));
263
QSharedPointer<Database>
264
KeePass1Reader::readDatabase(const QString& filename, const QString& password, const QString& keyfileName)
266
QFile dbFile(filename);
267
if (!dbFile.open(QFile::ReadOnly)) {
268
raiseError(dbFile.errorString());
272
auto db = readDatabase(&dbFile, password, keyfileName);
274
if (dbFile.error() != QFile::NoError) {
275
raiseError(dbFile.errorString());
280
db->metadata()->setName(QFileInfo(filename).completeBaseName());
286
bool KeePass1Reader::hasError()
291
QString KeePass1Reader::errorString()
296
SymmetricCipherStream*
297
KeePass1Reader::testKeys(const QString& password, const QByteArray& keyfileData, qint64 contentPos)
299
const QList<PasswordEncoding> encodings = {Windows1252, Latin1, UTF8};
301
QScopedPointer<SymmetricCipherStream> cipherStream;
302
QByteArray passwordData;
303
QTextCodec* codec = QTextCodec::codecForName("Windows-1252");
304
QByteArray passwordDataCorrect = codec->fromUnicode(password);
306
for (PasswordEncoding encoding : encodings) {
307
if (encoding == Windows1252) {
308
passwordData = passwordDataCorrect;
309
} else if (encoding == Latin1) {
312
passwordData = password.toLatin1();
314
if (passwordData == passwordDataCorrect) {
317
qWarning("Testing password encoded as Latin-1.");
319
} else if (encoding == UTF8) {
322
passwordData = password.toUtf8();
324
if (passwordData == passwordDataCorrect) {
327
qWarning("Testing password encoded as UTF-8.");
331
QByteArray finalKey = key(passwordData, keyfileData);
332
if (finalKey.isEmpty()) {
336
cipherStream.reset(new SymmetricCipherStream(m_device));
338
auto mode = SymmetricCipher::Aes256_CBC;
339
if (m_encryptionFlags & KeePass1::Twofish) {
340
mode = SymmetricCipher::Twofish_CBC;
342
if (!cipherStream->init(mode, SymmetricCipher::Decrypt, finalKey, m_encryptionIV)) {
343
raiseError(cipherStream->errorString());
346
if (!cipherStream->open(QIODevice::ReadOnly)) {
347
raiseError(cipherStream->errorString());
351
bool success = verifyKey(cipherStream.data());
353
cipherStream->reset();
354
cipherStream->close();
355
if (!m_device->seek(contentPos)) {
356
QString msg = tr("unable to seek to content position");
357
if (!m_device->errorString().isEmpty()) {
358
msg.append("\n").append(m_device->errorString());
366
if (!cipherStream->init(mode, SymmetricCipher::Decrypt, finalKey, m_encryptionIV)) {
367
raiseError(cipherStream->errorString());
370
cipherStream->open(QIODevice::ReadOnly);
373
cipherStream.reset();
378
raiseError(tr("Invalid credentials were provided, please try again.\n"
379
"If this reoccurs, then your database file may be corrupt."));
382
return cipherStream.take();
385
QByteArray KeePass1Reader::key(const QByteArray& password, const QByteArray& keyfileData)
387
Q_ASSERT(!m_masterSeed.isEmpty());
388
Q_ASSERT(!m_transformSeed.isEmpty());
391
key.setPassword(password);
392
key.setKeyfileData(keyfileData);
394
QByteArray transformedKey;
395
bool result = key.transform(*m_db->kdf(), transformedKey);
398
raiseError(tr("Key transformation failed"));
402
CryptoHash hash(CryptoHash::Sha256);
403
hash.addData(m_masterSeed);
404
hash.addData(transformedKey);
405
return hash.result();
408
bool KeePass1Reader::verifyKey(SymmetricCipherStream* cipherStream)
410
CryptoHash contentHash(CryptoHash::Sha256);
414
if (!Tools::readFromDevice(cipherStream, buffer)) {
417
contentHash.addData(buffer);
418
} while (!buffer.isEmpty());
420
return contentHash.result() == m_contentHashHeader;
423
Group* KeePass1Reader::readGroup(QIODevice* cipherStream)
425
QScopedPointer<Group> group(new Group());
426
group->setUpdateTimeinfo(false);
427
group->setParent(m_tmpParent);
432
quint32 groupLevel = 0;
433
bool groupIdSet = false;
434
bool groupLevelSet = false;
437
bool reachedEnd = false;
440
auto fieldType = Endian::readSizedInt<quint16>(cipherStream, KeePass1::BYTEORDER, &ok);
442
raiseError(tr("Invalid group field type number"));
446
auto fieldSize = static_cast<int>(Endian::readSizedInt<quint32>(cipherStream, KeePass1::BYTEORDER, &ok));
448
raiseError(tr("Invalid group field size"));
452
QByteArray fieldData = cipherStream->read(fieldSize);
453
if (fieldData.size() != fieldSize) {
454
raiseError(tr("Read group field data doesn't match size"));
463
if (fieldSize != 4) {
464
raiseError(tr("Incorrect group id field size"));
467
groupId = Endian::bytesToSizedInt<quint32>(fieldData, KeePass1::BYTEORDER);
471
group->setName(QString::fromUtf8(fieldData.constData()));
474
if (fieldSize != 5) {
475
raiseError(tr("Incorrect group creation time field size"));
478
QDateTime dateTime = dateFromPackedStruct(fieldData);
479
if (dateTime.isValid()) {
480
timeInfo.setCreationTime(dateTime);
485
if (fieldSize != 5) {
486
raiseError(tr("Incorrect group modification time field size"));
489
QDateTime dateTime = dateFromPackedStruct(fieldData);
490
if (dateTime.isValid()) {
491
timeInfo.setLastModificationTime(dateTime);
496
if (fieldSize != 5) {
497
raiseError(tr("Incorrect group access time field size"));
499
QDateTime dateTime = dateFromPackedStruct(fieldData);
500
if (dateTime.isValid()) {
501
timeInfo.setLastAccessTime(dateTime);
506
if (fieldSize != 5) {
507
raiseError(tr("Incorrect group expiry time field size"));
509
QDateTime dateTime = dateFromPackedStruct(fieldData);
510
if (dateTime.isValid()) {
511
timeInfo.setExpires(true);
512
timeInfo.setExpiryTime(dateTime);
517
if (fieldSize != 4) {
518
raiseError(tr("Incorrect group icon field size"));
521
auto iconNumber = Endian::bytesToSizedInt<quint32>(fieldData, KeePass1::BYTEORDER);
522
group->setIcon(iconNumber);
526
if (fieldSize != 2) {
527
raiseError(tr("Incorrect group level field size"));
530
groupLevel = Endian::bytesToSizedInt<quint16>(fieldData, KeePass1::BYTEORDER);
531
groupLevelSet = true;
542
raiseError(tr("Invalid group field type"));
545
} while (!reachedEnd);
547
if (!groupIdSet || !groupLevelSet) {
548
raiseError(tr("Missing group id or level"));
552
group->setUuid(QUuid::createUuid());
553
group->setTimeInfo(timeInfo);
554
m_groupIds.insert(groupId, group.data());
555
m_groupLevels.insert(group.data(), groupLevel);
560
Entry* KeePass1Reader::readEntry(QIODevice* cipherStream)
562
QScopedPointer<Entry> entry(new Entry());
563
entry->setUpdateTimeinfo(false);
564
entry->setGroup(m_tmpParent);
569
bool reachedEnd = false;
572
auto fieldType = Endian::readSizedInt<quint16>(cipherStream, KeePass1::BYTEORDER, &ok);
574
raiseError(tr("Missing entry field type number"));
578
auto fieldSize = static_cast<int>(Endian::readSizedInt<quint32>(cipherStream, KeePass1::BYTEORDER, &ok));
580
raiseError(tr("Invalid entry field size"));
584
QByteArray fieldData = cipherStream->read(fieldSize);
585
if (fieldData.size() != fieldSize) {
586
raiseError(tr("Read entry field data doesn't match size"));
595
if (fieldSize != 16) {
596
raiseError(tr("Invalid entry UUID field size"));
599
m_entryUuids.insert(fieldData, entry.data());
602
if (fieldSize != 4) {
603
raiseError(tr("Invalid entry group id field size"));
606
auto groupId = Endian::bytesToSizedInt<quint32>(fieldData, KeePass1::BYTEORDER);
607
m_entryGroupIds.insert(entry.data(), groupId);
611
if (fieldSize != 4) {
612
raiseError(tr("Invalid entry icon field size"));
615
auto iconNumber = Endian::bytesToSizedInt<quint32>(fieldData, KeePass1::BYTEORDER);
616
entry->setIcon(iconNumber);
620
entry->setTitle(QString::fromUtf8(fieldData.constData()));
623
entry->setUrl(QString::fromUtf8(fieldData.constData()));
626
entry->setUsername(QString::fromUtf8(fieldData.constData()));
629
entry->setPassword(QString::fromUtf8(fieldData.constData()));
632
parseNotes(QString::fromUtf8(fieldData.constData()), entry.data());
635
if (fieldSize != 5) {
636
raiseError(tr("Invalid entry creation time field size"));
639
QDateTime dateTime = dateFromPackedStruct(fieldData);
640
if (dateTime.isValid()) {
641
timeInfo.setCreationTime(dateTime);
646
if (fieldSize != 5) {
647
raiseError(tr("Invalid entry modification time field size"));
650
QDateTime dateTime = dateFromPackedStruct(fieldData);
651
if (dateTime.isValid()) {
652
timeInfo.setLastModificationTime(dateTime);
657
if (fieldSize != 5) {
658
raiseError(tr("Invalid entry creation time field size"));
661
QDateTime dateTime = dateFromPackedStruct(fieldData);
662
if (dateTime.isValid()) {
663
timeInfo.setLastAccessTime(dateTime);
668
if (fieldSize != 5) {
669
raiseError(tr("Invalid entry expiry time field size"));
672
QDateTime dateTime = dateFromPackedStruct(fieldData);
673
if (dateTime.isValid()) {
674
timeInfo.setExpires(true);
675
timeInfo.setExpiryTime(dateTime);
680
binaryName = QString::fromUtf8(fieldData.constData());
683
if (fieldSize != 0) {
684
entry->attachments()->set(binaryName, fieldData);
692
raiseError(tr("Invalid entry field type"));
695
} while (!reachedEnd);
697
entry->setTimeInfo(timeInfo);
702
void KeePass1Reader::parseNotes(const QString& rawNotes, Entry* entry)
704
QRegExp sequenceRegexp("Auto-Type(?:-(\\d+))?: (.+)", Qt::CaseInsensitive, QRegExp::RegExp2);
705
QRegExp windowRegexp("Auto-Type-Window(?:-(\\d+))?: (.+)", Qt::CaseInsensitive, QRegExp::RegExp2);
706
QHash<int, QString> sequences;
707
QMap<int, QStringList> windows;
711
bool lastLineAutoType = false;
712
const QStringList rawNotesLines = rawNotes.split("\n");
713
for (QString line : rawNotesLines) {
716
if (sequenceRegexp.exactMatch(line)) {
717
if (sequenceRegexp.cap(1).isEmpty()) {
718
entry->setDefaultAutoTypeSequence(sequenceRegexp.cap(2));
720
sequences[sequenceRegexp.cap(1).toInt()] = sequenceRegexp.cap(2);
723
lastLineAutoType = true;
724
} else if (windowRegexp.exactMatch(line)) {
726
if (windowRegexp.cap(1).isEmpty()) {
729
nr = windowRegexp.cap(1).toInt();
732
windows[nr].append(windowRegexp.cap(2));
734
lastLineAutoType = true;
737
if (!lastLineAutoType || !line.isEmpty()) {
740
lastLineAutoType = false;
744
entry->setNotes(notes.join("\n"));
746
QMapIterator<int, QStringList> i(windows);
747
while (i.hasNext()) {
750
QString sequence = sequences.value(i.key());
751
const QStringList windowList = i.value();
753
for (const QString& window : windowList) {
754
AutoTypeAssociations::Association assoc;
755
assoc.window = window;
756
assoc.sequence = sequence;
757
entry->autoTypeAssociations()->add(assoc);
762
bool KeePass1Reader::constructGroupTree(const QList<Group*>& groups)
764
for (int i = 0; i < groups.size(); i++) {
765
quint32 level = m_groupLevels.value(groups[i]);
768
groups[i]->setParent(m_db->rootGroup());
770
for (int j = (i - 1); j >= 0; j--) {
771
if (m_groupLevels.value(groups[j]) < level) {
772
if ((level - m_groupLevels.value(groups[j])) != 1) {
776
groups[i]->setParent(groups[j]);
782
if (groups[i]->parentGroup() == m_tmpParent) {
790
void KeePass1Reader::parseMetaStream(const Entry* entry)
792
QByteArray data = entry->attachments()->value("bin-stream");
794
if (entry->notes() == "KPX_GROUP_TREE_STATE") {
795
if (!parseGroupTreeState(data)) {
796
qWarning("Unable to parse group tree state metastream.");
798
} else if (entry->notes() == "KPX_CUSTOM_ICONS_4") {
799
if (!parseCustomIcons4(data)) {
800
qWarning("Unable to parse custom icons metastream.");
803
qWarning("Ignoring unknown metastream \"%s\".", entry->notes().toLocal8Bit().constData());
807
bool KeePass1Reader::parseGroupTreeState(const QByteArray& data)
809
if (data.size() < 4) {
814
auto num = Endian::bytesToSizedInt<quint32>(data.mid(pos, 4), KeePass1::BYTEORDER);
817
if (static_cast<quint32>(data.size() - 4) != (num * 5)) {
821
for (quint32 i = 0; i < num; i++) {
822
auto groupId = Endian::bytesToSizedInt<quint32>(data.mid(pos, 4), KeePass1::BYTEORDER);
825
bool expanded = data.at(pos);
828
if (m_groupIds.contains(groupId)) {
829
m_groupIds[groupId]->setExpanded(expanded);
836
bool KeePass1Reader::parseCustomIcons4(const QByteArray& data)
838
if (data.size() < 12) {
844
auto numIcons = Endian::bytesToSizedInt<quint32>(data.mid(pos, 4), KeePass1::BYTEORDER);
847
auto numEntries = Endian::bytesToSizedInt<quint32>(data.mid(pos, 4), KeePass1::BYTEORDER);
850
auto numGroups = Endian::bytesToSizedInt<quint32>(data.mid(pos, 4), KeePass1::BYTEORDER);
853
QList<QUuid> iconUuids;
855
for (quint32 i = 0; i < numIcons; i++) {
856
if (data.size() < (pos + 4)) {
859
auto iconSize = Endian::bytesToSizedInt<quint32>(data.mid(pos, 4), KeePass1::BYTEORDER);
862
if (static_cast<quint32>(data.size()) < (pos + iconSize)) {
865
QByteArray icon = data.mid(pos, iconSize);
868
QUuid uuid = QUuid::createUuid();
869
iconUuids.append(uuid);
870
m_db->metadata()->addCustomIcon(uuid, icon);
873
if (static_cast<quint32>(data.size()) < (pos + numEntries * 20)) {
877
for (quint32 i = 0; i < numEntries; i++) {
878
QByteArray entryUuid = data.mid(pos, 16);
881
auto iconId = Endian::bytesToSizedInt<quint32>(data.mid(pos, 4), KeePass1::BYTEORDER);
884
if (m_entryUuids.contains(entryUuid) && (iconId < static_cast<quint32>(iconUuids.size()))) {
885
m_entryUuids[entryUuid]->setIcon(iconUuids[iconId]);
889
if (static_cast<quint32>(data.size()) < (pos + numGroups * 8)) {
893
for (quint32 i = 0; i < numGroups; i++) {
894
auto groupId = Endian::bytesToSizedInt<quint32>(data.mid(pos, 4), KeePass1::BYTEORDER);
897
auto iconId = Endian::bytesToSizedInt<quint32>(data.mid(pos, 4), KeePass1::BYTEORDER);
900
if (m_groupIds.contains(groupId) && (iconId < static_cast<quint32>(iconUuids.size()))) {
901
m_groupIds[groupId]->setIcon(iconUuids[iconId]);
908
void KeePass1Reader::raiseError(const QString& errorMessage)
911
m_errorStr = errorMessage;
914
QDateTime KeePass1Reader::dateFromPackedStruct(const QByteArray& data)
916
Q_ASSERT(data.size() == 5);
918
quint32 dw1 = static_cast<uchar>(data.at(0));
919
quint32 dw2 = static_cast<uchar>(data.at(1));
920
quint32 dw3 = static_cast<uchar>(data.at(2));
921
quint32 dw4 = static_cast<uchar>(data.at(3));
922
quint32 dw5 = static_cast<uchar>(data.at(4));
924
int y = (dw1 << 6) | (dw2 >> 2);
925
int mon = ((dw2 & 0x00000003) << 2) | (dw3 >> 6);
926
int d = (dw3 >> 1) & 0x0000001F;
927
int h = ((dw3 & 0x00000001) << 4) | (dw4 >> 4);
928
int min = ((dw4 & 0x0000000F) << 2) | (dw5 >> 6);
929
int s = dw5 & 0x0000003F;
931
QDateTime dateTime = QDateTime(QDate(y, mon, d), QTime(h, min, s), Qt::UTC);
934
if (dateTime == QDateTime(QDate(2999, 12, 28), QTime(23, 59, 59), Qt::UTC)) {
941
bool KeePass1Reader::isMetaStream(const Entry* entry)
943
return entry->attachments()->keys().contains("bin-stream") && !entry->notes().isEmpty()
944
&& entry->title() == "Meta-Info" && entry->username() == "SYSTEM" && entry->url() == "$"
945
&& entry->iconNumber() == 0;
948
QByteArray KeePass1Reader::readKeyfile(QIODevice* device)
950
if (device->size() == 0) {
954
if (device->size() == 32) {
955
QByteArray data = device->read(32);
956
if (data.size() != 32) {
963
if (device->size() == 64) {
964
QByteArray data = device->read(64);
966
if (data.size() != 64) {
970
if (Tools::isHex(data)) {
971
return QByteArray::fromHex(data);
977
CryptoHash cryptoHash(CryptoHash::Sha256);
981
if (!Tools::readFromDevice(device, buffer)) {
984
cryptoHash.addData(buffer);
985
} while (!buffer.isEmpty());
987
return cryptoHash.result();
990
QByteArray KeePass1Key::rawKey() const
992
if (m_keyfileData.isEmpty()) {
993
return CryptoHash::hash(m_password, CryptoHash::Sha256);
994
} else if (m_password.isEmpty()) {
995
return m_keyfileData;
997
CryptoHash keyHash(CryptoHash::Sha256);
998
keyHash.addData(CryptoHash::hash(m_password, CryptoHash::Sha256));
999
keyHash.addData(m_keyfileData);
1000
return keyHash.result();
1004
void KeePass1Key::clear()
1006
CompositeKey::clear();
1009
m_keyfileData.clear();
1012
void KeePass1Key::setPassword(const QByteArray& password)
1014
m_password = password;
1017
void KeePass1Key::setKeyfileData(const QByteArray& keyfileData)
1019
m_keyfileData = keyfileData;