/* 
 * Encrypt / decrypt a file using RC4 algorithm
 * M. Divjak / 2000-05-09 / 2010-07-01
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define CHR unsigned char 
#define IM 9                    /* Init vector maxindex */
#define KM 255                  /* Key vector maxindex */
#define SM 255                  /* State vector maxindex */
#define DM 65535                /* Data buffer maxindex */

static void mkinit(CHR *init);
static void mkstate(CHR *state, CHR *key, int kl);
static void cypher(CHR *data, int dl, CHR *state,
    int *i, int *n);

int main(int argc, char *argv[]) {
    char enc[] = "-e";          /* Valid parameters */
    char dec[] = "-d"; 
    CHR init[IM+1];             /* Init vector */
    CHR key[KM+1];              /* Key vector */
    CHR state[SM+1];            /* State vector */
    CHR *data;                  /* Pointer to data buffer */
    int kl;                     /* Keylength */
    int dl;                     /* Datalength */
    int i,n;
    FILE *f1;                   /* Pointer to input file */
    FILE *f2;                   /* Pointer to output file */
    
    /* Check for correct number of parameters */
    if (argc != 5) {
        printf("Usage: %s -e|d <passwd> <infile> <outfile>\n", argv[0]);
        return 1;
    }
    /* Check for correct switch */
    if ((strcmp(enc,argv[1]) != 0) && 
        (strcmp(dec,argv[1]) != 0)) {
        printf("Error: Invalid switch %s\n", argv[1]);
        return 1;
    }
    /* Check for correct passwd */
    kl = strlen(argv[2]);
    if ((kl <= 12) || (kl >= 32)) {
        printf("Error: Invalid passwd length\n");
        return 1;
    }   
    /* Check for correct input file */
    if ((f1 = fopen(argv[3],"r")) == NULL) {
        printf("Error: Can't open %s\n", argv[3]);
        return 1;
    }
    /* Check for correct output file */
    if ((f2 = fopen(argv[4],"w")) == NULL) {
        printf("Error: Can't open %s\n", argv[4]);
        return 1;
    }
    /* Make key vector from passwd and init vector */
    for (i=0; i<=kl-1; i++) key[i] = (CHR)argv[2][i];
    if (strcmp(argv[1],enc) == 0) {
        mkinit(init);
        fwrite(init,1,IM+1,f2);
        printf("init=<%s>\n",init);
    }
    if (strcmp(argv[1],dec) == 0) {
        fread(init,1,IM+1,f1);
    }
    for (i=0; i<=IM; i++) key[kl+i] = init[i];
    kl = kl+IM+1;

    /* Make state vector from key vector */
    mkstate(state,key,kl);

    /* Allocate data buffer */ 
    if ((data = (CHR *)calloc(DM+1,sizeof(CHR))) == NULL) {
        printf("Error: Can't allocate data bufr\n");
        return 1;
    }
    /* Cypher data */
    i = 0;
    n = 0;
    while((dl = fread(data,1,DM+1,f1)) != 0) {
        cypher(data,dl,state,&i,&n);
        fwrite(data,1,dl,f2);
        printf(".");
    }
    /* Close files and buffers */   
    fclose(f1);
    fclose(f2);
    free(data);
    return 0;
}

/* Make init vector */
static void mkinit(CHR *init) {
    time_t t;
    t = time(NULL);
    sprintf(init,"%10ld",t);
    return;
}

/* Make state */
static void mkstate(CHR *state, CHR *key, int kl) {
    CHR c;
    int i,n;
    for (i=0; i<=SM; i++) state[i] = (CHR)i;
    n = 0;
    for (i=0; i<=SM; i++) {
        n = (n+state[i]+key[i%kl])%256;
        c = state[i];
        state[i] = state[n];
        state[n] = c;
    }
    return;
}

/* Cypher data */
static void cypher(CHR *data, int dl, CHR *state,
    int *i, int *n) {
    CHR c;
    int k,t;
    for (k=0; k<=dl-1; k++) {
        *i = (*i+1)%256;
        *n = (*n+state[*i])%256;
        c = state[*i];
        state[*i] = state[*n];
        state[*n] = c;
        t = (state[*i]+state[*n])%256;
        data[k] = data[k]^state[t];
    }
    return;
}

/* eof */
