mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-24 06:49:46 -05:00
Add word case option to passphrase generator (#3172)
* Closes #1933 * Adds word case options for lower, UPPER, and Title Case to passphrase generation
This commit is contained in:
parent
2ee97ed191
commit
7ead8e7290
@ -510,7 +510,6 @@ QString BrowserSettings::generatePassword()
|
||||
m_passwordGenerator.setFlags(passwordGeneratorFlags());
|
||||
return m_passwordGenerator.generatePassword();
|
||||
} else {
|
||||
m_passPhraseGenerator.setDefaultWordList();
|
||||
m_passPhraseGenerator.setWordCount(passPhraseWordCount());
|
||||
m_passPhraseGenerator.setWordSeparator(passPhraseWordSeparator());
|
||||
return m_passPhraseGenerator.generatePassphrase();
|
||||
|
@ -73,8 +73,6 @@ int Diceware::execute(const QStringList& arguments)
|
||||
|
||||
if (!parser.value(wordlistFile).isEmpty()) {
|
||||
dicewareGenerator.setWordList(parser.value(wordlistFile));
|
||||
} else {
|
||||
dicewareGenerator.setDefaultWordList();
|
||||
}
|
||||
|
||||
if (!dicewareGenerator.isValid()) {
|
||||
|
@ -28,9 +28,11 @@ const char* PassphraseGenerator::DefaultSeparator = " ";
|
||||
const char* PassphraseGenerator::DefaultWordList = "eff_large.wordlist";
|
||||
|
||||
PassphraseGenerator::PassphraseGenerator()
|
||||
: m_wordCount(0)
|
||||
, m_separator(PassphraseGenerator::DefaultSeparator)
|
||||
: m_wordCount(DefaultWordCount)
|
||||
, m_wordCase(LOWERCASE)
|
||||
, m_separator(DefaultSeparator)
|
||||
{
|
||||
setDefaultWordList();
|
||||
}
|
||||
|
||||
double PassphraseGenerator::calculateEntropy(const QString& passphrase)
|
||||
@ -46,12 +48,12 @@ double PassphraseGenerator::calculateEntropy(const QString& passphrase)
|
||||
|
||||
void PassphraseGenerator::setWordCount(int wordCount)
|
||||
{
|
||||
if (wordCount > 0) {
|
||||
m_wordCount = wordCount;
|
||||
} else {
|
||||
// safe default if something goes wrong
|
||||
m_wordCount = DefaultWordCount;
|
||||
}
|
||||
m_wordCount = qMax(1, wordCount);
|
||||
}
|
||||
|
||||
void PassphraseGenerator::setWordCase(PassphraseWordCase wordCase)
|
||||
{
|
||||
m_wordCase = wordCase;
|
||||
}
|
||||
|
||||
void PassphraseGenerator::setWordList(const QString& path)
|
||||
@ -88,6 +90,7 @@ void PassphraseGenerator::setWordSeparator(const QString& separator)
|
||||
|
||||
QString PassphraseGenerator::generatePassphrase() const
|
||||
{
|
||||
QString tmpWord;
|
||||
Q_ASSERT(isValid());
|
||||
|
||||
// In case there was an error loading the wordlist
|
||||
@ -98,7 +101,22 @@ QString PassphraseGenerator::generatePassphrase() const
|
||||
QStringList words;
|
||||
for (int i = 0; i < m_wordCount; ++i) {
|
||||
int wordIndex = randomGen()->randomUInt(static_cast<quint32>(m_wordlist.length()));
|
||||
words.append(m_wordlist.at(wordIndex));
|
||||
tmpWord = m_wordlist.at(wordIndex);
|
||||
|
||||
// convert case
|
||||
switch (m_wordCase) {
|
||||
case UPPERCASE:
|
||||
tmpWord = tmpWord.toUpper();
|
||||
break;
|
||||
case TITLECASE:
|
||||
tmpWord = tmpWord.replace(0, 1, tmpWord.left(1).toUpper());
|
||||
break;
|
||||
case LOWERCASE:
|
||||
default:
|
||||
tmpWord = tmpWord.toLower();
|
||||
break;
|
||||
}
|
||||
words.append(tmpWord);
|
||||
}
|
||||
|
||||
return words.join(m_separator);
|
||||
|
@ -28,9 +28,17 @@ public:
|
||||
PassphraseGenerator();
|
||||
Q_DISABLE_COPY(PassphraseGenerator)
|
||||
|
||||
enum PassphraseWordCase
|
||||
{
|
||||
LOWERCASE,
|
||||
UPPERCASE,
|
||||
TITLECASE
|
||||
};
|
||||
|
||||
double calculateEntropy(const QString& passphrase);
|
||||
void setWordCount(int wordCount);
|
||||
void setWordList(const QString& path);
|
||||
void setWordCase(PassphraseWordCase wordCase);
|
||||
void setDefaultWordList();
|
||||
void setWordSeparator(const QString& separator);
|
||||
bool isValid() const;
|
||||
@ -43,6 +51,7 @@ public:
|
||||
|
||||
private:
|
||||
int m_wordCount;
|
||||
PassphraseWordCase m_wordCase;
|
||||
QString m_separator;
|
||||
QVector<QString> m_wordlist;
|
||||
};
|
||||
|
@ -60,6 +60,7 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
|
||||
connect(m_ui->comboBoxWordList, SIGNAL(currentIndexChanged(int)), SLOT(updateGenerator()));
|
||||
connect(m_ui->optionButtons, SIGNAL(buttonClicked(int)), SLOT(updateGenerator()));
|
||||
connect(m_ui->tabWidget, SIGNAL(currentChanged(int)), SLOT(updateGenerator()));
|
||||
connect(m_ui->wordCaseComboBox, SIGNAL(currentIndexChanged(int)), SLOT(updateGenerator()));
|
||||
|
||||
// set font size of password quality and entropy labels dynamically to 80% of
|
||||
// the default font size, but make it no smaller than 8pt
|
||||
@ -74,6 +75,11 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
|
||||
// set default separator to Space
|
||||
m_ui->editWordSeparator->setText(PassphraseGenerator::DefaultSeparator);
|
||||
|
||||
// add passphrase generator case options
|
||||
m_ui->wordCaseComboBox->addItem(tr("lower case"), PassphraseGenerator::LOWERCASE);
|
||||
m_ui->wordCaseComboBox->addItem(tr("UPPER CASE"), PassphraseGenerator::UPPERCASE);
|
||||
m_ui->wordCaseComboBox->addItem(tr("Title Case"), PassphraseGenerator::TITLECASE);
|
||||
|
||||
QDir path(filePath()->wordlistPath(""));
|
||||
QStringList files = path.entryList(QDir::Files);
|
||||
m_ui->comboBoxWordList->addItems(files);
|
||||
@ -85,7 +91,6 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
|
||||
m_ui->labelWordList->setVisible(false);
|
||||
}
|
||||
|
||||
m_dicewareGenerator->setDefaultWordList();
|
||||
loadSettings();
|
||||
reset();
|
||||
}
|
||||
@ -139,6 +144,7 @@ void PasswordGeneratorWidget::loadSettings()
|
||||
config()->get("generator/WordSeparator", PassphraseGenerator::DefaultSeparator).toString());
|
||||
m_ui->comboBoxWordList->setCurrentText(
|
||||
config()->get("generator/WordList", PassphraseGenerator::DefaultWordList).toString());
|
||||
m_ui->wordCaseComboBox->setCurrentIndex(config()->get("generator/WordCase", 0).toInt());
|
||||
|
||||
// Password or diceware?
|
||||
m_ui->tabWidget->setCurrentIndex(config()->get("generator/Type", 0).toInt());
|
||||
@ -174,6 +180,7 @@ void PasswordGeneratorWidget::saveSettings()
|
||||
config()->set("generator/WordCount", m_ui->spinBoxWordCount->value());
|
||||
config()->set("generator/WordSeparator", m_ui->editWordSeparator->text());
|
||||
config()->set("generator/WordList", m_ui->comboBoxWordList->currentText());
|
||||
config()->set("generator/WordCase", m_ui->wordCaseComboBox->currentIndex());
|
||||
|
||||
// Password or diceware?
|
||||
config()->set("generator/Type", m_ui->tabWidget->currentIndex());
|
||||
@ -556,6 +563,9 @@ void PasswordGeneratorWidget::updateGenerator()
|
||||
m_updatingSpinBox = false;
|
||||
}
|
||||
|
||||
m_dicewareGenerator->setWordCase(
|
||||
static_cast<PassphraseGenerator::PassphraseWordCase>(m_ui->wordCaseComboBox->currentData().toInt()));
|
||||
|
||||
m_ui->spinBoxWordCount->setMinimum(minWordCount);
|
||||
m_ui->sliderWordCount->setMinimum(minWordCount);
|
||||
|
||||
|
@ -927,16 +927,10 @@ QProgressBar::chunk {
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0" alignment="Qt::AlignRight">
|
||||
<widget class="QLabel" name="labelWordList">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<item row="3" column="0" alignment="Qt::AlignRight">
|
||||
<widget class="QLabel" name="wordCaseLabel">
|
||||
<property name="text">
|
||||
<string>Wordlist:</string>
|
||||
<string>Word Case:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -950,16 +944,39 @@ QProgressBar::chunk {
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" alignment="Qt::AlignRight">
|
||||
<widget class="QLabel" name="labelWordCount">
|
||||
<item row="2" column="0" alignment="Qt::AlignRight">
|
||||
<widget class="QLabel" name="labelWordSeparator">
|
||||
<property name="text">
|
||||
<string>Word Co&unt:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>spinBoxLength</cstring>
|
||||
<string>Word Separator:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" alignment="Qt::AlignRight">
|
||||
<widget class="QLabel" name="labelWordList">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Wordlist:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<property name="sizeConstraint">
|
||||
@ -1005,10 +1022,13 @@ QProgressBar::chunk {
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0" alignment="Qt::AlignRight">
|
||||
<widget class="QLabel" name="labelWordSeparator">
|
||||
<item row="1" column="0" alignment="Qt::AlignRight">
|
||||
<widget class="QLabel" name="labelWordCount">
|
||||
<property name="text">
|
||||
<string>Word Separator:</string>
|
||||
<string>Word Co&unt:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>spinBoxLength</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -1020,14 +1040,19 @@ QProgressBar::chunk {
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<spacer name="verticalSpacer_3">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QComboBox" name="wordCaseComboBox"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
@ -1037,6 +1062,8 @@ QProgressBar::chunk {
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -190,6 +190,9 @@ add_unit_test(NAME testmerge SOURCES TestMerge.cpp
|
||||
add_unit_test(NAME testpasswordgenerator SOURCES TestPasswordGenerator.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
|
||||
add_unit_test(NAME testpassphrasegenerator SOURCES TestPassphraseGenerator.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
|
||||
add_unit_test(NAME testtotp SOURCES TestTotp.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
|
||||
|
54
tests/TestPassphraseGenerator.cpp
Normal file
54
tests/TestPassphraseGenerator.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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 "TestPassphraseGenerator.h"
|
||||
#include "core/PassphraseGenerator.h"
|
||||
#include "crypto/Crypto.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QTest>
|
||||
|
||||
QTEST_GUILESS_MAIN(TestPassphraseGenerator)
|
||||
|
||||
void TestPassphraseGenerator::initTestCase()
|
||||
{
|
||||
QVERIFY(Crypto::init());
|
||||
}
|
||||
|
||||
void TestPassphraseGenerator::testWordCase()
|
||||
{
|
||||
PassphraseGenerator generator;
|
||||
generator.setWordSeparator(" ");
|
||||
QVERIFY(generator.isValid());
|
||||
|
||||
QString passphrase;
|
||||
passphrase = generator.generatePassphrase();
|
||||
QCOMPARE(passphrase, passphrase.toLower());
|
||||
|
||||
generator.setWordCase(PassphraseGenerator::LOWERCASE);
|
||||
passphrase = generator.generatePassphrase();
|
||||
QCOMPARE(passphrase, passphrase.toLower());
|
||||
|
||||
generator.setWordCase(PassphraseGenerator::UPPERCASE);
|
||||
passphrase = generator.generatePassphrase();
|
||||
QCOMPARE(passphrase, passphrase.toUpper());
|
||||
|
||||
generator.setWordCase(PassphraseGenerator::TITLECASE);
|
||||
passphrase = generator.generatePassphrase();
|
||||
QRegularExpression regex("^([A-Z][a-z]* ?)+$");
|
||||
QVERIFY(regex.match(passphrase).hasMatch());
|
||||
}
|
32
tests/TestPassphraseGenerator.h
Normal file
32
tests/TestPassphraseGenerator.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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 KEEPASSXC_TESTPASSPHRASEGENERATOR_H
|
||||
#define KEEPASSXC_TESTPASSPHRASEGENERATOR_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class TestPassphraseGenerator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void testWordCase();
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_TESTPASSPHRASEGENERATOR_H
|
Loading…
Reference in New Issue
Block a user