mirror of
https://github.com/redelmann/scat
synced 2025-01-27 06:24:21 +01:00
119 lines
3.0 KiB
Haskell
119 lines
3.0 KiB
Haskell
|
{-# LANGUAGE OverloadedStrings, PatternGuards #-}
|
||
|
|
||
|
-- | Password scatterer.
|
||
|
module Main (main) where
|
||
|
|
||
|
import Data.Monoid
|
||
|
import Data.Digest.Pure.SHA
|
||
|
import Data.ByteString.Lazy (ByteString)
|
||
|
import qualified Data.ByteString.Char8 as C
|
||
|
import qualified Data.ByteString.Lazy.Char8 as LC
|
||
|
import qualified Data.ByteString.Lazy as BS
|
||
|
import System.IO
|
||
|
import System.Exit
|
||
|
import Control.Exception
|
||
|
import Control.Monad.Reader
|
||
|
|
||
|
import Scat.Schemas
|
||
|
import Scat.Builder
|
||
|
import Scat.Options
|
||
|
|
||
|
-- | Generates the seed integer given a key and a password.
|
||
|
scatter :: ByteString -> ByteString -> Integer
|
||
|
scatter k pw = integerDigest $ sha512 (k <> pw)
|
||
|
|
||
|
-- | Main type of the program.
|
||
|
type Scat a = ReaderT Options IO a
|
||
|
|
||
|
{- | Generates a password, given a input password,
|
||
|
a key (category, website, etc.),
|
||
|
and a password `Schema`.
|
||
|
|
||
|
The parameters are specified as command line arguments.
|
||
|
The password can be read from @stdin@ if not already provided. -}
|
||
|
main :: IO ()
|
||
|
main = getOptions >>= runReaderT scat
|
||
|
|
||
|
-- | Main program.
|
||
|
scat :: Scat ()
|
||
|
scat = do
|
||
|
k <- getKey
|
||
|
s <- getSchema
|
||
|
pw <- getPassword
|
||
|
printVerbose "Generated password:\n"
|
||
|
liftIO $ putStrLn $ evalBuilder s $ scatter k pw
|
||
|
|
||
|
printVerbose :: String -> Scat ()
|
||
|
printVerbose str = do
|
||
|
v <- fmap verbose ask
|
||
|
when v $ liftIO $ do
|
||
|
putStr str
|
||
|
hFlush stdout
|
||
|
|
||
|
-- | Gets the password.
|
||
|
getPassword :: Scat ByteString
|
||
|
getPassword = do
|
||
|
mpw <- fmap password ask
|
||
|
pw <- case mpw of
|
||
|
-- Ask for the password on stdin.
|
||
|
Nothing -> do
|
||
|
c <- fmap confirm ask
|
||
|
if c
|
||
|
then getPassConfirm
|
||
|
else getPass
|
||
|
|
||
|
-- Retrieve the password from the arguments.
|
||
|
Just st -> return $ C.pack st
|
||
|
return $ BS.fromChunks [pw]
|
||
|
where
|
||
|
getPass = askPassword "Password: "
|
||
|
|
||
|
getPassConfirm = do
|
||
|
a <- askPassword "Password: "
|
||
|
b <- askPassword "Confirm: "
|
||
|
if (a == b)
|
||
|
then return a
|
||
|
else do
|
||
|
printVerbose "Passwords do not match, please retry.\n"
|
||
|
getPassConfirm
|
||
|
|
||
|
askPassword :: String -> Scat C.ByteString
|
||
|
askPassword str = do
|
||
|
printVerbose str
|
||
|
old <- liftIO $ hGetEcho stdin
|
||
|
pw <- liftIO $ bracket_
|
||
|
(hSetEcho stdin False)
|
||
|
(hSetEcho stdin old)
|
||
|
C.getLine
|
||
|
printVerbose "\n"
|
||
|
return pw
|
||
|
|
||
|
-- | Gets the key.
|
||
|
getKey :: Scat ByteString
|
||
|
getKey = fmap (LC.pack . key) ask
|
||
|
|
||
|
-- | Gets the schema to generate the new password.
|
||
|
getSchema :: Scat Schema
|
||
|
getSchema = do
|
||
|
name <- fmap schema ask
|
||
|
case name of
|
||
|
-- Safe, the default.
|
||
|
"safe" -> return safe
|
||
|
|
||
|
-- Alphanumeric.
|
||
|
"alpha" -> return alphanumeric
|
||
|
|
||
|
-- PIN.
|
||
|
'p' : 'i' : 'n' : xs | [(n, "")] <- reads xs -> return $ pin n
|
||
|
|
||
|
-- Passphrase using Diceware's list.
|
||
|
"diceware" -> liftIO diceware
|
||
|
|
||
|
-- Passphrase using Pokemons.
|
||
|
"pokemons" -> liftIO pokemons
|
||
|
|
||
|
-- Unkown.
|
||
|
_ -> liftIO $ do
|
||
|
hPutStrLn stderr "Error: Unknown schema"
|
||
|
exitFailure
|