crypto-rng 0.3.0.0 → 0.3.0.1
raw patch · 3 files changed
+37/−18 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- ChangeLog.md +4/−0
- crypto-rng.cabal +1/−1
- src/Crypto/RNG.hs +32/−17
ChangeLog.md view
@@ -1,5 +1,9 @@ # Revision history for crypto-rng +## 0.3.0.1 -- 2022-02-24++* Improve performance with multiple capabilities.+ ## 0.3.0.0 -- 2022-02-21 * Use the entropy package instead of DRBG.
crypto-rng.cabal view
@@ -1,5 +1,5 @@ name: crypto-rng-version: 0.3.0.0+version: 0.3.0.1 synopsis: Cryptographic random number generator. description: Convenient wrapper for the source of random bytes
src/Crypto/RNG.hs view
@@ -35,6 +35,7 @@ import Control.Monad.Trans.Control import Data.Bits import Data.ByteString (ByteString)+import Data.Primitive.SmallArray import System.Entropy import qualified Data.ByteString as BS import qualified Data.ByteString.Short as SBS@@ -43,13 +44,10 @@ import Crypto.RNG.Class -- | The random number generator state.-newtype CryptoRNGState = CryptoRNGState (MVar Buffer)+data CryptoRNGState = CryptoRNGState !Int !(SmallArray (MVar Buffer)) -- | A buffer of random bytes for immediate consumption.-data Buffer = Buffer- { maxSize :: !Int- , bytes :: !BS.ByteString- }+newtype Buffer = Buffer { bytes :: BS.ByteString } instance R.StatefulGen CryptoRNGState IO where uniformWord8 st = mkWord <$> randomBytesIO 1 st@@ -65,40 +63,57 @@ -- | Create a new 'CryptoRNGState' based on system entropy with a buffer size of -- 32KB.+--+-- One buffer per capability is created. newCryptoRNGState :: MonadIO m => m CryptoRNGState newCryptoRNGState = newCryptoRNGStateSized $ 32 * 1024 --- | Create a new 'CryptoRNGState' based on system entropy with a buffer of+-- | Create a new 'CryptoRNGState' based on system entropy with buffers of -- specified size.+--+-- One buffer per capability is created. newCryptoRNGStateSized :: MonadIO m => Int -- ^ Buffer size. -> m CryptoRNGState-newCryptoRNGStateSized bufferSize = liftIO $ do- when (bufferSize <= 0) $ do+newCryptoRNGStateSized maxBufSize = liftIO $ do+ when (maxBufSize <= 0) $ do error "Buffer size must be larger than 0"- CryptoRNGState <$> newMVar (Buffer bufferSize BS.empty)+ n <- getNumCapabilities+ bufs <- replicateM n . newMVar $ Buffer BS.empty+ pure $ CryptoRNGState maxBufSize (smallArrayFromListN n bufs) -- | Generate a number of cryptographically secure random bytes. randomBytesIO :: Int -> CryptoRNGState -> IO ByteString-randomBytesIO n (CryptoRNGState rng) = modifyMVar rng $ \buf -> do- (rs, newBuf) <- generateBytes buf n []- pure (newBuf, BS.concat rs)+randomBytesIO n (CryptoRNGState maxBufSize bufs) = do+ (cid, _) <- threadCapability =<< myThreadId+ let mbuf = bufs `indexSmallArray` (cid `rem` sizeofSmallArray bufs)+ modifyMVar mbuf $ \buf -> do+ -- Unroll the first step of 'generateBytes' as the vast majority of time+ -- it's enough to get the full amount of requested bytes.+ let (r, newBytes) = BS.splitAt n (bytes buf)+ let k = n - BS.length r+ if k <= 0+ then newBytes `seq` pure (Buffer newBytes, r)+ else do+ (rs, newBuf) <- generateBytes maxBufSize buf k [r]+ pure (newBuf, BS.concat rs) generateBytes- :: Buffer+ :: Int+ -> Buffer -> Int -> [BS.ByteString] -> IO ([BS.ByteString], Buffer)-generateBytes buf n acc = do+generateBytes maxBufSize buf n acc = do (r, newBytes) <- BS.splitAt n <$> if BS.null (bytes buf)- then getEntropy (maxSize buf)+ then getEntropy maxBufSize else pure (bytes buf)- let newBuf = buf { bytes = newBytes }+ let newBuf = Buffer newBytes k = n - BS.length r newBuf `seq` if k <= 0 then pure (r : acc, newBuf)- else generateBytes newBuf k (r : acc)+ else generateBytes maxBufSize newBuf k (r : acc) ----------------------------------------