-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Hashing and checking of passwords -- -- A library providing functionality for working with plain-text and -- hashed passwords with different types of algorithms. -- --

API

-- -- Every supported hashing algorithm has its own module (e.g. -- Data.Password.Bcrypt) which exports its own -- hashPassword and checkPassword functions, as well as -- all the types and functions in this module. If you are not sure about -- the specifics of an algorithm you want to use, you can rest assured -- that by using the hashPassword function of the respective -- algorithm you are not making any big mistakes, security-wise. -- -- Of course, if you know what you're doing and you want more -- fine-grained control over the hashing function, you can adjust it -- using the hashPasswordWithParams function of the respective -- algorithm. -- --

Algorithms

-- -- Generally, the most "secure" algorithm is believed to be -- Argon2, then scrypt, then bcrypt, and -- lastly PBKDF2. bcrypt and PBKDF2 are the -- most established algorithms, so they have been tried and tested, -- though they both lack a memory cost, and therefore have a greater -- vulnerability to specialized hardware attacks. -- -- When choosing an algorithm, and you have no idea which to pick, just -- go for bcrypt if your password does not need the highest -- security possible. It's still a fine way for hashing passwords, and -- the cost is easily adjustable if needed. If your needs do require -- stronger protection, you should find someone who can advise you on -- this topic. (And if you're already knowledgeable enough, you know what -- to do) @package password @version 3.0.2.1 -- |

bcrypt

-- -- The bcrypt algorithm is a popular way of hashing passwords. -- It is based on the Blowfish cipher and fairly straightfoward in its -- usage. It has a cost parameter that, when increased, slows down the -- hashing speed. -- -- It is a straightforward and easy way to get decent protection on -- passwords, it has also been around long enough to be battle-tested and -- generally considered to provide a good amount of security. -- --

Other algorithms

-- -- bcrypt, together with PBKDF2, are only -- computationally intensive. And to protect from specialized hardware, -- new algorithms have been developed that are also resource intensive, -- like Scrypt and Argon2. Not having -- high resource demands, means an attacker with specialized software -- could take less time to brute-force a password, though with the -- default cost (10) and a decently long password, the amount of time to -- brute-force would still be significant. -- -- This the algorithm to use if you're not sure about your needs, but -- just want a decent, proven way to encrypt your passwords. module Data.Password.Bcrypt -- | Phantom type for bcrypt data Bcrypt -- | A plain-text password. -- -- This represents a plain-text password that has NOT been hashed. -- -- You should be careful with Password. Make sure not to write it -- to logs or store it in a database. -- -- You can construct a Password by using the mkPassword -- function or as literal strings together with the -- OverloadedStrings pragma (or manually, by using -- fromString on a String). Alternatively, you could also -- use some of the instances in the password-instances library. data Password -- | Construct a Password mkPassword :: Text -> Password -- | Hash the Password using the bcrypt hash algorithm. -- -- N.B.: bcrypt has a limit of 72 bytes as input, so -- anything longer than that will be cut off at the 72 byte point and -- thus any password that is 72 bytes or longer will match as long as the -- first 72 bytes are the same. -- --
--   >>> hashPassword $ mkPassword "foobar"
--   PasswordHash {unPasswordHash = "$2b$10$..."}
--   
hashPassword :: MonadIO m => Password -> m (PasswordHash Bcrypt) -- | A hashed password. -- -- This represents a password that has been put through a hashing -- function. The hashed password can be stored in a database. newtype PasswordHash a PasswordHash :: Text -> PasswordHash a [unPasswordHash] :: PasswordHash a -> Text -- | Check a Password against a PasswordHash Bcrypt. -- -- Returns PasswordCheckSuccess on success. -- --
--   >>> let pass = mkPassword "foobar"
--   
--   >>> passHash <- hashPassword pass
--   
--   >>> checkPassword pass passHash
--   PasswordCheckSuccess
--   
-- -- Returns PasswordCheckFail if an incorrect Password or -- PasswordHash Bcrypt is used. -- --
--   >>> let badpass = mkPassword "incorrect-password"
--   
--   >>> checkPassword badpass passHash
--   PasswordCheckFail
--   
-- -- This should always fail if an incorrect password is given. -- --
--   \(Blind badpass) -> let correctPasswordHash = hashPasswordWithSalt 8 salt "foobar" in checkPassword badpass correctPasswordHash == PasswordCheckFail
--   
checkPassword :: Password -> PasswordHash Bcrypt -> PasswordCheck -- | The result of checking a password against a hashed version. This is -- returned by the checkPassword functions. data PasswordCheck -- | The password check was successful. The plain-text password matches the -- hashed password. PasswordCheckSuccess :: PasswordCheck -- | The password check failed. The plain-text password does not match the -- hashed password. PasswordCheckFail :: PasswordCheck -- | Hash a password using the bcrypt algorithm with the given cost. -- -- The higher the cost, the longer hashPassword and -- checkPassword will take to run, thus increasing the security, -- but taking longer and taking up more resources. The optimal cost for -- generic user logins would be one that would take between 0.05 - 0.5 -- seconds to check on the machine that will run it. -- -- N.B.: It is advised to use hashPassword if you're unsure -- about the implications that changing the cost brings with it. hashPasswordWithParams :: MonadIO m => Int -> Password -> m (PasswordHash Bcrypt) -- | Extracts the cost parameter as an Int from a -- PasswordHash Bcrypt -- --
--   >>> let pass = mkPassword "foobar"
--   
--   >>> passHash <- hashPassword pass
--   
--   >>> extractParams passHash == Just 10
--   True
--   
extractParams :: PasswordHash Bcrypt -> Maybe Int -- | Hash a password with the given cost and also with the given -- Salt instead of generating a random salt. Using -- hashPasswordWithSalt is strongly disadvised, and -- hashPasswordWithParams should be used instead. Never use a -- static salt in production applications! -- -- N.B.: The salt HAS to be 16 bytes or this function will throw -- an error! -- --
--   >>> let salt = Salt "abcdefghijklmnop"
--   
--   >>> hashPasswordWithSalt 10 salt (mkPassword "foobar")
--   PasswordHash {unPasswordHash = "$2b$10$WUHhXETkX0fnYkrqZU3ta.N8Utt4U77kW4RVbchzgvBvBBEEdCD/u"}
--   
-- -- (Note that we use an explicit Salt in the example above. This -- is so that the example is reproducible, but in general you should use -- hashPassword. hashPassword (and -- hashPasswordWithParams) generates a new Salt everytime -- it is called.) hashPasswordWithSalt :: Int -> Salt Bcrypt -> Password -> PasswordHash Bcrypt -- | Generate a random 16-byte bcrypt salt newSalt :: MonadIO m => m (Salt Bcrypt) -- | A salt used by a hashing algorithm. newtype Salt a Salt :: ByteString -> Salt a [getSalt] :: Salt a -> ByteString -- | This is an unsafe function that shows a password in plain-text. -- --
--   >>> unsafeShowPassword ("foobar" :: Password)
--   "foobar"
--   
-- -- You should generally not use this function in production -- settings, as you don't want to accidentally print a password anywhere, -- like logs, network responses, database entries, etc. -- -- This will mostly be used by other libraries to handle the actual -- password internally, though it is conceivable that, even in a -- production setting, a password might have to be handled in an unsafe -- manner at some point. unsafeShowPassword :: Password -> Text -- |

Argon2

-- -- Argon2 is probably the newest password algorithm out there. -- Argon2 was selected as the winner of the Password Hashing Competition -- in July 2015. -- -- It has three variants, namely Argon2d, Argon2i and -- Argon2id. These protect against GPU cracking attacks, -- side-channel attacks, and both, respectively. -- -- All three modes allow specification by three parameters that control: -- -- -- --

Other algorithms

-- -- In comparison to other algorithms, Argon2 is the least -- "battle-tested", being the newest algorithm out there. -- -- It is, however, recommended over Scrypt most of the -- time, and it also seems like it might become the go-to password -- algorithm if no vulnarabilities are discovered within the next couple -- of years. module Data.Password.Argon2 -- | Phantom type for Argon2 data Argon2 -- | A plain-text password. -- -- This represents a plain-text password that has NOT been hashed. -- -- You should be careful with Password. Make sure not to write it -- to logs or store it in a database. -- -- You can construct a Password by using the mkPassword -- function or as literal strings together with the -- OverloadedStrings pragma (or manually, by using -- fromString on a String). Alternatively, you could also -- use some of the instances in the password-instances library. data Password -- | Construct a Password mkPassword :: Text -> Password -- | Hash the Password using the Argon2 hash algorithm -- --
--   >>> hashPassword $ mkPassword "foobar"
--   PasswordHash {unPasswordHash = "$argon2id$v=19$m=65536,t=2,p=1$...$..."}
--   
hashPassword :: MonadIO m => Password -> m (PasswordHash Argon2) -- | A hashed password. -- -- This represents a password that has been put through a hashing -- function. The hashed password can be stored in a database. newtype PasswordHash a PasswordHash :: Text -> PasswordHash a [unPasswordHash] :: PasswordHash a -> Text -- | Check a Password against a PasswordHash Argon2. -- -- Returns PasswordCheckSuccess on success. -- --
--   >>> let pass = mkPassword "foobar"
--   
--   >>> passHash <- hashPassword pass
--   
--   >>> checkPassword pass passHash
--   PasswordCheckSuccess
--   
-- -- Returns PasswordCheckFail if an incorrect Password or -- PasswordHash Argon2 is used. -- --
--   >>> let badpass = mkPassword "incorrect-password"
--   
--   >>> checkPassword badpass passHash
--   PasswordCheckFail
--   
-- -- This should always fail if an incorrect password is given. -- --
--   \(Blind badpass) -> let correctPasswordHash = hashPasswordWithSalt testParams salt "foobar" in checkPassword badpass correctPasswordHash == PasswordCheckFail
--   
checkPassword :: Password -> PasswordHash Argon2 -> PasswordCheck -- | The result of checking a password against a hashed version. This is -- returned by the checkPassword functions. data PasswordCheck -- | The password check was successful. The plain-text password matches the -- hashed password. PasswordCheckSuccess :: PasswordCheck -- | The password check failed. The plain-text password does not match the -- hashed password. PasswordCheckFail :: PasswordCheck -- | Hash a password using the Argon2 algorithm with the given -- Argon2Params. -- -- N.B.: If you have any doubt in your knowledge of cryptography -- and/or the Argon2 algorithm, please just use -- hashPassword. -- -- Advice to set the parameters: -- -- hashPasswordWithParams :: MonadIO m => Argon2Params -> Password -> m (PasswordHash Argon2) -- | Default parameters for the Argon2 algorithm. -- --
--   >>> defaultParams
--   Argon2Params {argon2Salt = 16, argon2Variant = Argon2id, argon2Version = Version13, argon2MemoryCost = 65536, argon2TimeCost = 2, argon2Parallelism = 1, argon2OutputLength = 32}
--   
defaultParams :: Argon2Params -- | Extracts Argon2Params from a PasswordHash Argon2. -- -- Returns 'Just Argon2Params' on success. -- --
--   >>> let pass = mkPassword "foobar"
--   
--   >>> passHash <- hashPassword pass
--   
--   >>> extractParams passHash == Just defaultParams
--   True
--   
extractParams :: PasswordHash Argon2 -> Maybe Argon2Params -- | Parameters used in the Argon2 hashing algorithm. data Argon2Params Argon2Params :: Word32 -> Variant -> Version -> Word32 -> Word32 -> Word32 -> Word32 -> Argon2Params -- | Bytes to randomly generate as a unique salt, default is 16 -- -- Limits are min: 8, and max: (2 ^ 32) - 1 [argon2Salt] :: Argon2Params -> Word32 -- | Which variant of Argon2 to use, default is Argon2id [argon2Variant] :: Argon2Params -> Variant -- | Which version of Argon2 to use, default is Version13 [argon2Version] :: Argon2Params -> Version -- | Memory cost, given in kibibytes, default is 65536 (i.e. -- 64MB) -- -- Limits are min: 8 * argon2Parallelism, and max is -- addressing space / 2, or (2 ^ 32) - 1, whichever is lower. [argon2MemoryCost] :: Argon2Params -> Word32 -- | Amount of computation realized, default is 2 -- -- Limits are min: 1, and max: (2 ^ 32) - 1 [argon2TimeCost] :: Argon2Params -> Word32 -- | Parallelism factor, default is 1 -- -- Limits are min: 1, and max: (2 ^ 24) - 1 [argon2Parallelism] :: Argon2Params -> Word32 -- | Output key length in bytes, default is 32 -- -- Limits are min: 4, and max: (2 ^ 32) - 1 [argon2OutputLength] :: Argon2Params -> Word32 -- | Which variant of Argon2 to use. You should choose the variant that is -- most applicable to your intention to hash inputs. data Variant -- | Argon2d is faster than Argon2i and uses data-depending memory access, -- which makes it suitable for cryptocurrencies and applications with no -- threats from side-channel timing attacks. Argon2d :: Variant -- | Argon2i uses data-independent memory access, which is preferred for -- password hashing and password-based key derivation. Argon2i is slower -- as it makes more passes over the memory to protect from tradeoff -- attacks. Argon2i :: Variant -- | Argon2id is a hybrid of Argon2i and Argon2d, using a combination of -- data-depending and data-independent memory accesses, which gives some -- of Argon2i's resistance to side-channel cache timing attacks and much -- of Argon2d's resistance to GPU cracking attacks Argon2id :: Variant -- | Which version of Argon2 to use data Version Version10 :: Version Version13 :: Version -- | Hash a password with the given Argon2Params and also with the -- given Salt instead of a random generated salt using -- argon2Salt from Argon2Params. (cf. -- hashPasswordWithParams) Using hashPasswordWithSalt is -- strongly disadvised and hashPasswordWithParams should be -- used instead. Never use a static salt in production -- applications! -- -- N.B.: The salt HAS to be 8 bytes or more, or this function will -- throw an error! -- --
--   >>> let salt = Salt "abcdefghijklmnop"
--   
--   >>> hashPasswordWithSalt defaultParams salt (mkPassword "foobar")
--   PasswordHash {unPasswordHash = "$argon2id$v=19$m=65536,t=2,p=1$YWJjZGVmZ2hpamtsbW5vcA$BztdyfEefG5V18ZNlztPrfZaU5duVFKZiI6dJeWht0o"}
--   
-- -- (Note that we use an explicit Salt in the example above. This -- is so that the example is reproducible, but in general you should use -- hashPassword. hashPassword generates a new Salt -- everytime it is called.) hashPasswordWithSalt :: Argon2Params -> Salt Argon2 -> Password -> PasswordHash Argon2 -- | Generate a random 16-byte Argon2 salt newSalt :: MonadIO m => m (Salt Argon2) -- | A salt used by a hashing algorithm. newtype Salt a Salt :: ByteString -> Salt a [getSalt] :: Salt a -> ByteString -- | This is an unsafe function that shows a password in plain-text. -- --
--   >>> unsafeShowPassword ("foobar" :: Password)
--   "foobar"
--   
-- -- You should generally not use this function in production -- settings, as you don't want to accidentally print a password anywhere, -- like logs, network responses, database entries, etc. -- -- This will mostly be used by other libraries to handle the actual -- password internally, though it is conceivable that, even in a -- production setting, a password might have to be handled in an unsafe -- manner at some point. unsafeShowPassword :: Password -> Text instance GHC.Show.Show Data.Password.Argon2.Argon2Params instance GHC.Classes.Eq Data.Password.Argon2.Argon2Params -- |

PBKDF2

-- -- The PBKDF2 algorithm is one of the oldest and most solid password -- algorithms out there. It has also, however, been shown to be the least -- secure out of all major password algorithms. The main reason for this -- is that it doesn't make use of any memory cost or other method of -- making it difficult for specialized hardware attacks, like GPU -- cracking attacks. -- -- It is still, however, used all over the world, since it has been shown -- to be a very reliable way to encrypt passwords. And it is most -- definitely better than trying to develop a password algorithm on your -- own, or god-forbid, not using any encryption on your stored -- passwords. -- --

Other algorithms

-- -- Seeing as PBKDF2 is shown to be very weak in terms of protection -- against GPU cracking attacks, it is generally advised to go with -- Bcrypt, if not Scrypt or -- Argon2. When unsure, Bcrypt would -- probably be the safest option, as it has no memory cost which could -- become a problem if not properly calibrated to the machine doing the -- password verifications. module Data.Password.PBKDF2 -- | Phantom type for PBKDF2 data PBKDF2 -- | A plain-text password. -- -- This represents a plain-text password that has NOT been hashed. -- -- You should be careful with Password. Make sure not to write it -- to logs or store it in a database. -- -- You can construct a Password by using the mkPassword -- function or as literal strings together with the -- OverloadedStrings pragma (or manually, by using -- fromString on a String). Alternatively, you could also -- use some of the instances in the password-instances library. data Password -- | Construct a Password mkPassword :: Text -> Password -- | Hash the Password using the PBKDF2 hash algorithm -- --
--   >>> hashPassword $ mkPassword "foobar"
--   PasswordHash {unPasswordHash = "sha512:25000:...:..."}
--   
hashPassword :: MonadIO m => Password -> m (PasswordHash PBKDF2) -- | A hashed password. -- -- This represents a password that has been put through a hashing -- function. The hashed password can be stored in a database. newtype PasswordHash a PasswordHash :: Text -> PasswordHash a [unPasswordHash] :: PasswordHash a -> Text -- | Check a Password against a PasswordHash PBKDF2. -- -- Returns PasswordCheckSuccess on success. -- --
--   >>> let pass = mkPassword "foobar"
--   
--   >>> passHash <- hashPassword pass
--   
--   >>> checkPassword pass passHash
--   PasswordCheckSuccess
--   
-- -- Returns PasswordCheckFail if an incorrect Password or -- PasswordHash PBKDF2 is used. -- --
--   >>> let badpass = mkPassword "incorrect-password"
--   
--   >>> checkPassword badpass passHash
--   PasswordCheckFail
--   
-- -- This should always fail if an incorrect password is given. -- --
--   \(Blind badpass) -> let correctPasswordHash = hashPasswordWithSalt testParams salt "foobar" in checkPassword badpass correctPasswordHash == PasswordCheckFail
--   
checkPassword :: Password -> PasswordHash PBKDF2 -> PasswordCheck -- | The result of checking a password against a hashed version. This is -- returned by the checkPassword functions. data PasswordCheck -- | The password check was successful. The plain-text password matches the -- hashed password. PasswordCheckSuccess :: PasswordCheck -- | The password check failed. The plain-text password does not match the -- hashed password. PasswordCheckFail :: PasswordCheck -- | Hash a password using the PBKDF2 algorithm with the given -- PBKDF2Params. -- -- N.B.: If you have any doubt in your knowledge of cryptography -- and/or the PBKDF2 algorithm, please just use -- hashPassword. hashPasswordWithParams :: MonadIO m => PBKDF2Params -> Password -> m (PasswordHash PBKDF2) -- | Default parameters for the PBKDF2 algorithm. -- --
--   >>> defaultParams
--   PBKDF2Params {pbkdf2Salt = 16, pbkdf2Algorithm = PBKDF2_SHA512, pbkdf2Iterations = 25000, pbkdf2OutputLength = 64}
--   
defaultParams :: PBKDF2Params -- | Extracts PBKDF2Params from a PasswordHash PBKDF2. -- -- Returns 'Just PBKDF2Params' on success. -- --
--   >>> let pass = mkPassword "foobar"
--   
--   >>> passHash <- hashPassword pass
--   
--   >>> extractParams passHash == Just defaultParams
--   True
--   
extractParams :: PasswordHash PBKDF2 -> Maybe PBKDF2Params -- | Parameters used in the PBKDF2 hashing algorithm. data PBKDF2Params PBKDF2Params :: Word32 -> PBKDF2Algorithm -> Word32 -> Word32 -> PBKDF2Params -- | Bytes to randomly generate as a unique salt, default is 16 [pbkdf2Salt] :: PBKDF2Params -> Word32 -- | Which algorithm to use for hashing, default is -- PBKDF2_SHA512 [pbkdf2Algorithm] :: PBKDF2Params -> PBKDF2Algorithm -- | Rounds to hash, default is 25,000 [pbkdf2Iterations] :: PBKDF2Params -> Word32 -- | Output key length in bytes, default is 64 -- -- Limits are min: 1, max: the amount of entropy of the hashing -- algorithm. This is limited automatically to 16, 20, 32, 64 -- for MD5, SHA1, SHA256, SHA512, respectively. [pbkdf2OutputLength] :: PBKDF2Params -> Word32 -- | Type of algorithm to use for hashing PBKDF2 passwords. -- -- N.B.: PBKDF2_MD5 and PBKDF2_SHA1 are not considered very -- secure. data PBKDF2Algorithm PBKDF2_MD5 :: PBKDF2Algorithm PBKDF2_SHA1 :: PBKDF2Algorithm PBKDF2_SHA256 :: PBKDF2Algorithm PBKDF2_SHA512 :: PBKDF2Algorithm -- | Hash a password with the given PBKDF2Params and also with the -- given Salt instead of a randomly generated salt using -- pbkdf2Salt from PBKDF2Params. (cf. -- hashPasswordWithParams) Using hashPasswordWithSalt is -- strongly disadvised and hashPasswordWithParams should be -- used instead. Never use a static salt in production -- applications! -- --
--   >>> let salt = Salt "abcdefghijklmnop"
--   
--   >>> hashPasswordWithSalt defaultParams salt (mkPassword "foobar")
--   PasswordHash {unPasswordHash = "sha512:25000:YWJjZGVmZ2hpamtsbW5vcA==:JRElYYrOMe9OIV4LDxaLTgO9ho8fFBVofXoQcdngi7AcuH6Amvmlj2B0y6y1UtQciXXBepSCS+rpy8/vDDQvoA=="}
--   
-- -- (Note that we use an explicit Salt in the example above. This -- is so that the example is reproducible, but in general you should use -- hashPassword. hashPassword (and -- hashPasswordWithParams) generates a new Salt everytime -- it is called.) hashPasswordWithSalt :: PBKDF2Params -> Salt PBKDF2 -> Password -> PasswordHash PBKDF2 -- | Generate a random 16-byte PBKDF2 salt newSalt :: MonadIO m => m (Salt PBKDF2) -- | A salt used by a hashing algorithm. newtype Salt a Salt :: ByteString -> Salt a [getSalt] :: Salt a -> ByteString -- | This is an unsafe function that shows a password in plain-text. -- --
--   >>> unsafeShowPassword ("foobar" :: Password)
--   "foobar"
--   
-- -- You should generally not use this function in production -- settings, as you don't want to accidentally print a password anywhere, -- like logs, network responses, database entries, etc. -- -- This will mostly be used by other libraries to handle the actual -- password internally, though it is conceivable that, even in a -- production setting, a password might have to be handled in an unsafe -- manner at some point. unsafeShowPassword :: Password -> Text instance GHC.Show.Show Data.Password.PBKDF2.PBKDF2Algorithm instance GHC.Classes.Eq Data.Password.PBKDF2.PBKDF2Algorithm instance GHC.Show.Show Data.Password.PBKDF2.PBKDF2Params instance GHC.Classes.Eq Data.Password.PBKDF2.PBKDF2Params -- |

scrypt

-- -- The scrypt algorithm is a fairly new one. First published in -- 2009, but published by the IETF in 2016 as RFC 7914. Originally -- used for the Tarsnap backup service, it is designed to be costly by -- requiring large amounts of memory. -- --

Other algorithms

-- -- scrypt does increase the memory requirement in contrast to -- Bcrypt and PBKDF2, but it turns out it -- is not as optimal as it could be, and thus others have set out to -- search for other algorithms that do fulfill on their promises. -- Argon2 seems to be the winner in that search. -- -- That is not to say using scrypt somehow means your passwords -- won't be properly protected. The cryptography is sound and thus is -- fine for protection against brute-force attacks. Because of the memory -- cost, it is generally advised to use Bcrypt if you're -- not sure this might be a problem on your system. module Data.Password.Scrypt -- | Phantom type for scrypt data Scrypt -- | A plain-text password. -- -- This represents a plain-text password that has NOT been hashed. -- -- You should be careful with Password. Make sure not to write it -- to logs or store it in a database. -- -- You can construct a Password by using the mkPassword -- function or as literal strings together with the -- OverloadedStrings pragma (or manually, by using -- fromString on a String). Alternatively, you could also -- use some of the instances in the password-instances library. data Password -- | Construct a Password mkPassword :: Text -> Password -- | Hash the Password using the Scrypt hash algorithm -- --
--   >>> hashPassword $ mkPassword "foobar"
--   PasswordHash {unPasswordHash = "14|8|1|...|..."}
--   
hashPassword :: MonadIO m => Password -> m (PasswordHash Scrypt) -- | A hashed password. -- -- This represents a password that has been put through a hashing -- function. The hashed password can be stored in a database. newtype PasswordHash a PasswordHash :: Text -> PasswordHash a [unPasswordHash] :: PasswordHash a -> Text -- | Check a Password against a PasswordHash Scrypt. -- -- Returns PasswordCheckSuccess on success. -- --
--   >>> let pass = mkPassword "foobar"
--   
--   >>> passHash <- hashPassword pass
--   
--   >>> checkPassword pass passHash
--   PasswordCheckSuccess
--   
-- -- Returns PasswordCheckFail if an incorrect Password or -- PasswordHash Scrypt is used. -- --
--   >>> let badpass = mkPassword "incorrect-password"
--   
--   >>> checkPassword badpass passHash
--   PasswordCheckFail
--   
-- -- This should always fail if an incorrect password is given. -- --
--   \(Blind badpass) -> let correctPasswordHash = hashPasswordWithSalt testParams salt "foobar" in checkPassword badpass correctPasswordHash == PasswordCheckFail
--   
checkPassword :: Password -> PasswordHash Scrypt -> PasswordCheck -- | The result of checking a password against a hashed version. This is -- returned by the checkPassword functions. data PasswordCheck -- | The password check was successful. The plain-text password matches the -- hashed password. PasswordCheckSuccess :: PasswordCheck -- | The password check failed. The plain-text password does not match the -- hashed password. PasswordCheckFail :: PasswordCheck -- | Hash a password using the Scrypt algorithm with the given -- ScryptParams. -- -- N.B.: If you have any doubt in your knowledge of cryptography -- and/or the Scrypt algorithm, please just use -- hashPassword. -- -- Advice for setting the parameters: -- -- hashPasswordWithParams :: MonadIO m => ScryptParams -> Password -> m (PasswordHash Scrypt) -- | Default parameters for the Scrypt algorithm. -- --
--   >>> defaultParams
--   ScryptParams {scryptSalt = 32, scryptRounds = 14, scryptBlockSize = 8, scryptParallelism = 1, scryptOutputLength = 64}
--   
defaultParams :: ScryptParams -- | Extracts ScryptParams from a PasswordHash Scrypt. -- -- Returns 'Just ScryptParams' on success. -- --
--   >>> let pass = mkPassword "foobar"
--   
--   >>> passHash <- hashPassword pass
--   
--   >>> extractParams passHash == Just defaultParams
--   True
--   
extractParams :: PasswordHash Scrypt -> Maybe ScryptParams -- | Parameters used in the Scrypt hashing algorithm. data ScryptParams ScryptParams :: Word32 -> Word32 -> Word32 -> Word32 -> Word32 -> ScryptParams -- | Bytes to randomly generate as a unique salt, default is 32 [scryptSalt] :: ScryptParams -> Word32 -- | log2(N) rounds to hash, default is 14 (i.e. 2^14 rounds) [scryptRounds] :: ScryptParams -> Word32 -- | Block size, default is 8 -- -- Limits are min: 1, and max: scryptBlockSize * -- scryptParallelism < 2 ^ 30 [scryptBlockSize] :: ScryptParams -> Word32 -- | Parallelism factor, default is 1 -- -- Limits are min: 0, and max: scryptBlockSize * -- scryptParallelism < 2 ^ 30 [scryptParallelism] :: ScryptParams -> Word32 -- | Output key length in bytes, default is 64 [scryptOutputLength] :: ScryptParams -> Word32 -- | Hash a password with the given ScryptParams and also with the -- given Salt instead of a randomly generated salt using -- scryptSalt from ScryptParams. Using -- hashPasswordWithSalt is strongly disadvised and -- hashPasswordWithParams should be used instead. Never use a -- static salt in production applications! -- -- The resulting PasswordHash has the parameters used to hash it, -- as well as the Salt appended to it, separated by |. -- -- The input Salt and resulting PasswordHash are both -- base64 encoded. -- --
--   >>> let salt = Salt "abcdefghijklmnopqrstuvwxyz012345"
--   
--   >>> hashPasswordWithSalt defaultParams salt (mkPassword "foobar")
--   PasswordHash {unPasswordHash = "14|8|1|YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU=|nENDaqWBmPKapAqQ3//H0iBImweGjoTqn5SvBS8Mc9FPFbzq6w65maYPZaO+SPamVZRXQjARQ8Y+5rhuDhjIhw=="}
--   
-- -- (Note that we use an explicit Salt in the example above. This -- is so that the example is reproducible, but in general you should use -- hashPassword. hashPassword generates a new Salt -- everytime it is called.) hashPasswordWithSalt :: ScryptParams -> Salt Scrypt -> Password -> PasswordHash Scrypt -- | Generate a random 32-byte scrypt salt newSalt :: MonadIO m => m (Salt Scrypt) -- | A salt used by a hashing algorithm. newtype Salt a Salt :: ByteString -> Salt a [getSalt] :: Salt a -> ByteString -- | This is an unsafe function that shows a password in plain-text. -- --
--   >>> unsafeShowPassword ("foobar" :: Password)
--   "foobar"
--   
-- -- You should generally not use this function in production -- settings, as you don't want to accidentally print a password anywhere, -- like logs, network responses, database entries, etc. -- -- This will mostly be used by other libraries to handle the actual -- password internally, though it is conceivable that, even in a -- production setting, a password might have to be handled in an unsafe -- manner at some point. unsafeShowPassword :: Password -> Text instance GHC.Show.Show Data.Password.Scrypt.ScryptParams instance GHC.Classes.Eq Data.Password.Scrypt.ScryptParams -- |

Password Validation

-- -- It is common for passwords to have a set of requirements. The most -- obvious requirement being a minimum length, but another common -- requirement is for the password to at least include a certain amount -- of characters of a certain category, like uppercase and lowercase -- alphabetic characters, numbers and/or other special characters. -- Though, nowadays, this last type of requirement is -- discouraged by security experts. -- -- This module provides an API which enables you to set up your own -- PasswordPolicy to validate the format of Passwords. -- --

Recommendations by the NIST

-- -- For policy recommendations and more, look to the following publication -- by the National Institute of Standards and Technology (especially the -- addendum): -- https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63b.pdf -- -- A short summary: -- -- -- --

Password Policies

-- -- The most important part is to have a valid and robust -- PasswordPolicy. -- -- A defaultPasswordPolicy_ is provided to quickly set up a NIST -- recommended validation of passwords, but you can also adjust it, or -- just create your own. -- -- Just remember that a PasswordPolicy must be validated first to -- make sure it is actually a ValidPasswordPolicy. Otherwise, -- you'd never be able to validate any given Passwords. -- --

Example usage

-- -- So let's say we're fine with the default policy, which requires the -- password to be between 8-64 characters, and doesn't enforce any -- specific character category usage, then our function would look like -- the following: -- --
--   myValidateFunc :: Password -> Bool
--   myValidateFunc = isValidPassword defaultPasswordPolicy_
--   
-- --

Custom policies

-- -- But, for example, if you'd like to enforce that a Password -- includes at least one special character, and be at least 12 characters -- long, you'll have to make your own PasswordPolicy. -- --
--   customPolicy :: PasswordPolicy
--   customPolicy =
--     defaultPasswordPolicy
--       { minimumLength = 12
--       , specialChars = 1
--       }
--   
-- -- This custom policy will then have to be validated first, so it can be -- used to validate Passwords further on. -- --

Template Haskell

-- -- The easiest way to validate a custom PasswordPolicy is by using -- a Template Haskell splice. Just turn on the {-# LANGUAGE -- TemplateHaskell #-} pragma, pass your policy to -- validatePasswordPolicyTH, surround it by $(...) and if -- it compiles it will be a ValidPasswordPolicy. -- --
--   customValidPolicy :: ValidPasswordPolicy
--   customValidPolicy = $(validatePasswordPolicyTH customPolicy)
--   
-- -- NB: any custom CharSetPredicate will be ignored by -- validatePasswordPolicyTH and replaced with the -- defaultCharSetPredicate. So if you want to use your own -- CharSetPredicate, you won't be able to validate your policy -- using validatePasswordPolicyTH. Most users, however, will find -- defaultCharSetPredicate to be sufficient. -- --

At runtime

-- -- Another way of validating your custom policy is -- validatePasswordPolicy. In an application, this might be -- implemented in the following way. -- --
--   main :: IO ()
--   main =
--       case (validatePasswordPolicy customPolicy) of
--         Left reasons -> error $ show reasons
--         Right validPolicy -> app `runReaderT` validPolicy
--   
--   customValidateFunc :: Password -> ReaderT ValidPasswordPolicy IO Bool
--   customValidateFunc pwd = do
--       policy <- ask
--       return $ isValidPassword policy pwd
--   
-- --

Let's get dangerous

-- -- Or, if you like living on the edge, you could also just match on -- Right. I hope you're certain your policy is valid, though. So -- please have at least a unit test to verify that passing your -- PasswordPolicy to validatePasswordPolicy actually -- returns a Right. -- --
--   Right validPolicy = validatePasswordPolicy customPolicy
--   
--   customValidateFunc :: Password -> Bool
--   customValidateFunc = isValidPassword validPolicy
--   
module Data.Password.Validate -- | Checks if a given Password adheres to the provided -- ValidPasswordPolicy. -- -- In case of an invalid password, returns the reasons why it wasn't -- valid. -- --
--   >>> let pass = mkPassword "This_Is_Valid_Password1234"
--   
--   >>> validatePassword defaultPasswordPolicy_ pass
--   ValidPassword
--   
validatePassword :: ValidPasswordPolicy -> Password -> ValidationResult -- | This function is equivalent to: -- --
--   validatePassword policy password == ValidPassword
--   
-- --
--   >>> let pass = mkPassword "This_Is_Valid_PassWord1234"
--   
--   >>> isValidPassword defaultPasswordPolicy_ pass
--   True
--   
isValidPassword :: ValidPasswordPolicy -> Password -> Bool -- | Result of validating a Password. data ValidationResult ValidPassword :: ValidationResult InvalidPassword :: [InvalidReason] -> ValidationResult -- | Verifies that a PasswordPolicy is valid and converts it into a -- ValidPasswordPolicy. -- --
--   >>> validatePasswordPolicy defaultPasswordPolicy
--   Right (...)
--   
validatePasswordPolicy :: PasswordPolicy -> Either [InvalidPolicyReason] ValidPasswordPolicy -- | Template Haskell validation function for PasswordPolicys. -- --
--   {-# LANGUAGE TemplateHaskell #-}
--   myPolicy :: PasswordPolicy
--   myPolicy = defaultPasswordPolicy{ specialChars = 1 }
--   
--   myValidPolicy :: ValidPasswordPolicy
--   myValidPolicy = $(validatePasswordPolicyTH myPolicy)
--   
-- -- For technical reasons, the charSetPredicate field is ignored -- and the defaultCharSetPredicate is used. If, for any reason, -- you do need to use a custom CharSetPredicate, please use -- validatePasswordPolicy and either handle the failure case at -- runtime and/or use a unit test to make sure your policy is valid. validatePasswordPolicyTH :: PasswordPolicy -> Q Exp -- | Set of policies used to validate a Password. -- -- When defining your own PasswordPolicy, please keep in mind -- that: -- -- -- -- or else the validation functions will return one or more -- InvalidPolicyReasons. -- -- If you're unsure of what to do, please use the default: -- defaultPasswordPolicy_ data PasswordPolicy PasswordPolicy :: !Int -> !Int -> !Int -> !Int -> !Int -> !Int -> CharSetPredicate -> PasswordPolicy -- | Required password minimum length [minimumLength] :: PasswordPolicy -> !Int -- | Required password maximum length [maximumLength] :: PasswordPolicy -> !Int -- | Required number of upper-case characters [uppercaseChars] :: PasswordPolicy -> !Int -- | Required number of lower-case characters [lowercaseChars] :: PasswordPolicy -> !Int -- | Required number of special characters [specialChars] :: PasswordPolicy -> !Int -- | Required number of ASCII-digit characters [digitChars] :: PasswordPolicy -> !Int -- | Which characters are acceptable for use in passwords (cf. -- defaultCharSetPredicate) [charSetPredicate] :: PasswordPolicy -> CharSetPredicate -- | A PasswordPolicy that has been checked to be valid data ValidPasswordPolicy -- | In case you'd want to retrieve the PasswordPolicy from the -- ValidPasswordPolicy fromValidPasswordPolicy :: ValidPasswordPolicy -> PasswordPolicy -- | Default value for the PasswordPolicy. -- -- Enforces that a password must be between 8-64 characters long, though -- can easily be adjusted by using record update syntax: -- --
--   myPolicy = defaultPasswordPolicy{ minimumLength = 12 }
--   
-- -- Do note that this being a default policy doesn't make it a good -- enough policy in every situation. The most important field, -- minimumLength, has 8 characters as the default, because -- it is the bare minimum for some sense of security. The longer -- the password, the more difficult it will be to guess or -- brute-force, so a minimum of 12 or 16 would be advised in a -- production setting. -- -- This policy on it's own is guaranteed to be valid. Any changes made to -- it might result in validatePasswordPolicy returning one or more -- InvalidPolicyReasons. -- --
--   >>> defaultPasswordPolicy
--   PasswordPolicy {minimumLength = 8, maximumLength = 64, uppercaseChars = 0, lowercaseChars = 0, specialChars = 0, digitChars = 0, charSetPredicate = <FUNCTION>}
--   
defaultPasswordPolicy :: PasswordPolicy -- | Unchangeable defaultPasswordPolicy, but guaranteed to be valid. defaultPasswordPolicy_ :: ValidPasswordPolicy -- | Predicate which defines the characters that can be used for a -- password. newtype CharSetPredicate CharSetPredicate :: (Char -> Bool) -> CharSetPredicate [getCharSetPredicate] :: CharSetPredicate -> Char -> Bool -- | The default character set consists of uppercase and lowercase letters, -- numbers, and special characters from the ASCII character set. -- (i.e. everything from the ASCII set except the control -- characters) defaultCharSetPredicate :: CharSetPredicate -- | Possible reasons for a Password to be invalid. data InvalidReason -- | Length of Password is too short. PasswordTooShort :: !MinimumLength -> !ProvidedLength -> InvalidReason -- | Length of Password is too long. PasswordTooLong :: !MaximumLength -> !ProvidedLength -> InvalidReason -- | Password does not contain required number of characters. NotEnoughReqChars :: !CharacterCategory -> !MinimumAmount -> !ProvidedAmount -> InvalidReason -- | Password contains characters that cannot be used InvalidCharacters :: !Text -> InvalidReason -- | Possible reasons for a PasswordPolicy to be invalid data InvalidPolicyReason -- | Value of minimumLength is bigger than maximumLength -- --
--   InvalidLength minimumLength maximumLength
--   
InvalidLength :: !MinimumLength -> !MaximumLength -> InvalidPolicyReason -- | Value of maximumLength is zero or less -- --
--   MaxLengthBelowZero maximumLength
--   
MaxLengthBelowZero :: !MaximumLength -> InvalidPolicyReason -- | The total of the character category amount requirements are higher -- than the maximum length of the password. (i.e. the Int -- signifies the total of lowercaseChars + uppercaseChars + -- digitChars + specialChars) -- --
--   CategoryAmountsAboveMaxLength maximumLength totalRequiredChars
--   
CategoryAmountsAboveMaxLength :: !MaximumLength -> !Int -> InvalidPolicyReason -- | charSetPredicate does not return True for a -- CharacterCategory that requires at least MinimumAmount -- characters in the password InvalidCharSetPredicate :: !CharacterCategory -> !MinimumAmount -> InvalidPolicyReason -- | Character categories data CharacterCategory -- | Uppercase letters Uppercase :: CharacterCategory -- | Lowercase letters Lowercase :: CharacterCategory -- | Special characters Special :: CharacterCategory -- | ASCII digits Digit :: CharacterCategory type MinimumLength = Int type MaximumLength = Int type ProvidedLength = Int type MinimumAmount = Int type ProvidedAmount = Int -- | Default character set -- -- Should be all non-control characters in the ASCII character set. defaultCharSet :: String -- | Validate CharSetPredicate to return True on at least one -- of the characters that is required. -- -- For instance, if PasswordPolicy states that the password -- requires at least one uppercase letter, then CharSetPredicate -- should return True on at least one uppercase letter. validateCharSetPredicate :: PasswordPolicy -> [InvalidPolicyReason] -- | Convert a CharacterCategory into its associated predicate -- function categoryToPredicate :: CharacterCategory -> Char -> Bool -- | Check if given Char is a special character. (i.e. any -- non-alphanumeric non-control ASCII character) isSpecial :: Char -> Bool -- | All Int fields of the PasswordPolicy in a row allButCSP :: PasswordPolicy -> [Int] instance GHC.Show.Show Data.Password.Validate.ValidPasswordPolicy instance GHC.Classes.Ord Data.Password.Validate.ValidPasswordPolicy instance GHC.Classes.Eq Data.Password.Validate.ValidPasswordPolicy instance GHC.Show.Show Data.Password.Validate.CharacterCategory instance GHC.Classes.Ord Data.Password.Validate.CharacterCategory instance GHC.Classes.Eq Data.Password.Validate.CharacterCategory instance GHC.Show.Show Data.Password.Validate.InvalidReason instance GHC.Classes.Ord Data.Password.Validate.InvalidReason instance GHC.Classes.Eq Data.Password.Validate.InvalidReason instance GHC.Show.Show Data.Password.Validate.InvalidPolicyReason instance GHC.Classes.Ord Data.Password.Validate.InvalidPolicyReason instance GHC.Classes.Eq Data.Password.Validate.InvalidPolicyReason instance GHC.Show.Show Data.Password.Validate.ValidationResult instance GHC.Classes.Eq Data.Password.Validate.ValidationResult instance GHC.Classes.Eq Data.Password.Validate.PasswordPolicy instance GHC.Classes.Ord Data.Password.Validate.PasswordPolicy instance GHC.Show.Show Data.Password.Validate.PasswordPolicy