diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,11 @@
 # Changelog for `password`
 
+## 3.0.2.0
+
+-   Add `extractParams` on `PasswordHash`s
+    Thanks to [@blackheaven](https://github.com/blackheaven)
+    [#61](https://github.com/cdepillabout/password/pull/61)
+
 ## 3.0.1.0
 
 -   Argon2 hashes without a version field are interpreted as being of version 1.0
diff --git a/password.cabal b/password.cabal
--- a/password.cabal
+++ b/password.cabal
@@ -1,7 +1,7 @@
 cabal-version: 1.12
 
 name:           password
-version:        3.0.1.0
+version:        3.0.2.0
 category:       Data
 synopsis:       Hashing and checking of passwords
 description:
@@ -71,9 +71,9 @@
   build-depends:
       base        >= 4.9      && < 5
     , base64      >= 0.3      && < 0.5
-    , bytestring  >= 0.10.8.1 && < 0.11
-    , cryptonite  >= 0.15.1   && < 0.30
-    , memory      >= 0.14     && < 0.17
+    , bytestring  >= 0.10.8.1 && < 0.12
+    , cryptonite  >= 0.15.1
+    , memory      >= 0.14
     , password-types             < 2
     , template-haskell
     , text        >= 1.2.2    && < 1.3
diff --git a/src/Data/Password/Argon2.hs b/src/Data/Password/Argon2.hs
--- a/src/Data/Password/Argon2.hs
+++ b/src/Data/Password/Argon2.hs
@@ -52,6 +52,7 @@
   -- * Hashing Manually (Argon2)
   , hashPasswordWithParams
   , defaultParams
+  , extractParams
   , Argon2Params(..)
   , Argon2.Variant(..)
   , Argon2.Version(..)
@@ -81,7 +82,7 @@
 import Data.Semigroup ((<>))
 #endif
 import Data.Text (Text)
-import qualified Data.Text as T (intercalate, split, splitAt, stripPrefix)
+import qualified Data.Text as T (dropWhileEnd, intercalate, split, splitAt, stripPrefix)
 import Data.Word (Word32)
 
 import Data.Password.Internal (
@@ -91,7 +92,6 @@
     showT,
     toBytes,
     unsafePad64,
-    unsafeRemovePad64,
  )
 import Data.Password.Types (
     Password,
@@ -205,8 +205,7 @@
     , encodeWithoutPadding key
     ]
   where
-    encodeWithoutPadding bs =
-        unsafeRemovePad64 (B.length bs) $ encodeBase64 bs
+    encodeWithoutPadding = T.dropWhileEnd (== '=') . encodeBase64
     parameters = T.intercalate ","
         [ "m=" <> showT argon2MemoryCost
         , "t=" <> showT argon2TimeCost
@@ -274,14 +273,17 @@
 --
 -- prop> \(Blind badpass) -> let correctPasswordHash = hashPasswordWithSalt testParams salt "foobar" in checkPassword badpass correctPasswordHash == PasswordCheckFail
 checkPassword :: Password -> PasswordHash Argon2 -> PasswordCheck
-checkPassword pass (PasswordHash passHash) =
+checkPassword pass passHash =
   fromMaybe PasswordCheckFail $ do
-    let paramList = T.split (== '$') passHash
-    (argon2Params, salt, hashedKey) <- parseArgon2Params paramList
+    (argon2Params, salt, hashedKey) <- parseArgon2PasswordHashParams passHash
     let producedKey = hashPasswordWithSalt' argon2Params salt pass
     guard $ hashedKey `constEq` producedKey
     return PasswordCheckSuccess
 
+parseArgon2PasswordHashParams :: PasswordHash Argon2 -> Maybe (Argon2Params, Salt Argon2, ByteString)
+parseArgon2PasswordHashParams (PasswordHash passHash) =
+  parseArgon2Params $ T.split (== '$') passHash
+
 parseArgon2Params :: [Text] -> Maybe (Argon2Params, Salt Argon2, ByteString)
 -- vp - version or params
 -- ps - params or salt
@@ -309,7 +311,7 @@
     salt <- from64 $ unsafePad64 salt64
     hashedKey <- from64 $ unsafePad64 hashedKey64
     let argon2OutputLength = fromIntegral $ B.length hashedKey -- only here because of warnings
-        argon2Salt = 16 -- only here because of warnings
+        argon2Salt = fromIntegral $ B.length salt
     pure (Argon2Params{..}, Salt salt, hashedKey)
   where
     parseParameters paramsT = do
@@ -325,6 +327,20 @@
             ("t=", i) -> go xs (m, readT i, p)
             ("p=", i) -> go xs (m, t, readT i)
             _ -> Nothing
+
+-- | Extracts 'Argon2Params' from a 'PasswordHash' 'Argon2'.
+--
+-- Returns 'Just Argon2Params' on success.
+--
+-- >>> let pass = mkPassword "foobar"
+-- >>> passHash <- hashPassword pass
+-- >>> extractParams passHash == Just defaultParams
+-- True
+--
+-- @since 3.0.2.0
+extractParams :: PasswordHash Argon2 -> Maybe Argon2Params
+extractParams passHash =
+  (\(params, _, _) -> params) <$> parseArgon2PasswordHashParams passHash
 
 -- | Strips the given 'match' if it matches and uses
 --   the function on the remainder of the given text.
diff --git a/src/Data/Password/Bcrypt.hs b/src/Data/Password/Bcrypt.hs
--- a/src/Data/Password/Bcrypt.hs
+++ b/src/Data/Password/Bcrypt.hs
@@ -45,6 +45,7 @@
   , PasswordCheck(..)
   -- * Hashing Manually (bcrypt)
   , hashPasswordWithParams
+  , extractParams
   -- ** Hashing with salt (DISADVISED)
   --
   -- | Hashing with a set 'Salt' is almost never what you want
@@ -59,9 +60,12 @@
     -- $setup
   ) where
 
+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 Text.Read (readMaybe)
 
 import Data.Password.Types (
     Password
@@ -182,6 +186,22 @@
         (toBytes passHash)
       then PasswordCheckSuccess
       else PasswordCheckFail
+
+-- | Extracts the cost parameter as an 'Int' from a 'PasswordHash' 'Bcrypt'
+--
+-- >>> let pass = mkPassword "foobar"
+-- >>> passHash <- hashPassword pass
+-- >>> extractParams passHash == Just 10
+-- True
+--
+-- @since 3.0.2.0
+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"]
+      readMaybe $ T.unpack cost
+    _ -> Nothing
 
 -- | Generate a random 16-byte @bcrypt@ salt
 --
diff --git a/src/Data/Password/Internal.hs b/src/Data/Password/Internal.hs
--- a/src/Data/Password/Internal.hs
+++ b/src/Data/Password/Internal.hs
@@ -18,9 +18,10 @@
   , fromBytes
   , from64
   , unsafePad64
-  , unsafeRemovePad64
   , readT
   , showT
+  , -- * Setup for doctests.
+    -- $setup
   ) where
 
 import Control.Monad.IO.Class (MonadIO(liftIO))
@@ -33,7 +34,6 @@
 #endif
 import Data.Text as T (
     Text,
-    dropEnd,
     length,
     pack,
     replicate,
@@ -43,9 +43,26 @@
 import Data.Text.Encoding (decodeUtf8, encodeUtf8)
 import Text.Read (readMaybe)
 
+-- $setup
+-- >>> import Data.ByteString as B (length)
+-- >>> import Data.ByteString.Base64 (encodeBase64)
+-- >>> import Data.Text as T (dropWhileEnd)
+-- >>> import Data.Word (Word16)
+-- >>> import Test.QuickCheck (ioProperty, quickCheck, (===))
+-- >>> import Test.QuickCheck.Instances.ByteString()
 
 -- | Generate a random x-byte-long salt.
 --
+-- >>> :{
+-- quickCheck $ \w ->
+--   ioProperty $ do
+--     let i :: Num a => a
+--         i = fromIntegral (w :: Word16)
+--     Salt bs <- newSalt i
+--     pure $ B.length bs === i
+-- :}
+-- +++ OK, passed 100 tests.
+--
 -- @since 2.0.0.0
 newSalt :: MonadIO m => Int -> m (Salt a)
 newSalt i = liftIO $ Salt <$> getRandomBytes i
@@ -90,6 +107,8 @@
 {-# INLINE showT #-}
 
 -- | (UNSAFE) Pad a base64 text to "length `rem` 4 == 0" with "="
+--
+-- prop> \bs -> let b64 = encodeBase64 bs in unsafePad64 (T.dropWhileEnd (== '=') b64) == b64
 unsafePad64 :: Text -> Text
 unsafePad64 t
     | remains == 0 = t
@@ -97,16 +116,3 @@
   where
     remains = T.length t `rem` 4
     pad = T.replicate (4 - remains) "="
-
--- | (UNSAFE) Removes the "=" padding from a base64 text
--- given the length of the original bytestring.
-unsafeRemovePad64 :: Int -> Text -> Text
-unsafeRemovePad64 bsLen = T.dropEnd drops
-  where
-    drops = case bsLen `rem` 3 of
-        -- 1 extra byte results in 2 characters (4 - 2 = 2)
-        1 -> 2
-        -- 2 extra bytes results in 3 characters (4 - 3 = 1)
-        2 -> 1
-        -- This will just be 0
-        other -> other
diff --git a/src/Data/Password/PBKDF2.hs b/src/Data/Password/PBKDF2.hs
--- a/src/Data/Password/PBKDF2.hs
+++ b/src/Data/Password/PBKDF2.hs
@@ -51,6 +51,7 @@
   -- * Hashing Manually (PBKDF2)
   , hashPasswordWithParams
   , defaultParams
+  , extractParams
   , PBKDF2Params(..)
   , PBKDF2Algorithm(..)
   -- ** Hashing with salt (DISADVISED)
@@ -233,8 +234,15 @@
 --
 -- prop> \(Blind badpass) -> let correctPasswordHash = hashPasswordWithSalt testParams salt "foobar" in checkPassword badpass correctPasswordHash == PasswordCheckFail
 checkPassword :: Password -> PasswordHash PBKDF2 -> PasswordCheck
-checkPassword pass (PasswordHash passHash) =
+checkPassword pass passHash =
   fromMaybe PasswordCheckFail $ do
+    (params, salt, hashedKey) <- parsePBKDF2PasswordHashParams passHash
+    let producedKey = hashPasswordWithSalt' params salt pass
+    guard $ hashedKey `constEq` producedKey
+    return PasswordCheckSuccess
+
+parsePBKDF2PasswordHashParams :: PasswordHash PBKDF2 -> Maybe (PBKDF2Params, Salt PBKDF2, ByteString)
+parsePBKDF2PasswordHashParams (PasswordHash passHash) = do
     -- This step makes it possible to also check the following format:
     -- "pbkdf2:sha256:150000:etc.etc."
     let passHash' = fromMaybe passHash $ "pbkdf2:" `T.stripPrefix` passHash
@@ -249,11 +257,22 @@
     salt <- from64 salt64
     hashedKey <- from64 hashedKey64
     let pbkdf2OutputLength = fromIntegral $ C8.length hashedKey
-        producedKey = hashPasswordWithSalt' PBKDF2Params{..} (Salt salt) pass
-    guard $ hashedKey `constEq` producedKey
-    return PasswordCheckSuccess
-  where
-    pbkdf2Salt = 16
+        pbkdf2Salt = fromIntegral $ C8.length salt
+    return (PBKDF2Params{..}, Salt salt, hashedKey)
+
+-- | Extracts 'PBKDF2Params' from a 'PasswordHash' 'PBKDF2'.
+--
+-- Returns 'Just PBKDF2Params' on success.
+--
+-- >>> let pass = mkPassword "foobar"
+-- >>> passHash <- hashPassword pass
+-- >>> extractParams passHash == Just defaultParams
+-- True
+--
+-- @since 3.0.2.0
+extractParams :: PasswordHash PBKDF2 -> Maybe PBKDF2Params
+extractParams passHash =
+  (\(params, _, _) -> params) <$> parsePBKDF2PasswordHashParams passHash
 
 
 -- | Type of algorithm to use for hashing PBKDF2 passwords.
diff --git a/src/Data/Password/Scrypt.hs b/src/Data/Password/Scrypt.hs
--- a/src/Data/Password/Scrypt.hs
+++ b/src/Data/Password/Scrypt.hs
@@ -46,6 +46,7 @@
   -- * Hashing Manually (scrypt)
   , hashPasswordWithParams
   , defaultParams
+  , extractParams
   , ScryptParams(..)
   -- ** Hashing with salt (DISADVISED)
   --
@@ -243,8 +244,15 @@
 --
 -- prop> \(Blind badpass) -> let correctPasswordHash = hashPasswordWithSalt testParams salt "foobar" in checkPassword badpass correctPasswordHash == PasswordCheckFail
 checkPassword :: Password -> PasswordHash Scrypt -> PasswordCheck
-checkPassword pass (PasswordHash passHash) =
+checkPassword pass passHash =
   fromMaybe PasswordCheckFail $ do
+    (params, salt, hashedKey) <- parseScryptPasswordHashParams passHash
+    let producedKey = hashPasswordWithSalt' params salt pass
+    guard $ hashedKey `constEq` producedKey
+    return PasswordCheckSuccess
+
+parseScryptPasswordHashParams :: PasswordHash Scrypt -> Maybe (ScryptParams, Salt Scrypt, ByteString)
+parseScryptPasswordHashParams (PasswordHash passHash) = do
     let paramList = T.split (== '|') passHash
     guard $ length paramList == 5
     let [ scryptRoundsT,
@@ -258,11 +266,22 @@
     salt <- from64 salt64
     hashedKey <- from64 hashedKey64
     let scryptOutputLength = fromIntegral $ C8.length hashedKey
-        producedKey = hashPasswordWithSalt' ScryptParams{..} (Salt salt) pass
-    guard $ hashedKey `constEq` producedKey
-    return PasswordCheckSuccess
-  where
-    scryptSalt = 32 -- only here because of warnings
+        scryptSalt = fromIntegral $ C8.length salt
+    return (ScryptParams{..}, Salt salt, hashedKey)
+
+-- | Extracts 'ScryptParams' from a 'PasswordHash' 'Scrypt'.
+--
+-- Returns 'Just ScryptParams' on success.
+--
+-- >>> let pass = mkPassword "foobar"
+-- >>> passHash <- hashPassword pass
+-- >>> extractParams passHash == Just defaultParams
+-- True
+--
+-- @since 3.0.2.0
+extractParams :: PasswordHash Scrypt -> Maybe ScryptParams
+extractParams passHash =
+  (\(params, _, _) -> params) <$> parseScryptPasswordHashParams passHash
 
 -- | Generate a random 32-byte @scrypt@ salt
 --
diff --git a/test/tasty/Argon2.hs b/test/tasty/Argon2.hs
--- a/test/tasty/Argon2.hs
+++ b/test/tasty/Argon2.hs
@@ -12,25 +12,26 @@
 testArgon2 :: TestTree
 testArgon2 = testGroup "Argon2"
   [ referenceTest
-  , testCorrectPassword "Argon2 (hashPassword)" hashFast checkPassword
+  , testCorrectPassword "Argon2 (hashPassword, fast)" hashFast checkPassword extractParams fastParams
+  , testCorrectPassword "Argon2 (hashPassword, slow)" hashSlow checkPassword extractParams slowParams
   , testIncorrectPassword "Argon2 (hashPassword) fail" hashFast checkPassword
   , testWithSalt "Argon2 (hashPasswordWithSalt)"
                  (hashPasswordWithSalt fastParams)
                  checkPassword
-  , testWithParams "Argon2 (Argon2i)" $ fastParams{ argon2Variant = Argon2i }
-  , testWithParams "Argon2 (Argon2d)" $ fastParams{ argon2Variant = Argon2d }
+                 extractParams
+                 fastParams
+  , testWithParams "Argon2 (Argon2i)" (fastParams{ argon2Variant = Argon2i })
+  , testWithParams "Argon2 (Argon2d)" (fastParams{ argon2Variant = Argon2d })
   , paddingTests
   , omittedVersionTest
   ]
   where
     testWithParams s params =
-      testWithSalt s (hashPasswordWithSalt params) checkPassword
+      testWithSalt s (hashPasswordWithSalt params) checkPassword extractParams params
     hashFast = hashPasswordWithParams fastParams
-    fastParams =
-      defaultParams{
-        argon2MemoryCost = 2 ^ (8 :: Int),
-        argon2TimeCost = 1
-      }
+    fastParams = defaultParams{ argon2MemoryCost = 2 ^ (8 :: Int), argon2TimeCost = 1 }
+    hashSlow = hashPasswordWithParams slowParams
+    slowParams = defaultParams{ argon2MemoryCost = 2 ^ (8 :: Int), argon2TimeCost = 4 }
 
 paddingTests :: TestTree
 paddingTests = testGroup "Padding"
diff --git a/test/tasty/Bcrypt.hs b/test/tasty/Bcrypt.hs
--- a/test/tasty/Bcrypt.hs
+++ b/test/tasty/Bcrypt.hs
@@ -4,12 +4,13 @@
 
 import Data.Password.Bcrypt
 
-import Internal (testCorrectPassword, testIncorrectPassword, testWithSalt)
+import Internal
 
 
 testBcrypt :: TestTree
 testBcrypt = testGroup "bcrypt"
-  [ testCorrectPassword "Bcrypt (hashPassword)" (hashPasswordWithParams 4) checkPassword
+  [ testCorrectPassword "Bcrypt (hashPassword)" (hashPasswordWithParams 4) checkPassword extractParams 4
+  , testCorrectPassword "Bcrypt (hashPassword 2)" (hashPasswordWithParams 5) checkPassword extractParams 5
   , testIncorrectPassword "Bcrypt (hashPassword) fail" (hashPasswordWithParams 4) checkPassword
-  , testWithSalt "Bcrypt (hashPasswordWithSalt)" (hashPasswordWithSalt 4) checkPassword
+  , testWithSalt "Bcrypt (hashPasswordWithSalt)" (hashPasswordWithSalt 4) checkPassword extractParams 4
   ]
diff --git a/test/tasty/Internal.hs b/test/tasty/Internal.hs
--- a/test/tasty/Internal.hs
+++ b/test/tasty/Internal.hs
@@ -11,15 +11,18 @@
 import Data.Password.Bcrypt (PasswordCheck(..), Salt(..))
 
 
-testCorrectPassword :: String
+testCorrectPassword :: (Eq params, Show params)
+                    => String
                     -> (Password -> IO (PasswordHash a))
                     -> (Password -> PasswordHash a -> PasswordCheck)
+                    -> (PasswordHash a -> Maybe params)
+                    -> params
                     -> TestTree
-testCorrectPassword s hashF checkF = testProperty s $
+testCorrectPassword s hashF checkF extractParamsF params = testProperty s $
   \pass -> ioProperty $ do
     let pw = mkPassword pass
     hpw <- hashF pw
-    return $ checkF pw hpw === PasswordCheckSuccess
+    return $ (checkF pw hpw === PasswordCheckSuccess) .&&. extractParamsF hpw === Just params
 
 testIncorrectPassword :: String
                       -> (Password -> IO (PasswordHash a))
@@ -35,15 +38,18 @@
   where
     isEmpty c = c `elem` ["", "\NUL"]
 
-testWithSalt :: String
+testWithSalt :: (Eq params, Show params)
+             => String
              -> (Salt a -> Password -> PasswordHash a)
              -> (Password -> PasswordHash a -> PasswordCheck)
+             -> (PasswordHash a -> Maybe params)
+             -> params
              -> TestTree
-testWithSalt s hashWithSalt checkF = testProperty s $
+testWithSalt s hashWithSalt checkF extractParamsF params = testProperty s $
   \pass salt ->
     let pw = mkPassword pass
         hpw = hashWithSalt salt pw
-    in checkF pw hpw === PasswordCheckSuccess
+    in (checkF pw hpw === PasswordCheckSuccess) .&&. extractParamsF hpw === Just params
 
 instance Arbitrary (Salt a) where
   arbitrary = Salt . pack <$> vector 16
diff --git a/test/tasty/PBKDF2.hs b/test/tasty/PBKDF2.hs
--- a/test/tasty/PBKDF2.hs
+++ b/test/tasty/PBKDF2.hs
@@ -28,9 +28,11 @@
       "PBKDF2 (hashPasswordWithSalt)"
       (hashPasswordWithSalt testParams)
       checkPassword
-  , testIt "PBKDF2 (md5)"    (defaultParams{ pbkdf2Algorithm = PBKDF2_MD5, pbkdf2Iterations = 1000 })
-  , testIt "PBKDF2 (sha1)"   (testParams{ pbkdf2Algorithm = PBKDF2_SHA1 })
-  , testIt "PBKDF2 (sha256)" (testParams{ pbkdf2Algorithm = PBKDF2_SHA256 })
+      extractParams
+      testParams
+  , testIt "PBKDF2 (md5)"    (defaultParams{ pbkdf2Algorithm = PBKDF2_MD5, pbkdf2Iterations = 1000, pbkdf2OutputLength = 16 })
+  , testIt "PBKDF2 (sha1)"   (testParams{ pbkdf2Algorithm = PBKDF2_SHA1, pbkdf2OutputLength = 20 })
+  , testIt "PBKDF2 (sha256)" (testParams{ pbkdf2Algorithm = PBKDF2_SHA256, pbkdf2OutputLength = 32 })
   , testFast Crypto.SHA1   20 PBKDF2.fastPBKDF2_SHA1
   , testFast Crypto.SHA256 32 PBKDF2.fastPBKDF2_SHA256
   , testFast Crypto.SHA512 64 PBKDF2.fastPBKDF2_SHA512
@@ -39,9 +41,11 @@
       "PBKDF2 (pbkdf2:sha-...)"
       (hashPasswordWithParams testParams)
       (\pass (PasswordHash hash) -> checkPassword pass . PasswordHash $ "pbkdf2:" <> hash)
+      extractParams
+      testParams
   ]
   where
-    testIt s params = testCorrectPassword s (hashPasswordWithParams params) checkPassword
+    testIt s params = testCorrectPassword s (hashPasswordWithParams params) checkPassword extractParams params
     testParams = defaultParams{ pbkdf2Iterations = 4 * 1000 }
 
 testFast :: (HashAlgorithm a, Show a)
diff --git a/test/tasty/Scrypt.hs b/test/tasty/Scrypt.hs
--- a/test/tasty/Scrypt.hs
+++ b/test/tasty/Scrypt.hs
@@ -16,15 +16,21 @@
 
 testScrypt :: TestTree
 testScrypt = testGroup "scrypt"
-  [ testCorrectPassword "Scrypt (hashPassword)" hash8Rounds checkPassword
+  [ 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 defaultParams{ scryptRounds = 8 })
+                 (hashPasswordWithSalt testsParams8Rounds)
                  checkPassword
+                 extractParams
+                 testsParams8Rounds
   , testProperty "scrypt <-> cryptonite" $ withMaxSuccess 10 checkScrypt
   ]
   where
-    hash8Rounds = hashPasswordWithParams defaultParams{ scryptRounds = 8 }
+    hash8Rounds = hashPasswordWithParams testsParams8Rounds
+    testsParams8Rounds = defaultParams{ scryptRounds = 8, scryptSalt = 16 }
+    hash4Rounds = hashPasswordWithParams testsParams4Rounds
+    testsParams4Rounds = defaultParams{ scryptRounds = 4, scryptSalt = 16 }
 
 checkScrypt :: Text -> Property
 checkScrypt pass = ioProperty $ do
