Cryptography - RC4 Algorithm



Rivest Cypher 4 is referred to as RC4. The stream cipher known as RC4 was created in 1987 by Ron Rivest. RC4 encrypts data bit by bit because it is a stream cipher. From all the stream ciphers, RC4 is the one that is used the most because of its simplicity and speed.

Although RC4 is renowned for its speed and ease of use in software, it has been discovered to have a number of vulnerabilities that make it insecure. It is highly vulnerable if the output keystream's beginning is not removed or if linked or non-random keys are used. Particularly, the use of RC4 has resulted to the creation of somewhat insecure protocols like WEP.

In 2015, there were rumours circulating that certain state cryptologic organisations may break RC4, if it was used in the TLS protocol. The Internet Engineering Task Force's RFC 7465 forbids using RC4 in TLS, and Mozilla and Microsoft have made recommendations along these lines.

How RC4 Works?

A keystream, or pseudo-random bit stream, is produced by RC4. These can be used for encryption using bit-wise exclusive or combined with the plaintext, just like any other stream cipher. As exclusive-OR is a symmetric operation, the same process is followed for decryption.

The cipher creates the keystream using a hidden internal state that is split into two parts −

  • The 256 bytes that are available are all permuted.
  • Each index pointer has eight bits.

It is known that the key-scheduling approach uses a variable-length key (usually between 40 and 256 bits) to initialise the permutation. The bitstream is then generated using a pseudo-random generating method.

Below is the images for encryption using XOR.

RC4 Algorithm

For Ecnryption

  • The Plaintext and a secret key are input by the user.
  • The encryption algorithm uses the KSA and PRGA algorithms to generate the keystream for the secret key that was entered.
  • The produced keystream is XORed with the plaintext. Because RC4 is a stream cipher, the encrypted text is created using byte-by-byte XORing.
  • Now the intended recipient receives this encrypted text in encrypted form.

For Decryption

  • The ciphertext is decrypted using the same byte-wise X-OR method.

Key-Generation

A 256-byte state vector S, containing elements S[0] to S[255], is initialised using a variable-length key ranging from 1 to 256 bytes. A byte k is created from S for encryption and decryption by carefully choosing one of the 255 entries, after which the entries in S perform another permutation.

Key-Scheduling

A temporary vector T is generated by setting the values of S's entries to operate in ascending order, ranging from 0 to 255. The key k is assigned to T if its length is 256 bytes. If not, the first k-len elements of T are copied from K for a key of length (k-len) bytes, and K is then repeated as many times as needed to fill T. Here's a representation of the concept −

The arrays S and T are initially initialised. Next, we use array K to calculate array T. Lastly, we apply the method given by array T to the first permutation of array S.

Example

// Initialize array S
int[] S = new int[256];
for (int i = 0; i < 256; i++) {
   S[i] = i;
}

// Calculate array T using array K
int[] T = new int[256];
for (int i = 0; i < 256; i++) {
   T[i] = K[i % K.length - len];
}

This Java code initialize array S with values from 0 to 255, and then calculating array T using the modulus operation on array K.

# Initializing arrays S and T
S = list(range(256))
T = [0] * 256

# Calculating array T using array K
for i in range(256):
   T[i] = K[i % len(K) - len]

# Performing initial permutation of array S using array T
j = 0
for i in range(256):
   j = (j + S[i] + T[i]) % 256
   # Swap S[i] and S[j]
   S[i], S[j] = S[j], S[i]

The input key will not be used after the vector S is set up. In this step, a scheme determined by S's current configuration is used to swap out each S[i] algorithm byte with a different byte in S. After reaching at S[255], the method proceeds, again starting from S[0].

# Initialize array S with values from 0 to 255
S = list(range(256))

# Start swapping process
i = 0
j = 0
for _ in range(256):
   i = (i + 1) % 256
   j = (j + S[i]) % 256
   # Swap S[i] and S[j]
   S[i], S[j] = S[j], S[i]

Features of RC4

  • RC4 uses symmetric keys, meaning that the same key is used for both encryption and decryption.
  • RC4 is an example of a stream cipher algorithm, means that data is encrypted and decrypted one byte at a time. The ciphertext is created by XORing the key stream of pseudorandom bits that it creates with the plaintext.
  • RC4 is flexible to various security needs since it allows different key sizes ranging from 40 bits to 2048 bits.
  • The RC4 encryption technique is quick and effective, making it a good fit for low-power gadgets and applications that need to send data at a high speed.
  • RC4 is widely used in many different applications, like file encryption, virtual private networks (VPN), secure sockets layer (SSL), and wireless networks.
  • RC4 is vulnerable to a number of attacks, one of which is a bias in the initial few bytes of the keystream that can be used to retrieve the key. Therefore, it is no longer advised to use RC4 in new applications.

Implementation using Python

Now we will implement the RC4 with the help of Python.

Example

# Function for encryption
def encrypt_rc4():

   global secret_message, secret_key, n

   # Given text and key
   secret_message = "101101100110"
   secret_key = "001010010010"

   # n is the number of bits 
   n = 3

   print("Secret message : ", secret_message)
   print("Secret key : ", secret_key)
   print("n : ", n)

   print(" ")

   # The initial state vector array
   S = [i for i in range(0, 2**n)]
   print("S : ", S)

   key_list = [secret_key[i:i + n] for i in range(0, len(secret_key), n)]

   # Convert key stream to decimal
   for i in range(len(key_list)):
      key_list[i] = int(key_list[i], 2)

   # Convert secret message to decimal
   global pt

   pt = [secret_message[i:i + n] for i in range(0, len(secret_message), n)]

   for i in range(len(pt)):
      pt[i] = int(pt[i], 2)

   print("Secret message (in array form): ", pt)

   # Making key stream equal to the length of state vector
   diff = int(len(S)-len(key_list))

   if diff != 0:
      for i in range(0, diff):
         key_list.append(key_list[i])

   print("Key list : ", key_list)
   print(" ")

   # Perform the KSA algorithm
   def key_scheduling_algorithm():
      j = 0
      N = len(S)
		
      # Iterate over the range [0, N]
      for i in range(0, N):
		
         # Find the key
         j = (j + S[i]+key_list[i]) % N
			
         # Update S[i] and S[j]
         S[i], S[j] = S[j], S[i]
         print(i, " ", end ="")
			
         # Print S
         print(S)

      initial_permutation_array = S
		
      print(" ")
      print("Initial permutation array : ",
         initial_permutation_array)

   print("KSA process : ")
   print(" ")
   key_scheduling_algorithm()
   print(" ")

   # Perform PGRA algorithm
   def pseudo_random_generation_algorithm():

      N = len(S)
      i = j = 0
      global key_stream
      key_stream = []

      # Iterate over [0, length of pt]
      for k in range(0, len(pt)):
         i = (i + 1) % N
         j = (j + S[i]) % N
			
         # Update S[i] and S[j]
         S[i], S[j] = S[j], S[i]
         print(k, " ", end ="")
         print(S)
         t = (S[i]+S[j]) % N
         key_stream.append(S[t])

      # Print the key stream
      print("Generated key stream : ", key_stream)
      print(" ")

   print("Key stream generation : ")
   print(" ")
   pseudo_random_generation_algorithm()

   # Performing XOR between generated key stream and secret message
   def perform_xor():
      global cipher_text
      cipher_text = []
      for i in range(len(pt)):
         c = key_stream[i] ^ pt[i]
         cipher_text.append(c)

   perform_xor()

   # Convert the encrypted text to bits form
   encrypted_to_bits = ""
   for i in cipher_text:
      encrypted_to_bits += '0'*(n-len(bin(i)[2:]))+bin(i)[2:]

   print(" ")
   print("Cipher text : ", encrypted_to_bits)

# Execute function
encrypt_rc4()

print("*********************************************************")

# Function for decryption of data
def decrypt_rc4():

   # The initial state vector array
   S = [i for i in range(0, 2**n)]

   key_list = [secret_key[i:i + n] for i in range(0, len(secret_key), n)]

   # Convert key stream to decimal
   for i in range(len(key_list)):
      key_list[i] = int(key_list[i], 2)

   # Convert secret message to decimal
   global pt

   pt = [secret_message[i:i + n] for i in range(0, len(secret_message), n)]

   for i in range(len(pt)):
      pt[i] = int(pt[i], 2)

   # Making key stream equal to the length of state vector
   diff = int(len(S)-len(key_list))

   if diff != 0:
      for i in range(0, diff):
         key_list.append(key_list[i])

   print(" ")

   # KSA algorithm
   def key_scheduling_algorithm():
      j = 0
      N = len(S)
		
      # Iterate over the range [0, N]
      for i in range(0, N):
         j = (j + S[i]+key_list[i]) % N
			
         # Update S[i] and S[j]
         S[i], S[j] = S[j], S[i]
         print(i, " ", end ="")
         print(S)

      initial_permutation_array = S
      print(" ")
      print("Initial permutation array : ",
         initial_permutation_array)

   print("KSA process : ")
   print(" ")
   key_scheduling_algorithm()
   print(" ")

   # Perform PRGA algorithm
   def pseudo_random_generation_algorithm():

      N = len(S)
      i = j = 0
      global key_stream
      key_stream = []

      # Iterate over the range
      for k in range(0, len(pt)):
         i = (i + 1) % N
         j = (j + S[i]) % N
			
         # Update S[i] and S[j]
         S[i], S[j] = S[j], S[i]
         print(k, " ", end ="")
         print(S)
         t = (S[i]+S[j]) % N
         key_stream.append(S[t])

      print("Generated key stream : ", key_stream)
      print(" ")

   print("Key stream generation : ")
   print(" ")
   pseudo_random_generation_algorithm()

   # Perform XOR between generated key stream and cipher text
   def perform_xor():
      global original_text
      original_text = []
      for i in range(len(cipher_text)):
         p = key_stream[i] ^ cipher_text[i]
         original_text.append(p)

      perform_xor()

   # convert the decrypted text to the bits form
   decrypted_to_bits = ""
   for i in original_text:
      decrypted_to_bits += '0'*(n-len(bin(i)[2:]))+bin(i)[2:]

   print(" ")
   print("Decrypted text : ",
      decrypted_to_bits)

# execute function
decrypt_rc4()

Following is the output of the above example −

Input/Output

Secret message :  101101100110
Secret key :  001010010010
n :  3
 
S :  [0, 1, 2, 3, 4, 5, 6, 7]
Secret message (in array form):  [5, 5, 4, 6]
Key list :  [1, 2, 2, 2, 1, 2, 2, 2]
 
KSA process : 
 
0  [1, 0, 2, 3, 4, 5, 6, 7]
1  [1, 3, 2, 0, 4, 5, 6, 7]
2  [1, 3, 7, 0, 4, 5, 6, 2]
3  [1, 0, 7, 3, 4, 5, 6, 2]
4  [1, 0, 7, 3, 6, 5, 4, 2]
5  [1, 0, 7, 3, 6, 5, 4, 2]
6  [1, 0, 7, 4, 6, 5, 3, 2]
7  [1, 0, 7, 4, 6, 5, 3, 2]
 
Initial permutation array :  [1, 0, 7, 4, 6, 5, 3, 2]
 
Key stream generation : 
 
0  [0, 1, 7, 4, 6, 5, 3, 2]
1  [0, 1, 2, 4, 6, 5, 3, 7]
2  [0, 1, 2, 4, 6, 5, 3, 7]
3  [0, 6, 2, 4, 1, 5, 3, 7]
Generated key stream :  [1, 1, 0, 7]
 
 
Cipher text :  100100100001
*********************************************************
KSA process : 
 
0  [1, 0, 2, 3, 4, 5, 6, 7]
1  [1, 3, 2, 0, 4, 5, 6, 7]
2  [1, 3, 7, 0, 4, 5, 6, 2]
3  [1, 0, 7, 3, 4, 5, 6, 2]
4  [1, 0, 7, 3, 6, 5, 4, 2]
5  [1, 0, 7, 3, 6, 5, 4, 2]
6  [1, 0, 7, 4, 6, 5, 3, 2]
7  [1, 0, 7, 4, 6, 5, 3, 2]
 
Initial permutation array :  [1, 0, 7, 4, 6, 5, 3, 2]
 
Generated key stream :  [1, 1, 0, 7]
 
Key stream generation : 
 
0  [0, 1, 7, 4, 6, 5, 3, 2]
1  [0, 1, 2, 4, 6, 5, 3, 7]
2  [0, 1, 2, 4, 6, 5, 3, 7]
3  [0, 6, 2, 4, 1, 5, 3, 7]
 
Decrypted text :  101101100110

Usage of RC4

Over the years, RC4 has grown in popularity and has become a standard in commercial applications. It has a reputation for being a simple, quick, and inexpensive encryption technology.

The key benefits of RC4 are its ease of implementation and use, as well as its speed of operation and deployment. It enables efficient and quick processing of large data streams. In terms of memory usage, RC4 stream ciphers are also efficient.

However, due to proof of flaws and cyberattacks in recent years, there have been calls to stop using RC4 encryption algorithms. Other drawbacks were identified, such as the inability to operate with small data streams and the need for additional investigation prior to implementing new systems.

The Internet Engineering Task Force (IETF) banned the usage of RC4 in TLS protocols in 2015. Because of threat vulnerabilities, Microsoft and Mozilla have also issued recommendations to limit the use of RC4. There are many RC4 based ecosystems such as WEP, WPA, BitTorrent protocol encryption, Microsoft Point-to-Point Encryption, etc.

RC4A is a more powerful variation of RC4. RC4A+ is a modified version of RC4 with a more complex 3-phase key schedule that is 1.7 times longer than the basic RC4.

Advantages of RC4

Below are some advantages of RC4 we need to consider while using RC4 −

  • RC4 is a very fast and efficient encryption method, making it appropriate for use in scenarios where these qualities are important.
  • As RC4 is a somewhat basic algorithm, it can be simply implemented in hardware or software.
  • RC4 is dynamic and adaptive to meet a range of security needs because it allows different key sizes.
  • RC4 is widely used in many different applications, like file encryption, virtual private networks (VPN), secure sockets layer (SSL), and wireless networks.

Disadvantages of RC4

RC4 has some serious advantages we need to consider while working with RC4 −

  • RC4 is not appropriate for new applications because of a number of tracked vulnerabilities. For example, the key can be recovered by taking advantage of a bias present in the first few bytes of the keystream.
  • Unlike to other encryption algorithms like AES or ChaCha20, RC4 is less secure because of some built-in vulnerabilities in its architecture.
  • The 2048 bit maximum key length for RC4 may not be enough for some applications that demand a higher level of encryption.
  • It is no longer suggested to use RC4 in new apps because of its vulnerabilities and flaws. Instead, AES-CTR or ChaCha20, two more secure stream cipher algorithms, need to be used.

RC4 and WEP

To protect wireless networks, a security mechanism known as WEP (Wired Equivalent Privacy) is used. It was developed with privacy and security features similar to those of a wired network. WEP was fully used in the early days of Wi-Fi, but it has several known vulnerabilities that make it incredibly easy to compromise. These weaknesses include a weak key scheduling mechanism (based on RC4), short initialization vectors (IVs), and a lack of key management tools.

Data is first encrypted using the RC4 technique in WEP before being sent over a wireless network. Because of weaknesses in the WEP protocol, network security can be compromised by many kinds of attacks, like the Fluhrer-Mantin-Shamir (FMS) and KoreK attacks, which can retrieve the WEP key.

WEP was a robust stream cipher, but implementation of RC4 introduced issues. For this reason, more secure encryption protocols like WPA (Wi-Fi Protected Access) and WPA2 eventually took the place of WEP.

Advertisements