An R implementation of Bruce Schneier's Solitaire encryption algorithm
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
R
man
.Rhistory
DESCRIPTION
LICENSE
NAMESPACE
README.md

README.md

solitaiRe

A R package (in progress, user beware) implementing the Solitaire encryption algorithm developed by Bruce Schneier for Neil Stephenson's Cryptonomicon. The package implements an S4 class "deck" containing a standard set of 54 playing cards, plus 2 jokers, in standard bridge order. Solitaire operations are implemented as simple S4 methods.The package also contains a few convenience functions for converting text data into an appropriate format for encryption. For more information on the algorithm, see

https://www.schneier.com/academic/solitaire/

The package can be installed and loaded using the devtools package with

devtools::install_github('areshenk/solitaiRe')
library(solitaiRe)

Using the package

Suppose that we have a message to be encrypted --say, this beautiful passage from Cormac McCarthy's Outer Dark:

message <- "The tinker in his burial tree was a wonder to the birds. 
The vultures that came by day to nose with their hooked beaks among his 
buttons and pockets like outrageous pets soon left him naked of his rags 
and flesh alike. Black mandrake sprang beneath the tree as it will where 
the seed of the hanged falls and in spring a new branch pierced his breast 
and flowered in a green boutonnière perennial beneath his yellow grin. 
He took the sparse winter snows upon what thatch of hair still clung to 
his dried skull and hunters that passed that way never chanced to see 
him brooding among his barren limbs. Until wind had tolled the thinker's 
bones and seasons loosed them one by one to the ground below and his 
bleached and weathered brisket hung in that lonesome wood like a bone
birdcage."

The first step is to strip the text of all non-alphabetic characters, and convert it to lower case

clean.text <- stripText(x = message)
print(clean.text)
## [1] "thetinkerinhisburialtreewasawondertothebirdsthevulturesthatcamebydaytonosewiththeirhookedbeaksamonghisbuttonsandpocketslikeoutrageouspetssoonlefthimnakedofhisragsandfleshalikeblackmandrakesprangbeneaththetreeasitwillwheretheseedofthehangedfallsandinspringanewbranchpiercedhisbreastandfloweredinagreenboutonnièreperennialbeneathhisyellowgrinhetookthesparsewintersnowsuponwhatthatchofhairstillclungtohisdriedskullandhuntersthatpassedthatwayneverchancedtoseehimbroodingamonghisbarrenlimbsuntilwindhadtolledthethinkersbonesandseasonsloosedthemonebyonetothegroundbelowandhisbleachedandweatheredbriskethunginthatlonesomewoodlikeabonebirdcage"

Note that the cleanText() function can also read directly from a text file by calling stripText(file = 'path-to-file')

By convention, the plaintext is written in five letter chunks, padded with X's. This can easily be done using

plaintext <- chunkText(clean.text, 
                       chunk.length = 5,
                       pad = T)
print(plaintext)
## [1] "theti nkeri nhisb urial treew asawo ndert otheb irdst hevul tures thatc ameby dayto nosew ithth eirho okedb eaksa mongh isbut tonsa ndpoc ketsl ikeou trage ouspe tssoo nleft himna kedof hisra gsand flesh alike black mandr akesp rangb eneat hthet reeas itwil lwher ethes eedof theha ngedf allsa ndins pring anewb ranch pierc edhis breas tandf lower edina green bouto nnièr epere nnial benea thhis yello wgrin hetoo kthes parse winte rsnow supon whatt hatch ofhai rstil lclun gtohi sdrie dskul landh unter sthat passe dthat wayne verch anced tosee himbr oodin gamon ghisb arren limbs until windh adtol ledth ethin kersb onesa ndsea sonsl oosed themo nebyo netot hegro undbe lowan dhisb leach edand weath eredb riske thung intha tlone somew oodli keabo nebir dcage "

We then create the deck that will be used to generate the key. Decks are created using the newDeck() function, which by default returns an unshuffled deck in standard bridge order:

newDeck()
## An object of class "deck"
## Slot "cards":
##  [1] "CA"  "C2"  "C3"  "C4"  "C5"  "C6"  "C7"  "C8"  "C9"  "C10" "CJ" 
## [12] "CQ"  "CK"  "DA"  "D2"  "D3"  "D4"  "D5"  "D6"  "D7"  "D8"  "D9" 
## [23] "D10" "DJ"  "DQ"  "DK"  "HA"  "H2"  "H3"  "H4"  "H5"  "H6"  "H7" 
## [34] "H8"  "H9"  "H10" "HJ"  "HQ"  "HK"  "SA"  "S2"  "S3"  "S4"  "S5" 
## [45] "S6"  "S7"  "S8"  "S9"  "S10" "SJ"  "SQ"  "SK"  "J1"  "J2"

where the first letter indicates the suit (C = Clubs, D = Diamonds, H = Hearts, S = Spades), and the second denotes the card value. The jokers are denoted "J1" and "J2".

We can create a randomly shuffled deck using

newDeck(method = 'shuffle')
## An object of class "deck"
## Slot "cards":
##  [1] "D3"  "D4"  "SJ"  "C4"  "H7"  "CA"  "DJ"  "SA"  "C8"  "C5"  "S5" 
## [12] "D6"  "SK"  "S7"  "CJ"  "H8"  "SQ"  "C2"  "CQ"  "DA"  "C6"  "H9" 
## [23] "S8"  "S2"  "C10" "S9"  "H2"  "HA"  "D10" "D2"  "HK"  "H4"  "S6" 
## [34] "H5"  "D8"  "DQ"  "H10" "H3"  "C9"  "J2"  "H6"  "S10" "D7"  "CK" 
## [45] "HJ"  "S4"  "D9"  "S3"  "C7"  "C3"  "DK"  "J1"  "D5"  "HQ"

We can also specify the deck manually using method = 'fixed' and passing an argument cards containing a character vector.

Alternatively, we can create a deck keyed by a passphrase (see the link above for a description of the procedure). To do this, we pass newDeck() an argument key containing the passphrase. Note that spaces and non-alphabetic text will be stripped from the passphrase automatically.

passphrase <- 'outerdark'
deck <- newDeck(method = 'key', key = passphrase)
deck
## An object of class "deck"
## Slot "cards":
##  [1] "HK"  "SA"  "S2"  "S3"  "S4"  "S5"  "S6"  "S7"  "D7"  "SK"  "SQ" 
## [12] "C3"  "C4"  "C10" "C7"  "C8"  "C9"  "D6"  "CQ"  "CK"  "DA"  "D2" 
## [23] "D3"  "D4"  "D5"  "CA"  "C2"  "D9"  "D10" "DJ"  "CJ"  "J1"  "HA" 
## [34] "S8"  "D8"  "S9"  "S10" "C5"  "C6"  "J2"  "DQ"  "SJ"  "H2"  "H3" 
## [45] "H4"  "H5"  "H6"  "H7"  "H8"  "H9"  "H10" "HJ"  "HQ"  "DK"

solitaiRe includes methods for each individual step of the encryption algorithm, but the easiest way to use it is with the encrypt() method:

cryptotext <- encrypt(deck, plaintext)
print(chunkText(cryptotext, chunk.length = 5))
## [1] "swcrr jzedh iybet itckg caxxs embxu rdcgn leiff ftkad jiasn viobo shtdy kcali kqouk haqtq chhff crksh zosce vaqwb quuvb dpcub vvjhd agssp yvnnn bmihv mzedb dsxdm ttyho nrojg wvvux fmnta hjxfp hcqvs wipne kfvet lcaru cprxv acwpf eqelb qydim yamsy twgku dbifa wnuhp lhpux ktzia lwssr pngxz jucgh pqzro fjldj ygdxz ghrxc fokmc wmume sscdl tvtaq qntgy keydt swicj nqnen upcym gtvxq oblfx kalox ejbyw vitde jgjdd dfewf xpozo jnggj flncm eyzkq uxptj eotrv knlqm jhodv tguah dkwki xaxhm xivkm lldzf htlbb qarnl fvsjv hlumv unpcv bbker zmoup afjgr xpyvq yylob kflnq dtois vjnsf narit rjqri twqch foeio ynwzu aylpc dssho zpssg zygdl xouax sxebe uyxra uwdsq kwbjl rsxxi crfro hcvia mvuex agsxw xhdva jkxuf yfznf rihaq firhd uawpx snkat vqdhe kuero qpzmt slakr ervwa uejmk "

The message can then be decrypted using the same deck with

decrypted <- decrypt(deck, cryptotext)
print(chunkText(decrypted, chunk.length = 5))
## [1] "theti nkeri nhisb urial treew asawo ndert otheb irdst hevul tures thatc ameby dayto nosew ithth eirho okedb eaksa mongh isbut tonsa ndpoc ketsl ikeou trage ouspe tssoo nleft himna kedof hisra gsand flesh alike black mandr akesp rangb eneat hthet reeas itwil lwher ethes eedof theha ngedf allsa ndins pring anewb ranch pierc edhis breas tandf lower edina green bouto nnifr epere nnial benea thhis yello wgrin hetoo kthes parse winte rsnow supon whatt hatch ofhai rstil lclun gtohi sdrie dskul landh unter sthat passe dthat wayne verch anced tosee himbr oodin gamon ghisb arren limbs until windh adtol ledth ethin kersb onesa ndsea sonsl oosed themo nebyo netot hegro undbe lowan dhisb leach edand weath eredb riske thung intha tlone somew oodli keabo nebir dcage "