3 * $Id: keygen.c,v 1.3 1997/09/17 15:29:28 mdw Exp $
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of `become'
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.
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.
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.
29 /*----- Revision history --------------------------------------------------*
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.
36 * Revision 1.2 1997/08/04 10:24:23 mdw
37 * Sources placed under CVS control.
39 * Revision 1.1 1997/07/21 13:47:48 mdw
44 /*----- Header files ------------------------------------------------------*/
46 /* --- ANSI headers --- */
55 /* --- Unix headers --- */
57 #include <sys/types.h>
64 /* --- Local headers --- */
73 /*----- Static variables --------------------------------------------------*/
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 */
81 kgFlag__cbreak = 1 /* Terminal is in cbreak mode */
84 /*----- Main code ---------------------------------------------------------*/
86 /* --- @kg__cbreak@ --- *
92 * Use: Makes the terminal return characters as soon as they're
98 /* --- Don't do this if I don't have to --- */
100 if (kg__flags & kgFlag__cbreak)
103 /* --- Fetch the old attributes, and remember them --- */
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));
109 /* --- Now modify them for raw mode --- */
111 kg__raw.c_lflag &= ~(ICANON | ECHO);
112 kg__raw.c_cc[VTIME] = 0;
113 kg__raw.c_cc[VMIN] = 1;
115 /* --- Remember the new state, and away we go --- */
117 kg__flags |= kgFlag__cbreak;
118 if (tcsetattr(kg__tty, TCSAFLUSH, &kg__raw))
119 die("couldn't set terminal attributes: %s", strerror(errno));
122 /* --- @kg__crepair@ --- *
128 * Use: Unbreaks a cbroken tty. Obvious, innit?
131 static void kg__crepair(void)
133 /* --- Don't do this if I don't have to --- */
135 if (~kg__flags & kgFlag__cbreak)
138 /* --- Reset the old attributes --- */
140 tcsetattr(kg__tty, TCSAFLUSH, &kg__old);
141 kg__flags &= ~kgFlag__cbreak;
144 /* --- @kg__signal@ --- *
146 * Arguments: @int sig@ = signal number
150 * Use: Tidies up if I get a signal.
153 static void kg__signal(int sig)
156 signal(sig, SIG_DFL);
160 /* --- @kgFmt__binary@ --- *
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
168 * Use: Writes bits on a stream.
171 static void kgFmt__binary(unsigned char *k, size_t bits, FILE *fp)
173 fwrite(k, 1, bits / 8, fp);
176 /* --- @kgFmt__base64@ --- *
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
184 * Use: Writes bits on a stream in an encoded way.
187 static void kgFmt__base64(unsigned char *k, size_t bits, FILE *fp)
189 static const char xlt[64] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
190 "abcdefghijklmnopqrstuvwxyz"
196 b = ((k[0] & 0xffu) << 16) | ((k[1] & 0xffu) << 8) | (k[2] & 0xffu);
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) {
214 b = (b << 8) | (*k++ & 0xffu);
216 b = (b << 8) | (*k++ & 0xffu);
219 putc(xlt[(b >> 18) & 0x3fu], fp);
220 putc(xlt[(b >> 12) & 0x3fu], fp);
223 putc(xlt[(b >> 6) & 0x3fu], fp);
224 putc(xlt[(b >> 0) & 0x3fu], fp);
227 putc(xlt[(b >> 6) & 0x3fu], fp);
238 /* --- @kg__gen@ --- *
240 * Arguments: @unsigned char *ui@ = pointer to array to fill in
241 * @size_t sz@ = number of bits to generate
245 * Use: Uses key timings to generate random numbers. Maybe.
248 static void kg__gen(unsigned char *ui, size_t sz)
250 int bits = 32 < sz ? 32 : sz;
251 size_t wsz = (sz + 31u) & ~31u;
252 unsigned long last, ldiff = 0;
254 unsigned long fact = 1000000 / CLOCKS_PER_SEC;
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",
263 gettimeofday(&tv, 0);
264 last = tv.tv_usec / fact + tv.tv_sec * fact;
271 /* --- Print current status --- */
273 fprintf(kg__ttyfp, "\r%5lu...", (unsigned long)sz);
276 /* --- Read the next character --- */
280 if (read(kg__tty, buff, sizeof(buff)) < 0)
281 die("couldn't read from terminal: %s", strerror(errno));
284 /* --- Fiddle with times --- *
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.
295 gettimeofday(&tv, 0);
296 n = tv.tv_usec / fact + tv.tv_sec * fact;
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); )
313 /* --- Find the useful bits in this value --- *
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.
330 while (i && (xor & i) != 0) {
335 while (i && (xor & i) == 0) {
342 while ((orr & 1) == 0) {
353 /* --- Now add the bits in the mixing bowl to my stash --- *
355 * There are two cases:
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.
361 * 2. I have too few bits to fit in the accumulator. Then shift them
365 while (sz && useful) {
367 D( printf("got %i bits, need %i/%i: %8lx\n",
368 useful, bits, sz, (unsigned long)xor); )
370 if (useful >= bits) {
372 D( printf("shifted acc = %08lx\n"
373 " new bits = %08lx\n"
375 (unsigned long)(a << bits),
376 (unsigned long)(xor >> (useful - bits)),
377 (unsigned long)((a << bits) | (xor >> (useful - bits)))); )
379 a = (a << bits) | (xor >> (useful - bits));
392 D( printf("writing %02x\n", (a >> 24) & 0xffu); )
393 *ui++ = (a >> 24) & 0xffu;
399 bits = 32 < sz ? 32 : sz;
403 D( printf("shifted acc = %08lx\n"
404 " new bits = %08lx\n"
406 (unsigned long)(a << useful),
407 (unsigned long)(xor),
408 (unsigned long)((a << useful) | (xor))); )
409 a = (a << useful) | xor;
418 fputs("\rDone! \n", kg__ttyfp);
419 putc('\a', kg__ttyfp); fflush(kg__ttyfp);
425 * Arguments: @int argc@ = number of arguments
426 * @char *argv[]@ = array of arguments
428 * Returns: Zero if it worked, nonzero if it didn't.
430 * Use: Generates random numbers from the keyboard.
433 int main(int argc, char *argv[])
437 const char *file = 0;
440 /* --- Formats table --- */
444 void (*proc)(unsigned char *k, size_t bits, FILE *fp);
446 { "tx", tx_putBits },
447 { "hex", tx_putBits },
448 { "binary", kgFmt__binary },
449 { "base64", kgFmt__base64 },
453 void (*fmt)(unsigned char *, size_t, FILE *) = tx_putBits;
455 /* --- Explain who I am --- */
459 /* --- Read arguments --- */
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' },
470 int i = mdwopt(argc, argv, "hb:o:f:", opts, 0, 0, 0);
477 "Usage: %s [--bits=BITS] [--output=FILE] [--format=FORMAT]\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"
483 "tx, hex Hexadecimal, with punctuating dashes.\n"
484 "binary Raw binary.\n"
485 "base64 Base64-encoded binary; the result is printable ASCII.\n",
492 die("bad number of bits (illegible or zero)");
494 die("can only generate a whole number of 8-bit bytes");
501 for (i = 0; format[i].name; i++) {
502 const char *p = format[i].name, *q = optarg;
512 die("ambiguous format name: `%s'", optarg);
513 fmt = format[i].proc;
517 die("unknown format name: `%s'", optarg);
526 fprintf(stderr, "Usage: %s [--bits=BITS] [--output=FILE]\n", quis());
530 /* --- Allocate memory --- */
532 uip = xmalloc(sz / 8);
534 /* --- Open the terminal --- *
536 * I'd like to be able to @fprintf@ to the terminal, so use @fopen@.
539 if ((kg__ttyfp = fopen("/dev/tty", "r+")) == 0)
540 die("couldn't open terminal: %s", strerror(errno));
541 kg__tty = fileno(kg__ttyfp);
543 /* --- Open the output file, if one is specified --- */
548 /* --- Open the file oddly --- *
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
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));
564 /* --- Tidy up nicely if I die --- */
566 signal(SIGINT, kg__signal);
567 signal(SIGTERM, kg__signal);
570 /* --- Put the terminal into cbreak, read the key, and restore --- */
576 /* --- Find some noise from the environment too --- */
580 rand_add(uip, sz / 8);
582 rand_extract(uip, sz / 8);
584 /* --- Now write the number and exit --- */
586 D( fputs("*** ", fp); tx_putBits(uip, sz, stdout); )
590 memset(uip, 0, sz / 8); /* Burn temporary buffer */
595 /*----- That's all, folks -------------------------------------------------*/