我正在创建一个内部应用程序,它将用于生成和管理自签名证书和证书颁发机构。它的主要用途是生成我的客户端防火墙在SSL解密中使用的证书。
我的目标是有一个确定性的RNG,它可以被输入到RSA密钥生成(rsa.GenerateKey)或证书生成(x509.CreateCertificate)中,因为它们的随机性来源都接受io.Reader接口。我希望能够使用256位Bip39记忆恢复私钥(甚至证书)。
初始熵来自bit 39兼容的CSPRNG (任何可用的系统随机设备),它创建助记符以及512位PBKDF2-SHA 512种子(分为2256位密钥,一个用于RSA密钥,一个用于证书)。
下面是一个基于ChaCha20的密码的简单实现。我的问题是:
因为这将只用于生成私钥和证书,而不是在任何类型的正在进行的通信或其他数据流中使用,而且我只希望助记符用于生成和恢复,所以我使用的是一个静态的nonce (12个\x00字节)和一个零化的源缓冲区。当然,如果我不对缓冲区进行零化,它仍然是完全确定性的,但是输出值会根据缓冲区大小而变化,我认为这是不可取的。
考虑到纯文本和现在是已知的(都是\x00),这是出于安全考虑吗?
谢谢!
package rng
import (
"github.com/sirupsen/logrus"
"golang.org/x/crypto/chacha20"
)
/*
* Simple seedable RNG implementation using ChaCha20 as a backend
* Performance is far from optimal as the buffer is zeroed out before each iteration
* This is to guarantee that byte generation is independent of the buffer size
*/
type Gen struct {
buffer []byte // stores the ChaCha output buffer
i int // index of where data reading should start in the buffer
cipher *chacha20.Cipher // underlying ChaCha cipher
}
func NewGenerator(key, nonce []byte, bufsize int) (*Gen, error) {
// bounds restriction
key = key[:chacha20.KeySize]
nonce = nonce[:chacha20.NonceSize]
// create the underlying chacha20 cipher instance
cipher, err := chacha20.NewUnauthenticatedCipher(key, nonce)
if err != nil {
logrus.Error("Could not create cipher: %v", err)
return nil, err
}
// create the generator with the desired buffer size
g := &Gen{
buffer: make([]byte, bufsize),
i: 0,
cipher: cipher,
}
// initialize the generator
g.next()
return g, nil
}
func (g *Gen) next() {
zeroize(g.buffer)
g.cipher.XORKeyStream(g.buffer, g.buffer)
g.i = 0
}
func (g *Gen) getBytes(n int, dst []byte) int {
max := cap(g.buffer) - g.i
if n >= max {
n = max
defer g.next()
} else {
defer func() { g.i += n }()
}
return copy(dst, g.buffer[g.i:g.i+n])
}
func (g *Gen) Read(dst []byte) (int, error) {
// total size of the destination buffer and a counter to keep track of the number of bytes
size := len(dst)
ctr := 0
for size > 0 {
// retrieve bytes from the buffer, it will automatically re-generate if needed
i := g.getBytes(size, dst[ctr:])
ctr += i
size -= i
}
return ctr, nil
}
func zeroize(buf []byte) int {
// set all values to 0 for the buffer
size := len(buf)
for i := 0; i < size; i++ {
buf[i] = 0
}
return size
}发布于 2022-08-02 10:41:46
考虑到纯文本和现在是已知的(所有\x00),这是否与安全性有关?
TL博士:没有。
这并不是一个问题,纯文本和现在是已知的。也不担心纯文本是不变的。从理论上讲,现在是已知的常量这一事实可能是一个安全问题,因为它允许通过使用不同密钥对问题的几个实例进行键搜索来摊销攻击成本。在这种所谓的多目标攻击中,查找密钥的预期工作量随着目标密钥的数量(更糟糕的是线性)而减少。攻击枚举密钥,使用该键运行CSPRNG --一个普通的nounce/明文--执行任何用于输出的操作(例如生成公钥),并测试结果是否在多个目标之间。这个最终的测试具有边际成本,并且成本随着目标数量的增加而略有增长,从而导致加速。
但是在上下文中,由于chacha20密钥是256位,假设它接近全熵,即使十亿个密钥也不会消耗30位的安全性,因此仍然有足够的安全性。
免责声明:我没有检查代码:crypto不是用于代码评审的,而且我对该语言也不太熟悉。
https://crypto.stackexchange.com/questions/101323
复制相似问题