A stream cipher you can hold in your hands

Solitaire, Bruce Schneier's 1999 hand cipher also called Pontifex, turns an ordinary deck of 54 playing cards into a keystream generator. Here is every step of it, running.

Most explanations of stream ciphers ask you to imagine a keystream appearing from a black box. Solitaire doesn't need the black box. Its internal state is a deck of cards, and you can watch the state change.

This is a teaching cipher. Solitaire has known statistical biases, and you can measure one yourself at the bottom of this page. Do not use it to protect real information.

Section 01The key is the deck

The order of the cards is the key. Two people whose decks are in the same order can talk. There are 54 factorial possible orders, a 72-digit number, or about 2237. That is an upper bound on what the key can hold. How hard a real key is to guess depends on how the deck was ordered: a hand shuffle is not perfectly uniform, and a deck keyed from a passphrase (the default below) is only as hard to guess as the passphrase. A large number of states also says nothing about whether the cipher itself is secure, which Section 06 tests.

Every card has a value. Clubs are worth 1 to 13, diamonds 14 to 26, hearts 27 to 39, spades 40 to 52. Both jokers are worth 53, which turns out to matter.

The deck in bridge order. This is the standard unkeyed starting position, used for test vectors. Don't use it as a key.

Section 02One round, five steps

A round shuffles the deck in four fixed, entirely deterministic ways, then reads a single card to produce one keystream letter. No randomness is involved anywhere: the same deck always yields the same letters, which is exactly what lets the person at the other end reproduce them.

Step through it. Watch the two jokers wander down the deck, drag the cut points around with them, and eventually select a card.

Round four is worth stopping on. The count lands on a joker, and a joker has no letter, so the round is thrown away and run again. That discarded round is the single most commonly mis-implemented part of Solitaire, and it is invisible to any test that just encrypts and decrypts: a broken implementation happily decrypts its own broken ciphertext.

Why the triple cut is the interesting step

The two joker moves are small and local. The triple cut is the step that mixes globally: everything above the topmost joker and everything below the bottommost joker trade places wholesale, so a card near the top can be flung to the bottom in one move. The count cut then rotates the whole deck by an amount that depends on the card that happens to be sitting on the bottom. Together they make the next state depend on the whole deck, not just its neighbourhood.

Section 03From cards to letters

The cipher itself is almost an afterthought. Convert each plaintext letter to a number (A=1 … Z=26), add the keystream number, wrap at 26, convert back. The arithmetic is simple. The cipher's security depends on keeping the deck order secret and on the keystream being unpredictable, and Section 06 shows the second part failing.

Decryption is the same operation with a minus sign. This symmetry is why the receiver needs an identically ordered deck and nothing else.

Section 04Try it with your own key

Everything above was recorded in advance. This part runs the cipher in your browser with the same algorithm, checked against the Python engine trace-for-trace in CI, so what you see here is what the package does.

How should the deck be keyed?

Letters only; everything else is ignored. Both ends need the same phrase.

The deck order is the key. Anyone who has a deck link can decrypt anything sent with that deck, so share links only for practice decks.

Section 05Never restart the keystream

If you tried both options above, you have already seen this. Here is the failure written out. The same starting deck always produces the same keystream, so two messages encrypted from the same starting order share a keystream, and an attacker can subtract one ciphertext from the other. The keystream, which is the thing you were protecting, cancels out completely.


The attacker never learns the key and does not need to. They learn the difference between your two plaintexts, and with any guess about one message, they get the other. This is not a flaw in Solitaire specifically; it is true of every additive stream cipher. Solitaire just makes it visible.

Here is the same second message encrypted from the deck the first message left behind. The keystream carries on instead of restarting, so the subtraction no longer cancels:

Continuing the deck avoids this particular leak. It does nothing about the weakness in the next section.

Section 06It looks random. It isn't.

The keystream above looks like noise. Here is the test that says otherwise, and you can run it yourself in a few seconds.

In an ideal stream over 26 letters, the chance that any letter equals the one before it is 1 in 26, about 3.85%. Solitaire repeats more often than that:

Each deck produces 250 keystream letters. The same seed always gives the same decks, so anyone can check your numbers. A 95% interval misses the true value about one run in 20, so now and then the uniform row will sit just off 3.85%. Try another seed: the Solitaire row stays high.

With a few hundred decks the interval for the difference between the two rates stays above zero. The gap is small but consistent, and it is enough to tell the output apart from random, which is the property a stream cipher is supposed to have. Telling a stream apart from random is a weaker result than reading a particular short message: an attacker with one 20-letter ciphertext learns little from a 0.6-point excess. It is still the property the cipher promised, and it is how attacks start. The bias was spotted by Paul Crowley in 1999, shortly after the cipher was published, and analysed in detail by Daniel Shiu in 2019. Crowley also showed that the deck transformation is not reversible, although the original description says it is: a joker on the bottom wraps to second from the top, which is exactly where a joker on the top lands too, so two different decks can step to the same next one. That does not stop ordinary encryption and decryption working, since both sides generate the keystream forwards.

Looking scrambled and being unpredictable are different claims, and the distance between them can be measured. Modern stream ciphers are designed so that no test that can feasibly be run tells their output apart from random, and they are published so that people can go looking.

Section 07Run it yourself

The worked examples on this page were generated by the engine in the package and are replayed from recorded traces, because a diagram that reimplements the algorithm eventually disagrees with it. Section 04 needs a second implementation to run your own key, so CI checks the browser engine against the Python one trace for trace, and the page deploys only after that check passes.

pip install git+https://github.com/jman4162/solitaire-cipher-lab

solitaire-lab teach --ordered-deck --step
solitaire-lab encrypt -p "CRYPTONOMICON" "DO NOT USE PC" --show-work
solitaire-lab worksheet -p "CRYPTONOMICON" -r 20
solitaire-lab analyze bias --samples 5000

worksheet is the one to try with a real deck in your hands. It prints the deck state after every round, so when your physical shuffle drifts from the machine's you can find the exact round where it happened.