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:
parent
500f917048
commit
d25fac9afe
42
README.md
42
README.md
@ -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
|
||||
|
@ -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.
|
||||
|
14
src/Scat.hs
14
src/Scat.hs
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user