{- |

"Internal" data structures representing precomputed HMAC keys and partial HMAC
contexts, supporting incremental computation and backtracking.

-}

module Crypto.PHKDF.HMAC.Subtle
  ( HmacCtx(..)
  , HmacKey(..)
  , hmacKey_ipad
  , hmacKey_opad
  ) where

import qualified Crypto.Hash.SHA256 as SHA256

-- | Fixed-size context representing the state of a partial HMAC computation
--   with a complete HMAC key and a partial message parameter.

data HmacCtx = HmacCtx
  { HmacCtx -> Ctx
hmacCtx_ipad :: !SHA256.Ctx
  , HmacCtx -> Ctx
hmacCtx_opad :: !SHA256.Ctx
  } deriving (HmacCtx -> HmacCtx -> Bool
(HmacCtx -> HmacCtx -> Bool)
-> (HmacCtx -> HmacCtx -> Bool) -> Eq HmacCtx
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: HmacCtx -> HmacCtx -> Bool
== :: HmacCtx -> HmacCtx -> Bool
$c/= :: HmacCtx -> HmacCtx -> Bool
/= :: HmacCtx -> HmacCtx -> Bool
Eq)

-- | A precomputed HMAC key. Computing an HMAC key costs two SHA256 blocks.
--
-- No additional blocks are incurred for keys that are 64 bytes or less in
-- length.  Keys that are longer than 64 bytes long must be first hashed
-- with SHA256 before the key can be derived, incurring extra blocks.
--
-- It is not uncommon that implementations of PBKDF2, HKDF, etc unnecessarily
-- redo this computation even though a single HMAC key is used repeatedly.
--
-- TODO: FIXME: this data structure is way larger than it should be.  We can
-- pack this into a single 64-byte bytestring, but right now it's 208 bytes
-- of data plus extra overhead.
--
-- On the other hand, this approach may actually be more efficient for the
-- core PHKDF algorithm as currently implemented.  Reducing the size of this
-- data structure while maintaining tight code involves some additional work
-- on cryptohash-sha256

newtype HmacKey = HmacKey { HmacKey -> HmacCtx
hmacKey_run :: HmacCtx } deriving (HmacKey -> HmacKey -> Bool
(HmacKey -> HmacKey -> Bool)
-> (HmacKey -> HmacKey -> Bool) -> Eq HmacKey
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: HmacKey -> HmacKey -> Bool
== :: HmacKey -> HmacKey -> Bool
$c/= :: HmacKey -> HmacKey -> Bool
/= :: HmacKey -> HmacKey -> Bool
Eq)

hmacKey_ipad :: HmacKey -> SHA256.Ctx
hmacKey_ipad :: HmacKey -> Ctx
hmacKey_ipad (HmacKey HmacCtx
ctx) = HmacCtx -> Ctx
hmacCtx_ipad HmacCtx
ctx

hmacKey_opad :: HmacKey -> SHA256.Ctx
hmacKey_opad :: HmacKey -> Ctx
hmacKey_opad (HmacKey HmacCtx
ctx) = HmacCtx -> Ctx
hmacCtx_opad HmacCtx
ctx