Script und Konsole, zlib und Bytes

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
rami
User
Beiträge: 6
Registriert: Mittwoch 24. Februar 2010, 18:01

Hallo!

Ich habe einen Text, der verschlüsselt, komprimiert und in einem bestimmten Format werden soll.
Dies geschieht durch:

Code: Alles auswählen

contents = zlib.compress(contents)
contents = tollekryptographischefunktion(password, contents.strip())
contents = zlib.compress(contents)
Von diesem wird nun jeweils ein Byte genommen (mit contents) und mit ord() in eine Zahl gewandelt:

Code: Alles auswählen

for i in range(0, len(infilecontents)):
        print ord(infilecontents[i]),
Der Output ist:

Code: Alles auswählen

120 156 1 32 0 223 255 199 49 14 225 96 246 70 122 214 237 227 30 75 127 173 56 155 245 122 199 206 203 16 65 78 155 243 133 175 50 245 45 34 146 17 144
Das ganze wird jetzt einem speziellen Format weiterverarbeitet, dessen Spezifikation tut nichts zur Sache.

So weit so gut - jetzt der Rückweg:

Ich parse das Format, bis ich bei diesen Zahlen bin. Auch hier bekomme ich als Debug-Ausgabe:

Code: Alles auswählen

120 156 1 32 0 223 255 199 49 14 225 96 246 70 122 214 237 227 30 75 127 173 56 155 245 122 199 206 203 16 65 78 155 243 133 175 50 245 45 34 146 17 144
Das Format an sich arbeitet also einwandfrei - schließlich bekomme ich zurück, was ich im ersten Schritt eingeworfen hab. So, nun will ich aber wieder zum Plaintext kommen. Dazu wandele ich die Zahlen wieder in die Bytes um:

Code: Alles auswählen

contents = ""
for byte in bytes:
	byte = chr(byte)
	contents += byte
Wenn ich mir jetzt contents ausgeben lasse, ist das wie erwartet sehr kryptisch (x� ���1�`�Fz���K�8��z���AN����2�-"��).
Mein Versuch, das ganze zu decodieren, sieht so aus:

Code: Alles auswählen

contents = zlib.decompress(contents)
contents = tollekryptographischefunktionDecrypt(password, contents)
contents = zlib.decompress(contents)

Code: Alles auswählen

Traceback (most recent call last):
[..]
    contents = zlib.decompress(contents)
zlib.error: Error -3 while decompressing data: incorrect header check
Momentmal - WTF? zlib kann seine eigenen Daten nicht mehr lesen?

Es kommt noch interessanter:
Wenn ich mein Programm in einer interaktiven python-Session als Modul lade und manuell ausführe und contents vor dem decompress in eine Variable schreibe und diese auf der Konsole ausgebe, scheint alles zu funktionieren:

Code: Alles auswählen

>>> g.debug
'x\x9c\x01 \x00\xdf\xff\xc71\x0e\xe1`\xf6Fz\xd6\xed\xe3\x1eK\x7f\xad8\x9b\xf5z\xc7\xce\xcb\x10AN\x9b\xf3\x85\xaf2\xf5-"\x92\x11\x90'
>>> zlib.decompress('x\x9c\x01 \x00\xdf\xff\xc71\x0e\xe1`\xf6Fz\xd6\xed\xe3\x1eK\x7f\xad8\x9b\xf5z\xc7\xce\xcb\x10AN\x9b\xf3\x85\xaf2\xf5-"\x92\x11\x90')
'\xc71\x0e\xe1`\xf6Fz\xd6\xed\xe3\x1eK\x7f\xad8\x9b\xf5z\xc7\xce\xcb\x10AN\x9b\xf3\x85\xaf2\xf5-'
>>> zlib.decompress(g.debug)
'\xc71\x0e\xe1`\xf6Fz\xd6\xed\xe3\x1eK\x7f\xad8\x9b\xf5z\xc7\xce\xcb\x10AN\x9b\xf3\x85\xaf2\xf5-'
Was ist auf der Konsole anders als in meinem Script?

Weitere Infos:
Die Datei enthält zwar ein,

Code: Alles auswählen

# -*- coding: utf-8 -*-
die Strings sind aber alle "normal" (ohne u davor).

Kann mir irgendjemand weiterhelfen?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo und willkommen im Forum!

Das contents.strip() scheint mir keine gute Idee zu sein. Immerhin abreitest du mit Binärdaten. Wenn das nicht helfen sollte, erstelle mal ein kleines (laufähiges!) Minimalbeispiel. Dann lässt es sich sicher schnell rausfinden.

Kommst du zufällig aus der C-Ecke?

Code: Alles auswählen

for i in range(0, len(infilecontents)):
        print ord(infilecontents[i]),
solltest du besser als
for elem in infilecontents:
print ord(elem),[/code]
schreiben. Ansonsten einfach noch mal einen Blick ins Tutorial werfen.

Strings mit += zusamenfügen ist auch nicht so schön. Erstelle lieber eine Liste und rufe dann auf dieser "".join auf.

Sebastian
Das Leben ist wie ein Tennisball.
rami
User
Beiträge: 6
Registriert: Mittwoch 24. Februar 2010, 18:01

Hallo,
EyDu hat geschrieben:Das contents.strip() scheint mir keine gute Idee zu sein.
Immerhin abreitest du mit Binärdaten.
Dachte ich auch, während ich den Post verfasst habe, aber ohne hat es auch nichts geändert. Kommt aber raus.
EyDu hat geschrieben:Wenn das nicht helfen sollte, erstelle mal ein kleines (laufähiges!) Minimalbeispiel. Dann lässt es sich sicher schnell rausfinden.
Ich probiers nachher mal, muss mich dazu auch noch mit jemandem absprechen, u.U. kann ich den Original-Source veröffentlichen.
Kommst du zufällig aus der C-Ecke?

Code: Alles auswählen

for i in range(0, len(infilecontents)):
        print ord(infilecontents[i]),
solltest du besser als

Code: Alles auswählen

for elem in infilecontents:
        print ord(elem),
schreiben. Ansonsten einfach noch mal einen Blick ins Tutorial werfen.

Strings mit += zusamenfügen ist auch nicht so schön. Erstelle lieber eine Liste und rufe dann auf dieser "".join auf
Nein, ich komme aus der PHP-Ecke ;-) Danke für die Tipps zum Codestil!

Edit: Es liegt an der PyCrypto-Library. Kennt jemand eine gute, komfortable AES-Implementierung in Python? Mitschuld sind wohl meine Workarounds dafür, dass bei der AES-Implementierung Länge des Plaintext durch 16 teilbar sein muss und der Key ebenfalls eine bestimmte Länge haben muss, was ich meinen Usern nicht abverlangen will. Kennt da vielleicht jemand eine Lösung?
lunar

Du wirst keine AES-Implementierung ohne diese Einschränkungen finden. Feste Schlüssellängen und Blockgrößen sind inhärente Eigenschaften einer Blockchiffre wie AES, und lassen sich nicht beliebig wählen. Du suchst keine „komfortable AES-Implementierung“, sondern ein kryptographisches Protokoll bzw. System zur Verschlüsselung. Das ist ein paar Ebenen höher. Was da geeignet ist, hängt vom Einsatzzweck ab, pauschal würde ich erstmal auf GnuPG verweisen.

Und versuche Dich bitte nicht an einer eigenen Implementierung, zumindest nicht für produktive Zwecke. Man kann da viel falsch machen, und Deinem Beitrag nach zu urteilen, bist Du eben auf diesem Weg … davon zeugt schon, dass Du offenbar die Benutzereingaben direkt als AES-Schlüssel verwenden willst.

Nichts für ungut …
rami
User
Beiträge: 6
Registriert: Mittwoch 24. Februar 2010, 18:01

lunar hat geschrieben:Du wirst keine AES-Implementierung ohne diese Einschränkungen finden. Feste Schlüssellängen und Blockgrößen sind inhärente Eigenschaften einer Blockchiffre wie AES, und lassen sich nicht beliebig wählen. Du suchst keine „komfortable AES-Implementierung“, sondern ein kryptographisches Protokoll bzw. System zur Verschlüsselung.
Hmpf. Okay
lunar hat geschrieben:Was da geeignet ist, hängt vom Einsatzzweck ab, pauschal würde ich erstmal auf GnuPG verweisen.
Assymetrische Verschlüsselung wäre für meinen Anwendungszweck übertrieben, aber ich behalte GPG mal im Hinterkopf
lunar hat geschrieben:Und versuche Dich bitte nicht an einer eigenen Implementierung, zumindest nicht für produktive Zwecke.
Nein, sicher nicht. Wenn ich eins über Kryptographie weiß, dann, dass man das Erfinden und Implementieren von den Algorithmen selber lieber den Experten überlässt.
lunar hat geschrieben:Man kann da viel falsch machen, und Deinem Beitrag nach zu urteilen, bist Du eben auf diesem Weg … davon zeugt schon, dass Du offenbar die Benutzereingaben direkt als AES-Schlüssel verwenden willst.
Okay, das ist keine gute Idee? Ich brauche eine symmetrische Verschlüsselung mit Passwörtern, weil es sich um eine Art Dateiverschlüsselung handelt. Was sollte es für einen Vorteil bringen, einen Zufallsschlüssel für AES zu verwenden und diesen dann anderweitig mit dem Passwort zu verschlüsseln?

Danke für deinen Beitrag, nett, dass du mich vor Dummheiten schützen willst, aber: Es wäre noch schön, wenn du mir einen Anstoß geben würdest, wie ich mich besser informieren kann :-)
lunar

@rami: GnuPG kann auch symmetrisch mit Hilfe eines Passworts verschlüsseln. Verwende einfach GnuPG.

Und Du versuchst Dich eben doch an einer eigenen Implementierung. Zwar nicht des Algorithmus selbst, sondern des umgebenden Protokolls, aber das ist schon schwer genug, wie Du wohl bemerkt hast.

Benutzereingaben sich Zeichen. Die druckbaren Zeichen aber repräsentieren als Bytewerte nur einen minimalen Teil des kompletten Schlüsselraums. Zudem unterliegen Benutzereingaben den Regeln einer Zeichenkodierung. Zeichenkodierungen aber nutzen oft feste Werte (e.g. Nullbytes in UTF-16), was den tatsächlichen Schlüssel noch weiter reduziert. Das löst man aber nicht mit einem "Zufallsschlüssel", den man
dann anderweitig verschlüsselt (wo immer Du das aus meinen Beitrag rausgelesen hast). Lies Dir doch bitte mal durch, was PBKDF 2 wirklich tut …
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Was ich komisch finde (ohne jetzt das Problem wirklich verstanden zu haben): Wenn das Komprimieren einer verschlüsselten komprimierten Bytefolge noch etwas bringt, dann ist der Verschlüsselungsalgorithmus `tollekryptographischefunktion` Mist. Komprimieren dampft ja die Daten nahe dem informationstheoretischen Minimum ein und wenn da beim Verschlüsseln wieder Redundanzen auftreten, dann könnte man diese nutzen, um die Verschlüsselung zu brechen.

Später wird AES erwähnt. AES wird immer zusammen mit einem Padding-Algorithmus und einem Block-Mode benutzt, üblich ist z.B. AES/CBC/PKCS5. Dieses Tripel muss man kennen.

Eine gute Crypto-Bibliothek (wie z.B. die von Java - in Python habe ich mich damit nie beschäftigt) kann das eigentlich automatisch. Zur Not kann man das Padding auch selbst machen, die üblichen Algorithmen sind sehr einfach. Man sollte sich aber an die Algorithmen halten, denn da haben schlaue Leute lange nachgedacht, wie man die Daten auf ein Vielfaches der Blockgröße aufrundet, ohne die Verschlüsselung zu schwächen.

Mit AES ist meist AES128 gemeint, der einen 128-Bit = 16 Byte Schlüssel benötigt. Kürzere Schlüssel zu benutzen schwächt natürlich wieder die Verschlüsselung. Ein optimaler Schlüssel ist eine echte 128-Bit-Zufallszahl. Wenn du aber unbedingt aus einem lesbaren String (bei typischer Kennwortlänge von 7 alphanumerischen Zeichen sind das 42 Bit) ein Kennwort berechnen willst, nimm die MD5-Summe davon. Das ist dann zumindest eine auf dem Kennwort basierende Pseudozufallszahl mit den gewünschten 128 Bits.

Stefan
lunar

@sma: AES kann man nicht mit „kürzeren“ Schlüsseln verwenden. 128, 192 oder 256 Bit sind die einzigen Schlüssellängen, die AES unterstützt.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Mit einem kürzeren Schlüssel meinte ich, das der Benutzer vielleicht "ABC" als Kennwort eingibt, was man dann entsprechend auffüllen muss. Es sind dann zwar 128-Bit, aber nur 18 davon sind signifikant, weil sie sich überhaupt bei anderen Kennwörtern ändern.

Stefan
rami
User
Beiträge: 6
Registriert: Mittwoch 24. Februar 2010, 18:01

Okay, vielen Dank für eure Hilfen. Der fertige Code funktioniert jetzt, nutzt AES im CFB-Mode, damit der Plaintext jede Länge haben kann, komprimiert nur vor der Verschlüsselung optional mit zlib. Als Key wird der MD5-Hash des Benutzerpassworts verwendet, um immer eine konstante Länge zu haben. Ich bin jetzt gewarnt, das Programm nicht für Staatsgeheimnisse einzusetzen, das habe ich aber so oder so nicht vorgehabt.
Antworten