Follow up r78003, forgot to make MonoBook use the footericons code.
[lhc/web/wiklou.git] / skins / common / password.js
1 /**
2 * Password strength checker
3 * @license WTFPL 2.0
4 * All scores are ranged approximately 0 (total disaster) - 100 (_looks_ great)
5 * @todo Check for popular passwords and keyboard sequences (QWERTY, etc)
6 */
7
8 // Estimates how hard it would be to pick the password using brute force
9 window.bruteForceComplexity = function( pwd ) {
10 var score = pwd.length * 5;
11
12 var regexes = [
13 /[a-z]/,
14 /[A-Z]/,
15 /[0-9]/,
16 /[-_;:\.,'"`~!@#$%\^&\*\(\)\[\]\{\} ]/
17 ];
18
19 var charClasses = 0;
20 for ( var i=0; i< regexes.length; i++ ) {
21 if ( pwd.match( regexes[i] ) ) {
22 charClasses++;
23 }
24 }
25
26 var matches = pwd.match( /[\x80-\uFFFF]/g );
27 if ( matches ) {
28 charClasses++;
29
30 var s = matches.join( '' );
31 // poor man's isUpper() and isLower()
32 if ( s != s.toLowerCase() && s != s.toUpperCase() ) {
33 charClasses++;
34 }
35 }
36 score += ( charClasses - 1 ) * 10;
37
38 return score;
39 };
40
41 // Calculates a penalty to brute force score due to character repetition
42 window.repetitionAdjustment = function( pwd ) {
43 var unique = '';
44 for ( var i=0; i< pwd.length; i++ ) {
45 if ( unique.indexOf( pwd[i] ) < 0 ) {
46 unique += pwd[i];
47 }
48 }
49 var ratio = pwd.length / unique.length - 0.4; // allow up to 40% repetition, reward for less, penalize for more
50
51 return ratio * 10;
52 };
53
54 // Checks how many simple sequences ("abc", "321") are there in the password
55 window.sequenceScore = function( pwd ) {
56 pwd = pwd.concat( '\0' );
57 var score = 100, sequence = 1;
58 for ( var i = 1; i < pwd.length; i++ ) {
59 if ( pwd.charCodeAt( i ) == pwd.charCodeAt(i - 1) + 1 ) {
60 sequence++;
61 } else {
62 if ( sequence > 2 ) {
63 score -= sequence * 7;
64 }
65 sequence = 1;
66 }
67 }
68 for ( var i = 1; i < pwd.length; i++ ) {
69 if ( pwd.charCodeAt( i ) == pwd.charCodeAt(i - 1) - 1 ) {
70 sequence++;
71 } else {
72 if ( sequence > 2 ) {
73 score -= Math.sqrt( sequence ) * 15;
74 }
75 sequence = 1;
76 }
77 }
78 return score;
79 };
80
81 (function( $ ) {
82 function passwordChanged() {
83 retypeChanged();
84 var pwd = $( passwordSecurity.password ).val();
85 if ( pwd == '' ) {
86 $( '#password-strength' ).html( '' );
87 return;
88 }
89 if ( pwd.length > 100 ) pwd = pwd.slice( 0, 100 );
90 var scores = [
91 bruteForceComplexity( pwd ),
92 repetitionAdjustment( pwd ),
93 sequenceScore( pwd )
94 ];
95
96 var score = Math.min( scores[0] - scores[1], scores[2] );
97 var result = 'good';
98 if ( score < 40 ) {
99 result = 'bad';
100 } else if ( score < 60 ) {
101 result = 'mediocre';
102 } else if ( score < 80 ) {
103 result = 'acceptable';
104 }
105 var message = '<span class="mw-password-' + result + '">' + passwordSecurity.messages['password-strength-' + result]
106 + '</span>';
107 $( '#password-strength' ).html(
108 passwordSecurity.messages['password-strength'].replace( '$1', message )
109 //+ scores
110 );
111 }
112
113 function retypeChanged() {
114 var pwd = $( passwordSecurity.password ).val();
115 var retype = $( passwordSecurity.retype ).val();
116 var message;
117 if ( pwd == '' || pwd == retype ) {
118 message = '';
119 } else if ( retype == '' ) {
120 message = passwordSecurity.messages['password-retype'];
121 } else {
122 message = passwordSecurity.messages['password-retype-mismatch'];
123 }
124 $( '#password-retype' ).html( message );
125 }
126
127 $( document ).ready( function() {
128 $( passwordSecurity.password ).bind( 'keyup change', passwordChanged );
129 $( passwordSecurity.retype ).bind( 'keyup change', retypeChanged );
130 })
131 })(jQuery);