home *** CD-ROM | disk | FTP | other *** search
/ PC-Online 1998 February / PCOnline_02_1998.iso / filesbbs / os2 / pgp263.arj / PGP263I.SRC / PGP263II.ZIP / src / random.c < prev    next >
C/C++ Source or Header  |  1996-01-06  |  22KB  |  707 lines

  1. /*
  2.  * True and cryptographic random number generation.
  3.  *
  4.  * (c) Copyright 1990-1996 by Philip Zimmermann.  All rights reserved.
  5.  * The author assumes no liability for damages resulting from the use
  6.  * of this software, even if the damage results from defects in this
  7.  * software.  No warranty is expressed or implied.
  8.  *
  9.  * Note that while most PGP source modules bear Philip Zimmermann's
  10.  * copyright notice, many of them have been revised or entirely written
  11.  * by contributors who frequently failed to put their names in their
  12.  * code.  Code that has been incorporated into PGP from other authors
  13.  * was either originally published in the public domain or is used with
  14.  * permission from the various authors.
  15.  *
  16.  * PGP is available for free to the public under certain restrictions.
  17.  * See the PGP User's Guide (included in the release package) for
  18.  * important information about licensing, patent restrictions on
  19.  * certain algorithms, trademarks, copyrights, and export controls.
  20.  *
  21.  * Written by Colin Plumb.
  22.  */
  23.  
  24. #include <stdlib.h>
  25. #include <stdio.h>
  26. #include <string.h>
  27. #include <assert.h>
  28. #include <signal.h>    /* For SIGINT */
  29. #include <time.h>
  30.  
  31. #ifdef AMIGA      /* Includes for timer -- RKNOP */
  32. #include <devices/timer.h>
  33. #include <exec/memory.h>
  34. #include <exec/libraries.h>
  35. #include <proto/dos.h>
  36. #include <proto/timer.h>
  37. #include <proto/exec.h>
  38. #endif /* AMIGA */
  39.  
  40. #ifdef __PUREC__
  41. #include <ext.h>
  42. #endif
  43.  
  44. #include "system.h"    /* For ttycbreak, getch, etc. */
  45. #include "idea.h"
  46. #include "md5.h"
  47. #include "noise.h"
  48. #include "language.h"
  49. #include "random.h"
  50. #include "fileio.h"    /* For FOPRBIN */
  51. #include "pgp.h"    /* For globalRandseedName */
  52. #include "randpool.h"
  53. #ifdef MACTC5
  54. #include "Macutil2.h"
  55. #include "Macutil3.h"
  56. #include "TimeManager.h"
  57. #include <unix.h>
  58. #endif
  59.  
  60. /*
  61.  * As of PGP 2.6.2, the randseed.bin file has been expanded.  An explanation
  62.  * of how the whole thing works in in order, as people are always suspiscious
  63.  * of the random number generator.  (After the xorbytes bug in 2.6, perhaps
  64.  * you should be.)  There are two random number generators in PGP.  One
  65.  * is the "cryptRand" family, which is based on X9.17, but uses IDEA instead
  66.  * of 2-key EDE triple-DES.  This is the generator with a lot of peer review.
  67.  * The implementation is in idea.c.
  68.  * The second is the "trueRand" family, which attempt to accurately measure
  69.  * the entropy available from keyboard I/O.  It keeps a lot more state.
  70.  * The implementation of this is in randpool.c.
  71.  * Originally, the trueRand generator was only used for generating primes,
  72.  * and the cryptRand for generating IDEA session keys.  But things have
  73.  * become a bit more complex.  In particular, the X9.17 specification
  74.  * wants a source of high-resolution time-of-day information, as a source
  75.  * of some "true" randomness to throw in.  So we use the trueRand pool
  76.  * for that.
  77.  * The cryptRand functions keep a state file around, usually named
  78.  * randseed.bin, for a seed, as the X9.17 generator requires 24 bytes of
  79.  * known initial information.
  80.  * This data in this file is carefully "washed" before and after use to
  81.  * help ensure that if the file is captured or altered, the keys will
  82.  * not be too vulnerable.  A washing consists of an encryption in PGP's
  83.  * usual CFB mode of the material coming from or being written to the
  84.  * randseed.bin file on disk.  Assuming the cipher is strong, the effects
  85.  * of the wash are as difficult to predict as the key that is used is
  86.  * difficult to guess.
  87.  * Beforehand, we use the MD5 of the file being encrypted as an additional
  88.  * source of randomness (on the theory that an attacker trying to break
  89.  * a session key probably doesn't have the plaintext, or he wouldn't need
  90.  * to bother), and use that as an IDEA key (with a fixed IV of zero)
  91.  * to encrypt the randseed.bin data.
  92.  * After generating an IDEA key and IV, some more random bytes are generated
  93.  * to reinitialize randseed.bin, and these are encrypted in the same manner
  94.  * as the PGP message before being written to disk, on the assumption that
  95.  * if an attacker can decrypt that, they can decrypt the message directly
  96.  * and not bother attacking the randseed.bin file.
  97.  * The previous code only saved the 24 bytes needed by the X9.17 algorithm.
  98.  * But in 2.6.2, we decided to make the randseed.bin file substantially
  99.  * larger to hold more information that a would-be attacker must guess.
  100.  * There are two reasons for this:
  101.  * - Every time you run PGP, especially when responding to one of PGP's
  102.  *   prompts, PGP samples the keystrokes for use as random numbers.
  103.  *   It is a shame to throw this entropy (randomness) away just because
  104.  *   there is no need for it in the current invocation of PGP.
  105.  * - A feature was added to 2.6.2 to generate files full of random bytes
  106.  *   for other programs to use as key material.  In this case, we haven't
  107.  *   got a message we're encrypting to take some entropy from, and we may
  108.  *   be asked to generate more than 24 random bytes, so there should be
  109.  *   more than 24 bytes of seed material to work from.
  110.  * The implementation is added on to the previous one, to offer assurance
  111.  * that it is no weaker.
  112.  * When the cryptRand generator is opened, the file is washed (if possible)
  113.  * and the first 24 bytes are fed to the cryptographic RNG, while the
  114.  * remainder is added to the trueRand random number pool.
  115.  * When saving, the randseed.bin file is refilled with newly generated
  116.  * bytes, again washed if possible.  It turns out (if you study the
  117.  * X9.17 RNG) that each of the 2^64 possible timestamp information
  118.  * values used in generating each 8 bytes of output generates a output
  119.  * value, so the entropy in the trueRand pool is put to good use; this
  120.  * is not just generating more data from 24 bytes of seed.
  121.  * The random pool is opened and saved with a washing key when
  122.  * generating a session key (see make_random_ideakey in crypto.c),
  123.  * but it is also opened (harmless if alreasy open) and saved
  124.  * (harmless if already saved) without a washing key in the exitPGP routine,
  125.  * to mix in any entropy collected in this invocation of PGP even if
  126.  * a session key was not generated.
  127.  */
  128.  
  129. /*
  130.  * The new randseed size, big enough to hold the full context of the cryptRand
  131.  * and trueRand generators.  With the current RANDPOOLBITS of 3072 (384 bytes),
  132.  * that's 408 bytes.  It's useless to make it any larger, although if
  133.  * RANDPOOLBITS is increased, it might be an idea to keep this smaller than
  134.  * one disk block on all systems (512 bytes is a good figure to use)
  135.  * so we don't change the space requirements for randseed.bin.
  136.  */
  137. #define RANDSEED_BYTES    (RANDPOOLBITS/8 + 24)
  138. /* Have we read in the randseed.bin file? */
  139. static boolean randSeedOpen = 0;
  140. static struct IdeaRandContext randContext;
  141.  
  142. /*
  143.  * Load the RNG state from the randseed.bin file on disk.
  144.  * Returns 0 on success, <0 on error.
  145.  *
  146.  * If cfb is non-zero, prewashes the data by encrypting with it.
  147.  */
  148. int
  149. cryptRandOpen(struct IdeaCfbContext *cfb)
  150. {
  151.     byte buf[256];
  152.     int len;
  153.     FILE *f;
  154.  
  155.     if (randSeedOpen)
  156.         return 0;    /* Already open */
  157.  
  158.     f = fopen(globalRandseedName, FOPRBIN);
  159.     if (!f)
  160.         return -1;
  161.  
  162.     /* First get the bare minimum 24 bytes we need for the IDEA RNG */
  163.     len = fread((char *)buf, 1, 24, f);
  164.     if (cfb)
  165.         ideaCfbEncrypt(cfb, buf, buf, 24);
  166.     ideaRandInit(&randContext, buf, buf+16);
  167.     randSeedOpen = TRUE;
  168.     if (len != 24) { /* Error */
  169.         fclose(f);
  170.         return -1;
  171.     }
  172.  
  173.     /* Read any extra into the random pool */
  174.     for (;;) {
  175.         len = fread((char *)buf, 1, sizeof(buf), f);
  176.         if (len <= 0)
  177.             break;
  178.         if (cfb)
  179.             ideaCfbEncrypt(cfb, buf, buf, len);
  180.         randPoolAddBytes(buf, len);
  181.     }
  182.  
  183.     fclose(f);
  184.     burn(buf);
  185. #ifdef MACTC5
  186.     PGPSetFinfo(globalRandseedName,'RSed','MPGP');
  187. #endif
  188.     return 0;
  189. }
  190.  
  191. /* Create a new state from the output of trueRandByte */
  192. void
  193. cryptRandInit(struct IdeaCfbContext *cfb)
  194. {
  195.     byte buf[24];
  196.     int i;
  197.  
  198.     for (i = 0; i < sizeof(buf); i++)
  199.         buf[i] = trueRandByte();
  200.     if (cfb)
  201.         ideaCfbEncrypt(cfb, buf, buf, sizeof(buf));
  202.  
  203.     ideaRandInit(&randContext, buf, buf+16);
  204.     randSeedOpen = TRUE;
  205.     burn(buf);
  206. }
  207.  
  208. byte
  209. cryptRandByte(void)
  210. {
  211.     if (!randSeedOpen)
  212.         cryptRandOpen((struct IdeaCfbContext *)0);
  213.     return ideaRandByte(&randContext);
  214. }
  215.  
  216. /*
  217.  * Write out a file of random bytes.  If cfb is defined, wash it with the
  218.  * cipher.
  219.  */
  220. int
  221. cryptRandWriteFile(char const *name, struct IdeaCfbContext *cfb, unsigned bytes)
  222. {
  223.     byte buf[256];
  224.     FILE *f;
  225.     int i, len;
  226.  
  227.     f = fopen(name, FOPWBIN);
  228.     if (!f)
  229.         return -1;
  230.  
  231.     while (bytes) {
  232.         len = (bytes < sizeof(buf)) ? bytes : sizeof(buf);
  233.         for (i = 0; i < len; i++)
  234.             buf[i] = ideaRandByte(&randContext);
  235.         if (cfb)
  236.             ideaCfbEncrypt(cfb, buf, buf, len);
  237.         i = fwrite(buf, 1, len, f);
  238.         if (i < len)
  239.             break;
  240.         bytes -= len;
  241.     }
  242.  
  243. #ifdef MACTC5
  244.     PGPSetFinfo((char *)name,'RSed','MPGP');
  245. #endif
  246.  
  247.     return (fclose(f) != 0 || bytes != 0) ? -1 : 0;
  248. }
  249.  
  250. /*
  251.  * Create a new RNG state, encrypt it with the supplied key, and save it
  252.  * out to disk.
  253.  *
  254.  * When we encrypt a file, the saved data is "postwashed" using the
  255.  * same key and initial vector (including the repeated check bytes and
  256.  * everything) that is used to encrypt the user's message.
  257.  * The hope is that this "postwash" renders it is at least as hard to
  258.  * derive old session keys from randseed.bin as it is to crack the the
  259.  * message directly.
  260.  *
  261.  * The purpose of using EXACTLY the same encryption is to make sure that
  262.  * there isn't related, but different data floating around that can be
  263.  * used for cryptanalysis.
  264.  *
  265.  * This function is always called by exitPGP, without a washing encryption,
  266.  * so this function ignores that call if it has previously been called
  267.  * to save washed bytes.
  268.  */
  269. #ifdef MACTC5
  270.     boolean savedwashed = FALSE;
  271. #endif
  272.  
  273. void
  274. cryptRandSave(struct IdeaCfbContext *cfb)
  275. {
  276. #ifndef MACTC5
  277.     static boolean savedwashed = FALSE;
  278. #endif
  279.  
  280.     if (!randSeedOpen)
  281.         return;    /* Do nothing */
  282.  
  283.     if (cfb)
  284.         savedwashed = TRUE;
  285.     else if (savedwashed)
  286.         return;    /* Don't re-save if it's already been saved washed. */
  287.  
  288.     (void)cryptRandWriteFile(globalRandseedName, cfb, RANDSEED_BYTES);
  289. #ifdef MACTC5
  290.     PGPSetFinfo(globalRandseedName,'RSed','MPGP');
  291. #endif
  292. }
  293.  
  294. /*
  295.  * True random bit handling
  296.  */
  297.  
  298. /*
  299.  * Because these truly random bytes are so unwieldy to accumulate,
  300.  * they can be regarded as a precious resource.  Unfortunately,
  301.  * cryptographic key generation algorithms may require a great many
  302.  * random bytes while searching about for large random prime numbers.
  303.  * Fortunately, they need not all be truly random.  We only need as
  304.  * many truly random bits as there are bits in the large prime we
  305.  * are searching for.  These random bytes can be recycled and modified
  306.  * via pseudorandom numbers until the key is generated, without losing
  307.  * any of the integrity of randomness of the final key.
  308.  *
  309.  * The technique used is a pool of random numbers, which bytes are
  310.  * taken from successively and, when the end is reached, the pool
  311.  * is stirred using an irreversible hash function.  Some (64 bytes)
  312.  * of the pool is not returned to ensure the sequence is not predictible
  313.  * from the values retriefed from trueRandByte().  To be precise,
  314.  * MD5Transform is used as a block cipher in CBC mode, and then the
  315.  * "key" (i.e. what is usually the material to be hashed) is overwritten
  316.  * with some of the just-generated random bytes.
  317.  *
  318.  * This is implemented in randpool.c; see that file for details.
  319.  *
  320.  * An estimate of the number of bits of true (Shannon) entropy in the
  321.  * pool is kept in trueRandBits.  This is incremented when timed
  322.  * keystrokes are available, and decremented when bits are explicitly
  323.  * consumed for some purpose (such as prime generation) or another.
  324.  *
  325.  * trueRandFlush is called to obliterate traces of old random bits after
  326.  * prime generation is completed.  (Primes are the most carefully-guarded
  327.  * values in PGP.)
  328.  */
  329.  
  330. static unsigned trueRandBits = 0;    /* Bits of entropy in pool */
  331.  
  332. #ifdef MACTC5
  333. #define CBITS 5
  334. #define TRByt 3
  335. #define TREvt 1
  336.  
  337. static void perturb(rbits)
  338. int rbits;
  339. {    
  340.     spinner();
  341.     randPoolAddBytes((byte *) seedBuffer, 8);
  342.     trueRandBits +=rbits;
  343.     if (trueRandBits > RANDPOOLBITS)
  344.         trueRandBits = RANDPOOLBITS;
  345.     return;
  346. }
  347. #endif
  348.  
  349. /* trueRandPending is bits to add to next accumulation request */
  350. static unsigned trueRandPending = 0;
  351.  
  352. /*
  353.  * Destroys already-used random numbers.  Ensures no sensitive data
  354.  * remains in memory that can be recovered later.  This is called
  355.  * after RSA key generation, so speed is not critical, but security is.
  356.  * RSA key generation takes long enough that interrupts and other
  357.  * tasks are likely to have used a measurable and difficult-to-predict
  358.  * amount of real time, so there is some virtue in sampling the clocks
  359.  * with noise().
  360.  */
  361. void
  362. trueRandFlush(void)
  363. {
  364.     noise();
  365.     randPoolStir();    /* Destroy evidence of what primes were generated */
  366.     randPoolStir();
  367.     randPoolStir();
  368.     randPoolStir();    /* Make *really* certain */
  369. }
  370.  
  371. /*
  372.  * "Consumes" count bits worth of entropy from the true random pool for some
  373.  * purpose, such as prime generation.
  374.  *
  375.  * Note that something like prime generation can end up calling trueRandByte
  376.  * more often than is implied by the count passed to trueRandClaim; this
  377.  * may happen if the random bit consumer is not perfectly efficient in its
  378.  * use of random bits.  For example, if a search for a suitable prime fails,
  379.  * the easiest thing to do is to get another load of random bits and try
  380.  * again.  It is perfectly acceptable if these bits are correlated with the
  381.  * bits used in the failed attempt, since they are discarded.
  382.  */
  383. void
  384. trueRandConsume(unsigned count)
  385. {
  386. #ifdef MACTC5
  387.     if( trueRandBits >= count )
  388.         trueRandBits -= count;
  389.     else
  390.         trueRandBits = 0;
  391. #else
  392.     assert (trueRandBits >= count);
  393.     trueRandBits -= count;
  394. #endif
  395. }
  396.  
  397. /*
  398.  * Returns a truly random byte if any are available.  It degenerates to
  399.  * a pseudorandom value if there are none.  It is not an error to call
  400.  * this if none are available.  For example, it is called when generating
  401.  * session keys to add to other sources of cryptographic random numbers.
  402.  *
  403.  * This forces an accumulation if any extra random bytes are pending.
  404.  */
  405. int
  406. trueRandByte(void)
  407. {
  408.     if (trueRandPending)
  409.         trueRandAccum(0);
  410. #ifdef MACTC5
  411.     while( trueRandBits < 8 ) {
  412.         perturb(TRByt);
  413.         spinner();
  414.         }
  415. #endif
  416.  
  417.     return randPoolGetByte();
  418. }
  419.  
  420. /*
  421.  * Given an event (typically a keystroke) coded by "event"
  422.  * at a random time, add all randomness to the random pool,
  423.  * compute a (conservative) estimate of the amount, add it
  424.  * to the pool, and return the amount of randomness.
  425.  * (The return value is just for informational purposes.)
  426.  *
  427.  * Double events are okay, but three in a row is considered
  428.  * suspiscous and the randomness is counted as 0.
  429.  */
  430. unsigned
  431. trueRandEvent(int event)
  432. {
  433.     static int event1 = 0, event2 = 0;
  434.     word32 delta;
  435.     unsigned cbits;
  436.  
  437.     delta = noise();
  438.     randPoolAddBytes((byte *)&event, sizeof(event));
  439.  
  440. #ifdef MACTC5
  441.     perturb(TRByt);
  442. #endif
  443.  
  444.     if (event == event1 && event == event2) {
  445.         cbits = 0;
  446.     } else {
  447.         event2 = event1;
  448.         event1 = event;
  449.  
  450.         for (cbits = 0; delta; cbits++)
  451.             delta >>= 1;
  452.  
  453.         /* Excessive paranoia? */
  454. #ifdef MACTC5
  455.         if (cbits > CBITS)
  456.             cbits = CBITS;
  457. #else
  458.         if (cbits > 8)
  459.             cbits = 8;
  460. #endif
  461.     }
  462.  
  463.     trueRandBits += cbits;
  464.     if (trueRandBits > RANDPOOLBITS)
  465.         trueRandBits = RANDPOOLBITS;
  466.  
  467.     return cbits;
  468. }
  469.  
  470.  
  471. /*
  472.  * Since getting random bits from the keyboard requires user attention,
  473.  * we buffer up requests for them until we can do one big request.
  474.  */
  475. void
  476. trueRandAccumLater(unsigned bitcount)
  477. {
  478.     trueRandPending += bitcount;    /* Wow, that was easy! :-) */
  479. #ifdef MACTC5
  480.     spinner();
  481. #endif
  482. }
  483.  
  484. static void flush_input(void);
  485.  
  486. #ifdef AMIGA  /* Globals used for timing here and in noise.c - RKNOP 940613 */
  487. struct Library *TimerBase=NULL;
  488. struct timerequest *TimerIO=NULL;
  489. union { struct timeval t;
  490.         struct EClockVal e;
  491.       } time0,time1;
  492. unsigned short use_eclock=0;
  493. #endif /* AMIGA */
  494.  
  495. /*
  496.  * Performs an accumulation of random bits.  As long as there are fewer bits
  497.  * in the buffer than are needed (the number passed, plus pending bits),
  498.  * prompt for more.
  499.  */
  500. void
  501. trueRandAccum(unsigned count)    /* Get this many random bits ready */
  502. {
  503.     int c;
  504. #if defined(MSDOS) || defined(__MSDOS__)
  505.     time_t timer;
  506. #endif
  507.  
  508.     count += trueRandPending;    /* Do deferred accumulation now */
  509.     trueRandPending = 0;
  510.  
  511.     if (count > RANDPOOLBITS)
  512.         count = RANDPOOLBITS;
  513.  
  514.     if (trueRandBits >= count)
  515.         return;
  516.  
  517.     fprintf(stderr,
  518. LANG("\nWe need to generate %u random bits.  This is done by measuring the\
  519. \ntime intervals between your keystrokes.  Please enter some random text\
  520. \non your keyboard until you hear the beep:\n"), count-trueRandBits);
  521.  
  522.     ttycbreak();
  523.  
  524. #ifdef AMIGA  /* RKNOP 940613 */
  525.         TimerIO=(struct timerequest *)AllocMem(sizeof(struct timerequest),
  526.                                                MEMF_PUBLIC|MEMF_CLEAR);
  527.         if (TimerIO)
  528.         {  if (OpenDevice(TIMERNAME,UNIT_MICROHZ,(struct IORequest *)TimerIO,0))
  529.               TimerBase=NULL;
  530.            else
  531.            {  TimerBase=(struct Library *)TimerIO->tr_node.io_Device;
  532.               if (TimerBase->lib_Version>=36) /* Use E-clock instead */
  533.               {  use_eclock=1;
  534.                  CloseDevice((struct IORequest *)TimerIO);
  535.                  if (!OpenDevice(TIMERNAME,UNIT_ECLOCK,
  536.                                  (struct IORequest *)TimerIO,0))
  537.                     TimerBase=(struct Library *)TimerIO->tr_node.io_Device;
  538.                  else
  539.                     TimerBase=NULL;
  540.               }
  541.               else use_eclock=0;
  542.            }
  543.         }
  544.         else TimerBase=NULL;
  545. #endif /* AMIGA */
  546.  
  547.     do {
  548.         /* display counter to show progress */
  549.         fprintf(stderr,"\r%4d ", count-trueRandBits);
  550.         fflush(stderr);    /* assure screen update */
  551.  
  552.         flush_input();    /* If there's no delay, we can't use it */
  553. #ifdef MACTC5
  554.         StartTMCounter();
  555. #endif
  556.         c = getch();    /* always wait for input */
  557. #ifdef MSDOS
  558.         if (c == 3)
  559.             breakHandler(SIGINT);
  560.         if (c == 0)
  561.             c = getch() + 256;
  562. #endif
  563.         /* Print flag indicating acceptance (or not) */
  564.                 /* putc a macro, not safe to have function as an arg!! */
  565.                 fputc(trueRandEvent(c) ? '.' : '?' , stderr);
  566. #ifdef MACTC5
  567.         StopTMCounter();
  568. #endif
  569.     } while (trueRandBits < count);
  570.  
  571.     fputs("\r   0 *", stderr);
  572.     fputs(LANG("\007 -Enough, thank you.\n"), stderr);
  573.  
  574. #if defined(MSDOS) || defined(__MSDOS__)
  575.     /* Wait until one full second has passed without keyboard input */
  576.     do {
  577.         flush_input();
  578.         sleep(1);
  579.     } while (kbhit());
  580. #else
  581. #ifdef AMIGA       /* Added RKNOP 940608 */
  582.         Delay(50*1);    /* dos.library function, wait 1 second */
  583.         if (TimerBase) CloseDevice((struct IORequest *)TimerIO);
  584.         TimerBase=NULL;
  585.         if (TimerIO) FreeMem(TimerIO,sizeof(struct timerequest));
  586.         TimerIO=NULL;
  587. #else
  588.     sleep(1);
  589.     flush_input();
  590. #endif
  591. #endif
  592.  
  593.     ttynorm();
  594. }
  595.  
  596. #ifndef EBCDIC                   /* already defined in usuals.h */
  597. #define BS 8
  598. #define LF 10
  599. #define CR 13
  600. #define DEL 127
  601. #endif
  602.  
  603. #ifdef VMS
  604. int putch(int);
  605. #else
  606. #define putch(c) putc(c, stderr)
  607. #endif
  608.  
  609. int
  610. getstring(char *strbuf, unsigned maxlen, int echo)
  611. /* Gets string from user, with no control characters allowed.
  612.  * Also accumulates random numbers.
  613.  * maxlen is max length allowed for string.
  614.  * echo is TRUE iff we should echo keyboard to screen.
  615.  * Returns null-terminated string in strbuf.
  616.  */
  617. {
  618.     unsigned i;
  619.     char c;
  620.  
  621.     ttycbreak();
  622.  
  623. #ifdef AMIGA     /* In case of -f (use stdio for plaintext input),
  624.                     use ReqTools for input from the user */
  625.         if (!IsInteractive(Input()))
  626.            return AmigaRequestString(strbuf,maxlen,echo);
  627. #endif
  628.  
  629.     fflush(stdout);
  630.     i=0;
  631.     for (;;) {
  632. #ifndef VMS
  633.         fflush(stderr);
  634. #endif /* VMS */
  635.         c = getch();
  636.         trueRandEvent(c);
  637. #ifdef VMS
  638.         if (c == 25) {  /*  Control-Y detected */
  639.             ttynorm();
  640.             breakHandler(SIGINT);
  641.         }
  642. #endif /* VMS */
  643. #if defined(MSDOS) || defined (__MSDOS__)
  644.         if (c == 3)
  645.             breakHandler(SIGINT);
  646. #endif
  647.         if (c==BS || c==DEL) {
  648.             if (i) {
  649.                 if (echo) {
  650.                     putch(BS);
  651.                     putch(' ');
  652.                     putch(BS);
  653.                 }
  654.                 i--;
  655.             } else {
  656.                 putch('\007');
  657.             }
  658.             continue;
  659.         }
  660.         if (c < ' ' && c != LF && c != CR) {
  661.             putch('\007');
  662. #if defined(MSDOS) || defined (__MSDOS__)
  663.             if (c == 3)
  664.                 breakHandler(SIGINT);    
  665.             if (c == 0)
  666.                 getch(); /* Skip extended key codes */
  667. #endif
  668.             continue;
  669.         }
  670.         if (echo)
  671.             putch(c);
  672.         if (c==CR) {
  673.             if (echo)
  674.                 putch(LF);
  675.             break;
  676.         }
  677.         if (c==LF)
  678.             break;
  679.         if (c=='\n')
  680.             break;
  681.         strbuf[i++] = c;
  682.         if (i >= maxlen) {
  683.             fputs("\007*\n", stderr);    /* -Enough! */
  684. #if 0
  685.             while (kbhit())
  686.                 getch();    /* clean up any typeahead */
  687. #endif
  688.             break;
  689.         }
  690.     }
  691.     strbuf[i] = '\0';    /* null termination of string */
  692.  
  693.     ttynorm();
  694.  
  695.     return(i);        /* returns string length */
  696. } /* getstring */
  697.  
  698.  
  699. static void
  700. flush_input(void)
  701. {    /* on unix ttycbreak() will flush the input queue */
  702. #if defined(MSDOS) || defined (__MSDOS__) || defined(MACTC5)
  703.     while (kbhit())    /* flush typahead buffer */
  704.         getch();
  705. #endif
  706. }
  707.