diff --git a/src/gui/DatabaseSettingsWidget.cpp b/src/gui/DatabaseSettingsWidget.cpp index dd18f3e0..5f1dfeed 100644 --- a/src/gui/DatabaseSettingsWidget.cpp +++ b/src/gui/DatabaseSettingsWidget.cpp @@ -18,17 +18,28 @@ #include "DatabaseSettingsWidget.h" #include "ui_DatabaseSettingsWidget.h" +#include +#include +#include +#include +#include +#include +#include + #include "core/Database.h" #include "core/Group.h" #include "core/Metadata.h" #include "crypto/SymmetricCipher.h" -#include "crypto/kdf/AesKdf.h" #include "format/KeePass2.h" #include "keys/CompositeKey.h" +#include "format/KeePass2.h" +#include "crypto/kdf/Kdf.h" +#include "MessageBox.h" DatabaseSettingsWidget::DatabaseSettingsWidget(QWidget* parent) : DialogyWidget(parent) , m_ui(new Ui::DatabaseSettingsWidget()) + , m_benchmarkField(nullptr) , m_db(nullptr) { m_ui->setupUi(this); @@ -39,7 +50,7 @@ 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->transformBenchmarkButton, SIGNAL(clicked()), SLOT(transformRoundsBenchmark())); + connect(m_ui->kdfComboBox, SIGNAL(currentIndexChanged(int)), SLOT(changeKdf(int))); } DatabaseSettingsWidget::~DatabaseSettingsWidget() @@ -56,8 +67,6 @@ void DatabaseSettingsWidget::load(Database* db) m_ui->dbDescriptionEdit->setText(meta->description()); m_ui->recycleBinEnabledCheckBox->setChecked(meta->recycleBinEnabled()); m_ui->defaultUsernameEdit->setText(meta->defaultUserName()); - m_ui->AlgorithmComboBox->setCurrentIndex(SymmetricCipher::cipherToAlgorithm(m_db->cipher())); - m_ui->transformRoundsSpinBox->setValue(static_cast(m_db->kdf())->rounds()); if (meta->historyMaxItems() > -1) { m_ui->historyMaxItemsSpinBox->setValue(meta->historyMaxItems()); m_ui->historyMaxItemsCheckBox->setChecked(true); @@ -76,6 +85,33 @@ void DatabaseSettingsWidget::load(Database* db) m_ui->historyMaxSizeCheckBox->setChecked(false); } + m_ui->algorithmComboBox->clear(); + for (QList::const_iterator ciphers = KeePass2::CIPHERS.constBegin(); + ciphers != KeePass2::CIPHERS.constEnd(); ++ciphers) { + KeePass2::UuidNamePair cipher = *ciphers; + m_ui->algorithmComboBox->addItem(cipher.name(), cipher.uuid().toByteArray()); + } + int cipherIndex = m_ui->algorithmComboBox->findData(m_db->cipher().toByteArray()); + if (cipherIndex > -1) { + m_ui->algorithmComboBox->setCurrentIndex(cipherIndex); + } + + bool blockSignals = m_ui->kdfComboBox->signalsBlocked(); + m_ui->kdfComboBox->blockSignals(true); + m_kdf.reset(m_db->kdf()->clone()); + m_ui->kdfComboBox->clear(); + for (QList::const_iterator kdfs = KeePass2::KDFS.constBegin(); + kdfs != KeePass2::KDFS.constEnd(); ++kdfs) { + KeePass2::UuidNamePair kdf = *kdfs; + m_ui->kdfComboBox->addItem(kdf.name(), kdf.uuid().toByteArray()); + } + int kdfIndex = m_ui->kdfComboBox->findData(KeePass2::kdfToUuid(*m_kdf).toByteArray()); + if (kdfIndex > -1) { + m_ui->kdfComboBox->setCurrentIndex(kdfIndex); + } + displayKdf(*m_kdf); + m_ui->kdfComboBox->blockSignals(blockSignals); + m_ui->dbNameEdit->setFocus(); } @@ -86,15 +122,7 @@ void DatabaseSettingsWidget::save() meta->setName(m_ui->dbNameEdit->text()); meta->setDescription(m_ui->dbDescriptionEdit->text()); meta->setDefaultUserName(m_ui->defaultUsernameEdit->text()); - m_db->setCipher(SymmetricCipher::algorithmToCipher(static_cast - (m_ui->AlgorithmComboBox->currentIndex()))); meta->setRecycleBinEnabled(m_ui->recycleBinEnabledCheckBox->isChecked()); - AesKdf* kdf = static_cast(m_db->kdf()); - if (static_cast(m_ui->transformRoundsSpinBox->value()) != kdf->rounds()) { - QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); - kdf->setRounds(m_ui->transformRoundsSpinBox->value()); - QApplication::restoreOverrideCursor(); - } bool truncate = false; @@ -126,6 +154,33 @@ void DatabaseSettingsWidget::save() truncateHistories(); } + m_db->setCipher(Uuid(m_ui->algorithmComboBox->currentData().toByteArray())); + + bool kdfValid = true; + for (int i = 0; i < m_kdfFields.size(); ++i) { + QPair field = m_kdfFields.at(i); + kdfValid &= m_kdf->setField(field.first, static_cast(qMax(0, field.second->value()))); + if (!kdfValid) { + break; + } + } + + 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 { + MessageBox::warning(this, tr("KDF unchanged"), + tr("Invalid KDF parameters; KDF unchanged."), + QMessageBox::Ok); + } + clearKdfWidgets(); + emit editFinished(true); } @@ -136,11 +191,12 @@ void DatabaseSettingsWidget::reject() void DatabaseSettingsWidget::transformRoundsBenchmark() { - QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); - int rounds = m_db->kdf()->benchmark(1000); - if (rounds != -1) { - m_ui->transformRoundsSpinBox->setValue(rounds); + if (m_benchmarkField == nullptr) { + Q_ASSERT(false); + return; } + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + m_benchmarkField->setValue(m_kdf->benchmark(1000)); QApplication::restoreOverrideCursor(); } @@ -151,3 +207,66 @@ 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 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(field.id(), spinBox)); + spinBox->setMinimum(static_cast(qMin(qMax(0ull, field.min()), 0x7FFFFFFFull))); + spinBox->setMaximum(static_cast(qMin(qMax(0ull, field.max()), 0x7FFFFFFFull))); + spinBox->setValue(static_cast(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 733b32f8..a6001b89 100644 --- a/src/gui/DatabaseSettingsWidget.h +++ b/src/gui/DatabaseSettingsWidget.h @@ -19,8 +19,12 @@ #define KEEPASSX_DATABASESETTINGSWIDGET_H #include +#include +#include +#include #include "gui/DialogyWidget.h" +#include "crypto/kdf/Kdf.h" class Database; @@ -45,11 +49,18 @@ private slots: void save(); void reject(); void transformRoundsBenchmark(); + void changeKdf(int index); private: void truncateHistories(); + void displayKdf(const Kdf& kdf); + void clearKdfWidgets(); const QScopedPointer m_ui; + QList m_kdfWidgets; + QList> m_kdfFields; + QSpinBox* m_benchmarkField; + QScopedPointer m_kdf; Database* m_db; Q_DISABLE_COPY(DatabaseSettingsWidget) diff --git a/src/gui/DatabaseSettingsWidget.ui b/src/gui/DatabaseSettingsWidget.ui index c38aebc8..701df42a 100644 --- a/src/gui/DatabaseSettingsWidget.ui +++ b/src/gui/DatabaseSettingsWidget.ui @@ -12,7 +12,7 @@ - + Qt::Vertical @@ -25,9 +25,9 @@ - + - + Qt::Horizontal @@ -40,181 +40,118 @@ - - - - 800 - 16777215 - - - - - - - - - - 0 - 0 - - - - 1 - - - 1000000000 - - - - - - - - 0 - 0 - - - - - 25 - 0 - - - - Benchmark - - - - - - - - - Database name: - - - - - - - Max. history size: - - - - - - - Transform rounds: - - - - - - - Max. history items: - - - - - - - - - - - - - 0 - 0 - - - - 2000000000 - - - - - - - - - Default username: - - - - - - - - - - - - - 0 - 0 - - - - MiB - - - 1 - - - 2000000000 - - - - - - - - - Use recycle bin - - - - - - - true - - - - - - - Database description: - - - - - - - - AES: 256 Bit (default) - - - - - Twofish: 256 Bit - - - - - - - - Algorithm: - - - - - + + + + + + + + Database name: + + + + + + + Max. history size: + + + + + + + Key derivation function: + + + + + + + Max. history items: + + + + + + + + + + + 0 + 0 + + + + 2000000000 + + + + + + + Default username: + + + + + + + + + + + 0 + 0 + + + + MiB + + + 1 + + + 2000000000 + + + + + + + Use recycle bin + + + + + + + true + + + + + + + Database description: + + + + + + + + + + Algorithm: + + + + - + Qt::Horizontal @@ -229,7 +166,7 @@ - + Qt::Vertical @@ -253,14 +190,14 @@ dbNameEdit dbDescriptionEdit - transformRoundsSpinBox - transformBenchmarkButton defaultUsernameEdit recycleBinEnabledCheckBox historyMaxItemsCheckBox historyMaxItemsSpinBox historyMaxSizeCheckBox historyMaxSizeSpinBox + algorithmComboBox + kdfComboBox buttonBox diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 17b2736c..92b7a6a5 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -45,6 +45,7 @@ #include "core/Metadata.h" #include "core/Tools.h" #include "crypto/Crypto.h" +#include "crypto/kdf/AesKdf.h" #include "format/KeePass2Reader.h" #include "gui/DatabaseTabWidget.h" #include "gui/DatabaseWidget.h" @@ -897,12 +898,12 @@ void TestGui::testDatabaseSettings() m_db->metadata()->setName("Save"); triggerAction("actionChangeDatabaseSettings"); QWidget* dbSettingsWidget = m_dbWidget->findChild("databaseSettingsWidget"); - QSpinBox* transformRoundsSpinBox = dbSettingsWidget->findChild("transformRoundsSpinBox"); + QSpinBox* transformRoundsSpinBox = dbSettingsWidget->findChild("kdfParams0"); transformRoundsSpinBox->setValue(100); QTest::keyClick(transformRoundsSpinBox, Qt::Key_Enter); // wait for modified timer QTRY_COMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("Save*")); - QCOMPARE(m_db->transformRounds(), Q_UINT64_C(100)); + QCOMPARE(static_cast(m_db->kdf())->rounds(), Q_UINT64_C(100)); triggerAction("actionDatabaseSave"); QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("Save"));