AES/CFB8的高效解密
Posted
技术标签:
【中文标题】AES/CFB8的高效解密【英文标题】:Efficient decryption of AES/CFB8 【发布时间】:2017-11-23 11:33:26 【问题描述】:我目前使用这个函数来解密一个在 CFB8 模式下使用 AES 加密的数据流: https://github.com/Lazersmoke/civskell/blob/ebf4d761362ee42935faeeac0fe447abe96db0b5/src/Civskell/Tech/Encrypt.hs#L167-L175
cfb8Decrypt :: AES128 -> BS.ByteString -> BS.ByteString -> (BS.ByteString,BS.ByteString)
cfb8Decrypt c i = BS.foldl magic (BS.empty,i)
where
magic (ds,iv) d = (ds `BS.snoc` pt,ivFinal)
where
pt = BS.head (ecbEncrypt c iv) `xor` d
-- snoc on cipher always
ivFinal = BS.tail iv `BS.snoc` d
如果你不了解 Haskell,下面是我认为这段代码如何工作的简要说明:(不是我写的)
给定一个 IV 和一个加密字节列表 对于每个加密字节: 在 ECB 模式下加密 IV。 获取加密 IV 的第一个字节并将其与加密字节进行异或。这是下一个明文字节。 从 IV 中删除第一个字节 将加密字节附加到 IV 下一个字符将使用这个新的 IV 解密并不是说 ECB 模式的加密是由 cryptonite 库处理的。我找不到支持 CFB8 的库。
现在,这行得通。但是,由于我需要解密的数据量很大,它会占用我的一个 CPU 内核,并且 80% 的时间都花在了解密上。
传入的数据甚至没有那么多,所以这是不可接受的。不幸的是,我的密码学知识相当有限,而且 CFB8 上的资源似乎相当稀少。 CFB8 似乎是一种不常见的操作模式,这也表明缺乏库支持。
那么,我的问题是:我将如何优化它?
传入的数据来自 TCP 流,但信息被分组到数据包中。每个数据包调用 cfb8Decrypt 函数 2-5 次,具体取决于大小。这是必要的,因为数据包的长度是在开始时传输的,但是这个大小信息的长度是可变的。使用1-4次解密后解密长度,整个数据包将被一次解密。我想过尝试减少这种情况,但我不确定它是否会对速度产生任何影响。
编辑:分析结果:http://svgur.com/i/40b.svg
【问题讨论】:
“我将如何优化这个?” - 从profiling你的程序开始。 我的意思是...我知道 80% 的时间都花在了解密上。你到底要我找什么? 您应该分析cfb8Decrypt
本身(除非您的意思是在cfb8Decrypt
中花费的80% 的时间都花在ecbEncrypt
上,在这种情况下您可能无能为力,除了用更快的版本替换ecbEncrypt
)。但是您在fold
中使用snoc
,因此您的程序至少具有二次复杂性(并且可能您在相当大的数据块上调用它,否则性能根本不会成为问题)。也许看看Builder。
是的,很多时间实际上都花在了 ecbEncrypt 上。我希望有一种比为每个字符调用 ecbEncrypt 更有效的方法。但是,使用 Builder 是一个很好的选择,因为 cfb8Decrypt 本身还有一些时间需要获得,而 snoc 可能就是它。我会将分析结果添加到问题中。
还可以考虑用 C 编写你的函数(使用适当的 C 函数替换 ecbEncrypt
,你可以找到它,例如 here),然后从 Haskell 的每个数据包调用一次。这将大大减少编组的数量。用 C 编写 Haskell 代码可能不太令人满意,但通常如果你想尽可能地发挥性能,那是你必须做的。
【参考方案1】:
CFB8 的创建是为了在嘈杂的通道上具有良好的错误传播特性。众所周知,它并不快;它实际上慢了 16 倍,因为它需要对每个字节进行块加密。目前它不是很热,因为我们倾向于将 CRC 用于数据层,而 MAC 用于加密级别的完整性以防止故意攻击。
如何加快速度?你唯一能做的就是使用一个快速的库。您当前使用的库似乎支持 AES-NI,因此请确保在您的 CPU 和 Bios 上启用。
但是,如果您必须逐块调用它,它很可能不会加速太多。您真的想使用本机调用来获取整个数据包并对其进行解密。 AES-NI 在实现 TLS 的 Atom 上最慢,仍然达到 20 MiB/s,但在服务器芯片上,AES-NI 通常远远超过 1 GiB/s 的限制。当 AES-NI 不可用时,组装或优化的 C 应该慢 6/7 倍。
像 Haskell 这样的函数式编程语言并不是真正为快速 I/O 或快速位操作而创建的。所以你可以打赌它会比例如慢得多。 Java 或 C#,它们已经比本机代码慢得多,更不用说汇编代码或专门的指令了。
现在的记忆速度非常快;然而,CPU 的速度要快得多。因此,应该避免虚假的内存分配和复制(同样,在功能齐全的语言上做起来并不容易,更有理由在本机代码中尽可能多地做)。但是请确保没有缓冲区溢出问题,否则您将在不安全的应用程序中使用快速 AES/CFB。
【讨论】:
以上是关于AES/CFB8的高效解密的主要内容,如果未能解决你的问题,请参考以下文章