0

注意: 对于那些想提出无聊、愚蠢的评论甚至建议来关闭有效问题的人,请在此处查看已接受的答案:使用 GNU/Linux 系统调用`splice`进行零复制套接字到套接字Haskell中的数据传输是如何为那些真正寻求建设性答案的人提供适当帮助的一个很好的例子!


嗨,我刚刚阅读PowerModMathematica 8 的文档并想测试 HaksellRSA包(ghc --make -O2 -O3 -fllvm -optlo-O3 test.hs):

{-# LANGUAGE OverloadedStrings #-}

module Main where

import Control.Monad
import System.Random
import Codec.Crypto.RSA
import Data.ByteString.Lazy
import Data.ByteString.Char8

import Criterion.Main
import Criterion.Config

main :: IO ()
main = do
  print m1
  print m4
  print m8
  defaultMainWith defaultConfig (return ()) [
    bgroup "RSA" [
       bench "1" $ ed m1
     , bench "4" $ ed m4
     , bench "8" $ ed m8
     ]
   ]

m1 = fromChunks [ Data.ByteString.Char8.replicate (1*1024) '0' ]
m4 = fromChunks [ Data.ByteString.Char8.replicate (4*1024) '0' ]
m8 = fromChunks [ Data.ByteString.Char8.replicate (8*1024) '0' ]

ed m = do
  g1 <- newStdGen
  let (el,il,g2) = generateKeyPair g1 1024
  loop 1 g2 el il m

loop :: RandomGen g => Int -> g -> PublicKey -> PrivateKey -> Data.ByteString.Lazy.ByteString -> IO ()
loop n g e i m = do
  let   nn     = n-1
  let  (em,ng) = encrypt g e  m
  let   dm     = decrypt   i em
  when (m == dm) $ Data.ByteString.Char8.putStr "1"
  when (nn > 0 ) $ loop nn ng e i m

在 Mathematica 中也试过这个:

{p, q} = Prime[RandomInteger[{10^4, 10^5}, {2}]];
{p, q, n = p q}
\[Lambda] = CarmichaelLambda[n]
d = NestWhile[#1 + 1 & , Round[n/3], GCD[\[Lambda], #1] =!= 1 &]
e = PowerMod[d, -1, \[Lambda]]
enc = PowerMod[#, e, n] &;
dec = PowerMod[#, d, n] &;
c = ConstantArray[48, 8 1024];
t = Table[c // enc // dec; // AbsoluteTiming, {10}][[All, 1]]

Haskell ( m8) 和 Mathematica 案例中的时序相似:

{0.313015, 0.302337, 0.303766, 0.303321, 0.303018, 0.302574, \
0.302511, 0.303958, 0.301411, 0.300820}

对于 RSA 来说,每 8192 字节长的消息 300 毫秒是可接受的性能吗?OpenSSL 或其他实现如何比较?

(测试台:64 位 linux;4xCORE,Intel(R) Core(TM) i5 CPU M 430 @ 2.27GHz)

4

2 回答 2

8

首先,好问题 - RSA 与 OpenSSL 的性能差异也是我的一个问题。也就是说,这里有一堆没有给出答案的文本。

Haskell RSA 软件包已更改

我最近将 RSA 移到使用CryptoRandomGenfrom RandomGen。您使用的速度非常慢StdGen,因此切换到 intel-aes 包中的生成器或HashDRBG从 DRBG 包(可能是缓冲版本)会有所帮助。

这不是您应该使用公钥密码术的方式

通常,您使用公钥来交换密钥或加密密钥,以便只有接收者才能解密它。您似乎打算使用 RSA 不断加密消息流。RSA 的性能很少有人关心,因为它是一种非常罕见的操作。

适当的基准测试

正如 Daniel 所说,您目前正在对密钥生成、加密和解密进行基准测试。你回答说你不会生成很多密钥,只是做很多 enc/dec 操作......所以你不认为你应该修复基准吗?

此外,您的基准似乎不完整,因此值得怀疑 - 至少它缺少导入。

其他令人担忧的事情

你说“密钥对的随机性[目前]不重要。” 在它们变得重要之前,没有理由担心密码学。

基准测试 Oli 也有一个好处。对 OpenSSL 进行基准测试是必经之路。

从命令行(就我的答案部分而言)OpenSSL 强制您半正确地使用 RSA,因此我们将只对非常小的文件的加密进行基准测试:

dd if=/dev/urandom of=64B bs=64 count=1
openssl genrsa -out test.key 1024
openssl rsa -in test.key -out public.pem -outform PEM -pubout
openssl rsa -in test.key -out private.pem -outform PEM
time openssl rsautl -raw -ssl -encrypt -inkey private.pem -in 64B -out 64B.enc

这给了我们 5 到 12 毫秒的时间。

现在为 Haskell。除了外观上的更改之外,我还使用 CryptoRandomGen 和不那么快但 OK 的 HashDRBG 生成器迁移到新的 RSA,同时使您的加密函数变得纯净并放弃不必要的比较。我们最终得到:

import Criterion.Main
import Criterion.Config
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import Codec.Crypto.RSA
import Crypto.Random.DRBG

main :: IO ()
main = do
  r1 <- newGenIO :: IO HashDRBG
  r2 <- newGenIO :: IO (GenBuffered HashDRBG)

  -- We don't care about the performance of generate, so we do it outside the benchmark framework
  let (pub,priv,g2) = generateKeyPair r2 1024

  defaultMainWith defaultConfig (return ()) [
    bgroup "RSA" [
       bench "1" $ whnf (enc r1 pub priv) m1
       , bench "2" $ whnf (enc r2 pub priv) m1
     ]
   ]

m1 :: L.ByteString
m1 = L.pack [0..63]

enc :: CryptoRandomGen g => g -> PublicKey -> PrivateKey -> L.ByteString -> L.ByteString
enc g pub priv m = 
    let (em,ng) = encrypt g pub m
        dm     = decrypt   priv em 
    in dm

这会产生大约 3.5 毫秒的测量结果(使用 GHC 7.4 和 -O2 编译)。需要明确的是:我并不是说 RSA 比 OpenSSL 更快——OpenSSL 测试有更多的开销(加载可执行文件、读取密钥、读取明文、加密、写入结果),而且它非常可信地可能是一个命令比 RSA 包快很多。我要说的是“嘿,看,Haskell RSA 代码的执行速度非常快,我并不在意,如果你愿意,你可以进一步完善基准测试。”

作为参考,openssl speed rsa1024它说它在 0.5 毫秒内签名(显然在我的机器上),我怀疑这是 16 字节的 RSA 加密以及其他操作。

于 2012-04-10T06:14:42.953 回答
3

简短的回答是我不知道,我没有什么可比较的。我试图找出如何使 openssl 执行相同的操作,但没有找到任何可以理解的(我)文档。所以我所能做的就是玩弄 Haskell RSA 代码(顺便说一句,你的代码不适用于 hackage 的最新版本,相关的约束现在CryptoRandomGen不是RandomGen,所以我捏造了一个instance CryptoRandomGen Stdgen,当然不是最理想的,但那没有t有很大的不同)。

我玩弄的结论是,Haskell RSA实现肯定是可以改进的,但可能不会太慢​​。它的速度是否可以接受当然取决于您的需求。

起点是您的基准代码与我的伪造CryptoRandomGen实例。在我的计算机(64 位 linux,Core i5 M2410,2.3GHz)上,报告的 8K 字符串的平均值约为 360 毫秒。

将密钥生成移出基准测试使时间缩短到约 295 毫秒,因此密钥生成相当重要,主要是由于次优的质数测试。强费马检验的更好实现大大减少了质数发现时间,使用 Baillie PSW 检验代替米勒拉宾可以进一步减少。

在密钥生成之外,缺少模幂实现,它在指数上使用一位移位,这对于大Integers 来说相当慢。改进带来了显着但不是显着减少的运行时间。

我可以稍微提高性能的另一点是八位字节流和Integers之间的转换,但与上述相比差异很小。

完成明显的事情后,剩下的就是找出时间花在哪里了。事实证明,绝大多数时间都花在了模幂运算上。幸运的是,单独对模幂算法进行基准测试并将其与 GMP 的性能进行比较相对容易。一个快速而肮脏的测试表明(至少在我的计算机上)GMP 的mpz_powm工作速度大约是我的 Haskell 实现的 2-2.5 倍(因此,如果 GHC 提供对 的直接绑定mpz_powm,则可以预期会有很大的加速)。

总而言之,8K 基准测试现在在约 270 毫秒(包括密钥生成)和约 245 毫秒(不包括密钥生成)内运行,其中约 200 毫秒用于模幂运算。

假设大小限制不允许比 GMP 更好的模幂运算,我估计一个好的 C 实现将是当前 RSA 包¹的 5-10 倍。

这可以接受吗?你的来电。

¹但我有一半希望会因一个不平凡的因素而偏离。

于 2012-04-10T05:27:56.600 回答