【发布时间】: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 代码可能不太令人满意,但通常如果你想尽可能地发挥性能,那是你必须做的。
标签: haskell encryption cryptography aes