argon2-1.3.0.0: src-tests/Tests.hs
{-# LANGUAGE OverloadedStrings #-}
import Crypto.Argon2
import qualified Crypto.Argon2.FFI as FFI
import Data.Bits (shiftL)
import qualified Data.ByteString as BS
import Data.Ix
import Test.QuickCheck
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck
arbitraryVariant :: Gen Argon2Variant
arbitraryVariant = arbitraryBoundedEnum
arbitraryVersion :: Gen Argon2Version
arbitraryVersion = arbitraryBoundedEnum
arbitraryHashOptions :: Gen HashOptions
arbitraryHashOptions =
do p <- arbitraryWithin 1 4
HashOptions <$> arbitraryWithin FFI.ARGON2_MIN_TIME (min FFI.ARGON2_MAX_TIME 65536)
<*> arbitraryWithin (FFI.ARGON2_MIN_MEMORY*p) (FFI.ARGON2_MIN_MEMORY*p*4) -- arbitraryWithin (max (max FFI.ARGON2_MIN_MEMORY (8 * p)) (shiftL p 3)) (min FFI.ARGON2_MAX_MEMORY 512)
<*> pure p
<*> arbitraryVariant
<*> arbitraryVersion
<*> arbitraryWithin FFI.ARGON2_MIN_OUTLEN (min FFI.ARGON2_MAX_OUTLEN 65536)
arbitraryWithin lower upper = arbitrary `suchThat` (inRange (lower,upper))
arbitraryBytes :: Gen BS.ByteString
arbitraryBytes = BS.pack <$> arbitrary
arbitraryPassword :: Gen BS.ByteString
arbitraryPassword = arbitraryBytes `suchThat`
(\pwd -> (FFI.ARGON2_MIN_PWD_LENGTH,FFI.ARGON2_MAX_PWD_LENGTH) `inRange` BS.length pwd)
arbitrarySalt = arbitraryBytes `suchThat`
(\salt -> (FFI.ARGON2_MIN_SALT_LENGTH,FFI.ARGON2_MAX_SALT_LENGTH) `inRange` BS.length salt)
main :: IO ()
main = defaultMain $ testGroup "Tests" [unitTests,props]
where
props = testGroup "Properties"
[ testProperty "Round trip"
(forAll arbitraryHashOptions $ \hashOptions ->
forAll arbitraryPassword $ \password ->
forAll arbitrarySalt $ \salt ->
verifyEncoded (either undefined id $ hashEncoded hashOptions password salt) password == Argon2Ok)
, testProperty "Unencoded hashing"
(forAll arbitraryHashOptions $ \hashOptions ->
forAll arbitraryPassword $ \password ->
forAll arbitrarySalt $ \salt ->
either undefined id (hash hashOptions password salt) /= password)
, testProperty "defaultHashOptions"
(forAll arbitraryVariant $ \variant ->
(forAll arbitraryPassword $ \password ->
forAll arbitrarySalt $ \salt ->
verifyEncoded (either undefined id $
hashEncoded (defaultHashOptions {hashVariant = variant})
password
salt)
password == Argon2Ok))
]
unitTests = testGroup "KATs"
[ testGroup "argon2 README" $
let opts = defaultHashOptions { hashVariant = Argon2i
, hashMemory = 2^16
, hashIterations = 2
, hashParallelism = 4
, hashLength = 24
, hashVersion = Argon2Version13
}
in [ testCase "hashEncoded" $
hashEncoded opts "password" "somesalt" @?= Right "$argon2i$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG"
, testCase "verifyEncoded" $
verifyEncoded "$argon2i$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG" "password" @?= Argon2Ok
, testCase "verifyEncoded 2" $
verifyEncoded "$argon2i$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG" "passvord" @?= Argon2VerifyMismatch
, testCase "verifyEncoded 3" $
verifyEncoded "$argon2d$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG" "password" @?= Argon2VerifyMismatch
, testCase "verifyEncoded 4" $
verifyEncoded "$argon2id$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG" "password" @?= Argon2VerifyMismatch
, testCase "verifyEncoded 5" $
verifyEncoded "$argon2id$v=19$m=1,t=2,p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG" "password" @?= Argon2MemoryTooLittle
, testCase "verifyEncoded 6" $
verifyEncoded "$argon2x$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG" "password" @?= Argon2DecodingFail
, testCase "verifyEncoded 7" $
verifyEncoded "$argon2id$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJ" "password" @?= Argon2DecodingFail
, testCase "hash" $
hash opts "password" "somesalt" @?= Right "\x45\xD7\xAC\x72\xE7\x6F\x24\x2B\x20\xB7\x7B\x9B\xF9\xBF\x9D\x59\x15\x89\x4E\x66\x9A\x24\xE6\xC6"
]
]