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的高效解密的主要内容,如果未能解决你的问题,请参考以下文章

脚本日志监控分析,解密跑批任务高效运维之道

云数据库 GaussDB(for Influx) 解密第十一期:让智能电网中时序数据处理更高效

java 加密解密算法MD5/SHA1,DSA

北京TMS320F28033单片机解密

wex5 实战 加密与解密系列 DES算法引入与调用

重磅!独家解密国内外第一个 AIOps 白皮书(正式版)