diff options
Diffstat (limited to 'test.c')
-rw-r--r-- | test.c | 259 |
1 files changed, 259 insertions, 0 deletions
@@ -0,0 +1,259 @@ +/********************************************************************************** + * Program to test the C implementation of the zxcvbn password strength estimator. + * Copyright (c) 2015, Tony Evans + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + **********************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/time.h> +#include <zxcvbn.h> + +/* For pre-compiled headers under windows */ +#ifdef _WIN32 +#include "stdafx.h" +#endif + +const char *UsrDict[] = +{ + "Onename.Twoname@example.com", "Onename", "Twoname", "example.com", "example", + 0 +}; + +static void CalcPass(const char *Pwd, int Quiet) +{ + double e; + if (!Quiet) + { + /* Output the details of how the entropy figure was calculated */ + int Len, ChkLen; + struct timeval t1, t2; + ZxcMatch_t *Info, *p; + + gettimeofday(&t1, 0); + e = ZxcvbnMatch(Pwd, UsrDict, &Info); + gettimeofday(&t2, 0); + + Len = strlen(Pwd); + printf("Pass %s \tLength %d\tEntropy %.3f\n", Pwd, Len, e); + p = Info; + ChkLen = 0; + while(p) + { + int n; + switch(p->Type) + { + case BRUTE_MATCH: printf(" Type: Bruteforce"); break; + case DICTIONARY_MATCH: printf(" Type: Dictionary"); break; + case DICT_LEET_MATCH: printf(" Type: Dict+Leet "); break; + case USER_MATCH: printf(" Type: User Words"); break; + case USER_LEET_MATCH: printf(" Type: User+Leet "); break; + case REPEATS_MATCH: printf(" Type: Repeated "); break; + case SEQUENCE_MATCH: printf(" Type: Sequence "); break; + case SPATIAL_MATCH: printf(" Type: Spatial "); break; + case DATE_MATCH: printf(" Type: Date "); break; + default: printf(" Type: Unknown%d ", p->Type); break; + } + ChkLen += p->Length; + printf(" Length %d Entropy %6.3f ", p->Length, p->Entrpy); + for(n = 0; n < p->Length; ++n, ++Pwd) + printf("%c", *Pwd); + printf("\n"); + p = p->Next; + } + ZxcvbnFreeInfo(Info); + t2.tv_sec -= t1.tv_sec; + t2.tv_usec -= t1.tv_usec; + t2.tv_usec += t2.tv_sec * 1000000; + printf(" Calculation Time %.2fms\n", t2.tv_usec/1000.0); + if (ChkLen != Len) + printf("*** Password length (%d) != sum of length of parts (%d) ***\n", Len, ChkLen); + } + else + { + /* Only get the final entropy figure */ + e = ZxcvbnMatch(Pwd, UsrDict, 0); + printf("Pass %s \tEntropy %.3f\n", Pwd, e); + } +} + +int DoChecks(char *file) +{ + char Line[500]; + int y = 0; + int w = 0; + int r = 0; + FILE *f = fopen(file, "r"); + if (f == NULL) + { + printf("Failed to open %s\n", file); + return 1; + } + memset(Line, 0, sizeof Line); + while(fgets(Line, sizeof Line - 4, f)) + { + /* Line is password + whitespace + expected entropy */ + char *Pwd, *s, *t; + double Ent, e, x; + unsigned int i; + ++y; + for(i = 0; i < sizeof Line - 5; ++i) + { + if (!Line[i] || (Line[i] == '\n')) + break; + } + /* Skip blank lines or those starting with # */ + if ((i < 3) || (Line[0] == '#')) + continue; + memset(Line + i, 0, 4); + Pwd = Line; + /* Skip leading whitespace */ + while(*Pwd && (*Pwd <= ' ')) + ++Pwd; + + /* Make password null termnated */ + s = Pwd; + t = strchr(s, '\t'); + if (t == NULL) + t = strstr(s, " "); + if (t == NULL) + { + printf("Bad test condition on line %d\n", y); + r = 1; + break; + } + *t++ = 0; + + /* Skip whitespace before entropy value */ + while(*t && (*t <= ' ')) + ++t; + if (!*t) + { + printf("Bad test condition on line %d\n", y); + r = 1; + break; + } + + Ent = atof(t); + if ((Ent < 0.0) || (Ent > 1000.0)) + { + printf("Bad entropy value on line %d\n", y); + r = 1; + break; + } + e = ZxcvbnMatch(Pwd, UsrDict, 0); + x = e / Ent; + /* More than 1% difference is a fail. */ + if ((x > 1.01) || (x < 1.0/1.01)) + { + printf("Line %2d Calculated entropy %5.2f, expected %5.2f <%s>\n", y, e, Ent, Pwd); + r = 1; + break; + } + ++w; + } + fclose(f); + if (!r) + printf("Tested %d words\n", w); + return r; +} + +int main(int argc, char **argv) +{ + int i, Quiet, Checks; + Quiet = 0; + Checks = 0; + if (!ZxcvbnInit("zxcvbn.dict")) + { + printf("Failed to open dictionary file\n"); + return 1; + } + if ((argc > 1) && (argv[1][0] == '-')) + { + Checks = !strcmp(argv[1], "-t"); + Quiet = !strcmp(argv[1], "-q"); + if ((Checks + Quiet) == 0) + { + char *s = strrchr(argv[0], '/'); + if (s == NULL) + s = argv[0]; + else + ++s; + printf( "Usage: %s [ -q ] [ pwd1 pwd2 ... ]\n" + " Output entropy of given passwords. If no passwords on command line read\n" + " them from stdin.\n" + " -q option stops password analysis details from being output.\n" + " %s -t file\n" + " Read the file and check for correct results.\n", s, s); + + return 1; + } + } + if (Checks) + { + for(i = 2; i < argc; ++i) + { + Checks = DoChecks(argv[i]); + if (Checks) + return 1; + } + return 0; + } + i = 1+Quiet; + if (i >= argc) + { + /* No test passwords on command line, so get them from stdin */ + char Line[500]; + while(fgets(Line, sizeof Line, stdin)) + { + /* Drop the trailing newline character */ + for(i = 0; i < (int)(sizeof Line - 1); ++i) + { + if (Line[i] < ' ') + { + Line[i] = 0; + break; + } + } + if (Line[0]) + CalcPass(Line, Quiet); + } + } + else + { + /* Do the test passwords on the command line */ + for(; i < argc; ++i) + { + CalcPass(argv[i], Quiet); + } + } + ZxcvbnUnInit(); + return 0; +} |