diff --git a/Crypto/Scrypt.hs b/Crypto/Scrypt.hs
deleted file mode 100644
--- a/Crypto/Scrypt.hs
+++ /dev/null
@@ -1,235 +0,0 @@
-{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings,
-    RecordWildCards, NamedFieldPuns #-}
-
--- |Scrypt is a sequential memory-hard key derivation function. This module
---  provides low-level bindings to the 'scrypt' key derivation function as
---  well as a higher-level password-storage API. It is based on a fast C
---  implementation of scrypt, written by Colin Percival. For further
---  information see <http://www.tarsnap.com/scrypt.html>.
---
-module Crypto.Scrypt (
-    -- * Parameters to the @scrypt@ function
-    -- $params
-     ScryptParams, scryptParams, defaultParams
-    -- * Password Storage
-    -- $password-storage
-    , EncryptedPass(..), encryptPass, encryptPass', verifyPass, verifyPass'
-    -- * Low-level bindings to the @scrypt@ key derivation function
-    -- $low-level
-    , Pass(..), Salt(..), PassHash(..), scrypt, scrypt'
-    ) where
-
-import Control.Applicative
-import qualified Data.ByteString.Base64 as Base64
-import Data.ByteString.Char8 hiding (map, concat)
-import Data.Maybe
-import Foreign
-import Foreign.C
-import System.Entropy (getEntropy)
-
-
-newtype Pass          = Pass     { unPass :: ByteString } deriving (Show, Eq)
-newtype Salt          = Salt     { unSalt :: ByteString } deriving (Show, Eq)
-newtype PassHash      = PassHash { unHash :: ByteString } deriving (Show,Eq)
-newtype EncryptedPass =
-    EncryptedPass { unEncryptedPass  :: ByteString } deriving (Show, Eq)
-
-------------------------------------------------------------------------------
--- $params
---
--- Scrypt takes three tuning parameters: @N@, @r@ and @p@. They affect running
--- time and memory usage:
---
--- /Memory usage/ is approximately @128*r*N@ bytes. Note that the
--- 'scryptParams' function takes @log_2(N)@ as a parameter. As an example,
--- the 'defaultParams'
---
--- >   log_2(N) = 14, r = 8 and p = 1
---
--- lead to 'scrypt' using @128 * 8 * 2^14 = 16M@ bytes of memory.
---
--- /Running time/ is proportional to all of @N@, @r@ and @p@. Since it's
--- influence on memory usage is small, @p@ can be used to independently tune
--- the running time.
---
-
--- |Encapsulates the three tuning parameters to the 'scrypt' function: @N@,
--- @r@ and @p@ (see above).
---
-data ScryptParams = Params { logN, r, p, bufLen :: Integer} deriving (Eq)
-
-instance Show ScryptParams where
-    show Params{..} = concat [ "ScryptParams "
-        , "{ logN=", show logN
-        , ", r="   , show r
-        , ", p="   , show p
-        , " }"
-        ]
-
--- |Constructor function for the 'ScryptParams' data type
---
-scryptParams
-    :: Integer
-    -- ^ @log_2(N)@. Scrypt's @N@ parameter must be a power of two greater
-    --   than one, thus it's logarithm to base two must be greater than zero.
-    --   @128*r*N@ must be smaller than the available memory address space.
-    -> Integer
-    -- ^ The parameter @r@, must be greater than zero.
-    -> Integer
-    -- ^ The parameter @p@, must be greater than zero. @r@ and @p@
-    --   must satisfy @r*p < 2^30@.
-    -> Maybe ScryptParams
-    -- ^ Returns 'Just' the parameter object for valid arguments,
-    --   otherwise 'Nothing'.
-    --
-scryptParams logN r p | valid     = Just ps
-                      | otherwise = Nothing
-  where
-    ps    = Params { logN, r, p, bufLen = 64 }
-    valid = and [ logN > 0, r > 0, p > 0
-                , r*p < 2^(30 :: Int)
-                , bufLen ps <= 2^(32 :: Int)-1 * 32
-                -- allocation fits into (virtual) memory
-                , 128*r*2^logN <= fromIntegral (maxBound :: CSize)
-                ]
-
--- |Default parameters as recommended in the scrypt paper:
---
---  >   N = 2^14, r = 8, p = 1
---
---  Equivalent to @'fromJust' ('scryptParams' 14 8 1)@.
---
-defaultParams :: ScryptParams
-defaultParams = fromJust (scryptParams 14 8 1)
-
-------------------------------------------------------------------------------
--- $password-storage
---
--- To allow storing encrypted passwords conveniently in a single database
--- column, the password storage API provides the data type 'EncryptedPass'. It
--- combines a 'Pass' as well as the 'Salt' and 'ScryptParams' used to compute
--- it into a single 'ByteString', separated by pipe (\"|\") characters. The
--- 'Salt' and 'PassHash' are base64-encoded. Storing the 'ScryptParams' with 
--- the password allows to gradually strengthen password encryption in case of
--- changing security requirements.
---
--- A usage example is given below, showing encryption, verification and
--- changing 'ScryptParams':
---
--- > >>> encrypted <- encryptPass defaultParams (Pass "secret")
--- > >>> print encrypted
--- > EncryptedPass {unEncryptedPass = "14|8|1|Wn5x[SNIP]nM=|Zl+p[SNIP]g=="}
--- > >>> print $ verifyPass defaultParams (Pass "secret") encrypted
--- > (True,Nothing)
--- > >>> print $ verifyPass defaultParams (Pass "wrong") encrypted
--- > (False,Nothing)
--- > >>> let newParams = fromJust $ scryptParams 16 8 1
--- > >>> print $ verifyPass newParams (Pass "secret") encrypted
--- > (True,Just (EncryptedPass {unEncryptedPass = "16|8|1|Wn5x[SNIP]nM=|ZmWw[SNIP]Q=="}))
---
-
-combine :: ScryptParams -> Salt -> PassHash -> EncryptedPass
-combine Params{..} (Salt salt) (PassHash passHash) =
-    EncryptedPass $ intercalate "|"
-        [ showBS logN, showBS r, showBS p
-        , Base64.encode salt, Base64.encode passHash]
-  where
-    showBS = pack . show
-
-separate :: EncryptedPass -> Maybe (ScryptParams, Salt, PassHash)
-separate = go . split '|' . unEncryptedPass
-  where
-    go [logN', r', p', salt', hash'] = do
-        [salt, hash] <- mapM decodeBase64 [salt', hash']
-        [logN, r, p] <- mapM (fmap fst . readInteger) [logN', r', p']
-        params       <- scryptParams logN r p
-        return (params, Salt salt, PassHash hash)
-    go _         = Nothing
-    decodeBase64 = either (const Nothing) Just . Base64.decode
-
--- |Encrypt the password with the given parameters and a random 32-byte salt.
--- The salt is read from @\/dev\/urandom@ on Unix systems or @CryptoAPI@ on
--- Windows.
---
-encryptPass :: ScryptParams -> Pass -> IO EncryptedPass
-encryptPass params pass = do
-    salt <- Salt <$> getEntropy 32
-    return $ combine params salt (scrypt params salt pass)
-
--- |Equivalent to @encryptPass defaultParams@.
---
-encryptPass' :: Pass -> IO EncryptedPass
-encryptPass' = encryptPass defaultParams
-
--- |Verify a 'Pass' against an 'EncryptedPass'. The function also takes
---  'ScryptParams' meeting your current security requirements. In case the
---  'EncryptedPass' was generated with different parameters, the function
---  returns an updated 'EncryptedPass', generated with the given 
---  'ScryptParams'. The 'Salt' is kept from the given 'EncryptedPass'.
---
-verifyPass
-    :: ScryptParams
-    -- ^ Parameters to use for updating the 'EncryptedPass'.
-    -> Pass
-    -- ^ The candidate 'Pass'.
-    -> EncryptedPass
-    -- ^ The 'EncryptedPass' to check against.
-    -> (Bool, Maybe EncryptedPass)
-    -- ^ Returns a pair of
-    --
-    --     * 'Bool' indicating verification success or failure.
-    --
-    --     * 'Just' a /new/ 'EncryptedPass' if the given 'ScryptParams' are
-    --      different from those encapsulated in the /given/ 'EncryptedPass',
-    --      otherwise 'Nothing'.
-    --
-verifyPass newParams candidate encrypted =
-    maybe (False, Nothing) verify (separate encrypted)
-  where
-    verify (params,salt,hash) =
-        let valid   = scrypt params salt candidate == hash
-            newHash = scrypt newParams salt candidate
-            newEncr = if not valid || params == newParams
-                        then Nothing
-                        else Just (combine newParams salt newHash)
-        in (valid, newEncr)
-
--- |Check the 'Pass' against the 'EncryptedPass', using the 'ScryptParams'
---  encapsulated in the 'EncryptedPass'.
---
-verifyPass' :: Pass -> EncryptedPass -> Bool
-verifyPass' pass encrypted = fst $ verifyPass defaultParams pass encrypted
-
-------------------------------------------------------------------------------
--- $low-level
---
--- Bindings to a fast C implementation of 'scrypt'. For password storage,
--- consider using the more convenient higher-level API above.
---
-
--- |Calculates a 64-byte hash from the given password, salt and parameters.
---
-scrypt :: ScryptParams -> Salt -> Pass -> PassHash
-scrypt Params{..} (Salt salt) (Pass pass) =
-    PassHash <$> unsafePerformIO $
-        useAsCStringLen salt $ \(saltPtr, saltLen) ->
-        useAsCStringLen pass $ \(passPtr, passLen) ->
-        allocaBytes (fromIntegral bufLen) $ \bufPtr -> do
-            throwErrnoIfMinus1_ "crypto_scrypt" $ crypto_scrypt
-                (castPtr passPtr) (fromIntegral passLen)
-                (castPtr saltPtr) (fromIntegral saltLen)
-                (2^logN) (fromIntegral r) (fromIntegral p)
-                bufPtr (fromIntegral bufLen)
-            packCStringLen (castPtr bufPtr, fromIntegral bufLen)
-
-foreign import ccall unsafe crypto_scrypt
-    :: Ptr Word8 -> CSize         -- password
-    -> Ptr Word8 -> CSize         -- salt
-    -> Word64 -> Word32 -> Word32 -- N, r, p
-    -> Ptr Word8 -> CSize         -- result buffer
-    -> IO CInt
-
--- |Note the prime symbol (\'). Calls 'scrypt' with 'defaultParams'.
---
-scrypt' :: Salt -> Pass -> PassHash
-scrypt' = scrypt defaultParams
diff --git a/scrypt.cabal b/scrypt.cabal
--- a/scrypt.cabal
+++ b/scrypt.cabal
@@ -1,5 +1,5 @@
 name:           scrypt
-version:        0.3.2
+version:        0.3.3
 license:        BSD3
 license-file:   LICENSE
 category:       Cryptography
@@ -7,7 +7,6 @@
 author:         Falko Peters <falko.peters@gmail.com>
 maintainer:     Falko Peters <falko.peters@gmail.com>
 stability:      experimental
-tested-with:    GHC == 6.12.3
 cabal-version:  >= 1.8
 homepage:       http://github.com/informatikr/scrypt
 bug-reports:    http://github.com/informatikr/scrypt/issues
@@ -30,14 +29,18 @@
     cbits/sha256.h,
     cbits/sysendian.h
 
+source-repository head
+    type:     git
+    location: http://github.com/informatikr/scrypt
+
 library
     exposed-modules: Crypto.Scrypt
-
+    hs-source-dirs: src
     build-depends:
         base == 4.*,
-        base64-bytestring == 0.1.*,
+        base64-bytestring >= 0.1 && < 2,
         bytestring == 0.9.*,
-        entropy == 0.2
+        entropy == 0.2.*
 
     ghc-options: -Wall
     ghc-prof-options: -auto-all
@@ -47,7 +50,19 @@
     include-dirs: cbits
     includes: crypto_scrypt.h
     install-includes: crypto_scrypt.h
-    
-source-repository head
-    type:     git
-    location: http://github.com/informatikr/scrypt
+
+test-suite scrypt-test
+    type: exitcode-stdio-1.0
+    hs-source-dirs: test
+    main-is: Test.hs
+    build-depends:
+        scrypt,
+        base == 4.*,
+        bytestring == 0.9.*,
+        HUnit == 1.2.*,
+        QuickCheck == 2.*,
+        test-framework == 0.6.*,
+        test-framework-hunit == 0.2.*,
+        test-framework-quickcheck2 == 0.2.*
+    ghc-options: -Wall
+    ghc-prof-options: -auto-all
diff --git a/src/Crypto/Scrypt.hs b/src/Crypto/Scrypt.hs
new file mode 100644
--- /dev/null
+++ b/src/Crypto/Scrypt.hs
@@ -0,0 +1,238 @@
+{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings,
+    RecordWildCards, NamedFieldPuns #-}
+
+-- |Scrypt is a sequential memory-hard key derivation function. This module
+--  provides low-level bindings to the 'scrypt' key derivation function as
+--  well as a higher-level password-storage API. It is based on a fast C
+--  implementation of scrypt, written by Colin Percival. For further
+--  information see <http://www.tarsnap.com/scrypt.html>.
+--
+module Crypto.Scrypt (
+    -- * Parameters to the @scrypt@ function
+    -- $params
+     ScryptParams, scryptParams, defaultParams
+    -- * Password Storage
+    -- $password-storage
+    , EncryptedPass(..), encryptPass, encryptPass', verifyPass, verifyPass'
+    -- * Low-level bindings to the @scrypt@ key derivation function
+    -- $low-level
+    , Pass(..), Salt(..), PassHash(..), scrypt, scrypt'
+    ) where
+
+import Control.Applicative
+import qualified Data.ByteString.Base64 as Base64
+import Data.ByteString.Char8 hiding (map, concat)
+import Data.Maybe
+import Foreign (Ptr, Word8, Word32, Word64, allocaBytes, castPtr)
+import Foreign.C
+import System.Entropy (getEntropy)
+import System.IO.Unsafe (unsafePerformIO)
+
+
+newtype Pass          = Pass     { unPass :: ByteString } deriving (Show, Eq)
+newtype Salt          = Salt     { unSalt :: ByteString } deriving (Show, Eq)
+newtype PassHash      = PassHash { unHash :: ByteString } deriving (Show, Eq)
+newtype EncryptedPass =
+    EncryptedPass { unEncryptedPass  :: ByteString } deriving (Show, Eq)
+
+------------------------------------------------------------------------------
+-- $params
+--
+-- Scrypt takes three tuning parameters: @N@, @r@ and @p@. They affect running
+-- time and memory usage:
+--
+-- /Memory usage/ is approximately @128*r*N@ bytes. Note that the
+-- 'scryptParams' function takes @log_2(N)@ as a parameter. As an example,
+-- the 'defaultParams'
+--
+-- >   log_2(N) = 14, r = 8 and p = 1
+--
+-- lead to 'scrypt' using @128 * 8 * 2^14 = 16M@ bytes of memory.
+--
+-- /Running time/ is proportional to all of @N@, @r@ and @p@. Since it's
+-- influence on memory usage is small, @p@ can be used to independently tune
+-- the running time.
+--
+
+-- |Encapsulates the three tuning parameters to the 'scrypt' function: @N@,
+-- @r@ and @p@ (see above).
+--
+data ScryptParams = Params { logN, r, p, bufLen :: Integer} deriving (Eq)
+
+instance Show ScryptParams where
+    show Params{..} = concat [ "ScryptParams "
+        , "{ logN=", show logN
+        , ", r="   , show r
+        , ", p="   , show p
+        , " }"
+        ]
+
+-- |Constructor function for the 'ScryptParams' data type
+--
+scryptParams
+    :: Integer
+    -- ^ @log_2(N)@. Scrypt's @N@ parameter must be a power of two greater
+    --   than one, thus it's logarithm to base two must be greater than zero.
+    --   @128*r*N@ must be smaller than the available memory address space.
+    -> Integer
+    -- ^ The parameter @r@, must be greater than zero.
+    -> Integer
+    -- ^ The parameter @p@, must be greater than zero. @r@ and @p@
+    --   must satisfy @r*p < 2^30@.
+    -> Maybe ScryptParams
+    -- ^ Returns 'Just' the parameter object for valid arguments,
+    --   otherwise 'Nothing'.
+    --
+scryptParams logN r p | valid     = Just ps
+                      | otherwise = Nothing
+  where
+    ps    = Params { logN, r, p, bufLen = 64 }
+    valid = and [ logN > 0, r > 0, p > 0
+                , r*p < 2^(30 :: Int)
+                , bufLen ps <= 2^(32 :: Int)-1 * 32
+                -- allocation fits into (virtual) memory
+                , 128*r*2^logN <= fromIntegral (maxBound :: CSize)
+                ]
+
+-- |Default parameters as recommended in the scrypt paper:
+--
+--  >   N = 2^14, r = 8, p = 1
+--
+--  Equivalent to @'fromJust' ('scryptParams' 14 8 1)@.
+--
+defaultParams :: ScryptParams
+defaultParams = fromJust (scryptParams 14 8 1)
+
+------------------------------------------------------------------------------
+-- $password-storage
+--
+-- To allow storing encrypted passwords conveniently in a single database
+-- column, the password storage API provides the data type 'EncryptedPass'. It
+-- combines a 'Pass' as well as the 'Salt' and 'ScryptParams' used to compute
+-- it into a single 'ByteString', separated by pipe (\"|\") characters. The
+-- 'Salt' and 'PassHash' are base64-encoded. Storing the 'ScryptParams' with 
+-- the password allows to gradually strengthen password encryption in case of
+-- changing security requirements.
+--
+-- A usage example is given below, showing encryption, verification and
+-- changing 'ScryptParams':
+--
+-- > >>> encrypted <- encryptPass defaultParams (Pass "secret")
+-- > >>> print encrypted
+-- > EncryptedPass {unEncryptedPass = "14|8|1|Wn5x[SNIP]nM=|Zl+p[SNIP]g=="}
+-- > >>> print $ verifyPass defaultParams (Pass "secret") encrypted
+-- > (True,Nothing)
+-- > >>> print $ verifyPass defaultParams (Pass "wrong") encrypted
+-- > (False,Nothing)
+-- > >>> let newParams = fromJust $ scryptParams 16 8 1
+-- > >>> print $ verifyPass newParams (Pass "secret") encrypted
+-- > (True,Just (EncryptedPass {unEncryptedPass = "16|8|1|Wn5x[SNIP]nM=|ZmWw[SNIP]Q=="}))
+--
+
+combine :: ScryptParams -> Salt -> PassHash -> EncryptedPass
+combine Params{..} (Salt salt) (PassHash passHash) =
+    EncryptedPass $ intercalate "|"
+        [ showBS logN, showBS r, showBS p
+        , Base64.encode salt, Base64.encode passHash]
+  where
+    showBS = pack . show
+
+separate :: EncryptedPass -> Maybe (ScryptParams, Salt, PassHash)
+separate = go . split '|' . unEncryptedPass
+  where
+    go [logN', r', p', salt', hash'] = do
+        [salt, hash] <- mapM decodeBase64 [salt', hash']
+        [logN, r, p] <- mapM (fmap fst . readInteger) [logN', r', p']
+        params       <- scryptParams logN r p
+        return (params, Salt salt, PassHash hash)
+    go _         = Nothing
+    decodeBase64 = either (const Nothing) Just . Base64.decode
+
+-- |Encrypt the password with the given parameters and a random 32-byte salt.
+-- The salt is read from @\/dev\/urandom@ on Unix systems or @CryptoAPI@ on
+-- Windows.
+--
+encryptPass :: ScryptParams -> Pass -> IO EncryptedPass
+encryptPass params pass = do
+    salt <- Salt <$> getEntropy 32
+    return $ combine params salt (scrypt params salt pass)
+
+-- |Equivalent to @encryptPass defaultParams@.
+--
+encryptPass' :: Pass -> IO EncryptedPass
+encryptPass' = encryptPass defaultParams
+
+-- |Verify a 'Pass' against an 'EncryptedPass'. The function also takes
+--  'ScryptParams' meeting your current security requirements. In case the
+--  'EncryptedPass' was generated with different parameters, the function
+--  returns an updated 'EncryptedPass', generated with the given 
+--  'ScryptParams'. The 'Salt' is kept from the given 'EncryptedPass'.
+--
+verifyPass
+    :: ScryptParams
+    -- ^ Parameters to use for updating the 'EncryptedPass'.
+    -> Pass
+    -- ^ The candidate 'Pass'.
+    -> EncryptedPass
+    -- ^ The 'EncryptedPass' to check against.
+    -> (Bool, Maybe EncryptedPass)
+    -- ^ Returns a pair of
+    --
+    --     * 'Bool' indicating verification success or failure.
+    --
+    --     * 'Just' a /new/ 'EncryptedPass' if the given 'ScryptParams' are
+    --      different from those encapsulated in the /given/ 'EncryptedPass',
+    --      otherwise 'Nothing'.
+    --
+verifyPass newParams candidate encrypted =
+    maybe (False, Nothing) verify (separate encrypted)
+  where
+    verify (params,salt,hash) =
+        let valid   = scrypt params salt candidate == hash
+            newHash = scrypt newParams salt candidate
+            newEncr = if not valid || params == newParams
+                        then Nothing
+                        else Just (combine newParams salt newHash)
+        in (valid, newEncr)
+
+-- |Check the 'Pass' against the 'EncryptedPass', using the 'ScryptParams'
+--  encapsulated in the 'EncryptedPass'.
+--
+verifyPass' :: Pass -> EncryptedPass -> Bool
+-- We never evaluate an eventual new 'EncryptedPass' from 'verifyPass', so it is
+-- safe to pass 'undefined' to verifyPass.
+verifyPass' pass encrypted = fst $ verifyPass undefined pass encrypted
+
+------------------------------------------------------------------------------
+-- $low-level
+--
+-- Bindings to a fast C implementation of 'scrypt'. For password storage,
+-- consider using the more convenient higher-level API above.
+--
+
+-- |Calculates a 64-byte hash from the given password, salt and parameters.
+--
+scrypt :: ScryptParams -> Salt -> Pass -> PassHash
+scrypt Params{..} (Salt salt) (Pass pass) =
+    PassHash <$> unsafePerformIO $
+        useAsCStringLen salt $ \(saltPtr, saltLen) ->
+        useAsCStringLen pass $ \(passPtr, passLen) ->
+        allocaBytes (fromIntegral bufLen) $ \bufPtr -> do
+            throwErrnoIfMinus1_ "crypto_scrypt" $ crypto_scrypt
+                (castPtr passPtr) (fromIntegral passLen)
+                (castPtr saltPtr) (fromIntegral saltLen)
+                (2^logN) (fromIntegral r) (fromIntegral p)
+                bufPtr (fromIntegral bufLen)
+            packCStringLen (castPtr bufPtr, fromIntegral bufLen)
+
+foreign import ccall unsafe crypto_scrypt
+    :: Ptr Word8 -> CSize         -- password
+    -> Ptr Word8 -> CSize         -- salt
+    -> Word64 -> Word32 -> Word32 -- N, r, p
+    -> Ptr Word8 -> CSize         -- result buffer
+    -> IO CInt
+
+-- |Note the prime symbol (\'). Calls 'scrypt' with 'defaultParams'.
+--
+scrypt' :: Salt -> Pass -> PassHash
+scrypt' = scrypt defaultParams
diff --git a/test/Test.hs b/test/Test.hs
new file mode 100644
--- /dev/null
+++ b/test/Test.hs
@@ -0,0 +1,119 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main where
+
+import Control.Applicative ((<$>))
+import Crypto.Scrypt
+import Data.ByteString (pack)
+import Data.Maybe
+import Test.Framework (Test, defaultMain, testGroup)
+import Test.Framework.Providers.HUnit (testCase)
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+import Test.HUnit ((@=?))
+import Test.QuickCheck
+import Test.QuickCheck.Property (morallyDubiousIOProperty)
+
+instance Arbitrary ScryptParams where
+    arbitrary = do
+        logN <- elements [1..14]
+        r    <- elements [1..8]
+        p    <- elements [1..2]
+        frequency
+            [(  1, return defaultParams)
+            ,(100, return . fromJust $ scryptParams logN r p)
+            ]
+
+instance Arbitrary Pass where
+    arbitrary = Pass . pack <$> listOf (arbitrary `suchThat` (/= 0))
+
+
+main :: IO ()
+main = defaultMain
+    [ testGroup "Test Vectors" testVectors
+    , testGroup "Properties"
+        [ testProperty "wrong pass is invalid" prop_WrongPassNotValid
+        , testProperty "encrypt/verify" prop_EncryptVerify
+        , testProperty "encrypt/verify'" prop_EncryptVerify'
+        , testProperty "new params ==> new encryption" prop_NewParamsNewEncr
+        ]
+    ]
+
+prop_WrongPassNotValid :: Pass -> Pass -> ScryptParams -> Property
+prop_WrongPassNotValid pass candidate params =
+    pass /= candidate ==> morallyDubiousIOProperty $ do
+        encr <- encryptPass params pass
+        let (valid, newEncr) = verifyPass params candidate encr
+        return $ not valid && isNothing newEncr
+
+prop_EncryptVerify :: Pass -> ScryptParams -> Property
+prop_EncryptVerify pass params =
+    morallyDubiousIOProperty $ do
+        encr <- encryptPass params pass
+        let (valid, newEncr) = verifyPass params pass encr
+        return $ valid && isNothing newEncr
+
+prop_EncryptVerify' :: Pass -> ScryptParams -> Property
+prop_EncryptVerify' pass params =
+    morallyDubiousIOProperty $ do
+        encr <- encryptPass params pass
+        return $ verifyPass' pass encr
+
+prop_NewParamsNewEncr :: Pass -> ScryptParams -> ScryptParams -> Property
+prop_NewParamsNewEncr pass oldParams newParams =
+    oldParams /= newParams ==> morallyDubiousIOProperty $ do
+        encr <- encryptPass oldParams pass        
+        let (valid,newEncr) = verifyPass newParams pass encr
+        return $ valid && isJust newEncr && fromJust newEncr /= encr
+
+-- |Test vectors from the scrypt paper.
+--
+testVectors :: [Test]
+testVectors = map toTestCase vecs
+  where
+    toTestCase (pass, salt, logN, r, p, dk) =
+        let params = fromJust $ scryptParams logN r p
+        in testCase (unwords ["vec:", show pass, show salt, show params]) $
+                PassHash (pack dk) @=? scrypt params (Salt salt) (Pass pass)
+    
+    vecs =
+        [( "", "", 4, 1, 1,
+            [ 0x77, 0xd6, 0x57, 0x62, 0x38, 0x65, 0x7b, 0x20
+            , 0x3b, 0x19, 0xca, 0x42, 0xc1, 0x8a, 0x04, 0x97
+            , 0xf1, 0x6b, 0x48, 0x44, 0xe3, 0x07, 0x4a, 0xe8
+            , 0xdf, 0xdf, 0xfa, 0x3f, 0xed, 0xe2, 0x14, 0x42
+            , 0xfc, 0xd0, 0x06, 0x9d, 0xed, 0x09, 0x48, 0xf8
+            , 0x32, 0x6a, 0x75, 0x3a, 0x0f, 0xc8, 0x1f, 0x17
+            , 0xe8, 0xd3, 0xe0, 0xfb, 0x2e, 0x0d, 0x36, 0x28
+            , 0xcf, 0x35, 0xe2, 0x0c, 0x38, 0xd1, 0x89, 0x06
+            ])
+        ,("password", "NaCl", 10, 8, 16,
+            [ 0xfd, 0xba, 0xbe, 0x1c, 0x9d, 0x34, 0x72, 0x00
+            , 0x78, 0x56, 0xe7, 0x19, 0x0d, 0x01, 0xe9, 0xfe
+            , 0x7c, 0x6a, 0xd7, 0xcb, 0xc8, 0x23, 0x78, 0x30
+            , 0xe7, 0x73, 0x76, 0x63, 0x4b, 0x37, 0x31, 0x62
+            , 0x2e, 0xaf, 0x30, 0xd9, 0x2e, 0x22, 0xa3, 0x88
+            , 0x6f, 0xf1, 0x09, 0x27, 0x9d, 0x98, 0x30, 0xda
+            , 0xc7, 0x27, 0xaf, 0xb9, 0x4a, 0x83, 0xee, 0x6d
+            , 0x83, 0x60, 0xcb, 0xdf, 0xa2, 0xcc, 0x06, 0x40
+            ])
+        ,("pleaseletmein", "SodiumChloride", 14, 8, 1,
+            [ 0x70, 0x23, 0xbd, 0xcb, 0x3a, 0xfd, 0x73, 0x48
+            , 0x46, 0x1c, 0x06, 0xcd, 0x81, 0xfd, 0x38, 0xeb
+            , 0xfd, 0xa8, 0xfb, 0xba, 0x90, 0x4f, 0x8e, 0x3e
+            , 0xa9, 0xb5, 0x43, 0xf6, 0x54, 0x5d, 0xa1, 0xf2
+            , 0xd5, 0x43, 0x29, 0x55, 0x61, 0x3f, 0x0f, 0xcf
+            , 0x62, 0xd4, 0x97, 0x05, 0x24, 0x2a, 0x9a, 0xf9
+            , 0xe6, 0x1e, 0x85, 0xdc, 0x0d, 0x65, 0x1e, 0x40
+            , 0xdf, 0xcf, 0x01, 0x7b, 0x45, 0x57, 0x58, 0x87
+            ])
+        -- ,("pleaseletmein", "SodiumChloride", 20, 8, 1,
+        --      [ 0x21, 0x01, 0xcb, 0x9b, 0x6a, 0x51, 0x1a, 0xae
+        --      , 0xad, 0xdb, 0xbe, 0x09, 0xcf, 0x70, 0xf8, 0x81
+        --      , 0xec, 0x56, 0x8d, 0x57, 0x4a, 0x2f, 0xfd, 0x4d
+        --      , 0xab, 0xe5, 0xee, 0x98, 0x20, 0xad, 0xaa, 0x47
+        --      , 0x8e, 0x56, 0xfd, 0x8f, 0x4b, 0xa5, 0xd0, 0x9f
+        --      , 0xfa, 0x1c, 0x6d, 0x92, 0x7c, 0x40, 0xf4, 0xc3
+        --      , 0x37, 0x30, 0x40, 0x49, 0xe8, 0xa9, 0x52, 0xfb
+        --      , 0xcb, 0xf4, 0x5c, 0x6f, 0xa7, 0x7a, 0x41, 0xa4
+        --      ])
+        ]
