{-# LANGUAGE ExistentialQuantification, MultiParamTypeClasses #-}
module Crypto.Nettle.Hash.Types
( HashAlgorithm(..)
, hash
, hash'
, hashLazy
, hashLazy'
, KeyedHashAlgorithm(..)
, KeyedHash(..)
, keyedHashDigestSize
, keyedHashDigestSize'
, keyedHashName
, keyedHashName'
, keyedHashInit
, keyedHashInit'
, keyedHashUpdate
, keyedHashUpdateLazy
, keyedHashFinalize
, keyedHash
, keyedHash'
, keyedHashLazy
, keyedHashLazy'
, module Data.Tagged
, HMAC
, hmacInit
, hmacInit'
, hmac
, hmac'
, hmacLazy
, hmacLazy'
) where
import Data.Tagged
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import Control.Applicative ((<$>))
import Data.Bits (xor)
import Data.List (foldl')
class HashAlgorithm a where
hashBlockSize :: Tagged a Int
hashDigestSize :: Tagged a Int
hashName :: Tagged a String
hashInit :: a
hashUpdate :: a -> B.ByteString -> a
hashUpdateLazy :: a -> L.ByteString -> a
hashUpdateLazy a = foldl' hashUpdate a . L.toChunks
hashFinalize :: a -> B.ByteString
hashHMAC :: B.ByteString -> Tagged a KeyedHash
hashHMAC = hmacInit
hash :: HashAlgorithm a => B.ByteString -> Tagged a B.ByteString
hash msg = hashFinalize <$> flip hashUpdate msg <$> tagSelf hashInit
hash' :: HashAlgorithm a => a -> B.ByteString -> B.ByteString
hash' a = flip witness a . hash
hashLazy :: HashAlgorithm a => L.ByteString -> Tagged a L.ByteString
hashLazy msg = L.fromStrict <$> hashFinalize <$> flip hashUpdateLazy msg <$> tagSelf hashInit
hashLazy' :: HashAlgorithm a => a -> L.ByteString -> L.ByteString
hashLazy' a = flip witness a . hashLazy
class KeyedHashAlgorithm k where
implKeyedHashDigestSize :: Tagged k Int
implKeyedHashName :: Tagged k String
implKeyedHashInit :: B.ByteString -> k
implKeyedHashUpdate :: k -> B.ByteString -> k
implKeyedHashUpdateLazy :: k -> L.ByteString -> k
implKeyedHashUpdateLazy k = foldl' implKeyedHashUpdate k . L.toChunks
implKeyedHashFinalize :: k -> B.ByteString
data KeyedHash = forall k. KeyedHashAlgorithm k => KeyedHash !k
keyedHashDigestSize :: KeyedHashAlgorithm k => k -> Int
keyedHashDigestSize k = implKeyedHashDigestSize `witness` k
keyedHashDigestSize' :: KeyedHash -> Int
keyedHashDigestSize' (KeyedHash k) = implKeyedHashDigestSize `witness` k
keyedHashName :: KeyedHashAlgorithm k => k -> String
keyedHashName k = implKeyedHashName `witness` k
keyedHashName' :: KeyedHash -> String
keyedHashName' (KeyedHash k) = implKeyedHashName `witness` k
keyedHashInit :: KeyedHashAlgorithm k => B.ByteString -> Tagged k KeyedHash
keyedHashInit key = KeyedHash <$> tagSelf (implKeyedHashInit key)
keyedHashInit' :: KeyedHashAlgorithm k => k -> B.ByteString -> KeyedHash
keyedHashInit' k key = keyedHashInit key `witness` k
keyedHashUpdate :: KeyedHash -> B.ByteString -> KeyedHash
keyedHashUpdate (KeyedHash k) = KeyedHash . implKeyedHashUpdate k
keyedHashUpdateLazy :: KeyedHash -> L.ByteString -> KeyedHash
keyedHashUpdateLazy (KeyedHash k) = KeyedHash . implKeyedHashUpdateLazy k
keyedHashFinalize :: KeyedHash -> B.ByteString
keyedHashFinalize (KeyedHash k) = implKeyedHashFinalize k
keyedHash :: KeyedHashAlgorithm k => B.ByteString -> B.ByteString -> Tagged k B.ByteString
keyedHash key msg = keyedHashFinalize <$> flip keyedHashUpdate msg <$> keyedHashInit key
keyedHash' :: KeyedHashAlgorithm k => k -> B.ByteString -> B.ByteString -> B.ByteString
keyedHash' k key msg = keyedHash key msg `witness` k
keyedHashLazy :: KeyedHashAlgorithm k => B.ByteString -> L.ByteString -> Tagged k B.ByteString
keyedHashLazy key msg = keyedHashFinalize <$> flip keyedHashUpdateLazy msg <$> keyedHashInit key
keyedHashLazy' :: KeyedHashAlgorithm k => k -> B.ByteString -> L.ByteString -> B.ByteString
keyedHashLazy' k key msg = keyedHashLazy key msg `witness` k
data HMAC a = HMAC !a !a
padZero :: Int -> B.ByteString -> B.ByteString
padZero len s = if len > B.length s then B.append s $ B.replicate (len - B.length s) 0 else s
instance HashAlgorithm a => KeyedHashAlgorithm (HMAC a) where
implKeyedHashDigestSize = rt hashDigestSize where
rt :: HashAlgorithm a => Tagged a x -> Tagged (HMAC a) x
rt = retag
implKeyedHashName = rt $ ("HMAC-" ++) <$> hashName where
rt :: HashAlgorithm a => Tagged a x -> Tagged (HMAC a) x
rt = retag
implKeyedHashInit key = untag $ tagSelf hashInit >>= \i -> do
blockSize <- hashBlockSize
let key' = padZero blockSize $ if B.length key > blockSize then hash' i key else key
let o_key = B.map (xor 0x5c) key'
let i_key = B.map (xor 0x36) key'
return $ HMAC (hashUpdate i o_key) (hashUpdate i i_key)
implKeyedHashUpdate (HMAC o i) = HMAC o . hashUpdate i
implKeyedHashUpdateLazy (HMAC o i) = HMAC o . hashUpdateLazy i
implKeyedHashFinalize (HMAC o i) = hashFinalize $ hashUpdate o $ hashFinalize i
hmacInit :: HashAlgorithm a => B.ByteString -> Tagged a KeyedHash
hmacInit = rt . keyedHashInit where
rt :: Tagged (HMAC a) x -> Tagged a x
rt = retag
hmacInit' :: HashAlgorithm a => a -> B.ByteString -> KeyedHash
hmacInit' a key = hmacInit key `witness` a
hmac :: HashAlgorithm a => B.ByteString -> B.ByteString -> Tagged a B.ByteString
hmac key = rt . keyedHash key where
rt :: Tagged (HMAC a) x -> Tagged a x
rt = retag
hmac' :: HashAlgorithm a => a -> B.ByteString -> B.ByteString -> B.ByteString
hmac' a key msg = hmac key msg `witness` a
hmacLazy :: HashAlgorithm a => B.ByteString -> L.ByteString -> Tagged a B.ByteString
hmacLazy key = rt . keyedHashLazy key where
rt :: Tagged (HMAC a) x -> Tagged a x
rt = retag
hmacLazy' :: HashAlgorithm a => a -> B.ByteString -> L.ByteString -> B.ByteString
hmacLazy' a key msg = hmacLazy key msg `witness` a