password 3.0.4.0 → 3.1.1.0
raw patch · 31 files changed
Files
- ChangeLog.md +30/−0
- README.md +3/−1
- password.cabal +47/−26
- src/Data/Password/Argon2.hs +15/−7
- src/Data/Password/Bcrypt.hs +21/−13
- src/Data/Password/Internal.hs +1/−14
- src/Data/Password/PBKDF2.hs +17/−14
- src/Data/Password/Scrypt.hs +15/−7
- src/Data/Password/Validate.hs +3/−2
- test/golden/Argon_defaultParams.golden +1/−0
- test/golden/Argon_otherSalt.golden +1/−0
- test/golden/Argon_outputLength24.golden +1/−0
- test/golden/Argon_variantArgon2i.golden +1/−0
- test/golden/Bcrypt_cost16.golden +1/−0
- test/golden/Bcrypt_cost4.golden +1/−0
- test/golden/Bcrypt_defaultParams.golden +1/−0
- test/golden/Bcrypt_otherSalt.golden +1/−0
- test/golden/PBKDF2_algorithmMD5.golden +1/−0
- test/golden/PBKDF2_defaultParams.golden +1/−0
- test/golden/PBKDF2_iterCounts1000.golden +1/−0
- test/golden/PBKDF2_otherSalt.golden +1/−0
- test/golden/PBKDF2_outputLength16.golden +1/−0
- test/golden/Scrypt_defaultParams.golden +1/−0
- test/golden/Scrypt_otherSalt.golden +1/−0
- test/golden/Scrypt_outputLength16.golden +1/−0
- test/golden/Scrypt_rounds8.golden +1/−0
- test/golden/Scrypt_saltLength16.golden +1/−0
- test/tasty/Argon2.hs +17/−0
- test/tasty/Bcrypt.hs +17/−0
- test/tasty/PBKDF2.hs +18/−0
- test/tasty/Scrypt.hs +28/−0
ChangeLog.md view
@@ -1,5 +1,35 @@ # Changelog for `password` +## 3.1.1.0++- Support `crypton` dependency to include `^>= 1.1.0`.+ Added the `memory-dep` flag to be able to enforce building with `memory`+ instead of `ram` when constrained to lower versions of `crypton`.+- Removed converting to and from `Bytes`, especially since `ram >= 0.21.0`+ (a dependency of `crypton >= 1.1`) has it as a newtype over `ByteString`.+ Thanks to [@Vlix](https://github.com/Vlix)+ [#91](https://github.com/cdepillabout/password/pull/91)++## 3.1.0.2++- Added reference to [password-cli](https://github.com/cdepillabout/password/tree/master/password-cli) package in modules and README.++## 3.1.0.1++- Redo the conditionals in the `password.cabal` file so that the `scrypt`+ library is only included as a test dependency on `x86_64`. This generally+ shouldn't affect users of the `password` library.+ Thanks to [@sternenseemann](https://github.com/sternenseemann)+ [#85](https://github.com/cdepillabout/password/pull/85)++## 3.1.0.0++- Switched default cryptographic backend library from `cryptonite` to `crypton`.+ The `crypton` flag is now a no-op, and the `cryptonite` flag is needed to build+ the `password` library using the `cryptonite` library.+ Thanks to [@Vlix](https://github.com/Vlix)+ [#81](https://github.com/cdepillabout/password/pull/81)+ ## 3.0.4.0 - Support `base64` package up to and including `base64-1.0`.
README.md view
@@ -8,7 +8,7 @@ This library provides functions for working with passwords and password hashes in Haskell. -Currently supports the following algorithms:+It currently supports the following algorithms: * `PBKDF2` * `bcrypt`@@ -17,3 +17,5 @@ Also, see the [password-instances](https://hackage.haskell.org/package/password-instances) package for instances for common typeclasses.++To quickly test and use `password`, you can use [password-cli](https://github.com/cdepillabout/password/tree/master/password-cli).
password.cabal view
@@ -1,8 +1,8 @@ cabal-version: 1.12 name: password-version: 3.0.4.0-category: Data+version: 3.1.1.0+category: Security synopsis: Hashing and checking of passwords description: A library providing functionality for working with plain-text and hashed passwords@@ -45,6 +45,7 @@ extra-source-files: README.md ChangeLog.md+ test/golden/*.golden flag argon2 description: Compile with Argon2 support?@@ -58,16 +59,23 @@ flag crypton description: Use the [crypton] library as the cryptographic backend.+ (Does nothing since version 3.1.0.0; might be removed in a future major version) default: False manual: True flag cryptonite description: Use the [cryptonite] library as the cryptographic backend.- (Does nothing until a future major version) default: False manual: True +flag memory-dep+ description: Compile with [memory] dependency instead of [ram]+ default: False+ -- This should NOT be manual, so that constraint solvers+ -- can figure out which version of 'crypton' is used.+ manual: False+ flag pbkdf2 description: Compile with PBKDF2 support? default: True@@ -80,9 +88,9 @@ custom-setup setup-depends:- base <5- , Cabal <4- , cabal-doctest >=1.0.6 && <1.1+ base < 5+ , Cabal < 4+ , cabal-doctest >= 1.0.6 && < 1.1 source-repository head type: git@@ -112,23 +120,27 @@ base >= 4.9 && < 5 , base64 >= 0.3 && < 1.1 , bytestring >= 0.9 && < 0.13- , memory < 1 , password-types < 2- , template-haskell+ , template-haskell >= 2.2 && < 3 , text >= 1.2.2 && < 3 ghc-options: -Wall default-language: Haskell2010- -- At some future major version bump, this should- -- be changed to the [cryptonite] flag and that- -- `if flag(cryptonite) build-depends: cryptonite`- if flag(crypton)+ -- 'cryptonite' always means 'memory'+ if flag(cryptonite) build-depends:- crypton >= 0.31 && < 0.35+ cryptonite >= 0.15.1 && < 0.31,+ memory < 1 else- build-depends:- cryptonite >= 0.15.1 && < 0.31+ if flag(memory-dep)+ build-depends:+ crypton >= 0.31 && < 1.1,+ memory < 1+ else+ build-depends:+ crypton >= 1.1 && < 1.2,+ ram < 1 test-suite doctests type:@@ -140,13 +152,11 @@ ghc-options: -threaded -O2 -rtsopts -with-rtsopts=-N build-depends:- base >=4.9 && <5+ base >= 4.9 && < 5 , base-compat , doctest- , password , QuickCheck , quickcheck-instances- , template-haskell default-language: Haskell2010 @@ -158,6 +168,7 @@ test/tasty main-is: Spec.hs+ other-modules: Data.Password.Internal -- We're also putting all the modules from@@ -186,41 +197,51 @@ other-modules: Scrypt Data.Password.Scrypt+ ghc-options: -threaded -O2 -rtsopts -with-rtsopts=-N+ build-depends:- base >=4.9 && <5+ base >= 4.9 && < 5 , base64 , password-types , bytestring , memory , quickcheck-instances- , scrypt , tasty , tasty-hunit+ , tasty-golden , tasty-quickcheck , template-haskell , text default-language: Haskell2010+ if flag(argon2) cpp-options: -DCABAL_FLAG_argon2+ if flag(bcrypt) cpp-options: -DCABAL_FLAG_bcrypt+ if flag(pbkdf2) cpp-options: -DCABAL_FLAG_pbkdf2+ if flag(scrypt) cpp-options: -DCABAL_FLAG_scrypt- -- At some future major version bump, this should- -- be changed to the [cryptonite] flag and that- -- `if flag(cryptonite) build-depends: cryptonite`- if flag(crypton)++ -- The scrypt package is optionally used in tests to ensure compatibility+ -- between password and scrypt. However, scrypt requires the x86_64 arch+ -- with the SSE2 instruction set.+ if arch(x86_64)+ build-depends: scrypt++ if flag(cryptonite) build-depends:- crypton+ cryptonite else build-depends:- cryptonite+ crypton
src/Data/Password/Argon2.hs view
@@ -33,6 +33,15 @@ It is, however, recommended over @'Data.Password.Scrypt.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.++== Testing++You can use [password-cli](https://github.com/cdepillabout/password/tree/master/password-cli) to test it:++> $ password-cli check argon2 --hash 'SOME-HASH'++> $ password-cli hash argon2 --password-file password.txt+ -} -- I think the portability is broadened to@@ -77,7 +86,7 @@ #if MIN_VERSION_base64(1,0,0) import Data.Base64.Types (extractBase64) #endif-import Data.ByteArray (Bytes, constEq, convert)+import Data.ByteArray (constEq) import Data.ByteString as B (ByteString, length) import Data.ByteString.Base64 (encodeBase64) import Data.Maybe (fromMaybe)@@ -85,6 +94,7 @@ import Data.Semigroup ((<>)) #endif import Data.Text (Text)+import Data.Text.Encoding (encodeUtf8) import qualified Data.Text as T (dropWhileEnd, intercalate, split, splitAt, stripPrefix) import Data.Word (Word32) @@ -93,7 +103,6 @@ from64, readT, showT,- toBytes, unsafePad64, ) import Data.Password.Types (@@ -223,14 +232,13 @@ -- | Only for internal use hashPasswordWithSalt' :: Argon2Params -> Salt Argon2 -> Password -> ByteString hashPasswordWithSalt' Argon2Params{..} (Salt salt) pass =- convert (argon2Hash :: Bytes)- where- argon2Hash = throwCryptoError $+ throwCryptoError $ Argon2.hash options- (toBytes $ unsafeShowPassword pass)- (convert salt :: Bytes)+ (encodeUtf8 $ unsafeShowPassword pass)+ salt $ fromIntegral argon2OutputLength+ where options = Argon2.Options { iterations = argon2TimeCost, memory = argon2MemoryCost,
src/Data/Password/Bcrypt.hs view
@@ -42,6 +42,15 @@ because of the 1 character-long "$2$" version prefix, @bcrypt@ has had a version increase shortly after release, turning the prefix into a 2 character-long one like "$2a$" pretty much from the very beginning.++== Testing++You can use [password-cli](https://github.com/cdepillabout/password/tree/master/password-cli) to test it:++> $ password-cli check bcrypt --hash 'SOME-HASH'++> $ password-cli hash bcrypt --password-file password.txt+ -} module Data.Password.Bcrypt (@@ -77,8 +86,8 @@ import Control.Monad (guard) import Control.Monad.IO.Class (MonadIO(liftIO)) import Crypto.KDF.BCrypt as Bcrypt (bcrypt, validatePassword)-import Data.ByteArray (Bytes, convert) import qualified Data.Text as T+import Data.Text.Encoding (encodeUtf8, decodeUtf8) import Text.Read (readMaybe) import Data.Password.Types (@@ -88,11 +97,7 @@ , unsafeShowPassword , Salt(..) )-import Data.Password.Internal (- PasswordCheck(..)- , fromBytes- , toBytes- )+import Data.Password.Internal (PasswordCheck(..)) import qualified Data.Password.Internal (newSalt) @@ -160,9 +165,9 @@ hashPasswordWithSalt cost (Salt salt) pass = let hash = Bcrypt.bcrypt cost- (convert salt :: Bytes)- (toBytes $ unsafeShowPassword pass)- in PasswordHash $ fromBytes hash+ salt+ (encodeUtf8 $ unsafeShowPassword pass)+ in PasswordHash $ decodeUtf8 hash -- | Hash a password using the /bcrypt/ algorithm with the given cost. --@@ -205,8 +210,8 @@ checkPassword :: Password -> PasswordHash Bcrypt -> PasswordCheck checkPassword pass (PasswordHash passHash) = if Bcrypt.validatePassword- (toBytes $ unsafeShowPassword pass)- (toBytes passHash)+ (encodeUtf8 $ unsafeShowPassword pass)+ (encodeUtf8 passHash) then PasswordCheckSuccess else PasswordCheckFail @@ -221,8 +226,11 @@ extractParams :: PasswordHash Bcrypt -> Maybe Int extractParams (PasswordHash passHash) = case T.split (== '$') passHash of- [_, version, cost, _pass] -> do- guard $ elem version $ map T.pack ["2", "2a", "2x", "2y", "2b"]+ [prefix, version, cost, _pass] -> do+ guard $+ -- 'prefix' should be nothing, because the hash should start with a '$'+ -- The order of versions is based on most likely to encounter in the wild.+ T.null prefix && elem version (T.pack <$> ["2b", "2y", "2x", "2a", "2"]) readMaybe $ T.unpack cost _ -> Nothing
src/Data/Password/Internal.hs view
@@ -14,8 +14,6 @@ PasswordCheck(..) , newSalt -- * Utility- , toBytes- , fromBytes , from64 , unsafePad64 , readT@@ -26,7 +24,6 @@ import Control.Monad.IO.Class (MonadIO (liftIO)) import Crypto.Random (getRandomBytes)-import Data.ByteArray (Bytes, convert) import Data.ByteString (ByteString) #if MIN_VERSION_base64(1,0,0) import Data.ByteString.Base64 (decodeBase64Untyped)@@ -44,7 +41,7 @@ unpack, ) import Data.Password.Types (Salt(..))-import Data.Text.Encoding (decodeUtf8, encodeUtf8)+import Data.Text.Encoding (encodeUtf8) import Text.Read (readMaybe) -- $setup@@ -82,16 +79,6 @@ -- ^ The password check failed. The plain-text password does not match the -- hashed password. deriving (Eq, Read, Show)---- | Converting 'Text' to 'Bytes'-toBytes :: Text -> Bytes-toBytes = convert . encodeUtf8-{-# INLINE toBytes #-}---- | Converting 'Bytes' to 'Text'-fromBytes :: Bytes -> Text-fromBytes = decodeUtf8 . convert-{-# INLINE fromBytes #-} -- | Decodes a base64 'Text' to a regular 'ByteString' (if possible) from64 :: Text -> Maybe ByteString
src/Data/Password/PBKDF2.hs view
@@ -35,6 +35,15 @@ 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.++== Testing++You can use [password-cli](https://github.com/cdepillabout/password/tree/master/password-cli) to test it:++> $ password-cli check pbkdf2 --hash 'SOME-HASH'++> $ password-cli hash pbkdf2 --password-file password.txt+ -} module Data.Password.PBKDF2 (@@ -76,13 +85,14 @@ #if MIN_VERSION_base64(1,0,0) import Data.Base64.Types (extractBase64) #endif-import Data.ByteArray (ByteArray, ByteArrayAccess, Bytes, constEq, convert)+import Data.ByteArray (constEq) import Data.ByteString (ByteString) import Data.ByteString.Base64 (encodeBase64) import qualified Data.ByteString.Char8 as C8 (length) import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Text as T (intercalate, pack, split, stripPrefix)+import Data.Text.Encoding (encodeUtf8) import Data.Word (Word32) import Data.Password.Types (@@ -92,12 +102,7 @@ , unsafeShowPassword , Salt(..) )-import Data.Password.Internal (- PasswordCheck(..)- , from64- , readT- , toBytes- )+import Data.Password.Internal (PasswordCheck(..), from64, readT) import qualified Data.Password.Internal (newSalt) @@ -200,13 +205,12 @@ -- | Only for internal use hashPasswordWithSalt' :: PBKDF2Params -> Salt PBKDF2 -> Password -> ByteString hashPasswordWithSalt' PBKDF2Params{..} (Salt salt) pass =- convert (pbkdf2Hash :: Bytes)- where- pbkdf2Hash = algToFunc+ algToFunc pbkdf2Algorithm params- (toBytes $ unsafeShowPassword pass)- (convert salt :: Bytes)+ (encodeUtf8 $ unsafeShowPassword pass)+ salt+ where params = PBKDF2.Parameters { PBKDF2.iterCounts = fromIntegral pbkdf2Iterations, PBKDF2.outputLength = fromIntegral $ maxOutputLength pbkdf2Algorithm pbkdf2OutputLength@@ -315,8 +319,7 @@ _ -> Nothing -- Which function to use, based on the given algorithm-algToFunc :: (ByteArrayAccess password, ByteArrayAccess salt, ByteArray hash)- => PBKDF2Algorithm -> PBKDF2.Parameters -> password -> salt -> hash+algToFunc :: PBKDF2Algorithm -> PBKDF2.Parameters -> ByteString -> ByteString -> ByteString algToFunc = \case PBKDF2_MD5 -> PBKDF2.generate (PBKDF2.prfHMAC Crypto.MD5) PBKDF2_SHA1 -> PBKDF2.fastPBKDF2_SHA1
src/Data/Password/Scrypt.hs view
@@ -30,6 +30,15 @@ Because of the memory cost, it is generally advised to use @'Data.Password.Bcrypt.Bcrypt'@ if you're not sure this might be a problem on your system.++== Testing++You can use [password-cli](https://github.com/cdepillabout/password/tree/master/password-cli) to test it:++> $ password-cli check scrypt --hash 'SOME-HASH'++> $ password-cli hash scrypt --password-file password.txt+ -} module Data.Password.Scrypt (@@ -69,12 +78,13 @@ #if MIN_VERSION_base64(1,0,0) import Data.Base64.Types (extractBase64) #endif-import Data.ByteArray (Bytes, constEq, convert)+import Data.ByteArray (constEq) import Data.ByteString (ByteString) import Data.ByteString.Base64 (encodeBase64) import qualified Data.ByteString.Char8 as C8 (length) import Data.Maybe (fromMaybe) import qualified Data.Text as T (intercalate, split)+import Data.Text.Encoding (encodeUtf8) import Data.Word (Word32) import Data.Password.Types (@@ -89,7 +99,6 @@ , from64 , readT , showT- , toBytes ) import qualified Data.Password.Internal (newSalt) @@ -198,12 +207,11 @@ -- | Only for internal use hashPasswordWithSalt' :: ScryptParams -> Salt Scrypt -> Password -> ByteString hashPasswordWithSalt' ScryptParams{..} (Salt salt) pass =- convert (scryptHash :: Bytes)- where- scryptHash = Scrypt.generate+ Scrypt.generate params- (toBytes $ unsafeShowPassword pass)- (convert salt :: Bytes)+ (encodeUtf8 $ unsafeShowPassword pass)+ salt+ where params = Scrypt.Parameters { n = 2 ^ scryptRounds, r = fromIntegral scryptBlockSize,
src/Data/Password/Validate.hs view
@@ -202,8 +202,9 @@ import Data.Char (chr, isAsciiLower, isAsciiUpper, isDigit, ord) import Data.Function (on)-import Data.List (foldl')-+#if !MIN_VERSION_base(4,20,0)+import Data.Foldable (foldl')+#endif import Data.Text (Text) import qualified Data.Text as T import Language.Haskell.TH (Exp, Q, appE)
+ test/golden/Argon_defaultParams.golden view
@@ -0,0 +1,1 @@+$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ0Mg$w9lYrby3BP07ISb/6DWrawOF7lP9EZZfSBV2SiukgSE
+ test/golden/Argon_otherSalt.golden view
@@ -0,0 +1,1 @@+$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQw$4ZXadpbypzQxRQe2bef8kyFJc0PWKBHXoA5FN4iXf20
+ test/golden/Argon_outputLength24.golden view
@@ -0,0 +1,1 @@+$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ0Mg$jr2U2k4fMCMYkjLZTi8264BWKmovscHJ
+ test/golden/Argon_variantArgon2i.golden view
@@ -0,0 +1,1 @@+$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ0Mg$wTEHt+UirwtEfvpsYyNNCosby+3w651waXa3dtyF3Fo
+ test/golden/Bcrypt_cost16.golden view
@@ -0,0 +1,1 @@+$2b$16$a07rXVLfZFOuKB.uKB.yKeckTbSjjG2z2Hz8Kb2s8Jsc82snjBHxm
+ test/golden/Bcrypt_cost4.golden view
@@ -0,0 +1,1 @@+$2b$04$a07rXVLfZFOuKB.uKB.yKe1sTwXsMijp7DB/Yz0JJk2mmZqbRc6p2
+ test/golden/Bcrypt_defaultParams.golden view
@@ -0,0 +1,1 @@+$2b$10$a07rXVLfZFOuKB.uKB.yKe1tQQIacGVaKJqgpYw212LZlYsLIMonK
+ test/golden/Bcrypt_otherSalt.golden view
@@ -0,0 +1,1 @@+$2b$10$a07rXVLfZFOuKB.uKB.uK.yahNLJ/XzWDg41m2qdAMMEpkL7RrFG.
+ test/golden/PBKDF2_algorithmMD5.golden view
@@ -0,0 +1,1 @@+md5:25000:c29tZXNhbHQ0Mg==:jiOmE1hpAp2eJwrJFbJPLw==
+ test/golden/PBKDF2_defaultParams.golden view
@@ -0,0 +1,1 @@+sha512:25000:c29tZXNhbHQ0Mg==:RUT4QuEfyBnGIUR3R1jXbD+MjSTU+dNOLWX0mNtLKGI03PIjmxYSIN6USII99OMMhI3KR4o0tVJ1DH8XZaCtbA==
+ test/golden/PBKDF2_iterCounts1000.golden view
@@ -0,0 +1,1 @@+sha512:1000:c29tZXNhbHQ0Mg==:1gWOWLAgx5ojc3rh9uegFTRyUgKYHS84GPARbqZYIyHeAZdRQzVOLaEAg00JxeglImCAYZbBZ/dBh0Al8q1d8Q==
+ test/golden/PBKDF2_otherSalt.golden view
@@ -0,0 +1,1 @@+sha512:25000:c29tZXNhbHQw:SjeECOnuJvRIgLwPtL/akEzSS/+5vI4ZNRxEnZUOHnDuubUg7FcIuhnwm8a9cvuOXQrqsCZPHGgh8LNpxLEvRg==
+ test/golden/PBKDF2_outputLength16.golden view
@@ -0,0 +1,1 @@+sha512:25000:c29tZXNhbHQ0Mg==:RUT4QuEfyBnGIUR3R1jXbA==
+ test/golden/Scrypt_defaultParams.golden view
@@ -0,0 +1,1 @@+14|8|1|c29tZXNhbHQ0Mg==|byRql1IUuO27GSmCJ0a6ce18VJ6DwIdEcVNvvdEDFBFXosA4Vqh3SES/rR0+pY6ntCTWq4GZlQGTY67TWvWI1A==
+ test/golden/Scrypt_otherSalt.golden view
@@ -0,0 +1,1 @@+14|8|1|c29tZXNhbHQw|us1zh+3aNbwanYjy6Ig+Ft8yAv741HCMOemJpSEP2OJSic/hwr75xCeGVGqU9yG5NUnDAZ1arPuwBkVRgdLemA==
+ test/golden/Scrypt_outputLength16.golden view
@@ -0,0 +1,1 @@+14|8|1|c29tZXNhbHQ0Mg==|byRql1IUuO27GSmCJ0a6cQ==
+ test/golden/Scrypt_rounds8.golden view
@@ -0,0 +1,1 @@+8|8|1|c29tZXNhbHQ0Mg==|XgnxMJ3Ecu5nfvZUyHX7sCxRWRwmeyXeRpqwMy1jCGyPZCYJdGaS0fSJbenFgRoWTFqRWaJ1SoxnpWIiUIyrPw==
+ test/golden/Scrypt_saltLength16.golden view
@@ -0,0 +1,1 @@+14|8|1|c29tZXNhbHQ0Mg==|byRql1IUuO27GSmCJ0a6ce18VJ6DwIdEcVNvvdEDFBFXosA4Vqh3SES/rR0+pY6ntCTWq4GZlQGTY67TWvWI1A==
test/tasty/Argon2.hs view
@@ -1,8 +1,11 @@ {-# LANGUAGE OverloadedStrings #-} module Argon2 (testArgon2) where +import Data.ByteString.Lazy (fromStrict)+import Data.Text.Encoding (encodeUtf8) import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit (assertBool, assertEqual, testCase)+import Test.Tasty.Golden (goldenVsString) import Data.Password.Argon2 @@ -24,6 +27,7 @@ , testWithParams "Argon2 (Argon2d)" (fastParams{ argon2Variant = Argon2d }) , paddingTests , omittedVersionTest+ , testGolden ] where testWithParams s params =@@ -86,3 +90,16 @@ v10Hash, v10HashWithoutVersion :: PasswordHash Argon2 v10Hash = PasswordHash "$argon2i$v=16$m=65536,t=2,p=1$Kx1BEcpIg0Ey5GyXq5do2w$0qRfWHw09EdqQkSsaG57O/ou8v/E6Vc83w" v10HashWithoutVersion = PasswordHash "$argon2i$m=65536,t=2,p=1$Kx1BEcpIg0Ey5GyXq5do2w$0qRfWHw09EdqQkSsaG57O/ou8v/E6Vc83w"++testGolden :: TestTree+testGolden = testGroup "Golden tests"+ [ go "defaultParams" "Argon_defaultParams" defaultParams "somesalt42"+ , go "variant = Argon2i" "Argon_variantArgon2i" defaultParams{argon2Variant = Argon2i} "somesalt42"+ , go "output length = 24" "Argon_outputLength24" defaultParams{argon2OutputLength = 24} "somesalt42"+ , go "other salt" "Argon_otherSalt" defaultParams "somesalt0"+ ]+ where go testName fileName params salt =+ goldenVsString+ testName+ ("test/golden/" <> fileName <> ".golden")+ (return $ fromStrict $ encodeUtf8 $ unPasswordHash $ hashPasswordWithSalt params (Salt salt) $ mkPassword "password")
test/tasty/Bcrypt.hs view
@@ -1,9 +1,12 @@ {-# LANGUAGE OverloadedStrings #-} module Bcrypt where +import Data.ByteString.Lazy (fromStrict) import Data.Text (pack)+import Data.Text.Encoding (encodeUtf8) import Test.Tasty import Test.Tasty.HUnit (testCase, assertEqual)+import Test.Tasty.Golden (goldenVsString) import Data.Password.Bcrypt @@ -22,6 +25,7 @@ -- These apparently do not work. -- , testPassword "2x" -- , testPassword "2"+ , testGolden ] -- | Tests if the version does not matter for matching the hash.@@ -34,3 +38,16 @@ where hash v = PasswordHash $ "$" <> pack v <> "$10$v0mMyoUN2ZvDqsJFPH6ft.FcpX67hdXhh7tWf8/hwWZrKZ8U2Phs6"++testGolden :: TestTree+testGolden = testGroup "Golden tests"+ [ go "defaultParams" "Bcrypt_defaultParams" defaultParams "somesalt00000042"+ , go "cost = 16" "Bcrypt_cost16" 16 "somesalt00000042"+ , go "cost = 4" "Bcrypt_cost4" 4 "somesalt00000042"+ , go "other salt" "Bcrypt_otherSalt" defaultParams "somesalt00000000"+ ]+ where go testName fileName params salt =+ goldenVsString+ testName+ ("test/golden/" <> fileName <> ".golden")+ (return $ fromStrict $ encodeUtf8 $ unPasswordHash $ hashPasswordWithSalt params (Salt salt) $ mkPassword "password")
test/tasty/PBKDF2.hs view
@@ -5,10 +5,13 @@ import Crypto.Hash.Algorithms as Crypto (HashAlgorithm, SHA1(..), SHA256(..), SHA512(..)) import Crypto.KDF.PBKDF2 as PBKDF2 import Data.ByteString (ByteString)+import Data.ByteString.Lazy (fromStrict) #if !MIN_VERSION_base(4,13,0) import Data.Semigroup ((<>)) #endif+import Data.Text.Encoding (encodeUtf8) import Test.Tasty+import Test.Tasty.Golden (goldenVsString) import Test.Tasty.QuickCheck import Test.QuickCheck.Instances.ByteString () import Test.QuickCheck.Instances.Text ()@@ -43,6 +46,7 @@ (\pass (PasswordHash hash) -> checkPassword pass . PasswordHash $ "pbkdf2:" <> hash) extractParams testParams+ , testGolden ] where testIt s params = testCorrectPassword s (hashPasswordWithParams params) checkPassword extractParams params@@ -65,3 +69,17 @@ PBKDF2.iterCounts = 2000, PBKDF2.outputLength = i }++testGolden :: TestTree+testGolden = testGroup "Golden tests"+ [ go "defaultParams" "PBKDF2_defaultParams" defaultParams "somesalt42"+ , go "algorithm = MD5" "PBKDF2_algorithmMD5" defaultParams{pbkdf2Algorithm = PBKDF2_MD5} "somesalt42"+ , go "output length = 16" "PBKDF2_outputLength16" defaultParams{pbkdf2OutputLength = 16} "somesalt42"+ , go "iterCounts = 1000" "PBKDF2_iterCounts1000" defaultParams{pbkdf2Iterations = 1000} "somesalt42"+ , go "other salt" "PBKDF2_otherSalt" defaultParams "somesalt0"+ ]+ where go testName fileName params salt =+ goldenVsString+ testName+ ("test/golden/" <> fileName <> ".golden")+ (return $ fromStrict $ encodeUtf8 $ unPasswordHash $ hashPasswordWithSalt params (Salt salt) $ mkPassword "password")
test/tasty/Scrypt.hs view
@@ -1,13 +1,20 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-} module Scrypt where +import Data.ByteString.Lazy (fromStrict) import Data.Maybe (fromJust) import Data.Text (Text) import Data.Text.Encoding (encodeUtf8) import Test.Tasty+import Test.Tasty.Golden (goldenVsString) import Test.Tasty.QuickCheck import Test.QuickCheck.Instances.Text () +#ifdef x86_64_HOST_ARCH import qualified Crypto.Scrypt as Scrypt+#endif+ import Data.Password.Types import Data.Password.Scrypt @@ -24,7 +31,10 @@ checkPassword extractParams testsParams8Rounds+#ifdef x86_64_HOST_ARCH , testProperty "scrypt <-> cryptonite" $ withMaxSuccess 10 checkScrypt+#endif+ , testGolden ] where hash8Rounds = hashPasswordWithParams testsParams8Rounds@@ -32,6 +42,9 @@ hash4Rounds = hashPasswordWithParams testsParams4Rounds testsParams4Rounds = defaultParams{ scryptRounds = 4, scryptSalt = 16 } +#ifdef x86_64_HOST_ARCH+-- The scrypt library is only available on x86_64, so we only compare password's+-- scrypt functionality against the actual scrypt library when building on x86_64. checkScrypt :: Text -> Property checkScrypt pass = ioProperty $ do s@(Scrypt.Salt salt) <- Scrypt.newSalt@@ -41,3 +54,18 @@ PasswordHash ourHash = hashPasswordWithSalt defaultParams{ scryptRounds = 8 } (Salt salt) $ mkPassword pass return $ scryptHash === encodeUtf8 ourHash+#endif++testGolden :: TestTree+testGolden = testGroup "Golden tests"+ [ go "defaultParams" "Scrypt_defaultParams" defaultParams "somesalt42"+ , go "rounds = 8" "Scrypt_rounds8" defaultParams{scryptRounds = 8} "somesalt42"+ , go "output length = 16" "Scrypt_outputLength16" defaultParams{scryptOutputLength = 16} "somesalt42"+ , go "scrypt length = 16" "Scrypt_saltLength16" defaultParams{scryptSalt = 16} "somesalt42"+ , go "Other salt" "Scrypt_otherSalt" defaultParams "somesalt0"+ ]+ where go testName fileName params salt =+ goldenVsString+ testName+ ("test/golden/" <> fileName <> ".golden")+ (return $ fromStrict $ encodeUtf8 $ unPasswordHash $ hashPasswordWithSalt params (Salt salt) $ mkPassword "password")