packages feed

password 3.0.4.0 → 3.1.1.0

raw patch · 31 files changed

Files

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")