Python Pycryptodome AES-GCM加密代码性能提升
Python Pycryptodome AES-GCM encryption code performance improvement
我有大约 19G 的数据,我正在做 tar 然后加密。我使用下面的代码来完成这项工作。
from subprocess import call
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import sys
cmd = ["tar","--acls","--selinux","-czPf","./out.tar.gz","./src"]
proc = call(cmd)
data = open("./out.tar.gz", "rb").read()
key = get_random_bytes(32)
cipher = AES.new(key, AES.MODE_GCM)
ciphertext, tag = cipher.encrypt_and_digest(data)
out = open("./out.bin", "wb")
[out.write(x) for x in (cipher.nonce, tag, ciphertext)]
out.close()
我使用的是 HP Gen10 硬件,具有 48 CPU 个内核和 128G 内存以及 1800.3 GB 硬盘 space。只有一个内核的使用率几乎达到 100%,内存使用率约为 43%。整个过程需要一天多的时间。
我在上面的代码中寻找提高性能的方法。
我在 SquareRootOfTwentyThree 评论后对代码进行了重大改进:
from subprocess import call
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import StringIO
key = get_random_bytes(32)
def readLargeFile(filename):
with open(filename, "rb") as f:
while True:
data = f.read(1024)
if not data:
break
yield data
cmd = ["tar","--acls","--selinux","-czPf","./out.tar.gz","./src"]
call(cmd)
cipher = AES.new(key, AES.MODE_GCM)
ciphertext = []
for data in readLargeFile("./out.tar.gz"):
ciphertext.append(cipher.encrypt(data))
out = open("./out.bin", "wb")
[out.write(x) for x in (cipher.nonce, cipher.digest(), b"".join(ciphertext))]
out.close()
file_in = open("./out.bin", "rb")
nonce, tag, ciphertext = [file_in.read(x) for x in (16, 16, -1)]
cipher = AES.new(key, AES.MODE_GCM, nonce)
#data = cipher.decrypt_and_verify(ciphertext, tag)
data = []
for buf in StringIO.StringIO(ciphertext).read(1024):
data.append(cipher.decrypt(buf))
cipher.verify(tag)
with open("./dst/out.tar.gz", "wb") as f:
f.write(b''.join(data))
cmd = ["tar","-xzPf","./dst/out.tar.gz","-C","./dst"]
proc = call(cmd)
加密成功但解密的验证()导致ValueError:MAC检查失败
注意:我使用的是 PyCryptodome v3.6.6
我以某种方式成功地进行了解密,下面是我的最新代码:
#! /usr/bin/python
from subprocess import Popen,PIPE,call
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import StringIO,io,tarfile
import os,sys
import datetime
print "*** Encryption Starts *** " + str(datetime.datetime.now())
key = get_random_bytes(32)
def readLargeFile(filename):
with open(filename, "rb") as f:
while True:
data = f.read(1024)
if not data:
break
yield data
cmd = ["tar --acls --selinux -czPf /nfs/out.tar.gz ./encrypt_disk/src/*"]
call(cmd, shell=True)
cipher = AES.new(key, AES.MODE_GCM)
ciphertext = []
for data in readLargeFile("/nfs/out.tar.gz"):
ciphertext.append(cipher.encrypt(data))
out = open("/nfs/out.bin", "wb")
[out.write(x) for x in (cipher.nonce, cipher.digest(), b"".join(ciphertext))]
out.close()
print "*** Encryption Ends *** " + str(datetime.datetime.now())
print "*** Decryption Starts *** " + str(datetime.datetime.now())
file_in = open("/nfs/out.bin", "rb")
nonce, tag, ciphertext = [file_in.read(x) for x in (16, 16, -1)]
cipher = AES.new(key, AES.MODE_GCM, nonce)
tar = tarfile.open(fileobj=StringIO.StringIO(cipher.decrypt_and_verify(ciphertext, tag)), mode='r|*')
os.chdir("/nfs/dst")
tar.extractall(path='.')
print "*** Decryption Ends *** " + str(datetime.datetime.now())
GCM 很难(尽管并非不可能)并行化。不过,在我使用了 3 年的 x86 笔记本电脑(使用 AESNI 和 CLMUL 加速指令)上,我使用 PyCryptodome 的 GCM 得到了 150 MB/s。 19GB 仅需 2 分钟,而不是一天!我使用了以下玩具代码:
data = os.urandom(1024*1024)
cipher = AES.new(key, AES.MODE_GCM)
for _ in range(1024):
cipher.encrypt(data)
tag = cipher.digest()
该代码不能直接用于您的用例,但它表明您一次加密完整的 19GB 可能存在问题。也许,您应该将处理分成块。
其他一些评论:
- 使用分析器确定您的程序在哪里花费最多时间。它可能不是您认为的位置(例如
tar
步骤呢?)。
- 确保您使用的是最新版本的 PyCryptodome (3.6.6),因为 CLMUL 加速 was added only recently。
- GCM 最多只能加密 256GB - 你离 19GB 不远了。
我有大约 19G 的数据,我正在做 tar 然后加密。我使用下面的代码来完成这项工作。
from subprocess import call
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import sys
cmd = ["tar","--acls","--selinux","-czPf","./out.tar.gz","./src"]
proc = call(cmd)
data = open("./out.tar.gz", "rb").read()
key = get_random_bytes(32)
cipher = AES.new(key, AES.MODE_GCM)
ciphertext, tag = cipher.encrypt_and_digest(data)
out = open("./out.bin", "wb")
[out.write(x) for x in (cipher.nonce, tag, ciphertext)]
out.close()
我使用的是 HP Gen10 硬件,具有 48 CPU 个内核和 128G 内存以及 1800.3 GB 硬盘 space。只有一个内核的使用率几乎达到 100%,内存使用率约为 43%。整个过程需要一天多的时间。 我在上面的代码中寻找提高性能的方法。
我在 SquareRootOfTwentyThree 评论后对代码进行了重大改进:
from subprocess import call
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import StringIO
key = get_random_bytes(32)
def readLargeFile(filename):
with open(filename, "rb") as f:
while True:
data = f.read(1024)
if not data:
break
yield data
cmd = ["tar","--acls","--selinux","-czPf","./out.tar.gz","./src"]
call(cmd)
cipher = AES.new(key, AES.MODE_GCM)
ciphertext = []
for data in readLargeFile("./out.tar.gz"):
ciphertext.append(cipher.encrypt(data))
out = open("./out.bin", "wb")
[out.write(x) for x in (cipher.nonce, cipher.digest(), b"".join(ciphertext))]
out.close()
file_in = open("./out.bin", "rb")
nonce, tag, ciphertext = [file_in.read(x) for x in (16, 16, -1)]
cipher = AES.new(key, AES.MODE_GCM, nonce)
#data = cipher.decrypt_and_verify(ciphertext, tag)
data = []
for buf in StringIO.StringIO(ciphertext).read(1024):
data.append(cipher.decrypt(buf))
cipher.verify(tag)
with open("./dst/out.tar.gz", "wb") as f:
f.write(b''.join(data))
cmd = ["tar","-xzPf","./dst/out.tar.gz","-C","./dst"]
proc = call(cmd)
加密成功但解密的验证()导致ValueError:MAC检查失败
注意:我使用的是 PyCryptodome v3.6.6
我以某种方式成功地进行了解密,下面是我的最新代码:
#! /usr/bin/python
from subprocess import Popen,PIPE,call
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import StringIO,io,tarfile
import os,sys
import datetime
print "*** Encryption Starts *** " + str(datetime.datetime.now())
key = get_random_bytes(32)
def readLargeFile(filename):
with open(filename, "rb") as f:
while True:
data = f.read(1024)
if not data:
break
yield data
cmd = ["tar --acls --selinux -czPf /nfs/out.tar.gz ./encrypt_disk/src/*"]
call(cmd, shell=True)
cipher = AES.new(key, AES.MODE_GCM)
ciphertext = []
for data in readLargeFile("/nfs/out.tar.gz"):
ciphertext.append(cipher.encrypt(data))
out = open("/nfs/out.bin", "wb")
[out.write(x) for x in (cipher.nonce, cipher.digest(), b"".join(ciphertext))]
out.close()
print "*** Encryption Ends *** " + str(datetime.datetime.now())
print "*** Decryption Starts *** " + str(datetime.datetime.now())
file_in = open("/nfs/out.bin", "rb")
nonce, tag, ciphertext = [file_in.read(x) for x in (16, 16, -1)]
cipher = AES.new(key, AES.MODE_GCM, nonce)
tar = tarfile.open(fileobj=StringIO.StringIO(cipher.decrypt_and_verify(ciphertext, tag)), mode='r|*')
os.chdir("/nfs/dst")
tar.extractall(path='.')
print "*** Decryption Ends *** " + str(datetime.datetime.now())
GCM 很难(尽管并非不可能)并行化。不过,在我使用了 3 年的 x86 笔记本电脑(使用 AESNI 和 CLMUL 加速指令)上,我使用 PyCryptodome 的 GCM 得到了 150 MB/s。 19GB 仅需 2 分钟,而不是一天!我使用了以下玩具代码:
data = os.urandom(1024*1024)
cipher = AES.new(key, AES.MODE_GCM)
for _ in range(1024):
cipher.encrypt(data)
tag = cipher.digest()
该代码不能直接用于您的用例,但它表明您一次加密完整的 19GB 可能存在问题。也许,您应该将处理分成块。
其他一些评论:
- 使用分析器确定您的程序在哪里花费最多时间。它可能不是您认为的位置(例如
tar
步骤呢?)。 - 确保您使用的是最新版本的 PyCryptodome (3.6.6),因为 CLMUL 加速 was added only recently。
- GCM 最多只能加密 256GB - 你离 19GB 不远了。