密码学用的确定性伪随机字节

3
有没有一种方法可以从高熵种子确定性地生成Go加密的随机字节?我找到了crypto/rand,它对于加密是安全的,但不是确定性的。我找到了math/rand,可以用种子初始化,但对于加密来说不安全。我找到了x/crypto/chacha20,我想知道是否可以使用XORKeyStream和值为1s的src值。种子将是密钥和nonce,可以使用crypto/rand生成。 编辑 作为我所追求的例子,cryptonite是主要的Haskell加密库,它有一个函数drgNewSeed,可以使用种子生成一个随机生成器。

1
我的早期评论可能是错误的。这个链接可能会有所帮助:https://crypto.stackexchange.com/questions/39186/what-does-it-mean-for-a-random-number-generator-to-be-cryptographically-secure - Burak Serdar
2
“确定性”和“密码学安全”不是矛盾的吗?如果您需要进行测试,请使用接口并在测试中模拟它。 - icza
2
许多密码学随机比特生成器的输出仅在其内部状态保密时才是不可预测的。流密码是真实存在的。这个问题并没有任何矛盾或不合理之处。 - erickson
3
可能是该领域内的行话,但这个短语已被接受,并且可以提高文献检索结果的精度。例如,请参阅NIST的建议,使用确定性随机比特生成器进行随机数生成。 - erickson
1
KDF不一定慢。你想到的类别是PBKDF(基于密码的KDF)。HKDF(HMAC KDF)是一种快速的KDF。但我同意,KDF通常不是你要找的东西。你的问题恰好描述了CSPRNG。我不确定为什么会有这么多反对意见。所有PRNG(包括CSPRNG)都是确定性的。这就是“P”的含义。生成一个随机但确定性的值流是加密系统的一半工作方式。(其他人生成一个随机但确定性的排列流。) - Rob Napier
显示剩余9条评论
2个回答

4

是的,XORKeyStream对于这个问题来说是可以的,而且是一个很好的CSPRNG设计。流密码的整个重点在于它生成一串"有效随机"的值,给定一个种子(密钥和IV)。然后将这些流值与明文进行异或运算。在这个背景下,“有效随机”意味着没有“高效算法”(即多项式时间内运行的算法)能够区分这个序列和“真正随机”的序列。而这就是你想要的。

不过,没有必要引入ChaCha20。您可以使用内置的AES密钥。任何块密码都可以使用几种模式之一转换为流密码,例如CTR、OFB或CFB。这些模式之间的区别对于这个问题并不重要。

// Defining some seed, split across a "key" and an "iv"
key, _ := hex.DecodeString("6368616e676520746869732070617373")
iv, _ := hex.DecodeString("0123456789abcdef0123456789abcdef")

// We can turn a block cipher into a stream cipher, and AES is handy
block, err := aes.NewCipher(key)

if err != nil {
    panic(err)
}

// Convert block cipher into a stream cipher using a streaming mode like CTR
// OFB or CFB would work, too
stream := cipher.NewCTR(block, iv)

for x := 0; x < 10; x++ {
        // Create a fixed value of the size you want
    value := []byte{0}
    
    // Transform it to a random value
    stream.XORKeyStream(value, value)

    fmt.Printf("%d\n", value)
}

Playground

在这里,你可以使用几种不同的方法。你可以使用安全散列函数,比如SHA-256,来哈希计数器(选择一个随机的128位数,并不断递增它,每个值都要进行哈希)。或者你可以对上一个结果进行哈希运算(我听说过一些争议,关于重复哈希是否会影响哈希的安全性。请参见https://crypto.stackexchange.com/questions/19392/any-weakness-when-performing-sha-256-repeatedlyhttps://crypto.stackexchange.com/questions/15481/will-repeated-rounds-of-sha-512-provide-random-numbers/15488以获取更多信息)。

你也可以使用块密码来完成相同的操作,通过加密计数器或上一个输出值。这与流密码模式非常接近。你也可以手动完成这个过程。

如果你想深入了解这个问题,可以在crypto.stackexchange.com上搜索"csprng stream cipher"。那是一个更好的地方,可以寻求加密建议,但是在我看来,这是一个编程特定的问题,因此应该放在这里讨论。


3
在随机数生成中,首先从计算机的不可预测元素中收集有限的随机性。然后,这种物理随机性通过去除可能的偏差(例如哈希)进行清理,然后使用伪随机数生成器(PRNG)将结果较小的真实随机性拉伸成多个。
PRNG是确定性的,这意味着如果已知初始值(初始真实随机性-种子),则其余部分将被知道。始终保持此值秘密!
我们并没有迷失方向,PRNG的重要设计目标是输出不应该可以从任何其他输出中预测出来。这是一个强烈的要求,表明仅通过查看输出无法了解内部状态。
Go的crypto/rand使用底层系统功能来获取物理随机性。
在Linux和FreeBSD上,Reader使用getrandom(2)(如果可用),否则使用/dev/urandom。在OpenBSD上,Reader使用getentropy(2)。在其他类Unix系统上,Reader从/dev/urandom读取。在Windows系统上,Reader使用CryptGenRandom API。在Wasm上,Reader使用Web Crypto API。

然后,可以使用像Hash-DRGB、HMAC-DRGB和CTR-DRGB这样的可能良好的确定性RBG,如NIST 800-90中所定义的。

您可以使用x/crypto/chacha20生成长的确定性随机序列。 保持密钥和nonce不变且保密,那么您将拥有一个确定性DRGB。 它非常快速,也是可寻址的。


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