Fix TestCli and TestGui entropy tests across platforms

* zxcvbn wordlists can vary between platforms depending on packager adjustments (ie, zxcvbn-c vs Ubuntu Focal)
This commit is contained in:
Jonathan White 2024-03-31 07:12:01 -04:00
parent 60908d4b9b
commit 5513ff5c1f
2 changed files with 39 additions and 64 deletions

View File

@ -26,6 +26,7 @@
#include "crypto/Crypto.h"
#include "keys/FileKey.h"
#include "keys/drivers/YubiKey.h"
#include "zxcvbn/zxcvbn.h"
#include "cli/Add.h"
#include "cli/AddGroup.h"
@ -1187,90 +1188,72 @@ void TestCli::testEdit()
void TestCli::testEstimate_data()
{
// clang-format off
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("length");
QTest::addColumn<QString>("entropy");
QTest::addColumn<QString>("log10");
QTest::addColumn<QStringList>("searchStrings");
QTest::newRow("Dictionary") << "password"
<< "8"
<< "1.0"
<< "0.3" << QStringList{"Type: Dictionary", "\tpassword"};
QTest::newRow("Dictionary")
<< "password"
<< QStringList{"Type: Dictionary", "\tpassword"};
QTest::newRow("Spatial") << "zxcv"
<< "4"
<< "10.3"
<< "3.1" << QStringList{"Type: Spatial", "\tzxcv"};
QTest::newRow("Spatial")
<< "sdfg"
<< QStringList{"Type: Spatial", "\tsdfg"};
QTest::newRow("Spatial(Rep)") << "sdfgsdfg"
<< "8"
<< "11.3"
<< "3.4" << QStringList{"Type: Spatial(Rep)", "\tsdfgsdfg"};
QTest::newRow("Spatial(Rep)")
<< "sdfgsdfg"
<< QStringList{"Type: Spatial(Rep)", "\tsdfgsdfg"};
QTest::newRow("Dictionary / Sequence")
<< "password123"
<< "11"
<< "4.5"
<< "1.3" << QStringList{"Type: Dictionary", "Type: Sequence", "\tpassword", "\t123"};
<< QStringList{"Type: Dictionary", "Type: Sequence", "\tpassword", "\t123"};
QTest::newRow("Dict+Leet") << "p455w0rd"
<< "8"
<< "2.5"
<< "0.7" << QStringList{"Type: Dict+Leet", "\tp455w0rd"};
QTest::newRow("Dict+Leet")
<< "p455w0rd"
<< QStringList{"Type: Dict+Leet", "\tp455w0rd"};
QTest::newRow("Dictionary(Rep)") << "hellohello"
<< "10"
<< "7.3"
<< "2.2" << QStringList{"Type: Dictionary(Rep)", "\thellohello"};
QTest::newRow("Dictionary(Rep)")
<< "hellohello"
<< QStringList{"Type: Dictionary(Rep)", "\thellohello"};
QTest::newRow("Sequence(Rep) / Dictionary")
<< "456456foobar"
<< "12"
<< "16.7"
<< "5.0" << QStringList{"Type: Sequence(Rep)", "Type: Dictionary", "\t456456", "\tfoobar"};
<< QStringList{"Type: Sequence(Rep)", "Type: Dictionary", "\t456456", "\tfoobar"};
QTest::newRow("Bruteforce(Rep) / Bruteforce")
<< "xzxzy"
<< "5"
<< "16.1"
<< "4.8" << QStringList{"Type: Bruteforce(Rep)", "Type: Bruteforce", "\txzxz", "\ty"};
<< QStringList{"Type: Bruteforce(Rep)", "Type: Bruteforce", "\txzxz", "\ty"};
QTest::newRow("Dictionary / Date(Rep)")
<< "pass20182018"
<< "12"
<< "15.1"
<< "4.56" << QStringList{"Type: Dictionary", "Type: Date(Rep)", "\tpass", "\t20182018"};
<< QStringList{"Type: Dictionary", "Type: Date(Rep)", "\tpass", "\t20182018"};
QTest::newRow("Dictionary / Date / Bruteforce")
<< "mypass2018-2"
<< "12"
<< "32.9"
<< "9.9" << QStringList{"Type: Dictionary", "Type: Date", "Type: Bruteforce", "\tmypass", "\t2018", "\t-2"};
<< QStringList{"Type: Dictionary", "Type: Date", "Type: Bruteforce", "\tmypass", "\t2018", "\t-2"};
QTest::newRow("Strong Password") << "E*!%.Qw{t.X,&bafw)\"Q!ah$%;U/"
<< "28"
<< "165.7"
<< "49.8" << QStringList{"Type: Bruteforce", "\tE*"};
QTest::newRow("Strong Password")
<< "E*!%.Qw{t.X,&bafw)\"Q!ah$%;U/"
<< QStringList{"Type: Bruteforce", "\tE*"};
// TODO: detect passphrases and adjust entropy calculation accordingly (issue #2347)
QTest::newRow("Strong Passphrase")
<< "squint wooing resupply dangle isolation axis headsman"
<< "53"
<< "151.2"
<< "45.5"
<< QStringList{
"Type: Dictionary", "Type: Bruteforce", "Multi-word extra bits 22.0", "\tsquint", "\t ", "\twooing"};
<< QStringList{"Type: Dictionary", "Type: Bruteforce", "Multi-word extra bits 22.0", "\tsquint", "\t ", "\twooing"};
// clang-format on
}
void TestCli::testEstimate()
{
QFETCH(QString, input);
QFETCH(QString, length);
QFETCH(QString, entropy);
QFETCH(QString, log10);
QFETCH(QStringList, searchStrings);
// Calculate expected values since zxcvbn output can vary by platform if different wordlists are used
const auto e = ZxcvbnMatch(input.toUtf8(), nullptr, nullptr);
auto length = QString::number(input.length());
auto entropy = QString("%1").arg(e, 0, 'f', 3);
auto log10 = QString("%1").arg(e * 0.301029996, 0, 'f', 3);
Estimate estimateCmd;
QVERIFY(!estimateCmd.name.isEmpty());
QVERIFY(estimateCmd.getDescriptionLine().contains(estimateCmd.name));

View File

@ -31,6 +31,7 @@
#include <QToolBar>
#include "config-keepassx-tests.h"
#include "core/PasswordHealth.h"
#include "core/Tools.h"
#include "crypto/Crypto.h"
#include "gui/ActionCollection.h"
@ -691,43 +692,33 @@ void TestGui::testAddEntry()
void TestGui::testPasswordEntryEntropy_data()
{
QTest::addColumn<QString>("password");
QTest::addColumn<QString>("expectedEntropyLabel");
QTest::addColumn<QString>("expectedStrengthLabel");
QTest::newRow("Empty password") << ""
<< "Entropy: 0.00 bit"
<< "Password Quality: Poor";
QTest::newRow("Well-known password") << "hello"
<< "Entropy: 6.38 bit"
<< "Password Quality: Poor";
QTest::newRow("Password composed of well-known words.") << "helloworld"
<< "Entropy: 13.10 bit"
<< "Password Quality: Poor";
QTest::newRow("Password composed of well-known words with number.") << "password1"
<< "Entropy: 4.00 bit"
<< "Password Quality: Poor";
QTest::newRow("Password out of small character space.") << "D0g.................."
<< "Entropy: 19.02 bit"
<< "Password Quality: Poor";
QTest::newRow("XKCD, easy substitutions.") << "Tr0ub4dour&3"
<< "Entropy: 30.87 bit"
<< "Password Quality: Poor";
QTest::newRow("XKCD, word generator.") << "correcthorsebatterystaple"
<< "Entropy: 47.98 bit"
<< "Password Quality: Weak";
QTest::newRow("Random characters, medium length.") << "YQC3kbXbjC652dTDH"
<< "Entropy: 95.83 bit"
<< "Password Quality: Good";
QTest::newRow("Random characters, long.") << "Bs5ZFfthWzR8DGFEjaCM6bGqhmCT4km"
<< "Entropy: 174.59 bit"
<< "Password Quality: Excellent";
QTest::newRow("Long password using Zxcvbn chunk estimation")
@ -735,7 +726,6 @@ void TestGui::testPasswordEntryEntropy_data()
"ashamed-anatomy-daybed-jam-swear-strudel-neatness-stalemate-unbundle-flavored-relation-emergency-underrate-"
"registry-getting-award-unveiled-unshaken-stagnate-cartridge-magnitude-ointment-hardener-enforced-scrubbed-"
"radial-fiddling-envelope-unpaved-moisture-unused-crawlers-quartered-crushed-kangaroo-tiptop-doily"
<< "Entropy: 1205.85 bit"
<< "Password Quality: Excellent";
QTest::newRow("Longer password above Zxcvbn threshold")
@ -753,7 +743,6 @@ void TestGui::testPasswordEntryEntropy_data()
"disparate-decorated-washroom-threefold-muzzle-buckwheat-kerosene-swell-why-reprocess-correct-shady-"
"impatient-slit-banshee-scrubbed-dreadful-unlocking-urologist-hurried-citable-fragment-septic-lapped-"
"prankish-phantom-unpaved-moisture-unused-crawlers-quartered-crushed-kangaroo-lapel-emporium-renounce"
<< "Entropy: 4210.27 bit"
<< "Password Quality: Excellent";
}
@ -802,11 +791,14 @@ void TestGui::testPasswordEntryEntropy()
auto* strengthLabel = pwGeneratorWidget->findChild<QLabel*>("strengthLabel");
QFETCH(QString, password);
QFETCH(QString, expectedEntropyLabel);
QFETCH(QString, expectedStrengthLabel);
// Dynamically calculate entropy due to variances with zxcvbn wordlists
PasswordHealth health(password);
auto expectedEntropy = QString("Entropy: %1 bit").arg(QString::number(health.entropy(), 'f', 2));
generatedPassword->setText(password);
QCOMPARE(entropyLabel->text(), expectedEntropyLabel);
QCOMPARE(entropyLabel->text(), expectedEntropy);
QCOMPARE(strengthLabel->text(), expectedStrengthLabel);
QTest::mouseClick(generatedPassword, Qt::LeftButton);