/***************************** desmods *************************
 * DESMODS: encrypt/decrypt bytes using the NBS DES algorithm.
 * Programmed by R.W.Outerbridge; uses Jim Gillogly's DES.
 *
 * Usage: des (+|-)([ecb]|<cbc|cks>) key1 <ivec>
 *              EN/DE   MODES           KEYS
 *
 *      + :     ENcrypt (default if MODE specified)
 *      - :     DEcrypt (presumes encrypted input)
 *
 *      Modes of Operation (choose ONE):
 *
 *      ecb : (default) Electronic Code Book.  Only uses one key.
 *              If simply "+" or "-" is specified, ecb is used.
 *      cbc : Cipher Block Chaining.  Uses two keys.
 *      cks : ChecKSum.  Generates a 64-bit checksum using two keys.
 *
 *      Both keys may be as long as you wish.  The practical limit
 *      on keylength is the size of your system's argument buffer.
 *      WARNING: on some machines all arguments become CAPITALS.
 *      WARNING: non-ascii machines MAY get different results.
 *
 *      Any character may be used in keys - depending on the O/S -
 *      except ASCII NULL (0x00).  The one letter key '#', when used 
 *      for "key1", will cause desmods to use a preset default key
 *      (for verification and testing).  Failing to specify "ivec", if
 *      required, will result in "key1" being used for both keys.  It
 *      is an error to omit "key1".  There is no provision for giving
 *      arbitrary, absolute, bit-valued keys.
 *
 *      As painful as they are to use, long keys are MUCH safer;
 *      think up nonsense phrases you can safely remember.
 *
 * 8501.29 : Default key ['#'] not erased from command line.  rwo
 */

#include <stdio.h>
#define toascii(a)      ((a)&0177)
#define EN      0
#define DE      1
#define CKS     2
#define MODS    6       /* three modes times two cases  */
typedef char    BYTE;   /* BYTE = (vax) ? int : char    */
        
/* cryptographic declarations   */
void copy8(), xor8(), getkey();
BYTE Block[8], Link[8], Temp[8], IV[8];
BYTE DFLTKY[8] = { 1,35,69,103,137,171,205,239 };
        /* DO NOT ALTER! => 0x0123456789abcdef <=       */

extern void desinit(), kinit(), des();

/* I/O declarations     */
void ruderr(), put8(), vraiput(), initio();
int IOedf, End, Once;
BYTE Last[8];

int Ecb(), Cbc(), Cks();
struct modes {
        char *name;
        int (*func)();
        };
struct modes ModsOp[MODS] = {   /* Must be CAPs for CP/M        */
        { "ecb", Ecb },
        { "ECB", Ecb },
        { "cbc", Cbc },
        { "CBC", Cbc },
        { "cks", Cks },
        { "CKS", Cks }  };

main(argc, argv)
int argc;
char **argv;
        {
        int (*xeqtr)();
        int step, ende, edio, ok, i;
        BYTE kv[8];

        argv++; argc--;
        if(argc > 3 || argc < 2) ruderr();
        else desinit();

        for(step=0; argc > 0; step++) {
                switch(step) {
                case 0:         /* set en/de and/or default mode        */
                        if(*argv[0] == '+' || *argv[0] == '-') {
                                ende = (*argv[0] == '+') ? EN : DE;
                                *argv[0]++ = NULL;
                                if(*argv[0] == NULL) {
                                        xeqtr = Ecb;    /* default mode */
                                        edio = ende;
                                        argv++; argc--;
                                        break;
                                        }
                                }
                        else ende = EN;

                        for(i=ok=0; i < MODS && !ok; i++) {
                                if(strcmp(argv[0], ModsOp[i].name) == 0) {
                                        xeqtr = ModsOp[i].func;
                                        ok = 1;
                                        }
                                }
                        if(!ok) {
                                fprintf(stderr,
                                        "Des: unknown mode >%s<.\n", argv[0]);
                                ruderr();
                                }
                        while(*argv[0]) *argv[0]++ = NULL;
                        argv++; argc--;

                        /* set appropriate IO modes     */
                        if(xeqtr == Cks) edio = CKS;
                        else edio = ende;

                /* falling through....  */
                case 1: /* get the key and IV, if needed and present    */
                        if(strcmp(argv[0], "#") == 0) {
                                copy8(DFLTKY, kv);
                                *argv[0] = NULL;
                                }
                        else getkey(argv[0], kv);
                        argv++; argc--;
                        
                        /* if nothing left, but an IV needed, use the key */
                        if(argc == 0) {
                                if(xeqtr != Ecb) copy8(kv, IV);
                                break;
                                }
                        else if(xeqtr == Ecb) {
                                fprintf(stderr, "Des: ivec ignored.\n");
                                while(*argv[0]) *argv[0]++ = NULL;
                                argv++; argc--;
                                break;
                                }

                        else getkey(argv[0], IV);
                        argv++; argc--;
                        break;

                default:
                        fprintf(stderr, "Des: Programming error!\n");
                        exit(1);
                        break;
                        }       /* switch       */
                }       /* argument parsing     */

        kinit(kv, ende);
        initio(edio);
        (*xeqtr)(ende); /* ta-da!       Take it away xeqtr!  */
        exit(0);
        }       /* end of main  */

void ruderr() {
        fprintf(stderr, "Usage: des (+|-)([ecb]|<cbc|cks>) key1 <ivec>\n");
        exit(1);
        }

Cbc(e_d)        /* Cipher Block Chaining                */
int e_d;        /* Ciphertext errors are self-healing.  */
        {
        copy8(IV, Link);
        while(get8(Block) != EOF) {
                if(e_d == DE) {
                        des(Block, Temp);
                        xor8(Temp, Link);
                        copy8(Block, Link);
                        put8(Temp);
                        }
                else {
                        xor8(Block, Link);
                        des(Block, Link);
                        put8(Link);
                        }
                }
        return;
        }

Cks(dummy)      /* CBC authentication checksum generator        */
int dummy;      /* The banks use this for verifications.        */
        {
        int i, j, k;
        long count = 0L;
        copy8(IV, Link);
        while(get8(Block) != EOF) {
                xor8(Block, Link);
                des(Block, Link);
                count++;
                }
        fprintf(stdout, ": %0ld bytes\t: ", count<<3);
        for(i=j=0; i < 2; i++) {
                for(k=0; k < 4; k++, j++)
                        fprintf(stdout, "%02x", Link[j]&0377);
                putc(' ', stdout);
                }
        fprintf(stdout, ":\n");
        return;
        }

Ecb(dummy)      /* Electronic Code Book : simple substitution   */
int dummy;      /* Yawn.        For static data and random access.      */
        {
        while(get8(Block) != EOF) {
                des(Block, Block);
                put8(Block);
                }
        return;
        }

void copy8(from, to)
register BYTE *from, *to;
        {
        register BYTE *ep;
        ep = &to[8];
        while(to < ep) *to++ = *from++;
        return;
        }

void xor8(to, with)
register BYTE *to, *with;
        {
        register BYTE *ep;
        ep = &to[8];
        while(to < ep) *to++ ^= *with++;
        return;
        }

void put8(block)
register BYTE *block;
        {
        if(IOedf == DE) copy8(block, Last);
        else vraiput(block, &block[8]);
        return;
        }

get8(input)     /* KLUDGE: incomplete blocks handled sleazily   */
register BYTE *input;
        {
        register int i, j;
        if(End == 1) return(EOF);       /* no more input        */

        for(i=0; i < 8 && ((j = getc(stdin)) != EOF); i++) *input++ = j;

        if(IOedf == DE) {       /* DECRYPTION   */
                /* complete block?      pending output? */
                if(i == 8 && (Once > 0)) vraiput(Last, &Last[8]);
                else if(j == EOF) {
                        End = 1;
                        if(Once > 0) {
                                /* incomplete block means no nulls      */
                                if(i != 0) i = 0;
                                else {  
                                        i = Last[7]&0377;
                                        if(i > 8) i = 0;        /* huh? */
                                        }
                                vraiput(Last, &Last[8-i]);
                                }
                        return(EOF);
                        }
                }
        else if(j == EOF) {     /* ENCRYPTION   */
                End = 1;
                if(i == 0 && (IOedf == EN || (Once > 0))) {
                        /* if no padding to do, print a kludge  */
                        if(IOedf == EN && (Once > 0)) putc('0', stdout);
                        return(EOF);
                        }
                for(j=i; j < 7; j++) *input++ = NULL;
                *input = 8-i;
                }
        Once = 1;
        return(0);
        }

void vraiput(cp, ep)
register BYTE *cp, *ep;
        {
        while(cp < ep) putc((char)*cp++, stdout);
        return;
        }

void initio(edf)        /* use this as a hook on systems without UNIX   */
int edf;
        {
        IOedf = edf;
        End = Once = 0;
        return;
        }

void getkey(aptr, kptr)
register char *aptr;
register BYTE *kptr;
        {
        register BYTE *store;
        register int i, first;
        BYTE hold[8];
        store = kptr;
        first = 1;
        kinit(DFLTKY, EN);
        copy8(DFLTKY, hold);
        while(*aptr || first) {
                kptr = store;
                for(i=0; i<8 && (*aptr != NULL); i++) {
                        *kptr++ = toascii(*aptr);
                        *aptr++ = NULL;
                        }
                while(i++ < 8) *kptr++ = NULL;
                xor8(store, hold);
                des(store, hold);
                first = 0;
                }
        copy8(hold, store); 
        return;
        }

/* des cks # < /dev/null 
 *      : 8 bytes       : c9574425 6a5ed31d :
 *      : (-DKSX64)     : acdcebea eb77deaa :
 *                      (rwo/8502.16.18:30/V1.2)                */
/************************* desmods ******************************/
