phkdf-0.0.0.0: lib/Crypto/PHKDF/HMAC.hs
{- |
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 = HmacKey . hmacCtx_init
-- | Initialize a new empty HMAC context from a literal HMAC key.
hmacCtx_init :: ByteString -> HmacCtx
hmacCtx_init key =
HmacCtx { hmacCtx_ipad = tweak 0x36, hmacCtx_opad = tweak 0x5c }
where
tweak c = SHA256.update SHA256.init $ B.map (xor c) k2
k1 = if B.length key > 64 then SHA256.hash key else key
k2 = B.append k1 (B.replicate (64 - B.length k1) 0)
-- | Initialize a new empty HMAC context from a precomputed HMAC key.
hmacCtx_initFromHmacKey :: HmacKey -> HmacCtx
hmacCtx_initFromHmacKey = hmacKey_run
-- | Append a bytestring onto the end of the message argument to HMAC.
hmacCtx_update :: ByteString -> HmacCtx -> HmacCtx
hmacCtx_update b (HmacCtx ic oc) = HmacCtx (SHA256.update ic b) oc
-- | Append zero or more bytestrings onto the end of the message argument to
-- HMAC.
hmacCtx_updates :: [ByteString] -> HmacCtx -> HmacCtx
hmacCtx_updates bs (HmacCtx ic oc) = HmacCtx (SHA256.updates ic bs) oc
-- | Finish computing the final 32-byte hash for an HMAC context.
hmacCtx_finalize :: HmacCtx -> ByteString
hmacCtx_finalize (HmacCtx ic oc) = outer
where
inner = SHA256.finalize ic
outer = SHA256.finalize (SHA256.update oc inner)