为什么 AES 的不同实现会产生不同的输出?

Why do different implementations of AES produce different output?

我觉得我对哈希函数及其所包含的契约有很好的理解。

输入 X 上的 SHA1 将始终产生相同的输出。您可以使用 Python 图书馆、Java 图书馆或笔和纸。它是一个函数,它是确定性的。我的 SHA1 和你的以及爱丽丝和鲍勃的一样。

据我了解,AES也是一个函数。你输入一些值,它会吐出密文。

那么,为什么会担心 Truecrypt(例如)是 "broken"?他们不是说 AES 坏了,而是说实现它的程序可能坏了。 AES 在理论上是可靠的。那么,为什么不能通过 Truecrypt 运行 一个文件,通过 "reference AES" 函数 运行 它,并验证结果是否相同?我知道这样绝对不行,但我不知道为什么。

是什么让 AES 在这方面不同于 SHA1?为什么 Truecrypt AES 输出的文件与 Schneier-Ifier* AES 不同,当它们都被赋予所有相同的输入时?

最后,我的问题归结为:

My_SHA1(X) == Bobs_SHA1(X) == ...等等

但是 TrueCrypt_AES(X) != HyperCrypt_AES(X) != VeraCrypt_AES(X) 等等。这是为什么呢?所有这些程序都包装了 AES,但是有不同的方法来确定诸如初始化向量之类的东西吗?

*如果我曾经写过一个文件加密程序,这将是我的文件加密程序的名称

在您给出的 SHA-1 示例中,函数只有一个输入,任何正确的 SHA-1 实现在提供相同输入数据时都应该产生与其他任何实现相同的输出。

然而,对于 AES,事情有点棘手,并且由于您没有指定 "AES" 的确切含义,这本身似乎可能是 感知 实现之间的差异。

首先,"AES" 不是单一算法,而是采用不同 密钥大小(128、192 或 256 位)的一系列算法。 AES 也是一种块密码,它需要一个 128 bits/16 字节的 纯文本 输入块,并使用密钥对其进行加密以产生单个 16 字节的输出块。

当然在实践中我们经常想一次加密超过16字节的数据,所以我们必须想办法重复应用AES算法来加密所有数据。天真地我们可以将它分成 16 字节的块并依次加密每个块,但这种模式(描述为电子密码本或 ECB)结果通常使用 horribly insecure. Instead, various other more secure modes,其中大部分需要初始化向量 (IV) 有助于确保使用相同密钥加密相同数据不会产生相同的密文(否则会泄露信息)。

这些模式中的大多数仍然在固定大小的数据块上运行,但同样我们经常希望加密不是块大小倍数的数据,因此我们必须使用某种形式的 padding,关于我们如何将消息填充到块大小的倍数的长度,同样有多种不同的可能性。

因此,将所有这些放在一起,如果以下 所有 相同,"AES" 的两个不同实现应该产生相同的输出:

  • 纯文本输入数据
  • 密钥(以及密钥大小)
  • IV
  • 模式(包括任何特定于模式的输入)
  • 填充

Iridium 涵盖了 TrueCrypt 和其他使用名义上相同 (AES) 算法的程序之间不同输出的许多原因。如果您只是检查实际的初始化向量,这些往往是使用 ECB 完成的。现在是使用 ECB 的唯一好时机——以确保算法本身得到正确实施。这是因为欧洲央行虽然不安全,但在没有 IV 的情况下也能正常工作,因此更容易检查 "apples to apples",尽管正如 Iridium 指出的那样,其他绊脚石仍然存在。

对于测试向量,密钥与纯文本一起指定。并且测试向量被指定为块大小的精确倍数。或者更具体地说,对于纯文本,它们的大小往往恰好是 1 个块。这样做是为了从可能的差异列表中删除填充和模式。因此,如果您在两个 AES 加密程序之间使用标准测试向量,则可以消除纯文本数据差异、密钥差异、IV、模式和填充的问题。

但请注意,您仍然可以有差异。 AES 与散列一样具有确定性,因此每次使用 AES 都可以获得与散列相同的结果。只是为了得到相同的输出结果需要控制更多的变量。 Iridium 没有提到但可能是一个问题的一项是输入的字节顺序(密钥和纯文本)。我 运行 在检查 Serpent 与 TrueCrypt 的参考实现时正是这样。如果我颠倒了它们之间的密钥和纯文本,它们只向文本向量 提供相同的输出。

详细说明一下,如果您的纯文本是所有 16 个字节都为 0,并且您的密钥是 31 个字节的 0 和一个字节的“33”(在 256 位版本中),如果“33” ' 字节位于参考实现字节串的左端,您必须向 TrueCrypt 提供 31 个“00”字节,然后在右侧提供“33”字节以获得相同的输出。所以正如我提到的,字节顺序问题。

至于 TrueCrypt 可能不安全,即使 AES 仍然安全,那是绝对正确的。我不知道 TrueCrypt 所谓的弱点的具体细节,但让我介绍几种程序可以正确使用 AES 但仍然不安全的方法。

一种方法是,在用户输入密码后,程序以不安全的方式为会话存储密码。如果它没有在内存中加密,或者如果它使用自己的内部密钥加密您的密钥但未能足够好地保护该密钥,您可以 Windows 将其写在硬盘驱动器上以供所有人读取(如果它交换)内存到硬盘。或者这样的交换不像以前那样常见,除非 TrueCrypt 作者在会话期间保护您的密钥,否则恶意程序也有可能出现并且 "debug" 密钥直接从 TrueCrypt 软件中取出。完全没有 AES 被破坏。

另一种可能被破坏的方式(理论上)是使定时攻击成为可能的方式。作为一个简单的例子,想象一个非常基本的加密,它使用你的 32 位密钥并将它分成 2 个每个 16 字节的块。然后它逐字节查看第一个块。它将纯文本向右旋转与密钥的字节 0 的值相对应的位数。然后它将纯文本与密钥的右侧 16 个字节进行异或运算。然后它会根据密钥的每个字节 1 再次进行位旋转。以此类推,16 次移位和 16 次异或运算。好吧,如果 "bad guy" 能够监控您的 CPU 的功耗,他们可以使用边信道攻击来为 CPU 计时和/或测量其每比特的功耗关键基础。事实上,位循环 120 位比位循环 121 位需要更长的时间(通常,取决于处理位循环的代码)。这种差异很小,但它确实存在并且已被证明会泄露关键信息。 XOR 步骤可能不会泄露密钥信息,但是基于上述攻击,攻击者很容易知道你的一半密钥,即使是在一个完整算法的实现上,如果实现本身没有正确完成——一个非常很难做到。

所以我不知道 TrueCrypt 是否以这些方式之一或以其他方式完全被破坏。但加密比看起来要难得多。如果里面的人说坏了,我就很容易相信了。