1 /* Copyright (C) 2000-2018 Peter Selinger.
2 This file is part of ccrypt. It is free software and it is covered
3 by the GNU general public license. See the file COPYING for details. */
4
5 /* Try to guess ccrypt keys by exhaustively searching keys that are
6 "similar" to a given key. This may be useful for recovering a
7 mistyped or partially forgotten password. */
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h> /* generated by configure */
11 #endif
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <getopt.h>
18
19 #include "lists.h"
20 #include "rijndael.h"
21 #include "readkey.h"
22
23 #define MAGIC "c051" /* magic string for this version of ccrypt */
24 #define NAME "ccguess" /* name of this program */
25 #define CLEARLINE "\x1B[G\x1B[K"
26
27 unsigned long long int global_count = 0;
28
29 /* ---------------------------------------------------------------------- */
30 /* general auxiliary */
31
32 /* count number of occurrences of c in s */
strcount(char * s,int c)33 int strcount(char *s, int c) {
34 int count;
35
36 count = 0;
37 while (*s != 0) {
38 if (*s == c) {
39 count++;
40 }
41 s++;
42 }
43 return count;
44 }
45
46 /* ---------------------------------------------------------------------- */
47 /* from ccryptlib.c */
48
49 /* hash a keystring into a 256-bit cryptographic random value. */
hashstring(char * keystring,xword32 hash[8])50 static void hashstring(char *keystring, xword32 hash[8]) {
51 int i;
52 roundkey rkk;
53 xword32 key[8]; /* rijndael key */
54
55 for (i=0; i<8; i++)
56 key[i] = hash[i] = 0;
57
58 do {
59 for (i=0; i<32; i++) {
60 if (*keystring != 0) {
61 ((xword8 *)key)[i] ^= *keystring;
62 keystring++;
63 }
64 }
65 xrijndaelKeySched(key, 256, 256, &rkk);
66 xrijndaelEncrypt(hash, &rkk);
67 } while (*keystring != 0);
68 }
69
70 /* ---------------------------------------------------------------------- */
71 /* key testing */
72
73 /* try the key on the given header. Return 1 if it matches, else 0. */
try_key(roundkey * rkk,xword32 header[8])74 int try_key(roundkey *rkk, xword32 header[8]) {
75 xword32 headercopy[8];
76
77 memcpy(headercopy, header, 32);
78 xrijndaelDecrypt(headercopy, rkk);
79 if (strncmp((char *)headercopy, MAGIC, 4) != 0) {
80 return 0;
81 } else {
82 return 1;
83 }
84 }
85
86 /* ---------------------------------------------------------------------- */
87 /* patterns */
88
89 /* instead of enumerating all individual variants of a key, we enumerate
90 patterns of variants. This makes it easy to weed out most duplicates. */
91
92 struct pattern_s {
93 char *s; /* pattern */
94 int l; /* length */
95 int w; /* number of wildcards */
96 int m; /* number of modifications (e.g., insertions, transpositions) */
97 int p; /* negative log of probability */
98 struct pattern_s *next, *next1;
99 };
100 typedef struct pattern_s pattern_t;
101
102 /* return a new pattern, or NULL with errno set on error */
pattern_new(char * s,int wildcard,int m,int p)103 pattern_t *pattern_new(char *s, int wildcard, int m, int p) {
104 pattern_t *pat;
105
106 pat = (pattern_t *)malloc(sizeof(pattern_t));
107 if (pat == NULL) {
108 return NULL;
109 }
110 pat->l = strlen(s);
111 pat->s = strdup(s);
112 pat->w = strcount(s, wildcard);
113 pat->m = m;
114 pat->p = p;
115 pat->next = NULL;
116
117 if (pat->s == NULL) {
118 free(pat);
119 return NULL;
120 }
121 return pat;
122 }
123
124 /* free a pattern */
pattern_free(pattern_t * pat)125 void pattern_free(pattern_t *pat) {
126 if (pat == NULL) {
127 return;
128 }
129 free(pat->s);
130 pat->s = NULL;
131 pat->next = NULL;
132 free(pat);
133 return;
134 }
135
136 /* ---------------------------------------------------------------------- */
137 /* pattern generation */
138
139 /* the functions insertion, replacement, transposition, and deletion
140 each generate a list of patterns without worrying about duplicates.
141 They return 0 on success, or 1 on error with errno set (typically
142 out of memory). */
143
144 /* prepend to plistp all patterns obtained from pat by an insertion. */
insertion(pattern_t * pat,pattern_t ** plistp,int wildcard)145 int insertion(pattern_t *pat, pattern_t **plistp, int wildcard) {
146 pattern_t *plist = *plistp;
147 int len = pat->l;
148 char *s = pat->s;
149 char buf[len+2];
150 int i;
151 pattern_t *pat1;
152
153 for (i=0; i<=len; i++) {
154 strncpy(buf, s, i);
155 strcpy(buf+i+1, s+i);
156 buf[i] = wildcard;
157 pat1 = pattern_new(buf, wildcard, pat->m + 1, pat->p + 2);
158 if (!pat1) {
159 return 1;
160 }
161 list_prepend(plist, pat1);
162 }
163 *plistp = plist;
164 return 0;
165 }
166
167 /* prepend to plistp all patterns obtained from pat by a replacement */
replacement(pattern_t * pat,pattern_t ** plistp,int wildcard)168 int replacement(pattern_t *pat, pattern_t **plistp, int wildcard) {
169 pattern_t *plist = *plistp;
170 int len = pat->l;
171 char *s = pat->s;
172 char buf[len+1];
173 int i;
174 pattern_t *pat1;
175
176 for (i=0; i<len; i++) {
177 strcpy(buf, s);
178 buf[i] = wildcard;
179 pat1 = pattern_new(buf, wildcard, pat->m + 1, pat->p + 1);
180 if (!pat1) {
181 return 1;
182 }
183 list_prepend(plist, pat1);
184 }
185 *plistp = plist;
186 return 0;
187 }
188
189 /* prepend to plistp all patterns obtained from pat by a transposition. */
transposition(pattern_t * pat,pattern_t ** plistp,int wildcard)190 int transposition(pattern_t *pat, pattern_t **plistp, int wildcard) {
191 pattern_t *plist = *plistp;
192 int len = pat->l;
193 char *s = pat->s;
194 char buf[len+1];
195 int i;
196 pattern_t *pat1;
197
198 for (i=0; i<len-1; i++) {
199 strcpy(buf, s);
200 buf[i] = s[i+1];
201 buf[i+1] = s[i];
202 pat1 = pattern_new(buf, wildcard, pat->m + 1, pat->p + 1);
203 if (!pat1) {
204 return 1;
205 }
206 list_prepend(plist, pat1);
207 }
208 *plistp = plist;
209 return 0;
210 }
211
212 /* prepend to plistp all patterns obtained from pat by a deletion */
deletion(pattern_t * pat,pattern_t ** plistp,int wildcard)213 int deletion(pattern_t *pat, pattern_t **plistp, int wildcard) {
214 pattern_t *plist = *plistp;
215 int len = pat->l;
216 char *s = pat->s;
217 char buf[len];
218 int i;
219 pattern_t *pat1;
220
221 for (i=0; i<len; i++) {
222 strncpy(buf, s, i);
223 strcpy(buf+i, s+i+1);
224 pat1 = pattern_new(buf, wildcard, pat->m + 1, pat->p + 1);
225 if (!pat1) {
226 return 1;
227 }
228 list_prepend(plist, pat1);
229 }
230 *plistp = plist;
231 return 0;
232 }
233
234 /* add to plistp all patterns that can be obtained from plistp by a
235 single transformation; do not generate duplicates. Assume that
236 plistp is sorted alphabetically, and return plistp sorted
237 alphabetically. Return 0 on success, or 1 on error with errno set
238 (typically out of memory). */
expand(pattern_t ** plistp,int wildcard)239 int expand(pattern_t **plistp, int wildcard) {
240 pattern_t *rawlist = NULL;
241 pattern_t *elt, *a, *b;
242 pattern_t *plist = *plistp;
243
244 /* generate single-transformation patterns */
245 list_forall(elt, plist) {
246 if (deletion(elt, &rawlist, wildcard) != 0) {
247 return 1;
248 }
249 if (insertion(elt, &rawlist, wildcard) != 0) {
250 return 1;
251 }
252 if (replacement(elt, &rawlist, wildcard) != 0) {
253 return 1;
254 }
255 if (transposition(elt, &rawlist, wildcard) != 0) {
256 return 1;
257 }
258 }
259
260 /* sort them alphabetically */
261 list_mergesort(pattern_t, rawlist, a, b, strcmp(a->s, b->s) <= 0);
262
263 /* merge sorted lists plist and rawlist */
264 list_merge_sorted(pattern_t, plist, rawlist, a, b, strcmp(a->s, b->s) <= 0);
265
266 /* eliminate duplicates */
267 list_uniq_sorted(pattern_t, plist, a, b, strcmp(a->s, b->s) == 0, a->m <= b->m, pattern_free);
268
269 *plistp = plist;
270 return 0;
271 }
272
273 /* list of printable characters */
274 char printable[] = {
275 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
276 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
277 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
278 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
279 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
280 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
281 126,
282 };
283
284 #define printable_size ((int)(sizeof(printable)/sizeof(char)))
285
286 char wildcard_chars[] = "*_#.@&$!%^-+=:";
287
288 /* Try all patterns in the list. Print out any that match. If cont is
289 set, continue trying after the first match is found. Return the
290 number of matching keys found. Chartable is an array of characters
291 to use of size ctsize. */
tryall(pattern_t * plist,int wildcard,char * chartable,int ctsize,xword32 (* headers)[8],int n,int cont)292 int tryall(pattern_t *plist, int wildcard, char *chartable, int ctsize, xword32 (*headers)[8], int n, int cont) {
293 pattern_t *pat;
294 pattern_t *elt;
295 pattern_t *matchlist = NULL; /* do avoid duplicates */
296 int i, j;
297 int matches;
298 int count = 0;
299 xword32 keyblock[8];
300 roundkey rkk;
301
302 list_forall(pat, plist) {
303 int w = pat->w;
304 int index[w];
305 int alpha[w];
306 char *s = strdup(pat->s);
307 int l = pat->l;
308
309 fprintf(stderr, "" CLEARLINE "%s %llu", s, global_count);
310 fflush(stderr);
311
312 /* index the wildcard positions */
313 i = 0;
314 for (j=0; j<l; j++) {
315 if (pat->s[j] == wildcard) {
316 index[i] = j;
317 i++;
318 }
319 }
320
321 /* iterate through all wildcard substitutions. */
322 for (i=0; i<w; i++) {
323 alpha[i] = 0;
324 s[index[i]] = chartable[0];
325 }
326 while (1) {
327 global_count++;
328 matches = 0;
329 for (i=0; i<n; i++) {
330 hashstring(s, keyblock);
331 xrijndaelKeySched(keyblock, 256, 256, &rkk);
332 if (try_key(&rkk, headers[i])) {
333 matches++;
334 }
335 }
336 if (matches > 0) {
337 list_find(elt, matchlist, strcmp(elt->s, s)==0);
338 if (!elt) {
339 count++;
340 fprintf(stderr, "" CLEARLINE "");
341 fflush(stderr);
342 printf("\n");
343 printf("Possible match: %s (%d change%s, found after trying %llu key%s)\n", s, pat->m, pat->m==1?"":"s", global_count, global_count==1?"":"s");
344 if (matches < n) {
345 printf("Warning: key only matches %d of %d files.\n", matches, n);
346 }
347 fflush(stdout);
348 elt = pattern_new(s, wildcard, pat->m, pat->p);
349 list_append(pattern_t, matchlist, elt);
350 if (matches == n && !cont) {
351 return count;
352 }
353 }
354 }
355
356 i = w-1;
357 while (i>=0 && alpha[i] >= ctsize-1) {
358 alpha[i]=0;
359 s[index[i]] = chartable[0];
360 i--;
361 }
362 if (i<0) {
363 break;
364 }
365 alpha[i]++;
366 s[index[i]] = chartable[alpha[i]];
367 }
368 free(s);
369 }
370 fprintf(stderr, "" CLEARLINE "");
371 fflush(stderr);
372 list_forall_unlink(elt, matchlist) {
373 pattern_free(elt);
374 }
375 return count;
376 }
377
378 /* guess the keys for decrypting headers, using keys similar to "key"
379 and with at most depth changes. If cont is set, continue trying
380 more keys after the first match is found. Return the number of
381 matches found, or -1 on failure (such as, out of memory), with
382 errno set. The headers argument is an array of n headers. Chartable
383 is an array of ctsize characters to use as password characters. */
ccguess(char * key,xword32 (* headers)[8],int n,char * chartable,int ctsize,int depth,int cont)384 int ccguess(char *key, xword32 (*headers)[8], int n, char *chartable, int ctsize, int depth, int cont) {
385 int wildcard;
386 pattern_t *plist;
387 pattern_t *a, *b;
388 int i;
389 char *p;
390
391 wildcard = '*';
392 /* try to pick a wildcard character that is not part of the key. */
393 for (p=wildcard_chars; *p != 0; p++) {
394 if (strchr(key, *p) == NULL) {
395 wildcard = *p;
396 break;
397 }
398 }
399
400 fprintf(stderr, "Generating patterns...");
401 fflush(stderr);
402 plist = pattern_new(key, wildcard, 0, 0);
403
404 for (i=0; i<depth; i++) {
405 fprintf(stderr, "%d..", i+1);
406 fflush(stderr);
407 if (expand(&plist, wildcard) != 0) {
408 return -1;
409 }
410 }
411 fprintf(stderr, "sorting...");
412 fflush(stderr);
413 /* We assume a simple statistical model where each modification
414 occurs independently with probability 0.1, except insertions
415 occur with probability 0.01. Therefore, the probability of a
416 particular pattern with m modifications is 0.1^p. However, there
417 are about 100 printable characters, so the probability of a
418 particular *instance* of a pattern with m modifications and w
419 wildcards is 0.1^p * 0.01^w = 0.1^(p+2w). To minimize the
420 expected time of search, we sort patterns by decreasing
421 probability of their instances. */
422 list_mergesort(pattern_t, plist, a, b, a->p + 2*a->w <= b->p + 2*b->w);
423 fprintf(stderr, "done.\n");
424
425 return tryall(plist, wildcard, chartable, ctsize, headers, n, cont);
426 }
427
428 /* ---------------------------------------------------------------------- */
429 /* user interface */
430
431 /* print usage information */
usage(FILE * fout)432 static void usage(FILE *fout) {
433 fprintf(fout, "" NAME " " VERSION ". Search for ccrypt encryption keys.\n");
434 fprintf(fout, "\n");
435 fprintf(fout,
436 "Usage: " NAME " [options] file...\n"
437 "Options:\n"
438 " -h, --help print this help message and exit\n"
439 " -V, --version print version info and exit\n"
440 " -L, --license print license info and exit\n"
441 " -K, --key <key> specify approximate key\n"
442 " -d, --depth try up to this many changes to key (default: 5)\n"
443 " -c, --continue keep trying more keys after first match\n"
444 " -n, --non-printable allow non-printable characters in keys\n"
445 " -t, --chartable <chars> characters to use in passwords (default: printable)\n"
446 "Arguments:\n"
447 " file... files to read encrypted data from, or '-' for stdin\n"
448 );
449 }
450
451 /* print version and copyright information */
version(FILE * fout)452 static void version(FILE *fout) {
453 fprintf(fout, "" NAME " " VERSION ". Search for ccrypt encryption keys.\n");
454 fprintf(fout, "Copyright (C) 2000-2018 Peter Selinger.\n");
455 }
456
license(FILE * fout)457 static void license(FILE *fout) {
458 fprintf(fout, "" NAME " " VERSION ". Search for ccrypt encryption keys.\n");
459 fprintf(fout, "Copyright (C) 2000-2018 Peter Selinger.\n");
460 fprintf(fout, "\n");
461 fprintf(fout,
462 "For the full text of the GNU General Public License, see the file\n"
463 "COPYING distributed with this software.\n"
464 "\n"
465 "This program is free software; you can redistribute it and/or modify\n"
466 "it under the terms of the GNU General Public License as published by\n"
467 "the Free Software Foundation; either version 2 of the License, or\n"
468 "(at your option) any later version.\n"
469 "\n"
470 "This program is distributed in the hope that it will be useful,\n"
471 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
472 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
473 "GNU General Public License for more details.\n"
474 "\n"
475 "You should have received a copy of the GNU General Public License\n"
476 "along with this program; if not, write to the Free Software Foundation,\n"
477 "Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n"
478 );
479 }
480
481 static struct option longopts[] = {
482 {"help", 0, 0, 'h'},
483 {"version", 0, 0, 'V'},
484 {"license", 0, 0, 'L'},
485 {"key", 1, 0, 'K'},
486 {"depth", 1, 0, 'd'},
487 {"continue", 0, 0, 'c'},
488 {"non-printable", 0, 0, 'n'},
489 {"chartable", 1, 0, 't'},
490 {0, 0, 0, 0}
491 };
492
493 static const char *shortopts = "hVLK:d:cnt:";
494
495 /* structure to hold command-line */
496 typedef struct {
497 char *keyword;
498 int depth;
499 char **infiles; /* input filenames ("-" for stdin) */
500 int nfiles; /* number of filenames */
501 int cont;
502 int non_printable;
503 char *chartable;
504 } cmdline;
505
read_commandline(int ac,char * av[])506 static cmdline read_commandline(int ac, char *av[]) {
507 cmdline cmd;
508 int c;
509 char *p;
510
511 /* defaults: */
512 cmd.keyword = NULL;
513 cmd.depth = 5;
514 cmd.nfiles = 0;
515 cmd.cont = 0;
516 cmd.non_printable = 0;
517 cmd.chartable = NULL;
518
519 while ((c = getopt_long(ac, av, shortopts, longopts, NULL)) != -1) {
520 switch (c) {
521 case 'h':
522 usage(stdout);
523 exit(0);
524 break;
525 case 'V':
526 version(stdout);
527 exit(0);
528 break;
529 case 'L':
530 license(stdout);
531 exit(0);
532 break;
533 case 'K':
534 free(cmd.keyword);
535 cmd.keyword = strdup(optarg);
536 if (!cmd.keyword) {
537 goto mem_error;
538 }
539 /* attempt to erase keyword from command line so that subsequent
540 calls to 'ps' don't display it */
541 for (p=optarg; *p; p++) {
542 *p = 0;
543 }
544 break;
545 case 'd':
546 cmd.depth = strtod(optarg, &p);
547 if (*p || cmd.depth <= 0) {
548 fprintf(stderr, "" NAME ": invalid depth -- %s\n", optarg);
549 exit(2);
550 }
551 break;
552 case 'c':
553 cmd.cont = 1;
554 break;
555 case 'n':
556 cmd.non_printable = 1;
557 cmd.chartable = NULL;
558 break;
559 case 't':
560 cmd.chartable = optarg;
561 cmd.non_printable = 0;
562 break;
563 case '?':
564 fprintf(stderr, "Try --help for more information.\n");
565 exit(2);
566 break;
567 default:
568 fprintf(stderr, "" NAME ": unimplemented option -- %c\n", c);
569 exit(2);
570 }
571 }
572
573 switch (ac-optind) {
574 case 0:
575 fprintf(stderr, "" NAME ": missing filename. Try --help for more information.\n");
576 exit(2);
577 break;
578 default:
579 cmd.infiles = &av[optind];
580 cmd.nfiles = ac-optind;
581 break;
582 }
583 return cmd;
584
585 mem_error:
586 fprintf(stderr, "" NAME ": %s\n", strerror(errno));
587 free(cmd.keyword);
588 exit(2);
589 }
590
main(int ac,char ** av)591 int main(int ac, char **av) {
592 xword32 (*headers)[8];
593 int r;
594 cmdline cmd;
595 FILE *fin;
596 int i, j;
597 char *file;
598 char bytes[256];
599 char *chartable;
600 int ctsize;
601
602 /* read command line */
603 cmd = read_commandline(ac, av);
604
605 /* prepare character table */
606 if (cmd.chartable) {
607 /* condense character table to eliminate duplicates */
608 memset(bytes, 0, 256);
609 for (i=0; cmd.chartable[i]; i++) {
610 bytes[((int)cmd.chartable[i]) & 0xff] = cmd.chartable[i];
611 }
612 for (i=0, j=0; i<256; i++) {
613 if (bytes[i]) {
614 bytes[j] = bytes[i];
615 j++;
616 }
617 }
618 chartable = bytes;
619 ctsize = j;
620 } else if (cmd.non_printable) {
621 for (i=1; i<256; i++) {
622 bytes[i-1] = i;
623 }
624 chartable = bytes;
625 ctsize = 255;
626 } else {
627 chartable = printable;
628 ctsize = printable_size;
629 }
630
631 /* read keyword from terminal if necessary */
632 if (cmd.keyword==NULL) {
633 cmd.keyword = readkey("Enter approximate key: ", "", NAME , 1);
634 if (cmd.keyword==NULL) { /* end of file: exit gracefully */
635 fprintf(stderr, "" NAME ": no key given\n");
636 return 2;
637 }
638 }
639
640 headers = (xword32 (*)[8]) malloc(cmd.nfiles * sizeof(xword32[8]));
641 if (!headers) {
642 fprintf(stderr, "" NAME ": %s\n", strerror(errno));
643 return 2;
644 }
645
646 /* read headers from all files */
647 for (i=0; i<cmd.nfiles; i++) {
648 file = cmd.infiles[i];
649
650 /* open encrypted file */
651 if (strcmp(file, "-") == 0) {
652 fin = stdin;
653 } else {
654 fin = fopen(file, "r");
655 if (!fin) {
656 fprintf(stderr, "" NAME ": could not read %s: %s\n", file, strerror(errno));
657 return 2;
658 }
659 }
660
661 /* read header */
662 r = fread(headers[i], 32, 1, fin);
663 if (r != 1) {
664 fprintf(stderr, "" NAME ": %s: file too short\n", file);
665 return 2;
666 }
667
668 /* close file */
669 if (strcmp(file, "-") != 0) {
670 fclose(fin);
671 }
672 }
673
674 /* payload */
675 r = ccguess(cmd.keyword, headers, cmd.nfiles, chartable, ctsize, cmd.depth, cmd.cont);
676 free(headers);
677
678 if (r < 0) {
679 fprintf(stderr, "" NAME ": %s\n", strerror(errno));
680 return 2;
681 }
682 return r>0 ? 0 : 1;
683 }
684