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 |
#1300 in Cryptography
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.
-
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).
-
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.
-
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.
-
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
- 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.
- 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 letterS
, 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:
- 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 asHELLOXZAWORLD
.
Non-Alphabet Encoding Table
Character | Encoding |
---|---|
(Space) | XZA |
. | XZB |
, | XZC |
' | XZD |
? | XZE |
! | XZF |
-
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 stringHELLO XZAWO RLDZZ
. -
Translate Each Character into a Number. Use the Letter Value Table above and translate each letter in the plaintext into its corresponding numeric value.
-
Key the Deck. Using the steps above, key the deck using the given passphrase.
-
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
.
-
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.
-
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
~3.5–4.5MB
~71K SLoC