RC4 is the most popular stream cipher. It’s popular because it’s fast, and it’s fast because it’s simple. There are other, newer ciphers that outperform RC4 in different ways, but RC4 is still the most widespread stream cipher.
Looking at the optimized C or assembly source code for RC4 can be a little disorienting, but the algorithm’s operation is simple: Given a key, RC4 creates a pseudo-random keystream that can be used to encrypt data. The rc4.c source code below clearly illustrates each step in the algorithm, and rc4demo.c provides an animation of RC4′s individual steps.
Overview
First, the RC4 algorithm creates a “state,” which begins as an array containing the values 0-255 in order. The algorithm then iterates through the array and swaps the elements around, based on the key value. The original key can then be discarded, because RC4 only uses the state to create a pseudo-random keystream, by algorithmically selecting elements and spitting them out. The added benefit of the keystream’s sole reliance on the state is that the original key doesn’t need to be kept in memory.
The Algorithm
- Initialize the state.
- Perform the key-scheduling algorithm (KSA) on the state, based on the input key (which can then be discarded).
- Algorithmically select elements of the (now scrambled) state and output them as the keystream.
Initialization
Initially, the state is an identity permutation containing the values 0 through 255:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Initialized State[]: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff |
The key-scheduling algorithm
RC4′s KSA uses a loop to iterate through the state and perform the following operation, which generates j and swaps State[i] and State[j]:
|
1 2 3 4 5 6 7 |
j = 0; for(i=0; i<256; i++) { j = (j + State[i] + key[i%keylen]) % 256; swap(&State[i], &State[j]); } |
The pseudo-random number generation algorithm
In a loop, RC4′s PRNGA deterministically selects elements from the state and outputs them as a keystream with the following code:
|
1 2 3 4 5 6 7 8 9 10 11 |
i = j = 0; for(k=0; k<msglength; k++) { i = (i+1) % 256; j = (j+State[i]) % 256; swap(&State[i], &State[j]); keystream[k] = State[(State[i]+State[j]) % 256]; } |
RC4 in C
The following code is a fully functional implementation of RC4, written for readability. The full source is here: rc4.c. Change the value of OUTPUT to see more or less information from each step.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
#include <stdlib.h> #include <stdio.h> #include <string.h> typedef unsigned char u_char; // Output level, from 0 to 3 #define OUTPUT 2 // Function prototypes void initialize(u_char *State); void swap(u_char *a, u_char *b); void ksa(u_char *State, u_char *key); u_char * prng(u_char *State, int msglength); void rc4(u_char *key, u_char *input, u_char *output); int main(int argc, char *argv[]) { u_char key[256]; // The encryption/decryption key u_char input[256]; // The input data u_char output[256]; // The output strncpy((char *) key, "Secret", 256); strncpy((char *) input, "Attack at dawn", 256); // encrypt rc4(key, input, output); #if OUTPUT >= 1 int i = 0; printf("nn--- Encryption ---"); printf("nOutput:n"); for(i=0; i<strlen((char *) input); i++) { if(output[i]<16) printf("0%x ", output[i]); else printf("%x ", output[i]); } printf("n"); #endif // decrypt rc4(key, output, input); #if OUTPUT >= 1 printf("nn--- Decryption ---"); printf("nOutput:n"); printf("%sn", input); #endif exit(0); } /* * RC4 functions */ /* Initialize State[256] to the identity permutation. */ void initialize(u_char *State) { int i; for(i=0; i<256; i++) { State[i] = i; } #if OUTPUT == 3 printf("Initialized State[]:n"); for(i=0; i<16; i++) printf(" 0%x ", State[i]); printf("n"); for(i=1; i<16; i++) { for(j=0; j<16; j++) printf(" %x ", State[16*i+j]); printf("n"); } #endif return; } /* Swap array elements i=State[i] and b=State[j]. */ void swap(u_char *i, u_char *j) { u_char temp; temp = *i; *i = *j; *j = temp; } /* Key scheduling algorithm. Swap array elements based on the key. */ void ksa(u_char *State, u_char *key) { int byte, i, keylen, j=0; keylen = (int) strlen((char *) key); for(i=0; i<256; i++) { j = (j + State[i] + key[i%keylen]) % 256; swap(&State[i], &State[j]); } #if OUTPUT >= 2 printf("Key scheduled State[]:n"); for(i=0; i<16; i++) { for(j=0; j<16; j++) { byte = State[16*i+j]; if(byte<16) printf(" 0%x ", byte); else printf(" %x ", byte); } printf("n"); } #endif return; } /* Pseudo-random number generator: Generate the keystream. */ u_char * prng(u_char *State, int msglength) { int i=0, j=0, k; u_char *keystream; keystream = (u_char *)malloc(sizeof(u_char)*msglength); for(k=0; k<msglength; k++) { i = (i+1) % 256; j = (j+State[i]) % 256; swap(&State[i], &State[j]); keystream[k] = State[(State[i]+State[j]) % 256]; } return keystream; } /* Encrypt or Decrypt */ void rc4(u_char *key, u_char *input, u_char *output) { int i; u_char State[256]; u_char *keystream; initialize(State); ksa(State, key); keystream = prng(State, strlen((char *) input)); for(i=0; i<strlen((char *) input); i++) output[i] = input[i] ^ keystream[i]; #if OUTPUT >= 1 printf("n--- Key Generation ---nKey: %sn", key); printf("Keystream:n"); for(i=0; i<strlen((char *) input); i++) { if(keystream[i]<16) printf("0%x ", keystream[i]); else printf("%x ", keystream[i]); } #endif } |
RC4 demo with animation
To see an animation of the RC4 algorithm’s steps, download and compile rc4demo.c with $ gcc rc4demo.c -o rc4demo. To run it, type $ ./rc4demo.
To see an example of RC4 with a smaller state, which can be helpful for learning purposes, change the values of SIZE and SQRT to reflect a smaller state with SIZE = SQRT*SQRT.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
/* * rc4demo.c - An animated demonstration of the RC4 stream cipher */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <math.h> typedef unsigned char u_char; // Change these to smaller values for simpler demonstrations. // SQRT must be an even number. #define SIZE 256 #define SQRT 16 // Change this to a lower value to make animations go faster. #define DELAY 70000 // RC4 function prototypes. void initialize(u_char *State); void swap(u_char *a, u_char *b); void ksa(u_char *State, u_char *key); u_char * prga(u_char *State, int msglength); void rc4(u_char *key, u_char *input, u_char *output); // Animation and output function prototypes. void displayInfo(u_char *State); void displayKSA(int i, int j, u_char *State); void displayPRGA(int i, int j, u_char *State, int msglength); void displayKey(int i, int j, int k, u_char *State, u_char * keystream, int msglength); void displayRC4(u_char *keystream, u_char *input, u_char *output); int main(int argc, char *argv[]) { u_char key[256]; // The encryption/decryption key u_char input[256]; // The input data u_char output[256]; // The output if(SQRT % 2 != 0 || sqrt(SIZE) != SQRT) { printf("Error! SQRT must be even, and the square root of SIZE.n"); exit(1); } strncpy((char *) key, "Secret", 256); // The key value strncpy((char *) input, "Attack at dawn", 256); // The input data // Encrypt or decrypt with RC4 rc4(key, input, output); exit(0); } /* * RC4 functions */ /* Initialize State[SIZE] to the identity permutation (0-255). */ void initialize(u_char *State) { int i; for(i=0; i<SIZE; i++) { State[i] = i; } return; } /* Swap array elements i=State[i] and j=State[j]. */ void swap(u_char *i, u_char *j) { u_char temp; temp = *i; *i = *j; *j = temp; } /* Key scheduling algorithm. Swap array elements based on the key. */ void ksa(u_char *State, u_char *key) { int i, j=0, keylen; keylen = (int) strlen((char *) key); for(i=0; i<SIZE; i++) { j = (j + State[i] + key[i%keylen]) % SIZE; // Display the animation displayKSA(i, j, State); swap(&State[i], &State[j]); usleep(DELAY); } printf("Key scheduling complete. Press Enter to begin PRGA...n"); getchar(); return; } /* Pseudo-random number generator: Generate the keystream. */ u_char * prga(u_char *State, int msglength) { int i=0, j=0, k; u_char *keystream; keystream = (u_char *)malloc(sizeof(u_char)*msglength); for(k=0; k<msglength; k++) { i = (i+1) % SIZE; j = (j+State[i]) % SIZE; // Generate j displayPRGA(i, j, State, msglength); // Swap State[i] and State[j] swap(&State[i], &State[j]); // For each iteration, select and output a byte of keystream. keystream[k] = State[(State[i]+State[j]) % SIZE]; displayKey(i, j, k, State, keystream, msglength); usleep(DELAY*2); } return keystream; } /* Encrypt or decrypt data */ void rc4(u_char *key, u_char *input, u_char *output) { int i; u_char State[SIZE]; u_char *keystream; // Initialize the state. initialize(State); displayInfo(State); // Perform key scheduling. ksa(State, key); // Generate the keystream. keystream = prga(State, strlen((char *) input)); // XOR the input data and the keystream. for(i=0; i<strlen((char *) input); i++) output[i] = input[i] ^ keystream[i]; displayRC4(keystream, input, output); } /* * Display and animation functions */ void displayInfo(u_char *State) { int byte, k, l ; system("clear"); printf("RC4 Demonstration. Initialized state:nn"); for(k=0; k<SQRT; k++) { for(l=0; l<SQRT; l++) { byte = State[SQRT*k+l]; if(byte<16) printf(" 0%x ", byte); else printf(" %x ", byte); } printf("n"); } printf("nPress Enter to begin key scheduling...n"); getchar(); } void displayKSA(int i, int j, u_char *State) { int byte, k, l; system("clear"); printf("Key scheduling. Elements in set: %in", SIZE); printf("i = %inj = %it= (j + State[i] + key[i%%keylen]) %% %i;nn", i, j, SIZE); for(k=0; k<SQRT; k++) { for(l=0; l<SQRT; l++) { byte = State[SQRT*k+l]; if((byte == State[i] || byte == State[j]) && i != SIZE - 1) printf(" 33[41m 33[37m"); if(byte<16) printf(" 0%x ", byte); else printf(" %x ", byte); if((byte == State[i] || byte == State[j]) && i != SIZE - 1) printf(" 33[0m"); } printf("n"); } printf("n"); } void displayPRGA(int i, int j, u_char *State, int msglength) { int byte, l, m; system("clear"); printf("PRGA Generation. Message length = %in", msglength); printf("i = %it= (i+1) %% %i;nj = %it= (j+State[i]) %% %i;nn", i, SIZE, j, SIZE); for(l=0; l<SQRT; l++) { for(m=0; m<SQRT; m++) { byte = State[SQRT*l+m]; if((byte == State[i] || byte == State[j])) printf(" 33[41m 33[37m"); if(byte<16) printf(" 0%x ", byte); else printf(" %x ", byte); if((byte == State[i] || byte == State[j])) printf(" 33[0m"); } printf("n"); } printf("n"); } void displayKey(int i, int j, int k, u_char *State, u_char * keystream, int msglength) { int l; printf("Key byte = State[(State[i] + State[j]) %% %i] = 0x%xn", SIZE, keystream[k]); printf("Keystream:t"); for(l=0; l<msglength; l++) { if(keystream[l]<16) printf("0%x ", keystream[l]); else printf("%x ", keystream[l]); } printf("n"); if(l == SIZE - 1) printf("PRGA complete. "); usleep(DELAY); } void displayRC4(u_char *keystream, u_char *input, u_char *output) { int i; printf("Input:tt"); for(i=0; i<strlen((char *) input); i++) { if(input[i]<16) printf("0%x ", input[i]); else printf("%x ", input[i]); } printf("n"); printf("Output:tt"); for(i=0; i<strlen((char *) output); i++) { if(output[i]<16) printf("0%x ", output[i]); else printf("%x ", output[i]); } printf("n"); } |
The code on this page was written for readability. To see what the source code for RC4 looks like in the BSD or Linux kernels, check the links below.
Further Reading
RC4 on Wikipedia (includes pseudocode)
crypto/rc4/rc4.c Source - RC4 in the FreeBSD kernel (very easy to read)
linux/crypto/arc4.c - RC4 in the Linux kernel (not so easy to read)


Very good explanation.
You have decoded RC4 in best possible way.
Thanks
Ashok