3 * $Id: keygen.c,v 1.2 1997/08/04 10:24:23 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.2 1997/08/04 10:24:23 mdw
33 * Sources placed under CVS control.
35 * Revision 1.1 1997/07/21 13:47:48 mdw
40 /*----- Header files ------------------------------------------------------*/
42 /* --- ANSI headers --- */
51 /* --- Unix headers --- */
53 #include <sys/types.h>
60 /* --- Local headers --- */
67 /*----- Static variables --------------------------------------------------*/
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 */
75 kgFlag__cbreak
= 1 /* Terminal is in cbreak mode */
78 /*----- Main code ---------------------------------------------------------*/
80 /* --- @kg__cbreak@ --- *
86 * Use: Makes the terminal return characters as soon as they're
92 /* --- Don't do this if I don't have to --- */
94 if (kg__flags
& kgFlag__cbreak
)
97 /* --- Fetch the old attributes, and remember them --- */
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
));
103 /* --- Now modify them for raw mode --- */
105 kg__raw
.c_lflag
&= ~(ICANON
| ECHO
);
106 kg__raw
.c_cc
[VTIME
] = 0;
107 kg__raw
.c_cc
[VMIN
] = 1;
109 /* --- Remember the new state, and away we go --- */
111 kg__flags
|= kgFlag__cbreak
;
112 if (tcsetattr(kg__tty
, TCSAFLUSH
, &kg__raw
))
113 die("couldn't set terminal attributes: %s", strerror(errno
));
116 /* --- @kg__crepair@ --- *
122 * Use: Unbreaks a cbroken tty. Obvious, innit?
125 static void kg__crepair(void)
127 /* --- Don't do this if I don't have to --- */
129 if (~kg__flags
& kgFlag__cbreak
)
132 /* --- Reset the old attributes --- */
134 tcsetattr(kg__tty
, TCSAFLUSH
, &kg__old
);
135 kg__flags
&= ~kgFlag__cbreak
;
138 /* --- @kg__signal@ --- *
140 * Arguments: @int sig@ = signal number
144 * Use: Tidies up if I get a signal.
147 static void kg__signal(int sig
)
150 signal(sig
, SIG_DFL
);
154 /* --- @kgFmt__binary@ --- *
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
162 * Use: Writes bits on a stream.
165 static void kgFmt__binary(unsigned char *k
, size_t bits
, FILE *fp
)
167 fwrite(k
, 1, bits
/ 8, fp
);
170 /* --- @kgFmt__base64@ --- *
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
178 * Use: Writes bits on a stream in an encoded way.
181 static void kgFmt__base64(unsigned char *k
, size_t bits
, FILE *fp
)
183 static const char xlt
[64] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
184 "abcdefghijklmnopqrstuvwxyz"
190 b
= ((k
[0] & 0xffu
) << 16) | ((k
[1] & 0xffu
) << 8) | (k
[2] & 0xffu
);
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) {
208 b
= (b
<< 8) | (*k
++ & 0xffu
);
210 b
= (b
<< 8) | (*k
++ & 0xffu
);
213 putc(xlt
[(b
>> 18) & 0x3fu
], fp
);
214 putc(xlt
[(b
>> 12) & 0x3fu
], fp
);
217 putc(xlt
[(b
>> 6) & 0x3fu
], fp
);
218 putc(xlt
[(b
>> 0) & 0x3fu
], fp
);
221 putc(xlt
[(b
>> 6) & 0x3fu
], fp
);
232 /* --- @kg__gen@ --- *
234 * Arguments: @unsigned char *ui@ = pointer to array to fill in
235 * @size_t sz@ = number of bits to generate
239 * Use: Uses key timings to generate random numbers. Maybe.
242 static void kg__gen(unsigned char *ui
, size_t sz
)
244 int bits
= 32 < sz ?
32 : sz
;
245 size_t wsz
= (sz
+ 31u) & ~31u;
246 unsigned long last
, ldiff
= 0;
248 unsigned long fact
= 1000000 / CLOCKS_PER_SEC
;
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",
257 gettimeofday(&tv
, 0);
258 last
= tv
.tv_usec
/ fact
+ tv
.tv_sec
* fact
;
265 /* --- Print current status --- */
267 fprintf(kg__ttyfp
, "\r%5lu...", (unsigned long)sz
);
270 /* --- Read the next character --- */
274 if (read(kg__tty
, buff
, sizeof(buff
)) < 0)
275 die("couldn't read from terminal: %s", strerror(errno
));
278 /* --- Fiddle with times --- *
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.
289 gettimeofday(&tv
, 0);
290 n
= tv
.tv_usec
/ fact
+ tv
.tv_sec
* fact
;
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); )
307 /* --- Find the useful bits in this value --- *
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.
324 while (i
&& (xor & i
) != 0) {
329 while (i
&& (xor & i
) == 0) {
336 while ((orr
& 1) == 0) {
347 /* --- Now add the bits in the mixing bowl to my stash --- *
349 * There are two cases:
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.
355 * 2. I have too few bits to fit in the accumulator. Then shift them
359 while (sz
&& useful
) {
361 D( printf("got %i bits, need %i/%i: %8lx\n",
362 useful
, bits
, sz
, (unsigned long)xor); )
364 if (useful
>= bits
) {
366 D( printf("shifted acc = %08lx\n"
367 " new bits = %08lx\n"
369 (unsigned long)(a
<< bits
),
370 (unsigned long)(xor >> (useful
- bits
)),
371 (unsigned long)((a
<< bits
) | (xor >> (useful
- bits
)))); )
373 a
= (a
<< bits
) | (xor >> (useful
- bits
));
386 D( printf("writing %02x\n", (a
>> 24) & 0xffu
); )
387 *ui
++ = (a
>> 24) & 0xffu
;
393 bits
= 32 < sz ?
32 : sz
;
397 D( printf("shifted acc = %08lx\n"
398 " new bits = %08lx\n"
400 (unsigned long)(a
<< useful
),
401 (unsigned long)(xor),
402 (unsigned long)((a
<< useful
) | (xor))); )
403 a
= (a
<< useful
) | xor;
412 fputs("\rDone! \n", kg__ttyfp
);
413 putc('\a', kg__ttyfp
); fflush(kg__ttyfp
);
419 * Arguments: @int argc@ = number of arguments
420 * @char *argv[]@ = array of arguments
422 * Returns: Zero if it worked, nonzero if it didn't.
424 * Use: Generates random numbers from the keyboard.
427 int main(int argc
, char *argv
[])
431 const char *file
= 0;
434 /* --- Formats table --- */
438 void (*proc
)(unsigned char *k
, size_t bits
, FILE *fp
);
440 { "tx", tx_putBits
},
441 { "hex", tx_putBits
},
442 { "binary", kgFmt__binary
},
443 { "base64", kgFmt__base64
},
447 void (*fmt
)(unsigned char *, size_t, FILE *) = tx_putBits
;
449 /* --- Explain who I am --- */
453 /* --- Read arguments --- */
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' },
464 int i
= mdwopt(argc
, argv
, "hb:o:f:", opts
, 0, 0, 0);
471 "Usage: %s [--bits=BITS] [--output=FILE] [--format=FORMAT]\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"
477 "tx, hex Hexadecimal, with punctuating dashes.\n"
478 "binary Raw binary.\n"
479 "base64 Base64-encoded binary; the result is printable ASCII.\n",
486 die("bad number of bits (illegible or zero)");
488 die("can only generate a whole number of 8-bit bytes");
495 for (i
= 0; format
[i
].name
; i
++) {
496 const char *p
= format
[i
].name
, *q
= optarg
;
506 die("ambiguous format name: `%s'", optarg
);
507 fmt
= format
[i
].proc
;
511 die("unknown format name: `%s'", optarg
);
520 fprintf(stderr
, "Usage: %s [--bits=BITS] [--output=FILE]\n", quis());
524 /* --- Allocate memory --- */
526 uip
= xmalloc(sz
/ 8);
528 /* --- Open the terminal --- *
530 * I'd like to be able to @fprintf@ to the terminal, so use @fopen@.
533 if ((kg__ttyfp
= fopen("/dev/tty", "r+")) == 0)
534 die("couldn't open terminal: %s", strerror(errno
));
535 kg__tty
= fileno(kg__ttyfp
);
537 /* --- Open the output file, if one is specified --- */
542 /* --- Open the file oddly --- *
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
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
));
558 /* --- Tidy up nicely if I die --- */
560 signal(SIGINT
, kg__signal
);
561 signal(SIGTERM
, kg__signal
);
564 /* --- Put the terminal into cbreak, read the key, and restore --- */
570 /* --- Now write the number and exit --- */
572 D( fputs("*** ", fp
); tx_putBits(uip
, sz
, stdout
); )
576 memset(uip
, 0, sz
/ 8); /* Burn temporary buffer */
580 /*----- That's all, folks -------------------------------------------------*/