ARC4: A lightweight encryption algorithm

Background

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.

The ARC4 cipher

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.

Usage Guidelines

  1. A random salt (nonce) must be used for each session.  It should be of the same length as the key.  It should contain genuinely random bits and should be sent unencrypted from the encrypting party to the decrypting party.  Both ends should XOR this salt with the key before ARC4 initialization. [ref]
  2. The first 768 (or up to 3072) bytes generated by ARC4 should be discarded by both ends after initialization. This avoids some key correlation attacks.  [ref]
  3. The state of the ARC4 “engine” must be saved and reused throughout the session, i.e., ARC4 must not be reinitialized for each transaction or packet.  Besides being more efficient, this avoids a known weakness.
  4. Encryption may be limited to only sensitive fields, and not the whole transaction.
  5. Each application must have its own unique key.  The key must not be shared by more than two parties.
  6. Keys, passwords & PINs should be at least 8 characters.

Source Code (in C): Not thread-safe, not guidelines compliant

// 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

}

ARC4 man page

NAME

arc4init, arc4gen, arc4crypt – ARC4 encryption

SYNOPSIS

void arc4init(unsigned char *key, int key_length);

unsigned char arc4gen();

void arc4crypt(unsigned char *ptr, int len);

DESCRIPTION

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.

DIAGNOSTIC MESSAGES

            None

SEE ALSO

arc4init_r, arc4gen_r, arc4crypt_r, arc4free_r


Thread-safe Source Code (in C)

// 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];

}

 

ARC4_r man page

NAME

arc4init_r, arc4gen_r, arc4crypt_r, arc4free_r – ARC4 encryption

SYNOPSIS

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);

DESCRIPTION

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));

DIAGNOSTIC MESSAGES

arc4init_r() returns null if it cannot allocate memory for encryption state.

SEE ALSO

arc4init, arc4gen, arc4crypt

 

References

http://en.wikipedia.org/wiki/RC4