[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-core-devel
Subject:    KPasswordDialog, password strength meter
From:       PJ <PJaenicke () gmx ! net>
Date:       2005-03-22 21:05:25
Message-ID: 200503222205.25302.PJaenicke () gmx ! net
[Download RAW message or body]

Hello,

its great to see that there is now a passwort strength meter in KDE, because 
IMHO bad passwords are the most common security problem.

But in my opinion the code which calculates the password strength has some 
shortcomings.
It doesn't check for stupid combinations and the repetition of strings.
So i worked on an algorithm to address this issues. 

It works as follows:
 - A character is put in one category (lowercase,uppercase,digit,other)
- Two succeeding characters are evaluated. Every combination of categories has 
a special rating (e.g. digit following a digit => rating=4; digit following a 
uppercase => rating=10)
- The password strength is the sum of all the ratings.

Since combinations which are thought to be strange have a higher rating, the 
user is rewarded for using them.

Additionally there is a check for simple combinations:
- The ascii-code-difference of two succeeding characters is calculated and 
stored in a list.
- If the current difference is in the list, the rating is set to zero.

Here are two examples of bad passwords which get a high rating from the 
Firefox code, but a low with the new proposal:

password    Firefox   new Proposal
----------------------------------
111111        60           4
a1.a1.        80          32

I would be happy if i could enhance KDE with this code, even if it is only a 
tiny detail.

PJ


["kpassdlg.diff" (text/x-diff)]

--- orginal/kpassdlg.cpp	2005-03-10 21:34:19.000000000 +0100
+++ kpassdlg.cpp	2005-03-22 21:03:16.000000000 +0100
@@ -616,30 +616,81 @@
       }
 
       // Password strength calculator
-      // Based on code in the Master Password dialog in Firefox
-      // (pref-masterpass.js)
-      // Original code triple-licensed under the MPL, GPL, and LGPL
-      // so is license-compatible with this file
 
       const double lengthFactor = d->reasonablePasswordLength / 8.0;
 
-      
-      int pwlength = (int) (pass.length() / lengthFactor);
-      if (pwlength > 5) pwlength = 5;
-
-      const QRegExp numRxp("[0-9]", true, false);
-      int numeric = (int) (pass.contains(numRxp) / lengthFactor);
-      if (numeric > 3) numeric = 3;
-
-      const QRegExp symbRxp("\\W", false, false);
-      int numsymbols = (int) (pass.contains(symbRxp) / lengthFactor);
-      if (numsymbols > 3) numsymbols = 3;
+      const char* pw = m_pEdit->password();
+         
+      enum Category{Lower=0,Upper,Digit,Other,Inval};
 
-      const QRegExp upperRxp("[A-Z]", true, false);
-      int upper = (int) (pass.contains(upperRxp) / lengthFactor);
-      if (upper > 3) upper = 3;
+     //                   prev is =  L  U  D  O  I
+      const int rating[5][5] =  { {  6, 8,10,11, 0 },   // current is Lower
+                                  {  8, 7,10,11, 0 },   // current is Upper
+                                  { 10,10, 4,11, 0 },   // current is Digit
+                                  { 11,11,11, 7, 0 },   // current is Other
+                                  {  0, 0, 0, 0, 0 } }; // current is Inval
+                                  
+      int maximumCharPerCategory[5] = {5,5,5,5,5};              
+      
+      bool pastDiff[256];
+      for(int i=0 ; i<=255 ; i++ ){
+        pastDiff[i] = false;
+      }
+      
+      Category currentCat = Inval;
+      Category prevCat    = Inval;
+                                  
+      int pwlen = strlen(pw);
+      int pwstrength = 0;
+      unsigned char prevASCII = 205;  
+        
+      
+      for (int i=0; i < pwlen ;i++)   //for each character
+      {
+          currentCat = Other;
+          if (( pw[i] >= 48 ) and (pw[i] <= 57)) {currentCat = Digit;};
+          if (( pw[i] >= 65 ) and (pw[i] <= 90)) {currentCat = Upper;};
+          if (( pw[i] >= 97 ) and (pw[i] <=122)) {currentCat = Lower;};
+          
+          int charRating = rating[currentCat][prevCat];
+          
+          if ( maximumCharPerCategory[currentCat] == 0 ) {
+            charRating = 0;
+          }
+          else {
+            maximumCharPerCategory[currentCat]--;
+          }
+        
+          unsigned char diff = pw[i] - prevASCII;
+          if (pastDiff[diff]) {
+             charRating = 0; 
+          } 
+          else {                   
+             pastDiff[diff] = true; 
+          }
+          
+          pwstrength += charRating; 
+          
+          prevCat = currentCat;
+          prevASCII = pw[i];
+      }
+      
+      // delete variables
+      currentCat = Inval;
+      prevCat    = Inval;
+      pwlen      = 0;
+      prevASCII   = 205;
+      
+      for(int i=0 ; i<=255 ; i++ ){
+          pastDiff[i] = false;
+      }
+      
+      for(int i=0 ; i<5 ; i++ ){
+         maximumCharPerCategory[i]=0;
+      }
 
-      int pwstrength=((pwlength*10)-20) + (numeric*10) + (numsymbols*15) + (upper*10);
+      
+      pwstrength = (int)(pwstrength / lengthFactor);
 
       if ( pwstrength < 0 ) {
 	      pwstrength = 0;
@@ -689,7 +740,7 @@
     return d->maximumPasswordLength;
 }
 
 void KPasswordDialog::setReasonablePasswordLength(int reasonableLength) {
 


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic