packages feed

password-3.1.1.0: test/tasty/Scrypt.hs

{-# 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

import Internal


testScrypt :: TestTree
testScrypt = testGroup "scrypt"
  [ testCorrectPassword "Scrypt (hashPassword, 8 rounds)" hash8Rounds checkPassword extractParams testsParams8Rounds
  , testCorrectPassword "Scrypt (hashPassword, 4 rounds)" hash4Rounds checkPassword extractParams testsParams4Rounds
  , testIncorrectPassword "Scrypt (hashPassword) fail" hash8Rounds checkPassword
  , testWithSalt "Scrypt (hashPasswordWithSalt)"
                 (hashPasswordWithSalt testsParams8Rounds)
                 checkPassword
                 extractParams
                 testsParams8Rounds
#ifdef x86_64_HOST_ARCH
  , testProperty "scrypt <-> cryptonite" $ withMaxSuccess 10 checkScrypt
#endif
  , testGolden
  ]
  where
    hash8Rounds = hashPasswordWithParams testsParams8Rounds
    testsParams8Rounds = defaultParams{ scryptRounds = 8, scryptSalt = 16 }
    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
  let params = fromJust $ Scrypt.scryptParams 8 8 1
      Scrypt.EncryptedPass scryptHash =
        Scrypt.encryptPass params s $ Scrypt.Pass $ encodeUtf8 pass
      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")