mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-26 07:49:50 -05:00
parent
dd1a233859
commit
2b08af712f
@ -49,10 +49,8 @@ const QString KeePass2::KDFPARAM_ARGON2_ASSOCDATA("A");
|
|||||||
|
|
||||||
const QList<QUuid> KeePass2::CIPHERS{KeePass2::CIPHER_AES256, KeePass2::CIPHER_TWOFISH, KeePass2::CIPHER_CHACHA20};
|
const QList<QUuid> KeePass2::CIPHERS{KeePass2::CIPHER_AES256, KeePass2::CIPHER_TWOFISH, KeePass2::CIPHER_CHACHA20};
|
||||||
|
|
||||||
const QList<QUuid> KeePass2::KDFS{KeePass2::KDF_ARGON2D,
|
const QList<QUuid> KeePass2::KDBX4_KDFS{KeePass2::KDF_ARGON2D, KeePass2::KDF_ARGON2ID, KeePass2::KDF_AES_KDBX4};
|
||||||
KeePass2::KDF_ARGON2ID,
|
const QList<QUuid> KeePass2::KDBX3_KDFS{KeePass2::KDF_AES_KDBX3};
|
||||||
KeePass2::KDF_AES_KDBX4,
|
|
||||||
KeePass2::KDF_AES_KDBX3};
|
|
||||||
|
|
||||||
QByteArray KeePass2::hmacKey(const QByteArray& masterSeed, const QByteArray& transformedMasterKey)
|
QByteArray KeePass2::hmacKey(const QByteArray& masterSeed, const QByteArray& transformedMasterKey)
|
||||||
{
|
{
|
||||||
|
@ -67,7 +67,8 @@ namespace KeePass2
|
|||||||
extern const QString KDFPARAM_ARGON2_ASSOCDATA;
|
extern const QString KDFPARAM_ARGON2_ASSOCDATA;
|
||||||
|
|
||||||
extern const QList<QUuid> CIPHERS;
|
extern const QList<QUuid> CIPHERS;
|
||||||
extern const QList<QUuid> KDFS;
|
extern const QList<QUuid> KDBX4_KDFS;
|
||||||
|
extern const QList<QUuid> KDBX3_KDFS;
|
||||||
|
|
||||||
enum class HeaderFieldID
|
enum class HeaderFieldID
|
||||||
{
|
{
|
||||||
|
@ -52,14 +52,15 @@ DatabaseSettingsWidgetEncryption::DatabaseSettingsWidgetEncryption(QWidget* pare
|
|||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
connect(m_ui->transformBenchmarkButton, SIGNAL(clicked()), SLOT(benchmarkTransformRounds()));
|
connect(m_ui->transformBenchmarkButton, SIGNAL(clicked()), SLOT(benchmarkTransformRounds()));
|
||||||
connect(m_ui->kdfComboBox, SIGNAL(currentIndexChanged(int)), SLOT(changeKdf(int)));
|
connect(m_ui->kdfComboBox, SIGNAL(currentIndexChanged(int)), SLOT(updateKdfFields()));
|
||||||
|
connect(m_ui->compatibilitySelection, SIGNAL(currentIndexChanged(int)), SLOT(loadKdfAlgorithms()));
|
||||||
m_ui->formatCannotBeChanged->setVisible(false);
|
m_ui->formatCannotBeChanged->setVisible(false);
|
||||||
|
|
||||||
connect(m_ui->memorySpinBox, SIGNAL(valueChanged(int)), this, SLOT(memoryChanged(int)));
|
connect(m_ui->memorySpinBox, SIGNAL(valueChanged(int)), this, SLOT(memoryChanged(int)));
|
||||||
connect(m_ui->parallelismSpinBox, SIGNAL(valueChanged(int)), this, SLOT(parallelismChanged(int)));
|
connect(m_ui->parallelismSpinBox, SIGNAL(valueChanged(int)), this, SLOT(parallelismChanged(int)));
|
||||||
|
|
||||||
m_ui->compatibilitySelection->addItem(tr("KDBX 4 (recommended)"), KeePass2::KDF_ARGON2D.toByteArray());
|
m_ui->compatibilitySelection->addItem(tr("KDBX 4 (recommended)"), KeePass2::KDF_ARGON2D);
|
||||||
m_ui->compatibilitySelection->addItem(tr("KDBX 3"), KeePass2::KDF_AES_KDBX3.toByteArray());
|
m_ui->compatibilitySelection->addItem(tr("KDBX 3"), KeePass2::KDF_AES_KDBX3);
|
||||||
m_ui->decryptionTimeSlider->setMinimum(Kdf::MIN_ENCRYPTION_TIME / 100);
|
m_ui->decryptionTimeSlider->setMinimum(Kdf::MIN_ENCRYPTION_TIME / 100);
|
||||||
m_ui->decryptionTimeSlider->setMaximum(Kdf::MAX_ENCRYPTION_TIME / 100);
|
m_ui->decryptionTimeSlider->setMaximum(Kdf::MAX_ENCRYPTION_TIME / 100);
|
||||||
m_ui->decryptionTimeSlider->setValue(Kdf::DEFAULT_ENCRYPTION_TIME / 100);
|
m_ui->decryptionTimeSlider->setValue(Kdf::DEFAULT_ENCRYPTION_TIME / 100);
|
||||||
@ -71,7 +72,6 @@ DatabaseSettingsWidgetEncryption::DatabaseSettingsWidgetEncryption(QWidget* pare
|
|||||||
m_ui->maxTimeLabel->setText(getTextualEncryptionTime(Kdf::MAX_ENCRYPTION_TIME));
|
m_ui->maxTimeLabel->setText(getTextualEncryptionTime(Kdf::MAX_ENCRYPTION_TIME));
|
||||||
|
|
||||||
connect(m_ui->decryptionTimeSlider, SIGNAL(valueChanged(int)), SLOT(updateDecryptionTime(int)));
|
connect(m_ui->decryptionTimeSlider, SIGNAL(valueChanged(int)), SLOT(updateDecryptionTime(int)));
|
||||||
connect(m_ui->compatibilitySelection, SIGNAL(currentIndexChanged(int)), SLOT(updateFormatCompatibility(int)));
|
|
||||||
|
|
||||||
// conditions under which a key re-transformation is needed
|
// conditions under which a key re-transformation is needed
|
||||||
connect(m_ui->decryptionTimeSlider, SIGNAL(valueChanged(int)), SLOT(markDirty()));
|
connect(m_ui->decryptionTimeSlider, SIGNAL(valueChanged(int)), SLOT(markDirty()));
|
||||||
@ -100,14 +100,9 @@ void DatabaseSettingsWidgetEncryption::initialize()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto version = KDBX4;
|
|
||||||
if (m_db->key() && m_db->kdf()) {
|
|
||||||
version = (m_db->kdf()->uuid() == KeePass2::KDF_AES_KDBX3) ? KDBX3 : KDBX4;
|
|
||||||
}
|
|
||||||
m_ui->compatibilitySelection->setCurrentIndex(version);
|
|
||||||
|
|
||||||
bool isNewDatabase = false;
|
bool isNewDatabase = false;
|
||||||
|
|
||||||
|
// Check for uninitialized database parameters and set initial values accordingly
|
||||||
if (!m_db->key()) {
|
if (!m_db->key()) {
|
||||||
m_db->setKey(QSharedPointer<CompositeKey>::create(), true, false, false);
|
m_db->setKey(QSharedPointer<CompositeKey>::create(), true, false, false);
|
||||||
m_db->setKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2D));
|
m_db->setKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2D));
|
||||||
@ -118,12 +113,11 @@ void DatabaseSettingsWidgetEncryption::initialize()
|
|||||||
isNewDatabase = true;
|
isNewDatabase = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool kdbx3Enabled = KeePass2Writer::kdbxVersionRequired(m_db.data(), true, true) <= KeePass2::FILE_VERSION_3_1;
|
// Initialize the basic settings tab
|
||||||
|
|
||||||
// check if the DB's custom data has a decryption time setting stored
|
// check if the DB's custom data has a decryption time setting stored
|
||||||
// and set the slider to it, otherwise just state that the time is unchanged
|
// and set the slider to it, otherwise just state that the time is unchanged
|
||||||
// (we cannot infer the time from the raw KDF settings)
|
// (we cannot infer the time from the raw KDF settings)
|
||||||
|
|
||||||
auto* cd = m_db->metadata()->customData();
|
auto* cd = m_db->metadata()->customData();
|
||||||
if (cd->hasKey(CD_DECRYPTION_TIME_PREFERENCE_KEY)) {
|
if (cd->hasKey(CD_DECRYPTION_TIME_PREFERENCE_KEY)) {
|
||||||
int decryptionTime = qMax(100, cd->value(CD_DECRYPTION_TIME_PREFERENCE_KEY).toInt());
|
int decryptionTime = qMax(100, cd->value(CD_DECRYPTION_TIME_PREFERENCE_KEY).toInt());
|
||||||
@ -138,16 +132,43 @@ void DatabaseSettingsWidgetEncryption::initialize()
|
|||||||
m_initWithAdvanced = true;
|
m_initWithAdvanced = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFormatCompatibility(m_db->kdf()->uuid() == KeePass2::KDF_AES_KDBX3 ? KDBX3 : KDBX4, isNewDatabase);
|
// Initialize the advanced settings tab
|
||||||
setupAlgorithmComboBox();
|
|
||||||
setupKdfComboBox(kdbx3Enabled);
|
|
||||||
loadKdfParameters();
|
|
||||||
|
|
||||||
if (!kdbx3Enabled) {
|
// Set up the KDBX version selector
|
||||||
|
bool isKdbx3 = m_db->formatVersion() <= KeePass2::FILE_VERSION_3_1;
|
||||||
|
m_ui->compatibilitySelection->blockSignals(true);
|
||||||
|
m_ui->compatibilitySelection->setCurrentIndex(isKdbx3 ? KDBX3 : KDBX4);
|
||||||
|
m_ui->compatibilitySelection->blockSignals(false);
|
||||||
|
|
||||||
|
// Disable KDBX selector if downgrading would lose data
|
||||||
|
if (!isKdbx3 && KeePass2Writer::kdbxVersionRequired(m_db.data(), true, true) >= KeePass2::FILE_VERSION_4) {
|
||||||
m_ui->compatibilitySelection->setEnabled(false);
|
m_ui->compatibilitySelection->setEnabled(false);
|
||||||
m_ui->formatCannotBeChanged->setVisible(true);
|
m_ui->formatCannotBeChanged->setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up encryption ciphers
|
||||||
|
m_ui->algorithmComboBox->clear();
|
||||||
|
for (auto& cipher : asConst(KeePass2::CIPHERS)) {
|
||||||
|
m_ui->algorithmComboBox->addItem(KeePass2::cipherToString(cipher), cipher);
|
||||||
|
}
|
||||||
|
int cipherIndex = m_ui->algorithmComboBox->findData(m_db->cipher());
|
||||||
|
if (cipherIndex > -1) {
|
||||||
|
m_ui->algorithmComboBox->setCurrentIndex(cipherIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up KDF algorithms
|
||||||
|
loadKdfAlgorithms();
|
||||||
|
|
||||||
|
// Perform Benchmark if requested
|
||||||
|
if (isNewDatabase) {
|
||||||
|
if (IS_ARGON2(m_ui->kdfComboBox->currentData())) {
|
||||||
|
m_ui->memorySpinBox->setValue(16);
|
||||||
|
m_ui->parallelismSpinBox->setValue(2);
|
||||||
|
}
|
||||||
|
benchmarkTransformRounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
// New databases always require saving
|
||||||
m_isDirty = isNewDatabase;
|
m_isDirty = isNewDatabase;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,37 +179,36 @@ void DatabaseSettingsWidgetEncryption::uninitialize()
|
|||||||
void DatabaseSettingsWidgetEncryption::showEvent(QShowEvent* event)
|
void DatabaseSettingsWidgetEncryption::showEvent(QShowEvent* event)
|
||||||
{
|
{
|
||||||
QWidget::showEvent(event);
|
QWidget::showEvent(event);
|
||||||
m_ui->decryptionTimeSlider->setFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatabaseSettingsWidgetEncryption::setupAlgorithmComboBox()
|
if (m_ui->decryptionTimeSlider->isVisible()) {
|
||||||
{
|
m_ui->decryptionTimeSlider->setFocus();
|
||||||
m_ui->algorithmComboBox->clear();
|
} else {
|
||||||
for (auto& cipher : asConst(KeePass2::CIPHERS)) {
|
m_ui->transformRoundsSpinBox->setFocus();
|
||||||
m_ui->algorithmComboBox->addItem(KeePass2::cipherToString(cipher), cipher.toByteArray());
|
|
||||||
}
|
|
||||||
int cipherIndex = m_ui->algorithmComboBox->findData(m_db->cipher().toByteArray());
|
|
||||||
if (cipherIndex > -1) {
|
|
||||||
m_ui->algorithmComboBox->setCurrentIndex(cipherIndex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseSettingsWidgetEncryption::setupKdfComboBox(bool enableKdbx3)
|
void DatabaseSettingsWidgetEncryption::loadKdfAlgorithms()
|
||||||
{
|
{
|
||||||
// Set up kdf combo box
|
bool isKdbx3 = m_ui->compatibilitySelection->currentIndex() == KDBX3;
|
||||||
bool block = m_ui->kdfComboBox->blockSignals(true);
|
|
||||||
|
m_ui->kdfComboBox->blockSignals(true);
|
||||||
m_ui->kdfComboBox->clear();
|
m_ui->kdfComboBox->clear();
|
||||||
for (auto& kdf : asConst(KeePass2::KDFS)) {
|
const auto& kdfs = isKdbx3 ? KeePass2::KDBX3_KDFS : KeePass2::KDBX4_KDFS;
|
||||||
if (kdf != KeePass2::KDF_AES_KDBX3 or enableKdbx3) {
|
for (auto& kdf : kdfs) {
|
||||||
m_ui->kdfComboBox->addItem(KeePass2::kdfToString(kdf), kdf.toByteArray());
|
m_ui->kdfComboBox->addItem(KeePass2::kdfToString(kdf), kdf);
|
||||||
|
// Set current index to the current database KDF if it matches
|
||||||
|
if (m_db && m_db->kdf() && m_db->kdf()->uuid() == kdf) {
|
||||||
|
m_ui->kdfComboBox->setCurrentIndex(m_ui->kdfComboBox->count() - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_ui->kdfComboBox->blockSignals(block);
|
m_ui->kdfComboBox->blockSignals(false);
|
||||||
|
|
||||||
|
// Ensure consistency with current index
|
||||||
|
updateKdfFields();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseSettingsWidgetEncryption::loadKdfParameters()
|
void DatabaseSettingsWidgetEncryption::loadKdfParameters()
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_db);
|
|
||||||
if (!m_db) {
|
if (!m_db) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -198,31 +218,37 @@ void DatabaseSettingsWidgetEncryption::loadKdfParameters()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int kdfIndex = m_ui->kdfComboBox->findData(m_db->kdf()->uuid().toByteArray());
|
// Load database KDF parameters if equal to current choice
|
||||||
if (kdfIndex > -1) {
|
bool dbIsArgon2 = IS_ARGON2(kdf->uuid());
|
||||||
bool block = m_ui->kdfComboBox->blockSignals(true);
|
bool kdfIsArgon2 = IS_ARGON2(m_ui->kdfComboBox->currentData().toUuid());
|
||||||
m_ui->kdfComboBox->setCurrentIndex(kdfIndex);
|
if (dbIsArgon2 && kdfIsArgon2) {
|
||||||
m_ui->kdfComboBox->blockSignals(block);
|
// Set Argon2 parameters
|
||||||
}
|
|
||||||
|
|
||||||
m_ui->transformRoundsSpinBox->setValue(kdf->rounds());
|
|
||||||
if (IS_ARGON2(m_db->kdf()->uuid())) {
|
|
||||||
auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
|
auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
|
||||||
|
m_ui->transformRoundsSpinBox->setValue(argon2Kdf->rounds());
|
||||||
m_ui->memorySpinBox->setValue(static_cast<int>(argon2Kdf->memory()) / (1 << 10));
|
m_ui->memorySpinBox->setValue(static_cast<int>(argon2Kdf->memory()) / (1 << 10));
|
||||||
m_ui->parallelismSpinBox->setValue(argon2Kdf->parallelism());
|
m_ui->parallelismSpinBox->setValue(argon2Kdf->parallelism());
|
||||||
|
} else if (!dbIsArgon2 && !kdfIsArgon2) {
|
||||||
|
// Set AES KDF parameters
|
||||||
|
m_ui->transformRoundsSpinBox->setValue(kdf->rounds());
|
||||||
|
} else {
|
||||||
|
// Set reasonable defaults and then benchmark
|
||||||
|
if (kdfIsArgon2) {
|
||||||
|
m_ui->memorySpinBox->setValue(16);
|
||||||
|
m_ui->parallelismSpinBox->setValue(2);
|
||||||
|
}
|
||||||
|
benchmarkTransformRounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateKdfFields();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseSettingsWidgetEncryption::updateKdfFields()
|
void DatabaseSettingsWidgetEncryption::updateKdfFields()
|
||||||
{
|
{
|
||||||
QUuid id = m_db->kdf()->uuid();
|
bool isArgon2 = IS_ARGON2(m_ui->kdfComboBox->currentData().toUuid());
|
||||||
|
m_ui->memoryUsageLabel->setVisible(isArgon2);
|
||||||
|
m_ui->memorySpinBox->setVisible(isArgon2);
|
||||||
|
m_ui->parallelismLabel->setVisible(isArgon2);
|
||||||
|
m_ui->parallelismSpinBox->setVisible(isArgon2);
|
||||||
|
|
||||||
m_ui->memoryUsageLabel->setVisible(IS_ARGON2(id));
|
loadKdfParameters();
|
||||||
m_ui->memorySpinBox->setVisible(IS_ARGON2(id));
|
|
||||||
m_ui->parallelismLabel->setVisible(IS_ARGON2(id));
|
|
||||||
m_ui->parallelismSpinBox->setVisible(IS_ARGON2(id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseSettingsWidgetEncryption::markDirty()
|
void DatabaseSettingsWidgetEncryption::markDirty()
|
||||||
@ -247,19 +273,17 @@ bool DatabaseSettingsWidgetEncryption::save()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto kdf = m_db->kdf();
|
|
||||||
Q_ASSERT(kdf);
|
|
||||||
|
|
||||||
if (!isAdvancedMode()) {
|
if (!isAdvancedMode()) {
|
||||||
|
// Basic mode maintains current database KDF
|
||||||
|
auto kdf = m_db->kdf();
|
||||||
|
Q_ASSERT(kdf);
|
||||||
if (kdf && !m_isDirty && !m_ui->decryptionTimeSettings->isVisible()) {
|
if (kdf && !m_isDirty && !m_ui->decryptionTimeSettings->isVisible()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int time = m_ui->decryptionTimeSlider->value() * 100;
|
|
||||||
updateFormatCompatibility(m_ui->compatibilitySelection->currentIndex(), false);
|
|
||||||
|
|
||||||
QApplication::setOverrideCursor(Qt::BusyCursor);
|
QApplication::setOverrideCursor(Qt::BusyCursor);
|
||||||
|
|
||||||
|
int time = m_ui->decryptionTimeSlider->value() * 100;
|
||||||
int rounds = AsyncTask::runAndWaitForFuture([&kdf, time]() { return kdf->benchmark(time); });
|
int rounds = AsyncTask::runAndWaitForFuture([&kdf, time]() { return kdf->benchmark(time); });
|
||||||
kdf->setRounds(rounds);
|
kdf->setRounds(rounds);
|
||||||
|
|
||||||
@ -274,12 +298,11 @@ bool DatabaseSettingsWidgetEncryption::save()
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove a stored decryption time from custom data when advanced settings are used
|
// Advanced mode sets KDF
|
||||||
// we don't know it until we actually run the KDF
|
auto kdfChoice = m_ui->kdfComboBox->currentData().toUuid();
|
||||||
m_db->metadata()->customData()->remove(CD_DECRYPTION_TIME_PREFERENCE_KEY);
|
|
||||||
|
|
||||||
// first perform safety check for KDF rounds
|
// first perform safety check for KDF rounds
|
||||||
if (IS_ARGON2(kdf->uuid()) && m_ui->transformRoundsSpinBox->value() > 10000) {
|
if (IS_ARGON2(kdfChoice) && m_ui->transformRoundsSpinBox->value() > 10000) {
|
||||||
QMessageBox warning;
|
QMessageBox warning;
|
||||||
warning.setIcon(QMessageBox::Warning);
|
warning.setIcon(QMessageBox::Warning);
|
||||||
warning.setWindowTitle(tr("Number of rounds too high", "Key transformation rounds"));
|
warning.setWindowTitle(tr("Number of rounds too high", "Key transformation rounds"));
|
||||||
@ -293,7 +316,7 @@ bool DatabaseSettingsWidgetEncryption::save()
|
|||||||
if (warning.clickedButton() != ok) {
|
if (warning.clickedButton() != ok) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (IS_AES_KDF(kdf->uuid()) && m_ui->transformRoundsSpinBox->value() < 100000) {
|
} else if (IS_AES_KDF(kdfChoice) && m_ui->transformRoundsSpinBox->value() < 100000) {
|
||||||
QMessageBox warning;
|
QMessageBox warning;
|
||||||
warning.setIcon(QMessageBox::Warning);
|
warning.setIcon(QMessageBox::Warning);
|
||||||
warning.setWindowTitle(tr("Number of rounds too low", "Key transformation rounds"));
|
warning.setWindowTitle(tr("Number of rounds too low", "Key transformation rounds"));
|
||||||
@ -309,9 +332,14 @@ bool DatabaseSettingsWidgetEncryption::save()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_db->setCipher(QUuid(m_ui->algorithmComboBox->currentData().toByteArray()));
|
m_db->setCipher(m_ui->algorithmComboBox->currentData().toUuid());
|
||||||
|
|
||||||
|
// remove a stored decryption time from custom data when advanced settings are used
|
||||||
|
// we don't know it until we actually run the KDF
|
||||||
|
m_db->metadata()->customData()->remove(CD_DECRYPTION_TIME_PREFERENCE_KEY);
|
||||||
|
|
||||||
// Save kdf parameters
|
// Save kdf parameters
|
||||||
|
auto kdf = KeePass2::uuidToKdf(kdfChoice);
|
||||||
kdf->setRounds(m_ui->transformRoundsSpinBox->value());
|
kdf->setRounds(m_ui->transformRoundsSpinBox->value());
|
||||||
if (IS_ARGON2(kdf->uuid())) {
|
if (IS_ARGON2(kdf->uuid())) {
|
||||||
auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
|
auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
|
||||||
@ -339,10 +367,11 @@ void DatabaseSettingsWidgetEncryption::benchmarkTransformRounds(int millisecs)
|
|||||||
{
|
{
|
||||||
QApplication::setOverrideCursor(Qt::BusyCursor);
|
QApplication::setOverrideCursor(Qt::BusyCursor);
|
||||||
m_ui->transformBenchmarkButton->setEnabled(false);
|
m_ui->transformBenchmarkButton->setEnabled(false);
|
||||||
m_ui->transformRoundsSpinBox->setFocus();
|
m_ui->transformRoundsSpinBox->setEnabled(false);
|
||||||
|
m_ui->transformRoundsSpinBox->clear();
|
||||||
|
|
||||||
// Create a new kdf with the current parameters
|
// Create a new kdf with the current parameters
|
||||||
auto kdf = KeePass2::uuidToKdf(QUuid(m_ui->kdfComboBox->currentData().toByteArray()));
|
auto kdf = KeePass2::uuidToKdf(m_ui->kdfComboBox->currentData().toUuid());
|
||||||
kdf->setRounds(m_ui->transformRoundsSpinBox->value());
|
kdf->setRounds(m_ui->transformRoundsSpinBox->value());
|
||||||
if (IS_ARGON2(kdf->uuid())) {
|
if (IS_ARGON2(kdf->uuid())) {
|
||||||
auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
|
auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
|
||||||
@ -361,23 +390,12 @@ void DatabaseSettingsWidgetEncryption::benchmarkTransformRounds(int millisecs)
|
|||||||
|
|
||||||
m_ui->transformRoundsSpinBox->setValue(rounds);
|
m_ui->transformRoundsSpinBox->setValue(rounds);
|
||||||
m_ui->transformBenchmarkButton->setEnabled(true);
|
m_ui->transformBenchmarkButton->setEnabled(true);
|
||||||
|
m_ui->transformRoundsSpinBox->setEnabled(true);
|
||||||
|
m_ui->transformRoundsSpinBox->setFocus();
|
||||||
m_ui->decryptionTimeSlider->setValue(millisecs / 100);
|
m_ui->decryptionTimeSlider->setValue(millisecs / 100);
|
||||||
QApplication::restoreOverrideCursor();
|
QApplication::restoreOverrideCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseSettingsWidgetEncryption::changeKdf(int index)
|
|
||||||
{
|
|
||||||
Q_ASSERT(m_db);
|
|
||||||
if (!m_db) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QUuid id(m_ui->kdfComboBox->itemData(index).toByteArray());
|
|
||||||
m_db->setKdf(KeePass2::uuidToKdf(id));
|
|
||||||
updateKdfFields();
|
|
||||||
benchmarkTransformRounds();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update memory spin box suffix on value change.
|
* Update memory spin box suffix on value change.
|
||||||
*/
|
*/
|
||||||
@ -403,31 +421,3 @@ void DatabaseSettingsWidgetEncryption::updateDecryptionTime(int value)
|
|||||||
{
|
{
|
||||||
m_ui->decryptionTimeValueLabel->setText(getTextualEncryptionTime(value * 100));
|
m_ui->decryptionTimeValueLabel->setText(getTextualEncryptionTime(value * 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseSettingsWidgetEncryption::updateFormatCompatibility(int index, bool retransform)
|
|
||||||
{
|
|
||||||
Q_ASSERT(m_db);
|
|
||||||
if (!m_db) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_ui->compatibilitySelection->currentIndex() != index) {
|
|
||||||
bool block = m_ui->compatibilitySelection->blockSignals(true);
|
|
||||||
m_ui->compatibilitySelection->setCurrentIndex(index);
|
|
||||||
m_ui->compatibilitySelection->blockSignals(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
QUuid kdfUuid(m_ui->compatibilitySelection->itemData(index).toByteArray());
|
|
||||||
if (retransform) {
|
|
||||||
auto kdf = KeePass2::uuidToKdf(kdfUuid);
|
|
||||||
m_db->setKdf(kdf);
|
|
||||||
|
|
||||||
if (IS_ARGON2(kdf->uuid())) {
|
|
||||||
auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
|
|
||||||
// Default to 64 MiB of memory and 2 threads
|
|
||||||
// these settings are safe for desktop and mobile devices
|
|
||||||
argon2Kdf->setMemory(1 << 16);
|
|
||||||
argon2Kdf->setParallelism(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -47,13 +47,10 @@ protected:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void benchmarkTransformRounds(int millisecs = Kdf::DEFAULT_ENCRYPTION_TIME);
|
void benchmarkTransformRounds(int millisecs = Kdf::DEFAULT_ENCRYPTION_TIME);
|
||||||
void changeKdf(int index);
|
|
||||||
void memoryChanged(int value);
|
void memoryChanged(int value);
|
||||||
void parallelismChanged(int value);
|
void parallelismChanged(int value);
|
||||||
void updateDecryptionTime(int value);
|
void updateDecryptionTime(int value);
|
||||||
void updateFormatCompatibility(int index, bool retransform = true);
|
void loadKdfAlgorithms();
|
||||||
void setupAlgorithmComboBox();
|
|
||||||
void setupKdfComboBox(bool enableKdbx3);
|
|
||||||
void loadKdfParameters();
|
void loadKdfParameters();
|
||||||
void updateKdfFields();
|
void updateKdfFields();
|
||||||
void markDirty();
|
void markDirty();
|
||||||
@ -71,7 +68,6 @@ private:
|
|||||||
|
|
||||||
bool m_isDirty = false;
|
bool m_isDirty = false;
|
||||||
bool m_initWithAdvanced = false;
|
bool m_initWithAdvanced = false;
|
||||||
bool m_formatCompatibilityDirty = false;
|
|
||||||
const QScopedPointer<Ui::DatabaseSettingsWidgetEncryption> m_ui;
|
const QScopedPointer<Ui::DatabaseSettingsWidgetEncryption> m_ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1590,24 +1590,74 @@ void TestGui::testDatabaseSettings()
|
|||||||
int autosaveDelayTestValue = 2;
|
int autosaveDelayTestValue = 2;
|
||||||
|
|
||||||
dbSettingsCategoryList->setCurrentCategory(1); // go into security category
|
dbSettingsCategoryList->setCurrentCategory(1); // go into security category
|
||||||
dbSettingsStackedWidget->findChild<QTabWidget*>()->setCurrentIndex(1); // go into encryption tab
|
auto securityTabWidget = dbSettingsStackedWidget->findChild<QTabWidget*>();
|
||||||
|
QCOMPARE(securityTabWidget->currentIndex(), 0);
|
||||||
|
|
||||||
auto encryptionSettings = dbSettingsDialog->findChild<QTabWidget*>("encryptionSettingsTabWidget");
|
// Interact with the password edit option
|
||||||
|
auto passwordEditWidget = securityTabWidget->findChild<PasswordEditWidget*>();
|
||||||
|
QVERIFY(passwordEditWidget);
|
||||||
|
auto editPasswordButton = passwordEditWidget->findChild<QPushButton*>("changeButton");
|
||||||
|
QVERIFY(editPasswordButton);
|
||||||
|
QVERIFY(editPasswordButton->isVisible());
|
||||||
|
QTest::mouseClick(editPasswordButton, Qt::LeftButton);
|
||||||
|
QApplication::processEvents();
|
||||||
|
auto passwordWidgets = dbSettingsDialog->findChildren<PasswordWidget*>();
|
||||||
|
QVERIFY(passwordWidgets.count() == 2);
|
||||||
|
QVERIFY(passwordWidgets[0]->isVisible());
|
||||||
|
passwordWidgets[0]->setText("b");
|
||||||
|
passwordWidgets[1]->setText("b");
|
||||||
|
|
||||||
|
// Cancel password change
|
||||||
|
auto cancelPasswordButton = passwordEditWidget->findChild<QPushButton*>("cancelButton");
|
||||||
|
QVERIFY(cancelPasswordButton);
|
||||||
|
QTest::mouseClick(cancelPasswordButton, Qt::LeftButton);
|
||||||
|
QApplication::processEvents();
|
||||||
|
QVERIFY(!passwordWidgets[0]->isVisible());
|
||||||
|
QVERIFY(editPasswordButton->isVisible());
|
||||||
|
|
||||||
|
// Switch to encryption tab and interact with various settings
|
||||||
|
securityTabWidget->setCurrentIndex(1);
|
||||||
|
QApplication::processEvents();
|
||||||
|
|
||||||
|
// Verify database is KDBX3
|
||||||
|
auto compatibilitySelection = securityTabWidget->findChild<QComboBox*>("compatibilitySelection");
|
||||||
|
QVERIFY(compatibilitySelection);
|
||||||
|
QVERIFY(compatibilitySelection->isEnabled());
|
||||||
|
QCOMPARE(compatibilitySelection->currentText(), QString("KDBX 3"));
|
||||||
|
|
||||||
|
// Verify advanced settings
|
||||||
|
auto encryptionSettings = securityTabWidget->findChild<QTabWidget*>("encryptionSettingsTabWidget");
|
||||||
auto advancedTab = encryptionSettings->findChild<QWidget*>("advancedTab");
|
auto advancedTab = encryptionSettings->findChild<QWidget*>("advancedTab");
|
||||||
encryptionSettings->setCurrentWidget(advancedTab);
|
encryptionSettings->setCurrentWidget(advancedTab);
|
||||||
|
|
||||||
QApplication::processEvents();
|
QApplication::processEvents();
|
||||||
|
|
||||||
|
// Verify KDF is AES KDBX3
|
||||||
|
auto kdfSelection = advancedTab->findChild<QComboBox*>("kdfComboBox");
|
||||||
|
QVERIFY(kdfSelection->isVisible());
|
||||||
|
QCOMPARE(kdfSelection->currentText(), QString("AES-KDF (KDBX 3)"));
|
||||||
|
|
||||||
auto transformRoundsSpinBox = advancedTab->findChild<QSpinBox*>("transformRoundsSpinBox");
|
auto transformRoundsSpinBox = advancedTab->findChild<QSpinBox*>("transformRoundsSpinBox");
|
||||||
QVERIFY(transformRoundsSpinBox);
|
QVERIFY(transformRoundsSpinBox);
|
||||||
QVERIFY(transformRoundsSpinBox->isVisible());
|
|
||||||
|
|
||||||
|
// Adjust compatibility to KDBX4 and wait for KDF to update
|
||||||
|
compatibilitySelection->setCurrentIndex(0);
|
||||||
|
QTRY_VERIFY(transformRoundsSpinBox->isEnabled());
|
||||||
|
QCOMPARE(compatibilitySelection->currentText().left(6), QString("KDBX 4"));
|
||||||
|
QCOMPARE(kdfSelection->currentText().left(7), QString("Argon2d"));
|
||||||
|
|
||||||
|
// Switch to AES KDBX4, change rounds, then accept
|
||||||
|
kdfSelection->setCurrentIndex(2);
|
||||||
|
QCOMPARE(kdfSelection->currentText(), QString("AES-KDF (KDBX 4)"));
|
||||||
transformRoundsSpinBox->setValue(123456);
|
transformRoundsSpinBox->setValue(123456);
|
||||||
QTest::keyClick(transformRoundsSpinBox, Qt::Key_Enter);
|
QTest::keyClick(transformRoundsSpinBox, Qt::Key_Enter);
|
||||||
QTRY_COMPARE(m_db->kdf()->rounds(), 123456);
|
QTRY_COMPARE(m_db->kdf()->rounds(), 123456);
|
||||||
|
QVERIFY(m_db->formatVersion() >= KeePass2::FILE_VERSION_4);
|
||||||
|
QCOMPARE(m_db->kdf()->uuid(), KeePass2::KDF_AES_KDBX4);
|
||||||
|
|
||||||
|
// Go back into database settings
|
||||||
|
triggerAction("actionDatabaseSettings");
|
||||||
|
|
||||||
// test disable and default values for maximum history items and size
|
// test disable and default values for maximum history items and size
|
||||||
triggerAction("actionDatabaseSettings");
|
|
||||||
auto* historyMaxItemsCheckBox = dbSettingsDialog->findChild<QCheckBox*>("historyMaxItemsCheckBox");
|
auto* historyMaxItemsCheckBox = dbSettingsDialog->findChild<QCheckBox*>("historyMaxItemsCheckBox");
|
||||||
auto* historyMaxItemsSpinBox = dbSettingsDialog->findChild<QSpinBox*>("historyMaxItemsSpinBox");
|
auto* historyMaxItemsSpinBox = dbSettingsDialog->findChild<QSpinBox*>("historyMaxItemsSpinBox");
|
||||||
auto* historyMaxSizeCheckBox = dbSettingsDialog->findChild<QCheckBox*>("historyMaxSizeCheckBox");
|
auto* historyMaxSizeCheckBox = dbSettingsDialog->findChild<QCheckBox*>("historyMaxSizeCheckBox");
|
||||||
|
Loading…
Reference in New Issue
Block a user