/********************************************************************
* lindner
* 3.18
* 1995/02/11 06:19:25
* /home/arcwelder/GopherSrc/CVS/gopher+/gopherd/authenticate.c,v
* Exp
*
* Paul Lindner, University of Minnesota CIS.
*
* Copyright 1991, 1992, 1993 by the Regents of the University of Minnesota
* see the file "Copyright" in the distribution for conditions of use.
*********************************************************************
* MODULE: authenticate.c
* Routines to do encryption and decryption, and validate tickets
*********************************************************************
* Revision History:
* authenticate.c,v
* Revision 3.18  1995/02/11  06:19:25  lindner
* allocate proper amount of space for des routines
*
* Revision 3.17  1995/02/07  07:02:23  lindner
* performance fixes
*
* Revision 3.16  1994/10/13  05:17:47  lindner
* Compiler complaint fixes
*
* Revision 3.15  1994/07/22  22:28:01  lindner
* NO_AUTHENTICATION mods
*
* Revision 3.14  1994/07/19  20:23:17  lindner
* Remove unused vars
*
* Revision 3.13  1994/06/29  05:39:01  lindner
* Add AUTH, remove admit1
*
* Revision 3.12  1994/02/20  16:51:57  lindner
* Don't compile authentication stuff unless needed
*
* Revision 3.11  1993/11/02  05:55:48  lindner
* Fix for interactive unix
*
* Revision 3.10  1993/09/18  03:28:05  lindner
* Fix for Abort() fcn declaration
*
* Revision 3.9  1993/09/11  05:05:08  lindner
* compiler warning fix
*
* Revision 3.8  1993/07/29  20:12:18  lindner
* Removed dead variables
*
* Revision 3.7  1993/07/27  05:27:39  lindner
* Mondo Debug overhaul from Mitra
*
* Revision 3.6  1993/07/07  19:34:17  lindner
* renamed encrypt() to UMNDESencrypt()
*
* Revision 3.5  1993/04/10  06:07:17  lindner
* none
*
* Revision 3.4  1993/04/09  16:51:54  lindner
* Little mods for combined public gopher and admit1 gopher.
*
* Revision 3.3  1993/04/07  05:16:57  lindner
* Unhid changes for Admit1 stuff.
*
* Revision 3.1  1993/03/19  20:02:13  lindner
* DES and ticket routines
*
*
*********************************************************************/

#include "String.h"
#include <stdio.h>

#include "gopherd.h"
#include "command.h"

/* -------------------------------------------------
*
* Sofware DES functions 12 Dec 1986 by Phil Karn, KA9Q;
* large sections from 1977 public-domain program by Jim Gillogly
* Modified by George Gonzalez to not do initial/final permutations
* Further bludgeoned by Farhad Anklesaria for AdmitOne authentication.
*
---------------------------------------------------- */

#ifndef NO_AUTHENTICATION

char* GDESencrypt();
char* GDESdecrypt();

typedef unsigned int Desnum;


#define  SLEN 255  /* Generic small buffer length */
#include "Debug.h"

static int IntelOrder = 1;     /*Hacked out.  fxa*/

/* Tables defined in the Data Encryption Standard documents */
/* permuted choice table (key) */
static char pc1[] = {
    57, 49, 41, 33, 25, 17,  9,  1, 58, 50, 42, 34, 26, 18,
    10,  2, 59, 51, 43, 35, 27, 19, 11,  3, 60, 52, 44, 36,

    63, 55, 47, 39, 31, 23, 15,  7, 62, 54, 46, 38, 30, 22,
    14,  6, 61, 53, 45, 37, 29, 21, 13,  5, 28, 20, 12,  4
    };

/* number left rotations of pc1 */
static char totrot[] = { 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 };

/* permuted choice key (table) */
static char pc2[] = {
    14, 17, 11, 24,  1,  5,  3, 28, 15,  6, 21, 10,
    23, 19, 12,  4, 26,  8, 16,  7, 27, 20, 13,  2,
    41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
    44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
    };

/* The (in)famous S-boxes */
static char si[8][64] = {
    /* S1 */
    14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7,
    0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8,
    4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0,
    15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13,

    /* S2 */
    15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10,
    3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5,
    0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15,
    13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9,

    /* S3 */
    10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8,
    13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1,
    13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7,
    1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12,

    /* S4 */
    7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15,
    13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9,
    10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4,
    3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14,

    /* S5 */
    2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9,
    14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6,
    4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14,
    11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3,

    /* S6 */
    12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11,
    10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8,
    9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6,
    4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13,

    /* S7 */
    4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1,
    13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6,
    1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2,
    6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12,

    /* S8 */
    13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7,
    1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2,
    7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8,
    2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11
    };

/* 32-bit permutation  P used on the output of the S-boxes */
static char p32i[] = {
    16,  7, 20, 21, 29, 12, 28, 17,  1, 15, 23, 26,  5, 18, 31, 10,
    2,  8, 24, 14, 32, 27,  3,  9, 19, 13, 30,  6, 22, 11,  4, 25
    };

/* Lookup tables initialized once only at startup by desinit() */



/* bit 0 == left-most in byte */
static short bytebit[] = { 0200,0100,0040,0020,0010,0004,0002,0001 };


static unsigned char (*kn)[8];
static int (*spbox)[64]; /* Combined S && P boxes */

void
DESAbort()
{
#ifndef VMS_SERVER
    exit(-1);
#else
    gopherd_exit(-1);
#endif
}

#ifdef DES_TEST
main()
{
    Desnum bkey[3];
    char *entext;
    DEBUG =1;

    entext = GDESencrypt("lindner", "156.106.128.92", "uglyduck", "lindner");

    desdone();
    desinit();
    GDESdecrypt("lindner", "156.106.128.92", "uglyduck", entext);

}
#endif


/*--------------------------------*/
/* Initialize the lookup table for the combined S && P boxes */
void
spinit()
{
    char pbox[ 32 ];
    short p, i, s, j, t, rowcol;
    int val;

    for ( p = 1; p <= 32; p++ ) {
         for ( i = 0; i <= 31; i++ ) {
              if ( p32i[i] == p ) {
                   pbox[p - 1] = 31 - i;
                   break;
              }
         }
    }
    for ( s = 0; s <= 7; s++ ) {
         for ( i = 0; i <= 63; i++ ) {
              val = 0;
              rowcol = (i & 32) | ((i & 1) ? 16 : 0) | ((i >> 1) & 0xf);
              t = si[s][rowcol];
              for ( j = 0; j <= 3; j++ ) {
                   if ( t & ( 8 >> j ) ) {
                        val |= 1L << pbox[ ( s << 2 ) + j ];
                   }
              }
              spbox[s][i] = val;
         }
    }
}


/*--------------------------------*/
/* Allocate space && initialize DES lookup arrays
* mode == 1: DEA without initial && final permutations for speed
*/

int
desinit()
{
    if ( spbox != 0 ) return 0;

    spbox = ( int (*)[64] ) malloc( sizeof( int ) * 8 * 64);
    if ( spbox != 0) {
         spinit();
         kn = (unsigned char (*)[8]) malloc( sizeof( char ) * 8 * 16);
         if ( kn == 0 ) {
              free( (char *) spbox );
              spbox = 0;
         }
    }
    if ( spbox == 0 || kn == 0 ) {
         return -1;
    } else {
         return 0;
    }
}

/*--------------------------------*/
/* Free up storage used by DES */

void
desdone()
{
    if (spbox != 0) {
         free( (char *) spbox );
         free( (char *) kn );
         spbox = 0;
         kn = 0;
    }
}


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

void setdeskey( keynum )
 Desnum* keynum;
{
    unsigned char key[9];
    unsigned char pc1m[56];  /* place to modify pc1 into */
    unsigned char pcr[56];  /* place to rotate pc1 into */
    register short i, j, k;

    for (i=0,j=3; i<4; i++)
         key[j--] = (keynum[0] >> (i*8)) & 0xFF;
    for (i=0,j=7; i<4; i++)
         key[j--] = (keynum[1] >> (i*8)) & 0xFF;

    /* Clear key schedule */
    for (i=0; i<16; i++) {
         for (j=0; j<8; j++) {
              kn[i][j]=0;
         }
    }

    for (j=0; j<56; j++) {  /* convert pc1 to bits of key */
         k = pc1[j] - 1;
         pc1m[j] = ( key[ k >> 3 ]  & bytebit[ k & 07 ]) ? 1 : 0;
    }

    for (i=0; i<16; i++) {  /* key chunk for each iteration */
         for (j=0; j<56; j++) { /* rotate pc1 the right amount */
              k = j + totrot[i];
              pcr[j] = pc1m[ k < ( j < 28 ? 28 : 56 ) ? k : k - 28 ];
         }
         for (j=0; j<48; j++) { /* select bits individually */
              if ( pcr[ pc2[ j ] - 1 ] ) {
                   kn[i] [j/6] |= bytebit[j % 6] >> 2;
              }
         }
    }
}


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

void byteswap( x )
 Desnum * x;
{
    register char * cp;
    register char tmp;

    cp = (char *) x;
    tmp = cp[3];
    cp[3] = cp[0];
    cp[0] = tmp;

    tmp = cp[2];
    cp[2] = cp[1];
    cp[1] = tmp;
}

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

void mv( in, out )
 Desnum *in, *out;
{
    out[0] = in[0];
    out[1] = in[1];

    if ( IntelOrder ) {
         byteswap( (Desnum * ) &out[0] );
         byteswap( (Desnum * ) &out[1] );
    }
}


/*--------------------------------*/
/* The nonlinear  f(r,k), the heart of DES */

int f( r, subkey )
 Desnum r;
 unsigned char subkey[8];
{
    register Desnum rval, rt;

    /* Run E(R) ^ K through the combined S & P boxes
     * This ; takes advantage of a convenient regularity in
     * E, namely that each group of 6 bits in E(R) feeding
     * a single S-box == a contiguous segment of R.
     */
    rt = (r >> 1) | ((r & 1) ? 0x80000000 : 0);
    rval = 0;
    rval |= spbox[0][((rt >> 26) ^ *subkey++) & 0x3f];
    rval |= spbox[1][((rt >> 22) ^ *subkey++) & 0x3f];
    rval |= spbox[2][((rt >> 18) ^ *subkey++) & 0x3f];
    rval |= spbox[3][((rt >> 14) ^ *subkey++) & 0x3f];
    rval |= spbox[4][((rt >> 10) ^ *subkey++) & 0x3f];
    rval |= spbox[5][((rt >> 6) ^ *subkey++) & 0x3f];
    rval |= spbox[6][((rt >> 2) ^ *subkey++) & 0x3f];
    rt = (r << 1) | ((r & 0x80000000) ? 1 : 0);
    rval |= spbox[7][(rt ^ *subkey) & 0x3f];
    return rval;
}

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

void pass( num, block )
 short num;
 Desnum *block;
{
    short b;

    b = num & 1;
    block[ b ] ^= f( block[ 1 - b ], kn[ num ] );
}




/*--------------------------------*/
/* In-place UMN DES encryption of 64-bit block */

void endes(block)
 Desnum *block;
{
    register short i;
    Desnum work[4];   /* Working data storage */
    Desnum tmp;

    mv( block, work ); /* Initial Permutation */
    for (i=0; i<16; i++)
         pass( i, work );

    tmp = work[0];
    work[0] = work[1];
    work[1] = tmp;

    mv(work, block );
}



/*--------------------------------*/
/* In-place decryption of 64-bit block */
void dedes(block)
 Desnum * block;
{
    register short i;
    Desnum work[4]; /* Working data storage */
    Desnum tmp;
    ;
    mv( block, work ); /* Initial permutation */

    work[3] =

    /* Left/right half swap */
    tmp = work[0];
    work[0] = work[1];
    work[1] = tmp;

    /* Do the 16 rounds in reverse order */
    for ( i = 0; i <=15; i++ )
         pass( 15 - i, work );

    mv(work, block );
}

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

short  Hx( c )
 char c;
{
    if ( c >= '0' && c <= '9' ) {
         return( c - '0' );
    } else {
         if ( c >= 'A' && c <= 'F' )
              return( c - 'A' + 10 );
         else if ( c >= 'a' && c <= 'f' )
              return( c - 'a' + 10);
         else
              return( 0 );
    }
}


/*--------------------------------*/
/* Take a character string and convert it into two long ints
*/

void HexToBin( In, Out )
 char * In;
 Desnum Out[];
{
    short i;
    int bytes[8];


    for (i=0; i < 8; i++)
         bytes[i] = (Hx(In[i*2]) * 16 ) + Hx(In[i*2 + 1]);

    Out[0] = (bytes[0]*16777216) + (bytes[1]*65536) + (bytes[2]*256) + bytes[3];

    Out[1] = (bytes[4]*16777216) + (bytes[5]*65536) + (bytes[6]*256) + bytes[7];

    Debug("Output of HextoBin is %X", Out[0]);
    Debug("%X\n", Out[1]);

}


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

void
GDESkey(user, ip, key, bkey)
 char *user;
 char *ip;
 char *key;
 Desnum bkey[3];
{
    char          keystr[256];
    char         *cp;
    Desnum        c;
    int i;

    strcpy(keystr, user);
    strcat(keystr, ip);
    strcat(keystr, key);

    Debug("Encoding key %s\n", keystr);

    /** keystr contains our text key, encode it into a 64 bit # **/
    cp = keystr;

    bkey[0] = 0;
    bkey[1] = 0;

    while (*cp) {
         for (i=0; *cp && i < 4; i++) {
              c = *cp++;
              bkey[0] ^= (c << (i*8));
         }
         for (i=0; *cp && i < 4; i++) {
              c = *cp++;
              bkey[1] ^= (c << (i*8));
         }
    }


    Debug("Key is %lu", bkey[0]);
    Debug(" %lu\n", bkey[1]);
}

int
GDESpackText(str, packedtext)
 char *str;
 Desnum packedtext[];
{
    char *cp;
    int  longnum;
    int  i;

    cp      = str;
    longnum = 0;
    while (*cp) {
         packedtext[longnum] = 0;
         packedtext[longnum+1] = 0;

         for (i=3; i >=0 && *cp; i--) {
              packedtext[longnum] |= (*cp++ << (i*8));
         }
         longnum++;
    }

    return(longnum);
}

void
GDESunpackText(str, packedtext, numlongs)
 char          *str;
 Desnum  packedtext[];
 int            numlongs;
{
    char *cp;
    int  i, x;

    cp      = str;

    for (i=0; i< numlongs; i++) {
         for (x=3; x >= 0; x--) {

              *cp++ = (packedtext[i]>>(x*8)) & 0xFF;
         }
    }
    Debug("Unpacked String: %s\n", str);
}

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

char*
GDESencrypt(user, ip, key, cleartext)
 char *user;
 char *ip;
 char *key;
 char *cleartext;
{
    Desnum bkey[3];
    Desnum packedtext[10];
    int  longnum = 0;
    int  i;
    Desnum temp[3];
    char temptext[10];
    static char encoded[512];

    encoded[0] = '\0';
    for (i=0; i<10; i++) {
         packedtext[i] = 0;
    }

    GDESkey(user, ip, key, bkey);

    if ( desinit() != 0 )
         DESAbort("Error initializing DES.\n");

    longnum = GDESpackText(cleartext, packedtext);

    setdeskey((char*) bkey);

    for (i = 0; i < longnum; i+=2) {
         temp[0] = packedtext[i];
         temp[1] = packedtext[i+1];

         endes(temp);

         sprintf(temptext, "%.8X%.8X", temp[0] , temp[1]);
         strcat(encoded, temptext);
         Debug("Encoded: %s\n", temptext);
    }
    Debug("Encoded %s\n", encoded);

    return(encoded);
}


char *
GDESdecrypt(user, ip, key, codedtext)
 char *user;
 char *ip;
 char *key;
 char *codedtext;
{
    static char decoded[512];
    Desnum bkey[3];
    Desnum packedtext[10], temp[3];
    int           longnum = 0;
    char *cp;
    int i;

    decoded[0] = '\0';
    for (i=0; i<10; i++) {
         packedtext[i] = 0;
    }

    if ( desinit() != 0 )
         DESAbort("Error initializing DES.\n");

    GDESkey(user, ip, key, bkey);
    setdeskey(bkey);


    cp = codedtext;
    while (*cp) {
         HexToBin(cp, temp);
         dedes(temp);

         packedtext[longnum] = temp[0];
         packedtext[longnum+1] = temp[1];
         longnum+=2;
         cp += 16;
    }

    GDESunpackText(decoded, packedtext, longnum);

    return(decoded);
}
#endif