/* Copyright (C) 2001 artofcode LLC.  All rights reserved.
  
  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 2 of the License, 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.,
  59 Temple Place, Suite 330, Boston, MA, 02111-1307.

*/

/*$Id: sarc4.c,v 1.5.2.2.2.1 2003/01/17 00:49:05 giles Exp $ */

/* Arcfour cipher and filter implementation */

#include "memory_.h"
#include "gserrors.h"
#include "gserror.h"
#include "strimpl.h"
#include "sarc4.h"

/* This is an independent implementation of the symmetric block
 * cipher commonly known as 'arcfour' based on Bruce Schneier's
 * description of the algorithm in _Applied Cryptography_. Arcfour
 * is believed to be functionally equivalent to the RC4(tm) cipher
 * mentioned in the PDF specification. (RC4 is a registered 
 * trademark of RSA Data Security, Inc.)
 */

/* stream implementation */

private_st_arcfour_state();	/* creates a gc object for our state, defined in sarc4.h */

/* initialize the S box using the given key */
int
s_arcfour_set_key(stream_arcfour_state * state, const unsigned char *key,
		  int keylength)
{
    unsigned int x, y;
    unsigned char s, *S = state->S;

    if (keylength < 1)
	return_error(gs_error_rangecheck);

    /* initialize to eponymous values */
    for (x = 0; x < 256; x++)
	S[x] = x;

    /* scramble based on the key */
    y = 0;
    for (x = 0; x < 256; x++) {
	y = (y + S[x] + key[x % keylength]) & 0xFF;
	s = S[x];
	S[x] = S[y];
	S[y] = s;
    }

    /* initialize the indicies */
    state->x = 0;
    state->y = 0;

    /* return successfully */
    return 0;
}

/* (de)crypt a section of text--the procedure is the same
 * in each direction. see strimpl.h for return codes.
 */
private int
s_arcfour_process(stream_state * ss, stream_cursor_read * pr,
		  stream_cursor_write * pw, bool last)
{
    stream_arcfour_state *const state = (stream_arcfour_state *) ss;
    unsigned int x = state->x;
    unsigned int y = state->y;
    unsigned char s, *S = state->S;
    unsigned char z;
   const unsigned char *limit;
    int status;

    /* figure out if we're going to run out of space */
    if ((pr->limit - pr->ptr) > (pw->limit - pw->ptr)) {
	limit = pr->ptr + (pw->limit - pw->ptr);
	status = 1;
    } else {
	limit = pr->limit;
	status = last ? EOFC : 0;
    }
    /* generate a pseudorandom byte stream and xor it with the input */
    while (pr->ptr < limit) {
	x = (x + 1) & 0xFF;
	y = (y + S[x]) & 0xFF;
	s = S[x];
	S[x] = S[y];
	S[y] = s;
	z = S[(S[x] + S[y]) & 0xFF];

	*++pw->ptr = (*++pr->ptr) ^ z;
    }
    /* save state */
    state->x = x;
    state->y = y;

    return status;
}

/* stream template */
const stream_template s_arcfour_template = {
    &st_arcfour_state, NULL, s_arcfour_process, 1, 1
};
