/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* lib/crypto/builtin/enc_provider/rc4.c */
/*
* Copyright (c) 2000 by Computer Science Laboratory,
* Rensselaer Polytechnic Institute
*
* #include STD_DISCLAIMER
*/
#include "crypto_int.h"
typedef struct
{
unsigned int x;
unsigned int y;
unsigned char state[256];
} ArcfourContext;
typedef struct {
int initialized;
ArcfourContext ctx;
} ArcFourCipherState;
/* gets the next byte from the PRNG */
#if ((__GNUC__ >= 2) )
static __inline__ unsigned int k5_arcfour_byte(ArcfourContext *);
#else
static unsigned int k5_arcfour_byte(ArcfourContext *);
#endif /* gcc inlines*/
/* Initializes the context and sets the key. */
static krb5_error_code k5_arcfour_init(ArcfourContext *ctx, const unsigned char *key,
unsigned int keylen);
/* Encrypts/decrypts data. */
static void k5_arcfour_crypt(ArcfourContext *ctx, unsigned char *dest,
const unsigned char *src, unsigned int len);
static inline unsigned int k5_arcfour_byte(ArcfourContext * ctx)
{
unsigned int x;
unsigned int y;
unsigned int sx, sy;
unsigned char *state;
state = ctx->state;
x = (ctx->x + 1) & 0xff;
sx = state[x];
y = (sx + ctx->y) & 0xff;
sy = state[y];
ctx->x = x;
ctx->y = y;
state[y] = sx;
state[x] = sy;
return state[(sx + sy) & 0xff];
}
static void k5_arcfour_crypt(ArcfourContext *ctx, unsigned char *dest,
const unsigned char *src, unsigned int len)
{
unsigned int i;
for (i = 0; i < len; i++)
dest[i] = src[i] ^ k5_arcfour_byte(ctx);
}
static krb5_error_code
k5_arcfour_init(ArcfourContext *ctx, const unsigned char *key,
unsigned int key_len)
{
unsigned int t, u;
unsigned int keyindex;
unsigned int stateindex;
unsigned char* state;
unsigned int counter;
if (key_len != 16)
return KRB5_BAD_MSIZE; /*this is probably not the correct error code
to return */
state = &ctx->state[0];
ctx->x = 0;
ctx->y = 0;
for (counter = 0; counter < 256; counter++)
state[counter] = counter;
keyindex = 0;
stateindex = 0;
for (counter = 0; counter < 256; counter++)
{
t = state[counter];
stateindex = (stateindex + key[keyindex] + t) & 0xff;
u = state[stateindex];
state[stateindex] = t;
state[counter] = u;
if (++keyindex >= key_len)
keyindex = 0;
}
return 0;
}
static krb5_error_code
k5_arcfour_docrypt(krb5_key key, const krb5_data *state, krb5_crypto_iov *data,
size_t num_data)
{
ArcfourContext *arcfour_ctx = NULL;
ArcFourCipherState *cipher_state = NULL;
krb5_error_code ret;
size_t i;
if (key->keyblock.length != 16)
return KRB5_BAD_KEYSIZE;
if (state != NULL && (state->length != sizeof(ArcFourCipherState)))
return KRB5_BAD_MSIZE;
if (state != NULL) {
cipher_state = (ArcFourCipherState *)(void *)state->data;
arcfour_ctx = &cipher_state->ctx;
if (cipher_state->initialized == 0) {
ret = k5_arcfour_init(arcfour_ctx, key->keyblock.contents,
key->keyblock.length);
if (ret != 0)
return ret;
cipher_state->initialized = 1;
}
} else {
arcfour_ctx = (ArcfourContext *)malloc(sizeof(ArcfourContext));
if (arcfour_ctx == NULL)
return ENOMEM;
ret = k5_arcfour_init(arcfour_ctx, key->keyblock.contents,
key->keyblock.length);
if (ret != 0) {
free(arcfour_ctx);
return ret;
}
}
for (i = 0; i < num_data; i++) {
krb5_crypto_iov *iov = &data[i];
if (ENCRYPT_IOV(iov))
k5_arcfour_crypt(arcfour_ctx, (unsigned char *)iov->data.data,
(const unsigned char *)iov->data.data, iov->data.length);
}
if (state == NULL)
zapfree(arcfour_ctx, sizeof(ArcfourContext));
return 0;
}
static krb5_error_code
k5_arcfour_init_state (const krb5_keyblock *key,
krb5_keyusage keyusage, krb5_data *new_state)
{
/* Note that we can't actually set up the state here because the key
* will change between now and when encrypt is called
* because it is data dependent. Yeah, this has strange
* properties. --SDH
*/
new_state->length = sizeof (ArcFourCipherState);
new_state->data = malloc (new_state->length);
if (new_state->data) {
memset (new_state->data, 0 , new_state->length);
/* That will set initialized to zero*/
}else {
return (ENOMEM);
}
return 0;
}
/* Since the arcfour cipher is identical going forwards and backwards,
we just call "docrypt" directly
*/
const struct krb5_enc_provider krb5int_enc_arcfour = {
/* This seems to work... although I am not sure what the
implications are in other places in the kerberos library */
1,
/* Keysize is arbitrary in arcfour, but the constraints of the
system, and to attempt to work with the MSFT system forces us
to 16byte/128bit. Since there is no parity in the key, the
byte and length are the same. */
16, 16,
k5_arcfour_docrypt,
k5_arcfour_docrypt,
NULL,
k5_arcfour_init_state, /*xxx not implemented yet*/
krb5int_default_free_state
};