为什么我的python aes-ctr-128微基准测试具有逐块加密的速度?

时间:2016-05-20 22:43:33

标签: python aes microbenchmark

我设计了一个拼图算法来获取每个加密块的值,该值指向要加密的下一个块。出于某种特殊原因,我必须使用aes-ctr-128。

我进行虚拟测试,看看它有多快或多慢。

这就是我所做的。我测试了pycrypto和密码学。

我首先创建一个带有随机字节的16MB文件。

我试过两种方式:

方法1。将文件加载到块大小为128位的块列表中。

方法2。只需将文件加载到字符串中即可。

现在我测试了加密每个128位块的总时间。我测试了加密整个文件的总时间。

结果如下:

  

pycrypto:

     
      
  1. 逐个加密128位块:每秒61,824 aes-ctr-128

  2.   
  3. 加密整个文件:每秒8,843,713 aes-ctr-128

  4.         

    加密

         
        
    1. 逐个加密128位块:每秒384,959 aes-ctr-128

    2.   
    3. 加密整个文件:每秒113,417,922 aes-ctr-128

    4.   

我想知道为什么方法1和2给我带来如此大的差异的结果?这两种方法是否可以提供相同的速度?

这是我的测试代码:

import os
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
backend = default_backend()

from Crypto.Cipher import AES
from Crypto.Util import Counter
import random
import time

BLOCK_SIZE = 16

def read_block(fname):
    block_list = []
    blobfo = open(fname)
    atEOF = False
    while not atEOF:
        blobdata = blobfo.read(BLOCK_SIZE)
        block_list.append(blobdata)
        if len(blobdata) < BLOCK_SIZE:
        # we should stop after this...
        atEOF = True
    return block_list


print 'loading data'
block_list = read_block('mediumdata')

print 'loading finish'
print len(block_list), 'blocks'

print 'start encryption'
NUM_COUNTER_BITS = 128
# Here I just use a random key
key = os.urandom(16)
t1 = time.time()
for block in block_list:
    ctr = Counter.new(NUM_COUNTER_BITS)
    cipher = AES.new(key, AES.MODE_CTR, counter=ctr)
    cipher.encrypt(block)
t2 = time.time()
print 'finish encryption'

print 'total time:', t2 - t1
print 'time for each aes:', (t2 - t1) / len(block_list)

print 'num of aes per sec:', len(block_list) / (t2 - t1)

print 'now try to encrypt whole file'
block = open('mediumdata').read()
print type(block)
print 'start encryption'
NUM_COUNTER_BITS = 128
key = os.urandom(16)
t1 = time.time()
ctr = Counter.new(NUM_COUNTER_BITS)
cipher = AES.new(key, AES.MODE_CTR, counter=ctr)
cipher.encrypt(block)
t2 = time.time()
print 'finish encryption'

print 'total time:', t2 - t1
print 'time for each aes:', (t2 - t1) / len(block_list)

print 'num of aes per sec:', len(block_list) / (t2 - t1)


print 'now try cryptography'

print 'start encryption'
t1 = time.time()
num = random.randint(1, 65530)
nonce = "".join(chr((num >> (i * 8)) & 0xFF) for i in range(16))
backend = default_backend()
cipher = Cipher(algorithms.AES(key), modes.CTR(nonce), backend=backend)
encryptor = cipher.encryptor()
for block in block_list:
    ciphertext = encryptor.update(block)
encryptor.finalize()
t2 = time.time()
print 'finish encryption'

print 'total time:', t2 - t1
print 'time for each aes:', (t2 - t1) / len(block_list)

print 'num of aes per sec:', len(block_list) / (t2 - t1)

print 'try a whole file'

block = open('mediumdata').read()

print 'start encryption'
t1 = time.time()
num = random.randint(1, 65530)
nonce = "".join(chr((num >> (i * 8)) & 0xFF) for i in range(16))
backend = default_backend()
cipher = Cipher(algorithms.AES(key), modes.CTR(nonce), backend=backend)
encryptor = cipher.encryptor()
ciphertext = encryptor.update(block)# + encryptor.finalize()
encryptor.finalize()
t2 = time.time()
print 'finish encryption'


print 'total time:', t2 - t1
print 'time for each aes:', (t2 - t1) / len(block_list)

print 'num of aes per sec:', len(block_list) / (t2 - t1)

我在这里错过了什么吗?

有没有办法让方法1更快?

1 个答案:

答案 0 :(得分:2)

AES是一种具有多个置换轮的分组密码。每一轮都有自己的圆键,需要从“主”键(代码中的key)派生。调用AES.new(key, mode, ...)将自动派生轮密钥,但此密钥计划进程非常繁重。与单呼叫方法相比,对每个块执行密钥调度将大大减慢处理速度,特别是如果实际加密代码使用AES-NI指令集。

此外,正如kennytm在comments中指出的那样,Python是一种解释型语言,因此迭代Python中的块而不是底层的本机加密代码(例如pyCrypto使用C库tomcrypt)必然会产生额外的性能损失。

方法1的代码被破坏,因为您正在为每个块创建一个新的Counter对象,该对象始终初始化为1。因此,您使用相同的密钥流对每个块进行异或,这会创建many-time pad并且可能使攻击者能够推断出明文。

我们可以通过只有一个Counter对象来解决这个问题。只有一个关键时间表可以大大提高性能。

改进方法1代码:

ctr = Counter.new(NUM_COUNTER_BITS)
cipher = AES.new(key, AES.MODE_CTR, counter=ctr)
for block in block_list:
    cipher.encrypt(block)

pyCrypto的结果:

1049887 blocks

Method 1
total time: 17.31999993324279785156
time for each aes: 1.64970134245e-05
num of aes per sec: 60617.0325662

Improved Method 1
total time: 0.78299999237060546875
time for each aes: 7.45794540146e-07
num of aes per sec: 1340851.86492

Method 2
total time: 0.147000074387
time for each aes: 1.4001513914e-07
num of aes per sec: 7142084.82126

Full code