Mix the noise from the key timings with some other environmental noise
[become] / src / keygen.c
CommitLineData
c4f2d992 1/* -*-c-*-
2 *
b80cbed5 3 * $Id: keygen.c,v 1.3 1997/09/17 15:29:28 mdw Exp $
c4f2d992 4 *
5 * Key generation
6 *
7 * (c) 1997 EBI
8 */
9
03f996bd 10/*----- Licensing notice --------------------------------------------------*
c4f2d992 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
03f996bd 25 * along with `become'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
c4f2d992 27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: keygen.c,v $
b80cbed5 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 *
03f996bd 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
c4f2d992 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"
c4f2d992 67#include "mdwopt.h"
b80cbed5 68#include "noise.h"
69#include "rand.h"
70#include "tx.h"
c4f2d992 71#include "utils.h"
72
73/*----- Static variables --------------------------------------------------*/
74
75static struct termios kg__raw, kg__old; /* Terminal settings */
76static int kg__tty; /* File handle for the terminal */
77static FILE *kg__ttyfp; /* Stream pointer for terminal */
78static unsigned int kg__flags; /* Various interesting flags */
79
80enum {
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
96void 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
131static 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
153static 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
171static 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
187static 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
248static 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,
03f996bd 257"I need to get %lu random bits; I'll do this by timing your keypresses.\n"
c4f2d992 258"Please type some arbitrary text until I say `done'.\n",
03f996bd 259 (unsigned long)sz);
c4f2d992 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
03f996bd 273 fprintf(kg__ttyfp, "\r%5lu...", (unsigned long)sz);
c4f2d992 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
433int 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
b80cbed5 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
c4f2d992 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 */
b80cbed5 591 rand_clear();
c4f2d992 592 return (0);
593}
594
595/*----- That's all, folks -------------------------------------------------*/