    {-# LANGUAGE ForeignFunctionInterface         #-}
    {-# LANGUAGE MultiParamTypeClasses            #-}
    {-# LANGUAGE FlexibleInstances                #-}
    {-# LANGUAGE DataKinds                        #-}

    -- | Portable C implementation of ChaCha20.
    module Raaz.Cipher.ChaCha20.Implementation.CPortable
           ( implementation, chacha20Random
           ) where

    import Control.Monad.IO.Class   ( liftIO )
    import Foreign.Ptr              ( Ptr    )

    import Raaz.Core
    import Raaz.Cipher.Internal
    import Raaz.Cipher.ChaCha20.Internal

    -- | The portable c implementation of chacha20 cipher.
    implementation :: SomeCipherI ChaCha20
    implementation :: SomeCipherI ChaCha20
    implementation  = CipherI ChaCha20 ChaCha20Mem ChaCha20Mem -> SomeCipherI ChaCha20
    forall cipher encMem decMem.
    CipherM cipher encMem decMem =>
    CipherI cipher encMem decMem -> SomeCipherI cipher
    SomeCipherI CipherI ChaCha20 ChaCha20Mem ChaCha20Mem
    chacha20Portable

    -- | Chacha20 block transformation.
    foreign import ccall unsafe
      "raaz/cipher/chacha20/cportable.h raazChaCha20Block"
      c_chacha20_block :: Pointer      -- Message
                       -> Int          -- number of blocks
                       -> Ptr KEY      -- key
                       -> Ptr IV       -- iv
                       -> Ptr Counter  -- Counter value
                       -> IO ()

    -- | Encrypting/Decrypting a block of chacha20.
    chacha20Block :: Pointer -> BLOCKS ChaCha20 -> MT ChaCha20Mem ()
    chacha20Block :: Pointer -> BLOCKS ChaCha20 -> MT ChaCha20Mem ()
    chacha20Block msgPtr :: Pointer
    msgPtr nblocks :: BLOCKS ChaCha20
    nblocks = do Ptr KEY
    keyPtr <- (ChaCha20Mem -> MemoryCell KEY)
    -> MT (MemoryCell KEY) (Ptr KEY) -> MT ChaCha20Mem (Ptr KEY)
    forall (mT :: * -> * -> *) mem submem a.
    MemoryThread mT =>
    (mem -> submem) -> mT submem a -> mT mem a
    onSubMemory ChaCha20Mem -> MemoryCell KEY
    keyCell     MT (MemoryCell KEY) (Ptr KEY)
    forall (mT :: * -> * -> *) a.
    (MemoryThread mT, Storable a) =>
    mT (MemoryCell a) (Ptr a)
    getCellPointer
                                      Ptr IV
    ivPtr  <- (ChaCha20Mem -> MemoryCell IV)
    -> MT (MemoryCell IV) (Ptr IV) -> MT ChaCha20Mem (Ptr IV)
    forall (mT :: * -> * -> *) mem submem a.
    MemoryThread mT =>
    (mem -> submem) -> mT submem a -> mT mem a
    onSubMemory ChaCha20Mem -> MemoryCell IV
    ivCell      MT (MemoryCell IV) (Ptr IV)
    forall (mT :: * -> * -> *) a.
    (MemoryThread mT, Storable a) =>
    mT (MemoryCell a) (Ptr a)
    getCellPointer
                                      Ptr Counter
    ctrPtr <- (ChaCha20Mem -> MemoryCell Counter)
    -> MT (MemoryCell Counter) (Ptr Counter)
    -> MT ChaCha20Mem (Ptr Counter)
    forall (mT :: * -> * -> *) mem submem a.
    MemoryThread mT =>
    (mem -> submem) -> mT submem a -> mT mem a
    onSubMemory ChaCha20Mem -> MemoryCell Counter
    counterCell MT (MemoryCell Counter) (Ptr Counter)
    forall (mT :: * -> * -> *) a.
    (MemoryThread mT, Storable a) =>
    mT (MemoryCell a) (Ptr a)
    getCellPointer
                                      IO () -> MT ChaCha20Mem ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO () -> MT ChaCha20Mem ()) -> IO () -> MT ChaCha20Mem ()
    forall a b. (a -> b) -> a -> b
    $ Pointer -> Int -> Ptr KEY -> Ptr IV -> Ptr Counter -> IO ()
    c_chacha20_block Pointer
    msgPtr (BLOCKS ChaCha20 -> Int
    forall a. Enum a => a -> Int
    fromEnum BLOCKS ChaCha20
    nblocks) Ptr KEY
    keyPtr Ptr IV
    ivPtr Ptr Counter
    ctrPtr

    -- | The chacha20 randomness generator.
    chacha20Random :: Pointer -> BLOCKS ChaCha20 -> MT ChaCha20Mem ()
    chacha20Random :: Pointer -> BLOCKS ChaCha20 -> MT ChaCha20Mem ()
    chacha20Random = Pointer -> BLOCKS ChaCha20 -> MT ChaCha20Mem ()
    chacha20Block

    ---------------------- DANGEROUS CODE --------------------------------------

    -- | The chacha20 randomness generator. We have set the alignment to
    -- 32 because this allows gcc to further optimise the implementation.
    chacha20Portable :: CipherI ChaCha20 ChaCha20Mem ChaCha20Mem
    chacha20Portable :: CipherI ChaCha20 ChaCha20Mem ChaCha20Mem
    chacha20Portable = String
    -> String
    -> (Pointer -> BLOCKS ChaCha20 -> MT ChaCha20Mem ())
    -> Alignment
    -> CipherI ChaCha20 ChaCha20Mem ChaCha20Mem
    forall prim mem.
    String
    -> String
    -> (Pointer -> BLOCKS prim -> MT mem ())
    -> Alignment
    -> CipherI prim mem mem
    makeCipherI
                       "chacha20-cportable"
                       "Implementation of the chacha20 stream cipher (RFC7539)"
                       Pointer -> BLOCKS ChaCha20 -> MT ChaCha20Mem ()
    chacha20Block
                       32