Subject RC4
Author Jim Starkey
I coded up an RC4 transform for a performance test. Running a 4M
record TPCC load, encrypting all client communications with RC4 added
about 7% (64 bit gcc on Linux) to elapsed time. Compiled for debug, the
difference was 12%. This is a huge improvement over AES.

RC4 is a stream, not block cipher. RC4 generates a random keystream
that is XORed bytewise with the plaintext. The basic foundation is a
256 byte array of scrambled value from 0 to 255. Each step swaps a pair
of entries.

RC4 got a bad rep over the WEP fiasco, but the problem was really with
how they used RC4, not RC4 itself. If you want more detail, see
http://www.rsa.com/rsalabs/node.asp?id=2009

Attached are the RC4 transform, an abstract "cipher" transform for
polymorphic ciphers, and an update of the AES transform with optional
CBC (cipher block chaining) to make Geoff happy.


--
Jim Starkey
Founder, NimbusDB, Inc.
978 526-1376


----------

// copyright (c) 2010 by NimbusDB, Inc.

#include "Cloud.h"
#include "AESTransform.h"
#include "TransformException.h"
#include "tomcrypt.h"

#define SETUP rijndael_setup
#define ECB_ENC rijndael_ecb_encrypt
#define ECB_DEC rijndael_ecb_decrypt
#define ECB_DONE rijndael_done
#define ECB_TEST rijndael_test
#define ECB_KS rijndael_keysize

extern int SETUP(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
extern int ECB_ENC(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
extern int ECB_DEC(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);

#ifdef _DEBUG
#undef THIS_FILE
static const char THIS_FILE[]=__FILE__;
#endif

AESTransform::AESTransform(int operatingMode, Transform *src)
{
mode = operatingMode;
source = src;
schedule = new symmetric_key;
blockSize = AES_blocksize;
blockChaining = false;
reset();
}

AESTransform::~AESTransform(void)
{
delete schedule;
}

unsigned int AESTransform::get(unsigned int bufferLength, UCHAR* buffer)
{
UCHAR *p = buffer;
UCHAR *endBuffer = buffer + bufferLength;
UCHAR input[AES_blocksize];

// If there is residual data from the last call, move it to output buffer

if (ptr < end)
{
int len = MIN(bufferLength, (unsigned int)(end - ptr));
memcpy (p, ptr, len);
ptr += len;
p += len;
}

// loop until output buffer is full or we run out of data

while (p < endBuffer && !done)
{
int len = source->get(sizeof(input), input);

// If we got less than a full block, we've run the input source dry

if (len < sizeof(input))
{
done = true;

if (len == 0)
break;

memset(input + len, 0, AES_blocksize - len);
}

// Encode or decode a block

switch(mode)
{
case AES_encrypt:
if (blockChaining)
{
for (uint n = 0; n < AES_blocksize; ++n)
chain[n] ^= input[n];

ECB_ENC(chain, block, schedule);
memcpy(chain, block, AES_blocksize);
}
else
ECB_ENC(input, block, schedule);

break;

case AES_decrypt:
if (len < sizeof(input))
throw TransformException("partial AES block");

ECB_DEC(input, block, schedule);

if (blockChaining)
{
for (uint n = 0; n < AES_blocksize; ++n)
block[n] ^= chain[n];

memcpy(chain, input, AES_blocksize);
}

break;
}

// Figure out how much of the block fits in the output buffer and more than amount of data

len = MIN(endBuffer - p, AES_blocksize);
memcpy(p, block, len);
p += len;

// If there's residual data, set up for the next call

if (len < sizeof(block))
{
ptr = block + len;

break;
}
}

return p - buffer;
}

unsigned int AESTransform::getLength(void)
{
int len = source->getLength();

return ROUNDUP(len, AES_blocksize);
}

void AESTransform::reset(void)
{
end = block + sizeof(block);
ptr = end;
done = false;

if (source)
source->reset();
}

void AESTransform::setKey(uint keyLength, const UCHAR* key)
{
int rounds = 0;

switch (keyLength)
{
case 16:
rounds = 10;
break;

case 24:
rounds = 12;
break;

case 32:
rounds = 14;
break;

default:
throw TransformException("Invalid AES keysize");
}

SETUP(key, keyLength, rounds, schedule);
}

void AESTransform::setInitialVector(uint vectorLength, const UCHAR* vector)
{
blockChaining = true;

if (vectorLength)
for (uint pos = 0; pos < sizeof(chain);)
{
int l = MIN(vectorLength, (sizeof(chain) - pos));
memcpy(chain + pos, vector, l);
pos += l;
}
else
for (uint pos = 0; pos < sizeof(chain); ++pos)
chain[pos] = pos;

}

uint AESTransform::getBlockSize(void)
{
return AES_blocksize;
}

----------

// copyright (c) 2010 by NimbusDB, Inc.

#ifndef _AES_TRANSFORM_H_
#define _AES_TRANSFORM_H_

#include "CipherTransform.h"

static const int AES_encrypt = 0;
static const int AES_decrypt = 1;
static const int AES_blocksize = 16;

union Symmetric_key;

class AESTransform : public CipherTransform
{
public:
AESTransform(int operatingMode, Transform *src);
virtual ~AESTransform(void);

virtual unsigned int get(unsigned int bufferLength, UCHAR* buffer);
virtual unsigned int getLength(void);
virtual void reset(void);
virtual void setInitialVector(uint vectorLength, const UCHAR* vector);
virtual uint getBlockSize(void);

void setKey(uint keyLength, const UCHAR* key);

int mode;
uint blockSize;
bool done;
bool blockChaining;
Transform *source;
UCHAR block[AES_blocksize];
UCHAR chain[AES_blocksize];
UCHAR *ptr;
UCHAR *end;
Symmetric_key *schedule;
};

#endif

----------

// copyright (c) 2010 by NimbusDB, Inc.


#ifndef _CIPHER_TRANSFORM_H_
#define _CIPHER_TRANSFORM_H_

#include "Transform.h"

class CipherTransform : public Transform
{
public:
virtual void setKey(uint keyLength, const unsigned char *key) = 0;
virtual void setInitialVector(uint vectorLength, const unsigned char *vector) = 0;
virtual uint getBlockSize() = 0;
};

#endif

----------

// copyright (c) 2010 by NimbusDB, Inc.

#include "Cloud.h"
#include "RC4Transform.h"
#include "TransformException.h"

//#define SWAP(i,j) { state[i] ^= state[j]; state[j] ^= state[i]; state[i] ^= state[j]; }
#define SWAP(i,j) { UCHAR c = state[i]; state[i] = state[j]; state[j] = c; }

RC4Transform::RC4Transform(Transform *src)
{
source = src;
}

RC4Transform::~RC4Transform(void)
{
}

void RC4Transform::setKey(uint keyLength, const UCHAR* key)
{
for (uint n = 0; n < sizeof(state); ++n)
state[n] = n;

for (uint k1 = 0, k2 = 0; k1 < 256; ++k1)
{
k2 = (k2 + key[k1 % keyLength] + state[k1]) & 0xff;
SWAP(k1, k2);
}

s1 = s2 = 0;
}

uint RC4Transform::get(uint bufferLength, UCHAR* buffer)
{
uint length = source->get(bufferLength, buffer);

for (UCHAR *p = buffer, *end = buffer + length; p < end;)
{
s1 = (s1 + 1) & 0xff;
s2 = (s2 + state[s1]) & 0xff;
SWAP(s1, s2);
UCHAR b = state[(state[s1] + state[s2]) & 0xff];
*p++ ^= b;
}

return length;
}

void RC4Transform::reset(void)
{
if (source)
source->reset();
}

uint RC4Transform::getLength(void)
{
if (source)
return source->getLength();

return 0;
}

uint RC4Transform::getBlockSize(void)
{
return 1;
}

void RC4Transform::setInitialVector(uint vectorLength, const UCHAR* vector)
{
}

----------

// copyright (c) 2010 by NimbusDB, Inc.

#ifndef _RC4_TRANSFORM_H_
#define _RC4_TRANSFORM_H_

#include "CipherTransform.h"

class RC4Transform : public CipherTransform
{
public:
RC4Transform(Transform *source);
~RC4Transform(void);

virtual uint get(uint bufferLength, UCHAR* buffer);
virtual void reset(void);
virtual uint getLength(void);
virtual uint getBlockSize(void);
virtual void setKey(uint keyLength, const UCHAR* key);
virtual void setInitialVector(uint vectorLength, const UCHAR* vector);

Transform *source;
uint s1, s2;
UCHAR state[256];
};

#endif


[Non-text portions of this message have been removed]