Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/zxcvbn-c/zxcvbn.c
Line
Count
Source (jump to first uncovered line)
1
/**********************************************************************************
2
 * C implementation of the zxcvbn password strength estimation method.
3
 * Copyright (c) 2015-2017 Tony Evans
4
 *
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:
11
 *
12
 * The above copyright notice and this permission notice shall be included in
13
 * all copies or substantial portions of the Software.
14
 *
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.
22
 *
23
 **********************************************************************************/
24
25
#include <zxcvbn.h>
26
#include <ctype.h>
27
#include <string.h>
28
#include <stdint.h>
29
#include <math.h>
30
#include <float.h>
31
32
/* printf */
33
#ifdef __cplusplus
34
#include <cstdio>
35
#else
36
#include <stdio.h>
37
#endif
38
39
#ifdef USE_DICT_FILE
40
#if defined(USE_FILE_IO) || !defined(__cplusplus)
41
#include <stdio.h>
42
#else
43
#include <fstream>
44
#endif
45
#endif
46
47
/* Minimum number of characters in a incrementing/decrementing sequence match */
48
0
#define MIN_SEQUENCE_LEN 3
49
50
/* Maximum number of characters to perform full entropy calculation */
51
#ifndef ZXCVBN_DETAIL_LEN
52
0
#define ZXCVBN_DETAIL_LEN 100
53
#endif
54
55
/* Year range for data matching */
56
0
#define MIN_YEAR 1901
57
0
#define MAX_YEAR 2050
58
59
/* Minimum number of characters in a spatial matching sequence */
60
0
#define MIN_SPATIAL_LEN 3
61
62
/* Minimum number of characters in a repeat sequence match */
63
0
#define MIN_REPEAT_LEN 2
64
65
/* Additional entropy to add when password is made of multiple matches. Use different
66
 * amounts depending on whether the match is at the end of the password, or in the
67
 * middle. If the match is at the begining then there is no additional entropy.
68
 */
69
0
#define MULTI_END_ADDITION 1.0
70
0
#define MULTI_MID_ADDITION 1.75
71
72
/*################################################################################*
73
 *################################################################################*
74
 * Begin utility function code
75
 *################################################################################*
76
 *################################################################################*/
77
78
/**********************************************************************************
79
 * Binomial coefficient function. Uses method described at
80
 *      http://blog.plover.com/math/choose.html
81
 */
82
static double nCk(int n, int k)
83
0
{
84
0
    int d;
85
0
    double r;
86
0
    if (k > n)
87
0
        return 0.0;
88
0
    if (!k)
89
0
        return 1.0;
90
0
    r = 1.0;
91
0
    for(d = 1; d <= k; ++d)
92
0
    {
93
0
        r *= n--;
94
0
        r /= d;
95
0
    }
96
0
    return r;
97
0
}
98
99
/**********************************************************************************
100
 * Binary search function to find a character in a string.
101
 * Parameters:
102
 *  Ch      The character to find
103
 *  Ents    The string to search
104
 *  NumEnts The number character groups in the string Ents
105
 *  SizeEnt The size of each character group.
106
 * Returns a pointer to the found character, or null if not found.
107
 */
108
static const uint8_t *CharBinSearch(uint8_t Ch, const uint8_t *Ents, unsigned int NumEnts, unsigned int SizeEnt)
109
0
{
110
0
    while(NumEnts > 0)
111
0
    {
112
0
        const uint8_t *Mid = Ents + (NumEnts >> 1) * SizeEnt;
113
0
        int Dif = Ch - *Mid;
114
0
        if (!Dif)
115
0
        {
116
0
            return Mid;
117
0
        }
118
0
        if (Dif > 0)
119
0
        {
120
0
            Ents = Mid + SizeEnt;
121
0
            --NumEnts;
122
0
        }
123
0
        NumEnts /= 2;
124
0
    }
125
0
    return 0;
126
0
}
127
128
/**********************************************************************************
129
 * Calculate potential number of different characters in the passed string.
130
 * Parameters:
131
 *  Str     The string of characters
132
 *  Len     The maximum number of characters to scan
133
 * Returns the potential number of different characters in the string.
134
 */
135
static int Cardinality(const uint8_t *Str, int Len)
136
0
{
137
0
    int Card=0, Types=0;
138
0
    int c;
139
0
    while(Len > 0)
140
0
    {
141
0
        c = *Str++ & 0xFF;
142
0
        if (!c)
143
0
            break;
144
0
        if (islower(c))      Types |= 1;    /* Lowercase letter */
145
0
        else if (isupper(c)) Types |= 2;    /* Uppercase letter */
146
0
        else if (isdigit(c)) Types |= 4;    /* Numeric digit */
147
0
        else if (c <= 0x7F)  Types |= 8;    /* Punctuation character */
148
0
        else                 Types |= 16;   /* Other (Unicode?) */
149
0
        --Len;
150
0
    }
151
0
    if (Types & 1)  Card += 26;
152
0
    if (Types & 2)  Card += 26;
153
0
    if (Types & 4)  Card += 10;
154
0
    if (Types & 8)  Card += 33;
155
0
    if (Types & 16) Card += 100;
156
0
    return Card;
157
0
}
158
159
/**********************************************************************************
160
 * Allocate a ZxcMatch_t struct, clear it to zero
161
 */
162
static ZxcMatch_t *AllocMatch()
163
0
{
164
0
    ZxcMatch_t *p = MallocFn(ZxcMatch_t, 1);
165
0
    memset(p, 0, sizeof *p);
166
0
    return p;
167
0
}
168
169
/**********************************************************************************
170
 * Add new match struct to linked list of matches. List ordered with shortest at
171
 * head of list. Note: passed new match struct in parameter Nu may be de allocated.
172
 */
173
static void AddResult(ZxcMatch_t **HeadRef, ZxcMatch_t *Nu, int MaxLen)
174
0
{
175
    /* Adjust the entropy to be used for calculations depending on whether the passed match is
176
     * at the begining, middle or end of the password
177
     */
178
0
    if (Nu->Begin)
179
0
    {
180
0
        if (Nu->Length >= MaxLen)
181
0
            Nu->MltEnpy = Nu->Entrpy + MULTI_END_ADDITION * log(2.0);
182
0
        else
183
0
            Nu->MltEnpy = Nu->Entrpy + MULTI_MID_ADDITION * log(2.0);
184
0
    }
185
0
    else
186
0
        Nu->MltEnpy = Nu->Entrpy;
187
188
    /* Find the correct insert point */
189
0
    while(*HeadRef && ((*HeadRef)->Length < Nu->Length))
190
0
        HeadRef = &((*HeadRef)->Next);
191
192
    /* Add new entry or replace existing */
193
0
    if (*HeadRef && ((*HeadRef)->Length == Nu->Length))
194
0
    {
195
        /* New entry has same length as existing, so one of them needs discarding */
196
0
        if ((*HeadRef)->MltEnpy <= Nu->MltEnpy)
197
0
        {
198
            /* Existing entry has lower entropy - keep it, discard new entry */
199
0
            FreeFn(Nu);
200
0
        }
201
0
        else
202
0
        {
203
            /* New entry has lower entropy - replace existing entry */
204
0
            Nu->Next = (*HeadRef)->Next;
205
0
            FreeFn(*HeadRef);
206
0
            *HeadRef = Nu;
207
0
        }
208
0
    }
209
0
    else
210
0
    {
211
        /* New entry has different length, so add it */
212
0
        Nu->Next = *HeadRef;
213
0
        *HeadRef = Nu;
214
0
    }
215
0
}
216
217
/**********************************************************************************
218
 * See if the match is repeated. If it is then add a new repeated match to the results.
219
 */
220
static void AddMatchRepeats(ZxcMatch_t **Result, ZxcMatch_t *Match, const uint8_t *Passwd, int MaxLen)
221
0
{
222
0
    int Len = Match->Length;
223
0
    const uint8_t *Rpt = Passwd + Len;
224
0
    int RepeatCount = 2;
225
226
0
    while(MaxLen >= (Len * RepeatCount))
227
0
    {
228
0
        if (strncmp((const char *)Passwd, (const char *)Rpt, Len) == 0)
229
0
        {
230
            /* Found a repeat */
231
0
            ZxcMatch_t *p = AllocMatch();
232
0
            p->Entrpy = Match->Entrpy + log(RepeatCount);
233
0
            p->Type = (ZxcTypeMatch_t)(Match->Type + MULTIPLE_MATCH);
234
0
            p->Length = Len * RepeatCount;
235
0
            p->Begin = Match->Begin;
236
0
            AddResult(Result, p, MaxLen);
237
0
        }
238
0
        else
239
0
            break;
240
0
        ++RepeatCount;
241
0
        Rpt += Len;
242
0
    }
243
0
}
244
245
/*################################################################################*
246
 *################################################################################*
247
 * Begin dictionary matching code
248
 *################################################################################*
249
 *################################################################################*/
250
251
#ifdef USE_DICT_FILE
252
/* Use dictionary data from file */
253
254
#if defined(USE_FILE_IO) || !defined(__cplusplus)
255
/* Use the FILE streams from stdio.h */
256
257
typedef FILE *FileHandle;
258
259
#define MyOpenFile(f, name)       (f = fopen(name, "rb"))
260
#define MyReadFile(f, buf, bytes) (fread(buf, 1, bytes, f) == (bytes))
261
#define MyCloseFile(f)            fclose(f)
262
263
#else
264
265
/* Use the C++ iostreams */
266
typedef std::ifstream FileHandle;
267
268
static inline void MyOpenFile(FileHandle & f, const char *Name)
269
{
270
    f.open(Name, std::ifstream::in | std::ifstream::binary);
271
}
272
static inline bool MyReadFile(FileHandle & f, void *Buf, unsigned int Num)
273
{
274
    return (bool)f.read((char *)Buf, Num);
275
}
276
static inline void MyCloseFile(FileHandle & f)
277
{
278
    f.close();
279
}
280
281
#endif
282
283
/* Include file contains the CRC of the dictionary data file. Used to detect corruption */
284
/* of the file. */
285
#include "dict-crc.h"
286
287
#define MAX_DICT_FILE_SIZE  (100+WORD_FILE_SIZE)
288
#define CHK_INIT 0xffffffffffffffffULL
289
290
/* Static table used for the crc implementation. */
291
static const uint64_t CrcTable[16] =
292
{
293
    0x0000000000000000ULL, 0x7d08ff3b88be6f81ULL, 0xfa11fe77117cdf02ULL, 0x8719014c99c2b083ULL,
294
    0xdf7adabd7a6e2d6fULL, 0xa2722586f2d042eeULL, 0x256b24ca6b12f26dULL, 0x5863dbf1e3ac9decULL,
295
    0x95ac9329ac4bc9b5ULL, 0xe8a46c1224f5a634ULL, 0x6fbd6d5ebd3716b7ULL, 0x12b5926535897936ULL,
296
    0x4ad64994d625e4daULL, 0x37deb6af5e9b8b5bULL, 0xb0c7b7e3c7593bd8ULL, 0xcdcf48d84fe75459ULL
297
};
298
299
static const unsigned int MAGIC = 'z' + ('x'<< 8) + ('c' << 16) + ('v' << 24);
300
301
static unsigned int NumNodes, NumChildLocs, NumRanks, NumWordEnd, NumChildMaps;
302
static unsigned int SizeChildMapEntry, NumLargeCounts, NumSmallCounts, SizeCharSet;
303
304
static unsigned int   *DictNodes;
305
static uint8_t        *WordEndBits;
306
static unsigned int   *ChildLocs;
307
static unsigned short *Ranks;
308
static uint8_t        *ChildMap;
309
static uint8_t        *EndCountLge;
310
static uint8_t        *EndCountSml;
311
static char           *CharSet;
312
313
/**********************************************************************************
314
 * Calculate the CRC-64 of passed data.
315
 * Parameters:
316
 *  Crc     The initial or previous CRC value
317
 *  v       Pointer to the data to add to CRC calculation
318
 *  Len     Length of the passed data
319
 * Returns the updated CRC value.
320
 */
321
static uint64_t CalcCrc64(uint64_t Crc, const void *v, unsigned int Len)
322
{
323
    const uint8_t *Data = (const unsigned char *)v;
324
    while(Len--)
325
    {
326
        Crc = CrcTable[(Crc ^ (*Data >> 0)) & 0x0f] ^ (Crc >> 4);
327
        Crc = CrcTable[(Crc ^ (*Data >> 4)) & 0x0f] ^ (Crc >> 4);
328
        ++Data;
329
    }
330
    return Crc;
331
}
332
333
/**********************************************************************************
334
 * Read the dictionary data from file.
335
 * Parameters:
336
 *  Filename    Name of the file to read.
337
 * Returns 1 on success, 0 on error
338
 */
339
int ZxcvbnInit(const char *Filename)
340
{
341
    FileHandle f;
342
    uint64_t Crc = CHK_INIT;
343
    if (DictNodes)
344
        return 1;
345
    MyOpenFile(f, Filename);
346
    if (f)
347
    {
348
        unsigned int i, DictSize;
349
350
        /* Get magic number */
351
        if (!MyReadFile(f, &i, sizeof i))
352
            i = 0;
353
354
        /* Get header data */
355
        if (!MyReadFile(f, &NumNodes, sizeof NumNodes))
356
            i = 0;
357
        if (!MyReadFile(f, &NumChildLocs, sizeof NumChildLocs))
358
            i = 0;
359
        if (!MyReadFile(f, &NumRanks, sizeof NumRanks))
360
            i = 0;
361
        if (!MyReadFile(f, &NumWordEnd, sizeof NumWordEnd))
362
            i = 0;
363
        if (!MyReadFile(f, &NumChildMaps, sizeof NumChildMaps))
364
            i = 0;
365
        if (!MyReadFile(f, &SizeChildMapEntry, sizeof SizeChildMapEntry))
366
            i = 0;
367
        if (!MyReadFile(f, &NumLargeCounts, sizeof NumLargeCounts))
368
            i = 0;
369
        if (!MyReadFile(f, &NumSmallCounts, sizeof NumSmallCounts))
370
            i = 0;
371
        if (!MyReadFile(f, &SizeCharSet, sizeof SizeCharSet))
372
            i = 0;
373
374
        /* Validate the header data */
375
        if (NumNodes >= (1<<17))
376
            i = 1;
377
        if (NumChildLocs >= (1<<BITS_CHILD_MAP_INDEX))
378
            i = 2;
379
        if (NumChildMaps >= (1<<BITS_CHILD_PATT_INDEX))
380
            i = 3;
381
        if ((SizeChildMapEntry*8) < SizeCharSet)
382
            i = 4;
383
        if (NumLargeCounts >= (1<<9))
384
            i = 5;
385
        if (NumSmallCounts != NumNodes)
386
            i = 6;
387
388
        if (i != MAGIC)
389
        {
390
            MyCloseFile(f);
391
            return 0;
392
        }
393
        Crc = CalcCrc64(Crc, &i,    sizeof i);
394
        Crc = CalcCrc64(Crc, &NumNodes,     sizeof NumNodes);
395
        Crc = CalcCrc64(Crc, &NumChildLocs, sizeof NumChildLocs);
396
        Crc = CalcCrc64(Crc, &NumRanks,     sizeof NumRanks);
397
        Crc = CalcCrc64(Crc, &NumWordEnd,   sizeof NumWordEnd);
398
        Crc = CalcCrc64(Crc, &NumChildMaps, sizeof NumChildMaps);
399
        Crc = CalcCrc64(Crc, &SizeChildMapEntry, sizeof SizeChildMapEntry);
400
        Crc = CalcCrc64(Crc, &NumLargeCounts,   sizeof NumLargeCounts);
401
        Crc = CalcCrc64(Crc, &NumSmallCounts,   sizeof NumSmallCounts);
402
        Crc = CalcCrc64(Crc, &SizeCharSet,      sizeof SizeCharSet);
403
404
        DictSize = NumNodes*sizeof(*DictNodes) + NumChildLocs*sizeof(*ChildLocs) + NumRanks*sizeof(*Ranks) +
405
                   NumWordEnd + NumChildMaps*SizeChildMapEntry + NumLargeCounts + NumSmallCounts + SizeCharSet;
406
        if (DictSize < MAX_DICT_FILE_SIZE)
407
        {
408
            DictNodes = MallocFn(unsigned int, DictSize / sizeof(unsigned int) + 1);
409
            if (!MyReadFile(f, DictNodes, DictSize))
410
            {
411
                FreeFn(DictNodes);
412
                DictNodes = 0;
413
            }
414
        }
415
        MyCloseFile(f);
416
417
        if (!DictNodes)
418
            return 0;
419
        /* Check crc */
420
        Crc = CalcCrc64(Crc, DictNodes, DictSize);
421
        if (memcmp(&Crc, WordCheck, sizeof Crc))
422
        {
423
            /* File corrupted */
424
            FreeFn(DictNodes);
425
            DictNodes = 0;
426
            return 0;
427
        }
428
        fflush(stdout);
429
        /* Set pointers to the data */
430
        ChildLocs = DictNodes + NumNodes;
431
        Ranks = (unsigned short *)(ChildLocs + NumChildLocs);
432
        WordEndBits = (unsigned char *)(Ranks + NumRanks);
433
        ChildMap = (unsigned char*)(WordEndBits + NumWordEnd);
434
        EndCountLge = ChildMap + NumChildMaps*SizeChildMapEntry;
435
        EndCountSml = EndCountLge + NumLargeCounts;
436
        CharSet = (char *)EndCountSml + NumSmallCounts;
437
        CharSet[SizeCharSet] = 0;
438
        return 1;
439
    }
440
    return 0;
441
}
442
/**********************************************************************************
443
 * Free the data allocated by ZxcvbnInit().
444
 */
445
void ZxcvbnUnInit()
446
{
447
    if (DictNodes)
448
        FreeFn(DictNodes);
449
    DictNodes = 0;
450
}
451
452
#else
453
454
/* Include the source file containing the dictionary data */
455
#include "dict-src.h"
456
457
#endif
458
459
/**********************************************************************************
460
 * Leet conversion strings
461
 */
462
/* String of normal chars that could be given as leet chars in the password */
463
static const uint8_t L33TChr[] = "abcegilostxz";
464
465
/* String of leet,normal,normal char triples. Used to convert supplied leet char to normal. */
466
static const uint8_t L33TCnv[] = "!i $s %x (c +t 0o 1il2z 3e 4a 5s 6g 7lt8b 9g <c @a [c {c |il";
467
0
#define LEET_NORM_MAP_SIZE 3
468
469
/* Struct holding additional data on the word match */
470
typedef struct
471
{
472
    int Rank;                        /* Rank of word in dictionary */
473
    int  Caps;                       /* Number of capital letters */
474
    int  Lower;                      /* Number of lower case letters */
475
    int NumLeet;                     /* Total number of leeted characters */
476
    uint8_t  Leeted[sizeof L33TChr]; /* Number of leeted chars for each char found in L33TChr */
477
    uint8_t  UnLeet[sizeof L33TChr]; /* Number of normal chars for each char found in L33TChr */
478
} DictMatchInfo_t;
479
480
/* Struct holding working data for the word match */
481
typedef struct
482
{
483
    uint32_t StartLoc;
484
    int     Ordinal;
485
    int     PwdLength;
486
    int     Begin;
487
    int     Caps;
488
    int     Lower;
489
    int     NumPossChrs;
490
    uint8_t Leeted[sizeof L33TChr];
491
    uint8_t UnLeet[sizeof L33TChr];
492
    uint8_t LeetCnv[sizeof L33TCnv / LEET_NORM_MAP_SIZE + 1];
493
    uint8_t First;
494
    uint8_t PossChars[CHARSET_SIZE];
495
} DictWork_t;
496
497
/**********************************************************************************
498
 * Given a map entry create a string of all possible characters for following to
499
 * a child node
500
 */
501
static int ListPossibleChars(uint8_t *List, const uint8_t *Map)
502
0
{
503
0
    unsigned int i, j, k;
504
0
    int Len = 0;
505
0
    for(k = i = 0; i < SizeChildMapEntry; ++i, ++Map)
506
0
    {
507
0
        if (!*Map)
508
0
        {
509
0
            k += 8;
510
0
            continue;
511
0
        }
512
0
        for(j = 0; j < 8; ++j)
513
0
        {
514
0
            if (*Map & (1<<j))
515
0
            {
516
0
                *List++ = CharSet[k];
517
0
                ++Len;
518
0
            }
519
0
            ++k;
520
0
        }
521
0
    }
522
0
    *List=0;
523
0
    return Len;
524
0
}
525
526
/**********************************************************************************
527
 * Increment count of each char that could be leeted.
528
 */
529
static void AddLeetChr(uint8_t c, int IsLeet, uint8_t *Leeted, uint8_t *UnLeet)
530
0
{
531
0
    const uint8_t *p = CharBinSearch(c, L33TChr, sizeof L33TChr - 1, 1);
532
0
    if (p)
533
0
    {
534
0
        int i = p - L33TChr;
535
0
        if (IsLeet > 0)
536
0
        {
537
0
            Leeted[i] += 1;
538
0
        }
539
0
        else if (IsLeet < 0)
540
0
        {
541
0
            Leeted[i] += 1;
542
0
            UnLeet[i] -= 1;
543
0
        }
544
0
        else
545
0
        {
546
0
            UnLeet[i] += 1;
547
0
        }
548
0
    }
549
0
}
550
551
/**********************************************************************************
552
 * Given details of a word match, update it with the entropy (as natural log of
553
 * number of possiblities)
554
 */
555
static void DictionaryEntropy(ZxcMatch_t *m, DictMatchInfo_t *Extra, const uint8_t *Pwd)
556
0
{
557
0
    double e = 0.0;
558
    /* Add allowance for uppercase letters */
559
0
    if (Extra->Caps)
560
0
    {
561
0
        if (Extra->Caps == m->Length)
562
0
        {
563
            /* All uppercase, common case so only 1 bit */
564
0
            e += log(2.0);
565
0
        }
566
0
        else if ((Extra->Caps == 1) && (isupper(*Pwd) || isupper(Pwd[m->Length - 1])))
567
0
        {
568
            /* Only first or last uppercase, also common so only 1 bit */
569
0
            e += log(2.0);
570
0
        }
571
0
        else
572
0
        {
573
            /* Get number of combinations of lowercase, uppercase letters */
574
0
            int Up = Extra->Caps;
575
0
            int Lo = Extra->Lower;
576
0
            int i = Up;
577
0
            if (i > Lo)
578
0
                i = Lo;
579
0
            for(Lo += Up; i >= 0; --i)
580
0
                e += nCk(Lo, i);
581
0
            if (e > 0.0)
582
0
                e = log(e);
583
0
        }
584
0
    }
585
    /* Add allowance for using leet substitution */
586
0
    if (Extra->NumLeet)
587
0
    {
588
0
        int i;
589
0
        double d = 0.0;
590
0
        for(i = sizeof Extra->Leeted - 1; i >= 0; --i)
591
0
        {
592
0
            int Sb = Extra->Leeted[i];
593
0
            if (Sb)
594
0
            {
595
0
                int Un = Extra->UnLeet[i];
596
0
                int j = m->Length - Extra->NumLeet;
597
0
                if ((j >= 0) && (Un > j))
598
0
                    Un = j;
599
0
                j = Sb;
600
0
                if (j > Un)
601
0
                    j = Un;
602
0
                for(Un += Sb; j >= 0; --j)
603
0
                {
604
0
                    double z = nCk(Un, j);
605
0
                    d += z;
606
0
                }
607
0
            }
608
0
        }
609
0
        if (d > 0.0)
610
0
            d = log(d);
611
0
        if (d < log(2.0))
612
0
            d = log(2.0);
613
0
        e += d;
614
0
    }
615
    /* Add entropy due to word's rank */
616
0
    e += log((double)Extra->Rank);
617
0
    m->Entrpy = e;
618
0
}
619
620
/**********************************************************************************
621
 * Function that does the word matching
622
 */
623
static void DoDictMatch(const uint8_t *Passwd, int Start, int MaxLen, DictWork_t *Wrk, ZxcMatch_t **Result, DictMatchInfo_t *Extra, int Lev)
624
0
{
625
0
    int Len;
626
0
    uint8_t TempLeet[LEET_NORM_MAP_SIZE];
627
0
    int Ord = Wrk->Ordinal;
628
0
    int Caps = Wrk->Caps;
629
0
    int Lower = Wrk->Lower;
630
0
    unsigned int NodeLoc = Wrk->StartLoc;
631
0
    uint8_t *PossChars = Wrk->PossChars;
632
0
    int NumPossChrs = Wrk->NumPossChrs;
633
0
    const uint8_t *Pwd = Passwd;
634
0
    uint32_t NodeData = DictNodes[NodeLoc];
635
0
    Passwd += Start;
636
0
    for(Len = 0; *Passwd && (Len < MaxLen); ++Len, ++Passwd)
637
0
    {
638
0
        uint8_t c;
639
0
        int w, x, y, z;
640
0
        const uint8_t *q;
641
0
        z = 0;
642
0
        if (!Len && Wrk->First)
643
0
        {
644
0
            c = Wrk->First;
645
0
        }
646
0
        else
647
0
        {
648
            /* Get char and set of possible chars at current point in word. */
649
0
            const uint8_t *Bmap;
650
0
            c = *Passwd;
651
0
            Bmap = ChildMap + (NodeData & ((1<<BITS_CHILD_PATT_INDEX)-1)) * SizeChildMapEntry;
652
0
            NumPossChrs = ListPossibleChars(PossChars, Bmap);
653
654
            /* Make it lowercase and update lowercase, uppercase counts */
655
0
            if (isupper(c))
656
0
            {
657
0
                c = tolower(c);
658
0
                ++Caps;
659
0
            }
660
0
            else if (islower(c))
661
0
            {
662
0
                ++Lower;
663
0
            }
664
            /* See if current char is a leet and can be converted  */
665
0
            q = CharBinSearch(c, L33TCnv, sizeof L33TCnv / LEET_NORM_MAP_SIZE, LEET_NORM_MAP_SIZE);
666
0
            if (q)
667
0
            {
668
                /* Found, see if used before */
669
0
                unsigned int j;
670
0
                unsigned int i = (q - L33TCnv ) / LEET_NORM_MAP_SIZE;
671
0
                if (Wrk->LeetCnv[i])
672
0
                {
673
                    /* Used before, so limit characters to try */
674
0
                    TempLeet[0] = c;
675
0
                    TempLeet[1] = Wrk->LeetCnv[i];
676
0
                    TempLeet[2] = 0;
677
0
                    q = TempLeet;
678
0
                }
679
0
                for(j = 0; (*q > ' ') && (j < LEET_NORM_MAP_SIZE); ++j, ++q)
680
0
                {
681
0
                    const uint8_t *r = CharBinSearch(*q, PossChars, NumPossChrs, 1);
682
0
                    if (r)
683
0
                    {
684
                        /* valid conversion from leet */
685
0
                        DictWork_t w;
686
0
                        w = *Wrk;
687
0
                        w.StartLoc = NodeLoc;
688
0
                        w.Ordinal = Ord;
689
0
                        w.PwdLength += Len;
690
0
                        w.Caps = Caps;
691
0
                        w.Lower = Lower;
692
0
                        w.First = *r;
693
0
                        w.NumPossChrs = NumPossChrs;
694
0
                        memcpy(w.PossChars, PossChars, sizeof w.PossChars);
695
0
                        if (j)
696
0
                        {
697
0
                            w.LeetCnv[i] = *r;
698
0
                            AddLeetChr(*r, -1, w.Leeted, w.UnLeet);
699
0
                        }
700
0
                        DoDictMatch(Pwd, Passwd - Pwd, MaxLen - Len, &w, Result, Extra, Lev+1);
701
0
                    }
702
0
                }
703
0
                return;
704
0
            }
705
0
        }
706
0
        q = CharBinSearch(c, PossChars, NumPossChrs, 1);
707
0
        if (q)
708
0
        {
709
            /* Found the char as a normal char */
710
0
            if (CharBinSearch(c, L33TChr, sizeof L33TChr - 1, 1))
711
0
            {
712
                /* Char matches, but also a normal equivalent to a leet char */
713
0
                AddLeetChr(c, 0,  Wrk->Leeted, Wrk->UnLeet);
714
0
            }
715
0
        }
716
0
        if (!q)
717
0
        {
718
            /* No match for char - return */
719
0
            return;
720
0
        }
721
        /* Add all the end counts of the child nodes before the one that matches */
722
0
        x = (q - Wrk->PossChars);
723
0
        y = (NodeData >> BITS_CHILD_PATT_INDEX) & ((1 << BITS_CHILD_MAP_INDEX) - 1);
724
0
        NodeLoc = ChildLocs[x+y];
725
0
        for(w=0; w<x; ++w)
726
0
        {
727
0
            unsigned int Cloc = ChildLocs[w+y];
728
0
            z = EndCountSml[Cloc];
729
0
            if (Cloc < NumLargeCounts)
730
0
                z += EndCountLge[Cloc]*256;
731
0
            Ord += z;
732
0
        }
733
734
        /* Move to next node */
735
0
        NodeData = DictNodes[NodeLoc];
736
0
        if (WordEndBits[NodeLoc >> 3] & (1<<(NodeLoc & 7)))
737
0
        {
738
            /* Word matches, save result */
739
0
            unsigned int v;
740
0
            ZxcMatch_t *p;
741
0
            v = Ranks[Ord];
742
0
            if (v & (1<<15))
743
0
                v = (v & ((1 << 15) - 1)) * 4 + (1 << 15);
744
0
            Extra->Caps = Caps;
745
0
            Extra->Rank = v;
746
0
            Extra->Lower = Lower;
747
0
            for(x = 0, y = sizeof Extra->Leeted - 1; y >= 0; --y)
748
0
                x += Wrk->Leeted[y];
749
0
            Extra->NumLeet = x;
750
751
0
            memcpy(Extra->UnLeet, Wrk->UnLeet, sizeof Extra->UnLeet);
752
0
            memcpy(Extra->Leeted, Wrk->Leeted, sizeof Extra->Leeted);
753
754
0
            p = AllocMatch();
755
0
            if (x)
756
0
                p->Type = DICT_LEET_MATCH;
757
0
            else
758
0
                p->Type = DICTIONARY_MATCH;
759
0
            p->Length = Wrk->PwdLength + Len + 1;
760
0
            p->Begin = Wrk->Begin;
761
0
            DictionaryEntropy(p, Extra, Pwd);
762
0
            AddMatchRepeats(Result, p, Pwd, MaxLen);
763
0
            AddResult(Result, p, MaxLen);
764
0
            ++Ord;
765
0
        }
766
0
    }
767
0
}
768
769
/**********************************************************************************
770
 * Try to match password part with user supplied dictionary words
771
 * Parameters:
772
 *  Result  Pointer head of linked list used to store results
773
 *  Words   Array of pointers to dictionary words
774
 *  Passwd  The start of the password
775
 *  Start   Where in the password to start attempting to match
776
 *  MaxLen  Maximum number characters to consider
777
 */
778
static void UserMatch(ZxcMatch_t **Result, const char *Words[], const uint8_t *Passwd, int Start, int MaxLen)
779
0
{
780
0
    int Rank;
781
0
    if (!Words)
782
0
        return;
783
0
    Passwd += Start;
784
0
    for(Rank = 0; Words[Rank]; ++Rank)
785
0
    {
786
0
        DictMatchInfo_t Extra;
787
0
        uint8_t LeetChr[sizeof L33TCnv / LEET_NORM_MAP_SIZE + 1];
788
0
        uint8_t TempLeet[3];
789
0
        int Len = 0;
790
0
        int Caps = 0;
791
0
        int Lowers = 0;
792
0
        int Leets = 0;
793
0
        const uint8_t *Wrd = (const uint8_t *)(Words[Rank]);
794
0
        const uint8_t *Pwd = Passwd;
795
0
        memset(Extra.Leeted, 0, sizeof Extra.Leeted);
796
0
        memset(Extra.UnLeet, 0, sizeof Extra.UnLeet);
797
0
        memset(LeetChr, 0, sizeof LeetChr);
798
0
        while(*Wrd)
799
0
        {
800
0
            const uint8_t *q;
801
0
            uint8_t d = tolower(*Wrd++);
802
0
            uint8_t c = *Pwd++;
803
0
            if (isupper(c))
804
0
            {
805
0
                c = tolower(c);
806
0
                ++Caps;
807
0
            }
808
0
            else if (islower(c))
809
0
            {
810
0
                ++Lowers;
811
0
            }
812
            /* See if current char is a leet and can be converted  */
813
0
            q = CharBinSearch(c, L33TCnv, sizeof L33TCnv / LEET_NORM_MAP_SIZE, LEET_NORM_MAP_SIZE);
814
0
            if (q)
815
0
            {
816
                /* Found, see if used before */
817
0
                unsigned int j;
818
0
                unsigned int i = (q - L33TCnv ) / LEET_NORM_MAP_SIZE;
819
0
                if (LeetChr[i])
820
0
                {
821
                    /* Used before, so limit characters to try */
822
0
                    TempLeet[0] = c;
823
0
                    TempLeet[1] = LeetChr[i];
824
0
                    TempLeet[2] = 0;
825
0
                    q = TempLeet;
826
0
                }
827
0
                c = d+1;
828
0
                for(j = 0; (*q > ' ') && (j < LEET_NORM_MAP_SIZE); ++j, ++q)
829
0
                {
830
0
                    if (d == *q)
831
0
                    {
832
0
                        c = d;
833
0
                        if (i)
834
0
                        {
835
0
                            LeetChr[i] = c;
836
0
                            AddLeetChr(c, 1, Extra.Leeted, Extra.UnLeet);
837
0
                            ++Leets;
838
0
                        }
839
0
                        break;
840
0
                    }
841
0
                }
842
0
                if (c != d)
843
0
                {
844
0
                    Len = 0;
845
0
                    break;
846
0
                }
847
0
            }
848
0
            else if (c == d)
849
0
            {
850
                /* Found the char as a normal char */
851
0
                if (CharBinSearch(c, L33TChr, sizeof L33TChr - 1, 1))
852
0
                {
853
                    /* Char matches, but also a normal equivalent to a leet char */
854
0
                    AddLeetChr(c, 0,  Extra.Leeted, Extra.UnLeet);
855
0
                }
856
0
            }
857
0
            else
858
0
            {
859
                /* No Match */
860
0
                Len = 0;
861
0
                break;
862
0
            }
863
0
            if (++Len > MaxLen)
864
0
            {
865
0
                Len = 0;
866
0
                break;
867
0
            }
868
0
        }
869
0
        if (Len)
870
0
        {
871
0
            ZxcMatch_t *p = AllocMatch();
872
0
            if (!Leets)
873
0
                p->Type = USER_MATCH;
874
0
            else
875
0
                p->Type = USER_LEET_MATCH;
876
0
            p->Length = Len;
877
0
            p->Begin = Start;
878
            /* Add Entrpy */
879
0
            Extra.Caps = Caps;
880
0
            Extra.Lower = Lowers;
881
0
            Extra.NumLeet = Leets;
882
0
            Extra.Rank = Rank+1;
883
0
            DictionaryEntropy(p, &Extra, Passwd);
884
0
            AddMatchRepeats(Result, p, Passwd, MaxLen);
885
0
            AddResult(Result, p, MaxLen);
886
0
        }
887
0
    }
888
0
}
889
890
/**********************************************************************************
891
 * Try to match password part with the dictionary words
892
 * Parameters:
893
 *  Result  Pointer head of linked list used to store results
894
 *  Passwd  The start of the password
895
 *  Start   Where in the password to start attempting to match
896
 *  MaxLen  Maximum number characters to consider
897
 */
898
static void DictionaryMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start, int MaxLen)
899
0
{
900
0
    DictWork_t Wrk;
901
0
    DictMatchInfo_t Extra;
902
903
0
    memset(&Extra, 0, sizeof Extra);
904
0
    memset(&Wrk, 0, sizeof Wrk);
905
0
    Wrk.Ordinal = 1;
906
0
    Wrk.StartLoc = ROOT_NODE_LOC;
907
0
    Wrk.Begin = Start;
908
0
    DoDictMatch(Passwd+Start, 0, MaxLen, &Wrk, Result, &Extra, 0);
909
0
}
910
911
912
/*################################################################################*
913
 *################################################################################*
914
 * Begin keyboard spatial sequence matching code
915
 *################################################################################*
916
 *################################################################################*/
917
918
/* Struct to hold information on a keyboard layout */
919
typedef struct Keyboard
920
{
921
    const uint8_t *Keys;
922
    const uint8_t *Shifts;
923
    int NumKeys;
924
    int NumNear;
925
    int NumShift;
926
    int NumBlank;
927
} Keyboard_t;
928
929
/* Struct to hold information on the match */
930
typedef struct
931
{
932
    int Keyb;
933
    int Turns;
934
    int Shifts;
935
} SpatialMatchInfo_t;
936
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`";
940
static const uint8_t US_Shift[] = "!1\"'#3$4%5&7(9)0*8:;<,>.?/@2AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz^6_-{[|\\}]~`";
941
942
943
/* Neighbour tables */
944
static const uint8_t UK_Qwerty[48*7] =
945
{
946
/* key, left, up-left, up-right, right, down-right, down-left */
947
    '#', '\'',']',   0,   0,   0,   0,    '\'',';', '[', ']', '#',   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, 
951
    '2', '1',   0,   0, '3', 'w', 'q',    '3', '2',   0,   0, '4', 'e', 'w',
952
    '4', '3',   0,   0, '5', 'r', 'e',    '5', '4',   0,   0, '6', 't', 'r',
953
    '6', '5',   0,   0, '7', 'y', 't',    '7', '6',   0,   0, '8', 'u', 'y',
954
    '8', '7',   0,   0, '9', 'i', 'u',    '9', '8',   0,   0, '0', 'o', 'i',
955
    ';', 'l', 'p', '[','\'', '/', '.',    '=', '-',   0,   0,   0, ']', '[',
956
    '[', 'p', '-', '=', ']', '\'',';',    '\\',  0,   0, 'a', 'z',   0,   0,
957
    ']', '[', '=',   0,   0, '#','\'',    '`',   0,   0,   0, '1',   0,   0,
958
    'a',   0, 'q', 'w', 's', 'z','\\',    'b', 'v', 'g', 'h', 'n',   0,   0,
959
    'c', 'x', 'd', 'f', 'v',   0,   0,    'd', 's', 'e', 'r', 'f', 'c', 'x',
960
    'e', 'w', '3', '4', 'r', 'd', 's',    'f', 'd', 'r', 't', 'g', 'v', 'c',
961
    'g', 'f', 't', 'y', 'h', 'b', 'v',    'h', 'g', 'y', 'u', 'j', 'n', 'b',
962
    'i', 'u', '8', '9', 'o', 'k', 'j',    'j', 'h', 'u', 'i', 'k', 'm', 'n',
963
    'k', 'j', 'i', 'o', 'l', ',', 'm',    'l', 'k', 'o', 'p', ';', '.', ',',
964
    'm', 'n', 'j', 'k', ',',   0,   0,    'n', 'b', 'h', 'j', 'm',   0,   0,
965
    'o', 'i', '9', '0', 'p', 'l', 'k',    'p', 'o', '0', '-', '[', ';', 'l',
966
    'q',   0, '1', '2', 'w', 'a',   0,    'r', 'e', '4', '5', 't', 'f', 'd',
967
    's', 'a', 'w', 'e', 'd', 'x', 'z',    't', 'r', '5', '6', 'y', 'g', 'f',
968
    'u', 'y', '7', '8', 'i', 'j', 'h',    'v', 'c', 'f', 'g', 'b',   0,   0,
969
    'w', 'q', '2', '3', 'e', 's', 'a',    'x', 'z', 's', 'd', 'c',   0,   0,
970
    'y', 't', '6', '7', 'u', 'h', 'g',    'z', '\\','a', 's', 'x',   0,   0
971
};
972
973
static const uint8_t US_Qwerty[47*7] =
974
{
975
/* key, left, up-left, up-right, right, down-right, down-left */
976
    '\'',';', '[', ']',   0,   0, '/',    ',', 'm', 'k', 'l', '.',   0,   0,
977
    '-', '0',   0,   0, '=', '[', 'p',    '.', ',', 'l', ';', '/',   0,   0,
978
    '/', '.', ';','\'',   0,   0,   0,    '0', '9',   0,   0, '-', 'p', 'o',
979
    '1', '`',   0,   0, '2', 'q',   0,    '2', '1',   0,   0, '3', 'w', 'q',
980
    '3', '2',   0,   0, '4', 'e', 'w',    '4', '3',   0,   0, '5', 'r', 'e',
981
    '5', '4',   0,   0, '6', 't', 'r',    '6', '5',   0,   0, '7', 'y', 't',
982
    '7', '6',   0,   0, '8', 'u', 'y',    '8', '7',   0,   0, '9', 'i', 'u',
983
    '9', '8',   0,   0, '0', 'o', 'i',    ';', 'l', 'p', '[','\'', '/', '.',
984
    '=', '-',   0,   0,   0, ']', '[',    '[', 'p', '-', '=', ']','\'', ';',
985
    '\\',']',   0,   0,   0,   0,   0,    ']', '[', '=',   0,'\\',   0,'\'',
986
    '`',   0,   0,   0, '1',   0,   0,    'a',   0, 'q', 'w', 's', 'z',   0,
987
    'b', 'v', 'g', 'h', 'n',   0,   0,    'c', 'x', 'd', 'f', 'v',   0,   0,
988
    'd', 's', 'e', 'r', 'f', 'c', 'x',    'e', 'w', '3', '4', 'r', 'd', 's',
989
    'f', 'd', 'r', 't', 'g', 'v', 'c',    'g', 'f', 't', 'y', 'h', 'b', 'v',
990
    'h', 'g', 'y', 'u', 'j', 'n', 'b',    'i', 'u', '8', '9', 'o', 'k', 'j',
991
    'j', 'h', 'u', 'i', 'k', 'm', 'n',    'k', 'j', 'i', 'o', 'l', ',', 'm',
992
    'l', 'k', 'o', 'p', ';', '.', ',',    'm', 'n', 'j', 'k', ',',   0,   0,
993
    'n', 'b', 'h', 'j', 'm',   0,   0,    'o', 'i', '9', '0', 'p', 'l', 'k',
994
    'p', 'o', '0', '-', '[', ';', 'l',    'q',   0, '1', '2', 'w', 'a',   0,
995
    'r', 'e', '4', '5', 't', 'f', 'd',    's', 'a', 'w', 'e', 'd', 'x', 'z',
996
    't', 'r', '5', '6', 'y', 'g', 'f',    'u', 'y', '7', '8', 'i', 'j', 'h',
997
    'v', 'c', 'f', 'g', 'b',   0,   0,    'w', 'q', '2', '3', 'e', 's', 'a',
998
    'x', 'z', 's', 'd', 'c',   0,   0,    'y', 't', '6', '7', 'u', 'h', 'g',
999
    'z',   0, 'a', 's', 'x',   0,   0,
1000
};
1001
static const uint8_t Dvorak[47*7] =
1002
{
1003
    '\'',  0, '1', '2', ',', 'a',   0,    ',','\'', '2', '3', '.', 'o', 'a',
1004
    '-', 's', '/', '=',   0,   0, 'z',    '.', ',', '3', '4', 'p', 'e', 'o',
1005
    '/', 'l', '[', ']', '=', '-', 's',    '0', '9',   0,   0, '[', 'l', 'r',
1006
    '1', '`',   0,   0, '2','\'',   0,    '2', '1',   0,   0, '3', ',','\'',
1007
    '3', '2',   0,   0, '4', '.', ',',    '4', '3',   0,   0, '5', 'p', '.',
1008
    '5', '4',   0,   0, '6', 'y', 'p',    '6', '5',   0,   0, '7', 'f', 'y',
1009
    '7', '6',   0,   0, '8', 'g', 'f',    '8', '7',   0,   0, '9', 'c', 'g',
1010
    '9', '8',   0,   0, '0', 'r', 'c',    ';',   0, 'a', 'o', 'q',   0,   0,
1011
    '=', '/', ']',   0,'\\',   0, '-',    '[', '0',   0,   0, ']', '/', 'l',
1012
    '\\','=',   0,   0,   0,   0,   0,    ']', '[',   0,   0,   0, '=', '/',
1013
    '`',   0,   0,   0, '1',   0,   0,    'a',   0,'\'', ',', 'o', ';',   0,
1014
    'b', 'x', 'd', 'h', 'm',   0,   0,    'c', 'g', '8', '9', 'r', 't', 'h',
1015
    'd', 'i', 'f', 'g', 'h', 'b', 'x',    'e', 'o', '.', 'p', 'u', 'j', 'q',
1016
    'f', 'y', '6', '7', 'g', 'd', 'i',    'g', 'f', '7', '8', 'c', 'h', 'd',
1017
    'h', 'd', 'g', 'c', 't', 'm', 'b',    'i', 'u', 'y', 'f', 'd', 'x', 'k',
1018
    'j', 'q', 'e', 'u', 'k',   0,   0,    'k', 'j', 'u', 'i', 'x',   0,   0,
1019
    'l', 'r', '0', '[', '/', 's', 'n',    'm', 'b', 'h', 't', 'w',   0,   0,
1020
    'n', 't', 'r', 'l', 's', 'v', 'w',    'o', 'a', ',', '.', 'e', 'q', ';',
1021
    'p', '.', '4', '5', 'y', 'u', 'e',    'q', ';', 'o', 'e', 'j',   0,   0,
1022
    'r', 'c', '9', '0', 'l', 'n', 't',    's', 'n', 'l', '/', '-', 'z', 'v',
1023
    't', 'h', 'c', 'r', 'n', 'w', 'm',    'u', 'e', 'p', 'y', 'i', 'k', 'j',
1024
    'v', 'w', 'n', 's', 'z',   0,   0,    'w', 'm', 't', 'n', 'v',   0,   0,
1025
    'x', 'k', 'i', 'd', 'b',   0,   0,    'y', 'p', '5', '6', 'f', 'i', 'u',
1026
    'z', 'v', 's', '-',   0,   0,   0
1027
};
1028
1029
static const uint8_t PC_Keypad[15*9] =
1030
{
1031
/*Key, left, up-left, up, up-right, right, down-right, down, down-left */
1032
    '*', '/',   0,   0,   0, '-', '+', '9', '8',
1033
    '+', '9', '*', '-',   0,   0,   0,   0, '6',
1034
    '-', '*',   0,   0,   0,   0,   0, '+', '9',
1035
    '.', '0', '2', '3',   0,   0,   0,   0,   0,
1036
    '/',   0,   0,   0,   0, '*', '9', '8', '7',
1037
    '0',   0, '1', '2', '3', '.',   0,   0,   0,
1038
    '1',   0,   0, '4', '5', '2', '0',   0,   0,
1039
    '2', '1', '4', '5', '6', '3', '.', '0',   0,
1040
    '3', '2', '5', '6',   0,   0,   0, '.', '0',
1041
    '4',   0,   0, '7', '8', '5', '2', '1',   0,
1042
    '5', '4', '7', '8', '9', '6', '3', '2', '1',
1043
    '6', '5', '8', '9', '+',   0,   0, '3', '2',
1044
    '7',   0,   0,   0, '/', '8', '5', '4',   0,
1045
    '8', '7',   0, '/', '*', '9', '6', '5', '4',
1046
    '9', '8', '/', '*', '-', '+',   0, '6', '5'
1047
};
1048
1049
static const uint8_t MacKeypad[16*9] =
1050
{
1051
    '*', '/',   0,   0,   0,   0,   0, '-', '9',
1052
    '+', '6', '9', '-',   0,   0,   0,   0, '3',
1053
    '-', '9', '/', '*',   0,   0,   0, '+', '6',
1054
    '.', '0', '2', '3',   0,   0,   0,   0,   0,
1055
    '/', '=',   0,   0,   0, '*', '-', '9', '8',
1056
    '0',   0, '1', '2', '3', '.',   0,   0,   0,
1057
    '1',   0,   0, '4', '5', '2', '0',   0,   0,
1058
    '2', '1', '4', '5', '6', '3', '.', '0',   0,
1059
    '3', '2', '5', '6', '+',   0,   0, '.', '0',
1060
    '4',   0,   0, '7', '8', '5', '2', '1',   0,
1061
    '5', '4', '7', '8', '9', '6', '3', '2', '1',
1062
    '6', '5', '8', '9', '-', '+',   0, '3', '2',
1063
    '7',   0,   0,   0, '=', '8', '5', '4',   0,
1064
    '8', '7',   0, '=', '/', '9', '6', '5', '4',
1065
    '9', '8', '=', '/', '*', '-', '+', '6', '5',
1066
    '=',   0,   0,   0,   0, '/', '9', '8', '7'
1067
};
1068
1069
static const Keyboard_t Keyboards[] =
1070
{
1071
    { US_Qwerty, US_Shift, sizeof US_Qwerty / 7, 7, sizeof US_Shift / 2, 66 },
1072
    { Dvorak,    US_Shift, sizeof Dvorak / 7,    7, sizeof US_Shift / 2, 66 },
1073
    { UK_Qwerty, UK_Shift, sizeof UK_Qwerty / 7, 7, sizeof UK_Shift / 2, 66 },
1074
    { MacKeypad, 0, sizeof MacKeypad / 9, 9, 0, 44 },
1075
    { PC_Keypad, 0, sizeof PC_Keypad / 9, 9, 0, 44 }
1076
};
1077
1078
/**********************************************************************************
1079
 * Match password for the given keyboard layout
1080
 */
1081
static int DoSptlMatch(const uint8_t *Passwd, int MaxLen, const Keyboard_t *Keyb, SpatialMatchInfo_t *Extra)
1082
0
{
1083
0
    int i;
1084
0
    int ShiftCount = 0;
1085
0
    int Turns = 0;
1086
0
    int Dir = -1;
1087
0
    int Len = 0;
1088
0
    uint8_t PrevChar = 0;
1089
0
    for( ; *Passwd && (Len < MaxLen); ++Passwd, ++Len)
1090
0
    {
1091
0
        const uint8_t *Key;
1092
0
        int s = 0;
1093
0
        uint8_t CurChar = *Passwd;
1094
        /* Try to unshift the character */
1095
0
        if (Keyb->Shifts)
1096
0
        {
1097
0
            Key = CharBinSearch(CurChar, Keyb->Shifts, Keyb->NumShift, 2);
1098
0
            if (Key)
1099
0
            {
1100
                /* Shifted char */
1101
0
                CurChar = Key[1];
1102
0
                s = 1;
1103
0
            }
1104
0
        }
1105
0
        if (PrevChar)
1106
0
        {
1107
            /* See if the pattern can be extended */
1108
0
            i = 0;
1109
0
            Key = CharBinSearch(PrevChar, Keyb->Keys, Keyb->NumKeys, Keyb->NumNear);
1110
0
            if (Key)
1111
0
            {
1112
0
                for(i = Keyb->NumNear - 1; i > 0; --i)
1113
0
                {
1114
0
                    if (Key[i] == CurChar)
1115
0
                        break;
1116
0
                }
1117
0
            }
1118
0
            if (i)
1119
0
            {
1120
0
                Turns += (i != Dir);
1121
0
                Dir = i;
1122
0
                ShiftCount += s;
1123
0
            }
1124
0
            else
1125
0
            {
1126
0
                break;
1127
0
            }
1128
0
        }
1129
0
        PrevChar = CurChar;
1130
0
    }
1131
0
    if (Len >= MIN_SPATIAL_LEN)
1132
0
    {
1133
0
        Extra->Turns = Turns;
1134
0
        Extra->Shifts = ShiftCount;
1135
0
        return Len;
1136
0
    }
1137
0
    return 0;
1138
0
}
1139
1140
/**********************************************************************************
1141
 *  Try to match spatial patterns on the keyboard
1142
 * Parameters:
1143
 *  Result  Pointer head of linked list used to store results
1144
 *  Passwd  The start of the password
1145
 *  Start   Where in the password to start attempting to match
1146
 *  MaxLen  Maximum number characters to consider
1147
 */
1148
static void SpatialMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start, int MaxLen)
1149
0
{
1150
0
    unsigned int Indx;
1151
0
    int Len, CurLen;
1152
0
    SpatialMatchInfo_t Extra;
1153
0
    const Keyboard_t *k;
1154
0
    Passwd += Start;
1155
1156
0
    for(CurLen = MaxLen; CurLen >= MIN_SPATIAL_LEN;CurLen = Len - 1)
1157
0
    {
1158
0
        Len = 0;
1159
0
        for(k = Keyboards, Indx = 0; Indx < (sizeof Keyboards / sizeof Keyboards[0]); ++Indx, ++k)
1160
0
        {
1161
0
            memset(&Extra, 0, sizeof Extra);
1162
0
            Len = DoSptlMatch(Passwd, CurLen, k, &Extra);
1163
0
            if (Len > 0)
1164
0
            {
1165
                /* Got a sequence of required length so add to result list */
1166
0
                int i, j, s;
1167
0
                double Degree, Entropy;
1168
0
                ZxcMatch_t *p;
1169
0
                Degree = (k->NumNear-1) - (double)k->NumBlank / (double)k->NumKeys;
1170
0
                s = k->NumKeys;
1171
0
                if (k->Shifts)
1172
0
                    s *= 2;
1173
1174
                /* Estimate the number of possible patterns with length ranging 2 to match length and */
1175
                /* with turns ranging from 0 to match turns */
1176
0
                Entropy = 0.0;
1177
0
                for(i = 2; i <= Len; ++i)
1178
0
                {
1179
0
                    int PossTurns = Extra.Turns;
1180
0
                    if (PossTurns >= i)
1181
0
                        PossTurns = i-1;
1182
0
                    for(j = 1; j <= PossTurns; ++j)
1183
0
                        Entropy += nCk(i-1, j-1) * pow(Degree, j) * s;
1184
0
                }
1185
0
                if (Entropy > 0.0)
1186
0
                    Entropy = log(Entropy);
1187
0
                if (Extra.Shifts)
1188
0
                {
1189
                    /* Add extra entropy for shifted keys. (% instead of 5, A instead of a etc.) */
1190
                    /* Math is similar to extra entropy from uppercase letters in dictionary matches. */
1191
0
                    int Shift = Extra.Shifts;
1192
0
                    int Unshift = Len - Shift;
1193
1194
0
                    Degree = 0.0;
1195
0
                    j = Shift;
1196
0
                    if (j > Unshift)
1197
0
                        j = Unshift;
1198
0
                    for(i = 0; i <= j; ++i)
1199
0
                    {
1200
0
                        Degree += nCk(Len, i);
1201
0
                    }
1202
0
                    if (Degree > 0.0)
1203
0
                        Entropy += log(Degree);
1204
0
                }
1205
0
                p = AllocMatch();
1206
0
                p->Type = SPATIAL_MATCH;
1207
0
                p->Begin = Start;
1208
0
                p->Entrpy = Entropy;
1209
0
                p->Length = Len;
1210
0
                AddMatchRepeats(Result, p, Passwd, MaxLen);
1211
0
                AddResult(Result, p, MaxLen);
1212
0
            }
1213
0
        }
1214
0
    }
1215
0
}
1216
1217
1218
/*################################################################################*
1219
 *################################################################################*
1220
 * Begin date matching code
1221
 *################################################################################*
1222
 *################################################################################*/
1223
1224
/* The possible date formats ordered by length (d for day, m for month, */
1225
/*  y for year, ? for separator) */
1226
static const char *Formats[] =
1227
{
1228
    "yyyy",
1229
    "d?m?yy",
1230
    "ddmmyy",
1231
    "dmyyyy",
1232
    "dd?m?yy",
1233
    "d?mm?yy",
1234
    "ddmyyyy",
1235
    "dmmyyyy",
1236
    "yyyymmd",
1237
    "yyyymdd",
1238
    "d?m?yyyy",
1239
    "dd?mm?yy",
1240
    "ddmmyyyy",
1241
    "yyyy?m?d",
1242
    "yyyymmdd",
1243
    "dd?m?yyyy",
1244
    "d?mm?yyyy",
1245
    "yyyy?mm?d",
1246
    "yyyy?m?dd",
1247
    "dd?mm?yyyy",
1248
    "yyyy?mm?dd",
1249
    0
1250
};
1251
/* Possible separator characters that could be used */
1252
static const char DateSeperators[] = "/\\-_. ";
1253
1254
/**********************************************************************************
1255
 * Try to match the password with the formats above.
1256
 */
1257
static void DateMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start, int MaxLen)
1258
0
{
1259
0
    int CurFmt;
1260
0
    int YrLen = 0;
1261
0
    int PrevLen = 0;
1262
0
    uint8_t Sep = 0;
1263
0
    Passwd += Start;
1264
1265
0
    for(CurFmt = 0; Formats[CurFmt]; ++CurFmt)
1266
0
    {
1267
0
        int Len = 0;
1268
0
        int Year = 0;
1269
0
        int Mon  = 0;
1270
0
        int Day  = 0;
1271
0
        int Fail = 0;
1272
0
        const uint8_t *p = Passwd;
1273
0
        const char *Fmt;
1274
0
        YrLen = 0;
1275
0
        Sep = 0;
1276
        /* Scan along the format, trying to match the password */
1277
0
        for(Fmt = Formats[CurFmt]; *Fmt && !Fail; ++Fmt)
1278
0
        {
1279
0
            if (*Fmt == '?')
1280
0
            {
1281
0
                if (!Sep && strchr(DateSeperators, *p))
1282
0
                        Sep = *p;
1283
0
                Fail = (*p != Sep);
1284
0
            }
1285
0
            else if (isdigit(*p))
1286
0
            {
1287
0
                if (*Fmt == 'd')
1288
0
                {
1289
0
                    Day = 10 * Day + *p - '0';
1290
0
                }
1291
0
                else if (*Fmt == 'm')
1292
0
                {
1293
0
                    Mon = 10 * Mon + *p - '0';
1294
0
                }
1295
0
                else
1296
0
                {
1297
0
                    Year = 10 * Year + *p - '0';
1298
0
                    ++YrLen;
1299
0
                }
1300
0
            }
1301
0
            else
1302
0
            {
1303
0
                Fail = 1;
1304
0
            }
1305
0
            ++p;
1306
0
            ++Len;
1307
0
            if (Len >= MaxLen)
1308
0
                break;
1309
0
        }
1310
0
        if (Len < 4)
1311
0
            Fail = 1;
1312
0
        if (!Fail)
1313
0
        {
1314
            /* Character matching is OK, now check to see if valid date */
1315
0
            if (((YrLen > 3) || (Len <= 4)) && ((Year < MIN_YEAR) || (Year > MAX_YEAR)))
1316
0
                Fail = 1;
1317
0
            else if (Len > 4)
1318
0
            {
1319
0
                if ((Mon > 12) && (Day < 13))
1320
0
                {
1321
                    /* Swap day,month to try to make both in range */
1322
0
                    int i = Mon;
1323
0
                    Mon = Day;
1324
0
                    Day = i;
1325
0
                }
1326
                /* Check for valid day, month. Currently assumes all months have 31 days. */
1327
0
                if ((Mon < 1) || (Mon > 12))
1328
0
                    Fail = 1;
1329
0
                else if ((Day < 1) || (Day > 31))
1330
0
                    Fail = 1;
1331
0
            }
1332
0
        }
1333
0
        if (!Fail && (Len > PrevLen))
1334
0
        {
1335
            /* String matched the date, store result */
1336
0
            double e;
1337
0
            ZxcMatch_t *p = AllocMatch();
1338
1339
0
            if (Len <= 4)
1340
0
                e = log(MAX_YEAR - MIN_YEAR + 1.0);
1341
0
            else if (YrLen > 3)
1342
0
                e = log(31 * 12 * (MAX_YEAR - MIN_YEAR + 1.0));
1343
0
            else
1344
0
                e = log(31 * 12 * 100.0);
1345
0
            if (Sep)
1346
0
                e += log(4.0);  /* Extra 2 bits for separator */
1347
0
            p->Entrpy = e;
1348
0
            p->Type = DATE_MATCH;
1349
0
            p->Length = Len;
1350
0
            p->Begin = Start;
1351
0
            AddMatchRepeats(Result, p, Passwd, MaxLen);
1352
0
            AddResult(Result, p, MaxLen);
1353
0
            PrevLen = Len;
1354
0
        }
1355
0
    }
1356
0
}
1357
1358
1359
/*################################################################################*
1360
 *################################################################################*
1361
 * Begin repeated character matching code
1362
 *################################################################################*
1363
 *################################################################################*/
1364
1365
/**********************************************************************************
1366
 * Try to match password part as a set of repeated characters.
1367
 * Parameters:
1368
 *  Result  Pointer head of linked list used to store results
1369
 *  Passwd  The start of the password
1370
 *  Start   Where in the password to start attempting to match
1371
 *  MaxLen  Maximum number characters to consider
1372
 */
1373
static void RepeatMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start, int MaxLen)
1374
0
{
1375
0
    int Len, i;
1376
0
    uint8_t c;
1377
0
    Passwd += Start;
1378
    /* Remember first char and the count its occurances */
1379
0
    c = *Passwd;
1380
0
    for(Len = 1; (Len < MaxLen) && (c == Passwd[Len]); ++Len)
1381
0
    { }
1382
0
    if (Len >= MIN_REPEAT_LEN)
1383
0
    {
1384
        /* Enough repeated char, so create results from number found down to min acceptable repeats */
1385
0
        double Card = Cardinality(&c, 1);
1386
0
        for(i = Len; i >= MIN_REPEAT_LEN; --i)
1387
0
        {
1388
0
            ZxcMatch_t *p = AllocMatch();
1389
0
            p->Type = REPEATS_MATCH;
1390
0
            p->Begin = Start;
1391
0
            p->Length = i;
1392
0
            p->Entrpy = log(Card * i);
1393
0
            AddResult(Result, p, MaxLen);
1394
0
        }
1395
0
    }
1396
1397
    /* Try to match a repeated sequence e.g. qxno6qxno6 */
1398
0
    for(Len = MaxLen/2; Len >= MIN_REPEAT_LEN; --Len)
1399
0
    {
1400
0
        const uint8_t *Rpt = Passwd + Len;
1401
0
        int RepeatCount = 2;
1402
0
        while(MaxLen >= (Len * RepeatCount))
1403
0
        {
1404
0
            if (strncmp((const char *)Passwd, (const char *)Rpt, Len) == 0)
1405
0
            {
1406
                /* Found a repeat */
1407
0
                int c = Cardinality(Passwd, Len);
1408
0
                ZxcMatch_t *p = AllocMatch();
1409
0
                p->Entrpy = log((double)c) * Len + log(RepeatCount);
1410
0
                p->Type = (ZxcTypeMatch_t)(BRUTE_MATCH + MULTIPLE_MATCH);
1411
0
                p->Length = Len * RepeatCount;
1412
0
                p->Begin = Start;
1413
0
                AddResult(Result, p, MaxLen);
1414
0
            }
1415
0
            else
1416
0
                break;
1417
0
            ++RepeatCount;
1418
0
            Rpt += Len;
1419
0
        }
1420
0
    }
1421
0
}
1422
1423
/**********************************************************************************
1424
 **********************************************************************************
1425
 * Begin character sequence matching code
1426
 **********************************************************************************
1427
 *********************************************************************************/
1428
1429
0
#define MAX_SEQUENCE_STEP 5
1430
/**********************************************************************************
1431
 * Try to match password part as a set of incrementing or decrementing characters.
1432
 * Parameters:
1433
 *  Result  Pointer head of linked list used to store results
1434
 *  Passwd  The start of the password
1435
 *  Start   Where in the password to start attempting to match
1436
 *  MaxLen  Maximum number characters to consider
1437
 */
1438
static void SequenceMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start, int MaxLen)
1439
0
{
1440
0
    int Len=0;
1441
0
    int SetLow, SetHigh, Dir;
1442
0
    uint8_t First, Next, IsDigits;
1443
0
    const uint8_t *Pwd;
1444
0
    Passwd += Start;
1445
0
    Pwd = Passwd;
1446
0
    First = Passwd[0];
1447
0
    Dir = Passwd[1] - First;
1448
0
    Len = 0;
1449
0
    IsDigits = 0;
1450
    /* Decide on min and max character code for sequence */
1451
0
    if (islower(*Passwd))
1452
0
    {
1453
0
        SetLow = 'a';
1454
0
        SetHigh = 'z';
1455
0
    }
1456
0
    else if (isupper(*Passwd))
1457
0
    {
1458
0
        SetLow = 'A';
1459
0
        SetHigh = 'Z';
1460
0
    }
1461
0
    else if (isdigit(*Passwd))
1462
0
    {
1463
0
        SetLow = '0';
1464
0
        SetHigh = '9';
1465
0
        if ((First == '0') && isdigit(Passwd[1]) && (Dir > MAX_SEQUENCE_STEP))
1466
0
        {
1467
            /* Special case for decrementing sequence of digits, treat '0 as a 'ten' character */
1468
0
            Dir = Passwd[1] - ('9' + 1);
1469
0
        }
1470
0
        IsDigits = 1;
1471
0
    }
1472
0
    else
1473
0
        return;
1474
1475
    /* Only consider it a sequence if the character increment is not too large */
1476
0
    if (Dir && (Dir <= MAX_SEQUENCE_STEP) && (Dir >= -MAX_SEQUENCE_STEP))
1477
0
    {
1478
0
        ++Len;
1479
0
        while(1)
1480
0
        {
1481
0
            Next = Passwd[0] + Dir;
1482
0
            if (IsDigits && (Dir > 0) && (Next == ('9' + 1)) && (Passwd[1] == '0'))
1483
0
            {
1484
                /* Incrementing digits, consider '0' to be same as a 'ten' character */ 
1485
0
                ++Len;
1486
0
                ++Passwd;
1487
0
                break;
1488
0
            }
1489
0
            if (IsDigits && (Dir < 0) && (Passwd[0] == '0') && (Passwd[1] == ('9'+1 + Dir)))
1490
0
            {
1491
0
                ++Len;
1492
0
                ++Passwd;
1493
0
                break;
1494
0
            }
1495
0
            if ((Next > SetHigh) || (Next < SetLow) || (Passwd[1] != Next))
1496
0
                break;
1497
0
            ++Len;
1498
0
            ++Passwd;
1499
0
            if (Len >= MaxLen)
1500
0
                break;
1501
0
        }
1502
0
    }
1503
0
    if (Len >= MIN_SEQUENCE_LEN)
1504
0
    {
1505
        /* Enough repeated char, so create results from number found down to min acceptable length */
1506
0
        int i;
1507
0
        double e;
1508
0
        if ((First == 'a') || (First == 'A') || (First == 'z') || (First == 'Z') ||
1509
0
            (First == '0') || (First == '1') || (First == '9'))
1510
0
            e = log(2.0);
1511
0
        else if (IsDigits)
1512
0
            e = log(10.0);
1513
0
        else if (isupper(First))
1514
0
            e = log(26*2.0);
1515
0
        else
1516
0
            e = log(26.0);
1517
0
        if (Dir < 0)
1518
0
            e += log(2.0);
1519
1520
0
        for(i = Len; i >= MIN_SEQUENCE_LEN; --i)
1521
0
        {
1522
0
            ZxcMatch_t *p = AllocMatch();
1523
            /* Add new result to head of list as it has lower entropy */
1524
0
            p->Type = SEQUENCE_MATCH;
1525
0
            p->Begin = Start;
1526
0
            p->Length = i;
1527
0
            p->Entrpy = e + log((double)i);
1528
0
            AddMatchRepeats(Result, p, Pwd, MaxLen);
1529
0
            AddResult(Result, p, MaxLen);
1530
0
        }
1531
0
    }
1532
0
}
1533
1534
/**********************************************************************************
1535
 **********************************************************************************
1536
 * Begin top level zxcvbn code
1537
 **********************************************************************************
1538
 *********************************************************************************/
1539
1540
/**********************************************************************************
1541
 * Matching a password is treated as a problem of finding the minimum distance
1542
 * between two vertexes in a graph. This is solved using Dijkstra's algorithm.
1543
 *
1544
 * There  are a series of nodes (or vertexes in graph terminology) which correspond
1545
 * to points between each character of the password. Also there is a start node
1546
 * before the first character and an end node after the last character.
1547
 *
1548
 * The paths between the nodes (or edges in graph terminology) correspond to the
1549
 * matched parts of the password (e.g. dictionary word, repeated characters etc).
1550
 * The distance of the path is equal to the entropy of the matched part. A default
1551
 * single character bruteforce match path is also added for all nodes except the
1552
 * end node.
1553
 *
1554
 * Dijkstra's algorithm finds the combination of these part matches (or paths)
1555
 * which gives the lowest entropy (or smallest distance) from begining to end
1556
 * of the password. 
1557
 */
1558
1559
/* Struct to hold the data of a node (imaginary point between password characters) */
1560
typedef struct
1561
{
1562
    ZxcMatch_t *Paths;  /* Partial matches that lead to a following node */
1563
    double      Dist;   /* Distance (or entropy) from start of password to this node */
1564
    ZxcMatch_t *From;   /* Which path was used to get to this node with lowest distance/entropy */
1565
    int         Visit;  /* Non zero when node has been visited during Dijkstra evaluation */
1566
} Node_t;
1567
1568
/**********************************************************************************
1569
 * Main function of the zxcvbn password entropy estimation
1570
 */
1571
double ZxcvbnMatch(const char *Pwd, const char *UserDict[], ZxcMatch_t **Info)
1572
0
{
1573
0
    int i, j;
1574
0
    ZxcMatch_t *Zp;
1575
0
    Node_t *Np;
1576
0
    double e;
1577
0
    int FullLen = strlen(Pwd);
1578
0
    int Len = FullLen;
1579
0
    const uint8_t *Passwd = (const uint8_t *)Pwd;
1580
0
    uint8_t *RevPwd;
1581
    /* Create the paths */
1582
0
    Node_t *Nodes = MallocFn(Node_t, Len+2);
1583
0
    memset(Nodes, 0, (Len+2) * sizeof *Nodes);
1584
0
    i = Cardinality(Passwd, Len);
1585
0
    e = log((double)i);
1586
1587
    /* Limit length used to full entropy estimation to prevent excessive calculation time */
1588
0
    if (Len > ZXCVBN_DETAIL_LEN)
1589
0
        Len = ZXCVBN_DETAIL_LEN;
1590
1591
    /* Do matching for all parts of the password */
1592
0
    for(i = 0; i < Len; ++i)
1593
0
    {
1594
0
        int MaxLen = Len - i;
1595
        /* Add all the 'paths' between groups of chars in the password, for current starting char */
1596
0
        UserMatch(&(Nodes[i].Paths), UserDict, Passwd, i, MaxLen);
1597
0
        DictionaryMatch(&(Nodes[i].Paths), Passwd, i, MaxLen);
1598
0
        DateMatch(&(Nodes[i].Paths), Passwd, i, MaxLen);
1599
0
        SpatialMatch(&(Nodes[i].Paths), Passwd, i, MaxLen);
1600
0
        SequenceMatch(&(Nodes[i].Paths), Passwd, i, MaxLen);
1601
0
        RepeatMatch(&(Nodes[i].Paths), Passwd, i, MaxLen);
1602
1603
        /* Initially set distance to nearly infinite */
1604
0
        Nodes[i].Dist = DBL_MAX;
1605
0
    }
1606
1607
    /* Reverse dictionary words check */
1608
0
    RevPwd = MallocFn(uint8_t, Len+1);
1609
0
    for(i = Len-1, j = 0; i >= 0; --i, ++j)
1610
0
        RevPwd[j] = Pwd[i];
1611
0
    RevPwd[j] = 0;
1612
0
    for(i = 0; i < Len; ++i)
1613
0
    {
1614
0
        ZxcMatch_t *Path = 0;
1615
0
        int MaxLen = Len - i;
1616
0
        DictionaryMatch(&Path, RevPwd, i, MaxLen);
1617
0
        UserMatch(&Path, UserDict, RevPwd, i, MaxLen);
1618
1619
        /* Now transfer any reverse matches to the normal results */
1620
0
        while(Path)
1621
0
        {
1622
0
            ZxcMatch_t *Nxt = Path->Next;
1623
0
            Path->Next = 0;
1624
0
            Path->Begin = Len - (Path->Begin + Path->Length);
1625
0
            AddResult(&(Nodes[Path->Begin].Paths), Path, MaxLen);
1626
0
            Path = Nxt;
1627
0
        }
1628
0
    }
1629
1630
    /* Add a set of brute force matches. Start by getting all the start points and all */
1631
    /* points at character position after end of the matches.  */
1632
0
    memset(RevPwd, 0, Len+1);
1633
0
    for(i = 0; i < Len; ++i)
1634
0
    {
1635
0
        ZxcMatch_t *Path = Nodes[i].Paths;
1636
0
        while(Path)
1637
0
        {
1638
0
            RevPwd[Path->Begin] |= 1;
1639
0
            RevPwd[Path->Begin + Path->Length] |= 2;
1640
0
            Path = Path->Next;
1641
0
        }
1642
0
    }
1643
0
    RevPwd[0] = 1;
1644
0
    RevPwd[Len] = 2;
1645
1646
    /* Add the brute force matches */
1647
0
    for(i = 0; i < Len; ++i)
1648
0
    {
1649
0
        int MaxLen = Len - i;
1650
0
        int j;
1651
0
        if (!RevPwd[i])
1652
0
            continue;
1653
0
        for(j = i+1; j <= Len; ++j)
1654
0
        {
1655
0
            if (RevPwd[j])
1656
0
            {
1657
0
                Zp = AllocMatch();
1658
0
                Zp->Type = BRUTE_MATCH;
1659
0
                Zp->Begin = i;
1660
0
                Zp->Length = j - i;
1661
0
                Zp->Entrpy = e * (j - i);
1662
0
                AddResult(&(Nodes[i].Paths), Zp, MaxLen);
1663
0
            }
1664
0
        }
1665
0
    }
1666
0
    FreeFn(RevPwd);
1667
0
    if (FullLen > Len)
1668
0
    {
1669
        /* Only the first MAX_DETAIL_LEN characters are used for full  entropy estimation, for */
1670
        /* very long passwords the remainding characters are treated as being a incrementing */
1671
        /* sequence. This will give a low (and safe) entropy value for them. */
1672
0
        Nodes[Len].Dist = DBL_MAX;
1673
0
        Zp = AllocMatch();
1674
0
        Zp->Type = LONG_PWD_MATCH;
1675
0
        Zp->Begin = Len;
1676
        /* Length is negative as only one extra node to represent many extra characters */
1677
0
        Zp->Length = Len - FullLen;
1678
0
        Zp->Entrpy = log(2 * (FullLen - Len));
1679
0
        AddResult(&(Nodes[i].Paths), Zp, FullLen - Len);
1680
0
        ++Len;
1681
0
    }
1682
    /* End node has infinite distance/entropy, start node has 0 distance */
1683
0
    Nodes[Len].Dist = DBL_MAX;
1684
0
    Nodes[0].Dist = 0.0;
1685
1686
    /* Reduce the paths using Dijkstra's algorithm */
1687
0
    for(i = 0; i < Len; ++i)
1688
0
    {
1689
0
        int j;
1690
0
        double MinDist = DBL_MAX;
1691
0
        int MinIdx = 0;
1692
        /* Find the unvisited node with minimum distance or entropy */
1693
0
        for(Np = Nodes, j = 0; j < Len; ++j, ++Np)
1694
0
        {
1695
0
            if (!Np->Visit && (Np->Dist < MinDist))
1696
0
            {
1697
0
                MinIdx = j;
1698
0
                MinDist = Np->Dist;
1699
0
            }
1700
0
        }
1701
        /* Mark the minimum distance node as visited */
1702
0
        Np = Nodes + MinIdx;
1703
0
        Np->Visit = 1;
1704
0
        e = Np->Dist;
1705
        /* Update all unvisited neighbouring nodes with their new distance. A node is a */
1706
        /* neighbour if there is a path/match from current node Np to it. The neighbour */
1707
        /* distance is the current node distance plus the path distance/entropy. Only */
1708
        /* update if the new distance is smaller. */
1709
0
        for(Zp = Np->Paths; Zp; Zp = Zp->Next)
1710
0
        {
1711
0
            Node_t *Ep;
1712
0
            double d = e + Zp->MltEnpy;
1713
0
            if (Zp->Length >= 0)
1714
0
                Ep = Np + Zp->Length;
1715
0
            else
1716
0
                Ep = Np + 1;
1717
0
            if (!Ep->Visit &&  (d < Ep->Dist))
1718
0
            {
1719
                /* Update as lower dist, also remember the 'from' node */
1720
0
                Ep->Dist = d;
1721
0
                Ep->From = Zp;
1722
0
            }
1723
0
        }
1724
0
    }
1725
    /* Make e hold entropy result and adjust to log base 2 */
1726
0
    e = Nodes[Len].Dist / log(2.0);
1727
1728
0
    if (Info)
1729
0
    {
1730
        /* Construct info on password parts */
1731
0
        *Info = 0;
1732
0
        for(Zp = Nodes[Len].From; Zp; )
1733
0
        {
1734
0
            ZxcMatch_t *Xp;
1735
0
            i = Zp->Begin;
1736
1737
            /* Remove all but required path */
1738
0
            Xp = Nodes[i].Paths;
1739
0
            Nodes[i].Paths = 0;
1740
0
            while(Xp)
1741
0
            {
1742
0
                ZxcMatch_t *p = Xp->Next;
1743
0
                if (Xp == Zp)
1744
0
                {
1745
                    /* Adjust the entropy to log to base 2 */
1746
0
                    Xp->Entrpy /= log(2.0);
1747
0
                    Xp->MltEnpy /= log(2.0);
1748
0
                    if (Xp->Length < 0)
1749
0
                        Xp->Length = -Xp->Length;
1750
1751
                    /* Put previous part at head of info list */
1752
0
                    Xp->Next = *Info;
1753
0
                    *Info = Xp;
1754
0
                }
1755
0
                else
1756
0
                {
1757
                    /* Not going on info list, so free it */
1758
0
                    FreeFn(Xp);
1759
0
                }
1760
0
                Xp = p;
1761
0
            }
1762
0
            Zp = Nodes[i].From;
1763
0
        }
1764
0
    }
1765
    /* Free all paths. Any being returned to caller have already been freed */
1766
0
    for(i = 0; i <= Len; ++i)
1767
0
    {
1768
0
        Zp = Nodes[i].Paths;
1769
0
        while(Zp)
1770
0
        {
1771
0
            ZxcMatch_t *p = Zp->Next;
1772
0
            FreeFn(Zp);
1773
0
            Zp = p;
1774
0
        }
1775
0
    }
1776
0
    FreeFn(Nodes);
1777
0
    return e;
1778
0
}
1779
1780
/**********************************************************************************
1781
 * Free the path info returned by ZxcvbnMatch().
1782
 */
1783
void ZxcvbnFreeInfo(ZxcMatch_t *Info)
1784
0
{
1785
0
    ZxcMatch_t *p;
1786
0
    while(Info)
1787
0
    {
1788
0
        p = Info->Next;
1789
0
        FreeFn(Info);
1790
0
        Info = p;
1791
0
    }
1792
0
}