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.
3+ * Copyright (c) 2015-2017 Tony Evans
334 *
34- * Redistribution and use in source and binary forms, with or without modification, are
35- * permitted provided that the following conditions are met:
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:
3611 *
37- * 1. Redistributions of source code must retain the above copyright notice, this list
38- * of conditions and the following disclaimer .
12+ * The above copyright notice and this permission notice shall be included in
13+ * all copies or substantial portions of the Software .
3914 *
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.
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.
4322 *
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.
47- *
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>
7647/* Minimum number of characters in a incrementing/decrementing sequence match */
7748#define MIN_SEQUENCE_LEN 3
7849
50+ /* Maximum number of characters to perform full entropy calculation */
51+ #ifndef ZXCVBN_DETAIL_LEN
52+ #define ZXCVBN_DETAIL_LEN 100
53+ #endif
54+
7955/* Year range for data matching */
8056#define MIN_YEAR 1901
8157#define MAX_YEAR 2050
@@ -466,7 +442,7 @@ int ZxcvbnInit(const char *Filename)
466442/**********************************************************************************
467443 * Free the data allocated by ZxcvbnInit().
468444 */
469- void ZxcvbnUnInit ()
445+ void ZxcvbnUnInit (void )
470446{
471447 if (DictNodes )
472448 FreeFn (DictNodes );
@@ -514,9 +490,8 @@ typedef struct
514490 uint8_t Leeted [sizeof L33TChr ];
515491 uint8_t UnLeet [sizeof L33TChr ];
516492 uint8_t LeetCnv [sizeof L33TCnv / LEET_NORM_MAP_SIZE + 1 ];
517- /* uint8_t LeetChr[3]; */
518493 uint8_t First ;
519- uint8_t PossChars [49 ];
494+ uint8_t PossChars [CHARSET_SIZE ];
520495} DictWork_t ;
521496
522497/**********************************************************************************
@@ -959,24 +934,25 @@ typedef struct
959934 int Shifts ;
960935} SpatialMatchInfo_t ;
961936
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`" ;
937+ /* Shift mapping, characters in pairs: first is shifted, second un-shifted. Ordered for increasing shifted character code.*/
938+ /* Note: on a UK keyboard \243 is the £ (Pound stirling), \244 is the ¤ (Euro), \254 is the ¬ (Not sign) */
939+ static const uint8_t UK_Shift [] = "!1\"2$4%5&7(9)0*8:;<,>.?/@'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz^6_-{[|\\}]~#\2433\2444\254`" ;
964940static const uint8_t US_Shift [] = "!1\"'#3$4%5&7(9)0*8:;<,>.?/@2AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz^6_-{[|\\}]~`" ;
965941
966942
967- /* Neighour tables */
943+ /* Neighbour tables */
968944static const uint8_t UK_Qwerty [48 * 7 ] =
969945{
970946/* key, left, up-left, up-right, right, down-right, down-left */
971947 '#' , '\'' ,']' , 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 ,
948+ ',' , 'm' , 'k' , 'l' , '.' , 0 , 0 , '-' , '0' , 0 , 0 , '= ' , '[ ' , 'p ' ,
949+ '.' , ',' , 'l' , ';' , '/' , 0 , 0 , '/' , '.' , ';' , '\'' , 0 , 0 , 0 ,
950+ '0' , '9' , 0 , 0 , '-' , 'p' , 'o' , '1' , '`' , 0 , 0 , '2' , 'q' , 0 ,
975951 '2' , '1' , 0 , 0 , '3' , 'w' , 'q' , '3' , '2' , 0 , 0 , '4' , 'e' , 'w' ,
976952 '4' , '3' , 0 , 0 , '5' , 'r' , 'e' , '5' , '4' , 0 , 0 , '6' , 't' , 'r' ,
977953 '6' , '5' , 0 , 0 , '7' , 'y' , 't' , '7' , '6' , 0 , 0 , '8' , 'u' , 'y' ,
978954 '8' , '7' , 0 , 0 , '9' , 'i' , 'u' , '9' , '8' , 0 , 0 , '0' , 'o' , 'i' ,
979- ';' , 'l' , 'o ' , 'p ' ,'\'' , '/' , '.' , '=' , '-' , 0 , 0 , 0 , ']' , '[' ,
955+ ';' , 'l' , 'p ' , '[ ' ,'\'' , '/' , '.' , '=' , '-' , 0 , 0 , 0 , ']' , '[' ,
980956 '[' , 'p' , '-' , '=' , ']' , '\'' ,';' , '\\' , 0 , 0 , 'a' , 'z' , 0 , 0 ,
981957 ']' , '[' , '=' , 0 , 0 , '#' ,'\'' , '`' , 0 , 0 , 0 , '1' , 0 , 0 ,
982958 'a' , 0 , 'q' , 'w' , 's' , 'z' ,'\\' , 'b' , 'v' , 'g' , 'h' , 'n' , 0 , 0 ,
@@ -1022,7 +998,7 @@ static const uint8_t US_Qwerty[47*7] =
1022998 'x' , 'z' , 's' , 'd' , 'c' , 0 , 0 , 'y' , 't' , '6' , '7' , 'u' , 'h' , 'g' ,
1023999 'z' , 0 , 'a' , 's' , 'x' , 0 , 0 ,
10241000};
1025- static const uint8_t Dvorak [48 * 7 ] =
1001+ static const uint8_t Dvorak [47 * 7 ] =
10261002{
10271003 '\'' , 0 , '1' , '2' , ',' , 'a' , 0 , ',' ,'\'' , '2' , '3' , '.' , 'o' , 'a' ,
10281004 '-' , 's' , '/' , '=' , 0 , 0 , 'z' , '.' , ',' , '3' , '4' , 'p' , 'e' , 'o' ,
@@ -1180,9 +1156,9 @@ static void SpatialMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
11801156 for (CurLen = MaxLen ; CurLen >= MIN_SPATIAL_LEN ;CurLen = Len - 1 )
11811157 {
11821158 Len = 0 ;
1183- memset (& Extra , 0 , sizeof Extra );
11841159 for (k = Keyboards , Indx = 0 ; Indx < (sizeof Keyboards / sizeof Keyboards [0 ]); ++ Indx , ++ k )
11851160 {
1161+ memset (& Extra , 0 , sizeof Extra );
11861162 Len = DoSptlMatch (Passwd , CurLen , k , & Extra );
11871163 if (Len > 0 )
11881164 {
@@ -1233,7 +1209,6 @@ static void SpatialMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
12331209 p -> Length = Len ;
12341210 AddMatchRepeats (Result , p , Passwd , MaxLen );
12351211 AddResult (Result , p , MaxLen );
1236- break ;
12371212 }
12381213 }
12391214 }
@@ -1248,7 +1223,7 @@ static void SpatialMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
12481223
12491224/* The possible date formats ordered by length (d for day, m for month, */
12501225/* y for year, ? for separator) */
1251- static const char * Formats [] =
1226+ static const char * const Formats [] =
12521227{
12531228 "yyyy" ,
12541229 "d?m?yy" ,
@@ -1429,9 +1404,9 @@ static void RepeatMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start, i
14291404 if (strncmp ((const char * )Passwd , (const char * )Rpt , Len ) == 0 )
14301405 {
14311406 /* Found a repeat */
1432- int c = Cardinality (Passwd , Len );
1407+ int c1 = Cardinality (Passwd , Len );
14331408 ZxcMatch_t * p = AllocMatch ();
1434- p -> Entrpy = log ((double )c ) * Len + log (RepeatCount );
1409+ p -> Entrpy = log ((double )c1 ) * Len + log (RepeatCount );
14351410 p -> Type = (ZxcTypeMatch_t )(BRUTE_MATCH + MULTIPLE_MATCH );
14361411 p -> Length = Len * RepeatCount ;
14371412 p -> Begin = Start ;
@@ -1506,7 +1481,7 @@ static void SequenceMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
15061481 Next = Passwd [0 ] + Dir ;
15071482 if (IsDigits && (Dir > 0 ) && (Next == ('9' + 1 )) && (Passwd [1 ] == '0' ))
15081483 {
1509- /* Incrementing digits, consider '0' to be same as a 'ten' character */
1484+ /* Incrementing digits, consider '0' to be same as a 'ten' character */
15101485 ++ Len ;
15111486 ++ Passwd ;
15121487 break ;
@@ -1515,8 +1490,9 @@ static void SequenceMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
15151490 {
15161491 ++ Len ;
15171492 ++ Passwd ;
1493+ break ;
15181494 }
1519- else if ((Next > SetHigh ) || (Next < SetLow ) || (Passwd [1 ] != Next ))
1495+ if ((Next > SetHigh ) || (Next < SetLow ) || (Passwd [1 ] != Next ))
15201496 break ;
15211497 ++ Len ;
15221498 ++ Passwd ;
@@ -1577,7 +1553,7 @@ static void SequenceMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
15771553 *
15781554 * Dijkstra's algorithm finds the combination of these part matches (or paths)
15791555 * which gives the lowest entropy (or smallest distance) from begining to end
1580- * of the password.
1556+ * of the password.
15811557 */
15821558
15831559/* Struct to hold the data of a node (imaginary point between password characters) */
@@ -1598,15 +1574,20 @@ double ZxcvbnMatch(const char *Pwd, const char *UserDict[], ZxcMatch_t **Info)
15981574 ZxcMatch_t * Zp ;
15991575 Node_t * Np ;
16001576 double e ;
1601- int Len = strlen (Pwd );
1577+ int FullLen = strlen (Pwd );
1578+ int Len = FullLen ;
16021579 const uint8_t * Passwd = (const uint8_t * )Pwd ;
16031580 uint8_t * RevPwd ;
16041581 /* Create the paths */
1605- Node_t * Nodes = MallocFn (Node_t , Len + 1 );
1606- memset (Nodes , 0 , (Len + 1 ) * sizeof * Nodes );
1582+ Node_t * Nodes = MallocFn (Node_t , Len + 2 );
1583+ memset (Nodes , 0 , (Len + 2 ) * sizeof * Nodes );
16071584 i = Cardinality (Passwd , Len );
16081585 e = log ((double )i );
16091586
1587+ /* Limit length used to full entropy estimation to prevent excessive calculation time */
1588+ if (Len > ZXCVBN_DETAIL_LEN )
1589+ Len = ZXCVBN_DETAIL_LEN ;
1590+
16101591 /* Do matching for all parts of the password */
16111592 for (i = 0 ; i < Len ; ++ i )
16121593 {
@@ -1666,7 +1647,6 @@ double ZxcvbnMatch(const char *Pwd, const char *UserDict[], ZxcMatch_t **Info)
16661647 for (i = 0 ; i < Len ; ++ i )
16671648 {
16681649 int MaxLen = Len - i ;
1669- int j ;
16701650 if (!RevPwd [i ])
16711651 continue ;
16721652 for (j = i + 1 ; j <= Len ; ++ j )
@@ -1683,14 +1663,28 @@ double ZxcvbnMatch(const char *Pwd, const char *UserDict[], ZxcMatch_t **Info)
16831663 }
16841664 }
16851665 FreeFn (RevPwd );
1666+ if (FullLen > Len )
1667+ {
1668+ /* Only the first MAX_DETAIL_LEN characters are used for full entropy estimation, for */
1669+ /* very long passwords the remainding characters are treated as being a incrementing */
1670+ /* sequence. This will give a low (and safe) entropy value for them. */
1671+ Nodes [Len ].Dist = DBL_MAX ;
1672+ Zp = AllocMatch ();
1673+ Zp -> Type = LONG_PWD_MATCH ;
1674+ Zp -> Begin = Len ;
1675+ /* Length is negative as only one extra node to represent many extra characters */
1676+ Zp -> Length = Len - FullLen ;
1677+ Zp -> Entrpy = log (2 * (FullLen - Len ));
1678+ AddResult (& (Nodes [i ].Paths ), Zp , FullLen - Len );
1679+ ++ Len ;
1680+ }
16861681 /* End node has infinite distance/entropy, start node has 0 distance */
1687- Nodes [i ].Dist = DBL_MAX ;
1682+ Nodes [Len ].Dist = DBL_MAX ;
16881683 Nodes [0 ].Dist = 0.0 ;
16891684
16901685 /* Reduce the paths using Dijkstra's algorithm */
16911686 for (i = 0 ; i < Len ; ++ i )
16921687 {
1693- int j ;
16941688 double MinDist = DBL_MAX ;
16951689 int MinIdx = 0 ;
16961690 /* Find the unvisited node with minimum distance or entropy */
@@ -1712,18 +1706,19 @@ double ZxcvbnMatch(const char *Pwd, const char *UserDict[], ZxcMatch_t **Info)
17121706 /* update if the new distance is smaller. */
17131707 for (Zp = Np -> Paths ; Zp ; Zp = Zp -> Next )
17141708 {
1715- Node_t * Ep = Np + Zp -> Length ;
1709+ Node_t * Ep ;
17161710 double d = e + Zp -> MltEnpy ;
1711+ if (Zp -> Length >= 0 )
1712+ Ep = Np + Zp -> Length ;
1713+ else
1714+ Ep = Np + 1 ;
17171715 if (!Ep -> Visit && (d < Ep -> Dist ))
17181716 {
17191717 /* Update as lower dist, also remember the 'from' node */
17201718 Ep -> Dist = d ;
17211719 Ep -> From = Zp ;
17221720 }
17231721 }
1724- /* If we got to the end node stop early */
1725- /*if (Nodes[Len].Dist < DBL_MAX/2.0) */
1726- /* break; */
17271722 }
17281723 /* Make e hold entropy result and adjust to log base 2 */
17291724 e = Nodes [Len ].Dist / log (2.0 );
@@ -1748,6 +1743,8 @@ double ZxcvbnMatch(const char *Pwd, const char *UserDict[], ZxcMatch_t **Info)
17481743 /* Adjust the entropy to log to base 2 */
17491744 Xp -> Entrpy /= log (2.0 );
17501745 Xp -> MltEnpy /= log (2.0 );
1746+ if (Xp -> Length < 0 )
1747+ Xp -> Length = - Xp -> Length ;
17511748
17521749 /* Put previous part at head of info list */
17531750 Xp -> Next = * Info ;
0 commit comments