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