cryptonite/Crypto/Hash.hs
Vincent Hanquez db7c3bbb4f [hash] massive overhaul of the hash interface
use the typeclass for the lowest IO impure C bindings definitions,
and define the pure interface as generic on top of this.

At the same time define an Hash.IO interface to allow mutable manipulations
of hash contextes when necessary.

Use HashAlgorithm instead of HashFunction in the [PubKey] sections

Tweak the HMAC, PBKDF2 functions to be more efficient and use the new interface
2015-04-30 06:18:07 +01:00

119 lines
4.1 KiB
Haskell

-- |
-- Module : Crypto.Hash
-- License : BSD-style
-- Maintainer : Vincent Hanquez <vincent@snarc.org>
-- Stability : experimental
-- Portability : unknown
--
-- Generalized cryptographic hash interface, that you can use with cryptographic hash
-- algorithm that belong to the HashAlgorithm type class.
--
-- > import Crypto.Hash
-- >
-- > sha1 :: ByteString -> Digest SHA1
-- > sha1 = hash
-- >
-- > hexSha3_512 :: ByteString -> String
-- > hexSha3_512 bs = show (hash bs :: Digest SHA3_512)
--
module Crypto.Hash
(
-- * Types
HashAlgorithm
, Context
, Digest
-- * Functions
, digestToHexByteString
, digestFromByteString
-- * hash methods parametrized by algorithm
, hashInitWith
, hashWith
-- * hash methods
, hashInit
, hashUpdates
, hashUpdate
, hashFinalize
, hashBlockSize
, hashDigestSize
, hash
, hashlazy
, module Crypto.Hash.Algorithms
) where
import Control.Monad
import Crypto.Hash.Types
import Crypto.Hash.Utils
import Crypto.Hash.Algorithms
import Foreign.Ptr (Ptr)
import Data.ByteString (ByteString)
import Crypto.Internal.ByteArray (ByteArrayAccess)
import qualified Crypto.Internal.ByteArray as B
import qualified Data.ByteString.Lazy as L
-- | Hash a strict bytestring into a digest.
hash :: (ByteArrayAccess ba, HashAlgorithm a) => ba -> Digest a
hash bs = hashFinalize $ hashUpdate hashInit bs
-- | Hash a lazy bytestring into a digest.
hashlazy :: HashAlgorithm a => L.ByteString -> Digest a
hashlazy lbs = hashFinalize $ hashUpdates hashInit (L.toChunks lbs)
-- | Return the hexadecimal (base16) bytestring of the digest
digestToHexByteString :: Digest a -> ByteString
digestToHexByteString = toHex . B.convert
-- | Initialize a new context for this hash algorithm
hashInit :: HashAlgorithm a
=> Context a
hashInit = doInit undefined B.allocAndFreeze
where
doInit :: HashAlgorithm a => a -> (Int -> (Ptr (Context a) -> IO ()) -> B.Bytes) -> Context a
doInit alg alloc = Context $ alloc (hashInternalContextSize alg) hashInternalInit
{-# NOINLINE hashInit #-}
-- | run hashUpdates on one single bytestring and return the updated context.
hashUpdate :: (ByteArrayAccess ba, HashAlgorithm a) => Context a -> ba -> Context a
hashUpdate ctx b = hashUpdates ctx [b]
-- | Update the context with a list of strict bytestring,
-- and return a new context with the updates.
hashUpdates :: (HashAlgorithm a, ByteArrayAccess ba)
=> Context a
-> [ba]
-> Context a
hashUpdates c l = doUpdates (B.copyAndFreeze c)
where doUpdates :: HashAlgorithm a => ((Ptr (Context a) -> IO ()) -> B.Bytes) -> Context a
doUpdates copy = Context $ copy $ \ctx ->
mapM_ (\b -> B.withByteArray b $ \d -> hashInternalUpdate ctx d (fromIntegral $ B.length b)) l
{-# NOINLINE hashUpdates #-}
-- | Finalize a context and return a digest.
hashFinalize :: HashAlgorithm a
=> Context a
-> Digest a
hashFinalize c = doFinalize undefined (B.copy c) (B.allocAndFreeze)
where doFinalize :: HashAlgorithm alg
=> alg
-> ((Ptr (Context alg) -> IO ()) -> IO B.Bytes)
-> (Int -> (Ptr (Digest alg) -> IO ()) -> B.Bytes)
-> Digest alg
doFinalize alg copy allocDigest =
Digest $ allocDigest (hashDigestSize alg) $ \dig ->
(void $ copy $ \ctx -> hashInternalFinalize ctx dig)
{-# NOINLINE hashFinalize #-}
-- | Initialize a new context for a specified hash algorithm
hashInitWith :: HashAlgorithm alg => alg -> Context alg
hashInitWith _ = hashInit
hashWith :: (ByteArrayAccess ba, HashAlgorithm alg) => alg -> ba -> Digest alg
hashWith _ = hash
digestFromByteString :: (HashAlgorithm a, ByteArrayAccess ba) => ba -> Maybe (Digest a)
digestFromByteString = from undefined
where
from :: (HashAlgorithm a, ByteArrayAccess ba) => a -> ba -> Maybe (Digest a)
from alg bs
| B.length bs == (hashDigestSize alg) = (Just $ Digest $ B.convert bs)
| otherwise = Nothing