1
0
mirror of https://github.com/redelmann/scat synced 2025-01-10 06:34:20 +01:00

Added an option to specify the size of the various schemas.

This commit is contained in:
Romain Edelmann 2013-08-15 15:06:14 +02:00
parent 500f917048
commit d25fac9afe
5 changed files with 106 additions and 71 deletions

View File

@ -1,7 +1,7 @@
# scat # scat
`scat` is a *password scatterer*. It allows the generation of unique passwords for each service, `scat` is a *password scatterer*. It allows the generation of unique passwords for each service,
website, email address or account you might have, all from a single password and a secure code you keep. website, email address or account you might have, all from a single password and an optional secure code you keep.
## Motivation ## Motivation
@ -9,7 +9,7 @@ Nowadays, accounts for many services such as Facebook, Twitter, Reddit, Google,
In a perfect world, all those accounts would have different passwords, so that, if someone gets to know, let's say, your Facebook password, In a perfect world, all those accounts would have different passwords, so that, if someone gets to know, let's say, your Facebook password,
they don't gain access to your bank account and your money as well. But, on the other hand, who would like to come up with dozens and dozens of different passwords? Moreover, if you ever lose access you key chain, how can you recover all your passwords? they don't gain access to your bank account and your money as well. But, on the other hand, who would like to come up with dozens and dozens of different passwords? Moreover, if you ever lose access you key chain, how can you recover all your passwords?
`scat` is the solution to this problem. It allows you to safely generate for each website or service you suscribe to a unique password. All you have to do is remember a single, as strong as possible, password and securely keep a long random code. `scat` is the solution to this problem. It allows you to safely generate for each website or service you suscribe to a unique password. All you have to do is remember a single, as strong as possible, password and, optionally, securely keep a long random code.
Given the same information, `scat` will always generate the same password, so if you were to completely lose your entire key chain, Given the same information, `scat` will always generate the same password, so if you were to completely lose your entire key chain,
you can always retrieve all the passwords generated by `scat`. you can always retrieve all the passwords generated by `scat`.
@ -23,7 +23,7 @@ Passwords generated by `scat` are very secure and independant of each others. If
### Example ### Example
To use `scat`, simply call it specifying which key, or service, it must generate a password for. To use `scat`, simply call it specifying which key, or service, it must generate a password for.
Then, simply enter your password (which is, in this example, `pony1234`, and code, `AGDE2-DGXA4-33DLQ-WEDAP-GYPQ9`): Then, simply enter your password (which is, in this example, `pony1234`), and an optional code (`AGDE2-DGXA4-33DLQ-WEDAP-GYPQ9`):
``` ```
> scat -c -S "github" > scat -c -S "github"
@ -52,7 +52,7 @@ The password generated for Facebook is completely different from the one it gene
Imagine now that your are on an other computer, with no access to your key chain, and you would like to login to Facebook (just for 5 minutes). Imagine now that your are on an other computer, with no access to your key chain, and you would like to login to Facebook (just for 5 minutes).
To your great despair, there is no way you can remember your obscure password! To your great despair, there is no way you can remember your obscure password!
However, as you always keep your code with you and `scat` is fully deterministic, you can simply call it once more, to generate once again the exact same password, this time from another computer. However, as `scat` is fully deterministic, you can simply call it once more, to generate once again the exact same password, this time from another computer.
``` ```
> scat -c -S "facebook" > scat -c -S "facebook"
@ -63,10 +63,12 @@ Generated password:
{g6e2hsKjh#Ra*\ks( {g6e2hsKjh#Ra*\ks(
``` ```
> **CAUTION** `scat` is not an excuse to use a weak password such as `pony1234`. Always use a strong password (that you can remember) with `scat`. > **CAUTION** `scat` is not an excuse to use a weak password such as `pony1234`. Try to always use a strong password (that you can remember) with `scat`.
## Generating a suitable code ## Generating a suitable code
Using an extra code is completely optional but adds even more security to the system. If you want to have this extra security, just proceed as follows!
There are many ways to generate and store a suitable code. For instance, you can use a key such as [those provided by yubico][yubico-key], which can [generate and store static keys][yubico-static]. Be sure to use the maximal key size! There are many ways to generate and store a suitable code. For instance, you can use a key such as [those provided by yubico][yubico-key], which can [generate and store static keys][yubico-static]. Be sure to use the maximal key size!
However, if you do not want to invest in a key, there exists secure solutions at your disposition that won't cost you anything. However, if you do not want to invest in a key, there exists secure solutions at your disposition that won't cost you anything.
@ -136,17 +138,19 @@ Generated password:
Snorlax 5, Weedle 35, Raichu 27, Alakazam 99 Snorlax 5, Weedle 35, Raichu 27, Alakazam 99
``` ```
The schema can also have their size specified using the `-n` option. Complete informations on the available schemas is available below.
### Summary of available schemas ### Summary of available schemas
Name | Comments | Entropy (in bits) Name | Comments | Default `n` | Entropy for default `n` (in bits)
:------- | :----------------------------------------------- | :---------------- :------- | :------------------------------------------------- | :---------: | :--------------------------------
safe | A mix of 18 ascii symbols. (default) | 115 safe | A mix of `n` ascii symbols. (default) | 18 | 115
alpha | A mix of 18 alphanumeric symbols. | 104 alpha | A mix of `n` alphanumeric symbols. | 18 | 104
parano | 78 ascii characters. | 512 parano | `n` ascii characters. | 78 | 512
pokemons | 4 classic Pokémons, each with a level up to 100. | 55 pokemons | `n` classic Pokémons, each with a level up to 100. | 4 | 55
diceware | 5 words out of the [diceware's list][diceware]. | 65 diceware | `n` words out of the [diceware's list][diceware]. | 5 | 65
pinXX | `XX` digits. (useful for PIN codes) | `3.3 * XX` pin | `n` digits. (useful for PIN codes) | 6 | 20
lockX | Android lockscreen pattern of length `X` | < 19 lock | Android lock screen pattern of length `n` | 9 | 17
## How it works ## How it works
@ -174,16 +178,14 @@ If you lack ideas but would like to participate anyway, you can also find here a
1. Create other schemas. 1. Create other schemas.
2. Let the user specify a size for some schemas. (By specify another command line option). 2. Compatible port in mainstream programming languages.
3. Compatible port in mainstream programming languages. 3. Some other improvements you might want!
4. Some other improvements you might want!
### Contributors ### Contributors
Name | Contributions Name | Contributions
:--- | :------------ :-------------- | :---------------------------
Romain Edelmann | Initial work on the project. Romain Edelmann | Initial work on the project.
[hackage]: http://hackage.haskell.org/package/scat [hackage]: http://hackage.haskell.org/package/scat

View File

@ -10,7 +10,7 @@ name: scat
-- PVP summary: +-+------- breaking API changes -- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions -- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change -- | | | +--- code changes with no API change
version: 1.0.1.0 version: 1.0.2.0
-- A short (one-line) description of the package. -- A short (one-line) description of the package.
synopsis: Generates unique passwords for various websites from a single password. synopsis: Generates unique passwords for various websites from a single password.

View File

@ -59,7 +59,8 @@ scat = do
pw <- getPassword pw <- getPassword
c <- getCode c <- getCode
printVerbose "Generated password:\n" printVerbose "Generated password:\n"
showGenerated $ evalBuilder s $ scatter k pw c ms <- fmap size ask
showGenerated $ evalBuilder (getBuilder s ms) $ scatter k pw c
-- | Prints out the generated password. -- | Prints out the generated password.
showGenerated :: String -> Scat () showGenerated :: String -> Scat ()
@ -161,17 +162,10 @@ getSchema = do
"parano" -> return paranoiac "parano" -> return paranoiac
-- PIN. -- PIN.
'p' : 'i' : 'n' : xs | [(n, "")] <- reads xs -> return $ pin n "pin" -> return pin
-- PIN with default size.
"pin" -> return $ pin 6
-- Pattern lock -- Pattern lock
'l' : 'o' : 'c' : 'k' : xs | [(n, "")] <- reads xs -> return $ "lock" -> return androidPatternLock
androidPatternLock n
-- Default size of pattern lock
"lock" -> return $ androidPatternLock 9
-- Passphrase using Diceware's list. -- Passphrase using Diceware's list.
"diceware" -> liftIO diceware "diceware" -> liftIO diceware

View File

@ -11,6 +11,7 @@ module Scat.Options
, useCode , useCode
, code , code
, schema , schema
, size
, verbose , verbose
, confirm , confirm
, ansi , ansi
@ -34,6 +35,8 @@ data Options = Options
-- ^ Extra code. -- ^ Extra code.
, schema :: String , schema :: String
-- ^ Name of the schema to use. -- ^ Name of the schema to use.
, size :: Maybe Int
-- ^ Size parameter of the schema.
, verbose :: Bool , verbose :: Bool
-- ^ Verbosity. If false, do not print anything but the generated password. -- ^ Verbosity. If false, do not print anything but the generated password.
, confirm :: Bool , confirm :: Bool
@ -81,6 +84,11 @@ options = Options
<> metavar "SCHEMA" <> metavar "SCHEMA"
<> value "safe" <> value "safe"
<> showDefault) <> showDefault)
<*> optional (option
(short 'n'
<> long "size"
<> help "Size parameter"
<> metavar "SIZE"))
<*> flag True False <*> flag True False
(long "silent" (long "silent"
<> help "Do not print anything but the generated password") <> help "Do not print anything but the generated password")

View File

@ -7,22 +7,31 @@ module Scat.Schemas
-- * Type -- * Type
Schema Schema
-- * Passwords -- ** Constructors
, withDefaultSize
, ignoreSize
-- ** Destructor
, getBuilder
-- * Built-in schemas
-- ** Passwords
, safe , safe
, alphanumeric , alphanumeric
, paranoiac , paranoiac
-- * PIN -- ** PIN
, pin , pin
-- * Pass phrases -- ** Pass phrases
, pokemons , pokemons
, diceware , diceware
-- * Pattern lock -- ** Pattern lock
, androidPatternLock , androidPatternLock
) where ) where
import Data.Ratio ((%))
import Data.List (intercalate, (\\)) import Data.List (intercalate, (\\))
import Data.Vector (Vector) import Data.Vector (Vector)
import qualified Data.Vector as V import qualified Data.Vector as V
@ -35,39 +44,60 @@ import Scat.Builder
import Paths_scat import Paths_scat
-- | Password builder. -- | Password builder.
type Schema = Builder String data Schema = Schema
{ defaultSize :: Int
, builder :: Int -> Builder String }
-- | Paranoiac mode, entropy of 512 bits. -- | Returns a `Builder` given an optional size.
getBuilder :: Schema -> Maybe Int -> Builder String
getBuilder schema Nothing = builder schema $ defaultSize schema
getBuilder schema (Just s) = builder schema s
-- | Specifies the Schema will not be sensible to any size parameter.
ignoreSize :: Builder String -> Schema
ignoreSize = Schema undefined . const
-- | Specifies the Schema accepts a size parameter.
withDefaultSize :: Int -> (Int -> Builder String) -> Schema
withDefaultSize = Schema
-- | Paranoiac mode, entropy of 512 bits with the default size of 78.
paranoiac :: Schema paranoiac :: Schema
paranoiac = replicateM 78 ascii paranoiac = withDefaultSize 78 $ \ s -> replicateM s ascii
{- | Generates a password of length 18, {- | Generates a password,
containing upper case letters, containing upper case letters,
lower case letters, lower case letters,
digits and symbols. digits and symbols.
Entropy of about 115 bits. -} Entropy of about 115 bits for length 18. -}
safe :: Schema safe :: Schema
safe = do safe = withDefaultSize 18 $ \ s -> do
nUpper <- inRange (2, 5) let number = max s 4
nDigit <- inRange (2, 5) lBound = max 1 $ floor $ number % 8
nSpecial <- inRange (2, 5) uBound = ceiling $ number % 4
let nLower = 18 - nUpper - nSpecial - nDigit nUpper <- inRange (lBound, uBound)
nDigit <- inRange (lBound, uBound)
nSpecial <- inRange (lBound, uBound)
let nLower = number - nUpper - nSpecial - nDigit
uppers <- replicateM nUpper upper uppers <- replicateM nUpper upper
digits <- replicateM nDigit digit digits <- replicateM nDigit digit
specials <- replicateM nSpecial special specials <- replicateM nSpecial special
lowers <- replicateM nLower lower lowers <- replicateM nLower lower
shuffle (uppers <> digits <> specials <> lowers) shuffle (uppers <> digits <> specials <> lowers)
{- | Generates a password of length 18, {- | Generates a password,
containing upper case letters, containing upper case letters,
lower case letters and lower case letters and
digits, but no symbols. digits, but no symbols.
Entropy of about 104.2 bits. -} Entropy of about 104.2 bits for length 18. -}
alphanumeric :: Schema alphanumeric :: Schema
alphanumeric = do alphanumeric = withDefaultSize 18 $ \ s -> do
nUpper <- inRange (2, 5) let number = max s 4
nDigit <- inRange (2, 5) lBound = max 1 $ floor $ number % 8
let nLower = 18 - nUpper - nDigit uBound = ceiling $ number % 4
nUpper <- inRange (lBound, uBound)
nDigit <- inRange (lBound, uBound)
let nLower = number - nUpper - nDigit
uppers <- replicateM nUpper upper uppers <- replicateM nUpper upper
digits <- replicateM nDigit digit digits <- replicateM nDigit digit
lowers <- replicateM nLower lower lowers <- replicateM nLower lower
@ -75,14 +105,13 @@ alphanumeric = do
{- | Generates a PIN number, of length `n`. {- | Generates a PIN number, of length `n`.
Entropy of about @3.32 * n@ bits. -} Entropy of about @3.32 * n@ bits. -}
pin :: Int -> Schema pin :: Schema
pin n = replicateM n digit pin = withDefaultSize 6 $ \ s -> replicateM s digit
-- | Generates an Android lock pattern.
-- | Generates an Android lock pattern, of specified length. androidPatternLock :: Schema
androidPatternLock :: Int -> Schema androidPatternLock = withDefaultSize 9 $ \ s -> do
androidPatternLock number = do xs <- loop (min s (height * width)) []
xs <- loop (min number (height * width)) []
return $ intercalate " - " $ map showPosition xs return $ intercalate " - " $ map showPosition xs
where where
-- Gets `n` points. -- Gets `n` points.
@ -133,25 +162,27 @@ androidPatternLock number = do
vstep = vdiff `div` steps vstep = vdiff `div` steps
hstep = hdiff `div` steps hstep = hdiff `div` steps
{- | Generates a password with 4 of the original Pokemons and their level. {- | Generates a password with `s` of the original Pokemons and their level.
Entropy of about 55.5 bits. -} Entropy of about 55.5 bits for 4 pokemons. -}
pokemons :: IO Schema pokemons :: IO Schema
pokemons = fromFile "pokemons.txt" $ \ vect -> do pokemons = fromFile "pokemons.txt" $ \ vect ->
ps <- replicateM 4 $ oneOfV vect withDefaultSize 4 $ \ s -> do
ls <- replicateM 4 $ inRange (1, 100 :: Int) ps <- replicateM s $ oneOfV vect
ls <- replicateM s $ inRange (1, 100 :: Int)
let ss = zipWith (\ p l -> p ++ " " ++ show l) ps ls let ss = zipWith (\ p l -> p ++ " " ++ show l) ps ls
return $ intercalate ", " ss return $ intercalate ", " ss
{- | Generates a password with 5 words {- | Generates a password with `s` words
from the Diceware list. from the Diceware list.
Entropy of about 64.6 bits. -} Entropy of about 64.6 bits for 5 words. -}
diceware :: IO Schema diceware :: IO Schema
diceware = fromFile "diceware.txt" $ \ vect -> do diceware = fromFile "diceware.txt" $ \ vect ->
ws <- replicateM 5 $ oneOfV vect withDefaultSize 5 $ \ s -> do
ws <- replicateM s $ oneOfV vect
return $ unwords ws return $ unwords ws
-- | Feeds all lines of a file to a builder. -- | Feeds all lines of a file to a function and gets the result.
fromFile :: FilePath -> (Vector String -> Builder a) -> IO (Builder a) fromFile :: FilePath -> (Vector String -> a) -> IO a
fromFile fp bs = do fromFile fp bs = do
fp' <- getDataFileName fp fp' <- getDataFileName fp
withFile fp' ReadMode $ \ h -> do withFile fp' ReadMode $ \ h -> do