packages feed

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