Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly generate a random bytestring in haskell

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
like image 695
Fabian Barkhau Avatar asked Jan 02 '14 18:01

Fabian Barkhau


3 Answers

  1. You can use any instance of CryptoRandomGen, as Ganesh shows
  2. You can use System.Random for non-secure random values (see jamshidh's answer)
  3. You can just get entropy provided by the platform's underlying generator (this is what 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.

like image 146
Thomas M. DuBuisson Avatar answered Nov 13 '22 20:11

Thomas M. DuBuisson


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
like image 5
GS - Apologise to Monica Avatar answered Nov 13 '22 20:11

GS - Apologise to Monica


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.

like image 4
jamshidh Avatar answered Nov 13 '22 20:11

jamshidh