{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-| Module : Data.Password.Types Copyright : (c) Dennis Gosnell, 2019; Felix Paulusma, 2020 License : BSD-style (see LICENSE file) Maintainer : cdep.illabout@gmail.com Stability : experimental Portability : POSIX This library provides datatypes for interacting with passwords. It provides the types 'Password' and 'PasswordHash', which correspond to plain-text and hashed passwords. == Special instances There is an accompanying package that provides canonical typeclass instances for 'Password' and 'PasswordHash' for many common typeclasses, like from , from , etc. See the package for more information. == Phantom types The 'PasswordHash' and 'Salt' data types have a phantom type parameter to be able to make sure salts and hashes can carry information about the algorithm they should be used with. For example, the @bcrypt@ algorithm requires its salt to be exactly 16 bytes (128 bits) long, so this way you won't accidentally use a @'Salt' PBKDF2@ when the hashing function requires a @'Salt' Bcrypt@. And checking a password using @bcrypt@ would obviously fail if checked against a @'PasswordHash' PBKDF2@. -} module Data.Password.Types ( -- * Plain-text Password Password , mkPassword -- * Password Hashing , PasswordHash (..) -- ** Unsafe debugging function to show a Password , unsafeShowPassword -- * Hashing salts , Salt (..) ) where import Data.ByteArray (constEq) import Data.ByteString (ByteString) import Data.Function (on) import Data.String (IsString(..)) import Data.Text (Text) import Data.Text.Encoding (encodeUtf8) -- $setup -- >>> :set -XOverloadedStrings -- | 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 -- library. newtype Password = Password Text deriving (IsString) -- | CAREFUL: 'Show'-ing a 'Password' will always print @"**PASSWORD**"@ -- -- >>> show ("hello" :: Password) -- "**PASSWORD**" instance Show Password where show _ = "**PASSWORD**" -- | Construct a 'Password' mkPassword :: Text -> Password mkPassword = Password {-# INLINE mkPassword #-} -- | 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 unsafeShowPassword (Password pass) = pass {-# INLINE unsafeShowPassword #-} -- | 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 { unPasswordHash :: Text } deriving (Ord, Read, Show) instance Eq (PasswordHash a) where (==) = constEq `on` encodeUtf8 . unPasswordHash -- | A salt used by a hashing algorithm. newtype Salt a = Salt { getSalt :: ByteString } deriving (Eq, Show)