packages feed

pgp-wordlist 0.1 → 0.1.0.1

raw patch · 9 files changed

+508/−418 lines, 9 filesdep +containersdep +vectordep −bimapdep ~base

Dependencies added: containers, vector

Dependencies removed: bimap

Dependency ranges changed: base

Files

pgp-wordlist.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                pgp-wordlist-version:             0.1+version:             0.1.0.1 synopsis:            Translate between binary data and a human-readable                      collection of words. description:         Translate between binary data and a human-readable@@ -44,11 +44,15 @@ library     exposed-modules:     Data.Text.PgpWordlist                        , Data.Text.PgpWordlist.Internal.AltList-                       , Data.Text.PgpWordlist.Internal.Tables+                       , Data.Text.PgpWordlist.Internal.Convert+                       , Data.Text.PgpWordlist.Internal.Types+                       , Data.Text.PgpWordlist.Internal.Word8Bimap+                       , Data.Text.PgpWordlist.Internal.Words     build-depends:       base >=4.6 && < 5-                       , bimap >=0.3 && <0.4-                       , bytestring >=0.10 && <0.11-                       , text >=1.2 && <1.3+                       , bytestring >= 0.10+                       , containers+                       , text >=1.2+                       , vector >= 0.5     hs-source-dirs:      src     ghc-options:         -Wall     default-language:    Haskell2010@@ -56,15 +60,15 @@   test-suite tasty-    main-is: test/Main.hs-    ghc-options: -Wall-    type: exitcode-stdio-1.0+    main-is:          test/Main.hs+    ghc-options:      -Wall+    type:             exitcode-stdio-1.0     default-language: Haskell2010-    build-depends: pgp-wordlist-                 , base-                 , bytestring-                 , HUnit-                 , tasty >= 0.10-                 , tasty-hunit >= 0.9-                 , tasty-quickcheck-                 , text+    build-depends:    pgp-wordlist+                    , base+                    , bytestring+                    , HUnit+                    , tasty >= 0.10+                    , tasty-hunit >= 0.9+                    , tasty-quickcheck+                    , text
src/Data/Text/PgpWordlist.hs view
@@ -5,4 +5,7 @@     , TranslationError(..) ) where -import Data.Text.PgpWordlist.Internal.Tables+++import           Data.Text.PgpWordlist.Internal.Convert+import           Data.Text.PgpWordlist.Internal.Types
src/Data/Text/PgpWordlist/Internal/AltList.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE CPP #-} --- | Defines a type of list that containins two alternating element types.+-- | A type of list that contains two alternating element types. module Data.Text.PgpWordlist.Internal.AltList (       AltList(..) @@ -20,70 +20,84 @@   -#if __GLASGOW_HASKELL__ < 710 import Control.Applicative++#if MIN_VERSION_base(4,8,0)+import qualified Data.Bifunctor as Bi #endif    -- | List of elements of alternating element types.-data AltList a b = Nil | Cons a (AltList b a)+data AltList a b = Nil | a :<> AltList b a     deriving (Eq, Ord, Show) +infixr 5 :<> -- same as (:)  + -- | Simple conversion function. Inverse of 'toList'. fromList :: [a] -> AltList a a-fromList = foldr Cons Nil+fromList = foldr (:<>) Nil  -- | Simple conversion function. Inverse of 'fromList'. toList :: AltList a a -> [a] toList Nil = []-toList (Cons x xs) = x : toList xs+toList (x :<> xs) = x : toList xs   --- | Map over the first component of an 'AltList'.+-- | Map over every other element of an 'AltList', starting with the first+--   entry. -- -- @ -- 'first' f ≡ 'bimap' f 'id' -- @ first :: (a -> a') -> AltList a b -> AltList a' b first _ Nil = Nil-first f (Cons x Nil) = Cons (f x) Nil-first f (Cons x (Cons y ys)) = Cons (f x) (Cons y (first f ys))+first f (x :<> Nil) = f x :<> Nil+first f (x :<> y :<> ys) = f x :<> y :<> first f ys   --- | Map over the second component of an 'AltList'.+-- | Map over every other element of an 'AltList', starting with the second+--   entry. -- -- @ -- 'second' g ≡ 'bimap' 'id' g -- @ second :: (b -> b') -> AltList a b -> AltList a b' second _ Nil = Nil-second _ (Cons x Nil) = Cons x Nil-second g (Cons x (Cons y ys)) = Cons x (Cons (g y) (second g ys))+second g (x :<> xs) = x :<> first g xs   --- | Map over the both components of an 'AltList'.+-- | Map over the both types of entries of an 'AltList'. -- -- @ -- 'bimap' f g ≡ 'second' g . 'first' f -- @ bimap :: (a -> a') -> (b -> b') -> AltList a b -> AltList a' b' bimap _ _ Nil = Nil-bimap f g (Cons x xs) = Cons (f x) (bimap g f xs)+bimap f g (x :<> xs) = f x :<> bimap g f xs   --- | Monomorphic bisequence.+#if MIN_VERSION_base(4,8,0)+instance Bi.Bifunctor AltList where+    first = first+    second = second+    bimap = bimap+#endif++++-- | The 'Data.Bifunctor.Bifunctor' analogon to 'Data.Traversable.sequenceA'. bisequence :: Applicative f => AltList (f a) (f b) -> f (AltList a b) bisequence Nil = pure Nil-bisequence (Cons x xs) = Cons <$> x <*> bisequence xs+bisequence (x :<> xs) = liftA2 (:<>) x (bisequence xs) --- | Monomorphic bitraverse.+-- | The 'Data.Bifunctor.Bifunctor' analogon to 'Data.Traversable.traverse'. bitraverse :: Applicative f => (a -> f c) -> (b -> f d) -> AltList a b -> f (AltList c d) bitraverse _ _ Nil = pure Nil-bitraverse f g (Cons x xs) = Cons <$> f x <*> bitraverse g f xs+bitraverse f g (x :<> xs) = liftA2 (:<>) (f x) (bitraverse g f xs)
+ src/Data/Text/PgpWordlist/Internal/Convert.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE OverloadedStrings #-}++-- | Core functionality for conversion between binary formats and PGP word+--   lists.+module Data.Text.PgpWordlist.Internal.Convert where++++import qualified Data.Text.PgpWordlist.Internal.AltList as Alt+import           Data.Text.PgpWordlist.Internal.Types+import           Data.Text.PgpWordlist.Internal.Words+import           Data.Text.PgpWordlist.Internal.Word8Bimap++import qualified Data.ByteString.Lazy                   as BSL+import           Data.Text                              (Text)+import qualified Data.Text                              as T+import           Data.Word++++-- | 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++++-- | Look up the word corresponding to a byte.+toEvenWord :: Word8 -> EvenWord+toEvenWord = lookupL evenMap++-- | Look up the word corresponding to a byte.+toOddWord :: Word8 -> OddWord+toOddWord = lookupL oddMap++++-- | Simple conversion, taking into account invalid words.+fromEvenWord :: Text -> Either TranslationError Word8+fromEvenWord word = case lookupR evenMap (EvenWord word) of+    Just i  -> Right i+    Nothing -> Left (case lookupR oddMap (OddWord word) of+        Just j  -> BadParity word j+        Nothing -> BadWord word)++-- | Simple conversion, taking into account invalid words.+fromOddWord :: Text -> Either TranslationError Word8+fromOddWord word = case lookupR oddMap (OddWord word) of+    Just i  -> Right i+    Nothing -> Left (case lookupR evenMap (EvenWord word) of+        Just j  -> BadParity word j+        Nothing -> BadWord word)++++-- | Mapping from and to 'EvenWord's+evenMap :: Word8Bimap EvenWord+evenMap = unsafeConstruct (map pick12 wordList)+  where+    pick12 :: (a,b,c) -> (a,b)+    pick12 (i,e,_) = (i,e)++-- | Mapping from and to 'OddWord's+oddMap :: Word8Bimap OddWord+oddMap = unsafeConstruct (map pick13 wordList)+  where+    pick13 :: (a,b,c) -> (a,c)+    pick13 (i,_,o) = (i,o)
− src/Data/Text/PgpWordlist/Internal/Tables.hs
@@ -1,367 +0,0 @@-{-# 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"    )-            ]
+ src/Data/Text/PgpWordlist/Internal/Types.hs view
@@ -0,0 +1,32 @@+module Data.Text.PgpWordlist.Internal.Types where++++import           Data.Text                              (Text)+import           Data.Text.PgpWordlist.Internal.AltList+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)++-- | Word that is supposed to occur in an even position+newtype EvenWord = EvenWord { unEvenWord :: Text }+    deriving (Eq, Ord, Show)+++-- | Word that is supposed to occur in an odd position+newtype OddWord = OddWord { unOddWord :: Text }+    deriving (Eq, Ord, Show)
+ src/Data/Text/PgpWordlist/Internal/Word8Bimap.hs view
@@ -0,0 +1,46 @@+-- | Bimap between 'Word8' an an arbitrary (ordered) type. Values of type+-- 'Word8Bimap' have to be constructed carefully to contain values for+-- all 256 'Word8' values, or the API will not be safe to use.+module Data.Text.PgpWordlist.Internal.Word8Bimap (+      Word8Bimap+    , unsafeConstruct+    , lookupL+    , lookupR+) where++++import           Data.Map    (Map)+import qualified Data.Map    as M+import           Data.Vector (Vector, (!))+import qualified Data.Vector as V++import           Data.Tuple+import           Data.Word++++-- | Bimap where one of the keys is (of the size of) a 'Word8'.+data Word8Bimap a = Bimap (Vector a) (Map a Word8)++++-- | Create a 'Word8Bimap' from an association list.+--+-- The list must contain all 'Word8' in order to make the API operating on+-- the result safe.+unsafeConstruct :: Ord a => [(Word8, a)] -> Word8Bimap a+unsafeConstruct assocs = Bimap vec inverseMap+  where+    vec = V.fromList (map snd assocs)+    inverseMap = M.fromList (map swap assocs)++++-- | Get the value corresponding to an index.+lookupL :: Word8Bimap a -> Word8 -> a+lookupL (Bimap l _) i = l ! fromIntegral i++-- | Get the index corresponding to a value, if present.+lookupR :: Ord a => Word8Bimap a -> a -> Maybe Word8+lookupR (Bimap _ r) i = M.lookup i r
+ src/Data/Text/PgpWordlist/Internal/Words.hs view
@@ -0,0 +1,272 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.Text.PgpWordlist.Internal.Words where++++import           Data.Text.PgpWordlist.Internal.Types+import           Data.Word++++-- | Database of PGP words+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"    )+            ]
test/Main.hs view
@@ -1,17 +1,18 @@ {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ParallelListComp #-}+{-# LANGUAGE ParallelListComp  #-}  module Main where   -import           Data.Text.PgpWordlist.Internal.Tables+import           Data.Text.PgpWordlist.Internal.Convert -import qualified Data.ByteString.Lazy                  as BSL+import qualified Data.ByteString.Lazy                   as BSL import           Test.Tasty-import qualified Test.Tasty.HUnit                      as HU-import qualified Test.Tasty.QuickCheck                 as QC+import qualified Test.Tasty.HUnit                       as HU+import qualified Test.Tasty.QuickCheck                  as QC import           Text.Printf+import           Data.Word   @@ -32,30 +33,32 @@ allBytesConvertible :: TestTree allBytesConvertible = testGroup "All bytes have a corresponding word" tests   where-    tests = map evenTest [0..] ++ map oddTest [0..]-    evenTest i = HU.testCase (printf "%02x is convertible to an even word" i)-                             (evenTestAssertion i)-    oddTest  i = HU.testCase (printf "%02x is convertible to an odd word" i)-                             (oddTestAssertion i)-    evenTestAssertion i = HU.assertBool "" (toEvenWord i `seq` True)-    oddTestAssertion  i = HU.assertBool "" (toOddWord  i `seq` True)+    tests = splice (map evenTest allWord8) (map oddTest allWord8)+    allWord8 = [minBound ..] :: [Word8]+    evenTest = testExists "%02x is convertible to an even word" toEvenWord+    oddTest  = testExists "%02x is convertible to an odd word"  toOddWord+    testExists msg f i = HU.testCase (printf msg i) (exists (f i))+    exists x =  HU.assertBool "" (x `seq` True) +    -- Combine two lists alternatingly+    -- splice [1,3,5] [2,4,6,8,10] ==> [1,2,3,4,5,6,7,10]+    splice []     ys = ys+    splice (x:xs) ys = x : splice ys xs  + examplesBackAndForth :: TestTree examplesBackAndForth = testGroup "Conversion beween various examples" tests   where     tests = concat-        [ [ HU.testCase (printf "#%2d: %s -> PGP words" i (bsToHex bytes))+        [ [ HU.testCase (printf "#%2d: Bytes to PGP words" i)                         (HU.assertEqual "" (toText bytes) pgpWords)-          , HU.testCase (printf "#%2d: PGP words -> %s" i (bsToHex bytes))+          , HU.testCase (printf "#%2d: PGP words to bytes" i)                         (HU.assertEqual "" (Right bytes) (fromText pgpWords))           ]         | (bytes, pgpWords) <- testCases         | i <- [0..] :: [Int]         ]-    bsToHex :: BSL.ByteString -> String-    bsToHex = concatMap (printf "%02x") . BSL.unpack      testCases = map (\(x,y) -> (BSL.pack x, y))         [ ( [0xce, 0xe7, 0x1c, 0xbf, 0xe6, 0x59, 0x49, 0x48]@@ -115,5 +118,7 @@               . testGroup "Random roundtrips"     tests = [ QC.testProperty "Bytes -> PGP words -> Bytes" $                 \bytes -> let bs = BSL.pack bytes-                          in  fromText (toText bs) == Right bs+                          in  length bytes > 10+                              QC.==>+                              fromText (toText bs) == Right bs             ]