-- | -- Module : Crypto.Random.AESCtr.Internal -- License : BSD-style -- Maintainer : Vincent Hanquez <vincent@snarc.org> -- Stability : stable -- Portability : unknown -- {-# LANGUAGE CPP #-} module Crypto.Random.AESCtr.Internal where import qualified Crypto.Cipher.AES as AES import Data.ByteString (ByteString) import qualified Data.ByteString as B import Data.Word import Data.Bits (xor) import Foreign.Ptr #if __GLASGOW_HASKELL__ > 704 import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr) #else import Foreign.ForeignPtr (unsafeForeignPtrToPtr) #endif import Foreign.Storable import qualified Data.ByteString.Internal as B data Word128 = Word128 {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64 {-| An opaque object containing an AES CPRNG -} data RNG = RNG {-# UNPACK #-} !Word128 -- iv {-# UNPACK #-} !Word128 -- cnt {-# UNPACK #-} !Int -- number of chunks generated since reseed {-# UNPACK #-} !AES.AES -- AES context getNbChunksGenerated :: RNG -> Int getNbChunksGenerated (RNG _ _ c _) = c put128 :: Word128 -> ByteString put128 (Word128 a b) = B.unsafeCreate 16 (write64 . castPtr) where write64 :: Ptr Word64 -> IO () write64 ptr = poke ptr a >> poke (ptr `plusPtr` 8) b get128 :: ByteString -> Word128 get128 (B.PS ps s _) = B.inlinePerformIO $ do let ptr = castPtr (unsafeForeignPtrToPtr ps `plusPtr` s) :: Ptr Word64 a <- peek ptr b <- peek (ptr `plusPtr` 8) return $ Word128 a b xor128 :: Word128 -> Word128 -> Word128 xor128 (Word128 a1 b1) (Word128 a2 b2) = Word128 (a1 `xor` a2) (b1 `xor` b2) add64 :: Word128 -> Word128 add64 (Word128 a b) = if nb < 64 then Word128 (a+1) nb else Word128 a nb where nb = b + 64 {- withBsPtr :: ByteString -> (Ptr Word8 -> IO a) -> IO a withBsPtr (B.PS ps s _) f = withForeignPtr ps $ \ptr -> f (ptr `plusPtr` s) -} makeParams :: ByteString -> (AES.AES, ByteString, ByteString) makeParams b = (key, cnt, iv) where key = AES.initAES $ B.take 32 left2 (cnt, left2) = B.splitAt 16 left1 (iv, left1) = B.splitAt 16 b chunkSize :: Int chunkSize = 1024 genNextChunk :: RNG -> (ByteString, RNG) genNextChunk (RNG iv counter nbChunks key) = (chunk, newrng) where newrng = RNG (get128 chunk) (add64 counter) (nbChunks+1) key chunk = AES.genCTR key bytes chunkSize bytes = put128 (iv `xor128` counter)