module Crypto.Hash
(
HashAlgorithm(..)
, HashFunctionBS
, HashFunctionLBS
, Context
, Digest
, digestToByteString
, digestToHexByteString
, hash
, hashlazy
, hashUpdate
, hashInitAlg
, MD2(..)
, MD4(..)
, MD5(..)
, SHA1(..)
, SHA224(..)
, SHA256(..)
, SHA384(..)
, SHA512(..)
, RIPEMD160(..)
, Tiger(..)
, SHA3_224(..)
, SHA3_256(..)
, SHA3_384(..)
, SHA3_512(..)
, Skein256_224(..)
, Skein256_256(..)
, Skein512_224(..)
, Skein512_256(..)
, Skein512_384(..)
, Skein512_512(..)
, Whirlpool(..)
, HMAC(..)
, hmac
, hmacAlg
) where
import Crypto.Hash.Types
import Crypto.Hash.Utils
import Data.ByteString (ByteString)
import Data.Byteable
import Data.Bits (xor)
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import qualified Crypto.Hash.MD2 as MD2
import qualified Crypto.Hash.MD4 as MD4
import qualified Crypto.Hash.MD5 as MD5
import qualified Crypto.Hash.SHA1 as SHA1
import qualified Crypto.Hash.SHA224 as SHA224
import qualified Crypto.Hash.SHA256 as SHA256
import qualified Crypto.Hash.SHA384 as SHA384
import qualified Crypto.Hash.SHA512 as SHA512
import qualified Crypto.Hash.SHA3 as SHA3
import qualified Crypto.Hash.RIPEMD160 as RIPEMD160
import qualified Crypto.Hash.Tiger as Tiger
import qualified Crypto.Hash.Skein256 as Skein256
import qualified Crypto.Hash.Skein512 as Skein512
import qualified Crypto.Hash.Whirlpool as Whirlpool
type HashFunctionBS a = ByteString -> Digest a
type HashFunctionLBS a = L.ByteString -> Digest a
hashUpdate :: HashAlgorithm a => Context a -> ByteString -> Context a
hashUpdate ctx b = hashUpdates ctx [b]
hash :: HashAlgorithm a => ByteString -> Digest a
hash bs = hashFinalize $ hashUpdate hashInit bs
hashlazy :: HashAlgorithm a => L.ByteString -> Digest a
hashlazy lbs = hashFinalize $ hashUpdates hashInit (L.toChunks lbs)
digestToHexByteString :: Digest a -> ByteString
digestToHexByteString = toHex . toBytes
#define DEFINE_INSTANCE(NAME, MODULENAME, BLOCKSIZE) \
data NAME = NAME deriving Show; \
instance HashAlgorithm NAME where \
{ hashInit = Context c where { (MODULENAME.Ctx c) = MODULENAME.init } \
; hashBlockSize ~(Context _) = BLOCKSIZE \
; hashUpdates (Context c) bs = Context nc where { (MODULENAME.Ctx nc) = MODULENAME.updates (MODULENAME.Ctx c) bs } \
; hashFinalize (Context c) = Digest $ MODULENAME.finalize (MODULENAME.Ctx c) \
; digestFromByteString bs = if B.length bs == len then (Just $ Digest bs) else Nothing where { len = B.length (MODULENAME.finalize MODULENAME.init) } \
};
#define DEFINE_INSTANCE_LEN(NAME, MODULENAME, LEN, BLOCKSIZE) \
data NAME = NAME deriving Show; \
instance HashAlgorithm NAME where \
{ hashInit = Context c where { (MODULENAME.Ctx c) = MODULENAME.init LEN } \
; hashBlockSize ~(Context _) = BLOCKSIZE \
; hashUpdates (Context c) bs = Context nc where { (MODULENAME.Ctx nc) = MODULENAME.updates (MODULENAME.Ctx c) bs } \
; hashFinalize (Context c) = Digest $ MODULENAME.finalize (MODULENAME.Ctx c) \
; digestFromByteString bs = if B.length bs == len then (Just $ Digest bs) else Nothing where { len = B.length (MODULENAME.finalize (MODULENAME.init LEN)) } \
};
DEFINE_INSTANCE(MD2, MD2, 16)
DEFINE_INSTANCE(MD4, MD4, 64)
DEFINE_INSTANCE(MD5, MD5, 64)
DEFINE_INSTANCE(SHA1, SHA1, 64)
DEFINE_INSTANCE(SHA224, SHA224, 64)
DEFINE_INSTANCE(SHA256, SHA256, 64)
DEFINE_INSTANCE(SHA384, SHA384, 128)
DEFINE_INSTANCE(SHA512, SHA512, 128)
DEFINE_INSTANCE(RIPEMD160, RIPEMD160, 64)
DEFINE_INSTANCE(Whirlpool, Whirlpool, 64)
DEFINE_INSTANCE(Tiger, Tiger, 64)
DEFINE_INSTANCE_LEN(SHA3_224, SHA3, 224, 144)
DEFINE_INSTANCE_LEN(SHA3_256, SHA3, 256, 136)
DEFINE_INSTANCE_LEN(SHA3_384, SHA3, 384, 104)
DEFINE_INSTANCE_LEN(SHA3_512, SHA3, 512, 72)
DEFINE_INSTANCE_LEN(Skein256_224, Skein256, 224, 32)
DEFINE_INSTANCE_LEN(Skein256_256, Skein256, 256, 32)
DEFINE_INSTANCE_LEN(Skein512_224, Skein512, 224, 64)
DEFINE_INSTANCE_LEN(Skein512_256, Skein512, 256, 64)
DEFINE_INSTANCE_LEN(Skein512_384, Skein512, 384, 64)
DEFINE_INSTANCE_LEN(Skein512_512, Skein512, 512, 64)
hashInitAlg :: HashAlgorithm alg => alg -> Context alg
hashInitAlg _ = hashInit
newtype HMAC a = HMAC { hmacGetDigest :: Digest a }
instance Byteable (HMAC a) where
toBytes (HMAC b) = toBytes b
instance Eq (HMAC a) where
(HMAC b1) == (HMAC b2) = constEqBytes (toBytes b1) (toBytes b2)
hmac :: HashAlgorithm a
=> ByteString
-> ByteString
-> HMAC a
hmac secret msg = doHMAC hashInit
where doHMAC :: HashAlgorithm a => Context a -> HMAC a
doHMAC ctxInit = HMAC $ hashF $ B.append opad (toBytes $ hashF $ B.append ipad msg)
where opad = B.map (xor 0x5c) k'
ipad = B.map (xor 0x36) k'
k' = B.append kt pad
kt = if B.length secret > fromIntegral blockSize then toBytes (hashF secret) else secret
pad = B.replicate (fromIntegral blockSize B.length kt) 0
hashF = hashFinalize . hashUpdate ctxInit
blockSize = hashBlockSize ctxInit
hmacAlg :: HashAlgorithm a
=> a
-> ByteString
-> ByteString
-> HMAC a
hmacAlg _ secret msg = hmac secret msg