Advanced RC4 Encryption
Introduction
Here we go through building a program in C to apply RC4 encryption on data. But in comparison to our "Simplified RC4 Encryption" article, here we use a bigger data stream and a bit more advanced C programming, involving pointers and sructures.
Clean Advanced RC4 Encryption / Decryption
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// create the structure
typedef struct {
unsigned int i;
unsigned int j;
unsigned char s[256];
} Rc4Context;
// initialize the Permutations on S array
void rc4Init(Rc4Context* context, const unsigned char* key, size_t length)
{
unsigned int i;
unsigned int j;
unsigned char temp;
if (context == NULL || key == NULL)
return;
context->i = 0;
context->j = 0;
// create a clean 0-255 s Array
for (i = 0; i < 256; i++) {
context->s[i] = i;
}
// apply permutation on a clean S array using the key
for (i = 0, j = 0; i < 256; i++){
j = (j + context->s[i] + key[i % length]) % 256;
temp = context->s[i];
context->s[i] = context->s[j];
context->s[j] = temp;
}
}
// initiatlize an encryption / decryption function
void rc4Cipher(Rc4Context* context, const unsigned char* input, unsigned char* output, size_t length){
unsigned char temp;
// Restore context
unsigned int i = context->i;
unsigned int j = context->j;
unsigned char* s = context->s;
// Encryption loop
while (length > 0)
{
// Adjust indices
i = (i + 1) % 256;
j = (j + s[i]) % 256;
// Swap the values of S[i] and S[j]
temp = s[i];
s[i] = s[j];
s[j] = temp;
// Valid input and output?
if (input != NULL && output != NULL)
{
//XOR the input data with the RC4 stream
*output = *input ^ s[(s[i] + s[j]) % 256];
//Increment data pointers
input++;
output++;
}
// Remaining bytes to process
length--;
}
// Save context
context->i = i;
context->j = j;
}
int main(void)
{
Rc4Context ctx;
// declare the key for S array permutations
const unsigned char key[] = "demo";
// call the rc4Init function
rc4Init(&ctx, key, strlen((const char*)key));
// create the plain text to encrypt
const unsigned char plaintext[] = "HELLO";
// create buffers for encryption / decryption
unsigned char ciphertext[sizeof plaintext];
unsigned char decrypted[sizeof plaintext];
// call encryption function
rc4Cipher(&ctx, plaintext, ciphertext, sizeof plaintext);
// to decrypt, we must reset context with the same key
Rc4Context ctx2;
rc4Init(&ctx2, key, strlen((const char*)key));
rc4Cipher(&ctx2, ciphertext, decrypted, sizeof ciphertext);
// Step 5: print results
printf("Plaintext: %s\n", plaintext);
printf("Ciphertext (hexadecimal): ");
for (size_t i = 0; i < sizeof plaintext; i++) {
printf("%02X ", ciphertext[i]);
}
printf("\n");
printf("Ciphertext (decimal): ");
for (size_t i = 0; i < sizeof plaintext; i++) {
printf("%3d ", ciphertext[i]);
}
printf("\nDecrypted: %s\n", decrypted);
return 0;
}
Create a structure and a pointer to the structure
#include <stdio.h>
// first we define our structure we call Rc4Context
// it will store our i, our j, and our S array with 256
// bytes
typedef struct {
unsigned int i;
unsigned int j;
unsigned char s[256];
} Rc4Context;
// second we define a function rc4Init that will
// essentailly generate our S array
// we want our function to accept the pointer
// to the structure (or the actual memory address of
// the memory block that is populated by the
// structure
// let's decalre this memory block in the main()
void rc4Init(Rc4Context* context){
printf("the value is: %p\n", context);
}
int main(){
// ctx is a structure variable
// It is the actual block of memory that holds i, j, and
// s[256]
Rc4Context ctx;
// now we want to call the function rc4Init and we want
// to paste the memory adress of the structure
// or the pointer to the structure
// we paste the pointer to the structure by adding &
// to the structure variable
rc4Init (&ctx);
return 0;
}
Second we create a Key for our K Array
#include <stdio.h>
typedef struct {
unsigned int i;
unsigned int j;
unsigned char s[256];
} Rc4Context;
// key is a point that points to data of type
// const unsigned char
// const unsigned char* key expects a pointer to the
// first byte of some array of unsigned chars
// (in our case, the "demo" string inside the key array)
// in --> main()
// In practice: I expect you to give me the address of
// the first byte of some array of bytes, and I promise not
// to modify those bytes (because of "const").
void rc4Init(Rc4Context* context, const unsigned char* key){
printf("the mem addr of struc: %p\n\n", context);
printf("the mem addr of the key: %p\n\n", key);
}
int main(){
// our Key is demo. it is in the array and
// there is a trailing string. so the array is:
// [d, e, m, o, \0]
// in decimal it is [100, 101, 109, 111, 0]
// when we wrote const unsigned char key[] = "demo";
// C stored this in memory as raw bytes:
// [100, 101, 109, 111, 0]. So, key[0] = 100
// when we create a K[] array using this key
// C will automatically use ASCII from demo
// to populate the array! We don't need to convert
const unsigned char key[] = "demo";
printf("'d' is --> %d in ASCII\n\n", key[0]);
Rc4Context ctx;
// In C, when you pass an array to a function,
// it passes a pointer to its first element
// so key in the call becomes the memory address of
// the first element of the key array
// const unsigned char* → &key[0]
rc4Init (&ctx, key);
return 0;
}
Third we Declare and Pass the length of our key
#include <stdio.h>
#include <string.h>
typedef struct {
unsigned int i;
unsigned int j;
unsigned char s[256];
} Rc4Context;
// we need to include <string.h> to use strlen
// next we want to return the length of our key
// we need this for our function to know how many bytes
// the key has (in case it is changed)
// our function expects size_t (size_t length)
// which is the type returned by functions like
// strlen or sizeof etc..
// length is just a parameter variable, it can by anything
// so let's send the length of the key using main()
void rc4Init(Rc4Context* context, const unsigned char* key, size_t length){
printf("the mem addr of struc: %p\n\n", context);
printf("the mem addr of the key: %p\n\n", key);
// it prints the number of characters in the
// unsigned char key[]
printf("key length: %zu\n", length);
}
int main(){
const unsigned char key[] = "demo";
Rc4Context ctx;
// in C functions cannot directly take arrays
// as parameters. they always decay to pointers
// starting with the pointer to the first element
// in the array
// in C, a string is defined as a sequence of "char" in
// memory ending with \0
// To walk through a string
// ('d' → 'e' → 'm' → 'o' → '\0')
// you need a pointer that can move along memory one
// char at a time
// strlen("demo") counts characters until it hits the
// null terminator \0. So it returns 4
// This gives your function the exact number of bytes
// in the key, without you hardcoding it.
// When passed to a function, key decays into a
// pointer to its first element (&key[0] → 'd')
// (const char*)key
// casts that pointer "from const unsigned char*" to
// "const char*", because "strlen" is declared as
// size_t strlen(const char* str);
// strlen() Walks through the characters starting at
// that pointer until it finds the terminating \0
// It counts how many characters were seen before (4)
rc4Init (&ctx, key, strlen((const char*)key));
return 0;
}
Fourth Generate our S Array
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
unsigned int i;
unsigned int j;
unsigned char s[256];
} Rc4Context;
void rc4Init(Rc4Context* context, const unsigned char* key, size_t length)
{
unsigned int i;
unsigned int j;
unsigned char temp;
if (context == NULL || key == NULL)
return; // can't return error code from void
// now we want to assign 0 to both i and j
// context is a pointer to the structure Rc4Context
// context-> i = 0; means go to the struct that
// context points to, then access its member "i"
context->i = 0;
context->j = 0;
// the loop below does the identity permutation
// it will run through values 0 - 255 included
// for i = 0, go to the element 0 in the S array,
// and assign 0 to the value stored in this
// element in place 0
// so i = 0, S[0] = 0
// essentially it produces an array with values
// from 0 to 255
for (i = 0; i < 256; i++) {
context->s[i] = i;
}
// the logit below prints the elements of the S array
// so that each elements is printed in hexadecimal
// after printing 16 values, it inserts a newline
printf("S array after init (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
// context->s[i] basically prints the value
// of the element in the location i of the array
// so at context->s[245] will print the number
// 245 that is stored in location 245 of the array
// %02x means print integer in hexadecimal
// always use 2 digits, pad with 0 if needed
printf("%02X ", context->s[i]);
// the below prints a new line every time
// when i + 1 divided by 16 gives a remainder 0
// it happens on when i + 1 is 16, 32, 48, etc..
// so new line every 16 elements
// it is i+1 because if we used only i, when i
// is 0 and divided by 16, the remainder is 0
// so a new line would have been created
// after we printed the elements in location S[0]
// which we do not want
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
// the below prints the same but in decimal
// it will print number from 0 to 255
printf("\n");
printf("S array after init (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
}
int main(void)
{
Rc4Context ctx;
const unsigned char key[] = "demo";
rc4Init(&ctx, key, strlen((const char*)key));
return 0;
}
Generate our K Array
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
unsigned int i;
unsigned int j;
unsigned char s[256];
} Rc4Context;
void rc4Init(Rc4Context* context, const unsigned char* key, size_t length)
{
unsigned int i;
unsigned int j;
unsigned char temp;
if (context == NULL || key == NULL)
return; // can't return error code from void
context->i = 0;
context->j = 0;
for (i = 0; i < 256; i++) {
context->s[i] = i;
}
printf("S array after init (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n");
printf("S array after init (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
// we generate our K array here. for every i (0 - 255)
// we declare the value of K[] in location i
// to be (i % length) --> (the remainder of i / length)
// and -> i / length = i / 4 (because length holds 4)
// so for i = 0, K[i] = K[0], and K[0] will be
// key[0 % 4] ==> remainder 0. key[0] is the letter "d"
// or in ASCII it is 100. so the first element
// of the K[] array in location "0" will be 100
// second is "e" or 101, third is "m" or 109 etc..
// but when i = 4, the remainder is again 0, so
// K[4] will equal to K[0] or "d" or 100 and we repeat
unsigned char K[256];
for (i = 0; i < 256; i++) {
K[i] = key[i % length];
}
// Show ASCII values of the key "demo"
printf("\nKey string: %s\n", key);
printf("ASCII decimal values: ");
for (i = 0; i < length; i++) {
printf("'%d' ", key[i]);
}
printf("\n\n");
// Print K array
printf("K array (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", K[i]);
if ((i + 1) % 16 == 0) printf("\n");
}
printf("\n\nK array (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", K[i]);
if ((i + 1) % 16 == 0) printf("\n");
}
}
int main(void)
{
Rc4Context ctx;
const unsigned char key[] = "demo";
rc4Init(&ctx, key, strlen((const char*)key));
return 0;
}
Generate New S Array using the Key
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
unsigned int i;
unsigned int j;
unsigned char s[256];
} Rc4Context;
void rc4Init(Rc4Context* context, const unsigned char* key, size_t length)
{
unsigned int i;
unsigned int j;
unsigned char temp;
if (context == NULL || key == NULL)
return;
context->i = 0;
context->j = 0;
for (i = 0; i < 256; i++) {
context->s[i] = i;
}
printf("S array after init (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n");
printf("S array after init (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
unsigned char K[256];
for (i = 0; i < 256; i++) {
K[i] = key[i % length];
}
printf("\nKey string: %s\n", key);
printf("ASCII decimal values: ");
for (i = 0; i < length; i++) {
printf("'%d' ", key[i]);
}
printf("\n\n");
printf("K array (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", K[i]);
if ((i + 1) % 16 == 0) printf("\n");
}
printf("\n\nK array (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", K[i]);
if ((i + 1) % 16 == 0) printf("\n");
}
// here we generate a new S array, we use
// the S array we created, and the K array we
// created using our key "demo"
for (i = 0, j = 0; i < 256; i++){
// Randomize the permutations using the K[]
// so, for i = 0 and for j = 0
// 0 = (0 + 0 + 100) % 256 = 100
// j = 100
j = (j + context->s[i] + K[i]) % 256;
// Swap the values of S[i] and S[j]
// temp = 0
temp = context->s[i];
// s[0] = 0 it becomes s[100] = 100
// so s[0] was 0 but s[0] now became s[100]
// which is 100. now s[0] is 100
context->s[i] = context->s[j];
// s[j] was s[100] = 100 but became
// s[0] = 0 as now we swap it with temp which = 0
// because we equaled temp to s[i] = s[0] = 0
context->s[j] = temp;
}
printf("\n\nNew S array (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n\nNew S array (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", context->s[i]);
if ((i + 1) % 16 == 0) printf("\n");
}
}
int main(void)
{
Rc4Context ctx;
const unsigned char key[] = "demo";
rc4Init(&ctx, key, strlen((const char*)key));
return 0;
}
Generate New S Array without Separately Generating the K array
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
unsigned int i;
unsigned int j;
unsigned char s[256];
} Rc4Context;
void rc4Init(Rc4Context* context, const unsigned char* key, size_t length)
{
unsigned int i;
unsigned int j;
unsigned char temp;
if (context == NULL || key == NULL)
return;
context->i = 0;
context->j = 0;
for (i = 0; i < 256; i++) {
context->s[i] = i;
}
printf("S array after init (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n");
printf("S array after init (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\nKey string: %s\n", key);
printf("ASCII decimal values: ");
for (i = 0; i < length; i++) {
printf("'%d' ", key[i]);
}
printf("\n\n");
for (i = 0, j = 0; i < 256; i++){
j = (j + context->s[i] + key[i % length]) % 256;
temp = context->s[i];
context->s[i] = context->s[j];
context->s[j] = temp;
}
printf("New S array (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n\nNew S array (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", context->s[i]);
if ((i + 1) % 16 == 0) printf("\n");
}
}
int main(void)
{
Rc4Context ctx;
const unsigned char key[] = "demo";
rc4Init(&ctx, key, strlen((const char*)key));
return 0;
}
Generate the Keystream Using A new S Array
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
unsigned int i;
unsigned int j;
unsigned char s[256];
} Rc4Context;
void rc4Init(Rc4Context* context, const unsigned char* key, size_t length)
{
unsigned int i;
unsigned int j;
unsigned char temp;
if (context == NULL || key == NULL)
return;
context->i = 0;
context->j = 0;
for (i = 0; i < 256; i++) {
context->s[i] = i;
}
printf("S array after init (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n");
printf("S array after init (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\nKey string: %s\n", key);
printf("ASCII decimal values: ");
for (i = 0; i < length; i++) {
printf("'%d' ", key[i]);
}
printf("\n\n");
for (i = 0, j = 0; i < 256; i++){
j = (j + context->s[i] + key[i % length]) % 256;
temp = context->s[i];
context->s[i] = context->s[j];
context->s[j] = temp;
}
printf("New S array (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n\nNew S array (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", context->s[i]);
if ((i + 1) % 16 == 0) printf("\n");
}
printf("\n\n");
}
// here we declare a function to build a keystream
// and to encrypt our plain text ("HELLO")
// the function is called rc4Cipher and it expects
// 1. "Rc4Context* context" - pointer to an RC4 context struct
// 2. "size_t length" - number of bytes to process (6)
void rc4Cipher(Rc4Context* context, size_t length){
unsigned char temp;
int t;
unsigned char KeyStream[length];
unsigned int n;
unsigned int i = context->i;
unsigned int j = context->j;
unsigned char* s = context->s;
for (n = 0; n < length; n++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
temp = s[i];
s[i] = s[j];
s[j] = temp;
t = (s[i] + s[j]) % 256;
KeyStream[n] = s[t];
context->i = i;
context->j = j;
}
printf("KeyStream array is: ");
for (i = 0; i < length; i++) {
printf("%d ", KeyStream[i]);
}
printf("\n");
printf("\n");
}
int main(void)
{
Rc4Context ctx;
const unsigned char key[] = "demo";
rc4Init(&ctx, key, strlen((const char*)key));
const unsigned char plaintext[] = "HELLO";
rc4Cipher(&ctx, sizeof plaintext);
return 0;
}
Encrypt the Plain Text Using a New Keystream
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
unsigned int i;
unsigned int j;
unsigned char s[256];
} Rc4Context;
void rc4Init(Rc4Context* context, const unsigned char* key, size_t length)
{
unsigned int i;
unsigned int j;
unsigned char temp;
if (context == NULL || key == NULL)
return;
context->i = 0;
context->j = 0;
for (i = 0; i < 256; i++) {
context->s[i] = i;
}
printf("S array after init (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n");
printf("S array after init (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\nKey string: %s\n", key);
printf("ASCII decimal values: ");
for (i = 0; i < length; i++) {
printf("'%d' ", key[i]);
}
printf("\n\n");
for (i = 0, j = 0; i < 256; i++){
j = (j + context->s[i] + key[i % length]) % 256;
temp = context->s[i];
context->s[i] = context->s[j];
context->s[j] = temp;
}
printf("New S array (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n\nNew S array (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", context->s[i]);
if ((i + 1) % 16 == 0) printf("\n");
}
printf("\n\n");
}
// here we declared a function to build a keystream
// and now we need to use it to encrypt our plain text ("HELLO")
void rc4Cipher(Rc4Context* context, const unsigned char* input, unsigned char* output, size_t length){
unsigned char temp;
int t;
unsigned char KeyStream[length];
unsigned int n;
unsigned int i = context->i;
unsigned int j = context->j;
unsigned char* s = context->s;
for (n = 0; n < length; n++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
temp = s[i];
s[i] = s[j];
s[j] = temp;
t = (s[i] + s[j]) % 256;
KeyStream[n] = s[t];
// Encrypt plaintext with XOR
output[n] = input[n] ^ KeyStream[n];
}
context->i = i;
context->j = j;
printf("KeyStream array is: ");
for (i = 0; i < length; i++) {
printf("%d ", KeyStream[i]);
}
printf("\n");
printf("\n");
printf("Encrypted array is: ");
for (i = 0; i < length; i++) {
printf("%d ", output[i]);
}
printf("\n");
printf("\n");
}
int main(void)
{
Rc4Context ctx;
const unsigned char key[] = "demo";
rc4Init(&ctx, key, strlen((const char*)key));
const unsigned char plaintext[] = "HELLO";
unsigned char ciphertext[sizeof plaintext];
rc4Cipher(&ctx, plaintext, ciphertext, sizeof plaintext);
return 0;
}
Decrypt the Cipher Text Using a Keystream
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
unsigned int i;
unsigned int j;
unsigned char s[256];
} Rc4Context;
void rc4Init(Rc4Context* context, const unsigned char* key, size_t length)
{
unsigned int i;
unsigned int j;
unsigned char temp;
if (context == NULL || key == NULL)
return;
context->i = 0;
context->j = 0;
for (i = 0; i < 256; i++) {
context->s[i] = i;
}
printf("S array after init (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n");
printf("S array after init (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\nKey string: %s\n", key);
printf("ASCII decimal values: ");
for (i = 0; i < length; i++) {
printf("'%d' ", key[i]);
}
printf("\n\n");
for (i = 0, j = 0; i < 256; i++){
j = (j + context->s[i] + key[i % length]) % 256;
temp = context->s[i];
context->s[i] = context->s[j];
context->s[j] = temp;
}
printf("New S array (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n\nNew S array (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", context->s[i]);
if ((i + 1) % 16 == 0) printf("\n");
}
printf("\n\n");
}
void rc4Cipher(Rc4Context* context, const unsigned char* input, unsigned char* output, size_t length){
unsigned char temp;
int t;
unsigned char KeyStream[length];
unsigned int n;
unsigned int i = context->i;
unsigned int j = context->j;
unsigned char* s = context->s;
for (n = 0; n < length; n++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
temp = s[i];
s[i] = s[j];
s[j] = temp;
t = (s[i] + s[j]) % 256;
KeyStream[n] = s[t];
output[n] = input[n] ^ KeyStream[n];
}
context->i = i;
context->j = j;
printf("KeyStream array is: ");
for (i = 0; i < length; i++) {
printf("%d ", KeyStream[i]);
}
printf("\n");
printf("\n");
printf("Encrypted array is: ");
for (i = 0; i < length; i++) {
printf("%d ", output[i]);
}
printf("\n");
printf("\n");
}
int main(void)
{
Rc4Context ctx;
const unsigned char key[] = "demo";
rc4Init(&ctx, key, strlen((const char*)key));
const unsigned char plaintext[] = "HELLO";
unsigned char ciphertext[sizeof plaintext];
unsigned char decrypted[sizeof plaintext];
rc4Cipher(&ctx, plaintext, ciphertext, sizeof plaintext);
// to decrypt, we must reset context with the same key
Rc4Context ctx2;
rc4Init(&ctx2, key, strlen((const char*)key));
rc4Cipher(&ctx2, ciphertext, decrypted, sizeof ciphertext);
printf("\nDecrypted : %s\n", decrypted);
return 0;
}
Encryption / Decryption but not Printing NULL Terminator
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
unsigned int i;
unsigned int j;
unsigned char s[256];
} Rc4Context;
void rc4Init(Rc4Context* context, const unsigned char* key, size_t length)
{
unsigned int i;
unsigned int j;
unsigned char temp;
if (context == NULL || key == NULL)
return;
context->i = 0;
context->j = 0;
for (i = 0; i < 256; i++) {
context->s[i] = i;
}
printf("S array after init (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n");
printf("S array after init (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\nKey string: %s\n", key);
printf("ASCII decimal values: ");
for (i = 0; i < length; i++) {
printf("'%d' ", key[i]);
}
printf("\n\n");
for (i = 0, j = 0; i < 256; i++){
j = (j + context->s[i] + key[i % length]) % 256;
temp = context->s[i];
context->s[i] = context->s[j];
context->s[j] = temp;
}
printf("New S array (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n\nNew S array (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", context->s[i]);
if ((i + 1) % 16 == 0) printf("\n");
}
printf("\n\n");
}
void rc4Cipher(Rc4Context* context, const unsigned char* input, unsigned char* output, size_t length){
unsigned char temp;
int t;
unsigned char KeyStream[length];
unsigned int n;
unsigned int i = context->i;
unsigned int j = context->j;
unsigned char* s = context->s;
for (n = 0; n < length; n++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
temp = s[i];
s[i] = s[j];
s[j] = temp;
t = (s[i] + s[j]) % 256;
KeyStream[n] = s[t];
output[n] = input[n] ^ KeyStream[n];
}
context->i = i;
context->j = j;
printf("KeyStream array is: ");
for (i = 0; i < length; i++) {
printf("%d ", KeyStream[i]);
}
printf("\n");
printf("\n");
printf("Encrypted array is: ");
for (i = 0; i < length; i++) {
printf("%d ", output[i]);
}
printf("\n");
printf("\n");
}
int main(void)
{
Rc4Context ctx;
const unsigned char key[] = "demo";
rc4Init(&ctx, key, strlen((const char*)key));
const unsigned char plaintext[] = "HELLO";
// only 5 characters, no terminator encrypted
size_t pt_len = strlen((const char*)plaintext);
unsigned char ciphertext[pt_len];
unsigned char decrypted[pt_len + 1]; // +1 for '\0'
rc4Cipher(&ctx, plaintext, ciphertext, pt_len);
// decrypt
Rc4Context ctx2;
rc4Init(&ctx2, key, strlen((const char*)key));
rc4Cipher(&ctx2, ciphertext, decrypted, pt_len);
// add back the string terminator
decrypted[pt_len] = '\0';
printf("Decrypted : %s\n", decrypted);
return 0;
}
Advanced RC4 Encryption
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
unsigned int i;
unsigned int j;
unsigned char s[256];
} Rc4Context;
void rc4Init(Rc4Context* context, const unsigned char* key, size_t length)
{
unsigned int i;
unsigned int j;
unsigned char temp;
if (context == NULL || key == NULL)
return;
context->i = 0;
context->j = 0;
for (i = 0; i < 256; i++) {
context->s[i] = i;
}
printf("S array after init (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n");
printf("S array after init (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\nKey string: %s\n", key);
printf("ASCII decimal values: ");
for (i = 0; i < length; i++) {
printf("'%d' ", key[i]);
}
printf("\n\n");
for (i = 0, j = 0; i < 256; i++){
j = (j + context->s[i] + key[i % length]) % 256;
temp = context->s[i];
context->s[i] = context->s[j];
context->s[j] = temp;
}
printf("New S array (hexadecimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%02X ", context->s[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n\nNew S array (decimal):\n\n");
for (i = 0; i < 256; i++) {
printf("%3d ", context->s[i]);
if ((i + 1) % 16 == 0) printf("\n");
}
printf("\n\n");
}
// here we declare a function to build a keystream
// and to encrypt our plain text ("HELLO")
// the function is called rc4Cipher and it expects
// 1. "Rc4Context* context" - pointer to an RC4 context
// struct
// that includes i, j, and a new s[256] after permutation
// we pass it with &ctx in int main()
// 2. "const unsigned char* input" - pointer to the data
// you want to process (which is "HELLO")
// we pass it with plaintext in int main()
// 3. "unsigned char* output" - pointer to a buffer where
// the result will be written (the encrypted or decrypted)
// values
// we pass it with ciphertext in int main()
// "size_t length" - number of bytes to process (6)
// we pass it with "sizeof plaintext" in int main()
void rc4Cipher(Rc4Context* context, const unsigned char* input, unsigned char* output, size_t length){
unsigned char temp;
// Restore context
unsigned int i = context->i;
unsigned int j = context->j;
unsigned char* s = context->s;
// Encryption loop
while (length > 0)
{
// Adjust indices
i = (i + 1) % 256;
j = (j + s[i]) % 256;
// Swap the values of S[i] and S[j]
temp = s[i];
s[i] = s[j];
s[j] = temp;
// Valid input and output?
if (input != NULL && output != NULL)
{
//XOR the input data with the RC4 stream
*output = *input ^ s[(s[i] + s[j]) % 256];
//Increment data pointers
input++;
output++;
}
// Remaining bytes to process
length--;
}
// Save context
context->i = i;
context->j = j;
}
int main(void)
{
Rc4Context ctx;
const unsigned char key[] = "demo";
rc4Init(&ctx, key, strlen((const char*)key));
// we prepare the plain text to encrypt
const unsigned char plaintext[] = "HELLO";
// the below lines will be of size 6
// the size of the plaintext[]
// 'H' 'E' 'L' 'L' '0' '\0'
unsigned char ciphertext[sizeof plaintext];
unsigned char decrypted[sizeof plaintext];
// once we created our new S array it is time
// to generate a key stream and encrypt the plaintext
// to do this we call the rc4Cipher function that
// will perform the encryption. we add 4 pearameters
// to call the function
// 1. "&ctx" passes the address of Rc4Context structure
// by this time this structure contains i, j, and
// the new s[256] array after we performed permutations
// with our key "demo"
// 2. "plaintext" this is our input data ("HELLO")
// because of how our function is built -->
// const unsigned char* input, the [0] element of
// the array plaintext[] will be addressed by the
// function, and it will continu untill it processes
// the length bytes
// 3. ciphertext - output buffer (where encrypted data
// will be written)
// It must be big enough to hold at least `length` bytes
// 4. sizeof plaintext - tells the function how many
// bytes to process (6 in case of "HELLO")
rc4Cipher(&ctx, plaintext, ciphertext, sizeof plaintext);
// to decrypt, we must reset context with the same key
Rc4Context ctx2;
rc4Init(&ctx2, key, strlen((const char*)key));
rc4Cipher(&ctx2, ciphertext, decrypted, sizeof ciphertext);
// Step 5: print results
printf("Plaintext : %s\n", plaintext);
printf("Ciphertext (hexadecimal): ");
for (size_t i = 0; i < sizeof plaintext; i++) {
printf("%02X ", ciphertext[i]);
}
printf("\n");
printf("Ciphertext (decimal): ");
for (size_t i = 0; i < sizeof plaintext; i++) {
printf("%3d ", ciphertext[i]);
}
printf("\nDecrypted : %s\n", decrypted);
return 0;
}