【问题标题】:Interoperability of AES CTR mode?AES CTR 模式的互操作性?
【发布时间】:2012-09-13 19:43:49
【问题描述】:

我在 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 代码中的一些模糊的 cmets 表明这不是(或至少没有)完全支持:

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 模式的问题?

【问题讨论】:

  • 计数器中包含多少位似乎变化很大。有些使用 64 位,有些使用整个 128 位。但这对您的情况应该没有任何影响,因为 LSB 为 0,并且每个 IV 的块数永远不会超过 256 个。
  • @CodesInChaos 确实如此,而且您加密超过 2^64 个块的机会也不高,因此前 64 位将不受影响。
  • @owlstead 2^32 个数据包足以潜在地导致翻转到 OP 方案中的最高位。这仍然不太可能。在整个 IV 是随机的方案中,即使是第二个块也可能导致 128 位和 64 位计数器之间存在差异。
  • @CodesInChaos:我同意,但我希望包号是完全唯一的,并且它永远不会包含超过 blocksize * 256 字节。在我看来,有人在 CommonCrypto 实现中被破坏了,或者库的使用方式与预期不同(过早地完成加密而不是更新)。
  • @owlstead 是的,它们是独一无二的,不应该有重复使用相同 IV 的情况,除非随机随机数被偶然重复使用。此外,密钥永远不会重复使用,因此每个新的 nonce 都带有一个新的随机密钥。

标签: cryptography aes commoncrypto ctr-mode


【解决方案1】:

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

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

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

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

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

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

【讨论】:

  • 请注意,阅读规范后,您可以合理地确定 NIST 仅使用 Big Endian 模式,因此 Apple 最终只选择了该模式也就不足为奇了。不过,加密库中的类似言论确实让我不寒而栗,这似乎表明实施者没有阅读规范,或者不理解它们。在这种情况下,不应允许他们创建加密库。
  • 感谢您的信息。我会仔细阅读它,看看我是否能让 CommonCrypto 像预期的那样工作,或者向 Apple 提交错误。
  • 这里的用例是一个加密的 UDP 流,IV 和密钥通过加密的 TCP 侧通道发送一次。我需要数据包计数器来构建 IV,因为数据包可能会丢失。
  • 我不同意使用 8 字节随机部分“你有 2^32 的机会在 nonces 中发生冲突”的说法。更准确地说,平均需要 2**32 条消息才能看到重复的密钥流。但是,您需要在此之前停止使用 way 键。对于 CCM(使用 CTR),RFC5084/CMS 建议随机数使用 12 个字节。其他来源也在同一个范围内。作为一般建议,8 字节并不安全。
  • 根据@SquareRootOfTwentyThree 的评论更改了我的建议虽然与 8 字节随机数相比,不兼容的可能性略高,但我认为在这种情况下增加安全性是值得的。如果库需要 8 字节随机数,请确保限制使用密钥加密的消息数量。
【解决方案2】:

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

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

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

【讨论】:

  • 当前的 CC 标头表示 CCCryptorReset仅用于 CBC 模式。 如果您查看源代码,您会发现它会检查 macOS 10.13/iOS 11 或更高版本,如果不是,它会退回到一个兼容性函数,该函数忽略调用ccSetIV 实际上返回kCCParamError 的事实。寻找CCCryptorReset_binary_compatibilityopensource.apple.com/source/CommonCrypto/…
猜你喜欢
  • 2012-11-17
  • 2011-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-07
  • 1970-01-01
相关资源
最近更新 更多