{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE EmptyDataDecls #-}

{- |
   Maintainer:  simons@cryp.to
   Stability:   provisional
   Portability: portable

   Low-level bindings to OpenSSL's EVP interface. Most users do not need this
   code. Check out "OpenSSL.Digest" for a more comfortable interface.
-}

module OpenSSL.EVP.Digest.Digest where

import OpenSSL.EVP.Digest.Algorithm
import OpenSSL.EVP.Digest.Context
import OpenSSL.EVP.Digest.Error ( throwIfZero )

import Foreign
import Foreign.C

-- | Configure the given digest context to use the given message digest
-- algorithm. Throws an exception to signal failure, i.e. because the system is
-- out of memory.

initDigest :: Algorithm -> Context -> IO ()
initDigest algo ctx =
  throwIfZero "OpenSSL.EVP.Digest.initDigest" (_initDigest ctx algo nullPtr)

-- | Hash the given block of memory and update the digest state accordingly.
-- This function can be called many times. Then use 'finalizeDigest' to
-- retrieve the actual hash value.

updateDigest :: Context -> Ptr a -> CSize -> IO ()
updateDigest ctx ptr len =
  throwIfZero "OpenSSL.EVP.Digest.updateDigest" (_updateDigest ctx ptr len)

-- | Finalize the digest calculation and return the result in the 'Word8' array
-- passed as an argument. Naturally, that array is expected to be large enough
-- to contain the digest. 'digestSize' or 'maxDigestSize' are your friends.
-- This function does /not/ clean up the digest context; this has to be done
-- with an explicit call to 'freeContext' (or 'resetContext', if you want to
-- re-use it). However, it does invalidate the digest state so that no further
-- calls of 'digestUpdate' can be made without re-initializing the context
-- first.

finalizeDigest :: Context -> Ptr Word8 -> IO ()
finalizeDigest ctx ptr =
  throwIfZero "OpenSSL.EVP.Digest.finalizeDigest" (_finalizeDigest ctx ptr nullPtr)

-------------------------------------------------------------------------------

-- | We don't support choosing a custom engine to implement the given
-- algorithm. This type is just a place holder, and we always pass 'nullPtr'
-- whereever it is required to let OpenSSL choose whatever engine it thinks is
-- best.

data OpaqueEngine

foreign import ccall unsafe "openssl/evp.h EVP_DigestInit_ex"
  _initDigest :: Context -> Algorithm -> Ptr OpaqueEngine -> IO CInt

foreign import ccall unsafe "openssl/evp.h EVP_DigestUpdate"
  _updateDigest :: Context -> Ptr a -> CSize -> IO CInt

foreign import ccall unsafe "openssl/evp.h EVP_DigestFinal_ex"
  _finalizeDigest :: Context -> Ptr Word8 -> Ptr CUInt -> IO CInt