Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--INSTALL.md1
-rw-r--r--cmake/FindArgon2.cmake21
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/cli/CMakeLists.txt1
-rw-r--r--src/core/Database.cpp6
-rw-r--r--src/crypto/kdf/AesKdf.cpp63
-rw-r--r--src/crypto/kdf/AesKdf.h12
-rw-r--r--src/crypto/kdf/Argon2Kdf.cpp116
-rw-r--r--src/crypto/kdf/Argon2Kdf.h50
-rw-r--r--src/crypto/kdf/Kdf.cpp39
-rw-r--r--src/crypto/kdf/Kdf.h17
-rw-r--r--src/format/KeePass2.cpp8
-rw-r--r--src/format/KeePass2.h1
-rw-r--r--src/gui/DatabaseSettingsWidget.cpp67
-rw-r--r--src/gui/DatabaseSettingsWidget.h1
-rw-r--r--src/gui/DatabaseSettingsWidget.ui210
-rw-r--r--src/main.cpp1
18 files changed, 458 insertions, 160 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 34ac87f04..e6d4270ca 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -261,8 +261,8 @@ endif()
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_NONE QT_NO_DEBUG)
find_package(LibGPGError REQUIRED)
-
find_package(Gcrypt 1.7.0 REQUIRED)
+find_package(Argon2 REQUIRED)
find_package(ZLIB REQUIRED)
diff --git a/INSTALL.md b/INSTALL.md
index 0bfa86b2c..2690e6091 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -26,6 +26,7 @@ The following libraries are required:
* libmicrohttpd
* libxi, libxtst, qtx11extras (optional for auto-type on X11)
* libsodium (>= 1.0.12, optional for keepassxc-browser support)
+* libargon2
Prepare the Building Environment
diff --git a/cmake/FindArgon2.cmake b/cmake/FindArgon2.cmake
new file mode 100644
index 000000000..8378ebd54
--- /dev/null
+++ b/cmake/FindArgon2.cmake
@@ -0,0 +1,21 @@
+# Copyright (C) 2017 KeePassXC Team
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+find_path(ARGON2_INCLUDE_DIR argon2.h)
+find_library(ARGON2_LIBRARIES argon2)
+mark_as_advanced(ARGON2_LIBRARIES ARGON2_INCLUDE_DIR)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Argon2 DEFAULT_MSG ARGON2_LIBRARIES ARGON2_INCLUDE_DIR) \ No newline at end of file
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c04d7110f..1e927ee38 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -78,6 +78,7 @@ set(keepassx_SOURCES
crypto/kdf/Kdf.cpp
crypto/kdf/Kdf_p.h
crypto/kdf/AesKdf.cpp
+ crypto/kdf/Argon2Kdf.cpp
format/CsvExporter.cpp
format/KeePass1.h
format/KeePass1Reader.cpp
@@ -252,6 +253,7 @@ target_link_libraries(keepassx_core
Qt5::Network
Qt5::Concurrent
Qt5::Widgets
+ ${ARGON2_LIBRARIES}
${GCRYPT_LIBRARIES}
${GPGERROR_LIBRARIES}
${ZLIB_LIBRARIES})
diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt
index 4c8620d55..225ce47dc 100644
--- a/src/cli/CMakeLists.txt
+++ b/src/cli/CMakeLists.txt
@@ -46,6 +46,7 @@ target_link_libraries(keepassxc-cli
keepassx_core
Qt5::Core
${GCRYPT_LIBRARIES}
+ ${ARGON2_LIBRARIES}
${GPGERROR_LIBRARIES}
${ZLIB_LIBRARIES}
${ZXCVBN_LIBRARIES})
diff --git a/src/core/Database.cpp b/src/core/Database.cpp
index 2f2f2967d..ae1664118 100644
--- a/src/core/Database.cpp
+++ b/src/core/Database.cpp
@@ -45,7 +45,7 @@ Database::Database()
m_data.cipher = KeePass2::CIPHER_AES;
m_data.compressionAlgo = CompressionGZip;
m_data.kdf = QSharedPointer<AesKdf>::create();
- m_data.kdf->randomizeTransformSalt();
+ m_data.kdf->randomizeSeed();
m_data.hasKey = false;
setRootGroup(new Group());
@@ -258,7 +258,7 @@ void Database::setCompressionAlgo(Database::CompressionAlgorithm algo)
bool Database::setKey(const CompositeKey& key, bool updateChangedTime, bool updateTransformSalt)
{
if (updateTransformSalt) {
- m_data.kdf->randomizeTransformSalt();
+ m_data.kdf->randomizeSeed();
}
QByteArray transformedMasterKey;
@@ -490,7 +490,7 @@ void Database::setKdf(QSharedPointer<Kdf> kdf)
bool Database::changeKdf(QSharedPointer<Kdf> kdf)
{
- kdf->randomizeTransformSalt();
+ kdf->randomizeSeed();
QByteArray transformedMasterKey;
if (!m_data.key.transform(*kdf, transformedMasterKey)) {
return false;
diff --git a/src/crypto/kdf/AesKdf.cpp b/src/crypto/kdf/AesKdf.cpp
index 7056698d7..3177506fc 100644
--- a/src/crypto/kdf/AesKdf.cpp
+++ b/src/crypto/kdf/AesKdf.cpp
@@ -15,12 +15,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "AesKdf.h"
+
#include <QtConcurrent>
#include "format/KeePass2.h"
#include "crypto/CryptoHash.h"
-#include "crypto/Random.h"
-#include "AesKdf.h"
+
+AesKdf::AesKdf()
+ : Kdf::Kdf(KeePass2::KDF_AES)
+{
+}
bool AesKdf::transform(const QByteArray& raw, QByteArray& result) const
{
@@ -44,7 +49,7 @@ bool AesKdf::transform(const QByteArray& raw, QByteArray& result) const
return true;
}
-bool AesKdf::transformKeyRaw(const QByteArray& key, const QByteArray& seed, quint64 rounds, QByteArray* result)
+bool AesKdf::transformKeyRaw(const QByteArray& key, const QByteArray& seed, int rounds, QByteArray* result)
{
QByteArray iv(16, 0);
SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Ecb,
@@ -65,44 +70,6 @@ bool AesKdf::transformKeyRaw(const QByteArray& key, const QByteArray& seed, quin
return true;
}
-AesKdf::AesKdf()
- : Kdf::Kdf(KeePass2::KDF_AES)
- , m_rounds(100000ull)
- , m_seed(QByteArray(32, 0))
-{
-}
-
-quint64 AesKdf::rounds() const
-{
- return m_rounds;
-}
-
-QByteArray AesKdf::seed() const
-{
- return m_seed;
-}
-
-bool AesKdf::setRounds(quint64 rounds)
-{
- m_rounds = rounds;
- return true;
-}
-
-bool AesKdf::setSeed(const QByteArray& seed)
-{
- if (seed.size() != 32) {
- return false;
- }
-
- m_seed = seed;
- return true;
-}
-
-void AesKdf::randomizeTransformSalt()
-{
- setSeed(randomGen()->randomArray(32));
-}
-
QSharedPointer<Kdf> AesKdf::clone() const
{
return QSharedPointer<AesKdf>::create(*this);
@@ -117,17 +84,13 @@ int AesKdf::benchmarkImpl(int msec) const
SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Ecb, SymmetricCipher::Encrypt);
cipher.init(seed, iv);
- int rounds = 0;
+ quint64 rounds = 1000000;
QElapsedTimer timer;
timer.start();
- do {
- if (!cipher.processInPlace(key, 10000)) {
- rounds = -1;
- break;
- }
- rounds += 10000;
+
+ if (!cipher.processInPlace(key, rounds)) {
+ return -1;
}
- while (!timer.hasExpired(msec));
- return rounds;
+ return static_cast<int>(rounds * (static_cast<float>(msec) / timer.elapsed()));
}
diff --git a/src/crypto/kdf/AesKdf.h b/src/crypto/kdf/AesKdf.h
index 28206a8d6..3e2c8ada6 100644
--- a/src/crypto/kdf/AesKdf.h
+++ b/src/crypto/kdf/AesKdf.h
@@ -26,25 +26,15 @@ public:
AesKdf();
bool transform(const QByteArray& raw, QByteArray& result) const override;
- void randomizeTransformSalt() override;
QSharedPointer<Kdf> clone() const override;
- quint64 rounds() const override;
- QByteArray seed() const override;
-
- bool setRounds(quint64 rounds) override;
- bool setSeed(const QByteArray& seed) override;
-
protected:
int benchmarkImpl(int msec) const override;
private:
- quint64 m_rounds;
- QByteArray m_seed;
-
static bool transformKeyRaw(const QByteArray& key,
const QByteArray& seed,
- quint64 rounds,
+ int rounds,
QByteArray* result) Q_REQUIRED_RESULT;
};
diff --git a/src/crypto/kdf/Argon2Kdf.cpp b/src/crypto/kdf/Argon2Kdf.cpp
new file mode 100644
index 000000000..fa410dc93
--- /dev/null
+++ b/src/crypto/kdf/Argon2Kdf.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 KeePassXC Team
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "Argon2Kdf.h"
+
+#include <QtConcurrent>
+#include <argon2.h>
+
+#include "format/KeePass2.h"
+#include "crypto/CryptoHash.h"
+
+/**
+ * KeePass' Argon2 implementation supports all parameters that are defined in the official specification,
+ * but only the number of iterations, the memory size and the degree of parallelism can be configured by
+ * the user in the database settings dialog. For the other parameters, KeePass chooses reasonable defaults:
+ * a 256-bit salt is generated each time the database is saved, the tag length is 256 bits, no secret key
+ * or associated data. KeePass uses the latest version of Argon2, v1.3.
+ */
+Argon2Kdf::Argon2Kdf()
+ : Kdf::Kdf(KeePass2::KDF_ARGON2)
+ , m_memory(1<<16)
+ , m_parallelism(2)
+{
+ m_rounds = 1;
+}
+
+quint32 Argon2Kdf::memory() const
+{
+ // Convert to Megabytes
+ return m_memory / (1<<10);
+}
+
+bool Argon2Kdf::setMemory(quint32 memoryMegabytes)
+{
+ // TODO: add bounds check
+ // Convert to Kibibytes
+ m_memory = (1<<10) * memoryMegabytes;
+ return true;
+}
+
+quint32 Argon2Kdf::parallelism() const
+{
+ return m_parallelism;
+}
+
+bool Argon2Kdf::setParallelism(quint32 threads)
+{
+ // TODO: add bounds check
+ m_parallelism = threads;
+ return true;
+}
+
+bool Argon2Kdf::transform(const QByteArray& raw, QByteArray& result) const
+{
+ result.clear();
+ result.resize(32);
+
+ if (!transformKeyRaw(raw, seed(), rounds(), memory(), parallelism(), result)) {
+ return false;
+ }
+
+ result = CryptoHash::hash(result, CryptoHash::Sha256);
+ return true;
+}
+
+bool Argon2Kdf::transformKeyRaw(const QByteArray& key, const QByteArray& seed, int rounds,
+ quint32 memory, quint32 parallelism, QByteArray& result)
+{
+ // Time Cost, Mem Cost, Threads/Lanes, Password, length, Salt, length, out, length
+ int rc = argon2d_hash_raw(rounds, memory, parallelism, key.data(), key.size(),
+ seed.data(), seed.size(), result.data(), result.size());
+ if (rc != ARGON2_OK) {
+ qWarning("Argon2 error: %s", argon2_error_message(rc));
+ return false;
+ }
+
+ return true;
+}
+
+QSharedPointer<Kdf> Argon2Kdf::clone() const
+{
+ return QSharedPointer<Argon2Kdf>::create(*this);
+}
+
+int Argon2Kdf::benchmarkImpl(int msec) const
+{
+ QByteArray key = QByteArray(16, '\x7E');
+ QByteArray seed = QByteArray(32, '\x4B');
+
+ QElapsedTimer timer;
+ timer.start();
+
+ int rounds = 4;
+
+ int rc = argon2d_hash_raw(rounds, m_memory, m_parallelism, key.data(), key.size(), seed.data(), seed.size(), key.data(), key.size());
+ if (rc != ARGON2_OK) {
+ qWarning("Argon2 error: %s", argon2_error_message(rc));
+ return -1;
+ }
+
+ return static_cast<int>(rounds * (static_cast<float>(msec) / timer.elapsed()));
+} \ No newline at end of file
diff --git a/src/crypto/kdf/Argon2Kdf.h b/src/crypto/kdf/Argon2Kdf.h
new file mode 100644
index 000000000..c01698120
--- /dev/null
+++ b/src/crypto/kdf/Argon2Kdf.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 KeePassXC Team
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KEEPASSX_ARGON2KDF_H
+#define KEEPASSX_ARGON2KDF_H
+
+#include "Kdf.h"
+
+class Argon2Kdf : public Kdf {
+public:
+ Argon2Kdf();
+
+ bool transform(const QByteArray& raw, QByteArray& result) const override;
+ QSharedPointer<Kdf> clone() const override;
+
+ quint32 memory() const;
+ bool setMemory(quint32 memory_kb);
+ quint32 parallelism() const;
+ bool setParallelism(quint32 threads);
+
+protected:
+ int benchmarkImpl(int msec) const override;
+
+ quint32 m_memory;
+ quint32 m_parallelism;
+
+private:
+ static bool transformKeyRaw(const QByteArray& key,
+ const QByteArray& seed,
+ int rounds,
+ quint32 memory,
+ quint32 parallelism,
+ QByteArray& result) Q_REQUIRED_RESULT;
+};
+
+#endif // KEEPASSX_ARGON2KDF_H
diff --git a/src/crypto/kdf/Kdf.cpp b/src/crypto/kdf/Kdf.cpp
index cfc3025c1..5134adc5f 100644
--- a/src/crypto/kdf/Kdf.cpp
+++ b/src/crypto/kdf/Kdf.cpp
@@ -20,8 +20,12 @@
#include <QtConcurrent>
+#include "crypto/Random.h"
+
Kdf::Kdf(Uuid uuid)
- : m_uuid(uuid)
+ : m_rounds(KDF_DEFAULT_ROUNDS)
+ , m_seed(QByteArray(KDF_DEFAULT_SEED_SIZE, 0))
+ , m_uuid(uuid)
{
}
@@ -30,6 +34,37 @@ Uuid Kdf::uuid() const
return m_uuid;
}
+int Kdf::rounds() const
+{
+ return m_rounds;
+}
+
+QByteArray Kdf::seed() const
+{
+ return m_seed;
+}
+
+bool Kdf::setRounds(int rounds)
+{
+ m_rounds = rounds;
+ return true;
+}
+
+bool Kdf::setSeed(const QByteArray& seed)
+{
+ if (seed.size() != m_seed.size()) {
+ return false;
+ }
+
+ m_seed = seed;
+ return true;
+}
+
+void Kdf::randomizeSeed()
+{
+ setSeed(randomGen()->randomArray(m_seed.size()));
+}
+
int Kdf::benchmark(int msec) const
{
BenchmarkThread thread1(msec, this);
@@ -41,7 +76,7 @@ int Kdf::benchmark(int msec) const
thread1.wait();
thread2.wait();
- return qMin(thread1.rounds(), thread2.rounds());
+ return qMax(1, qMin(thread1.rounds(), thread2.rounds()));
}
Kdf::BenchmarkThread::BenchmarkThread(int msec, const Kdf* kdf)
diff --git a/src/crypto/kdf/Kdf.h b/src/crypto/kdf/Kdf.h
index 5330e71d0..cb0bcc364 100644
--- a/src/crypto/kdf/Kdf.h
+++ b/src/crypto/kdf/Kdf.h
@@ -22,6 +22,9 @@
#include "core/Uuid.h"
+#define KDF_DEFAULT_SEED_SIZE 32
+#define KDF_DEFAULT_ROUNDS 100000ull
+
class Kdf
{
public:
@@ -30,12 +33,13 @@ public:
Uuid uuid() const;
- virtual quint64 rounds() const = 0;
- virtual bool setRounds(quint64 rounds) = 0;
- virtual QByteArray seed() const = 0;
- virtual bool setSeed(const QByteArray& seed) = 0;
+ int rounds() const;
+ virtual bool setRounds(int rounds);
+ QByteArray seed() const;
+ virtual bool setSeed(const QByteArray& seed);
+ virtual void randomizeSeed();
+
virtual bool transform(const QByteArray& raw, QByteArray& result) const = 0;
- virtual void randomizeTransformSalt() = 0;
virtual QSharedPointer<Kdf> clone() const = 0;
int benchmark(int msec) const;
@@ -43,6 +47,9 @@ public:
protected:
virtual int benchmarkImpl(int msec) const = 0;
+ int m_rounds;
+ QByteArray m_seed;
+
private:
class BenchmarkThread;
const Uuid m_uuid;
diff --git a/src/format/KeePass2.cpp b/src/format/KeePass2.cpp
index 61bd383df..fd57148d0 100644
--- a/src/format/KeePass2.cpp
+++ b/src/format/KeePass2.cpp
@@ -16,15 +16,16 @@
*/
#include "KeePass2.h"
-#include "crypto/kdf/AesKdf.h"
#include <QSharedPointer>
-
+#include "crypto/kdf/AesKdf.h"
+#include "crypto/kdf/Argon2Kdf.h"
const Uuid KeePass2::CIPHER_AES = Uuid(QByteArray::fromHex("31c1f2e6bf714350be5805216afc5aff"));
const Uuid KeePass2::CIPHER_TWOFISH = Uuid(QByteArray::fromHex("ad68f29f576f4bb9a36ad47af965346c"));
const Uuid KeePass2::CIPHER_CHACHA20 = Uuid(QByteArray::fromHex("D6038A2B8B6F4CB5A524339A31DBB59A"));
const Uuid KeePass2::KDF_AES = Uuid(QByteArray::fromHex("C9D9F39A628A4460BF740D08C18A4FEA"));
+const Uuid KeePass2::KDF_ARGON2 = Uuid(QByteArray::fromHex("EF636DDF8C29444B91F7A9A403E30A0C"));
const QByteArray KeePass2::INNER_STREAM_SALSA20_IV("\xE8\x30\x09\x4B\x97\x20\x5D\x2A");
@@ -35,12 +36,15 @@ const QList<QPair<Uuid, QString>> KeePass2::CIPHERS {
};
const QList<QPair<Uuid, QString>> KeePass2::KDFS {
qMakePair(KeePass2::KDF_AES, QObject::tr("AES-KDF")),
+ qMakePair(KeePass2::KDF_ARGON2, QObject::tr("Argon2")),
};
QSharedPointer<Kdf> KeePass2::uuidToKdf(const Uuid& uuid)
{
if (uuid == KDF_AES) {
return QSharedPointer<AesKdf>::create();
+ } else if (uuid == KDF_ARGON2) {
+ return QSharedPointer<Argon2Kdf>::create();
}
Q_ASSERT_X(false, "uuidToKdf", "Invalid UUID");
diff --git a/src/format/KeePass2.h b/src/format/KeePass2.h
index c7945e907..99bc5a0b0 100644
--- a/src/format/KeePass2.h
+++ b/src/format/KeePass2.h
@@ -40,6 +40,7 @@ namespace KeePass2
extern const Uuid CIPHER_CHACHA20;
extern const Uuid KDF_AES;
+ extern const Uuid KDF_ARGON2;
extern const QByteArray INNER_STREAM_SALSA20_IV;
diff --git a/src/gui/DatabaseSettingsWidget.cpp b/src/gui/DatabaseSettingsWidget.cpp
index 51c0f4183..2f4fe177d 100644
--- a/src/gui/DatabaseSettingsWidget.cpp
+++ b/src/gui/DatabaseSettingsWidget.cpp
@@ -26,6 +26,7 @@
#include "core/Group.h"
#include "core/Metadata.h"
#include "crypto/SymmetricCipher.h"
+#include "crypto/kdf/Argon2Kdf.h"
#include "MessageBox.h"
DatabaseSettingsWidget::DatabaseSettingsWidget(QWidget* parent)
@@ -42,6 +43,7 @@ DatabaseSettingsWidget::DatabaseSettingsWidget(QWidget* parent)
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(kdfChanged(int)));
}
DatabaseSettingsWidget::~DatabaseSettingsWidget()
@@ -83,19 +85,29 @@ void DatabaseSettingsWidget::load(Database* db)
m_ui->algorithmComboBox->setCurrentIndex(cipherIndex);
}
- bool blockSignals = m_ui->kdfComboBox->signalsBlocked();
+ // Setup kdf combo box
m_ui->kdfComboBox->blockSignals(true);
-
m_ui->kdfComboBox->clear();
for (auto& kdf: asConst(KeePass2::KDFS)) {
m_ui->kdfComboBox->addItem(kdf.second, kdf.first.toByteArray());
}
- int kdfIndex = m_ui->kdfComboBox->findData(m_db->kdf()->uuid().toByteArray());
+ m_ui->kdfComboBox->blockSignals(false);
+
+ auto kdfUuid = m_db->kdf()->uuid();
+ int kdfIndex = m_ui->kdfComboBox->findData(kdfUuid.toByteArray());
if (kdfIndex > -1) {
m_ui->kdfComboBox->setCurrentIndex(kdfIndex);
+ kdfChanged(kdfIndex);
+ }
+
+ // Setup kdf parameters
+ auto kdf = m_db->kdf();
+ m_ui->transformRoundsSpinBox->setValue(kdf->rounds());
+ if (kdfUuid == KeePass2::KDF_ARGON2) {
+ auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
+ m_ui->memorySpinBox->setValue(argon2Kdf->memory());
+ m_ui->parallelismSpinBox->setValue(argon2Kdf->parallelism());
}
- m_ui->kdfComboBox->blockSignals(blockSignals);
- m_ui->transformRoundsSpinBox->setValue(static_cast<unsigned>(m_db->kdf()->rounds()));
m_ui->dbNameEdit->setFocus();
}
@@ -139,8 +151,14 @@ void DatabaseSettingsWidget::save()
m_db->setCipher(Uuid(m_ui->algorithmComboBox->currentData().toByteArray()));
+ // Save kdf parameters
auto kdf = KeePass2::uuidToKdf(Uuid(m_ui->kdfComboBox->currentData().toByteArray()));
- kdf->setRounds(static_cast<quint64>(qMax(0, m_ui->transformRoundsSpinBox->value())));
+ kdf->setRounds(m_ui->transformRoundsSpinBox->value());
+ if (kdf->uuid() == KeePass2::KDF_ARGON2) {
+ auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
+ argon2Kdf->setMemory(m_ui->memorySpinBox->value());
+ argon2Kdf->setParallelism(m_ui->parallelismSpinBox->value());
+ }
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
// TODO: we should probably use AsyncTask::runAndWaitForFuture() here,
@@ -164,11 +182,26 @@ void DatabaseSettingsWidget::reject()
void DatabaseSettingsWidget::transformRoundsBenchmark()
{
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
- m_ui->transformRoundsSpinBox->setValue(AsyncTask::runAndWaitForFuture([this]() {
- int rounds = m_db->kdf()->benchmark(1000);
- QApplication::restoreOverrideCursor();
- return rounds;
- }));
+ m_ui->transformBenchmarkButton->setEnabled(false);
+
+ // Create a new kdf with the current parameters
+ auto kdf = KeePass2::uuidToKdf(Uuid(m_ui->kdfComboBox->currentData().toByteArray()));
+ kdf->setRounds(m_ui->transformRoundsSpinBox->value());
+ if (kdf->uuid() == KeePass2::KDF_ARGON2) {
+ auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
+ argon2Kdf->setMemory(m_ui->memorySpinBox->value());
+ argon2Kdf->setParallelism(m_ui->parallelismSpinBox->value());
+ }
+
+ // Determine the number of rounds required to meet 1 second delay
+ int rounds = AsyncTask::runAndWaitForFuture([this, kdf]() {
+ return kdf->benchmark(1000);
+ });
+
+ m_ui->transformRoundsSpinBox->setValue(rounds);
+
+ QApplication::restoreOverrideCursor();
+ m_ui->transformBenchmarkButton->setEnabled(true);
}
void DatabaseSettingsWidget::truncateHistories()
@@ -178,3 +211,15 @@ void DatabaseSettingsWidget::truncateHistories()
entry->truncateHistory();
}
}
+
+void DatabaseSettingsWidget::kdfChanged(int index)
+{
+ Uuid id(m_ui->kdfComboBox->itemData(index).toByteArray());
+ if (id == KeePass2::KDF_ARGON2) {
+ m_ui->memorySpinBox->setEnabled(true);
+ m_ui->parallelismSpinBox->setEnabled(true);
+ } else {
+ m_ui->memorySpinBox->setEnabled(false);
+ m_ui->parallelismSpinBox->setEnabled(false);
+ }
+}
diff --git a/src/gui/DatabaseSettingsWidget.h b/src/gui/DatabaseSettingsWidget.h
index ec3d6415a..83a5eb098 100644
--- a/src/gui/DatabaseSettingsWidget.h
+++ b/src/gui/DatabaseSettingsWidget.h
@@ -51,6 +51,7 @@ private slots:
void save();
void reject();
void transformRoundsBenchmark();
+ void kdfChanged(int index);
private:
void truncateHistories();
diff --git a/src/gui/DatabaseSettingsWidget.ui b/src/gui/DatabaseSettingsWidget.ui
index 8aaab19e7..5a6778678 100644
--- a/src/gui/DatabaseSettingsWidget.ui
+++ b/src/gui/DatabaseSettingsWidget.ui
@@ -2,6 +2,14 @@
<ui version="4.0">
<class>DatabaseSettingsWidget</class>
<widget class="QWidget" name="DatabaseSettingsWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>1082</width>
+ <height>506</height>
+ </rect>
+ </property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,2,5,1">
<item>
<spacer name="verticalSpacer_2">
@@ -35,39 +43,36 @@
<widget class="QWidget" name="widget" native="true">
<property name="maximumSize">
<size>
- <width>800</width>
+ <width>400</width>
<height>16777215</height>
</size>
</property>
<layout class="QGridLayout" name="gridLayout">
- <item row="4" column="2">
+ <item row="11" column="1">
+ <widget class="QCheckBox" name="historyMaxSizeCheckBox">
+ <property name="text">
+ <string>Max. history size:</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_3">
<item>
- <widget class="QSpinBox" name="transformRoundsSpinBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <widget class="QSpinBox" name="memorySpinBox">
+ <property name="suffix">
+ <string> MB</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
- <number>1000000000</number>
+ <number>1048576</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 name="value">
+ <number>64</number>
</property>
</widget>
</item>
@@ -80,31 +85,55 @@
</property>
</widget>
</item>
- <item row="8" column="1">
- <widget class="QCheckBox" name="historyMaxSizeCheckBox">
+ <item row="10" column="1">
+ <widget class="QCheckBox" name="historyMaxItemsCheckBox">
<property name="text">
- <string>Max. history size:</string>
+ <string>Max. history items:</string>
</property>
</widget>
</item>
- <item row="4" column="1" alignment="Qt::AlignRight">
- <widget class="QLabel" name="transformRoundsLabel">
+ <item row="4" column="1">
+ <widget class="QLabel" name="kdfLabel">
<property name="text">
- <string>Transform rounds:</string>
+ <string>Key Derivation Function</string>
</property>
</widget>
</item>
- <item row="7" column="1">
- <widget class="QCheckBox" name="historyMaxItemsCheckBox">
+ <item row="3" 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="3" column="1" alignment="Qt::AlignRight">
+ <widget class="QLabel" name="algorithmLabel">
<property name="text">
- <string>Max. history items:</string>
+ <string>Encryption Algorithm:</string>
</property>
</widget>
</item>
- <item row="0" column="2">
- <widget class="QLineEdit" name="dbNameEdit"/>
+ <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="7" column="2">
+ <item row="1" column="2">
+ <widget class="QLineEdit" name="dbDescriptionEdit"/>
+ </item>
+ <item row="4" column="2">
+ <widget class="QComboBox" name="kdfComboBox"/>
+ </item>
+ <item row="10" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QSpinBox" name="historyMaxItemsSpinBox">
@@ -121,17 +150,7 @@
</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">
+ <item row="11" column="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSpinBox" name="historyMaxSizeSpinBox">
@@ -155,54 +174,101 @@
</layout>
</item>
<item row="6" column="2">
- <widget class="QCheckBox" name="recycleBinEnabledCheckBox">
- <property name="text">
- <string>Use recycle bin</string>
+ <widget class="QSpinBox" name="transformRoundsSpinBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>1000000000</number>
</property>
</widget>
</item>
<item row="5" column="2">
+ <widget class="QToolButton" name="transformBenchmarkButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Benchmark 1-second delay</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" 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">
+ <item row="2" column="1">
+ <widget class="QLabel" name="defaultUsernameLabel">
<property name="text">
- <string>Database description:</string>
+ <string>Default username:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</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>
+ <item row="6" column="1">
+ <widget class="QLabel" name="transformRoundsLabel">
+ <property name="text">
+ <string>Transform rounds:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
</widget>
</item>
- <item row="2" column="1" alignment="Qt::AlignRight">
- <widget class="QLabel" name="algorithmLabel">
+ <item row="7" column="1">
+ <widget class="QLabel" name="memoryUsageLabel">
<property name="text">
- <string>Encryption Algorithm:</string>
+ <string>Memory Usage:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
- <item row="3" column="2">
- <widget class="QComboBox" name="kdfComboBox"/>
+ <item row="8" column="1">
+ <widget class="QLabel" name="parallelismLabel">
+ <property name="text">
+ <string>Parallelism:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
</item>
- <item row="3" column="1">
- <widget class="QLabel" name="kdfLabel">
+ <item row="8" column="2">
+ <widget class="QSpinBox" name="parallelismSpinBox">
+ <property name="suffix">
+ <string> thread</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>128</number>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="1">
+ <widget class="QCheckBox" name="recycleBinEnabledCheckBox">
<property name="text">
- <string>Key Derivation Function</string>
+ <string>Use recycle bin</string>
</property>
</widget>
</item>
@@ -249,10 +315,6 @@
<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>
diff --git a/src/main.cpp b/src/main.cpp
index deabeabb7..da71739c4 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -27,7 +27,6 @@
#include "crypto/Crypto.h"
#include "gui/Application.h"
#include "gui/MainWindow.h"
-#include "gui/csvImport/CsvImportWizard.h"
#include "gui/MessageBox.h"
#if defined(WITH_ASAN) && defined(WITH_LSAN)