4

我在 CTR 模式下使用 AES128 加密进行加密,针对不同的客户端(Android/Java 和 iOS/ObjC)实现。加密数据包时使用的 16 字节 IV 格式如下:

<11 byte nonce> | <4 byte packet counter> | 0

每发送一个数据包,数据包计数器(包含在发送的数据包中)就会增加一。最后一个字节用作块计数器,因此少于 256 个块的数据包总是获得唯一的计数器值。我假设 CTR 模式指定计数器应该为每个块增加 1,以大端方式使用最后 8 个字节作为计数器,或者这至少是事实上的标准。Sun 加密实现中似乎也是这种情况。

当相应的 iOS 实现(使用 CommonCryptor,iOS 5.1)在解码数据包时未能解码除第一个块之外的每个块时,我有点惊讶。似乎 CommonCryptor 以其他方式定义了计数器。CommonCryptor 可以在 big endian 和 little endian 模式下创建,但 CommonCryptor 代码中的一些模糊注释表明这不(或至少尚未)完全支持:

http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-60026/Source/API/CommonCryptor.c

/* corecrypto only implements CTR_BE.  No use of CTR_LE was found so we're marking
   this as unimplemented for now.  Also in Lion this was defined in reverse order.
   See <rdar://problem/10306112> */

通过逐块解码,每次按照上面指定的方式设置 IV,它都能很好地工作。

我的问题:在一次解码多个块时,是否有一种“正确”的方式来实现 CTR/IV 模式,或者在使用不同的加密库时我是否可以期望它是互操作性问题?CommonCrypto 在这方面是否存在问题,还是只是以不同方式实现 CTR 模式的问题?

4

2 回答 2

4

NIST 建议 sp800-38a 附录 B中(粗略地)指定了计数器的定义。请注意,NIST 仅在安全性方面指定如何使用 CTR 模式;它没有为计数器定义一种标准算法。

要直接回答您的问题,无论您做什么,您都应该期望计数器每次增加一。根据 NIST 规范,计数器应表示 128 位大端整数。可能只有最低有效(最右边)位会增加,但这通常不会产生影响,除非您传递 2^32 - 1 或 2^64 - 1 值。

为了兼容性,您可以决定使用第一个(最左边的)12 个字节作为随机数,并将后一个字节保留为零,然后让 CTR 的实现来执行增量。在这种情况下,您只需在开始时使用 96 位/12 字节随机数,在这种情况下,不需要数据包计数器。

但是,在计数器用完所有可用位之前,您只能使用 2^32 * 16 字节的纯文本。如果计数器返回零或随机数本身包含在计数器中,这是特定于实现的,因此您可能希望将自己限制为 68,719,476,736 = ~68 GB 的消息(是的,以 10 为底,千兆表示1,000,000,000)。

  • 由于生日问题,您有 2^48 的机会 (48 = 96 / 2) 为随机数创建冲突(每条消息都需要,而不是每个块),因此您应该限制消息的数量;
  • 如果某些攻击者欺骗您为相同的 nonce 解密 2^32 个数据包,您的计数器就会用完。

如果这仍然不兼容(测试!),则使用最初的 8 个字节作为 nonce。不幸的是,这确实意味着由于生日问题,您需要限制消息的数量。

于 2012-09-21T14:16:59.270 回答
3

进一步的调查揭示了 CommonCrypto 问题:

在 iOS 6.0.1 中,little endian 选项现在未实现。此外,我已经验证了 CommonCrypto 存在错误,因为 CCCryptorReset 方法实际上并没有按应有的方式更改 IV,而是使用预先存在的 IV。6.0.1 中的行为与 5.x 不同。

如果您使用无效的 IV 初始化 CommonCrypto,并在加密之前将其重置为实际的 IV,这可能是一个安全风险。这将导致您的所有数据都使用相同的(无效的)IV 加密,并且多个流(可能应该具有不同的 IV 但使用相同的密钥)将通过具有相应 ctr 的数据包的简单 XOR 泄漏数据。

于 2012-11-12T08:59:11.257 回答