/*
 -------------------------------------------------------------------------
 KeyRand.Asm by Robert Rothenburg Walking-Owl <rrothenb@ic.sunysb.edu>
 KeyRand.H-- by CyberRax the Hacking One <cyberrax@yahoo.com>
 -------------------------------------------------------------------------

 This routine latches onto the keyboard interrupt serivce routine (Int 09)
 and adds keystroke timings to a random number pool. This is presumably a
 real and unpredictable source of randomness for applications which need
 it (such as crypto).

 The advantage of latching onto the keyboard interrupt is that a program
 can collect random bits (or bytes) in the background from the user's key-
 strokes.

 No guarantees or warrantees of any sort are made on this software, implied
 or otherwise.  Nor will the author be responsible for any damages that may
 result from using this software: in other words, use at your own risk.

 This software is Copyright (c) 1994, although you may freely distribute,
 use and or modify it so long as this copyright notice is not removed from
 the source.  If you distribute a modified version, please do so with
 commented code noting the modifications and date, etc.

        4 Dec. 1994  - initial publication (R.Walking-Owl)
        4 Jan. 2001  - converted to SPHINX/Sheker C-- header file (CyberRax)
        5 Jan. 2001  - removed the possibility to disable the random
                       pool (CyberRax)
        3 Feb. 2002  - _KEYRAND_ definiton
                       ES preservation (CyberRax)

 The procedures in this routine were written to be incorporated into Pascal
 programs, although it should be no trouble to use them in C (either define
 the routines in a header file with "pascal" calls or put underscores prior
 to the procedure names, whatever suits your taste...

 A brief description of the functions follows:

   INSTALLRANDOM returns -1 if successful, 0 if not. UNINSTALLRANDOM does
       the same: their purpose should be obvious.  Remember to have your
       Ctrl-Break handler uninstall the interrupt!

   GETRANDPOOLADDR returns the address of the random data pool. This may have
       to be rewritten if huge or flat memory-models are used.  (Adjust the
       RandPoolSize symbol to suit your needs.)
   <note from CyberRax: this procedure is meaningless under C-- because there
   are no restrictions which would disable the *using* of pool's address. It
   has been kept only because there might(?) be some use for it when the code
   is being used under some other compiler>

   NOISE returns 8-bits of noise from the random number pool.

   NUMRANDBITS returns the number of random bits (or bytes) in the pool.

 No arguments are required for any of these routines, so they should be
 fairly portable across compilers. The source has been tested with both
 MASM 5.0 and TASM (version?) and assembles with no problems.

 <Notes:

  This headerred version uses some specific features of SPHINX/Sheker C--
  (aka SPHINX C-- 0.204+) so it should be quite impossible to use it directly
  (rather than the generated code) under other compilers (including earlier
  versions of C-- than ca. 0.230). If this is neccessary then R. Walking-Owl's
  original code (which is available from Simtel.Net (or was it Garbo?))
  should be used.

  If you define any of the used variables then <?include "KEYRAND.H--">
  *must* come after the definitions and *NOT* before!

  Variables:
   randpoolsize - size of random number pool we want. If not defined then
                  it's set to 32 bytes
   samplebytes  - to sample bytes. If not definied then bits are sampled
 >

 -------------------------------------------------------------------------
*/

? ifndef _KEYRAND_
? define _KEYRAND_ TRUE

? include "JSTUFF.H--"

? ifndef randpoolsize
   ? define randpoolsize 32     // size of random number pool we want
? endif

//? define samplebytes     TRUE   // sample bytes, not bits

word oldint09vector[2] = 0;         // where the original vector is


/*
 -------------------------------------------------------------------------
 the random-number pool...
 -------------------------------------------------------------------------
 Note: one could maintain a random-seed file and load RandPoolSize bytes
       into the randompool (prior to initializing the handler, pref.)
*/

byte randompool[randpoolsize];


/*
 -------------------------------------------------------------------------
 some routines to poll the system timer for some random data
 -------------------------------------------------------------------------
 These routines are loosely based on similarly named procedures in the
 PGP 2.6.2 sources.  A more detailed explanation of what they do can be
 found there.

 A potential modification would be to change the system timer interrupt
 so that it is called every few ms. rather than 18.2 times per second:
 one would also have to make appropriate changes to the system timer
 routines so that the clock doesn't advance several hours per minute...
*/

? define timer0    0x40
? define timercntl 0x32

/*
 -------------------------------------------------------------------------
 routine to sample the pc timer port
 -------------------------------------------------------------------------
 based on the pctimer0() function in noise.c from the pgp 2.6.2 sources
*/

:word PCTIMER0()
{
$PUSH BX

? ifdef cpu < 2
   $PUSH CX
? endif

$IN AL , timer0
AL = 0xC2;
$OUT timercntl,AL
$IN AL,timer0
AL &= 0x80;
BX = AX;

? ifdef cpu < 2
   CL = 8;
   BX <<= CL;
? else
   BX <<= 8;
? endif

AL = 0;
$OUT timercntl,AL
$IN AL,timer0
AX &= 0xFF;
AX >>= 1;
BX |= AX;
$IN AL,timer0
AX &= 0xFF;

? ifdef cpu < 2
   CL = 7;
   AX <<= CL;
? else
   AX <<= 7;
? endif

AX |= BX;

? ifdef cpu < 2
   $POP CX;
? endif
$POP BX
}

word bitsinpool = 0, lasttick;  // save value of last call to pctimer0


/*
 -------------------------------------------------------------------------
 the random sampling handler
 -------------------------------------------------------------------------
*/

interrupt RANDOMHANDLER()
{
$PUSHF
@DISABLE();
$PUSH AX,BX,SI

BX = PCTIMER0();
AX >< CSWORD[#lasttick];
BX -= AX;

? ifdef samplebytes
   SI = 0;
   $PUSH CX
   CX = randpoolsize;
   BX >>= 1;
  SWIRL_RANDOM_POOL_BITS:
   CSBYTE[#randompool + SI] >>= 1;
   SI++;
   $LOOP SWIRL_RANDOM_POOL_BITS;
   CSWORD[#bitsinpool]++;
   $POP CX;
? else
   SI = randpoolsize - 1;
   BL ^= CSBYTE[#randompool + SI];
  SWIRL_RANDOM_POOL_BYTES:
   AL = CSBYTE[#randompool + SI - 1];
   CSBYTE[SI + #randompool] = AL;
   SI--;
   IF (SI <> FALSE)
      GOTO SWIRL_RANDOM_POOL_BYTES;
   CSBYTE[#randompool] = BL;
   CSWORD[#bitsinpool] += 8;
? endif

IF (CSWORD[#bitsinpool] <= randpoolsize * 8)
   GOTO NO_ADJUST;
CSWORD[#bitsinpool] = randpoolsize * 8;

NO_ADJUST:
$POP SI
$IN AL, 0x60;

$POP BX,AX
$POPF
$CS:
$JMP FAR oldint09vector
}


/*
 -------------------------------------------------------------------------
 returns 8 bits of noise
 -------------------------------------------------------------------------
 a routine could be written to extract n bits of noise from the random
 pool as well...
*/

:char NOISE()
{
AL = 0;
IF (CSWORD[#bitsinpool] < 8)
   GOTO NO_BITS_LEFT;
AH = CSBYTE[#randompool];
SI = 0;
SHIFT_POOL_DOWN:
AL = CSBYTE[#randompool + SI + 1];
CSBYTE[#randompool + SI] = AL;
SI++;
IF (SI < randpoolsize)
   GOTO SHIFT_POOL_DOWN;

CSWORD[#bitsinpool] -= 8;

NO_BITS_LEFT:
}


/*
 -------------------------------------------------------------------------
 returns the number of random bits in the pool
 -------------------------------------------------------------------------
*/

:word NUMRANDBITS()
{
AX = CSWORD[#bitsinpool];
}


/*
 -------------------------------------------------------------------------
 returns the address of the randompool
 -------------------------------------------------------------------------
 note: it's probably a good idea to wipe the random pool when you are
       done with it. (the uninstall procedure doesn't do that!)
*/

:void GETRANDPOOLADD()
{
DX = CS;
AX = #randompool;
}


/*
 -------------------------------------------------------------------------
 check to see if installed Int 09 handler is at vector (ax is nz. if ok)
 -------------------------------------------------------------------------

 Note: this routine checks the addresses at the interrupt vector, and not
       for a marker at that address which is required should this code be
       incorporated into a TSR.

 Because this routine is automagically called by the install and uninstall
 procedures, there is no need for it to be public.  You can change that if
 you'd like...
*/

:word CHECKFORINST()
{
$PUSHF
@DISABLE();
DX = -1;
ES = 0;
AX = #RANDOMHANDLER;
IF (ESWORD[0x24] <> AX)
   GOTO LATCH;
AX = CS;
IF (ESWORD[0x26] == AX)
   GOTO NO_LATCH;
LATCH:
!DX;
NO_LATCH:
AX = DX;
$POPF
}


/*
 -------------------------------------------------------------------------
 installs the handler and initializes stuff
 -------------------------------------------------------------------------
*/

:word INSTALLRANDOM()
{
$PUSHF
@DISABLE();
$PUSH DS,ES
IF (CHECKFORINST() <> FALSE)
   GOTO HANDLER_IS_INSTALLED;

GETINT(9);
IF (BX <> #RANDOMHANDLER)
   GOTO HANDLER_NOT_INSTALLED;
AX = ES;
DX = CS;
IF (AX <> DX)
   GOTO HANDLER_NOT_INSTALLED;
HANDLER_IS_INSTALLED:
AX = 0;
GOTO EXIT_INSTALL;
HANDLER_NOT_INSTALLED:
CSWORD[#oldint09vector + 2] = ES;
CSWORD[#oldint09vector] = BX;
DS = CS;
SETINT(9,#RANDOMHANDLER);
PCTIMER0();
AX >< CSWORD[#lasttick];
CHECKFORINST();                         // verify install
EXIT_INSTALL:
$POP ES,DS
$POPF
}


/*
 -------------------------------------------------------------------------
 uninstalls the handler
 -------------------------------------------------------------------------
 note: rememer to have ctrl-break or critical-error routines call this
       procedure!
*/

:word UNINSTALLRANDOM()
{
$PUSHF
@DISABLE();
$PUSH DS,ES

IF (CHECKFORINST() == FALSE)           // are we installed?
   GOTO CANT_UNINSTALL;

DS = CSWORD[#oldint09vector + 2];
SETINT(9,CSWORD[#oldint09vector]);

CHECKFORINST();                        // verify it!
!AX;

CANT_UNINSTALL:
$POP ES,DS
$POPF
}

/*
 A particularly psychotic idea might be to have a TSR collect random data
 from keystroke timings into a huge file while the user does other normal
 things like typing DOS commands or using a word processor.
*/

? endif
