diff options
author | Janek Bevendorff <janek@jbev.net> | 2017-12-16 20:36:42 +0300 |
---|---|---|
committer | Jonathan White <support@dmapps.us> | 2018-01-13 22:23:28 +0300 |
commit | 15648991fc0636320804804000066a27973fb7e5 (patch) | |
tree | 714fa266f9035e081d657644548e6002fa582163 | |
parent | d00ccd2eb5016dec4be962385e61d5bccd606ce1 (diff) |
Refactor Kdf class, remove fields concept
-rw-r--r-- | src/core/Database.cpp | 27 | ||||
-rw-r--r-- | src/core/Database.h | 12 | ||||
-rw-r--r-- | src/crypto/kdf/AesKdf.cpp | 49 | ||||
-rw-r--r-- | src/crypto/kdf/AesKdf.h | 24 | ||||
-rw-r--r-- | src/crypto/kdf/Kdf.cpp | 34 | ||||
-rw-r--r-- | src/crypto/kdf/Kdf.h | 37 | ||||
-rw-r--r-- | src/format/Kdbx3Reader.cpp | 37 | ||||
-rw-r--r-- | src/format/Kdbx3Writer.cpp | 2 | ||||
-rw-r--r-- | src/format/KeePass1Reader.cpp | 2 | ||||
-rw-r--r-- | src/format/KeePass2.cpp | 48 | ||||
-rw-r--r-- | src/format/KeePass2.h | 19 | ||||
-rw-r--r-- | src/gui/DatabaseSettingsWidget.cpp | 129 | ||||
-rw-r--r-- | src/gui/DatabaseSettingsWidget.h | 8 | ||||
-rw-r--r-- | src/gui/DatabaseSettingsWidget.ui | 307 | ||||
-rw-r--r-- | src/keys/CompositeKey.cpp | 8 | ||||
-rw-r--r-- | tests/TestKeePass1Reader.cpp | 3 | ||||
-rw-r--r-- | tests/gui/TestGui.cpp | 2 |
17 files changed, 278 insertions, 470 deletions
diff --git a/src/core/Database.cpp b/src/core/Database.cpp index ae22603f3..3aeaa3d26 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -33,7 +33,6 @@ #include "format/KeePass2Writer.h" #include "keys/PasswordKey.h" #include "keys/FileKey.h" -#include "keys/CompositeKey.h" QHash<Uuid, Database*> Database::m_uuidMap; @@ -45,7 +44,7 @@ Database::Database() { m_data.cipher = KeePass2::CIPHER_AES; m_data.compressionAlgo = CompressionGZip; - m_data.kdf = new AesKdf(); + m_data.kdf = QSharedPointer<AesKdf>::create(); m_data.kdf->randomizeTransformSalt(); m_data.hasKey = false; @@ -485,23 +484,18 @@ QString Database::saveToFile(QString filePath) } } -Kdf* Database::kdf() const { +QSharedPointer<Kdf> Database::kdf() const +{ return m_data.kdf; } -void Database::setKdf(Kdf* kdf) { - if (m_data.kdf == kdf) { - return; - } - - if (m_data.kdf != nullptr) { - delete m_data.kdf; - } - - m_data.kdf = kdf; +void Database::setKdf(QSharedPointer<Kdf> kdf) +{ + m_data.kdf = std::move(kdf); } -bool Database::changeKdf(Kdf* kdf) { +bool Database::changeKdf(QSharedPointer<Kdf> kdf) +{ kdf->randomizeTransformSalt(); QByteArray transformedMasterKey; if (!m_data.key.transform(*kdf, transformedMasterKey)) { @@ -514,8 +508,3 @@ bool Database::changeKdf(Kdf* kdf) { return true; } - -Database::DatabaseData::~DatabaseData() -{ - delete kdf; -} diff --git a/src/core/Database.h b/src/core/Database.h index 44854b854..c199069d3 100644 --- a/src/core/Database.h +++ b/src/core/Database.h @@ -58,17 +58,15 @@ public: Uuid cipher; CompressionAlgorithm compressionAlgo; QByteArray transformedMasterKey; - Kdf* kdf; + QSharedPointer<Kdf> kdf; CompositeKey key; bool hasKey; QByteArray masterSeed; QByteArray challengeResponseKey; - - ~DatabaseData(); }; Database(); - ~Database(); + ~Database() override; Group* rootGroup(); const Group* rootGroup() const; @@ -92,7 +90,7 @@ public: Uuid cipher() const; Database::CompressionAlgorithm compressionAlgo() const; - Kdf* kdf() const; + QSharedPointer<Kdf> kdf() const; QByteArray transformedMasterKey() const; const CompositeKey& key() const; QByteArray challengeResponseKey() const; @@ -100,7 +98,7 @@ public: void setCipher(const Uuid& cipher); void setCompressionAlgo(Database::CompressionAlgorithm algo); - void setKdf(Kdf* kdf); + void setKdf(QSharedPointer<Kdf> kdf); bool setKey(const CompositeKey& key, bool updateChangedTime = true, bool updateTransformSalt = false); bool hasKey() const; @@ -117,7 +115,7 @@ public: * Returns a unique id that is only valid as long as the Database exists. */ Uuid uuid(); - bool changeKdf(Kdf* kdf); + bool changeKdf(QSharedPointer<Kdf> kdf); static Database* databaseByUuid(const Uuid& uuid); static Database* openDatabaseFile(QString fileName, CompositeKey key); diff --git a/src/crypto/kdf/AesKdf.cpp b/src/crypto/kdf/AesKdf.cpp index a4dd75650..8d3801a5f 100644 --- a/src/crypto/kdf/AesKdf.cpp +++ b/src/crypto/kdf/AesKdf.cpp @@ -18,20 +18,10 @@ #include <QtConcurrent> #include "format/KeePass2.h" -#include "crypto/SymmetricCipher.h" #include "crypto/CryptoHash.h" #include "crypto/Random.h" #include "AesKdf.h" -const QList<Kdf::Field> AesKdf::FIELDS = AesKdf::initFields(); - -QList<Kdf::Field> AesKdf::initFields() -{ - return QList<Kdf::Field> { - Kdf::Field(static_cast<quint32>(Fields::ROUNDS), "Transform rounds", 1, UINT64_MAX, true), - }; -} - bool AesKdf::transform(const QByteArray& raw, QByteArray& result) const { QByteArray resultLeft; @@ -76,7 +66,8 @@ bool AesKdf::transformKeyRaw(const QByteArray& key, const QByteArray& seed, quin } AesKdf::AesKdf() - : m_rounds(100000ull) + : Kdf::Kdf(KeePass2::KDF_AES) + , m_rounds(100000ull) , m_seed(QByteArray(32, 0)) { } @@ -112,39 +103,9 @@ void AesKdf::randomizeTransformSalt() setSeed(randomGen()->randomArray(32)); } -Kdf::Type AesKdf::type() const -{ - return Kdf::Type::AES; -} - -Kdf* AesKdf::clone() const -{ - return static_cast<Kdf*>(new AesKdf(*this)); -} - -const QList<Kdf::Field> AesKdf::fields() const +QSharedPointer<Kdf> AesKdf::clone() const { - return FIELDS; -} - -quint64 AesKdf::field(quint32 id) const -{ - switch (static_cast<Fields>(id)) { - case Fields::ROUNDS: - return m_rounds; - default: - return 0; - } -} - -bool AesKdf::setField(quint32 id, quint64 val) -{ - switch (static_cast<Fields>(id)) { - case Fields::ROUNDS: - return setRounds(val); - default: - return false; - } + return QSharedPointer<AesKdf>::create(*this); } int AesKdf::benchmarkImpl(int msec) const @@ -170,4 +131,4 @@ int AesKdf::benchmarkImpl(int msec) const while (!t.hasExpired(msec)); return rounds; -}
\ No newline at end of file +} diff --git a/src/crypto/kdf/AesKdf.h b/src/crypto/kdf/AesKdf.h index 660c022ed..28206a8d6 100644 --- a/src/crypto/kdf/AesKdf.h +++ b/src/crypto/kdf/AesKdf.h @@ -27,26 +27,13 @@ public: bool transform(const QByteArray& raw, QByteArray& result) const override; void randomizeTransformSalt() override; - Kdf::Type type() const override; - Kdf* clone() const override; + QSharedPointer<Kdf> clone() const override; - const QList<Field> fields() const override; - quint64 field(quint32 id) const override; - bool setField(quint32 id, quint64 val) override; + quint64 rounds() const override; + QByteArray seed() const override; - quint64 rounds() const; - QByteArray seed() const; - - bool setRounds(quint64 rounds); - bool setSeed(const QByteArray& seed); - - enum class Fields: quint32 - { - ROUNDS, - SEED - }; - - static const QList<Kdf::Field> FIELDS; + bool setRounds(quint64 rounds) override; + bool setSeed(const QByteArray& seed) override; protected: int benchmarkImpl(int msec) const override; @@ -59,7 +46,6 @@ private: const QByteArray& seed, quint64 rounds, QByteArray* result) Q_REQUIRED_RESULT; - static QList<Kdf::Field> initFields(); }; #endif // KEEPASSX_AESKDF_H diff --git a/src/crypto/kdf/Kdf.cpp b/src/crypto/kdf/Kdf.cpp index c1bfdf7e1..6829b650a 100644 --- a/src/crypto/kdf/Kdf.cpp +++ b/src/crypto/kdf/Kdf.cpp @@ -18,42 +18,16 @@ #include "Kdf.h" #include "Kdf_p.h" -#include <QThread> -#include <QElapsedTimer> #include <QtConcurrent> -Kdf::Field::Field(quint32 id, const QString& name, quint64 min, quint64 max, bool benchmark) - : m_id(id) - , m_name(name) - , m_min(min) - , m_max(max) - , m_benchmark(benchmark) +Kdf::Kdf(Uuid uuid) + : m_uuid(uuid) { } -quint32 Kdf::Field::id() const +Uuid Kdf::uuid() const { - return m_id; -} - -QString Kdf::Field::name() const -{ - return m_name; -} - -quint64 Kdf::Field::min() const -{ - return m_min; -} - -quint64 Kdf::Field::max() const -{ - return m_max; -} - -bool Kdf::Field::benchmarked() const -{ - return m_benchmark; + return m_uuid; } int Kdf::benchmark(int msec) const diff --git a/src/crypto/kdf/Kdf.h b/src/crypto/kdf/Kdf.h index 14ce24501..ba171dcba 100644 --- a/src/crypto/kdf/Kdf.h +++ b/src/crypto/kdf/Kdf.h @@ -25,41 +25,19 @@ class Kdf { public: - enum class Type - { - AES - }; + explicit Kdf(Uuid uuid); + virtual ~Kdf() = default; - class Field - { - public: - Field(quint32 id, const QString& name, quint64 min, quint64 max, bool benchmark = false); - - quint32 id() const; - QString name() const; - quint64 min() const; - quint64 max() const; - bool benchmarked() const; - - private: - quint32 m_id; - QString m_name; - quint64 m_min; - quint64 m_max; - bool m_benchmark; - }; - - virtual ~Kdf() {} + Uuid uuid() const; + virtual quint64 rounds() const = 0; + virtual bool setRounds(quint64 rounds) = 0; virtual QByteArray seed() const = 0; - virtual Type type() const = 0; + virtual bool setSeed(const QByteArray& seed) = 0; virtual bool transform(const QByteArray& raw, QByteArray& result) const = 0; virtual void randomizeTransformSalt() = 0; - virtual Kdf* clone() const = 0; + virtual QSharedPointer<Kdf> clone() const = 0; - virtual const QList<Field> fields() const = 0; - virtual quint64 field(quint32 id) const = 0; - virtual bool setField(quint32 id, quint64 val) = 0; virtual int benchmark(int msec) const; protected: @@ -67,6 +45,7 @@ protected: private: class BenchmarkThread; + const Uuid m_uuid; }; diff --git a/src/format/Kdbx3Reader.cpp b/src/format/Kdbx3Reader.cpp index 0ea6f817e..3c3529f95 100644 --- a/src/format/Kdbx3Reader.cpp +++ b/src/format/Kdbx3Reader.cpp @@ -318,36 +318,33 @@ void Kdbx3Reader::setTransformSeed(const QByteArray& data) { if (data.size() != 32) { raiseError("Invalid transform seed size"); - } else { - AesKdf* aesKdf; - if (m_db->kdf()->type() == Kdf::Type::AES) { - aesKdf = static_cast<AesKdf*>(m_db->kdf()); - } else { - aesKdf = new AesKdf(); - m_db->setKdf(aesKdf); - } + return; + } - aesKdf->setSeed(data); + auto kdf = m_db->kdf(); + if (!kdf) { + kdf = QSharedPointer<AesKdf>::create(); + m_db->setKdf(kdf); } + + kdf->setSeed(data); } void Kdbx3Reader::setTransformRounds(const QByteArray& data) { if (data.size() != 8) { raiseError("Invalid transform rounds size"); - } else { - quint64 rounds = Endian::bytesToSizedInt<quint64>(data, KeePass2::BYTEORDER); - - AesKdf* aesKdf; - if (m_db->kdf()->type() == Kdf::Type::AES) { - aesKdf = static_cast<AesKdf*>(m_db->kdf()); - } else { - aesKdf = new AesKdf(); - m_db->setKdf(aesKdf); - } + return; + } - aesKdf->setRounds(rounds); + auto rounds = Endian::bytesToSizedInt<quint64>(data, KeePass2::BYTEORDER); + auto kdf = m_db->kdf(); + if (!kdf) { + kdf = QSharedPointer<AesKdf>::create(); + m_db->setKdf(kdf); } + + kdf->setRounds(rounds); } void Kdbx3Reader::setEncryptionIV(const QByteArray& data) diff --git a/src/format/Kdbx3Writer.cpp b/src/format/Kdbx3Writer.cpp index 990aa71ee..770e7270e 100644 --- a/src/format/Kdbx3Writer.cpp +++ b/src/format/Kdbx3Writer.cpp @@ -80,7 +80,7 @@ bool Kdbx3Writer::writeDatabase(QIODevice* device, Database* db) CHECK_RETURN_FALSE(writeHeaderField(KeePass2::CompressionFlags, Endian::sizedIntToBytes<qint32>(db->compressionAlgo(), KeePass2::BYTEORDER))); - AesKdf* kdf = static_cast<AesKdf*>(db->kdf()); + auto kdf = db->kdf(); CHECK_RETURN_FALSE(writeHeaderField(KeePass2::MasterSeed, masterSeed)); CHECK_RETURN_FALSE(writeHeaderField(KeePass2::TransformSeed, kdf->seed())); CHECK_RETURN_FALSE(writeHeaderField(KeePass2::TransformRounds, diff --git a/src/format/KeePass1Reader.cpp b/src/format/KeePass1Reader.cpp index ccb3d1ad1..c6e2ed03c 100644 --- a/src/format/KeePass1Reader.cpp +++ b/src/format/KeePass1Reader.cpp @@ -160,7 +160,7 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor raiseError("Invalid number of transform rounds"); return nullptr; } - AesKdf* kdf = new AesKdf(); + auto kdf = QSharedPointer<AesKdf>::create(); kdf->setRounds(m_transformRounds); kdf->setSeed(m_transformSeed); db->setKdf(kdf); diff --git a/src/format/KeePass2.cpp b/src/format/KeePass2.cpp index e79dff513..61bd383df 100644 --- a/src/format/KeePass2.cpp +++ b/src/format/KeePass2.cpp @@ -16,9 +16,9 @@ */ #include "KeePass2.h" -#include "crypto/CryptoHash.h" #include "crypto/kdf/AesKdf.h" -#include "core/Uuid.h" +#include <QSharedPointer> + const Uuid KeePass2::CIPHER_AES = Uuid(QByteArray::fromHex("31c1f2e6bf714350be5805216afc5aff")); const Uuid KeePass2::CIPHER_TWOFISH = Uuid(QByteArray::fromHex("ad68f29f576f4bb9a36ad47af965346c")); @@ -28,33 +28,25 @@ const Uuid KeePass2::KDF_AES = Uuid(QByteArray::fromHex("C9D9F39A628A4460BF740D0 const QByteArray KeePass2::INNER_STREAM_SALSA20_IV("\xE8\x30\x09\x4B\x97\x20\x5D\x2A"); -const QList<KeePass2::UuidNamePair> KeePass2::CIPHERS { - KeePass2::UuidNamePair(KeePass2::CIPHER_AES, "AES: 256-bit"), - KeePass2::UuidNamePair(KeePass2::CIPHER_TWOFISH, "Twofish: 256-bit"), - KeePass2::UuidNamePair(KeePass2::CIPHER_CHACHA20, "ChaCha20: 256-bit") +const QList<QPair<Uuid, QString>> KeePass2::CIPHERS { + qMakePair(KeePass2::CIPHER_AES, QObject::tr("AES: 256-bit")), + qMakePair(KeePass2::CIPHER_TWOFISH, QObject::tr("Twofish: 256-bit")), + qMakePair(KeePass2::CIPHER_CHACHA20, QObject::tr("ChaCha20: 256-bit")) }; -const QList<KeePass2::UuidNamePair> KeePass2::KDFS { - KeePass2::UuidNamePair(KeePass2::KDF_AES, "AES-KDF"), +const QList<QPair<Uuid, QString>> KeePass2::KDFS { + qMakePair(KeePass2::KDF_AES, QObject::tr("AES-KDF")), }; -Kdf* KeePass2::uuidToKdf(const Uuid& uuid) { +QSharedPointer<Kdf> KeePass2::uuidToKdf(const Uuid& uuid) +{ if (uuid == KDF_AES) { - return static_cast<Kdf*>(new AesKdf()); + return QSharedPointer<AesKdf>::create(); } + Q_ASSERT_X(false, "uuidToKdf", "Invalid UUID"); return nullptr; } -Uuid KeePass2::kdfToUuid(const Kdf& kdf) -{ - switch (kdf.type()) { - case Kdf::Type::AES: - return KDF_AES; - default: - return Uuid(); - } -} - KeePass2::ProtectedStreamAlgo KeePass2::idToProtectedStreamAlgo(quint32 id) { switch (id) { @@ -68,19 +60,3 @@ KeePass2::ProtectedStreamAlgo KeePass2::idToProtectedStreamAlgo(quint32 id) return KeePass2::InvalidProtectedStreamAlgo; } } - -KeePass2::UuidNamePair::UuidNamePair(const Uuid& uuid, const QString& name) - : m_uuid(uuid) - , m_name(name) -{ -} - -Uuid KeePass2::UuidNamePair::uuid() const -{ - return m_uuid; -} - -QString KeePass2::UuidNamePair::name() const -{ - return m_name; -} diff --git a/src/format/KeePass2.h b/src/format/KeePass2.h index 6356e15da..c7945e907 100644 --- a/src/format/KeePass2.h +++ b/src/format/KeePass2.h @@ -43,20 +43,8 @@ namespace KeePass2 extern const QByteArray INNER_STREAM_SALSA20_IV; - class UuidNamePair - { - public: - UuidNamePair(const Uuid& uuid, const QString& name); - Uuid uuid() const; - QString name() const; - - private: - Uuid m_uuid; - QString m_name; - }; - - extern const QList<UuidNamePair> CIPHERS; - extern const QList<UuidNamePair> KDFS; + extern const QList<QPair<Uuid, QString>> CIPHERS; + extern const QList<QPair<Uuid, QString>> KDFS; enum HeaderFieldID { @@ -81,8 +69,7 @@ namespace KeePass2 InvalidProtectedStreamAlgo = -1 }; - Kdf* uuidToKdf(const Uuid& uuid); - Uuid kdfToUuid(const Kdf& kdf); + QSharedPointer<Kdf> uuidToKdf(const Uuid& uuid); ProtectedStreamAlgo idToProtectedStreamAlgo(quint32 id); } diff --git a/src/gui/DatabaseSettingsWidget.cpp b/src/gui/DatabaseSettingsWidget.cpp index ce1fb8dbb..51c0f4183 100644 --- a/src/gui/DatabaseSettingsWidget.cpp +++ b/src/gui/DatabaseSettingsWidget.cpp @@ -20,6 +20,7 @@ #include <QMessageBox> +#include "core/Global.h" #include "core/AsyncTask.h" #include "core/Database.h" #include "core/Group.h" @@ -40,7 +41,6 @@ DatabaseSettingsWidget::DatabaseSettingsWidget(QWidget* parent) m_ui->historyMaxItemsSpinBox, SLOT(setEnabled(bool))); connect(m_ui->historyMaxSizeCheckBox, SIGNAL(toggled(bool)), m_ui->historyMaxSizeSpinBox, SLOT(setEnabled(bool))); - connect(m_ui->kdfComboBox, SIGNAL(currentIndexChanged(int)), SLOT(changeKdf(int))); connect(m_ui->transformBenchmarkButton, SIGNAL(clicked()), SLOT(transformRoundsBenchmark())); } @@ -61,8 +61,7 @@ void DatabaseSettingsWidget::load(Database* db) if (meta->historyMaxItems() > -1) { m_ui->historyMaxItemsSpinBox->setValue(meta->historyMaxItems()); m_ui->historyMaxItemsCheckBox->setChecked(true); - } - else { + } else { m_ui->historyMaxItemsSpinBox->setValue(Metadata::DefaultHistoryMaxItems); m_ui->historyMaxItemsCheckBox->setChecked(false); } @@ -70,17 +69,14 @@ void DatabaseSettingsWidget::load(Database* db) if (historyMaxSizeMiB > 0) { m_ui->historyMaxSizeSpinBox->setValue(historyMaxSizeMiB); m_ui->historyMaxSizeCheckBox->setChecked(true); - } - else { + } else { m_ui->historyMaxSizeSpinBox->setValue(Metadata::DefaultHistoryMaxSize); m_ui->historyMaxSizeCheckBox->setChecked(false); } m_ui->algorithmComboBox->clear(); - for (QList<KeePass2::UuidNamePair>::const_iterator ciphers = KeePass2::CIPHERS.constBegin(); - ciphers != KeePass2::CIPHERS.constEnd(); ++ciphers) { - KeePass2::UuidNamePair cipher = *ciphers; - m_ui->algorithmComboBox->addItem(cipher.name(), cipher.uuid().toByteArray()); + for (auto& cipher: asConst(KeePass2::CIPHERS)) { + m_ui->algorithmComboBox->addItem(cipher.second, cipher.first.toByteArray()); } int cipherIndex = m_ui->algorithmComboBox->findData(m_db->cipher().toByteArray()); if (cipherIndex > -1) { @@ -89,20 +85,17 @@ void DatabaseSettingsWidget::load(Database* db) bool blockSignals = m_ui->kdfComboBox->signalsBlocked(); m_ui->kdfComboBox->blockSignals(true); - m_kdf.reset(m_db->kdf()->clone()); + m_ui->kdfComboBox->clear(); - for (QList<KeePass2::UuidNamePair>::const_iterator kdfs = KeePass2::KDFS.constBegin(); - kdfs != KeePass2::KDFS.constEnd(); ++kdfs) { - KeePass2::UuidNamePair kdf = *kdfs; - m_ui->kdfComboBox->addItem(kdf.name(), kdf.uuid().toByteArray()); + for (auto& kdf: asConst(KeePass2::KDFS)) { + m_ui->kdfComboBox->addItem(kdf.second, kdf.first.toByteArray()); } - int kdfIndex = m_ui->kdfComboBox->findData(KeePass2::kdfToUuid(*m_kdf).toByteArray()); + int kdfIndex = m_ui->kdfComboBox->findData(m_db->kdf()->uuid().toByteArray()); if (kdfIndex > -1) { m_ui->kdfComboBox->setCurrentIndex(kdfIndex); } - displayKdf(*m_kdf); m_ui->kdfComboBox->blockSignals(blockSignals); - m_ui->transformRoundsSpinBox->setValue(static_cast<unsigned>(m_kdf->rounds())); + m_ui->transformRoundsSpinBox->setValue(static_cast<unsigned>(m_db->kdf()->rounds())); m_ui->dbNameEdit->setFocus(); } @@ -121,8 +114,7 @@ void DatabaseSettingsWidget::save() int historyMaxItems; if (m_ui->historyMaxItemsCheckBox->isChecked()) { historyMaxItems = m_ui->historyMaxItemsSpinBox->value(); - } - else { + } else { historyMaxItems = -1; } if (historyMaxItems != meta->historyMaxItems()) { @@ -133,8 +125,7 @@ void DatabaseSettingsWidget::save() int historyMaxSize; if (m_ui->historyMaxSizeCheckBox->isChecked()) { historyMaxSize = m_ui->historyMaxSizeSpinBox->value() * 1048576; - } - else { + } else { historyMaxSize = -1; } if (historyMaxSize != meta->historyMaxSize()) { @@ -148,31 +139,20 @@ void DatabaseSettingsWidget::save() m_db->setCipher(Uuid(m_ui->algorithmComboBox->currentData().toByteArray())); - bool kdfValid = true; - for (int i = 0; i < m_kdfFields.size(); ++i) { - QPair<quint32, QSpinBox*> field = m_kdfFields.at(i); - kdfValid &= m_kdf->setField(field.first, static_cast<quint64>(qMax(0, field.second->value()))); - if (!kdfValid) { - break; - } - } + auto kdf = KeePass2::uuidToKdf(Uuid(m_ui->kdfComboBox->currentData().toByteArray())); + kdf->setRounds(static_cast<quint64>(qMax(0, m_ui->transformRoundsSpinBox->value()))); - if (kdfValid) { - Kdf* kdf = m_kdf.take(); - bool ok = m_db->changeKdf(kdf); - if (!ok) { - MessageBox::warning(this, tr("KDF unchanged"), - tr("Failed to transform key with new KDF parameters; KDF unchanged."), - QMessageBox::Ok); - delete kdf; // m_db has not taken ownership - } - } else { + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // TODO: we should probably use AsyncTask::runAndWaitForFuture() here, + // but not without making Database thread-safe + bool ok = m_db->changeKdf(kdf); + QApplication::restoreOverrideCursor(); + + if (!ok) { MessageBox::warning(this, tr("KDF unchanged"), - tr("Invalid KDF parameters; KDF unchanged."), + tr("Failed to transform key with new KDF parameters; KDF unchanged."), QMessageBox::Ok); } - clearKdfWidgets(); - emit editFinished(true); } @@ -185,7 +165,7 @@ void DatabaseSettingsWidget::transformRoundsBenchmark() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); m_ui->transformRoundsSpinBox->setValue(AsyncTask::runAndWaitForFuture([this]() { - int rounds = m_kdf->benchmark(1000); + int rounds = m_db->kdf()->benchmark(1000); QApplication::restoreOverrideCursor(); return rounds; })); @@ -198,66 +178,3 @@ void DatabaseSettingsWidget::truncateHistories() entry->truncateHistory(); } } - -void DatabaseSettingsWidget::changeKdf(int index) { - QByteArray uuidBytes = m_ui->kdfComboBox->itemData(index).toByteArray(); - if (uuidBytes.size() != Uuid::Length) { - return; - } - Kdf* newKdf = KeePass2::uuidToKdf(Uuid(uuidBytes)); - if (newKdf != nullptr) { - m_kdf.reset(newKdf); - displayKdf(*m_kdf); - } -} - -void DatabaseSettingsWidget::displayKdf(const Kdf& kdf) -{ - clearKdfWidgets(); - - QWidget* lastWidget = nullptr; - int columnStart = m_ui->gridLayout->columnCount(); - int rowStart = m_ui->gridLayout->rowCount(); - QList<Kdf::Field> fields = kdf.fields(); - for (int i = 0; i < fields.size(); i++) { - const Kdf::Field& field = fields.at(i); - QLabel* label = new QLabel(QString("%1:").arg(field.name())); - QSpinBox* spinBox = new QSpinBox(); - m_kdfWidgets.append(label); - m_kdfWidgets.append(spinBox); - m_kdfFields.append(QPair<quint32, QSpinBox*>(field.id(), spinBox)); - spinBox->setMinimum(static_cast<qint32>(qMin(qMax(0ull, field.min()), 0x7FFFFFFFull))); - spinBox->setMaximum(static_cast<qint32>(qMin(qMax(0ull, field.max()), 0x7FFFFFFFull))); - spinBox->setValue(static_cast<qint32>(qMin(qMax(0ull, kdf.field(field.id())), 0x7FFFFFFFull))); - spinBox->setObjectName(QString("kdfParams%1").arg(i)); - m_ui->gridLayout->addWidget(label, rowStart + i, columnStart - 3, Qt::AlignRight); - if (field.benchmarked()) { - Q_ASSERT(m_benchmarkField == nullptr); - QPushButton* benchBtn = new QPushButton("Benchmark"); - connect(benchBtn, &QPushButton::clicked, this, &DatabaseSettingsWidget::transformRoundsBenchmark); - m_kdfWidgets.append(benchBtn); - m_ui->gridLayout->addWidget(spinBox, rowStart + i, columnStart - 2); - m_ui->gridLayout->addWidget(benchBtn, rowStart + i, columnStart - 1); - m_benchmarkField = spinBox; - lastWidget = benchBtn; - } else { - m_ui->gridLayout->addWidget(spinBox, rowStart + i, columnStart - 2, 1, 2); - lastWidget = spinBox; - } - } - if (lastWidget != nullptr) { - QWidget::setTabOrder(lastWidget, m_ui->buttonBox->button(QDialogButtonBox::StandardButton::Cancel)); - QWidget::setTabOrder(m_ui->buttonBox->button(QDialogButtonBox::StandardButton::Cancel), m_ui->buttonBox->button(QDialogButtonBox::StandardButton::Ok)); - } -} - -void DatabaseSettingsWidget::clearKdfWidgets() -{ - m_benchmarkField = nullptr; - for (int i = 0; i < m_kdfWidgets.size(); ++i) { - m_ui->gridLayout->removeWidget(m_kdfWidgets.at(i)); - m_kdfWidgets.at(i)->deleteLater(); - } - m_kdfWidgets.clear(); - m_kdfFields.clear(); -} diff --git a/src/gui/DatabaseSettingsWidget.h b/src/gui/DatabaseSettingsWidget.h index fe8e25c6b..ec3d6415a 100644 --- a/src/gui/DatabaseSettingsWidget.h +++ b/src/gui/DatabaseSettingsWidget.h @@ -40,6 +40,7 @@ Q_OBJECT public: explicit DatabaseSettingsWidget(QWidget* parent = nullptr); ~DatabaseSettingsWidget(); + Q_DISABLE_COPY(DatabaseSettingsWidget) void load(Database* db); @@ -50,19 +51,12 @@ private slots: void save(); void reject(); void transformRoundsBenchmark(); - void changeKdf(int index); private: void truncateHistories(); - void displayKdf(const Kdf& kdf); - void clearKdfWidgets(); const QScopedPointer<Ui::DatabaseSettingsWidget> m_ui; - QList<QWidget*> m_kdfWidgets; - QScopedPointer<Kdf> m_kdf; Database* m_db; - - Q_DISABLE_COPY(DatabaseSettingsWidget) }; #endif // KEEPASSX_DATABASESETTINGSWIDGET_H diff --git a/src/gui/DatabaseSettingsWidget.ui b/src/gui/DatabaseSettingsWidget.ui index 701df42a0..8aaab19e7 100644 --- a/src/gui/DatabaseSettingsWidget.ui +++ b/src/gui/DatabaseSettingsWidget.ui @@ -2,17 +2,9 @@ <ui version="4.0"> <class>DatabaseSettingsWidget</class> <widget class="QWidget" name="DatabaseSettingsWidget"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>600</width> - <height>340</height> - </rect> - </property> <layout class="QVBoxLayout" name="verticalLayout" stretch="1,2,5,1"> <item> - <spacer name="topSpacer"> + <spacer name="verticalSpacer_2"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> @@ -25,9 +17,9 @@ </spacer> </item> <item> - <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1,0"> + <layout class="QHBoxLayout" name="horizontalLayout_5" stretch="0,1,0"> <item> - <spacer name="leftSpacer"> + <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> @@ -40,118 +32,185 @@ </spacer> </item> <item> - <layout class="QGridLayout" name="gridLayout"> - <item row="7" column="2" colspan="2"> - <widget class="QComboBox" name="kdfComboBox"/> - </item> - <item row="0" column="1" alignment="Qt::AlignRight"> - <widget class="QLabel" name="dbNameLabel"> - <property name="text"> - <string>Database name:</string> - </property> - </widget> - </item> - <item row="5" column="1"> - <widget class="QCheckBox" name="historyMaxSizeCheckBox"> - <property name="text"> - <string>Max. history size:</string> - </property> - </widget> - </item> - <item row="7" column="1" alignment="Qt::AlignRight"> - <widget class="QLabel" name="kdfLabel"> - <property name="text"> - <string>Key derivation function:</string> - </property> - </widget> - </item> - <item row="4" column="1"> - <widget class="QCheckBox" name="historyMaxItemsCheckBox"> - <property name="text"> - <string>Max. history items:</string> - </property> - </widget> - </item> - <item row="0" column="2" colspan="2"> - <widget class="QLineEdit" name="dbNameEdit"/> - </item> - <item row="4" column="2" colspan="2"> - <widget class="QSpinBox" name="historyMaxItemsSpinBox"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximum"> - <number>2000000000</number> - </property> - </widget> - </item> - <item row="2" column="1" alignment="Qt::AlignRight"> - <widget class="QLabel" name="defaultUsernameLabel"> - <property name="text"> - <string>Default username:</string> - </property> - </widget> - </item> - <item row="1" column="2" colspan="2"> - <widget class="QLineEdit" name="dbDescriptionEdit"/> - </item> - <item row="5" column="2" colspan="2"> - <widget class="QSpinBox" name="historyMaxSizeSpinBox"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="suffix"> - <string> MiB</string> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>2000000000</number> - </property> - </widget> - </item> - <item row="3" column="2" colspan="2"> - <widget class="QCheckBox" name="recycleBinEnabledCheckBox"> - <property name="text"> - <string>Use recycle bin</string> - </property> - </widget> - </item> - <item row="2" column="2" colspan="2"> - <widget class="QLineEdit" name="defaultUsernameEdit"> - <property name="enabled"> - <bool>true</bool> - </property> - </widget> - </item> - <item row="1" column="1" alignment="Qt::AlignRight"> - <widget class="QLabel" name="dbDescriptionLabel"> - <property name="text"> - <string>Database description:</string> - </property> - </widget> - </item> - <item row="6" column="2" colspan="2"> - <widget class="QComboBox" name="algorithmComboBox"/> - </item> - <item row="6" column="1" alignment="Qt::AlignRight"> - <widget class="QLabel" name="algorithmLabel"> - <property name="text"> - <string>Algorithm:</string> - </property> - </widget> - </item> - </layout> + <widget class="QWidget" name="widget" native="true"> + <property name="maximumSize"> + <size> + <width>800</width> + <height>16777215</height> + </size> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="4" column="2"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QSpinBox" name="transformRoundsSpinBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>1000000000</number> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="transformBenchmarkButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Benchmark</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="1" alignment="Qt::AlignRight"> + <widget class="QLabel" name="dbNameLabel"> + <property name="text"> + <string>Database name:</string> + </property> + </widget> + </item> + <item row="8" column="1"> + <widget class="QCheckBox" name="historyMaxSizeCheckBox"> + <property name="text"> + <string>Max. history size:</string> + </property> + </widget> + </item> + <item row="4" column="1" alignment="Qt::AlignRight"> + <widget class="QLabel" name="transformRoundsLabel"> + <property name="text"> + <string>Transform rounds:</string> + </property> + </widget> + </item> + <item row="7" column="1"> + <widget class="QCheckBox" name="historyMaxItemsCheckBox"> + <property name="text"> + <string>Max. history items:</string> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QLineEdit" name="dbNameEdit"/> + </item> + <item row="7" column="2"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QSpinBox" name="historyMaxItemsSpinBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximum"> + <number>2000000000</number> + </property> + </widget> + </item> + </layout> + </item> + <item row="5" column="1" alignment="Qt::AlignRight"> + <widget class="QLabel" name="defaultUsernameLabel"> + <property name="text"> + <string>Default username:</string> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QLineEdit" name="dbDescriptionEdit"/> + </item> + <item row="8" column="2"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QSpinBox" name="historyMaxSizeSpinBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="suffix"> + <string> MiB</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>2000000000</number> + </property> + </widget> + </item> + </layout> + </item> + <item row="6" column="2"> + <widget class="QCheckBox" name="recycleBinEnabledCheckBox"> + <property name="text"> + <string>Use recycle bin</string> + </property> + </widget> + </item> + <item row="5" column="2"> + <widget class="QLineEdit" name="defaultUsernameEdit"> + <property name="enabled"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="1" alignment="Qt::AlignRight"> + <widget class="QLabel" name="dbDescriptionLabel"> + <property name="text"> + <string>Database description:</string> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QComboBox" name="algorithmComboBox"> + <item> + <property name="text"> + <string>AES: 256 Bit (default)</string> + </property> + </item> + <item> + <property name="text"> + <string>Twofish: 256 Bit</string> + </property> + </item> + </widget> + </item> + <item row="2" column="1" alignment="Qt::AlignRight"> + <widget class="QLabel" name="algorithmLabel"> + <property name="text"> + <string>Encryption Algorithm:</string> + </property> + </widget> + </item> + <item row="3" column="2"> + <widget class="QComboBox" name="kdfComboBox"/> + </item> + <item row="3" column="1"> + <widget class="QLabel" name="kdfLabel"> + <property name="text"> + <string>Key Derivation Function</string> + </property> + </widget> + </item> + </layout> + </widget> </item> <item> - <spacer name="rightSpacer"> + <spacer name="horizontalSpacer_2"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> @@ -166,7 +225,7 @@ </layout> </item> <item> - <spacer name="bottomSpacer"> + <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> @@ -190,14 +249,14 @@ <tabstops> <tabstop>dbNameEdit</tabstop> <tabstop>dbDescriptionEdit</tabstop> + <tabstop>transformRoundsSpinBox</tabstop> + <tabstop>transformBenchmarkButton</tabstop> <tabstop>defaultUsernameEdit</tabstop> <tabstop>recycleBinEnabledCheckBox</tabstop> <tabstop>historyMaxItemsCheckBox</tabstop> <tabstop>historyMaxItemsSpinBox</tabstop> <tabstop>historyMaxSizeCheckBox</tabstop> <tabstop>historyMaxSizeSpinBox</tabstop> - <tabstop>algorithmComboBox</tabstop> - <tabstop>kdfComboBox</tabstop> <tabstop>buttonBox</tabstop> </tabstops> <resources/> diff --git a/src/keys/CompositeKey.cpp b/src/keys/CompositeKey.cpp index 88ac4cf6a..8f6917afc 100644 --- a/src/keys/CompositeKey.cpp +++ b/src/keys/CompositeKey.cpp @@ -17,19 +17,11 @@ */ #include "CompositeKey.h" -#include "ChallengeResponseKey.h" - -#include <QElapsedTimer> #include <QFile> #include <QtConcurrent> -#include "crypto/kdf/Kdf.h" -#include "format/KeePass2.h" #include "core/Global.h" #include "crypto/CryptoHash.h" -#include "crypto/SymmetricCipher.h" -#include "keys/FileKey.h" -#include "keys/PasswordKey.h" CompositeKey::CompositeKey() { diff --git a/tests/TestKeePass1Reader.cpp b/tests/TestKeePass1Reader.cpp index 7e015c2bc..c372c8715 100644 --- a/tests/TestKeePass1Reader.cpp +++ b/tests/TestKeePass1Reader.cpp @@ -26,7 +26,6 @@ #include "core/Group.h" #include "core/Metadata.h" #include "crypto/Crypto.h" -#include "crypto/kdf/AesKdf.h" #include "format/KeePass1Reader.h" #include "format/KeePass2Reader.h" #include "format/KeePass2Writer.h" @@ -111,7 +110,7 @@ void TestKeePass1Reader::testBasic() void TestKeePass1Reader::testMasterKey() { QVERIFY(m_db->hasKey()); - QCOMPARE(static_cast<AesKdf*>(m_db->kdf())->rounds(), static_cast<quint64>(713)); + QCOMPARE(m_db->kdf()->rounds(), static_cast<quint64>(713)); } void TestKeePass1Reader::testCustomIcons() diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 92b7a6a5f..9f1e7abc2 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -903,7 +903,7 @@ void TestGui::testDatabaseSettings() QTest::keyClick(transformRoundsSpinBox, Qt::Key_Enter); // wait for modified timer QTRY_COMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("Save*")); - QCOMPARE(static_cast<AesKdf*>(m_db->kdf())->rounds(), Q_UINT64_C(100)); + QCOMPARE(m_db->kdf()->rounds(), Q_UINT64_C(100)); triggerAction("actionDatabaseSave"); QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("Save")); |