{
> Bruce Schneier suggests keyboard latency as a good source of randomness.
> I suspect you'd find the delays far more regular than you'd guess, but
> it's worth looking at.  Show us an implementation!

Thanks, DJ.  (And I was hoping a simple appeal to a recognized authority
would suffice!  I guess I should have known better.)  But fair enough -
here's an admittedly crude implementation:
}

program TestKey;

{ Test keyboard latency as a potential source of random numbers. }
{ NOTE - NO ERROR CHECKING!                                      }

uses
  Dos, Crt;

const
  FileName = 'RANDOM.TST';

var
  PrevTime : Word;
  RandByte : Byte;
  Regs     : Registers;
  Bit      : Word;
  RandFile : file of Byte;
  Ch       : Char;

begin
  { open the file }
  Assign( RandFile, FileName );
  Rewrite( RandFile );

  { get DOS time and save low word }
  Regs.AH := 0;
  Intr( $1A, Regs );
  PrevTime := Regs.DX;

  Bit := 0;
  RandByte := 0;
  WriteLn( 'Start typing now.  Press <ESC> to quit.' );

  repeat
    { get a keystroke }
    repeat until KeyPressed;
    Ch := ReadKey;

    { if the Escape key }
    if Ch = #27 then
      Break;

    { get DOS time }
    Regs.AH := 0;
    Intr( $1A, Regs );

    { calculate the time difference, isolate the low-order bit, and }
    { use it to build the hopefully-random byte in progress.        }
    RandByte := RandByte + ( ( Lo( Regs.DX - PrevTime ) and 1 ) shl Bit );
    PrevTime := Regs.DX;
    Inc( Bit );

    { If we have a full byte, write it to the file and start over }
    if ( Bit > 7 ) then begin
      Write( RandFile, RandByte );
      RandByte := 0
      Bit := 0;
    end;

    { Ignore special keys, display the rest }
    if Ch = #0 then
      Ch := ReadKey
    else
      Write( Ch );
    if Ch = #13 then
      Write( #10 )

  until False;
  Close( RandFile )
end.

{
I ran this thing and typed in the first page of a DOC file.  This is what I
got:

  24  98  AD  94  E2  C8  00  58  20  83  09  F1  5D  F5  76  59
  31  A9  86  DC  32  6D  96  17  65  C4  75  31  A3  18  F5  97
  87  0A  69  B4  B6  E7  0A  C1  F8  09  BE  B0  7B  C5  4A  BA
  69  42  8C  D4  E4  71  12  DF  5E  19  5C  A0  0D  79  D7  F1
  66  BC  40  36  E9  8D  DB  B5  37  A9  7A  0C  02  90  05  04
  EA  53  38  CA  94  18  92  8A  46  A6  F1  56  D0  E7  38  97
  25  2D  C3  C8  7E  79  DE  02  58  FC  36  7E  BC  3C  F9  6D
  E6  2E  C0  28  06  AD  C1  4B  55  CD  C4  98  DD  08  DD  4E
  11  56  76  83  BC  7A  AF  05  F6  AC  C3  40  28  D5  2B  8E
  C1  93  B5  F9  54  E2  00  3D  5A  5D  37  36  C3  5F  37  3A
  AB  60  36  72  27  26  21  86  2F  B4  6B  D9  70  94  DE  00
  C8  23  34  5F  83  C9  FB  AF  F8  F5  CE  21  B3  40  FA  ED
  21  4B  65  00  D9  A0  6E  43  E4  FF  66  1B  BC  17  80  29
  FD  6F  10  4B  D7  D3  ED  5C  C9  18  0E  24  89  1F  03  BC
  B3  0B  CB  E5  1E  16  B8  DA  99  EC  93  84  7E  8A  FE  61
  9D  B7  2E  30  11  7F  0A  C6  83  C2  C1  97  3B  08  61  8D
  7B  5E  7E  69  99  F8  F3  36  BA  31  6E  41  60  8C  DC  B7
  48  FB  44  A2  78  D5  AF  88  D9  10  50  E7  C7  BE  68  41
  C2  E8  D8  1B

The distribution of bytes looks like this:

         0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
     +-----------------------------------------------------------------
 0   |   4   0   2   1   1   2   1   0   2   2   3   1   1   1   1   0
 1   |   2   2   1   0   0   0   1   2   3   1   0   2   0   0   1   1
 2   |   1   3   0   1   2   1   1   1   2   1   0   1   0   1   2   1
 3   |   1   3   1   0   1   0   5   3   2   0   1   1   1   1   0   0
 4   |   3   2   1   1   1   0   1   0   1   0   1   3   0   0   1   0
 5   |   1   0   0   1   1   1   2   0   2   1   1   0   2   2   2   2
 6   |   2   2   0   0   0   2   2   0   1   3   0   1   0   2   2   1
 7   |   1   1   1   0   0   1   2   0   1   2   2   2   0   0   4   1
 8   |   1   0   0   4   1   0   2   1   1   1   2   0   2   2   1   0
 9   |   1   0   1   2   3   0   1   3   2   2   0   0   0   1   0   0
 A   |   2   0   1   1   0   0   1   0   0   2   0   1   1   2   0   3
 B   |   1   0   0   2   2   2   1   2   1   0   2   0   5   0   2   0
 C   |   1   4   2   3   2   1   1   1   3   2   1   1   0   1   1   0
 D   |   1   0   0   1   1   2   0   2   1   3   1   1   2   2   2   1
 E   |   0   0   2   0   2   1   1   3   1   1   1   0   1   2   0   0
 F   |   0   3   0   1   0   3   1   0   3   2   1   2   1   1   1   1


Question - is it random?  Well, it's far too small a sample to be sure, and I
wasn't about to retype "War and Peace" to get a bigger sample.  It passes
one test of randomness - non-compressability.  LHA won't compress it, anyway.
(A text file of similar size compressed to 64%.)  I don't consider this
definite proof of randomness, although it looks hopeful.

Question - is it unpredictable?  Given the above source code and the list of
bytes generated, I don't think I could predict what the next byte would be.

Question - can it be reliably reproduced?  Not at my level of typing ability,
unless I cheat and just hold down a key.  Even then, there are minor
variations between bytes.

Problem - the timer is too coarse.  It uses the BIOS time-of-day count, which
ticks over about 18.2 times per second.  For a 30-wpm (3 character-per-second)
typist like me, it _seems_ to produce acceptable results.  For a professional
typist, churning out 110 wpm (11 characters per second), it probably won't.
Something using the 8253 timer (a modification of the Abrash "Zen Timer", for
example) would probably be adequate for even the fastest typist, but that's a
bit too exotic for me to attempt.

Problem - it takes a lot of typing to generate a decent pad.  By my
calculations, a 110-wpm typist would have to type for 20 minutes to create a
pad big enough to encrypt one double-spaced typewritten page.  Possible
solution - rewrite the thing as a TSR, so that a typist can do useful work
while generating random (?) numbers in the background.  Then, turn the whole
typing pool loose on the problem.

Does anyone have a better timer routine and a faster typist?
}
