pgp-wordlist-0.1: src/Data/Text/PgpWordlist/Internal/Tables.hs
{-# LANGUAGE OverloadedStrings #-}
-- | Core functionality for conversion between binary formats and PGP word
-- lists.
module Data.Text.PgpWordlist.Internal.Tables where
import Data.Text.PgpWordlist.Internal.AltList (AltList)
import qualified Data.Text.PgpWordlist.Internal.AltList as Alt
import Data.Bimap (Bimap, (!))
import qualified Data.Bimap as BM
import qualified Data.ByteString.Lazy as BSL
import Data.Text (Text)
import qualified Data.Text as T
import Data.Word
-- | Abstract representation of a PGP word list.
newtype PgpWordlist = PgpWordlist (AltList EvenWord OddWord)
deriving (Eq, Ord, Show)
-- | Possible translation errors from a list of PGP words to binary format.
data TranslationError =
BadWord Text -- ^ Word is not recognized
| BadParity Text Word8 -- ^ Word is recognized, but from the wrong
-- alphabet. Duplicates, omissions, and neighbour
-- transpositions are often cause for this.
deriving (Eq, Ord, Show)
-- | Inverse of 'fromText', modulo whitespace count.
toText :: BSL.ByteString -> Text
toText = T.intercalate " "
. Alt.toList
. Alt.bimap (unEvenWord . toEvenWord) (unOddWord . toOddWord)
. Alt.fromList
. BSL.unpack
-- | Convert a text of whitespace-separated words to their binary
-- representation. The whitespace splitting behaviour is given by 'T.words'.
fromText :: Text -> Either TranslationError BSL.ByteString
fromText = fmap (BSL.pack . Alt.toList)
. Alt.bitraverse fromEvenWord fromOddWord
. Alt.fromList
. T.words
-- | Word that is supposed to occur in an even position
newtype EvenWord = EvenWord { unEvenWord :: Text }
deriving (Eq, Ord, Show)
-- | Look up the word corresponding to a byte.
toEvenWord :: Word8 -> EvenWord
toEvenWord = (evenMap !) -- evenMap is total, so the lookup is safe
-- | Simple conversion, taking into account invalid words.
fromEvenWord :: Text -> Either TranslationError Word8
fromEvenWord word = case BM.lookupR (EvenWord word) evenMap of
Just i -> Right i
Nothing -> Left (case BM.lookupR (OddWord word) oddMap of
Just j -> BadParity word j
Nothing -> BadWord word)
-- | Mapping from and to 'EvenWord's
evenMap :: Bimap Word8 EvenWord
evenMap = BM.fromList (map pick12 wordList)
where
pick12 :: (a,b,c) -> (a,b)
pick12 (i,e,_) = (i,e)
-- | Word that is supposed to occur in an odd position
newtype OddWord = OddWord { unOddWord :: Text }
deriving (Eq, Ord, Show)
-- | Look up the word corresponding to a byte.
toOddWord :: Word8 -> OddWord
toOddWord = (oddMap !) -- oddMap is total, so the lookup is safe
-- | Simple conversion, taking into account invalid words.
fromOddWord :: Text -> Either TranslationError Word8
fromOddWord word = case BM.lookupR (OddWord word) oddMap of
Just i -> Right i
Nothing -> Left (case BM.lookupR (EvenWord word) evenMap of
Just j -> BadParity word j
Nothing -> BadWord word)
-- | Mapping from and to 'OddWord's
oddMap :: Bimap Word8 OddWord
oddMap = BM.fromList (map pick13 wordList)
where
pick13 :: (a,b,c) -> (a,c)
pick13 (i,_,o) = (i,o)
wordList :: [(Word8, EvenWord, OddWord)]
wordList = map (\(i,e,o) -> (i, EvenWord e, OddWord o)) table
where
table = [ (0x00, "aardvark" , "adroitness" )
, (0x01, "absurd" , "adviser" )
, (0x02, "accrue" , "aftermath" )
, (0x03, "acme" , "aggregate" )
, (0x04, "adrift" , "alkali" )
, (0x05, "adult" , "almighty" )
, (0x06, "afflict" , "amulet" )
, (0x07, "ahead" , "amusement" )
, (0x08, "aimless" , "antenna" )
, (0x09, "algol" , "applicant" )
, (0x0a, "allow" , "apollo" )
, (0x0b, "alone" , "armistice" )
, (0x0c, "ammo" , "article" )
, (0x0d, "ancient" , "asteroid" )
, (0x0e, "apple" , "atlantic" )
, (0x0f, "artist" , "atmosphere" )
, (0x10, "assume" , "autopsy" )
, (0x11, "athens" , "babylon" )
, (0x12, "atlas" , "backwater" )
, (0x13, "aztec" , "barbecue" )
, (0x14, "baboon" , "belowground")
, (0x15, "backfield", "bifocals" )
, (0x16, "backward" , "bodyguard" )
, (0x17, "banjo" , "bookseller" )
, (0x18, "beaming" , "borderline" )
, (0x19, "bedlamp" , "bottomless" )
, (0x1a, "beehive" , "bradbury" )
, (0x1b, "beeswax" , "bravado" )
, (0x1c, "befriend" , "brazilian" )
, (0x1d, "belfast" , "breakaway" )
, (0x1e, "berserk" , "burlington" )
, (0x1f, "billiard" , "businessman")
, (0x20, "bison" , "butterfat" )
, (0x21, "blackjack", "camelot" )
, (0x22, "blockade" , "candidate" )
, (0x23, "blowtorch", "cannonball" )
, (0x24, "bluebird" , "capricorn" )
, (0x25, "bombast" , "caravan" )
, (0x26, "bookshelf", "caretaker" )
, (0x27, "brackish" , "celebrate" )
, (0x28, "breadline", "cellulose" )
, (0x29, "breakup" , "certify" )
, (0x2a, "brickyard", "chambermaid")
, (0x2b, "briefcase", "cherokee" )
, (0x2c, "burbank" , "chicago" )
, (0x2d, "button" , "clergyman" )
, (0x2e, "buzzard" , "coherence" )
, (0x2f, "cement" , "combustion" )
, (0x30, "chairlift", "commando" )
, (0x31, "chatter" , "company" )
, (0x32, "checkup" , "component" )
, (0x33, "chisel" , "concurrent" )
, (0x34, "choking" , "confidence" )
, (0x35, "chopper" , "conformist" )
, (0x36, "christmas", "congregate" )
, (0x37, "clamshell", "consensus" )
, (0x38, "classic" , "consulting" )
, (0x39, "classroom", "corporate" )
, (0x3a, "cleanup" , "corrosion" )
, (0x3b, "clockwork", "councilman" )
, (0x3c, "cobra" , "crossover" )
, (0x3d, "commence" , "crucifix" )
, (0x3e, "concert" , "cumbersome" )
, (0x3f, "cowbell" , "customer" )
, (0x40, "crackdown", "dakota" )
, (0x41, "cranky" , "decadence" )
, (0x42, "crowfoot" , "december" )
, (0x43, "crucial" , "decimal" )
, (0x44, "crumpled" , "designing" )
, (0x45, "crusade" , "detector" )
, (0x46, "cubic" , "detergent" )
, (0x47, "dashboard", "determine" )
, (0x48, "deadbolt" , "dictator" )
, (0x49, "deckhand" , "dinosaur" )
, (0x4a, "dogsled" , "direction" )
, (0x4b, "dragnet" , "disable" )
, (0x4c, "drainage" , "disbelief" )
, (0x4d, "dreadful" , "disruptive" )
, (0x4e, "drifter" , "distortion" )
, (0x4f, "dropper" , "document" )
, (0x50, "drumbeat" , "embezzle" )
, (0x51, "drunken" , "enchanting" )
, (0x52, "dupont" , "enrollment" )
, (0x53, "dwelling" , "enterprise" )
, (0x54, "eating" , "equation" )
, (0x55, "edict" , "equipment" )
, (0x56, "egghead" , "escapade" )
, (0x57, "eightball", "eskimo" )
, (0x58, "endorse" , "everyday" )
, (0x59, "endow" , "examine" )
, (0x5a, "enlist" , "existence" )
, (0x5b, "erase" , "exodus" )
, (0x5c, "escape" , "fascinate" )
, (0x5d, "exceed" , "filament" )
, (0x5e, "eyeglass" , "finicky" )
, (0x5f, "eyetooth" , "forever" )
, (0x60, "facial" , "fortitude" )
, (0x61, "fallout" , "frequency" )
, (0x62, "flagpole" , "gadgetry" )
, (0x63, "flatfoot" , "galveston" )
, (0x64, "flytrap" , "getaway" )
, (0x65, "fracture" , "glossary" )
, (0x66, "framework", "gossamer" )
, (0x67, "freedom" , "graduate" )
, (0x68, "frighten" , "gravity" )
, (0x69, "gazelle" , "guitarist" )
, (0x6a, "geiger" , "hamburger" )
, (0x6b, "glitter" , "hamilton" )
, (0x6c, "glucose" , "handiwork" )
, (0x6d, "goggles" , "hazardous" )
, (0x6e, "goldfish" , "headwaters" )
, (0x6f, "gremlin" , "hemisphere" )
, (0x70, "guidance" , "hesitate" )
, (0x71, "hamlet" , "hideaway" )
, (0x72, "highchair", "holiness" )
, (0x73, "hockey" , "hurricane" )
, (0x74, "indoors" , "hydraulic" )
, (0x75, "indulge" , "impartial" )
, (0x76, "inverse" , "impetus" )
, (0x77, "involve" , "inception" )
, (0x78, "island" , "indigo" )
, (0x79, "jawbone" , "inertia" )
, (0x7a, "keyboard" , "infancy" )
, (0x7b, "kickoff" , "inferno" )
, (0x7c, "kiwi" , "informant" )
, (0x7d, "klaxon" , "insincere" )
, (0x7e, "locale" , "insurgent" )
, (0x7f, "lockup" , "integrate" )
, (0x80, "merit" , "intention" )
, (0x81, "minnow" , "inventive" )
, (0x82, "miser" , "istanbul" )
, (0x83, "mohawk" , "jamaica" )
, (0x84, "mural" , "jupiter" )
, (0x85, "music" , "leprosy" )
, (0x86, "necklace" , "letterhead" )
, (0x87, "neptune" , "liberty" )
, (0x88, "newborn" , "maritime" )
, (0x89, "nightbird", "matchmaker" )
, (0x8a, "oakland" , "maverick" )
, (0x8b, "obtuse" , "medusa" )
, (0x8c, "offload" , "megaton" )
, (0x8d, "optic" , "microscope" )
, (0x8e, "orca" , "microwave" )
, (0x8f, "payday" , "midsummer" )
, (0x90, "peachy" , "millionaire")
, (0x91, "pheasant" , "miracle" )
, (0x92, "physique" , "misnomer" )
, (0x93, "playhouse", "molasses" )
, (0x94, "pluto" , "molecule" )
, (0x95, "preclude" , "montana" )
, (0x96, "prefer" , "monument" )
, (0x97, "preshrunk", "mosquito" )
, (0x98, "printer" , "narrative" )
, (0x99, "prowler" , "nebula" )
, (0x9a, "pupil" , "newsletter" )
, (0x9b, "puppy" , "norwegian" )
, (0x9c, "python" , "october" )
, (0x9d, "quadrant" , "ohio" )
, (0x9e, "quiver" , "onlooker" )
, (0x9f, "quota" , "opulent" )
, (0xa0, "ragtime" , "orlando" )
, (0xa1, "ratchet" , "outfielder" )
, (0xa2, "rebirth" , "pacific" )
, (0xa3, "reform" , "pandemic" )
, (0xa4, "regain" , "pandora" )
, (0xa5, "reindeer" , "paperweight")
, (0xa6, "rematch" , "paragon" )
, (0xa7, "repay" , "paragraph" )
, (0xa8, "retouch" , "paramount" )
, (0xa9, "revenge" , "passenger" )
, (0xaa, "reward" , "pedigree" )
, (0xab, "rhythm" , "pegasus" )
, (0xac, "ribcage" , "penetrate" )
, (0xad, "ringbolt" , "perceptive" )
, (0xae, "robust" , "performance")
, (0xaf, "rocker" , "pharmacy" )
, (0xb0, "ruffled" , "phonetic" )
, (0xb1, "sailboat" , "photograph" )
, (0xb2, "sawdust" , "pioneer" )
, (0xb3, "scallion" , "pocketful" )
, (0xb4, "scenic" , "politeness" )
, (0xb5, "scorecard", "positive" )
, (0xb6, "scotland" , "potato" )
, (0xb7, "seabird" , "processor" )
, (0xb8, "select" , "provincial" )
, (0xb9, "sentence" , "proximate" )
, (0xba, "shadow" , "puberty" )
, (0xbb, "shamrock" , "publisher" )
, (0xbc, "showgirl" , "pyramid" )
, (0xbd, "skullcap" , "quantity" )
, (0xbe, "skydive" , "racketeer" )
, (0xbf, "slingshot", "rebellion" )
, (0xc0, "slowdown" , "recipe" )
, (0xc1, "snapline" , "recover" )
, (0xc2, "snapshot" , "repellent" )
, (0xc3, "snowcap" , "replica" )
, (0xc4, "snowslide", "reproduce" )
, (0xc5, "solo" , "resistor" )
, (0xc6, "southward", "responsive" )
, (0xc7, "soybean" , "retraction" )
, (0xc8, "spaniel" , "retrieval" )
, (0xc9, "spearhead", "retrospect" )
, (0xca, "spellbind", "revenue" )
, (0xcb, "spheroid" , "revival" )
, (0xcc, "spigot" , "revolver" )
, (0xcd, "spindle" , "sandalwood" )
, (0xce, "spyglass" , "sardonic" )
, (0xcf, "stagehand", "saturday" )
, (0xd0, "stagnate" , "savagery" )
, (0xd1, "stairway" , "scavenger" )
, (0xd2, "standard" , "sensation" )
, (0xd3, "stapler" , "sociable" )
, (0xd4, "steamship", "souvenir" )
, (0xd5, "sterling" , "specialist" )
, (0xd6, "stockman" , "speculate" )
, (0xd7, "stopwatch", "stethoscope")
, (0xd8, "stormy" , "stupendous" )
, (0xd9, "sugar" , "supportive" )
, (0xda, "surmount" , "surrender" )
, (0xdb, "suspense" , "suspicious" )
, (0xdc, "sweatband", "sympathy" )
, (0xdd, "swelter" , "tambourine" )
, (0xde, "tactics" , "telephone" )
, (0xdf, "talon" , "therapist" )
, (0xe0, "tapeworm" , "tobacco" )
, (0xe1, "tempest" , "tolerance" )
, (0xe2, "tiger" , "tomorrow" )
, (0xe3, "tissue" , "torpedo" )
, (0xe4, "tonic" , "tradition" )
, (0xe5, "topmost" , "travesty" )
, (0xe6, "tracker" , "trombonist" )
, (0xe7, "transit" , "truncated" )
, (0xe8, "trauma" , "typewriter" )
, (0xe9, "treadmill", "ultimate" )
, (0xea, "trojan" , "undaunted" )
, (0xeb, "trouble" , "underfoot" )
, (0xec, "tumor" , "unicorn" )
, (0xed, "tunnel" , "unify" )
, (0xee, "tycoon" , "universe" )
, (0xef, "uncut" , "unravel" )
, (0xf0, "unearth" , "upcoming" )
, (0xf1, "unwind" , "vacancy" )
, (0xf2, "uproot" , "vagabond" )
, (0xf3, "upset" , "vertigo" )
, (0xf4, "upshot" , "virginia" )
, (0xf5, "vapor" , "visitor" )
, (0xf6, "village" , "vocalist" )
, (0xf7, "virus" , "voyager" )
, (0xf8, "vulcan" , "warranty" )
, (0xf9, "waffle" , "waterloo" )
, (0xfa, "wallet" , "whimsical" )
, (0xfb, "watchword", "wichita" )
, (0xfc, "wayside" , "wilmington" )
, (0xfd, "willow" , "wyoming" )
, (0xfe, "woodlark" , "yesteryear" )
, (0xff, "zulu" , "yucatan" )
]