/*--- cippkg.c -- Routines to make a cryptographic package from 
 *  large integer manipulation routines in other files.
 *
 *   Mark Riordan   18 Aug 1990, 27 January 1991 - 10 March 1991.
 *
 *  I hereby place all the code in this file into the public domain.
 *  Mark Riordan   riordanmr@clvax1.cl.msu.edu
 */

#include <stdio.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include "zbigint.h"
#include "zproto.h"
#include "zmiscpro.h"
#include "msqrtpro.h"
#include "newdespr.h"
#include "typtime.h"
#include "cippkgpr.h"
#include "newdespr.h"
#include "newdes.h"
#include "blockcip.h"
#include "blockcpr.h"
#include "prcodepr.h"
#include "prencode.h"

#define BYTES_REPEATED 8

#define TRUE 1
#define FALSE 0

/*--- function ChooseNumber ----------------------------------------------
 *
 *  Choose a number, p, of a certain size.
 *
 *    Entry    nbits       is the number of bits desired in
 *                         the output number.
 *             seed_bytes  is a pseudo-random input used to help randomize
 *                         the process.
 *             nbytes_seed is the number of bytes in the above array.
 *
 *    Exit     p           is a number of the desired size (at least
 *                         nbits bits).
 */

void
ChooseNumber(nbits,seed_bytes,nbytes_seed,p)
MYINT nbits;
unsigned char seed_bytes[];
int nbytes_seed;
BIGINT *p;
{
   MYINT lastbits = nbits%NBITS;
   MYINT usedbytes = (nbits+BITS_PER_BYTE-1) / BITS_PER_BYTE;
   MYINT mask, maskadd, maskhigh;
   unsigned char newdes_key[NEWDES_USER_KEY_BYTES], block[NEWDES_BLOCK_BYTES];
   MYINT jkey, isb;
   TypCipherInfo cipinfo;


   /* Make sure caller didn't ask for too big a number. */
   if(nbits > NBITS * (ZSIZEP-1)) {
      zhalt("nbits too big in ChooseNumber");
   } else {
      /* Use NEWDES encryption as a random number generator.
       * Make the encryption key based on the seed bytes given
       * to us.
       * To enhance debugging, set the key and initial values to
       * some arbitrary values to start with.
       */
      for(jkey=0; jkey<NEWDES_USER_KEY_BYTES; jkey++) {
         newdes_key[jkey] = (unsigned char) 'P';
      }
      for(jkey=0; jkey<NEWDES_BLOCK_BYTES; jkey++) {
         block[jkey] = (unsigned char) 'm';
      }
      /* Use all of the random bytes given to us, even if we
       * have to take several passes through the key array.
       * Fill all of the key bytes, even if we have to reuse
       * some of the random bytes.
       */
      for(isb=0,jkey=0; isb<nbytes_seed || jkey<NEWDES_USER_KEY_BYTES;
        isb++,jkey++) {
         newdes_key[isb%NEWDES_USER_KEY_BYTES] ^= seed_bytes[isb%nbytes_seed];
      }
      cipinfo.algorithm = newdes;
      cipinfo.blockmode = ecb;
      BlockInit(&cipinfo);
      BlockSetKey(newdes_key,NEWDES_USER_KEY_BYTES,1,&cipinfo);

      /* Encipher this key with itself just to further confuse things.
       */

      for(jkey=0; jkey<=(NEWDES_USER_KEY_BYTES-NEWDES_BLOCK_BYTES); jkey++) {
         BlockEncipherBlock(newdes_key+jkey,&cipinfo);
      }
      for(jkey=NEWDES_USER_KEY_BYTES-NEWDES_BLOCK_BYTES; jkey>0; jkey--) {
         BlockEncipherBlock(newdes_key+jkey,&cipinfo);
      }

      /* Now that the key is really scrambled, set it again.  */
      BlockSetKey(newdes_key,NEWDES_USER_KEY_BYTES,1,&cipinfo);

      /* OK, now we are ready to start putting random bits into "p".
       * Set the initial length of this BIGINT to the correct value
       * based on the desired number of bits.
       * Then fill in the individual "val"s of the BIGINT with
       * pseudo-random values generated by successive encryptions
       * of the single block "block".
       */
      p->len = (nbits + NBITS - 1) / NBITS;
      for(jkey=0; jkey < p->len; jkey++) {
         BlockEncipherBlock(block,&cipinfo);
         p->val[jkey] = (*((MYINT *)block)) & RADIXM;
      }
#if 0
      byteptr = (unsigned char *) &(p[1]);
      for(; usedbytes--; byteptr++) {
         newdes_block(byteptr);
      }
#endif

      /* Finally, mask off the proper number of bits in the
       * high-order MYINT.  This prevents us from returning
       * a number with too many bits.
       * Also, make sure the top bit is set.
       */
      maskhigh = 1;
      if(lastbits == 0) lastbits = NBITS;
      maskadd = 1;
      mask = 0;
      while(--lastbits) {
         mask |= maskadd;
         maskadd <<= 1;
         maskhigh <<= 1;
      }
      p->val[(p->len)-1] &= mask;
      p->val[(p->len)-1] |= maskhigh;
   }
   BlockFinish(&cipinfo);
}


/*--- function ChoosePrime ------------------------------------------
 *
 *  Chooses a prime number.
 *
 *    Entry    nbits       is the number of bits desired in
 *                         the output number.
 *             seed_bytes  is a pseudo-random input used to help randomize
 *                         the process.
 *             nbytes_seed is the number of bytes in the above array.
 *
 *    Exit     p           is a prime number of the desired size.
 *                         It is equal to 3 mod 4 on the belief
 *                         that this increases crytologic security.
 */
void
ChoosePrime(nbits,seed_bytes,nbytes_seed,p)
MYINT nbits;
unsigned char seed_bytes[];
int nbytes_seed;
BIGINT *p;
{
   ChooseNumber(nbits,seed_bytes,nbytes_seed,p);
   p->val[0] |= 3;
   while(!zprime(p,(MYINT)100)) {
      zsadd(p,(MYINT)4,p);
   }
}


/*--- function BitsInBignum -----------------------------------------------
 *
 *   Determines the number of bits that can safely fit in a given bignum.
 *   That is, it determines the largest n such that 2 ** n < the bignum.
 *
 *    Entry    a    is a bignum
 *
 *    Exit     returns n, as described above.
 */
MYINT
BitsInBignum(a)
BIGINT *a;
{
   MYINT nbits;
   register MYINT highword, mask;

   /* Loop through the bits in the high-order word of a, looking
    * for the most significant bit that is 1.
    */
   highword = a->val[a->len];
   mask = RADIX>>1;
   nbits = NBITS-1;
   for(; nbits && (highword & mask) == 0; mask >>= 1) {
      nbits--;
   }

   nbits += NBITS * (a->len - 1);
   return(nbits);
}

/*--- function BignumToBytes --------------------------------------------
 *
 *   Convert a bignum to a byte string, least significant bytes first.
 *
 *    Entry    a         is the number to convert
 *             minbytes  is the minimum number of bytes to use.
 *
 *    Exit     bytes     is the converted byte string.
 *                       (basically a mod-256 number, end first)
 *             Returns the number of bytes output.
 */
int
BignumToBytes(a,minbytes,bytes)
BIGINT *a;
int minbytes;
unsigned char bytes[];
{
   int i;
   BIGINT z_temp;

   zcopy(a,&z_temp);

   i = -1;
   do {
      bytes[++i] = (unsigned char ) zsdiv(&z_temp,(MYINT)256,&z_temp) ;
   } while (ztest(&z_temp));

   while(i < minbytes-1) bytes[++i] = 0;
   return(i+1);
}

/*--- function BytesToBignum ----------------------------------------
 *
 *   Convert a byte string to a bignum.  Assume the byte string
 *   represents a large integer, with least significant bytes first.
 *
 *    Entry    bytes    is the byte string.
 *             nbytes   is the number of bytes in the string.
 *
 *    Exit     a        is the corresponding bignum.
 */
void
BytesToBignum(bytes,nbytes,a)
unsigned char bytes[];
int nbytes;
BIGINT *a;
{
   zset((MYINT)0,a);
   while (nbytes--) {
      zsmul(a,(MYINT)256,a);
      zsadd(a,(MYINT)(bytes[nbytes]),a);
   };
}


/*--- function BytesToBignumRedun ----------------------------------------
 *
 *   Convert a byte string to a bignum, according to the following scheme:
 *
 *   Set the top byte of the number to the number of bytes of data
 *   in the bytes[] array (1<=x<=255), to ensure that the BIGINT we
 *   generate is large even if the byte string has a lot of leading zeros.
 *   This also allows decoding the exact number of bytes.
 *   Add padding bytes if necessary.
 *   Shift into the BIGINT at the left (least significant bits) a
 *   duplicate of the first 8 bytes, to be used later as a checksum.
 *   Finally, shift in the entire byte string (at the left),
 *   acting as if the byte string represents a large integer,
 *   with most significant bytes last.
 *
 *   The BIGINT will look like this (logically):   (BR == BYTES_REPEATED)
 *
 *   COUNT padding bytes[BR-1]...bytes[0] bytes[nbytes] ... bytes[1] bytes[0]
 *
 *   The purpose of adding in the 8 extra bytes is to allow
 *   the decryption routine to uniquely identify the proper plaintext
 *   from the 4 square roots.
 *   If the input buffer consists of less than 8 bytes, the buffer
 *   is repeated as many times as necessary to use up exactly 8 bytes.
 *
 *    Entry    bytes      is the byte string.
 *             nbytes     is the number of bytes in the string.
 *             totalbytes is the total number of bytes to put in
 *                        the output number.  This must be at least
 *                        BYTES_REPEATED+1+nbytes but may be more
 *                        if the caller decides that the bytes need
 *                        to be padded.
 *
 *    Exit     a          is the corresponding bignum.
 *             Returns 1 if encoding succeeded, else 0.
 */
int
BytesToBignumRedun(bytes,nbytes,totalbytes,a)
unsigned char bytes[];
int nbytes;
int totalbytes;
BIGINT *a;
{
   register int mynbytes;
   int rep_bytes;
   int lpcount;
   int paddingbytes = totalbytes - nbytes - BYTES_REPEATED - 1;

   if(paddingbytes < 0) return 0;

   /* Set the top byte. */
   zset((MYINT)((unsigned int)nbytes),a);

   /* Add padding bytes if necessary.
    */
   while(paddingbytes--) zsmul(a,(MYINT)256,a);

   /* Shift in the first 8 bytes of the input buffer, starting
    * at the end because the decoding scheme is best done as a LIFO scheme.
    * Start over
    * again at the end as many times as necessary to make
    * up exactly 8 bytes.
    */
   rep_bytes = nbytes<BYTES_REPEATED ? nbytes : BYTES_REPEATED;

   mynbytes = BYTES_REPEATED % rep_bytes;
   for(lpcount=BYTES_REPEATED; lpcount--; ) {
      zsmul(a,(MYINT)256,a);
      if(--mynbytes < 0) mynbytes = rep_bytes-1;
      zsadd(a,(MYINT)(bytes[mynbytes]),a);
   }

   /* OK, now shift in the entire buffer, starting at the end.
    */
   for(mynbytes=nbytes; mynbytes--; ) {
      zsmul(a,(MYINT)256,a);
      zsadd(a,(MYINT)(bytes[mynbytes]),a);
   }
   return 1;
}

/*--- function WhetherGoodMsg -----------------------------------------
 *
 *  Identify whether a given string of bytes decoded directly
 *  from a BIGINT is a valid message.
 *  It is assumed that the original encoding to byte form was
 *  performed as per BytesToBignum.
 *  The input bytes should look like: (BR = BYTES_REPEATED)
 *
 *  bytes[0] bytes[1]...bytes[x] bytes[0] bytes[1]...bytes[BR] TOP_FLAG
 *
 *  Entry   bytes   is an array of bytes that was decoded from
 *                  a BIGINT.
 *          nbytes  is the number of bytes in that array.
 *
 *  Exit    Returns the number of data bytes if the bytes are
 *             encoded correctly as per BytesToBignum; else 0.
 */

int
WhetherGoodMsg(bytes,nbytes)
unsigned char *bytes;
int nbytes;
{
   unsigned char *msgptr, *checkptr;
   int lpcount = BYTES_REPEATED;
   int ndatabytes = bytes[nbytes-1];

   if(ndatabytes == 0 || (ndatabytes > (nbytes-BYTES_REPEATED - 1))) {
      return (0);
   } else {
      /* Top byte checked out OK; proceed to check the check
       * bytes against the data bytes.
       */
      checkptr = bytes+ndatabytes;
      msgptr = bytes;
      while(lpcount--) {
         if(*(checkptr++) != *(msgptr++)) return (0);
         /* If there are less than BYTES_REPEATED data bytes
          * and we have gone past the last one, wrap around to
          * the beginning of the array.
          */
         if(msgptr == bytes+ndatabytes) msgptr = bytes;
      }
   }
   return((int)((unsigned int)bytes[nbytes-1]));
}

/*--- function RabinEncipherBignum --------------------------------------------
 *
 *    Encipher a message in the form of a large integer.
 *    (This is what you'll see in all the textbooks; in real life,
 *    you'll use this routine only directly through RabinEncipherBuf.)
 *
 *    Entry    zplain       is the input message
 *             modulus      is the modulus to use--the product of
 *                          two primes.
 *
 *    Exit     zcipher      is the encrypted output.
 */
void
RabinEncipherBignum(zplain,modulus,zcipher)
BIGINT *zplain;
BIGINT *modulus;
BIGINT *zcipher;
{
   zmulmod(zplain,zplain,modulus,zcipher);
}

/*--- function RabinEncipherBuf ---------------------------------------------
 *
 *  Encipher an input buffer of arbitrary size.  The buffer contains
 *  unsigned bytes.
 *
 *    Entry    bytesplain  is the buffer of bytes.
 *             nbytes      is the number of bytes in the buffer.
 *             modulus     is the system modulus.
 *
 *    Exit     bytescip    is the output buffer, containing the
 *                         enciphered result.
 *             Returns the number of bytes in bytescip.
 *
 *    We must break up the input buffer into chunks to be encoded
 *    as bignums.  These chunks must be encodable into bignums
 *    smaller than "n", the system modulus.
 *    I chose to make the chunks multiples of 8-bit bytes;
 *    the chunk size is the largest number of bytes whose binary
 *    value is less than n.  If the input message is small compared
 *    to the size of the modulus, the ciphertext will be large compared
 *    plaintext, but it will decipher to exactly the same size.
 *
 *    The output conceptually consists of a series of enciphered
 *    bignums, one for each input chunk/bignum.
 *
 *    This routine then encodes each enciphered bignum into a output chunk.
 *    I have chosen to use fixed-size (for a given n) output chunks.
 *    Each output chunk must be big enough to encode the largest
 *    possible result, which is n-1.
 *    Note that this means that the output chunk will always be
 *    one byte longer than the input chunk.  The only way
 *    that input chunks could be the same size as output chunks would
 *    be if n were a power of 256, in which case it certainly wouldn't
 *    be the product of two primes.
 *
 *    In short, then, this routine does:
 *
 *    For each input chunk in the input buffer:
 *       Encode into bignum
 *       Encipher into another bignum
 *       Encode this bignum into a chunk of bytes
 */
int
RabinEncipherBuf(bytesplain,nbytes,modulus,bytescip)
unsigned char *bytesplain;
int nbytes;
BIGINT *modulus;
unsigned char *bytescip;
{
   int in_chunk_size, out_chunk_size, bytes_this_out_chunk;
   unsigned char *inptr, *outptr;
   int chunk, total_bytes_out = 0;
   BIGINT inbignum, outbignum;

   /* The output chunk size must be big enough to represent the
    * modulus.
    * We therefore take the number of bits required to represent
    * the modulus, divide by 8, and round up.
    */
   out_chunk_size = (int)((zlog(modulus) + BITS_PER_BYTE-1) / BITS_PER_BYTE);

   /* The input chunk size should be the largest number of bytes
    * that will fit inside the modulus, minus the bytes repeated
    * and the top flag byte.
    */
   in_chunk_size = out_chunk_size-1 - BYTES_REPEATED - 1;

#if 0
   in_chunk_size = ((zlog(modulus)-1) / BITS_PER_BYTE) - BYTES_REPEATED - 1;
   out_chunk_size = in_chunk_size + 1 + BYTES_REPEATED + 1;
#endif
   for(inptr=bytesplain, outptr=bytescip; nbytes > 0;
    nbytes -= in_chunk_size) {
      chunk = nbytes > in_chunk_size ? in_chunk_size : nbytes;
      BytesToBignumRedun(inptr, chunk, out_chunk_size-1,&inbignum);
      RabinEncipherBignum(&inbignum,modulus,&outbignum);
      bytes_this_out_chunk = BignumToBytes(&outbignum,out_chunk_size,outptr);
      if(bytes_this_out_chunk != out_chunk_size) {
         zhalt("Internal error in RabinEncipherBuf");
      }
      inptr += chunk;
      outptr += bytes_this_out_chunk;
      total_bytes_out += bytes_this_out_chunk;
   }
   return(total_bytes_out);
}

/*--- function RabinDecipherBignum ------------------------------------------
 *
 *  Decipher a message in the form of a large integer.
 *
 *    Entry    cipnum   is the "ciphertext" (a bignum);
 *
 *    Exit     zplain   is an array of BIGINTs containing the 4 square roots.
 */
void
RabinDecipherBignum(cipnum,prime1,prime2,planum)
BIGINT *cipnum;
BIGINT *prime1;
BIGINT *prime2;
BIGINT *planum;
{

   msqrt(cipnum,prime1,prime2,planum);

}

/*--- function RabinDecipherBuf ---------------------------------------------
 *
 *  Decipher an input buffer.  The buffer contains
 *  unsigned bytes.
 *
 *    Entry    bytescip    is the buffer of bytes.
 *             nbytes      is the number of bytes in the buffer.
 *                         It must be a multiple of the output chunk size.
 *             prime1,
 *             prime2      are the two prime numbers in the system.
 *
 *    Exit     bytesplain  is the output buffer, containing the
 *                         enciphered result.
 *             Returns the number of bytes in bytesplain.
 *               This will be zero if an error occurred.
 *
 *    This routine does:
 *
 *    For each input chunk in the input buffer:
 *       Encode into bignum
 *       Decipher into another bignum
 *       Encode this bignum into a chunk of bytes
 */
int
RabinDecipherBuf(bytescip,nbytes,prime1,prime2,bytesplain)
unsigned char *bytescip;
int nbytes;
BIGINT *prime1;
BIGINT *prime2;
unsigned char *bytesplain;
{
   int in_chunk_size, out_chunk_size, bytes_this_out_chunk, out_bytes;
   unsigned char *inptr, *outptr;
   int total_bytes_out = 0, gotmsg, j;
   BIGINT inbignum, modulus, planum[4];

   zmul(prime1,prime2,&modulus);

   in_chunk_size = (int) ((zlog(&modulus) + BITS_PER_BYTE-1) / BITS_PER_BYTE);
   out_chunk_size = in_chunk_size-1 - BYTES_REPEATED - 1;

   for(inptr=bytescip, outptr=bytesplain; nbytes > 0;
    nbytes -= in_chunk_size) {
      BytesToBignum(inptr,in_chunk_size, &inbignum);

      msqrt(&inbignum,prime1,prime2,planum);

      for(j=0, gotmsg=0; j<4 && !gotmsg; j++) {
#if 0
         printf("Deciphered #%d: ",j);
         zwrite(&planum[j]); putchar('\n');
#endif
         bytes_this_out_chunk = BignumToBytes(&planum[j],out_chunk_size,outptr);
         if(out_bytes=WhetherGoodMsg(outptr,bytes_this_out_chunk)) gotmsg = 1;
      }
      if(!gotmsg) {
         return(0);
      }
      inptr += in_chunk_size;
      outptr += out_bytes;
      total_bytes_out += out_bytes;
   }
   return(total_bytes_out);
}

/*--- function HexToBin ---------------------------------------------------
 *
 *  Converts a string of (an even number of) hex digits to binary.
 *
 *  Entry    hex      is a string of hex characters, upper or lower case.
 *                    Spaces are to be ignored.
 *                    Conversion stops at the first non-space that
 *                    is a non-hex digit.
 *           maxbytes is the amount of space set aside in "bin".
 *
 *  Exit     bin    is an array of binary bytes.
 *           Returns the number of bytes converted if successful, else 0
 *              (if the number of hex digits is odd).
 */
int
HexToBin(hex,maxbytes,bin)
char *hex;
int maxbytes;
unsigned char *bin;
{
   char ch, looping=1, upper=1;
   unsigned char val;
   int nbytes = 0;


   for(; nbytes<maxbytes; hex++) {
      ch = *hex;
      if(ch == ' ') continue;
      if(islower(ch)) ch = (char)toupper(ch);
      if(isdigit(ch)) {
         val = (unsigned char) (ch - '0');
      } else if(ch>='A' && ch<='F') {
         val = (unsigned char)(ch - 'A' + 10);

      /* End of hex digits--time to bail out.
       */
      } else {
         return (upper ? nbytes : 0);
      }

      /* If this is an upper digit, set the top 4 bits of the destination
       * byte with this value, else -OR- in the value.
       */
      if(upper) {
         *bin = (unsigned char) (val << 4);
         upper = 0;
      } else {
         *(bin++) |= val;
         upper = 1;
         nbytes++;  /* Increment only after byte has been filled. */
      }
   }
   return (nbytes);
}


/*--- function BinToHex --------------------------------------------------
 *
 *  Convert an array of bytes to hexadecimal.
 *
 *  Entry   bin      is an array of bytes.
 *          nbytes   is the number of bytes in the array.
 *
 *  Exit    hex      is a string of hexadecimal digits, zero-terminated.
 */
void
BinToHex(bin,nbytes,hex)
unsigned char *bin;
int nbytes;
char *hex;
{
#define btoh(byte) (byte<10 ? byte+'0' : byte-10+'A')

   unsigned char byte;

   while(nbytes--) {
      byte = (unsigned char)((*(bin)>>4) & 0xf);
      *(hex++) = (char) btoh(byte);
      byte = (unsigned char) (*(bin++) & 0xf);
      *(hex++) = (char) btoh(byte);
   }
   *hex = '\0';
}

/*--- function ExpandBytes -----------------------------------------------
 *
 *  Fill a target array with bytes from source array.  Make sure
 *  that the entire target array is filled, even if we have
 *  to starting repeating bytes from the beginning of the source array.
 *  Use all of the bytes in the source array, even if we have to
 *  start XORing bytes into the beginning of the target array.
 *
 *  This function is used to help expand pseudo-random data.
 *
 *  Entry:  source   is a string of bytes.
 *          nsour    is the number of bytes in source.
 *          ntarg    is the size of the buffer "target".
 *
 *  Exit:   target   has been filled with the bytes from source.
 *                   All bytes from "source" have been used, and
 *                   all positions in "target" have been filled.
 */
void
ExpandBytes(source,nsour,target,ntarg)
unsigned char *source;
int nsour;
unsigned char *target;
int ntarg;
{
   int j;

   for(j=0; j<ntarg; j++) target[j] = 0;

   for(j=0; j<nsour || j<ntarg; j++) {
      target[j%ntarg] ^= source[j%nsour];
   }
}

/*--- function RandomizeBytes -------------------------------------------
 *
 *  Given an array of pseudo-random bytes, create another array
 *  with the bytes (and bits) thoroughly garbled.
 *  Similar to ExpandBytes, but scrambles the bytes in a much
 *  less predictable way.
 *
 *  Entry:  source   is a string of bytes.
 *          nsour    is the number of bytes in source.
 *          ntarg    is the size of the buffer "target".
 *
 *  Exit:   target   has been filled with the bytes from source.
 *                   All bytes from "source" have been used, and
 *                   all positions in "target" have been filled.
 */
void
RandomizeBytes(source,nsour,target,ntarg)
unsigned char *source;
int nsour;
unsigned char *target;
int ntarg;
{
   unsigned char mykey[NEWDES_USER_KEY_BYTES];
   TypCipherInfo cipinfo;
   int jkey;

   ExpandBytes(source,nsour,mykey,NEWDES_USER_KEY_BYTES);
   ExpandBytes(source,nsour,target,ntarg);

   cipinfo.algorithm = newdes;
   cipinfo.blockmode = ecb;
   BlockInit(&cipinfo);
   BlockSetKey(mykey,NEWDES_USER_KEY_BYTES,1,&cipinfo);

   for(jkey=0; jkey<3; jkey++) BlockEncipherBlock(target,&cipinfo);

   for(jkey=0; jkey<=(ntarg-NEWDES_BLOCK_BYTES); jkey++) {
      BlockEncipherBlock(target+jkey,&cipinfo);
   }
   for(jkey=ntarg-NEWDES_BLOCK_BYTES; jkey>0; jkey--) {
      BlockEncipherBlock(target+jkey,&cipinfo);
   }
   BlockFinish(&cipinfo);
}

/*--- function ConcatArrays ----------------------------------------
 *
 *  Concatenates a number of arrays into one big array.
 *  I'm not really happy with this routine, but I can't think
 *  of a better calling sequence.
 *
 *  Entry:  arrays    is an array of pointers to arrays.
 *          lengths   is an array of lengths of each of the above arrays.
 *          narray    is the number of arrays in "arrays" and "lengths".
 *          targlimit is the size of the "targ" buffer.
 *
 *  Exit:   targ      contains all the arrays in "arrays", concatenated.
 *          Returns the number of bytes in "targ"; guaranteed to be
 *            no more than "targlimit".
 */
int
ConcatArrays(arrays,lengths,narray,targ,targlimit)
unsigned char **arrays;
int lengths[];
int narray;
unsigned char *targ;
int targlimit;
{
   int iarray, itarg=0, j;

   for(iarray=0; iarray<narray; iarray++) {
      for(j=0; j<lengths[iarray] && itarg<targlimit; j++) {
         targ[itarg++] = arrays[iarray][j];
      }
   }
   return (itarg);
}


/*--- function GetEncodedBytes ------------------------------------------
 *
 *  Read a series of lines of RFC 1113 printable encoded bytes
 *  and decode them all into a big buffer.
 *  Each line must start with a space; the function stops reading
 *  when it hits a line that doesn't start with a space.
 *
 *  Entry:  stream    is a stream to read from.
 *          maxbytes  is the size of the buffer "bytes".
 *
 *  Exit:   bytes     contains the bytes read in and decoded from
 *                    the stream "stream".
 */
int
GetEncodedBytes(stream,bytes,maxbytes)
FILE *stream;
unsigned char *bytes;
int maxbytes;
{
#define LINEBUFSIZE 100
   char line[LINEBUFSIZE];
   unsigned char *bptr=bytes;
   int ch, nbytes=0, nbyline, reading=TRUE;

   do {
      ch = fgetc(stream);
      if(ch==' ' || ch=='\t') {
         if(fgets(line,LINEBUFSIZE,stream)) {
            nbyline = prdecode(line,bptr,maxbytes);
            bptr += nbyline;
            maxbytes -= nbyline;
         } else {
            reading = FALSE;
         }
      } else {
         ungetc(ch,stream);
         reading = FALSE;
      }
   } while(reading);
   return(bptr-bytes);
}

/*--- function PutEncodedBytes -------------------------------------------
 *
 *  Given a buffer of bytes, encode them to printable form as
 *  per RFC 1113, and write them to a stream.  Intended for writing
 *  out message keys in message headers and the like; each line
 *  starts with a space to indicate continuation.  Not intended
 *  for writing out large messages.
 *
 *  Entry:  stream    is an output stream.
 *          bytes     is the input buffer.
 *          nbytes    is the number of bytes to encode and write out.
 *
 *  Exit:   The bytes have been encoded & written out, with one
 *            leading space per line.
 */
void
PutEncodedBytes(stream,bytes,nbytes)
FILE *stream;
unsigned char *bytes;
int nbytes;
{
   char line[LINEBUFSIZE];
   int chunk, bytescoded;

   line[0] = ' ';
   for(; nbytes > 0; nbytes -= PRENCODE_BYTES, bytes += chunk) {
      chunk = nbytes < PRENCODE_BYTES ? nbytes : PRENCODE_BYTES;
      bytescoded = prencode(bytes,chunk,line+1);
      fputs(line,stream); fputc('\n',stream);
   }
}


#if 0
/*--- function WilliamsE --------------------------------------------------
 *
 *  This computes Williams' "E1" function, used to help find the
 *  correct square root of a quadratic residue modulo the product
 *  of a pair of primes.  (There are four candidates.)
 *
 *  I wound up not using this; see WilliamsD below.
 *
 *  Entry   m        is a BIGINT message
 *          r        is a BIGINT product of two primes.
 *
 *  Exit    e        is a BIGINT function of m and modulus
 *                   that is intended to make it easier to
 *                   uniquely determine the square root of m^2 mod modulus
 *
 *                   e = 4(2m+1) when jacobi(2m+1,r) == 1
 *                     = 2(2m+1) when jacobi(2m+1,r) == -1
 *                     = undefined in the very unlikely event that
 *                       jacobi(2m+1,r) == 0
 *                       This will never happen in my implementation
 *                       of the Rabin system.
 */
void
WilliamsE(m,r,e)
BIGINT *m;
BIGINT *r;
BIGINT *e;
{
      int jac;

      zadd(m,m,e);                /* e = 2m */
      zsadd(e,(MYINT)1,e);        /* e = 2m+1   */
      jac = zjacobi(e,r);
      printf("Jacobi(2M+1|R) = %d\n",jac);
      zadd(e,e,e);                /* e = 2(2M+1) */
      if(jac == 1) {
         zadd(e,e,e);             /* e = 4(2M+1) */
      }

}

/*--- funciton WilliamsD ---------------------------------------------
 *
 *  Compute Williams' "D1" function.
 *
 *  This is used in H. C. Williams' modification of the RSA encryption
 *  system, as explained in  IEEE Transactions on Information Theory,
 *  Volume IT-26, No 6, November 1980.
 *  I wound up not using it because, despite what others say, I
 *  thought that Williams' system is more similar to the proprietary
 *  RSA system than to the public-domain Rabin system, and I didn't
 *  want to risk getting involved in a patent dispute.
 *
 *  Entry   L
 *          r
 *
 *  Exit    d
 */
void
WilliamsD(L,r,d)
BIGINT *L;
BIGINT *r;
BIGINT *d;
{
   switch(3 &  zlowbits(L)) {
      case 0:
         zsdiv(L,(MYINT)4,d);    /* d =  L/4       */
         zsadd(d,(MYINT)-1,d);   /* d =  L/4-1     */
         zsdiv(d,(MYINT)2,d);    /* d = (L/4-1)/2  */
         break;

      case 1:
         zsub(r,L,d);            /* d =   r-L         */
         zsdiv(d,(MYINT)4,d);    /* d =  (r-L)/4      */
         zsadd(d,(MYINT)-1,d);   /* d =  (r-L)/4-1    */
         zsdiv(d,(MYINT)2,d);    /* d = ((r-L)/4-1)/2 */
         break;

      case 2:
         zsdiv(L,(MYINT)2,d);    /* d =  L/2          */
         zsadd(d,(MYINT)-1,d);   /* d =  L/2-1        */
         zsdiv(d,(MYINT)2,d);    /* d = (L/2-1)/2     */
         break;

      case 3:
         zsub(r,L,d);            /* d =   r-L         */
         zsdiv(d,(MYINT)2,d);    /* d =  (r-L)/2      */
         zsadd(d,(MYINT)-1,d);   /* d =  (r-L)/2-1    */
         zsdiv(d,(MYINT)2,d);    /* d = ((r-L)/2-1)/2 */
         break;
   }
}
#endif


/* Here are some pseudo-random routines that I can't bear to
 * delete.  Keep or discard as you choose.
 */
#if 0
/*--- function myzrstart ---------------------------------------------
 *
 *  Initialize the random number generator.
 *
 *    Entry    s       is the seed
 *             biggest is the biggest number we'll ever want to
 *                     get out of this generator.
 *
 *    Exit     myzseed    is now s.
 *             myzranmod  is the random number modulus.  It is of the form
 *                        2^n - 1 and is >= biggest.
 *             myzranmult is the multiplier used.  It is of the form
 *                        3^m mod myzranmod, where m>=n (same n as above)
 *                        and where myzranmult and myzranmod are
 *                        relatively prime.
 *
 *   Mark Riordan  18 August 1990
 */
void
myzrstart(s,biggest)
MYINT s;
BIGINT *biggest;
{ register MYINT i, ones;
  long e[2];
  MYINT domore = 1;

  zintoz(s,myzseed);

  /* Make myzranmod the biggest number representable in the same
   * number of bits as "biggest" (using our encoding of bignums).
   * This will be a number with all 1 bits; hence, 2^n - 1
   */
  ones = myzranmod[0] = biggest[0];
  for (i=1; i<=ones; i++) myzranmod[i] = RADIXM;

  /* e = some number slightly > log2(myzranmod) */
  e[0] = 1; e[1] = ones*NBITS;

  do {
     zintoz((long)3,myzranmult);
     /* Compute  myzranmult = 3 ^ e mod myzranmod */
     zexp(myzranmult,e,myzranmod,myzranmult);
     domore = zinv(myzranmult,myzranmod,z_temp);
     if(domore) zadd(myzranmod,z_two,myzranmod);
  } while(domore);
}

/*--- function myzrandom -------------------------------------------
 *
 *   Returns the next random number in a sequence.
 *
 *    Entry    bound is the upper bound--numbers returned are
 *                   in the range 0 to bound-1.
 *
 *    Exit     zran  is the random number.
 *
 *    Uses a linear congruential random number generator--good only
 *    for mathematical purposes.
 */
void
myzrandom(bound,zran)
BIGINT *bound;
BIGINT *zran;
{
  if (myzseed[0] < 0) zhalt((long)5);
  /* Compute  seed = seed*mult  mod  ranmod  */
  zmulmod(myzseed,myzranmult,myzranmod,myzseed);
  /* Compute  result = seed mod bound */
  zmod(myzseed,bound,zran);
}

#endif


/* Here are a couple of routines useful for timing individual
 * operations--mostly for debugging and benchmarking.
 */
#ifdef DEBUG

#ifdef MSDOS
#define TYPTIME clock_t

#else
#include <sys/time.h>
#include <sys/resource.h>
#define TYPTIME struct rusage
#endif


void
display_time(begtime,endtime,msg)
double begtime,endtime;
char *msg;
{
   double elapsed;

   elapsed = endtime - begtime;
   printf("\nElapsed time for %s: %8.3lf sec\n",msg,elapsed);

}

double
my_get_time()
{
#ifdef MSDOS
   clock_t myclock;

   myclock = clock();

   return((double) (myclock/(double)CLOCKS_PER_SEC));
#else
#define RUSAGE_SELF 0
   struct rusage ruse;
   double mytime;

   getrusage(RUSAGE_SELF,&ruse);
   mytime = (double)ruse.ru_utime.tv_sec +
      (double)ruse.ru_utime.tv_usec/(double)1000000.0;
   return(mytime);
#endif
}

#endif
