您引用的博客文章的表述非常不精确。但是,通过查看 KWallet 源代码,尤其是这个文件(用于密码散列)和那个文件(用于调用加密代码),我们看到以下内容:
通过重复应用 SHA-1 ,密码被扩展为大小为 20、40 或 56 字节(160、320 或 448 位)的对称密钥。即:
如果密码长度为 0 到 16 个字符,则密钥的长度为 20 字节,并且是对密码重复应用 SHA-1(2000 次)(密码经过哈希处理,然后再次对 20 字节输出进行哈希处理,并且再次,对于 SHA-1 的 2000 次调用)。
如果密码长度为 17 到 32 个字符,则密钥的长度为 40 个字节:前 16 个字符按上述处理,其余字符按类似处理,产生 20 个以上字节。
如果密码长度为 33 到 48 个字符,则密钥长度为 56 个字节:前 16 个字符按上述处理,第二个 16 个字节字符的块也是如此,然后是第三个块;然后第一个块的输出被截断为 16 个字节,总共 56 个字节。
如果密码长度为 49 个或更多字符,则 2000 次 SHA-1 调用将发生四次。对于第四块,将使用密码的其余部分。然后将四个 20 字节的输出分别截断为 14 字节,然后将它们连接起来,产生一个 56 字节的输出。
作为密码散列函数,它很差。它笨重而且不规则。它没有加盐,允许有效的并行攻击,当必须破坏几个 KWallet 实例时(像往常一样,“并行性”也意味着可以使用预先计算的表,从而消除 2000 SHA-1 调用的减慢效果)。此外,它会“拆分”密码,因此如果攻击者找到密码哈希,那么他可以独立于其他块攻击每个块。这意味着整个事物的安全性在很大程度上取决于 Blowfish 块密码在用作散列函数时的表现。充其量,这是一个研究不足的属性。
加密声称使用 CBC,但没有。在 CBC 中,您应该加密一系列块(使用 Blowfish 每块 8 个字节);在加密一个块之前,它首先与前一个加密块进行异或。对于第一个块,我们必须变出一个正式的“前一个块”,即初始化向量。CBC 需要随机 IV。
在 KWallet 代码中,我们看到该代码确实准备了一个大小合适的随机 IV……然后完全无法执行 CBC。对加密的调用是:
int rc = bf.encrypt(wholeFile.data(), wholeFile.size());
(第 291 行backendpersisthandler.cpp
)
查看此encrypt()
方法的实现,在 中cbc.cc
,我们看到它将创建一个与整个文件大小相同的临时缓冲区;然后用零填充它;然后将所有这些零与要加密的数据进行异或(这不会改变数据......);然后继续彼此独立地加密每个块。这确实是 ECB 模式,而不是 CBC。很明显,这是一个编程错误;但是,要吸取的教训是,由于 KWallet 似乎可以工作,因此没有检测到错误:无法通过功能测试安全性。
(这意味着计算出来的随机 IV 在这里什么都不做;它本身是加密的,但不会影响整个文件中的任何其他字节。)
众所周知, ECB 模式在以下方面很弱:它会泄露有关哪些块彼此相等的信息。对于未压缩的图片,这是致命的,如通常的企鹅图像所示。对于 KWallet 来说,这是一个令人担忧的来源:内部结构有一些冗余,这可能是可利用的,尽管需要一些努力来确定问题的严重程度。
请注意,由于密码未加盐,因此受相同密码保护的两个连续版本的 KWallet 将使用相同的对称密钥,因此 ECB 泄露的块等式适用于所有连续版本的 KWallet。
有一个自制的MAC。文件的最后 20 个字节(不包括一些填充)包含在其余部分上计算的 SHA-1 值。一般来说,这不是一个好的 MAC。如果加密使用 RC4 或 CTR 模式下的分组密码,那将是一个非常糟糕的 MAC。在 ECB 模式下使用分组密码(如此处使用的),它并没有那么糟糕,但仍然很差。
整个代码都散发着自制加密货币的味道,被不掌握这些概念的人拼凑在一起。这不好。另外,我发现代码在很多方面都很糟糕(例如,当密码块被散列时,重复调用 SHA-1 的循环被无情地重复;这是一种射击罪行)。为了“改进” KWallet,我建议删除整个代码并从头开始。
代码似乎包含一些可选的支持,不做密码散列和加密本身(正如我们所见,它做得很差),而是使用GnuPG。现在这是个好主意。GnuPG 实现了OpenPGP 格式,尽管它有所有缺点,但至少在密码学上是不错的(如果使用得当),而且 GnuPG 也被认为是一个相当不错的实现。要改善 KWallet 的存储,请查看您是否可以说服您的计算机使用此 GnuPG 支持代码和格式。