Solitaire Cipher
January 18, 2011
In his book Cryptonomicon, Neal Stephenson has his characters communicate using a cipher called Pontifex. Pontifex is based on the Solitaire cipher developed by Bruce Schneier, and uses an ordinary deck of cards, thirteen cards in each of four suits, plus two distinguishable jokers, to generate a keystream that is added to plain-text to form cipher-text, or subtracted from cipher-text to form plain-text.
After the deck is keyed, a single step consists of four operations on the deck. First, the “A” joker is moved one card down the deck, wrapping around the end of the deck if necessary. Second, the “B” joker is moved two cards down the deck, again wrapping around the deck if necessary. Third, a triple-cut swaps all the cards above the highest joker in the deck with all the cards below the lowest joker in the deck, leaving the two jokers and the cards between them in place. Fourth, a counted cut, based on the number of the bottom card in the deck, moves the top “count” cards to just above the bottom card; the cards are numbered 1 to 52 in “bridge order” with ace low to king high in each suit, clubs, diamonds, hearts, spades, and either joker counting as 53. Then look at the top card in the deck and count down the given number to determine the current key card.
For example, given an initial deck in bridge order 1, 2, …, 52, A, B, where the two jokers are A and B, the first operation moves the A joker one card down the deck leaving 1, 2, …, 52, B A, the second operation moves the B joker two cards down the deck leaving 1, B, 2, …, 52, A, the third operation performs a triple cut (the second half of the cut is empty) leaving B, 2, …, 52, A, 1, and the fourth step performs a count cut taking one card (because the bottom card on the deck is 1) leaving 2, …, 52, A, B, 1. Then the output card is 4, the four of clubs, because the top card of the deck is 2 and the second card below it is 4.
Before encrypting or decrypting a message, the deck must be “keyed.” Begin with a deck in bridge order and perform a single step. Then, for each character in the key, do a counted cut on the number of the current character, with A=1 … Z=26, followed by another single step. Once the deck is keyed and you have a keystream, each character is added (for encryption) or subtracted (for decryption) from the current text character, wrapping around the alphabet as necessary, so that A+A=B and T+Q=K; note that Z is the identity character, so F+Z=F. The plain-text has nulls (the letter X) added to the end to make the message length a multiple of five, and the cipher-text is split into five-character blocks for convenience.
Schneier gives three examples. Given the plaintext AAAAAAAAAA and null key, the keystream is 4 49 10 (53) 24 8 51 44 6 4 33 (the joker is skipped) and the ciphertext is EXKYI ZSGEH. Given the plaintext AAAAAAAAAAAAAAA and key FOO, the keystream is 8 19 7 25 20 (53) 9 8 22 32 43 5 26 17 (53) 38 48 and the ciphertext is ITHZU JIWGR FARMW. Given the plaintext SOLITAIRE and key CRYPTONOMICON, the keystream is 44 46 32 18 17 18 23 44 22 42 and the ciphertext is KIRAK SFJAN.
If you actually run the cipher with a deck of cards, you will find that, with just a little practice, your hands work the keystream generator themselves with little conscious thought, and you will soon memorize the wrap-around character addition rules like T+Q=K; the biggest problem with the cipher, like any output-feedback cipher, is that a single mistake renders all trailing text unreadable. This cipher is best used for low-volume transmission of short messages. If you use it for real security, your key should have at least eighty characters, and you should never use the same key to transmit two different messages. An easy way for two communicants to manage keys is for both to use some printed source, say the lead editorial in the daily newspaper or the pages of a favorite novel (be sure both are using the same edition), selecting the key as the first 80 characters starting at the 37th, say, and giving the date or page as a header to the encrypted message.
Your task is to write functions that encrypt and decrypt using the solitaire cipher. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.
My Python solution:
Line 84 should be:
and not:
My solution (Haskell): https://gist.github.com/785960
My python version.
My attempt in scheme.
Another attempt using pattern matching.
Here is v. 0.0.1 of my writing of this algorithm. This is not neat and pretty, merely functional. The first block of code is solitaire.py. It executes when called on the console (only tested on UNIX-like systems) and must be passed arguments to work. There is a class and list of functions in solitaire.py. Deck class handles all the stuff you’d expect a deck in solitaire to handle: pushing cards, popping cards, getting characters, advancing state, and a built-in make. The functions in solitaire.py are mainly for loading and unloading deck states and to operate the command-line form.
Advpass option for command line operation is a demonstration of nested decks, basically a deck of decks. The deck class is flexible enough to take characters, strings, binary blobs, functions, or anything else that counts as an object as a card’s representation.
I wrote pyrand.py to interface to /dev/urandom and serve me up tasty 32-bit unsigned random integers. Replace in code with your own solution as I suspect pyrand is the slow spot in the shuffle routines in solitaire.py…
This is the first time this code has left this system, consider it licensed under BSD by:
Daniel Duffield
LucianSolaris@gmail.com
https://www.facebook.com/LucianSolaris
solitaire.py:
pyrand.py:
A bug was discovered in the functions pushCard(), pushAlpha(), and pushJoker(). They caused the deck to be loaded in the wrong order. The corrected code is below.
solitaire.py: (v. 0.0.2)
A couple more bugs squashed, one involving returning the last card counted instead of the next one after the last one counted. Now operates to real-life version.
Build deck file that resembles the standard hand-held algorithm by inputting:
Generate a key with:
Then open soldeck001.deck in a text editor. The second and third values are the count values (the number the card represents, used for counting) and face values (characters). Model your real deck after this one and check for yourself!
I’m sure many of those who read this thread have heard of Pontifex, or the Solitaire cipher. This is my attempt to implement a general form of that algorithm which can accept as face values any type of data, including more decks. This python module even allows for a deck of decks! As written, it is meant to operate just like the card algorithm, so if you wanted to you could print up code books of what your ‘field agents’ are carrying decks of cards to generate! This module also has two password generating routines, one which uses the solitaire deck simply, and another that is a play on the deck of decks idea.
Here is v. 0.0.3 of my writing of this algorithm. This is not neat and pretty, merely functional. The first block of code is solitaire.py. It executes when called on the console (only tested on UNIX-like systems) and must be passed arguments to work. There is a class and list of functions in solitaire.py. Deck class handles all the stuff you’d expect a deck in solitaire to handle: pushing cards, popping cards, getting characters, advancing state, and a built-in make. The functions in solitaire.py are mainly for loading and unloading deck states and to operate the command-line form.
Advpass option for command line operation is a demonstration of nested decks, basically a deck of decks. The deck class is flexible enough to take characters, strings, binary blobs, functions, or anything else that counts as an object as a card’s representation.
I wrote pyrand.py to interface to /dev/urandom and serve me up tasty 32-bit unsigned random integers. Replace in code with your own solution as I suspect pyrand is the slow spot in the shuffle routines in solitaire.py.
I decided to write this because I wanted to start writing python code where I implement quantum computing proof cryptographic algorithms and protocols. I think OpenPGP is sorely lacking in post-quantum computing foresight at this point and warez has to get out there to handle this threat.
Yea yea, I know it’s sloppy code. I wrote it to work, and I may work on cleaning it up and making it faster and more general. In the event I don’t, here’s a little present for y’all:
This is the first time this code has left this system, consider it licensed under BSD by:
Daniel Duffield
LucianSolaris@gmail.com
https://www.facebook.com/LucianSolaris
Output of `./solitaire.py –help`:
solitaire.py:
pyrand.py: