Last active
June 26, 2020 14:08
-
-
Save JustusAdam/e006e77a8407cc76e6dc853f2a255ff2 to your computer and use it in GitHub Desktop.
A simple('ish) and transparent password generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- A one-file Haskell password generator file. | |
-- | |
-- has two generation methods | |
-- chars which works simply by generating random characters | |
-- dict which selects words from a dictionary. | |
-- | |
-- The `dict` generation method needs `aspell` to be installed on the system (as | |
-- it uses the `aspell` wordlists). The language argument to the `dicts` method | |
-- must be a valid `aspell` language identifierm with the default being 'en'. | |
-- | |
-- Also need the vector, MonadRandom and optparse-simple libraries to be | |
-- installed. | |
import Control.Monad.Random as Random | |
import qualified Data.Char as C | |
import Data.Ratio ((%)) | |
import qualified Data.Vector as V | |
import Options.Applicative.Simple | |
import qualified System.Process as P | |
lowerCaseLetters = ['a' .. 'z'] | |
upperCaseLetters = ['A' .. 'Z'] | |
numbers = ['0' .. '9'] | |
symbols = "-_/[]{}()*&^%$#@." | |
charGen length symbols = go | |
where | |
allowedVals = lowerCaseLetters ++ upperCaseLetters ++ numbers ++ symbols | |
go = do | |
p <- sequence $ replicate length (uniform allowedVals) | |
if and $ | |
flip sequence p $ | |
map | |
(any . flip elem) | |
[lowerCaseLetters, upperCaseLetters, numbers, symbols] | |
then return p | |
else go | |
capitalize [] = [] | |
capitalize (x:xs) = C.toUpper x : xs | |
-- aspell -d en dump master | aspell -l en expand > words | |
dictGen language len symbols = do | |
dump <- P.readProcess "aspell" ["-d", language, "dump", "master"] "" | |
words <- | |
V.fromList . filter ('\'' `notElem`) . words <$> | |
P.readProcess "aspell" ["-l", language, "expand"] dump | |
let getWord = do | |
idx <- getRandomR (0, V.length words) | |
pure $ capitalize $ words V.! idx | |
ensure coll word | |
| any (`elem` coll) word = pure word | |
| otherwise = (: word) <$> uniform coll | |
getNumber = pure <$> uniform numbers | |
getSymbol = pure <$> uniform symbols | |
selectNext prev = do | |
new <- | |
(++ prev) <$> | |
join | |
(Random.fromList | |
[(getNumber, 1 % 4), (getSymbol, 1 % 4), (getWord, 1 % 2)]) | |
if length new < len | |
then selectNext new | |
else ensure numbers new >>= ensure symbols | |
selectNext "" | |
toClipboard = const (error "Sorry, the clipboard copy does not work yet") | |
-- void . P.readProcess "xclip" ["-selection", "clipboard"] | |
main = do | |
((len, syms, copy), method) <- | |
simpleOptions | |
"2.1.0" | |
"Password Generator" | |
"Generates strong, random passwords" | |
((,,) <$> | |
option | |
auto | |
(short 'l' <> | |
long "length" <> value 20 <> help "(minimal) Length of the password") <*> | |
strOption | |
(long "symbols" <> value symbols <> help "valid symbol characters") <*> | |
switch (long "copy" <> help "Copy result to clipboard (uses xclip)")) $ do | |
addCommand | |
"chars" | |
"Generates each character individually" | |
id | |
(pure charGen) | |
addCommand | |
"dict" | |
"Generate using words from a dictionary" | |
id | |
(dictGen <$> | |
strOption | |
(short 'd' <> | |
long "language" <> | |
help "Language to use for the dictionary." <> value "en")) | |
(if copy then toClipboard else putStr) =<< method len syms |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment