mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-02-22 23:49:58 -05:00
CLI: set decryption time on create.
Added an option to set the target decryption time on database creation for the CLI create command. This required some refactoring, in particular the extraction of the min, max and defaut decryption times in the `Kdf` module. Some work was done to allow changing those constant only in the `Kdf` module, should we ever want to change them.
This commit is contained in:
parent
a41c26e9cd
commit
0b6d9cb472
@ -1,4 +1,4 @@
|
||||
.TH KEEPASSXC-CLI 1 "June 15, 2019"
|
||||
.TH KEEPASSXC-CLI 1 "Jan 04, 2020"
|
||||
|
||||
.SH NAME
|
||||
keepassxc-cli \- command line interface for the \fBKeePassXC\fP password manager.
|
||||
@ -179,6 +179,12 @@ Copies the current TOTP instead of current password to clipboard. Will report
|
||||
an error if no TOTP is configured for the entry.
|
||||
|
||||
|
||||
.SS "Create options"
|
||||
|
||||
.IP "\fB-t\fP, \fB--decryption-time\fP <time>"
|
||||
Target decryption time in MS for the database.
|
||||
|
||||
|
||||
.SS "Show options"
|
||||
|
||||
.IP "\fB-a\fP, \fB--attributes\fP <attribute>..."
|
||||
|
@ -30,12 +30,19 @@
|
||||
#include "keys/CompositeKey.h"
|
||||
#include "keys/Key.h"
|
||||
|
||||
const QCommandLineOption Create::DecryptionTimeOption =
|
||||
QCommandLineOption(QStringList() << "t"
|
||||
<< "decryption-time",
|
||||
QObject::tr("Target decryption time in MS for the database."),
|
||||
QObject::tr("time"));
|
||||
|
||||
Create::Create()
|
||||
{
|
||||
name = QString("create");
|
||||
description = QObject::tr("Create a new database.");
|
||||
positionalArguments.append({QString("database"), QObject::tr("Path of the database."), QString("")});
|
||||
options.append(Command::KeyFileOption);
|
||||
options.append(Create::DecryptionTimeOption);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -53,14 +60,16 @@ Create::Create()
|
||||
*/
|
||||
int Create::execute(const QStringList& arguments)
|
||||
{
|
||||
QTextStream out(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
QTextStream err(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
QSharedPointer<QCommandLineParser> parser = getCommandLineParser(arguments);
|
||||
if (parser.isNull()) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
bool quiet = parser->isSet(Command::QuietOption);
|
||||
|
||||
QTextStream out(quiet ? Utils::DEVNULL : Utils::STDOUT, QIODevice::WriteOnly);
|
||||
QTextStream err(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
|
||||
const QString& databaseFilename = args.at(0);
|
||||
@ -69,6 +78,23 @@ int Create::execute(const QStringList& arguments)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Validate the decryption time before asking for a password.
|
||||
QString decryptionTimeValue = parser->value(Create::DecryptionTimeOption);
|
||||
int decryptionTime = 0;
|
||||
if (decryptionTimeValue.length() != 0) {
|
||||
decryptionTime = decryptionTimeValue.toInt();
|
||||
if (decryptionTime <= 0) {
|
||||
err << QObject::tr("Invalid decryption time %1.").arg(decryptionTimeValue) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (decryptionTime < Kdf::MIN_ENCRYPTION_TIME || decryptionTime > Kdf::MAX_ENCRYPTION_TIME) {
|
||||
err << QObject::tr("Target decryption time must be between %1 and %2.")
|
||||
.arg(QString::number(Kdf::MIN_ENCRYPTION_TIME), QString::number(Kdf::MAX_ENCRYPTION_TIME))
|
||||
<< endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
auto key = QSharedPointer<CompositeKey>::create();
|
||||
|
||||
auto password = Utils::getPasswordFromStdin();
|
||||
@ -96,6 +122,23 @@ int Create::execute(const QStringList& arguments)
|
||||
QSharedPointer<Database> db(new Database);
|
||||
db->setKey(key);
|
||||
|
||||
if (decryptionTime != 0) {
|
||||
auto kdf = db->kdf();
|
||||
Q_ASSERT(kdf);
|
||||
|
||||
out << QObject::tr("Benchmarking key derivation function for %1ms delay.").arg(decryptionTimeValue) << endl;
|
||||
int rounds = kdf->benchmark(decryptionTime);
|
||||
out << QObject::tr("Setting %1 rounds for key derivation function.").arg(QString::number(rounds)) << endl;
|
||||
kdf->setRounds(rounds);
|
||||
|
||||
bool ok = db->changeKdf(kdf);
|
||||
|
||||
if (!ok) {
|
||||
err << QObject::tr("Error while setting database key derivation settings.") << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
QString errorMessage;
|
||||
if (!db->saveAs(databaseFilename, &errorMessage, true, false)) {
|
||||
err << QObject::tr("Failed to save the database: %1.").arg(errorMessage) << endl;
|
||||
|
@ -28,6 +28,8 @@ public:
|
||||
Create();
|
||||
int execute(const QStringList& arguments) override;
|
||||
|
||||
static const QCommandLineOption DecryptionTimeOption;
|
||||
|
||||
private:
|
||||
bool loadFileKey(const QString& path, QSharedPointer<FileKey>& fileKey);
|
||||
};
|
||||
|
@ -48,6 +48,19 @@ public:
|
||||
|
||||
int benchmark(int msec) const;
|
||||
|
||||
/*
|
||||
* Default target encryption time, in MS.
|
||||
*/
|
||||
static const int DEFAULT_ENCRYPTION_TIME = 1000;
|
||||
/*
|
||||
* Minimum target encryption time, in MS.
|
||||
*/
|
||||
static const int MIN_ENCRYPTION_TIME = 100;
|
||||
/*
|
||||
* Maximum target encryption time, in MS.
|
||||
*/
|
||||
static const int MAX_ENCRYPTION_TIME = 5000;
|
||||
|
||||
protected:
|
||||
virtual int benchmarkImpl(int msec) const = 0;
|
||||
|
||||
|
@ -45,9 +45,17 @@ DatabaseSettingsWidgetEncryption::DatabaseSettingsWidgetEncryption(QWidget* pare
|
||||
|
||||
m_ui->compatibilitySelection->addItem(tr("KDBX 4.0 (recommended)"), KeePass2::KDF_ARGON2.toByteArray());
|
||||
m_ui->compatibilitySelection->addItem(tr("KDBX 3.1"), KeePass2::KDF_AES_KDBX3.toByteArray());
|
||||
m_ui->decryptionTimeSlider->setValue(10);
|
||||
m_ui->decryptionTimeSlider->setMinimum(Kdf::MIN_ENCRYPTION_TIME / 100);
|
||||
m_ui->decryptionTimeSlider->setMaximum(Kdf::MAX_ENCRYPTION_TIME / 100);
|
||||
m_ui->decryptionTimeSlider->setValue(Kdf::DEFAULT_ENCRYPTION_TIME / 100);
|
||||
updateDecryptionTime(m_ui->decryptionTimeSlider->value());
|
||||
|
||||
m_ui->transformBenchmarkButton->setText(
|
||||
QObject::tr("Benchmark %1 delay")
|
||||
.arg(DatabaseSettingsWidgetEncryption::getTextualEncryptionTime(Kdf::DEFAULT_ENCRYPTION_TIME)));
|
||||
m_ui->minTimeLabel->setText(DatabaseSettingsWidgetEncryption::getTextualEncryptionTime(Kdf::MIN_ENCRYPTION_TIME));
|
||||
m_ui->maxTimeLabel->setText(DatabaseSettingsWidgetEncryption::getTextualEncryptionTime(Kdf::MAX_ENCRYPTION_TIME));
|
||||
|
||||
connect(m_ui->activateChangeDecryptionTimeButton, SIGNAL(clicked()), SLOT(activateChangeDecryptionTime()));
|
||||
connect(m_ui->decryptionTimeSlider, SIGNAL(valueChanged(int)), SLOT(updateDecryptionTime(int)));
|
||||
connect(m_ui->compatibilitySelection, SIGNAL(currentIndexChanged(int)), SLOT(updateFormatCompatibility(int)));
|
||||
@ -373,11 +381,7 @@ void DatabaseSettingsWidgetEncryption::setAdvancedMode(bool advanced)
|
||||
|
||||
void DatabaseSettingsWidgetEncryption::updateDecryptionTime(int value)
|
||||
{
|
||||
if (value < 10) {
|
||||
m_ui->decryptionTimeValueLabel->setText(tr("%1 ms", "milliseconds", value * 100).arg(value * 100));
|
||||
} else {
|
||||
m_ui->decryptionTimeValueLabel->setText(tr("%1 s", "seconds", value / 10).arg(value / 10.0, 0, 'f', 1));
|
||||
}
|
||||
m_ui->decryptionTimeValueLabel->setText(DatabaseSettingsWidgetEncryption::getTextualEncryptionTime(value * 100));
|
||||
}
|
||||
|
||||
void DatabaseSettingsWidgetEncryption::updateFormatCompatibility(int index, bool retransform)
|
||||
@ -409,3 +413,12 @@ void DatabaseSettingsWidgetEncryption::updateFormatCompatibility(int index, bool
|
||||
activateChangeDecryptionTime();
|
||||
}
|
||||
}
|
||||
|
||||
QString DatabaseSettingsWidgetEncryption::getTextualEncryptionTime(int millisecs)
|
||||
{
|
||||
if (millisecs < 1000) {
|
||||
return QObject::tr("%1 ms", "milliseconds", millisecs).arg(millisecs);
|
||||
} else {
|
||||
return QObject::tr("%1 s", "seconds", millisecs / 1000).arg(millisecs / 1000.0, 0, 'f', 1);
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@
|
||||
|
||||
#include "DatabaseSettingsWidget.h"
|
||||
|
||||
#include "crypto/kdf/Kdf.h"
|
||||
|
||||
#include <QPointer>
|
||||
#include <QScopedPointer>
|
||||
|
||||
@ -49,11 +51,13 @@ public slots:
|
||||
void uninitialize() override;
|
||||
bool save() override;
|
||||
|
||||
static QString getTextualEncryptionTime(int millisecs);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
||||
private slots:
|
||||
void benchmarkTransformRounds(int millisecs = 1000);
|
||||
void benchmarkTransformRounds(int millisecs = Kdf::DEFAULT_ENCRYPTION_TIME);
|
||||
void changeKdf(int index);
|
||||
void memoryChanged(int value);
|
||||
void parallelismChanged(int value);
|
||||
|
@ -95,12 +95,6 @@
|
||||
<property name="accessibleName">
|
||||
<string>Decryption time in seconds</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>50</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
@ -124,9 +118,9 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<widget class="QLabel" name="minTimeLabel">
|
||||
<property name="text">
|
||||
<string>100 ms</string>
|
||||
<string>?? ms</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -144,9 +138,9 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<widget class="QLabel" name="maxTimeLabel">
|
||||
<property name="text">
|
||||
<string>5 s</string>
|
||||
<string>? s</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -326,9 +320,6 @@
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::WheelFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Benchmark 1-second delay</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -608,6 +608,48 @@ void TestCli::testCreate()
|
||||
auto db3 =
|
||||
QSharedPointer<Database>(Utils::unlockDatabase(databaseFilename3, true, keyfilePath, "", Utils::DEVNULL));
|
||||
QVERIFY(db3);
|
||||
|
||||
// Invalid decryption time (format).
|
||||
QString databaseFilename4 = testDir->path() + "/testCreate4.kdbx";
|
||||
pos = m_stdoutFile->pos();
|
||||
errPos = m_stderrFile->pos();
|
||||
createCmd.execute({"create", databaseFilename4, "-t", "NAN"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stderrFile->seek(errPos);
|
||||
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray(""));
|
||||
QCOMPARE(m_stderrFile->readAll(), QByteArray("Invalid decryption time NAN.\n"));
|
||||
|
||||
// Invalid decryption time (range).
|
||||
pos = m_stdoutFile->pos();
|
||||
errPos = m_stderrFile->pos();
|
||||
createCmd.execute({"create", databaseFilename4, "-t", "10"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stderrFile->seek(errPos);
|
||||
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray(""));
|
||||
QVERIFY(m_stderrFile->readAll().contains(QByteArray("Target decryption time must be between")));
|
||||
|
||||
int encryptionTime = 500;
|
||||
// Custom encryption time
|
||||
pos = m_stdoutFile->pos();
|
||||
errPos = m_stderrFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
int epochBefore = QDateTime::currentMSecsSinceEpoch();
|
||||
createCmd.execute({"create", databaseFilename4, "-t", QString::number(encryptionTime)});
|
||||
// Removing 100ms to make sure we account for changes in computation time.
|
||||
QVERIFY(QDateTime::currentMSecsSinceEpoch() > (epochBefore + encryptionTime - 100));
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stderrFile->seek(errPos);
|
||||
|
||||
QCOMPARE(m_stderrFile->readAll(), QByteArray(""));
|
||||
QCOMPARE(m_stdoutFile->readLine(), QByteArray("Enter password to encrypt database (optional): \n"));
|
||||
QCOMPARE(m_stdoutFile->readLine(), QByteArray("Benchmarking key derivation function for 500ms delay.\n"));
|
||||
QVERIFY(m_stdoutFile->readLine().contains(QByteArray("rounds for key derivation function.\n")));
|
||||
|
||||
Utils::Test::setNextPassword("a");
|
||||
auto db4 = QSharedPointer<Database>(Utils::unlockDatabase(databaseFilename4, true, "", "", Utils::DEVNULL));
|
||||
QVERIFY(db4);
|
||||
}
|
||||
|
||||
void TestCli::testInfo()
|
||||
|
Loading…
x
Reference in New Issue
Block a user