Properly save custom header data

Ensure adding custom data upgrades to KDBX4
Implement review feedback
This commit is contained in:
Janek Bevendorff 2018-02-18 21:01:22 +01:00
parent 114f00e1e8
commit 5410d78bbb
26 changed files with 189 additions and 174 deletions

View File

@ -1,6 +1,5 @@
/*
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
*
* 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
@ -50,21 +49,15 @@ bool CustomData::containsValue(const QString& value) const
void CustomData::set(const QString& key, const QString& value)
{
bool emitModified = false;
bool addAttribute = !m_data.contains(key);
bool changeValue = !addAttribute && (m_data.value(key) != value);
if (addAttribute ) {
if (addAttribute) {
emit aboutToBeAdded(key);
}
}
if (addAttribute || changeValue) {
m_data.insert(key, value);
emitModified = true;
}
if (emitModified) {
emit modified();
}
@ -85,13 +78,10 @@ void CustomData::remove(const QString& key)
void CustomData::rename(const QString& oldKey, const QString& newKey)
{
if (!m_data.contains(oldKey)) {
Q_ASSERT(false);
return;
}
if (m_data.contains(newKey)) {
Q_ASSERT(false);
const bool containsOldKey = m_data.contains(oldKey);
const bool containsNewKey = m_data.contains(newKey);
Q_ASSERT(containsOldKey && !containsNewKey);
if (!containsOldKey || containsNewKey) {
return;
}
@ -108,16 +98,17 @@ void CustomData::rename(const QString& oldKey, const QString& newKey)
void CustomData::copyDataFrom(const CustomData* other)
{
if (*this != *other) {
emit aboutToBeReset();
m_data = other->m_data;
emit reset();
emit modified();
if (*this == *other) {
return;
}
}
emit aboutToBeReset();
m_data = other->m_data;
emit reset();
emit modified();
}
bool CustomData::operator==(const CustomData& other) const
{
return (m_data == other.m_data);
@ -143,7 +134,12 @@ bool CustomData::isEmpty() const
return m_data.isEmpty();
}
int CustomData::dataSize()
int CustomData::size() const
{
return m_data.size();
}
int CustomData::dataSize() const
{
int size = 0;

View File

@ -1,6 +1,5 @@
/*
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
*
* 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
@ -16,17 +15,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KEEPASSX_CUSTOMDATA_H
#define KEEPASSX_CUSTOMDATA_H
#ifndef KEEPASSXC_CUSTOMDATA_H
#define KEEPASSXC_CUSTOMDATA_H
#include <QMap>
#include <QHash>
#include <QObject>
#include <QSet>
#include <QStringList>
class CustomData : public QObject
{
Q_OBJECT
Q_OBJECT
public:
explicit CustomData(QObject* parent = nullptr);
@ -40,7 +39,8 @@ public:
void rename(const QString& oldKey, const QString& newKey);
void clear();
bool isEmpty() const;
int dataSize();
int size() const;
int dataSize() const;
void copyDataFrom(const CustomData* other);
bool operator==(const CustomData& other) const;
bool operator!=(const CustomData& other) const;
@ -61,4 +61,4 @@ private:
QHash<QString, QString> m_data;
};
#endif // KEEPASSX_CUSTOMDATA_H
#endif // KEEPASSXC_CUSTOMDATA_H

View File

@ -320,6 +320,17 @@ bool Database::verifyKey(const CompositeKey& key) const
return (m_data.key.rawKey() == key.rawKey());
}
QVariantMap Database::publicCustomData() const
{
return m_data.publicCustomData;
}
void Database::setPublicCustomData(const QVariantMap& customData)
{
m_data.publicCustomData = customData;
}
void Database::createRecycleBin()
{
Group* recycleBin = Group::createRecycleBin();
@ -578,14 +589,6 @@ void Database::setKdf(QSharedPointer<Kdf> kdf)
m_data.kdf = std::move(kdf);
}
void Database::setPublicCustomData(QByteArray data) {
m_data.publicCustomData = data;
}
QByteArray Database::publicCustomData() const {
return m_data.publicCustomData;
}
bool Database::changeKdf(QSharedPointer<Kdf> kdf)
{
kdf->randomizeSeed();

View File

@ -59,12 +59,12 @@ public:
Uuid cipher;
CompressionAlgorithm compressionAlgo;
QByteArray transformedMasterKey;
QByteArray publicCustomData;
QSharedPointer<Kdf> kdf;
CompositeKey key;
bool hasKey;
QByteArray masterSeed;
QByteArray challengeResponseKey;
QVariantMap publicCustomData;
};
Database();
@ -93,7 +93,6 @@ public:
Uuid cipher() const;
Database::CompressionAlgorithm compressionAlgo() const;
QSharedPointer<Kdf> kdf() const;
QByteArray publicCustomData() const;
QByteArray transformedMasterKey() const;
const CompositeKey& key() const;
QByteArray challengeResponseKey() const;
@ -102,11 +101,12 @@ public:
void setCipher(const Uuid& cipher);
void setCompressionAlgo(Database::CompressionAlgorithm algo);
void setKdf(QSharedPointer<Kdf> kdf);
void setPublicCustomData(QByteArray data);
bool setKey(const CompositeKey& key, bool updateChangedTime = true,
bool updateTransformSalt = false);
bool hasKey() const;
bool verifyKey(const CompositeKey& key) const;
QVariantMap publicCustomData() const;
void setPublicCustomData(const QVariantMap& customData);
void recycleEntry(Entry* entry);
void recycleGroup(Group* group);
void emptyRecycleBin();

View File

@ -342,12 +342,12 @@ const EntryAttachments* Entry::attachments() const
return m_attachments;
}
CustomData *Entry::customData()
CustomData* Entry::customData()
{
return m_customData;
}
const CustomData *Entry::customData() const
const CustomData* Entry::customData() const
{
return m_customData;
}
@ -618,7 +618,7 @@ void Entry::truncateHistory()
size += historyItem->attributes()->attributesSize();
size += historyItem->autoTypeAssociations()->associationsSize();
size += historyItem->attachments()->attachmentsSize();
size += customData()->dataSize();
size += historyItem->customData()->dataSize();
const QStringList tags = historyItem->tags().split(delimiter, QString::SkipEmptyParts);
for (const QString& tag : tags) {
size += tag.toUtf8().size();

View File

@ -108,8 +108,8 @@ public:
const EntryAttributes* attributes() const;
EntryAttachments* attachments();
const EntryAttachments* attachments() const;
CustomData *customData();
const CustomData *customData() const;
CustomData* customData();
const CustomData* customData() const;
static const int DefaultIconNumber;
static const int ResolveMaximumDepth;
@ -231,11 +231,10 @@ private:
Uuid m_uuid;
EntryData m_data;
EntryAttributes* const m_attributes;
EntryAttachments* const m_attachments;
AutoTypeAssociations* const m_autoTypeAssociations;
CustomData* const m_customData;
QPointer<EntryAttributes> m_attributes;
QPointer<EntryAttachments> m_attachments;
QPointer<AutoTypeAssociations> m_autoTypeAssociations;
QPointer<CustomData> m_customData;
QList<Entry*> m_history;
Entry* m_tmpHistoryItem;

View File

@ -254,12 +254,12 @@ bool Group::isExpired() const
return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < QDateTime::currentDateTimeUtc();
}
CustomData *Group::customData()
CustomData* Group::customData()
{
return m_customData;
}
const CustomData *Group::customData() const
const CustomData* Group::customData() const
{
return m_customData;
}

View File

@ -84,8 +84,8 @@ public:
bool resolveAutoTypeEnabled() const;
Entry* lastTopVisibleEntry() const;
bool isExpired() const;
CustomData *customData();
const CustomData *customData() const;
CustomData* customData();
const CustomData* customData() const;
static const int DefaultIconNumber;
static const int RecycleBinIconNumber;
@ -191,7 +191,7 @@ private:
QList<Group*> m_children;
QList<Entry*> m_entries;
CustomData *const m_customData;
QPointer<CustomData> m_customData;
QPointer<Group> m_parent;

View File

@ -294,12 +294,12 @@ int Metadata::historyMaxSize() const
return m_data.historyMaxSize;
}
CustomData *Metadata::customData()
CustomData* Metadata::customData()
{
return m_customData;
}
const CustomData *Metadata::customData() const
const CustomData* Metadata::customData() const
{
return m_customData;
}

View File

@ -98,8 +98,8 @@ public:
int masterKeyChangeForce() const;
int historyMaxItems() const;
int historyMaxSize() const;
CustomData *customData();
const CustomData *customData() const;
CustomData* customData();
const CustomData* customData() const;
static const int DefaultHistoryMaxItems;
static const int DefaultHistoryMaxSize;
@ -175,7 +175,7 @@ private:
QDateTime m_masterKeyChanged;
QDateTime m_settingsChanged;
CustomData *const m_customData;
QPointer<CustomData> m_customData;
bool m_updateDatetime;
};

View File

@ -209,9 +209,14 @@ bool Kdbx4Reader::readHeaderField(StoreDataStream& device)
break;
}
case KeePass2::HeaderFieldID::PublicCustomData:
m_db->setPublicCustomData(fieldData);
case KeePass2::HeaderFieldID::PublicCustomData: {
QBuffer variantBuffer;
variantBuffer.setBuffer(&fieldData);
variantBuffer.open(QBuffer::ReadOnly);
QVariantMap data = readVariantMap(&variantBuffer);
m_db->setPublicCustomData(data);
break;
}
case KeePass2::HeaderFieldID::ProtectedStreamKey:
case KeePass2::HeaderFieldID::TransformRounds:
@ -291,10 +296,10 @@ bool Kdbx4Reader::readInnerHeaderField(QIODevice* device)
}
/**
* Helper method for reading KDF parameters into variant map.
* Helper method for reading a serialized variant map.
*
* @param device input device
* @return filled variant map
* @return de-serialized variant map
*/
QVariantMap Kdbx4Reader::readVariantMap(QIODevice* device)
{

View File

@ -22,6 +22,8 @@
#include "streams/HmacBlockStream.h"
#include "core/Database.h"
#include "core/Metadata.h"
#include "core/CustomData.h"
#include "crypto/CryptoHash.h"
#include "crypto/Random.h"
#include "format/KeePass2RandomStream.h"
@ -86,10 +88,14 @@ bool Kdbx4Writer::writeDatabase(QIODevice* device, Database* db)
raiseError(tr("Failed to serialize KDF parameters variant map"));
return false;
}
QByteArray publicCustomData = db->publicCustomData();
CHECK_RETURN_FALSE(writeHeaderField<quint32>(&header, KeePass2::HeaderFieldID::KdfParameters, kdfParamBytes));
QVariantMap publicCustomData = db->publicCustomData();
if (!publicCustomData.isEmpty()) {
CHECK_RETURN_FALSE(writeHeaderField<quint32>(&header, KeePass2::HeaderFieldID::PublicCustomData, publicCustomData));
QByteArray serialized;
serializeVariantMap(publicCustomData, serialized);
CHECK_RETURN_FALSE(writeHeaderField<quint32>(
&header, KeePass2::HeaderFieldID::PublicCustomData, serialized));
}
CHECK_RETURN_FALSE(writeHeaderField<quint32>(&header, KeePass2::HeaderFieldID::EndOfHeader, endOfHeader));
@ -230,7 +236,7 @@ void Kdbx4Writer::writeAttachments(QIODevice* device, Database* db)
}
/**
* Serialize KDF parameter variant map to byte array.
* Serialize variant map to byte array.
*
* @param map input variant map
* @param outputBytes output byte array

View File

@ -397,7 +397,7 @@ void KdbxXmlReader::parseBinaries()
}
}
void KdbxXmlReader::parseCustomData(CustomData *customData)
void KdbxXmlReader::parseCustomData(CustomData* customData)
{
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "CustomData");
@ -410,7 +410,7 @@ void KdbxXmlReader::parseCustomData(CustomData *customData)
}
}
void KdbxXmlReader::parseCustomDataItem(CustomData *customData)
void KdbxXmlReader::parseCustomDataItem(CustomData* customData)
{
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "Item");
@ -748,7 +748,7 @@ Entry* KdbxXmlReader::parseEntry(bool history)
}
continue;
}
if (m_xml.name() == "CustomData" ){
if (m_xml.name() == "CustomData") {
parseCustomData(entry->customData());
continue;
}

View File

@ -66,8 +66,8 @@ protected:
virtual void parseCustomIcons();
virtual void parseIcon();
virtual void parseBinaries();
virtual void parseCustomData(CustomData *customData);
virtual void parseCustomDataItem(CustomData *customData);
virtual void parseCustomData(CustomData* customData);
virtual void parseCustomDataItem(CustomData* customData);
virtual bool parseRoot();
virtual Group* parseGroup();
virtual void parseDeletedObjects();

View File

@ -129,7 +129,9 @@ void KdbxXmlWriter::writeMetadata()
if (m_kdbxVersion < KeePass2::FILE_VERSION_4) {
writeBinaries();
}
writeCustomData(m_meta->customData());
if (m_kdbxVersion >= KeePass2::FILE_VERSION_4) {
writeCustomData(m_meta->customData());
}
m_xml.writeEndElement();
}
@ -218,8 +220,11 @@ void KdbxXmlWriter::writeBinaries()
m_xml.writeEndElement();
}
void KdbxXmlWriter::writeCustomData(const CustomData *customData)
void KdbxXmlWriter::writeCustomData(const CustomData* customData)
{
if (customData->isEmpty()) {
return;
}
m_xml.writeStartElement("CustomData");
const QList<QString> keyList = customData->keys();
@ -276,7 +281,7 @@ void KdbxXmlWriter::writeGroup(const Group* group)
writeUuid("LastTopVisibleEntry", group->lastTopVisibleEntry());
if (!group->customData()->isEmpty()){
if (m_kdbxVersion >= KeePass2::FILE_VERSION_4) {
writeCustomData(group->customData());
}
@ -405,7 +410,7 @@ void KdbxXmlWriter::writeEntry(const Entry* entry)
writeAutoType(entry);
if (!entry->customData()->isEmpty()){
if (m_kdbxVersion >= KeePass2::FILE_VERSION_4) {
writeCustomData(entry->customData());
}

View File

@ -51,7 +51,7 @@ private:
void writeCustomIcons();
void writeIcon(const Uuid& uuid, const QImage& icon);
void writeBinaries();
void writeCustomData(const CustomData *customData);
void writeCustomData(const CustomData* customData);
void writeCustomDataItem(const QString& key, const QString& value);
void writeRoot();
void writeGroup(const Group* group);

View File

@ -19,6 +19,8 @@
#include <QFile>
#include "core/Database.h"
#include "core/Group.h"
#include "core/Metadata.h"
#include "crypto/kdf/AesKdf.h"
#include "format/KeePass2Writer.h"
#include "format/Kdbx3Writer.h"
@ -48,12 +50,24 @@ bool KeePass2Writer::writeDatabase(const QString& filename, Database* db)
* @param db source database
* @return true on success
*/
bool KeePass2Writer::writeDatabase(QIODevice* device, Database* db) {
m_error = false;
m_errorStr.clear();
// determine KDBX3 vs KDBX4
if (db->kdf()->uuid() == KeePass2::KDF_AES_KDBX3 && db->publicCustomData().isEmpty()) {
bool hasCustomData = !db->publicCustomData().isEmpty() || (db->metadata()->customData() && !db->metadata()->customData()->isEmpty());
if (!hasCustomData) {
for (const auto& entry: db->rootGroup()->entriesRecursive(true)) {
if ((entry->customData() && !entry->customData()->isEmpty()) ||
(entry->group() && entry->group()->customData() && !entry->group()->customData()->isEmpty())) {
hasCustomData = true;
break;
}
}
}
if (db->kdf()->uuid() == KeePass2::KDF_AES_KDBX3 && !hasCustomData) {
m_version = KeePass2::FILE_VERSION_3_1;
m_writer.reset(new Kdbx3Writer());
} else {

View File

@ -18,8 +18,6 @@
#include "EditWidgetProperties.h"
#include "ui_EditWidgetProperties.h"
#include <QStandardItemModel>
EditWidgetProperties::EditWidgetProperties(QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::EditWidgetProperties())
@ -29,14 +27,14 @@ EditWidgetProperties::EditWidgetProperties(QWidget* parent)
m_ui->setupUi(this);
m_ui->customDataTable->setModel(m_customDataModel);
this->connect( m_ui->removeCustomDataButton, SIGNAL(clicked()), SLOT(removeSelectedPluginData()));
connect(m_ui->removeCustomDataButton, SIGNAL(clicked()), SLOT(removeSelectedPluginData()));
}
EditWidgetProperties::~EditWidgetProperties()
{
}
void EditWidgetProperties::setFields(const TimeInfo &timeInfo, const Uuid &uuid)
void EditWidgetProperties::setFields(const TimeInfo& timeInfo, const Uuid& uuid)
{
static const QString timeFormat("d MMM yyyy HH:mm:ss");
m_ui->modifiedEdit->setText(
@ -48,35 +46,37 @@ void EditWidgetProperties::setFields(const TimeInfo &timeInfo, const Uuid &uuid)
m_ui->uuidEdit->setText(uuid.toHex());
}
void EditWidgetProperties::setCustomData(const CustomData *customData)
void EditWidgetProperties::setCustomData(const CustomData* customData)
{
Q_ASSERT(customData != nullptr);
Q_ASSERT(customData);
m_customData->copyDataFrom(customData);
this->updateModel();
}
const CustomData *EditWidgetProperties::customData() const
const CustomData* EditWidgetProperties::customData() const
{
return m_customData;
}
void EditWidgetProperties::removeSelectedPluginData()
{
const QItemSelectionModel *pSelectionModel = m_ui->customDataTable->selectionModel();
if (pSelectionModel){
for( const QModelIndex& index : pSelectionModel->selectedRows(0) ){
const QItemSelectionModel* itemSelectionModel = m_ui->customDataTable->selectionModel();
if (itemSelectionModel) {
for (const QModelIndex& index : itemSelectionModel->selectedRows(0)) {
const QString key = index.data().toString();
m_customData->remove(key);
}
this->updateModel();
updateModel();
}
}
void EditWidgetProperties::updateModel()
{
m_customDataModel->clear();
for( const QString& key : m_customData->keys() ){
m_customDataModel->appendRow(QList<QStandardItem*>() << new QStandardItem( key ) << new QStandardItem( m_customData->value( key ) ));
for (const QString& key : m_customData->keys()) {
m_customDataModel->appendRow(QList<QStandardItem*>()
<< new QStandardItem(key)
<< new QStandardItem(m_customData->value(key)));
}
}

View File

@ -38,10 +38,10 @@ public:
explicit EditWidgetProperties(QWidget* parent = nullptr);
~EditWidgetProperties();
void setFields(const TimeInfo &timeInfo, const Uuid &uuid);
void setCustomData(const CustomData *customData);
void setFields(const TimeInfo& timeInfo, const Uuid& uuid);
void setCustomData(const CustomData* customData);
const CustomData *customData() const;
const CustomData* customData() const;
private slots:
void removeSelectedPluginData();

View File

@ -73,4 +73,4 @@ void WelcomeWidget::refreshLastDatabases()
itm->setText(database);
m_ui->recentListWidget->addItem(itm);
}
}
}

View File

@ -33,7 +33,6 @@ QTEST_GUILESS_MAIN(TestKdbx3)
void TestKdbx3::initTestCaseImpl()
{
}
Database* TestKdbx3::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString)

View File

@ -131,6 +131,7 @@ void TestKdbx4::testFormat400Upgrade()
{
QFETCH(Uuid, kdfUuid);
QFETCH(Uuid, cipherUuid);
QFETCH(bool, addCustomData);
QFETCH(quint32, expectedVersion);
QScopedPointer<Database> sourceDb(new Database());
@ -147,6 +148,12 @@ void TestKdbx4::testFormat400Upgrade()
// upgrade to KDBX 4 by changing KDF and Cipher
sourceDb->changeKdf(KeePass2::uuidToKdf(kdfUuid));
sourceDb->setCipher(cipherUuid);
if (addCustomData) {
sourceDb->metadata()->customData()->set("CustomPublicData", "Hey look, I turned myself into a pickle!");
sourceDb->rootGroup()->customData()->set("CustomGroupData", "I just killed my family! I don't care who they were!");
}
KeePass2Writer writer;
writer.writeDatabase(&buffer, sourceDb.data());
if (writer.hasError()) {
@ -165,26 +172,39 @@ void TestKdbx4::testFormat400Upgrade()
QCOMPARE(targetDb->metadata()->name(), sourceDb->metadata()->name());
QCOMPARE(reader.version(), expectedVersion);
QCOMPARE(targetDb->kdf()->uuid(), sourceDb->kdf()->uuid());
QCOMPARE(targetDb->cipher(), cipherUuid);
QCOMPARE(*targetDb->metadata()->customData(), *sourceDb->metadata()->customData());
QCOMPARE(*targetDb->rootGroup()->customData(), *sourceDb->rootGroup()->customData());
}
void TestKdbx4::testFormat400Upgrade_data()
{
QTest::addColumn<Uuid>("kdfUuid");
QTest::addColumn<Uuid>("cipherUuid");
QTest::addColumn<bool>("addCustomData");
QTest::addColumn<quint32>("expectedVersion");
auto constexpr kdbx3 = KeePass2::FILE_VERSION_3_1 & KeePass2::FILE_VERSION_CRITICAL_MASK;
auto constexpr kdbx4 = KeePass2::FILE_VERSION_4 & KeePass2::FILE_VERSION_CRITICAL_MASK;
QTest::newRow("Argon2 + AES") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES << kdbx4;
QTest::newRow("AES-KDF + AES") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES << kdbx4;
QTest::newRow("AES-KDF (legacy) + AES") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES << kdbx3;
QTest::newRow("Argon2 + ChaCha20") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_CHACHA20 << kdbx4;
QTest::newRow("AES-KDF + ChaCha20") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_CHACHA20 << kdbx4;
QTest::newRow("AES-KDF (legacy) + ChaCha20") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_CHACHA20 << kdbx3;
QTest::newRow("Argon2 + Twofish") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_TWOFISH << kdbx4;
QTest::newRow("AES-KDF + Twofish") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_TWOFISH << kdbx4;
QTest::newRow("AES-KDF (legacy) + Twofish") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_TWOFISH << kdbx3;
QTest::newRow("Argon2 + AES") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES << false << kdbx4;
QTest::newRow("AES-KDF + AES") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES << false << kdbx4;
QTest::newRow("AES-KDF (legacy) + AES") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES << false << kdbx3;
QTest::newRow("Argon2 + AES + CustomData") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES << true << kdbx4;
QTest::newRow("AES-KDF + AES + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES << true << kdbx4;
QTest::newRow("AES-KDF (legacy) + AES + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES << true << kdbx4;
QTest::newRow("Argon2 + ChaCha20") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_CHACHA20 << false << kdbx4;
QTest::newRow("AES-KDF + ChaCha20") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_CHACHA20 << false << kdbx4;
QTest::newRow("AES-KDF (legacy) + ChaCha20") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_CHACHA20 << false << kdbx3;
QTest::newRow("Argon2 + ChaCha20 + CustomData") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_CHACHA20 << true << kdbx4;
QTest::newRow("AES-KDF + ChaCha20 + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_CHACHA20 << true << kdbx4;
QTest::newRow("AES-KDF (legacy) + ChaCha20 + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_CHACHA20 << true << kdbx4;
QTest::newRow("Argon2 + Twofish") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_TWOFISH << false << kdbx4;
QTest::newRow("AES-KDF + Twofish") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_TWOFISH << false << kdbx4;
QTest::newRow("AES-KDF (legacy) + Twofish") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_TWOFISH << false << kdbx3;
QTest::newRow("Argon2 + Twofish + CustomData") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_TWOFISH << true << kdbx4;
QTest::newRow("AES-KDF + Twofish + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_TWOFISH << true << kdbx4;
QTest::newRow("AES-KDF (legacy) + Twofish + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_TWOFISH << true << kdbx4;
}

View File

@ -124,15 +124,6 @@ void TestKeePass2Format::testXmlCustomIcons()
}
}
void TestKeePass2Format::testXmlCustomData()
{
QHash<QString, QString> customFields = m_xmlDb->metadata()->customFields();
QCOMPARE(customFields.size(), 2);
QCOMPARE(customFields.value("A Sample Test Key"), QString("valu"));
QCOMPARE(customFields.value("custom key"), QString("blub"));
}
void TestKeePass2Format::testXmlGroupRoot()
{
const Group* group = m_xmlDb->rootGroup();
@ -155,7 +146,6 @@ void TestKeePass2Format::testXmlGroupRoot()
QCOMPARE(group->autoTypeEnabled(), Group::Inherit);
QCOMPARE(group->searchingEnabled(), Group::Inherit);
QCOMPARE(group->lastTopVisibleEntry()->uuid().toBase64(), QString("+wSUOv6qf0OzW8/ZHAs2sA=="));
QCOMPARE(group->children().size(), 3);
QVERIFY(m_xmlDb->metadata()->recycleBin() == m_xmlDb->rootGroup()->children().at(2));

View File

@ -40,7 +40,6 @@ private slots:
*/
void testXmlMetadata();
void testXmlCustomIcons();
void testXmlCustomData();
void testXmlGroupRoot();
void testXmlGroup1();
void testXmlGroup2();

View File

@ -40,31 +40,31 @@ void TestModified::testSignals()
CompositeKey compositeKey;
Database* db = new Database();
Group* root = db->rootGroup();
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
QScopedPointer<Database> db(new Database());
auto* root = db->rootGroup();
QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
db->setKey(compositeKey);
QCOMPARE(spyModified.count(), ++spyCount);
Group* group1 = new Group();
auto* group1 = new Group();
group1->setParent(root);
QCOMPARE(spyModified.count(), ++spyCount);
Group* group2 = new Group();
auto* group2 = new Group();
group2->setParent(root);
QCOMPARE(spyModified.count(), ++spyCount);
group2->setParent(root, 0);
QCOMPARE(spyModified.count(), ++spyCount);
Entry* entry1 = new Entry();
auto* entry1 = new Entry();
entry1->setGroup(group1);
QCOMPARE(spyModified.count(), ++spyCount);
Database* db2 = new Database();
Group* root2 = db2->rootGroup();
QSignalSpy spyModified2(db2, SIGNAL(modifiedImmediate()));
QScopedPointer<Database> db2(new Database());
auto* root2 = db2->rootGroup();
QSignalSpy spyModified2(db2.data(), SIGNAL(modifiedImmediate()));
group1->setParent(root2);
QCOMPARE(spyModified.count(), ++spyCount);
@ -74,7 +74,7 @@ void TestModified::testSignals()
QCOMPARE(spyModified.count(), spyCount);
QCOMPARE(spyModified2.count(), ++spyCount2);
Entry* entry2 = new Entry();
auto* entry2 = new Entry();
entry2->setGroup(group2);
QCOMPARE(spyModified.count(), ++spyCount);
QCOMPARE(spyModified2.count(), spyCount2);
@ -87,11 +87,11 @@ void TestModified::testSignals()
QCOMPARE(spyModified.count(), spyCount);
QCOMPARE(spyModified2.count(), ++spyCount2);
Group* group3 = new Group();
auto* group3 = new Group();
group3->setParent(root);
QCOMPARE(spyModified.count(), ++spyCount);
Group* group4 = new Group();
auto* group4 = new Group();
group4->setParent(group3);
QCOMPARE(spyModified.count(), ++spyCount);
@ -103,21 +103,18 @@ void TestModified::testSignals()
QCOMPARE(spyModified.count(), spyCount);
QCOMPARE(spyModified2.count(), spyCount2);
delete db;
delete db2;
}
void TestModified::testGroupSets()
{
int spyCount = 0;
Database* db = new Database();
Group* root = db->rootGroup();
QScopedPointer<Database> db(new Database());
auto* root = db->rootGroup();
Group* group = new Group();
auto* group = new Group();
group->setParent(root);
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
root->setUuid(Uuid::random());
QCOMPARE(spyModified.count(), ++spyCount);
@ -169,22 +166,20 @@ void TestModified::testGroupSets()
QCOMPARE(spyModified.count(), ++spyCount);
group->setIcon(group->iconUuid());
QCOMPARE(spyModified.count(), spyCount);
delete db;
}
void TestModified::testEntrySets()
{
int spyCount = 0;
Database* db = new Database();
Group* root = db->rootGroup();
QScopedPointer<Database> db(new Database());
auto* root = db->rootGroup();
Group* group = new Group();
auto* group = new Group();
group->setParent(root);
Entry* entry = new Entry();
auto* entry = new Entry();
entry->setGroup(group);
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
entry->setUuid(Uuid::random());
QCOMPARE(spyModified.count(), ++spyCount);
@ -284,8 +279,6 @@ void TestModified::testEntrySets()
QCOMPARE(spyModified.count(), ++spyCount);
entry->attributes()->set("test key2", entry->attributes()->value("test key2"), true);
QCOMPARE(spyModified.count(), spyCount);
delete db;
}
void TestModified::testHistoryItems()
@ -313,7 +306,7 @@ void TestModified::testHistoryItems()
entry->setTitle("b");
entry->endUpdate();
QCOMPARE(entry->historyItems().size(), ++historyItemsSize);
Entry *historyEntry = entry->historyItems().at(historyItemsSize - 1);
auto *historyEntry = entry->historyItems().at(historyItemsSize - 1);
QCOMPARE(historyEntry->title(), QString("a"));
QCOMPARE(historyEntry->uuid(), entry->uuid());
QCOMPARE(historyEntry->tags(), entry->tags());
@ -342,12 +335,11 @@ void TestModified::testHistoryItems()
QVERIFY(!entry->historyItems().at(historyItemsSize - 1)->attributes()->keys().contains("k"));
QScopedPointer<Database> db(new Database());
Group* root = db->rootGroup();
auto* root = db->rootGroup();
db->metadata()->setHistoryMaxItems(3);
db->metadata()->setHistoryMaxSize(-1);
Entry* historyEntry2;
Entry* entry2 = new Entry();
auto* entry2 = new Entry();
entry2->setGroup(root);
entry2->beginUpdate();
entry2->setTitle("1");
@ -373,7 +365,7 @@ void TestModified::testHistoryItems()
entry2->endUpdate();
QCOMPARE(entry2->historyItems().size(), 1);
historyEntry2 = entry2->historyItems().at(0);
auto* historyEntry2 = entry2->historyItems().at(0);
QCOMPARE(historyEntry2->title(), QString("4"));
db->metadata()->setHistoryMaxItems(-1);
@ -451,7 +443,6 @@ void TestModified::testHistoryMaxSize()
QScopedPointer<Database> db(new Database());
const QString key("test");
auto entry1 = new Entry();
entry1->setGroup(db->rootGroup());
QCOMPARE(entry1->historyItems().size(), 0);
@ -584,15 +575,15 @@ void TestModified::testHistoryMaxSize()
void TestModified::testCustomData()
{
int spyCount = 0;
Database* db = new Database();
Group* root = db->rootGroup();
QScopedPointer<Database> db(new Database());
auto* root = db->rootGroup();
Group* group = new Group();
auto* group = new Group();
group->setParent(root);
Entry* entry = new Entry();
auto* entry = new Entry();
entry->setGroup(group);
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
db->metadata()->customData()->set("Key", "Value");
QCOMPARE(spyModified.count(), ++spyCount);
@ -608,6 +599,4 @@ void TestModified::testCustomData()
QCOMPARE(spyModified.count(), ++spyCount);
group->customData()->set("Key", "Value");
QCOMPARE(spyModified.count(), spyCount);
delete db;
}

View File

@ -39,16 +39,6 @@
<Binary ID="0" Compressed="True">H4sIAAAAAAAEAO29B2AcSZYlJi9tynt/SvVK1+B0oQiAYBMk2JBAEOzBiM3mkuwdaUcjKasqgcplVmVdZhZAzO2dvPfee++999577733ujudTif33/8/XGZkAWz2zkrayZ4hgKrIHz9+fB8/InZ29+7t3//0wcHD/wfGx4SmCgAAAA==</Binary>
<Binary ID="1" Compressed="True">H4sIAAAAAAAEAO29B2AcSZYlJi9tynt/SvVK1+B0oQiAYBMk2JBAEOzBiM3mkuwdaUcjKasqgcplVmVdZhZAzO2dvPfee++999577733ujudTif33/8/XGZkAWz2zkrayZ4hgKrIHz9+fB8/IrLJdJafX8yLn377/wCfD1fOCwAAAA==</Binary>
</Binaries>
<CustomData>
<Item>
<Key>A Sample Test Key</Key>
<Value>valu</Value>
</Item>
<Item>
<Key>custom key</Key>
<Value>blub</Value>
</Item>
</CustomData>
</Meta>
<Root>
<Group>
@ -479,4 +469,4 @@
</DeletedObject>
</DeletedObjects>
</Root>
</KeePassFile>
</KeePassFile>