为什么我的带有逐块加密的python aes-ctr-128微基准测试速度慢?
我设计了一个拼图算法来获取每个加密块的值,这些值指向下一个要加密的块。出于某种特殊原因,我不得不使用aes-ctr-128 我运行一个虚拟测试,看看它有多快或多慢 这就是我所做的。我测试了pycrypto和密码学 我首先创建一个带有随机字节的16MB文件 我试过两种方法: 方法1。将文件加载到块大小为128位的块列表中 方法2。只需将文件加载到字符串中即可 现在我测试了加密每个128位块的总时间。我测试了加密整个文件的总时间 结果如下: pycrypto:为什么我的带有逐块加密的python aes-ctr-128微基准测试速度慢?,python,aes,microbenchmark,Python,Aes,Microbenchmark,我设计了一个拼图算法来获取每个加密块的值,这些值指向下一个要加密的块。出于某种特殊原因,我不得不使用aes-ctr-128 我运行一个虚拟测试,看看它有多快或多慢 这就是我所做的。我测试了pycrypto和密码学 我首先创建一个带有随机字节的16MB文件 我试过两种方法: 方法1。将文件加载到块大小为128位的块列表中 方法2。只需将文件加载到字符串中即可 现在我测试了加密每个128位块的总时间。我测试了加密整个文件的总时间 结果如下: pycrypto: 逐个加密128位块:每秒61824 a
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)
导入操作系统
从cryptography.hazmat.primitives.ciphers导入密码、算法、模式
从cryptography.hazmat.backends导入默认\u后端
backend=默认值_backend()
从Crypto.Cipher导入AES
从Crypto.Util导入计数器
随机输入
导入时间
块大小=16
def read_块(fname):
块列表=[]
blobfo=打开(fname)
atEOF=错误
虽然不包括:
blobdata=blobfo.read(块大小)
block_list.append(blobdata)
如果len(blobdata)<块大小:
#我们应该在这之后停止。。。
atEOF=真
返回块列表
打印“加载数据”
块列表=读取块('mediumdata'))
打印“加载完成”
打印透镜(块列表),“块”
打印“开始加密”
NUM_计数器_位=128
#这里我只使用一个随机键
key=os.uradom(16)
t1=时间。时间()
对于块列表中的块:
ctr=计数器。新(计数器位数)
密码=AES.new(密钥,AES.MODE\u CTR,计数器=CTR)
加密(块)
t2=时间。时间()
打印“完成加密”
打印“总时间:”,t2-t1
打印“每个aes的时间:”,(t2-t1)/len(块列表)
打印“每秒不良事件数:”,len(块列表)/(t2-t1)
打印“现在尝试加密整个文件”
block=open('mediumdata')。read()
打印类型(块)
打印“开始加密”
NUM_计数器_位=128
key=os.uradom(16)
t1=时间。时间()
ctr=计数器。新(计数器位数)
密码=AES.new(密钥,AES.MODE\u CTR,计数器=CTR)
加密(块)
t2=时间。时间()
打印“完成加密”
打印“总时间:”,t2-t1
打印“每个aes的时间:”,(t2-t1)/len(块列表)
打印“每秒不良事件数:”,len(块列表)/(t2-t1)
打印“现在尝试加密”
打印“开始加密”
t1=时间。时间()
num=random.randint(165530)
nonce=“”.join(chr((num>>(i*8))&0xFF)表示范围(16)内的i)
backend=默认值_backend()
cipher=cipher(algorithms.AES(key)、modes.CTR(nonce)、backend=backend)
encryptor=cipher.encryptor()
对于块列表中的块:
密文=加密程序。更新(块)
encryptor.finalize()
t2=时间。时间()
打印“完成加密”
打印“总时间:”,t2-t1
打印“每个aes的时间:”,(t2-t1)/len(块列表)
打印“每秒不良事件数:”,len(块列表)/(t2-t1)
打印“尝试整个文件”
block=open('mediumdata')。read()
打印“开始加密”
t1=时间。时间()
num=random.randint(165530)
nonce=“”.join(chr((num>>(i*8))&0xFF)表示范围(16)内的i)
backend=默认值_backend()
cipher=cipher(algorithms.AES(key)、modes.CTR(nonce)、backend=backend)
encryptor=cipher.encryptor()
密文=encryptor.update(block)#+encryptor.finalize()
encryptor.finalize()
t2=时间。时间()
打印“完成加密”
打印“总时间:”,t2-t1
打印“每个aes的时间:”,(t2-t1)/len(块列表)
打印“每秒不良事件数:”,len(块列表)/(t2-t1)
我错过什么了吗
有没有办法使方法1更快?AES是一种具有多轮置换的分组密码。每一轮都有自己的轮键,需要从“主”键(代码中的
key
)派生。调用AES.new(key,mode,…)
将自动派生圆键,但是这个键调度过程相当繁重。与一次调用方法相比,为每个块执行密钥调度将大大降低处理速度,特别是在实际加密代码使用AES-NI指令集的情况下
此外,正如kennytm在文章中指出的,Python是一种解释语言,因此在Python中迭代块而不是底层本机加密代码(例如,pyCrypto使用C库tomcrypt)必然会导致额外的性能损失
方法1的代码被破坏,因为您正在为每个块创建一个新的计数器对象,它总是初始化为
1
。因此,您使用相同的密钥流对每个块进行异或运算,这会创建一个密钥流,并可能使攻击者推断出明文
我们可以通过只有一个计数器对象来解决这个问题。同时拥有一个单键时间表可以显著提高性能
改进的方法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
1049887座
方法1
总时间:17.3199939324279785156
每次aes的时间:1.64970134245e-05
每秒不良事件数:60617.0325662
改进方法1
总时间:0.7829999237060546875
每次aes的时间:7.45794540146e-07
每秒不良事件数:1340851.86492
方法2
总时间:0.147000074387
每次aes的时间:1.4001513914e-07
每秒不良事件数:7142084.82126
AES是一种具有多轮置换的分组密码。每一轮都有自己的轮键,需要从“主”键(代码中的
key
)派生。愈伤组织