3 * $Id: keygen.c,v 1.1 1997/07/21 13:47:48 mdw Exp $
10 /*----- Licencing 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
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.1 1997/07/21 13:47:48 mdw
37 /*----- Header files ------------------------------------------------------*/
39 /* --- ANSI headers --- */
48 /* --- Unix headers --- */
50 #include <sys/types.h>
57 /* --- Local headers --- */
64 /*----- Static variables --------------------------------------------------*/
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 */
72 kgFlag__cbreak
= 1 /* Terminal is in cbreak mode */
75 /*----- Main code ---------------------------------------------------------*/
77 /* --- @kg__cbreak@ --- *
83 * Use: Makes the terminal return characters as soon as they're
89 /* --- Don't do this if I don't have to --- */
91 if (kg__flags
& kgFlag__cbreak
)
94 /* --- Fetch the old attributes, and remember them --- */
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
));
100 /* --- Now modify them for raw mode --- */
102 kg__raw
.c_lflag
&= ~(ICANON
| ECHO
);
103 kg__raw
.c_cc
[VTIME
] = 0;
104 kg__raw
.c_cc
[VMIN
] = 1;
106 /* --- Remember the new state, and away we go --- */
108 kg__flags
|= kgFlag__cbreak
;
109 if (tcsetattr(kg__tty
, TCSAFLUSH
, &kg__raw
))
110 die("couldn't set terminal attributes: %s", strerror(errno
));
113 /* --- @kg__crepair@ --- *
119 * Use: Unbreaks a cbroken tty. Obvious, innit?
122 static void kg__crepair(void)
124 /* --- Don't do this if I don't have to --- */
126 if (~kg__flags
& kgFlag__cbreak
)
129 /* --- Reset the old attributes --- */
131 tcsetattr(kg__tty
, TCSAFLUSH
, &kg__old
);
132 kg__flags
&= ~kgFlag__cbreak
;
135 /* --- @kg__signal@ --- *
137 * Arguments: @int sig@ = signal number
141 * Use: Tidies up if I get a signal.
144 static void kg__signal(int sig
)
147 signal(sig
, SIG_DFL
);
151 /* --- @kgFmt__binary@ --- *
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
159 * Use: Writes bits on a stream.
162 static void kgFmt__binary(unsigned char *k
, size_t bits
, FILE *fp
)
164 fwrite(k
, 1, bits
/ 8, fp
);
167 /* --- @kgFmt__base64@ --- *
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
175 * Use: Writes bits on a stream in an encoded way.
178 static void kgFmt__base64(unsigned char *k
, size_t bits
, FILE *fp
)
180 static const char xlt
[64] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
181 "abcdefghijklmnopqrstuvwxyz"
187 b
= ((k
[0] & 0xffu
) << 16) | ((k
[1] & 0xffu
) << 8) | (k
[2] & 0xffu
);
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) {
205 b
= (b
<< 8) | (*k
++ & 0xffu
);
207 b
= (b
<< 8) | (*k
++ & 0xffu
);
210 putc(xlt
[(b
>> 18) & 0x3fu
], fp
);
211 putc(xlt
[(b
>> 12) & 0x3fu
], fp
);
214 putc(xlt
[(b
>> 6) & 0x3fu
], fp
);
215 putc(xlt
[(b
>> 0) & 0x3fu
], fp
);
218 putc(xlt
[(b
>> 6) & 0x3fu
], fp
);
229 /* --- @kg__gen@ --- *
231 * Arguments: @unsigned char *ui@ = pointer to array to fill in
232 * @size_t sz@ = number of bits to generate
236 * Use: Uses key timings to generate random numbers. Maybe.
239 static void kg__gen(unsigned char *ui
, size_t sz
)
241 int bits
= 32 < sz ?
32 : sz
;
242 size_t wsz
= (sz
+ 31u) & ~31u;
243 unsigned long last
, ldiff
= 0;
245 unsigned long fact
= 1000000 / CLOCKS_PER_SEC
;
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",
254 gettimeofday(&tv
, 0);
255 last
= tv
.tv_usec
/ fact
+ tv
.tv_sec
* fact
;
262 /* --- Print current status --- */
264 fprintf(kg__ttyfp
, "\r%5i...", sz
);
267 /* --- Read the next character --- */
271 if (read(kg__tty
, buff
, sizeof(buff
)) < 0)
272 die("couldn't read from terminal: %s", strerror(errno
));
275 /* --- Fiddle with times --- *
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.
286 gettimeofday(&tv
, 0);
287 n
= tv
.tv_usec
/ fact
+ tv
.tv_sec
* fact
;
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); )
304 /* --- Find the useful bits in this value --- *
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.
321 while (i
&& (xor & i
) != 0) {
326 while (i
&& (xor & i
) == 0) {
333 while ((orr
& 1) == 0) {
344 /* --- Now add the bits in the mixing bowl to my stash --- *
346 * There are two cases:
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.
352 * 2. I have too few bits to fit in the accumulator. Then shift them
356 while (sz
&& useful
) {
358 D( printf("got %i bits, need %i/%i: %8lx\n",
359 useful
, bits
, sz
, (unsigned long)xor); )
361 if (useful
>= bits
) {
363 D( printf("shifted acc = %08lx\n"
364 " new bits = %08lx\n"
366 (unsigned long)(a
<< bits
),
367 (unsigned long)(xor >> (useful
- bits
)),
368 (unsigned long)((a
<< bits
) | (xor >> (useful
- bits
)))); )
370 a
= (a
<< bits
) | (xor >> (useful
- bits
));
383 D( printf("writing %02x\n", (a
>> 24) & 0xffu
); )
384 *ui
++ = (a
>> 24) & 0xffu
;
390 bits
= 32 < sz ?
32 : sz
;
394 D( printf("shifted acc = %08lx\n"
395 " new bits = %08lx\n"
397 (unsigned long)(a
<< useful
),
398 (unsigned long)(xor),
399 (unsigned long)((a
<< useful
) | (xor))); )
400 a
= (a
<< useful
) | xor;
409 fputs("\rDone! \n", kg__ttyfp
);
410 putc('\a', kg__ttyfp
); fflush(kg__ttyfp
);
416 * Arguments: @int argc@ = number of arguments
417 * @char *argv[]@ = array of arguments
419 * Returns: Zero if it worked, nonzero if it didn't.
421 * Use: Generates random numbers from the keyboard.
424 int main(int argc
, char *argv
[])
428 const char *file
= 0;
431 /* --- Formats table --- */
435 void (*proc
)(unsigned char *k
, size_t bits
, FILE *fp
);
437 { "tx", tx_putBits
},
438 { "hex", tx_putBits
},
439 { "binary", kgFmt__binary
},
440 { "base64", kgFmt__base64
},
444 void (*fmt
)(unsigned char *, size_t, FILE *) = tx_putBits
;
446 /* --- Explain who I am --- */
450 /* --- Read arguments --- */
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' },
461 int i
= mdwopt(argc
, argv
, "hb:o:f:", opts
, 0, 0, 0);
468 "Usage: %s [--bits=BITS] [--output=FILE] [--format=FORMAT]\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"
474 "tx, hex Hexadecimal, with punctuating dashes.\n"
475 "binary Raw binary.\n"
476 "base64 Base64-encoded binary; the result is printable ASCII.\n",
483 die("bad number of bits (illegible or zero)");
485 die("can only generate a whole number of 8-bit bytes");
492 for (i
= 0; format
[i
].name
; i
++) {
493 const char *p
= format
[i
].name
, *q
= optarg
;
503 die("ambiguous format name: `%s'", optarg
);
504 fmt
= format
[i
].proc
;
508 die("unknown format name: `%s'", optarg
);
517 fprintf(stderr
, "Usage: %s [--bits=BITS] [--output=FILE]\n", quis());
521 /* --- Allocate memory --- */
523 uip
= xmalloc(sz
/ 8);
525 /* --- Open the terminal --- *
527 * I'd like to be able to @fprintf@ to the terminal, so use @fopen@.
530 if ((kg__ttyfp
= fopen("/dev/tty", "r+")) == 0)
531 die("couldn't open terminal: %s", strerror(errno
));
532 kg__tty
= fileno(kg__ttyfp
);
534 /* --- Open the output file, if one is specified --- */
539 /* --- Open the file oddly --- *
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
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
));
555 /* --- Tidy up nicely if I die --- */
557 signal(SIGINT
, kg__signal
);
558 signal(SIGTERM
, kg__signal
);
561 /* --- Put the terminal into cbreak, read the key, and restore --- */
567 /* --- Now write the number and exit --- */
569 D( fputs("*** ", fp
); tx_putBits(uip
, sz
, stdout
); )
573 memset(uip
, 0, sz
/ 8); /* Burn temporary buffer */
577 /*----- That's all, folks -------------------------------------------------*/