Copyright | (c) Dennis Gosnell 2019 |
---|---|
License | BSD-style (see LICENSE file) |
Maintainer | cdep.illabout@gmail.com |
Stability | experimental |
Portability | POSIX |
Safe Haskell | None |
Language | Haskell2010 |
This module provides an easy way for interacting with passwords from Haskell.
It provides the types Pass
and PassHash
, which correspond to plain-text and
hashed passwords.
It also provides functions for hashing (hashPass
) and checking passwords
(checkPass
).
The real benefit of this module is that there is a corresponding
password-instances
module that provides canonical typeclass instances for
Pass
and PassHash
for many common typeclasses, like
FromJSON from
aeson,
PersistField
from
persistent, etc.
See the password-instances module for more information.
Synopsis
- data Pass
- mkPass :: Text -> Pass
- newtype PassHash = PassHash {
- unPassHash :: Text
- newtype Salt = Salt {}
- hashPass :: MonadIO m => Pass -> m PassHash
- hashPassWithSalt :: Salt -> Pass -> PassHash
- newSalt :: MonadIO m => m Salt
- checkPass :: Pass -> PassHash -> PassCheck
- data PassCheck
- unsafeShowPassword :: Pass -> String
- unsafeShowPasswordText :: Pass -> Text
Plaintext Password
A plain-text password.
This represents a plain-text password that has NOT been hashed.
You should be careful with Pass
. Make sure not to write it to logs or
store it in a database.
You can construct a Pass
by using the mkPass
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.
Hashed Password
A hashed password.
This represents a password that has been put through a hashing function. The hashed password can be stored in a database.
Functions for Hashing Plaintext Passwords
hashPass :: MonadIO m => Pass -> m PassHash Source #
Just like hashPassWithSalt
, but generate a new Salt
everytime with a
call to newSalt
.
>>>
hashPass $ mkPass "foobar"
PassHash {unPassHash = "14|8|1|...|..."}
hashPassWithSalt :: Salt -> Pass -> PassHash Source #
Hash a password with the given Salt
.
The resulting PassHash
has the parameters used to hash it, as well as the
Salt
appended to it, separated by |
.
The input Salt
and resulting PassHash
are both byte-64 encoded.
>>>
let salt = Salt "abcdefghijklmnopqrstuvwxyz012345"
>>>
hashPassWithSalt salt (mkPass "foobar")
PassHash {unPassHash = "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 hashPass
. hashPass
generates a new Salt
everytime it is called.)
This function uses the hash function from the scrypt package: encryptPass
.
Functions for Checking Plaintext Passwords Against Hashed Passwords
checkPass :: Pass -> PassHash -> PassCheck Source #
Check a Pass
against a PassHash
.
Returns PassCheckSuccess
on success.
>>>
let salt = Salt "abcdefghijklmnopqrstuvwxyz012345"
>>>
let pass = mkPass "foobar"
>>>
let passHash = hashPassWithSalt salt pass
>>>
checkPass pass passHash
PassCheckSuccess
Returns PassCheckFail
If an incorrect Pass
or PassHash
is used.
>>>
let badpass = mkPass "incorrect-password"
>>>
checkPass badpass passHash
PassCheckFail
This should always fail if an incorrect password is given.
\(Blind badpass) -> let correctPassHash = hashPassWithSalt salt "foobar" in checkPass badpass correctPassHash == PassCheckFail
The result of a checking a password against a hashed version. This is
returned by checkPass
.
PassCheckSuccess | The password check was successful. The plain-text password matches the hashed password. |
PassCheckFail | The password check failed. The plain-text password does not match the hashed password. |
Unsafe Debugging Functions for Showing a Password
unsafeShowPassword :: Pass -> String Source #
This is an unsafe function that shows a password in plain-text.
>>>
unsafeShowPasswordText $ mkPass "foobar"
"foobar"
You should generally not use this function.
unsafeShowPasswordText :: Pass -> Text Source #
This is like unsafeShowPassword
but produces a Text
instead of a
String
.
Setup for doctests.
>>>
:set -XOverloadedStrings
Import needed libraries.
>>>
import Data.ByteString (pack)
>>>
import Test.QuickCheck (Arbitrary(arbitrary), Blind(Blind), vector)
>>>
import Test.QuickCheck.Instances.ByteString ()
>>>
import Test.QuickCheck.Instances.Text ()
Arbitrary
instances for types exported from this library.
>>>
instance Arbitrary Salt where arbitrary = Salt . pack <$> vector 32
>>>
instance Arbitrary Pass where arbitrary = fmap Pass arbitrary
>>>
instance Arbitrary PassHash where arbitrary = hashPassWithSalt <$> arbitrary <*> arbitrary
Arbitrary
instances for types exported from Crypto.Scrypt.
>>>
instance Arbitrary Scrypt.Pass where arbitrary = fmap Scrypt.Pass arbitrary
>>>
instance Arbitrary EncryptedPass where arbitrary = encryptPass defaultParams <$> arbitrary <*> arbitrary