diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp index 71246d9bf..2d020a7d5 100644 --- a/tests/TestCli.cpp +++ b/tests/TestCli.cpp @@ -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("input"); - QTest::addColumn("length"); - QTest::addColumn("entropy"); - QTest::addColumn("log10"); QTest::addColumn("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)); diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 5aa459ffa..6cf096201 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -31,6 +31,7 @@ #include #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("password"); - QTest::addColumn("expectedEntropyLabel"); QTest::addColumn("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("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);