#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include "mini_des.h"

/* Burt Rosenberg (c) 1995
   Modified from RSAREF, (c) RSA Laboratories.

   A 3-round DES, without IP or IP^-1, using ECB.

   Usage: argv[0] [-d|-e|-x apply-word] [-k keyword-16-hex] 

   Update: 7 Feb 03
*/
   
DES_SHORT_CTX context ;
unsigned char key[8];
unsigned char word[8];

int hex_to_bytes(unsigned char *, int *, unsigned char *, int) ;
int bytes_to_hex(unsigned char *, int *, unsigned char *, int) ;
void print_N (char *, int) ;
int get_clear(char *) ;
int get_cipher(char *) ;
int get_16hex(char *, char *) ;
void print_usage(char **) ;

int main(int argc, char *argv[]){

   char input[100], output[100] ;
   char encodedBlock[100] ;
   int encodedBlockLen ;
   int i, strl ;
   int mode ;
   int keyok ;

   keyok = 0 ;

   /* setup defaults */
   /* process options on command line */
   i = 0 ;
   while ( (++i)<argc ) {
      if ( argv[i][0]!='-' ) print_usage(argv) ;
      switch (argv[i][1]) {
         case 'x': if ( (++i)==argc || get_16hex(word, argv[i])!=8) 
                      print_usage(argv) ;
                   mode = APPLY ;
                   break ;
         case 'k': if ( (++i)==argc || get_16hex(key, argv[i])!=8) 
                      print_usage(argv) ;
                   keyok = 1 ;
                   break ;
         case 'e': mode = ENCRYPT ;
                   break ;
         case 'd': mode = DECRYPT ;
                   break ;
         case 'h': print_usage(argv) ;
                   break ;
         default:  fprintf(stderr,"Unknown option %c.\n", argv[i][1]) ;
                   exit(1) ;
      }
   }
   if ( !keyok ) print_usage(argv) ;

   /* do the work */

   if ( mode==ENCRYPT ) {
      
      DES_SHORTInit (&context, key, ENCRYPT) ;
      while ( strl = get_clear( input ) ) {
         DES_SHORTUpdate (&context, output, input, strl) ;
         bytes_to_hex( encodedBlock, &encodedBlockLen, output, strl ) ;
         print_N(encodedBlock,encodedBlockLen) ;
         printf("\n") ;
      }

   } 
   if ( mode==DECRYPT ) {

      DES_SHORTInit (&context, key, DECRYPT) ;
      while ( encodedBlockLen = get_cipher( encodedBlock ) ) {
         hex_to_bytes( input, &strl, encodedBlock, encodedBlockLen ) ;
         DES_SHORTUpdate (&context, output, input, strl) ;
         print_N(output,strl) ;
      }
   }
   if ( mode==APPLY ) {

      bytes_to_hex( output, &strl, word, 8 ) ;
      printf("x=") ;
      print_N(output,16) ;

      DES_SHORTInit (&context, key, ENCRYPT) ;
      DES_SHORTUpdate (&context, output, word, 8) ;
      bytes_to_hex( encodedBlock, &encodedBlockLen, output, 8 ) ;
      printf(" e_k(x)=") ;
      print_N(encodedBlock,16) ;

      DES_SHORTInit (&context, key, DECRYPT) ;
      DES_SHORTUpdate (&context, output, word, 8) ;
      bytes_to_hex( encodedBlock, &encodedBlockLen, output, 8 ) ;
      printf(" d_k(x)=") ;
      print_N(encodedBlock,16) ;
      printf("\n") ;

      {
         unsigned char expanded[6] ;
         E_expand( expanded, word ) ;
      }
   }
}

/* subroutines: */

void print_usage(char * argv[]) {
   fprintf(stderr,
      "Usage: %s [-h|-d|-e|-x _hex-16-char_] -k _keyword_\n", argv[0]) ;
   fprintf(stderr,
      "Options:\n" ) ;
   fprintf(stderr,
      "\t-d: decode stdin->stdout.\n\t-e: encode stdin->stdout [default].\n") ;
   fprintf(stderr,
      "\t-h: help\n\t-k: use given 16-digit hex _keyword_.\n" ) ;
   fprintf(stderr,
      "\t-x: decode and encode the given _hex-16-char_ (not ascii!).\n" ) ;
   exit(0) ;
}

void print_N ( char * s, int N ) {
   while (N--) {
      if ( *s!=(char)ASCIIPAD ) printf("%c",*s) ;
      s++ ;
   }
} 

int not_hex_digit( char c ) {
     if ( c<'0' ) return(1) ;
     if ( c<='9') return(0) ;
     if ( c<'A' ) return(1) ;
     if ( c<='F') return(0) ;
     return(1) ;
}

int get_16hex( char * buf, char * keyword ) {
    int j ;
    char keywrd2[16] ;
    j = strlen(keyword) ;
    if ( j!=16 ) return(0) ;

    for (j=0;j<16;j++) {
       keyword[j] = (char) toupper(keyword[j]) ;
       if ( not_hex_digit(keyword[j])) return(0) ;
    }
    hex_to_bytes( buf, &j, keyword, 16 ) ;

    return(8) ;
}
    
int get_cipher( char * buf ) {
   int i = 0 ;
   int j ;
   while ( i<16 ) {
      j = getc( stdin ) ;
      if ( j==EOF ) break ;
      if ( j>='0' && j<='F' ) buf[i++] = j ;
   }
   while ( i%16 ) buf[i++] = '0' ;
   return(i) ;
}

int get_clear( char * buf ) {
    int i = 0 ;
    int j ;
    while ( i<8 ) {
       j = getc( stdin ) ;
       if ( j==EOF ) break ;
       buf[i++] = j ;
    }
    while ( i%8 ) buf[i++] = ASCIIPAD ;
    return(i) ;
}

int bytes_to_hex( unsigned char * o_s, int *o_cnt,
   unsigned char * i_s, int i_cnt ) {
   char i ;
   *o_cnt = 0 ;
   while ( i_cnt-- ) {

      i = (*i_s>>4) & 0x0F ;
      i += ( i>9 ) ? 'A'-10 : '0' ;
      *o_s++ = i ;

      i = (*i_s) & 0x0F ;
      i += ( i>9 ) ? 'A'-10 : '0' ;
      *o_s++ = i ;

      *o_cnt += 2 ;
      i_s++ ;
   }
}

int hex_to_bytes(  unsigned char * o_s, int *o_cnt,
   unsigned char * i_s, int i_cnt ) {
   char i ;
   assert( !(i_cnt%2) ) ;
   *o_cnt = 0 ;
   while ( i_cnt ) {
      i = *i_s++ & 0x7F ;
      i -= ( i>'9' ) ? 'A'-10 : '0' ;
      *o_s = i<<4 ;
      i = *i_s++ & 0x7F ;
      i -= ( i>'9' ) ? 'A'-10 : '0' ;
      *o_s |= i ;
      o_s++ ;
      (*o_cnt)++ ;
      i_cnt -= 2 ;
   }
}

