/****************************************************************************\
**      XFDES.C - FAST DES ENCRYPTION ALOGRITHM FUNCTIONS FOR TURBO-C       **
******************************************************************************
**             VAX/FDES Routines Modified By Doctor Dissector               **
******************************************************************************
** Copyright (c) 1991, By Doctor Dissector            Last Update: 03/04/91 **
\****************************************************************************/

/*
** XFDES - Version 1.05
** Copyright 1991 By Doctor Dissector
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 1, or (at your option)
** any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

char xfdes_c_msg[]="xfdes v1.05 - 4/3/91 - (c)1991 - filename: xfdes.c";

/*=[ Include Files ]========================================================*/

#include "xfdes.h"

/*=[ Global Variables ]=====================================================*/

/* The C and D arrays used to calculate the key schedule. */
static obpb1 C[28], D[28];

/* Key schedule. Alternating longs with low and high bits of key.
** Low and high 24 bits of keys are stored alternately.
*/
sbpb24 KS[32];

/* The current block, divided into 2 halves. */
obpb1 L[32], R[32];

/* Tables that combine the S and P operations. */
sbpb24 S0L[64], S1L[64], S2L[64], S3L[64],
       S4L[64], S5L[64], S6L[64], S7L[64],
       S0H[64], S1H[64], S2H[64], S3H[64],
       S4H[64], S5H[64], S6H[64], S7H[64];

/* It is slightly faster to indirect through this table than to specify
** the desired table directly.
*/
static sbpb24 *stt[] =
 {
  S0L, S0H,
  S1L, S1H,
  S2L, S2H,
  S3L, S3H,
  S4L, S4H,
  S5L, S5H,
  S6L, S6H,
  S7L, S7H,
 };

/*===========================================================================*/

/* Convert unsl in twenty-four bit contiguous format
** to six bits per byte format.  Return result.
*/
static sbpb24
tfTOsixbit(tf)
    ebpb24 tf;
{
    sbpb24 res=0;


    res |= (tf >> 0) & 077;
    res |= (((tf >> 6) & 077) << 8);
    res |= (((tf >> 12) & 077) << 16);
    return(res | (((tf >> 18) & 077) << 24));
}

/*===========================================================================*/

/* Convert unsl in six bits per byte format
** to twenty-four bit contiguous format.  Return result.
*/
static ebpb24
sixbitTOtf(sb)
    sbpb24 sb;
{
    ebpb24 res=0;


    res |= ((sb >> 0) & 077);
    res |= (((sb >> 8) & 077) << 6);
    res |= (((sb >> 16) & 077) << 12);
    return(res | (((sb >> 24) & 077) << 18));
}

/*===========================================================================*/

/* Set up the key schedule from the key. */
static void
fsetkey(key)
    obpb1 *key;
{
    reg int i, j, k;
    obpb1 s, t;


    /* First, generate C and D by permuting the key.  The low order bit of each
    ** 8-bit char is not used, so C and D are only 28 bits apiece.
    */
    for(i=0; i<28; i++) {
        C[i]=key[PC1_C[i]];
        D[i]=key[PC1_D[i]];
     }
    /* To generate Ki, rotate C and D according to schedule and pick up a
    ** permutation using PC2.
    */
    for(i=0; i<32; i+=2) {
        for(k=0 ; k<shifts[i/2]; k++) {     /* rotate */
            s=C[0];
            t=D[0];
            for(j=0; j<27; j++) {
                C[j]=C[j+1];
                D[j]=D[j+1];
             }
            C[27]=s;
            D[27]=t;
         }
        /* Get Ki. Note C and D are concatenated. */
        KS[i]=KS[i+1]=0;
        for(j=0; j<24; j++) {
            KS[i] |= ((sbpb24) C[PC2_C[j]] << j);
            KS[i+1] |= ((sbpb24) D[PC2_D[j]] << j);
         }
        KS[i]=tfTOsixbit(KS[i]);
        KS[i+1]=tfTOsixbit(KS[i+1]);
     }
}

/*===========================================================================*/

/* Lookup an S-box entry.*/
static fbpb4
lookupS(tableno, t6bits)
    unsl tableno;
    sbpb6R t6bits;
{
    sbpb6  fixed6bits;
    fbpb4R r;
    fbpb4  fixedr;


    fixed6bits=(((t6bits >> 0) & 01) << 5)+
               (((t6bits >> 1) & 01) << 3)+
               (((t6bits >> 2) & 01) << 2)+
               (((t6bits >> 3) & 01) << 1)+
               (((t6bits >> 4) & 01) << 0)+
               (((t6bits >> 5) & 01) << 4);
    r=(fbpb4)S[(int)tableno][(int)fixed6bits];
    fixedr=(((r >> 3) & 01) << 0)+
           (((r >> 2) & 01) << 1)+
           (((r >> 1) & 01) << 2)+
           (((r >> 0) & 01) << 3);
    return(fixedr);
}

/*===========================================================================*/

/* The payoff: encrypt a block. */
static void
xform(quarters, saltvalue)
    sbpb24 *quarters;
    sbpb24 saltvalue;
{
    reg    sbpb6  *dp;
    reg    int    mi;
    reg    sbpb24 *kp, *kend, k;
    static sbpb24 Dl, Dh;
           sbpb24 Rl, Rh, Ll, Lh, negsalt;


    negsalt = ~saltvalue;
    Ll=Lh=Rl=Rh=0;
    kend=&KS[32];
    for(mi=24; mi>-1; mi--) {
        for(kp=KS; kp<kend; ) {
            reg sbpb24 **stp;
            k=(Rl ^ Rh);
            k &= (~negsalt);
            Dl=(k ^( Rl ^ *kp++));
            Dh=(k ^( Rh ^ *kp++));
            stp=((sbpb24 **) stt);
            dp=((sbpb6 *) & Dl);
            Ll ^= (*stp++)[*dp];
            Lh ^= (*stp++)[*dp++];
            Ll ^= (*stp++)[*dp];
            Lh ^= (*stp++)[*dp++];
            Ll ^= (*stp++)[*dp];
            Lh ^= (*stp++)[*dp++];
            Ll ^= (*stp++)[*dp];
            Lh ^= (*stp++)[*dp++];
            dp=((sbpb6 *) & Dh);
            Ll ^= (*stp++)[*dp];
            Lh ^= (*stp++)[*dp++];
            Ll ^= (*stp++)[*dp];
            Lh ^= (*stp++)[*dp++];
            Ll ^= (*stp++)[*dp];
            Lh ^= (*stp++)[*dp++];
            Ll ^= (*stp++)[*dp];
            Lh ^= (*stp++)[*dp++];
            k=(Ll ^ Lh);
            k &= (~negsalt);
            Dl=(k ^ Ll ^ *kp++);
            Dh=(k ^ Lh ^ *kp++);
            stp=((sbpb24 **) stt);
            dp=((sbpb6 *) & Dl);
            Rl ^= (*stp++)[*dp];
            Rh ^= (*stp++)[*dp++];
            Rl ^= (*stp++)[*dp];
            Rh ^= (*stp++)[*dp++];
            Rl ^= (*stp++)[*dp];
            Rh ^= (*stp++)[*dp++];
            Rl ^= (*stp++)[*dp];
            Rh ^= (*stp++)[*dp++];
            dp=((sbpb6 *) & Dh);
            Rl ^= (*stp++)[*dp];
            Rh ^= (*stp++)[*dp++];
            Rl ^= (*stp++)[*dp];
            Rh ^= (*stp++)[*dp++];
            Rl ^= (*stp++)[*dp];
            Rh ^= (*stp++)[*dp++];
            Rl ^= (*stp++)[*dp];
            Rh ^= (*stp++)[*dp++];
         }
        Ll ^= Rl;
        Lh ^= Rh;
        Rl ^= Ll;
        Rh ^= Lh;
        Ll ^= Rl;
        Lh ^= Rh;
     }
    {
        reg sbpb24 *qp;


        qp=quarters;
        *qp++=Ll;
        *qp++=Lh;
        *qp++=Rl;
        *qp++=Rh;
     }
}

/*===========================================================================*/

/* Final permutation.  Takes input from globals L and R. */
static void
Fperm(block)
    unsb *block;
{
    reg int j, k;


    for(j=0; j<64; j++) {
        k=FP[j];
        block[j]=((k<32) ? L[k] : R[k-32]);
     }
}

/*===========================================================================*/

/* Inverse of E permutation. */
static void
undoe(fromarr, toarr)
    obpb1 *fromarr, *toarr;
{
    reg int j;


    for(j=0; j<32; j++)
        toarr[j]=fromarr[1+(j & 03)+6*(j >> 2)];
}

/*===========================================================================*/

static void
toBA64(quarters, onebits64)
    reg sbpb24 *quarters;
    obpb1 *onebits64;
{
           int    j;
    static obpb1  tmpE[48];
    reg    unsb   *onebits48;
    reg    sbpb24 quarter;


    onebits48=tmpE;
    quarter=sixbitTOtf(*quarters++);
    for(j=0; j<24; j++)
        *onebits48++=((quarter >> j) & 01);
    quarter=sixbitTOtf(*quarters++);
    for(j=0; j<24; j++)
        *onebits48++=((quarter >> j) & 01);
    undoe(tmpE,L);
    onebits48=tmpE;
    quarter=sixbitTOtf(*quarters++);
    for(j=0; j<24; j++)
        *onebits48++=((quarter >> j) & 01);
    quarter=sixbitTOtf(*quarters++);
    for(j=0; j<24; j++)
        *onebits48++=((quarter >> j) & 01);
    undoe(tmpE,R);
    Fperm(onebits64);
}

/*===========================================================================*/

static char *
fcrypt(pw, salt)
    const char *pw;
    char *salt;
{
    reg    int    i;
    reg    obpb1  j, c;
    static obpb1  block[66];
    static char   iobuf[16];
    static sbpb24 out96[4];
           sbpb24 saltvalue=0;


    for(i=0; i<66; i++)
        block[i]=0;
    for(i=0; ((c=*pw)!=NULL) && (i<64); pw++) {
        for(j=0; j<7; j++, i++)
            block[i]=((c >> (6-j)) & 01);
        i++;
     }
    fsetkey(block);
    for(i=0; i<2; i++) {
        c=iobuf[i]=*salt++;
        if (c>'Z')
            c-=6;
        if (c>'9')
            c-=7;
        c-='.';
        for(j=0; j<6; j++)
            saltvalue |= ((c >> j) & 01) << (6*i+j);
     }
    saltvalue=tfTOsixbit(saltvalue);
    xform(out96, saltvalue);
    toBA64(out96, block);
    for(i=0; i<11; i++) {
        c=0;
        for(j=0; j<6; j++) {
            c <<= 1;
            c |= block[6*i+j];
         }
        c+='.';
        if (c>'9')
            c+=7;
        if (c>'Z')
            c+=6;
        iobuf[i+2]=c;
     }
    iobuf[i+2]=0;
    if (!iobuf[1])
        iobuf[1]=iobuf[0];
    return(iobuf);
}

/*===========================================================================*/

static void
init(tableno, lowptr, highptr)
    unsl tableno;
    sbpb24 *lowptr, *highptr;
{
       reg    int k, i;
    static  obpb1 tmp32[32];
    static  obpb1 tmpP32[32];
    static  obpb1 tmpE[48];
           sbpb6R j;


    for(j=0; j<64; j++) {
        k=lookupS(tableno, j);
        for(i=0; i<32; i++)
            tmp32[i]=0;
        for(i=0; i<4; i++)
            tmp32[4*(int)tableno+i]=(obpb1)(k >> i) & 01;
        for(i=0; i<32; i++)
            tmpP32[i]=tmp32[P[i]-1];
        for(i=0; i<48; i++)
            tmpE[i]=tmpP32[(int)E[i]-1];
        lowptr[j]=highptr[j]=0;
        for(i=0; i<24; i++)
            lowptr[(int)j] |= (unsl)tmpE[i] << i;
        for(k=0, i=24; i<48; i++, k++)
            highptr[(int)j] |= (unsl)tmpE[i] << k;
        lowptr[j]=tfTOsixbit(lowptr[j]);
        highptr[j]=tfTOsixbit(highptr[j]);
     }
}

/*===========================================================================*/

static void
init_des(void)
{
    init((unsl)0, S0L, S0H);
    init((unsl)1, S1L, S1H);
    init((unsl)2, S2L, S2H);
    init((unsl)3, S3L, S3H);
    init((unsl)4, S4L, S4H);
    init((unsl)5, S5L, S5H);
    init((unsl)6, S6L, S6H);
    init((unsl)7, S7L, S7H);
}
