1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
/* Internal tests that cannot be run against the library without unnecessary export of symbols.
Copyright (c) 2017, Perlbotics / see LICENSE.txt
*/
#include "zxcvbn.h"
/* need access to internals for testing; not linked against library, just inlined */
#include "zxcvbn.c"
/**********************************************************************************
* Internal checks: Validate if the first element of each group is sorted in
* ascending order. CharBinSearch(...) fails otherwise.
* Returns 0 on success.
* Returns element index [1..] of first error entry that is less than previous one.
*/
static int check_order(const uint8_t *Ents, unsigned int NumEnts, unsigned int SizeEnts) {
const uint8_t *last;
unsigned int i;
if (!Ents) return 0;
last = 0;
for (i = 0; i < NumEnts; ++i, Ents += SizeEnts) {
if (last && *last > *Ents) {
unsigned int j;
printf("Entry#%d [%d]: '%c' > '%c' (0x%02X > 0x%02X)\n A: ", i, i * SizeEnts, *last, *Ents, *last, *Ents);
for (j = 0; j < SizeEnts; ++j) {
printf("'%c' ", last[j] ? last[j] : ' ');
}
printf("\n >\n B: ");
for (j = 0; j < SizeEnts; ++j) {
printf("'%c' ", Ents[j] ? Ents[j] : ' ');
}
printf("\n");
return i;
}
last = Ents;
}
return 0; /* cannot be a misordered position; first possible one: 1 */
}
/**********************************************************************************
* Internal checks: Checks keyboard data integrity.
* Returns 0 on succes.
* Otherwise, number of errors are reported.
*/
static unsigned int selftest_keyboards() {
unsigned int errors;
const Keyboard_t *k;
unsigned int Indx;
const uint8_t *keys;
int i,j,errpos, blanks;
errors = 0;
for(k = Keyboards, Indx = 0; Indx < (sizeof Keyboards / sizeof Keyboards[0]); ++Indx, ++k) {
/* if one of these assrtion fails, we cannot use binary search algorithm */
if (k->Shifts && strlen((const char*)k->Shifts) % 2 == 1) {
printf("ERROR: Keyboard[%d]: Shifts-string has odd number of entries.\n", Indx);
++errors;
}
if ( (errpos = check_order(k->Shifts, k->NumShift, 2)) ) {
printf("ERROR: Keyboard[%d]: Error above in sort order of Shifts-string near item #%d.\n", Indx, errpos);
++errors;
}
if ( (errpos = check_order(k->Keys, k->NumKeys, k->NumNear)) ) {
printf("ERROR: Keyboard[%d]: Error above in sort order of keyboard-entries! Problem near item #%d.\n", Indx, errpos);
++errors;
continue;
}
/* For each key (c0), check all its neighbours (ci):
* Does the neighbour key (c1==ci) have an entry (cx) in the opposite direction [rev_idx]
* pointing back to the current key c0?
* c0: ...ci.. --> c1: ..cx... --> cx==c0?
*/
keys = k->Keys;
blanks = 0;
for(i = 0; i < k->NumKeys; ++i) {
uint8_t c0;
c0 = keys[i * k->NumNear];
for (j = 0; j < k->NumNear - 1; ++j) {
const uint8_t *c1;
uint8_t ci, cx;
int rev_idx;
/* rev_idx: reverse/opposite index to find opposite key location [0..6|8] --> [0..6|8] */
rev_idx = (j + (k->NumNear - 1)/2) % (k->NumNear - 1);
ci = keys[i * k->NumNear + j + 1];
if (ci) {
c1 = CharBinSearch(ci, keys, k->NumKeys, k->NumNear);
if (c1) {
if (ci == c0) {
printf("ERROR: Keyboard[%d]: recursion - key '%c' cannot be its own neighbour!\n", Indx, *c1);
++errors;
} else {
if ( (cx = c1[ 1 + rev_idx ]) ) {
if ( cx != c0 ) {
printf("ERROR: Keyboard[%d]: c0='%c':...(ci=%c)... -> c1='%c':...(cx=%c)... --!--> c0='%c':... \n",
Indx, c0, ci, *c1, cx, c0);
++errors;
}
} else { /* reverse pointer is NULL */
printf("ERROR: Keyboard[%d]: reverse entry missing in row c1='%c'[%d] pointing back to c0='%c'!\n", Indx, *c1, 1+rev_idx, c0);
++errors;
}
}
} else {
printf("ERROR: Keyboard[%d]: no entry (neighbour list) found for src-char c1==ci='%c'\n", Indx, ci);
++errors;
}
} else { /* blank neighbour key reference found */
++blanks;
}
}
}
if (blanks != k->NumBlank) {
printf("ERROR: Keyboard[%d]: number of blank keys announced (%d) does not match number of blank keys counted (%d)!\n",
Indx, k->NumBlank, blanks);
++errors;
}
}
return errors;
}
int main() {
unsigned int errors;
if( (errors = selftest_keyboards()) ){ /* currently only these */
printf("FAILED: [KEYBOARDS] - selftest returned %d error(s).\n", errors);
}
return errors ? 1 : 0;
}
|