Skip to main content
Stream Ciphers Beginner

The RC4 Stream Cipher

RC4 was the simplest, fastest cipher of its era. It powered SSL, WEP, and WPA for over a decade before a decade of accumulating bias attacks quietly killed it. Learn how it works and exactly why it's insecure today.

PL
Pashalis Laoutaris
August 4, 2026
7 min read

Interactive RC4 Visualizer

🔐 RC4 Stream Cipher

Defaults match the standard published RC4 test vector.
Enter text and click a button to start!
Keystream Bytes
Click Encrypt to generate the keystream and ciphertext.

The RC4 Stream Cipher

Introduction

RC4 (“Rivest Cipher 4”) was designed by Ron Rivest in 1987 for RSA Security. Unlike the block ciphers covered elsewhere on this site, RC4 is a stream cipher: instead of processing data in fixed-size blocks, it generates a continuous pseudorandom byte stream that’s simply XORed with the plaintext, byte by byte. Its extreme simplicity made it enormously fast in software. For over a decade, RC4 was the default cipher behind SSL/TLS, WEP, and WPA. It’s also now considered broken, and understanding exactly why is a great lesson in why “simple and fast” isn’t the same as “secure.”

Table of Contents

How RC4 Works

RC4 operates in two phases:

  1. Key-Scheduling Algorithm (KSA): uses the secret key to scramble a 256-byte internal state array into a key-dependent permutation.
  2. Pseudo-Random Generation Algorithm (PRGA): repeatedly shuffles that state array to spit out one keystream byte at a time. The sender XORs it with plaintext to encrypt, and the receiver XORs it with ciphertext to decrypt (XOR is its own inverse, so encryption and decryption are literally the same operation).

The Key-Scheduling Algorithm (KSA)

for i = 0 to 255:
    S[i] = i

j = 0
for i = 0 to 255:
    j = (j + S[i] + key[i mod key_length]) mod 256
    swap(S[i], S[j])

This initializes S as the identity permutation, then uses the key to shuffle it. Every key produces a different, unpredictable-looking permutation of the 256 possible byte values.

The Pseudo-Random Generation Algorithm (PRGA)

i = 0
j = 0
for each byte of plaintext:
    i = (i + 1) mod 256
    j = (j + S[i]) mod 256
    swap(S[i], S[j])
    keystream_byte = S[(S[i] + S[j]) mod 256]
    ciphertext_byte = plaintext_byte XOR keystream_byte

Each iteration continues shuffling the state array and emits one keystream byte, for as many bytes as the message requires. RC4 can encrypt a stream of any length, with no padding, no block boundaries, and almost no computational overhead.

Interactive Visualizer

The visualizer above runs genuine RC4 (real KSA, real PRGA) and matches the classic published RC4 test vectors exactly. Try the default key “Key” and plaintext “Plaintext”: you should see ciphertext bbf316e8d940af0ad3, the standard reference value used to validate RC4 implementations.

A Worked Example

Using one of RC4’s most commonly cited reference test vectors:

  • Key: “Key”
  • Plaintext: “Plaintext”
  • Ciphertext: bb f3 16 e8 d9 40 af 0a d3

Python Implementation

The KSA and PRGA pseudocode above translate almost line for line into Python:

def rc4_ksa(key):
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]
    return S

def rc4_prga(S, length):
    S = S.copy()
    i = j = 0
    keystream = bytearray()
    for _ in range(length):
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        keystream.append(S[(S[i] + S[j]) % 256])
    return bytes(keystream)

def rc4_crypt(key, data):
    """Encrypts and decrypts: XORing the keystream twice recovers the original data."""
    S = rc4_ksa(key)
    keystream = rc4_prga(S, len(data))
    return bytes(d ^ k for d, k in zip(data, keystream))

if __name__ == "__main__":
    key = b"Key"
    plaintext = b"Plaintext"

    ciphertext = rc4_crypt(key, plaintext)
    recovered = rc4_crypt(key, ciphertext)

    print(f"Ciphertext: {ciphertext.hex()}")
    print(f"Recovered:  {recovered}")

This reproduces the classic reference test vector above exactly: bbf316e8d940af0ad3. Calling rc4_crypt a second time on the ciphertext, with the same key, recovers b"Plaintext" exactly, since generating the identical keystream and XORing it in again undoes the first XOR.

Limitations

This is a direct, complete implementation of the real algorithm; RC4 has no simplified “toy” version to speak of, since the whole cipher is already this small:

  • No built-in defense against the biases this article covers. The code faithfully reproduces RC4, including every one of its documented statistical weaknesses. There’s no way to “implement RC4 more carefully” to avoid them. The weaknesses are in the algorithm itself, not in any particular implementation.
  • No key-length validation. RC4 technically supports key lengths from 1 to 256 bytes; this code doesn’t check that the supplied key is a sensible length for the intended use.
  • Not intended for real security, in any form. As the Why RC4 Is Broken section below documents, this cipher is formally prohibited in TLS today; this code exists to demonstrate the KSA/PRGA mechanics, not to encrypt anything that matters.

Why RC4 Is Broken

RC4’s downfall wasn’t a single catastrophic flaw. It was a slow accumulation of statistical biases discovered over roughly two decades:

  • Biased keystream bytes: Early bytes of RC4’s keystream are measurably non-random: the very first byte, for instance, is 0 far more often than the 1-in-256 chance a truly random byte would be. Given enough ciphertexts encrypted under related keys, these biases leak information about the plaintext.
  • The FMS attack (2001): Fluhrer, Mantin, and Shamir showed that RC4’s KSA leaks information about the key itself when keys are related in the way WEP generated them (a short fixed secret combined with a public, changing initialization vector). That relationship enables full key recovery from a modest number of captured packets. This attack alone made WEP practically breakable within minutes.
  • Broadcast and bias attacks (2013-2015): Researchers demonstrated increasingly practical attacks recovering plaintext (including session cookies) from RC4-protected TLS traffic, given enough repeated encryptions of the same data. These attacks exploited the same class of keystream biases at internet scale.

By 2015, RFC 7465 formally prohibited RC4 in TLS entirely, and every major browser and server has since removed support.

RC4’s Legacy

RC4’s story is a case study in why cryptographic transitions take so painfully long. It was known to have theoretical weaknesses years before it was actually removed from real-world protocols. Ripping out a cipher used everywhere is slow, risky, compatibility-sensitive work. Its replacement in modern TLS is almost always ChaCha20. ChaCha20 was specifically designed to offer RC4’s core appeal (fast, simple, efficient in pure software without special hardware), while being built from the start to resist exactly the kind of statistical attacks that eventually killed RC4.

FAQ

Is RC4 still used anywhere?

It’s been formally prohibited in TLS since 2015 (RFC 7465) and removed from virtually all modern browsers, servers, and libraries. Any system still using it should migrate to AES-GCM or ChaCha20-Poly1305 immediately. RC4 offers no meaningful security today.

It’s extraordinarily simple to implement, extremely fast in software (especially compared to block ciphers on 1990s and 2000s hardware), and requires no padding since it’s a pure stream cipher. That was a very attractive combination before its statistical weaknesses were fully understood.

What exactly made WEP so vulnerable?

WEP combined a short static key with a small, frequently repeating public initialization vector (IV). That fed RC4 related keys across many packets, and the FMS attack could exploit those related keys to recover the static key directly from captured traffic. That was a flaw in how WEP used RC4, compounded by weaknesses in RC4’s KSA itself.

What replaced RC4?

In TLS, RC4 was replaced primarily by AES-GCM and later ChaCha20-Poly1305. Both are authenticated encryption schemes that provide integrity protection RC4 never had, on top of much stronger confidentiality guarantees.

Is RC4 a block cipher or a stream cipher?

Stream cipher. It generates a continuous keystream and XORs it with plaintext one byte at a time, with no fixed block size, unlike AES, DES, or the other ciphers covered elsewhere on this site.

References

  1. Rivest, R. RC4 was originally a trade secret; details became public in 1994 when source code was leaked to the Cypherpunks mailing list.

  2. Fluhrer, S., Mantin, I., and Shamir, A. “Weaknesses in the Key Scheduling Algorithm of RC4.” SAC 2001 (the original FMS attack paper).

  3. RFC 7465. “Prohibiting RC4 Cipher Suites.” IETF, 2015. Available at: https://datatracker.ietf.org/doc/html/rfc7465

  4. Wikipedia. “RC4.” Available at: https://en.wikipedia.org/wiki/RC4