【问题标题】:Speed difference between AES/CBC encryption and decryption?AES/CBC加密和解密的速度区别?
【发布时间】:2013-12-08 11:49:28
【问题描述】:

我想知道,理论上,在以下条件下,AES/CBC 解密与 AES/CBC 加密相比会慢多少:

  • 32字节(256位)的加密密钥;
  • 16 字节(128 位)的块大小。

我问的原因是我想知道我拥有的一个实现的解密速度是否异常慢。我对不同大小的随机内存块做了一些测试。结果如下:

64B:

64KB:

10MB – 520MB:

所有数据都存储在我系统的内部存储器中。应用程序自己生成要加密的数据。虚拟内存在测试 PC 上被禁用,因此不会有任何 I/O 调用。

在分析表时,加密和解密的区别是否意味着我的实现异常缓慢?我是不是做错了什么?

更新:

  • 此测试在另一台电脑上执行;
  • 此测试使用随机数据执行;
  • Crypto++ 用于 AES/CBC 加密和解密。

解密实现如下:

CryptoPP::AES::Decryption aesDecryption(aesKey, ENCRYPTION_KEY_SIZE_AES);
CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, aesIv);

CryptoPP::ArraySink * decSink = new CryptoPP::ArraySink(data, dataSizeMax);
CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, decSink);
stfDecryptor.Put(reinterpret_cast<const unsigned char*>(ciphertext), cipherSize);
stfDecryptor.MessageEnd();

*dataOutputSize = decSink->TotalPutLength(); 

更新 2:

  • 添加了 64 字节块的结果

【问题讨论】:

  • 在我看来异常缓慢。
  • 使用本机实现,它们应该具有非常相似的性能。通过优化的实现,解密可能会更快,因为它可以并行化。
  • 不要使用空内存块。并运行您的代码两次。第一次可能会遇到热身问题。有可能您的库解密速度很慢,也有可能您的基准被破坏了。
  • @CodesInChaos 我做了一个新的测试并添加了解密代码
  • 您似乎在内存中的缓冲区中丢弃大量数据。如果您一次输入 64KB 块的密码,您是否看到相同的性能差异?您确定 I/O 速度不包含在您的测量中吗?

标签: c++ cryptography aes benchmarking crypto++


【解决方案1】:

作为对称加密,加解密应该是fairly close in speed。不确定您的实施,但there are ways to optimize if you're concerned about how the algorithm was used。在实验中,AES is not the fastest and CBC 会增加安全性,但会减慢速度。这是一个比较,因为您询问的是键和块大小:

【讨论】:

  • 加密/解密在 CBC 这样的模式下不一定是关闭的。例如,与加密相比,我希望在解密 CBC 时使用一点切片的 AES 实现要快得多,因为它需要并行处理多个块。
  • 这如何回答这个问题?
  • 评论不适合,我正在尽我所能提供帮助。如果您觉得它不完美,因此您需要对其投反对票并谴责它,请继续做任何您喜欢的事情,我不是为了投票而来的,我只是来学习和分享的。
  • @stackuser 我不会投反对票,因为我不喜欢这些信息或惹恼你,而是因为它没有回答问题。如果人们寻找答案,那么如果他们找到这些信息就不是很有用。换句话说,互联网上有很多信息,找到正确的信息是诀窍。这个问题专门针对加密速度的解密,答案中根本不存在这些信息。
  • @stackuser - 关于 " 有一些方法可以优化..." - Crypto++ 可能不会那样做。我们已经在努力避免侧信道攻击,我认为再引入四个表来泄露信息并不是一个好主意。我们可能应该实现一个位切片的实现,但 ROI 不存在。大多数主要平台都具有优于位切片的硬件加速。硬件加速针对侧通道进行了强化。我认为一般来说将资源用于新算法会更好;或者特别是 ARM A-32 Rijndael。
【解决方案2】:

在分析表时,加密和解密的区别是否意味着我的实现异常缓慢?我是不是做错了什么?

三四件事突然出现在我身上。我有点同意@JamesKPolk - 数字看起来不对。首先,加密库通常以 CTR 模式而不是 CBC 模式为基准。另请参阅SUPERCOP benchmarks。您必须使用每字节周期数 (cpb) 来规范跨机器的测量单位。在没有上下文的情况下说“9 MB/s”毫无意义。

其次,我们需要知道机器及其 CPU 频率。看起来您正在以 9 MB/s 的速度推送数据进行加密,以 6.5 MB/s 的速度进行解密。现代 iCore 机器,如Core-i5 running at 2.7 GHz,将以 2.5 或 3.0 cpb 左右的速度推送 CBC 模式数据,大约为 980 MB/s 或 1 GB/s。甚至我的旧Core2 Duo running at 2.0 GHz 移动数据的速度也比您显示的要快。 Core 2 以 14.5 cpb 或 130 MB/s 的速度移动数据。

第三,废弃这段代码。有很大的改进空间,因此在这种情况下不值得批评;推荐的代码如下。值得一提的是,您正在创建很多对象,例如 ArraySourceStreamTransformationFilter。过滤器添加了填充,这会扰乱 AES 加密和解密基准并扭曲结果。只需要一个加密对象,然后调用ProcessBlockProcessString即可。

CryptoPP::AES::Decryption aesDecryption(aesKey, ENCRYPTION_KEY_SIZE_AES);
CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, aesIv);
...

第四,Crypto++ wiki 有一个Benchmarks article,代码如下。它是一个新部分,当您提出问题时不可用。以下是运行测试的方法。

AutoSeededRandomPool prng;
SecByteBlock key(16);
prng.GenerateBlock(key, key.size());

CTR_Mode<AES>::Encryption cipher;
cipher.SetKeyWithIV(key, key.size(), key);

const int BUF_SIZE = RoundUpToMultipleOf(2048U,
    dynamic_cast<StreamTransformation&>(cipher).OptimalBlockSize());

AlignedSecByteBlock buf(BUF_SIZE);
prng.GenerateBlock(buf, buf.size());

const double runTimeInSeconds = 3.0;
const double cpuFreq = 2.7 * 1000 * 1000 * 1000;
double elapsedTimeInSeconds;
unsigned long i=0, blocks=1;

ThreadUserTimer timer;
timer.StartTimer();

do
{
    blocks *= 2;
    for (; i<blocks; i++)
        cipher.ProcessString(buf, BUF_SIZE);
    elapsedTimeInSeconds = timer.ElapsedTimeAsDouble();
}
while (elapsedTimeInSeconds < runTimeInSeconds);

const double bytes = static_cast<double>(BUF_SIZE) * blocks;
const double ghz = cpuFreq / 1000 / 1000 / 1000;
const double mbs = bytes / 1024 / 1024 / elapsedTimeInSeconds;
const double cpb = elapsedTimeInSeconds * cpuFreq / bytes;

std::cout << cipher.AlgorithmName() << " benchmarks..." << std::endl;
std::cout << "  " << ghz << " GHz cpu frequency"  << std::endl;
std::cout << "  " << cpb << " cycles per byte (cpb)" << std::endl;
std::cout << "  " << mbs << " MiB per second (MiB)" << std::endl;

在 Core-i5 6400 上以 2.7 GHz 运行代码会导致:

$ ./bench.exe
AES/CTR benchmarks...
  2.7 GHz cpu frequency
  0.58228 cycles per byte (cpb)
  4422.13 MiB per second (MiB)

第五,当我修改上面显示的基准程序以操作64字节块时:

const int BUF_SIZE = 64;
unsigned int blocks = 0;
...

do
{
    blocks++;
    cipher.ProcessString(buf, BUF_SIZE);
    elapsedTimeInSeconds = timer.ElapsedTimeAsDouble();
}
while (elapsedTimeInSeconds < runTimeInSeconds);

对于 64 字节块,Core-i5 6400 在 2.7 GHz 下的速度为 3.4 cpb 或 760 MB/s。该库因缓冲区较小而受到打击,但大多数(所有?)库都会这样做。

$ ./bench.exe
AES/CTR benchmarks...
  2.7 GHz cpu frequency
  3.39823 cycles per byte (cpb)
  757.723 MiB per second (MiB)

第六,您需要让处理器退出省电模式或低能耗状态,以获得最佳/最一致的结果。该库使用governor.sh 在 Linux 上执行此操作。它位于TestScript/ 目录中。

第七,当我切换到CTR模式解密时:

CTR_Mode<AES>::Decryption cipher;
cipher.SetKeyWithIV(key, key.size(), key);

然后我看到批量解密的速率大致相同:

$ ./bench.exe
AES/CTR benchmarks...
  2.7 GHz cpu frequency
  0.579923 cycles per byte (cpb)
  4440.11 MiB per second (MiB)

第八,这里是一组不同机器上的基准数据。它应该在您调整测试时提供一个粗略的目标。

运行频率为 980 MHz 的 Beaglebone 开发板移动数据的速率是您报告的两倍。 Beaglebone 实现了无聊的 40 cpb 和 20 MB/s,因为它是标准的 C/C++;并且没有针对 A-32 进行优化。


我的收获是:

  • CTR 模式批量加密和解密在现代机器上大致相同
  • CTR 模式密钥设置不一样,在现代机器上解密需要更长的时间
  • 小块比大块更昂贵

这是我希望看到的一切。

我认为您的下一步是使用 Crypto++ wiki 上的the sample program 收集一些数据,然后评估结果。

【讨论】:

    【解决方案3】:

    理论上,AES 解密要慢 30%。这是 Rijndael 系统的一般属性。

    来源:http://www4.ncsu.edu/~hartwig/Teaching/437/aes.pdf

    【讨论】:

    • 这个 30% 的数字是在 8 位 CPU 的上下文中。我不认为大 CPU 有很大的不同。
    猜你喜欢
    • 1970-01-01
    • 2013-08-11
    • 1970-01-01
    • 1970-01-01
    • 2017-10-11
    • 2014-02-06
    • 1970-01-01
    • 1970-01-01
    • 2018-10-26
    相关资源
    最近更新 更多