#ciphers #solitaire #cryptonomicon #pontifex

app thieves-cant

A Rust implementation of the Solitaire Cipher designed by Bruce Schneier

3 releases (stable)

Uses old Rust 2015

1.0.1 Dec 7, 2018
1.0.0 Dec 3, 2018
0.1.0 Apr 23, 2018

#516 in Cryptography

GPL-3.0 license

41KB
883 lines

Thieves' Cant

A Rust implementation of the Solitaire Cipher designed by Bruce Schneier.

Abstract

The Solitaire Cipher was invented by famed cryptographer Bruce Schneier for Neal Stephenson's novel Cryptonomicon. It involves using a standard 52-card deck of playing cards, plus two (preferably distinct) Joker cards used to generate a keystream which is combined with the plaintext message to produce encrypted ciphertext. You can read more about the Solitaire Cipher here.

Algorithm

There are three distinct operations within the Solitaire Cipher: keying the deck, encrypting plaintext, and decrypting ciphertext. Each of these operations use many of the same steps, with only one or two differing between them. The steps are detailed below:

Keying the Deck

To begin generating a keystream, the user must first properly key the deck. You typically use a passphrase; either a single word or sentence. For our example, we will be using the passphrase SOLITAIRE. Start with step one, and then repeat steps two through six for each letter within the passphrase. In our example, you will perform these steps nine times. Note that you will always work with the deck face up, so the top card within the deck will always be visible to you.

  1. Rearranging the Deck. Rearrange the deck in order of face value and suit: from Ace to King and each suit in Bridge Order (Clubs, Diamonds, Hearts, Spades). Last should be the two Jokers, dubbed A and B (how you distinguish the jokers is up to you, but you MUST remain consistent with which Joker cards are A and B).

  2. Move Joker A. Find Joker A in the deck, and move it down one position; that is, swap it with the card directly beneath. If Joker A is the last card on the deck, place it below the top card in the deck.

  3. Move Joker B. Find Joker B in the deck, and move it down two positions. If Joker B is the second-to-last card in the deck, place it below the top card. If Joker B is the last card in the deck, place it below the second card from the top.

  4. Perform a Triple Cut. Swap all of the cards above the first Joker card from the top of the deck (regardless of which joker it is) with all of the cards below the second Joker card. For example: if the deck is arranged like so:

4 of spades, 3 of clubs, Joker B, ...rest of the cards..., Joker A, King of diamonds, Queen of
Hearts

After performing the triple cut, the deck will look like this:

King of diamonds, Queen of Hearts, Joker B, ...rest of the cards..., Joker A, 4 of spades, 3 of
clubs
  1. Perform a Count Cut. Look at the last card in the deck, and convert it to a number using the Card Value Table below. Then, starting from the top of the deck, count down that many cards and cut the deck. Place the cut deck BEFORE the last card in the deck. For example, if your deck is arranged like so:
3 of clubs, 2 of hearts, 9 of spades, ...rest of the cards..., 2 of clubs

After performing the count cut, the deck will look like this:

9 of spades, ...rest of the cards..., 3 of clubs, 2 of hearts, 2 of clubs

Card Value Table

A 2 3 4 5 6 7 8 9 10 J Q K
Clubs 1 2 3 4 5 6 7 8 9 10 11 12 13
Diamonds 14 15 16 17 18 19 20 21 22 23 24 25 26
Hearts 27 28 29 30 31 32 33 34 35 36 37 38 39
Spades 40 41 42 43 44 45 46 47 48 49 50 51 52

Note that Jokers, no matter which one, have a value of 53.

  1. Perform a Value Cut. The value cut is very similar to the Count Cut, except that it uses the value of a letter within the passphrase to determine how far down the deck to cut. To determine how far down the deck to cut, refer to the Letter Value Table below. After cutting the deck, place the cards BEFORE the last card in the deck, just like you did when doing the Count Cut. For example, using the passphrase SOLITAIRE from above, keying the deck with the letter S, you count down nineteen cards, cut the deck, and then place those nineteen cards before the last card in the deck.

Letter Value Table

Letter Value
A 1
B 2
C 3
D 4
E 5
F 6
G 7
H 8
I 9
J 10
K 11
L 12
M 13
N 14
O 15
P 16
Q 17
R 18
S 19
T 20
U 21
V 22
W 23
X 24
Y 25
Z 26

Encrypting Plaintext

To encrypt plaintext, you must first generate a keystream with the same length as the plaintext that they want to encrypt. Then, for each character in the plaintext message, add it to the corresponding keystream character in order to produce an encrypted ciphertext character. The exact steps are detailed below:

  1. Encode Special Characters. The Solitaire Cipher doesn't support operations on non-alphabet (A-Z) characters. Thus, you first have to encode any special characters into a combination of alpha characters. Only a limited subset of non-alphabet characters are supported by Thieves' Cant, as shown in the Non-Alphabet Encoding table below. For example: the message HELLO WORLD will be encoded as HELLOXZAWORLD.

Non-Alphabet Encoding Table

Character Encoding
(Space) XZA
. XZB
, XZC
' XZD
? XZE
! XZF
  1. Group Plaintext Characters. By convention, the plaintext message should be broken up into groups of five characters. If the last group doesn't have five characters in it, pad the message with 'Z' characters. For example: the message HELLOXZAWORLD is broken up into the string HELLO XZAWO RLDZZ.

  2. Translate Each Character into a Number. Use the Letter Value Table above and translate each letter in the plaintext into its corresponding numeric value.

  3. Key the Deck. Using the steps above, key the deck using the given passphrase.

  4. Find the Output Card. Follow steps 2-6 from the "Keying the Deck" operation. Then, to find the output card, first look at the top card in the deck, and convert it to a number using the Card Value Table above. Then, count down that many cards and record the card that comes AFTER the cards you have counted down. For example, if you have the following deck:

3 of clubs, 2 of diamonds, ace of spades, 9 of hearts, ...rest of the cards...

the output card will be the 9 of hearts, with the value of 35.

  1. Generate Ciphertext Character. Add the value of the output card to the value of the character in the plaintext. If the resulting sum is greater than 26, then subtract 26 from the sum until the value is less-than-or-equal-to 26. Convert that value into a character using the Letter Value Table above.

  2. Rinse and Repeat. Repeat steps 5 and 6 for each character in the plaintext message.

Following the steps above, encrypting the text HI THERE using the key SOLITAIRE will result in the ciphertext OEPXV NZLBM.

Decrypting Ciphertext

To decrypt ciphertext, users should follow all of the steps found within the Encrypting Plaintext section above. However, on step 4, when generating the plaintext value, you instead subtract the value of the ciphertext character from the card output value. If the resulting difference is less than 1, add 26 to that value until it's between 1 and 26. Then convert the value into a character using the Letter Value Table. Remember to strip away any 'Z' padding characters from the end of the plaintext, and decode the special character encodings into the proper character.

Usage

$ tvct deck -n // Generate a new, randomly shuffled deck.

$ tvct deck -n -f key.deck // Generate a new, randomly shuffled deck, in a new location.

$ tvct deck -n -k "SOLITAIRE" -f key.deck // Generate a new deck, keyed using the string "SOLITAIRE", with a new file location.

$ tvct deck // Print out the order of the cards within the deck.

$ tvct encrypt "HELLO WORLD" // Encrypt a message; output: OAHBF TUMYB BELRT

$ tvct encrypt -f key.deck "HELLO WORLD" // Encrypt the message using a different key file

$ tvct decrypt "OAHBF TUMYB BELRT" // Decrypt the ciphertext, output: HELLO WORLD

$ tvct decrypt -f key.deck "OOAHBF TUMYB BELRT" // Decrypt the ciphertext with a different key file

Dependencies

~3MB
~63K SLoC