{-# LANGUAGE FlexibleContexts #-}
module Raaz.Random.ChaCha20PRG
( reseedMT, fillRandomBytesMT, RandomState(..)
) where
import Control.Applicative
import Control.Monad
import Foreign.Ptr (Ptr, castPtr)
import Prelude
import Raaz.Core
import Raaz.Cipher.ChaCha20.Internal
import Raaz.Cipher.ChaCha20.Recommendation
import Raaz.Entropy
maxCounterVal :: Counter
maxCounterVal = 1024 * 1024 * 1024
data RandomState = RandomState { chacha20State :: ChaCha20Mem
, auxBuffer :: RandomBuf
, remainingBytes :: MemoryCell (BYTES Int)
}
withAuxBuffer :: (Ptr something -> MT RandomState a) -> MT RandomState a
withAuxBuffer action = onSubMemory auxBuffer getBufferPointer >>= action . castPtr
getRemainingBytes :: MT RandomState (BYTES Int)
getRemainingBytes = onSubMemory remainingBytes extract
setRemainingBytes :: BYTES Int -> MT RandomState ()
setRemainingBytes = onSubMemory remainingBytes . initialise
instance Memory RandomState where
memoryAlloc = RandomState <$> memoryAlloc <*> memoryAlloc <*> memoryAlloc
unsafeToPointer = unsafeToPointer . chacha20State
newSample :: MT RandomState ()
newSample = do
seedIfReq
withAuxBuffer $ onSubMemory chacha20State . flip chacha20Random randomBufferSize
setRemainingBytes $ inBytes randomBufferSize
fillKeyIVWith fillExistingBytes
seed :: MT RandomState ()
seed = do onSubMemory (counterCell . chacha20State) $ initialise (0 :: Counter)
fillKeyIVWith getEntropy
seedIfReq :: MT RandomState ()
seedIfReq = do c <- onSubMemory (counterCell . chacha20State) extract
when (c > maxCounterVal) seed
fillKeyIVWith :: (BYTES Int -> Pointer -> MT RandomState a)
-> MT RandomState ()
fillKeyIVWith filler = let
keySize = sizeOf (undefined :: KEY)
ivSize = sizeOf (undefined :: IV)
in do onSubMemory (keyCell . chacha20State) getCellPointer >>= void . filler keySize . castPtr
onSubMemory (ivCell . chacha20State) getCellPointer >>= void . filler ivSize . castPtr
reseedMT :: MT RandomState ()
reseedMT = seed >> newSample
fillRandomBytesMT :: LengthUnit l => l -> Pointer -> MT RandomState ()
fillRandomBytesMT l = go (inBytes l)
where go m ptr
| m > 0 = do mGot <- fillExistingBytes m ptr
when (mGot <= 0) newSample
go (m - mGot) $ movePtr ptr mGot
| otherwise = return ()
fillExistingBytes :: BYTES Int -> Pointer -> MT RandomState (BYTES Int)
fillExistingBytes req ptr = withAuxBuffer $ \ sptr -> do
r <- getRemainingBytes
let m = min r req
l = r - m
tailPtr = movePtr sptr l
in do
memcpy (destination ptr) (source tailPtr) m
memset tailPtr 0 m
setRemainingBytes l
return m