packages feed

hs-blake2-0.0.1: src/Crypto/Hash/Tsuraan/Blake2/Internal.hs

{-# LANGUAGE OverloadedStrings #-}
module Crypto.Hash.Tsuraan.Blake2.Internal
( BlakeState(..)
, runInit
, runInitKey
, runUpdate
, runFinalize
, runHasher
) where

import Data.ByteString.Internal ( create, toForeignPtr )
import Data.ByteString ( ByteString )
import System.IO.Unsafe ( unsafePerformIO )
import Foreign.ForeignPtr ( ForeignPtr, withForeignPtr, mallocForeignPtr )
import Foreign.Ptr ( Ptr, plusPtr )
import Foreign.C ( CInt(..) )
import Foreign.Storable ( Storable )
import Data.Word ( Word8 )

type BlakeHasher =
  Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Int -> Int -> Int -> IO CInt

data BlakeState a = BlakeState Int (ForeignPtr a) deriving ( Show )

runInit :: Storable a => (Ptr a -> Int -> IO CInt) -> Int -> IO (BlakeState a)
runInit ifn outlen = do
  fptr <- mallocForeignPtr
  withForeignPtr fptr $ \ptr -> do
    success <- ifn ptr outlen
    if success == 0
      then return $ BlakeState outlen fptr
      else error "runInit failure; probably a bad digest size"

runInitKey :: Storable a => (Ptr a -> Int -> Ptr Word8 -> Int -> IO CInt)
           -> ByteString -> Int -> IO (BlakeState a)
runInitKey ifn key outlen =
  let (kforeignptr, koff, klen) = toForeignPtr key
  in withForeignPtr kforeignptr $ \kptr -> do
      fptr <- mallocForeignPtr
      withForeignPtr fptr $ \ptr -> do
        success <- ifn ptr outlen (kptr `plusPtr` koff) klen
        if success == 0
          then return $ BlakeState outlen fptr
          else error "runInit failure; bad digest size or bad key size"

runUpdate :: (Ptr a -> Ptr Word8 -> Int -> IO CInt)
          -> BlakeState a -> ByteString -> IO ()
runUpdate ufn (BlakeState _ ctx) string =
  let (sforeignptr, soff, slen) = toForeignPtr string
  in withForeignPtr sforeignptr $ \sptr ->
      withForeignPtr ctx $ \cptr -> do
        _ <- ufn cptr (sptr `plusPtr` soff) slen
        return ()

runFinalize :: (Ptr a -> Ptr Word8 -> Int -> IO CInt)
            -> BlakeState a -> IO ByteString
runFinalize ffn (BlakeState hashlen fptr) =
  create hashlen $ \hptr ->
    withForeignPtr fptr $ \ctx -> do
      _ <- ffn ctx hptr hashlen
      return ()

runHasher :: BlakeHasher -> ByteString -> Int -> ByteString -> ByteString
runHasher hasher key hashlen bytes = unsafePerformIO $ create hashlen $ \hptr ->
  let (kforeignptr, koff, klen) = toForeignPtr key
      (bforeignptr, boff, blen) = toForeignPtr bytes
  in withForeignPtr kforeignptr $ \kptr ->
      withForeignPtr bforeignptr $ \bptr -> do
        result <- hasher hptr (bptr `plusPtr` boff) (kptr `plusPtr` koff)
                         hashlen blen klen
        if result /= 0
          then error "Blake2bp Error: probably bad key length or hash length"
          else return ()

-- Inline here gives us a ~15% speedup on ghc 7.8.2
{-# INLINE runHasher #-}