diff --git a/src/format/Kdbx4Reader.cpp b/src/format/Kdbx4Reader.cpp index 8b9d4ab2b..4a9649a62 100644 --- a/src/format/Kdbx4Reader.cpp +++ b/src/format/Kdbx4Reader.cpp @@ -22,7 +22,6 @@ #include "core/Group.h" #include "core/Endian.h" #include "crypto/CryptoHash.h" -#include "crypto/kdf/AesKdf.h" #include "format/KeePass2RandomStream.h" #include "format/KdbxXmlReader.h" #include "streams/HmacBlockStream.h" @@ -34,7 +33,7 @@ Database* Kdbx4Reader::readDatabaseImpl(QIODevice* device, const QByteArray& hea { Q_ASSERT(m_kdbxVersion == KeePass2::FILE_VERSION_4); - m_binaryPool.clear(); + m_binaryPoolInverse.clear(); if (hasError()) { return nullptr; @@ -135,7 +134,7 @@ Database* Kdbx4Reader::readDatabaseImpl(QIODevice* device, const QByteArray& hea Q_ASSERT(xmlDevice); - KdbxXmlReader xmlReader(KeePass2::FILE_VERSION_4, m_binaryPool); + KdbxXmlReader xmlReader(KeePass2::FILE_VERSION_4, binaryPool()); xmlReader.readDatabase(xmlDevice, m_db.data(), &randomStream); if (xmlReader.hasError()) { @@ -273,14 +272,20 @@ bool Kdbx4Reader::readInnerHeaderField(QIODevice* device) setProtectedStreamKey(fieldData); break; - case KeePass2::InnerHeaderFieldID::Binary: + case KeePass2::InnerHeaderFieldID::Binary: { if (fieldLen < 1) { raiseError(tr("Invalid inner header binary size")); return false; } - m_binaryPool.insert(QString::number(m_binaryPool.size()), fieldData.mid(1)); + auto data = fieldData.mid(1); + if (m_binaryPoolInverse.contains(data)) { + qWarning("Skipping duplicate binary record"); + break; + } + m_binaryPoolInverse.insert(data, QString::number(m_binaryPoolInverse.size())); break; } + } return true; } @@ -417,7 +422,22 @@ QVariantMap Kdbx4Reader::readVariantMap(QIODevice* device) return vm; } +/** + * @return mapping from attachment keys to binary data + */ QHash Kdbx4Reader::binaryPool() const { - return m_binaryPool; + QHash binaryPool; + for (auto it = m_binaryPoolInverse.cbegin(); it != m_binaryPoolInverse.cend(); ++it) { + binaryPool.insert(it.value(), it.key()); + } + return binaryPool; +} + +/** + * @return mapping from binary data to attachment keys + */ +QHash Kdbx4Reader::binaryPoolInverse() const +{ + return m_binaryPoolInverse; } diff --git a/src/format/Kdbx4Reader.h b/src/format/Kdbx4Reader.h index 9764ed886..24d4a9142 100644 --- a/src/format/Kdbx4Reader.h +++ b/src/format/Kdbx4Reader.h @@ -32,6 +32,7 @@ Q_DECLARE_TR_FUNCTIONS(Kdbx4Reader) public: Database* readDatabaseImpl(QIODevice* device, const QByteArray& headerData, const CompositeKey& key, bool keepDatabase) override; + QHash binaryPoolInverse() const; QHash binaryPool() const; protected: @@ -41,7 +42,7 @@ private: bool readInnerHeaderField(QIODevice* device); QVariantMap readVariantMap(QIODevice* device); - QHash m_binaryPool; + QHash m_binaryPoolInverse; }; #endif // KEEPASSX_KDBX4READER_H diff --git a/src/format/Kdbx4Writer.cpp b/src/format/Kdbx4Writer.cpp index 42cc3ecb5..760baa545 100644 --- a/src/format/Kdbx4Writer.cpp +++ b/src/format/Kdbx4Writer.cpp @@ -211,12 +211,20 @@ bool Kdbx4Writer::writeInnerHeaderField(QIODevice* device, KeePass2::InnerHeader void Kdbx4Writer::writeAttachments(QIODevice* device, Database* db) { const QList allEntries = db->rootGroup()->entriesRecursive(true); + QSet writtenAttachments; + for (Entry* entry : allEntries) { const QList attachmentKeys = entry->attachments()->keys(); for (const QString& key : attachmentKeys) { - QByteArray data = entry->attachments()->value(key); - data.prepend("\x01"); + QByteArray data("\x01"); + data.append(entry->attachments()->value(key)); + + if (writtenAttachments.contains(data)) { + continue; + } + writeInnerHeaderField(device, KeePass2::InnerHeaderFieldID::Binary, data); + writtenAttachments.insert(data); } } } diff --git a/src/format/KdbxXmlReader.cpp b/src/format/KdbxXmlReader.cpp index 0b09e8a2f..3d08bb55e 100644 --- a/src/format/KdbxXmlReader.cpp +++ b/src/format/KdbxXmlReader.cpp @@ -40,7 +40,7 @@ KdbxXmlReader::KdbxXmlReader(quint32 version) * @param version KDBX version * @param binaryPool binary pool */ -KdbxXmlReader::KdbxXmlReader(quint32 version, QHash& binaryPool) +KdbxXmlReader::KdbxXmlReader(quint32 version, const QHash& binaryPool) : m_kdbxVersion(version) , m_binaryPool(binaryPool) { diff --git a/src/format/KdbxXmlReader.h b/src/format/KdbxXmlReader.h index e31757ccf..7ec592eb4 100644 --- a/src/format/KdbxXmlReader.h +++ b/src/format/KdbxXmlReader.h @@ -42,7 +42,7 @@ Q_DECLARE_TR_FUNCTIONS(KdbxXmlReader) public: explicit KdbxXmlReader(quint32 version); - explicit KdbxXmlReader(quint32 version, QHash& binaryPool); + explicit KdbxXmlReader(quint32 version, const QHash& binaryPool); virtual ~KdbxXmlReader() = default; virtual Database* readDatabase(const QString& filename);