raaz-0.3.0: indef/keyed/hash/Mac/Implementation.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RecordWildCards #-}
-- | An implementation for simple MAC which is based on a
-- cryptographic hash. This construction is safe only for certain
-- hashes like blake2 and therefore should not be used
-- indiscriminately. In particular, sha2 hashes should not be used in
-- this mode as they are prone to length extension attack.
--
-- If you want to use sha2 hashs for message authentication, you
-- should make use of the more complicated HMAC construction.
--
module Mac.Implementation
( Prim
, name
, description
, Internals
, BufferAlignment
, BufferPtr
, processBlocks
, processLast
, additionalBlocks
, Key (..)
) where
import Data.ByteString as BS
import Raaz.Core
import Raaz.Core.Transfer.Unsafe
import Raaz.Primitive.Keyed.Internal
import qualified Implementation as Base
import qualified Utils as U
import qualified Buffer as B
type Prim = Keyed Base.Prim
-- | Name of the implementation.
name :: String
name = Base.name ++ "-keyed-hash"
-- | Description of the implementation.
description :: String
description = "Implementation of a MAC based on simple keyed hashing that makes use of "
++ Base.name
++ " implementation."
type BufferAlignment = Base.BufferAlignment
type BufferPtr = AlignedBlockPtr BufferAlignment Prim
toKeyedBlocks :: BlockCount Base.Prim -> BlockCount Prim
toKeyedBlocks = toEnum . fromEnum
fromKeyedBlocks :: BlockCount Prim -> BlockCount Base.Prim
fromKeyedBlocks = toEnum . fromEnum
-- | The additional space required in the buffer for processing the data.
additionalBlocks :: BlockCount Prim
additionalBlocks = toKeyedBlocks Base.additionalBlocks
trim :: Key (Keyed Base.Prim) -> BS.ByteString
trim (Key hKey) = BS.take sz hKey
where sz = fromEnum $ sizeOf (Proxy :: Proxy Base.Prim)
-- | The internal memory used by the implementation.
data Internals = MACInternals { hashInternals :: Base.Internals
, keyBuffer :: B.Buffer 1
, atStart :: MemoryCell Bool
-- Flag to check whether the key has been processed or not.
-- see the note on Delayed key processing
}
-- | Process the key inside the buffer with the process Buffer
-- function.
processKey :: Internals -> IO ()
processKey MACInternals{..} = U.processBuffer keyBuffer hashInternals
-- | Process the key in the buffer with the processLast function.
processKeyLast :: Internals -> IO ()
processKeyLast MACInternals{..} = Base.processLast bufPtr bufsz hashInternals
where bufPtr = B.unsafeGetBufferPointer keyBuffer
bufsz = inBytes $ blocksOf 1 (Proxy :: Proxy Base.Prim)
instance Memory Internals where
memoryAlloc = MACInternals <$> memoryAlloc <*> memoryAlloc <*> memoryAlloc
unsafeToPointer = unsafeToPointer . hashInternals
-- * Delayed key processing ::
--
-- It would look like the initialisation step is pretty straight
-- forward. Write the padded key to the buffer and then run process
-- blocks on it. This will work as long as the message that needs to
-- be authenticated is at-least 1 byte long.
--
-- For null bytes the padded key block is the last block and hashes
-- like blake2 pass a different finalisation flag for the last
-- block. At initialisation we cannot predict whether the message we
-- are about to see is empty or not. So we keep everything ready
-- (i.e. write the key into the keybuffer) and mark a flag that says
-- we are at the start of the message processing. The first time we
-- call processBlocks or processLast, will have to do the appropriate
-- initialisation and then proceed from there on.
instance Initialisable Internals (Key (Keyed Base.Prim)) where
initialise hKey imem
= do initialise hash0 $ hashInternals imem
writeKeyIntoBuffer $ keyBuffer imem
initialise True $ atStart imem
where kbs = trim hKey
hash0 :: Base.Prim
hash0 = hashInit $ Raaz.Core.length kbs
keyWrite = padWrite 0 (blocksOf 1 proxyPrim) $ writeByteString kbs
writeKeyIntoBuffer = unsafeTransfer keyWrite . B.unsafeGetBufferPointer
proxyPrim = Proxy :: Proxy Base.Prim
instance Extractable Internals Prim where
extract = fmap unsafeToKeyed . extract . hashInternals
-- | The function that process bytes in multiples of the block size of
-- the primitive.
processBlocks :: BufferPtr
-> BlockCount Prim
-> Internals
-> IO ()
processBlocks aptr blks imem@MACInternals{..} = do
start <- extract atStart
when start $ do processKey imem
initialise False atStart
Base.processBlocks (castPointer aptr) (fromKeyedBlocks blks) hashInternals
-- | Process the last bytes of the stream.
processLast :: BufferPtr
-> BYTES Int
-> Internals
-> IO ()
processLast aptr sz imem@MACInternals{..} = do
start <- extract atStart
if start && sz == 0 then processKeyLast imem
else do when start $ processKey imem
Base.processLast (castPointer aptr) sz hashInternals