|
Neil Bawd 99-07-14
"Cheap, fast, good: choose two."
Alleged RC4, now known as ARCFOUR, is cheap and fast and good.
It does need a computer.
SOLITAIRE is cheap and good, but it's not fast. It's very
cheap; it doesn't need a computer, just a deck of cards. It's
more secure than all other known paper- and-pencil ciphers
other than a one-time pad. And for fast, it's as fast as an
actual cipher used by a Soviet spy. This was described by
David Kahn in Kahn on Codes.
Using the Soviet cipher or SOLITAIRE will take an evening to
encrypt or decrypt a message with paper and pencil. However
one side or the other will almost always have a computer. I
have the Forth code on my Palm.
A deck of cards is easier to conceal than a computer or codebook. But soon the secret police will seize and torture anyone with a deck of cards. A card deck is a munition and criminal to export.
TEXTThe algorithm takes the normal cards of a deck as the numbers 1-52, with two Jokers as 53-54. The letters of the alphabet are 1-26. Adding a card number to a letter number modulo 26 enciphers a letter; subtracting a card number modulo 26 deciphers a letter.
The card numbering arranges the suits in Bridge order - Clubs, Diamonds, Hearts, Spades. The suits are arranged A23456789JQK. So Clubs are 1-13, Diamonds 14-26, Hearts 27-39, Spades 40-52. The Queen of Hearts is 12+26 = 38; the Ace of Spades is 40.
The two Jokers should be distinguishable. Usually one is larger than the other.
If not, draw an A on one and B on the other.
The key for a message is the initial arrangement of the deck. There are 54! ways to do this.
A daily Bridge column in a selected newspaper could be used, with the first two cards played positioning the Jokers. Or an agreed-upon arrangement could be used, modified by a keyphrase, to stack the deck.
After stacking the deck, to encipher or decipher each letter of a message:
1. Find the A-Joker and move it down 1. When the A-Joker is the bottom card, move it around the top card.
2. Find the B-Joker and move it down 2. When the B-Joker is one of the two bottom cards, move it around the top card.
3. Exchange the cards above the first occurring Joker with the cards below the second occurring Joker.
4. Count down and cut using the value in the bottom card. The bottom card stays put.
5. Count down using the value in the top card. Both jokers
count as #CARDS-1.
Take the value of the next card, and add to encrypt or subtract to decrypt modulo 26 with the letter of the message.
Do all this twice because it's prone to human error.
SOLITAIRE is a Forth implementation of the Solitaire
cryptosystem, as designed by Bruce Schneier and described at
Counterpane,
as well as in Neil Stephenson's novel Cryptonomicon,
ISBN 0-380-97346-4.
NOTE: the Solitaire encryption algorithm is strong cryptography. That means the security it affords is based on the secrecy of the key rather than the secrecy of the algorithm itself. That also means that this program and programs derived from it may be treated as a munition for the purpose of export regulation in the United States and other countries. You are encouraged to seek competent legal counsel before distributing copies of this program.
[The source code for cipher generation has been removed.]
ZAP ( -- )
STACK ( -- )
ALICE ( str len -- )
BOB ( str len -- )
There is environmental dependency on 1 CHARS is 1.
#CARDS ( -- n )
DECK
A-Joker
B-Joker
Clubs: 1-13. Diamonds: 14-26. Hearts: 27-39. Spades: 40-52.
Program Text 154 CONSTANT #CARDS CREATE DECK #CARDS ALLOT #CARDS 1- CONSTANT A-Joker #CARDS CONSTANT B-Joker
CEXCHANGE ( addr addr -- )
SREVERSE.
SREVERSE ( str len -- )
SROTATE.
SROTATE ( str len k -- )
SROTATE splits a string into two parts, reverses each
part, and then reverses the whole string.
str len 1 SROTATE moves the first char to the end.
str len DUP 1- SROTATE moves the last char to the
beginning.
: CEXCHANGE ( addr addr -- )
OVER C@ >R DUP C@ ROT C! R> SWAP C! ;
: SREVERSE ( str len -- )
1- OVER + ( str addr)
BEGIN 2DUP U< WHILE
2DUP CEXCHANGE
1 /STRING \ Because 1 CHARS is 1.
REPEAT 2DROP ;
: SROTATE ( str len k -- )
>R 2DUP 2DUP R> /STRING ( s n s n s+k n-k)
DUP >R 2SWAP R> - ( s n s+k n-k s k)
SREVERSE SREVERSE SREVERSE ;
Find-the-A-Joker ( -- i )
Move-the-A-Joker-Down-1 and
Exchange-Above-and-Below-Jokers.
Find-the-B-Joker ( -- i )
Move-the-B-Joker-Down-2 and
Exchange-Above-and-Below-Jokers.
: Find-the-A-Joker ( -- i )
#CARDS 0 DO
I DECK + C@ A-Joker = IF
I UNLOOP
EXIT THEN
LOOP TRUE ABORT" The A-Joker is missing. " ;
: Find-the-B-Joker ( -- i )
#CARDS 0 DO
I DECK + C@ B-Joker = IF
I UNLOOP
EXIT THEN
LOOP TRUE ABORT" The B-Joker is missing. " ;
Move-the-A-Joker-Down-1 ( -- )
CIPHER and
STACK.
Move-the-B-Joker-Down-2 ( -- )
CIPHER and STACK.
Exchange-Above-and-Below-Jokers ( -- )
CIPHER and STACK.
Count-and-Cut-with-Bottom-Card ( -- )
CIPHER and STACK.
Count-with-Top-Card ( -- n )
#CARDS-1. Used in
CIPHER.
: Move-the-A-Joker-Down-1 ( -- )
Find-the-A-Joker ( i)
DUP #CARDS 1- < IF
DECK + 2 1 SROTATE
ELSE \ A-Joker is at the bottom of the deck.
DROP DECK #CARDS 1 /STRING DUP 1- SROTATE
THEN ( ) ;
: Move-the-B-Joker-Down-2 ( -- )
Find-the-B-Joker ( i)
DUP #CARDS 2 - < IF
DECK + 3 1 SROTATE
ELSE
#CARDS 1- = IF \ B-Joker is at bottom.
DECK #CARDS 2 /STRING DUP 1- SROTATE
ELSE \ B-Joker is next to bottom.
DECK #CARDS 1 /STRING 1- DUP 1- SROTATE
THEN THEN ;
: Exchange-Above-and-Below-Jokers ( -- )
Find-the-A-Joker Find-the-B-Joker ( A B)
2DUP MAX >R MIN >R ( )( R: max min)
DECK #CARDS R@ /STRING 2R@ - 1+ SROTATE
DECK #CARDS R> SROTATE R> DROP ;
: Count-and-Cut-with-Bottom-Card
DECK #CARDS 1- 2DUP + C@ OVER MIN SROTATE ;
: Count-with-Top-Card ( -- n )
DECK C@ #CARDS 1- MIN DECK + C@ ;
CIPHER
ENCIPHER DECIPHER
CIPHER will do the following:
#CARDS-2, then
skip it and repeat from the beginningFALSE [IF]
: CIPHER
### ##### # # ### ### #### ##### ####
# # # # # # # # # # # # # #
# # ## # # # # # # # # #
# #### # # # ### # # #### #### # #
# # # ## # # # # # # # #
# # # # # # # # # # # # # #
### ##### # # ### ### # # ##### ####
;
[THEN]
Stack-Cut ( n -- )
STACK.
Is-Alpha ( char -- flag )
Alpha>Num ( char -- n )
Num>Alpha ( n -- char )
: Stack-Cut ( n -- ) DECK #CARDS 1- ROT SROTATE ; : Is-Alpha ( char -- flag ) 32 OR [CHAR] a - 26 U< ; : Alpha>Num ( char -- n ) 32 OR [CHAR] a 1- - ; : Num>Alpha ( n -- char ) 1- 26 MOD [CHAR] A + ;
COL ( -- addr )
.CIPHER and ZAP.
.CIPHER ( char -- )
ENCIPHER and DECIPHER.
ENCIPHER ( char -- )
ALICE.
DECIPHER ( char -- )
BOB.
\ Now legal.
: CIPHER ( -- n )
BEGIN
Move-the-A-Joker-Down-1
Move-the-B-Joker-Down-2
Exchange-Above-and-Below-Jokers
Count-and-Cut-with-Bottom-Card
Count-with-Top-Card ( n)
DUP #CARDS 2 - >
WHILE DROP REPEAT ;
VARIABLE COL
: .CIPHER ( char -- )
EMIT 1 COL +! ( )
COL @ 1+ 6 MOD 0= IF
SPACE 1 COL +!
COL @ 60 = IF CR 0 COL ! THEN
THEN ;
: ENCIPHER ( char -- )
Alpha>Num CIPHER + 1- 26 MOD 1+ Num>Alpha
.CIPHER ;
: DECIPHER ( char -- )
Alpha>Num CIPHER - 51 + 26 MOD 1+ Num>Alpha 32 OR
.CIPHER ;
ZAP STACK ALICE BOB
Program Text 8
: ZAP ( -- )
#CARDS 0 DO I 1+ I DECK + C! LOOP
0 COL ! ;
: STACK ( str len -- )
ZAP
BEGIN DUP WHILE
Move-the-A-Joker-Down-1
Move-the-B-Joker-Down-2
Exchange-Above-and-Below-Jokers
Count-and-Cut-with-Bottom-Card
OVER C@ Alpha>Num Stack-Cut
1 /STRING
REPEAT 2DROP ;
: ALICE ( str len -- )
BEGIN DUP WHILE
OVER C@ Is-Alpha IF
OVER C@ ENCIPHER
THEN
1 /STRING
REPEAT 2DROP
BEGIN COL @ 6 MOD WHILE
[CHAR] X ENCIPHER
REPEAT ;
: BOB ( str len -- )
BEGIN DUP WHILE
OVER C@ Is-Alpha IF
OVER C@ DECIPHER
THEN
1 /STRING
REPEAT 2DROP ;
MARKER TESTING
: Plaintext: BL WORD COUNT PAD 2DUP C! 1+ SWAP MOVE ;
: Key: BL WORD COUNT OVER C@ [CHAR] ' = IF
1 /STRING 1- STACK
ELSE 2DROP POSTPONE \
ZAP
THEN ;
: Output: POSTPONE \ ;
: Ciphertext: POSTPONE \ CR PAD COUNT ALICE ;
Plaintext: AAAAAAAAAAAAAAA
Key: <null key>
Output: 4 49 10 53 24 8 51 44 6 4 33 20 39 19 34 42
Ciphertext: EXKYI ZSGEH UNTIQ
Plaintext: AAAAAAAAAAAAAAA
Key: 'f'
Output: 49 24 8 46 16 1 12 33 10 10 9 27 4 32 24
Ciphertext: XYIUQ BMHKK JBEGY
Plaintext: AAAAAAAAAAAAAAA
Key: 'fo'
Output: 19 46 9 24 12 1 4 43 11 32 23 39 29 34 22
Ciphertext: TUJYM BERLG XNDIW
Plaintext: AAAAAAAAAAAAAAA
Key: 'foo'
Output: 8 19 7 25 20 53 9 8 22 32 43 5 26 17 53 38 48
Ciphertext: ITHZU JIWGR FARMW
Plaintext: AAAAAAAAAAAAAAA
Key: 'a'
Output: 49 14 3 26 11 32 18 2 46 37 34 42 13 18 28
Ciphertext: XODAL GSCUL IQNSC
..
Plaintext: AAAAAAAAAAAAAAA
Key: 'aa'
Output: 14 7 32 22 38 23 23 2 26 8 12 2 34 16 15
Ciphertext: OHGWM XXCAI MCIQP
Plaintext: AAAAAAAAAAAAAAA
Key: 'aaa'
Output: 3 28 18 42 24 33 1 16 51 53 39 6 29 43 46 45
Ciphertext: DCSQY HBQZN GDRUT
Plaintext: AAAAAAAAAAAAAAA
Key: 'b'
Output: 49 16 4 30 12 40 8 19 37 25 47 29 18 16 18
Ciphertext: XQEEM OITLZ VDSQS
Plaintext: AAAAAAAAAAAAAAA
Key: 'bc'
Output: 16 13 32 17 10 42 34 7 2 37 6 48 44 28 53 4
Ciphertext: QNGRK QIHCL GWSCE
Plaintext: AAAAAAAAAAAAAAA
Key: 'bcd'
Output: 5 38 20 27 50 1 38 26 49 33 39 42 49 2 35
Ciphertext: FMUBY BMAXH NQXCJ
Plaintext: AAAAAAAAAAAAAAAAAAAAAAAAA
Key: 'cryptonomicon'
Ciphertext: SUGSR SXSWQ RMXOH IPBFP XARYQ
Plaintext: SOLITAIRE
Key: 'cryptonomicon'
Ciphertext: KIRAK SFJAN
( END ) TESTING
The following is a version of ZAP that arranges the deck in Eight Kings sequence.
Eight-Kings ( -- addr )
ZAP ( -- )
CREATE Eight-Kings
8 C, 13 C, 3 C, 10 C, 2 C, 7 C,
9 C, 5 C, 12 C, 4 C, 1 C, 6 C, 10 C,
: ZAP ( -- )
DECK 52 0 DO
I 13 MOD Eight-Kings + C@
I 4 MOD 13 * +
OVER C! 1+
LOOP
53 OVER C! 1+ 54 SWAP C! ;