{-# LINE 1 "Data/Digest/BCrypt.hsc" #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LINE 2 "Data/Digest/BCrypt.hsc" #-}

module Data.Digest.BCrypt
    ( bcrypt
    , genSalt
    , BSalt
    , unBSalt
    )
where


{-# LINE 12 "Data/Digest/BCrypt.hsc" #-}

{-# LINE 13 "Data/Digest/BCrypt.hsc" #-}

{-# LINE 14 "Data/Digest/BCrypt.hsc" #-}

import Foreign
import Foreign.C.Types
import Foreign.C.String
import qualified System.IO.Unsafe ( unsafePerformIO )
import qualified Data.ByteString.Unsafe as B
import qualified Data.ByteString.Internal as B ( fromForeignPtr
                                               , c_strlen
                                               )
import qualified Data.ByteString as B

-- | BCrypt salt for passing to bcrypt.
newtype BSalt = BSalt { -- | Deconstruct a BSalt to a bytestring
                        unBSalt::B.ByteString
                      } deriving (Eq, Ord, Show)

-- | Given a cost from 4-32 and a random seed of 16 bytes generate a salt.
-- Seed should be 16 bytes from a secure random generator
genSalt :: Monad m =>
           Integer         -- ^ Compute cost
           -> B.ByteString -- ^ 16 byte secure random seed
           -> m BSalt        -- ^ Monadic for controlling any error conditions
genSalt cost seed
       | B.length seed /= 16 = fail "Bad seed size"
       | otherwise = return $ unsafePerformIO $
        B.useAsCString seed $ \s ->
             allocaBytes 30 $ \out -> do
{-# LINE 41 "Data/Digest/BCrypt.hsc" #-}
                 let seed' = (fromIntegral cost::CInt)
                 bsalt <- c_bcrypt_gensalt out seed' (castPtr s)
                 result <- B.packCString bsalt
                 return $ BSalt result

-- | Hash a password based on a BSalt with a given cost
bcrypt :: B.ByteString -- ^ Data to hash
          -> BSalt     -- ^ salt generated by genSalt
          -> B.ByteString
bcrypt key (BSalt salt) = unsafePerformIO $
       B.useAsCString key $ \k -> B.useAsCString salt $ \s ->
           allocaBytes 128 $ \out ->
{-# LINE 53 "Data/Digest/BCrypt.hsc" #-}
               B.packCString =<< c_bcrypt out k s

foreign import ccall unsafe "bcrypt.h bcrypt_gensalt"
    c_bcrypt_gensalt :: CString -> CInt -> Ptr Word8 -> IO CString

foreign import ccall unsafe "bcrypt.h bcrypt"
    c_bcrypt :: CString -> CString -> CString -> IO CString