{-# LANGUAGE BangPatterns #-} {-# LANGUAGE ScopedTypeVariables #-} -- | -- Module : Crypto.Hash -- License : BSD-style -- Maintainer : Vincent Hanquez -- 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 Context, Digest, -- * Functions digestFromByteString, -- * Hash methods parametrized by algorithm hashInitWith, hashWith, hashPrefixWith, -- * Hash methods hashInit, hashUpdates, hashUpdate, hashFinalize, hashFinalizePrefix, hashBlockSize, hashDigestSize, hash, hashPrefix, hashlazy, -- * Hash algorithms module Crypto.Hash.Algorithms, ) where import Basement.Block (Block, unsafeFreeze) import Basement.Block.Mutable (copyFromPtr, new) import Basement.Types.OffsetSize (CountOf (..)) import Crypto.Hash.Algorithms import Crypto.Hash.Types import Crypto.Internal.ByteArray (ByteArrayAccess) import qualified Crypto.Internal.ByteArray as B import Crypto.Internal.Compat (unsafeDoIO) import qualified Data.ByteString.Lazy as L import Data.Int (Int32) import Data.Word (Word8) import Foreign.Ptr (Ptr, plusPtr) -- | Hash a strict bytestring into a digest. hash :: (ByteArrayAccess ba, HashAlgorithm a) => ba -> Digest a hash bs = hashFinalize $ hashUpdate hashInit bs -- | Hash the first N bytes of a bytestring, with code path independent from N. hashPrefix :: (ByteArrayAccess ba, HashAlgorithmPrefix a) => ba -> Int -> Digest a hashPrefix = hashFinalizePrefix hashInit -- | Hash a lazy bytestring into a digest. hashlazy :: HashAlgorithm a => L.ByteString -> Digest a hashlazy lbs = hashFinalize $ hashUpdates hashInit (L.toChunks lbs) -- | Initialize a new context for this hash algorithm hashInit :: forall a. HashAlgorithm a => Context a hashInit = Context $ B.allocAndFreeze (hashInternalContextSize (undefined :: a)) $ \(ptr :: Ptr (Context a)) -> hashInternalInit ptr -- | run hashUpdates on one single bytestring and return the updated context. hashUpdate :: (ByteArrayAccess ba, HashAlgorithm a) => Context a -> ba -> Context a hashUpdate ctx b | B.null b = ctx | otherwise = hashUpdates ctx [b] -- | Update the context with a list of strict bytestring, -- and return a new context with the updates. hashUpdates :: forall a ba . (HashAlgorithm a, ByteArrayAccess ba) => Context a -> [ba] -> Context a hashUpdates c l | null ls = c | otherwise = Context $ B.copyAndFreeze c $ \(ctx :: Ptr (Context a)) -> mapM_ (\b -> B.withByteArray b (processBlocks ctx (B.length b))) ls where ls = filter (not . B.null) l -- process the data in 2GB chunks to fit in uint32_t and Int on 32 bit systems processBlocks ctx bytesLeft dataPtr | bytesLeft == 0 = return () | otherwise = do hashInternalUpdate ctx dataPtr (fromIntegral actuallyProcessed) processBlocks ctx (bytesLeft - actuallyProcessed) (dataPtr `plusPtr` actuallyProcessed) where actuallyProcessed = min bytesLeft (fromIntegral (maxBound :: Int32)) -- | Finalize a context and return a digest. hashFinalize :: forall a . HashAlgorithm a => Context a -> Digest a hashFinalize !c = Digest $ B.allocAndFreeze (hashDigestSize (undefined :: a)) $ \(dig :: Ptr (Digest a)) -> do ((!_) :: B.Bytes) <- B.copy c $ \(ctx :: Ptr (Context a)) -> hashInternalFinalize ctx dig return () -- | Update the context with the first N bytes of a bytestring and return the -- digest. The code path is independent from N but much slower than a normal -- 'hashUpdate'. The function can be called for the last bytes of a message, in -- order to exclude a variable padding, without leaking the padding length. The -- begining of the message, never impacted by the padding, should preferably go -- through 'hashUpdate' for better performance. hashFinalizePrefix :: forall a ba . (HashAlgorithmPrefix a, ByteArrayAccess ba) => Context a -> ba -> Int -> Digest a hashFinalizePrefix !c b len = Digest $ B.allocAndFreeze (hashDigestSize (undefined :: a)) $ \(dig :: Ptr (Digest a)) -> do ((!_) :: B.Bytes) <- B.copy c $ \(ctx :: Ptr (Context a)) -> B.withByteArray b $ \d -> hashInternalFinalizePrefix ctx d (fromIntegral $ B.length b) (fromIntegral len) dig return () -- | Initialize a new context for a specified hash algorithm hashInitWith :: HashAlgorithm alg => alg -> Context alg hashInitWith _ = hashInit -- | Run the 'hash' function but takes an explicit hash algorithm parameter hashWith :: (ByteArrayAccess ba, HashAlgorithm alg) => alg -> ba -> Digest alg hashWith _ = hash -- | Run the 'hashPrefix' function but takes an explicit hash algorithm parameter hashPrefixWith :: (ByteArrayAccess ba, HashAlgorithmPrefix alg) => alg -> ba -> Int -> Digest alg hashPrefixWith _ = hashPrefix -- | Try to transform a bytearray into a Digest of specific algorithm. -- -- If the digest is not the right size for the algorithm specified, then -- Nothing is returned. digestFromByteString :: forall a ba. (HashAlgorithm a, ByteArrayAccess ba) => ba -> Maybe (Digest a) digestFromByteString = from undefined where from :: a -> ba -> Maybe (Digest a) from alg bs | B.length bs == (hashDigestSize alg) = Just $ Digest $ unsafeDoIO $ copyBytes bs | otherwise = Nothing copyBytes :: ba -> IO (Block Word8) copyBytes ba = do muArray <- new count B.withByteArray ba $ \ptr -> copyFromPtr ptr muArray 0 count unsafeFreeze muArray where count = CountOf (B.length ba)