Frage

Ich verwende Python 2.7.1 Ich möchte STH mit AES im CTR -Modus verschlüsseln.Ich habe die PyCrypto-Bibliothek für Python installiert.Ich habe den folgenden Code geschrieben:

secret = os.urandom(16)
crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter=lambda: secret)
encrypted = crypto.encrypt("asdk")
print crypto.decrypt(encrypted)

Ich muss crypto.decrypt so oft ausführen, wie mein Klartext Bytes groß ist, um die entschlüsselten Daten korrekt zu erhalten.D.h.:

encrypted = crypto.encrypt("test")
print crypto.decrypt(encrypted)
print crypto.decrypt(encrypted)
print crypto.decrypt(encrypted)
print crypto.decrypt(encrypted)

Der letzte Aufruf zum Entschlüsseln gibt mir den Klartext zurück.Die anderen Ausgaben von decrypt sind einige Kauderwelsch-Strings.Ich frage mich, ob das normal ist oder nicht?Muss ich jedes Mal eine Schleife mit der Größe meines Klartextes einbinden, oder habe ich etwas falsch gemacht?

War es hilfreich?

Lösung

Laut @gertvdijk ist AES_CTR eine Stream-Verschlüsselung, die dies tut nicht brauchen Polsterung.Daher habe ich die entsprechenden Codes gelöscht.

Hier ist etwas, das ich weiß.

  1. Sie müssen denselben Schlüssel verwenden (den ersten Parameter in AES.new(...)) bei der Ver- und Entschlüsselung und halten Sie den Schlüssel geheim.

  2. Die Verschlüsselungs-/Entschlüsselungsmethoden sind Staatsbürgerlich, das bedeutet crypto.en(de)crypt("abcd")==crypto.en(de)crypt("abcd") Ist nicht immer wahr.In Ihrer CTR gibt Ihr Counter-Callback immer das Gleiche zurück, sodass er beim Verschlüsseln zustandslos wird (ich bin nicht 100 % sicher, dass das der Grund ist), aber wir stellen immer noch fest, dass er bei der Entschlüsselung einigermaßen zustandsbehaftet ist.Zusammenfassend lässt sich sagen, dass wir dafür immer ein neues Objekt verwenden sollten.

  3. Der counter callback Die Funktion sollte sich sowohl bei der Verschlüsselung als auch bei der Entschlüsselung gleich verhalten.In Ihrem Fall geht es darum, dass beide das gleiche Geheimnis zurückgeben.Dennoch glaube ich nicht, dass secret ist ein Geheimnis".Sie können einen Zufallsgenerator verwenden "secret" und geben Sie es ohne Verschlüsselung an die kommunizierenden Peers weiter, sodass die andere Seite es direkt verwenden kann, sofern dies der Fall ist secret Ist nicht vorhersehbar.

Deshalb würde ich meine Chiffre so schreiben und hoffe, dass sie etwas Hilfe bietet.

import os
import hashlib
import Crypto.Cipher.AES as AES

class Cipher:

        @staticmethod
        def md5sum( raw ):
                m = hashlib.md5()
                m.update(raw)
                return m.hexdigest()

        BS = AES.block_size

        @staticmethod 
        def pad( s ):
                """note that the padding is no necessary"""
                """return s + (Cipher.BS - len(s) % Cipher.BS) * chr(Cipher.BS - len(s) % Cipher.BS)"""
                return s

        @staticmethod
        def unpad( s ):
                """return s[0:-ord(s[-1])]"""
                return s

        def __init__(self, key):
                self.key = Cipher.md5sum(key)
                #the state of the counter callback 
                self.cnter_cb_called = 0 
                self.secret = None

        def _reset_counter_callback_state( self, secret ):
                self.cnter_cb_called = 0
                self.secret = secret

        def _counter_callback( self ):
                """
                this function should be stateful
                """
                self.cnter_cb_called += 1
                return self.secret[self.cnter_cb_called % Cipher.BS] * Cipher.BS


        def encrypt(self, raw):
                secret = os.urandom( Cipher.BS ) #random choose a "secret" which is not secret
                self._reset_counter_callback_state( secret )
                cipher = AES.new( self.key, AES.MODE_CTR, counter = self._counter_callback )
                raw_padded = Cipher.pad( raw )
                enc_padded = cipher.encrypt( raw_padded )
                return secret+enc_padded #yes, it is not secret

        def decrypt(self, enc):
                secret = enc[:Cipher.BS]
                self._reset_counter_callback_state( secret )
                cipher = AES.new( self.key, AES.MODE_CTR, counter = self._counter_callback )
                enc_padded = enc[Cipher.BS:] #we didn't encrypt the secret, so don't decrypt it
                raw_padded = cipher.decrypt( enc_padded )
                return Cipher.unpad( raw_padded )

Einige Tests:

>>> from Cipher import Cipher
>>> x = Cipher("this is key")
>>> "a"==x.decrypt(x.encrypt("a"))
True
>>> "b"==x.decrypt(x.encrypt("b"))
True
>>> "c"==x.decrypt(x.encrypt("c"))
True
>>> x.encrypt("a")==x.encrypt("a")
False #though the input is same, the outputs are different

Referenz: http://packages.python.org/pycrypto/Crypto.Cipher.blockalgo-module.html#MODE_CTR

Andere Tipps

Ich werde auf @ Gertvdijks Erklärung, warum die Verschlüsselung aufwendig ist, warum sich die Zeichnung benimmt, wie es in der ursprünglichen Frage (meine Bearbeitung abgelehnt wurde), aber auch darauf hinweisen, dass die Einrichtung des Zählers zum Zurückgeben eines statischen Werts ein Major ist Fehler und zeigen Sie, wie Sie es richtig einrichten.

Setzen Sie den Zähler für neue Operationen

zurück

der Grund, warum sich dies verhält, wie Sie sich in der Frage beschrieben haben, ist, dass Ihr einfacher Text (4 Bytes / 32 Bit) viermal so klein ist, wie die Größe des Schlüsselstroms blockiert, dass die CTR-Chips für die Verschlüsselung (16 Bytes / 128 Bits).

Da Sie anstelle eines tatsächlichen Zählers denselben festen Wert verwenden, spuckt die Chiffre den gleichen 16-Byte-Blöcke von TESTREAM aus. Sie können dies beobachten, indem Sie wiederholt 16 NULL-Bytes verschlüsseln:

generasacodicetagpre.

Sie setzen den Status des Chiffres auch nicht vor, bevor Sie die Entschlüsselung durchführen, sodass die 4 Bytes von CEHRTEXT von den nächsten 4 Bytes der XOR-Taste vom ersten Ausgangsstromblock entschlüsselt werden. Dies kann auch durch Verschlüsseln und Entschlüsseln von Null-Bytes beachtet werden:

generasacodicetagpre.

Wenn dies so arbeiten würde, wie Sie wollten, sollte das Ergebnis beider beiden Vorgängen gleich sein. Stattdessen können Sie die ersten vier Bytes des 16 Byteblocks im ersten Ergebnis sehen, und die zweiten vier Bytes im zweiten Ergebnis.

Nachdem Sie den 16 Byte-Block der XOR-Taste aufgebraucht haben, indem Sie vier Operationen auf vier-Byte-Werten (für 16 Byte-Total) durchführen, wird ein neuer Block der XOR-Taste erzeugt. Die ersten vier Bytes (sowie alle anderen) jedes XOR-Schlüsselblocks sind gleich, also wenn Sie diesmal entschlüsseln, gibt es den Klartext zurück.

Das ist wirklich schlecht! Sie sollten AES-CTR diesen Weg nicht verwenden - es entspricht der einfachen XOR-Verschlüsselung mit einem 16-Byte-Wiederholungsschlüssel, der ziemlich leicht gebrochen werden kann.

Lösung

Sie müssen den Zustand der Chiffre zurücksetzen, bevor Sie einen Betrieb in einem neuen Datenstrom (oder einem anderen Vorgang darauf) durchführen, da die ursprüngliche Instanz nicht mehr im richtigen Anfangszustand liegt. Ihr Problem wird gelöst, indem ein neues crypto-Objekt für die Entschlüsselung instanziiert wird, sowie das Zurücksetzen der Zähler- und Keystream-Position.

Sie müssen auch eine ordnungsgemäße Zählerfunktion verwenden , die eine Nonce mit einem Zählerwert kombiniert, der jedes Mal steigt, wenn ein neuer Block von Keystream erzeugt wird. Pycrypto hat eine Counter-Klasse, die dies für Sie tun kann.

generasacodicetagpre.

Beginnen Sie mit einem neuen Kryptoobjekt für neue Vorgänge

Der Grund dafür, dass sich das so verhält, wie Sie es in der Frage beschrieben haben, liegt darin, dass Ihr Klartext (4 Bytes/32 Bits) viermal so klein ist wie die Größe, mit der die kryptografische Engine für den von Ihnen gewählten AES-Modus arbeitet (128 Bits) und diese auch wiederverwendet Instanz des Kryptoobjekts.Verwenden Sie einfach nicht dasselbe Objekt wieder, wenn Sie eine Operation an einem neuen Datenstrom (oder eine andere Operation daran) ausführen.Ihr Problem wird durch die Instanziierung einer neuen Lösung gelöst crypto Objekt für die Entschlüsselung, etwa so:

# *NEVER* USE A FIXED LIKE COUNTER BELOW IN PRODUCTION CODE. READ THE DOCS.
counter = os.urandom(16)
key = os.urandom(32)  # 256 bits key

# Instantiate a crypto object first for encryption
encrypto = AES.new(key, AES.MODE_CTR, counter=lambda: counter)
encrypted = encrypto.encrypt("asdk")

# Instantiate a new crypto object for decryption
decrypto = AES.new(key, AES.MODE_CTR, counter=lambda: counter)
print decrypto.decrypt(encrypted) # prints "asdk"

Warum es nicht um das Auffüllen mit AES-CTR geht

Diese Antwort begann als Antwort auf die Antwort von Marcus, in dem er zunächst darauf hinwies, dass die Verwendung einer Polsterung das Problem lösen würde.Ich verstehe zwar, dass es wie Symptome eines Polsterungsproblems aussieht, aber das ist ganz sicher nicht der Fall.

Der springende Punkt bei AES-CTR ist, dass Sie brauchen keine Polsterung, da es ein ist Stream Chiffre (im Gegensatz zu ECB/CBC usw.)!Stromchiffren arbeiten mit Datenströmen, indem sie Daten in Blöcke aufteilen und diese in der eigentlichen kryptografischen Berechnung verketten.

Zusätzlich zu dem, was Marcus sagt, der Crypto.Util.Counter Klasse kann verwendet werden, um Ihre Zählerblockfunktion zu erstellen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top