module Crypto.KDF.PBKDF2 (
    fastpbkdf2_hmac_sha1
  , fastpbkdf2_hmac_sha256
  , fastpbkdf2_hmac_sha512
  ) where
import Data.ByteString
import Data.ByteString.Unsafe
import Data.Monoid
import Foreign.C.Types
import Foreign.ForeignPtr
import Foreign.Ptr
import System.IO.Unsafe
foreign import ccall "fastpbkdf2_hmac_sha1" c_fastpbkdf2_hmac_sha1 :: Signature
foreign import ccall "fastpbkdf2_hmac_sha256" c_fastpbkdf2_hmac_sha256 :: Signature
foreign import ccall "fastpbkdf2_hmac_sha512" c_fastpbkdf2_hmac_sha512 :: Signature
type Signature = Ptr CChar -> CSize -> Ptr CChar -> CSize -> CInt -> Ptr CChar -> CSize -> IO ()
fastpbkdf2_fn :: Signature
              -> ByteString
              
              -> ByteString
              
              -> Int
              
              -> Int
              
              -> ByteString
fastpbkdf2_fn fn password salt iterations keyLen = unsafeDupablePerformIO $ do
  withPositive (iterations, keyLen) $ do
    let outSize = CSize (fromIntegral keyLen)
    outForeignPtr <- mallocForeignPtrBytes keyLen
    withForeignPtr outForeignPtr $ \ptrOut -> do
      unsafeUseAsCStringLen password $ \(passwordPtr, passwordSizeInt) -> do
        unsafeUseAsCStringLen salt   $ \(saltPtr, saltSizeInt) -> do
          let passwordSize = CSize (fromIntegral passwordSizeInt)
          let saltSize     = CSize (fromIntegral saltSizeInt)
          let iters        = CInt (fromIntegral iterations)
          fn passwordPtr passwordSize saltPtr saltSize iters ptrOut outSize
          unsafePackCStringLen (ptrOut, keyLen)
withPositive :: (Int, Int) -> IO a -> IO a
withPositive (iter, keyLen) action
  | iter   <= 0 = error $ "fastpbkdf2: iteration count must be > 0 (it was " <> show iter <> ")"
  | keyLen <= 0 = error $ "fastpbkdf2: key length must be > 0 (it was " <> show keyLen <> ")"
  | otherwise = action
fastpbkdf2_hmac_sha1 :: ByteString
                     
                     -> ByteString
                     
                     -> Int
                     
                     -> Int
                     
                     -> ByteString
fastpbkdf2_hmac_sha1 = fastpbkdf2_fn c_fastpbkdf2_hmac_sha1
fastpbkdf2_hmac_sha256 :: ByteString
                     
                     -> ByteString
                     
                     -> Int
                     
                     -> Int
                     
                     -> ByteString
fastpbkdf2_hmac_sha256 = fastpbkdf2_fn c_fastpbkdf2_hmac_sha256
fastpbkdf2_hmac_sha512 :: ByteString
                     
                     -> ByteString
                     
                     -> Int
                     
                     -> Int
                     
                     -> ByteString
fastpbkdf2_hmac_sha512 = fastpbkdf2_fn c_fastpbkdf2_hmac_sha512