    -- |
    -- Module      : Crypto.MAC
    -- License     : BSD-style
    -- Maintainer  : Vincent Hanquez <vincent@snarc.org>
    -- Stability   : experimental
    -- Portability : unknown
    --
    -- Crypto hash generic MAC (Message Authentification Code) module
    --
    {-# LANGUAGE BangPatterns #-}
    module Crypto.MAC
        (
        -- * MAC algorithms
          HMAC(..)
        , hmac
        , hmacAlg
        -- ** Incremental MAC algorithms
        , HMACContext
        , hmacInit
        , hmacInitAlg
        , hmacUpdate
        , hmacFinalize
        ) where

    import Crypto.Hash
    import Data.ByteString (ByteString)
    import Data.Byteable
    import Data.Bits (xor)
    import qualified Data.ByteString as B

    -- -------------------------------------------------------------------------- --
    -- Incremental HMAC

    -- | Represent an ongoing HMAC state, that can be appended with 'hmacUpdate'
    -- and finalize to an HMAC with 'hmacFinalize'
    data HMACContext hashalg = HMACContext !(Context hashalg) !(Context hashalg)

    -- | Initialize a new incremental HMAC context
    hmacInit :: HashAlgorithm a
             => ByteString -- ^ Secret key
             -> HMACContext a
    hmacInit :: forall a. HashAlgorithm a => ByteString -> HMACContext a
    hmacInit ByteString
    secret = Context a -> Context a -> HMACContext a
    forall hashalg.
    Context hashalg -> Context hashalg -> HMACContext hashalg
    HMACContext Context a
    octx Context a
    ictx
        where ctxInit :: Context a
    ctxInit = Context a
    forall a. HashAlgorithm a => Context a
    hashInit
              ictx :: Context a
    ictx = Context a -> [ByteString] -> Context a
    forall a. HashAlgorithm a => Context a -> [ByteString] -> Context a
    hashUpdates Context a
    ctxInit [ByteString
    ipad]
              octx :: Context a
    octx = Context a -> [ByteString] -> Context a
    forall a. HashAlgorithm a => Context a -> [ByteString] -> Context a
    hashUpdates Context a
    ctxInit [ByteString
    opad]
              ipad :: ByteString
    ipad = (Word8 -> Word8) -> ByteString -> ByteString
    B.map (Word8 -> Word8 -> Word8
    forall a. Bits a => a -> a -> a
    xor Word8
    0x36) ByteString
    k'
              opad :: ByteString
    opad = (Word8 -> Word8) -> ByteString -> ByteString
    B.map (Word8 -> Word8 -> Word8
    forall a. Bits a => a -> a -> a
    xor Word8
    0x5c) ByteString
    k'

              k' :: ByteString
    k'  = ByteString -> ByteString -> ByteString
    B.append ByteString
    kt ByteString
    pad
              kt :: ByteString
    kt  = if ByteString -> Int
    B.length ByteString
    secret Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int -> Int
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    blockSize then Digest a -> ByteString
    forall a. Byteable a => a -> ByteString
    toBytes (ByteString -> Digest a
    hashF ByteString
    secret) else ByteString
    secret
              pad :: ByteString
    pad = Int -> Word8 -> ByteString
    B.replicate (Int -> Int
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    blockSize Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - ByteString -> Int
    B.length ByteString
    kt) Word8
    0
              hashF :: ByteString -> Digest a
    hashF = Context a -> Digest a
    forall a. HashAlgorithm a => Context a -> Digest a
    hashFinalize (Context a -> Digest a)
    -> (ByteString -> Context a) -> ByteString -> Digest a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Context a -> ByteString -> Context a
    forall a. HashAlgorithm a => Context a -> ByteString -> Context a
    hashUpdate Context a
    ctxInit
              blockSize :: Int
    blockSize = Context a -> Int
    forall a. HashAlgorithm a => Context a -> Int
    hashBlockSize Context a
    ctxInit

    -- | Initialize a new incremental HMAC context with a given hash algorithm.
    hmacInitAlg :: HashAlgorithm a
                => a           -- ^ the hash algorithm the actual value is unused.
                -> ByteString  -- ^ Secret key
                -> HMACContext a
    hmacInitAlg :: forall a. HashAlgorithm a => a -> ByteString -> HMACContext a
    hmacInitAlg a
    _ ByteString
    secret = ByteString -> HMACContext a
    forall a. HashAlgorithm a => ByteString -> HMACContext a
    hmacInit ByteString
    secret

    -- | Incrementally update a HMAC context
    hmacUpdate :: HashAlgorithm a
               => HMACContext a
               -> ByteString -- ^ Message to Mac
               -> HMACContext a
    hmacUpdate :: forall a.
    HashAlgorithm a =>
    HMACContext a -> ByteString -> HMACContext a
    hmacUpdate (HMACContext Context a
    octx Context a
    ictx) ByteString
    msg =
        Context a -> Context a -> HMACContext a
    forall hashalg.
    Context hashalg -> Context hashalg -> HMACContext hashalg
    HMACContext Context a
    octx (Context a -> ByteString -> Context a
    forall a. HashAlgorithm a => Context a -> ByteString -> Context a
    hashUpdate Context a
    ictx ByteString
    msg)

    -- | Finalize a HMAC context and return the HMAC.
    hmacFinalize :: HashAlgorithm a
                 => HMACContext a
                 -> HMAC a
    hmacFinalize :: forall a. HashAlgorithm a => HMACContext a -> HMAC a
    hmacFinalize (HMACContext Context a
    octx Context a
    ictx) =
        Digest a -> HMAC a
    forall a. Digest a -> HMAC a
    HMAC (Digest a -> HMAC a) -> Digest a -> HMAC a
    forall a b. (a -> b) -> a -> b
    $ Context a -> Digest a
    forall a. HashAlgorithm a => Context a -> Digest a
    hashFinalize (Context a -> Digest a) -> Context a -> Digest a
    forall a b. (a -> b) -> a -> b
    $ Context a -> [ByteString] -> Context a
    forall a. HashAlgorithm a => Context a -> [ByteString] -> Context a
    hashUpdates Context a
    octx [Digest a -> ByteString
    forall a. Byteable a => a -> ByteString
    toBytes (Digest a -> ByteString) -> Digest a -> ByteString
    forall a b. (a -> b) -> a -> b
    $ Context a -> Digest a
    forall a. HashAlgorithm a => Context a -> Digest a
    hashFinalize Context a
    ictx]