1- // Copyright (c) 2014-2024, The Monero Project
2- //
3- // All rights reserved.
4- //
5- // Redistribution and use in source and binary forms, with or without modification, are
6- // permitted provided that the following conditions are met:
7- //
8- // 1. Redistributions of source code must retain the above copyright notice, this list of
9- // conditions and the following disclaimer.
10- //
11- // 2. Redistributions in binary form must reproduce the above copyright notice, this list
12- // of conditions and the following disclaimer in the documentation and/or other
13- // materials provided with the distribution.
14- //
15- // 3. Neither the name of the copyright holder nor the names of its contributors may be
16- // used to endorse or promote products derived from this software without specific
17- // prior written permission.
18- //
19- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20- // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21- // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22- // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23- // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24- // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25- // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26- // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27- // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28-
29- /**********************************************************************************
1+ /**********************************************************************************
302 * C implementation of the zxcvbn password strength estimation method.
31- * Copyright (c) 2015, Tony Evans
32- * All rights reserved.
33- *
34- * Redistribution and use in source and binary forms, with or without modification, are
35- * permitted provided that the following conditions are met:
3+ * Copyright (c) 2015-2017 Tony Evans
364 *
37- * 1. Redistributions of source code must retain the above copyright notice, this list
38- * of conditions and the following disclaimer.
5+ * Permission is hereby granted, free of charge, to any person obtaining a copy
6+ * of this software and associated documentation files (the "Software"), to deal
7+ * in the Software without restriction, including without limitation the rights
8+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+ * copies of the Software, and to permit persons to whom the Software is
10+ * furnished to do so, subject to the following conditions:
3911 *
40- * 2. Redistributions in binary form must reproduce the above copyright notice, this
41- * list of conditions and the following disclaimer in the documentation and/or other
42- * materials provided with the distribution.
12+ * The above copyright notice and this permission notice shall be included in
13+ * all copies or substantial portions of the Software.
4314 *
44- * 3. Neither the name of the copyright holder nor the names of its contributors may be
45- * used to endorse or promote products derived from this software without specific
46- * prior written permission.
15+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+ * THE SOFTWARE.
4722 *
48- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
49- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
50- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
51- * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
52- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
53- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
54- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
55- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
56- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
57- * DAMAGE.
58- *
5923 **********************************************************************************/
6024
61- #include " zxcvbn.h"
25+ #include < zxcvbn.h>
6226#include <ctype.h>
6327#include <string.h>
6428#include <stdint.h>
6529#include <math.h>
6630#include <float.h>
6731
32+ /* printf */
33+ #ifdef __cplusplus
34+ #include <cstdio>
35+ #else
36+ #include <stdio.h>
37+ #endif
38+
6839#ifdef USE_DICT_FILE
6940#if defined(USE_FILE_IO ) || !defined(__cplusplus )
7041#include <stdio.h>
7344#endif
7445#endif
7546
47+ /* For pre-compiled headers under windows */
48+ #ifdef _WIN32
49+ #include "stdafx.h"
50+ #endif
51+
7652/* Minimum number of characters in a incrementing/decrementing sequence match */
7753#define MIN_SEQUENCE_LEN 3
7854
55+ /* Maximum number of characters to perform full entropy calculation */
56+ #ifndef ZXCVBN_DETAIL_LEN
57+ #define ZXCVBN_DETAIL_LEN 100
58+ #endif
59+
7960/* Year range for data matching */
8061#define MIN_YEAR 1901
8162#define MAX_YEAR 2050
@@ -466,7 +447,7 @@ int ZxcvbnInit(const char *Filename)
466447/**********************************************************************************
467448 * Free the data allocated by ZxcvbnInit().
468449 */
469- void ZxcvbnUnInit ()
450+ void ZxcvbnUnInit (void )
470451{
471452 if (DictNodes )
472453 FreeFn (DictNodes );
@@ -514,9 +495,8 @@ typedef struct
514495 uint8_t Leeted [sizeof L33TChr ];
515496 uint8_t UnLeet [sizeof L33TChr ];
516497 uint8_t LeetCnv [sizeof L33TCnv / LEET_NORM_MAP_SIZE + 1 ];
517- /* uint8_t LeetChr[3]; */
518498 uint8_t First ;
519- uint8_t PossChars [49 ];
499+ uint8_t PossChars [CHARSET_SIZE ];
520500} DictWork_t ;
521501
522502/**********************************************************************************
@@ -959,24 +939,25 @@ typedef struct
959939 int Shifts ;
960940} SpatialMatchInfo_t ;
961941
962- /* Shift mapping, characters in pairs: first is shifted, second un-shifted. */
963- static const uint8_t UK_Shift [] = "!1\"2$4%5&7(9)0*8:;<,>.?/@'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz^6_-{[|\\}]~#\x80" "4\xA3" "3\xAC`" ;
942+ /* Shift mapping, characters in pairs: first is shifted, second un-shifted. Ordered for increasing shifted character code.*/
943+ /* Note: on a UK keyboard \243 is the £ (Pound stirling), \244 is the ¤ (Euro), \254 is the ¬ (Not sign) */
944+ static const uint8_t UK_Shift [] = "!1\"2$4%5&7(9)0*8:;<,>.?/@'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz^6_-{[|\\}]~#\2433\2444\254`" ;
964945static const uint8_t US_Shift [] = "!1\"'#3$4%5&7(9)0*8:;<,>.?/@2AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz^6_-{[|\\}]~`" ;
965946
966947
967- /* Neighour tables */
948+ /* Neighbour tables */
968949static const uint8_t UK_Qwerty [48 * 7 ] =
969950{
970951/* key, left, up-left, up-right, right, down-right, down-left */
971952 '#' , '\'' ,']' , 0 , 0 , 0 , 0 , '\'' ,';' , '[' , ']' , '#' , 0 , '/' ,
972- ',' , 'm' , 'k' , 'l' , '.' , 0 , 0 , '-' , '0' , 0 , 0 , '- ' , 'p ' , 'o ' ,
973- '.' , ',' , 'l' , ';' , '/' , 0 , 0 , '/' , '.' , ';' , '\'' , 0 , 0 , 0 ,
974- '0' , '9' , 0 , 0 , '-' , 'p' , 'o' , '1' , '`' , 0 , 0 , '2' , 'q' , 0 ,
953+ ',' , 'm' , 'k' , 'l' , '.' , 0 , 0 , '-' , '0' , 0 , 0 , '= ' , '[ ' , 'p ' ,
954+ '.' , ',' , 'l' , ';' , '/' , 0 , 0 , '/' , '.' , ';' , '\'' , 0 , 0 , 0 ,
955+ '0' , '9' , 0 , 0 , '-' , 'p' , 'o' , '1' , '`' , 0 , 0 , '2' , 'q' , 0 ,
975956 '2' , '1' , 0 , 0 , '3' , 'w' , 'q' , '3' , '2' , 0 , 0 , '4' , 'e' , 'w' ,
976957 '4' , '3' , 0 , 0 , '5' , 'r' , 'e' , '5' , '4' , 0 , 0 , '6' , 't' , 'r' ,
977958 '6' , '5' , 0 , 0 , '7' , 'y' , 't' , '7' , '6' , 0 , 0 , '8' , 'u' , 'y' ,
978959 '8' , '7' , 0 , 0 , '9' , 'i' , 'u' , '9' , '8' , 0 , 0 , '0' , 'o' , 'i' ,
979- ';' , 'l' , 'o ' , 'p ' ,'\'' , '/' , '.' , '=' , '-' , 0 , 0 , 0 , ']' , '[' ,
960+ ';' , 'l' , 'p ' , '[ ' ,'\'' , '/' , '.' , '=' , '-' , 0 , 0 , 0 , ']' , '[' ,
980961 '[' , 'p' , '-' , '=' , ']' , '\'' ,';' , '\\' , 0 , 0 , 'a' , 'z' , 0 , 0 ,
981962 ']' , '[' , '=' , 0 , 0 , '#' ,'\'' , '`' , 0 , 0 , 0 , '1' , 0 , 0 ,
982963 'a' , 0 , 'q' , 'w' , 's' , 'z' ,'\\' , 'b' , 'v' , 'g' , 'h' , 'n' , 0 , 0 ,
@@ -1022,7 +1003,7 @@ static const uint8_t US_Qwerty[47*7] =
10221003 'x' , 'z' , 's' , 'd' , 'c' , 0 , 0 , 'y' , 't' , '6' , '7' , 'u' , 'h' , 'g' ,
10231004 'z' , 0 , 'a' , 's' , 'x' , 0 , 0 ,
10241005};
1025- static const uint8_t Dvorak [48 * 7 ] =
1006+ static const uint8_t Dvorak [47 * 7 ] =
10261007{
10271008 '\'' , 0 , '1' , '2' , ',' , 'a' , 0 , ',' ,'\'' , '2' , '3' , '.' , 'o' , 'a' ,
10281009 '-' , 's' , '/' , '=' , 0 , 0 , 'z' , '.' , ',' , '3' , '4' , 'p' , 'e' , 'o' ,
@@ -1180,9 +1161,9 @@ static void SpatialMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
11801161 for (CurLen = MaxLen ; CurLen >= MIN_SPATIAL_LEN ;CurLen = Len - 1 )
11811162 {
11821163 Len = 0 ;
1183- memset (& Extra , 0 , sizeof Extra );
11841164 for (k = Keyboards , Indx = 0 ; Indx < (sizeof Keyboards / sizeof Keyboards [0 ]); ++ Indx , ++ k )
11851165 {
1166+ memset (& Extra , 0 , sizeof Extra );
11861167 Len = DoSptlMatch (Passwd , CurLen , k , & Extra );
11871168 if (Len > 0 )
11881169 {
@@ -1233,7 +1214,6 @@ static void SpatialMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
12331214 p -> Length = Len ;
12341215 AddMatchRepeats (Result , p , Passwd , MaxLen );
12351216 AddResult (Result , p , MaxLen );
1236- break ;
12371217 }
12381218 }
12391219 }
@@ -1248,7 +1228,7 @@ static void SpatialMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
12481228
12491229/* The possible date formats ordered by length (d for day, m for month, */
12501230/* y for year, ? for separator) */
1251- static const char * Formats [] =
1231+ static const char * const Formats [] =
12521232{
12531233 "yyyy" ,
12541234 "d?m?yy" ,
@@ -1429,9 +1409,9 @@ static void RepeatMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start, i
14291409 if (strncmp ((const char * )Passwd , (const char * )Rpt , Len ) == 0 )
14301410 {
14311411 /* Found a repeat */
1432- int c = Cardinality (Passwd , Len );
1412+ int c1 = Cardinality (Passwd , Len );
14331413 ZxcMatch_t * p = AllocMatch ();
1434- p -> Entrpy = log ((double )c ) * Len + log (RepeatCount );
1414+ p -> Entrpy = log ((double )c1 ) * Len + log (RepeatCount );
14351415 p -> Type = (ZxcTypeMatch_t )(BRUTE_MATCH + MULTIPLE_MATCH );
14361416 p -> Length = Len * RepeatCount ;
14371417 p -> Begin = Start ;
@@ -1506,7 +1486,7 @@ static void SequenceMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
15061486 Next = Passwd [0 ] + Dir ;
15071487 if (IsDigits && (Dir > 0 ) && (Next == ('9' + 1 )) && (Passwd [1 ] == '0' ))
15081488 {
1509- /* Incrementing digits, consider '0' to be same as a 'ten' character */
1489+ /* Incrementing digits, consider '0' to be same as a 'ten' character */
15101490 ++ Len ;
15111491 ++ Passwd ;
15121492 break ;
@@ -1515,8 +1495,9 @@ static void SequenceMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
15151495 {
15161496 ++ Len ;
15171497 ++ Passwd ;
1498+ break ;
15181499 }
1519- else if ((Next > SetHigh ) || (Next < SetLow ) || (Passwd [1 ] != Next ))
1500+ if ((Next > SetHigh ) || (Next < SetLow ) || (Passwd [1 ] != Next ))
15201501 break ;
15211502 ++ Len ;
15221503 ++ Passwd ;
@@ -1577,7 +1558,7 @@ static void SequenceMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
15771558 *
15781559 * Dijkstra's algorithm finds the combination of these part matches (or paths)
15791560 * which gives the lowest entropy (or smallest distance) from begining to end
1580- * of the password.
1561+ * of the password.
15811562 */
15821563
15831564/* Struct to hold the data of a node (imaginary point between password characters) */
@@ -1598,15 +1579,20 @@ double ZxcvbnMatch(const char *Pwd, const char *UserDict[], ZxcMatch_t **Info)
15981579 ZxcMatch_t * Zp ;
15991580 Node_t * Np ;
16001581 double e ;
1601- int Len = strlen (Pwd );
1582+ int FullLen = strlen (Pwd );
1583+ int Len = FullLen ;
16021584 const uint8_t * Passwd = (const uint8_t * )Pwd ;
16031585 uint8_t * RevPwd ;
16041586 /* Create the paths */
1605- Node_t * Nodes = MallocFn (Node_t , Len + 1 );
1606- memset (Nodes , 0 , (Len + 1 ) * sizeof * Nodes );
1587+ Node_t * Nodes = MallocFn (Node_t , Len + 2 );
1588+ memset (Nodes , 0 , (Len + 2 ) * sizeof * Nodes );
16071589 i = Cardinality (Passwd , Len );
16081590 e = log ((double )i );
16091591
1592+ /* Limit length used to full entropy estimation to prevent excessive calculation time */
1593+ if (Len > ZXCVBN_DETAIL_LEN )
1594+ Len = ZXCVBN_DETAIL_LEN ;
1595+
16101596 /* Do matching for all parts of the password */
16111597 for (i = 0 ; i < Len ; ++ i )
16121598 {
@@ -1666,7 +1652,6 @@ double ZxcvbnMatch(const char *Pwd, const char *UserDict[], ZxcMatch_t **Info)
16661652 for (i = 0 ; i < Len ; ++ i )
16671653 {
16681654 int MaxLen = Len - i ;
1669- int j ;
16701655 if (!RevPwd [i ])
16711656 continue ;
16721657 for (j = i + 1 ; j <= Len ; ++ j )
@@ -1683,14 +1668,28 @@ double ZxcvbnMatch(const char *Pwd, const char *UserDict[], ZxcMatch_t **Info)
16831668 }
16841669 }
16851670 FreeFn (RevPwd );
1671+ if (FullLen > Len )
1672+ {
1673+ /* Only the first MAX_DETAIL_LEN characters are used for full entropy estimation, for */
1674+ /* very long passwords the remainding characters are treated as being a incrementing */
1675+ /* sequence. This will give a low (and safe) entropy value for them. */
1676+ Nodes [Len ].Dist = DBL_MAX ;
1677+ Zp = AllocMatch ();
1678+ Zp -> Type = LONG_PWD_MATCH ;
1679+ Zp -> Begin = Len ;
1680+ /* Length is negative as only one extra node to represent many extra characters */
1681+ Zp -> Length = Len - FullLen ;
1682+ Zp -> Entrpy = log (2 * (FullLen - Len ));
1683+ AddResult (& (Nodes [i ].Paths ), Zp , FullLen - Len );
1684+ ++ Len ;
1685+ }
16861686 /* End node has infinite distance/entropy, start node has 0 distance */
1687- Nodes [i ].Dist = DBL_MAX ;
1687+ Nodes [Len ].Dist = DBL_MAX ;
16881688 Nodes [0 ].Dist = 0.0 ;
16891689
16901690 /* Reduce the paths using Dijkstra's algorithm */
16911691 for (i = 0 ; i < Len ; ++ i )
16921692 {
1693- int j ;
16941693 double MinDist = DBL_MAX ;
16951694 int MinIdx = 0 ;
16961695 /* Find the unvisited node with minimum distance or entropy */
@@ -1712,18 +1711,19 @@ double ZxcvbnMatch(const char *Pwd, const char *UserDict[], ZxcMatch_t **Info)
17121711 /* update if the new distance is smaller. */
17131712 for (Zp = Np -> Paths ; Zp ; Zp = Zp -> Next )
17141713 {
1715- Node_t * Ep = Np + Zp -> Length ;
1714+ Node_t * Ep ;
17161715 double d = e + Zp -> MltEnpy ;
1716+ if (Zp -> Length >= 0 )
1717+ Ep = Np + Zp -> Length ;
1718+ else
1719+ Ep = Np + 1 ;
17171720 if (!Ep -> Visit && (d < Ep -> Dist ))
17181721 {
17191722 /* Update as lower dist, also remember the 'from' node */
17201723 Ep -> Dist = d ;
17211724 Ep -> From = Zp ;
17221725 }
17231726 }
1724- /* If we got to the end node stop early */
1725- /*if (Nodes[Len].Dist < DBL_MAX/2.0) */
1726- /* break; */
17271727 }
17281728 /* Make e hold entropy result and adjust to log base 2 */
17291729 e = Nodes [Len ].Dist / log (2.0 );
@@ -1748,6 +1748,8 @@ double ZxcvbnMatch(const char *Pwd, const char *UserDict[], ZxcMatch_t **Info)
17481748 /* Adjust the entropy to log to base 2 */
17491749 Xp -> Entrpy /= log (2.0 );
17501750 Xp -> MltEnpy /= log (2.0 );
1751+ if (Xp -> Length < 0 )
1752+ Xp -> Length = - Xp -> Length ;
17511753
17521754 /* Put previous part at head of info list */
17531755 Xp -> Next = * Info ;
0 commit comments