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