module Text.Password.Strength.Internal.Estimate (
Guesses,
Estimates,
Estimate(..),
estimateAll,
estimate,
) where
import Data.Maybe (fromMaybe)
import Control.Lens ((^.))
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import qualified Data.Text as Text
import Text.Password.Strength.Internal.Config
import Text.Password.Strength.Internal.Date
import Text.Password.Strength.Internal.Keyboard
import Text.Password.Strength.Internal.L33t
import Text.Password.Strength.Internal.Match
import Text.Password.Strength.Internal.Math
import Text.Password.Strength.Internal.Sequence
import Text.Password.Strength.Internal.Token
type Guesses = Map Token Integer
type Estimates = Map Token Estimate
newtype Estimate = Estimate
{ Estimate -> Estimates -> Integer
getEstimate :: Estimates -> Integer }
estimateAll :: Config -> Matches -> Guesses
estimateAll :: Config -> Matches -> Guesses
estimateAll Config
cfg Matches
ms =
(Estimate -> Integer) -> Estimates -> Guesses
forall a b k. (a -> b) -> Map k a -> Map k b
Map.map (Estimate -> Estimates -> Integer
`getEstimate` Estimates
estimates) Estimates
estimates
where
estimate' :: Token -> [Match] -> Maybe (Estimates -> Integer)
estimate' :: Token -> [Match] -> Maybe (Estimates -> Integer)
estimate' Token
_ [] = Maybe (Estimates -> Integer)
forall a. Maybe a
Nothing
estimate' Token
t [Match]
ms' = (Estimates -> Integer) -> Maybe (Estimates -> Integer)
forall a. a -> Maybe a
Just (\Estimates
e -> [Integer] -> Integer
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
minimum ([Integer] -> Integer) -> [Integer] -> Integer
forall a b. (a -> b) -> a -> b
$ (Match -> Integer) -> [Match] -> [Integer]
forall a b. (a -> b) -> [a] -> [b]
map (\Match
m -> Config -> Token -> Match -> Estimates -> Integer
estimate Config
cfg Token
t Match
m Estimates
e) [Match]
ms')
estimates :: Estimates
estimates :: Estimates
estimates =
let get :: Token -> [Match] -> Maybe Estimate
get Token
t [Match]
m = (Estimates -> Integer) -> Estimate
Estimate ((Estimates -> Integer) -> Estimate)
-> Maybe (Estimates -> Integer) -> Maybe Estimate
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Token -> [Match] -> Maybe (Estimates -> Integer)
estimate' Token
t [Match]
m
ins :: Token -> [Match] -> Estimates -> Estimates
ins Token
t [Match]
m Estimates
tbl = Estimates -> (Estimate -> Estimates) -> Maybe Estimate -> Estimates
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Estimates
tbl (\Estimate
e -> Token -> Estimate -> Estimates -> Estimates
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert Token
t Estimate
e Estimates
tbl) (Token -> [Match] -> Maybe Estimate
get Token
t [Match]
m)
in (Token -> [Match] -> Estimates -> Estimates)
-> Estimates -> Matches -> Estimates
forall k a b. (k -> a -> b -> b) -> b -> Map k a -> b
Map.foldrWithKey Token -> [Match] -> Estimates -> Estimates
ins Estimates
forall k a. Map k a
Map.empty Matches
ms
estimate :: Config -> Token -> Match -> Estimates -> Integer
estimate :: Config -> Token -> Match -> Estimates -> Integer
estimate Config
cfg Token
token Match
match Estimates
es =
case Match
match of
DictionaryMatch Rank
n ->
Token -> Integer -> Integer
caps Token
token (Rank -> Integer
forall a. Integral a => a -> Integer
toInteger Rank
n)
ReverseDictionaryMatch Rank
n ->
Token -> Integer -> Integer
caps Token
token (Rank -> Integer
forall a. Integral a => a -> Integer
toInteger Rank
n Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
2)
L33tMatch Rank
n L33t
l ->
let s :: Rank
s = L33t
l L33t -> Getting Rank L33t Rank -> Rank
forall s a. s -> Getting a s a -> a
^. Getting Rank L33t Rank
Lens' L33t Rank
l33tSub
u :: Rank
u = L33t
l L33t -> Getting Rank L33t Rank -> Rank
forall s a. s -> Getting a s a -> a
^. Getting Rank L33t Rank
Lens' L33t Rank
l33tUnsub
in Rank -> Integer
forall a. Integral a => a -> Integer
toInteger Rank
n Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Rank -> Rank -> Integer
variations' Rank
s Rank
u
KeyboardMatch KeyboardPattern
k ->
KeyboardPattern -> Integer
keyboardEstimate KeyboardPattern
k
SequenceMatch Rank
delta ->
let f :: Char -> Bool
f = (Config
cfg Config
-> Getting (Char -> Bool) Config (Char -> Bool) -> Char -> Bool
forall s a. s -> Getting a s a -> a
^. Getting (Char -> Bool) Config (Char -> Bool)
forall c. HasConfig c => Lens' c (Char -> Bool)
obviousSequenceStart)
in (Char -> Bool) -> Text -> Rank -> Integer
estimateSequence Char -> Bool
f (Token
token Token -> Getting Text Token Text -> Text
forall s a. s -> Getting a s a -> a
^. Getting Text Token Text
Lens' Token Text
tokenChars) Rank
delta
DateMatch Date
d ->
Date -> Integer
estimateDate Date
d
RepeatMatch Rank
n Token
t ->
let worstcase :: Integer
worstcase = Rank -> Integer
bruteForce (Rank -> Integer) -> Rank -> Integer
forall a b. (a -> b) -> a -> b
$ Text -> Rank
Text.length (Token
token Token -> Getting Text Token Text -> Text
forall s a. s -> Getting a s a -> a
^. Getting Text Token Text
Lens' Token Text
tokenChars)
guess :: Maybe Integer
guess = (Estimate -> Estimates -> Integer
`getEstimate` Estimates
es) (Estimate -> Integer) -> Maybe Estimate -> Maybe Integer
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Token -> Estimates -> Maybe Estimate
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Token
t Estimates
es
in Integer -> Maybe Integer -> Integer
forall a. a -> Maybe a -> a
fromMaybe Integer
worstcase Maybe Integer
guess Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Rank -> Integer
forall a. Integral a => a -> Integer
toInteger Rank
n