加密的确定性伪随机字节

Deterministic pseudorandom bytes for crypto

有没有办法从高熵种子确定性地为 Go 中的加密生成随机字节?

我发现 crypto/rand,它对加密是安全的,但不是确定性的。

我找到了math/rand,它可以用种子初始化,但对加密不安全。

我发现 x/crypto/chacha20, and I was wondering if it would be safe to use XORKeyStreamsrc 值为 1s。种子将是密钥和随机数,可以用 crypto/rand.

生成

编辑

作为我所追求的示例,cryptonite 是主要的 Haskell 加密库,它有一个函数 drgNewSeed,您可以使用它来制作随机生成器来自种子。

在随机数生成中,首先,有限的随机性从计算机的不可预测的元素中获取。然后清除这种物理随机性可能存在的偏差,例如散列,然后使用伪随机数生成器 (PRNG) 将生成的更小 真正的随机性 扩展为许多。

PRNG 是确定性的,这意味着如果初始值(初始真正的随机性 - 种子)是已知的,那么其余的将是已知的。 永远保密

我们没有迷路,PRNG 的重要设计目标是输出不应从任何其他输出中预测。这是一个强烈的要求,表明不可能仅通过查看输出来学习内部状态。

Go 的 crypto/rand 使用底层系统功能来获得物理随机性。

On Linux and FreeBSD, Reader uses getrandom(2) if available, /dev/urandom otherwise. On OpenBSD, Reader uses getentropy(2). On other Unix-like systems, Reader reads from /dev/urandom. On Windows systems, Reader uses the CryptGenRandom API. On Wasm, Reader uses the Web Crypto API.

然后可以使用可能好的确定性 RBG,如 NIST 800-90

中定义的 Hash-DRGB、HMAC-DRGB 和 CTR-DRGB

您可以使用x/crypto/chacha20生成长确定性随机序列。 保持密钥和随机数固定和保密然后你将有一个确定性的 DRGB。它非常快,也可搜索。

是的,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-repeatedly and https://crypto.stackexchange.com/questions/15481/will-repeated-rounds-of-sha-512-provide-random-numbers/15488。)

您也可以使用块密码来做同样的事情,通过加密计数器或先前的输出。这与流密码模式所做的非常接近。你也可以手工完成。

如果您想深入了解,可以在 crypto.stackexchange.com 上搜索 "csprng stream cipher"。这是一个更好的寻求加密建议的地方,但我认为这是一个特定于编程的问题,所以属于这里。