Solitaire: a stream cipher you run with a deck of cards
Most explanations of stream ciphers ask you to imagine a keystream coming out of a black box. Solitaire has no black box. Bruce Schneier designed it in 1999 for Neal Stephenson’s Cryptonomicon, where it is called Pontifex, so that a person could run a modern-style stream cipher with nothing but a deck of cards. The internal state is the order of the deck, and you can watch it change.
I built Solitaire Cipher Lab to make that state visible. It is an interactive page you can step through card by card, plus a Python package with a command line. It is a teaching tool. Solitaire has known statistical biases, measured below, and should not protect anything real.
The key is the deck
There is no password and no number. The key is the order of 54 cards: the standard 52 plus two jokers, called A and B. Two people whose decks are in the same order can exchange messages. Anyone else faces 54 factorial possible orders, a 72-digit number.
Every card has a value. Clubs are 1 to 13, diamonds 14 to 26, hearts 27 to 39, spades 40 to 52, and both jokers are 53. Shuffling a deck and copying its order to a friend is the real way to share a key. Schneier also gives a way to key the deck from a passphrase, which the lab supports, but a passphrase has far less entropy than a shuffle.
One round, five steps
Each round rearranges the deck with four fixed moves and then reads one card:
- Move joker A down one position.
- Move joker B down two positions.
- Triple cut: swap everything above the first joker with everything below the second.
- Count cut: read the bottom card’s value, move that many cards from the top to just above the bottom card. The bottom card stays where it is.
- Output: read the top card’s value, count that many cards down, and the next card is the keystream value.
Nothing in a round is random. The same deck always produces the same letters, which is what lets the receiver reproduce them.
Round one on the unkeyed deck, rendered by the package from a recorded trace. Outlined cards mark what each step acted on. In step two, joker B runs off the bottom and wraps to just below the top card.
The wrap rule in that figure is the detail implementations get wrong most often. A joker that
runs off the bottom of the deck lands second from the top, below the top card. The other trap is the
output step. If the count lands on a joker, the round produces no letter and is thrown away.
On the unkeyed deck this happens in round four: the top card is the three of clubs, the count
lands on joker B, and the keystream skips to round five. An implementation that gets this
wrong still decrypts its own ciphertext correctly, so a round-trip test will not catch it.
Published test vectors do: the unkeyed deck encrypting fifteen A’s must give EXKYIZSGEHUNTIQ.
The two joker moves are local. The triple cut is the step that mixes the whole deck, since a card near the top can land at the bottom in one move. The count cut then rotates the deck by an amount set by whichever card sits at the bottom, so the next state depends on the entire deck.
From cards to letters
The cipher layer is small. Convert each plaintext letter to a number (A is 1, Z is 26), add the keystream value, and wrap at 26. Keystream values above 26 lose 26 first, so the unkeyed deck’s first six values, 4, 49, 10, 24, 8, and 51, become the letters D, W, J, X, H, and Y. To decrypt, subtract instead of add. The receiver needs only an identically ordered deck. All of the security sits in the deck.
Never use the same deck twice
Encrypt two messages from the same starting deck and the same keystream K goes into both:
C1 = P1 + K
C2 = P2 + K
C1 - C2 = P1 - P2
The keystream cancels. In the lab’s example, ATTACKATDAWN and RETREATATTEN encrypted under
one deck give ciphertexts whose difference, letter by letter, equals the difference of the
plaintexts: IOZIXJGSJGRZ both ways. The attacker never learns the key and does not need it.
A good guess at one message yields the other. This is true of every additive stream cipher,
from Solitaire to a reused nonce in a modern one. Solitaire makes it something you can work
out on paper.
Measuring the bias
An ideal keystream over 26 letters repeats the previous letter 1 time in 26, about 3.85% of the time. Solitaire repeats more often. The lab measured 373,500 adjacent pairs from 1,500 randomly shuffled decks and found a repeat rate of 4.46%, 1.16 times the ideal. Paul Crowley reported this bias in 1999, shortly after the cipher was published, at about 1 in 22.5 (4.44%). Daniel Shiu analysed it in 2019 and found a model that explains most of the repetitions.
A stream cipher is supposed to be indistinguishable from random. This one is distinguishable
with a counter. The gap is small, but it is a measurement, and you can reproduce it in a few
seconds with solitaire-lab analyze bias.
Crowley also showed that the deck update is not reversible, although Schneier’s description says it is. A joker on the bottom wraps to second from the top, and a joker on the top moves to the same place, so two different decks can step to the same next deck. Encryption still works, since both ends only run the deck forward. The package carries a test that builds both decks and checks that they collide.
How the page stays faithful to the engine
Every figure and recorded example on the page comes from execution traces written by the Python engine, so the page cannot show a step the engine does not take. The part where you encrypt your own message needs a second implementation in JavaScript. That one is checked against the Python engine on more than 500 generated cases, and the check compares full traces instead of ciphertexts. The reason showed up when I mutated the JavaScript engine to test the checks. One mutation left every keystream letter correct and changed a single metadata flag. A ciphertext comparison would have passed it. The trace comparison failed on random deck 54.
Try it with a real deck
The lab runs in the browser. Step through a round, key a deck with a passphrase or paste in the order of a deck you just shuffled, encrypt a message, then encrypt a second one with the same deck and watch the keystream cancel.
To run it by hand, install the package and print a worksheet:
pip install git+https://github.com/jman4162/solitaire-cipher-lab
solitaire-lab worksheet -p "CRYPTONOMICON" -r 20
The worksheet lists the deck order after every round. When your physical deck drifts from the machine’s, you can find the round where it happened.
This is an independent project I build on my own time. The views are my own and do not represent any current or former employer.
Frequently asked questions
What is the Solitaire cipher?
A stream cipher Bruce Schneier designed in 1999 for Neal Stephenson's novel Cryptonomicon, where it is called Pontifex. Its key and its entire internal state is the order of a 54-card deck (52 cards plus two jokers), and each round of four fixed card moves produces one keystream letter.
Is the Solitaire cipher secure?
No. Its keystream repeats a letter about 4.46% of the time instead of the 3.85% (1 in 26) an ideal stream would, which makes its output distinguishable from random. It is useful for teaching how stream ciphers work and fail, and should not protect real information.
Why can't you reuse a deck order in Solitaire?
Encrypting two messages from the same starting deck adds the same keystream to both. Subtracting one ciphertext from the other cancels the keystream and leaves the difference of the two plaintexts, which an attacker can unpick with a guess at either message. This is true of every additive stream cipher.
How do I try the Solitaire cipher?
The Solitaire Cipher Lab runs in the browser with nothing to install: step through a round card by card, encrypt your own message with a passphrase or a deck you shuffled by hand, and watch key reuse cancel out. The Python package adds a command line with a printable worksheet for running the cipher with a real deck.