/*
* Copyright (C) 2019 KeePassXC Team
*
* 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 .
*/
#include
#include
#include "Database.h"
#include "Entry.h"
#include "Group.h"
#include "PasswordHealth.h"
#include "zxcvbn.h"
// Define the static member variable with the custom field name
const QString PasswordHealth::OPTION_KNOWN_BAD = QStringLiteral("KnownBad");
PasswordHealth::PasswordHealth(double entropy)
: m_score(entropy)
, m_entropy(entropy)
{
switch (quality()) {
case Quality::Bad:
case Quality::Poor:
m_scoreReasons hierarchy().join('/'), entry->title());
}
}
}
/**
* Call operator of the Health Checker class.
*
* Returns the health of the password in `entry`, considering
* password entropy, re-use, expiration, etc.
*/
QSharedPointer HealthChecker::evaluate(const Entry* entry) const
{
// Pointer sanity check
if (!entry) {
return {};
}
// First analyse the password itself
const auto pwd = entry->password();
auto health = QSharedPointer(new PasswordHealth(pwd));
// Second, if the password is in the database more than once,
// reduce the score accordingly
const auto& used = m_reuse[pwd];
const auto count = used.size();
if (count > 1) {
constexpr auto penalty = 15;
health->adjustScore(-penalty * (count - 1));
health->addScoreReason(QApplication::tr("Password is used %1 times").arg(QString::number(count)));
// Add the first 20 uses of the password to prevent the details display from growing too large
for (int i = 0; i < used.size(); ++i) {
health->addScoreDetails(used[i]);
if (i == 19) {
health->addScoreDetails("");
break;
}
}
// Don't allow re-used passwords to be considered "good"
// no matter how great their entropy is.
if (health->score() > 64) {
health->setScore(64);
}
}
// Third, if the password has already expired, reduce score to 0;
// or, if the password is going to expire in the next 30 days,
// reduce score by 2 points per day.
if (entry->isExpired()) {
health->setScore(0);
health->addScoreReason(QApplication::tr("Password has expired"));
health->addScoreDetails(QApplication::tr("Password expiry was %1")
.arg(entry->timeInfo().expiryTime().toString(Qt::DefaultLocaleShortDate)));
} else if (entry->timeInfo().expires()) {
const auto days = QDateTime::currentDateTime().daysTo(entry->timeInfo().expiryTime());
if (days 60) {
health->setScore(60);
}
// clang-format off
health->adjustScore((30 - days) * -2);
health->addScoreReason(days timeInfo().expiryTime().toString(Qt::DefaultLocaleShortDate)));
//clang-format on
}
}
// Return the result
return health;
}