n0blinder

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