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