{- |

An alternate implementation of HMAC in terms of cryptohash-sha256, because
the HMAC implementation provided there doesn't support precomputed keys or
streaming inputs.  TODO: prepare a patch for cryptohash-sha256.

-}


module Crypto.PHKDF.HMAC
  ( HmacCtx
  , HmacKey
  , hmacKey_init
  , hmacKey_run
  , hmacCtx_init
  , hmacCtx_initFromHmacKey
  , hmacCtx_update
  , hmacCtx_updates
  , hmacCtx_finalize
  ) where

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

import           Crypto.PHKDF.HMAC.Subtle

-- | Precompute an HMAC key for some literal HMAC key.

hmacKey_init :: ByteString -> HmacKey
hmacKey_init :: ByteString -> HmacKey
hmacKey_init = HmacCtx -> HmacKey
HmacKey (HmacCtx -> HmacKey)
-> (ByteString -> HmacCtx) -> ByteString -> HmacKey
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> HmacCtx
hmacCtx_init

-- | Initialize a new empty HMAC context from a literal HMAC key.

hmacCtx_init :: ByteString -> HmacCtx
hmacCtx_init :: ByteString -> HmacCtx
hmacCtx_init ByteString
key =
    HmacCtx { hmacCtx_ipad :: Ctx
hmacCtx_ipad = Word8 -> Ctx
tweak Word8
0x36, hmacCtx_opad :: Ctx
hmacCtx_opad = Word8 -> Ctx
tweak Word8
0x5c }
  where
    tweak :: Word8 -> Ctx
tweak Word8
c = Ctx -> ByteString -> Ctx
SHA256.update Ctx
SHA256.init (ByteString -> Ctx) -> ByteString -> Ctx
forall a b. (a -> b) -> a -> b
$ (Word8 -> Word8) -> ByteString -> ByteString
B.map (Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
xor Word8
c) ByteString
k2
    k1 :: ByteString
k1 = if ByteString -> Int
B.length ByteString
key Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
64 then ByteString -> ByteString
SHA256.hash ByteString
key else ByteString
key
    k2 :: ByteString
k2 = ByteString -> ByteString -> ByteString
B.append ByteString
k1 (Int -> Word8 -> ByteString
B.replicate (Int
64 Int -> Int -> Int
forall a. Num a => a -> a -> a
- ByteString -> Int
B.length ByteString
k1) Word8
0)

-- | Initialize a new empty HMAC context from a precomputed HMAC key.

hmacCtx_initFromHmacKey :: HmacKey -> HmacCtx
hmacCtx_initFromHmacKey :: HmacKey -> HmacCtx
hmacCtx_initFromHmacKey = HmacKey -> HmacCtx
hmacKey_run

-- | Append a bytestring onto the end of the message argument to HMAC.

hmacCtx_update ::  ByteString -> HmacCtx -> HmacCtx
hmacCtx_update :: ByteString -> HmacCtx -> HmacCtx
hmacCtx_update ByteString
b (HmacCtx Ctx
ic Ctx
oc) = Ctx -> Ctx -> HmacCtx
HmacCtx (Ctx -> ByteString -> Ctx
SHA256.update Ctx
ic ByteString
b) Ctx
oc

-- | Append zero or more bytestrings onto the end of the message argument to
--   HMAC.

hmacCtx_updates :: [ByteString] -> HmacCtx -> HmacCtx
hmacCtx_updates :: [ByteString] -> HmacCtx -> HmacCtx
hmacCtx_updates [ByteString]
bs (HmacCtx Ctx
ic Ctx
oc) = Ctx -> Ctx -> HmacCtx
HmacCtx (Ctx -> [ByteString] -> Ctx
SHA256.updates Ctx
ic [ByteString]
bs) Ctx
oc

-- | Finish computing the final 32-byte hash for an HMAC context.

hmacCtx_finalize :: HmacCtx -> ByteString
hmacCtx_finalize :: HmacCtx -> ByteString
hmacCtx_finalize (HmacCtx Ctx
ic Ctx
oc) = ByteString
outer
  where
    inner :: ByteString
inner = Ctx -> ByteString
SHA256.finalize Ctx
ic
    outer :: ByteString
outer = Ctx -> ByteString
SHA256.finalize (Ctx -> ByteString -> Ctx
SHA256.update Ctx
oc ByteString
inner)