There are several applications that need to securely communicate that run on platforms that do not have the processing capability required for conventional encryption algorithms. This note describes how to use ARC4, a public, lightweight, shared secret key encryption algorithm that provides sufficient security and can be supported on low end platforms. It assumes that the encryption key is available to both parties. A simple PKI based key distribution mechanism is described separately.
RC4 is a fast shared key stream cipher. It generates a sequence of bytes which can be simply XORed with sensitive plaintext to encrypt it. The same mechanism is used to decrypt the encrypted text. It was designed by Ron Rivest, who is one of the developers of the RSA algorithm. It is used in WEP, WPA, and is an option for TLS and SSH. Except for its name, RC4 is in the public domain. It is henceforth referred to as ARC4.
ARC4 has been analyzed extensively and its weaknesses have been documented. There are a few other fast encryption algorithms, but none of them have been analyzed as well. It is better to use an algorithm with known issues than one with possible unknown issues.
A property of ARC4 is that, given the same key, it will generate the same stream. This property can be exploited by attackers to deduce & record the key stream using a known plaintext attack. This key stream can then be used to decrypt subsequent messages. One way to avoid this vulnerability is to add a random salt (nonce) to the key. This nonce should be selected by the encrypting party and sent in the clear to the decrypting party.
The Usage Guidelines section describes how to work around the known issues in ARC4 and the following source code implements the relevant guidelines.
// file: arc4.c
static unsigned char S[256]; // static private state table S
static unsigned int i, j; // static private counters
unsigned char arc4_gen();
// arc4_init: key-scheduling algorithm
void arc4_init(unsigned char *key, int key_length)
{
unsigned char si; // holds S[i]
int k = 0; // index into key & salt
for (i = 0; i < 256; i++)
S[i] = i;
for (i = j = 0; i < 256; i++, k++) {
if (k >= key_length) // k = k % key_length
k = 0;
si = S[i];
j = (si + j + (key[k])) & 255;
S[i] = S[j]; // swap S[i] & S[j]
S[j] = si;
}
i = j = 0; // reset static counters
}
// arc4gen: pseudo-random stream cipher generation
unsigned char arc4gen()
{
unsigned char si, sj;
i = (i + 1) & 255;
si = S[i];
j = (j + si) & 255;
sj = S[j];
S[i] = sj; // swap S[i] & S[j]
S[j] = si;
return S[(si + sj) & 255];
}
// arc4crypt: encrypt or decrypt using stream cipher
void arc4crypt(unsigned char *pdata, int len)
{
while (len-- > 0)
*pdata++ ^= arc4gen(); // XOR in place
}
arc4init, arc4gen, arc4crypt – ARC4 encryption
void arc4init(unsigned char *key, int key_length);
unsigned char arc4gen();
void arc4crypt(unsigned char *ptr, int len);
These functions provide basic encryption and decryption using ARC4. This version of the functions is not reentrant or thread safe since the encryption state is stored as a static table. It does not follow the guidelines and does not protect against known attacks.
The arc4init() function initializes the encryption state and must be called once before encrypting or decrypting data.
arc4init(key, key_length);
The arc4gen() function returns one byte which is the next value in the pseudo random stream. It updates the encryption state table. It is used for both encrypting and decrypting a byte of data by XORing the value as follows.
enc_byte = clear_byte ^ arc4gen();
The arc4crypt() function is used to encrypt or decrypt an array of bytes.
arc4_crypt(sensitive_field1, sizeof(sensitive_field1));
These functions may not be suitable in some applications where two peers are exchanging encrypted traffic if the encryption states of the traffic in the two directions are different. However, they can be used in simple communicating applications where data is encrypted and decrypted in a lock-step manner.
None
arc4init_r, arc4gen_r, arc4crypt_r, arc4free_r
// file: arc4_r.c Thread safe
struct arc4 {
unsigned char S[256]; // state table S
unsigned int i, j; // counters
};
unsigned char arc4gen_r(struct arc4 *ap);
// arc4init_r: key-scheduling algorithm; return context
void *arc4init_r(unsigned char *key,
int key_length,
unsigned char *salt,
int skip)
{
struct arc4 *ap;
char *pi;
unsigned char si; // holds S[i]
unsigned char ks; // key byte
int k = 0; // index into key & salt
int i, j;
ap = (struct arc4 *) malloc(sizeof *ap);
if (ap == 0)
return 0; // boom, like that
for (i = 0, pi = ap->S; i < 256; i++, pi++)
*pi = i;
for (i = j = 0, pi = ap->S; i < 256; i++, k++, pi++) {
if (k >= key_length) // k = k % key_length
k = 0;
ks = key[k];
if (salt)
ks ^= salt[k];
si = *pi;
j = (si + j + ks)) & 255;
*pi = ap->S[j]; // swap S[i] & S[j]
ap->S[j] = si;
}
ap->i = ap->j = 0; // reset counters
if (skip) // discard first few bytes
for (i = 0; i < 768; ++i)
(void) arc4_gen(ap);
return (ap);
}
// arc4gen_r: pseudo-random stream cipher generation
unsigned char arc4gen_r(struct arc4 *ap)
{
unsigned char si, sj;
ap->i = (ap->i + 1) & 255;
si = ap->S[ap->i];
ap->j = (ap->j + si) & 255;
sj = ap->S[ap->j];
ap->S[ap->i] = sj; // swap S[i] & S[j]
ap->S[ap->j] = si;
return ap->S[(si + sj) & 255];
}
arc4init_r, arc4gen_r, arc4crypt_r, arc4free_r – ARC4 encryption
void *arc4init_r(unsigned char *key, int key_length,
unsigned char *salt, int skip);
unsigned char arc4gen_r(void *ap);
void arc4crypt_r(void *ap, unsigned char *ptr, int len);
void arc4free_r(void *ap);
These functions provide encryption and decryption using ARC4. This version of the functions is reentrant and thread safe since the encryption state is stored in blob of memory that is returned by arc4init_r(). The memory must be released eventually via arc4free_r(). This version also follows the guidelines and supports skipping of the first few bytes of the stream cipher, to protect against key correlation attacks.
The arc4init_r() function initializes the encryption state and must be called once before encrypting or decrypting data. If used, salt is an array of random values and must be of the same length as the key. If salt is not desired, then specify a null pointer or an array of zeros. The salt must be sent to the other party so that it can be passed to arc4init_r() at that end. If skip is non-zero, then the first 768 bytes of the stream are discarded.
void *ap;
unsigned char salt[128];
int salt_length = strlen(passwd);
for (int i = 0; i < salt_length; ++i)
salt[i] = random();
ap = arc4init(key, key_length, salt, 1);
The arc4gen_r() function returns one byte which is the next value in the pseudo random stream. It updates the encryption state table referenced by its parameter ap. It is used for both encrypting and decrypting a byte of data by XORing the value as follows.
enc_byte = clear_byte ^ arc4gen_r(ap);
The arc4crypt_r() function is used to encrypt or decrypt an array of bytes.
arc4_crypt_r(ap, sensitive_fld1, sizeof(sensitive_fld1));
arc4init_r() returns null if it cannot allocate memory for encryption state.
arc4init, arc4gen, arc4crypt
http://en.wikipedia.org/wiki/RC4