chiark / gitweb /
Change directory after becoming someone else, instead of before. This
[become] / src / keygen.c
1 /* -*-c-*-
2  *
3  * $Id: keygen.c,v 1.3 1997/09/17 15:29:28 mdw Exp $
4  *
5  * Key generation
6  *
7  * (c) 1997 EBI
8  */
9
10 /*----- Licensing notice --------------------------------------------------*
11  *
12  * This file is part of `become'
13  *
14  * `Become' is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * `Become' is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with `become'; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Revision history --------------------------------------------------*
30  *
31  * $Log: keygen.c,v $
32  * Revision 1.3  1997/09/17 15:29:28  mdw
33  * Mix the noise from the key timings with some other environmental noise
34  * (obtained from `noise_acquire') for a little bit more randomness.
35  *
36  * Revision 1.2  1997/08/04 10:24:23  mdw
37  * Sources placed under CVS control.
38  *
39  * Revision 1.1  1997/07/21  13:47:48  mdw
40  * Initial revision
41  *
42  */
43
44 /*----- Header files ------------------------------------------------------*/
45
46 /* --- ANSI headers --- */
47
48 #include <ctype.h>
49 #include <errno.h>
50 #include <signal.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54
55 /* --- Unix headers --- */
56
57 #include <sys/types.h>
58 #include <sys/time.h>
59 #include <fcntl.h>
60
61 #include <termios.h>
62 #include <unistd.h>
63
64 /* --- Local headers --- */
65
66 #include "config.h"
67 #include "mdwopt.h"
68 #include "noise.h"
69 #include "rand.h"
70 #include "tx.h"
71 #include "utils.h"
72
73 /*----- Static variables --------------------------------------------------*/
74
75 static struct termios kg__raw, kg__old; /* Terminal settings */
76 static int kg__tty;                     /* File handle for the terminal */
77 static FILE *kg__ttyfp;                 /* Stream pointer for terminal */
78 static unsigned int kg__flags;          /* Various interesting flags */
79
80 enum {
81   kgFlag__cbreak = 1                    /* Terminal is in cbreak mode */
82 };
83
84 /*----- Main code ---------------------------------------------------------*/
85
86 /* --- @kg__cbreak@ --- *
87  *
88  * Arguments:   ---
89  *
90  * Returns:     ---
91  *
92  * Use:         Makes the terminal return characters as soon as they're
93  *              asked for.
94  */
95
96 void kg__cbreak(void)
97 {
98   /* --- Don't do this if I don't have to --- */
99
100   if (kg__flags & kgFlag__cbreak)
101     return;
102
103   /* --- Fetch the old attributes, and remember them --- */
104
105   if (tcgetattr(kg__tty, &kg__old))
106     die("couldn't read terminal attributes: %s", strerror(errno));
107   memcpy(&kg__raw, &kg__old, sizeof(kg__raw));
108
109   /* --- Now modify them for raw mode --- */
110
111   kg__raw.c_lflag &= ~(ICANON | ECHO);
112   kg__raw.c_cc[VTIME] = 0;
113   kg__raw.c_cc[VMIN] = 1;
114
115   /* --- Remember the new state, and away we go --- */
116
117   kg__flags |= kgFlag__cbreak;
118   if (tcsetattr(kg__tty, TCSAFLUSH, &kg__raw))
119     die("couldn't set terminal attributes: %s", strerror(errno));
120 }
121
122 /* --- @kg__crepair@ --- *
123  *
124  * Arguments:   ---
125  *
126  * Returns:     ---
127  *
128  * Use:         Unbreaks a cbroken tty.  Obvious, innit?
129  */
130
131 static void kg__crepair(void)
132 {
133   /* --- Don't do this if I don't have to --- */
134
135   if (~kg__flags & kgFlag__cbreak)
136     return;
137
138   /* --- Reset the old attributes --- */
139
140   tcsetattr(kg__tty, TCSAFLUSH, &kg__old);
141   kg__flags &= ~kgFlag__cbreak;
142 }
143
144 /* --- @kg__signal@ --- *
145  *
146  * Arguments:   @int sig@ = signal number
147  *
148  * Returns:     ---
149  *
150  * Use:         Tidies up if I get a signal.
151  */
152
153 static void kg__signal(int sig)
154 {
155   kg__crepair();
156   signal(sig, SIG_DFL);
157   raise(sig);
158 }
159
160 /* --- @kgFmt__binary@ --- *
161  *
162  * Arguments:   @unsigned char *k@ = pointer to key buffer
163  *              @size_t bits@ = number of bits to write
164  *              @FILE *fp@ = stream to write on
165  *
166  * Returns:     ---
167  *
168  * Use:         Writes bits on a stream.
169  */
170
171 static void kgFmt__binary(unsigned char *k, size_t bits, FILE *fp)
172 {
173   fwrite(k, 1, bits / 8, fp);
174 }
175
176 /* --- @kgFmt__base64@ --- *
177  *
178  * Arguments:   @unsigned char *k@ = pointer to key buffer
179  *              @size_t bits@ = number of bits to write
180  *              @FILE *fp@ = stream to write on
181  *
182  * Returns:     ---
183  *
184  * Use:         Writes bits on a stream in an encoded way.
185  */
186
187 static void kgFmt__base64(unsigned char *k, size_t bits, FILE *fp)
188 {
189   static const char xlt[64] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
190                                 "abcdefghijklmnopqrstuvwxyz"
191                                 "0123456789+/" };
192   unsigned long b;
193   int ll = 0;
194
195   while (bits > 24) {
196     b = ((k[0] & 0xffu) << 16) | ((k[1] & 0xffu) << 8) | (k[2] & 0xffu);
197     k += 3;
198     bits -= 24;
199     putc(xlt[(b >> 18) & 0x3fu], fp);
200     putc(xlt[(b >> 12) & 0x3fu], fp);
201     putc(xlt[(b >>  6) & 0x3fu], fp);
202     putc(xlt[(b >>  0) & 0x3fu], fp);
203     if ((ll += 4) > 70) {
204       putc('\n', fp);
205       ll = 0;
206     }
207   }
208
209   b = 0;
210   switch (bits) {
211     case 24:
212       b = *k++ & 0xffu;
213     case 16:
214       b = (b << 8) | (*k++ & 0xffu);
215     case 8:
216       b = (b << 8) | (*k++ & 0xffu);
217   }
218   b <<= (24 - bits);
219   putc(xlt[(b >> 18) & 0x3fu], fp);
220   putc(xlt[(b >> 12) & 0x3fu], fp);
221   switch (bits) {
222     case 24:
223       putc(xlt[(b >>  6) & 0x3fu], fp);
224       putc(xlt[(b >>  0) & 0x3fu], fp);
225       break;
226     case 16:
227       putc(xlt[(b >>  6) & 0x3fu], fp);
228       putc('=', fp);
229       break;
230     case 8:
231       fputs("==", fp);
232       break;
233   }
234
235   putc('\n', fp);
236 }
237
238 /* --- @kg__gen@ --- *
239  *
240  * Arguments:   @unsigned char *ui@ = pointer to array to fill in
241  *              @size_t sz@ = number of bits to generate
242  *
243  * Returns:     ---
244  *
245  * Use:         Uses key timings to generate random numbers.  Maybe.
246  */
247
248 static void kg__gen(unsigned char *ui, size_t sz)
249 {
250   int bits = 32 < sz ? 32 : sz;
251   size_t wsz = (sz + 31u) & ~31u;
252   unsigned long last, ldiff = 0;
253   unsigned long a = 0;
254   unsigned long fact = 1000000 / CLOCKS_PER_SEC;
255
256   fprintf(kg__ttyfp,
257 "I need to get %lu random bits; I'll do this by timing your keypresses.\n"
258 "Please type some arbitrary text until I say `done'.\n",
259           (unsigned long)sz);
260
261   {
262     struct timeval tv;
263     gettimeofday(&tv, 0);
264     last = tv.tv_usec / fact + tv.tv_sec * fact;
265   }
266
267   while (sz) {
268     int useful;
269     uint_32 orr, xor;
270
271     /* --- Print current status --- */
272
273     fprintf(kg__ttyfp, "\r%5lu...", (unsigned long)sz);
274     fflush(kg__ttyfp);
275
276     /* --- Read the next character --- */
277
278     {
279       char buff[16];
280       if (read(kg__tty, buff, sizeof(buff)) < 0)
281         die("couldn't read from terminal: %s", strerror(errno));
282     }
283
284     /* --- Fiddle with times --- *
285      *
286      * Read the time now.  Turn it into 32 bits of useful information, and
287      * find the difference between that and the previous time.  Compare this
288      * with the difference between the previous pair of keypresses.
289      */
290
291     {
292       struct timeval tv;
293       uint_32 n, nd;
294
295       gettimeofday(&tv, 0);
296       n = tv.tv_usec / fact + tv.tv_sec * fact;
297       if (!ldiff) {
298         ldiff = n - last;
299         last = n;
300         continue;
301       }
302       nd = n - last;
303       orr = nd | ldiff;
304       xor = nd ^ ldiff;
305       D( printf("\nlast = %08lx, next = %08lx, ldiff = %08lx, nd = %08lx\n",
306                 (unsigned long)last, (unsigned long)n,
307                 (unsigned long)ldiff, (unsigned long)nd);
308          printf("xor = %08lx\n", (unsigned long)xor); )
309       ldiff = nd;
310       last = n;
311     }
312
313     /* --- Find the useful bits in this value --- *
314      *
315      * Find the least significant set bit in @bowl@ and chop it off.  Then
316      * find the most significant set bit and chop that off two.  The rest is
317      * probably interesting.
318      */
319
320     {
321       unsigned long i;
322
323       if (!orr || !xor)
324         continue; /* erk! */
325
326       useful = 30;
327
328       i = 0x80000000ul;
329       if (xor & i) {
330         while (i && (xor & i) != 0) {
331           i >>= 1;
332           useful--;
333         }
334       } else {
335         while (i && (xor & i) == 0) {
336           i >>= 1;
337           useful--;
338         }
339       }
340       xor &= ~-i;
341
342       while ((orr & 1) == 0) {
343         useful--;
344         orr >>= 1;
345         xor >>= 1;
346       }
347       xor >>= 1;
348
349       if (useful < 1)
350         continue;
351     }
352
353     /* --- Now add the bits in the mixing bowl to my stash --- *
354      *
355      * There are two cases:
356      *
357      * 1. I have more bits than will fit into the accumulator.  Then I must
358      *    put as many bits into the accumulator as will fit, store the
359      *    accumulator, and remove the spent bits.
360      *
361      * 2. I have too few bits to fit in the accumulator.  Then shift them
362      *    in and return.
363      */
364
365     while (sz && useful) {
366
367       D( printf("got %i bits, need %i/%i: %8lx\n",
368                 useful, bits, sz, (unsigned long)xor); )
369
370       if (useful >= bits) {
371
372         D( printf("shifted acc = %08lx\n"
373                   "   new bits = %08lx\n"
374                   "     result = %08lx\n",
375                   (unsigned long)(a << bits),
376                   (unsigned long)(xor >> (useful - bits)),
377                   (unsigned long)((a << bits) | (xor >> (useful - bits)))); )
378
379         a = (a << bits) | (xor >> (useful - bits));
380
381         sz -= bits;
382         wsz -= bits;
383         useful -= bits;
384
385         if (sz == 0) {
386           a <<= wsz;
387           bits = 32 - wsz;
388         } else
389           bits = 32;
390
391         while (bits > 0) {
392           D( printf("writing %02x\n", (a >> 24) & 0xffu); )
393           *ui++ = (a >> 24) & 0xffu;
394           a <<= 8;
395           bits -= 8;
396         }
397         a = 0;
398
399         bits = 32 < sz ? 32 : sz;
400
401       } else {
402
403         D( printf("shifted acc = %08lx\n"
404                   "   new bits = %08lx\n"
405                   "     result = %08lx\n",
406                   (unsigned long)(a << useful),
407                   (unsigned long)(xor),
408                   (unsigned long)((a << useful) | (xor))); )
409         a = (a << useful) | xor;
410         bits -= useful;
411         sz -= useful;
412         wsz -= useful;
413         useful = 0;
414       }
415     }
416   }
417
418   fputs("\rDone!         \n", kg__ttyfp);
419   putc('\a', kg__ttyfp); fflush(kg__ttyfp);
420   sleep(1);
421 }
422
423 /* --- @main@ --- *
424  *
425  * Arguments:   @int argc@ = number of arguments
426  *              @char *argv[]@ = array of arguments
427  *
428  * Returns:     Zero if it worked, nonzero if it didn't.
429  *
430  * Use:         Generates random numbers from the keyboard.
431  */
432
433 int main(int argc, char *argv[])
434 {
435   unsigned char *uip;
436   size_t sz = 128;
437   const char *file = 0;
438   FILE *fp;
439
440   /* --- Formats table --- */
441
442   static struct {
443     const char *name;
444     void (*proc)(unsigned char *k, size_t bits, FILE *fp);
445   } format[] = {
446     { "tx",     tx_putBits },
447     { "hex",    tx_putBits },
448     { "binary", kgFmt__binary },
449     { "base64", kgFmt__base64 },
450     { 0, 0 }
451   };
452
453   void (*fmt)(unsigned char *, size_t, FILE *) = tx_putBits;
454
455   /* --- Explain who I am --- */
456
457   ego(argv[0]);
458
459   /* --- Read arguments --- */
460
461   for (;;) {
462     static struct option opts[] = {
463       { "help",         0,              0,      'h' },
464       { "bits",         gFlag_argReq,   0,      'b' },
465       { "output",       gFlag_argReq,   0,      'o' },
466       { "format",       gFlag_argReq,   0,      'f' },
467       { 0,              0,              0,      0 }
468     };
469
470     int i = mdwopt(argc, argv, "hb:o:f:", opts, 0, 0, 0);
471
472     if (i < 0)
473       break;
474     switch (i) {
475       case 'h':
476         printf(""
477 "Usage: %s [--bits=BITS] [--output=FILE] [--format=FORMAT]\n"
478 "\n"
479 "Generates BITS (by default, 128) random bits by timing keypresses.  The\n"
480 "resulting number is written to FILE, or standard output in the given\n"
481 "format.  Formats currently supported are:\n"
482 "\n"
483 "tx, hex                Hexadecimal, with punctuating dashes.\n"
484 "binary         Raw binary.\n"
485 "base64         Base64-encoded binary; the result is printable ASCII.\n",
486                quis());
487         exit(0);
488         break;
489       case 'b':
490         sz = atoi(optarg);
491         if (sz == 0)
492           die("bad number of bits (illegible or zero)");
493         if (sz % 8)
494           die("can only generate a whole number of 8-bit bytes");
495         break;
496       case 'o':
497         file = optarg;
498         break;
499       case 'f':
500         fmt = 0;
501         for (i = 0; format[i].name; i++) {
502           const char *p = format[i].name, *q = optarg;
503           for (;;) {
504             if (*q == 0)
505               break;
506             if (*p != *q)
507               break;
508             p++, q++;
509           }
510           if (!*q) {
511             if (fmt)
512               die("ambiguous format name: `%s'", optarg);
513             fmt = format[i].proc;
514           }
515         }
516         if (!fmt)
517           die("unknown format name: `%s'", optarg);
518         break;
519       case '?':
520         exit(1);
521         break;
522     }
523   }
524
525   if (optind < argc) {
526     fprintf(stderr, "Usage: %s [--bits=BITS] [--output=FILE]\n", quis());
527     exit(1);
528   }
529
530   /* --- Allocate memory --- */
531
532   uip = xmalloc(sz / 8);
533
534   /* --- Open the terminal --- *
535    *
536    * I'd like to be able to @fprintf@ to the terminal, so use @fopen@.
537    */
538
539   if ((kg__ttyfp = fopen("/dev/tty", "r+")) == 0)
540     die("couldn't open terminal: %s", strerror(errno));
541   kg__tty = fileno(kg__ttyfp);
542
543   /* --- Open the output file, if one is specified --- */
544
545   if (file) {
546     int fd;
547
548     /* --- Open the file oddly --- *
549      *
550      * There's a good reason for this.  I want to be able to @fprintf@ (for
551      * the benefit of @tx_putWords@ mainly) but I also want to ensure that
552      * only the user has read permissions for the file.  So I'll use @open@
553      * with an appropriate mode and then @fdopen@ the file descriptor to get
554      * a stream.  Yuk.
555      */
556
557     if ((fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
558       die("couldn't open output file: %s", strerror(errno));
559     if ((fp = fdopen(fd, "w")) == 0)
560       die("couldn't attach stream to output file: %s", strerror(errno));
561   } else
562     fp = stdout;
563
564   /* --- Tidy up nicely if I die --- */
565
566   signal(SIGINT, kg__signal);
567   signal(SIGTERM, kg__signal);
568   atexit(kg__crepair);
569
570   /* --- Put the terminal into cbreak, read the key, and restore --- */
571
572   kg__cbreak();
573   kg__gen(uip, sz);
574   kg__crepair();
575
576   /* --- Find some noise from the environment too --- */
577
578   rand_clear();
579   noise_acquire();
580   rand_add(uip, sz / 8);
581   rand_churn();
582   rand_extract(uip, sz / 8);
583
584   /* --- Now write the number and exit --- */
585
586   D( fputs("*** ", fp); tx_putBits(uip, sz, stdout); )
587   fmt(uip, sz, fp);
588   if (file)
589     fclose(fp);
590   memset(uip, 0, sz / 8);               /* Burn temporary buffer */
591   rand_clear();
592   return (0);
593 }
594
595 /*----- That's all, folks -------------------------------------------------*/