wordpass 1.0.0.7 → 1.0.0.9
raw patch · 8 files changed
+85/−117 lines, 8 filesdep +QuickCheckdep +optparse-applicativedep −hflagsdep −random-fudep −random-sourcedep ~basedep ~vector
Dependencies added: QuickCheck, optparse-applicative
Dependencies removed: hflags, random-fu, random-source
Dependency ranges changed: base, vector
Files
- Data/Random/Choice.hs +0/−21
- Data/Random/RVar/Enum.hs +0/−15
- Data/Random/Vector.hs +0/−12
- Text/WordPass.hs +20/−18
- WordPass.hs +47/−36
- changelog +5/−0
- stack.yaml +0/−1
- wordpass.cabal +13/−14
− Data/Random/Choice.hs
@@ -1,21 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}--- | Random choice between two alternatives of inequal probability.-module Data.Random.Choice(randomChoice) where--import Data.Random.RVar-import Data.Random.Distribution-import Data.Random.Distribution.Uniform---- | Performs random choice between two RVar values.--- Input is a _ratio_ of the _relative_ probabilities between first and--- second option (A/B).-randomChoice :: (Fractional r, Ord r, Data.Random.Distribution.Distribution Uniform r) => r ->- RVar b -> RVar b -> RVar b-randomChoice ratio variantA variantB = do draw <- uniform 0 1- if draw >= probabilityOfVariantA- then variantA- else variantB- where- probabilityOfVariantA = 1/(1+1/ratio)--
− Data/Random/RVar/Enum.hs
@@ -1,15 +0,0 @@-{-# LANGUAGE OverlappingInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleInstances #-}--- | Provides a random variable (@RVar@) instance for any @Enum@ instance.--- Very convenient, even if may overlap with more specific instances for integer types.-module Data.Random.RVar.Enum() where--import Data.Random.Distribution-import Data.Random.Distribution.Uniform-import Control.Applicative((<$>))---- TODO: Send to library author as a "missing instance"-instance (Enum a) => Distribution Uniform a where- rvarT (Uniform l h) = toEnum <$> uniformT (fromEnum l) (fromEnum h)-
− Data/Random/Vector.hs
@@ -1,12 +0,0 @@--- | Provides a convenient way to pick a random element of a vector.-module Data.Random.Vector(randomElement) where--import Data.Random.RVar-import Data.Random.Distribution.Uniform-import qualified Data.Vector as V-import Control.Applicative---- | Take a random element of a vector.-randomElement :: V.Vector a -> RVar a-randomElement elements = (elements V.!) <$> uniform 0 (V.length elements - 1)-
Text/WordPass.hs view
@@ -1,4 +1,5 @@-{-# LANGUAGE TemplateHaskell, NoMonomorphismRestriction #-}+{-# LANGUAGE NoMonomorphismRestriction #-}+{-# LANGUAGE ScopedTypeVariables #-} -- | Main module generating passwords. module Text.WordPass where @@ -12,12 +13,7 @@ import qualified Data.Set as Set import Data.Set (Set) import Data.Char (isAlpha, isPunctuation, isSymbol)-import Data.Random.RVar-import Data.Random.RVar.Enum()-import Data.Random.Choice-import Data.Random.Vector-import Data.Random.Distribution.Uniform-import Data.Random.Source.IO()+import Test.QuickCheck.Gen import qualified Data.Vector as V import Control.Applicative import Control.Monad (replicateM, foldM, filterM)@@ -36,7 +32,7 @@ resolveSymbolicLink s = do b <- isSymbolicLink `fmap` getSymbolicLinkStatus s if b then do newPath <- readSymbolicLink s- resolveSymbolicLink $! takeDirectory s </> newPath + resolveSymbolicLink $! takeDirectory s </> newPath else return s -- | Reads a dict format to get a list of unique words without any special@@ -92,9 +88,9 @@ defaultDictionary = "/usr/share/dict/british-english" -- | Pick a random password, given a words list, and a number of words it will contain.-randomPassword :: WordList -> Int -> RVar Text-randomPassword wordlist numWords = do ws <- replicateM numWords $ randomElement wordlist- seps <- replicateM numWords randomSeparator+randomPassword :: WordList -> Int -> Gen Text+randomPassword wordlist numWords = do ws <- replicateM numWords $ randomElement wordlist+ seps <- replicateM numWords randomSeparator return $ Text.concat $ zipWith Text.append ws seps -- | Estimate strength of random password with given inputs.@@ -113,25 +109,31 @@ -- * Random separators -- | Randomly pick a word separator as a two-digit number, or a symbol -- character.-randomSeparator :: RVar Text-randomSeparator = randomChoice ratio randomSymbolSeparator randomNumericSeparator+randomSeparator :: Gen Text+randomSeparator = do+ r <- choose (0.0, 1.0::Double)+ if r > ratio+ then randomSymbolSeparator+ else randomNumericSeparator where- ratio = numSymbols % numNumericSeparators+ ratio :: Double = fromIntegral numSymbols / fromIntegral(numNumericSeparators+numSymbols) -- | Two-digit number as a separator 10^2 = 6.6 bits of entropy.-randomNumericSeparator :: RVar Text-randomNumericSeparator = Text.pack <$> show <$> uniform 0 (numNumericSeparators :: Int)+randomNumericSeparator :: Gen Text+randomNumericSeparator = Text.pack . show <$> choose (0, numNumericSeparators-1) -- | Conjunction of two unary predicates (|||) :: (t -> Bool) -> (t -> Bool) -> t -> Bool (|||) f g x = f x || g x +randomElement :: V.Vector a -> Gen a+randomElement v = (v V.!) <$> choose (0, V.length v-1)+ -- | List of symbol and punctuation characters in ASCII -- Should be 5 bits of entropy symbolChars :: V.Vector Char symbolChars = V.fromList $ filter (isSymbol ||| isPunctuation) $ map toEnum [0..127] -- | Text with random symbol character, 5 bits of entropy-randomSymbolSeparator :: RVar Text+randomSymbolSeparator :: Gen Text randomSymbolSeparator = Text.singleton <$> randomElement symbolChars-
WordPass.hs view
@@ -1,55 +1,66 @@-{-# LANGUAGE TemplateHaskell, NoMonomorphismRestriction #-}+{-# LANGUAGE NoMonomorphismRestriction #-}+{-# LANGUAGE RecordWildCards #-} -- | Main module generating passwords. module Main where import qualified Data.Text.IO as Text import qualified Data.Set as Set-import Data.Random.Sample-import Data.Random.RVar.Enum()-import Data.Random.Source.DevRandom-import Data.Random.Source.IO() import qualified Data.Vector as V import Control.Applicative import Control.Monad (replicateM_)-import HFlags+import Options.Applicative import Text.WordPass+import Test.QuickCheck.Gen(generate) -- * Command-line flags--- | Number of words per password-defineFlag "w:words" (4 :: Int) "Number of words for each password."---- | Number of passwords-defineFlag "p:passwords" (10 :: Int) "Number of passwords to generate."---- | Default word list directory.-defineFlag "d:directory" ("/usr/share/dict" :: FilePath) "Default directory to search for dictionaries\n (works only if --wordlist options is NOT USED.)"---- | Pick specific wordlist.-defineFlag "l:wordlist" ("" :: FilePath) "Select particular dictionary (filepath)."---- | Pick specific wordlist.-defineFlag "t:pseudorandom" (False :: Bool) "Generate passwords using StdRandom generator, instead of DevRandom. (Faster, but less safe. Good for testing."---- | Fake flag to avoid GHC losing the last instance declaration-defineFlag "fakeflag" (False :: Bool) "This flag does nothing and is never used"+data Options = Options {+ optWords :: Int+ -- ^ Number of words per password+ , optCount :: Int+ -- ^ Number of passwords+ , optWordlist :: Either FilePath FilePath+ -- ^ Default word list directory or pick specific wordlist.+ } deriving (Show) +options :: Parser Options+options = Options+ <$> option auto (short 'w' <>+ long "words" <>+ metavar "INT" <>+ value 4 <>+ help "Number of words for each password.")+ <*> option auto (short 'p' <>+ long "passwords" <>+ metavar "COUNT" <>+ value 10 <>+ help "Number of passwords to generate.")+ <*> ((Left <$> strOption (short 'd' <>+ long "directory" <>+ metavar "DIR" <>+ value "/usr/share/dict" <>+ help "Default directory to search for dictionaries."))+ <|> (Right <$> strOption (short 'l' <>+ long "wordlist" <>+ metavar "FILENAME" <>+ help "Use specific wordlist file."))) -- | Read wordlist given by explict filepath, or search for all wordlists in a given directory.-selectWordList :: FilePath -> FilePath -> IO WordSet-selectWordList "" dir = readDictDir dir-selectWordList filename _ = readDict filename+selectWordList :: Either FilePath FilePath -> IO WordSet+selectWordList (Left dir ) = readDictDir dir+selectWordList (Right filename) = readDict filename +parseOptions = info (options <**> helper)+ (fullDesc <>+ progDesc "Generate xkcd-style passwords" <>+ header "WordPass - dictionary-based password generator")+ main :: IO ()-main = do _args <- $initHFlags "WordPass - dictionary-based password generator"- dictWords <- (V.fromList . Set.toList) <$> selectWordList flags_wordlist flags_directory+main = do Options {..} <- execParser parseOptions+ dictWords <- (V.fromList . Set.toList) <$> selectWordList optWordlist putStrLn $ "Read " ++ show (V.length dictWords) ++ " words from dictionaries." putStr "Estimated password strength (bits): "- print $ randomPasswordStrength dictWords flags_words- replicateM_ flags_passwords $ do - let rand = randomPassword dictWords flags_words- rv <- if flags_pseudorandom- then sample rand- else sampleFrom DevRandom rand+ print $ randomPasswordStrength dictWords optWords+ replicateM_ optCount $ do+ let rand = randomPassword dictWords optWords+ rv <- generate rand Text.putStrLn rv--
changelog view
@@ -1,4 +1,9 @@ -*-Changelog-*-+1.0.0.9 May 2018+ * Use Test.QuickCheck.Gen for random generation instead of RVar.++1.0.0.8 Nov 2017+ * Relax deps for GHC 8.2. 1.0.0.7 Nov 2016 * Updated to directory-1.2.6.2 on GHC 8.0.
stack.yaml view
@@ -36,7 +36,6 @@ # non-dependency (i.e. a user package), and its test suites and benchmarks # will not be run. This is useful for tweaking upstream packages. packages:-#- haskeleton/ - '.' # Dependency packages to be pulled from upstream that are not in the resolver # (e.g., acme-missiles-0.3)
wordpass.cabal view
@@ -1,5 +1,5 @@ name: wordpass-version: 1.0.0.7+version: 1.0.0.9 synopsis: Dictionary-based password generator description: This script reads dict word lists and generates word-based passwords. Not unlike <http://xkcd.com/936/ xkcd>.@@ -22,7 +22,7 @@ extra-source-files: README.md changelog stack.yaml cabal-version: >=1.10 stability: stable-tested-with: GHC==8.0.1+tested-with: GHC==7.8.4,GHC==7.10.3,GHC==8.0.1,GHC==8.2.2 source-repository head type: git@@ -30,36 +30,35 @@ executable wordpass main-is: WordPass.hs- other-modules: Data.Random.RVar.Enum, Data.Random.Vector, Data.Random.Choice, Text.WordPass+ other-modules: Text.WordPass other-extensions: OverlappingInstances, MultiParamTypeClasses, FlexibleInstances- build-depends: base >=4.4 && <4.10,+ build-depends: base >=4.4 && <4.11, text >=1.1 && <1.4, containers >=0.5 && <0.6,- random-fu >=0.2 && <0.3,- random-source >=0.3 && <0.4,- vector >=0.10 && <0.12,+ vector >=0.10 && <0.13, directory >= 1.2 && <1.4,+ optparse-applicative >= 0.12 && <0.14, unix-compat >= 0.4 && <0.5, deepseq >= 1.3 && <1.5, filepath >= 1.3 && <1.5,- hflags >= 0.4 && <0.5+ QuickCheck >= 2.0 && <3.0 ghc-options: -O3 -- hs-source-dirs: default-language: Haskell2010 library- exposed-modules: Data.Random.RVar.Enum, Data.Random.Vector, Data.Random.Choice, Text.WordPass+ exposed-modules: Text.WordPass other-extensions: OverlappingInstances, MultiParamTypeClasses, FlexibleInstances- build-depends: base >=4.4 && <4.10,+ build-depends: base >=4.4 && <4.11, text >=1.1 && <1.4, containers >=0.5 && <0.6,- random-fu >=0.2 && <0.3,- random-source >=0.3 && <0.4,- vector >=0.10 && <0.12,+ vector >=0.10 && <0.13, directory >= 1.2 && <1.4, unix-compat >= 0.4 && <0.5, deepseq >= 1.3 && <1.5,- filepath >= 1.3 && <1.5+ QuickCheck >= 2.0 && <3.0,+ filepath >= 1.3 && <1.5,+ optparse-applicative >= 0.12 && <0.14 ghc-options: -O3 -- hs-source-dirs: default-language: Haskell2010