[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-core-devel
Subject: Password strengh meter in KNewPasswordDialog
From: Rolf Eike Beer <kde () opensource ! sf-tec ! de>
Date: 2013-04-03 20:39:47
Message-ID: 2702746.3mTRqzdGb2 () eto
[Download RAW message or body]
[Attachment #2 (multipart/mixed)]
Hi all,
the current issue of (German) Linux Magazin has an article comparing some
GnuPG frontends. One issue discussed there is the "password strength meter"
that gives e.g. 25% strength indication for things like 123456789. I don't
know about Kleopatra, but KGpg uses KNewPasswordDialog and it's strength meter
for this. I propose to change the algorithm used to calculate the password
strength to remove key sequences from the "length" calculation of the
password, i.e. 123 has the same length as 1. Also punish all passwords harder
that do not contain all types of characters, so a password containing only
lowercase characters and numbers needs to be much longer than one also
containing specials and uppercase characters.
I've attached my strength test program containing both the old and the
proposed new version of the code. I've tested the new version in 2 variants,
once with and without the call to toLower() before checking for sequences.
These are some test passwords I used, mostly some examples of "simple"
passwords users will use. The last one is a scrambled version of a password I
saw used somewhere (i.e. every letter replaced with something from the same
character class to retain the score) that was not totally obvious.
old nEw new
abcdef 45 12 12
abcdefghi 72 22 22
1 12 1 1
12 15 2 2
123 17 2 2
1234 20 2 2
12345 22 2 2
123456 25 2 2
123456789 32 2 2
qwertz 45 25 25
1234test 40 20 20
test1234 30 10 10
a1b2c3d4 100 60 60
a1b2c3d4e5 100 85 85
a1b2c3d4e5f6 100 100 100
a1a1a1a1 40 20 20
........ 30 2 2
KKvfnDd. 90 57 55
Also I propose to change the color of the strength indicator to red below 50%
and to yellow below 75%. Since this does not affect any strings and improves
security I would also push this into 4.10 in noone objects.
Comments?
Eike
["pwdstrength.cpp" (pwdstrength.cpp)]
#include <QString>
#include <QDebug>
const int reasonablePasswordLength = 8;
int effectivePasswordLength(const QString &password)
{
enum Category {
Digit,
Upper,
Vowel,
Consonant,
Special
};
Category previousCategory = Vowel;
QString vowels("aeiou");
int count = 0;
int catCount = 0;
unsigned int catMask = 0;
for (int i = 0; i < password.length(); ++i) {
QChar currentChar = password.at(i);
if (!password.left(i).contains(currentChar)) {
Category currentCategory;
switch (currentChar.category()) {
case QChar::Letter_Uppercase:
currentCategory = Upper;
break;
case QChar::Letter_Lowercase:
if (vowels.contains(currentChar)) {
currentCategory = Vowel;
} else {
currentCategory = Consonant;
}
break;
case QChar::Number_DecimalDigit:
currentCategory = Digit;
break;
default:
currentCategory = Special;
break;
}
switch (currentCategory) {
case Vowel:
if (previousCategory != Consonant) {
++count;
}
break;
case Consonant:
if (previousCategory != Vowel) {
++count;
}
break;
default:
if (previousCategory != currentCategory) {
++count;
}
break;
}
previousCategory = currentCategory;
if (!(catMask & (1 << currentCategory))) {
++catCount;
catMask |= (1 << currentCategory);
}
}
}
// passwords that have many category changes but few categories are weaker
return (count * catCount) / 5;
}
int passwordStrength(const QString &password)
{
QString sPass = password.simplified().toLower();
if (sPass.length() < 2)
return sPass.length();
int i = 0;
while (i < sPass.length()) {
// duplicate characters do not improve the length
if (sPass[i] == sPass[i + 1]) {
sPass.remove(i + 1, 1);
continue;
}
// the sequence detection is only reliable in the ASCII range
if (!sPass[i].isLetterOrNumber()) {
++i;
continue;
}
if (sPass[i].unicode() == sPass[i + 1].unicode() + 1 || sPass[i].unicode() == \
sPass[i + 1].unicode() - 1) { // Remove the old one here. Otherwise we would not \
catch 123 as a sequence sPass.remove(i, 1);
continue;
}
++i;
}
int pwstrength = (20 * sPass.length() + 80 * effectivePasswordLength(password)) / \
qMax(reasonablePasswordLength, 2); if (pwstrength < 0) {
pwstrength = 0;
} else if (pwstrength > 100) {
pwstrength = 100;
}
return pwstrength;
}
int old_effectivePasswordLength(const QString &password)
{
enum Category {
Digit,
Upper,
Vowel,
Consonant,
Special
};
Category previousCategory = Vowel;
QString vowels("aeiou");
int count = 0;
for (int i = 0; i < password.length(); ++i) {
QChar currentChar = password.at(i);
if (!password.left(i).contains(currentChar)) {
Category currentCategory;
switch (currentChar.category()) {
case QChar::Letter_Uppercase:
currentCategory = Upper;
break;
case QChar::Letter_Lowercase:
if (vowels.contains(currentChar)) {
currentCategory = Vowel;
} else {
currentCategory = Consonant;
}
break;
case QChar::Number_DecimalDigit:
currentCategory = Digit;
break;
default:
currentCategory = Special;
break;
}
switch (currentCategory) {
case Vowel:
if (previousCategory != Consonant) {
++count;
}
break;
case Consonant:
if (previousCategory != Vowel) {
++count;
}
break;
default:
if (previousCategory != currentCategory) {
++count;
}
break;
}
previousCategory = currentCategory;
}
}
return count;
}
int old_passwordStrength(const QString &password)
{
int pwstrength = (20 * password.length() + 80 * \
old_effectivePasswordLength(password)) / qMax(reasonablePasswordLength, 2); if \
(pwstrength < 0) { pwstrength = 0;
} else if (pwstrength > 100) {
pwstrength = 100;
}
return pwstrength;
}
int main(int argc, char **argv)
{
for (int i = 1; i < argc; i++)
qDebug() << old_passwordStrength(QString::fromAscii(argv[i])) << \
passwordStrength(QString::fromAscii(argv[i]));
return 0;
}
["signature.asc" (application/pgp-signature)]
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic