随机性在签名中的重要性
签名生成算法使用随机密钥k
作为短暂私钥/公钥对的基础。k的值并不重要,只要它是随机的。如果使用相同的值k在不同的消息(交易)上生成两个签名,那么任何人都可以计算出签名私钥。在签名算法中重用相同的k值的会导致私钥的暴露!
如果在两个不同的交易中,在签名算法中使用相同的值k,则私钥可以被计算并暴露给世界!
这不仅仅是一个理论上的可能性。我们已经看到比特币中几种不同实现的交易签名算法因为这个问题导致私人密钥暴露。人们由于无意中重复使用k值而导致资金被窃取。重用k值的最常见原因是随机数生成器未正确初始化。
为了避免这个漏洞,业界最佳实践不是用熵播种的随机数生成器生成k值,而是使用交易数据本身播种的确定性随机进程。这确保每个交易产生不同的k值。在互联网工程任务组(InternetEngineering Task Force)发布的RFC 6979中定义了k值的确定性初始化的行业标准算法。
如果你正在实现一种用于在区块链中签署交易的算法,则必须使用RFC 6979
或类似的确定性随机算法
来确保为每个交易生成不同的k值。
RFC 6979: 确定性随机算法
确定性使用数字签名算法(DSA)和 椭圆曲线数字签名算法(ECDSA)
该标准定义了确定性的数字签名生成过程: 此类签名与标准数字签名算法(DSA)和椭圆曲线数字签名算法(ECDSA)数字签名兼容,并且可以使用未经修改的验证程序进行处理,这些验证程序无需了解其中描述的过程。确定性签名保留了与数字签名相关联的密码安全性功能,但是由于它们不需要访问高质量随机性源,因此可以在各种环境中更轻松地实现。
- 签署相同的交易将生成相同的k值
- 签署不同的交易将生成不同的k值
// secp256k1库中的用确定性随机算法产生随机数k的方法
static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, unsigned int counter, const void *data) {
secp256k1_rfc6979_hmac_sha256_t rng;
unsigned int i;
secp256k1_rfc6979_hmac_sha256_initialize(&rng, key32, 32, msg32, 32, (const unsigned char*)data, data != NULL ? 32 : 0);
for (i = 0; i <= counter; i++) {
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
}
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
return 1;
}
参考: RFC 6979