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