如何在Haskell中正确生成随机的字节串

11

我刚学习 Haskell,想编写一个生成随机字节字符串的函数。

从我的角度来看,Crypto.Random(来自 crypto-api v0.3.1)似乎是最好的模块可供使用,但我还是无法理解它。

我想要做类似下面的事情:

let size = 2048
let bytestring = randomByteString size
3个回答

8
  1. 你可以使用任何一个CryptoRandomGen的实例,就像Ganesh所展示的那样。
  2. 对于非安全的随机值,你可以使用System.Random(参见jamshidh的答案)。
  3. 你可以直接获取平台底层生成器提供的熵(这就是SystemRandom在幕后执行的操作)。

选项3:从你的平台获取熵

最后一种技术是最简单的,但在没有RDRAND指令的系统上具有更高的开销 - 它必须打开文件,读取并关闭文件。这是使用entropy包:

import System.Entropy

someFunc = do
    randBytes <- getEntropy 2048
    ....

选项1: 使用CryptoRandomGen实例

如果你想要一个纯生成器,但是使用高熵种子进行播种,那么可以使用DRBG包中任意一个已实例化的CryptoRandomGen值:

import Crypto.Random.DRBG

接下来是 Ganesh 的示例(在这里复制)使用不同的签名在 newGenIO 上:

do
  g <- newGenIO :: IO CtrDRBG
  case genBytes size g of
    Left err -> error $ show err
    Right (result, g2) -> return result

对于那些好奇的人,我在我的博客上审查了几种可用的安全随机数生成器 - 其中大多数都不符合任何标准(这不太好,它们似乎完全是程序员临时发明的),除了RDRANDHashDRBGHmacDRBG

我选择选项3,因为它正好是我想要的功能,我不介意开销,因为我将生成数千兆字节的数据,并且根据软件包描述,它应该具有强加密性能。谢谢! - Fabian Barkhau
cryptonite包中有一个Crypto.Random模块,其中还包含一个getRandomBytes函数。请参考https://www.stackage.org/haddock/lts-13.23/cryptonite-0.25/Crypto-Random.html。 - sbditto85

5

你需要在 IO 单子中执行此操作以初始化生成器的熵。以下代码片段可以用作简单示例,但在更复杂的代码中,您可能应该保留生成器 g2 并稍后重用它。

do
    g <- newGenIO :: IO SystemRandom
    case genBytes size of
        Left err -> error $ show err
        Right (result, g2) -> return result

对于不会出现性能瓶颈的一次性使用,直接使用 System.Entropy 模块更加简洁,可能也更少出错(请参见我的回答)。但我同意你的观点,在部署环境中持续使用 RNG,使用 SystemRandom 再加上像 MonadCryptoRandom 这样的单子是一个不错的函数式解决方案。 - Thomas M. DuBuisson

4

这里有很多部分,所以我稍后会在下面描述所有内容的工作方式.... 但现在,这就是代码

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

首先,需要注意的是您需要提供一个随机生成器。所有生成伪随机数的计算机程序都需要随机数生成器,但大多数都使用副作用隐藏在后台。由于Haskell中没有副作用,您需要自己管理状态(如果您想回去隐藏一些细节,可以使用随机数生成器monad来完成此操作,但由于这是一项学习练习,我将保留它显式)。
所有随机生成器都需要进行种子设置...您可以通过提供自己的种子创建一个随机生成器,但这被认为是不安全的,因为任何人都可以反向工程化您的代码并查看种子。请记住,伪随机生成器实际上并不是随机的,而是遵循数学公式和给定种子的明确定义可再现系列。(这些数字只有在运行算法本身时,才能预测有关结果值的任何统计信息)。
大多数操作系统都有某种API调用,可以在编译时生成某些不可预测的东西(即更真实的随机数),但这些运行速度较慢,因此通常的策略是运行一次以种子随机生成器。运行 getStdGen 将为您完成此操作,但由于它需要与操作系统通信,因此其类型为 IO a。我已经在 main 中完成了这个操作,它的类型已经是 IO()。
函数 next 返回两个值,一个随机 Int 和下一个随机生成器(如果您在相同的随机生成器上运行 next,则会获得相同的结果...试试看)。您需要将此值附加到结果列表中,并再次将下一个生成器输入函数以获取列表中的下一个值。
请注意,ByteString 表示 Word8 值的列表,而 next 返回 Int,因此我们需要使用 fromIntegral 将 Int 转换为 Word8。然后使用 pack 将其转换为 ByteString。

2
由于他正在查看加密API,我猜他需要比stdgen更好的随机数。 - Arjan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接