From e3da80fcc61476a931564cac98ea56186dabacce Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 25 Sep 2010 12:41:00 +0200 Subject: Add KeePass2Writer. Support attributes MasterKeyChanged, MasterKeyChangeRec, MasterKeyChangeForce and Tags. Close streams in the dtor. --- src/CMakeLists.txt | 1 + src/core/Database.cpp | 58 +++++++++++++++ src/core/Database.h | 25 +++++++ src/core/Entry.cpp | 10 +++ src/core/Entry.h | 3 + src/core/Metadata.cpp | 30 ++++++++ src/core/Metadata.h | 10 +++ src/format/KeePass2.h | 13 ++-- src/format/KeePass2Reader.cpp | 46 +++++++----- src/format/KeePass2Reader.h | 8 +-- src/format/KeePass2Writer.cpp | 132 ++++++++++++++++++++++++++++++++++ src/format/KeePass2Writer.h | 46 ++++++++++++ src/format/KeePass2XmlReader.cpp | 23 +++++- src/format/KeePass2XmlReader.h | 1 + src/format/KeePass2XmlWriter.cpp | 10 ++- src/streams/HashedBlockStream.cpp | 5 ++ src/streams/HashedBlockStream.h | 1 + src/streams/LayeredStream.cpp | 5 ++ src/streams/LayeredStream.h | 1 + src/streams/SymmetricCipherStream.cpp | 5 ++ src/streams/SymmetricCipherStream.h | 1 + src/streams/qtiocompressor.cpp | 2 +- 22 files changed, 397 insertions(+), 39 deletions(-) create mode 100644 src/format/KeePass2Writer.cpp create mode 100644 src/format/KeePass2Writer.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 367696c3e..aa8fc04ca 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -31,6 +31,7 @@ set(keepassx_SOURCES crypto/Random.cpp crypto/SymmetricCipher.cpp format/KeePass2Reader.cpp + format/KeePass2Writer.cpp format/KeePass2XmlReader.cpp format/KeePass2XmlWriter.cpp gui/DatabaseWidget.cpp diff --git a/src/core/Database.cpp b/src/core/Database.cpp index fcb5eab83..88c12e0ed 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -102,3 +102,61 @@ void Database::addDeletedObject(const DeletedObject& delObj) { m_deletedObjects.append(delObj); } + +Uuid Database::cipher() const +{ + return m_cipher; +} + +Database::CompressionAlgorithm Database::compressionAlgo() const +{ + return m_compressionAlgo; +} + +QByteArray Database::transformSeed() const +{ + return m_transformSeed; +} + +quint64 Database::transformRounds() const +{ + return m_transformRounds; +} + +QByteArray Database::transformedMasterKey() const +{ + return m_transformedMasterKey; +} + +void Database::setCipher(const Uuid& cipher) +{ + Q_ASSERT(!cipher.isNull()); + + m_cipher = cipher; +} + +void Database::setCompressionAlgo(Database::CompressionAlgorithm algo) +{ + Q_ASSERT(static_cast(algo) <= CompressionAlgorithmMax); + + m_compressionAlgo = algo; +} + +void Database::setTransformSeed(const QByteArray& seed) +{ + Q_ASSERT(seed.size() == 32); + + m_transformSeed = seed; +} + +void Database::setTransformRounds(quint64 rounds) +{ + m_transformRounds = rounds; +} + +void Database::setTransformedMasterKey(QByteArray& key) +{ + Q_ASSERT(key.size() == 32); + + m_transformedMasterKey = key; +} diff --git a/src/core/Database.h b/src/core/Database.h index 486b8178c..5ec296e20 100644 --- a/src/core/Database.h +++ b/src/core/Database.h @@ -36,6 +36,13 @@ class Database : public QObject Q_OBJECT public: + enum CompressionAlgorithm + { + CompressionNone = 0, + CompressionGZip = 1 + }; + static const quint32 CompressionAlgorithmMax = CompressionGZip; + Database(); Group* rootGroup(); const Group* rootGroup() const; @@ -47,6 +54,18 @@ public: QList deletedObjects(); void addDeletedObject(const DeletedObject& delObj); + Uuid cipher() const; + Database::CompressionAlgorithm compressionAlgo() const; + QByteArray transformSeed() const; + quint64 transformRounds() const; + QByteArray transformedMasterKey() const; + + void setCipher(const Uuid& cipher); + void setCompressionAlgo(Database::CompressionAlgorithm algo); + void setTransformSeed(const QByteArray& seed); + void setTransformRounds(quint64 rounds); + void setTransformedMasterKey(QByteArray& key); + Q_SIGNALS: void groupDataChanged(Group* group); void groupAboutToAdd(Group* group, int index); @@ -61,6 +80,12 @@ private: Metadata* m_metadata; Group* m_rootGroup; QList m_deletedObjects; + + Uuid m_cipher; + CompressionAlgorithm m_compressionAlgo; + QByteArray m_transformSeed; + quint64 m_transformRounds; + QByteArray m_transformedMasterKey; }; #endif // KEEPASSX_DATABASE_H diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index 783b824fb..9416262da 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -74,6 +74,11 @@ QString Entry::overrideUrl() const return m_overrideUrl; } +QString Entry::tags() const +{ + return m_tags; +} + TimeInfo Entry::timeInfo() const { return m_timeInfo; @@ -172,6 +177,11 @@ void Entry::setOverrideUrl(const QString& url) m_overrideUrl = url; } +void Entry::setTags(const QString& tags) +{ + m_tags = tags; +} + void Entry::setTimeInfo(const TimeInfo& timeInfo) { m_timeInfo = timeInfo; diff --git a/src/core/Entry.h b/src/core/Entry.h index 5147fbcf9..b14a3fdf3 100644 --- a/src/core/Entry.h +++ b/src/core/Entry.h @@ -49,6 +49,7 @@ public: QColor foregroundColor() const; QColor backgroundColor() const; QString overrideUrl() const; + QString tags() const; TimeInfo timeInfo() const; bool autoTypeEnabled() const; int autoTypeObfuscation() const; @@ -68,6 +69,7 @@ public: void setForegroundColor(const QColor& color); void setBackgroundColor(const QColor& color); void setOverrideUrl(const QString& url); + void setTags(const QString& tags); void setTimeInfo(const TimeInfo& timeInfo); void setAutoTypeEnabled(bool enable); void setAutoTypeObfuscation(int obfuscation); @@ -98,6 +100,7 @@ private: QColor m_foregroundColor; QColor m_backgroundColor; QString m_overrideUrl; + QString m_tags; TimeInfo m_timeInfo; bool m_autoTypeEnabled; int m_autoTypeObfuscation; diff --git a/src/core/Metadata.cpp b/src/core/Metadata.cpp index 284ff6bc7..3dd38bd19 100644 --- a/src/core/Metadata.cpp +++ b/src/core/Metadata.cpp @@ -145,6 +145,21 @@ const Group* Metadata::lastTopVisibleGroup() const return m_lastTopVisibleGroup; } +QDateTime Metadata::masterKeyChanged() const +{ + return m_masterKeyChanged; +} + +int Metadata::masterKeyChangeRec() const +{ + return m_masterKeyChangeRec; +} + +int Metadata::masterKeyChangeForce() const +{ + return m_masterKeyChangeForce; +} + QHash Metadata::customFields() const { return m_customFields; @@ -271,6 +286,21 @@ void Metadata::setLastTopVisibleGroup(Group* group) m_lastTopVisibleGroup = group; } +void Metadata::setMasterKeyChanged(const QDateTime& value) +{ + m_masterKeyChanged = value; +} + +void Metadata::setMasterKeyChangeRec(int value) +{ + m_masterKeyChangeRec = value; +} + +void Metadata::setMasterKeyChangeForce(int value) +{ + m_masterKeyChangeForce = value; +} + void Metadata::addCustomField(const QString& key, const QString& value) { Q_ASSERT(!m_customFields.contains(key)); diff --git a/src/core/Metadata.h b/src/core/Metadata.h index f49a4f9ff..00484cf52 100644 --- a/src/core/Metadata.h +++ b/src/core/Metadata.h @@ -57,6 +57,9 @@ public: QDateTime entryTemplatesGroupChanged() const; const Group* lastSelectedGroup() const; const Group* lastTopVisibleGroup() const; + QDateTime masterKeyChanged() const; + int masterKeyChangeRec() const; + int masterKeyChangeForce() const; QHash customFields() const; void setGenerator(const QString& value); @@ -82,6 +85,9 @@ public: void setEntryTemplatesGroupChanged(const QDateTime& value); void setLastSelectedGroup(Group* group); void setLastTopVisibleGroup(Group* group); + void setMasterKeyChanged(const QDateTime& value); + void setMasterKeyChangeRec(int value); + void setMasterKeyChangeForce(int value); void addCustomField(const QString& key, const QString& value); void removeCustomField(const QString& key); @@ -112,6 +118,10 @@ private: Group* m_lastSelectedGroup; Group* m_lastTopVisibleGroup; + QDateTime m_masterKeyChanged; + int m_masterKeyChangeRec; + int m_masterKeyChangeForce; + QHash m_customFields; }; diff --git a/src/format/KeePass2.h b/src/format/KeePass2.h index 0785a4eac..ecf7921b1 100644 --- a/src/format/KeePass2.h +++ b/src/format/KeePass2.h @@ -20,6 +20,8 @@ #include +#include "core/Uuid.h" + namespace KeePass2 { const quint32 SIGNATURE_1 = 0x9AA2D903; @@ -27,6 +29,10 @@ namespace KeePass2 const quint32 FILE_VERSION = 0x00020000; const quint32 FILE_VERSION_CRITICAL_MASK = 0xFFFF0000; + const QSysInfo::Endian BYTEORDER = QSysInfo::LittleEndian; + + const Uuid CIPHER_AES = Uuid(QByteArray::fromHex("31c1f2e6bf714350be5805216afc5aff")); + enum HeaderFieldID { EndOfHeader = 0, @@ -41,13 +47,6 @@ namespace KeePass2 StreamStartBytes = 9, InnerRandomStreamID = 10 }; - - enum CompressionAlgorithm - { - CompressionNone = 0, - CompressionGZip = 1, - CompressionCount = 2 - }; } #endif // KEEPASSX_KEEPASS2_H diff --git a/src/format/KeePass2Reader.cpp b/src/format/KeePass2Reader.cpp index 13df6b97d..f26fa188b 100644 --- a/src/format/KeePass2Reader.cpp +++ b/src/format/KeePass2Reader.cpp @@ -21,37 +21,37 @@ #include #include -#include "KeePass2XmlReader.h" +#include "core/Database.h" #include "crypto/CryptoHash.h" +#include "format/KeePass2.h" +#include "format/KeePass2XmlReader.h" #include "streams/HashedBlockStream.h" #include "streams/QtIOCompressor" #include "streams/SymmetricCipherStream.h" -const QSysInfo::Endian KeePass2Reader::BYTEORDER = QSysInfo::LittleEndian; - Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& key) { + m_db = new Database(); m_device = device; m_error = false; m_errorStr = QString(); m_headerEnd = false; - m_cipher = Uuid(); bool ok; - quint32 signature1 = Endian::readUInt32(m_device, BYTEORDER, &ok); + quint32 signature1 = Endian::readUInt32(m_device, KeePass2::BYTEORDER, &ok); if (!ok || signature1 != KeePass2::SIGNATURE_1) { raiseError("1"); return 0; } - quint32 signature2 = Endian::readUInt32(m_device, BYTEORDER, &ok); + quint32 signature2 = Endian::readUInt32(m_device, KeePass2::BYTEORDER, &ok); if (!ok || signature2 != KeePass2::SIGNATURE_2) { raiseError("2"); return 0; } - quint32 version = Endian::readUInt32(m_device, BYTEORDER, &ok) & KeePass2::FILE_VERSION_CRITICAL_MASK; + quint32 version = Endian::readUInt32(m_device, KeePass2::BYTEORDER, &ok) & KeePass2::FILE_VERSION_CRITICAL_MASK; quint32 expectedVersion = KeePass2::FILE_VERSION & KeePass2::FILE_VERSION_CRITICAL_MASK; // TODO do we support old Kdbx versions? if (!ok || (version != expectedVersion)) { @@ -62,9 +62,12 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke while (readHeaderField() && !error()) { } + QByteArray transformedMasterKey = key.transform(m_db->transformSeed(), m_db->transformRounds()); + m_db->setTransformedMasterKey(transformedMasterKey); + CryptoHash hash(CryptoHash::Sha256); hash.addData(m_masterSeed); - hash.addData(key.transform(m_transformSeed, m_transformRounds)); + hash.addData(transformedMasterKey); QByteArray finalKey = hash.result(); SymmetricCipherStream cipherStream(device, SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt, finalKey, m_encryptionIV); @@ -83,7 +86,7 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke QIODevice* xmlDevice; QScopedPointer ioCompressor; - if (m_compression == KeePass2::CompressionNone) { + if (m_db->compressionAlgo() == Database::CompressionNone) { xmlDevice = &hashedStream; } else { @@ -94,9 +97,9 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke } KeePass2XmlReader xmlReader; - Database* db = xmlReader.readDatabase(xmlDevice); + xmlReader.readDatabase(xmlDevice, m_db); // TODO forward error messages from xmlReader - return db; + return m_db; } Database* KeePass2Reader::readDatabase(const QString& filename, const CompositeKey& key) @@ -136,7 +139,7 @@ bool KeePass2Reader::readHeaderField() quint8 fieldID = fieldIDArray.at(0); bool ok; - quint16 fieldLen = Endian::readUInt16(m_device, BYTEORDER, &ok); + quint16 fieldLen = Endian::readUInt16(m_device, KeePass2::BYTEORDER, &ok); if (!ok) { raiseError(""); return false; @@ -206,7 +209,14 @@ void KeePass2Reader::setCipher(const QByteArray& data) raiseError(""); } else { - m_cipher = Uuid(data); + Uuid uuid(data); + + if (uuid != KeePass2::CIPHER_AES) { + raiseError(""); + } + else { + m_db->setCipher(uuid); + } } } @@ -216,13 +226,13 @@ void KeePass2Reader::setCompressionFlags(const QByteArray& data) raiseError(""); } else { - quint32 id = Endian::bytesToUInt32(data, BYTEORDER); + quint32 id = Endian::bytesToUInt32(data, KeePass2::BYTEORDER); - if (id >= KeePass2::CompressionCount) { + if (id > Database::CompressionAlgorithmMax) { raiseError(""); } else { - m_compression = static_cast(id); + m_db->setCompressionAlgo(static_cast(id)); } } } @@ -243,7 +253,7 @@ void KeePass2Reader::setTransformSeed(const QByteArray& data) raiseError(""); } else { - m_transformSeed = data; + m_db->setTransformSeed(data); } } @@ -253,7 +263,7 @@ void KeePass2Reader::setTansformRounds(const QByteArray& data) raiseError(""); } else { - m_transformRounds = Endian::bytesToUInt64(data, BYTEORDER); + m_db->setTransformRounds(Endian::bytesToUInt64(data, KeePass2::BYTEORDER)); } } diff --git a/src/format/KeePass2Reader.h b/src/format/KeePass2Reader.h index e839fcaa8..9730d9538 100644 --- a/src/format/KeePass2Reader.h +++ b/src/format/KeePass2Reader.h @@ -23,7 +23,6 @@ #include "core/Endian.h" #include "core/Uuid.h" #include "keys/CompositeKey.h" -#include "format/KeePass2.h" class Database; @@ -52,18 +51,13 @@ private: void setStreamStartBytes(const QByteArray& data); void setInnerRandomStreamID(const QByteArray& data); - static const QSysInfo::Endian BYTEORDER; - QIODevice* m_device; bool m_error; QString m_errorStr; bool m_headerEnd; - Uuid m_cipher; - KeePass2::CompressionAlgorithm m_compression; + Database* m_db; QByteArray m_masterSeed; - QByteArray m_transformSeed; - quint64 m_transformRounds; QByteArray m_encryptionIV; QByteArray m_streamStartBytes; }; diff --git a/src/format/KeePass2Writer.cpp b/src/format/KeePass2Writer.cpp new file mode 100644 index 000000000..115c143a5 --- /dev/null +++ b/src/format/KeePass2Writer.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2010 Felix Geyer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "KeePass2Writer.h" + +#include + +#include "core/Database.h" +#include "core/Endian.h" +#include "crypto/CryptoHash.h" +#include "crypto/Random.h" +#include "format/KeePass2XmlWriter.h" +#include "streams/HashedBlockStream.h" +#include "streams/QtIOCompressor" +#include "streams/SymmetricCipherStream.h" + +#define CHECK_RETURN(x) if (!(x)) return; +#define CHECK_RETURN_FALSE(x) if (!(x)) return false; + +KeePass2Writer::KeePass2Writer() + : m_error(false) +{ +} + +void KeePass2Writer::writeDatabase(QIODevice* device, Database* db) +{ + m_error = false; + m_errorStr = QString(); + + m_device = device; + + QByteArray masterSeed = Random::randomArray(32); + QByteArray encryptionIV = Random::randomArray(16); + QByteArray startBytes = Random::randomArray(32); + QByteArray endOfHeader = "\r\n\r\n"; + + CryptoHash hash(CryptoHash::Sha256); + hash.addData(masterSeed); + hash.addData(db->transformedMasterKey()); + QByteArray finalKey = hash.result(); + + + CHECK_RETURN(writeData(Endian::int32ToBytes(KeePass2::SIGNATURE_1, KeePass2::BYTEORDER))); + CHECK_RETURN(writeData(Endian::int32ToBytes(KeePass2::SIGNATURE_2, KeePass2::BYTEORDER))); + CHECK_RETURN(writeData(Endian::int32ToBytes(KeePass2::FILE_VERSION, KeePass2::BYTEORDER))); + + CHECK_RETURN(writeHeaderField(KeePass2::CipherID, db->cipher().toByteArray())); + CHECK_RETURN(writeHeaderField(KeePass2::CompressionFlags, Endian::int32ToBytes(db->compressionAlgo(), KeePass2::BYTEORDER))); + CHECK_RETURN(writeHeaderField(KeePass2::MasterSeed, masterSeed)); + CHECK_RETURN(writeHeaderField(KeePass2::TransformSeed, db->transformSeed())); + CHECK_RETURN(writeHeaderField(KeePass2::TransformRounds, Endian::int64ToBytes(db->transformRounds(), KeePass2::BYTEORDER))); + CHECK_RETURN(writeHeaderField(KeePass2::EncryptionIV, encryptionIV)); + CHECK_RETURN(writeHeaderField(KeePass2::StreamStartBytes, startBytes)); + CHECK_RETURN(writeHeaderField(KeePass2::EndOfHeader, endOfHeader)); + + SymmetricCipherStream cipherStream(device, SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt, finalKey, encryptionIV); + cipherStream.open(QIODevice::WriteOnly); + m_device = &cipherStream; + CHECK_RETURN(writeData(startBytes)); + + HashedBlockStream hashedStream(&cipherStream); + hashedStream.open(QIODevice::WriteOnly); + + QScopedPointer ioCompressor; + + if (db->compressionAlgo() == Database::CompressionNone) { + m_device = &hashedStream; + } + else { + ioCompressor.reset(new QtIOCompressor(&hashedStream)); + ioCompressor->setStreamFormat(QtIOCompressor::GzipFormat); + ioCompressor->open(QIODevice::WriteOnly); + m_device = ioCompressor.data(); + } + + KeePass2XmlWriter xmlWriter; + xmlWriter.writeDatabase(m_device, db); +} + +bool KeePass2Writer::writeData(const QByteArray& data) +{ + if (m_device->write(data) != data.size()) { + m_error = true; + m_errorStr = m_device->errorString(); + return false; + } + else { + return true; + } +} + +bool KeePass2Writer::writeHeaderField(KeePass2::HeaderFieldID fieldId, const QByteArray& data) +{ + QByteArray fieldIdArr; + fieldIdArr[0] = fieldId; + CHECK_RETURN_FALSE(writeData(fieldIdArr)); + CHECK_RETURN_FALSE(writeData(Endian::int16ToBytes(data.size(), KeePass2::BYTEORDER))); + CHECK_RETURN_FALSE(writeData(data)); + + return true; +} + +void KeePass2Writer::writeDatabase(const QString& filename, Database* db) +{ + QFile file(filename); + file.open(QIODevice::WriteOnly|QIODevice::Truncate); + writeDatabase(&file, db); +} + +bool KeePass2Writer::error() +{ + return m_error; +} + +QString KeePass2Writer::errorString() +{ + return m_errorStr; +} diff --git a/src/format/KeePass2Writer.h b/src/format/KeePass2Writer.h new file mode 100644 index 000000000..2477468d4 --- /dev/null +++ b/src/format/KeePass2Writer.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010 Felix Geyer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSX_KEEPASS2WRITER_H +#define KEEPASSX_KEEPASS2WRITER_H + +#include + +#include "format/KeePass2.h" +#include "keys/CompositeKey.h" + +class Database; + +class KeePass2Writer +{ +public: + KeePass2Writer(); + void writeDatabase(QIODevice* device, Database* db); + void writeDatabase(const QString& filename, Database* db); + bool error(); + QString errorString(); + +private: + bool writeData(const QByteArray& data); + bool writeHeaderField(KeePass2::HeaderFieldID fieldId, const QByteArray& data); + + QIODevice* m_device; + bool m_error; + QString m_errorStr; +}; + +#endif // KEEPASSX_KEEPASS2WRITER_H diff --git a/src/format/KeePass2XmlReader.cpp b/src/format/KeePass2XmlReader.cpp index 605dc93aa..62a4e81e7 100644 --- a/src/format/KeePass2XmlReader.cpp +++ b/src/format/KeePass2XmlReader.cpp @@ -29,11 +29,11 @@ KeePass2XmlReader::KeePass2XmlReader() { } -Database* KeePass2XmlReader::readDatabase(QIODevice* device) +void KeePass2XmlReader::readDatabase(QIODevice* device, Database* db) { m_xml.setDevice(device); - m_db = new Database(); + m_db = db; m_meta = m_db->metadata(); m_tmpParent = new Group(); @@ -50,8 +50,13 @@ Database* KeePass2XmlReader::readDatabase(QIODevice* device) } delete m_tmpParent; +} - return m_db; +Database* KeePass2XmlReader::readDatabase(QIODevice* device) +{ + Database* db = new Database(); + readDatabase(device, db); + return db; } Database* KeePass2XmlReader::readDatabase(const QString& filename) @@ -120,6 +125,15 @@ void KeePass2XmlReader::parseMeta() else if (m_xml.name() == "MaintenanceHistoryDays") { m_meta->setMaintenanceHistoryDays(readNumber()); } + else if (m_xml.name() == "MasterKeyChanged") { + m_meta->setMasterKeyChanged(readDateTime()); + } + else if (m_xml.name() == "MasterKeyChangeRec") { + m_meta->setMasterKeyChangeRec(readNumber()); + } + else if (m_xml.name() == "MasterKeyChangeForce") { + m_meta->setMasterKeyChangeForce(readNumber()); + } else if (m_xml.name() == "MemoryProtection") { parseMemoryProtection(); } @@ -461,6 +475,9 @@ Entry* KeePass2XmlReader::parseEntry(bool history) else if (m_xml.name() == "OverrideURL") { entry->setOverrideUrl(readString()); } + else if (m_xml.name() == "Tags") { + entry->setTags(readString()); + } else if (m_xml.name() == "Times") { entry->setTimeInfo(parseTimes()); } diff --git a/src/format/KeePass2XmlReader.h b/src/format/KeePass2XmlReader.h index f6c4d8993..da6b17c36 100644 --- a/src/format/KeePass2XmlReader.h +++ b/src/format/KeePass2XmlReader.h @@ -38,6 +38,7 @@ class KeePass2XmlReader public: KeePass2XmlReader(); Database* readDatabase(QIODevice* device); + void readDatabase(QIODevice* device, Database* db); Database* readDatabase(const QString& filename); bool error(); QString errorString(); diff --git a/src/format/KeePass2XmlWriter.cpp b/src/format/KeePass2XmlWriter.cpp index d3b2a612a..77a7d8adb 100644 --- a/src/format/KeePass2XmlWriter.cpp +++ b/src/format/KeePass2XmlWriter.cpp @@ -53,7 +53,7 @@ void KeePass2XmlWriter::writeDatabase(QIODevice* device, Database* db) void KeePass2XmlWriter::writeDatabase(const QString& filename, Database* db) { QFile file(filename); - file.open(QIODevice::WriteOnly); + file.open(QIODevice::WriteOnly|QIODevice::Truncate); writeDatabase(&file, db); } @@ -69,6 +69,9 @@ void KeePass2XmlWriter::writeMetadata() writeString("DefaultUserName", m_meta->defaultUserName()); writeDateTime("DefaultUserNameChanged", m_meta->defaultUserNameChanged()); writeNumber("MaintenanceHistoryDays", m_meta->maintenanceHistoryDays()); + writeDateTime("MasterKeyChanged", m_meta->masterKeyChanged()); + writeNumber("MasterKeyChangeRec", m_meta->masterKeyChangeRec()); + writeNumber("MasterKeyChangeForce", m_meta->masterKeyChangeForce()); writeMemoryProtection(); writeCustomIcons(); writeBool("RecycleBinEnabled", m_meta->recycleBinEnabled()); @@ -263,16 +266,17 @@ void KeePass2XmlWriter::writeEntry(const Entry* entry) writeColor("ForegroundColor", entry->foregroundColor()); writeColor("BackgroundColor", entry->backgroundColor()); writeString("OverrideURL", entry->overrideUrl()); + writeString("Tags", entry->tags()); writeTimes(entry->timeInfo()); - Q_FOREACH (const QString& key, entry->attributes()) { + Q_FOREACH (const QString& key, entry->attributes().keys()) { m_xml.writeStartElement("String"); writeString("Key", key); writeString("Value", entry->attributes().value(key)); m_xml.writeEndElement(); } - Q_FOREACH (const QString& key, entry->attachments()) { + Q_FOREACH (const QString& key, entry->attachments().keys()) { m_xml.writeStartElement("Binary"); writeString("Key", key); writeBinary("Value", entry->attachments().value(key)); diff --git a/src/streams/HashedBlockStream.cpp b/src/streams/HashedBlockStream.cpp index c0f97764a..3ad35e30a 100644 --- a/src/streams/HashedBlockStream.cpp +++ b/src/streams/HashedBlockStream.cpp @@ -38,6 +38,11 @@ HashedBlockStream::HashedBlockStream(QIODevice* baseDevice, qint32 blockSize) init(); } +HashedBlockStream::~HashedBlockStream() +{ + close(); +} + void HashedBlockStream::init() { m_buffer.clear(); diff --git a/src/streams/HashedBlockStream.h b/src/streams/HashedBlockStream.h index 3aa23bf22..fc20ce14f 100644 --- a/src/streams/HashedBlockStream.h +++ b/src/streams/HashedBlockStream.h @@ -29,6 +29,7 @@ class HashedBlockStream : public LayeredStream public: explicit HashedBlockStream(QIODevice* baseDevice); HashedBlockStream(QIODevice* baseDevice, qint32 blockSize); + ~HashedBlockStream(); bool reset(); void close(); diff --git a/src/streams/LayeredStream.cpp b/src/streams/LayeredStream.cpp index d0078085c..b71eb228a 100644 --- a/src/streams/LayeredStream.cpp +++ b/src/streams/LayeredStream.cpp @@ -24,6 +24,11 @@ LayeredStream::LayeredStream(QIODevice* baseDevice) connect(baseDevice, SIGNAL(aboutToClose()), SLOT(closeStream())); } +LayeredStream::~LayeredStream() +{ + close(); +} + bool LayeredStream::isSequential() const { return true; diff --git a/src/streams/LayeredStream.h b/src/streams/LayeredStream.h index 86e9d1a6b..0c008ae93 100644 --- a/src/streams/LayeredStream.h +++ b/src/streams/LayeredStream.h @@ -26,6 +26,7 @@ class LayeredStream : public QIODevice public: explicit LayeredStream(QIODevice* baseDevice); + virtual ~LayeredStream(); bool isSequential() const; virtual QString errorString() const; diff --git a/src/streams/SymmetricCipherStream.cpp b/src/streams/SymmetricCipherStream.cpp index fd7354727..edfbc0b74 100644 --- a/src/streams/SymmetricCipherStream.cpp +++ b/src/streams/SymmetricCipherStream.cpp @@ -28,6 +28,11 @@ SymmetricCipherStream::SymmetricCipherStream(QIODevice* baseDevice, SymmetricCip m_cipher = new SymmetricCipher(algo, mode, direction, key, iv); } +SymmetricCipherStream::~SymmetricCipherStream() +{ + close(); +} + bool SymmetricCipherStream::reset() { if (isWritable()) { diff --git a/src/streams/SymmetricCipherStream.h b/src/streams/SymmetricCipherStream.h index 90bfc7257..186bd1200 100644 --- a/src/streams/SymmetricCipherStream.h +++ b/src/streams/SymmetricCipherStream.h @@ -30,6 +30,7 @@ class SymmetricCipherStream : public LayeredStream public: SymmetricCipherStream(QIODevice* baseDevice, SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode, SymmetricCipher::Direction direction, const QByteArray& key, const QByteArray& iv); + ~SymmetricCipherStream(); bool reset(); void close(); diff --git a/src/streams/qtiocompressor.cpp b/src/streams/qtiocompressor.cpp index 0de5dff4d..569596302 100644 --- a/src/streams/qtiocompressor.cpp +++ b/src/streams/qtiocompressor.cpp @@ -45,7 +45,7 @@ ****************************************************************************/ #include "qtiocompressor.h" -#include "zlib.h" +#include #include typedef Bytef ZlibByte; -- cgit v1.2.3