2016-05-20 8 views
0

Ich entwarf einen Puzzle-Algorithmus, um den Wert jedes verschlüsselten Blocks zu nehmen, der auf den nächsten zu verschlüsselnden Block zeigt. Ich muss AES-CTR-128 dafür mit einem bestimmten Grund verwenden.Warum ist mein Python AES-CTR-128 Mikro-Benchmark mit blockweise Verschlüsselung langsam?

Ich führe einen Dummy-Test, um zu sehen, wie schnell oder langsam es sein könnte.

Hier ist was ich getan habe. Ich habe sowohl Pycrypto als auch Kryptographie getestet.

Ich erstelle zuerst eine 16MB-Datei mit zufälligen Bytes.

Ich habe versucht, auf zwei Arten:

Methode 1. Laden Sie die Datei in eine Sperrliste mit 128-Bit-Blockgröße.

Methode 2. Laden Sie einfach die Datei in eine Zeichenfolge.

Jetzt habe ich die Gesamtzeit getestet, um jeden 128-Bit-Block zu verschlüsseln. Und ich habe die Gesamtzeit getestet, um die gesamte Datei zu verschlüsseln.

Hier ist das Ergebnis:

PyCrypto:

  1. durch ein 128-Bit-Block eines zu verschlüsseln: 61.824 aes-CTR-128 pro

    sec
  2. die gesamte zu verschlüsseln Datei: 8.843.713 AES-CTR-128 pro Sekunde

Kryptografie

  1. 128-Bit-Block einer nach dem anderen zu verschlüsseln: 384.959 aes-CTR-128 pro sec

  2. die gesamte Datei zu verschlüsseln: 113.417.922 aes-CTR-128 pro sec

Ich frage mich, warum Methode 1 und 2 mir das Ergebnis mit so viel Unterschied gab? Nehmen diese beiden Methoden die gleiche Geschwindigkeit an?

Hier ist mein Testcode:

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) 

Habe ich hier etwas vermissen?

Gibt es eine Möglichkeit, dass ich Methode 1 schneller machen kann?

+0

Verschlüsselungen sind, neigen dazu, langsam zu sein, ich glaube nicht, dass Sie gleiche Geschwindigkeit – YOU

+0

bekommen @YOU Ist Methode 1 sollte die gleiche Geschwindigkeit wie Methode 2 bekommen? – Luke

+0

@YOU Die Verschlüsselungsgeschwindigkeit hängt vom Gerät ab, sie muss nicht langsam sein. Ein iPhone6S arbeitet mit mehr als 400 MB/s. – zaph

Antwort

2

AES ist eine Blockchiffre mit mehreren Permutationsrunden. Jede Runde hat ihren eigenen runden Schlüssel, der vom "Master" Schlüssel (key in Ihrem Code) abgeleitet werden muss. Der Aufruf AES.new(key, mode, ...) leitet automatisch die Rundenschlüssel ab, aber dieser Schlüsselzeitplan Prozess ist ziemlich schwer. Das Ausführen des Schlüsselzeitplans für jeden Block wird die Verarbeitung im Vergleich zu dem Ein-Anruf-Ansatz beträchtlich verlangsamen, insbesondere wenn der tatsächliche Verschlüsselungscode den AES-NI-Befehlssatz verwendet.Wie Kennynt in der comments ausführt, ist Python eine interpretierte Sprache, so dass die Iteration über die Blöcke in Python anstelle des zugrundeliegenden nativen Kryptocodes (zB pyCrypto verwendet die C-Bibliothek tomcrypt) notwendigerweise eine zusätzliche Leistungseinbuße zur Folge hat .


Der Code für Methode 1 gebrochen, weil Sie für jeden Block ein neues Counter-Objekt erstellen, die immer auf 1 initialisiert wird. Daher XORing Sie jeden Block mit dem gleichen Schlüsselstrom, der eine many-time pad erstellt und wahrscheinlich ermöglicht es einem Angreifer, den Klartext abzuleiten.

Wir können dies beheben, indem wir nur ein einzelnes Counter-Objekt haben. Durch die Verwendung eines einzigen Schlüsselplans wird die Leistung erheblich gesteigert.

Verbesserte Methode 1 Code:

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

Ergebnisse für 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

+0

Ich habe keine erfahrung mit dem 'kryptografie' Modul, also würde ich vermuten, dass entweder die itera Das Problem ist in Python ein großes Problem oder es hat einen Fehler darin. –

+0

Es macht Sinn, dass jedes Counter-Objekt initialisiert wird. Wenn also in Methode 1 ein neues Counter-Objekt erstellt wird, addieren sich die Kosten. Ich habe zwei Fragen. 1. Methode 2 muss auch den Counter-Wert für jeden 128-Bit-Block um 1 aktualisieren, warum ist es schneller? Liegt es daran, dass es sich um ein Update handelt, anstatt ein neues Counter-Objekt zu erstellen? – Luke

+0

2. In meiner realen Implementierung hängt der Counter-Wert für die nächsten 128 Bit nach meinem Entwurf von der Verschlüsselungsausgabe für die vorherigen 128 Bit ab. Auf diese Weise gibt es eine Möglichkeit, dass ich den Counter-Wert nur aktualisieren, aber ein neues Counter-Objekt nicht initialisieren kann? Danke für Ihre Hilfe. – Luke