mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-24 23:09:44 -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());
|
m_passwordGenerator.setFlags(passwordGeneratorFlags());
|
||||||
return m_passwordGenerator.generatePassword();
|
return m_passwordGenerator.generatePassword();
|
||||||
} else {
|
} else {
|
||||||
m_passPhraseGenerator.setDefaultWordList();
|
|
||||||
m_passPhraseGenerator.setWordCount(passPhraseWordCount());
|
m_passPhraseGenerator.setWordCount(passPhraseWordCount());
|
||||||
m_passPhraseGenerator.setWordSeparator(passPhraseWordSeparator());
|
m_passPhraseGenerator.setWordSeparator(passPhraseWordSeparator());
|
||||||
return m_passPhraseGenerator.generatePassphrase();
|
return m_passPhraseGenerator.generatePassphrase();
|
||||||
|
@ -73,8 +73,6 @@ int Diceware::execute(const QStringList& arguments)
|
|||||||
|
|
||||||
if (!parser.value(wordlistFile).isEmpty()) {
|
if (!parser.value(wordlistFile).isEmpty()) {
|
||||||
dicewareGenerator.setWordList(parser.value(wordlistFile));
|
dicewareGenerator.setWordList(parser.value(wordlistFile));
|
||||||
} else {
|
|
||||||
dicewareGenerator.setDefaultWordList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dicewareGenerator.isValid()) {
|
if (!dicewareGenerator.isValid()) {
|
||||||
|
@ -28,9 +28,11 @@ const char* PassphraseGenerator::DefaultSeparator = " ";
|
|||||||
const char* PassphraseGenerator::DefaultWordList = "eff_large.wordlist";
|
const char* PassphraseGenerator::DefaultWordList = "eff_large.wordlist";
|
||||||
|
|
||||||
PassphraseGenerator::PassphraseGenerator()
|
PassphraseGenerator::PassphraseGenerator()
|
||||||
: m_wordCount(0)
|
: m_wordCount(DefaultWordCount)
|
||||||
, m_separator(PassphraseGenerator::DefaultSeparator)
|
, m_wordCase(LOWERCASE)
|
||||||
|
, m_separator(DefaultSeparator)
|
||||||
{
|
{
|
||||||
|
setDefaultWordList();
|
||||||
}
|
}
|
||||||
|
|
||||||
double PassphraseGenerator::calculateEntropy(const QString& passphrase)
|
double PassphraseGenerator::calculateEntropy(const QString& passphrase)
|
||||||
@ -46,12 +48,12 @@ double PassphraseGenerator::calculateEntropy(const QString& passphrase)
|
|||||||
|
|
||||||
void PassphraseGenerator::setWordCount(int wordCount)
|
void PassphraseGenerator::setWordCount(int wordCount)
|
||||||
{
|
{
|
||||||
if (wordCount > 0) {
|
m_wordCount = qMax(1, wordCount);
|
||||||
m_wordCount = wordCount;
|
}
|
||||||
} else {
|
|
||||||
// safe default if something goes wrong
|
void PassphraseGenerator::setWordCase(PassphraseWordCase wordCase)
|
||||||
m_wordCount = DefaultWordCount;
|
{
|
||||||
}
|
m_wordCase = wordCase;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PassphraseGenerator::setWordList(const QString& path)
|
void PassphraseGenerator::setWordList(const QString& path)
|
||||||
@ -88,6 +90,7 @@ void PassphraseGenerator::setWordSeparator(const QString& separator)
|
|||||||
|
|
||||||
QString PassphraseGenerator::generatePassphrase() const
|
QString PassphraseGenerator::generatePassphrase() const
|
||||||
{
|
{
|
||||||
|
QString tmpWord;
|
||||||
Q_ASSERT(isValid());
|
Q_ASSERT(isValid());
|
||||||
|
|
||||||
// In case there was an error loading the wordlist
|
// In case there was an error loading the wordlist
|
||||||
@ -98,7 +101,22 @@ QString PassphraseGenerator::generatePassphrase() const
|
|||||||
QStringList words;
|
QStringList words;
|
||||||
for (int i = 0; i < m_wordCount; ++i) {
|
for (int i = 0; i < m_wordCount; ++i) {
|
||||||
int wordIndex = randomGen()->randomUInt(static_cast<quint32>(m_wordlist.length()));
|
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);
|
return words.join(m_separator);
|
||||||
|
@ -28,9 +28,17 @@ public:
|
|||||||
PassphraseGenerator();
|
PassphraseGenerator();
|
||||||
Q_DISABLE_COPY(PassphraseGenerator)
|
Q_DISABLE_COPY(PassphraseGenerator)
|
||||||
|
|
||||||
|
enum PassphraseWordCase
|
||||||
|
{
|
||||||
|
LOWERCASE,
|
||||||
|
UPPERCASE,
|
||||||
|
TITLECASE
|
||||||
|
};
|
||||||
|
|
||||||
double calculateEntropy(const QString& passphrase);
|
double calculateEntropy(const QString& passphrase);
|
||||||
void setWordCount(int wordCount);
|
void setWordCount(int wordCount);
|
||||||
void setWordList(const QString& path);
|
void setWordList(const QString& path);
|
||||||
|
void setWordCase(PassphraseWordCase wordCase);
|
||||||
void setDefaultWordList();
|
void setDefaultWordList();
|
||||||
void setWordSeparator(const QString& separator);
|
void setWordSeparator(const QString& separator);
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
@ -43,6 +51,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
int m_wordCount;
|
int m_wordCount;
|
||||||
|
PassphraseWordCase m_wordCase;
|
||||||
QString m_separator;
|
QString m_separator;
|
||||||
QVector<QString> m_wordlist;
|
QVector<QString> m_wordlist;
|
||||||
};
|
};
|
||||||
|
@ -60,6 +60,7 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
|
|||||||
connect(m_ui->comboBoxWordList, SIGNAL(currentIndexChanged(int)), SLOT(updateGenerator()));
|
connect(m_ui->comboBoxWordList, SIGNAL(currentIndexChanged(int)), SLOT(updateGenerator()));
|
||||||
connect(m_ui->optionButtons, SIGNAL(buttonClicked(int)), SLOT(updateGenerator()));
|
connect(m_ui->optionButtons, SIGNAL(buttonClicked(int)), SLOT(updateGenerator()));
|
||||||
connect(m_ui->tabWidget, SIGNAL(currentChanged(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
|
// set font size of password quality and entropy labels dynamically to 80% of
|
||||||
// the default font size, but make it no smaller than 8pt
|
// the default font size, but make it no smaller than 8pt
|
||||||
@ -74,6 +75,11 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
|
|||||||
// set default separator to Space
|
// set default separator to Space
|
||||||
m_ui->editWordSeparator->setText(PassphraseGenerator::DefaultSeparator);
|
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(""));
|
QDir path(filePath()->wordlistPath(""));
|
||||||
QStringList files = path.entryList(QDir::Files);
|
QStringList files = path.entryList(QDir::Files);
|
||||||
m_ui->comboBoxWordList->addItems(files);
|
m_ui->comboBoxWordList->addItems(files);
|
||||||
@ -85,7 +91,6 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
|
|||||||
m_ui->labelWordList->setVisible(false);
|
m_ui->labelWordList->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_dicewareGenerator->setDefaultWordList();
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
@ -139,6 +144,7 @@ void PasswordGeneratorWidget::loadSettings()
|
|||||||
config()->get("generator/WordSeparator", PassphraseGenerator::DefaultSeparator).toString());
|
config()->get("generator/WordSeparator", PassphraseGenerator::DefaultSeparator).toString());
|
||||||
m_ui->comboBoxWordList->setCurrentText(
|
m_ui->comboBoxWordList->setCurrentText(
|
||||||
config()->get("generator/WordList", PassphraseGenerator::DefaultWordList).toString());
|
config()->get("generator/WordList", PassphraseGenerator::DefaultWordList).toString());
|
||||||
|
m_ui->wordCaseComboBox->setCurrentIndex(config()->get("generator/WordCase", 0).toInt());
|
||||||
|
|
||||||
// Password or diceware?
|
// Password or diceware?
|
||||||
m_ui->tabWidget->setCurrentIndex(config()->get("generator/Type", 0).toInt());
|
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/WordCount", m_ui->spinBoxWordCount->value());
|
||||||
config()->set("generator/WordSeparator", m_ui->editWordSeparator->text());
|
config()->set("generator/WordSeparator", m_ui->editWordSeparator->text());
|
||||||
config()->set("generator/WordList", m_ui->comboBoxWordList->currentText());
|
config()->set("generator/WordList", m_ui->comboBoxWordList->currentText());
|
||||||
|
config()->set("generator/WordCase", m_ui->wordCaseComboBox->currentIndex());
|
||||||
|
|
||||||
// Password or diceware?
|
// Password or diceware?
|
||||||
config()->set("generator/Type", m_ui->tabWidget->currentIndex());
|
config()->set("generator/Type", m_ui->tabWidget->currentIndex());
|
||||||
@ -556,6 +563,9 @@ void PasswordGeneratorWidget::updateGenerator()
|
|||||||
m_updatingSpinBox = false;
|
m_updatingSpinBox = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_dicewareGenerator->setWordCase(
|
||||||
|
static_cast<PassphraseGenerator::PassphraseWordCase>(m_ui->wordCaseComboBox->currentData().toInt()));
|
||||||
|
|
||||||
m_ui->spinBoxWordCount->setMinimum(minWordCount);
|
m_ui->spinBoxWordCount->setMinimum(minWordCount);
|
||||||
m_ui->sliderWordCount->setMinimum(minWordCount);
|
m_ui->sliderWordCount->setMinimum(minWordCount);
|
||||||
|
|
||||||
|
@ -927,16 +927,10 @@ QProgressBar::chunk {
|
|||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout_3">
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
<item row="0" column="0" alignment="Qt::AlignRight">
|
<item row="3" column="0" alignment="Qt::AlignRight">
|
||||||
<widget class="QLabel" name="labelWordList">
|
<widget class="QLabel" name="wordCaseLabel">
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Wordlist:</string>
|
<string>Word Case:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -950,16 +944,39 @@ QProgressBar::chunk {
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0" alignment="Qt::AlignRight">
|
<item row="2" column="0" alignment="Qt::AlignRight">
|
||||||
<widget class="QLabel" name="labelWordCount">
|
<widget class="QLabel" name="labelWordSeparator">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Word Co&unt:</string>
|
<string>Word Separator:</string>
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>spinBoxLength</cstring>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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">
|
<item row="1" column="1">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
<property name="sizeConstraint">
|
<property name="sizeConstraint">
|
||||||
@ -1005,10 +1022,13 @@ QProgressBar::chunk {
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0" alignment="Qt::AlignRight">
|
<item row="1" column="0" alignment="Qt::AlignRight">
|
||||||
<widget class="QLabel" name="labelWordSeparator">
|
<widget class="QLabel" name="labelWordCount">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Word Separator:</string>
|
<string>Word Co&unt:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>spinBoxLength</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -1020,17 +1040,24 @@ QProgressBar::chunk {
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="3" column="1">
|
||||||
<spacer name="verticalSpacer_3">
|
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||||
<property name="orientation">
|
<item>
|
||||||
<enum>Qt::Vertical</enum>
|
<widget class="QComboBox" name="wordCaseComboBox"/>
|
||||||
</property>
|
</item>
|
||||||
<property name="sizeHint" stdset="0">
|
<item>
|
||||||
<size>
|
<spacer name="horizontalSpacer_4">
|
||||||
<width>20</width>
|
<property name="orientation">
|
||||||
<height>40</height>
|
<enum>Qt::Horizontal</enum>
|
||||||
</size>
|
</property>
|
||||||
</property>
|
<property name="sizeHint" stdset="0">
|
||||||
</spacer>
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
@ -190,6 +190,9 @@ add_unit_test(NAME testmerge SOURCES TestMerge.cpp
|
|||||||
add_unit_test(NAME testpasswordgenerator SOURCES TestPasswordGenerator.cpp
|
add_unit_test(NAME testpasswordgenerator SOURCES TestPasswordGenerator.cpp
|
||||||
LIBS ${TEST_LIBRARIES})
|
LIBS ${TEST_LIBRARIES})
|
||||||
|
|
||||||
|
add_unit_test(NAME testpassphrasegenerator SOURCES TestPassphraseGenerator.cpp
|
||||||
|
LIBS ${TEST_LIBRARIES})
|
||||||
|
|
||||||
add_unit_test(NAME testtotp SOURCES TestTotp.cpp
|
add_unit_test(NAME testtotp SOURCES TestTotp.cpp
|
||||||
LIBS ${TEST_LIBRARIES})
|
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