Limit zxcvbn entropy estimation length

Limit the use of zxcvbn based password entropy estimation to 256 bytes. After this threshold, the average per-byte entropy from the zxcvbn calculation is added for each additional byte. In practice, this produces a slightly higher entropy calculation for purely randomized passwords than zxcvbn would normally calculate. However, the time to calculate is capped leading to a much better user experience and removing unnecessary calculations.

Fixes #7712
This commit is contained in:
Patrick Sean Klein 2022-04-03 17:31:15 +02:00 committed by Jonathan White
parent 1009650b5c
commit 65a1d1b0f7
6 changed files with 156 additions and 107 deletions

View file

@ -56,7 +56,7 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
connect(shortcut, &QShortcut::activated, this, [this] { applyPassword(); });
connect(m_ui->editNewPassword, SIGNAL(textChanged(QString)), SLOT(updateButtonsEnabled(QString)));
connect(m_ui->editNewPassword, SIGNAL(textChanged(QString)), SLOT(updatePasswordStrength(QString)));
connect(m_ui->editNewPassword, SIGNAL(textChanged(QString)), SLOT(updatePasswordStrength()));
connect(m_ui->buttonAdvancedMode, SIGNAL(toggled(bool)), SLOT(setAdvancedMode(bool)));
connect(m_ui->buttonAddHex, SIGNAL(clicked()), SLOT(excludeHexChars()));
connect(m_ui->editAdditionalChars, SIGNAL(textChanged(QString)), SLOT(updateGenerator()));
@ -115,9 +115,7 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
loadSettings();
}
PasswordGeneratorWidget::~PasswordGeneratorWidget()
{
}
PasswordGeneratorWidget::~PasswordGeneratorWidget() = default;
void PasswordGeneratorWidget::closeEvent(QCloseEvent* event)
{
@ -253,15 +251,11 @@ void PasswordGeneratorWidget::regeneratePassword()
{
if (m_ui->tabWidget->currentIndex() == Password) {
if (m_passwordGenerator->isValid()) {
QString password = m_passwordGenerator->generatePassword();
m_ui->editNewPassword->setText(password);
updatePasswordStrength(password);
m_ui->editNewPassword->setText(m_passwordGenerator->generatePassword());
}
} else {
if (m_dicewareGenerator->isValid()) {
QString password = m_dicewareGenerator->generatePassphrase();
m_ui->editNewPassword->setText(password);
updatePasswordStrength(password);
m_ui->editNewPassword->setText(m_dicewareGenerator->generatePassphrase());
}
}
}
@ -274,21 +268,52 @@ void PasswordGeneratorWidget::updateButtonsEnabled(const QString& password)
m_ui->buttonCopy->setEnabled(!password.isEmpty());
}
void PasswordGeneratorWidget::updatePasswordStrength(const QString& password)
void PasswordGeneratorWidget::updatePasswordStrength()
{
PasswordHealth health(password);
// Calculate the password / passphrase health
PasswordHealth passwordHealth(0);
if (m_ui->tabWidget->currentIndex() == Diceware) {
// Diceware estimates entropy differently
health = PasswordHealth(m_dicewareGenerator->estimateEntropy());
m_ui->charactersInPassphraseLabel->setText(QString::number(password.length()));
passwordHealth.init(m_dicewareGenerator->estimateEntropy());
m_ui->charactersInPassphraseLabel->setText(QString::number(m_ui->editNewPassword->text().length()));
} else {
passwordHealth = PasswordHealth(m_ui->editNewPassword->text());
}
m_ui->entropyLabel->setText(tr("Entropy: %1 bit").arg(QString::number(health.entropy(), 'f', 2)));
// Update the entropy text labels
m_ui->entropyLabel->setText(tr("Entropy: %1 bit").arg(QString::number(passwordHealth.entropy(), 'f', 2)));
m_ui->entropyProgressBar->setValue(std::min(int(passwordHealth.entropy()), m_ui->entropyProgressBar->maximum()));
m_ui->entropyProgressBar->setValue(std::min(int(health.entropy()), m_ui->entropyProgressBar->maximum()));
// Update the visual strength meter
QString style = m_ui->entropyProgressBar->styleSheet();
QRegularExpression re("(QProgressBar::chunk\\s*\\{.*?background-color:)[^;]+;",
QRegularExpression::CaseInsensitiveOption | QRegularExpression::DotMatchesEverythingOption);
style.replace(re, "\\1 %1;");
colorStrengthIndicator(health);
StateColorPalette statePalette;
switch (passwordHealth.quality()) {
case PasswordHealth::Quality::Bad:
case PasswordHealth::Quality::Poor:
m_ui->entropyProgressBar->setStyleSheet(
style.arg(statePalette.color(StateColorPalette::HealthCritical).name()));
m_ui->strengthLabel->setText(tr("Password Quality: %1").arg(tr("Poor", "Password quality")));
break;
case PasswordHealth::Quality::Weak:
m_ui->entropyProgressBar->setStyleSheet(style.arg(statePalette.color(StateColorPalette::HealthBad).name()));
m_ui->strengthLabel->setText(tr("Password Quality: %1").arg(tr("Weak", "Password quality")));
break;
case PasswordHealth::Quality::Good:
m_ui->entropyProgressBar->setStyleSheet(style.arg(statePalette.color(StateColorPalette::HealthOk).name()));
m_ui->strengthLabel->setText(tr("Password Quality: %1").arg(tr("Good", "Password quality")));
break;
case PasswordHealth::Quality::Excellent:
m_ui->entropyProgressBar->setStyleSheet(
style.arg(statePalette.color(StateColorPalette::HealthExcellent).name()));
m_ui->strengthLabel->setText(tr("Password Quality: %1").arg(tr("Excellent", "Password quality")));
break;
}
}
void PasswordGeneratorWidget::applyPassword()
@ -471,41 +496,6 @@ void PasswordGeneratorWidget::excludeHexChars()
updateGenerator();
}
void PasswordGeneratorWidget::colorStrengthIndicator(const PasswordHealth& health)
{
// Take the existing stylesheet and convert the text and background color to arguments
QString style = m_ui->entropyProgressBar->styleSheet();
QRegularExpression re("(QProgressBar::chunk\\s*\\{.*?background-color:)[^;]+;",
QRegularExpression::CaseInsensitiveOption | QRegularExpression::DotMatchesEverythingOption);
style.replace(re, "\\1 %1;");
StateColorPalette statePalette;
switch (health.quality()) {
case PasswordHealth::Quality::Bad:
case PasswordHealth::Quality::Poor:
m_ui->entropyProgressBar->setStyleSheet(
style.arg(statePalette.color(StateColorPalette::HealthCritical).name()));
m_ui->strengthLabel->setText(tr("Password Quality: %1").arg(tr("Poor", "Password quality")));
break;
case PasswordHealth::Quality::Weak:
m_ui->entropyProgressBar->setStyleSheet(style.arg(statePalette.color(StateColorPalette::HealthBad).name()));
m_ui->strengthLabel->setText(tr("Password Quality: %1").arg(tr("Weak", "Password quality")));
break;
case PasswordHealth::Quality::Good:
m_ui->entropyProgressBar->setStyleSheet(style.arg(statePalette.color(StateColorPalette::HealthOk).name()));
m_ui->strengthLabel->setText(tr("Password Quality: %1").arg(tr("Good", "Password quality")));
break;
case PasswordHealth::Quality::Excellent:
m_ui->entropyProgressBar->setStyleSheet(
style.arg(statePalette.color(StateColorPalette::HealthExcellent).name()));
m_ui->strengthLabel->setText(tr("Password Quality: %1").arg(tr("Excellent", "Password quality")));
break;
}
}
PasswordGenerator::CharClasses PasswordGeneratorWidget::charClasses()
{
PasswordGenerator::CharClasses classes;