home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / misc / volume02 / xrand < prev    next >
Encoding:
Internet Message Format  |  1991-08-27  |  20.8 KB

  1. From mipos3!intelca!oliveb!ames!necntc!ncoast!allbery Fri Apr 15 12:42:45 PDT 1988
  2. Article 349 of comp.sources.misc:
  3. Path: td2cad!mipos3!intelca!oliveb!ames!necntc!ncoast!allbery
  4. From: agn@UNH.CS.CMU.EDU (Andreas Nowatzyk)
  5. Newsgroups: comp.sources.misc
  6. Subject: v02i096: xrand, xcrypt -- a random number generator & application
  7. Keywords: pseudo random numbers, rand, random, encryption, cipher
  8. Message-ID: <8804131846.AA29500@rutgers.edu>
  9. Date: 13 Apr 88 17:19:34 GMT
  10. Sender: allbery@ncoast.UUCP
  11. Reply-To: agn@UNH.CS.CMU.EDU (Andreas Nowatzyk)
  12. Organization: Carnegie-Mellon University, CS/RI
  13. Lines: 681
  14. Approved: allbery@ncoast.UUCP
  15.  
  16. comp.sources.misc: Volume 2, Issue 96
  17. Submitted-By: "Andreas Nowatzyk" <agn@UNH.CS.CMU.EDU>
  18. Archive-Name: xrand
  19.  
  20. The attached random-number generator was writen because of a need for
  21. a good source of random numbers for a research project that involved
  22. systems without such facility (for example a research prototype with
  23. only a C-compiler and minimal libraries).
  24.  
  25. Since the results are critical to my work, a serious attempt was made
  26. to write a 'really good' RNG: that is several CPU weeks on assorted
  27. machines were spend to try out (and discard) various approaches.
  28.  
  29. The generator below is based on 2 well known methods: Linear Congruential
  30. (LCGs) and Additive Congruential generators (ACGs). 
  31.  
  32. The LCG produces the longest possible sequence of 32 bit 'random' numbers,
  33. each being unique in that sequence (it has only 32 bits of state).
  34. It suffers from 2 problems:
  35. a) Independence isn't great, that is the (n+1)th number is somewhat
  36.    related to the preceding one, unlike flipping a coin where knowing the
  37.    past outcomes don't help to predict the next result.
  38. b) Taking parts of a LCG generated number can be quite non-random: for
  39.    example, looking at only the least significant byte gives a permuted
  40.    8-bit counter (that has a  period length of only 256).
  41. The advantage of an LCA is that it is perfectly uniform when run for the
  42. entire period length (and very uniform for smaller sequences too, if
  43. the parameters are chosen carefully).
  44.  
  45. ACG's have extremly long period lengths and provide good independence.
  46. Unfortunately, uniformity isn't not too great. Furthermore, I didn't
  47. find any theoretically analysis of ACG's that addresses uniformity.
  48.  
  49. The RNG given below will return numbers generated by an LCA that are
  50. permuted under control of a ACG. 2 permutations take place: the
  51. 4 bytes of one LCG generated number are subjected to one of 16 permutations
  52. selected by 4 bits of the ACG. The permutation a such that byte of the
  53. result may come from each byte of the LCG number. This effectively destroys
  54. the structure within a word. Finally, the sequence of such numbers is
  55. permuted within a range of 256 numbers. This greatly improves independence.
  56.  
  57. xcrypt is a recreational application (as I have little need for secrecy) 
  58. of this random number generator to en/decrypt files. However, I do believe
  59. that this byproduct is quite strong and a challenge ($50)
  60. to this effect has been posted to sci.crypt. Breaking xcrypt would probably
  61. reveal a flaw in xrand and I'm highly interested in any such problem.
  62.  
  63.   Cheers  --  Andreas
  64.  
  65. -------------------- Cut here --------------------
  66. #
  67. # type    sh /usru0/agn/x.shar   to unpack this archive.
  68. #
  69. echo extracting xrand.1...
  70. cat >xrand.1 <<'!E!O!F!'
  71. .TH XRAND 3 4/13/88
  72. .CM 4
  73. .SH NAME
  74. xrand \- pseudo random number generators
  75. .SH SYNOPSIS
  76. void rnd_init(seed)
  77. unsigned seed;
  78.  
  79. long rnd_i()
  80.  
  81. unsigned long rnd_u()
  82.  
  83. long rnd_ri(range)
  84. long range;
  85.  
  86. double rnd_01d()
  87.  
  88. double rnd_ned(lamda)
  89. double lamda;
  90. .SH DESCRIPTION
  91. xrand is a collection of pseudo-random number generators that were
  92. carefully tested for uniform distribution and independence.
  93. These tests included testing parts of the generated numbers
  94. (say a byte or a bit) for the same properties.
  95.  
  96. .B rnd_init
  97. must be called before any random numbers are drawn.
  98. Different seeds will
  99. yield different, but deterministic sequences of random numbers with
  100. like statistical properties.
  101. .B rnd_i
  102. returns positive integers,
  103. .B rnd_u
  104. returns unsigned integers (all 32 bit are 
  105. .I random
  106. ), 
  107. .B rnd_ri
  108. returns integers in the range [0..range-1],
  109. .B rnd_01d
  110. returns doubles in the range of [0.0..1.0),
  111. .B rnd_ned
  112. returns negative-exponential distributed doubles in
  113. the range [0.0..+infinity).
  114. .SH ENVIRONMENT
  115. .B rnd_ned
  116. requires
  117. .B log()
  118. from the math library.
  119. .SH "SEE ALSO"
  120. .I rand(3C),
  121. which produces pseudo-random numbers which are not very random and
  122. .I
  123. random(3),
  124. which is about 45% faster than xrand but has inferior uniformity.
  125. .SH BUGS
  126. .B xrand
  127. assumes that long words have 32 bits and that 2's complement
  128. arithmetic is used.
  129. .SH HISTORY
  130. .TP
  131. 13-Apr-88  Andreas Nowatzyk (agn) at Carnegie-Mellon University
  132. Created.
  133. !E!O!F!
  134. #
  135. # type    sh /usru0/agn/x.shar   to unpack this archive.
  136. #
  137. echo extracting xrand.c...
  138. cat >xrand.c <<'!E!O!F!'
  139. #include <math.h>
  140.  
  141. /* Random number generators:
  142.  *
  143.  *  rnd_init (unsigned seed) 
  144.  *            : initializes the generator
  145.  *
  146.  *  rnd_i ()        : returns positive integers [0,0x7fffffff]
  147.  *  rnd_u ()        : returns unsigned's        [0,0xffffffff]
  148.  *  rnd_ri (long n)    : returns positive integers [0,n-1]
  149.  *  rnd_01d ()        : returns doubles        [0.0,1.0)
  150.  *              Note: ")" is no typo - rnd_01d will not return a 1.0,
  151.  *                              but can return the next smaller FP number.
  152.  *  rnd_ned (double lam): returns neg. exponential distributed doubles [0.0,+inf)
  153.  *
  154.  *  Algorithm M as describes in Knuth's "Art of Computer Programming", Vol 2. 1969
  155.  *  is used with a linear congruential generator (to get a good uniform
  156.  *  distribution) that is permuted with a Fibonacci additive congruential
  157.  *  generator to get good independence.
  158.  *
  159.  *  Bit, byte, and word distributions were extensively tested and pass
  160.  *  Chi-squared test near perfect scores (>7E8 numbers tested, Uniformity
  161.  *  assumption holds with probability > 0.999)
  162.  *
  163.  *  Run-up tests for on 7E8 numbers confirm independence with
  164.  *  probability > 0.97.
  165.  *
  166.  *  Plotting random points in 2d reveals no apparent structure.
  167.  *
  168.  *  Autocorrelation on sequences of 5E5 numbers (A(i) = SUM X(n)*X(n-i), i=1..512)
  169.  *  results in no obvious structure (A(i) ~ const).
  170.  *
  171.  *  On a SUN 3/60, rnd_u() takes about 19.4 usec per call, which is about 44%
  172.  *  slower than Berkeley's random() (13.5 usec/call).
  173.  *
  174.  *  Except for speed and memory requirements, this generator outperforms
  175.  *  random() for all tests. (random() scored rather low on uniformity tests,
  176.  *  while independence test differences were less dramatic).
  177.  *
  178.  *  Thanks to M.Mauldin, H.Walker, J.Saxe and M.Molloy for inspiration & help.
  179.  *
  180.  *  (c) Copyright 1988 by A. Nowatzyk
  181.  *
  182.  */
  183.  
  184. /* LC-parameter selection follows recommendations in 
  185.  * "Handbook of Mathematical Functions" by Abramowitz & Stegun 10th, edi.
  186.  */
  187. #define LC_A 66049            /* = 251^2, ~= sqrt(2^32)            */
  188. #define LC_C 3907864577            /* result of a long trial & error series    */
  189.  
  190. #define Xrnd(x) (x * LC_A + LC_C)   /* the LC polynomial            */
  191.             
  192. static unsigned long Fib[55];        /* will use X(n) = X(n-55) - X(n-24)    */
  193. static int Fib_ind;            /* current index in circular buffer        */
  194. static unsigned long Xrnd_var;        /* LCA - recurrence variable        */
  195. static unsigned long auxtab[256];   /* temporal permutation table        */
  196. static unsigned long prmtab[64] = { /* spatial permutation table        */
  197.     0xffffffff, 0x00000000,  0x00000000,  0x00000000,  /* 3210 */
  198.     0x0000ffff, 0x00ff0000,  0x00000000,  0xff000000,  /* 2310 */
  199.     0xff0000ff, 0x0000ff00,  0x00000000,  0x00ff0000,  /* 3120 */
  200.     0x00ff00ff, 0x00000000,  0xff00ff00,  0x00000000,  /* 1230 */
  201.  
  202.     0xffff0000, 0x000000ff,  0x00000000,  0x0000ff00,  /* 3201 */
  203.     0x00000000, 0x00ff00ff,  0x00000000,  0xff00ff00,  /* 2301 */
  204.     0xff000000, 0x00000000,  0x000000ff,  0x00ffff00,  /* 3102 */
  205.     0x00000000, 0x00000000,  0x00000000,  0xffffffff,  /* 2103 */
  206.  
  207.     0xff00ff00, 0x00000000,  0x00ff00ff,  0x00000000,  /* 3012 */
  208.     0x0000ff00, 0x00000000,  0x00ff0000,  0xff0000ff,  /* 2013 */
  209.     0x00000000, 0x00000000,  0xffffffff,  0x00000000,  /* 1032 */
  210.     0x00000000, 0x0000ff00,  0xffff0000,  0x000000ff,  /* 1023 */
  211.  
  212.     0x00000000, 0xffffffff,  0x00000000,  0x00000000,  /* 0321 */
  213.     0x00ffff00, 0xff000000,  0x00000000,  0x000000ff,  /* 0213 */
  214.     0x00000000, 0xff000000,  0x0000ffff,  0x00ff0000,  /* 0132 */
  215.     0x00000000, 0xff00ff00,  0x00000000,  0x00ff00ff   /* 0123 */
  216. };
  217.  
  218. union hack {                /* used to access doubles as unsigneds    */
  219.     double d;
  220.     unsigned long u[2];
  221. };
  222.  
  223. static union hack man;            /* mantissa bit vector            */
  224.  
  225. rnd_init (seed)                /* modified: seed 0-31 use precomputed stuff */
  226.     unsigned seed;
  227. {
  228.     register unsigned long u;
  229.     register int i;
  230.     double x, y;
  231.     union hack t;
  232.  
  233.     static unsigned seed_tab[32] = {
  234.         0xbdcc47e5, 0x54aea45d, 0xec0df859, 0xda84637b,
  235.         0xc8c6cb4f, 0x35574b01, 0x28260b7d, 0x0d07fdbf,
  236.         0x9faaeeb0, 0x613dd169, 0x5ce2d818, 0x85b9e706,
  237.         0xab2469db, 0xda02b0dc, 0x45c60d6e, 0xffe49d10,
  238.         0x7224fea3, 0xf9684fc9, 0xfc7ee074, 0x326ce92a,
  239.         0x366d13b5, 0x17aaa731, 0xeb83a675, 0x7781cb32,
  240.         0x4ec7c92d, 0x7f187521, 0x2cf346b4, 0xad13310f,
  241.         0xb89cff2b, 0x12164de1, 0xa865168d, 0x32b56cdf  };
  242.  
  243.     if (seed < 32)
  244.     u = seed_tab[seed];
  245.     else
  246.     u = seed ^ seed_tab[seed & 31];
  247.  
  248.     for (i = 55; i--;)            /* set up Fibonacci additive congruential    */
  249.     Fib[i] = u = Xrnd(u);
  250.  
  251.     for (i = 256; i--;)
  252.     auxtab[i] = u = Xrnd(u);
  253.  
  254.     Fib_ind = u % 55;            /* select a starting point            */
  255.  
  256.     Xrnd_var = u;
  257.  
  258.     if (sizeof(x) != 2 * sizeof(unsigned long)) {
  259.     x = 0.0;
  260.     y = 1.0;
  261.     y /= x;                /*** intentional divide by 0: rnd_01d will
  262.                      not work because a double doesn't fit
  263.                      in 2 unsigned longs on your machine! ***/
  264.     };
  265.  
  266.     x = 1.0;
  267.     y = 0.5;
  268.     do {                /* find largest fp-number < 2.0        */
  269.     t.d = x;
  270.     x += y;
  271.     y *= 0.5;
  272.     } while (x != t.d && x < 2.0);
  273.  
  274.     man.d = 1.0;
  275.     man.u[0] ^= t.u[0];
  276.     man.u[1] ^= t.u[1];            /* man is now 1 for each mantissa bit    */
  277. }
  278.  
  279. long rnd_i ()
  280. /*
  281.  * returns a positive, uniformly distributed random number in [0,0x7fffffff]
  282.  */
  283.     register unsigned long i, j, *t = Fib;
  284.  
  285.     i = Fib_ind;
  286.     j = t[i];                    /* = X(n-55) */
  287.     j -= (i >= 24) ? t[i - 24] : t[i + 21]; /* = X(n-24) */
  288.     t[i] = j;
  289.     if (++i >= 55) i = 0;
  290.     Fib_ind = i;
  291.  
  292.     t = &auxtab[(j >> 24) & 0xff];
  293.     i = *t;
  294.     Xrnd_var = *t = Xrnd(Xrnd_var);
  295.     t = &prmtab[j & 0x3c];
  296.  
  297.     j =  *t++ & i;
  298.     j |= *t++ & ((i << 24) | ((i >>  8) & 0x00ffffff));
  299.     j |= *t++ & ((i << 16) | ((i >> 16) & 0x0000ffff));
  300.     j |= *t   & ((i <<  8) | ((i >> 24) & 0x000000ff));
  301.     
  302.     return j & 0x7fffff;
  303. }
  304.  
  305. unsigned long rnd_u ()
  306. /*
  307.  * same as rnd_i, but gives full 32 bit range
  308.  */
  309.     register unsigned long i, j, *t = Fib;
  310.  
  311.     i = Fib_ind;
  312.     j = t[i];                    /* = X(n-55) */
  313.     j -= (i >= 24) ? t[i - 24] : t[i + 21]; /* = X(n-24) */
  314.     t[i] = j;
  315.     if (++i >= 55) i = 0;
  316.     Fib_ind = i;
  317.  
  318.     t = &auxtab[(j >> 24) & 0xff];
  319.     i = *t;
  320.     Xrnd_var = *t = Xrnd(Xrnd_var);
  321.     t = &prmtab[j & 0x3c];
  322.  
  323.     j =  *t++ & i;
  324.     j |= *t++ & ((i << 24) | ((i >>  8) & 0x00ffffff));
  325.     j |= *t++ & ((i << 16) | ((i >> 16) & 0x0000ffff));
  326.     j |= *t   & ((i <<  8) | ((i >> 24) & 0x000000ff));
  327.     
  328.     return j;
  329. }
  330.  
  331. long rnd_ri (rng)
  332.     long rng;
  333. /*
  334.  * randint: Return a random integer in a given Range [0..rng-1]
  335.  *          Note:  0 < rng
  336.  */
  337. {
  338.     register unsigned long  r, a;
  339.  
  340.     do {
  341.     r = rnd_i();
  342.     a = (r / rng) + 1;
  343.     a *= rng;
  344.     } while (a >= 0x7ffffff);
  345.     
  346.     a--;
  347.     return a - r;
  348. }
  349.  
  350. double rnd_01d ()
  351. /*
  352.  * returns a uniformly distributed double in the range of [0..1)
  353.  *         or  0.0 <= rnd_01d() < 1.0 to be precise
  354.  *
  355.  * Note: this code assumes that 2 'unsigned long's can hold a 'double'
  356.  *       (works on SUN-3's, SUN-4's, MIPS, VAXen, IBM RT's)
  357.  */
  358. {
  359.     union hack t;
  360.  
  361.     t.d = 1.0;
  362.  
  363.     t.u[0] |= rnd_u() & man.u[0];          /* munch in 1st part   */
  364.     t.u[1] |= rnd_u() & man.u[1];          /* munch in 2nd part   */
  365.  
  366.     return t.d - 1.0;
  367. }
  368.  
  369. double rnd_ned (lam)
  370.     double lam;
  371. /*
  372.  * returns a neg. exponential distributed double in the range of [0..+infinity)
  373.  *         or  0.0 <= rnd_neg() < +infinity to be precise
  374.  *
  375.  * Note: this code assumes that 2 'unsigned long's can hold a 'double'
  376.  *       it also assumes that 'log()' behaves as advertised.
  377.  *
  378.  */
  379. {
  380.     union hack t;
  381.  
  382.     t.d = 1.0;
  383.  
  384.     t.u[0] |= rnd_u() & man.u[0];          /* munch in 1st part   */
  385.     t.u[1] |= rnd_u() & man.u[1];          /* munch in 2nd part   */
  386.  
  387.     return -log(2.0 - t.d) / lam;
  388. }
  389. !E!O!F!
  390. #
  391. # type    sh /usru0/agn/x.shar   to unpack this archive.
  392. #
  393. echo extracting xcrypt.1...
  394. cat >xcrypt.1 <<'!E!O!F!'
  395. .TH XCRYPT 1 4/13/88
  396. .CM 4
  397. .SH NAME
  398. xcrypt \- data encryption/decryption program
  399. .SH SYNOPSIS
  400. encrypt <plain-text file> <cipher-text file>
  401.  
  402. decrypt <cipher-text file> <plain-text file>
  403. .SH DESCRIPTION
  404. .B xcrypt
  405. uses the observation that a good pseudo-random number
  406. generator can be used to generate a strong encryption.
  407. .B xcrypt
  408. is based on
  409. .B xrand(3)
  410. that was extensively tested for randomness (some CPU-week were devoted
  411. to this effort).
  412.  
  413. .B xcrypt
  414. invoked with a name starting with either 'e' or 'E' will write an
  415. encrypted version of the plain-text file to the cipher text file.
  416. Any other invocation will cause an decryption.
  417. Both files are treated as binary files. ASCII-text files will become
  418. binary files upon encryption.
  419. The key string will be prompted from <stdin>.
  420. .B xcrypt
  421. will compute for a few seconds before starting the translation, which
  422. is essentially I/O limited.
  423. This initialization phase is indispensable
  424. and was designed to thwart brute force key guessing attempts.
  425. Key lengths of 10-1000 characters are meaningful.
  426. .SH ENVIRONMENT
  427. .B xcrypt
  428. requires
  429. .B xrand(3)
  430. .SH HISTORY
  431. .TP
  432. 13-Apr-88  Andreas Nowatzyk (agn) at Carnegie-Mellon University
  433. Created.
  434. !E!O!F!
  435. #
  436. # type    sh /usru0/agn/x.shar   to unpack this archive.
  437. #
  438. echo extracting xcrypt.c...
  439. cat >xcrypt.c <<'!E!O!F!'
  440. /*
  441.  * xcrypt: a Encryption / Decryption program.
  442.  *
  443.  * xcrypt is loosely based on an idea that was proposed by M.Mauldin @ CMU
  444.  * (and potentially many other): use a pseudo-random number generator (RNG)
  445.  * to produce one-time pads.
  446.  *
  447.  * So, the basic approach is simply to initialize the RNG with the
  448.  * encryption key and then decide what part of the RNG output is
  449.  * used to encrypt the message. In xcrypt, a high quality RNG is
  450.  * employed that was extensively tested for uniform distribution
  451.  * and independence. This is expected to yield a rather strong encryption.
  452.  *
  453.  * Selecting the starting point of the random number sequence is based on a
  454.  * seed that is derived from the unix time()-call and the process ID.
  455.  * This seed (4 bytes) is prefixed to the ciphertext (similar to the typical
  456.  * unix password encoding scheme). You may consider transmitting the seed
  457.  * by an other channel.
  458.  *
  459.  * Actually, both the seed and the key are used to determine the initial
  460.  * state of the RNG, which is less computationally expensive than letting
  461.  * the RNG run for value-of(seed) iterations.
  462.  *
  463.  * This particular RNG has 9990bits of state (yes, more than 1Kbyte!), so
  464.  * so keys of 1000 char are still meaningful (albeit cumbersome).
  465.  *
  466.  * In order to thwart a key-guessing, brute force attack, the RNG is
  467.  * run for 60000 to 120000 iterations before the output is used for
  468.  * encryption. The RNG output of this initialization phase is used to
  469.  * construct 2 sets of 256 permutations of [0,1,...,255], that will
  470.  * be used later. Thus, any shortcut of these iterations (the actual
  471.  * number depends on the key and is not known to the attacker) would
  472.  * also have to produce all the intermediate results. Given the low
  473.  * complexity of an iteration, it's non-linearity and the intermediate
  474.  * result requirement, it appears to me that it is provable that no
  475.  * dramatic shortcut exists.
  476.  *
  477.  * The RNG delivers 32bit random numbers. All 32 bits are
  478.  * used to encrypt an 8bit character:
  479.  *    1. use 8bit to XOR the plaintext character
  480.  *    2. use 8bit to select a random permutation (from set 1, see above)
  481.  *    3. use 8bit to XOR the result of step 2
  482.  *    4. use 8bit to select a random permutation (set 2)
  483.  * Every bit of the RNG output will change the plain-to-cipher text
  484.  * character mapping, but unlike the simple XOR case, it is not possible
  485.  * to reconstruct the RNG output from a plain/cipher text pair (you
  486.  * would have to guess about 24bits!).
  487.  *
  488.  * The strength of xcrypt is based on:
  489.  *   1. a high quality RNG with plenty of state information.
  490.  *   2. a computation intensive initialization phase
  491.  *   3. 'information loss' in the encryption phase that
  492.  *      prevents the reconstruction of the 'PAD' even in the
  493.  *      known plain text case.
  494.  *
  495.  * Note: 'xcrypt' is a small just-for-fun program, but the underlying
  496.  *       RNG was written and extensively tested as part of a serious
  497.  *       research project.
  498.  *
  499.  * April, 1988  A. Nowatzyk
  500.  *
  501.  */
  502.  
  503. #include <stdio.h>
  504. #include <sys/file.h>
  505.  
  506. char perm_tab1[0x10000];    /* permutation tables        */
  507. char perm_tab2[0x10000];
  508.  
  509. #include "xrand.c"
  510.  
  511. main(argc, argv)
  512. int    argc;
  513. char    *argv[];
  514. {
  515.     unsigned        seed;
  516.     char            buf[4];
  517.     int             ifd, ofd;
  518.  
  519.     if (argc != 3 || 0 > (ifd = open(argv[1], O_RDONLY)) || 
  520.         0 > (ofd = open(argv[2], O_WRONLY | O_CREAT, 0600))) {
  521.     fprintf(stderr, "en/decrypt <input-file> <output-file>\n");
  522.     exit(1);
  523.     }
  524.  
  525.     if (*argv[0] == 'e' || *argv[0] == 'E') {    /** encryption **/
  526.  
  527.     seed = time(0l);    /* get an arbitrary seed    */
  528.     seed ^= getpid();
  529.  
  530.     get_key(seed);        /* get the key            */
  531.  
  532.     buf[0] = seed;        /* write out the seed        */
  533.     buf[1] = seed >> 8;
  534.     buf[2] = seed >> 16;
  535.     buf[3] = seed >> 24;
  536.     write(ofd, buf, 4);
  537.  
  538.     encrypt(ifd, ofd);
  539.     
  540.     } else {            /** decryption ******************/
  541.  
  542.     if (4 != read(ifd, buf, 4)) {    /* retrieve seed    */
  543.         fprintf(stderr, "Can't read '%s'\n", argv[1]);
  544.         exit(1);
  545.     }
  546.     seed  =  buf[0]        & 0x000000ff;
  547.     seed |= (buf[1] << 8)  & 0x0000ff00;
  548.     seed |= (buf[2] << 16) & 0x00ff0000;
  549.     seed |= (buf[3] << 24) & 0xff000000;
  550.  
  551.     get_key(seed);        /* get the key            */
  552.  
  553.     inv_tab(perm_tab1);    /* invert permutation tables    */
  554.     inv_tab(perm_tab2);
  555.     
  556.     decrypt(ifd, ofd);
  557.     }
  558.  
  559.     close(ifd);
  560.     close(ofd);
  561.     exit(0);
  562. }
  563.  
  564. get_key(seed)
  565.     unsigned seed;
  566. {
  567.     char            buf[1024];
  568.     register int    i, j, k;
  569.     static char    *key_chars = 
  570.     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  571.     register char   t;
  572.  
  573.     rnd_init(seed);
  574.  
  575.     printf("Key:");        /* I know, there are better ways */
  576.     gets(buf);
  577.  
  578.     for (i = 0, j = 0; buf[i] && i < 1024; i++)
  579.     for (k = 0; key_chars[k]; k++)
  580.         if (buf[i] == key_chars[k]) {
  581.         j++;
  582.         Fib[(7 * k) % 55] ^= 625111 * k + rnd_u();
  583.         buf[i] = '?';    /* munch key over        */
  584.         break;
  585.         }
  586.  
  587.     if (j < 10)
  588.     fprintf(stderr, "Warning: your key is rather short\n");
  589.  
  590.     i = rnd_u();        /* initial permutation table 1    */
  591.     for (j = 0; j < 256; j++) {
  592.     for (k = 0; k < 256; k++)
  593.         perm_tab1[j * 256 + k] = i + k;
  594.     i++;
  595.     }
  596.     for (j = 30000 + rnd_ri(30000l); j--;) {    /* shuffle pt 1 */
  597.     i = rnd_u();
  598.     k = (i & 0xff00) | ((i >> 24) & 0xff);
  599.     i &= 0xffff;
  600.                 /* the random number is broken into
  601.                  * 3 parts, n1, n2, n3 of 1 byte each.
  602.                  * the n1-th and n2-th elements of
  603.                                  * the n3-th permutation are transposed.
  604.                                  */
  605.     t = perm_tab1[k];
  606.     perm_tab1[k] = perm_tab1[i];
  607.     perm_tab1[i] = t;
  608.     }
  609.  
  610.     i = rnd_u();        /* initial permutation table 2    */
  611.     for (j = 0; j < 256; j++) {
  612.     for (k = 0; k < 256; k++)
  613.         perm_tab2[j * 256 + k] = i + k;
  614.     i++;
  615.     }
  616.     for (j = 30000 + rnd_ri(30000l); j--;) {    /* shuffle pt 2 */
  617.     i = rnd_u();
  618.     k = (i & 0xff00) | ((i >> 24) & 0xff);
  619.     i &= 0xffff;
  620.     t = perm_tab2[k];
  621.     perm_tab2[k] = perm_tab2[i];
  622.     perm_tab2[i] = t;
  623.     }
  624. }
  625.  
  626. inv_tab(pt)            /* compute inverse permutations */
  627.     char *pt;
  628. {
  629.     register int i, j;
  630.     char t[256];
  631.  
  632.     for (i = 0; i < 256; i++) {
  633.     for (j = 0; j < 256; j++)
  634.         t[pt[i * 256 + j] & 0xff] = j;
  635.     for (j = 0; j < 256; j++)
  636.         pt[i * 256 + j] = t[j];
  637.     }
  638. }
  639.  
  640. encrypt (ifd, ofd)        /* encrypt stuff        */
  641.     int ifd, ofd;
  642. {
  643.     register int i, j, k;
  644.     register unsigned long u;
  645.     register char *p;
  646.     char buf[1024];
  647.  
  648.     while (0 < (i = read(ifd, buf, 1024))) {
  649.     for (p = buf, j = i; j--; p++) {
  650.  
  651.         u = rnd_u();        /* draw a random number */
  652.  
  653.         k = *p & 0xff;        /* get char to encrypt    */
  654.  
  655.         k ^= u & 0xffff;        /* step 1        */
  656.         k = perm_tab1[k] & 0xff;
  657.  
  658.         k ^= (u >> 16) & 0xffff;    /* step 2        */
  659.         *p = perm_tab2[k];
  660.     }
  661.     write(ofd, buf, i);
  662.     }
  663. }
  664.  
  665. decrypt (ifd, ofd)        /* decrypt stuff        */
  666.     int ifd, ofd;
  667. {
  668.     register int i, j, k;
  669.     register unsigned long u;
  670.     register char *p;
  671.     char buf[1024];
  672.  
  673.     while (0 < (i = read(ifd, buf, 1024))) {
  674.     for (p = buf, j = i; j--; p++) {
  675.  
  676.         u = rnd_u();        /* draw a random number */
  677.  
  678.         k = *p & 0xff;        /* get char to decrypt    */
  679.  
  680.         k |= (u >> 16) & 0xff00;    /* undo step 2        */
  681.         k = (perm_tab2[k] ^ (u >> 16)) & 0xff;
  682.  
  683.         k |= u & 0xff00;        /* undo step 1        */
  684.         *p = perm_tab1[k] ^ u;
  685.     }
  686.     write(ofd, buf, i);
  687.     }
  688. }
  689. !E!O!F!
  690. -- 
  691.    --  Andreas Nowatzyk  (DC5ZV)
  692.  
  693.    Carnegie-Mellon University         Arpa-net:   agn@unh.cs.cmu.edu
  694.    Computer Science Department         Usenet:   ...!seismo!unh.cs.cmu.edu!agn
  695.  
  696.  
  697.