age-0.0.1.0: test/Test/Crypto/Age/Scrypt.hs
{-# LANGUAGE TemplateHaskell #-}
module Test.Crypto.Age.Scrypt
( tests
, goldenWorkFactor
) where
import Crypto.Age.Scrypt
( WorkFactor
, bytesToSalt
, mkWorkFactor
, saltToBytes
, workFactorBuilder
, workFactorParser
)
import Data.Attoparsec.ByteString ( parseOnly )
import Data.ByteString ( ByteString )
import qualified Data.ByteString as BS
import Data.ByteString.Builder ( Builder, toLazyByteString )
import qualified Data.ByteString.Builder as Builder
import Data.Word ( Word8 )
import Hedgehog
( Property, checkParallel, discover, forAll, property, tripping )
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Prelude
import Test.Crypto.Age.Scrypt.Gen ( genSalt, genWorkFactorInRange )
import Test.Golden ( goldenTestWithEncoderAndDecoder )
tests :: IO Bool
tests = checkParallel $$(discover)
------------------------------------------------------------------------------
-- Properties
------------------------------------------------------------------------------
-- | Test that 'bytesToSalt' only returns 'Just' when given 16 bytes.
prop_bytesToSalt :: Property
prop_bytesToSalt = property $ do
bs <- forAll $ Gen.bytes (Range.constant 0 256)
let bsLen = BS.length bs
case bytesToSalt bs of
Nothing
| bsLen == 16 -> fail "failed when given 16 bytes"
| otherwise -> pure ()
Just _
| bsLen == 16 -> pure ()
| otherwise -> fail $ "succeeded when given " <> show bsLen <> " bytes"
-- | Test that 'saltToBytes' and 'bytesToSalt' round trip.
prop_roundTrip_saltBytes :: Property
prop_roundTrip_saltBytes = property $ do
salt <- forAll genSalt
tripping
salt
saltToBytes
bytesToSalt
-- | Test that 'mkWorkFactor' only returns 'Just' when given an integer
-- @0 < n <= 64@.
prop_mkWorkFactor :: Property
prop_mkWorkFactor = property $ do
n <- forAll $ Gen.word8 Range.constantBounded
case mkWorkFactor n of
Nothing
| isValid n -> fail $ "failed when given " <> show n
| otherwise -> pure ()
Just _
| isValid n -> pure ()
| otherwise -> fail $ "succeeded when given " <> show n
where
isValid :: Word8 -> Bool
isValid w8
| w8 >= 1 && w8 <= 64 = True
| otherwise = False
-- | Test that 'workFactorBuilder' and 'workFactorParser' round trip.
prop_roundTrip_encodeParseWorkFactor :: Property
prop_roundTrip_encodeParseWorkFactor = property $ do
workFactor <- forAll $ genWorkFactorInRange (Range.constant 1 64)
tripping
workFactor
(toStrictByteString . workFactorBuilder)
(parseOnly workFactorParser)
-- | Test that 'workFactorParser' fails when parsing a work factor with leading
-- zeroes.
prop_workFactorParser_failOnLeadingZeroes :: Property
prop_workFactorParser_failOnLeadingZeroes = property $ do
workFactor <- forAll $ genWorkFactorInRange (Range.constant 1 64)
let encodedWorkFactor = toStrictByteString (workFactorBuilder workFactor)
leadingZeroes <- forAll $ Gen.utf8 (Range.constant 1 256) (pure '0')
let encodedWorkFactorWithLeadingZeroes = leadingZeroes <> encodedWorkFactor
case parseOnly workFactorParser encodedWorkFactorWithLeadingZeroes of
Left _ -> pure ()
Right _ -> fail $ "succeeded when parsing " <> show encodedWorkFactorWithLeadingZeroes
-- | Test that 'workFactorParser' fails when parsing a work factor that is @0@
-- or greater than @64@.
prop_workFactorParser_cannotParseIntOutOfRange :: Property
prop_workFactorParser_cannotParseIntOutOfRange = property $ do
n <- forAll $ Gen.word Range.linearBounded
let encoded = toStrictByteString (Builder.wordDec n)
case parseOnly workFactorParser encoded of
Left _
| n <= 0 || n > 64 -> pure ()
| otherwise -> fail $ "failed when parsing " <> show n
Right _
| n > 0 && n <= 64 -> pure ()
| otherwise -> fail $ "succeeded when parsing " <> show n
prop_golden_workFactor :: Property
prop_golden_workFactor =
goldenTestWithEncoderAndDecoder
(toStrictByteString . workFactorBuilder)
(parseOnly workFactorParser)
goldenWorkFactor
"test/golden/binary/work-factor/golden.bin"
------------------------------------------------------------------------------
-- Golden examples
------------------------------------------------------------------------------
goldenWorkFactor :: WorkFactor
goldenWorkFactor =
case mkWorkFactor 13 of
Nothing -> error "goldenWorkFactor: impossible: could not construct work factor"
Just x -> x
------------------------------------------------------------------------------
-- Helpers
------------------------------------------------------------------------------
toStrictByteString :: Builder -> ByteString
toStrictByteString = BS.toStrict . toLazyByteString