为什么在OpenSSL的随机数生成器中,未初始化的内存是安全可用的?

3

因此,距离一个Debian维护者臭名昭著地通过注释未初始化数据的用法使RNG种子变得可预测已经过去了11年多。

这个问题在网上引起了很多激烈的讨论,大部分关注点似乎都集中在批评审查流程或攻击相关开发人员上。

然而,我找不到有关该段代码实际存在的思考过程的任何信息。很多用户认为“最坏情况下,它不会有任何损害”-然而,这对我来说似乎完全违反直觉。

毕竟,从未初始化的内存读取会导致未定义的行为,这可能会导致鼻子恶魔、运行nethack或格式化您的硬盘。因此,我认为将这种逻辑引入任何程序-更不用说加密库-会使您距离彻底失败只有一个侵略性编译器优化的距离。

因此,我的问题是:
  • 我是否有什么误解?这实际上并没有引发不确定行为,而是在标准下被定义的行为?
  • 如果它确实引发了UB,那么为什么最初在OpenSSL中包含了这种行为?

1
我没有参与其中,所以请将此视为相对了解的猜测。有理论上的未定义行为(可能导致鼻妖等后果)和实际上的未定义行为(会得到一些不可预测的结果)。使用未定义行为并不是一个好的选择,但使用它的实际结果是在修复之前比修复之后更加随机。这是一个代码出现问题,然后用更多但不同的有问题的代码“修复”的情况——而且乍一看第二批代码也没有问题是不明显的。 - Jonathan Leffler
请参见nasal demons - Jonathan Leffler
值得一提的是,Debian社区曾向OpenSSL开发团队询问是否可以这么做。问题在于,他们并没有得到一个直截了当的“是”或“否”的答案。有些开发团队给出直白的答案就像律师诚实回答问题或者把果冻粘在墙上一样难。 - jww
2个回答

3

这个 Debian 的“修复”完全错误了。只有 ssleay_rand_add 函数向池中添加熵,而实际罪魁祸首在于调用的地方,应该在那里进行修正 - 因为函数本身没有任何问题。

所以基本上就是这种情况:

void add_entropy(void *buf, size_t length) {
    actually_add_entropy(buf, length);
}

int main(void) {
    char buf[256];

    // uninitialized local variable
    add_entropy(buf, length);

    // calculate more entropy to buf
    // ...
    add_entropy(buf, length);
}

如果您查看代码,问题在于第一个调用中传递的参数是真正的罪魁祸首,而第二个情况是可以的,因为现在已经初始化了 buf。正确的修复方法不是像下面这样“修复”add_entropy
void add_entropy(void *buf, size_t length) {
    // valgrind complains about buf being uninitialized so
    // actually_add_entropy(buf, length);
}

然而,他们试图消除未初始化数组的使用是正确的 - 除了UB和USB之外,这可能掩盖了代码中的严重问题 - 很可能熵仅从未初始化数据初始化,因此可能仍然可以猜测或控制 - 但这一次更难以注意到。

所以缓冲区并不总是未初始化的,只是在某些调用中 - 他们停止了在 所有 调用中添加它?这就解释了为什么“删除一个熵源”实际上意味着删除(几乎)所有 熵源。 - Treeston
@Treeston 没错。在 ssh-keygen 中,只有运行进程的 初始 值,即进程 ID,被用来作为随机生成器的种子。 - Antti Haapala -- Слава Україні
很遗憾,从加密工程的角度来看,存在两个高级别的失败。首先,用户没有正确使用库,因为他们没有种子生成器。OpenSSL的维基现在试图非常清楚地表明他们应该种子生成器。其次,库易于不正确使用,而更难以正确使用。我认为这是通过从OS生成器中提取种子来解决的。生成器仍然混合时间和PID等内容,但它们不是熵的唯一来源。这些变化使生成器更容易正确使用。 - jww

1

这并非如此,只是发生了(鼻妖的一种表现形式),足够好地运作。Debian删除UB是正确的。OpenSSL最初拥有如此糟糕的代码是错误的。


1
他们删除未初始化的内容是正确的,但删除所有熵是错误的。 - Antti Haapala -- Слава Україні

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