zxcvbn-hs-0.2.0.0: src/Text/Password/Strength/Internal/Math.hs
{-|
Copyright:
This file is part of the package zxcvbn-hs. It is subject to the
license terms in the LICENSE file found in the top-level directory
of this distribution and at:
https://code.devalot.com/sthenauth/zxcvbn-hs
No part of this package, including this file, may be copied,
modified, propagated, or distributed except according to the terms
contained in the LICENSE file.
License: MIT
-}
module Text.Password.Strength.Internal.Math
( variations
, variations'
, bruteForce
, caps
) where
--------------------------------------------------------------------------------
-- Library Imports:
import Control.Lens ((^.))
import Data.Char (isUpper)
import qualified Data.Text as Text
import Numeric.SpecFunctions (choose)
--------------------------------------------------------------------------------
-- Project Imports:
import Text.Password.Strength.Internal.Token
--------------------------------------------------------------------------------
-- | Equation 2, section 4, page 163 (8/18)
--
-- NOTE: The other implementations don't seem to divide the sum by two
-- but the equation in the paper clearly does.
variations :: Int -> Int -> Integer
variations 0 _ = 1
variations _ 0 = 1
variations u l = max 1 (sum (floor . choose (u+l) <$> [1 .. min u l]) `div` 2)
--------------------------------------------------------------------------------
-- | Like equation 2, but modified for l33t and keyboard variations.
--
-- This equation does not appear in the 2016 paper although it is
-- mentioned. Therefore it was adapted from the CoffeeScript and
-- Python implementations.
variations' :: Int -> Int -> Integer
variations' 0 _ = 2
variations' _ 0 = 2
variations' u l = variations u l
--------------------------------------------------------------------------------
-- | Calculate the brute force score for text with the given length.
bruteForce :: Int -> Integer
bruteForce = (10 ^)
--------------------------------------------------------------------------------
-- | Score the use of uppercase letters according to the paper. This
-- is specified in the paragraph preceding equation 2.
caps :: Token -> Integer -> Integer
caps token n =
let text = token ^. tokenChars
upper = Text.length (Text.filter isUpper text)
lower = Text.length text - upper
allLower = lower == Text.length text
allUpper = lower == 0
firstUpper = upper == 1 && Text.all isUpper (Text.take 1 text)
lastUpper = upper == 1 && Text.all isUpper (Text.takeEnd 1 text)
in case () of
() | allLower -> n
| firstUpper -> n * 2
| lastUpper -> n * 2
| allUpper -> n * 2
| otherwise -> n * variations upper lower
--------------------------------------------------------------------------------
-- NOTE: Equation 3 is in Keybaord.hs.