Stream Ciphers

Imagine sending a 2GB video file securely. The only provably unbreakable cipher, the One-Time Pad, needs a key as long as the message. Generating and distributing gigabytes of true random key material for every transmission is completely impractical. Stream ciphers solve this problem. They keep the one-time pad's beautifully simple encryption rule. But they replace the impossible requirement — a truly random key — with something achievable. That something is a short random seed stretched into a long pseudorandom stream. The core move is a substitution that looks almost too simple to matter. Swap the random pad in the one-time pad for the output of a Pseudorandom Generator (PRG). This single substitution converts an information-theoretically secure but unusable scheme. It becomes a computationally secure and eminently practical one instead. Stream ciphers power real systems constantly. RC4 was for years the workhorse of TLS and WEP/WPA. ChaCha20 secures a large fraction of modern TLS and VPN traffic. A5/1 encrypts GSM voice calls. Understanding stream ciphers is understanding how theoretical pseudorandomness becomes deployable cryptography. This article explains precisely how a stream cipher is built from a PRG. It shows why the construction is secure exactly to the extent the PRG is secure. It also shows where the construction's sharp edges lie. These ciphers are also a byword for cryptographic disaster when reused carelessly. The infamous WEP protocol, and many other real-world breaks, trace back to exactly one misuse: reusing keystream. We will build the formal syntax and walk through the encryption/decryption algorithm. We will prove indistinguishability under a single security definition (IND-eavesdropper security). We will also trace a worked example bit by bit. By the end you will be able to state the construction precisely. You will prove its security reduces to PRG security. You will diagnose the classic two-time-pad attack. And you will see how this foundational idea sets up the more structured world of Block Ciphers and the PRP abstraction.

Key takeaways
  • A stream cipher encrypts by XORing plaintext with a pseudorandom keystream generated from a short shared key.
  • It is the practical descendant of the One-Time Pad, trading perfect secrecy for computational security and reusable short keys.
  • Security reduces directly to the security of the underlying Pseudorandom Generator (PRG): if the PRG is secure, the cipher is IND-secure against eavesdroppers.
  • The single fatal misuse is keystream reuse — encrypting two messages with the same key/nonce leaks their XOR (the "two-time pad" attack).
  • Real ciphers (RC4, ChaCha20, A5/1) instantiate this abstract PRG with fast, structured pseudorandom generators, often keyed additionally by a nonce.
  • This construction is deterministic and malleable, motivating the move to authenticated encryption and to Block Ciphers with the Pseudorandom Permutation abstraction for richer security guarantees.
The core insight

A stream cipher is exactly the one-time pad with the truly random pad replaced by the deterministic, seed-expanded output of a secure pseudorandom generator.

The idea

The One-Time Pad encrypts a plaintext m∈{0,1}nm \in \{0,1\}^n under a uniformly random key k∈{0,1}nk \in \{0,1\}^n by computing c=m⊕kc = m \oplus k, achieving perfect (information-theoretic) secrecy: the ciphertext reveals literally zero information about mm. Its fatal flaw is practical, not mathematical — the key must be as long as the message and must never be reused, making it unusable for high-volume communication. A stream cipher keeps the encryption rule c=m⊕keystreamc = m \oplus \text{keystream} but replaces the truly random pad with a pseudorandom one. Formally, let G:{0,1}s→{0,1}nG: \{0,1\}^s \to \{0,1\}^n be a PRG that stretches a short seed of length ss into a long pseudorandom output of length n≫sn \gg s. The shared secret key k∈{0,1}sk \in \{0,1\}^s is fed to GG to produce a keystream G(k)G(k), and encryption is

c=m⊕G(k).c = m \oplus G(k).

Decryption is identical: m=c⊕G(k)m = c \oplus G(k), since XOR is its own inverse. The entire security of the scheme now rests on one question: can an adversary distinguish G(k)G(k) from a truly random string of the same length, without knowing kk? If not — if GG is a secure PRG — then the ciphertext is computationally indistinguishable from a one-time-pad encryption, and the scheme achieves the strongest notion of secrecy achievable without a full-length key: indistinguishability under a single-message eavesdropping attack (IND-EAV / semantic security). Two important refinements make this construction deployable. First, real stream ciphers are usually keyed generators with a nonce: G(k,IV)G(k, \text{IV}), so that the same key kk can safely produce many different keystreams by varying a public initialization vector or nonce per message — this is how a fixed key can be reused across many messages without ever reusing keystream. Second, stream ciphers come in two structural flavors: synchronous stream ciphers, where the keystream depends only on (k,IV)(k, \text{IV}) and not on the ciphertext (RC4, ChaCha20, A5/1 are synchronous), and self-synchronizing stream ciphers, where the keystream depends on previously produced ciphertext bits (rare in modern designs, historically used in some CFB-mode constructions). This article focuses on the synchronous case, which is the dominant paradigm and the one that cleanly generalizes the one-time pad.

How it works

The mechanics are almost embarrassingly simple compared to the depth of the security argument behind them. Key generation picks a random seed; encryption expands it with the PRG and XORs; decryption re-expands the same seed and XORs again. ``` ALGORITHM StreamCipher // --- Key Generation --- function KeyGen(securityParam s) -> key: k <- Uniform({0,1}^s) // sample a truly random short seed return k // --- Encryption --- // G is a public, fixed PRG stretching s bits to n bits (n = |m|) function Encrypt(k, m): n <- length(m) ks <- G(k, n) // expand seed into n-bit keystream c <- m XOR ks // bitwise XOR, same length return c // --- Decryption --- function Decrypt(k, c): n <- length(c) ks <- G(k, n) // regenerate identical keystream m <- c XOR ks return m // --- Nonce-based variant used in real systems (RC4-style, ChaCha20-style) --- function EncryptWithNonce(k, IV, m): n <- length(m) ks <- G(k, IV, n) // keystream now depends on (k, IV) c <- m XOR ks return (IV, c) // IV sent alongside ciphertext, in the clear function DecryptWithNonce(k, IV, c): n <- length(c) ks <- G(k, IV, n) m <- c XOR ks return m ``` Correctness is immediate: decryption recovers mm because

c⊕G(k)=(m⊕G(k))⊕G(k)=m⊕(G(k)⊕G(k))=m⊕0=m,c \oplus G(k) = (m \oplus G(k)) \oplus G(k) = m \oplus (G(k) \oplus G(k)) = m \oplus 0 = m,

using associativity/commutativity of XOR and x⊕x=0x \oplus x = 0. The scheme requires the sender and receiver to hold the same key kk (and, in the nonce variant, to agree on or transmit the same IV) — this is a private-key (symmetric) primitive. The architecture below shows the data flow for the nonce-based synchronous stream cipher, the form used by ChaCha20 and modern designs.

Figure
Stream cipher architecture: PRG expands (key, nonce) into a keystream XORed with plaintext
key k nonce IV PRG G(k, IV) keystream ⊕ plaintext m ciphertext c IV transmitted alongside ciphertext (public, not secret)

Complexity & correctness

Time complexity. Encryption and decryption each cost exactly one PRG evaluation plus one XOR over nn bits: O(TG(n)+n)O(T_G(n) + n), where TG(n)T_G(n) is the time to produce nn bits of PRG output. For stream-oriented PRGs like RC4 or ChaCha20, keystream generation is O(n)O(n) with small constants (a handful of ALU operations per output byte), so total encryption/decryption time is linear in the message length, O(n)O(n), with no dependence on key length beyond a fixed initialization cost. This is asymptotically optimal — any cipher must read and touch every bit of the message at least once, so Ω(n)\Omega(n) is a lower bound, and stream ciphers meet it.

Space complexity. The scheme needs O(s)O(s) space to store the key (and IV), plus whatever internal state the PRG maintains (RC4: a 256-byte permutation table plus two indices; ChaCha20: a 512-bit state block), which is O(1)O(1) independent of message length — the keystream is generated incrementally, not stored in full. This is what makes stream ciphers attractive for streaming data of unknown or unbounded length: unlike block ciphers that pad to fixed block sizes, a stream cipher can encrypt one byte at a time as it arrives.

Correctness was shown above via the XOR self-inverse identity; it holds unconditionally, for any function GG, deterministic or not, keyed correctly on both ends.

Security — the real content. Correctness is trivial; the interesting claim is security, and it must be argued carefully, because the construction only inherits security from GG — it does not manufacture it. Define the experiment PrivKA,Πeav\text{PrivK}^{\text{eav}}_{A,\Pi}: an adversary AA picks two equal-length messages m0,m1m_0, m_1; a challenger picks a random key kk, flips a bit bb, and returns c=mb⊕G(k)c = m_b \oplus G(k); AA must guess bb. The scheme is IND-EAV secure if every efficient AA guesses correctly with probability at most 12+negl(s)\tfrac12 + \text{negl}(s). Proof sketch (reduction). Suppose an efficient adversary AA wins this game with advantage ε\varepsilon (i.e., guesses bb correctly with probability 12+ε\tfrac12 + \varepsilon). Build a PRG distinguisher DD that, given a string ww (either G(k)G(k) for random kk, or a truly uniform random string of the same length), runs AA, computes c=mb⊕wc = m_b \oplus w for a randomly chosen bit bb, and outputs 11 if AA's guess equals bb.

  • If ww is truly random, then c=mb⊕wc = m_b \oplus w is a uniformly random string independent of bb — this is exactly the one-time pad case, which has perfect secrecy, so AA guesses bb correctly with probability exactly 12\tfrac12.
  • If w=G(k)w = G(k), then DD perfectly simulates the real eavesdropping experiment, so AA guesses correctly with probability 12+ε\tfrac12 + \varepsilon.

Hence DD distinguishes G(k)G(k) from random with advantage exactly ε\varepsilon. Since GG is a secure PRG, DD's advantage must be negligible, forcing ε\varepsilon to be negligible. This is a tight reduction: the cipher's insecurity is upper-bounded by the PRG's insecurity, and no attack can do better against the cipher than the best attack does against the PRG itself. This is the entire content of "stream ciphers emulate the one-time pad computationally": wherever the one-time pad's argument used true randomness, the reduction substitutes indistinguishability from true randomness, and the eavesdropper's view is unaffected up to negligible probability.

Honest tradeoffs.

  • This proof only covers a single ciphertext per key (the "eav" game encrypts once). It says nothing about reusing the same keystream for two messages — and indeed reuse is catastrophically insecure, independent of how good GG is (see Worked Example).
  • The construction as given is not CCA-secure or even secure against active tampering: XOR encryption is malleable — flipping a ciphertext bit flips the corresponding plaintext bit predictably, so an attacker who can inject ciphertext can make targeted plaintext edits without ever learning the key. Real deployments (e.g., ChaCha20-Poly1305) pair the stream cipher with a MAC for authenticated encryption.
  • A PRG is required to be a secure PRG (indistinguishable from random to all efficient adversaries), not merely "look complicated"; historically several deployed generators (RC4's early keystream bytes, A5/1) had statistical biases that violated this and were exploited in real attacks.

Worked example

We trace a small, explicit instance so the mechanics and the classic vulnerability are both visible in bits, not just symbols. Suppose (toy-sized for clarity) the PRG GG maps a 4-bit seed to an 16-bit keystream via some fixed public expansion rule (in a real system this would be RC4/ChaCha20-style; here we hardcode illustrative output).

StepValue
Key kk`1011`
G(k)G(k) (16-bit keystream)`1100101100111010`
Plaintext m1m_1 = "HI" (ASCII, 16 bits)`0100100001001001`
c1=m1⊕G(k)c_1 = m_1 \oplus G(k)`1000001101110011`

Decryption: the receiver, holding the same kk, recomputes G(k)=G(k) = `1100101100111010` and computes

c1⊕G(k)=1000001101110011⊕1100101100111010=0100100001001001=m1.✓c_1 \oplus G(k) = 1000001101110011 \oplus 1100101100111010 = 0100100001001001 = m_1. ✓

Now the classic pitfall — keystream reuse ("two-time pad"). Suppose the sender mistakenly reuses the same key (and, in a nonce-based scheme, the same nonce) to send a second message m2=m_2 = "NO" = `0100111001001111`:

StepValue
c2=m2⊕G(k)c_2 = m_2 \oplus G(k) (same keystream!)`1000010001110001`

An eavesdropper who intercepts both c1c_1 and c2c_2, without knowing kk at all, computes:

c1⊕c2=m1⊕G(k)⊕m2⊕G(k)=m1⊕m2,c_1 \oplus c_2 = m_1 \oplus G(k) \oplus m_2 \oplus G(k) = m_1 \oplus m_2,

because G(k)⊕G(k)=0G(k) \oplus G(k) = 0 cancels exactly. Concretely:

c1⊕c2=1000001101110011⊕1000010001110001=0000011100000010=m1⊕m2.c_1 \oplus c_2 = 1000001101110011 \oplus 1000010001110001 = 0000011100000010 = m_1 \oplus m_2.

This equals `0100100001001001 XOR 0100111001001111`, the XOR of the two plaintexts — recoverable entirely from public ciphertext, no key needed. Given known structure (English text, known headers, ASCII patterns), m1⊕m2m_1 \oplus m_2 is frequently enough to recover both m1m_1 and m2m_2 via crib-dragging (guessing likely words in one message and checking if XORing them into m1⊕m2m_1 \oplus m_2 yields plausible text in the other). This is exactly the attack that broke the original WEP protocol and the Venona project's Soviet one-time-pad reuse in the 1940s — the mathematics is identical whether the "pad" is a true random pad reused, or a PRG-based keystream reused.

Common pitfalls & traps

PitfallWhy it happensConsequence
Keystream reuse (same key + nonce twice)Nonce not incremented, counter reset, or key reused without fresh IVc1⊕c2=m1⊕m2c_1 \oplus c_2 = m_1 \oplus m_2 leaks both plaintexts; the single most catastrophic stream-cipher bug
Treating any "complex-looking" PRG as secureConfusing statistical complexity with cryptographic indistinguishabilityWeak PRGs (biased RC4 initial bytes, LFSR-based generators) are distinguishable and breakable (e.g., A5/1 attacks)
No integrity protectionXOR-based encryption is inherently malleableAttacker flips ciphertext bits to flip chosen plaintext bits undetected — must pair with a MAC
Predictable or short noncesImplementer assumes nonce uniqueness "probably" holdsBirthday-bound nonce collisions silently reintroduce keystream reuse
Assuming security beyond a single ciphertextMisreading the IND-EAV definition as covering many messages under one keyThe formal single-message game does not, by itself, guarantee safety of naive multi-message reuse

Pioneers & history

The stream cipher idea is the direct engineering descendant of the one-time pad, refracted through the mid-20th-century theory of pseudorandomness and computational hardness.

Origins · 1 minGilbert Vernam and how this was discovered
GV
Gilbert Vernam
1890–1960 · American engineer at AT&T who patented the additive stream cipher (the "Vernam cipher") in 1919, XORing telegraph signals with a key tape — the direct ancestor of the modern stream cipher

CS
Claude Shannon
1916–2001 · American mathematician whose 1949 paper "Communication Theory of Secrecy Systems" proved the one-time pad's perfect secrecy and thereby defined exactly what a stream cipher can never fully achieve with a short key

History
1917–1919
Gilbert Vernam devises the additive telegraph cipher, the prototype one-time-pad/stream-cipher construction.
1949
Claude Shannon formalizes perfect secrecy and proves the one-time pad optimal, framing the tradeoff stream ciphers exploit.
1980
s–1987: RC4 designed by Ron Rivest (kept as a trade secret until leaked in 1994) becomes the dominant stream cipher in SSL/TLS and WEP.
2001
Fluhrer–Mantin–Shamir attack exposes structural bias in RC4's key scheduling, foreshadowing its eventual deprecation.
2008
Daniel J. Bernstein publishes ChaCha20, a modern ARX-based stream cipher later standardized (RFC 7539) for TLS and used in WireGuard.

Where this matters

Stream ciphers are load-bearing infrastructure across the internet and telecommunications stack. TLS 1.2/1.3 offers ChaCha20-Poly1305 as a cipher suite, favored especially on mobile devices lacking AES hardware acceleration, because ChaCha20's PRG-style keystream generation is fast in pure software. WireGuard, a modern VPN protocol, uses ChaCha20 as its sole symmetric cipher. GSM cellular networks historically encrypted voice traffic with the A5/1 stream cipher, whose LFSR-based PRG was eventually shown to be distinguishable from random, enabling real-time call interception. WEP (802.11 Wi-Fi security) built directly on RC4 and was broken largely due to nonce/IV reuse — the exact two-time-pad flaw described above — motivating its replacement by WPA/WPA2. Beyond networking, disk and database encryption products sometimes use stream-cipher-style keystream generation for its low per-byte overhead and suitability for random-access or streaming workloads. The general lesson — build practical encryption from a PRG stretching a short key into a long pseudorandom stream — also underlies the design philosophy of counter-mode (CTR) block cipher operation, which turns a block cipher into a stream cipher by using it as the PRG itself, directly bridging this article to the Pseudorandom Permutation abstraction used to model AES and its relatives.

Ask the tutor: How does CTR mode turn a block cipher into a stream cipher, and does it inherit the same keystream-reuse vulnerability?

Is a stream cipher as secure as the one-time pad?

No — it trades perfect (information-theoretic) secrecy for computational secrecy. The one-time pad is secure against any adversary, even one with unlimited computing power, because the key is truly random and as long as the message. A stream cipher is only secure against efficient (polynomial-time) adversaries, and only because the PRG's output is computationally, not statistically, indistinguishable from random. Given unbounded time, an adversary could in principle brute-force the short seed and recover the entire keystream. In exchange for this weaker guarantee, stream ciphers need only a short, reusable-in-structure key, making them practical for real communication, whereas the one-time pad's key-length requirement makes it essentially unusable outside constrained scenarios like historical diplomatic and intelligence channels.

Why is reusing the keystream so catastrophic compared to other cryptographic mistakes?

Because XOR cancellation makes the attack require zero cryptanalysis of the underlying PRG. If c1=m1⊕G(k)c_1 = m_1 \oplus G(k) and c2=m2⊕G(k)c_2 = m_2 \oplus G(k) use the identical keystream, then c1⊕c2=m1⊕m2c_1 \oplus c_2 = m_1 \oplus m_2 exactly, algebraically, regardless of how strong GG is. This converts a cryptographic attack into a classical language/pattern-recovery problem (crib-dragging), often solvable by hand once enough reused-key ciphertexts accumulate — this is precisely how Venona-era one-time-pad reuse and WEP's IV reuse were both broken, decades apart, by the same underlying algebraic identity.

Why do real stream ciphers use a nonce or IV in addition to the key?

A fixed key alone would force generating a brand-new random key for every message to avoid keystream reuse, defeating the purpose of a shared symmetric key. Introducing a public nonce lets G(k,IV)G(k, \text{IV}) produce a different keystream for each message while reusing the same secret key across many messages, as long as the (key, nonce) pair is never repeated. The nonce does not need to be secret — it is typically sent alongside the ciphertext — but it does need to be unique per message under a given key, which is why protocols use counters or sufficiently long random nonces to make collisions negligible.

Can a stream cipher provide message integrity or authentication on its own?

No. XOR-based stream cipher encryption is malleable: an attacker who intercepts c=m⊕G(k)c = m \oplus G(k) can compute c′=c⊕Δc' = c \oplus \Delta for any chosen Δ\Delta, and the receiver will decrypt c′c' to m⊕Δm \oplus \Delta — a targeted, predictable modification, with no error or rejection, since there is no built-in check on ciphertext validity. Confidentiality and integrity are logically separate goals; real systems pair a stream cipher with a message authentication code (e.g., ChaCha20-Poly1305 combines the ChaCha20 stream cipher with the Poly1305 MAC) to achieve authenticated encryption, detecting any tampering.

How does a stream cipher relate to block ciphers used in counter (CTR) mode?

CTR mode turns a block cipher into a stream cipher by using the block cipher itself, evaluated on a counter value Ek(nonce∥i)E_k(\text{nonce} \| i) for i=0,1,2,…i = 0, 1, 2, \dots, as the keystream generator: this is literally the "PRG" GG from this article, instantiated using a Pseudorandom Permutation rather than a dedicated stream cipher design. This shows the two constructions are not competitors but the same underlying idea (PRG-based keystream XOR) with different engines producing the pseudorandom stream — a dedicated stream cipher like ChaCha20, or a block cipher run in counter mode.

Why were RC4 and A5/1 eventually deprecated despite years of successful deployment?

Both were found to have statistical biases in their keystream that violated the ideal PRG requirement of indistinguishability from uniform randomness. RC4's early output bytes were shown (Fluhrer–Mantin–Shamir, 2001, and later refinements) to be non-uniform in ways exploitable to recover plaintext given enough ciphertexts under related keys, which was catastrophic combined with WEP's weak key-scheduling and IV reuse. A5/1's linear feedback shift register design admitted known-plaintext and even ciphertext-only attacks recovering the internal state in real time. Both cases confirm the theoretical point precisely: the security proof for the stream cipher construction is only as strong as the PRG's actual, empirically verified indistinguishability from random — a "PRG" that fails this property invalidates the entire security reduction.

What is the difference between synchronous and self-synchronizing stream ciphers?

A synchronous stream cipher generates its keystream purely from (k,IV,position)(k, \text{IV}, \text{position}), independent of the ciphertext itself; RC4, ChaCha20, and CTR-mode block ciphers are all synchronous. A self-synchronizing stream cipher instead derives keystream bits from a window of previous ciphertext, so a single bit error or resynchronization event affects only a bounded number of subsequent output bits before the cipher "heals." Self-synchronizing designs (historically related to CFB mode) trade off some parallelism and require careful analysis of error propagation, and are far less common in modern high-throughput systems than synchronous designs.

Before this

Where this leads