I'm new to haskell and would like to write a function to generate random byte strings.
From my perspective Crypto.Random
(from crypto-api v0.3.1) seems to be the best module to use, but I can't figure it out.
I would like to do something like the following:
let size = 2048
let bytestring = randomByteString size
CryptoRandomGen
, as Ganesh showsSystem.Random
for non-secure random values (see jamshidh's answer)SystemRandom
does under the covers).Option 3: Getting Entropy From Your Platform
The last technique is the easiest, but has higher overhead on systems without the RDRAND instruction - it has to open a file, read, and close the file. This is using the entropy
package:
import System.Entropy
someFunc = do
randBytes <- getEntropy 2048
....
Option 1: Using a CryptoRandomGen instance
If you want a pure generator but seeded with a high-entropy seed then you can use any of the CryptoRandomGen
instantiated values from the DRBG
package:
import Crypto.Random.DRBG
Then just Ganesh's example (reproduced here) with a different signature on the newGenIO
:
do
g <- newGenIO :: IO CtrDRBG
case genBytes size g of
Left err -> error $ show err
Right (result, g2) -> return result
For the curious, I reviewed a few of the available secure RNGs on my blog - most of them don't conform to any standard (which isn't good, they appear to be totally off the cuff inventions of the programmer) with the exception of RDRAND
, HashDRBG
, and HmacDRBG
.
You need to do this in the IO
monad to initialise the entropy for the generator. Something like this fragment will do for a simple example, though in more complicated code you should probably keep hold of the generator g2
and re-use it later.
do
g <- newGenIO :: IO SystemRandom
case genBytes size of
Left err -> error $ show err
Right (result, g2) -> return result
There are many part here, so I will describe how this all works below.... But for now, here is the code
import Data.ByteString
import Data.Word8
import System.Random
randomBytes::Int->StdGen->[Word8]
randomBytes 0 _ = []
randomBytes count g = fromIntegral value:randomBytes (count - 1) nextG
where (value, nextG) = next g
randomByteString::Int->StdGen->ByteString
randomByteString count g = pack $ randomBytes count g
main = do
g <- getStdGen
let bytestring = randomByteString 2048 g
print bytestring
First of all, note that you need to supply a random generator. All computer programs that generate pseudo-random numbers need a random number generator, but most hide this behind the scenes using side effects. Since there are no side effects in Haskell, you need to manage state yourself (there is a random number generator monad that can do this for you, if you want to go back and hide some of the details, but since this is a learning exercise, I will leave it explicit).
All random generators need to be seeded.... You can create a random generator by supplying your own seed, but this is considered insecure, because anyone could reverse engineer your code and see the seed. Remember that pseudo-random generators are not actually random, but are a well defined reproducible series that follow from a mathematical formula and a given seed. (the numbers are only random in the sense that nothing statistical can be predicted about the resulting values without just running the algorithm itself).
Most OSs have some sort of API call that will generate something unpredictable at compile time (ie- a more real random number), but these run slowly, so a usual strategy is to run this once to seed the random generator. Running getStdGen
will do this for you, but because it needs to communicate with the OS, its type is IO a. I have done this in main, which is already type IO().
The function next
returns two things, a random Int, and the next random generator (if you run next on the same random generator, you get the same result.... Try it). You need to append this value to the resultant list, and feed the next generator into the function again to get the next value on the list.
Note that a ByteString represents a list of Word8 values, and next returns Int, so we need to convert from Int to Word8 using fromIntegral
. This then is converted to a ByteString using pack.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With