1
0
mirror of https://github.com/redelmann/scat synced 2025-01-09 22:24:19 +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` 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
@ -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,
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,
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
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"
@ -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).
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"
@ -63,10 +63,12 @@ Generated password:
{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
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!
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
```
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
Name | Comments | Entropy (in bits)
:------- | :----------------------------------------------- | :----------------
safe | A mix of 18 ascii symbols. (default) | 115
alpha | A mix of 18 alphanumeric symbols. | 104
parano | 78 ascii characters. | 512
pokemons | 4 classic Pokémons, each with a level up to 100. | 55
diceware | 5 words out of the [diceware's list][diceware]. | 65
pinXX | `XX` digits. (useful for PIN codes) | `3.3 * XX`
lockX | Android lockscreen pattern of length `X` | < 19
Name | Comments | Default `n` | Entropy for default `n` (in bits)
:------- | :------------------------------------------------- | :---------: | :--------------------------------
safe | A mix of `n` ascii symbols. (default) | 18 | 115
alpha | A mix of `n` alphanumeric symbols. | 18 | 104
parano | `n` ascii characters. | 78 | 512
pokemons | `n` classic Pokémons, each with a level up to 100. | 4 | 55
diceware | `n` words out of the [diceware's list][diceware]. | 5 | 65
pin | `n` digits. (useful for PIN codes) | 6 | 20
lock | Android lock screen pattern of length `n` | 9 | 17
## 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.
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.
4. Some other improvements you might want!
3. Some other improvements you might want!
### Contributors
Name | Contributions
:--- | :------------
Name | Contributions
:-------------- | :---------------------------
Romain Edelmann | Initial work on the project.
[hackage]: http://hackage.haskell.org/package/scat

View File

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

View File

@ -59,7 +59,8 @@ scat = do
pw <- getPassword
c <- getCode
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.
showGenerated :: String -> Scat ()
@ -161,17 +162,10 @@ getSchema = do
"parano" -> return paranoiac
-- PIN.
'p' : 'i' : 'n' : xs | [(n, "")] <- reads xs -> return $ pin n
-- PIN with default size.
"pin" -> return $ pin 6
"pin" -> return pin
-- Pattern lock
'l' : 'o' : 'c' : 'k' : xs | [(n, "")] <- reads xs -> return $
androidPatternLock n
-- Default size of pattern lock
"lock" -> return $ androidPatternLock 9
"lock" -> return androidPatternLock
-- Passphrase using Diceware's list.
"diceware" -> liftIO diceware

View File

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

View File

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