mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-04-03 20:47:37 +03:00
Add -i/--include option to "generate" CLI command. (#7112)
This commit is contained in:
parent
b3896f2600
commit
a0a063b57f
9 changed files with 359 additions and 213 deletions
|
@ -7557,6 +7557,10 @@ Please consider generating a new key file.</source>
|
||||||
<source>AES-KDF (KDBX 3)</source>
|
<source>AES-KDF (KDBX 3)</source>
|
||||||
<translation type="unfinished">AES-KDF (KDBX 3.1) {3)?}</translation>
|
<translation type="unfinished">AES-KDF (KDBX 3.1) {3)?}</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use custom character set</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>QtIOCompressor</name>
|
<name>QtIOCompressor</name>
|
||||||
|
|
|
@ -53,6 +53,12 @@ const QCommandLineOption Generate::ExcludeCharsOption = QCommandLineOption(QStri
|
||||||
QObject::tr("Exclude character set"),
|
QObject::tr("Exclude character set"),
|
||||||
QObject::tr("chars"));
|
QObject::tr("chars"));
|
||||||
|
|
||||||
|
const QCommandLineOption Generate::CustomCharacterSetOption =
|
||||||
|
QCommandLineOption(QStringList() << "c"
|
||||||
|
<< "custom",
|
||||||
|
QObject::tr("Use custom character set"),
|
||||||
|
QObject::tr("chars"));
|
||||||
|
|
||||||
const QCommandLineOption Generate::ExcludeSimilarCharsOption =
|
const QCommandLineOption Generate::ExcludeSimilarCharsOption =
|
||||||
QCommandLineOption(QStringList() << "exclude-similar", QObject::tr("Exclude similar looking characters"));
|
QCommandLineOption(QStringList() << "exclude-similar", QObject::tr("Exclude similar looking characters"));
|
||||||
|
|
||||||
|
@ -71,6 +77,7 @@ Generate::Generate()
|
||||||
options.append(Generate::ExcludeCharsOption);
|
options.append(Generate::ExcludeCharsOption);
|
||||||
options.append(Generate::ExcludeSimilarCharsOption);
|
options.append(Generate::ExcludeSimilarCharsOption);
|
||||||
options.append(Generate::IncludeEveryGroupOption);
|
options.append(Generate::IncludeEveryGroupOption);
|
||||||
|
options.append(Generate::CustomCharacterSetOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,9 +127,15 @@ QSharedPointer<PasswordGenerator> Generate::createGenerator(QSharedPointer<QComm
|
||||||
|
|
||||||
// The default charset will be used if no explicit class
|
// The default charset will be used if no explicit class
|
||||||
// option was set.
|
// option was set.
|
||||||
passwordGenerator->setCharClasses(classes);
|
if (flags != 0x0) {
|
||||||
passwordGenerator->setFlags(flags);
|
passwordGenerator->setFlags(flags);
|
||||||
passwordGenerator->setExcludedChars(parser->value(Generate::ExcludeCharsOption));
|
}
|
||||||
|
QString customCharacterSet = parser->value(Generate::CustomCharacterSetOption);
|
||||||
|
if (classes != 0x0 || !customCharacterSet.isNull()) {
|
||||||
|
passwordGenerator->setCharClasses(classes);
|
||||||
|
}
|
||||||
|
passwordGenerator->setCustomCharacterSet(customCharacterSet);
|
||||||
|
passwordGenerator->setExcludedCharacterSet(parser->value(Generate::ExcludeCharsOption));
|
||||||
|
|
||||||
if (!passwordGenerator->isValid()) {
|
if (!passwordGenerator->isValid()) {
|
||||||
err << QObject::tr("Invalid password generator after applying all options") << endl;
|
err << QObject::tr("Invalid password generator after applying all options") << endl;
|
||||||
|
|
|
@ -39,6 +39,7 @@ public:
|
||||||
static const QCommandLineOption ExcludeCharsOption;
|
static const QCommandLineOption ExcludeCharsOption;
|
||||||
static const QCommandLineOption ExcludeSimilarCharsOption;
|
static const QCommandLineOption ExcludeSimilarCharsOption;
|
||||||
static const QCommandLineOption IncludeEveryGroupOption;
|
static const QCommandLineOption IncludeEveryGroupOption;
|
||||||
|
static const QCommandLineOption CustomCharacterSetOption;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSXC_GENERATE_H
|
#endif // KEEPASSXC_GENERATE_H
|
||||||
|
|
|
@ -20,51 +20,43 @@
|
||||||
|
|
||||||
#include "crypto/Random.h"
|
#include "crypto/Random.h"
|
||||||
|
|
||||||
const char* PasswordGenerator::DefaultAdditionalChars = "";
|
const int PasswordGenerator::DefaultLength = 32;
|
||||||
|
const char* PasswordGenerator::DefaultCustomCharacterSet = "";
|
||||||
const char* PasswordGenerator::DefaultExcludedChars = "";
|
const char* PasswordGenerator::DefaultExcludedChars = "";
|
||||||
|
|
||||||
PasswordGenerator::PasswordGenerator()
|
PasswordGenerator::PasswordGenerator()
|
||||||
: m_length(0)
|
: m_length(PasswordGenerator::DefaultLength)
|
||||||
, m_classes(nullptr)
|
, m_classes(PasswordGenerator::CharClass::DefaultCharset)
|
||||||
, m_flags(nullptr)
|
, m_flags(PasswordGenerator::GeneratorFlag::DefaultFlags)
|
||||||
, m_additional(PasswordGenerator::DefaultAdditionalChars)
|
, m_custom(PasswordGenerator::DefaultCustomCharacterSet)
|
||||||
, m_excluded(PasswordGenerator::DefaultExcludedChars)
|
, m_excluded(PasswordGenerator::DefaultExcludedChars)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void PasswordGenerator::setLength(int length)
|
void PasswordGenerator::setLength(int length)
|
||||||
{
|
{
|
||||||
if (length <= 0) {
|
|
||||||
m_length = DefaultLength;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_length = length;
|
m_length = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PasswordGenerator::setCharClasses(const CharClasses& classes)
|
void PasswordGenerator::setCharClasses(const PasswordGenerator::CharClasses& classes)
|
||||||
{
|
{
|
||||||
if (classes == 0) {
|
|
||||||
m_classes = DefaultCharset;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_classes = classes;
|
m_classes = classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PasswordGenerator::setCustomCharacterSet(const QString& customCharacterSet)
|
||||||
|
{
|
||||||
|
m_custom = customCharacterSet;
|
||||||
|
}
|
||||||
|
void PasswordGenerator::setExcludedCharacterSet(const QString& excludedCharacterSet)
|
||||||
|
{
|
||||||
|
m_excluded = excludedCharacterSet;
|
||||||
|
}
|
||||||
|
|
||||||
void PasswordGenerator::setFlags(const GeneratorFlags& flags)
|
void PasswordGenerator::setFlags(const GeneratorFlags& flags)
|
||||||
{
|
{
|
||||||
m_flags = flags;
|
m_flags = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PasswordGenerator::setAdditionalChars(const QString& chars)
|
|
||||||
{
|
|
||||||
m_additional = chars;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PasswordGenerator::setExcludedChars(const QString& chars)
|
|
||||||
{
|
|
||||||
m_excluded = chars;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString PasswordGenerator::generatePassword() const
|
QString PasswordGenerator::generatePassword() const
|
||||||
{
|
{
|
||||||
Q_ASSERT(isValid());
|
Q_ASSERT(isValid());
|
||||||
|
@ -114,9 +106,9 @@ QString PasswordGenerator::generatePassword() const
|
||||||
|
|
||||||
bool PasswordGenerator::isValid() const
|
bool PasswordGenerator::isValid() const
|
||||||
{
|
{
|
||||||
if (m_classes == 0 && m_additional.isEmpty()) {
|
if (m_classes == CharClass::NoClass && m_custom.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
} else if (m_length == 0) {
|
} else if (m_length <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,10 +258,10 @@ QVector<PasswordGroup> PasswordGenerator::passwordGroups() const
|
||||||
|
|
||||||
passwordGroups.append(group);
|
passwordGroups.append(group);
|
||||||
}
|
}
|
||||||
if (!m_additional.isEmpty()) {
|
if (!m_custom.isEmpty()) {
|
||||||
PasswordGroup group;
|
PasswordGroup group;
|
||||||
|
|
||||||
for (auto ch : m_additional) {
|
for (auto ch : m_custom) {
|
||||||
group.append(ch);
|
group.append(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,38 +294,43 @@ QVector<PasswordGroup> PasswordGenerator::passwordGroups() const
|
||||||
|
|
||||||
int PasswordGenerator::numCharClasses() const
|
int PasswordGenerator::numCharClasses() const
|
||||||
{
|
{
|
||||||
int numClasses = 0;
|
// Actually compute the non empty password groups
|
||||||
|
auto non_empty_groups = passwordGroups();
|
||||||
if (m_classes & LowerLetters) {
|
return non_empty_groups.size();
|
||||||
numClasses++;
|
|
||||||
}
|
|
||||||
if (m_classes & UpperLetters) {
|
|
||||||
numClasses++;
|
|
||||||
}
|
|
||||||
if (m_classes & Numbers) {
|
|
||||||
numClasses++;
|
|
||||||
}
|
|
||||||
if (m_classes & Braces) {
|
|
||||||
numClasses++;
|
|
||||||
}
|
|
||||||
if (m_classes & Punctuation) {
|
|
||||||
numClasses++;
|
|
||||||
}
|
|
||||||
if (m_classes & Quotes) {
|
|
||||||
numClasses++;
|
|
||||||
}
|
|
||||||
if (m_classes & Dashes) {
|
|
||||||
numClasses++;
|
|
||||||
}
|
|
||||||
if (m_classes & Math) {
|
|
||||||
numClasses++;
|
|
||||||
}
|
|
||||||
if (m_classes & Logograms) {
|
|
||||||
numClasses++;
|
|
||||||
}
|
|
||||||
if (m_classes & EASCII) {
|
|
||||||
numClasses++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return numClasses;
|
int PasswordGenerator::getMinLength() const
|
||||||
|
{
|
||||||
|
if ((m_flags & CharFromEveryGroup)) {
|
||||||
|
return numCharClasses();
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
void PasswordGenerator::reset()
|
||||||
|
{
|
||||||
|
m_classes = CharClass::DefaultCharset;
|
||||||
|
m_flags = GeneratorFlag::DefaultFlags;
|
||||||
|
m_custom = DefaultCustomCharacterSet;
|
||||||
|
m_excluded = DefaultExcludedChars;
|
||||||
|
m_length = DefaultLength;
|
||||||
|
}
|
||||||
|
int PasswordGenerator::getLength() const
|
||||||
|
{
|
||||||
|
return m_length;
|
||||||
|
}
|
||||||
|
const PasswordGenerator::GeneratorFlags& PasswordGenerator::getFlags() const
|
||||||
|
{
|
||||||
|
return m_flags;
|
||||||
|
}
|
||||||
|
const PasswordGenerator::CharClasses& PasswordGenerator::getActiveClasses() const
|
||||||
|
{
|
||||||
|
return m_classes;
|
||||||
|
}
|
||||||
|
const QString& PasswordGenerator::getCustomCharacterSet() const
|
||||||
|
{
|
||||||
|
return m_custom;
|
||||||
|
}
|
||||||
|
const QString& PasswordGenerator::getExcludedCharacterSet() const
|
||||||
|
{
|
||||||
|
return m_excluded;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#ifndef KEEPASSX_PASSWORDGENERATOR_H
|
#ifndef KEEPASSX_PASSWORDGENERATOR_H
|
||||||
#define KEEPASSX_PASSWORDGENERATOR_H
|
#define KEEPASSX_PASSWORDGENERATOR_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
typedef QVector<QChar> PasswordGroup;
|
typedef QVector<QChar> PasswordGroup;
|
||||||
|
@ -28,6 +29,7 @@ class PasswordGenerator
|
||||||
public:
|
public:
|
||||||
enum CharClass
|
enum CharClass
|
||||||
{
|
{
|
||||||
|
NoClass = 0,
|
||||||
LowerLetters = (1 << 0),
|
LowerLetters = (1 << 0),
|
||||||
UpperLetters = (1 << 1),
|
UpperLetters = (1 << 1),
|
||||||
Numbers = (1 << 2),
|
Numbers = (1 << 2),
|
||||||
|
@ -41,10 +43,11 @@ public:
|
||||||
EASCII = (1 << 9),
|
EASCII = (1 << 9),
|
||||||
DefaultCharset = LowerLetters | UpperLetters | Numbers
|
DefaultCharset = LowerLetters | UpperLetters | Numbers
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(CharClasses, CharClass)
|
Q_DECLARE_FLAGS(CharClasses, CharClass);
|
||||||
|
|
||||||
enum GeneratorFlag
|
enum GeneratorFlag
|
||||||
{
|
{
|
||||||
|
NoFlags = 0,
|
||||||
ExcludeLookAlike = (1 << 0),
|
ExcludeLookAlike = (1 << 0),
|
||||||
CharFromEveryGroup = (1 << 1),
|
CharFromEveryGroup = (1 << 1),
|
||||||
AdvancedMode = (1 << 2),
|
AdvancedMode = (1 << 2),
|
||||||
|
@ -56,17 +59,25 @@ public:
|
||||||
PasswordGenerator();
|
PasswordGenerator();
|
||||||
|
|
||||||
void setLength(int length);
|
void setLength(int length);
|
||||||
void setCharClasses(const CharClasses& classes);
|
|
||||||
void setFlags(const GeneratorFlags& flags);
|
void setFlags(const GeneratorFlags& flags);
|
||||||
void setAdditionalChars(const QString& chars);
|
void setCharClasses(const CharClasses& classes);
|
||||||
void setExcludedChars(const QString& chars);
|
void setCustomCharacterSet(const QString& customCharacterSet);
|
||||||
|
void setExcludedCharacterSet(const QString& excludedCharacterSet);
|
||||||
|
void reset();
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
int getMinLength() const;
|
||||||
|
|
||||||
|
int getLength() const;
|
||||||
|
const GeneratorFlags& getFlags() const;
|
||||||
|
const CharClasses& getActiveClasses() const;
|
||||||
|
const QString& getCustomCharacterSet() const;
|
||||||
|
const QString& getExcludedCharacterSet() const;
|
||||||
|
|
||||||
QString generatePassword() const;
|
QString generatePassword() const;
|
||||||
|
|
||||||
static const int DefaultLength = 32;
|
static const int DefaultLength;
|
||||||
static const char* DefaultAdditionalChars;
|
static const char* DefaultCustomCharacterSet;
|
||||||
static const char* DefaultExcludedChars;
|
static const char* DefaultExcludedChars;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -76,10 +87,8 @@ private:
|
||||||
int m_length;
|
int m_length;
|
||||||
CharClasses m_classes;
|
CharClasses m_classes;
|
||||||
GeneratorFlags m_flags;
|
GeneratorFlags m_flags;
|
||||||
QString m_additional;
|
QString m_custom;
|
||||||
QString m_excluded;
|
QString m_excluded;
|
||||||
|
|
||||||
Q_DISABLE_COPY(PasswordGenerator)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(PasswordGenerator::CharClasses)
|
Q_DECLARE_OPERATORS_FOR_FLAGS(PasswordGenerator::CharClasses)
|
||||||
|
|
|
@ -576,51 +576,15 @@ void PasswordGeneratorWidget::updateGenerator()
|
||||||
auto classes = charClasses();
|
auto classes = charClasses();
|
||||||
auto flags = generatorFlags();
|
auto flags = generatorFlags();
|
||||||
|
|
||||||
int length = 0;
|
m_passwordGenerator->setLength(m_ui->spinBoxLength->value());
|
||||||
if (flags.testFlag(PasswordGenerator::CharFromEveryGroup)) {
|
|
||||||
if (classes.testFlag(PasswordGenerator::LowerLetters)) {
|
|
||||||
++length;
|
|
||||||
}
|
|
||||||
if (classes.testFlag(PasswordGenerator::UpperLetters)) {
|
|
||||||
++length;
|
|
||||||
}
|
|
||||||
if (classes.testFlag(PasswordGenerator::Numbers)) {
|
|
||||||
++length;
|
|
||||||
}
|
|
||||||
if (classes.testFlag(PasswordGenerator::Braces)) {
|
|
||||||
++length;
|
|
||||||
}
|
|
||||||
if (classes.testFlag(PasswordGenerator::Punctuation)) {
|
|
||||||
++length;
|
|
||||||
}
|
|
||||||
if (classes.testFlag(PasswordGenerator::Quotes)) {
|
|
||||||
++length;
|
|
||||||
}
|
|
||||||
if (classes.testFlag(PasswordGenerator::Dashes)) {
|
|
||||||
++length;
|
|
||||||
}
|
|
||||||
if (classes.testFlag(PasswordGenerator::Math)) {
|
|
||||||
++length;
|
|
||||||
}
|
|
||||||
if (classes.testFlag(PasswordGenerator::Logograms)) {
|
|
||||||
++length;
|
|
||||||
}
|
|
||||||
if (classes.testFlag(PasswordGenerator::EASCII)) {
|
|
||||||
++length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
length = qMax(length, m_ui->spinBoxLength->value());
|
|
||||||
m_passwordGenerator->setLength(length);
|
|
||||||
m_passwordGenerator->setCharClasses(classes);
|
|
||||||
m_passwordGenerator->setFlags(flags);
|
|
||||||
if (m_ui->buttonAdvancedMode->isChecked()) {
|
if (m_ui->buttonAdvancedMode->isChecked()) {
|
||||||
m_passwordGenerator->setAdditionalChars(m_ui->editAdditionalChars->text());
|
m_passwordGenerator->setCharClasses(classes);
|
||||||
m_passwordGenerator->setExcludedChars(m_ui->editExcludedChars->text());
|
m_passwordGenerator->setCustomCharacterSet(m_ui->editAdditionalChars->text());
|
||||||
|
m_passwordGenerator->setCustomCharacterSet(m_ui->editExcludedChars->text());
|
||||||
} else {
|
} else {
|
||||||
m_passwordGenerator->setAdditionalChars("");
|
m_passwordGenerator->setCharClasses(classes);
|
||||||
m_passwordGenerator->setExcludedChars("");
|
|
||||||
}
|
}
|
||||||
|
m_passwordGenerator->setFlags(flags);
|
||||||
|
|
||||||
if (m_passwordGenerator->isValid()) {
|
if (m_passwordGenerator->isValid()) {
|
||||||
m_ui->buttonGenerate->setEnabled(true);
|
m_ui->buttonGenerate->setEnabled(true);
|
||||||
|
|
|
@ -1195,6 +1195,10 @@ void TestCli::testGenerate_data()
|
||||||
<< QStringList{"generate", "-L", "2", "--upper", "-l", "--every-group"} << "^[a-z][A-Z]|[A-Z][a-z]$";
|
<< QStringList{"generate", "-L", "2", "--upper", "-l", "--every-group"} << "^[a-z][A-Z]|[A-Z][a-z]$";
|
||||||
QTest::newRow("numbers + lowercase (every)")
|
QTest::newRow("numbers + lowercase (every)")
|
||||||
<< QStringList{"generate", "-L", "2", "-n", "-l", "--every-group"} << "^[a-z][0-9]|[0-9][a-z]$";
|
<< QStringList{"generate", "-L", "2", "-n", "-l", "--every-group"} << "^[a-z][0-9]|[0-9][a-z]$";
|
||||||
|
QTest::newRow("custom character set")
|
||||||
|
<< QStringList{"generate", "-L", "200", "-n", "-c", "abc"} << "^[abc0-9]{200}$";
|
||||||
|
QTest::newRow("custom character set without extra options uses only custom chars")
|
||||||
|
<< QStringList{"generate", "-L", "200", "-c", "a"} << "^a{200}$";
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestCli::testGenerate()
|
void TestCli::testGenerate()
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "TestPasswordGenerator.h"
|
#include "TestPasswordGenerator.h"
|
||||||
#include "core/PasswordGenerator.h"
|
|
||||||
#include "crypto/Crypto.h"
|
#include "crypto/Crypto.h"
|
||||||
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
@ -24,120 +23,261 @@
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN(TestPasswordGenerator)
|
QTEST_GUILESS_MAIN(TestPasswordGenerator)
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(PasswordGenerator::CharClasses)
|
||||||
|
Q_DECLARE_METATYPE(PasswordGenerator::GeneratorFlags)
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
PasswordGenerator::CharClasses to_flags(PasswordGenerator::CharClass x)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
PasswordGenerator::GeneratorFlags to_flags(PasswordGenerator::GeneratorFlag x)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void TestPasswordGenerator::initTestCase()
|
void TestPasswordGenerator::initTestCase()
|
||||||
{
|
{
|
||||||
QVERIFY(Crypto::init());
|
QVERIFY(Crypto::init());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestPasswordGenerator::testAdditionalChars()
|
void TestPasswordGenerator::init()
|
||||||
{
|
{
|
||||||
PasswordGenerator generator;
|
m_generator.reset();
|
||||||
QVERIFY(!generator.isValid());
|
}
|
||||||
generator.setAdditionalChars("aql");
|
|
||||||
generator.setLength(2000);
|
void TestPasswordGenerator::testCustomCharacterSet_data()
|
||||||
QVERIFY(generator.isValid());
|
{
|
||||||
QString password = generator.generatePassword();
|
QTest::addColumn<PasswordGenerator::CharClasses>("activeCharacterClasses");
|
||||||
|
QTest::addColumn<QString>("customCharacterSet");
|
||||||
|
QTest::addColumn<QRegularExpression>("expected");
|
||||||
|
|
||||||
|
QTest::addRow("With active classes") << to_flags(PasswordGenerator::CharClass::UpperLetters) << "abc"
|
||||||
|
<< QRegularExpression("^[abcA-Z]{2000}$");
|
||||||
|
QTest::addRow("Without any active class")
|
||||||
|
<< to_flags(PasswordGenerator::CharClass::NoClass) << "abc" << QRegularExpression("^[abc]{2000}$");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestPasswordGenerator::testCustomCharacterSet()
|
||||||
|
{
|
||||||
|
QFETCH(PasswordGenerator::CharClasses, activeCharacterClasses);
|
||||||
|
QFETCH(QString, customCharacterSet);
|
||||||
|
QFETCH(QRegularExpression, expected);
|
||||||
|
|
||||||
|
m_generator.setCharClasses(activeCharacterClasses);
|
||||||
|
m_generator.setCustomCharacterSet(customCharacterSet);
|
||||||
|
m_generator.setLength(2000);
|
||||||
|
|
||||||
|
QVERIFY(m_generator.isValid());
|
||||||
|
QString password = m_generator.generatePassword();
|
||||||
QCOMPARE(password.size(), 2000);
|
QCOMPARE(password.size(), 2000);
|
||||||
QRegularExpression regex(R"(^[aql]+$)");
|
QVERIFY(expected.match(password).hasMatch());
|
||||||
QVERIFY(regex.match(password).hasMatch());
|
}
|
||||||
|
|
||||||
|
void TestPasswordGenerator::testCharClasses_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<PasswordGenerator::CharClasses>("activeCharacterClasses");
|
||||||
|
QTest::addColumn<QRegularExpression>("expected");
|
||||||
|
|
||||||
|
QTest::addRow("Lower Letters") << to_flags(PasswordGenerator::CharClass::LowerLetters)
|
||||||
|
<< QRegularExpression(R"(^[a-z]{2000}$)");
|
||||||
|
QTest::addRow("Upper Letters") << to_flags(PasswordGenerator::CharClass::UpperLetters)
|
||||||
|
<< QRegularExpression(R"(^[A-Z]{2000}$)");
|
||||||
|
QTest::addRow("Numbers") << to_flags(PasswordGenerator::CharClass::Numbers) << QRegularExpression(R"(^\d{2000}$)");
|
||||||
|
QTest::addRow("Braces") << to_flags(PasswordGenerator::CharClass::Braces)
|
||||||
|
<< QRegularExpression(R"(^[\(\)\[\]\{\}]{2000}$)");
|
||||||
|
QTest::addRow("Punctuation") << to_flags(PasswordGenerator::CharClass::Punctuation)
|
||||||
|
<< QRegularExpression(R"(^[\.,:;]{2000}$)");
|
||||||
|
QTest::addRow("Quotes") << to_flags(PasswordGenerator::CharClass::Quotes) << QRegularExpression(R"(^["']{2000}$)");
|
||||||
|
QTest::addRow("Dashes") << to_flags(PasswordGenerator::CharClass::Dashes)
|
||||||
|
<< QRegularExpression(R"(^[\-/\\_|]{2000}$)");
|
||||||
|
QTest::addRow("Math") << to_flags(PasswordGenerator::CharClass::Math) << QRegularExpression(R"(^[!\*\+\-<=>\?]+$)");
|
||||||
|
QTest::addRow("Logograms") << to_flags(PasswordGenerator::CharClass::Logograms)
|
||||||
|
<< QRegularExpression(R"(^[#`~%&^$@]{2000}$)");
|
||||||
|
QTest::addRow("Extended ASCII") << to_flags(PasswordGenerator::CharClass::EASCII)
|
||||||
|
<< QRegularExpression(R"(^[^a-zA-Z0-9\.,:;"'\-/\\_|!\*\+\-<=>\?#`~%&^$@]{2000}$)");
|
||||||
|
QTest::addRow("Combinations 1") << (PasswordGenerator::CharClass::LowerLetters
|
||||||
|
| PasswordGenerator::CharClass::UpperLetters
|
||||||
|
| PasswordGenerator::CharClass::Braces)
|
||||||
|
<< QRegularExpression(R"(^[a-zA-Z\(\)\[\]\{\}]{2000}$)");
|
||||||
|
QTest::addRow("Combinations 2") << (PasswordGenerator::CharClass::Quotes | PasswordGenerator::CharClass::Numbers
|
||||||
|
| PasswordGenerator::CharClass::Dashes)
|
||||||
|
<< QRegularExpression(R"(^["'\d\-/\\_|]{2000}$)");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestPasswordGenerator::testCharClasses()
|
void TestPasswordGenerator::testCharClasses()
|
||||||
{
|
{
|
||||||
PasswordGenerator generator;
|
|
||||||
QVERIFY(!generator.isValid());
|
|
||||||
generator.setCharClasses(PasswordGenerator::CharClass::LowerLetters);
|
|
||||||
generator.setLength(16);
|
|
||||||
QVERIFY(generator.isValid());
|
|
||||||
QCOMPARE(generator.generatePassword().size(), 16);
|
|
||||||
|
|
||||||
generator.setLength(2000);
|
QFETCH(PasswordGenerator::CharClasses, activeCharacterClasses);
|
||||||
QString password = generator.generatePassword();
|
QFETCH(QRegularExpression, expected);
|
||||||
|
|
||||||
|
m_generator.setCharClasses(activeCharacterClasses);
|
||||||
|
m_generator.setLength(2000);
|
||||||
|
|
||||||
|
QVERIFY(m_generator.isValid());
|
||||||
|
QString password = m_generator.generatePassword();
|
||||||
QCOMPARE(password.size(), 2000);
|
QCOMPARE(password.size(), 2000);
|
||||||
QRegularExpression regex(R"(^[a-z]+$)");
|
QVERIFY(expected.match(password).hasMatch());
|
||||||
QVERIFY(regex.match(password).hasMatch());
|
}
|
||||||
|
|
||||||
generator.setCharClasses(PasswordGenerator::CharClass::UpperLetters);
|
void TestPasswordGenerator::testLookalikeExclusion_data()
|
||||||
password = generator.generatePassword();
|
{
|
||||||
regex.setPattern(R"(^[A-Z]+$)");
|
QTest::addColumn<PasswordGenerator::CharClasses>("activeCharacterClasses");
|
||||||
QVERIFY(regex.match(password).hasMatch());
|
QTest::addColumn<QRegularExpression>("expected");
|
||||||
|
QTest::addRow("Upper Letters") << (PasswordGenerator::CharClass::LowerLetters
|
||||||
|
| PasswordGenerator::CharClass::UpperLetters)
|
||||||
|
<< QRegularExpression("^[^lBGIO]{2000}$");
|
||||||
|
|
||||||
generator.setCharClasses(PasswordGenerator::CharClass::Numbers);
|
QTest::addRow("Letters and Numbers") << (PasswordGenerator::CharClass::LowerLetters
|
||||||
password = generator.generatePassword();
|
| PasswordGenerator::CharClass::UpperLetters
|
||||||
regex.setPattern(R"(^\d+$)");
|
| PasswordGenerator::CharClass::Numbers)
|
||||||
QVERIFY(regex.match(password).hasMatch());
|
<< QRegularExpression("^[^lBGIO0168]{2000}$");
|
||||||
|
|
||||||
generator.setCharClasses(PasswordGenerator::CharClass::Braces);
|
QTest::addRow("Letters, Numbers and extended ASCII")
|
||||||
password = generator.generatePassword();
|
<< (PasswordGenerator::CharClass::LowerLetters | PasswordGenerator::CharClass::UpperLetters
|
||||||
regex.setPattern(R"(^[\(\)\[\]\{\}]+$)");
|
| PasswordGenerator::CharClass::Numbers | PasswordGenerator::CharClass::EASCII)
|
||||||
QVERIFY(regex.match(password).hasMatch());
|
<< QRegularExpression("^[^lBGIO0168﹒]{2000}$");
|
||||||
|
|
||||||
generator.setCharClasses(PasswordGenerator::CharClass::Punctuation);
|
|
||||||
password = generator.generatePassword();
|
|
||||||
regex.setPattern(R"(^[\.,:;]+$)");
|
|
||||||
QVERIFY(regex.match(password).hasMatch());
|
|
||||||
|
|
||||||
generator.setCharClasses(PasswordGenerator::CharClass::Quotes);
|
|
||||||
password = generator.generatePassword();
|
|
||||||
regex.setPattern(R"(^["']+$)");
|
|
||||||
QVERIFY(regex.match(password).hasMatch());
|
|
||||||
|
|
||||||
generator.setCharClasses(PasswordGenerator::CharClass::Dashes);
|
|
||||||
password = generator.generatePassword();
|
|
||||||
regex.setPattern(R"(^[\-/\\_|]+$)");
|
|
||||||
QVERIFY(regex.match(password).hasMatch());
|
|
||||||
|
|
||||||
generator.setCharClasses(PasswordGenerator::CharClass::Math);
|
|
||||||
password = generator.generatePassword();
|
|
||||||
regex.setPattern(R"(^[!\*\+\-<=>\?]+$)");
|
|
||||||
QVERIFY(regex.match(password).hasMatch());
|
|
||||||
|
|
||||||
generator.setCharClasses(PasswordGenerator::CharClass::Logograms);
|
|
||||||
password = generator.generatePassword();
|
|
||||||
regex.setPattern(R"(^[#`~%&^$@]+$)");
|
|
||||||
QVERIFY(regex.match(password).hasMatch());
|
|
||||||
|
|
||||||
generator.setCharClasses(PasswordGenerator::CharClass::EASCII);
|
|
||||||
password = generator.generatePassword();
|
|
||||||
regex.setPattern(R"(^[^a-zA-Z0-9\.,:;"'\-/\\_|!\*\+\-<=>\?#`~%&^$@]+$)");
|
|
||||||
QVERIFY(regex.match(password).hasMatch());
|
|
||||||
|
|
||||||
generator.setCharClasses(PasswordGenerator::CharClass::LowerLetters | PasswordGenerator::CharClass::UpperLetters
|
|
||||||
| PasswordGenerator::CharClass::Braces);
|
|
||||||
password = generator.generatePassword();
|
|
||||||
regex.setPattern(R"(^[a-zA-Z\(\)\[\]\{\}]+$)");
|
|
||||||
QVERIFY(regex.match(password).hasMatch());
|
|
||||||
|
|
||||||
generator.setCharClasses(PasswordGenerator::CharClass::Quotes | PasswordGenerator::CharClass::Numbers
|
|
||||||
| PasswordGenerator::CharClass::Dashes);
|
|
||||||
password = generator.generatePassword();
|
|
||||||
regex.setPattern(R"(^["'\d\-/\\_|]+$)");
|
|
||||||
QVERIFY(regex.match(password).hasMatch());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestPasswordGenerator::testLookalikeExclusion()
|
void TestPasswordGenerator::testLookalikeExclusion()
|
||||||
{
|
{
|
||||||
PasswordGenerator generator;
|
QFETCH(PasswordGenerator::CharClasses, activeCharacterClasses);
|
||||||
generator.setLength(2000);
|
QFETCH(QRegularExpression, expected);
|
||||||
generator.setCharClasses(PasswordGenerator::CharClass::LowerLetters | PasswordGenerator::CharClass::UpperLetters);
|
|
||||||
QVERIFY(generator.isValid());
|
m_generator.setFlags(PasswordGenerator::ExcludeLookAlike);
|
||||||
QString password = generator.generatePassword();
|
m_generator.setCharClasses(activeCharacterClasses);
|
||||||
|
m_generator.setLength(2000);
|
||||||
|
|
||||||
|
QVERIFY(m_generator.isValid());
|
||||||
|
QString password = m_generator.generatePassword();
|
||||||
QCOMPARE(password.size(), 2000);
|
QCOMPARE(password.size(), 2000);
|
||||||
|
QVERIFY(expected.match(password).hasMatch());
|
||||||
generator.setFlags(PasswordGenerator::GeneratorFlag::ExcludeLookAlike);
|
}
|
||||||
password = generator.generatePassword();
|
|
||||||
QRegularExpression regex("^[^lBGIO]+$");
|
void TestPasswordGenerator::testValidity_data()
|
||||||
QVERIFY(regex.match(password).hasMatch());
|
{
|
||||||
|
QTest::addColumn<PasswordGenerator::CharClasses>("activeCharacterClasses");
|
||||||
generator.setCharClasses(PasswordGenerator::CharClass::LowerLetters | PasswordGenerator::CharClass::UpperLetters
|
QTest::addColumn<PasswordGenerator::GeneratorFlags>("generatorFlags");
|
||||||
| PasswordGenerator::CharClass::Numbers);
|
QTest::addColumn<QString>("customCharacterSet");
|
||||||
password = generator.generatePassword();
|
QTest::addColumn<QString>("excludedCharacters");
|
||||||
regex.setPattern("^[^lBGIO0168]+$");
|
QTest::addColumn<int>("length");
|
||||||
QVERIFY(regex.match(password).hasMatch());
|
QTest::addColumn<bool>("isValid");
|
||||||
|
|
||||||
generator.setCharClasses(PasswordGenerator::CharClass::LowerLetters | PasswordGenerator::CharClass::UpperLetters
|
QTest::addRow("No active class") << to_flags(PasswordGenerator::CharClass::NoClass)
|
||||||
| PasswordGenerator::CharClass::Numbers | PasswordGenerator::CharClass::EASCII);
|
<< PasswordGenerator::GeneratorFlags() << QString() << QString()
|
||||||
password = generator.generatePassword();
|
<< PasswordGenerator::DefaultLength << false;
|
||||||
regex.setPattern("^[^lBGIO0168﹒]+$");
|
QTest::addRow("0 length") << to_flags(PasswordGenerator::CharClass::DefaultCharset)
|
||||||
QVERIFY(regex.match(password).hasMatch());
|
<< PasswordGenerator::GeneratorFlags() << QString() << QString() << 0 << false;
|
||||||
|
QTest::addRow("All active classes excluded")
|
||||||
|
<< to_flags(PasswordGenerator::CharClass::Numbers) << PasswordGenerator::GeneratorFlags() << QString()
|
||||||
|
<< QString("0123456789") << PasswordGenerator::DefaultLength << false;
|
||||||
|
QTest::addRow("All active classes excluded")
|
||||||
|
<< to_flags(PasswordGenerator::CharClass::NoClass) << PasswordGenerator::GeneratorFlags() << QString()
|
||||||
|
<< QString("0123456789") << PasswordGenerator::DefaultLength << false;
|
||||||
|
QTest::addRow("One from every class with too few classes")
|
||||||
|
<< (PasswordGenerator::CharClass::LowerLetters | PasswordGenerator::CharClass::UpperLetters)
|
||||||
|
<< to_flags(PasswordGenerator::GeneratorFlag::CharFromEveryGroup) << QString() << QString() << 1 << false;
|
||||||
|
QTest::addRow("One from every class with excluded classes")
|
||||||
|
<< (PasswordGenerator::CharClass::LowerLetters | PasswordGenerator::CharClass::UpperLetters
|
||||||
|
| PasswordGenerator::CharClass::Numbers)
|
||||||
|
<< to_flags(PasswordGenerator::GeneratorFlag::CharFromEveryGroup) << QString() << QString("0123456789") << 2
|
||||||
|
<< true;
|
||||||
|
QTest::addRow("Defaults valid") << to_flags(PasswordGenerator::CharClass::DefaultCharset)
|
||||||
|
<< to_flags(PasswordGenerator::GeneratorFlag::DefaultFlags)
|
||||||
|
<< PasswordGenerator::DefaultCustomCharacterSet
|
||||||
|
<< PasswordGenerator::DefaultExcludedChars << PasswordGenerator::DefaultLength
|
||||||
|
<< true;
|
||||||
|
QTest::addRow("No active classes but custom charset")
|
||||||
|
<< to_flags(PasswordGenerator::CharClass::NoClass) << to_flags(PasswordGenerator::GeneratorFlag::DefaultFlags)
|
||||||
|
<< QString("a") << QString() << 1 << true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestPasswordGenerator::testValidity()
|
||||||
|
{
|
||||||
|
QFETCH(PasswordGenerator::CharClasses, activeCharacterClasses);
|
||||||
|
QFETCH(PasswordGenerator::GeneratorFlags, generatorFlags);
|
||||||
|
QFETCH(QString, customCharacterSet);
|
||||||
|
QFETCH(QString, excludedCharacters);
|
||||||
|
QFETCH(int, length);
|
||||||
|
QFETCH(bool, isValid);
|
||||||
|
|
||||||
|
m_generator.setCharClasses(activeCharacterClasses);
|
||||||
|
m_generator.setFlags(generatorFlags);
|
||||||
|
m_generator.setCustomCharacterSet(customCharacterSet);
|
||||||
|
m_generator.setExcludedCharacterSet(excludedCharacters);
|
||||||
|
m_generator.setLength(length);
|
||||||
|
QCOMPARE(m_generator.isValid(), isValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestPasswordGenerator::testMinLength_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<PasswordGenerator::CharClasses>("activeCharacterClasses");
|
||||||
|
QTest::addColumn<PasswordGenerator::GeneratorFlags>("generatorFlags");
|
||||||
|
QTest::addColumn<QString>("customCharacterSet");
|
||||||
|
QTest::addColumn<QString>("excludedCharacters");
|
||||||
|
QTest::addColumn<int>("expectedMinLength");
|
||||||
|
|
||||||
|
QTest::addRow("No restriction without charsFromEveryGroup")
|
||||||
|
<< to_flags(PasswordGenerator::CharClass::Numbers)
|
||||||
|
<< to_flags(PasswordGenerator::GeneratorFlag::CharFromEveryGroup)
|
||||||
|
<< PasswordGenerator::DefaultCustomCharacterSet << PasswordGenerator::DefaultExcludedChars << 1;
|
||||||
|
|
||||||
|
QTest::addRow("Min length should equal number of active classes")
|
||||||
|
<< (PasswordGenerator::CharClass::LowerLetters | PasswordGenerator::CharClass::UpperLetters
|
||||||
|
| PasswordGenerator::CharClass::Numbers)
|
||||||
|
<< to_flags(PasswordGenerator::GeneratorFlag::CharFromEveryGroup) << QString() << QString() << 3;
|
||||||
|
QTest::addRow("Classes fully excluded by excluded characters do not count towards min length")
|
||||||
|
<< (PasswordGenerator::CharClass::Numbers | PasswordGenerator::LowerLetters
|
||||||
|
| PasswordGenerator::CharClass::UpperLetters)
|
||||||
|
<< to_flags(PasswordGenerator::GeneratorFlag::CharFromEveryGroup) << QString() << QString("0123456789") << 2;
|
||||||
|
|
||||||
|
QTest::addRow("Custom charset counts as class")
|
||||||
|
<< to_flags(PasswordGenerator::CharClass::UpperLetters)
|
||||||
|
<< to_flags(PasswordGenerator::GeneratorFlag::CharFromEveryGroup) << QString("a") << QString() << 2;
|
||||||
|
QTest::addRow("Custom characters count even if included by an active class already")
|
||||||
|
<< (PasswordGenerator::CharClass::LowerLetters | PasswordGenerator::CharClass::UpperLetters
|
||||||
|
| PasswordGenerator::CharClass::Numbers)
|
||||||
|
<< to_flags(PasswordGenerator::GeneratorFlag::CharFromEveryGroup) << QString("012345") << QString() << 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestPasswordGenerator::testMinLength()
|
||||||
|
{
|
||||||
|
QFETCH(PasswordGenerator::CharClasses, activeCharacterClasses);
|
||||||
|
QFETCH(PasswordGenerator::GeneratorFlags, generatorFlags);
|
||||||
|
QFETCH(QString, customCharacterSet);
|
||||||
|
QFETCH(QString, excludedCharacters);
|
||||||
|
QFETCH(int, expectedMinLength);
|
||||||
|
|
||||||
|
m_generator.setCharClasses(activeCharacterClasses);
|
||||||
|
m_generator.setFlags(generatorFlags);
|
||||||
|
m_generator.setCustomCharacterSet(customCharacterSet);
|
||||||
|
m_generator.setExcludedCharacterSet(excludedCharacters);
|
||||||
|
QCOMPARE(m_generator.getMinLength(), expectedMinLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestPasswordGenerator::testReset()
|
||||||
|
{
|
||||||
|
PasswordGenerator default_generator;
|
||||||
|
|
||||||
|
// Modify generator
|
||||||
|
m_generator.setCharClasses(PasswordGenerator::CharClass::NoClass);
|
||||||
|
m_generator.setFlags(PasswordGenerator::GeneratorFlag::NoFlags);
|
||||||
|
m_generator.setCustomCharacterSet("avc");
|
||||||
|
m_generator.setExcludedCharacterSet("asdv");
|
||||||
|
m_generator.setLength(m_generator.getLength() + 1);
|
||||||
|
|
||||||
|
Q_ASSERT(m_generator.getActiveClasses() != default_generator.getActiveClasses());
|
||||||
|
Q_ASSERT(m_generator.getFlags() != default_generator.getFlags());
|
||||||
|
Q_ASSERT(m_generator.getCustomCharacterSet() != default_generator.getCustomCharacterSet());
|
||||||
|
Q_ASSERT(m_generator.getExcludedCharacterSet() != default_generator.getExcludedCharacterSet());
|
||||||
|
|
||||||
|
m_generator.reset();
|
||||||
|
QCOMPARE(m_generator.getActiveClasses(), default_generator.getActiveClasses());
|
||||||
|
QCOMPARE(m_generator.getFlags(), default_generator.getFlags());
|
||||||
|
QCOMPARE(m_generator.getCustomCharacterSet(), default_generator.getCustomCharacterSet());
|
||||||
|
QCOMPARE(m_generator.getExcludedCharacterSet(), default_generator.getExcludedCharacterSet());
|
||||||
|
QCOMPARE(m_generator.getLength(), default_generator.getLength());
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,17 +18,31 @@
|
||||||
#ifndef KEEPASSXC_TESTPASSWORDGENERATOR_H
|
#ifndef KEEPASSXC_TESTPASSWORDGENERATOR_H
|
||||||
#define KEEPASSXC_TESTPASSWORDGENERATOR_H
|
#define KEEPASSXC_TESTPASSWORDGENERATOR_H
|
||||||
|
|
||||||
|
#include "core/PasswordGenerator.h"
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
class TestPasswordGenerator : public QObject
|
class TestPasswordGenerator : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
PasswordGenerator m_generator;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
void testAdditionalChars();
|
void init();
|
||||||
|
|
||||||
|
void testCustomCharacterSet_data();
|
||||||
|
void testCustomCharacterSet();
|
||||||
|
void testCharClasses_data();
|
||||||
void testCharClasses();
|
void testCharClasses();
|
||||||
|
void testLookalikeExclusion_data();
|
||||||
void testLookalikeExclusion();
|
void testLookalikeExclusion();
|
||||||
|
void testMinLength_data();
|
||||||
|
void testMinLength();
|
||||||
|
void testValidity_data();
|
||||||
|
void testValidity();
|
||||||
|
void testReset();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSXC_TESTPASSWORDGENERATOR_H
|
#endif // KEEPASSXC_TESTPASSWORDGENERATOR_H
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue