diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,7 @@
+## 1.4.18
+
+* Expose Yesod.Auth.Util.PasswordStore
+
 ## 1.4.17.3
 
 * Some translation fixes
diff --git a/Yesod/Auth/Email.hs b/Yesod/Auth/Email.hs
--- a/Yesod/Auth/Email.hs
+++ b/Yesod/Auth/Email.hs
@@ -116,7 +116,7 @@
 import qualified Yesod.Auth.Message       as Msg
 import           Yesod.Core
 import           Yesod.Form
-import qualified Yesod.PasswordStore      as PS
+import qualified Yesod.Auth.Util.PasswordStore as PS
 import           Control.Applicative      ((<$>), (<*>))
 import qualified Crypto.Hash              as H
 import qualified Crypto.Nonce             as Nonce
diff --git a/Yesod/Auth/Message.hs b/Yesod/Auth/Message.hs
--- a/Yesod/Auth/Message.hs
+++ b/Yesod/Auth/Message.hs
@@ -19,6 +19,7 @@
     , russianMessage
     , dutchMessage
     , danishMessage
+    , koreanMessage
     ) where
 
 import           Data.Monoid (mappend, (<>))
@@ -784,3 +785,50 @@
 danishMessage Logout = "Log ud"
 danishMessage LogoutTitle = "Log ud"
 danishMessage AuthError = "Fejl ved bekræftelse af identitet"
+
+koreanMessage :: AuthMessage -> Text
+koreanMessage NoOpenID = "OpenID ID가 없습니다"
+koreanMessage LoginOpenID = "OpenID로 로그인"
+koreanMessage LoginGoogle = "Google로 로그인"
+koreanMessage LoginYahoo = "Yahoo로 로그인"
+koreanMessage Email = "이메일"
+koreanMessage UserName = "사용자 이름"
+koreanMessage Password = "비밀번호"
+koreanMessage CurrentPassword = "현재 비밀번호"
+koreanMessage Register = "등록"
+koreanMessage RegisterLong = "새 계정 등록"
+koreanMessage EnterEmail = "이메일 주소를 아래에 입력하시면 확인 이메일이 발송됩니다."
+koreanMessage ConfirmationEmailSentTitle = "확인 이메일을 보냈습니다"
+koreanMessage (ConfirmationEmailSent email) =
+    "확인 이메일을 " `mappend`
+    email `mappend`
+    "에 보냈습니다."
+koreanMessage AddressVerified = "주소가 인증되었습니다. 새 비밀번호를 설정하세요."
+koreanMessage InvalidKeyTitle = "인증키가 잘못되었습니다"
+koreanMessage InvalidKey = "죄송합니다. 잘못된 인증키입니다."
+koreanMessage InvalidEmailPass = "이메일 주소나 비밀번호가 잘못되었습니다"
+koreanMessage BadSetPass = "비밀번호를 설정하기 위해서는 로그인해야 합니다"
+koreanMessage SetPassTitle = "비밀번호 설정"
+koreanMessage SetPass = "새 비밀번호 설정"
+koreanMessage NewPass = "새 비밀번호"
+koreanMessage ConfirmPass = "확인"
+koreanMessage PassMismatch = "비밀번호가 맞지 않습니다. 다시 시도해주세요."
+koreanMessage PassUpdated = "비밀번호가 업데이트 되었습니다"
+koreanMessage Facebook = "Facebook으로 로그인"
+koreanMessage LoginViaEmail = "이메일로"
+koreanMessage InvalidLogin = "잘못된 로그인입니다"
+koreanMessage NowLoggedIn = "로그인했습니다"
+koreanMessage LoginTitle = "로그인"
+koreanMessage PleaseProvideUsername = "사용자 이름을 입력하세요"
+koreanMessage PleaseProvidePassword = "비밀번호를 입력하세요"
+koreanMessage NoIdentifierProvided = "이메일 주소나 사용자 이름이 입력되어 있지 않습니다"
+koreanMessage InvalidEmailAddress = "이메일 주소가 잘못되었습니다"
+koreanMessage PasswordResetTitle = "비밀번호 변경"
+koreanMessage ProvideIdentifier = "이메일 주소나 사용자 이름"
+koreanMessage SendPasswordResetEmail = "비밀번호 재설정 이메일 보내기"
+koreanMessage PasswordResetPrompt = "이메일 주소나 사용자 이름을 아래에 입력하시면 비밀번호 재설정 이메일이 발송됩니다."
+koreanMessage InvalidUsernamePass = "사용자 이름이나 비밀번호가 잘못되었습니다"
+koreanMessage (IdentifierNotFound ident) = ident `mappend` "는 등록되어 있지 않습니다"
+koreanMessage Logout = "로그아웃"
+koreanMessage LogoutTitle = "로그아웃"
+koreanMessage AuthError = "인증오류"
diff --git a/Yesod/Auth/Util/PasswordStore.hs b/Yesod/Auth/Util/PasswordStore.hs
new file mode 100644
--- /dev/null
+++ b/Yesod/Auth/Util/PasswordStore.hs
@@ -0,0 +1,464 @@
+{-# LANGUAGE OverloadedStrings, BangPatterns #-}
+{-# LANGUAGE CPP #-}
+-- |
+-- This is a fork of pwstore-fast, originally copyright (c) Peter Scott, 2011,
+-- and released under a BSD-style licence.
+--
+-- Securely store hashed, salted passwords. If you need to store and verify
+-- passwords, there are many wrong ways to do it, most of them all too
+-- common. Some people store users' passwords in plain text. Then, when an
+-- attacker manages to get their hands on this file, they have the passwords for
+-- every user's account. One step up, but still wrong, is to simply hash all
+-- passwords with SHA1 or something. This is vulnerable to rainbow table and
+-- dictionary attacks. One step up from that is to hash the password along with
+-- a unique salt value. This is vulnerable to dictionary attacks, since guessing
+-- a password is very fast. The right thing to do is to use a slow hash
+-- function, to add some small but significant delay, that will be negligible
+-- for legitimate users but prohibitively expensive for someone trying to guess
+-- passwords by brute force. That is what this library does. It iterates a
+-- SHA256 hash, with a random salt, a few thousand times. This scheme is known
+-- as PBKDF1, and is generally considered secure; there is nothing innovative
+-- happening here.
+--
+-- The API here is very simple. What you store are called /password hashes/.
+-- They are strings (technically, ByteStrings) that look like this:
+--
+-- > "sha256|14|jEWU94phx4QzNyH94Qp4CQ==|5GEw+jxP/4WLgzt9VS3Ee3nhqBlDsrKiB+rq7JfMckU="
+--
+-- Each password hash shows the algorithm, the strength (more on that later),
+-- the salt, and the hashed-and-salted password. You store these on your server,
+-- in a database, for when you need to verify a password. You make a password
+-- hash with the 'makePassword' function. Here's an example:
+--
+-- > >>> makePassword "hunter2" 14
+-- > "sha256|14|Zo4LdZGrv/HYNAUG3q8WcA==|zKjbHZoTpuPLp1lh6ATolWGIKjhXvY4TysuKvqtNFyk="
+--
+-- This will hash the password @\"hunter2\"@, with strength 14, which is a good
+-- default value. The strength here determines how long the hashing will
+-- take. When doing the hashing, we iterate the SHA256 hash function
+-- @2^strength@ times, so increasing the strength by 1 makes the hashing take
+-- twice as long. When computers get faster, you can bump up the strength a
+-- little bit to compensate. You can strengthen existing password hashes with
+-- the 'strengthenPassword' function. Note that 'makePassword' needs to generate
+-- random numbers, so its return type is 'IO' 'ByteString'. If you want to avoid
+-- the 'IO' monad, you can generate your own salt and pass it to
+-- 'makePasswordSalt'.
+--
+-- Your strength value should not be less than 12, and 14 is a good default
+-- value at the time of this writing, in 2013.
+--
+-- Once you've got your password hashes, the second big thing you need to do
+-- with them is verify passwords against them. When a user gives you a password,
+-- you compare it with a password hash using the 'verifyPassword' function:
+--
+-- > >>> verifyPassword "wrong guess" passwordHash
+-- > False
+-- > >>> verifyPassword "hunter2" passwordHash
+-- > True
+--
+-- These two functions are really all you need. If you want to make existing
+-- password hashes stronger, you can use 'strengthenPassword'. Just pass it an
+-- existing password hash and a new strength value, and it will return a new
+-- password hash with that strength value, which will match the same password as
+-- the old password hash.
+--
+-- Note that, as of version 2.4, you can also use PBKDF2, and specify the exact
+-- iteration count. This does not have a significant effect on security, but can
+-- be handy for compatibility with other code.
+--
+-- @since 1.4.18
+
+module Yesod.Auth.Util.PasswordStore (
+
+        -- * Algorithms
+        pbkdf1,                 -- :: ByteString -> Salt -> Int -> ByteString
+        pbkdf2,                 -- :: ByteString -> Salt -> Int -> ByteString
+
+        -- * Registering and verifying passwords
+        makePassword,           -- :: ByteString -> Int -> IO ByteString
+        makePasswordWith,       -- :: (ByteString -> Salt -> Int -> ByteString) ->
+                                --    ByteString -> Int -> IO ByteString
+        makePasswordSalt,       -- :: ByteString -> ByteString -> Int -> ByteString
+        makePasswordSaltWith,   -- :: (ByteString -> Salt -> Int -> ByteString) ->
+                                --    ByteString -> Salt -> Int -> ByteString
+        verifyPassword,         -- :: ByteString -> ByteString -> Bool
+        verifyPasswordWith,     -- :: (ByteString -> Salt -> Int -> ByteString) ->
+                                --    (Int -> Int) -> ByteString -> ByteString -> Bool
+
+        -- * Updating password hash strength
+        strengthenPassword,     -- :: ByteString -> Int -> ByteString
+        passwordStrength,       -- :: ByteString -> Int
+
+        -- * Utilities
+        Salt,
+        isPasswordFormatValid,  -- :: ByteString -> Bool
+        genSaltIO,              -- :: IO Salt
+        genSaltRandom,          -- :: (RandomGen b) => b -> (Salt, b)
+        makeSalt,               -- :: ByteString -> Salt
+        exportSalt,             -- :: Salt -> ByteString
+        importSalt              -- :: ByteString -> Salt
+  ) where
+
+import qualified Crypto.MAC.HMAC as CH
+import qualified Crypto.Hash as CH
+import qualified Data.ByteString.Char8 as B
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as BL
+import qualified Data.Binary as Binary
+import Control.Monad
+import Control.Monad.ST
+import Data.STRef
+import Data.Bits
+import Data.ByteString.Char8 (ByteString)
+import Data.ByteString.Base64 (encode, decodeLenient)
+import System.IO
+import System.Random
+import Data.Maybe
+import qualified Control.Exception
+import Data.ByteArray (convert)
+
+---------------------
+-- Cryptographic base
+---------------------
+
+-- | PBKDF1 key-derivation function. Takes a password, a 'Salt', and a number of
+-- iterations. The number of iterations should be at least 1000, and probably
+-- more. 5000 is a reasonable number, computing almost instantaneously. This
+-- will give a 32-byte 'ByteString' as output. Both the salt and this 32-byte
+-- key should be stored in the password file. When a user wishes to authenticate
+-- a password, just pass it and the salt to this function, and see if the output
+-- matches.
+--
+-- @since 1.4.18
+--
+pbkdf1 :: ByteString -> Salt -> Int -> ByteString
+pbkdf1 password (SaltBS salt) iter = hashRounds first_hash (iter + 1)
+  where
+    first_hash =
+      convert $
+      ((CH.hashFinalize $ CH.hashInit `CH.hashUpdate` password `CH.hashUpdate` salt) :: CH.Digest CH.SHA256)
+
+
+-- | Hash a 'ByteString' for a given number of rounds. The number of rounds is 0
+-- or more. If the number of rounds specified is 0, the ByteString will be
+-- returned unmodified.
+hashRounds :: ByteString -> Int -> ByteString
+hashRounds (!bs) 0 = bs
+hashRounds bs rounds = hashRounds (convert (CH.hash bs :: CH.Digest CH.SHA256)) (rounds - 1)
+
+-- | Computes the hmacSHA256 of the given message, with the given 'Salt'.
+hmacSHA256 :: ByteString
+           -- ^ The secret (the salt)
+           -> ByteString
+           -- ^ The clear-text message
+           -> ByteString
+           -- ^ The encoded message
+hmacSHA256 secret msg =
+    convert (CH.hmacGetDigest (CH.hmac secret msg) :: CH.Digest CH.SHA256)
+
+-- | PBKDF2 key-derivation function.
+-- For details see @http://tools.ietf.org/html/rfc2898@.
+-- @32@ is the most common digest size for @SHA256@, and is
+-- what the algorithm internally uses.
+-- @HMAC+SHA256@ is used as @PRF@, because @HMAC+SHA1@ is considered too weak.
+--
+-- @since 1.4.18
+--
+pbkdf2 :: ByteString -> Salt -> Int -> ByteString
+pbkdf2 password (SaltBS salt) c =
+    let hLen = 32
+        dkLen = hLen in go hLen dkLen
+  where
+    go hLen dkLen | dkLen > (2^(32 :: Int) - 1) * hLen = error "Derived key too long."
+                  | otherwise =
+                      let !l = ceiling ((fromIntegral dkLen / fromIntegral hLen) :: Double)
+                          !r = dkLen - (l - 1) * hLen
+                          chunks = [f i | i <- [1 .. l]]
+                      in (B.concat . init $ chunks) `B.append` B.take r (last chunks)
+
+    -- The @f@ function, as defined in the spec.
+    -- It calls 'u' under the hood.
+    f :: Int -> ByteString
+    f i = let !u1 = hmacSHA256 password (salt `B.append` int i)
+      -- Using the ST Monad, for maximum performance.
+      in runST $ do
+          u <- newSTRef u1
+          accum <- newSTRef u1
+          forM_ [2 .. c] $ \_ -> do
+            modifySTRef' u (hmacSHA256 password)
+            currentU <- readSTRef u
+            modifySTRef' accum (`xor'` currentU)
+          readSTRef accum
+
+    -- int(i), as defined in the spec.
+    int :: Int -> ByteString
+    int i = let str = BL.unpack . Binary.encode $ i
+            in BS.pack $ drop (length str - 4) str
+
+    -- | A convenience function to XOR two 'ByteString' together.
+    xor' :: ByteString -> ByteString -> ByteString
+    xor' !b1 !b2 = BS.pack $ BS.zipWith xor b1 b2
+
+-- | Generate a 'Salt' from 128 bits of data from @\/dev\/urandom@, with the
+-- system RNG as a fallback. This is the function used to generate salts by
+-- 'makePassword'.
+--
+-- @since 1.4.18
+--
+genSaltIO :: IO Salt
+genSaltIO =
+    Control.Exception.catch genSaltDevURandom def
+  where
+    def :: IOError -> IO Salt
+    def _ = genSaltSysRandom
+
+-- | Generate a 'Salt' from @\/dev\/urandom@.
+genSaltDevURandom :: IO Salt
+genSaltDevURandom = withFile "/dev/urandom" ReadMode $ \h -> do
+                      rawSalt <- B.hGet h 16
+                      return $ makeSalt rawSalt
+
+-- | Generate a 'Salt' from 'System.Random'.
+genSaltSysRandom :: IO Salt
+genSaltSysRandom = randomChars >>= return . makeSalt . B.pack
+    where randomChars = sequence $ replicate 16 $ randomRIO ('\NUL', '\255')
+
+-----------------------
+-- Password hash format
+-----------------------
+
+-- Format: "sha256|strength|salt|hash", where strength is an unsigned int, salt
+-- is a base64-encoded 16-byte random number, and hash is a base64-encoded hash
+-- value.
+
+-- | Try to parse a password hash.
+readPwHash :: ByteString -> Maybe (Int, Salt, ByteString)
+readPwHash pw | length broken /= 4
+                || algorithm /= "sha256"
+                || B.length hash /= 44 = Nothing
+              | otherwise = case B.readInt strBS of
+                              Just (strength, _) -> Just (strength, SaltBS salt, hash)
+                              Nothing -> Nothing
+    where broken = B.split '|' pw
+          [algorithm, strBS, salt, hash] = broken
+
+-- | Encode a password hash, from a @(strength, salt, hash)@ tuple, where
+-- strength is an 'Int', and both @salt@ and @hash@ are base64-encoded
+-- 'ByteString's.
+writePwHash :: (Int, Salt, ByteString) -> ByteString
+writePwHash (strength, SaltBS salt, hash) =
+    B.intercalate "|" ["sha256", B.pack (show strength), salt, hash]
+
+-----------------
+-- High level API
+-----------------
+
+-- | Hash a password with a given strength (14 is a good default). The output of
+-- this function can be written directly to a password file or
+-- database. Generates a salt using high-quality randomness from
+-- @\/dev\/urandom@ or (if that is not available, for example on Windows)
+-- 'System.Random', which is included in the hashed output.
+--
+-- @since 1.4.18
+--
+makePassword :: ByteString -> Int -> IO ByteString
+makePassword = makePasswordWith pbkdf1
+
+-- | A generic version of 'makePassword', which allow the user
+-- to choose the algorithm to use.
+--
+-- >>> makePasswordWith pbkdf1 "password" 14
+--
+-- @since 1.4.18
+--
+makePasswordWith :: (ByteString -> Salt -> Int -> ByteString)
+                 -- ^ The algorithm to use (e.g. pbkdf1)
+                 -> ByteString
+                 -- ^ The password to encrypt
+                 -> Int
+                 -- ^ log2 of the number of iterations
+                 -> IO ByteString
+makePasswordWith algorithm password strength = do
+  salt <- genSaltIO
+  return $ makePasswordSaltWith algorithm (2^) password salt strength
+
+-- | A generic version of 'makePasswordSalt', meant to give the user
+-- the maximum control over the generation parameters.
+-- Note that, unlike 'makePasswordWith', this function takes the @raw@
+-- number of iterations. This means the user will need to specify a
+-- sensible value, typically @10000@ or @20000@.
+--
+-- @since 1.4.18
+--
+makePasswordSaltWith :: (ByteString -> Salt -> Int -> ByteString)
+                     -- ^ A function modeling an algorithm (e.g. 'pbkdf1')
+                     -> (Int -> Int)
+                     -- ^ A function to modify the strength
+                     -> ByteString
+                     -- ^ A password, given as clear text
+                     -> Salt
+                     -- ^ A hash 'Salt'
+                     -> Int
+                     -- ^ The password strength (e.g. @10000, 20000, etc.@)
+                     -> ByteString
+makePasswordSaltWith algorithm strengthModifier pwd salt strength = writePwHash (strength, salt, hash)
+    where hash = encode $ algorithm pwd salt (strengthModifier strength)
+
+-- | Hash a password with a given strength (14 is a good default), using a given
+-- salt. The output of this function can be written directly to a password file
+-- or database. Example:
+--
+-- > >>> makePasswordSalt "hunter2" (makeSalt "72cd18b5ebfe6e96") 14
+-- > "sha256|14|NzJjZDE4YjVlYmZlNmU5Ng==|yuiNrZW3KHX+pd0sWy9NTTsy5Yopmtx4UYscItSsoxc="
+--
+-- @since 1.4.18
+--
+makePasswordSalt :: ByteString -> Salt -> Int -> ByteString
+makePasswordSalt = makePasswordSaltWith pbkdf1 (2^)
+
+-- | 'verifyPasswordWith' @algorithm userInput pwHash@ verifies
+-- the password @userInput@ given by the user against the stored password
+-- hash @pwHash@, with the hashing algorithm @algorithm@.  Returns 'True' if the
+-- given password is correct, and 'False' if it is not.
+-- This function allows the programmer to specify the algorithm to use,
+-- e.g. 'pbkdf1' or 'pbkdf2'.
+-- Note: If you want to verify a password previously generated with
+-- 'makePasswordSaltWith', but without modifying the number of iterations,
+-- you can do:
+--
+-- > >>> verifyPasswordWith pbkdf2 id "hunter2" "sha256..."
+-- > True
+--
+-- @since 1.4.18
+--
+verifyPasswordWith :: (ByteString -> Salt -> Int -> ByteString)
+                   -- ^ A function modeling an algorithm (e.g. pbkdf1)
+                   -> (Int -> Int)
+                   -- ^ A function to modify the strength
+                   -> ByteString
+                   -- ^ User password
+                   -> ByteString
+                   -- ^ The generated hash (e.g. sha256|14...)
+                   -> Bool
+verifyPasswordWith algorithm strengthModifier userInput pwHash =
+    case readPwHash pwHash of
+      Nothing -> False
+      Just (strength, salt, goodHash) ->
+          encode (algorithm userInput salt (strengthModifier strength)) == goodHash
+
+-- | Like 'verifyPasswordWith', but uses 'pbkdf1' as algorithm.
+--
+-- @since 1.4.18
+--
+verifyPassword :: ByteString -> ByteString -> Bool
+verifyPassword = verifyPasswordWith pbkdf1 (2^)
+
+-- | Try to strengthen a password hash, by hashing it some more
+-- times. @'strengthenPassword' pwHash new_strength@ will return a new password
+-- hash with strength at least @new_strength@. If the password hash already has
+-- strength greater than or equal to @new_strength@, then it is returned
+-- unmodified. If the password hash is invalid and does not parse, it will be
+-- returned without comment.
+--
+-- This function can be used to periodically update your password database when
+-- computers get faster, in order to keep up with Moore's law. This isn't hugely
+-- important, but it's a good idea.
+--
+-- @since 1.4.18
+--
+strengthenPassword :: ByteString -> Int -> ByteString
+strengthenPassword pwHash newstr =
+    case readPwHash pwHash of
+      Nothing -> pwHash
+      Just (oldstr, salt, hashB64) ->
+          if oldstr < newstr then
+              writePwHash (newstr, salt, newHash)
+          else
+              pwHash
+          where newHash = encode $ hashRounds hash extraRounds
+                extraRounds = (2^newstr) - (2^oldstr)
+                hash = decodeLenient hashB64
+
+-- | Return the strength of a password hash.
+--
+-- @since 1.4.18
+--
+passwordStrength :: ByteString -> Int
+passwordStrength pwHash = case readPwHash pwHash of
+                            Nothing               -> 0
+                            Just (strength, _, _) -> strength
+
+------------
+-- Utilities
+------------
+
+-- | A salt is a unique random value which is stored as part of the password
+-- hash. You can generate a salt with 'genSaltIO' or 'genSaltRandom', or if you
+-- really know what you're doing, you can create them from your own ByteString
+-- values with 'makeSalt'.
+--
+-- @since 1.4.18
+--
+newtype Salt = SaltBS ByteString
+    deriving (Show, Eq, Ord)
+
+-- | Create a 'Salt' from a 'ByteString'. The input must be at least 8
+-- characters, and can contain arbitrary bytes. Most users will not need to use
+-- this function.
+--
+-- @since 1.4.18
+--
+makeSalt :: ByteString -> Salt
+makeSalt = SaltBS . encode . check_length
+    where check_length salt | B.length salt < 8 =
+                                error "Salt too short. Minimum length is 8 characters."
+                            | otherwise = salt
+
+-- | Convert a 'Salt' into a 'ByteString'. The resulting 'ByteString' will be
+-- base64-encoded. Most users will not need to use this function.
+--
+-- @since 1.4.18
+--
+exportSalt :: Salt -> ByteString
+exportSalt (SaltBS bs) = bs
+
+-- | Convert a raw 'ByteString' into a 'Salt'.
+-- Use this function with caution, since using a weak salt will result in a
+-- weak password.
+--
+-- @since 1.4.18
+--
+importSalt :: ByteString -> Salt
+importSalt = SaltBS
+
+-- | Is the format of a password hash valid? Attempts to parse a given password
+-- hash. Returns 'True' if it parses correctly, and 'False' otherwise.
+--
+-- @since 1.4.18
+--
+isPasswordFormatValid :: ByteString -> Bool
+isPasswordFormatValid = isJust . readPwHash
+
+-- | Generate a 'Salt' with 128 bits of data taken from a given random number
+-- generator. Returns the salt and the updated random number generator. This is
+-- meant to be used with 'makePasswordSalt' by people who would prefer to either
+-- use their own random number generator or avoid the 'IO' monad.
+--
+-- @since 1.4.18
+--
+genSaltRandom :: (RandomGen b) => b -> (Salt, b)
+genSaltRandom gen = (salt, newgen)
+    where rands _ 0 = []
+          rands g n = (a, g') : rands g' (n-1 :: Int)
+              where (a, g') = randomR ('\NUL', '\255') g
+          salt   = makeSalt $ B.pack $ map fst (rands gen 16)
+          newgen = snd $ last (rands gen 16)
+
+#if !MIN_VERSION_base(4, 6, 0)
+-- | Strict version of 'modifySTRef'
+modifySTRef' :: STRef s a -> (a -> a) -> ST s ()
+modifySTRef' ref f = do
+    x <- readSTRef ref
+    let x' = f x
+    x' `seq` writeSTRef ref x'
+#endif
diff --git a/Yesod/PasswordStore.hs b/Yesod/PasswordStore.hs
deleted file mode 100644
--- a/Yesod/PasswordStore.hs
+++ /dev/null
@@ -1,418 +0,0 @@
-{-# LANGUAGE OverloadedStrings, BangPatterns #-}
-{-# LANGUAGE CPP #-}
--- |
--- Module      : Crypto.PasswordStore
--- Copyright   : (c) Peter Scott, 2011
--- License     : BSD-style
---
--- Maintainer  : pjscott@iastate.edu
--- Stability   : experimental
--- Portability : portable
---
--- Securely store hashed, salted passwords. If you need to store and verify
--- passwords, there are many wrong ways to do it, most of them all too
--- common. Some people store users' passwords in plain text. Then, when an
--- attacker manages to get their hands on this file, they have the passwords for
--- every user's account. One step up, but still wrong, is to simply hash all
--- passwords with SHA1 or something. This is vulnerable to rainbow table and
--- dictionary attacks. One step up from that is to hash the password along with
--- a unique salt value. This is vulnerable to dictionary attacks, since guessing
--- a password is very fast. The right thing to do is to use a slow hash
--- function, to add some small but significant delay, that will be negligible
--- for legitimate users but prohibitively expensive for someone trying to guess
--- passwords by brute force. That is what this library does. It iterates a
--- SHA256 hash, with a random salt, a few thousand times. This scheme is known
--- as PBKDF1, and is generally considered secure; there is nothing innovative
--- happening here.
---
--- The API here is very simple. What you store are called /password hashes/.
--- They are strings (technically, ByteStrings) that look like this:
---
--- > "sha256|14|jEWU94phx4QzNyH94Qp4CQ==|5GEw+jxP/4WLgzt9VS3Ee3nhqBlDsrKiB+rq7JfMckU="
---
--- Each password hash shows the algorithm, the strength (more on that later),
--- the salt, and the hashed-and-salted password. You store these on your server,
--- in a database, for when you need to verify a password. You make a password
--- hash with the 'makePassword' function. Here's an example:
---
--- > >>> makePassword "hunter2" 14
--- > "sha256|14|Zo4LdZGrv/HYNAUG3q8WcA==|zKjbHZoTpuPLp1lh6ATolWGIKjhXvY4TysuKvqtNFyk="
---
--- This will hash the password @\"hunter2\"@, with strength 14, which is a good
--- default value. The strength here determines how long the hashing will
--- take. When doing the hashing, we iterate the SHA256 hash function
--- @2^strength@ times, so increasing the strength by 1 makes the hashing take
--- twice as long. When computers get faster, you can bump up the strength a
--- little bit to compensate. You can strengthen existing password hashes with
--- the 'strengthenPassword' function. Note that 'makePassword' needs to generate
--- random numbers, so its return type is 'IO' 'ByteString'. If you want to avoid
--- the 'IO' monad, you can generate your own salt and pass it to
--- 'makePasswordSalt'.
---
--- Your strength value should not be less than 12, and 14 is a good default
--- value at the time of this writing, in 2013.
---
--- Once you've got your password hashes, the second big thing you need to do
--- with them is verify passwords against them. When a user gives you a password,
--- you compare it with a password hash using the 'verifyPassword' function:
---
--- > >>> verifyPassword "wrong guess" passwordHash
--- > False
--- > >>> verifyPassword "hunter2" passwordHash
--- > True
---
--- These two functions are really all you need. If you want to make existing
--- password hashes stronger, you can use 'strengthenPassword'. Just pass it an
--- existing password hash and a new strength value, and it will return a new
--- password hash with that strength value, which will match the same password as
--- the old password hash.
---
--- Note that, as of version 2.4, you can also use PBKDF2, and specify the exact
--- iteration count. This does not have a significant effect on security, but can
--- be handy for compatibility with other code.
-
-module Yesod.PasswordStore (
-
-        -- * Algorithms
-        pbkdf1,                 -- :: ByteString -> Salt -> Int -> ByteString
-        pbkdf2,                 -- :: ByteString -> Salt -> Int -> ByteString
-
-        -- * Registering and verifying passwords
-        makePassword,           -- :: ByteString -> Int -> IO ByteString
-        makePasswordWith,       -- :: (ByteString -> Salt -> Int -> ByteString) ->
-                                --    ByteString -> Int -> IO ByteString
-        makePasswordSalt,       -- :: ByteString -> ByteString -> Int -> ByteString
-        makePasswordSaltWith,   -- :: (ByteString -> Salt -> Int -> ByteString) ->
-                                --    ByteString -> Salt -> Int -> ByteString
-        verifyPassword,         -- :: ByteString -> ByteString -> Bool
-        verifyPasswordWith,     -- :: (ByteString -> Salt -> Int -> ByteString) ->
-                                --    (Int -> Int) -> ByteString -> ByteString -> Bool
-
-        -- * Updating password hash strength
-        strengthenPassword,     -- :: ByteString -> Int -> ByteString
-        passwordStrength,       -- :: ByteString -> Int
-
-        -- * Utilities
-        Salt,
-        isPasswordFormatValid,  -- :: ByteString -> Bool
-        genSaltIO,              -- :: IO Salt
-        genSaltRandom,          -- :: (RandomGen b) => b -> (Salt, b)
-        makeSalt,               -- :: ByteString -> Salt
-        exportSalt,             -- :: Salt -> ByteString
-        importSalt              -- :: ByteString -> Salt
-  ) where
-
-import qualified Crypto.MAC.HMAC as CH
-import qualified Crypto.Hash as CH
-import qualified Data.ByteString.Char8 as B
-import qualified Data.ByteString as BS
-import qualified Data.ByteString.Lazy as BL
-import qualified Data.Binary as Binary
-import Control.Monad
-import Control.Monad.ST
-import Data.STRef
-import Data.Bits
-import Data.ByteString.Char8 (ByteString)
-import Data.ByteString.Base64 (encode, decodeLenient)
-import System.IO
-import System.Random
-import Data.Maybe
-import qualified Control.Exception
-import Data.ByteArray (convert)
-
----------------------
--- Cryptographic base
----------------------
-
--- | PBKDF1 key-derivation function. Takes a password, a 'Salt', and a number of
--- iterations. The number of iterations should be at least 1000, and probably
--- more. 5000 is a reasonable number, computing almost instantaneously. This
--- will give a 32-byte 'ByteString' as output. Both the salt and this 32-byte
--- key should be stored in the password file. When a user wishes to authenticate
--- a password, just pass it and the salt to this function, and see if the output
--- matches.
-pbkdf1 :: ByteString -> Salt -> Int -> ByteString
-pbkdf1 password (SaltBS salt) iter = hashRounds first_hash (iter + 1)
-  where
-    first_hash =
-      convert $
-      ((CH.hashFinalize $ CH.hashInit `CH.hashUpdate` password `CH.hashUpdate` salt) :: CH.Digest CH.SHA256)
-
-
--- | Hash a 'ByteString' for a given number of rounds. The number of rounds is 0
--- or more. If the number of rounds specified is 0, the ByteString will be
--- returned unmodified.
-hashRounds :: ByteString -> Int -> ByteString
-hashRounds (!bs) 0 = bs
-hashRounds bs rounds = hashRounds (convert (CH.hash bs :: CH.Digest CH.SHA256)) (rounds - 1)
-
--- | Computes the hmacSHA256 of the given message, with the given 'Salt'.
-hmacSHA256 :: ByteString
-           -- ^ The secret (the salt)
-           -> ByteString
-           -- ^ The clear-text message
-           -> ByteString
-           -- ^ The encoded message
-hmacSHA256 secret msg =
-    convert (CH.hmacGetDigest (CH.hmac secret msg) :: CH.Digest CH.SHA256)
-
--- | PBKDF2 key-derivation function.
--- For details see @http://tools.ietf.org/html/rfc2898@.
--- @32@ is the most common digest size for @SHA256@, and is
--- what the algorithm internally uses.
--- @HMAC+SHA256@ is used as @PRF@, because @HMAC+SHA1@ is considered too weak.
-pbkdf2 :: ByteString -> Salt -> Int -> ByteString
-pbkdf2 password (SaltBS salt) c =
-    let hLen = 32
-        dkLen = hLen in go hLen dkLen
-  where
-    go hLen dkLen | dkLen > (2^(32 :: Int) - 1) * hLen = error "Derived key too long."
-                  | otherwise =
-                      let !l = ceiling ((fromIntegral dkLen / fromIntegral hLen) :: Double)
-                          !r = dkLen - (l - 1) * hLen
-                          chunks = [f i | i <- [1 .. l]]
-                      in (B.concat . init $ chunks) `B.append` B.take r (last chunks)
-
-    -- The @f@ function, as defined in the spec.
-    -- It calls 'u' under the hood.
-    f :: Int -> ByteString
-    f i = let !u1 = hmacSHA256 password (salt `B.append` int i)
-      -- Using the ST Monad, for maximum performance.
-      in runST $ do
-          u <- newSTRef u1
-          accum <- newSTRef u1
-          forM_ [2 .. c] $ \_ -> do
-            modifySTRef' u (hmacSHA256 password)
-            currentU <- readSTRef u
-            modifySTRef' accum (`xor'` currentU)
-          readSTRef accum
-
-    -- int(i), as defined in the spec.
-    int :: Int -> ByteString
-    int i = let str = BL.unpack . Binary.encode $ i
-            in BS.pack $ drop (length str - 4) str
-
-    -- | A convenience function to XOR two 'ByteString' together.
-    xor' :: ByteString -> ByteString -> ByteString
-    xor' !b1 !b2 = BS.pack $ BS.zipWith xor b1 b2
-
--- | Generate a 'Salt' from 128 bits of data from @\/dev\/urandom@, with the
--- system RNG as a fallback. This is the function used to generate salts by
--- 'makePassword'.
-genSaltIO :: IO Salt
-genSaltIO =
-    Control.Exception.catch genSaltDevURandom def
-  where
-    def :: IOError -> IO Salt
-    def _ = genSaltSysRandom
-
--- | Generate a 'Salt' from @\/dev\/urandom@.
-genSaltDevURandom :: IO Salt
-genSaltDevURandom = withFile "/dev/urandom" ReadMode $ \h -> do
-                      rawSalt <- B.hGet h 16
-                      return $ makeSalt rawSalt
-
--- | Generate a 'Salt' from 'System.Random'.
-genSaltSysRandom :: IO Salt
-genSaltSysRandom = randomChars >>= return . makeSalt . B.pack
-    where randomChars = sequence $ replicate 16 $ randomRIO ('\NUL', '\255')
-
------------------------
--- Password hash format
------------------------
-
--- Format: "sha256|strength|salt|hash", where strength is an unsigned int, salt
--- is a base64-encoded 16-byte random number, and hash is a base64-encoded hash
--- value.
-
--- | Try to parse a password hash.
-readPwHash :: ByteString -> Maybe (Int, Salt, ByteString)
-readPwHash pw | length broken /= 4
-                || algorithm /= "sha256"
-                || B.length hash /= 44 = Nothing
-              | otherwise = case B.readInt strBS of
-                              Just (strength, _) -> Just (strength, SaltBS salt, hash)
-                              Nothing -> Nothing
-    where broken = B.split '|' pw
-          [algorithm, strBS, salt, hash] = broken
-
--- | Encode a password hash, from a @(strength, salt, hash)@ tuple, where
--- strength is an 'Int', and both @salt@ and @hash@ are base64-encoded
--- 'ByteString's.
-writePwHash :: (Int, Salt, ByteString) -> ByteString
-writePwHash (strength, SaltBS salt, hash) =
-    B.intercalate "|" ["sha256", B.pack (show strength), salt, hash]
-
------------------
--- High level API
------------------
-
--- | Hash a password with a given strength (14 is a good default). The output of
--- this function can be written directly to a password file or
--- database. Generates a salt using high-quality randomness from
--- @\/dev\/urandom@ or (if that is not available, for example on Windows)
--- 'System.Random', which is included in the hashed output.
-makePassword :: ByteString -> Int -> IO ByteString
-makePassword = makePasswordWith pbkdf1
-
--- | A generic version of 'makePassword', which allow the user
--- to choose the algorithm to use.
---
--- >>> makePasswordWith pbkdf1 "password" 14
---
-makePasswordWith :: (ByteString -> Salt -> Int -> ByteString)
-                 -- ^ The algorithm to use (e.g. pbkdf1)
-                 -> ByteString
-                 -- ^ The password to encrypt
-                 -> Int
-                 -- ^ log2 of the number of iterations
-                 -> IO ByteString
-makePasswordWith algorithm password strength = do
-  salt <- genSaltIO
-  return $ makePasswordSaltWith algorithm (2^) password salt strength
-
--- | A generic version of 'makePasswordSalt', meant to give the user
--- the maximum control over the generation parameters.
--- Note that, unlike 'makePasswordWith', this function takes the @raw@
--- number of iterations. This means the user will need to specify a
--- sensible value, typically @10000@ or @20000@.
-makePasswordSaltWith :: (ByteString -> Salt -> Int -> ByteString)
-                     -- ^ A function modeling an algorithm (e.g. 'pbkdf1')
-                     -> (Int -> Int)
-                     -- ^ A function to modify the strength
-                     -> ByteString
-                     -- ^ A password, given as clear text
-                     -> Salt
-                     -- ^ A hash 'Salt'
-                     -> Int
-                     -- ^ The password strength (e.g. @10000, 20000, etc.@)
-                     -> ByteString
-makePasswordSaltWith algorithm strengthModifier pwd salt strength = writePwHash (strength, salt, hash)
-    where hash = encode $ algorithm pwd salt (strengthModifier strength)
-
--- | Hash a password with a given strength (14 is a good default), using a given
--- salt. The output of this function can be written directly to a password file
--- or database. Example:
---
--- > >>> makePasswordSalt "hunter2" (makeSalt "72cd18b5ebfe6e96") 14
--- > "sha256|14|NzJjZDE4YjVlYmZlNmU5Ng==|yuiNrZW3KHX+pd0sWy9NTTsy5Yopmtx4UYscItSsoxc="
-makePasswordSalt :: ByteString -> Salt -> Int -> ByteString
-makePasswordSalt = makePasswordSaltWith pbkdf1 (2^)
-
--- | 'verifyPasswordWith' @algorithm userInput pwHash@ verifies
--- the password @userInput@ given by the user against the stored password
--- hash @pwHash@, with the hashing algorithm @algorithm@.  Returns 'True' if the
--- given password is correct, and 'False' if it is not.
--- This function allows the programmer to specify the algorithm to use,
--- e.g. 'pbkdf1' or 'pbkdf2'.
--- Note: If you want to verify a password previously generated with
--- 'makePasswordSaltWith', but without modifying the number of iterations,
--- you can do:
---
--- > >>> verifyPasswordWith pbkdf2 id "hunter2" "sha256..."
--- > True
---
-verifyPasswordWith :: (ByteString -> Salt -> Int -> ByteString)
-                   -- ^ A function modeling an algorithm (e.g. pbkdf1)
-                   -> (Int -> Int)
-                   -- ^ A function to modify the strength
-                   -> ByteString
-                   -- ^ User password
-                   -> ByteString
-                   -- ^ The generated hash (e.g. sha256|14...)
-                   -> Bool
-verifyPasswordWith algorithm strengthModifier userInput pwHash =
-    case readPwHash pwHash of
-      Nothing -> False
-      Just (strength, salt, goodHash) ->
-          encode (algorithm userInput salt (strengthModifier strength)) == goodHash
-
--- | Like 'verifyPasswordWith', but uses 'pbkdf1' as algorithm.
-verifyPassword :: ByteString -> ByteString -> Bool
-verifyPassword = verifyPasswordWith pbkdf1 (2^)
-
--- | Try to strengthen a password hash, by hashing it some more
--- times. @'strengthenPassword' pwHash new_strength@ will return a new password
--- hash with strength at least @new_strength@. If the password hash already has
--- strength greater than or equal to @new_strength@, then it is returned
--- unmodified. If the password hash is invalid and does not parse, it will be
--- returned without comment.
---
--- This function can be used to periodically update your password database when
--- computers get faster, in order to keep up with Moore's law. This isn't hugely
--- important, but it's a good idea.
-strengthenPassword :: ByteString -> Int -> ByteString
-strengthenPassword pwHash newstr =
-    case readPwHash pwHash of
-      Nothing -> pwHash
-      Just (oldstr, salt, hashB64) ->
-          if oldstr < newstr then
-              writePwHash (newstr, salt, newHash)
-          else
-              pwHash
-          where newHash = encode $ hashRounds hash extraRounds
-                extraRounds = (2^newstr) - (2^oldstr)
-                hash = decodeLenient hashB64
-
--- | Return the strength of a password hash.
-passwordStrength :: ByteString -> Int
-passwordStrength pwHash = case readPwHash pwHash of
-                            Nothing               -> 0
-                            Just (strength, _, _) -> strength
-
-------------
--- Utilities
-------------
-
--- | A salt is a unique random value which is stored as part of the password
--- hash. You can generate a salt with 'genSaltIO' or 'genSaltRandom', or if you
--- really know what you're doing, you can create them from your own ByteString
--- values with 'makeSalt'.
-newtype Salt = SaltBS ByteString
-    deriving (Show, Eq, Ord)
-
--- | Create a 'Salt' from a 'ByteString'. The input must be at least 8
--- characters, and can contain arbitrary bytes. Most users will not need to use
--- this function.
-makeSalt :: ByteString -> Salt
-makeSalt = SaltBS . encode . check_length
-    where check_length salt | B.length salt < 8 =
-                                error "Salt too short. Minimum length is 8 characters."
-                            | otherwise = salt
-
--- | Convert a 'Salt' into a 'ByteString'. The resulting 'ByteString' will be
--- base64-encoded. Most users will not need to use this function.
-exportSalt :: Salt -> ByteString
-exportSalt (SaltBS bs) = bs
-
--- | Convert a raw 'ByteString' into a 'Salt'.
--- Use this function with caution, since using a weak salt will result in a
--- weak password.
-importSalt :: ByteString -> Salt
-importSalt = SaltBS
-
--- | Is the format of a password hash valid? Attempts to parse a given password
--- hash. Returns 'True' if it parses correctly, and 'False' otherwise.
-isPasswordFormatValid :: ByteString -> Bool
-isPasswordFormatValid = isJust . readPwHash
-
--- | Generate a 'Salt' with 128 bits of data taken from a given random number
--- generator. Returns the salt and the updated random number generator. This is
--- meant to be used with 'makePasswordSalt' by people who would prefer to either
--- use their own random number generator or avoid the 'IO' monad.
-genSaltRandom :: (RandomGen b) => b -> (Salt, b)
-genSaltRandom gen = (salt, newgen)
-    where rands _ 0 = []
-          rands g n = (a, g') : rands g' (n-1 :: Int)
-              where (a, g') = randomR ('\NUL', '\255') g
-          salt   = makeSalt $ B.pack $ map fst (rands gen 16)
-          newgen = snd $ last (rands gen 16)
-
-#if !MIN_VERSION_base(4, 6, 0)
--- | Strict version of 'modifySTRef'
-modifySTRef' :: STRef s a -> (a -> a) -> ST s ()
-modifySTRef' ref f = do
-    x <- readSTRef ref
-    let x' = f x
-    x' `seq` writeSTRef ref x'
-#endif
diff --git a/yesod-auth.cabal b/yesod-auth.cabal
--- a/yesod-auth.cabal
+++ b/yesod-auth.cabal
@@ -1,5 +1,5 @@
 name:            yesod-auth
-version:         1.4.17.3
+version:         1.4.18
 license:         MIT
 license-file:    LICENSE
 author:          Michael Snoyman, Patrick Brisbin
@@ -77,8 +77,8 @@
                      Yesod.Auth.GoogleEmail
                      Yesod.Auth.GoogleEmail2
                      Yesod.Auth.Hardcoded
+                     Yesod.Auth.Util.PasswordStore
     other-modules:   Yesod.Auth.Routes
-                     Yesod.PasswordStore
     ghc-options:     -Wall
 
 source-repository head
