    {-# LANGUAGE ExistentialQuantification, MultiParamTypeClasses #-}

    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Crypto.Nettle.Hash.Types
    -- Copyright   :  (c) 2013 Stefan Bühler
    -- License     :  MIT-style (see the file COPYING)
    --
    -- Maintainer  :  stbuehler@web.de
    -- Stability   :  experimental
    -- Portability :  portable
    --
    -- Collection of internal types due to cyclic dependencies
    --
    -----------------------------------------------------------------------------

    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')

    {-|
    'HashAlgorithm' is a class that hash algorithms will implement. generating a digest is a 3 step procedure:

      * 'hashInit' to create a new context

      * 'hashUpdate' to hash data

      * 'hashFinalize' to extract the final digest

    The final digest has 'hashDigestSize' bytes, and the algorithm uses 'hashBlockSize' as internal block size.
    -}
    class HashAlgorithm a where
    	-- | Block size in bytes the hash algorithm operates on
    	hashBlockSize  :: Tagged a Int
    	-- | Digest size in bytes the hash algorithm returns
    	hashDigestSize :: Tagged a Int
    	-- | Name of the hash algorithm
    	hashName       :: Tagged a String
    	-- | Initialize a new context for this hash algorithm
    	hashInit       :: a
    	-- | Update the context with bytestring, and return a new context with the updates.
    	hashUpdate     :: a -> B.ByteString -> a
    	-- | Update the context with a lazy bytestring, and return a new context with the updates.
    	hashUpdateLazy :: a -> L.ByteString -> a
    	hashUpdateLazy a :: a
    a = (a -> ByteString -> a) -> a -> [ByteString] -> a
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl' a -> ByteString -> a
    forall a. HashAlgorithm a => a -> ByteString -> a
    hashUpdate a
    a ([ByteString] -> a)
    -> (ByteString -> [ByteString]) -> ByteString -> a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> [ByteString]
    L.toChunks
    	-- | Finalize a context and return a digest.
    	hashFinalize   :: a -> B.ByteString
    	-- | Use 'HashAlgorithm' for HMAC; can use a optimized variant or the default 'hmacInit' one
    	hashHMAC       :: B.ByteString -> Tagged a KeyedHash
    	hashHMAC = ByteString -> Tagged a KeyedHash
    forall a. HashAlgorithm a => ByteString -> Tagged a KeyedHash
    hmacInit

    {-|
    Helper to hash a single (strict) 'B.ByteString' in one step.

    Example:

    > untag (hash (fromString "abc") :: Tagged SHA256 B.ByteString)
    -}
    hash :: HashAlgorithm a => B.ByteString -> Tagged a B.ByteString
    hash :: ByteString -> Tagged a ByteString
    hash msg :: ByteString
    msg = a -> ByteString
    forall a. HashAlgorithm a => a -> ByteString
    hashFinalize (a -> ByteString) -> (a -> a) -> a -> ByteString
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> (a -> ByteString -> a) -> ByteString -> a -> a
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip a -> ByteString -> a
    forall a. HashAlgorithm a => a -> ByteString -> a
    hashUpdate ByteString
    msg (a -> ByteString) -> Tagged a a -> Tagged a ByteString
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> a -> Tagged a a
    forall a. a -> Tagged a a
    tagSelf a
    forall a. HashAlgorithm a => a
    hashInit
    {-|
    Untagged variant of 'hash'; takes a (possible 'undefined') typed 'HashAlgorithm' context as parameter.

    Example:

    > hash' (undefined :: SHA256) $ fromString "abc"
    -}
    hash' :: HashAlgorithm a => a -> B.ByteString -> B.ByteString
    hash' :: a -> ByteString -> ByteString
    hash' a :: a
    a = (Tagged a ByteString -> a -> ByteString)
    -> a -> Tagged a ByteString -> ByteString
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip Tagged a ByteString -> a -> ByteString
    forall a b. Tagged a b -> a -> b
    witness a
    a (Tagged a ByteString -> ByteString)
    -> (ByteString -> Tagged a ByteString) -> ByteString -> ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> Tagged a ByteString
    forall a. HashAlgorithm a => ByteString -> Tagged a ByteString
    hash

    {-|
    Helper to hash a single (lazy) 'L.ByteString' in one step.

    Example:

    > untag (hashLazy (fromString "abc") :: Tagged SHA256 L.ByteString)
    -}
    hashLazy :: HashAlgorithm a => L.ByteString -> Tagged a L.ByteString
    hashLazy :: ByteString -> Tagged a ByteString
    hashLazy msg :: ByteString
    msg = ByteString -> ByteString
    L.fromStrict (ByteString -> ByteString) -> (a -> ByteString) -> a -> ByteString
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> a -> ByteString
    forall a. HashAlgorithm a => a -> ByteString
    hashFinalize (a -> ByteString) -> (a -> a) -> a -> ByteString
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> (a -> ByteString -> a) -> ByteString -> a -> a
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip a -> ByteString -> a
    forall a. HashAlgorithm a => a -> ByteString -> a
    hashUpdateLazy ByteString
    msg (a -> ByteString) -> Tagged a a -> Tagged a ByteString
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> a -> Tagged a a
    forall a. a -> Tagged a a
    tagSelf a
    forall a. HashAlgorithm a => a
    hashInit
    {-|
    Untagged variant of 'hashLazy'; takes a (possible 'undefined') typed 'HashAlgorithm' context as parameter.

    Example:

    > hashLazy' (undefined :: SHA256) $ fromString "abc"
    -}
    hashLazy' :: HashAlgorithm a => a -> L.ByteString -> L.ByteString
    hashLazy' :: a -> ByteString -> ByteString
    hashLazy' a :: a
    a = (Tagged a ByteString -> a -> ByteString)
    -> a -> Tagged a ByteString -> ByteString
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip Tagged a ByteString -> a -> ByteString
    forall a b. Tagged a b -> a -> b
    witness a
    a (Tagged a ByteString -> ByteString)
    -> (ByteString -> Tagged a ByteString) -> ByteString -> ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> Tagged a ByteString
    forall a. HashAlgorithm a => ByteString -> Tagged a ByteString
    hashLazy

    {-|
    'KeyedHashAlgorithm' is a class for keyed hash algorithms that take a key and a message to produce a digest.
    The most popular example is 'HMAC'.
    -}
    class KeyedHashAlgorithm k where
    	-- | Digest size in bytes the keyed hash algorithm returns
    	implKeyedHashDigestSize :: Tagged k Int
    	-- | Name
    	implKeyedHashName :: Tagged k String
    	-- | Initialize state from a key
    	implKeyedHashInit :: B.ByteString -> k
    	-- | Add more message data to the state
    	implKeyedHashUpdate :: k -> B.ByteString -> k
    	-- | Add more lazy message data to the state
    	implKeyedHashUpdateLazy :: k -> L.ByteString -> k
    	implKeyedHashUpdateLazy k :: k
    k = (k -> ByteString -> k) -> k -> [ByteString] -> k
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl' k -> ByteString -> k
    forall k. KeyedHashAlgorithm k => k -> ByteString -> k
    implKeyedHashUpdate k
    k ([ByteString] -> k)
    -> (ByteString -> [ByteString]) -> ByteString -> k
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> [ByteString]
    L.toChunks
    	-- | Produce final digest
    	implKeyedHashFinalize :: k -> B.ByteString

    {-|
    'KeyedHash' hides the 'KeyedHashAlgorithm' implementation.
    -}
    data KeyedHash = forall k. KeyedHashAlgorithm k => KeyedHash !k

    {-|
    Untagged variant of 'implKeyedHashDigestSize'; takes a (possible 'undefined') key typed value from a 'KeyedHashAlgorithm' instance as parameter.
    -}
    keyedHashDigestSize :: KeyedHashAlgorithm k => k -> Int
    keyedHashDigestSize :: k -> Int
    keyedHashDigestSize k :: k
    k = Tagged k Int
    forall k. KeyedHashAlgorithm k => Tagged k Int
    implKeyedHashDigestSize Tagged k Int -> k -> Int
    forall a b. Tagged a b -> a -> b
    `witness` k
    k
    {-|
    Get 'implKeyedHashDigestSize' from a 'KeyedHash'
    -}
    keyedHashDigestSize' :: KeyedHash -> Int
    keyedHashDigestSize' :: KeyedHash -> Int
    keyedHashDigestSize' (KeyedHash k :: k
    k) = Tagged k Int
    forall k. KeyedHashAlgorithm k => Tagged k Int
    implKeyedHashDigestSize Tagged k Int -> k -> Int
    forall a b. Tagged a b -> a -> b
    `witness` k
    k
    {-|
    Untagged variant of 'implKeyedHashName'; takes a (possible 'undefined') key typed value from a 'KeyedHashAlgorithm' instance as parameter.
    -}
    keyedHashName :: KeyedHashAlgorithm k => k -> String
    keyedHashName :: k -> String
    keyedHashName k :: k
    k = Tagged k String
    forall k. KeyedHashAlgorithm k => Tagged k String
    implKeyedHashName Tagged k String -> k -> String
    forall a b. Tagged a b -> a -> b
    `witness` k
    k
    {-|
    Get 'implKeyedHashName' from a 'KeyedHash'
    -}
    keyedHashName' :: KeyedHash -> String
    keyedHashName' :: KeyedHash -> String
    keyedHashName' (KeyedHash k :: k
    k) = Tagged k String
    forall k. KeyedHashAlgorithm k => Tagged k String
    implKeyedHashName Tagged k String -> k -> String
    forall a b. Tagged a b -> a -> b
    `witness` k
    k
    {-|
    Initialize a 'KeyedHash' context from a @key@
    -}
    keyedHashInit :: KeyedHashAlgorithm k => B.ByteString {- ^ @key@ argument -} -> Tagged k KeyedHash
    keyedHashInit :: ByteString -> Tagged k KeyedHash
    keyedHashInit key :: ByteString
    key = k -> KeyedHash
    forall k. KeyedHashAlgorithm k => k -> KeyedHash
    KeyedHash (k -> KeyedHash) -> Tagged k k -> Tagged k KeyedHash
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> k -> Tagged k k
    forall a. a -> Tagged a a
    tagSelf (ByteString -> k
    forall k. KeyedHashAlgorithm k => ByteString -> k
    implKeyedHashInit ByteString
    key)
    {-|
    Untagged variant of 'keyedHashInit'; takes a (possible 'undefined') key typed value from a 'KeyedHashAlgorithm' instance as parameter.
    -}
    keyedHashInit' :: KeyedHashAlgorithm k => k -> B.ByteString -> KeyedHash
    keyedHashInit' :: k -> ByteString -> KeyedHash
    keyedHashInit' k :: k
    k key :: ByteString
    key = ByteString -> Tagged k KeyedHash
    forall k. KeyedHashAlgorithm k => ByteString -> Tagged k KeyedHash
    keyedHashInit ByteString
    key Tagged k KeyedHash -> k -> KeyedHash
    forall a b. Tagged a b -> a -> b
    `witness` k
    k
    {-|
    Add more message data to the context
    -}
    keyedHashUpdate :: KeyedHash -> B.ByteString -> KeyedHash
    keyedHashUpdate :: KeyedHash -> ByteString -> KeyedHash
    keyedHashUpdate (KeyedHash k :: k
    k) = k -> KeyedHash
    forall k. KeyedHashAlgorithm k => k -> KeyedHash
    KeyedHash (k -> KeyedHash) -> (ByteString -> k) -> ByteString -> KeyedHash
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . k -> ByteString -> k
    forall k. KeyedHashAlgorithm k => k -> ByteString -> k
    implKeyedHashUpdate k
    k
    {-|
    Add more lazy message data to the context
    -}
    keyedHashUpdateLazy :: KeyedHash -> L.ByteString -> KeyedHash
    keyedHashUpdateLazy :: KeyedHash -> ByteString -> KeyedHash
    keyedHashUpdateLazy (KeyedHash k :: k
    k) = k -> KeyedHash
    forall k. KeyedHashAlgorithm k => k -> KeyedHash
    KeyedHash (k -> KeyedHash) -> (ByteString -> k) -> ByteString -> KeyedHash
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . k -> ByteString -> k
    forall k. KeyedHashAlgorithm k => k -> ByteString -> k
    implKeyedHashUpdateLazy k
    k
    {-|
    Produce final digest
    -}
    keyedHashFinalize :: KeyedHash -> B.ByteString
    keyedHashFinalize :: KeyedHash -> ByteString
    keyedHashFinalize (KeyedHash k :: k
    k) = k -> ByteString
    forall k. KeyedHashAlgorithm k => k -> ByteString
    implKeyedHashFinalize k
    k
    {-|
    Helper to hash @key@ and @message@ in one step

    Example:

    > untag (keyedHash (fromString "secretkey") (fromString "secret message") :: Tagged (HMAC SHA256) B.ByteString)
    -}
    keyedHash :: KeyedHashAlgorithm k => B.ByteString -> B.ByteString -> Tagged k B.ByteString
    keyedHash :: ByteString -> ByteString -> Tagged k ByteString
    keyedHash key :: ByteString
    key msg :: ByteString
    msg = KeyedHash -> ByteString
    keyedHashFinalize (KeyedHash -> ByteString)
    -> (KeyedHash -> KeyedHash) -> KeyedHash -> ByteString
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> (KeyedHash -> ByteString -> KeyedHash)
    -> ByteString -> KeyedHash -> KeyedHash
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip KeyedHash -> ByteString -> KeyedHash
    keyedHashUpdate ByteString
    msg (KeyedHash -> ByteString)
    -> Tagged k KeyedHash -> Tagged k ByteString
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> ByteString -> Tagged k KeyedHash
    forall k. KeyedHashAlgorithm k => ByteString -> Tagged k KeyedHash
    keyedHashInit ByteString
    key
    {-|
    Untagged variant of 'keyedHash'; takes a (possible 'undefined') key typed value from a 'KeyedHashAlgorithm' instance as parameter.

    Example:

    > keyedHash' (undefined :: HMAC SHA256) (fromString "secretkey") (fromString "secret message")
    -}
    keyedHash' :: KeyedHashAlgorithm k => k -> B.ByteString -> B.ByteString -> B.ByteString
    keyedHash' :: k -> ByteString -> ByteString -> ByteString
    keyedHash' k :: k
    k key :: ByteString
    key msg :: ByteString
    msg = ByteString -> ByteString -> Tagged k ByteString
    forall k.
    KeyedHashAlgorithm k =>
    ByteString -> ByteString -> Tagged k ByteString
    keyedHash ByteString
    key ByteString
    msg Tagged k ByteString -> k -> ByteString
    forall a b. Tagged a b -> a -> b
    `witness` k
    k
    {-|
    Helper to hash @key@ and lazy @message@ in one step

    Example:

    > untag (keyedHashLazy (fromString "secretkey") (fromString "secret message") :: Tagged (HMAC SHA256) B.ByteString)
    -}
    keyedHashLazy :: KeyedHashAlgorithm k => B.ByteString -> L.ByteString -> Tagged k B.ByteString
    keyedHashLazy :: ByteString -> ByteString -> Tagged k ByteString
    keyedHashLazy key :: ByteString
    key msg :: ByteString
    msg = KeyedHash -> ByteString
    keyedHashFinalize (KeyedHash -> ByteString)
    -> (KeyedHash -> KeyedHash) -> KeyedHash -> ByteString
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> (KeyedHash -> ByteString -> KeyedHash)
    -> ByteString -> KeyedHash -> KeyedHash
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip KeyedHash -> ByteString -> KeyedHash
    keyedHashUpdateLazy ByteString
    msg (KeyedHash -> ByteString)
    -> Tagged k KeyedHash -> Tagged k ByteString
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> ByteString -> Tagged k KeyedHash
    forall k. KeyedHashAlgorithm k => ByteString -> Tagged k KeyedHash
    keyedHashInit ByteString
    key
    {-|
    Untagged variant of 'keyedHashLazy'; takes a (possible 'undefined') key typed value from a 'KeyedHashAlgorithm' instance as parameter.

    Example:

    > keyedHashLazy' (undefined :: HMAC SHA256) (fromString "secretkey") (fromString "secret message")
    -}
    keyedHashLazy' :: KeyedHashAlgorithm k => k -> B.ByteString -> L.ByteString -> B.ByteString
    keyedHashLazy' :: k -> ByteString -> ByteString -> ByteString
    keyedHashLazy' k :: k
    k key :: ByteString
    key msg :: ByteString
    msg = ByteString -> ByteString -> Tagged k ByteString
    forall k.
    KeyedHashAlgorithm k =>
    ByteString -> ByteString -> Tagged k ByteString
    keyedHashLazy ByteString
    key ByteString
    msg Tagged k ByteString -> k -> ByteString
    forall a b. Tagged a b -> a -> b
    `witness` k
    k

    {-|
    'HMAC' is a generic 'KeyedHashAlgorithm' instance to calculate the 'HMAC' based
    on a 'HashAlgorithm'
    -}
    data HMAC a = HMAC !a !a

    padZero :: Int -> B.ByteString -> B.ByteString
    padZero :: Int -> ByteString -> ByteString
    padZero len :: Int
    len s :: ByteString
    s = if Int
    len Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > ByteString -> Int
    B.length ByteString
    s then ByteString -> ByteString -> ByteString
    B.append ByteString
    s (ByteString -> ByteString) -> ByteString -> ByteString
    forall a b. (a -> b) -> a -> b
    $ Int -> Word8 -> ByteString
    B.replicate (Int
    len Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - ByteString -> Int
    B.length ByteString
    s) 0 else ByteString
    s

    instance HashAlgorithm a => KeyedHashAlgorithm (HMAC a) where
    	implKeyedHashDigestSize :: Tagged (HMAC a) Int
    implKeyedHashDigestSize = Tagged a Int -> Tagged (HMAC a) Int
    forall a x. HashAlgorithm a => Tagged a x -> Tagged (HMAC a) x
    rt Tagged a Int
    forall a. HashAlgorithm a => Tagged a Int
    hashDigestSize where
    		rt :: HashAlgorithm a => Tagged a x -> Tagged (HMAC a) x
    		rt :: Tagged a x -> Tagged (HMAC a) x
    rt = Tagged a x -> Tagged (HMAC a) x
    forall k1 k2 (s :: k1) b (t :: k2). Tagged s b -> Tagged t b
    retag
    	implKeyedHashName :: Tagged (HMAC a) String
    implKeyedHashName = Tagged a String -> Tagged (HMAC a) String
    forall a x. HashAlgorithm a => Tagged a x -> Tagged (HMAC a) x
    rt (Tagged a String -> Tagged (HMAC a) String)
    -> Tagged a String -> Tagged (HMAC a) String
    forall a b. (a -> b) -> a -> b
    $ ("HMAC-" String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++) (String -> String) -> Tagged a String -> Tagged a String
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Tagged a String
    forall a. HashAlgorithm a => Tagged a String
    hashName where
    		rt :: HashAlgorithm a => Tagged a x -> Tagged (HMAC a) x
    		rt :: Tagged a x -> Tagged (HMAC a) x
    rt = Tagged a x -> Tagged (HMAC a) x
    forall k1 k2 (s :: k1) b (t :: k2). Tagged s b -> Tagged t b
    retag
    	implKeyedHashInit :: ByteString -> HMAC a
    implKeyedHashInit key :: ByteString
    key = Tagged a (HMAC a) -> HMAC a
    forall k (s :: k) b. Tagged s b -> b
    untag (Tagged a (HMAC a) -> HMAC a) -> Tagged a (HMAC a) -> HMAC a
    forall a b. (a -> b) -> a -> b
    $ a -> Tagged a a
    forall a. a -> Tagged a a
    tagSelf a
    forall a. HashAlgorithm a => a
    hashInit Tagged a a -> (a -> Tagged a (HMAC a)) -> Tagged a (HMAC a)
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \i :: a
    i -> do
    		Int
    blockSize <- Tagged a Int
    forall a. HashAlgorithm a => Tagged a Int
    hashBlockSize
    		let key' :: ByteString
    key' = Int -> ByteString -> ByteString
    padZero Int
    blockSize (ByteString -> ByteString) -> ByteString -> ByteString
    forall a b. (a -> b) -> a -> b
    $ if ByteString -> Int
    B.length ByteString
    key Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    blockSize then a -> ByteString -> ByteString
    forall a. HashAlgorithm a => a -> ByteString -> ByteString
    hash' a
    i ByteString
    key else ByteString
    key
    		let o_key :: ByteString
    o_key = (Word8 -> Word8) -> ByteString -> ByteString
    B.map (Word8 -> Word8 -> Word8
    forall a. Bits a => a -> a -> a
    xor 0x5c) ByteString
    key'
    		let i_key :: ByteString
    i_key = (Word8 -> Word8) -> ByteString -> ByteString
    B.map (Word8 -> Word8 -> Word8
    forall a. Bits a => a -> a -> a
    xor 0x36) ByteString
    key'
    		HMAC a -> Tagged a (HMAC a)
    forall (m :: * -> *) a. Monad m => a -> m a
    return (HMAC a -> Tagged a (HMAC a)) -> HMAC a -> Tagged a (HMAC a)
    forall a b. (a -> b) -> a -> b
    $ a -> a -> HMAC a
    forall a. a -> a -> HMAC a
    HMAC (a -> ByteString -> a
    forall a. HashAlgorithm a => a -> ByteString -> a
    hashUpdate a
    i ByteString
    o_key) (a -> ByteString -> a
    forall a. HashAlgorithm a => a -> ByteString -> a
    hashUpdate a
    i ByteString
    i_key)
    	implKeyedHashUpdate :: HMAC a -> ByteString -> HMAC a
    implKeyedHashUpdate (HMAC o :: a
    o i :: a
    i) = a -> a -> HMAC a
    forall a. a -> a -> HMAC a
    HMAC a
    o (a -> HMAC a) -> (ByteString -> a) -> ByteString -> HMAC a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . a -> ByteString -> a
    forall a. HashAlgorithm a => a -> ByteString -> a
    hashUpdate a
    i
    	implKeyedHashUpdateLazy :: HMAC a -> ByteString -> HMAC a
    implKeyedHashUpdateLazy (HMAC o :: a
    o i :: a
    i) = a -> a -> HMAC a
    forall a. a -> a -> HMAC a
    HMAC a
    o (a -> HMAC a) -> (ByteString -> a) -> ByteString -> HMAC a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . a -> ByteString -> a
    forall a. HashAlgorithm a => a -> ByteString -> a
    hashUpdateLazy a
    i
    	implKeyedHashFinalize :: HMAC a -> ByteString
    implKeyedHashFinalize (HMAC o :: a
    o i :: a
    i) = a -> ByteString
    forall a. HashAlgorithm a => a -> ByteString
    hashFinalize (a -> ByteString) -> a -> ByteString
    forall a b. (a -> b) -> a -> b
    $ a -> ByteString -> a
    forall a. HashAlgorithm a => a -> ByteString -> a
    hashUpdate a
    o (ByteString -> a) -> ByteString -> a
    forall a b. (a -> b) -> a -> b
    $ a -> ByteString
    forall a. HashAlgorithm a => a -> ByteString
    hashFinalize a
    i

    {-|
    'hmacInit' is the default implementation for 'hashHMAC' and initializes a 'KeyedHash' to calculate
    the HMAC for a message with the given @key@.

    Example:

    > let c = untag (hmacInit (fromString "secretkey") :: Tagged SHA256 KeyedHash) in keyedHashFinalize $ keyedHashUpdate c (fromString "secret message")
    -}
    hmacInit :: HashAlgorithm a => B.ByteString {- ^ @key@ argument -} -> Tagged a KeyedHash
    hmacInit :: ByteString -> Tagged a KeyedHash
    hmacInit = Tagged (HMAC a) KeyedHash -> Tagged a KeyedHash
    forall a x. Tagged (HMAC a) x -> Tagged a x
    rt (Tagged (HMAC a) KeyedHash -> Tagged a KeyedHash)
    -> (ByteString -> Tagged (HMAC a) KeyedHash)
    -> ByteString
    -> Tagged a KeyedHash
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> Tagged (HMAC a) KeyedHash
    forall k. KeyedHashAlgorithm k => ByteString -> Tagged k KeyedHash
    keyedHashInit where
    	rt :: Tagged (HMAC a) x -> Tagged a x
    	rt :: Tagged (HMAC a) x -> Tagged a x
    rt = Tagged (HMAC a) x -> Tagged a x
    forall k1 k2 (s :: k1) b (t :: k2). Tagged s b -> Tagged t b
    retag

    {-|
    Untagged variant of 'hmacInit'; takes a (possible 'undefined') typed 'HashAlgorithm' context as parameter.

    Example:

    > keyedHashFinalize $ flip keyedHashUpdate (fromString "secret message") $ hmacInit' (undefined :: SHA256) (fromString "secretkey")
    -}
    hmacInit' :: HashAlgorithm a => a -> B.ByteString -> KeyedHash
    hmacInit' :: a -> ByteString -> KeyedHash
    hmacInit' a :: a
    a key :: ByteString
    key = ByteString -> Tagged a KeyedHash
    forall a. HashAlgorithm a => ByteString -> Tagged a KeyedHash
    hmacInit ByteString
    key Tagged a KeyedHash -> a -> KeyedHash
    forall a b. Tagged a b -> a -> b
    `witness` a
    a

    {-|
    calculate HMAC with a 'HashAlgorithm' for a @key@ and @message@

    Example:

    > untag (hmac (fromString "secretkey") (fromString "secret message") :: Tagged SHA256 B.ByteString)
    -}
    hmac :: HashAlgorithm a => B.ByteString {- ^ @key@ argument -} -> B.ByteString {- ^ @message@ argument -} -> Tagged a B.ByteString
    hmac :: ByteString -> ByteString -> Tagged a ByteString
    hmac key :: ByteString
    key = Tagged (HMAC a) ByteString -> Tagged a ByteString
    forall a x. Tagged (HMAC a) x -> Tagged a x
    rt (Tagged (HMAC a) ByteString -> Tagged a ByteString)
    -> (ByteString -> Tagged (HMAC a) ByteString)
    -> ByteString
    -> Tagged a ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> ByteString -> Tagged (HMAC a) ByteString
    forall k.
    KeyedHashAlgorithm k =>
    ByteString -> ByteString -> Tagged k ByteString
    keyedHash ByteString
    key where
    	rt :: Tagged (HMAC a) x -> Tagged a x
    	rt :: Tagged (HMAC a) x -> Tagged a x
    rt = Tagged (HMAC a) x -> Tagged a x
    forall k1 k2 (s :: k1) b (t :: k2). Tagged s b -> Tagged t b
    retag

    {-|
    Untagged variant of 'hmac'; takes a (possible 'undefined') typed 'HashAlgorithm' context as parameter.

    Example:

    > hmac' (undefined :: SHA256) (fromString "secretkey") (fromString "secret message")
    -}
    hmac' :: HashAlgorithm a => a -> B.ByteString -> B.ByteString -> B.ByteString
    hmac' :: a -> ByteString -> ByteString -> ByteString
    hmac' a :: a
    a key :: ByteString
    key msg :: ByteString
    msg = ByteString -> ByteString -> Tagged a ByteString
    forall a.
    HashAlgorithm a =>
    ByteString -> ByteString -> Tagged a ByteString
    hmac ByteString
    key ByteString
    msg Tagged a ByteString -> a -> ByteString
    forall a b. Tagged a b -> a -> b
    `witness` a
    a

    {-|
    calculate HMAC with a 'HashAlgorithm' for a @key@ and lazy @message@

    Example:

    > untag (hmacLazy (fromString "secretkey") (fromString "secret message") :: Tagged SHA256 B.ByteString)
    -}
    hmacLazy :: HashAlgorithm a => B.ByteString {- ^ @key@ argument -} -> L.ByteString {- ^ @message@ argument -} -> Tagged a B.ByteString
    hmacLazy :: ByteString -> ByteString -> Tagged a ByteString
    hmacLazy key :: ByteString
    key = Tagged (HMAC a) ByteString -> Tagged a ByteString
    forall a x. Tagged (HMAC a) x -> Tagged a x
    rt (Tagged (HMAC a) ByteString -> Tagged a ByteString)
    -> (ByteString -> Tagged (HMAC a) ByteString)
    -> ByteString
    -> Tagged a ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> ByteString -> Tagged (HMAC a) ByteString
    forall k.
    KeyedHashAlgorithm k =>
    ByteString -> ByteString -> Tagged k ByteString
    keyedHashLazy ByteString
    key where
    	rt :: Tagged (HMAC a) x -> Tagged a x
    	rt :: Tagged (HMAC a) x -> Tagged a x
    rt = Tagged (HMAC a) x -> Tagged a x
    forall k1 k2 (s :: k1) b (t :: k2). Tagged s b -> Tagged t b
    retag

    {-|
    Untagged variant of 'hmacLazy'; takes a (possible 'undefined') typed 'HashAlgorithm' context as parameter.

    Example:

    > hmacLazy' (undefined :: SHA256) (fromString "secretkey") (fromString "secret message")
    -}
    hmacLazy' :: HashAlgorithm a => a -> B.ByteString -> L.ByteString -> B.ByteString
    hmacLazy' :: a -> ByteString -> ByteString -> ByteString
    hmacLazy' a :: a
    a key :: ByteString
    key msg :: ByteString
    msg = ByteString -> ByteString -> Tagged a ByteString
    forall a.
    HashAlgorithm a =>
    ByteString -> ByteString -> Tagged a ByteString
    hmacLazy ByteString
    key ByteString
    msg Tagged a ByteString -> a -> ByteString
    forall a b. Tagged a b -> a -> b
    `witness` a
    a